mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-17 19:13:34 +08:00
Node.js v16 will be entering an LTS state prior to the release of Angular v13 which allows Node.js v16 to be marked as officially supported by the Angular CLI. The initial bootstrapping check now adds Node.js v16 to the output message in the event of an unsupported Node.js version. NOTE: Prior to the final v13 release, the Node.js v16 minor should be updated to the actual LTS version once available.
255 lines
7.2 KiB
TypeScript
255 lines
7.2 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import { JsonObject } from '@angular-devkit/core';
|
|
import { execSync } from 'child_process';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import * as ts from 'typescript';
|
|
|
|
const distRoot = path.join(__dirname, '../dist');
|
|
const { packages: monorepoPackages } = require('../.monorepo.json');
|
|
|
|
export interface PackageInfo {
|
|
name: string;
|
|
root: string;
|
|
bin: { [name: string]: string };
|
|
relative: string;
|
|
main: string;
|
|
dist: string;
|
|
build: string;
|
|
tar: string;
|
|
private: boolean;
|
|
experimental: boolean;
|
|
packageJson: JsonObject;
|
|
dependencies: string[];
|
|
reverseDependencies: string[];
|
|
|
|
snapshot: boolean;
|
|
snapshotRepo: string;
|
|
snapshotHash: string;
|
|
|
|
version: string;
|
|
}
|
|
export type PackageMap = { [name: string]: PackageInfo };
|
|
|
|
export function loadRootPackageJson() {
|
|
return require('../package.json');
|
|
}
|
|
|
|
function loadPackageJson(p: string) {
|
|
const root = loadRootPackageJson();
|
|
const pkg = require(p);
|
|
|
|
for (const key of Object.keys(root)) {
|
|
switch (key) {
|
|
// Keep the following keys from the package.json of the package itself.
|
|
case 'bin':
|
|
case 'description':
|
|
case 'dependencies':
|
|
case 'name':
|
|
case 'main':
|
|
case 'peerDependencies':
|
|
case 'optionalDependencies':
|
|
case 'typings':
|
|
case 'version':
|
|
case 'private':
|
|
case 'workspaces':
|
|
case 'resolutions':
|
|
case 'scripts':
|
|
continue;
|
|
|
|
// Remove the following keys from the package.json.
|
|
case 'devDependencies':
|
|
delete pkg[key];
|
|
continue;
|
|
|
|
// Merge the following keys with the root package.json.
|
|
case 'keywords':
|
|
const a = pkg[key] || [];
|
|
const b = Object.keys(
|
|
root[key].concat(a).reduce((acc: { [k: string]: boolean }, curr: string) => {
|
|
acc[curr] = true;
|
|
|
|
return acc;
|
|
}, {}),
|
|
);
|
|
pkg[key] = b;
|
|
break;
|
|
|
|
// Overwrite engines to a common default.
|
|
case 'engines':
|
|
pkg['engines'] = {
|
|
'node': '^12.20.0 || ^14.15.0 || >=16.10.0',
|
|
'npm': '^6.11.0 || ^7.5.6',
|
|
'yarn': '>= 1.13.0',
|
|
};
|
|
break;
|
|
|
|
// Overwrite the package's key with to root one.
|
|
default:
|
|
pkg[key] = root[key];
|
|
}
|
|
}
|
|
|
|
return pkg;
|
|
}
|
|
|
|
function* _findPrimaryPackageJsonFiles(dir: string, exclude: RegExp): Iterable<string> {
|
|
const subdirectories = [];
|
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
const p = path.join(dir, entry.name);
|
|
|
|
if (exclude.test(p)) {
|
|
continue;
|
|
}
|
|
|
|
if (entry.isDirectory() && entry.name !== 'node_modules') {
|
|
subdirectories.push(p);
|
|
continue;
|
|
}
|
|
|
|
if (entry.name === 'package.json') {
|
|
yield p;
|
|
|
|
// First package.json found will be the package's root package.json
|
|
// Secondary entrypoint package.json files should not be found here
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (const directory of subdirectories) {
|
|
yield* _findPrimaryPackageJsonFiles(directory, exclude);
|
|
}
|
|
}
|
|
|
|
const tsConfigPath = path.join(__dirname, '../tsconfig.json');
|
|
const tsConfig = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
|
|
const pattern =
|
|
'^(' +
|
|
(tsConfig.config.exclude as string[])
|
|
.map((ex) => path.join(path.dirname(tsConfigPath), ex))
|
|
.map(
|
|
(ex) =>
|
|
'(' +
|
|
ex
|
|
.replace(/[-[\]{}()+?./\\^$|]/g, '\\$&')
|
|
.replace(/(\\\\|\\\/)\*\*/g, '((/|\\\\).+?)?')
|
|
.replace(/\*/g, '[^/\\\\]*') +
|
|
')',
|
|
)
|
|
.join('|') +
|
|
')($|/|\\\\)';
|
|
const excludeRe = new RegExp(pattern);
|
|
|
|
// Find all the package.json that aren't excluded from tsconfig.
|
|
const packageJsonPaths = [
|
|
..._findPrimaryPackageJsonFiles(path.join(__dirname, '..', 'packages'), excludeRe),
|
|
];
|
|
|
|
function _exec(cmd: string) {
|
|
return execSync(cmd).toString().trim();
|
|
}
|
|
|
|
let gitShaCache: string;
|
|
function _getSnapshotHash(_pkg: PackageInfo): string {
|
|
if (!gitShaCache) {
|
|
gitShaCache = _exec('git log --format=%h -n1');
|
|
}
|
|
|
|
return gitShaCache;
|
|
}
|
|
|
|
const stableVersion = loadRootPackageJson().version;
|
|
const experimentalVersion = stableToExperimentalVersion(stableVersion);
|
|
|
|
/**
|
|
* Convert a stable version to its experimental equivalent. For example,
|
|
* stable = 10.2.3, experimental = 0.1002.3
|
|
* @param stable Must begin with d+.d+ where d is a 0-9 digit.
|
|
*/
|
|
export function stableToExperimentalVersion(stable: string): string {
|
|
return `0.${stable.replace(/^(\d+)\.(\d+)/, (_, major, minor) => {
|
|
return '' + (parseInt(major, 10) * 100 + parseInt(minor, 10));
|
|
})}`;
|
|
}
|
|
|
|
// All the supported packages. Go through the packages directory and create a map of
|
|
// name => PackageInfo. This map is partial as it lacks some information that requires the
|
|
// map itself to finish building.
|
|
export const packages: PackageMap = packageJsonPaths
|
|
.map((pkgPath) => ({ root: path.dirname(pkgPath) }))
|
|
.reduce((packages: PackageMap, pkg) => {
|
|
const pkgRoot = pkg.root;
|
|
const packageJson = loadPackageJson(path.join(pkgRoot, 'package.json'));
|
|
const name = packageJson['name'];
|
|
if (!name) {
|
|
// Only build the entry if there's a package name.
|
|
return packages;
|
|
}
|
|
if (!(name in monorepoPackages)) {
|
|
throw new Error(
|
|
`Package ${name} found in ${JSON.stringify(pkg.root)}, not found in .monorepo.json.`,
|
|
);
|
|
}
|
|
|
|
const bin: { [name: string]: string } = {};
|
|
Object.keys(packageJson['bin'] || {}).forEach((binName) => {
|
|
let p = path.resolve(pkg.root, packageJson['bin'][binName]);
|
|
if (!fs.existsSync(p)) {
|
|
p = p.replace(/\.js$/, '.ts');
|
|
}
|
|
bin[binName] = p;
|
|
});
|
|
|
|
const experimental = !!packageJson.private || !!packageJson.experimental;
|
|
|
|
packages[name] = {
|
|
build: path.join(distRoot, pkgRoot.substr(path.dirname(__dirname).length)),
|
|
dist: path.join(distRoot, name),
|
|
root: pkgRoot,
|
|
relative: path.relative(path.dirname(__dirname), pkgRoot),
|
|
main: path.resolve(pkgRoot, 'src/index.ts'),
|
|
private: !!packageJson.private,
|
|
experimental,
|
|
// yarn doesn't take kindly to @ in tgz filenames
|
|
// https://github.com/yarnpkg/yarn/issues/6339
|
|
tar: path.join(distRoot, name.replace(/\/|@/g, '_') + '.tgz'),
|
|
bin,
|
|
name,
|
|
packageJson,
|
|
|
|
snapshot: !!monorepoPackages[name].snapshotRepo,
|
|
snapshotRepo: monorepoPackages[name].snapshotRepo,
|
|
get snapshotHash() {
|
|
return _getSnapshotHash(this);
|
|
},
|
|
|
|
dependencies: [],
|
|
reverseDependencies: [],
|
|
version: experimental ? experimentalVersion : stableVersion,
|
|
};
|
|
|
|
return packages;
|
|
}, {});
|
|
|
|
// Update with dependencies.
|
|
for (const pkgName of Object.keys(packages)) {
|
|
const pkg = packages[pkgName];
|
|
const pkgJson = require(path.join(pkg.root, 'package.json'));
|
|
pkg.dependencies = Object.keys(packages).filter((name) => {
|
|
return name in (pkgJson.dependencies || {}) || name in (pkgJson.devDependencies || {});
|
|
});
|
|
pkg.dependencies.forEach((depName) => packages[depName].reverseDependencies.push(pkgName));
|
|
}
|
|
|
|
/** The set of packages which are built and released. */
|
|
export const releasePackages = Object.fromEntries(
|
|
Object.entries(packages).filter(([_, pkg]) => !pkg.private),
|
|
);
|