2

I am using two repos, call it web-common and A-frontend. I normally npm link web-common from A-frontend. Both have several of the same dependencies, React, Typescript, Google Maps, MobX, etc. I've never had an issue with this workflow. As I added RxJS I started getting errors likes this:

TS2345: Argument of type 'import("/Users/fharvey/code/monolith/A-frontend/node_modules/rxjs/internal/types").OperatorFunction<import("/Users/fharvey/code/monolith/web-common/services/scaffold/replay_pb").ReplayAllMessagesResult, import("/Users/fharvey/code/monolith/A-frontend/src/app/components/pages/RobotReporting/index").TypeSiloCollec...' is not assignable to parameter of type 'import("/Users/fharvey/code/monolith/web-common/node_modules/rxjs/internal/types").OperatorFunction<import("/Users/fharvey/code/monolith/web-common/services/scaffold/replay_pb").ReplayAllMessagesResult, import("/Users/fharvey/code/monolith/A-frontend/src/app/components/pages/RobotReporting/index").TypeSiloCollecti...'.
  Types of parameters 'source' and 'source' are incompatible.

which, in short, is saying ~"A-frontend/node_modules/rxjs/internal/types".OperatorFunction is not the same as ~"web-common/node_modules/rxjs/internal/types".OperatorFunction. I have seen plenty in regards to this:

There is one tricky part here, and that is that this works in my dev build, but not my prod build.

Common Webpack Config

const path = require('path')
const webpack = require('webpack')
const CircularDependencyPlugin = require('circular-dependency-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const GitRevisionPlugin = require('git-revision-webpack-plugin')
const gitRevisionPlugin = new GitRevisionPlugin({
    /*
     * The gist of this command is
     * 1) Make -rc.X tags sort after main tags (so v3.0.2 is a higher version than v3.0.2-rc.3)
     * 2) Get all tags that point to the current commit, sort by version
     * 3) Return the first (i.e. most recent) tag name
     */
    versionCommand: 'config versionsort.suffix --add "-rc"; git tag --points-at HEAD --sort=-version:refname | head -n 1'
})
// Paths are relative to the client folder, not to this config file, as this is where node is run from

const commonConfig = {
    context: path.resolve('./src'),
    entry: {
        // 'sign-in': 'sign-in',
        // 'app': 'app',
        // home: 'home',                // Signed-out bundle
        'index': 'global'           // Signed-in bundle
    },
    module: {
        rules: [
            {
                test: /\.md$/, use: [
                    { loader: 'html-loader' },
                    { loader: 'markdown-loader' }
                ]
            },
            {
                test: /\.tsx?$/,
                loader: 'ts-loader'
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        options: { importLoaders: 1 }
                    },
                    { loader: 'postcss-loader' },
                    {
                        loader: 'less-loader',
                        options: {
                            javascriptEnabled: true
                        }
                    }
                ],
                // fallback: 'style-loader'
            },
            {
                test: /\.ttf$/,
                use: 'file-loader'
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.(svg|png)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 8192
                        }
                    }
                ]
            }
        ]
    },
    output: {
        publicPath: '/static/',
    },
    plugins: [
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), // don't bundle unnecessary moment.js bloat
        new webpack.IgnorePlugin(/\.test\.tsx?/),
        new CircularDependencyPlugin({
            exclude: /a\.js|node_modules/,
            failOnError: true
        }),
        gitRevisionPlugin,
        new webpack.EnvironmentPlugin({
            VERSION: gitRevisionPlugin.version()
        })
    ],
    resolve: {
        extensions: ['.js', '.ts', '.tsx'],
        modules: [
            path.resolve('./src'),
            'node_modules'
        ],
        symlinks: false,    // linked dependency peer dependencies resolve correctly
        alias: {
            rxjs: path.resolve('./node_modules/rxjs'),
        },
    },
    optimization: {
        splitChunks: {
            // include all types of chunks
            chunks: 'all',
            cacheGroups: {
                react: {
                    test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
                    name: 'vendor-react',
                    chunks: 'all',
                },
                antd: {
                    test: /[\\/]node_modules[\\/](antd)[\\/]/,
                    name: 'vendor-antd',
                    chunks: 'all',
                }
            }
        }
    },
    target: 'web'
}

module.exports = commonConfig

Dev Webpack Config

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")

const commonConfig = require('./common.config')

