cleanup(angular): move the angular cli migration generator from @nrwl/workspace to @nrwl/angular (#9244)

* cleanup(angular): move the angular cli migration generator from @nrwl/workspace to @nrwl/angular

* cleanup(angular): support merged packages (cli, tao, nx)

* cleanup(angular): update make-angular-cli-faster to support packages consolidation
This commit is contained in:
Leosvel Pérez Espinosa 2022-03-18 21:18:10 +00:00 committed by GitHub
parent 3824eebc23
commit 88a7ad7654
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 782 additions and 264 deletions

View File

@ -157,12 +157,28 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
npmScope: 'projscope',
affected: { defaultBase: 'main' },
implicitDependencies: {
'angular.json': '*',
'package.json': '*',
'tslint.json': '*',
'package.json': {
dependencies: '*',
devDependencies: '*',
},
'.eslintrc.json': '*',
'tsconfig.base.json': '*',
'nx.json': '*',
},
tasksRunnerOptions: {
default: {
runner: '@nrwl/workspace/tasks-runners/default',
options: {
cacheableOperations: ['build', 'lint', 'test', 'e2e'],
},
},
},
targetDependencies: {
build: [
{
target: 'build',
projects: 'dependencies',
},
],
},
cli: { defaultCollection: '@nrwl/angular', packageManager },
defaultProject: project,

View File

@ -125,8 +125,8 @@ describe('list', () => {
// check for schematics
expect(listOutput).toContain('workspace');
expect(listOutput).toContain('ng-add');
expect(listOutput).toContain('library');
expect(listOutput).toContain('workspace-generator');
// check for builders
expect(listOutput).toContain('run-commands');

View File

@ -422,8 +422,8 @@ export function runNgAdd(
}
): string {
try {
packageInstall('@nrwl/workspace');
return execSync(`npx ng add @nrwl/workspace ${command}`, {
packageInstall('@nrwl/angular');
return execSync(`npx ng add @nrwl/angular ${command}`, {
cwd: tmpProjPath(),
env: { ...(opts.env || process.env), NX_INVOKED_BY_RUNNER: undefined },
encoding: 'utf-8',

View File

@ -47,7 +47,6 @@
"factory": "./src/generators/init/init.compat#initSchematic",
"schema": "./src/generators/init/schema.json",
"description": "Initializes the @nrwl/angular plugin.",
"aliases": ["ng-add"],
"hidden": true
},
"karma": {
@ -91,6 +90,12 @@
"aliases": ["host"],
"description": "Generate a Host Angular Micro Frontend Application."
},
"ng-add": {
"factory": "./src/generators/ng-add/compat",
"schema": "./src/generators/ng-add/schema.json",
"description": "Migrates an Angular CLI workspace to Nx or adds the Angular plugin to an Nx workspace.",
"hidden": true
},
"ngrx": {
"factory": "./src/generators/ngrx/compat",
"schema": "./src/generators/ngrx/schema.json",
@ -197,7 +202,6 @@
"factory": "./src/generators/init/init",
"schema": "./src/generators/init/schema.json",
"description": "Initializes the @nrwl/angular plugin.",
"aliases": ["ng-add"],
"hidden": true
},
"karma": {
@ -241,6 +245,12 @@
"aliases": ["host"],
"description": "Generate a Host Angular Micro Frontend Application."
},
"ng-add": {
"factory": "./src/generators/ng-add/ng-add",
"schema": "./src/generators/ng-add/schema.json",
"description": "Migrates an Angular CLI workspace to Nx or adds the Angular plugin to an Nx workspace.",
"hidden": true
},
"ngrx": {
"factory": "./src/generators/ngrx/ngrx",
"schema": "./src/generators/ngrx/schema.json",

View File

@ -8,6 +8,7 @@ import {
updateWorkspaceConfiguration,
} from '@nrwl/devkit';
import { jestInitGenerator } from '@nrwl/jest';
import { Linter } from '@nrwl/linter';
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
import { setDefaultCollection } from '@nrwl/workspace/src/utilities/set-default-collection';
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
@ -22,8 +23,9 @@ import { Schema } from './schema';
export async function angularInitGenerator(
host: Tree,
options: Schema
rawOptions: Schema
): Promise<GeneratorCallback> {
const options = normalizeOptions(rawOptions);
setDefaults(host, options);
addPostInstall(host);
@ -41,6 +43,18 @@ export async function angularInitGenerator(
return runTasksInSerial(depsTask, unitTestTask, e2eTask);
}
function normalizeOptions(options: Schema): Required<Schema> {
return {
e2eTestRunner: options.e2eTestRunner ?? E2eTestRunner.Cypress,
linter: options.linter ?? Linter.EsLint,
skipFormat: options.skipFormat ?? false,
skipInstall: options.skipInstall ?? false,
skipPackageJson: options.skipPackageJson ?? false,
style: options.style ?? 'css',
unitTestRunner: options.unitTestRunner ?? UnitTestRunner.Jest,
};
}
function setDefaults(host: Tree, options: Schema) {
const workspace = readWorkspaceConfiguration(host);

View File

@ -3,11 +3,11 @@ import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
import type { Styles } from '../utils/types';
export interface Schema {
unitTestRunner: UnitTestRunner;
unitTestRunner?: UnitTestRunner;
e2eTestRunner?: E2eTestRunner;
skipFormat?: boolean;
skipInstall?: boolean;
style?: Styles;
linter: Exclude<Linter, Linter.TsLint>;
linter?: Exclude<Linter, Linter.TsLint>;
skipPackageJson?: boolean;
}

View File

@ -38,6 +38,7 @@
"description": "The file extension to be used for style files.",
"type": "string",
"default": "css",
"enum": ["css", "scss", "sass", "less"],
"x-prompt": {
"message": "Which stylesheet format would you like to use?",
"type": "list",
@ -50,6 +51,10 @@
"value": "scss",
"label": "SASS(.scss) [ http://sass-lang.com ]"
},
{
"value": "sass",
"label": "SASS(.sass) [ http://sass-lang.com ]"
},
{
"value": "less",
"label": "LESS [ http://lesscss.org ]"

View File

@ -167,13 +167,34 @@ Object {
"defaultProject": "myApp",
"implicitDependencies": Object {
".eslintrc.json": "*",
"angular.json": "*",
"nx.json": "*",
"package.json": "*",
"package.json": Object {
"dependencies": "*",
"devDependencies": "*",
},
"tsconfig.base.json": "*",
"tslint.json": "*",
},
"npmScope": "my-app",
"targetDependencies": Object {
"build": Array [
Object {
"projects": "dependencies",
"target": "build",
},
],
},
"tasksRunnerOptions": Object {
"default": Object {
"options": Object {
"cacheableOperations": Array [
"build",
"lint",
"test",
"e2e",
],
},
"runner": "@nrwl/workspace/tasks-runners/default",
},
},
}
`;

View File

@ -0,0 +1,4 @@
import { convertNxGenerator } from '@nrwl/devkit';
import { ngAddGenerator } from './ng-add';
export default convertNxGenerator(ngAddGenerator);

View File

@ -0,0 +1,69 @@
/**
* This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching
* and faster execution of tasks.
*
* It does this by:
*
* - Patching the Angular CLI to warn you in case you accidentally use the undecorated ng command.
* - Symlinking the ng to nx command, so all commands run through the Nx CLI
* - Updating the package.json postinstall script to give you control over this script
*
* The Nx CLI decorates the Angular CLI, so the Nx CLI is fully compatible with it.
* Every command you run should work the same when using the Nx CLI, except faster.
*
* Because of symlinking you can still type `ng build/test/lint` in the terminal. The ng command, in this case,
* will point to nx, which will perform optimizations before invoking ng. So the Angular CLI is always invoked.
* The Nx CLI simply does some optimizations before invoking the Angular CLI.
*
* To opt out of this patch:
* - Replace occurrences of nx with ng in your package.json
* - Remove the script from your postinstall script in your package.json
* - Delete and reinstall your node_modules
*/
const fs = require('fs');
const os = require('os');
const cp = require('child_process');
const isWindows = os.platform() === 'win32';
let output;
try {
output = require('@nrwl/workspace').output;
} catch (e) {
console.warn('Angular CLI could not be decorated to enable computation caching. Please ensure @nrwl/workspace is installed.');
process.exit(0);
}
/**
* Symlink of ng to nx, so you can keep using `ng build/test/lint` and still
* invoke the Nx CLI and get the benefits of computation caching.
*/
function symlinkNgCLItoNxCLI() {
try {
const ngPath = './node_modules/.bin/ng';
const nxPath = './node_modules/.bin/nx';
if (isWindows) {
/**
* This is the most reliable way to create symlink-like behavior on Windows.
* Such that it works in all shells and works with npx.
*/
['', '.cmd', '.ps1'].forEach(ext => {
if (fs.existsSync(nxPath + ext)) fs.writeFileSync(ngPath + ext, fs.readFileSync(nxPath + ext));
});
} else {
// If unix-based, symlink
cp.execSync(`ln -sf ./nx ${ngPath}`);
}
}
catch(e) {
output.error({ title: 'Unable to create a symlink from the Angular CLI to the Nx CLI:' + e.message });
throw e;
}
}
try {
symlinkNgCLItoNxCLI();
require('nx/src/cli/decorate-cli').decorateCli();
output.log({ title: 'Angular CLI has been decorated to enable computation caching.' });
} catch(e) {
output.error({ title: 'Decoration of the Angular CLI did not complete successfully' });
}

View File

@ -0,0 +1,29 @@
{
"npmScope": "<%= npmScope %>",
"affected": {
"defaultBase": "<%= defaultBase %>"
},
"implicitDependencies": {
"package.json": {
"dependencies": "*",
"devDependencies": "*"
},
".eslintrc.json": "*"
},
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/workspace/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"]
}
}
},
"targetDependencies": {
"build": [
{
"target": "build",
"projects": "dependencies"
}
]
}
}

View File

@ -6,7 +6,7 @@ import {
} from '@nrwl/devkit';
import { createTree } from '@nrwl/devkit/testing';
import { initGenerator } from './init';
import { migrateFromAngularCli } from './migrate-from-angular-cli';
describe('workspace', () => {
let tree: Tree;
@ -77,7 +77,7 @@ describe('workspace', () => {
it('should error if no package.json is present', async () => {
tree.delete('package.json');
try {
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
fail('should throw');
} catch (e) {
expect(e.message).toContain('Cannot find package.json');
@ -88,7 +88,7 @@ describe('workspace', () => {
tree.delete('/e2e/protractor.conf.js');
try {
await initGenerator(tree, { name: 'proj1' });
await migrateFromAngularCli(tree, { name: 'proj1' });
} catch (e) {
expect(e.message).toContain(
'An e2e project with Protractor was found but "e2e/protractor.conf.js" could not be found.'
@ -101,7 +101,9 @@ describe('workspace', () => {
project.targets.e2e.executor = '@cypress/schematic:cypress';
updateProjectConfiguration(tree, 'myApp', project);
await expect(initGenerator(tree, { name: 'myApp' })).rejects.toThrow(
await expect(
migrateFromAngularCli(tree, { name: 'myApp' })
).rejects.toThrow(
'An e2e project with Cypress was found but "cypress.json" could not be found.'
);
});
@ -116,7 +118,9 @@ describe('workspace', () => {
};
updateProjectConfiguration(tree, 'myApp', project);
await expect(initGenerator(tree, { name: 'myApp' })).rejects.toThrow(
await expect(
migrateFromAngularCli(tree, { name: 'myApp' })
).rejects.toThrow(
'An e2e project with Cypress was found but "cypress.config.json" could not be found.'
);
});
@ -127,7 +131,9 @@ describe('workspace', () => {
updateProjectConfiguration(tree, 'myApp', project);
tree.write('cypress.json', '{}');
await expect(initGenerator(tree, { name: 'myApp' })).rejects.toThrow(
await expect(
migrateFromAngularCli(tree, { name: 'myApp' })
).rejects.toThrow(
'An e2e project with Cypress was found but the "cypress" directory could not be found.'
);
});
@ -137,7 +143,9 @@ describe('workspace', () => {
project.targets.e2e.executor = '@my-org/my-package:my-executor';
updateProjectConfiguration(tree, 'myApp', project);
await expect(initGenerator(tree, { name: 'myApp' })).rejects.toThrow(
await expect(
migrateFromAngularCli(tree, { name: 'myApp' })
).rejects.toThrow(
`An e2e project was found but it's using an unsupported executor "@my-org/my-package:my-executor".`
);
});
@ -145,7 +153,7 @@ describe('workspace', () => {
it('should error if no angular.json is present', async () => {
try {
tree.delete('angular.json');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
} catch (e) {
expect(e.message).toContain('Cannot find angular.json');
}
@ -164,7 +172,7 @@ describe('workspace', () => {
})
);
try {
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
} catch (e) {
expect(e.message).toContain('Can only convert projects with one app');
}
@ -183,7 +191,7 @@ describe('workspace', () => {
})
);
try {
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
} catch (e) {
expect(e.message).toContain('Can only convert projects with one app');
}
@ -239,7 +247,7 @@ describe('workspace', () => {
tree.write('/projects/myApp/e2e/protractor.conf.js', '// content');
tree.write('/projects/myApp/src/app/app.module.ts', '// content');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
const a = readJson(tree, '/angular.json');
@ -247,30 +255,30 @@ describe('workspace', () => {
});
it('should set the default collection to @nrwl/angular', async () => {
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(readJson(tree, 'nx.json').cli.defaultCollection).toBe(
'@nrwl/angular'
);
});
it('should create nx.json', async () => {
await initGenerator(tree, { name: 'myApp', defaultBase: 'main' });
await migrateFromAngularCli(tree, { name: 'myApp', defaultBase: 'main' });
expect(readJson(tree, 'nx.json')).toMatchSnapshot();
});
it('should work if angular-cli workspace had tsconfig.base.json', async () => {
tree.rename('tsconfig.json', 'tsconfig.base.json');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(readJson(tree, 'tsconfig.base.json')).toMatchSnapshot();
});
it('should update tsconfig.base.json if present', async () => {
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(readJson(tree, 'tsconfig.base.json')).toMatchSnapshot();
});
it('should work without nested tsconfig files', async () => {
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
});
@ -321,7 +329,7 @@ describe('workspace', () => {
'/src/tsconfig.spec.json',
'{"extends": "../tsconfig.json", "compilerOptions": {}}'
);
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
});
@ -374,7 +382,7 @@ describe('workspace', () => {
tree.write('/projects/myApp/e2e/protractor.conf.js', '// content');
tree.write('/projects/myApp/src/app/app.module.ts', '// content');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(tree.exists('/tslint.json')).toBe(true);
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
@ -406,7 +414,7 @@ describe('workspace', () => {
);
tree.write('/karma.conf.js', '// content');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(tree.exists('/apps/myApp/tsconfig.app.json')).toBe(true);
expect(tree.exists('/apps/myApp/karma.conf.js')).toBe(true);
@ -416,7 +424,7 @@ describe('workspace', () => {
it('should work with existing .prettierignore file', async () => {
tree.write('/.prettierignore', '# existing ignore rules');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
const prettierIgnore = tree.read('/.prettierignore').toString();
expect(prettierIgnore).toBe('# existing ignore rules');
@ -424,7 +432,7 @@ describe('workspace', () => {
it('should update tsconfigs', async () => {
tree.write('/.prettierignore', '# existing ignore rules');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
const prettierIgnore = tree.read('/.prettierignore').toString();
expect(prettierIgnore).toBe('# existing ignore rules');
@ -432,7 +440,7 @@ describe('workspace', () => {
it('should work with no root tslint.json', async () => {
tree.delete('/tslint.json');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(tree.exists('/tslint.json')).toBe(false);
});
@ -518,7 +526,7 @@ describe('workspace', () => {
});
it('should migrate e2e tests correctly', async () => {
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(tree.exists('cypress.json')).toBe(false);
expect(tree.exists('cypress')).toBe(false);
@ -550,7 +558,7 @@ describe('workspace', () => {
updateProjectConfiguration(tree, 'myApp', project);
tree.delete('cypress.json');
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(tree.exists('cypress.json')).toBe(false);
expect(tree.exists('cypress')).toBe(false);
@ -569,7 +577,7 @@ describe('workspace', () => {
delete project.targets['cypress-open'];
updateProjectConfiguration(tree, 'myApp', project);
await initGenerator(tree, { name: 'myApp' });
await migrateFromAngularCli(tree, { name: 'myApp' });
expect(tree.exists('cypress.json')).toBe(false);
expect(tree.exists('cypress')).toBe(false);
@ -589,7 +597,7 @@ describe('workspace', () => {
});
it('should update package.json', async () => {
await initGenerator(tree, {
await migrateFromAngularCli(tree, {
name: 'myApp',
preserveAngularCliLayout: true,
});
@ -600,7 +608,7 @@ describe('workspace', () => {
});
it('should create nx.json', async () => {
await initGenerator(tree, {
await migrateFromAngularCli(tree, {
name: 'myApp',
preserveAngularCliLayout: true,
});
@ -610,7 +618,7 @@ describe('workspace', () => {
});
it('should create decorate-angular-cli.js', async () => {
await initGenerator(tree, {
await migrateFromAngularCli(tree, {
name: 'myApp',
preserveAngularCliLayout: true,
});

View File

@ -1,7 +1,6 @@
import {
addDependenciesToPackageJson,
addProjectConfiguration,
convertNxGenerator,
formatFiles,
generateFiles,
getProjects,
@ -12,30 +11,26 @@ import {
normalizePath,
NxJsonConfiguration,
offsetFromRoot,
ProjectConfiguration,
readJson,
readProjectConfiguration,
readWorkspaceConfiguration,
TargetConfiguration,
Tree,
updateJson,
updateProjectConfiguration,
updateWorkspaceConfiguration,
visitNotIgnoredFiles,
writeJson,
ProjectConfiguration,
TargetConfiguration,
} from '@nrwl/devkit';
import { readFileSync } from 'fs';
import { DEFAULT_NRWL_PRETTIER_CONFIG } from '@nrwl/workspace/src/generators/workspace/workspace';
import { deduceDefaultBase } from '@nrwl/workspace/src/utilities/default-base';
import { resolveUserExistingPrettierConfig } from '@nrwl/workspace/src/utilities/prettier';
import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript';
import { prettierVersion } from '@nrwl/workspace/src/utils/versions';
import { basename, relative } from 'path';
import { resolveUserExistingPrettierConfig } from '../../utilities/prettier';
import { deduceDefaultBase } from '../../utilities/default-base';
import {
angularCliVersion,
nxVersion,
prettierVersion,
} from '../../utils/versions';
import { DEFAULT_NRWL_PRETTIER_CONFIG } from '../workspace/workspace';
import { angularDevkitVersion, nxVersion } from '../../utils/versions';
import { Schema } from './schema';
import { getRootTsConfigPathInTree } from '../../utilities/typescript';
function updatePackageJson(tree) {
updateJson(tree, 'package.json', (packageJson) => {
@ -66,12 +61,14 @@ function updatePackageJson(tree) {
if (!packageJson.dependencies['@nrwl/angular']) {
packageJson.dependencies['@nrwl/angular'] = nxVersion;
}
delete packageJson.dependencies['@nrwl/workspace'];
if (!packageJson.devDependencies['@angular/cli']) {
packageJson.devDependencies['@angular/cli'] = angularDevkitVersion;
}
if (!packageJson.devDependencies['@nrwl/workspace']) {
packageJson.devDependencies['@nrwl/workspace'] = nxVersion;
}
if (!packageJson.devDependencies['@angular/cli']) {
packageJson.devDependencies['@angular/cli'] = angularCliVersion;
if (!packageJson.devDependencies['nx']) {
packageJson.devDependencies['nx'] = nxVersion;
}
if (!packageJson.devDependencies['prettier']) {
packageJson.devDependencies['prettier'] = prettierVersion;
@ -623,25 +620,7 @@ function replaceCypressGlobConfig(
);
}
async function createAdditionalFiles(host: Tree, options: Schema) {
const nxJson: NxJsonConfiguration = {
npmScope: options.npmScope,
affected: {
defaultBase: options.defaultBase
? `${options.defaultBase}`
: deduceDefaultBase(),
},
implicitDependencies: {
'angular.json': '*',
'package.json': '*',
'tslint.json': '*',
'.eslintrc.json': '*',
'nx.json': '*',
},
};
writeJson(host, 'nx.json', nxJson);
host.write('libs/.gitkeep', '');
async function createAdditionalFiles(host: Tree) {
const recommendations = [
'nrwl.angular-console',
'angular.ng-template',
@ -749,8 +728,7 @@ function checkCanConvertToWorkspace(host: Tree) {
}
function createNxJson(host: Tree) {
const json = JSON.parse(host.read('angular.json').toString());
const projects = json.projects || {};
const { projects = {}, newProjectRoot = '' } = readJson(host, 'angular.json');
const hasLibraries = Object.keys(projects).find(
(project) =>
projects[project].projectType &&
@ -782,19 +760,17 @@ function createNxJson(host: Tree) {
},
},
},
workspaceLayout: { appsDir: newProjectRoot, libsDir: newProjectRoot },
});
}
function decorateAngularClI(host: Tree) {
const decorateCli = readFileSync(
joinPathFragments(
__dirname as any,
'..',
'utils',
'decorate-angular-cli.js__tmpl__'
)
).toString();
host.write('decorate-angular-cli.js', decorateCli);
function decorateAngularCli(host: Tree) {
generateFiles(
host,
joinPathFragments(__dirname, 'files', 'decorate-angular-cli'),
'.',
{ tmpl: '' }
);
updateJson(host, 'package.json', (json) => {
if (
json.scripts &&
@ -815,11 +791,13 @@ function decorateAngularClI(host: Tree) {
});
}
function addFiles(host: Tree) {
function addFiles(host: Tree, options: Schema) {
generateFiles(host, joinPathFragments(__dirname, './files/root'), '.', {
tmpl: '',
dot: '.',
rootTsConfigPath: getRootTsConfigPathInTree(host),
npmScope: options.npmScope,
defaultBase: options.defaultBase ?? deduceDefaultBase(),
});
if (!host.exists('.prettierignore')) {
@ -857,15 +835,18 @@ function renameDirSyncInTree(tree: Tree, from: string, to: string) {
});
}
export async function initGenerator(tree: Tree, schema: Schema) {
export async function migrateFromAngularCli(tree: Tree, schema: Schema) {
if (schema.preserveAngularCliLayout) {
updateJson(tree, 'package.json', (json) => {
delete json.dependencies?.['@nrwl/workspace'];
return json;
});
addDependenciesToPackageJson(tree, {}, { '@nrwl/workspace': nxVersion });
addDependenciesToPackageJson(
tree,
{},
{
nx: nxVersion,
'@nrwl/workspace': nxVersion,
}
);
createNxJson(tree);
decorateAngularClI(tree);
decorateAngularCli(tree);
} else {
const options = {
...schema,
@ -874,15 +855,15 @@ export async function initGenerator(tree: Tree, schema: Schema) {
checkCanConvertToWorkspace(tree);
moveExistingFiles(tree, options);
addFiles(tree);
await createAdditionalFiles(tree, options);
addFiles(tree, options);
await createAdditionalFiles(tree);
updatePackageJson(tree);
updateAngularCLIJson(tree, options);
updateTsLint(tree);
updateProjectTsLint(tree, options);
updateTsConfig(tree);
updateTsConfigsJson(tree, options);
decorateAngularClI(tree);
decorateAngularCli(tree);
await formatFiles(tree);
}
if (!schema.skipInstall) {
@ -891,7 +872,3 @@ export async function initGenerator(tree: Tree, schema: Schema) {
};
}
}
export const initSchematic = convertNxGenerator(initGenerator);
export default initGenerator;

View File

@ -0,0 +1,36 @@
import * as angularCliMigrator from './migrate-from-angular-cli';
import * as initGenerator from '../init/init';
import { ngAddGenerator } from './ng-add';
import { Tree } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
describe('ngAdd generator', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace(2);
jest
.spyOn(angularCliMigrator, 'migrateFromAngularCli')
.mockImplementation(() => Promise.resolve(() => {}));
jest
.spyOn(initGenerator, 'angularInitGenerator')
.mockImplementation(() => Promise.resolve(() => {}));
jest.clearAllMocks();
});
it('should initialize the Angular plugin when in an Nx workspace', async () => {
await ngAddGenerator(tree, {});
expect(initGenerator.angularInitGenerator).toHaveBeenCalled();
expect(angularCliMigrator.migrateFromAngularCli).not.toHaveBeenCalled();
});
it('should perform a migration when in an Angular CLI workspace', async () => {
tree.delete('nx.json');
await ngAddGenerator(tree, {});
expect(angularCliMigrator.migrateFromAngularCli).toHaveBeenCalled();
expect(initGenerator.angularInitGenerator).not.toHaveBeenCalled();
});
});

View File

@ -0,0 +1,18 @@
import { Tree } from '@nrwl/devkit';
import { migrateFromAngularCli } from './migrate-from-angular-cli';
import { angularInitGenerator } from '../init/init';
import { Schema } from './schema';
function getWorkspaceType(tree: Tree): 'angular' | 'nx' {
return tree.exists('nx.json') ? 'nx' : 'angular';
}
export async function ngAddGenerator(tree: Tree, options: Schema) {
if (getWorkspaceType(tree) === 'angular') {
return await migrateFromAngularCli(tree, options);
}
return angularInitGenerator(tree, options);
}
export default ngAddGenerator;

View File

@ -0,0 +1,19 @@
import { Linter } from '@nrwl/linter';
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
import type { Styles } from '../utils/types';
export interface Schema {
name?: string;
skipInstall?: boolean;
npmScope?: string;
preserveAngularCliLayout?: boolean;
defaultBase?: string;
unitTestRunner?: UnitTestRunner;
e2eTestRunner?: E2eTestRunner;
skipFormat?: boolean;
skipInstall?: boolean;
style?: Styles;
linter?: Exclude<Linter, Linter.TsLint>;
skipPackageJson?: boolean;
}

View File

@ -0,0 +1,71 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "NxAngularNgAddGenerator",
"title": "Angular plugin initialization",
"cli": "nx",
"description": "Migrates an Angular CLI workspace to Nx or adds the Angular plugin to an Nx workspace. NOTE: Does not work in the `--dry-run` mode.",
"type": "object",
"properties": {
"npmScope": {
"type": "string",
"description": "Npm scope for importing libs. NOTE: only used if running the generator in an Angular CLI workspace."
},
"defaultBase": {
"type": "string",
"description": "Default base branch for affected. NOTE: only used if running the generator in an Angular CLI workspace."
},
"skipInstall": {
"type": "boolean",
"description": "Skip installing added packages.",
"default": false
},
"preserveAngularCliLayout": {
"type": "boolean",
"description": "Preserve the Angular CLI layout instead of moving the app into apps. NOTE: only used if running the generator in an Angular CLI workspace.",
"default": false
},
"name": {
"type": "string",
"description": "Project name. NOTE: only used if running the generator in an Angular CLI workspace.",
"$default": {
"$source": "projectName"
}
},
"unitTestRunner": {
"type": "string",
"enum": ["karma", "jest", "none"],
"description": "Test runner to use for unit tests. NOTE: only used if running the generator in an Nx workspace.",
"default": "jest"
},
"e2eTestRunner": {
"type": "string",
"enum": ["protractor", "cypress", "none"],
"description": "Test runner to use for end to end (e2e) tests. NOTE: only used if running the generator in an Nx workspace.",
"default": "cypress"
},
"skipFormat": {
"description": "Skip formatting files. NOTE: only used if running the generator in an Nx workspace.",
"type": "boolean",
"default": false
},
"linter": {
"description": "The tool to use for running lint checks. NOTE: only used if running the generator in an Nx workspace.",
"type": "string",
"enum": ["eslint", "none"],
"default": "eslint"
},
"style": {
"description": "The file extension to be used for style files. NOTE: only used if running the generator in an Nx workspace.",
"type": "string",
"default": "css",
"enum": ["css", "scss", "sass", "less"]
},
"skipPackageJson": {
"type": "boolean",
"default": false,
"description": "Do not add dependencies to `package.json`. NOTE: only used if running the generator in an Nx workspace."
}
},
"additionalProperties": false
}

View File

@ -16,8 +16,10 @@
"Cypress",
"CLI"
],
"main": "src/index.js",
"typings": "src/index.d.ts",
"bin": {
"make-angular-cli-faster": "./make-angular-cli-faster.js"
"make-angular-cli-faster": "./src/index.js"
},
"author": "Victor Savkin",
"license": "MIT",
@ -26,9 +28,13 @@
},
"homepage": "https://nx.dev",
"dependencies": {
"yargs-parser": "20.0.0",
"yargs": "15.4.1",
"@nrwl/devkit": "*",
"@nrwl/workspace": "*",
"enquirer": "^2.3.6",
"@nrwl/workspace": "*"
"nx": "*",
"semver": "7.3.4",
"tmp": "~0.2.1",
"yargs": "15.4.1",
"yargs-parser": "20.0.0"
}
}

View File

@ -30,7 +30,7 @@
"options": {
"commands": [
{
"command": "node ./scripts/chmod build/packages/make-angular-cli-faster/src/make-angular-cli-faster.js"
"command": "node ./scripts/chmod build/packages/make-angular-cli-faster/src/index.js"
},
{
"command": "node ./scripts/copy-readme.js make-angular-cli-faster"

View File

@ -0,0 +1,21 @@
#!/usr/bin/env node
import * as yargsParser from 'yargs-parser';
import {
Args,
makeAngularCliFaster,
} from './utilities/make-angular-cli-faster';
const args = yargsParser(process.argv, {
string: ['version'],
boolean: ['verbose'],
});
makeAngularCliFaster(args as Args)
.then(() => {
process.exit(0);
})
.catch((e) => {
console.log(e);
process.exit(1);
});

View File

@ -1,94 +0,0 @@
#!/usr/bin/env node
import { statSync } from 'fs';
import { execSync } from 'child_process';
import * as yargsParser from 'yargs-parser';
const enquirer = require('enquirer');
const parsedArgs = yargsParser(process.argv, {
string: ['version'],
boolean: ['verbose'],
});
function isYarn() {
try {
statSync('yarn.lock');
return true;
} catch (e) {
return false;
}
}
function addDependency(dep: string) {
const stdio = parsedArgs.verbose ? [0, 1, 2] : ['ignore', 'ignore', 'ignore'];
if (isYarn()) {
execSync(`yarn add -D ${dep}`, { stdio: stdio as any });
} else {
execSync(`npm i --save-dev ${dep}`, { stdio: stdio as any });
}
}
function addNxCloud() {
return enquirer
.prompt([
{
name: 'NxCloud',
message: `Use the free tier of the distributed cache provided by Nx Cloud?`,
type: 'list',
choices: [
{
value: 'yes',
name: 'Yes [Faster command execution, faster CI. Learn more at https://nx.app]',
},
{
value: 'no',
name: 'No [Only use local computation cache]',
},
],
default: 'no',
},
])
.then((a: { NxCloud: 'yes' | 'no' }) => a.NxCloud === 'yes');
}
async function main() {
const version = parsedArgs.version ? parsedArgs.version : `latest`;
const output = require('@nrwl/workspace/src/utils/output').output;
output.log({ title: 'Nx initialization' });
addDependency(`nx@${version}`);
addDependency(`@nrwl/cli@${version}`);
addDependency(`@nrwl/workspace@${version}`);
execSync(`nx g @nrwl/workspace:ng-add --preserve-angular-cli-layout`, {
stdio: [0, 1, 2],
});
if (await addNxCloud()) {
output.log({ title: 'Nx Cloud initialization' });
addDependency(`@nrwl/nx-cloud`);
execSync(`nx g @nrwl/nx-cloud:init`, { stdio: [0, 1, 2] });
}
execSync('npm install', { stdio: ['ignore', 'ignore', 'ignore'] });
output.success({
title: 'Angular CLI is faster now!',
bodyLines: [
`Execute 'npx ng build' twice to see the computation caching in action.`,
`Learn more about computation caching, how it is shared with your teammates,`,
`and how it can speed up your CI by up to 10 times at https://nx.dev/angular`,
],
});
}
main()
.then(() => {
process.exit(0);
})
.catch((e) => {
console.log(e);
process.exit(1);
});

View File

@ -0,0 +1,38 @@
import { output } from '@nrwl/workspace/src/utilities/output';
import { determineMigration, migrateWorkspace } from './migration';
import { initNxCloud, promptForNxCloud } from './nx-cloud';
import { installDependencies } from './package-manager';
export interface Args {
version?: string;
verbose?: boolean;
}
export async function makeAngularCliFaster(args: Args) {
output.log({ title: '🐳 Nx initialization' });
output.log({ title: '🧐 Checking versions compatibility' });
const migration = await determineMigration(args.version);
const useNxCloud = await promptForNxCloud();
output.log({ title: '📦 Installing dependencies' });
installDependencies(migration, useNxCloud);
output.log({ title: '📝 Setting up workspace for faster computation' });
migrateWorkspace(migration);
if (useNxCloud) {
output.log({ title: '🛠️ Setting up Nx Cloud' });
initNxCloud();
}
output.success({
title: '🎉 Angular CLI is faster now!',
bodyLines: [
`Execute 'npx ng build' twice to see the computation caching in action.`,
'Learn more about computation caching, how it is shared with your teammates,' +
' and how it can speed up your CI by up to 10 times at https://nx.dev/getting-started/nx-and-angular',
],
});
}

View File

@ -0,0 +1,182 @@
import { getPackageManagerCommand, readJsonFile } from '@nrwl/devkit';
import { output } from '@nrwl/workspace/src/utilities/output';
import { execSync } from 'child_process';
import { prompt } from 'enquirer';
import { appRootPath } from 'nx/src/utils/app-root';
import { lt, lte, major, satisfies } from 'semver';
import { resolvePackageVersion } from './package-manager';
import { MigrationDefinition } from './types';
// up to this version the generator was in the @nrwl/workspace package
const latestWorkspaceVersionWithMigration = '13.9.3';
// up to this version the preserveAngularCLILayout option was used
const latestVersionWithOldFlag = '13.8.3';
// map of Angular major versions to the versions of Nx that are compatible with them,
// key is the Angular major version, value is an object with the range of supported
// versions and the max version of the range if there's a bigger major version that
// is already supported
const nxAngularVersionMap: Record<number, { range: string; max?: string }> = {
13: { range: '>= 13.2.0' },
};
// latest major version of Angular that is compatible with Nx, based on the map above
const latestCompatibleAngularMajorVersion = Math.max(
...Object.keys(nxAngularVersionMap).map((x) => +x)
);
export async function determineMigration(
version: string | undefined
): Promise<MigrationDefinition> {
const angularVersion = getInstalledAngularVersion();
const majorAngularVersion = major(angularVersion);
if (version) {
const normalizedVersion = normalizeVersion(version);
if (lte(normalizedVersion, latestWorkspaceVersionWithMigration)) {
// specified version should use @nrwl/workspace:ng-add
return { packageName: '@nrwl/workspace', version: normalizedVersion };
}
// if greater than the latest workspace version with the migration,
// check the versions map for compatibility with Angular
if (
nxAngularVersionMap[majorAngularVersion] &&
satisfies(
normalizedVersion,
nxAngularVersionMap[majorAngularVersion].range
)
) {
// there's a match, use @nrwl/angular:ng-add
return { packageName: '@nrwl/angular', version: normalizedVersion };
}
// it's not compatible with the currently installed Angular version,
// suggest the latest possible version to use
return await findAndSuggestVersionToUse(
angularVersion,
majorAngularVersion,
version
);
}
const latestNxCompatibleVersion = getNxVersionBasedOnInstalledAngularVersion(
angularVersion,
majorAngularVersion
);
// should use @nrwl/workspace:ng-add if the version is less than the
// latest workspace version that has the migration, otherwise use
// @nrwl/angular:ng-add
return {
packageName: lte(
latestNxCompatibleVersion,
latestWorkspaceVersionWithMigration
)
? '@nrwl/workspace'
: '@nrwl/angular',
version: latestNxCompatibleVersion,
};
}
export function migrateWorkspace(migration: MigrationDefinition): void {
const preserveAngularCliLayoutFlag = lte(
migration.version,
latestVersionWithOldFlag
)
? '--preserveAngularCLILayout'
: '--preserve-angular-cli-layout';
execSync(
`${getPackageManagerCommand().exec} nx g ${
migration.packageName
}:ng-add ${preserveAngularCliLayoutFlag}`,
{ stdio: [0, 1, 2] }
);
}
async function findAndSuggestVersionToUse(
angularVersion: string,
majorAngularVersion: number,
userSpecifiedVersion: string
): Promise<MigrationDefinition> {
const latestNxCompatibleVersion = getNxVersionBasedOnInstalledAngularVersion(
angularVersion,
majorAngularVersion
);
const useSuggestedVersion = await promptForVersion(latestNxCompatibleVersion);
if (useSuggestedVersion) {
// should use @nrwl/workspace:ng-add if the version is less than the
// latest workspace version that has the migration, otherwise use
// @nrwl/angular:ng-add
return {
packageName: lte(
latestNxCompatibleVersion,
latestWorkspaceVersionWithMigration
)
? '@nrwl/workspace'
: '@nrwl/angular',
version: latestNxCompatibleVersion,
};
}
output.error({
title: `❌ Cannot proceed with the migration.`,
bodyLines: [
`The specified Nx version "${userSpecifiedVersion}" is not compatible with the installed Angular version "${angularVersion}".`,
],
});
process.exit(1);
}
function getNxVersionBasedOnInstalledAngularVersion(
angularVersion: string,
majorAngularVersion: number
): string {
if (lt(angularVersion, '13.0.0')) {
// the @nrwl/angular:ng-add generator is only available for versions supporting
// Angular >= 13.0.0, fall back to @nrwl/workspace:ng-add
return latestWorkspaceVersionWithMigration;
}
if (nxAngularVersionMap[majorAngularVersion]?.max) {
// use the max of the range
return nxAngularVersionMap[majorAngularVersion].max;
}
if (majorAngularVersion > latestCompatibleAngularMajorVersion) {
// installed Angular version is not supported yet, we can't @nrwl/angular:ng-add,
// fall back to @nrwl/workspace:ng-add
return latestWorkspaceVersionWithMigration;
}
// use latest, only the last version in the map should not contain a max
return resolvePackageVersion('@nrwl/angular', 'latest');
}
async function promptForVersion(version: string): Promise<boolean> {
const { useVersion } = await prompt<{ useVersion: boolean }>([
{
name: 'useVersion',
message: `The provided version of Nx is not compatible with the installed Angular CLI version. Would you like to use the recommended version "${version}" instead?`,
type: 'confirm',
initial: true,
},
]);
return useVersion;
}
function getInstalledAngularVersion(): string {
const packageJsonPath = require.resolve('@angular/core/package.json', {
paths: [appRootPath],
});
return readJsonFile(packageJsonPath).version;
}
function normalizeVersion(version: string): string {
if (
version.startsWith('^') ||
version.startsWith('~') ||
version.split('.').length < 3
) {
return resolvePackageVersion('@nrwl/angular', version);
}
return version;
}

View File

@ -0,0 +1,29 @@
import { getPackageManagerCommand } from '@nrwl/devkit';
import { execSync } from 'child_process';
import { prompt } from 'enquirer';
export async function promptForNxCloud(): Promise<boolean> {
const { useNxCloud } = await prompt<{ useNxCloud: 'Yes' | 'No' }>([
{
name: 'useNxCloud',
message: `Use Nx Cloud? (It's free and doesn't require registration.)`,
type: 'select',
choices: [
{
name: 'Yes',
hint: 'Yes [Faster builds, run details, Github integration. Learn more at https://nx.app]',
},
{ name: 'No' },
],
initial: 'Yes' as any,
},
]);
return useNxCloud === 'Yes';
}
export function initNxCloud(): void {
execSync(`${getPackageManagerCommand().exec} nx g @nrwl/nx-cloud:init`, {
stdio: [0, 1, 2],
});
}

View File

@ -0,0 +1,93 @@
import {
getPackageManagerCommand,
readJsonFile,
writeJsonFile,
} from '@nrwl/devkit';
import { execSync } from 'child_process';
import { copyFileSync, existsSync, unlinkSync, writeFileSync } from 'fs';
import { appRootPath } from 'nx/src/utils/app-root';
import { sortObjectByKeys } from 'nx/src/utils/object-sort';
import { dirname, join } from 'path';
import { gte, lt, major } from 'semver';
import { dirSync } from 'tmp';
import { MigrationDefinition } from './types';
// version when the Nx CLI changed from @nrwl/tao & @nrwl/cli to nx
const versionWithConsolidatedPackages = '13.9.0';
export function installDependencies(
{ packageName, version }: MigrationDefinition,
useNxCloud: boolean
): void {
const json = readJsonFile(join(appRootPath, 'package.json'));
json.devDependencies ??= {};
json.devDependencies['@nrwl/workspace'] = version;
if (gte(version, versionWithConsolidatedPackages)) {
json.devDependencies['nx'] = version;
} else {
json.devDependencies['@nrwl/cli'] = version;
json.devDependencies['@nrwl/tao'] = version;
}
if (useNxCloud) {
// get the latest @nrwl/nx-cloud version compatible with the Nx major
// version being installed
json.devDependencies['@nrwl/nx-cloud'] = resolvePackageVersion(
'@nrwl/nx-cloud',
`^${major(version)}.0.0`
);
}
json.devDependencies = sortObjectByKeys(json.devDependencies);
if (packageName === '@nrwl/angular') {
json.dependencies ??= {};
json.dependencies['@nrwl/angular'] = version;
json.dependencies = sortObjectByKeys(json.dependencies);
}
writeFileSync(`package.json`, JSON.stringify(json, null, 2));
execSync(getPackageManagerCommand().install, {
stdio: [0, 1, 2],
});
}
export function resolvePackageVersion(
packageName: string,
version: string
): string {
const dir = dirSync().name;
const npmrc = checkForNPMRC();
if (npmrc) {
// Creating a package.json is needed for .npmrc to resolve
writeJsonFile(`${dir}/package.json`, {});
// Copy npmrc if it exists, so that npm still follows it.
copyFileSync(npmrc, `${dir}/.npmrc`);
}
const pmc = getPackageManagerCommand();
execSync(`${pmc.add} ${packageName}@${version}`, { stdio: [], cwd: dir });
const packageJsonPath = require.resolve(`${packageName}/package.json`, {
paths: [dir],
});
const { version: resolvedVersion } = readJsonFile(packageJsonPath);
try {
unlinkSync(dir);
} catch {
// It's okay if this fails, the OS will clean it up eventually
}
return resolvedVersion;
}
function checkForNPMRC(): string | null {
let directory = process.cwd();
while (!existsSync(join(directory, 'package.json'))) {
directory = dirname(directory);
}
const path = join(directory, '.npmrc');
return existsSync(path) ? path : null;
}

View File

@ -0,0 +1,4 @@
export interface MigrationDefinition {
packageName: string;
version: string;
}

View File

@ -9,13 +9,6 @@
"hidden": true
},
"ng-add": {
"factory": "./src/generators/init/init#initSchematic",
"schema": "./src/generators/init/schema.json",
"description": "Convert an existing Angular CLI project into an Nx Workspace",
"hidden": true
},
"preset": {
"factory": "./src/generators/preset/preset#presetSchematic",
"schema": "./src/generators/preset/schema.json",
@ -87,13 +80,6 @@
"hidden": true
},
"ng-add": {
"factory": "./src/generators/init/init#initGenerator",
"schema": "./src/generators/init/schema.json",
"description": "Convert an existing Angular CLI project into an Nx Workspace",
"hidden": true
},
"preset": {
"factory": "./src/generators/preset/preset#presetGenerator",
"schema": "./src/generators/preset/schema.json",

View File

@ -1,7 +0,0 @@
export interface Schema {
name: string;
skipInstall?: boolean;
npmScope?: string;
preserveAngularCliLayout?: boolean;
defaultBase?: string;
}

View File

@ -1,35 +0,0 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "SchematicsNxNgAdd",
"title": "Init Workspace",
"cli": "ng",
"description": "NOTE: Does not work in the --dry-run mode",
"type": "object",
"properties": {
"npmScope": {
"type": "string",
"description": "Npm scope for importing libs."
},
"defaultBase": {
"type": "string",
"description": "Default base branch for affected."
},
"skipInstall": {
"type": "boolean",
"description": "Skip installing after adding @nrwl/workspace",
"default": false
},
"preserveAngularCliLayout": {
"type": "boolean",
"description": "Preserve the Angular CLI layout instead of moving the app into apps.",
"default": false
},
"name": {
"type": "string",
"description": "Project name.",
"$default": {
"$source": "projectName"
}
}
}
}

View File

@ -116,6 +116,7 @@ const IGNORE_MATCHES = {
'@nrwl/devkit',
],
nest: ['semver'],
'make-angular-cli-faster': ['@angular/core'],
};
export default async function getMissingDependencies(

View File

@ -189,7 +189,6 @@ function build(nxVersion: string) {
].map((f) => `${f}/package.json`),
'create-nx-workspace/bin/create-nx-workspace.js',
'create-nx-plugin/bin/create-nx-plugin.js',
'make-angular-cli-faster/src/make-angular-cli-faster.js',
'add-nx-to-monorepo/src/add-nx-to-monorepo.js',
].map((f) => `${BUILD_DIR}/${f}`);

View File

@ -28,7 +28,6 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i "" "s|ANGULAR_CLI_VERSION|$ANGULAR_CLI_VERSION|g" create-nx-plugin/bin/create-nx-plugin.js
sed -i "" "s|TYPESCRIPT_VERSION|$TYPESCRIPT_VERSION|g" create-nx-plugin/bin/create-nx-plugin.js
sed -i "" "s|PRETTIER_VERSION|$PRETTIER_VERSION|g" create-nx-plugin/bin/create-nx-plugin.js
sed -i "" "s|NX_VERSION|$NX_VERSION|g" make-angular-cli-faster/src/make-angular-cli-faster.js
sed -i "" "s|NX_VERSION|$NX_VERSION|g" add-nx-to-monorepo/src/add-nx-to-monorepo.js
else
sed -i "s|exports.nxVersion = '\*';|exports.nxVersion = '$NX_VERSION';|g" {react,next,web,jest,node,linter,express,nest,cypress,storybook,angular,workspace,nx-plugin,react-native,detox,js}/src/utils/versions.js
@ -41,7 +40,6 @@ else
sed -i "s|ANGULAR_CLI_VERSION|$ANGULAR_CLI_VERSION|g" create-nx-plugin/bin/create-nx-plugin.js
sed -i "s|TYPESCRIPT_VERSION|$TYPESCRIPT_VERSION|g" create-nx-plugin/bin/create-nx-plugin.js
sed -i "s|PRETTIER_VERSION|$PRETTIER_VERSION|g" create-nx-plugin/bin/create-nx-plugin.js
sed -i "s|NX_VERSION|$NX_VERSION|g" make-angular-cli-faster/src/make-angular-cli-faster.js
sed -i "s|NX_VERSION|$NX_VERSION|g" add-nx-to-monorepo/src/add-nx-to-monorepo.js
fi