feat(angular): add convert-to-rspack generator (#29860)
## Current Behavior Nx currently does not offer an automated method for switching from an Angular Webpack build to an Angular Rspack build. ## Expected Behavior Provide a generator `convert-to-rspack` in `@nx/angular` that will allow conversion from an Angular Webpack build to an Angular Rspack build. Usage: `nx g convert-to-rspack --project=myapp` ## TODO - [x] handle more builder options - [x] take existing custom webpack configs and migrate into the rspack config that is created
This commit is contained in:
parent
f40873ffbe
commit
0082081d5c
@ -7314,6 +7314,14 @@
|
|||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "convert-to-rspack",
|
||||||
|
"path": "/nx-api/angular/generators/convert-to-rspack",
|
||||||
|
"name": "convert-to-rspack",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "directive",
|
"id": "directive",
|
||||||
"path": "/nx-api/angular/generators/directive",
|
"path": "/nx-api/angular/generators/directive",
|
||||||
|
|||||||
@ -208,6 +208,15 @@
|
|||||||
"path": "/nx-api/angular/generators/convert-to-application-executor",
|
"path": "/nx-api/angular/generators/convert-to-application-executor",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
},
|
},
|
||||||
|
"/nx-api/angular/generators/convert-to-rspack": {
|
||||||
|
"description": "Converts Angular Webpack projects to use Rspack.",
|
||||||
|
"file": "generated/packages/angular/generators/convert-to-rspack.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-rspack",
|
||||||
|
"originalFilePath": "/packages/angular/src/generators/convert-to-rspack/schema.json",
|
||||||
|
"path": "/nx-api/angular/generators/convert-to-rspack",
|
||||||
|
"type": "generator"
|
||||||
|
},
|
||||||
"/nx-api/angular/generators/directive": {
|
"/nx-api/angular/generators/directive": {
|
||||||
"description": "Generate an Angular directive.",
|
"description": "Generate an Angular directive.",
|
||||||
"file": "generated/packages/angular/generators/directive.json",
|
"file": "generated/packages/angular/generators/directive.json",
|
||||||
|
|||||||
@ -203,6 +203,15 @@
|
|||||||
"path": "angular/generators/convert-to-application-executor",
|
"path": "angular/generators/convert-to-application-executor",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Converts Angular Webpack projects to use Rspack.",
|
||||||
|
"file": "generated/packages/angular/generators/convert-to-rspack.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-rspack",
|
||||||
|
"originalFilePath": "/packages/angular/src/generators/convert-to-rspack/schema.json",
|
||||||
|
"path": "angular/generators/convert-to-rspack",
|
||||||
|
"type": "generator"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Generate an Angular directive.",
|
"description": "Generate an Angular directive.",
|
||||||
"file": "generated/packages/angular/generators/directive.json",
|
"file": "generated/packages/angular/generators/directive.json",
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "convert-to-rspack",
|
||||||
|
"factory": "./src/generators/convert-to-rspack/convert-to-rspack",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "GeneratorNxApp",
|
||||||
|
"title": "Creates an Angular application.",
|
||||||
|
"description": "Creates an Angular application.",
|
||||||
|
"type": "object",
|
||||||
|
"cli": "nx",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"aliases": ["name", "projectName"],
|
||||||
|
"description": "Project for which to convert to rspack.",
|
||||||
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"description": "Skip formatting files.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"skipInstall": {
|
||||||
|
"description": "Skip installing dependencies.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Converts Angular Webpack projects to use Rspack.",
|
||||||
|
"implementation": "/packages/angular/src/generators/convert-to-rspack/convert-to-rspack.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/angular/src/generators/convert-to-rspack/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
@ -366,6 +366,7 @@
|
|||||||
- [component-story](/nx-api/angular/generators/component-story)
|
- [component-story](/nx-api/angular/generators/component-story)
|
||||||
- [component-test](/nx-api/angular/generators/component-test)
|
- [component-test](/nx-api/angular/generators/component-test)
|
||||||
- [convert-to-application-executor](/nx-api/angular/generators/convert-to-application-executor)
|
- [convert-to-application-executor](/nx-api/angular/generators/convert-to-application-executor)
|
||||||
|
- [convert-to-rspack](/nx-api/angular/generators/convert-to-rspack)
|
||||||
- [directive](/nx-api/angular/generators/directive)
|
- [directive](/nx-api/angular/generators/directive)
|
||||||
- [federate-module](/nx-api/angular/generators/federate-module)
|
- [federate-module](/nx-api/angular/generators/federate-module)
|
||||||
- [init](/nx-api/angular/generators/init)
|
- [init](/nx-api/angular/generators/init)
|
||||||
|
|||||||
@ -144,3 +144,25 @@ describe('Move Angular Project', () => {
|
|||||||
expect(lib2File).toContain(`extends ${newModule}`);
|
expect(lib2File).toContain(`extends ${newModule}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Convert Angular Webpack Project to Rspack', () => {
|
||||||
|
let proj: string;
|
||||||
|
let app1: string;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
proj = newProject({ packages: ['@nx/angular'] });
|
||||||
|
app1 = uniq('app1');
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/angular:app ${app1} --bundler=webpack --no-interactive`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => cleanupProject());
|
||||||
|
|
||||||
|
it('should convert an Angular Webpack project to Rspack', async () => {
|
||||||
|
runCLI(`generate @nx/angular:convert-to-rspack --project=${app1}`);
|
||||||
|
const buildOutput = runCLI(`build ${app1}`);
|
||||||
|
expect(buildOutput).toContain('rspack build');
|
||||||
|
expect(buildOutput).toContain('browser compiled');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -38,6 +38,11 @@
|
|||||||
"schema": "./src/generators/convert-to-application-executor/schema.json",
|
"schema": "./src/generators/convert-to-application-executor/schema.json",
|
||||||
"description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder."
|
"description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder."
|
||||||
},
|
},
|
||||||
|
"convert-to-rspack": {
|
||||||
|
"factory": "./src/generators/convert-to-rspack/convert-to-rspack",
|
||||||
|
"schema": "./src/generators/convert-to-rspack/schema.json",
|
||||||
|
"description": "Converts Angular Webpack projects to use Rspack."
|
||||||
|
},
|
||||||
"directive": {
|
"directive": {
|
||||||
"factory": "./src/generators/directive/directive",
|
"factory": "./src/generators/directive/directive",
|
||||||
"schema": "./src/generators/directive/schema.json",
|
"schema": "./src/generators/directive/schema.json",
|
||||||
|
|||||||
@ -50,6 +50,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@phenomnomnominal/tsquery": "~5.0.1",
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
||||||
"@typescript-eslint/type-utils": "^8.0.0",
|
"@typescript-eslint/type-utils": "^8.0.0",
|
||||||
|
"enquirer": "~2.3.6",
|
||||||
"picocolors": "^1.1.0",
|
"picocolors": "^1.1.0",
|
||||||
"magic-string": "~0.30.2",
|
"magic-string": "~0.30.2",
|
||||||
"minimatch": "9.0.3",
|
"minimatch": "9.0.3",
|
||||||
@ -60,6 +61,7 @@
|
|||||||
"@nx/js": "file:../js",
|
"@nx/js": "file:../js",
|
||||||
"@nx/eslint": "file:../eslint",
|
"@nx/eslint": "file:../eslint",
|
||||||
"@nx/webpack": "file:../webpack",
|
"@nx/webpack": "file:../webpack",
|
||||||
|
"@nx/rspack": "file:../rspack",
|
||||||
"@nx/module-federation": "file:../module-federation",
|
"@nx/module-federation": "file:../module-federation",
|
||||||
"@nx/web": "file:../web",
|
"@nx/web": "file:../web",
|
||||||
"@nx/workspace": "file:../workspace",
|
"@nx/workspace": "file:../workspace",
|
||||||
|
|||||||
@ -0,0 +1,487 @@
|
|||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { convertToRspack } from './convert-to-rspack';
|
||||||
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
|
readJson,
|
||||||
|
readNxJson,
|
||||||
|
readProjectConfiguration,
|
||||||
|
updateJson,
|
||||||
|
writeJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import * as _configUtils from '@nx/devkit/src/utils/config-utils';
|
||||||
|
|
||||||
|
jest.mock('@nx/devkit/src/utils/config-utils', () => ({
|
||||||
|
...jest.requireActual('@nx/devkit/src/utils/config-utils'),
|
||||||
|
loadConfigFile: jest.fn().mockImplementation(async (path) => {
|
||||||
|
return () => {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('convert-to-rspack', () => {
|
||||||
|
it('should convert a basic angular webpack application to rspack', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
addProjectConfiguration(tree, 'app', {
|
||||||
|
root: 'apps/app',
|
||||||
|
sourceRoot: 'apps/app/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@angular-devkit/build-angular:browser',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/apps/app',
|
||||||
|
index: 'apps/app/src/index.html',
|
||||||
|
main: 'apps/app/src/main.ts',
|
||||||
|
polyfills: ['tslib'], // zone.js is not in nx repo's node_modules so simulating it with a package that is
|
||||||
|
tsConfig: 'apps/app/tsconfig.app.json',
|
||||||
|
assets: [
|
||||||
|
'apps/app/src/favicon.ico',
|
||||||
|
'apps/app/src/assets',
|
||||||
|
{ input: 'apps/app/public', glob: '**/*' },
|
||||||
|
],
|
||||||
|
styles: ['apps/app/src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
writeJson(tree, 'apps/app/tsconfig.json', {});
|
||||||
|
updateJson(tree, 'package.json', (json) => {
|
||||||
|
json.scripts ??= {};
|
||||||
|
json.scripts.build = 'nx build';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToRspack(tree, { project: 'app' });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const updatedProject = readProjectConfiguration(tree, 'app');
|
||||||
|
const pkgJson = readJson(tree, 'package.json');
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
expect(tree.read('apps/app/rspack.config.ts', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import { createConfig } from '@nx/angular-rspack';
|
||||||
|
|
||||||
|
export default createConfig({
|
||||||
|
options: {
|
||||||
|
root: __dirname,
|
||||||
|
|
||||||
|
outputPath: {
|
||||||
|
base: '../../dist/apps/app',
|
||||||
|
},
|
||||||
|
index: './src/index.html',
|
||||||
|
browser: './src/main.ts',
|
||||||
|
polyfills: ['tslib'],
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
assets: [
|
||||||
|
'./src/favicon.ico',
|
||||||
|
'./src/assets',
|
||||||
|
{
|
||||||
|
input: './public',
|
||||||
|
glob: '**/*',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
styles: ['./src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
expect(pkgJson.devDependencies['@nx/angular-rspack']).toBeDefined();
|
||||||
|
expect(
|
||||||
|
nxJson.plugins.find((p) =>
|
||||||
|
typeof p === 'string' ? false : p.plugin === '@nx/rspack/plugin'
|
||||||
|
)
|
||||||
|
).toBeDefined();
|
||||||
|
expect(pkgJson.scripts?.build).toBeUndefined();
|
||||||
|
expect(updatedProject.targets.build).not.toBeDefined();
|
||||||
|
expect(updatedProject.targets.serve).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should normalize paths to libs in workspace correctly', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
addProjectConfiguration(tree, 'app', {
|
||||||
|
root: 'apps/app',
|
||||||
|
sourceRoot: 'apps/app/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@angular-devkit/build-angular:browser',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/apps/app',
|
||||||
|
index: 'apps/app/src/index.html',
|
||||||
|
main: 'apps/app/src/main.ts',
|
||||||
|
polyfills: ['tslib', 'apps/app/src/polyfills.ts'],
|
||||||
|
tsConfig: 'apps/app/tsconfig.app.json',
|
||||||
|
assets: ['libs/mylib/src/favicon.ico'],
|
||||||
|
styles: ['apps/app/src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
writeJson(tree, 'apps/app/tsconfig.json', {});
|
||||||
|
updateJson(tree, 'package.json', (json) => {
|
||||||
|
json.scripts ??= {};
|
||||||
|
json.scripts.build = 'nx build';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
tree.write('libs/mylib/src/favicon.ico', 'libs/mylib/src/favicon.ico');
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToRspack(tree, { project: 'app' });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const updatedProject = readProjectConfiguration(tree, 'app');
|
||||||
|
const pkgJson = readJson(tree, 'package.json');
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
expect(tree.read('apps/app/rspack.config.ts', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import { createConfig } from '@nx/angular-rspack';
|
||||||
|
|
||||||
|
export default createConfig({
|
||||||
|
options: {
|
||||||
|
root: __dirname,
|
||||||
|
|
||||||
|
outputPath: {
|
||||||
|
base: '../../dist/apps/app',
|
||||||
|
},
|
||||||
|
index: './src/index.html',
|
||||||
|
browser: './src/main.ts',
|
||||||
|
polyfills: ['tslib', './src/polyfills.ts'],
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
assets: ['../../libs/mylib/src/favicon.ico'],
|
||||||
|
styles: ['./src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
expect(pkgJson.devDependencies['@nx/angular-rspack']).toBeDefined();
|
||||||
|
expect(
|
||||||
|
nxJson.plugins.find((p) =>
|
||||||
|
typeof p === 'string' ? false : p.plugin === '@nx/rspack/plugin'
|
||||||
|
)
|
||||||
|
).toBeDefined();
|
||||||
|
expect(pkgJson.scripts?.build).toBeUndefined();
|
||||||
|
expect(updatedProject.targets.build).not.toBeDefined();
|
||||||
|
expect(updatedProject.targets.serve).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert a basic angular webpack application with configurations to rspack', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
addProjectConfiguration(tree, 'app', {
|
||||||
|
root: 'apps/app',
|
||||||
|
sourceRoot: 'apps/app/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@angular-devkit/build-angular:browser',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/apps/app',
|
||||||
|
index: 'apps/app/src/index.html',
|
||||||
|
main: 'apps/app/src/main.ts',
|
||||||
|
polyfills: ['tslib'],
|
||||||
|
tsConfig: 'apps/app/tsconfig.app.json',
|
||||||
|
assets: [
|
||||||
|
'apps/app/src/favicon.ico',
|
||||||
|
'apps/app/src/assets',
|
||||||
|
{ input: 'apps/app/public', glob: '**/*' },
|
||||||
|
],
|
||||||
|
styles: ['apps/app/src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
production: {
|
||||||
|
outputPath: 'dist/apps/app-prod',
|
||||||
|
index: 'apps/app/src/index.prod.html',
|
||||||
|
main: 'apps/app/src/main.prod.ts',
|
||||||
|
tsConfig: 'apps/app/tsconfig.prod.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
writeJson(tree, 'apps/app/tsconfig.json', {});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToRspack(tree, { project: 'app' });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const updatedProject = readProjectConfiguration(tree, 'app');
|
||||||
|
const pkgJson = readJson(tree, 'package.json');
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
expect(tree.read('apps/app/rspack.config.ts', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import { createConfig } from '@nx/angular-rspack';
|
||||||
|
|
||||||
|
export default createConfig(
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
root: __dirname,
|
||||||
|
|
||||||
|
outputPath: {
|
||||||
|
base: '../../dist/apps/app',
|
||||||
|
},
|
||||||
|
index: './src/index.html',
|
||||||
|
browser: './src/main.ts',
|
||||||
|
polyfills: ['tslib'],
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
assets: [
|
||||||
|
'./src/favicon.ico',
|
||||||
|
'./src/assets',
|
||||||
|
{
|
||||||
|
input: './public',
|
||||||
|
glob: '**/*',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
styles: ['./src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
production: {
|
||||||
|
options: {
|
||||||
|
outputPath: {
|
||||||
|
base: '../../dist/apps/app-prod',
|
||||||
|
},
|
||||||
|
index: './src/index.prod.html',
|
||||||
|
browser: './src/main.prod.ts',
|
||||||
|
tsConfig: './tsconfig.prod.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
expect(pkgJson.devDependencies['@nx/angular-rspack']).toBeDefined();
|
||||||
|
expect(
|
||||||
|
nxJson.plugins.find((p) =>
|
||||||
|
typeof p === 'string' ? false : p.plugin === '@nx/rspack/plugin'
|
||||||
|
)
|
||||||
|
).toBeDefined();
|
||||||
|
expect(updatedProject.targets.build).not.toBeDefined();
|
||||||
|
expect(updatedProject.targets.serve).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert an angular webpack application with custom webpack config function to rspack', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
addProjectConfiguration(tree, 'app', {
|
||||||
|
root: 'apps/app',
|
||||||
|
sourceRoot: 'apps/app/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/angular:webpack-browser',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/apps/app',
|
||||||
|
index: 'apps/app/src/index.html',
|
||||||
|
main: 'apps/app/src/main.ts',
|
||||||
|
polyfills: ['tslib'],
|
||||||
|
tsConfig: 'apps/app/tsconfig.app.json',
|
||||||
|
assets: [
|
||||||
|
'apps/app/src/favicon.ico',
|
||||||
|
'apps/app/src/assets',
|
||||||
|
{ input: 'apps/app/public', glob: '**/*' },
|
||||||
|
],
|
||||||
|
styles: ['apps/app/src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
customWebpackConfig: {
|
||||||
|
path: 'apps/app/webpack.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
writeJson(tree, 'apps/app/tsconfig.json', {});
|
||||||
|
tree.write(
|
||||||
|
'apps/app/module-federation.config.js',
|
||||||
|
`
|
||||||
|
module.exports = {
|
||||||
|
name: 'app',
|
||||||
|
exposes: {
|
||||||
|
'./app': './src/app/index.ts',
|
||||||
|
},
|
||||||
|
remotes: ['remote1', 'remote2'],
|
||||||
|
};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
tree.write(
|
||||||
|
'apps/app/webpack.config.js',
|
||||||
|
`
|
||||||
|
const { withModuleFederation } = require('@nx/module-federation/angular');
|
||||||
|
const config = require('./module-federation.config');
|
||||||
|
module.exports = withModuleFederation(config, { dts: false });
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToRspack(tree, { project: 'app' });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const updatedProject = readProjectConfiguration(tree, 'app');
|
||||||
|
expect(tree.read('apps/app/rspack.config.ts', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import { createConfig } from '@nx/angular-rspack';
|
||||||
|
import baseWebpackConfig from './webpack.config';
|
||||||
|
import webpackMerge from 'webpack-merge';
|
||||||
|
|
||||||
|
const baseConfig = createConfig({
|
||||||
|
options: {
|
||||||
|
root: __dirname,
|
||||||
|
|
||||||
|
outputPath: {
|
||||||
|
base: '../../dist/apps/app',
|
||||||
|
},
|
||||||
|
index: './src/index.html',
|
||||||
|
browser: './src/main.ts',
|
||||||
|
polyfills: ['tslib'],
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
assets: [
|
||||||
|
'./src/favicon.ico',
|
||||||
|
'./src/assets',
|
||||||
|
{
|
||||||
|
input: './public',
|
||||||
|
glob: '**/*',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
styles: ['./src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default webpackMerge(baseConfig[0], baseWebpackConfig);
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
expect(tree.read('apps/app/webpack.config.js', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"const { NxModuleFederationPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const config = require('./module-federation.config');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
new NxModuleFederationPlugin(
|
||||||
|
{ config },
|
||||||
|
{
|
||||||
|
dts: false,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert an angular webpack application with custom webpack config to rspack', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
addProjectConfiguration(tree, 'app', {
|
||||||
|
root: 'apps/app',
|
||||||
|
sourceRoot: 'apps/app/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/angular:webpack-browser',
|
||||||
|
options: {
|
||||||
|
outputPath: 'dist/apps/app',
|
||||||
|
index: 'apps/app/src/index.html',
|
||||||
|
main: 'apps/app/src/main.ts',
|
||||||
|
polyfills: ['tslib'],
|
||||||
|
tsConfig: 'apps/app/tsconfig.app.json',
|
||||||
|
assets: [
|
||||||
|
'apps/app/src/favicon.ico',
|
||||||
|
'apps/app/src/assets',
|
||||||
|
{ input: 'apps/app/public', glob: '**/*' },
|
||||||
|
],
|
||||||
|
styles: ['apps/app/src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
customWebpackConfig: {
|
||||||
|
path: 'apps/app/webpack.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
writeJson(tree, 'apps/app/tsconfig.json', {});
|
||||||
|
tree.write(
|
||||||
|
'apps/app/module-federation.config.js',
|
||||||
|
`
|
||||||
|
module.exports = {
|
||||||
|
name: 'app',
|
||||||
|
exposes: {
|
||||||
|
'./app': './src/app/index.ts',
|
||||||
|
},
|
||||||
|
remotes: ['remote1', 'remote2'],
|
||||||
|
};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
tree.write('apps/app/webpack.config.js', ``);
|
||||||
|
|
||||||
|
jest
|
||||||
|
.spyOn(_configUtils, 'loadConfigFile')
|
||||||
|
.mockImplementation(async (path) => {
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: ['style-loader', 'css-loader'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToRspack(tree, { project: 'app' });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const updatedProject = readProjectConfiguration(tree, 'app');
|
||||||
|
expect(tree.read('apps/app/rspack.config.ts', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import { createConfig } from '@nx/angular-rspack';
|
||||||
|
import baseWebpackConfig from './webpack.config';
|
||||||
|
import webpackMerge from 'webpack-merge';
|
||||||
|
|
||||||
|
const baseConfig = createConfig({
|
||||||
|
options: {
|
||||||
|
root: __dirname,
|
||||||
|
|
||||||
|
outputPath: {
|
||||||
|
base: '../../dist/apps/app',
|
||||||
|
},
|
||||||
|
index: './src/index.html',
|
||||||
|
browser: './src/main.ts',
|
||||||
|
polyfills: ['tslib'],
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
assets: [
|
||||||
|
'./src/favicon.ico',
|
||||||
|
'./src/assets',
|
||||||
|
{
|
||||||
|
input: './public',
|
||||||
|
glob: '**/*',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
styles: ['./src/styles.scss'],
|
||||||
|
scripts: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default webpackMerge(baseConfig[0], baseWebpackConfig);
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,466 @@
|
|||||||
|
import {
|
||||||
|
type Tree,
|
||||||
|
readProjectConfiguration,
|
||||||
|
addDependenciesToPackageJson,
|
||||||
|
formatFiles,
|
||||||
|
GeneratorCallback,
|
||||||
|
runTasksInSerial,
|
||||||
|
ensurePackage,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
workspaceRoot,
|
||||||
|
joinPathFragments,
|
||||||
|
readJson,
|
||||||
|
writeJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import type { ConvertToRspackSchema } from './schema';
|
||||||
|
import { angularRspackVersion, nxVersion } from '../../utils/versions';
|
||||||
|
import { createConfig } from './lib/create-config';
|
||||||
|
import { getCustomWebpackConfig } from './lib/get-custom-webpack-config';
|
||||||
|
import { updateTsconfig } from './lib/update-tsconfig';
|
||||||
|
import { validateSupportedBuildExecutor } from './lib/validate-supported-executor';
|
||||||
|
import { join } from 'path/posix';
|
||||||
|
import { relative } from 'path';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||||
|
import { prompt } from 'enquirer';
|
||||||
|
|
||||||
|
const SUPPORTED_EXECUTORS = [
|
||||||
|
'@angular-devkit/build-angular:browser',
|
||||||
|
'@angular-devkit/build-angular:dev-server',
|
||||||
|
'@nx/angular:webpack-browser',
|
||||||
|
'@nx/angular:dev-server',
|
||||||
|
'@nx/angular:module-federation-dev-server',
|
||||||
|
];
|
||||||
|
|
||||||
|
const RENAMED_OPTIONS = {
|
||||||
|
main: 'browser',
|
||||||
|
ngswConfigPath: 'serviceWorker',
|
||||||
|
};
|
||||||
|
|
||||||
|
const REMOVED_OPTIONS = [
|
||||||
|
'publicHost',
|
||||||
|
'disableHostCheck',
|
||||||
|
'resourcesOutputPath',
|
||||||
|
'routesFile',
|
||||||
|
'routes',
|
||||||
|
'discoverRoutes',
|
||||||
|
'appModuleBundle',
|
||||||
|
'inputIndexPath',
|
||||||
|
'outputIndexPath',
|
||||||
|
'buildOptimizer',
|
||||||
|
'deployUrl',
|
||||||
|
'buildTarget',
|
||||||
|
'browserTarget',
|
||||||
|
];
|
||||||
|
|
||||||
|
function normalizeFromProjectRoot(
|
||||||
|
tree: Tree,
|
||||||
|
path: string,
|
||||||
|
projectRoot: string
|
||||||
|
) {
|
||||||
|
if (projectRoot === '.') {
|
||||||
|
if (!path.startsWith('./')) {
|
||||||
|
return `./${path}`;
|
||||||
|
} else {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
} else if (path.startsWith(projectRoot)) {
|
||||||
|
return path.replace(projectRoot, '.');
|
||||||
|
} else if (!path.startsWith('./')) {
|
||||||
|
if (tree.exists(path)) {
|
||||||
|
const pathWithWorkspaceRoot = joinPathFragments(workspaceRoot, path);
|
||||||
|
const projectRootWithWorkspaceRoot = joinPathFragments(
|
||||||
|
workspaceRoot,
|
||||||
|
projectRoot
|
||||||
|
);
|
||||||
|
return relative(projectRootWithWorkspaceRoot, pathWithWorkspaceRoot);
|
||||||
|
}
|
||||||
|
return `./${path}`;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultNormalizer = (tree: Tree, path: string, root: string) =>
|
||||||
|
normalizeFromProjectRoot(tree, path, root);
|
||||||
|
|
||||||
|
const PATH_NORMALIZER = {
|
||||||
|
index: (
|
||||||
|
tree: Tree,
|
||||||
|
path: string | { input: string; output: string },
|
||||||
|
root: string
|
||||||
|
) => {
|
||||||
|
if (typeof path === 'string') {
|
||||||
|
return normalizeFromProjectRoot(tree, path, root);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
input: normalizeFromProjectRoot(tree, path.input, root),
|
||||||
|
output: path.output ?? 'index.html',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
indexHtmlTransformer: defaultNormalizer,
|
||||||
|
main: defaultNormalizer,
|
||||||
|
server: defaultNormalizer,
|
||||||
|
tsConfig: defaultNormalizer,
|
||||||
|
outputPath: (tree: Tree, path: string, root: string) => {
|
||||||
|
const relativePathFromWorkspaceRoot = relative(
|
||||||
|
joinPathFragments(workspaceRoot, root),
|
||||||
|
workspaceRoot
|
||||||
|
);
|
||||||
|
return joinPathFragments(relativePathFromWorkspaceRoot, path);
|
||||||
|
},
|
||||||
|
proxyConfig: defaultNormalizer,
|
||||||
|
polyfills: (tree: Tree, paths: string | string[], root: string) => {
|
||||||
|
const normalizedPaths: string[] = [];
|
||||||
|
const normalizeFn = (path: string) => {
|
||||||
|
try {
|
||||||
|
const resolvedPath = require.resolve(path, {
|
||||||
|
paths: [join(workspaceRoot, 'node_modules')],
|
||||||
|
});
|
||||||
|
normalizedPaths.push(path);
|
||||||
|
} catch {
|
||||||
|
normalizedPaths.push(normalizeFromProjectRoot(tree, path, root));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof paths === 'string') {
|
||||||
|
normalizeFn(paths);
|
||||||
|
} else {
|
||||||
|
for (const path of paths) {
|
||||||
|
normalizeFn(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalizedPaths;
|
||||||
|
},
|
||||||
|
styles: (
|
||||||
|
tree: Tree,
|
||||||
|
paths: Array<
|
||||||
|
string | { input: string; bundleName: string; inject: boolean }
|
||||||
|
>,
|
||||||
|
root: string
|
||||||
|
) => {
|
||||||
|
const normalizedPaths: Array<
|
||||||
|
string | { input: string; bundleName: string; inject: boolean }
|
||||||
|
> = [];
|
||||||
|
for (const path of paths) {
|
||||||
|
if (typeof path === 'string') {
|
||||||
|
normalizedPaths.push(normalizeFromProjectRoot(tree, path, root));
|
||||||
|
} else {
|
||||||
|
normalizedPaths.push({
|
||||||
|
input: normalizeFromProjectRoot(tree, path.input, root),
|
||||||
|
bundleName: path.bundleName,
|
||||||
|
inject: path.inject ?? true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalizedPaths;
|
||||||
|
},
|
||||||
|
scripts: (
|
||||||
|
tree: Tree,
|
||||||
|
paths: Array<
|
||||||
|
string | { input: string; bundleName: string; inject: boolean }
|
||||||
|
>,
|
||||||
|
root: string
|
||||||
|
) => {
|
||||||
|
const normalizedPaths: Array<
|
||||||
|
string | { input: string; bundleName: string; inject: boolean }
|
||||||
|
> = [];
|
||||||
|
for (const path of paths) {
|
||||||
|
if (typeof path === 'string') {
|
||||||
|
normalizedPaths.push(normalizeFromProjectRoot(tree, path, root));
|
||||||
|
} else {
|
||||||
|
normalizedPaths.push({
|
||||||
|
input: normalizeFromProjectRoot(tree, path.input, root),
|
||||||
|
bundleName: path.bundleName,
|
||||||
|
inject: path.inject ?? true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalizedPaths;
|
||||||
|
},
|
||||||
|
assets: (
|
||||||
|
tree: Tree,
|
||||||
|
paths: Array<string | { input: string; [key: string]: any }>,
|
||||||
|
root: string
|
||||||
|
) => {
|
||||||
|
const normalizedPaths: Array<
|
||||||
|
string | { input: string; [key: string]: any }
|
||||||
|
> = [];
|
||||||
|
for (const path of paths) {
|
||||||
|
if (typeof path === 'string') {
|
||||||
|
normalizedPaths.push(normalizeFromProjectRoot(tree, path, root));
|
||||||
|
} else {
|
||||||
|
normalizedPaths.push({
|
||||||
|
...path,
|
||||||
|
input: normalizeFromProjectRoot(tree, path.input, root),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalizedPaths;
|
||||||
|
},
|
||||||
|
fileReplacements: (
|
||||||
|
tree: Tree,
|
||||||
|
paths: Array<
|
||||||
|
{ replace: string; with: string } | { src: string; replaceWith: string }
|
||||||
|
>,
|
||||||
|
root: string
|
||||||
|
) => {
|
||||||
|
const normalizedPaths: Array<
|
||||||
|
{ replace: string; with: string } | { src: string; replaceWith: string }
|
||||||
|
> = [];
|
||||||
|
for (const path of paths) {
|
||||||
|
normalizedPaths.push({
|
||||||
|
replace: normalizeFromProjectRoot(
|
||||||
|
tree,
|
||||||
|
'src' in path ? path.src : path.replace,
|
||||||
|
root
|
||||||
|
),
|
||||||
|
with: normalizeFromProjectRoot(
|
||||||
|
tree,
|
||||||
|
'replaceWith' in path ? path.replaceWith : path.with,
|
||||||
|
root
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return normalizedPaths;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleBuildTargetOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: Record<string, any>,
|
||||||
|
newConfigurationOptions: Record<string, any>,
|
||||||
|
root: string
|
||||||
|
) {
|
||||||
|
let customWebpackConfigPath: string | undefined;
|
||||||
|
if (!options || Object.keys(options).length === 0) {
|
||||||
|
return customWebpackConfigPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.customWebpackConfig) {
|
||||||
|
customWebpackConfigPath = options.customWebpackConfig.path;
|
||||||
|
delete options.customWebpackConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.outputs) {
|
||||||
|
// handled by the Rspack inference plugin
|
||||||
|
delete options.outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(options)) {
|
||||||
|
let optionName = key;
|
||||||
|
let optionValue =
|
||||||
|
key in PATH_NORMALIZER ? PATH_NORMALIZER[key](tree, value, root) : value;
|
||||||
|
if (REMOVED_OPTIONS.includes(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (key in RENAMED_OPTIONS) {
|
||||||
|
optionName = RENAMED_OPTIONS[key];
|
||||||
|
}
|
||||||
|
newConfigurationOptions[optionName] = optionValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof newConfigurationOptions.polyfills === 'string') {
|
||||||
|
newConfigurationOptions.polyfills = [newConfigurationOptions.polyfills];
|
||||||
|
}
|
||||||
|
let outputPath = newConfigurationOptions.outputPath;
|
||||||
|
if (typeof outputPath === 'string') {
|
||||||
|
if (!/\/browser\/?$/.test(outputPath)) {
|
||||||
|
console.warn(
|
||||||
|
`The output location of the browser build has been updated from "${outputPath}" to ` +
|
||||||
|
`"${join(outputPath, 'browser')}". ` +
|
||||||
|
'You might need to adjust your deployment pipeline or, as an alternative, ' +
|
||||||
|
'set outputPath.browser to "" in order to maintain the previous functionality.'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
outputPath = outputPath.replace(/\/browser\/?$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfigurationOptions['outputPath'] = {
|
||||||
|
base: outputPath,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof newConfigurationOptions.resourcesOutputPath === 'string') {
|
||||||
|
const media = newConfigurationOptions.resourcesOutputPath.replaceAll(
|
||||||
|
'/',
|
||||||
|
''
|
||||||
|
);
|
||||||
|
if (media && media !== 'media') {
|
||||||
|
newConfigurationOptions['outputPath'] = {
|
||||||
|
base: outputPath,
|
||||||
|
media,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return customWebpackConfigPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDevServerTargetOptions(
|
||||||
|
tree: Tree,
|
||||||
|
options: Record<string, any>,
|
||||||
|
newConfigurationOptions: Record<string, any>,
|
||||||
|
root: string
|
||||||
|
) {
|
||||||
|
for (const [key, value] of Object.entries(options)) {
|
||||||
|
let optionName = key;
|
||||||
|
let optionValue =
|
||||||
|
key in PATH_NORMALIZER ? PATH_NORMALIZER[key](tree, value, root) : value;
|
||||||
|
if (REMOVED_OPTIONS.includes(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (key in RENAMED_OPTIONS) {
|
||||||
|
optionName = RENAMED_OPTIONS[key];
|
||||||
|
}
|
||||||
|
newConfigurationOptions[optionName] = optionValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getProjectToConvert(tree: Tree) {
|
||||||
|
const projects = new Set<string>();
|
||||||
|
for (const executor of SUPPORTED_EXECUTORS) {
|
||||||
|
forEachExecutorOptions(tree, executor, (_, project) => {
|
||||||
|
projects.add(project);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { project } = await prompt<{ project: string }>({
|
||||||
|
type: 'select',
|
||||||
|
name: 'project',
|
||||||
|
message: 'Which project would you like to convert to rspack?',
|
||||||
|
choices: Array.from(projects),
|
||||||
|
});
|
||||||
|
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function convertToRspack(
|
||||||
|
tree: Tree,
|
||||||
|
schema: ConvertToRspackSchema
|
||||||
|
) {
|
||||||
|
let { project: projectName } = schema;
|
||||||
|
if (!projectName) {
|
||||||
|
projectName = await getProjectToConvert(tree);
|
||||||
|
}
|
||||||
|
const project = readProjectConfiguration(tree, projectName);
|
||||||
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
|
||||||
|
const createConfigOptions: Record<string, any> = {
|
||||||
|
root: project.root,
|
||||||
|
};
|
||||||
|
const configurationOptions: Record<string, Record<string, any>> = {};
|
||||||
|
const buildTargetNames: string[] = [];
|
||||||
|
const serveTargetNames: string[] = [];
|
||||||
|
let customWebpackConfigPath: string | undefined;
|
||||||
|
|
||||||
|
validateSupportedBuildExecutor(Object.values(project.targets));
|
||||||
|
|
||||||
|
for (const [targetName, target] of Object.entries(project.targets)) {
|
||||||
|
if (
|
||||||
|
target.executor === '@angular-devkit/build-angular:browser' ||
|
||||||
|
target.executor === '@nx/angular:webpack-browser'
|
||||||
|
) {
|
||||||
|
customWebpackConfigPath = handleBuildTargetOptions(
|
||||||
|
tree,
|
||||||
|
target.options,
|
||||||
|
createConfigOptions,
|
||||||
|
project.root
|
||||||
|
);
|
||||||
|
if (target.configurations) {
|
||||||
|
for (const [configurationName, configuration] of Object.entries(
|
||||||
|
target.configurations
|
||||||
|
)) {
|
||||||
|
configurationOptions[configurationName] = {};
|
||||||
|
handleBuildTargetOptions(
|
||||||
|
tree,
|
||||||
|
configuration,
|
||||||
|
configurationOptions[configurationName],
|
||||||
|
project.root
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildTargetNames.push(targetName);
|
||||||
|
} else if (
|
||||||
|
target.executor === '@angular-devkit/build-angular:dev-server' ||
|
||||||
|
target.executor === '@nx/angular:dev-server' ||
|
||||||
|
target.executor === '@nx/angular:module-federation-dev-server'
|
||||||
|
) {
|
||||||
|
createConfigOptions.devServer = {};
|
||||||
|
if (target.options) {
|
||||||
|
handleDevServerTargetOptions(
|
||||||
|
tree,
|
||||||
|
target.options,
|
||||||
|
createConfigOptions.devServer,
|
||||||
|
project.root
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (target.configurations) {
|
||||||
|
for (const [configurationName, configuration] of Object.entries(
|
||||||
|
target.configurations
|
||||||
|
)) {
|
||||||
|
configurationOptions[configurationName] ??= {};
|
||||||
|
configurationOptions[configurationName].devServer ??= {};
|
||||||
|
handleDevServerTargetOptions(
|
||||||
|
tree,
|
||||||
|
configuration,
|
||||||
|
configurationOptions[configurationName].devServer,
|
||||||
|
project.root
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serveTargetNames.push(targetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const customWebpackConfigInfo = customWebpackConfigPath
|
||||||
|
? await getCustomWebpackConfig(tree, project.root, customWebpackConfigPath)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
createConfig(
|
||||||
|
tree,
|
||||||
|
createConfigOptions,
|
||||||
|
configurationOptions,
|
||||||
|
customWebpackConfigInfo?.normalizedPathToCustomWebpackConfig,
|
||||||
|
customWebpackConfigInfo?.isWebpackConfigFunction
|
||||||
|
);
|
||||||
|
updateTsconfig(tree, project.root);
|
||||||
|
|
||||||
|
for (const targetName of [...buildTargetNames, ...serveTargetNames]) {
|
||||||
|
delete project.targets[targetName];
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProjectConfiguration(tree, projectName, project);
|
||||||
|
|
||||||
|
const { rspackInitGenerator } = ensurePackage<typeof import('@nx/rspack')>(
|
||||||
|
'@nx/rspack',
|
||||||
|
nxVersion
|
||||||
|
);
|
||||||
|
|
||||||
|
await rspackInitGenerator(tree, {
|
||||||
|
addPlugin: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// This is needed to prevent a circular execution of the build target
|
||||||
|
const rootPkgJson = readJson(tree, 'package.json');
|
||||||
|
if (rootPkgJson.scripts?.build === 'nx build') {
|
||||||
|
delete rootPkgJson.scripts.build;
|
||||||
|
writeJson(tree, 'package.json', rootPkgJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!schema.skipInstall) {
|
||||||
|
const installTask = addDependenciesToPackageJson(
|
||||||
|
tree,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
'@nx/angular-rspack': angularRspackVersion,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
tasks.push(installTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!schema.skipFormat) {
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runTasksInSerial(...tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default convertToRspack;
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { createConfig } from './create-config';
|
||||||
|
|
||||||
|
describe('createConfig', () => {
|
||||||
|
it('should create a config file', () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const opts = {
|
||||||
|
root: 'root',
|
||||||
|
index: 'src/index.html',
|
||||||
|
browser: 'src/main.ts',
|
||||||
|
tsconfigPath: 'tsconfig.app.json',
|
||||||
|
polyfills: ['zone.js'],
|
||||||
|
assets: ['public'],
|
||||||
|
styles: ['src/styles.css'],
|
||||||
|
scripts: [],
|
||||||
|
jit: false,
|
||||||
|
inlineStylesExtension: 'css',
|
||||||
|
fileReplacements: [],
|
||||||
|
hasServer: false,
|
||||||
|
skipTypeChecking: false,
|
||||||
|
};
|
||||||
|
createConfig(tree, opts);
|
||||||
|
expect(tree.read('root/rspack.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
import { createConfig }from '@nx/angular-rspack';
|
||||||
|
|
||||||
|
|
||||||
|
export default createConfig({
|
||||||
|
options: {
|
||||||
|
root: __dirname,
|
||||||
|
|
||||||
|
"index": "src/index.html",
|
||||||
|
"browser": "src/main.ts",
|
||||||
|
"tsconfigPath": "tsconfig.app.json",
|
||||||
|
"polyfills": [
|
||||||
|
"zone.js"
|
||||||
|
],
|
||||||
|
"assets": [
|
||||||
|
"public"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"scripts": [],
|
||||||
|
"jit": false,
|
||||||
|
"inlineStylesExtension": "css",
|
||||||
|
"fileReplacements": [],
|
||||||
|
"hasServer": false,
|
||||||
|
"skipTypeChecking": false
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a config file with configurations', () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const opts = {
|
||||||
|
root: 'root',
|
||||||
|
index: 'src/index.html',
|
||||||
|
browser: 'src/main.ts',
|
||||||
|
};
|
||||||
|
const configurationOptions = {
|
||||||
|
production: {
|
||||||
|
index: 'src/index.prod.html',
|
||||||
|
browser: 'src/main.prod.ts',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
createConfig(tree, opts, configurationOptions);
|
||||||
|
expect(tree.read('root/rspack.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
import { createConfig }from '@nx/angular-rspack';
|
||||||
|
|
||||||
|
|
||||||
|
export default createConfig({
|
||||||
|
options: {
|
||||||
|
root: __dirname,
|
||||||
|
|
||||||
|
"index": "src/index.html",
|
||||||
|
"browser": "src/main.ts"
|
||||||
|
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
production: {
|
||||||
|
options: {
|
||||||
|
|
||||||
|
"index": "src/index.prod.html",
|
||||||
|
"browser": "src/main.prod.ts"
|
||||||
|
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import { joinPathFragments, type Tree } from '@nx/devkit';
|
||||||
|
|
||||||
|
export function createConfig(
|
||||||
|
tree: Tree,
|
||||||
|
opts: Record<string, any>,
|
||||||
|
configurationOptions: Record<string, Record<string, any>> = {},
|
||||||
|
existingWebpackConfigPath?: string,
|
||||||
|
isExistingWebpackConfigFunction?: boolean
|
||||||
|
) {
|
||||||
|
const { root, ...createConfigOptions } = opts;
|
||||||
|
const hasConfigurations = Object.keys(configurationOptions).length > 0;
|
||||||
|
const expandedConfigurationOptions = hasConfigurations
|
||||||
|
? Object.entries(configurationOptions)
|
||||||
|
.map(([configurationName, configurationOptions]) => {
|
||||||
|
return `
|
||||||
|
${configurationName}: {
|
||||||
|
options: {
|
||||||
|
${JSON.stringify(configurationOptions, undefined, 2).slice(1, -1)}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
})
|
||||||
|
.join(',\n')
|
||||||
|
: '';
|
||||||
|
const configContents = `
|
||||||
|
import { createConfig }from '@nx/angular-rspack';
|
||||||
|
${
|
||||||
|
existingWebpackConfigPath
|
||||||
|
? `import baseWebpackConfig from '${existingWebpackConfigPath}';
|
||||||
|
${
|
||||||
|
isExistingWebpackConfigFunction
|
||||||
|
? ''
|
||||||
|
: `import webpackMerge from 'webpack-merge';`
|
||||||
|
}`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
${
|
||||||
|
existingWebpackConfigPath ? 'const baseConfig = ' : 'export default '
|
||||||
|
}createConfig({
|
||||||
|
options: {
|
||||||
|
root: __dirname,
|
||||||
|
${JSON.stringify(createConfigOptions, undefined, 2).slice(1, -1)}
|
||||||
|
}
|
||||||
|
}${hasConfigurations ? `, {${expandedConfigurationOptions}}` : ''});
|
||||||
|
${
|
||||||
|
existingWebpackConfigPath
|
||||||
|
? `
|
||||||
|
export default ${
|
||||||
|
isExistingWebpackConfigFunction
|
||||||
|
? `async function (env, argv) {
|
||||||
|
const oldConfig = await baseWebpackConfig;
|
||||||
|
const browserConfig = baseConfig[0];
|
||||||
|
return oldConfig(browserConfig);
|
||||||
|
}`
|
||||||
|
: 'webpackMerge(baseConfig[0], baseWebpackConfig);'
|
||||||
|
}
|
||||||
|
`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
tree.write(joinPathFragments(root, 'rspack.config.ts'), configContents);
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { convertWebpackConfigToUseNxModuleFederationPlugin } from './get-custom-webpack-config';
|
||||||
|
|
||||||
|
describe('convertconvertWebpackConfigToUseNxModuleFederationPlugin', () => {
|
||||||
|
it('should convert a basic webpack config to use Nx Module Federation Plugin', () => {
|
||||||
|
// ARRANGE
|
||||||
|
const webpackConfigContents = `
|
||||||
|
import { withModuleFederation } from '@nx/module-federation/angular';
|
||||||
|
import config from './module-federation.config';
|
||||||
|
export default withModuleFederation(config, { dts: false });
|
||||||
|
`;
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
const newWebpackConfigContents =
|
||||||
|
convertWebpackConfigToUseNxModuleFederationPlugin(webpackConfigContents);
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(newWebpackConfigContents).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
import { NxModuleFederationPlugin } from '@nx/module-federation/rspack';
|
||||||
|
import config from './module-federation.config';
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [
|
||||||
|
new NxModuleFederationPlugin(config, {
|
||||||
|
dts: false,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert a basic cjs webpack config to use Nx Module Federation Plugin', () => {
|
||||||
|
// ARRANGE
|
||||||
|
const webpackConfigContents = `
|
||||||
|
const { withModuleFederation } = require('@nx/module-federation/angular');
|
||||||
|
const config = require('./module-federation.config');
|
||||||
|
module.exports = withModuleFederation(config, { dts: false });
|
||||||
|
`;
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
const newWebpackConfigContents =
|
||||||
|
convertWebpackConfigToUseNxModuleFederationPlugin(webpackConfigContents);
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(newWebpackConfigContents).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
const { NxModuleFederationPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const config = require('./module-federation.config');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
new NxModuleFederationPlugin({ config }, {
|
||||||
|
dts: false,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
import { logger, Tree } from '@nx/devkit';
|
||||||
|
import { loadConfigFile } from '@nx/devkit/src/utils/config-utils';
|
||||||
|
import { join, relative } from 'path';
|
||||||
|
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||||
|
|
||||||
|
const FILE_EXTENSION_REGEX = /\.[^.]+$/;
|
||||||
|
|
||||||
|
export async function getCustomWebpackConfig(
|
||||||
|
tree: Tree,
|
||||||
|
projectRoot: string,
|
||||||
|
pathToCustomWebpackConfig: string
|
||||||
|
) {
|
||||||
|
const webpackConfigContents = tree.read(pathToCustomWebpackConfig, 'utf-8');
|
||||||
|
if (
|
||||||
|
webpackConfigContents.includes('@nx/module-federation/angular') &&
|
||||||
|
webpackConfigContents.includes('withModuleFederation')
|
||||||
|
) {
|
||||||
|
tree.write(
|
||||||
|
pathToCustomWebpackConfig,
|
||||||
|
convertWebpackConfigToUseNxModuleFederationPlugin(webpackConfigContents)
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
isWebpackConfigFunction: false,
|
||||||
|
normalizedPathToCustomWebpackConfig: `./${relative(
|
||||||
|
projectRoot,
|
||||||
|
pathToCustomWebpackConfig
|
||||||
|
).replace(FILE_EXTENSION_REGEX, '')}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const configFile = await loadConfigFile(
|
||||||
|
join(tree.root, pathToCustomWebpackConfig)
|
||||||
|
);
|
||||||
|
const webpackConfig =
|
||||||
|
'default' in configFile ? configFile.default : configFile;
|
||||||
|
return {
|
||||||
|
isWebpackConfigFunction: typeof webpackConfig === 'function',
|
||||||
|
normalizedPathToCustomWebpackConfig: `./${relative(
|
||||||
|
projectRoot,
|
||||||
|
pathToCustomWebpackConfig
|
||||||
|
).replace(FILE_EXTENSION_REGEX, '')}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertWebpackConfigToUseNxModuleFederationPlugin(
|
||||||
|
webpackConfigContents: string
|
||||||
|
): string {
|
||||||
|
let newWebpackConfigContents = webpackConfigContents;
|
||||||
|
let ast = tsquery.ast(webpackConfigContents);
|
||||||
|
|
||||||
|
const withModuleFederationImportNodes = tsquery(
|
||||||
|
ast,
|
||||||
|
'ImportDeclaration:has(StringLiteral[value=@nx/module-federation/angular])'
|
||||||
|
);
|
||||||
|
if (withModuleFederationImportNodes.length > 0) {
|
||||||
|
const withModuleFederationImportNode = withModuleFederationImportNodes[0];
|
||||||
|
newWebpackConfigContents = `${webpackConfigContents.slice(
|
||||||
|
0,
|
||||||
|
withModuleFederationImportNode.getStart()
|
||||||
|
)}import { NxModuleFederationPlugin } from '@nx/module-federation/rspack';${webpackConfigContents.slice(
|
||||||
|
withModuleFederationImportNode.getEnd()
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
ast = tsquery.ast(newWebpackConfigContents);
|
||||||
|
const exportedWithModuleFederationNodes = tsquery(
|
||||||
|
ast,
|
||||||
|
'ExportAssignment:has(CallExpression > Identifier[name=withModuleFederation])'
|
||||||
|
);
|
||||||
|
if (exportedWithModuleFederationNodes.length > 0) {
|
||||||
|
const exportedWithModuleFederationNode =
|
||||||
|
exportedWithModuleFederationNodes[0];
|
||||||
|
newWebpackConfigContents = `${newWebpackConfigContents.slice(
|
||||||
|
0,
|
||||||
|
exportedWithModuleFederationNode.getStart()
|
||||||
|
)}${newWebpackConfigContents.slice(
|
||||||
|
exportedWithModuleFederationNode.getEnd()
|
||||||
|
)}
|
||||||
|
export default {
|
||||||
|
plugins: [
|
||||||
|
new NxModuleFederationPlugin(config, {
|
||||||
|
dts: false,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
"Could not find 'export default withModuleFederation' in the webpack config file. Skipping conversion."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const withModuleFederationRequireNodes = tsquery(
|
||||||
|
ast,
|
||||||
|
'VariableStatement:has(CallExpression > Identifier[name=withModuleFederation], StringLiteral[value=@nx/module-federation/angular])'
|
||||||
|
);
|
||||||
|
if (withModuleFederationRequireNodes.length > 0) {
|
||||||
|
const withModuleFederationRequireNode = withModuleFederationRequireNodes[0];
|
||||||
|
newWebpackConfigContents = `${webpackConfigContents.slice(
|
||||||
|
0,
|
||||||
|
withModuleFederationRequireNode.getStart()
|
||||||
|
)}const { NxModuleFederationPlugin } = require('@nx/module-federation/rspack');${webpackConfigContents.slice(
|
||||||
|
withModuleFederationRequireNode.getEnd()
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
ast = tsquery.ast(newWebpackConfigContents);
|
||||||
|
const exportedWithModuleFederationNodes = tsquery(
|
||||||
|
ast,
|
||||||
|
'ExpressionStatement:has(BinaryExpression > PropertyAccessExpression:has(Identifier[name=module], Identifier[name=exports]), CallExpression:has(Identifier[name=withModuleFederation]))'
|
||||||
|
);
|
||||||
|
if (exportedWithModuleFederationNodes.length > 0) {
|
||||||
|
const exportedWithModuleFederationNode =
|
||||||
|
exportedWithModuleFederationNodes[0];
|
||||||
|
newWebpackConfigContents = `${newWebpackConfigContents.slice(
|
||||||
|
0,
|
||||||
|
exportedWithModuleFederationNode.getStart()
|
||||||
|
)}${newWebpackConfigContents.slice(
|
||||||
|
exportedWithModuleFederationNode.getEnd()
|
||||||
|
)}
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
new NxModuleFederationPlugin({ config }, {
|
||||||
|
dts: false,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
"Could not find 'module.exports = withModuleFederation' in the webpack config file. Skipping conversion."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newWebpackConfigContents;
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { joinPathFragments, readJson, Tree, writeJson } from '@nx/devkit';
|
||||||
|
|
||||||
|
export function updateTsconfig(tree: Tree, projectRoot: string) {
|
||||||
|
const tsconfigPath = joinPathFragments(projectRoot, 'tsconfig.json');
|
||||||
|
const tsconfig = readJson(tree, tsconfigPath);
|
||||||
|
tsconfig['ts-node'] = {
|
||||||
|
compilerOptions: {
|
||||||
|
module: 'CommonJS',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
writeJson(tree, tsconfigPath, tsconfig);
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { TargetConfiguration } from '@nx/devkit';
|
||||||
|
|
||||||
|
const SUPPORTED_BUILD_EXECUTORS = [
|
||||||
|
'@angular-devkit/build-angular:browser',
|
||||||
|
'@nx/angular:webpack-browser',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function validateSupportedBuildExecutor(targets: TargetConfiguration[]) {
|
||||||
|
const executorsUsedByProject = targets.map((target) => target.executor);
|
||||||
|
if (
|
||||||
|
!executorsUsedByProject.some((executor) =>
|
||||||
|
SUPPORTED_BUILD_EXECUTORS.includes(executor)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
'The project does not use a supported build executor. Please use one of the following executors: ' +
|
||||||
|
SUPPORTED_BUILD_EXECUTORS.join(', ')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/angular/src/generators/convert-to-rspack/schema.d.ts
vendored
Normal file
5
packages/angular/src/generators/convert-to-rspack/schema.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export interface ConvertToRspackSchema {
|
||||||
|
project: string;
|
||||||
|
skipFormat?: boolean;
|
||||||
|
skipInstall?: boolean;
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "GeneratorNxApp",
|
||||||
|
"title": "Creates an Angular application.",
|
||||||
|
"description": "Creates an Angular application.",
|
||||||
|
"type": "object",
|
||||||
|
"cli": "nx",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"aliases": ["name", "projectName"],
|
||||||
|
"description": "Project for which to convert to rspack.",
|
||||||
|
"$default": {
|
||||||
|
"$source": "argv",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"description": "Skip formatting files.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"skipInstall": {
|
||||||
|
"description": "Skip installing dependencies.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,6 +28,7 @@ export const backwardCompatibleVersions: VersionMap = {
|
|||||||
angularVersion: '~17.3.0',
|
angularVersion: '~17.3.0',
|
||||||
angularDevkitVersion: '~17.3.0',
|
angularDevkitVersion: '~17.3.0',
|
||||||
ngPackagrVersion: '~17.3.0',
|
ngPackagrVersion: '~17.3.0',
|
||||||
|
angularRspackVersion: '~20.6.1',
|
||||||
ngrxVersion: '~17.0.0',
|
ngrxVersion: '~17.0.0',
|
||||||
rxjsVersion: '~7.8.0',
|
rxjsVersion: '~7.8.0',
|
||||||
zoneJsVersion: '~0.14.3',
|
zoneJsVersion: '~0.14.3',
|
||||||
@ -55,6 +56,7 @@ export const backwardCompatibleVersions: VersionMap = {
|
|||||||
angularVersion: '~18.2.0',
|
angularVersion: '~18.2.0',
|
||||||
angularDevkitVersion: '~18.2.0',
|
angularDevkitVersion: '~18.2.0',
|
||||||
ngPackagrVersion: '~18.2.0',
|
ngPackagrVersion: '~18.2.0',
|
||||||
|
angularRspackVersion: '~20.6.1',
|
||||||
ngrxVersion: '~18.0.2',
|
ngrxVersion: '~18.0.2',
|
||||||
rxjsVersion: '~7.8.0',
|
rxjsVersion: '~7.8.0',
|
||||||
zoneJsVersion: '~0.14.3',
|
zoneJsVersion: '~0.14.3',
|
||||||
|
|||||||
@ -3,6 +3,7 @@ export const nxVersion = require('../../package.json').version;
|
|||||||
export const angularVersion = '~19.2.0';
|
export const angularVersion = '~19.2.0';
|
||||||
export const angularDevkitVersion = '~19.2.0';
|
export const angularDevkitVersion = '~19.2.0';
|
||||||
export const ngPackagrVersion = '~19.2.0';
|
export const ngPackagrVersion = '~19.2.0';
|
||||||
|
export const angularRspackVersion = '~20.6.1';
|
||||||
export const ngrxVersion = '^19.0.0';
|
export const ngrxVersion = '^19.0.0';
|
||||||
export const rxjsVersion = '~7.8.0';
|
export const rxjsVersion = '~7.8.0';
|
||||||
export const zoneJsVersion = '~0.15.0';
|
export const zoneJsVersion = '~0.15.0';
|
||||||
|
|||||||
35
pnpm-lock.yaml
generated
35
pnpm-lock.yaml
generated
@ -171,7 +171,7 @@ importers:
|
|||||||
version: 0.1902.0(chokidar@3.6.0)
|
version: 0.1902.0(chokidar@3.6.0)
|
||||||
'@angular-devkit/build-angular':
|
'@angular-devkit/build-angular':
|
||||||
specifier: 19.2.0
|
specifier: 19.2.0
|
||||||
version: 19.2.0(rggnxzdz2sowtz4b5zhppsknj4)
|
version: 19.2.0(35iavymh3xczxvipjxbozhxvvq)
|
||||||
'@angular-devkit/core':
|
'@angular-devkit/core':
|
||||||
specifier: 19.2.0
|
specifier: 19.2.0
|
||||||
version: 19.2.0(chokidar@3.6.0)
|
version: 19.2.0(chokidar@3.6.0)
|
||||||
@ -297,7 +297,7 @@ importers:
|
|||||||
version: 3.13.2(rollup@4.22.0)(webpack-sources@3.2.3)
|
version: 3.13.2(rollup@4.22.0)(webpack-sources@3.2.3)
|
||||||
'@nx/angular':
|
'@nx/angular':
|
||||||
specifier: 20.5.0-rc.4
|
specifier: 20.5.0-rc.4
|
||||||
version: 20.5.0-rc.4(6bzzb6mump3mw6fvztrlsbt2rq)
|
version: 20.5.0-rc.4(gup3gk2idig5qjq722krlimspe)
|
||||||
'@nx/conformance':
|
'@nx/conformance':
|
||||||
specifier: 1.3.0-beta.7
|
specifier: 1.3.0-beta.7
|
||||||
version: 1.3.0-beta.7(@nx/js@20.5.0-rc.4(@babel/traverse@7.26.9)(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(nx@20.5.0-rc.4(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.11)))(typescript@5.7.3)(verdaccio@6.0.5(encoding@0.1.13)(typanion@3.14.0)))(nx@20.5.0-rc.4(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.11)))
|
version: 1.3.0-beta.7(@nx/js@20.5.0-rc.4(@babel/traverse@7.26.9)(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(nx@20.5.0-rc.4(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.11)))(typescript@5.7.3)(verdaccio@6.0.5(encoding@0.1.13)(typanion@3.14.0)))(nx@20.5.0-rc.4(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.11)))
|
||||||
@ -1384,6 +1384,17 @@ packages:
|
|||||||
'@angular/platform-browser': 19.2.0
|
'@angular/platform-browser': 19.2.0
|
||||||
rxjs: ^6.5.3 || ^7.4.0
|
rxjs: ^6.5.3 || ^7.4.0
|
||||||
|
|
||||||
|
'@angular/ssr@19.2.0':
|
||||||
|
resolution: {integrity: sha512-2Tnunv0FDMxf5KHpxnxHhxQ5Xd+4qZ+fGBBbvH2njO5tDdFab6QxTRpKH9xWNiLjAqVIarytmTsuYtpchRmnaw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@angular/common': ^19.0.0 || ^19.2.0-next.0
|
||||||
|
'@angular/core': ^19.0.0 || ^19.2.0-next.0
|
||||||
|
'@angular/platform-server': ^19.0.0 || ^19.2.0-next.0
|
||||||
|
'@angular/router': ^19.0.0 || ^19.2.0-next.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@angular/platform-server':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@antfu/utils@0.7.10':
|
'@antfu/utils@0.7.10':
|
||||||
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
|
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
|
||||||
|
|
||||||
@ -18457,13 +18468,13 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- chokidar
|
- chokidar
|
||||||
|
|
||||||
'@angular-devkit/build-angular@19.2.0(rggnxzdz2sowtz4b5zhppsknj4)':
|
'@angular-devkit/build-angular@19.2.0(35iavymh3xczxvipjxbozhxvvq)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
'@angular-devkit/architect': 0.1902.0(chokidar@3.6.0)
|
'@angular-devkit/architect': 0.1902.0(chokidar@3.6.0)
|
||||||
'@angular-devkit/build-webpack': 0.1902.0(chokidar@3.6.0)(webpack-dev-server@5.2.0(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))))(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0)))
|
'@angular-devkit/build-webpack': 0.1902.0(chokidar@3.6.0)(webpack-dev-server@5.2.0(bufferutil@4.0.7)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0))))(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0)))
|
||||||
'@angular-devkit/core': 19.2.0(chokidar@3.6.0)
|
'@angular-devkit/core': 19.2.0(chokidar@3.6.0)
|
||||||
'@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(typescript@5.7.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(@types/node@20.16.10)(chokidar@3.6.0)(jiti@1.21.6)(less@4.2.2)(ng-packagr@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(typescript@5.7.3))(tailwindcss@3.4.4(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.7.3)))(tslib@2.7.0)(typescript@5.7.3))(postcss@8.5.2)(sass-embedded@1.85.1)(stylus@0.64.0)(tailwindcss@3.4.4(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.7.3)))(terser@5.39.0)(typescript@5.7.3)(yaml@2.6.1)
|
'@angular/build': 19.2.0(cxjrqumqlcnastohrhciqzxvse)
|
||||||
'@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(typescript@5.7.3)
|
'@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(typescript@5.7.3)
|
||||||
'@babel/core': 7.26.9
|
'@babel/core': 7.26.9
|
||||||
'@babel/generator': 7.26.9
|
'@babel/generator': 7.26.9
|
||||||
@ -18517,6 +18528,7 @@ snapshots:
|
|||||||
webpack-merge: 6.0.1
|
webpack-merge: 6.0.1
|
||||||
webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.5.0(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.25.0)(webpack-cli@5.1.4)))(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0)))
|
webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.5.0(webpack@5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.25.0)(webpack-cli@5.1.4)))(webpack@5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.0.4)(webpack@5.88.0)))
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
'@angular/ssr': 19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(@angular/router@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(@angular/platform-browser@18.2.5(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(rxjs@7.8.1))
|
||||||
esbuild: 0.25.0
|
esbuild: 0.25.0
|
||||||
jest: 29.7.0(@types/node@20.16.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.7.3))
|
jest: 29.7.0(@types/node@20.16.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.7.3))
|
||||||
jest-environment-jsdom: 29.7.0(bufferutil@4.0.7)
|
jest-environment-jsdom: 29.7.0(bufferutil@4.0.7)
|
||||||
@ -18687,7 +18699,7 @@ snapshots:
|
|||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
typescript: 5.7.3
|
typescript: 5.7.3
|
||||||
|
|
||||||
'@angular/build@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(typescript@5.7.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(@types/node@20.16.10)(chokidar@3.6.0)(jiti@1.21.6)(less@4.2.2)(ng-packagr@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(typescript@5.7.3))(tailwindcss@3.4.4(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.7.3)))(tslib@2.7.0)(typescript@5.7.3))(postcss@8.5.2)(sass-embedded@1.85.1)(stylus@0.64.0)(tailwindcss@3.4.4(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.7.3)))(terser@5.39.0)(typescript@5.7.3)(yaml@2.6.1)':
|
'@angular/build@19.2.0(cxjrqumqlcnastohrhciqzxvse)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
'@angular-devkit/architect': 0.1902.0(chokidar@3.6.0)
|
'@angular-devkit/architect': 0.1902.0(chokidar@3.6.0)
|
||||||
@ -18719,6 +18731,7 @@ snapshots:
|
|||||||
vite: 6.1.0(@types/node@20.16.10)(jiti@1.21.6)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.6.1)
|
vite: 6.1.0(@types/node@20.16.10)(jiti@1.21.6)(less@4.2.2)(sass-embedded@1.85.1)(sass@1.85.0)(stylus@0.64.0)(terser@5.39.0)(yaml@2.6.1)
|
||||||
watchpack: 2.4.2
|
watchpack: 2.4.2
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
'@angular/ssr': 19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(@angular/router@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(@angular/platform-browser@18.2.5(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(rxjs@7.8.1))
|
||||||
less: 4.2.2
|
less: 4.2.2
|
||||||
lmdb: 3.2.6
|
lmdb: 3.2.6
|
||||||
ng-packagr: 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(typescript@5.7.3))(tailwindcss@3.4.4(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.7.3)))(tslib@2.7.0)(typescript@5.7.3)
|
ng-packagr: 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(typescript@5.7.3))(tailwindcss@3.4.4(ts-node@10.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@types/node@20.16.10)(typescript@5.7.3)))(tslib@2.7.0)(typescript@5.7.3)
|
||||||
@ -18809,6 +18822,14 @@ snapshots:
|
|||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@angular/ssr@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(@angular/router@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(@angular/platform-browser@18.2.5(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(rxjs@7.8.1))':
|
||||||
|
dependencies:
|
||||||
|
'@angular/common': 19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1)
|
||||||
|
'@angular/core': 19.2.0(rxjs@7.8.1)(zone.js@0.14.10)
|
||||||
|
'@angular/router': 19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(@angular/platform-browser@18.2.5(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10))(rxjs@7.8.1))(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.14.10)))(rxjs@7.8.1)
|
||||||
|
tslib: 2.8.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@antfu/utils@0.7.10': {}
|
'@antfu/utils@0.7.10': {}
|
||||||
|
|
||||||
'@apidevtools/json-schema-ref-parser@9.0.9':
|
'@apidevtools/json-schema-ref-parser@9.0.9':
|
||||||
@ -23434,9 +23455,9 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
|
|
||||||
'@nx/angular@20.5.0-rc.4(6bzzb6mump3mw6fvztrlsbt2rq)':
|
'@nx/angular@20.5.0-rc.4(gup3gk2idig5qjq722krlimspe)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@angular-devkit/build-angular': 19.2.0(rggnxzdz2sowtz4b5zhppsknj4)
|
'@angular-devkit/build-angular': 19.2.0(35iavymh3xczxvipjxbozhxvvq)
|
||||||
'@angular-devkit/core': 19.2.0(chokidar@3.6.0)
|
'@angular-devkit/core': 19.2.0(chokidar@3.6.0)
|
||||||
'@angular-devkit/schematics': 19.2.0(chokidar@3.6.0)
|
'@angular-devkit/schematics': 19.2.0(chokidar@3.6.0)
|
||||||
'@nx/devkit': 20.5.0-rc.4(nx@20.5.0-rc.4(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.11)))
|
'@nx/devkit': 20.5.0-rc.4(nx@20.5.0-rc.4(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.7.3))(@swc/core@1.5.7(@swc/helpers@0.5.11)))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user