const devConfig = Object.assign(commonConfig, {
    mode: 'development',
    devtool: 'eval-cheap-source-map',
    output: Object.assign(commonConfig.output, {
        filename: '[name].js', // [name] resolves to name of bundle (e.g., home, customer)
        chunkFilename: '[name].js',
        path: path.resolve('./build/static')
    }),
    stats: {
        warnings: false
    },
    module: {
        rules: [{
            test: /\.md$/,
            use: [{
                loader: 'html-loader'
            },
            {
                loader: 'markdown-loader'
            }
            ]
        },
        {
            test: /\.tsx?$/,
            use: [{
                loader: 'cache-loader'
            }, // caches typescript compilation,
            {
                loader: 'ts-loader',
                options: {
                    // disables type-checking so it can be handled by fork-ts-checker-webpack-plugin
                    transpileOnly: true
                }
            }
            ]
        },
        {
            test: /\.less$/,
            use: [
                MiniCssExtractPlugin.loader,
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 1
                    }
                },
                {
                    loader: 'postcss-loader'
                },
                {
                    loader: 'less-loader',
                    options: {
                        javascriptEnabled: true
                    }
                }
            ]
        },
        {
            test: /\.ttf$/,
            use: 'file-loader'
        },
        {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
        },
        {
            test: /\.(svg|png)$/,
            use: [{
                loader: 'url-loader',
                options: {
                    limit: 8192
                }
            }]
        }
        ]
    },
    plugins: commonConfig.plugins.concat(
        new MiniCssExtractPlugin({
            filename: '[name].css'
        }),
        // Move typescript type checking to a separate process to speed up compile time
        new ForkTsCheckerWebpackPlugin({
            tsconfig: '../tsconfig.json',
            useTypescriptIncrementalApi: true, // uses incremental compilation api from typescript (2.7+)
            async: true // Used for docker
        }),
        new HtmlWebpackPlugin({
            filename: '../index.html',
            template: 'signed-in-template.html',
            favicon: 'assets/images/favicon.ico'
        }),
        new webpack.EnvironmentPlugin({
            //  some stuff 
        })
    )
})

module.exports = devConfig

Prod Webpack Config

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const TerserJSPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

const commonConfig = require('./common.config')

const prodConfig = Object.assign(commonConfig, {
    mode: 'production',
    devtool: false,
    output: Object.assign(commonConfig.output, {
        filename: '[name].[contenthash].js', // [name] resolves to name of bundle (e.g., home, customer)
        chunkFilename: '[name].[contenthash].js',
        path: path.resolve('./prod/static')
    }),
    plugins: commonConfig.plugins.concat(
        new MiniCssExtractPlugin({
            filename: '[name].[contenthash].css'
        }),
        new ForkTsCheckerWebpackPlugin({
            tsconfig: '../tsconfig.json',
            useTypescriptIncrementalApi: true, // uses incremental compilation api from typescript (2.7+)
            async: true // Makes docker work
        }),
        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV': JSON.stringify('production')
            }
        }),
        new HtmlWebpackPlugin({
            filename: '../index.html',
            template: 'signed-in-template.html',
            favicon: 'assets/images/favicon.ico'
        }),
        new webpack.EnvironmentPlugin({
            // some stuff
        })
    ),
    optimization: Object.assign(commonConfig.optimization, {
        minimizer: [
            new TerserJSPlugin({
                parallel: true
            }),
            new OptimizeCSSAssetsPlugin({})
        ]
    })
})

module.exports = prodConfig

tsconfig

{
    "compilerOptions": {
        "skipLibCheck": true,
        "alwaysStrict": true,
        "baseUrl": "src",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "forceConsistentCasingInFileNames": true,
        "jsx": "react",
        "module": "esnext",
        "moduleResolution": "node",
        "lib": [
            "dom",
            "es2015" // Promise, classes, etc. tsc injects shims for ES5 browsers
        ],
        "noImplicitAny": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "strictNullChecks": true,
        "target": "es5",
        "types": [
            "reflect-metadata",
            "googlemaps",
            "google.analytics",
            "jest",
            "jest-enzyme"
        ],
        "paths": {
            "*": [
                "node_modules/@types/*",
                "*"
            ],
            "rxjs": [
                "node_modules/rxjs"
            ],
            "rxjs/*": [
                "node_modules/rxjs/*"
            ]
        },
    },
    "exclude": [
        "node_modules",
        "src/**/*.test.ts",
        "src/**/*.test.tsx",
        "node_modules/web-common/node_modules/*"
    ],
    "include": [
        "src/**/*.ts",
        "src/**/*.tsx",
        "typings/*.d.ts"
    ]
}

I apologize for not cutting down on my pasting, at this point I have literally no idea why this is happening and thought I would just include everything. I am not using any import ... from "rxjs/internal" but that is happening behind the scenes. This is not happening, and has never happened, with any other dependency. I have tried installing web-common instead of linking, no dice.

0

1 Answer 1

1

I think this is also an error for your dev build, but it'll just not fail on a TS error, because you set transpileOnly: true in the dev ts-loader configuration.

The types are always incompatible, because they come from 2 distinct RxJS installations -- as 2 distinct modules. The only straightforward way to fix this I can think of is to physically install web-common into your project, at least before doing a production build.


It is only a Typescript error, and as long as the 2 RxJS installations are compatible versions, the JS code that's actually built will probably run just fine.

However, webpack probably thinks about this the same way, and will probably add 2 versions of RxJS to the bundle -- 1 used by web-common and another used by A-frontend.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.