cleanup(core): inline angular cli nested project migration logic into nx init (#15396)
This commit is contained in:
parent
fca1f97ab7
commit
e1a2c98556
@ -121,6 +121,7 @@
|
||||
/packages/nx/src/adapter @AgentEnder @leosvelperez
|
||||
/packages/nx/src/native @vsavkin @FrozenPandaz @Cammisuli
|
||||
/packages/nx/src/lock-file @meeroslav @FrozenPandaz
|
||||
/packages/nx/src/nx-init/angular/** @Coly010 @leosvelperez @FrozenPandaz
|
||||
/e2e/nx*/** @FrozenPandaz @AgentEnder @vsavkin
|
||||
/packages/workspace/** @FrozenPandaz @AgentEnder @vsavkin
|
||||
/e2e/workspace-create/** @FrozenPandaz @AgentEnder @vsavkin
|
||||
|
||||
65
e2e/nx-init/src/nx-init-angular.test.ts
Normal file
65
e2e/nx-init/src/nx-init-angular.test.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { PackageManager } from 'nx/src/utils/package-manager';
|
||||
import {
|
||||
checkFilesDoNotExist,
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
getPackageManagerCommand,
|
||||
getPublishedVersion,
|
||||
getSelectedPackageManager,
|
||||
runCLI,
|
||||
runCommand,
|
||||
runNgNew,
|
||||
uniq,
|
||||
} from '../../utils';
|
||||
|
||||
describe('nx init (Angular CLI)', () => {
|
||||
let project: string;
|
||||
let packageManager: PackageManager;
|
||||
let pmc: ReturnType<typeof getPackageManagerCommand>;
|
||||
|
||||
beforeEach(() => {
|
||||
project = uniq('proj');
|
||||
packageManager = getSelectedPackageManager();
|
||||
// TODO: solve issues with pnpm and remove this fallback
|
||||
packageManager = packageManager === 'pnpm' ? 'yarn' : packageManager;
|
||||
pmc = getPackageManagerCommand({ packageManager });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupProject();
|
||||
});
|
||||
|
||||
it('should successfully convert an Angular CLI workspace to an Nx workspace', () => {
|
||||
runNgNew(project, packageManager);
|
||||
|
||||
const output = runCommand(
|
||||
`${pmc.runUninstalledPackage} nx@${getPublishedVersion()} init -y`
|
||||
);
|
||||
|
||||
expect(output).toContain('Nx is now enabled in your workspace!');
|
||||
// angular.json should have been deleted
|
||||
checkFilesDoNotExist('angular.json');
|
||||
// check nx config files exist
|
||||
checkFilesExist('nx.json', 'project.json');
|
||||
|
||||
// check build
|
||||
const coldBuildOutput = runCLI(`build ${project} --outputHashing none`);
|
||||
expect(coldBuildOutput).toContain(
|
||||
`> nx run ${project}:build:production --outputHashing none`
|
||||
);
|
||||
expect(coldBuildOutput).toContain(
|
||||
`Successfully ran target build for project ${project}`
|
||||
);
|
||||
checkFilesExist(`dist/${project}/main.js`);
|
||||
|
||||
// run build again to check is coming from cache
|
||||
const cachedBuildOutput = runCLI(`build ${project} --outputHashing none`);
|
||||
expect(cachedBuildOutput).toContain(
|
||||
`> nx run ${project}:build:production --outputHashing none [local cache]`
|
||||
);
|
||||
expect(cachedBuildOutput).toContain('Nx read the output from the cache');
|
||||
expect(cachedBuildOutput).toContain(
|
||||
`Successfully ran target build for project ${project}`
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -15,7 +15,7 @@ import {
|
||||
} from './get-env-info';
|
||||
import * as isCI from 'is-ci';
|
||||
|
||||
import { angularCliVersion } from '@nrwl/workspace/src/utils/versions';
|
||||
import { angularCliVersion as defaultAngularCliVersion } from '@nrwl/workspace/src/utils/versions';
|
||||
import { dump } from '@zkochan/js-yaml';
|
||||
import { execSync, ExecSyncOptions } from 'child_process';
|
||||
|
||||
@ -247,7 +247,8 @@ export function packageInstall(
|
||||
|
||||
export function runNgNew(
|
||||
projectName: string,
|
||||
packageManager = getSelectedPackageManager()
|
||||
packageManager = getSelectedPackageManager(),
|
||||
angularCliVersion = defaultAngularCliVersion
|
||||
): string {
|
||||
projName = projectName;
|
||||
|
||||
|
||||
@ -101,8 +101,7 @@ async function addNxToMonorepo() {
|
||||
repoRoot,
|
||||
targetDefaults,
|
||||
cacheableOperations,
|
||||
scriptOutputs,
|
||||
undefined
|
||||
scriptOutputs
|
||||
);
|
||||
|
||||
addDepsToPackageJson(repoRoot, useCloud);
|
||||
@ -111,7 +110,7 @@ async function addNxToMonorepo() {
|
||||
runInstall(repoRoot);
|
||||
|
||||
if (useCloud) {
|
||||
initCloud(repoRoot);
|
||||
initCloud(repoRoot, 'nx-init-monorepo');
|
||||
}
|
||||
|
||||
printFinalMessage();
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { prerelease } from 'semver';
|
||||
import * as parser from 'yargs-parser';
|
||||
import { addNxToNest } from '../nx-init/add-nx-to-nest';
|
||||
import { addNxToNpmRepo } from '../nx-init/add-nx-to-npm-repo';
|
||||
import { addNxToAngularCliRepo } from '../nx-init/angular';
|
||||
import { generateEncapsulatedNxSetup } from '../nx-init/encapsulated/add-nx-scripts';
|
||||
import { directoryExists, readJsonFile } from '../utils/fileutils';
|
||||
import { PackageJson } from '../utils/package-json';
|
||||
import * as parser from 'yargs-parser';
|
||||
import { generateEncapsulatedNxSetup } from '../nx-init/encapsulated/add-nx-scripts';
|
||||
import { prerelease } from 'semver';
|
||||
|
||||
export async function initHandler() {
|
||||
const args = process.argv.slice(2).join(' ');
|
||||
@ -43,10 +44,7 @@ export async function initHandler() {
|
||||
} else if (existsSync('package.json')) {
|
||||
const packageJson: PackageJson = readJsonFile('package.json');
|
||||
if (existsSync('angular.json')) {
|
||||
// TODO(leo): remove make-angular-cli-faster
|
||||
execSync(`npx --yes make-angular-cli-faster@${version} ${args}`, {
|
||||
stdio: [0, 1, 2],
|
||||
});
|
||||
await addNxToAngularCliRepo();
|
||||
} else if (isCRA(packageJson)) {
|
||||
// TODO(jack): remove cra-to-nx
|
||||
execSync(`npx --yes cra-to-nx@${version} ${args}`, {
|
||||
|
||||
@ -108,8 +108,7 @@ export async function addNxToNest(packageJson: PackageJson) {
|
||||
repoRoot,
|
||||
[],
|
||||
[...cacheableOperations, ...nestCacheableScripts],
|
||||
{},
|
||||
packageJson.name
|
||||
{}
|
||||
);
|
||||
|
||||
const pmc = getPackageManagerCommand();
|
||||
@ -136,7 +135,7 @@ export async function addNxToNest(packageJson: PackageJson) {
|
||||
runInstall(repoRoot);
|
||||
|
||||
if (useCloud) {
|
||||
initCloud(repoRoot);
|
||||
initCloud(repoRoot, 'nx-init-nest');
|
||||
}
|
||||
|
||||
printFinalMessage();
|
||||
|
||||
@ -76,7 +76,7 @@ export async function addNxToNpmRepo() {
|
||||
useCloud = false;
|
||||
}
|
||||
|
||||
createNxJsonFile(repoRoot, [], cacheableOperations, {}, packageJson.name);
|
||||
createNxJsonFile(repoRoot, [], cacheableOperations, {});
|
||||
|
||||
const pmc = getPackageManagerCommand();
|
||||
|
||||
@ -93,7 +93,7 @@ export async function addNxToNpmRepo() {
|
||||
runInstall(repoRoot, pmc);
|
||||
|
||||
if (useCloud) {
|
||||
initCloud(repoRoot);
|
||||
initCloud(repoRoot, 'nx-init-npm-repo');
|
||||
}
|
||||
|
||||
printFinalMessage();
|
||||
|
||||
388
packages/nx/src/nx-init/angular/index.ts
Normal file
388
packages/nx/src/nx-init/angular/index.ts
Normal file
@ -0,0 +1,388 @@
|
||||
import { prompt } from 'enquirer';
|
||||
import { unlinkSync } from 'fs';
|
||||
import { dirname, join, relative, resolve } from 'path';
|
||||
import { toNewFormat } from '../../adapter/angular-json';
|
||||
import { NxJsonConfiguration } from '../../config/nx-json';
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
} from '../../config/workspace-json-project-json';
|
||||
import { fileExists, readJsonFile, writeJsonFile } from '../../utils/fileutils';
|
||||
import { sortObjectByKeys } from '../../utils/object-sort';
|
||||
import { output } from '../../utils/output';
|
||||
import { PackageJson } from '../../utils/package-json';
|
||||
import { normalizePath } from '../../utils/path';
|
||||
import {
|
||||
addDepsToPackageJson,
|
||||
addVsCodeRecommendedExtensions,
|
||||
askAboutNxCloud,
|
||||
createNxJsonFile,
|
||||
initCloud,
|
||||
runInstall,
|
||||
} from '../utils';
|
||||
import { getLegacyMigrationFunctionIfApplicable } from './legacy-angular-versions';
|
||||
import yargsParser = require('yargs-parser');
|
||||
|
||||
const defaultCacheableOperations: string[] = [
|
||||
'build',
|
||||
'server',
|
||||
'test',
|
||||
'lint',
|
||||
];
|
||||
const parsedArgs = yargsParser(process.argv, {
|
||||
boolean: ['yes'],
|
||||
string: ['cacheable'], // only used for testing
|
||||
alias: {
|
||||
yes: ['y'],
|
||||
},
|
||||
});
|
||||
|
||||
let repoRoot: string;
|
||||
let workspaceTargets: string[];
|
||||
|
||||
export async function addNxToAngularCliRepo() {
|
||||
repoRoot = process.cwd();
|
||||
|
||||
output.log({ title: '🧐 Checking versions compatibility' });
|
||||
const legacyMigrationFn = await getLegacyMigrationFunctionIfApplicable(
|
||||
repoRoot,
|
||||
parsedArgs.yes !== true
|
||||
);
|
||||
if (legacyMigrationFn) {
|
||||
output.log({ title: '💽 Running migration for a legacy Angular version' });
|
||||
await legacyMigrationFn();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
output.success({
|
||||
title:
|
||||
'✅ The Angular version is compatible with the latest version of Nx!',
|
||||
});
|
||||
|
||||
output.log({ title: '🐳 Nx initialization' });
|
||||
const cacheableOperations = await collectCacheableOperations();
|
||||
const useNxCloud = parsedArgs.yes !== true ? await askAboutNxCloud() : false;
|
||||
|
||||
output.log({ title: '📦 Installing dependencies' });
|
||||
installDependencies(useNxCloud);
|
||||
|
||||
output.log({ title: '📝 Setting up workspace' });
|
||||
await setupWorkspace(cacheableOperations);
|
||||
|
||||
if (useNxCloud) {
|
||||
output.log({ title: '🛠️ Setting up Nx Cloud' });
|
||||
initCloud(repoRoot, 'nx-init-angular');
|
||||
}
|
||||
|
||||
output.success({
|
||||
title: '🎉 Nx is now enabled in your workspace!',
|
||||
bodyLines: [
|
||||
`Execute 'npx nx build' twice to see the computation caching in action.`,
|
||||
'Learn more about the changes done to your workspace at https://nx.dev/recipes/adopting-nx/migration-angular.',
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async function collectCacheableOperations(): Promise<string[]> {
|
||||
let cacheableOperations: string[];
|
||||
|
||||
workspaceTargets = getWorkspaceTargets();
|
||||
const defaultCacheableTargetsInWorkspace = defaultCacheableOperations.filter(
|
||||
(t) => workspaceTargets.includes(t)
|
||||
);
|
||||
|
||||
if (parsedArgs.yes !== true) {
|
||||
output.log({
|
||||
title: `🧑🔧 Please answer the following questions about the targets found in your angular.json in order to generate task runner configuration`,
|
||||
});
|
||||
|
||||
cacheableOperations = (
|
||||
(await prompt([
|
||||
{
|
||||
type: 'multiselect',
|
||||
name: 'cacheableOperations',
|
||||
initial: defaultCacheableTargetsInWorkspace as any,
|
||||
message:
|
||||
'Which of the following targets are cacheable? (Produce the same output given the same input, e.g. build, test and lint usually are, serve and start are not)',
|
||||
// enquirer mutates the array below, create a new one to avoid it
|
||||
choices: [...workspaceTargets],
|
||||
},
|
||||
])) as any
|
||||
).cacheableOperations;
|
||||
} else {
|
||||
cacheableOperations = parsedArgs.cacheable
|
||||
? parsedArgs.cacheable.split(',')
|
||||
: defaultCacheableTargetsInWorkspace;
|
||||
}
|
||||
|
||||
return cacheableOperations;
|
||||
}
|
||||
|
||||
function installDependencies(useNxCloud: boolean): void {
|
||||
addDepsToPackageJson(repoRoot, useNxCloud);
|
||||
addPluginDependencies();
|
||||
runInstall(repoRoot);
|
||||
}
|
||||
|
||||
function addPluginDependencies(): void {
|
||||
const packageJsonPath = join(repoRoot, 'package.json');
|
||||
const packageJson = readJsonFile<PackageJson>(packageJsonPath);
|
||||
const nxVersion = require('../../../package.json').version;
|
||||
|
||||
packageJson.devDependencies ??= {};
|
||||
packageJson.devDependencies['@nrwl/angular'] = nxVersion;
|
||||
packageJson.devDependencies['@nrwl/workspace'] = nxVersion;
|
||||
|
||||
const peerDepsToInstall = [
|
||||
'@angular-devkit/core',
|
||||
'@angular-devkit/schematics',
|
||||
'@schematics/angular',
|
||||
];
|
||||
const angularCliVersion =
|
||||
packageJson.devDependencies['@angular/cli'] ??
|
||||
packageJson.dependencies?.['@angular/cli'] ??
|
||||
packageJson.devDependencies['@angular-devkit/build-angular'] ??
|
||||
packageJson.dependencies?.['@angular-devkit/build-angular'];
|
||||
|
||||
for (const dep of peerDepsToInstall) {
|
||||
if (!packageJson.devDependencies[dep] && !packageJson.dependencies?.[dep]) {
|
||||
packageJson.devDependencies[dep] = angularCliVersion;
|
||||
}
|
||||
}
|
||||
|
||||
packageJson.devDependencies = sortObjectByKeys(packageJson.devDependencies);
|
||||
|
||||
writeJsonFile(packageJsonPath, packageJson);
|
||||
}
|
||||
|
||||
type AngularJsonConfigTargetConfiguration = Exclude<
|
||||
TargetConfiguration,
|
||||
'command' | 'executor' | 'outputs' | 'dependsOn' | 'inputs'
|
||||
> & {
|
||||
builder: string;
|
||||
};
|
||||
type AngularJsonProjectConfiguration = {
|
||||
root: string;
|
||||
sourceRoot: string;
|
||||
architect?: Record<string, AngularJsonConfigTargetConfiguration>;
|
||||
};
|
||||
interface AngularJsonConfig {
|
||||
projects: Record<string, AngularJsonProjectConfiguration>;
|
||||
defaultProject?: string;
|
||||
}
|
||||
|
||||
async function setupWorkspace(cacheableOperations: string[]): Promise<void> {
|
||||
const angularJsonPath = join(repoRoot, 'angular.json');
|
||||
const angularJson = readJsonFile<AngularJsonConfig>(angularJsonPath);
|
||||
const workspaceCapabilities = getWorkspaceCapabilities(angularJson.projects);
|
||||
createNxJson(angularJson, cacheableOperations, workspaceCapabilities);
|
||||
addVsCodeRecommendedExtensions(
|
||||
repoRoot,
|
||||
[
|
||||
'nrwl.angular-console',
|
||||
'angular.ng-template',
|
||||
workspaceCapabilities.eslintProjectConfigFile
|
||||
? 'dbaeumer.vscode-eslint'
|
||||
: undefined,
|
||||
].filter(Boolean)
|
||||
);
|
||||
replaceNgWithNxInPackageJsonScripts();
|
||||
|
||||
// convert workspace config format to standalone project configs
|
||||
// update its targets outputs and delete angular.json
|
||||
const projects = toNewFormat(angularJson).projects;
|
||||
for (const [projectName, project] of Object.entries(projects)) {
|
||||
updateProjectOutputs(project);
|
||||
writeJsonFile(join(project.root, 'project.json'), {
|
||||
$schema: normalizePath(
|
||||
relative(
|
||||
join(repoRoot, project.root),
|
||||
join(repoRoot, 'node_modules/nx/schemas/project-schema.json')
|
||||
)
|
||||
),
|
||||
name: projectName,
|
||||
...project,
|
||||
root: undefined,
|
||||
});
|
||||
}
|
||||
unlinkSync(angularJsonPath);
|
||||
}
|
||||
|
||||
type WorkspaceCapabilities = {
|
||||
eslintProjectConfigFile: boolean;
|
||||
test: boolean;
|
||||
karmaProjectConfigFile: boolean;
|
||||
};
|
||||
|
||||
function createNxJson(
|
||||
angularJson: AngularJsonConfig,
|
||||
cacheableOperations: string[],
|
||||
{
|
||||
eslintProjectConfigFile,
|
||||
test,
|
||||
karmaProjectConfigFile,
|
||||
}: WorkspaceCapabilities
|
||||
): void {
|
||||
createNxJsonFile(repoRoot, [], cacheableOperations, {});
|
||||
|
||||
const nxJson = readJsonFile<NxJsonConfiguration>(join(repoRoot, 'nx.json'));
|
||||
nxJson.namedInputs = {
|
||||
sharedGlobals: [],
|
||||
default: ['{projectRoot}/**/*', 'sharedGlobals'],
|
||||
production: [
|
||||
'default',
|
||||
...(test
|
||||
? [
|
||||
'!{projectRoot}/tsconfig.spec.json',
|
||||
'!{projectRoot}/**/*.spec.[jt]s',
|
||||
karmaProjectConfigFile ? '!{projectRoot}/karma.conf.js' : undefined,
|
||||
].filter(Boolean)
|
||||
: []),
|
||||
eslintProjectConfigFile ? '!{projectRoot}/.eslintrc.json' : undefined,
|
||||
].filter(Boolean),
|
||||
};
|
||||
nxJson.targetDefaults = {};
|
||||
if (workspaceTargets.includes('build')) {
|
||||
nxJson.targetDefaults.build = {
|
||||
dependsOn: ['^build'],
|
||||
inputs: ['production', '^production'],
|
||||
};
|
||||
}
|
||||
if (workspaceTargets.includes('server')) {
|
||||
nxJson.targetDefaults.server = { inputs: ['production', '^production'] };
|
||||
}
|
||||
if (workspaceTargets.includes('test')) {
|
||||
const inputs = ['default', '^production'];
|
||||
if (fileExists(join(repoRoot, 'karma.conf.js'))) {
|
||||
inputs.push('{workspaceRoot}/karma.conf.js');
|
||||
}
|
||||
nxJson.targetDefaults.test = { inputs };
|
||||
}
|
||||
if (workspaceTargets.includes('lint')) {
|
||||
const inputs = ['default'];
|
||||
if (fileExists(join(repoRoot, '.eslintrc.json'))) {
|
||||
inputs.push('{workspaceRoot}/.eslintrc.json');
|
||||
}
|
||||
nxJson.targetDefaults.lint = { inputs };
|
||||
}
|
||||
if (workspaceTargets.includes('e2e')) {
|
||||
nxJson.targetDefaults.e2e = { inputs: ['default', '^production'] };
|
||||
}
|
||||
// Angular 14 workspaces support defaultProject, keep it until we drop support
|
||||
nxJson.defaultProject = angularJson.defaultProject;
|
||||
writeJsonFile(join(repoRoot, 'nx.json'), nxJson);
|
||||
}
|
||||
|
||||
function updateProjectOutputs(project: ProjectConfiguration): void {
|
||||
Object.values(project.targets ?? {}).forEach((target) => {
|
||||
if (
|
||||
[
|
||||
'@angular-devkit/build-angular:browser',
|
||||
'@angular-builders/custom-webpack:browser',
|
||||
'ngx-build-plus:browser',
|
||||
'@angular-devkit/build-angular:server',
|
||||
'@angular-builders/custom-webpack:server',
|
||||
'ngx-build-plus:server',
|
||||
].includes(target.executor)
|
||||
) {
|
||||
target.outputs = ['{options.outputPath}'];
|
||||
} else if (target.executor === '@angular-eslint/builder:lint') {
|
||||
target.outputs = ['{options.outputFile}'];
|
||||
} else if (target.executor === '@angular-devkit/build-angular:ng-packagr') {
|
||||
try {
|
||||
const ngPackageJsonPath = join(repoRoot, target.options.project);
|
||||
const ngPackageJson = readJsonFile(ngPackageJsonPath);
|
||||
const outputPath = relative(
|
||||
repoRoot,
|
||||
resolve(dirname(ngPackageJsonPath), ngPackageJson.dest)
|
||||
);
|
||||
target.outputs = [`{workspaceRoot}/${normalizePath(outputPath)}`];
|
||||
} catch {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getWorkspaceTargets(): string[] {
|
||||
const { projects } = readJsonFile<AngularJsonConfig>(
|
||||
join(repoRoot, 'angular.json')
|
||||
);
|
||||
const targets = new Set<string>();
|
||||
for (const project of Object.values(projects ?? {})) {
|
||||
for (const target of Object.keys(project.architect ?? {})) {
|
||||
targets.add(target);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(targets);
|
||||
}
|
||||
|
||||
function getWorkspaceCapabilities(
|
||||
projects: Record<string, AngularJsonProjectConfiguration>
|
||||
): WorkspaceCapabilities {
|
||||
const capabilities = {
|
||||
eslintProjectConfigFile: false,
|
||||
test: false,
|
||||
karmaProjectConfigFile: false,
|
||||
};
|
||||
|
||||
for (const project of Object.values(projects)) {
|
||||
if (
|
||||
!capabilities.eslintProjectConfigFile &&
|
||||
projectHasEslintConfig(project)
|
||||
) {
|
||||
capabilities.eslintProjectConfigFile = true;
|
||||
}
|
||||
if (!capabilities.test && projectUsesKarmaBuilder(project)) {
|
||||
capabilities.test = true;
|
||||
}
|
||||
if (
|
||||
!capabilities.karmaProjectConfigFile &&
|
||||
projectHasKarmaConfig(project)
|
||||
) {
|
||||
capabilities.karmaProjectConfigFile = true;
|
||||
}
|
||||
|
||||
if (
|
||||
capabilities.eslintProjectConfigFile &&
|
||||
capabilities.test &&
|
||||
capabilities.karmaProjectConfigFile
|
||||
) {
|
||||
return capabilities;
|
||||
}
|
||||
}
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
function projectUsesKarmaBuilder(
|
||||
project: AngularJsonProjectConfiguration
|
||||
): boolean {
|
||||
return Object.values(project.architect ?? {}).some(
|
||||
(target) => target.builder === '@angular-devkit/build-angular:karma'
|
||||
);
|
||||
}
|
||||
|
||||
function projectHasKarmaConfig(
|
||||
project: AngularJsonProjectConfiguration
|
||||
): boolean {
|
||||
return fileExists(join(project.root, 'karma.conf.js'));
|
||||
}
|
||||
|
||||
function projectHasEslintConfig(
|
||||
project: AngularJsonProjectConfiguration
|
||||
): boolean {
|
||||
return fileExists(join(project.root, '.eslintrc.json'));
|
||||
}
|
||||
|
||||
function replaceNgWithNxInPackageJsonScripts(): void {
|
||||
const packageJsonPath = join(repoRoot, 'package.json');
|
||||
const packageJson = readJsonFile<PackageJson>(packageJsonPath);
|
||||
packageJson.scripts ??= {};
|
||||
Object.keys(packageJson.scripts).forEach((script) => {
|
||||
packageJson.scripts[script] = packageJson.scripts[script]
|
||||
.replace(/^nx$/, 'nx')
|
||||
.replace(/^ng /, 'nx ')
|
||||
.replace(/ ng /g, ' nx ');
|
||||
});
|
||||
writeJsonFile(packageJsonPath, packageJson);
|
||||
}
|
||||
133
packages/nx/src/nx-init/angular/legacy-angular-versions.ts
Normal file
133
packages/nx/src/nx-init/angular/legacy-angular-versions.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import { execSync } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import { gte, major } from 'semver';
|
||||
import { readJsonFile, writeJsonFile } from '../../utils/fileutils';
|
||||
import { sortObjectByKeys } from '../../utils/object-sort';
|
||||
import { output } from '../../utils/output';
|
||||
import { readModulePackageJson } from '../../utils/package-json';
|
||||
import {
|
||||
PackageManagerCommands,
|
||||
getPackageManagerCommand,
|
||||
resolvePackageVersionUsingInstallation,
|
||||
resolvePackageVersionUsingRegistry,
|
||||
} from '../../utils/package-manager';
|
||||
import { askAboutNxCloud, initCloud } from '../utils';
|
||||
|
||||
// map of Angular major versions to Nx versions to use for legacy `nx init` migrations,
|
||||
// key is major Angular version and value is Nx version to use
|
||||
const nxAngularLegacyVersionMap: Record<number, string> = {};
|
||||
// min major angular version supported in latest Nx
|
||||
const minMajorAngularVersionSupported = 14;
|
||||
// version when the Nx CLI changed from @nrwl/tao & @nrwl/cli to nx
|
||||
const versionWithConsolidatedPackages = '13.9.0';
|
||||
|
||||
export async function getLegacyMigrationFunctionIfApplicable(
|
||||
repoRoot: string,
|
||||
interactive: boolean
|
||||
): Promise<() => Promise<void> | null> {
|
||||
const angularVersion =
|
||||
readModulePackageJson('@angular/core').packageJson.version;
|
||||
const majorAngularVersion = major(angularVersion);
|
||||
if (majorAngularVersion > minMajorAngularVersionSupported) {
|
||||
// non-legacy
|
||||
return null;
|
||||
}
|
||||
|
||||
let legacyMigrationCommand: string;
|
||||
let pkgName: string;
|
||||
let pkgVersion: string;
|
||||
if (majorAngularVersion < 13) {
|
||||
// for versions lower than 13, the migration was in @nrwl/workspace:ng-add
|
||||
pkgName = '@nrwl/workspace';
|
||||
pkgVersion = await resolvePackageVersion(
|
||||
pkgName,
|
||||
`^${majorAngularVersion}.0.0`
|
||||
);
|
||||
legacyMigrationCommand = `ng g ${pkgName}:ng-add --preserveAngularCLILayout`;
|
||||
} else if (majorAngularVersion < 14) {
|
||||
// for v13, the migration was in @nrwl/angular:ng-add
|
||||
pkgName = '@nrwl/angular';
|
||||
pkgVersion = await resolvePackageVersion(pkgName, '~14.1.0');
|
||||
legacyMigrationCommand = `ng g ${pkgName}:ng-add --preserve-angular-cli-layout`;
|
||||
} else {
|
||||
// use the latest Nx version that supported the Angular version
|
||||
legacyMigrationCommand = `nx@${
|
||||
nxAngularLegacyVersionMap[majorAngularVersion]
|
||||
} init ${process.argv.slice(2).join(' ')}`;
|
||||
}
|
||||
|
||||
return async () => {
|
||||
output.log({ title: '🐳 Nx initialization' });
|
||||
const useNxCloud = interactive ? await askAboutNxCloud() : false;
|
||||
|
||||
output.log({ title: '📦 Installing dependencies' });
|
||||
const pmc = getPackageManagerCommand();
|
||||
await installDependencies(repoRoot, pkgName, pkgVersion, useNxCloud, pmc);
|
||||
|
||||
output.log({ title: '📝 Setting up workspace' });
|
||||
execSync(`${pmc.exec} ${legacyMigrationCommand}`, { stdio: [0, 1, 2] });
|
||||
|
||||
if (useNxCloud) {
|
||||
output.log({ title: '🛠️ Setting up Nx Cloud' });
|
||||
initCloud(repoRoot, 'nx-init-angular');
|
||||
}
|
||||
|
||||
output.success({
|
||||
title: '🎉 Nx is now enabled in your workspace!',
|
||||
bodyLines: [
|
||||
`Execute 'npx nx build' twice to see the computation caching in action.`,
|
||||
'Learn more about the changes done to your workspace at https://nx.dev/recipes/adopting-nx/migration-angular.',
|
||||
],
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async function installDependencies(
|
||||
repoRoot: string,
|
||||
pkgName: string,
|
||||
pkgVersion: string,
|
||||
useNxCloud: boolean,
|
||||
pmc: PackageManagerCommands
|
||||
): Promise<void> {
|
||||
const json = readJsonFile(join(repoRoot, 'package.json'));
|
||||
|
||||
json.devDependencies ??= {};
|
||||
json.devDependencies['@nrwl/workspace'] = pkgVersion;
|
||||
|
||||
if (gte(pkgVersion, versionWithConsolidatedPackages)) {
|
||||
json.devDependencies['nx'] = pkgVersion;
|
||||
} else {
|
||||
json.devDependencies['@nrwl/cli'] = pkgVersion;
|
||||
json.devDependencies['@nrwl/tao'] = pkgVersion;
|
||||
}
|
||||
|
||||
if (useNxCloud) {
|
||||
// get the latest @nrwl/nx-cloud version compatible with the Nx major
|
||||
// version being installed
|
||||
json.devDependencies['@nrwl/nx-cloud'] = await resolvePackageVersion(
|
||||
'@nrwl/nx-cloud',
|
||||
`^${major(pkgVersion)}.0.0`
|
||||
);
|
||||
}
|
||||
json.devDependencies = sortObjectByKeys(json.devDependencies);
|
||||
|
||||
if (pkgName === '@nrwl/angular') {
|
||||
json.dependencies ??= {};
|
||||
json.dependencies['@nrwl/angular'] = pkgVersion;
|
||||
json.dependencies = sortObjectByKeys(json.dependencies);
|
||||
}
|
||||
writeJsonFile(`package.json`, json);
|
||||
|
||||
execSync(pmc.install, { stdio: [0, 1, 2] });
|
||||
}
|
||||
|
||||
async function resolvePackageVersion(
|
||||
packageName: string,
|
||||
version: string
|
||||
): Promise<string> {
|
||||
try {
|
||||
return await resolvePackageVersionUsingRegistry(packageName, version);
|
||||
} catch {
|
||||
return await resolvePackageVersionUsingInstallation(packageName, version);
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
import { joinPathFragments } from '../utils/path';
|
||||
import { readJsonFile, writeJsonFile } from '../utils/fileutils';
|
||||
import * as enquirer from 'enquirer';
|
||||
import { execSync } from 'child_process';
|
||||
import * as enquirer from 'enquirer';
|
||||
import { join } from 'path';
|
||||
import { fileExists, readJsonFile, writeJsonFile } from '../utils/fileutils';
|
||||
import {
|
||||
getPackageManagerCommand,
|
||||
PackageManagerCommands,
|
||||
} from '../utils/package-manager';
|
||||
import { runNxSync } from '../utils/child-process';
|
||||
import { joinPathFragments } from '../utils/path';
|
||||
|
||||
export function askAboutNxCloud() {
|
||||
return enquirer
|
||||
@ -35,8 +36,7 @@ export function createNxJsonFile(
|
||||
repoRoot: string,
|
||||
targetDefaults: string[],
|
||||
cacheableOperations: string[],
|
||||
scriptOutputs: { [name: string]: string },
|
||||
defaultProject: string | undefined
|
||||
scriptOutputs: { [name: string]: string }
|
||||
) {
|
||||
const nxJsonPath = joinPathFragments(repoRoot, 'nx.json');
|
||||
let nxJson = {} as any;
|
||||
@ -121,9 +121,42 @@ export function runInstall(
|
||||
execSync(pmc.install, { stdio: [0, 1, 2], cwd: repoRoot });
|
||||
}
|
||||
|
||||
export function initCloud(repoRoot: string) {
|
||||
runNxSync(`g @nrwl/nx-cloud:init --installationSource=add-nx-to-monorepo`, {
|
||||
stdio: [0, 1, 2],
|
||||
cwd: repoRoot,
|
||||
});
|
||||
export function initCloud(
|
||||
repoRoot: string,
|
||||
installationSource:
|
||||
| 'nx-init-angular'
|
||||
| 'nx-init-cra'
|
||||
| 'nx-init-monorepo'
|
||||
| 'nx-init-nest'
|
||||
| 'nx-init-npm-repo'
|
||||
) {
|
||||
runNxSync(
|
||||
`g @nrwl/nx-cloud:init --installationSource=${installationSource}`,
|
||||
{
|
||||
stdio: [0, 1, 2],
|
||||
cwd: repoRoot,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function addVsCodeRecommendedExtensions(
|
||||
repoRoot: string,
|
||||
extensions: string[]
|
||||
): void {
|
||||
const vsCodeExtensionsPath = join(repoRoot, '.vscode/extensions.json');
|
||||
|
||||
if (fileExists(vsCodeExtensionsPath)) {
|
||||
const vsCodeExtensionsJson = readJsonFile(vsCodeExtensionsPath);
|
||||
|
||||
vsCodeExtensionsJson.recommendations ??= [];
|
||||
extensions.forEach((extension) => {
|
||||
if (!vsCodeExtensionsJson.recommendations.includes(extension)) {
|
||||
vsCodeExtensionsJson.recommendations.push(extension);
|
||||
}
|
||||
});
|
||||
|
||||
writeJsonFile(vsCodeExtensionsPath, vsCodeExtensionsJson);
|
||||
} else {
|
||||
writeJsonFile(vsCodeExtensionsPath, { recommendations: extensions });
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user