feat(webpack): generate React and Web apps with webpack.config.js file (#14285)
This commit is contained in:
parent
0925c294d1
commit
454fba49b2
35
e2e/node/src/node-esbuild.test.ts
Normal file
35
e2e/node/src/node-esbuild.test.ts
Normal file
@ -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);
|
||||
});
|
||||
35
e2e/node/src/node-webpack.test.ts
Normal file
35
e2e/node/src/node-webpack.test.ts
Normal file
@ -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);
|
||||
});
|
||||
@ -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`);
|
||||
|
||||
@ -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));
|
||||
`
|
||||
);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@ -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<NxJsonConfiguration>(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<NxJsonConfiguration>(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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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;
|
||||
});
|
||||
@ -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';
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
69
packages/react/plugins/with-react.ts
Normal file
69
packages/react/plugins/with-react.ts
Normal file
@ -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;
|
||||
};
|
||||
}
|
||||
@ -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 () => {
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -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;
|
||||
});
|
||||
@ -72,7 +72,10 @@ function createBuildTarget(options: NormalizedSchema): TargetConfiguration {
|
||||
),
|
||||
],
|
||||
scripts: [],
|
||||
webpackConfig: '@nrwl/react/plugins/webpack',
|
||||
webpackConfig: joinPathFragments(
|
||||
options.appProjectRoot,
|
||||
'webpack.config.js'
|
||||
),
|
||||
},
|
||||
configurations: {
|
||||
development: {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
@ -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);
|
||||
// Nx plugins for webpack to build config object from Nx options and context.
|
||||
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config));
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
// Nx plugins for webpack to build config object from Nx options and context.
|
||||
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config));
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -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;
|
||||
});
|
||||
@ -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'.
|
||||
@ -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 {
|
||||
|
||||
@ -83,6 +83,7 @@ export interface WebpackExecutorOptions {
|
||||
|
||||
export interface NormalizedWebpackExecutorOptions
|
||||
extends WebpackExecutorOptions {
|
||||
outputFileName: string;
|
||||
assets?: AssetGlobPattern[];
|
||||
root?: string;
|
||||
projectRoot?: string;
|
||||
|
||||
@ -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,
|
||||
},
|
||||
() => {
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user