From 454fba49b2a700bba74542c90cd9fd7dcb947a41 Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Thu, 12 Jan 2023 10:06:25 -0500 Subject: [PATCH] feat(webpack): generate React and Web apps with webpack.config.js file (#14285) --- e2e/node/src/node-esbuild.test.ts | 35 ++++ e2e/node/src/node-webpack.test.ts | 35 ++++ e2e/node/src/node.test.ts | 12 +- e2e/react/src/react.module-federation.test.ts | 29 +-- .../src/generators/application/application.ts | 1 + .../application/lib/normalize-options.ts | 1 + .../application/application.spec.ts | 175 +++++++++--------- .../src/generators/application/application.ts | 13 +- .../files/common/webpack.config.js__tmpl__ | 8 + packages/react/index.ts | 1 + packages/react/plugins/storybook/index.ts | 8 +- packages/react/plugins/webpack.ts | 67 +------ packages/react/plugins/with-react.ts | 69 +++++++ .../application/application.spec.ts | 4 +- .../index.html__tmpl__ | 0 .../public/favicon.ico | Bin .../src/app/__fileName__.spec.tsx__tmpl__ | 0 .../src/app/nx-welcome.tsx | 0 .../src/assets/.gitkeep | 0 .../environments/environment.prod.ts__tmpl__ | 0 .../src/environments/environment.ts__tmpl__ | 0 .../src/main.tsx__tmpl__ | 0 .../tsconfig.app.json__tmpl__ | 0 .../{common => base-webpack}/.babelrc__tmpl__ | 0 .../src/app/__fileName__.spec.tsx__tmpl__ | 0 .../src/app/nx-welcome.tsx | 0 .../src/assets/.gitkeep | 0 .../environments/environment.prod.ts__tmpl__ | 0 .../src/environments/environment.ts__tmpl__ | 0 .../{common => base-webpack}/src/favicon.ico | Bin .../{common => base-webpack}/src/index.html | 0 .../src/main.tsx__tmpl__ | 0 .../tsconfig.app.json__tmpl__ | 0 .../base-webpack/webpack.config.js__tmpl__ | 9 + .../src/app/__fileName__.module.__style__ | 0 .../src/app/__fileName__.tsx__tmpl__ | 0 .../src/styles.__style__ | 0 .../src/app/__fileName__.__style__ | 0 .../src/app/__fileName__.tsx__tmpl__ | 0 .../src/styles.__style__ | 0 .../src/app/__fileName__.tsx__tmpl__ | 0 .../src/app/__fileName__.tsx__tmpl__ | 0 .../src/app/__fileName__.tsx__tmpl__ | 0 .../generators/application/lib/add-project.ts | 5 +- .../lib/create-application-files.ts | 14 +- .../webpack.server.config.js__tmpl__ | 10 +- .../module-federation.config.js__tmpl__ | 17 +- .../webpack.config.js__tmpl__ | 15 +- .../webpack.config.prod.js__tmpl__ | 11 +- ...module-federation.server.config.js__tmpl__ | 9 +- .../webpack.server.config.js__tmpl__ | 8 +- .../module-federation.config.js__tmpl__ | 9 +- .../webpack.config.js__tmpl__ | 15 +- .../src/generators/setup-ssr/setup-ssr.ts | 2 +- .../application/application.spec.ts | 1 + .../src/generators/application/application.ts | 6 +- .../{app => app-webpack}/.babelrc__tmpl__ | 0 .../src/app/app.element.__style__ | 0 .../src/app/app.element.spec.ts__tmpl__ | 0 .../src/app/app.element.ts__tmpl__ | 0 .../{app => app-webpack}/src/assets/.gitkeep | 0 .../environments/environment.prod.ts__tmpl__ | 0 .../src/environments/environment.ts__tmpl__ | 0 .../{app => app-webpack}/src/favicon.ico | Bin .../files/{app => app-webpack}/src/index.html | 0 .../{app => app-webpack}/src/main.ts__tmpl__ | 0 .../{app => app-webpack}/src/styles.__style__ | 0 .../{app => app-webpack}/tsconfig.app.json | 0 .../files/{app => app-webpack}/tsconfig.json | 0 .../app-webpack/webpack.config.js__tmpl__ | 8 + .../application/files/app/browserslist | 13 -- .../executors/dev-server/dev-server.impl.ts | 11 +- .../webpack/src/executors/webpack/schema.d.ts | 1 + ...gin.ts => generate-package-json-plugin.ts} | 18 +- packages/webpack/src/utils/config.ts | 8 +- .../src/utils/webpack/custom-webpack.ts | 10 + packages/webpack/src/utils/with-nx.ts | 25 ++- packages/webpack/src/utils/with-web.ts | 12 +- 78 files changed, 404 insertions(+), 291 deletions(-) create mode 100644 e2e/node/src/node-esbuild.test.ts create mode 100644 e2e/node/src/node-webpack.test.ts create mode 100644 packages/node/src/generators/application/files/common/webpack.config.js__tmpl__ create mode 100644 packages/react/plugins/with-react.ts rename packages/react/src/generators/application/files/{common-vite => base-vite}/index.html__tmpl__ (100%) rename packages/react/src/generators/application/files/{common-vite => base-vite}/public/favicon.ico (100%) rename packages/react/src/generators/application/files/{common-vite => base-vite}/src/app/__fileName__.spec.tsx__tmpl__ (100%) rename packages/react/src/generators/application/files/{common-vite => base-vite}/src/app/nx-welcome.tsx (100%) rename packages/react/src/generators/application/files/{common-vite => base-vite}/src/assets/.gitkeep (100%) rename packages/react/src/generators/application/files/{common-vite => base-vite}/src/environments/environment.prod.ts__tmpl__ (100%) rename packages/react/src/generators/application/files/{common-vite => base-vite}/src/environments/environment.ts__tmpl__ (100%) rename packages/react/src/generators/application/files/{common-vite => base-vite}/src/main.tsx__tmpl__ (100%) rename packages/react/src/generators/application/files/{common-vite => base-vite}/tsconfig.app.json__tmpl__ (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/.babelrc__tmpl__ (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/src/app/__fileName__.spec.tsx__tmpl__ (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/src/app/nx-welcome.tsx (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/src/assets/.gitkeep (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/src/environments/environment.prod.ts__tmpl__ (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/src/environments/environment.ts__tmpl__ (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/src/favicon.ico (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/src/index.html (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/src/main.tsx__tmpl__ (100%) rename packages/react/src/generators/application/files/{common => base-webpack}/tsconfig.app.json__tmpl__ (100%) create mode 100644 packages/react/src/generators/application/files/base-webpack/webpack.config.js__tmpl__ rename packages/react/src/generators/application/files/{css-module => style-css-module}/src/app/__fileName__.module.__style__ (100%) rename packages/react/src/generators/application/files/{css-module => style-css-module}/src/app/__fileName__.tsx__tmpl__ (100%) rename packages/react/src/generators/application/files/{css-module => style-css-module}/src/styles.__style__ (100%) rename packages/react/src/generators/application/files/{global-css => style-global-css}/src/app/__fileName__.__style__ (100%) rename packages/react/src/generators/application/files/{global-css => style-global-css}/src/app/__fileName__.tsx__tmpl__ (100%) rename packages/react/src/generators/application/files/{global-css => style-global-css}/src/styles.__style__ (100%) rename packages/react/src/generators/application/files/{none => style-none}/src/app/__fileName__.tsx__tmpl__ (100%) rename packages/react/src/generators/application/files/{styled-jsx => style-styled-jsx}/src/app/__fileName__.tsx__tmpl__ (100%) rename packages/react/src/generators/application/files/{styled-module => style-styled-module}/src/app/__fileName__.tsx__tmpl__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/.babelrc__tmpl__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/app/app.element.__style__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/app/app.element.spec.ts__tmpl__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/app/app.element.ts__tmpl__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/assets/.gitkeep (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/environments/environment.prod.ts__tmpl__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/environments/environment.ts__tmpl__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/favicon.ico (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/index.html (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/main.ts__tmpl__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/src/styles.__style__ (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/tsconfig.app.json (100%) rename packages/web/src/generators/application/files/{app => app-webpack}/tsconfig.json (100%) create mode 100644 packages/web/src/generators/application/files/app-webpack/webpack.config.js__tmpl__ delete mode 100644 packages/web/src/generators/application/files/app/browserslist rename packages/webpack/src/plugins/{generate-package-json-webpack-plugin.ts => generate-package-json-plugin.ts} (86%) diff --git a/e2e/node/src/node-esbuild.test.ts b/e2e/node/src/node-esbuild.test.ts new file mode 100644 index 0000000000000..9c1dc776a8915 --- /dev/null +++ b/e2e/node/src/node-esbuild.test.ts @@ -0,0 +1,35 @@ +import { + checkFilesDoNotExist, + checkFilesExist, + cleanupProject, + newProject, + runCLI, + runCLIAsync, + tmpProjPath, + uniq, + updateFile, +} from '@nrwl/e2e/utils'; +import { execSync } from 'child_process'; + +describe('Node Applications + esbuild', () => { + beforeEach(() => newProject()); + + afterEach(() => cleanupProject()); + + it('should generate an app using esbuild', async () => { + const app = uniq('nodeapp'); + + runCLI(`generate @nrwl/node:app ${app} --bundler=esbuild --no-interactive`); + + checkFilesDoNotExist(`apps/${app}/webpack.config.js`); + + updateFile(`apps/${app}/src/main.ts`, `console.log('Hello World!');`); + await runCLIAsync(`build ${app}`); + + checkFilesExist(`dist/apps/${app}/main.cjs`); + const result = execSync(`node dist/apps/${app}/main.cjs`, { + cwd: tmpProjPath(), + }).toString(); + expect(result).toMatch(/Hello World!/); + }, 300_000); +}); diff --git a/e2e/node/src/node-webpack.test.ts b/e2e/node/src/node-webpack.test.ts new file mode 100644 index 0000000000000..6a4dd84a70b1a --- /dev/null +++ b/e2e/node/src/node-webpack.test.ts @@ -0,0 +1,35 @@ +import { + checkFilesDoNotExist, + checkFilesExist, + cleanupProject, + newProject, + runCLI, + runCLIAsync, + tmpProjPath, + uniq, + updateFile, +} from '@nrwl/e2e/utils'; +import { execSync } from 'child_process'; + +describe('Node Applications + webpack', () => { + beforeEach(() => newProject()); + + afterEach(() => cleanupProject()); + + it('should generate an app using webpack', async () => { + const app = uniq('nodeapp'); + + runCLI(`generate @nrwl/node:app ${app} --bundler=webpack --no-interactive`); + + checkFilesExist(`apps/${app}/webpack.config.js`); + + updateFile(`apps/${app}/src/main.ts`, `console.log('Hello World!');`); + await runCLIAsync(`build ${app}`); + + checkFilesExist(`dist/apps/${app}/main.js`); + const result = execSync(`node dist/apps/${app}/main.js`, { + cwd: tmpProjPath(), + }).toString(); + expect(result).toMatch(/Hello World!/); + }, 300_000); +}); diff --git a/e2e/node/src/node.test.ts b/e2e/node/src/node.test.ts index bb4c759fa9522..68ea8f4cd734c 100644 --- a/e2e/node/src/node.test.ts +++ b/e2e/node/src/node.test.ts @@ -59,8 +59,8 @@ describe('Node Applications', () => { updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`); await runCLIAsync(`build ${nodeapp}`); - checkFilesExist(`dist/apps/${nodeapp}/main.js`); - const result = execSync(`node dist/apps/${nodeapp}/main.js`, { + checkFilesExist(`dist/apps/${nodeapp}/main.cjs`); + const result = execSync(`node dist/apps/${nodeapp}/main.cjs`, { cwd: tmpProjPath(), }).toString(); expect(result).toContain('Hello World!'); @@ -76,13 +76,15 @@ describe('Node Applications', () => { }); await runCLIAsync(`build ${nodeapp}`); - checkFilesExist(`dist/apps/${nodeapp}/index.js`); + checkFilesExist(`dist/apps/${nodeapp}/index.cjs`); }, 300000); it('should be able to generate an empty application with additional entries', async () => { const nodeapp = uniq('nodeapp'); - runCLI(`generate @nrwl/node:app ${nodeapp} --linter=eslint`); + runCLI( + `generate @nrwl/node:app ${nodeapp} --linter=eslint --bundler=webpack` + ); const lintResults = runCLI(`lint ${nodeapp}`); expect(lintResults).toContain('All files pass linting.'); @@ -267,7 +269,7 @@ describe('Build Node apps', () => { expect(satisfies(packageJson.dependencies['tslib'], '^2.3.0')).toBeTruthy(); const nodeapp = uniq('nodeapp'); - runCLI(`generate @nrwl/node:app ${nodeapp}`); + runCLI(`generate @nrwl/node:app ${nodeapp} --bundler=webpack`); const jslib = uniq('jslib'); runCLI(`generate @nrwl/js:lib ${jslib} --buildable`); diff --git a/e2e/react/src/react.module-federation.test.ts b/e2e/react/src/react.module-federation.test.ts index 35a67e643b1d7..896138100462b 100644 --- a/e2e/react/src/react.module-federation.test.ts +++ b/e2e/react/src/react.module-federation.test.ts @@ -48,19 +48,26 @@ describe('React Module Federation', () => { updateFile( `apps/${shell}/webpack.config.js`, stripIndents` - const { withModuleFederation } = require('@nrwl/react/module-federation'); - const moduleFederationConfig = require('./module-federation.config'); - - module.exports = withModuleFederation({ - ...moduleFederationConfig, - remotes: [ - '${remote1}', - ['${remote2}', 'http://localhost:${readPort( + import { ModuleFederationConfig } from '@nrwl/devkit'; + import { composePlugins, withNx } from '@nrwl/webpack'; + import { withReact } from '@nrwl/react'; + import { withModuleFederation } from '@nrwl/react/module-federation'); + + const baseConfig = require('./module-federation.config'); + + const config: ModuleFederationConfig = { + ...baseConfig, + remotes: [ + '${remote1}', + ['${remote2}', 'http://localhost:${readPort( remote2 )}/remoteEntry.js'], - ['${remote3}', 'http://localhost:${readPort(remote3)}'], - ], - }); + ['${remote3}', 'http://localhost:${readPort(remote3)}'], + ], + }; + + // Nx plugins for webpack to build config object from Nx options and context. + module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config)); ` ); diff --git a/packages/express/src/generators/application/application.ts b/packages/express/src/generators/application/application.ts index b6fade32b9f92..57dec0160b290 100644 --- a/packages/express/src/generators/application/application.ts +++ b/packages/express/src/generators/application/application.ts @@ -66,6 +66,7 @@ export async function applicationGenerator(tree: Tree, schema: Schema) { const initTask = await initGenerator(tree, { ...options, skipFormat: true }); const applicationTask = await nodeApplicationGenerator(tree, { ...schema, + bundler: 'webpack', skipFormat: true, }); addMainFile(tree, options); diff --git a/packages/nest/src/generators/application/lib/normalize-options.ts b/packages/nest/src/generators/application/lib/normalize-options.ts index aa48f80cf83cb..6175739e66ee3 100644 --- a/packages/nest/src/generators/application/lib/normalize-options.ts +++ b/packages/nest/src/generators/application/lib/normalize-options.ts @@ -43,5 +43,6 @@ export function toNodeApplicationGeneratorOptions( tags: options.tags, unitTestRunner: options.unitTestRunner, setParserOptionsProject: options.setParserOptionsProject, + bundler: 'webpack', // Some features require webpack plugins such as TS transformers }; } diff --git a/packages/node/src/generators/application/application.spec.ts b/packages/node/src/generators/application/application.spec.ts index 0d8199c85c499..9da9d5779fd56 100644 --- a/packages/node/src/generators/application/application.spec.ts +++ b/packages/node/src/generators/application/application.spec.ts @@ -1,6 +1,11 @@ import * as devkit from '@nrwl/devkit'; -import { getProjects, NxJsonConfiguration, readJson, Tree } from '@nrwl/devkit'; -import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing'; +import { + getProjects, + readJson, + readProjectConfiguration, + Tree, +} from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; // nx-ignore-next-line import { applicationGenerator as angularApplicationGenerator } from '@nrwl/angular/generators'; @@ -13,7 +18,7 @@ describe('app', () => { let tree: Tree; beforeEach(() => { - tree = createTreeWithEmptyV1Workspace(); + tree = createTreeWithEmptyWorkspace(); overrideCollectionResolutionForTesting({ '@nrwl/cypress': join(__dirname, '../../../../cypress/generators.json'), @@ -32,27 +37,26 @@ describe('app', () => { }); describe('not nested', () => { - it('should update workspace.json', async () => { + it('should update project config', async () => { await applicationGenerator(tree, { name: 'myNodeApp', standaloneConfig: false, }); - const workspaceJson = readJson(tree, '/workspace.json'); - const nxJson = readJson(tree, 'nx.json'); - const project = workspaceJson.projects['my-node-app']; - expect(project.root).toEqual('apps/my-node-app'); - expect(project.architect).toEqual( + const project = readProjectConfiguration(tree, 'my-node-app'); + expect(project.root).toEqual('my-node-app'); + expect(project.targets).toEqual( expect.objectContaining({ build: { - builder: '@nrwl/webpack:webpack', + executor: '@nrwl/webpack:webpack', outputs: ['{options.outputPath}'], options: { target: 'node', compiler: 'tsc', - outputPath: 'dist/apps/my-node-app', - main: 'apps/my-node-app/src/main.ts', - tsConfig: 'apps/my-node-app/tsconfig.app.json', - assets: ['apps/my-node-app/src/assets'], + outputPath: 'dist/my-node-app', + main: 'my-node-app/src/main.ts', + tsConfig: 'my-node-app/tsconfig.app.json', + webpackConfig: 'my-node-app/webpack.config.js', + assets: ['my-node-app/src/assets'], }, configurations: { production: { @@ -63,7 +67,7 @@ describe('app', () => { }, }, serve: { - builder: '@nrwl/js:node', + executor: '@nrwl/js:node', options: { buildTarget: 'my-node-app:build', }, @@ -75,14 +79,16 @@ describe('app', () => { }, }) ); - expect(workspaceJson.projects['my-node-app'].architect.lint).toEqual({ - builder: '@nrwl/linter:eslint', + expect(project.targets.lint).toEqual({ + executor: '@nrwl/linter:eslint', outputs: ['{options.outputFile}'], options: { - lintFilePatterns: ['apps/my-node-app/**/*.ts'], + lintFilePatterns: ['my-node-app/**/*.ts'], }, }); - expect(workspaceJson.projects['my-node-app-e2e']).toBeUndefined(); + expect(() => readProjectConfiguration(tree, 'my-node-app-e2e')).toThrow( + /Cannot find/ + ); }); it('should update tags', async () => { @@ -104,13 +110,13 @@ describe('app', () => { name: 'myNodeApp', standaloneConfig: false, }); - expect(tree.exists(`apps/my-node-app/jest.config.ts`)).toBeTruthy(); - expect(tree.exists('apps/my-node-app/src/main.ts')).toBeTruthy(); + expect(tree.exists(`my-node-app/jest.config.ts`)).toBeTruthy(); + expect(tree.exists('my-node-app/src/main.ts')).toBeTruthy(); - const tsconfig = readJson(tree, 'apps/my-node-app/tsconfig.json'); + const tsconfig = readJson(tree, 'my-node-app/tsconfig.json'); expect(tsconfig).toMatchInlineSnapshot(` Object { - "extends": "../../tsconfig.base.json", + "extends": "../tsconfig.base.json", "files": Array [], "include": Array [], "references": Array [ @@ -124,19 +130,19 @@ describe('app', () => { } `); - const tsconfigApp = readJson(tree, 'apps/my-node-app/tsconfig.app.json'); - expect(tsconfigApp.compilerOptions.outDir).toEqual('../../dist/out-tsc'); + const tsconfigApp = readJson(tree, 'my-node-app/tsconfig.app.json'); + expect(tsconfigApp.compilerOptions.outDir).toEqual('../dist/out-tsc'); expect(tsconfigApp.extends).toEqual('./tsconfig.json'); expect(tsconfigApp.exclude).toEqual([ 'jest.config.ts', 'src/**/*.spec.ts', 'src/**/*.test.ts', ]); - const eslintrc = readJson(tree, 'apps/my-node-app/.eslintrc.json'); + const eslintrc = readJson(tree, 'my-node-app/.eslintrc.json'); expect(eslintrc).toMatchInlineSnapshot(` Object { "extends": Array [ - "../../.eslintrc.json", + "../.eslintrc.json", ], "ignorePatterns": Array [ "!**/*", @@ -178,36 +184,33 @@ describe('app', () => { standaloneConfig: false, }); - const tsconfig = readJson(tree, 'apps/my-node-app/tsconfig.json'); - expect(tsconfig.extends).toBe('../../tsconfig.json'); + const tsconfig = readJson(tree, 'my-node-app/tsconfig.json'); + expect(tsconfig.extends).toBe('../tsconfig.json'); }); }); describe('nested', () => { - it('should update workspace.json', async () => { + it('should update project config', async () => { await applicationGenerator(tree, { name: 'myNodeApp', directory: 'myDir', standaloneConfig: false, }); - const workspaceJson = readJson(tree, '/workspace.json'); - const nxJson = readJson(tree, 'nx.json'); + const project = readProjectConfiguration(tree, 'my-dir-my-node-app'); - expect(workspaceJson.projects['my-dir-my-node-app'].root).toEqual( - 'apps/my-dir/my-node-app' - ); + expect(project.root).toEqual('my-dir/my-node-app'); - expect( - workspaceJson.projects['my-dir-my-node-app'].architect.lint - ).toEqual({ - builder: '@nrwl/linter:eslint', + expect(project.targets.lint).toEqual({ + executor: '@nrwl/linter:eslint', outputs: ['{options.outputFile}'], options: { - lintFilePatterns: ['apps/my-dir/my-node-app/**/*.ts'], + lintFilePatterns: ['my-dir/my-node-app/**/*.ts'], }, }); - expect(workspaceJson.projects['my-dir-my-node-app-e2e']).toBeUndefined(); + expect(() => + readProjectConfiguration(tree, 'my-dir-my-node-app-e2e') + ).toThrow(/Cannot find/); }); it('should update tags', async () => { @@ -239,8 +242,8 @@ describe('app', () => { // Make sure these exist [ - `apps/my-dir/my-node-app/jest.config.ts`, - 'apps/my-dir/my-node-app/src/main.ts', + `my-dir/my-node-app/jest.config.ts`, + 'my-dir/my-node-app/src/main.ts', ].forEach((path) => { expect(tree.exists(path)).toBeTruthy(); }); @@ -248,17 +251,17 @@ describe('app', () => { // Make sure these have properties [ { - path: 'apps/my-dir/my-node-app/tsconfig.app.json', + path: 'my-dir/my-node-app/tsconfig.app.json', lookupFn: (json) => json.compilerOptions.outDir, - expectedValue: '../../../dist/out-tsc', + expectedValue: '../../dist/out-tsc', }, { - path: 'apps/my-dir/my-node-app/tsconfig.app.json', + path: 'my-dir/my-node-app/tsconfig.app.json', lookupFn: (json) => json.compilerOptions.types, expectedValue: ['node'], }, { - path: 'apps/my-dir/my-node-app/tsconfig.app.json', + path: 'my-dir/my-node-app/tsconfig.app.json', lookupFn: (json) => json.exclude, expectedValue: [ 'jest.config.ts', @@ -267,9 +270,9 @@ describe('app', () => { ], }, { - path: 'apps/my-dir/my-node-app/.eslintrc.json', + path: 'my-dir/my-node-app/.eslintrc.json', lookupFn: (json) => json.extends, - expectedValue: ['../../../.eslintrc.json'], + expectedValue: ['../../.eslintrc.json'], }, ].forEach(hasJsonValue); }); @@ -283,21 +286,18 @@ describe('app', () => { standaloneConfig: false, }); expect(tree.exists('jest.config.ts')).toBeFalsy(); - expect(tree.exists('apps/my-node-app/src/test-setup.ts')).toBeFalsy(); - expect(tree.exists('apps/my-node-app/src/test.ts')).toBeFalsy(); - expect(tree.exists('apps/my-node-app/tsconfig.spec.json')).toBeFalsy(); - expect(tree.exists('apps/my-node-app/jest.config.ts')).toBeFalsy(); - const workspaceJson = readJson(tree, 'workspace.json'); - expect( - workspaceJson.projects['my-node-app'].architect.test - ).toBeUndefined(); - expect(workspaceJson.projects['my-node-app'].architect.lint) - .toMatchInlineSnapshot(` + expect(tree.exists('my-node-app/src/test-setup.ts')).toBeFalsy(); + expect(tree.exists('my-node-app/src/test.ts')).toBeFalsy(); + expect(tree.exists('my-node-app/tsconfig.spec.json')).toBeFalsy(); + expect(tree.exists('my-node-app/jest.config.ts')).toBeFalsy(); + const project = readProjectConfiguration(tree, 'my-node-app'); + expect(project.targets.test).toBeUndefined(); + expect(project.targets.lint).toMatchInlineSnapshot(` Object { - "builder": "@nrwl/linter:eslint", + "executor": "@nrwl/linter:eslint", "options": Object { "lintFilePatterns": Array [ - "apps/my-node-app/**/*.ts", + "my-node-app/**/*.ts", ], }, "outputs": Array [ @@ -318,12 +318,10 @@ describe('app', () => { standaloneConfig: false, }); - expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy(); - const serve = readJson(tree, 'workspace.json').projects['my-frontend'] - .architect.serve; - expect(serve.options.proxyConfig).toEqual( - 'apps/my-frontend/proxy.conf.json' - ); + expect(tree.exists('my-frontend/proxy.conf.json')).toBeTruthy(); + const project = readProjectConfiguration(tree, 'my-frontend'); + const serve = project.targets.serve; + expect(serve.options.proxyConfig).toEqual('my-frontend/proxy.conf.json'); }); it('should configure proxies for multiple node projects with the same frontend app', async () => { @@ -341,9 +339,9 @@ describe('app', () => { standaloneConfig: false, }); - expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy(); + expect(tree.exists('my-frontend/proxy.conf.json')).toBeTruthy(); - expect(readJson(tree, 'apps/my-frontend/proxy.conf.json')).toEqual({ + expect(readJson(tree, 'my-frontend/proxy.conf.json')).toEqual({ '/api': { target: 'http://localhost:3333', secure: false }, '/billing-api': { target: 'http://localhost:3333', secure: false }, }); @@ -358,12 +356,10 @@ describe('app', () => { standaloneConfig: false, }); - expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy(); - const serve = readJson(tree, 'workspace.json').projects['my-frontend'] - .architect.serve; - expect(serve.options.proxyConfig).toEqual( - 'apps/my-frontend/proxy.conf.json' - ); + expect(tree.exists('my-frontend/proxy.conf.json')).toBeTruthy(); + const project = readProjectConfiguration(tree, 'my-frontend'); + const serve = project.targets.serve; + expect(serve.options.proxyConfig).toEqual('my-frontend/proxy.conf.json'); }); }); @@ -375,18 +371,18 @@ describe('app', () => { babelJest: true, } as Schema); - expect(tree.read(`apps/my-node-app/jest.config.ts`, 'utf-8')) + expect(tree.read(`my-node-app/jest.config.ts`, 'utf-8')) .toMatchInlineSnapshot(` "/* eslint-disable */ export default { displayName: 'my-node-app', - preset: '../../jest.preset.js', + preset: '../jest.preset.js', testEnvironment: 'node', transform: { '^.+\\\\\\\\.[tj]s$': 'babel-jest' }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/apps/my-node-app' + coverageDirectory: '../coverage/my-node-app' }; " `); @@ -399,15 +395,15 @@ describe('app', () => { js: true, } as Schema); - expect(tree.exists(`apps/my-node-app/jest.config.js`)).toBeTruthy(); - expect(tree.exists('apps/my-node-app/src/main.js')).toBeTruthy(); + expect(tree.exists(`my-node-app/jest.config.js`)).toBeTruthy(); + expect(tree.exists('my-node-app/src/main.js')).toBeTruthy(); - const tsConfig = readJson(tree, 'apps/my-node-app/tsconfig.json'); + const tsConfig = readJson(tree, 'my-node-app/tsconfig.json'); expect(tsConfig.compilerOptions).toEqual({ allowJs: true, }); - const tsConfigApp = readJson(tree, 'apps/my-node-app/tsconfig.app.json'); + const tsConfigApp = readJson(tree, 'my-node-app/tsconfig.app.json'); expect(tsConfigApp.include).toEqual(['src/**/*.ts', 'src/**/*.js']); expect(tsConfigApp.exclude).toEqual([ 'jest.config.ts', @@ -418,16 +414,15 @@ describe('app', () => { ]); }); - it('should update workspace.json', async () => { + it('should add project config', async () => { await applicationGenerator(tree, { name: 'myNodeApp', js: true, } as Schema); - const workspaceJson = readJson(tree, '/workspace.json'); - const project = workspaceJson.projects['my-node-app']; - const buildTarget = project.architect.build; + const project = readProjectConfiguration(tree, 'my-node-app'); + const buildTarget = project.targets.build; - expect(buildTarget.options.main).toEqual('apps/my-node-app/src/main.js'); + expect(buildTarget.options.main).toEqual('my-node-app/src/main.js'); }); it('should generate js files for nested libs as well', async () => { @@ -436,10 +431,8 @@ describe('app', () => { directory: 'myDir', js: true, } as Schema); - expect( - tree.exists(`apps/my-dir/my-node-app/jest.config.js`) - ).toBeTruthy(); - expect(tree.exists('apps/my-dir/my-node-app/src/main.js')).toBeTruthy(); + expect(tree.exists(`my-dir/my-node-app/jest.config.js`)).toBeTruthy(); + expect(tree.exists('my-dir/my-node-app/src/main.js')).toBeTruthy(); }); }); diff --git a/packages/node/src/generators/application/application.ts b/packages/node/src/generators/application/application.ts index 34ddf6985a667..5c9722f3fb7bd 100644 --- a/packages/node/src/generators/application/application.ts +++ b/packages/node/src/generators/application/application.ts @@ -65,6 +65,10 @@ function getWebpackBuildConfig( ), tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), assets: [joinPathFragments(project.sourceRoot, 'assets')], + webpackConfig: joinPathFragments( + options.appProjectRoot, + 'webpack.config.js' + ), }, configurations: { production: { @@ -151,6 +155,10 @@ function addAppFiles(tree: Tree, options: NormalizedSchema) { } ); + if (options.bundler !== 'webpack') { + tree.delete(joinPathFragments(options.appProjectRoot, 'webpack.config.js')); + } + if (options.framework) { generateFiles( tree, @@ -358,11 +366,6 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-'); const appProjectRoot = joinPathFragments(appsDir, appDirectory); - if (options.framework) { - options.bundler = options.bundler ?? 'esbuild'; - } else { - options.bundler = 'webpack'; - } const parsedTags = options.tags ? options.tags.split(',').map((s) => s.trim()) diff --git a/packages/node/src/generators/application/files/common/webpack.config.js__tmpl__ b/packages/node/src/generators/application/files/common/webpack.config.js__tmpl__ new file mode 100644 index 0000000000000..9d8cd094f7e87 --- /dev/null +++ b/packages/node/src/generators/application/files/common/webpack.config.js__tmpl__ @@ -0,0 +1,8 @@ +const { composePlugins, withNx} = require('@nrwl/webpack'); + +// Nx plugins for webpack. +module.exports = composePlugins(withNx(), (config) => { + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + return config; +}); diff --git a/packages/react/index.ts b/packages/react/index.ts index f3748f09a6308..50c4f6fcf0fc3 100644 --- a/packages/react/index.ts +++ b/packages/react/index.ts @@ -22,3 +22,4 @@ export { cypressComponentConfigGenerator } from './src/generators/cypress-compon export { componentTestGenerator } from './src/generators/component-test/component-test'; export { setupTailwindGenerator } from './src/generators/setup-tailwind/setup-tailwind'; export type { SupportedStyles } from './typings/style'; +export * from './plugins/with-react'; diff --git a/packages/react/plugins/storybook/index.ts b/packages/react/plugins/storybook/index.ts index 97fe4d59cd6e8..0c8ad363970e7 100644 --- a/packages/react/plugins/storybook/index.ts +++ b/packages/react/plugins/storybook/index.ts @@ -1,4 +1,5 @@ import { + ExecutorContext, joinPathFragments, logger, readJsonFile, @@ -15,7 +16,7 @@ import { gte } from 'semver'; import { Configuration, DefinePlugin, WebpackPluginInstance } from 'webpack'; import * as mergeWebpack from 'webpack-merge'; import { mergePlugins } from './merge-plugins'; -import { withReact } from '../webpack'; +import { withReact } from '../with-react'; import { withNx, withWeb } from '@nrwl/webpack'; // This is shamelessly taken from CRA and modified for NX use @@ -128,7 +129,10 @@ export const webpack = async ( withWeb(), withReact() ); - const finalConfig = configure(baseWebpackConfig, { options: builderOptions }); + const finalConfig = configure(baseWebpackConfig, { + options: builderOptions, + context: {} as ExecutorContext, // The context is not used here. + }); // Check whether the project .babelrc uses @emotion/babel-plugin. There's currently // a Storybook issue (https://github.com/storybookjs/storybook/issues/13277) which apparently diff --git a/packages/react/plugins/webpack.ts b/packages/react/plugins/webpack.ts index 81a63ce024b89..9b894728334d5 100644 --- a/packages/react/plugins/webpack.ts +++ b/packages/react/plugins/webpack.ts @@ -1,69 +1,4 @@ -import type { Configuration } from 'webpack'; -import ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); -import { NormalizedWebpackExecutorOptions } from '@nrwl/webpack'; -import { ExecutorContext } from 'nx/src/config/misc-interfaces'; - -// Add React-specific configuration -export function withReact() { - return function configure( - config: Configuration, - _ctx?: { - options: NormalizedWebpackExecutorOptions; - context: ExecutorContext; - } - ): Configuration { - config.module.rules.push({ - test: /\.svg$/, - issuer: /\.(js|ts|md)x?$/, - use: [ - { - loader: require.resolve('@svgr/webpack'), - options: { - svgo: false, - titleProp: true, - ref: true, - }, - }, - { - loader: require.resolve('file-loader'), - options: { - name: '[name].[hash].[ext]', - }, - }, - ], - }); - - if (config.mode === 'development' && config['devServer']?.hot) { - // add `react-refresh/babel` to babel loader plugin - const babelLoader = config.module.rules.find( - (rule) => - typeof rule !== 'string' && - rule.loader?.toString().includes('babel-loader') - ); - if (babelLoader && typeof babelLoader !== 'string') { - babelLoader.options['plugins'] = [ - ...(babelLoader.options['plugins'] || []), - [ - require.resolve('react-refresh/babel'), - { - skipEnvCheck: true, - }, - ], - ]; - } - // add https://github.com/pmmmwh/react-refresh-webpack-plugin to webpack plugin - config.plugins.push(new ReactRefreshPlugin()); - } - - // enable webpack node api - config.node = { - __dirname: true, - __filename: true, - }; - - return config; - }; -} +import { withReact } from './with-react'; // Support existing default exports as well as new named export. const legacyExport: any = withReact(); diff --git a/packages/react/plugins/with-react.ts b/packages/react/plugins/with-react.ts new file mode 100644 index 0000000000000..ae562fb33aa87 --- /dev/null +++ b/packages/react/plugins/with-react.ts @@ -0,0 +1,69 @@ +import type { Configuration } from 'webpack'; +import { NormalizedWebpackExecutorOptions } from '@nrwl/webpack'; +import { ExecutorContext } from 'nx/src/config/misc-interfaces'; + +const processed = new Set(); + +export function withReact() { + return function configure( + config: Configuration, + _ctx?: { + options: NormalizedWebpackExecutorOptions; + context: ExecutorContext; + } + ): Configuration { + if (processed.has(config)) return config; + + config.module.rules.push({ + test: /\.svg$/, + issuer: /\.(js|ts|md)x?$/, + use: [ + { + loader: require.resolve('@svgr/webpack'), + options: { + svgo: false, + titleProp: true, + ref: true, + }, + }, + { + loader: require.resolve('file-loader'), + options: { + name: '[name].[hash].[ext]', + }, + }, + ], + }); + + if (config.mode === 'development' && config['devServer']?.hot) { + // add `react-refresh/babel` to babel loader plugin + const babelLoader = config.module.rules.find( + (rule) => + typeof rule !== 'string' && + rule.loader?.toString().includes('babel-loader') + ); + if (babelLoader && typeof babelLoader !== 'string') { + babelLoader.options['plugins'] = [ + ...(babelLoader.options['plugins'] || []), + [ + require.resolve('react-refresh/babel'), + { + skipEnvCheck: true, + }, + ], + ]; + } + const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); + config.plugins.push(new ReactRefreshPlugin()); + } + + // enable webpack node api + config.node = { + __dirname: true, + __filename: true, + }; + + processed.add(config); + return config; + }; +} diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 2c2cd18375d00..c928477e5796a 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -314,7 +314,7 @@ describe('app', () => { scripts: [], styles: ['apps/my-app/src/styles.css'], tsConfig: 'apps/my-app/tsconfig.app.json', - webpackConfig: '@nrwl/react/plugins/webpack', + webpackConfig: 'apps/my-app/webpack.config.js', }); expect(targetConfig.build.configurations.production).toEqual({ optimization: true, @@ -792,7 +792,7 @@ describe('app', () => { expect( workspaceJson.get('my-app').targets.build.options.webpackConfig - ).toEqual('@nrwl/react/plugins/webpack'); + ).toEqual('apps/my-app/webpack.config.js'); }); it('should NOT add custom webpack config if bundler is vite', async () => { diff --git a/packages/react/src/generators/application/files/common-vite/index.html__tmpl__ b/packages/react/src/generators/application/files/base-vite/index.html__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common-vite/index.html__tmpl__ rename to packages/react/src/generators/application/files/base-vite/index.html__tmpl__ diff --git a/packages/react/src/generators/application/files/common-vite/public/favicon.ico b/packages/react/src/generators/application/files/base-vite/public/favicon.ico similarity index 100% rename from packages/react/src/generators/application/files/common-vite/public/favicon.ico rename to packages/react/src/generators/application/files/base-vite/public/favicon.ico diff --git a/packages/react/src/generators/application/files/common-vite/src/app/__fileName__.spec.tsx__tmpl__ b/packages/react/src/generators/application/files/base-vite/src/app/__fileName__.spec.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common-vite/src/app/__fileName__.spec.tsx__tmpl__ rename to packages/react/src/generators/application/files/base-vite/src/app/__fileName__.spec.tsx__tmpl__ diff --git a/packages/react/src/generators/application/files/common-vite/src/app/nx-welcome.tsx b/packages/react/src/generators/application/files/base-vite/src/app/nx-welcome.tsx similarity index 100% rename from packages/react/src/generators/application/files/common-vite/src/app/nx-welcome.tsx rename to packages/react/src/generators/application/files/base-vite/src/app/nx-welcome.tsx diff --git a/packages/react/src/generators/application/files/common-vite/src/assets/.gitkeep b/packages/react/src/generators/application/files/base-vite/src/assets/.gitkeep similarity index 100% rename from packages/react/src/generators/application/files/common-vite/src/assets/.gitkeep rename to packages/react/src/generators/application/files/base-vite/src/assets/.gitkeep diff --git a/packages/react/src/generators/application/files/common-vite/src/environments/environment.prod.ts__tmpl__ b/packages/react/src/generators/application/files/base-vite/src/environments/environment.prod.ts__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common-vite/src/environments/environment.prod.ts__tmpl__ rename to packages/react/src/generators/application/files/base-vite/src/environments/environment.prod.ts__tmpl__ diff --git a/packages/react/src/generators/application/files/common-vite/src/environments/environment.ts__tmpl__ b/packages/react/src/generators/application/files/base-vite/src/environments/environment.ts__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common-vite/src/environments/environment.ts__tmpl__ rename to packages/react/src/generators/application/files/base-vite/src/environments/environment.ts__tmpl__ diff --git a/packages/react/src/generators/application/files/common-vite/src/main.tsx__tmpl__ b/packages/react/src/generators/application/files/base-vite/src/main.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common-vite/src/main.tsx__tmpl__ rename to packages/react/src/generators/application/files/base-vite/src/main.tsx__tmpl__ diff --git a/packages/react/src/generators/application/files/common-vite/tsconfig.app.json__tmpl__ b/packages/react/src/generators/application/files/base-vite/tsconfig.app.json__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common-vite/tsconfig.app.json__tmpl__ rename to packages/react/src/generators/application/files/base-vite/tsconfig.app.json__tmpl__ diff --git a/packages/react/src/generators/application/files/common/.babelrc__tmpl__ b/packages/react/src/generators/application/files/base-webpack/.babelrc__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common/.babelrc__tmpl__ rename to packages/react/src/generators/application/files/base-webpack/.babelrc__tmpl__ diff --git a/packages/react/src/generators/application/files/common/src/app/__fileName__.spec.tsx__tmpl__ b/packages/react/src/generators/application/files/base-webpack/src/app/__fileName__.spec.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common/src/app/__fileName__.spec.tsx__tmpl__ rename to packages/react/src/generators/application/files/base-webpack/src/app/__fileName__.spec.tsx__tmpl__ diff --git a/packages/react/src/generators/application/files/common/src/app/nx-welcome.tsx b/packages/react/src/generators/application/files/base-webpack/src/app/nx-welcome.tsx similarity index 100% rename from packages/react/src/generators/application/files/common/src/app/nx-welcome.tsx rename to packages/react/src/generators/application/files/base-webpack/src/app/nx-welcome.tsx diff --git a/packages/react/src/generators/application/files/common/src/assets/.gitkeep b/packages/react/src/generators/application/files/base-webpack/src/assets/.gitkeep similarity index 100% rename from packages/react/src/generators/application/files/common/src/assets/.gitkeep rename to packages/react/src/generators/application/files/base-webpack/src/assets/.gitkeep diff --git a/packages/react/src/generators/application/files/common/src/environments/environment.prod.ts__tmpl__ b/packages/react/src/generators/application/files/base-webpack/src/environments/environment.prod.ts__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common/src/environments/environment.prod.ts__tmpl__ rename to packages/react/src/generators/application/files/base-webpack/src/environments/environment.prod.ts__tmpl__ diff --git a/packages/react/src/generators/application/files/common/src/environments/environment.ts__tmpl__ b/packages/react/src/generators/application/files/base-webpack/src/environments/environment.ts__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common/src/environments/environment.ts__tmpl__ rename to packages/react/src/generators/application/files/base-webpack/src/environments/environment.ts__tmpl__ diff --git a/packages/react/src/generators/application/files/common/src/favicon.ico b/packages/react/src/generators/application/files/base-webpack/src/favicon.ico similarity index 100% rename from packages/react/src/generators/application/files/common/src/favicon.ico rename to packages/react/src/generators/application/files/base-webpack/src/favicon.ico diff --git a/packages/react/src/generators/application/files/common/src/index.html b/packages/react/src/generators/application/files/base-webpack/src/index.html similarity index 100% rename from packages/react/src/generators/application/files/common/src/index.html rename to packages/react/src/generators/application/files/base-webpack/src/index.html diff --git a/packages/react/src/generators/application/files/common/src/main.tsx__tmpl__ b/packages/react/src/generators/application/files/base-webpack/src/main.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common/src/main.tsx__tmpl__ rename to packages/react/src/generators/application/files/base-webpack/src/main.tsx__tmpl__ diff --git a/packages/react/src/generators/application/files/common/tsconfig.app.json__tmpl__ b/packages/react/src/generators/application/files/base-webpack/tsconfig.app.json__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/common/tsconfig.app.json__tmpl__ rename to packages/react/src/generators/application/files/base-webpack/tsconfig.app.json__tmpl__ diff --git a/packages/react/src/generators/application/files/base-webpack/webpack.config.js__tmpl__ b/packages/react/src/generators/application/files/base-webpack/webpack.config.js__tmpl__ new file mode 100644 index 0000000000000..8d695556b2320 --- /dev/null +++ b/packages/react/src/generators/application/files/base-webpack/webpack.config.js__tmpl__ @@ -0,0 +1,9 @@ +const { composePlugins, withNx } = require('@nrwl/webpack'); +const { withReact } = require('@nrwl/react'); + +// Nx plugins for webpack. +module.exports = composePlugins(withNx(), withReact(), (config) => { + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + return config; +}); diff --git a/packages/react/src/generators/application/files/css-module/src/app/__fileName__.module.__style__ b/packages/react/src/generators/application/files/style-css-module/src/app/__fileName__.module.__style__ similarity index 100% rename from packages/react/src/generators/application/files/css-module/src/app/__fileName__.module.__style__ rename to packages/react/src/generators/application/files/style-css-module/src/app/__fileName__.module.__style__ diff --git a/packages/react/src/generators/application/files/css-module/src/app/__fileName__.tsx__tmpl__ b/packages/react/src/generators/application/files/style-css-module/src/app/__fileName__.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/css-module/src/app/__fileName__.tsx__tmpl__ rename to packages/react/src/generators/application/files/style-css-module/src/app/__fileName__.tsx__tmpl__ diff --git a/packages/react/src/generators/application/files/css-module/src/styles.__style__ b/packages/react/src/generators/application/files/style-css-module/src/styles.__style__ similarity index 100% rename from packages/react/src/generators/application/files/css-module/src/styles.__style__ rename to packages/react/src/generators/application/files/style-css-module/src/styles.__style__ diff --git a/packages/react/src/generators/application/files/global-css/src/app/__fileName__.__style__ b/packages/react/src/generators/application/files/style-global-css/src/app/__fileName__.__style__ similarity index 100% rename from packages/react/src/generators/application/files/global-css/src/app/__fileName__.__style__ rename to packages/react/src/generators/application/files/style-global-css/src/app/__fileName__.__style__ diff --git a/packages/react/src/generators/application/files/global-css/src/app/__fileName__.tsx__tmpl__ b/packages/react/src/generators/application/files/style-global-css/src/app/__fileName__.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/global-css/src/app/__fileName__.tsx__tmpl__ rename to packages/react/src/generators/application/files/style-global-css/src/app/__fileName__.tsx__tmpl__ diff --git a/packages/react/src/generators/application/files/global-css/src/styles.__style__ b/packages/react/src/generators/application/files/style-global-css/src/styles.__style__ similarity index 100% rename from packages/react/src/generators/application/files/global-css/src/styles.__style__ rename to packages/react/src/generators/application/files/style-global-css/src/styles.__style__ diff --git a/packages/react/src/generators/application/files/none/src/app/__fileName__.tsx__tmpl__ b/packages/react/src/generators/application/files/style-none/src/app/__fileName__.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/none/src/app/__fileName__.tsx__tmpl__ rename to packages/react/src/generators/application/files/style-none/src/app/__fileName__.tsx__tmpl__ diff --git a/packages/react/src/generators/application/files/styled-jsx/src/app/__fileName__.tsx__tmpl__ b/packages/react/src/generators/application/files/style-styled-jsx/src/app/__fileName__.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/styled-jsx/src/app/__fileName__.tsx__tmpl__ rename to packages/react/src/generators/application/files/style-styled-jsx/src/app/__fileName__.tsx__tmpl__ diff --git a/packages/react/src/generators/application/files/styled-module/src/app/__fileName__.tsx__tmpl__ b/packages/react/src/generators/application/files/style-styled-module/src/app/__fileName__.tsx__tmpl__ similarity index 100% rename from packages/react/src/generators/application/files/styled-module/src/app/__fileName__.tsx__tmpl__ rename to packages/react/src/generators/application/files/style-styled-module/src/app/__fileName__.tsx__tmpl__ diff --git a/packages/react/src/generators/application/lib/add-project.ts b/packages/react/src/generators/application/lib/add-project.ts index 384e0a84e34ca..646e0fc12e40a 100644 --- a/packages/react/src/generators/application/lib/add-project.ts +++ b/packages/react/src/generators/application/lib/add-project.ts @@ -72,7 +72,10 @@ function createBuildTarget(options: NormalizedSchema): TargetConfiguration { ), ], scripts: [], - webpackConfig: '@nrwl/react/plugins/webpack', + webpackConfig: joinPathFragments( + options.appProjectRoot, + 'webpack.config.js' + ), }, configurations: { development: { diff --git a/packages/react/src/generators/application/lib/create-application-files.ts b/packages/react/src/generators/application/lib/create-application-files.ts index 20f73aeaedbd9..db25f808c65d1 100644 --- a/packages/react/src/generators/application/lib/create-application-files.ts +++ b/packages/react/src/generators/application/lib/create-application-files.ts @@ -9,15 +9,15 @@ import { getAppTests } from './get-app-tests'; export function createApplicationFiles(host: Tree, options: NormalizedSchema) { let styleSolutionSpecificAppFiles: string; if (options.styledModule && options.style !== 'styled-jsx') { - styleSolutionSpecificAppFiles = '../files/styled-module'; + styleSolutionSpecificAppFiles = '../files/style-styled-module'; } else if (options.style === 'styled-jsx') { - styleSolutionSpecificAppFiles = '../files/styled-jsx'; + styleSolutionSpecificAppFiles = '../files/style-styled-jsx'; } else if (options.style === 'none') { - styleSolutionSpecificAppFiles = '../files/none'; + styleSolutionSpecificAppFiles = '../files/style-none'; } else if (options.globalCss) { - styleSolutionSpecificAppFiles = '../files/global-css'; + styleSolutionSpecificAppFiles = '../files/style-global-css'; } else { - styleSolutionSpecificAppFiles = '../files/css-module'; + styleSolutionSpecificAppFiles = '../files/style-css-module'; } const relativePathToRootTsConfig = getRelativePathToRootTsConfig( @@ -38,7 +38,9 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) { host, join( __dirname, - options.bundler === 'vite' ? '../files/common-vite' : '../files/common' + options.bundler === 'vite' + ? '../files/base-vite' + : '../files/base-webpack' ), options.appProjectRoot, templateVariables diff --git a/packages/react/src/generators/host/files/module-federation-ssr/webpack.server.config.js__tmpl__ b/packages/react/src/generators/host/files/module-federation-ssr/webpack.server.config.js__tmpl__ index d512dfd6508ab..708029099d19e 100644 --- a/packages/react/src/generators/host/files/module-federation-ssr/webpack.server.config.js__tmpl__ +++ b/packages/react/src/generators/host/files/module-federation-ssr/webpack.server.config.js__tmpl__ @@ -1,8 +1,12 @@ -const { withModuleFederationForSSR } = require('@nrwl/react/module-federation'); -const baseConfig = require("./module-federation.server.config"); +import { composePlugins, withNx } from '@nrwl/webpack'; +import { withReact } from '@nrwl/react'; +import { withModuleFederationForSSR } from '@nrwl/react/module-federation'; + +const baseConfig = require('./module-federation.config'); const defaultConfig = { ...baseConfig }; -module.exports = withModuleFederationForSSR(defaultConfig); +// Nx plugins for webpack to build config object from Nx options and context. +module.exports = composePlugins(withNx(), withReact(), withModuleFederationForSSR(defaultConfig)); diff --git a/packages/react/src/generators/host/files/module-federation/module-federation.config.js__tmpl__ b/packages/react/src/generators/host/files/module-federation/module-federation.config.js__tmpl__ index 74c0e6454f789..08a4297504354 100644 --- a/packages/react/src/generators/host/files/module-federation/module-federation.config.js__tmpl__ +++ b/packages/react/src/generators/host/files/module-federation/module-federation.config.js__tmpl__ @@ -1,13 +1,6 @@ -// @ts-check - -/** - * @type {import('@nrwl/devkit').ModuleFederationConfig} - **/ -const moduleFederationConfig = { - name: '<%= projectName %>', - remotes: [ - <% remotes.forEach(function(r) {%> "<%= r.fileName %>", <% }); %> - ], +module.exports = { + name: '<%= projectName %>', + remotes: [ + <% remotes.forEach(function(r) {%> "<%= r.fileName %>", <% }); %> + ], }; - -module.exports = moduleFederationConfig; \ No newline at end of file diff --git a/packages/react/src/generators/host/files/module-federation/webpack.config.js__tmpl__ b/packages/react/src/generators/host/files/module-federation/webpack.config.js__tmpl__ index e76b71fd0da37..31055fab95c01 100644 --- a/packages/react/src/generators/host/files/module-federation/webpack.config.js__tmpl__ +++ b/packages/react/src/generators/host/files/module-federation/webpack.config.js__tmpl__ @@ -1,13 +1,12 @@ -// @ts-check - +const { composePlugins, withNx } = require('@nrwl/webpack'); +const { withReact } = require('@nrwl/react'); const { withModuleFederation } = require('@nrwl/react/module-federation'); + const baseConfig = require('./module-federation.config'); -/** - * @type {import('@nrwl/devkit').ModuleFederationConfig} - **/ -const defaultConfig = { - ...baseConfig, +const config = { + ...baseConfig, }; -module.exports = withModuleFederation(defaultConfig); \ No newline at end of file +// Nx plugins for webpack to build config object from Nx options and context. +module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config)); diff --git a/packages/react/src/generators/host/files/module-federation/webpack.config.prod.js__tmpl__ b/packages/react/src/generators/host/files/module-federation/webpack.config.prod.js__tmpl__ index 84d3282cb35cb..d4698dc91e42e 100644 --- a/packages/react/src/generators/host/files/module-federation/webpack.config.prod.js__tmpl__ +++ b/packages/react/src/generators/host/files/module-federation/webpack.config.prod.js__tmpl__ @@ -1,11 +1,9 @@ -// @ts-check +const { composePlugins, withNx } = require('@nrwl/webpack'); +const { withReact } = require('@nrwl/react'); +const { withModuleFederation } = require('@nrwl/react/module-federation')); -const { withModuleFederation } = require('@nrwl/react/module-federation'); const baseConfig = require('./module-federation.config'); -/** - * @type {import('@nrwl/devkit').ModuleFederationConfig} - **/ const prodConfig = { ...baseConfig, /* @@ -30,4 +28,5 @@ const prodConfig = { ], }; -module.exports = withModuleFederation(prodConfig); +// Nx plugins for webpack to build config object from Nx options and context. +module.exports = composePlugins(withNx(), withReact(), withModuleFederation(prodConfig)); diff --git a/packages/react/src/generators/remote/files/module-federation-ssr/module-federation.server.config.js__tmpl__ b/packages/react/src/generators/remote/files/module-federation-ssr/module-federation.server.config.js__tmpl__ index d9cd790734b44..07de46836b47d 100644 --- a/packages/react/src/generators/remote/files/module-federation-ssr/module-federation.server.config.js__tmpl__ +++ b/packages/react/src/generators/remote/files/module-federation-ssr/module-federation.server.config.js__tmpl__ @@ -1,13 +1,6 @@ -// @ts-check - -/** - * @type {import('@nrwl/devkit').ModuleFederationConfig} - **/ -const moduleFederationConfig = { +module.exports = { name: '<%= projectName %>', exposes: { './Module': '<%= appProjectRoot %>/src/remote-entry.ts', }, }; - -module.exports = moduleFederationConfig; diff --git a/packages/react/src/generators/remote/files/module-federation-ssr/webpack.server.config.js__tmpl__ b/packages/react/src/generators/remote/files/module-federation-ssr/webpack.server.config.js__tmpl__ index d512dfd6508ab..36f12af17e390 100644 --- a/packages/react/src/generators/remote/files/module-federation-ssr/webpack.server.config.js__tmpl__ +++ b/packages/react/src/generators/remote/files/module-federation-ssr/webpack.server.config.js__tmpl__ @@ -1,8 +1,12 @@ +const { composePlugins, withNx } = require('@nrwl/webpack'); +const { withReact } = require('@nrwl/react'); const { withModuleFederationForSSR } = require('@nrwl/react/module-federation'); + const baseConfig = require("./module-federation.server.config"); const defaultConfig = { - ...baseConfig + ...baseConfig, }; -module.exports = withModuleFederationForSSR(defaultConfig); +// Nx plugins for webpack to build config object from Nx options and context. +module.exports = composePlugins(withNx(), withReact(), withModuleFederationForSSR(defaultConfig)); diff --git a/packages/react/src/generators/remote/files/module-federation/module-federation.config.js__tmpl__ b/packages/react/src/generators/remote/files/module-federation/module-federation.config.js__tmpl__ index ce5c9e4b4858f..67bfd8b4ea8e2 100644 --- a/packages/react/src/generators/remote/files/module-federation/module-federation.config.js__tmpl__ +++ b/packages/react/src/generators/remote/files/module-federation/module-federation.config.js__tmpl__ @@ -1,13 +1,6 @@ -// @ts-check - -/** - * @type {import('@nrwl/devkit').ModuleFederationConfig} - **/ -const moduleFederationConfig = { +module.exports = { name: '<%= projectName %>', exposes: { './Module': './src/remote-entry.ts', }, }; - -module.exports = moduleFederationConfig; diff --git a/packages/react/src/generators/remote/files/module-federation/webpack.config.js__tmpl__ b/packages/react/src/generators/remote/files/module-federation/webpack.config.js__tmpl__ index e76b71fd0da37..42c59270e0fb5 100644 --- a/packages/react/src/generators/remote/files/module-federation/webpack.config.js__tmpl__ +++ b/packages/react/src/generators/remote/files/module-federation/webpack.config.js__tmpl__ @@ -1,13 +1,12 @@ -// @ts-check +const { composePlugins, withNx } = require('@nrwl/webpack'); +const { withReact } = require('@nrwl/react'); +const { withModuleFederation } = require('@nrwl/react/module-federation')); -const { withModuleFederation } = require('@nrwl/react/module-federation'); const baseConfig = require('./module-federation.config'); -/** - * @type {import('@nrwl/devkit').ModuleFederationConfig} - **/ -const defaultConfig = { - ...baseConfig, +const config = { + ...baseConfig, }; -module.exports = withModuleFederation(defaultConfig); \ No newline at end of file +// Nx plugins for webpack to build config object from Nx options and context. +module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config)); diff --git a/packages/react/src/generators/setup-ssr/setup-ssr.ts b/packages/react/src/generators/setup-ssr/setup-ssr.ts index 44dcea0138c33..fefa3d3a2b037 100644 --- a/packages/react/src/generators/setup-ssr/setup-ssr.ts +++ b/packages/react/src/generators/setup-ssr/setup-ssr.ts @@ -107,7 +107,7 @@ export async function setupSsrGenerator(tree: Tree, options: Schema) { compiler: 'babel', externalDependencies: 'all', outputHashing: 'none', - webpackConfig: '@nrwl/react/plugins/webpack', + webpackConfig: joinPathFragments(projectRoot, 'webpack.config.js'), }, configurations: { development: { diff --git a/packages/web/src/generators/application/application.spec.ts b/packages/web/src/generators/application/application.spec.ts index 55c939ebebaab..6129d93e3255e 100644 --- a/packages/web/src/generators/application/application.spec.ts +++ b/packages/web/src/generators/application/application.spec.ts @@ -351,6 +351,7 @@ describe('app', () => { scripts: [], styles: ['apps/my-app/src/styles.css'], tsConfig: 'apps/my-app/tsconfig.app.json', + webpackConfig: 'apps/my-app/webpack.config.js', }); expect(architectConfig.build.configurations.production).toEqual({ optimization: true, diff --git a/packages/web/src/generators/application/application.ts b/packages/web/src/generators/application/application.ts index 634f5bc130b54..2670fbfc5488c 100644 --- a/packages/web/src/generators/application/application.ts +++ b/packages/web/src/generators/application/application.ts @@ -44,7 +44,7 @@ function createApplicationFiles(tree: Tree, options: NormalizedSchema) { tree, join( __dirname, - options.bundler === 'vite' ? './files/app-vite' : './files/app' + options.bundler === 'vite' ? './files/app-vite' : './files/app-webpack' ), options.appProjectRoot, { @@ -81,6 +81,10 @@ async function setupBundler(tree: Tree, options: NormalizedSchema) { tsConfig, compiler: options.compiler ?? 'babel', devServer: true, + webpackConfig: joinPathFragments( + options.appProjectRoot, + 'webpack.config.js' + ), }); const project = readProjectConfiguration(tree, options.projectName); const prodConfig = project.targets.build.configurations.production; diff --git a/packages/web/src/generators/application/files/app/.babelrc__tmpl__ b/packages/web/src/generators/application/files/app-webpack/.babelrc__tmpl__ similarity index 100% rename from packages/web/src/generators/application/files/app/.babelrc__tmpl__ rename to packages/web/src/generators/application/files/app-webpack/.babelrc__tmpl__ diff --git a/packages/web/src/generators/application/files/app/src/app/app.element.__style__ b/packages/web/src/generators/application/files/app-webpack/src/app/app.element.__style__ similarity index 100% rename from packages/web/src/generators/application/files/app/src/app/app.element.__style__ rename to packages/web/src/generators/application/files/app-webpack/src/app/app.element.__style__ diff --git a/packages/web/src/generators/application/files/app/src/app/app.element.spec.ts__tmpl__ b/packages/web/src/generators/application/files/app-webpack/src/app/app.element.spec.ts__tmpl__ similarity index 100% rename from packages/web/src/generators/application/files/app/src/app/app.element.spec.ts__tmpl__ rename to packages/web/src/generators/application/files/app-webpack/src/app/app.element.spec.ts__tmpl__ diff --git a/packages/web/src/generators/application/files/app/src/app/app.element.ts__tmpl__ b/packages/web/src/generators/application/files/app-webpack/src/app/app.element.ts__tmpl__ similarity index 100% rename from packages/web/src/generators/application/files/app/src/app/app.element.ts__tmpl__ rename to packages/web/src/generators/application/files/app-webpack/src/app/app.element.ts__tmpl__ diff --git a/packages/web/src/generators/application/files/app/src/assets/.gitkeep b/packages/web/src/generators/application/files/app-webpack/src/assets/.gitkeep similarity index 100% rename from packages/web/src/generators/application/files/app/src/assets/.gitkeep rename to packages/web/src/generators/application/files/app-webpack/src/assets/.gitkeep diff --git a/packages/web/src/generators/application/files/app/src/environments/environment.prod.ts__tmpl__ b/packages/web/src/generators/application/files/app-webpack/src/environments/environment.prod.ts__tmpl__ similarity index 100% rename from packages/web/src/generators/application/files/app/src/environments/environment.prod.ts__tmpl__ rename to packages/web/src/generators/application/files/app-webpack/src/environments/environment.prod.ts__tmpl__ diff --git a/packages/web/src/generators/application/files/app/src/environments/environment.ts__tmpl__ b/packages/web/src/generators/application/files/app-webpack/src/environments/environment.ts__tmpl__ similarity index 100% rename from packages/web/src/generators/application/files/app/src/environments/environment.ts__tmpl__ rename to packages/web/src/generators/application/files/app-webpack/src/environments/environment.ts__tmpl__ diff --git a/packages/web/src/generators/application/files/app/src/favicon.ico b/packages/web/src/generators/application/files/app-webpack/src/favicon.ico similarity index 100% rename from packages/web/src/generators/application/files/app/src/favicon.ico rename to packages/web/src/generators/application/files/app-webpack/src/favicon.ico diff --git a/packages/web/src/generators/application/files/app/src/index.html b/packages/web/src/generators/application/files/app-webpack/src/index.html similarity index 100% rename from packages/web/src/generators/application/files/app/src/index.html rename to packages/web/src/generators/application/files/app-webpack/src/index.html diff --git a/packages/web/src/generators/application/files/app/src/main.ts__tmpl__ b/packages/web/src/generators/application/files/app-webpack/src/main.ts__tmpl__ similarity index 100% rename from packages/web/src/generators/application/files/app/src/main.ts__tmpl__ rename to packages/web/src/generators/application/files/app-webpack/src/main.ts__tmpl__ diff --git a/packages/web/src/generators/application/files/app/src/styles.__style__ b/packages/web/src/generators/application/files/app-webpack/src/styles.__style__ similarity index 100% rename from packages/web/src/generators/application/files/app/src/styles.__style__ rename to packages/web/src/generators/application/files/app-webpack/src/styles.__style__ diff --git a/packages/web/src/generators/application/files/app/tsconfig.app.json b/packages/web/src/generators/application/files/app-webpack/tsconfig.app.json similarity index 100% rename from packages/web/src/generators/application/files/app/tsconfig.app.json rename to packages/web/src/generators/application/files/app-webpack/tsconfig.app.json diff --git a/packages/web/src/generators/application/files/app/tsconfig.json b/packages/web/src/generators/application/files/app-webpack/tsconfig.json similarity index 100% rename from packages/web/src/generators/application/files/app/tsconfig.json rename to packages/web/src/generators/application/files/app-webpack/tsconfig.json diff --git a/packages/web/src/generators/application/files/app-webpack/webpack.config.js__tmpl__ b/packages/web/src/generators/application/files/app-webpack/webpack.config.js__tmpl__ new file mode 100644 index 0000000000000..c083417ac7556 --- /dev/null +++ b/packages/web/src/generators/application/files/app-webpack/webpack.config.js__tmpl__ @@ -0,0 +1,8 @@ +const { composePlugins, withNx, withWeb } = require('@nrwl/webpack'); + +// Nx plugins for webpack. +module.exports = composePlugins(withNx(), withWeb(), (config) => { + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + return config; +}); diff --git a/packages/web/src/generators/application/files/app/browserslist b/packages/web/src/generators/application/files/app/browserslist deleted file mode 100644 index 8d6179367e7ba..0000000000000 --- a/packages/web/src/generators/application/files/app/browserslist +++ /dev/null @@ -1,13 +0,0 @@ -# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries -# -# If you need to support different browsers in production, you may tweak the list below. - -last 1 Chrome version -last 1 Firefox version -last 2 Edge major versions -last 2 Safari major version -last 2 iOS major versions -Firefox ESR -not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/packages/webpack/src/executors/dev-server/dev-server.impl.ts b/packages/webpack/src/executors/dev-server/dev-server.impl.ts index 0da28079446ad..8ec3325e32874 100644 --- a/packages/webpack/src/executors/dev-server/dev-server.impl.ts +++ b/packages/webpack/src/executors/dev-server/dev-server.impl.ts @@ -56,7 +56,7 @@ export async function* devServerExecutor( ); } - let webpackConfig = getDevServerConfig(context, buildOptions, serveOptions); + let config = getDevServerConfig(context, buildOptions, serveOptions); if (buildOptions.webpackConfig) { let customWebpack = resolveCustomWebpackConfig( @@ -68,16 +68,17 @@ export async function* devServerExecutor( customWebpack = await customWebpack; } - webpackConfig = await customWebpack(webpackConfig, { - buildOptions, + config = await customWebpack(config, { + options: buildOptions, + context, configuration: serveOptions.buildTarget.split(':')[2], }); } return yield* eachValueFrom( - runWebpackDevServer(webpackConfig, webpack, WebpackDevServer).pipe( + runWebpackDevServer(config, webpack, WebpackDevServer).pipe( tap(({ stats }) => { - console.info(stats.toString((webpackConfig as any).stats)); + console.info(stats.toString((config as any).stats)); }), map(({ baseUrl, stats }) => { return { diff --git a/packages/webpack/src/executors/webpack/schema.d.ts b/packages/webpack/src/executors/webpack/schema.d.ts index 6416958256ec2..dd9432f03aa99 100644 --- a/packages/webpack/src/executors/webpack/schema.d.ts +++ b/packages/webpack/src/executors/webpack/schema.d.ts @@ -83,6 +83,7 @@ export interface WebpackExecutorOptions { export interface NormalizedWebpackExecutorOptions extends WebpackExecutorOptions { + outputFileName: string; assets?: AssetGlobPattern[]; root?: string; projectRoot?: string; diff --git a/packages/webpack/src/plugins/generate-package-json-webpack-plugin.ts b/packages/webpack/src/plugins/generate-package-json-plugin.ts similarity index 86% rename from packages/webpack/src/plugins/generate-package-json-webpack-plugin.ts rename to packages/webpack/src/plugins/generate-package-json-plugin.ts index 977772e70cceb..44484747847d6 100644 --- a/packages/webpack/src/plugins/generate-package-json-webpack-plugin.ts +++ b/packages/webpack/src/plugins/generate-package-json-plugin.ts @@ -1,37 +1,35 @@ import { type Compiler, sources, type WebpackPluginInstance } from 'webpack'; import { + createLockFile, + createPackageJson, ExecutorContext, type ProjectGraph, serializeJson, - createPackageJson, - createLockFile, } from '@nrwl/devkit'; import { getHelperDependenciesFromProjectGraph, HelperDependency, } from '@nrwl/js/src/utils/compiler-helper-dependency'; import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript'; - -import { NormalizedWebpackExecutorOptions } from '../executors/webpack/schema'; import { getLockFileName } from 'nx/src/lock-file/lock-file'; -export class GeneratePackageJsonWebpackPlugin implements WebpackPluginInstance { +const pluginName = 'GeneratePackageJsonPlugin'; + +export class GeneratePackageJsonPlugin implements WebpackPluginInstance { private readonly projectGraph: ProjectGraph; constructor( - private readonly context: ExecutorContext, - private readonly options: NormalizedWebpackExecutorOptions + private readonly options: { tsConfig: string; outputFileName: string }, + private readonly context: ExecutorContext ) { this.projectGraph = context.projectGraph; } apply(compiler: Compiler): void { - const pluginName = this.constructor.name; - compiler.hooks.thisCompilation.tap(pluginName, (compilation) => { compilation.hooks.processAssets.tap( { - name: 'nx-generate-package-json-plugin', + name: pluginName, stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL, }, () => { diff --git a/packages/webpack/src/utils/config.ts b/packages/webpack/src/utils/config.ts index 83b53f013af63..19d1025b2a6fe 100644 --- a/packages/webpack/src/utils/config.ts +++ b/packages/webpack/src/utils/config.ts @@ -17,18 +17,18 @@ export function getBaseWebpackPartial( export type NxWebpackPlugin = ( config: Configuration, - ctx?: { + ctx: { options: NormalizedWebpackExecutorOptions; - context?: ExecutorContext; + context: ExecutorContext; } ) => Configuration; export function composePlugins(...plugins: NxWebpackPlugin[]) { return function combined( config: Configuration, - ctx?: { + ctx: { options: NormalizedWebpackExecutorOptions; - context?: ExecutorContext; + context: ExecutorContext; } ): Configuration { for (const plugin of plugins) { diff --git a/packages/webpack/src/utils/webpack/custom-webpack.ts b/packages/webpack/src/utils/webpack/custom-webpack.ts index fcbaa515a062f..dc963026e246d 100644 --- a/packages/webpack/src/utils/webpack/custom-webpack.ts +++ b/packages/webpack/src/utils/webpack/custom-webpack.ts @@ -1,5 +1,9 @@ export function tsNodeRegister(file: string = '', tsConfig?: string) { if (!file?.endsWith('.ts')) return; + + // Avoid double-registering which can lead to issues type-checking already transformed files. + if (isRegistered()) return; + // Register TS compiler lazily require('ts-node').register({ project: tsConfig, @@ -29,3 +33,9 @@ export function resolveCustomWebpackConfig(path: string, tsConfig: string) { // `{ default: { ... } }` return customWebpackConfig.default || customWebpackConfig; } +export function isRegistered() { + return ( + require.extensions['.ts'] != undefined || + require.extensions['.tsx'] != undefined + ); +} diff --git a/packages/webpack/src/utils/with-nx.ts b/packages/webpack/src/utils/with-nx.ts index 3fd2d6bed7a3d..f421a0209060f 100644 --- a/packages/webpack/src/utils/with-nx.ts +++ b/packages/webpack/src/utils/with-nx.ts @@ -11,7 +11,7 @@ import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); import { NormalizedWebpackExecutorOptions } from '../executors/webpack/schema'; import { StatsJsonPlugin } from '../plugins/stats-json-plugin'; import { createCopyPlugin } from './create-copy-plugin'; -import { GeneratePackageJsonWebpackPlugin } from '../plugins/generate-package-json-webpack-plugin'; +import { GeneratePackageJsonPlugin } from '../plugins/generate-package-json-plugin'; import { getOutputHashFormat } from './hash-format'; const IGNORED_WEBPACK_WARNINGS = [ @@ -22,6 +22,8 @@ const IGNORED_WEBPACK_WARNINGS = [ const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx']; const mainFields = ['main', 'module']; +const processed = new Set(); + export function withNx(opts?: { skipTypeChecking?: boolean }) { return function configure( config: Configuration, @@ -33,7 +35,10 @@ export function withNx(opts?: { skipTypeChecking?: boolean }) { context: ExecutorContext; } ): Configuration { + if (processed.has(config)) return config; + const plugins: WebpackPluginInstance[] = []; + if (!opts?.skipTypeChecking) { plugins.push( new ForkTsCheckerWebpackPlugin({ @@ -88,7 +93,7 @@ export function withNx(opts?: { skipTypeChecking?: boolean }) { } if (options.generatePackageJson && context) { - plugins.push(new GeneratePackageJsonWebpackPlugin(context, options)); + plugins.push(new GeneratePackageJsonPlugin(options, context)); } if (options.statsJson) { @@ -118,7 +123,7 @@ export function withNx(opts?: { skipTypeChecking?: boolean }) { ? `[name]${hashFormat.chunk}.js` : '[name].js'; - return { + const updated = { ...config, target: options.target, node: false as const, @@ -132,14 +137,14 @@ export function withNx(opts?: { skipTypeChecking?: boolean }) { mode: process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'production' - ? process.env.NODE_ENV - : 'none', + ? (process.env.NODE_ENV as 'development' | 'production') + : ('none' as const), devtool: options.sourceMap === 'hidden' ? 'hidden-source-map' : options.sourceMap ? 'source-map' - : false, + : (false as const), entry, output: { ...config.output, @@ -150,7 +155,7 @@ export function withNx(opts?: { skipTypeChecking?: boolean }) { hashFunction: 'xxhash64', // Disabled for performance pathinfo: false, - scriptType: 'module', + scriptType: 'module' as const, }, watch: options.watch, watchOptions: { @@ -186,6 +191,7 @@ export function withNx(opts?: { skipTypeChecking?: boolean }) { ? new TerserPlugin({ parallel: true, terserOptions: { + keep_classnames: true, ecma: 2020, safari10: true, output: { @@ -208,7 +214,7 @@ export function withNx(opts?: { skipTypeChecking?: boolean }) { }, performance: { ...config.performance, - hints: false, + hints: false as const, }, experiments: { ...config.experiments, cacheUnaffected: true }, ignoreWarnings: [ @@ -269,6 +275,9 @@ export function withNx(opts?: { skipTypeChecking?: boolean }) { usedExports: !!options.verbose, }, }; + + processed.add(updated); + return updated; }; } diff --git a/packages/webpack/src/utils/with-web.ts b/packages/webpack/src/utils/with-web.ts index 2ad4276e1d0e2..586891251038d 100644 --- a/packages/webpack/src/utils/with-web.ts +++ b/packages/webpack/src/utils/with-web.ts @@ -7,6 +7,7 @@ import { } from 'webpack'; import { SubresourceIntegrityPlugin } from 'webpack-subresource-integrity'; import * as path from 'path'; +import { basename } from 'path'; import { getOutputHashFormat } from '@nrwl/webpack/src/utils/hash-format'; import { PostcssCliResources } from '@nrwl/webpack/src/utils/webpack/plugins/postcss-cli-resources'; import { normalizeExtraEntryPoints } from '@nrwl/webpack/src/utils/webpack/normalize-entry'; @@ -19,7 +20,6 @@ import CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); import MiniCssExtractPlugin = require('mini-css-extract-plugin'); import autoprefixer = require('autoprefixer'); import postcssImports = require('postcss-import'); -import { basename } from 'path'; interface PostcssOptions { (loader: any): any; @@ -27,11 +27,15 @@ interface PostcssOptions { config?: string; } +const processed = new Set(); + export function withWeb() { return function configure( config: Configuration, { options }: { options: NormalizedWebpackExecutorOptions } ): Configuration { + if (processed.has(config)) return config; + const plugins = []; const stylesOptimization = @@ -246,9 +250,6 @@ export function withWeb() { }) ); - // context needs to be set for babel to pick up correct babelrc - config.context = path.join(options.root, options.projectRoot); - config.output = { ...config.output, crossOriginLoading: options.subresourceIntegrity @@ -303,7 +304,7 @@ export function withWeb() { config.module = { ...config.module, rules: [ - ...config.module.rules, + ...(config.module.rules ?? []), ...rules, { test: /\.(bmp|png|jpe?g|gif|webp|avif)$/, @@ -323,6 +324,7 @@ export function withWeb() { }, ], }; + processed.add(config); return config; }; }