nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.56k stars 2.36k forks source link

@nx/webpack: Cannot exclude external dependencies when targeting a specific version of node #27580

Open a88zach opened 2 months ago

a88zach commented 2 months ago

Current Behavior

When targeting a specific version version of node in a webpack config and setting externalDependencies: 'all', the external dependencies are still bundled in the output

Expected Behavior

External dependencies should not be in the output bundle

GitHub Repo

No response

Steps to Reproduce

  1. Create a new node app that uses the webpack bundler
  2. Update the webpack config
    
    // webpack.config.js

const { join } = require('path');

const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');

module.exports = { output: { path: join( __dirname, '../../dist/apps/app-name', ), target: 'node20', // <- HERE library: { type: 'commonjs', }, }, plugins: [ new NxAppWebpackPlugin({ externalDependencies: 'all', compiler: 'tsc', main: './src/main.ts', optimization: false, outputHashing: false, tsConfig: './tsconfig.app.json', generatePackageJson: true, }), ], };

3. Build the app and view the generated output

### Nx Report

```shell
Node           : 20.16.0
OS             : darwin-arm64
Native Target  : aarch64-macos
yarn           : 3.8.4

nx                 : 19.6.1
@nx/js             : 19.6.1
@nx/jest           : 19.6.1
@nx/linter         : 19.6.1
@nx/eslint         : 19.6.1
@nx/workspace      : 19.6.1
@nx/cypress        : 19.6.1
@nx/devkit         : 19.6.1
@nx/esbuild        : 19.6.1
@nx/eslint-plugin  : 19.6.1
@nx/next           : 19.6.1
@nx/node           : 19.6.1
@nx/plugin         : 19.6.1
@nx/react          : 19.6.1
@nx/storybook      : 19.6.1
@nrwl/tao          : 19.6.1
@nx/vite           : 19.6.1
@nx/web            : 19.6.1
@nx/webpack        : 19.6.1
typescript         : 5.5.4
---------------------------------------
Registered Plugins:
@nx/eslint/plugin
./tools/typescheck.plugin.ts
@nx/jest/plugin
@nx/webpack/plugin
@nx/storybook/plugin
@nx/next/plugin
---------------------------------------
Local workspace plugins:
         @wastewizer/typescheck

Failure Logs

No response

Package Manager Version

No response

Operating System

Additional Information

The issue is here: https://github.com/nrwl/nx/blob/master/packages/webpack/src/plugins/nx-webpack-plugin/lib/apply-base-config.ts#L337

Webpack allows you to target a specific version of node: https://webpack.js.org/configuration/target/

a88zach commented 2 months ago

It is also worth noting that you currently cannot specify the importType for the nodeExternals package that is used to exclude 3rd party packages. To get around this, I had to fallback to composing the webpack configuration so I could set the externals after nx modified the config

// ./tools/createWebpackConfig.cjs

const { join } = require('path');

const { composePlugins, withNx } = require('@nx/webpack');
const {
  NxTsconfigPathsWebpackPlugin,
} = require('@nx/webpack/tsconfig-paths-plugin');
const nodeExternals = require('webpack-node-externals');

module.exports = ({
  packageRoot,
  outputPath,
  additionalEntryPoints,
  assets,
}) => {
  return composePlugins(
    () => ({
      output: {
        path: join(packageRoot, outputPath),
        library: {
          type: 'module',
        },
      },
      target: 'node20',
      externalsPresets: {
        node: true,
      },
      experiments: {
        outputModule: true,
      },
      plugins: [
        new NxTsconfigPathsWebpackPlugin({
          tsConfig: join(packageRoot, 'tsconfig.app.json'),
        }),
      ],
    }),
    withNx({
      compiler: 'tsc',
      main: join(packageRoot, 'src/main.ts'),
      optimization: false,
      outputHashing: false,
      tsConfig: join(packageRoot, 'tsconfig.app.json'),
      outputFileName: 'main.js',
      generatePackageJson: true,
      additionalEntryPoints,
      assets,
    }),
    (config) => {
      const externals = [];
      externals.push(
        nodeExternals({
          modulesDir: join(__dirname, '../node_modules'),
          importType: 'node-commonjs',
        }),
      );

      config.externals = externals;

      return config;
    },
  );
};
// webpack.config.cjs

const createWebpackConfig = require('../../../tools/createWebpackConfig.cjs');

module.exports = createWebpackConfig({
  packageRoot: __dirname,
  outputPath: '../../dist/apps/app-name',
});