mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-14 17:43:52 +08:00
334 lines
10 KiB
TypeScript
334 lines
10 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. 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
|
|
*/
|
|
// tslint:disable:no-implicit-dependencies
|
|
import { JsonObject, logging } from '@angular-devkit/core';
|
|
import * as fs from 'fs';
|
|
import * as glob from 'glob';
|
|
import * as path from 'path';
|
|
import * as ts from 'typescript';
|
|
import { packages } from '../lib/packages';
|
|
|
|
const minimatch = require('minimatch');
|
|
const tar = require('tar');
|
|
|
|
const gitIgnore = fs.readFileSync(path.join(__dirname, '../.gitignore'), 'utf-8')
|
|
.split('\n')
|
|
.map(line => line.replace(/#.*/, ''))
|
|
.filter(line => !line.match(/^\s*$/));
|
|
|
|
|
|
function _gitIgnoreMatch(p: string) {
|
|
p = path.relative(path.dirname(__dirname), p);
|
|
|
|
return gitIgnore.some(line => minimatch(p, line));
|
|
}
|
|
|
|
|
|
function _mkdirp(p: string) {
|
|
// Create parent folder if necessary.
|
|
if (!fs.existsSync(path.dirname(p))) {
|
|
_mkdirp(path.dirname(p));
|
|
}
|
|
fs.mkdirSync(p);
|
|
}
|
|
|
|
|
|
function _tar(out: string, dir: string) {
|
|
return tar.create({
|
|
gzip: true,
|
|
strict: true,
|
|
portable: true,
|
|
cwd: dir,
|
|
file: out,
|
|
sync: true,
|
|
}, ['.']);
|
|
}
|
|
|
|
|
|
function _copy(from: string, to: string) {
|
|
// Create parent folder if necessary.
|
|
if (!fs.existsSync(path.dirname(to))) {
|
|
_mkdirp(path.dirname(to));
|
|
}
|
|
|
|
from = path.relative(process.cwd(), from);
|
|
to = path.relative(process.cwd(), to);
|
|
|
|
const buffer = fs.readFileSync(from);
|
|
fs.writeFileSync(to, buffer);
|
|
}
|
|
|
|
|
|
function _recursiveCopy(from: string, to: string, logger: logging.Logger) {
|
|
if (!fs.existsSync(from)) {
|
|
logger.error(`File "${from}" does not exist.`);
|
|
process.exit(4);
|
|
}
|
|
if (fs.statSync(from).isDirectory()) {
|
|
fs.readdirSync(from).forEach(fileName => {
|
|
_recursiveCopy(path.join(from, fileName), path.join(to, fileName), logger);
|
|
});
|
|
} else {
|
|
_copy(from, to);
|
|
}
|
|
}
|
|
|
|
|
|
function _rm(p: string) {
|
|
p = path.relative(process.cwd(), p);
|
|
fs.unlinkSync(p);
|
|
}
|
|
|
|
|
|
function _rimraf(p: string) {
|
|
glob.sync(path.join(p, '**/*'), { dot: true, nodir: true })
|
|
.forEach(p => fs.unlinkSync(p));
|
|
glob.sync(path.join(p, '**/*'), { dot: true })
|
|
.sort((a, b) => b.length - a.length)
|
|
.forEach(p => fs.rmdirSync(p));
|
|
}
|
|
|
|
|
|
function _clean(logger: logging.Logger) {
|
|
logger.info('Cleaning...');
|
|
logger.info(' Removing dist/...');
|
|
_rimraf(path.join(__dirname, '../dist'));
|
|
}
|
|
|
|
|
|
function _sortPackages() {
|
|
// Order packages in order of dependency.
|
|
// We use bubble sort because we need a full topological sort but adding another dependency
|
|
// or implementing a full topo sort would be too much work and I'm lazy. We don't anticipate
|
|
// any large number of
|
|
const sortedPackages = Object.keys(packages);
|
|
let swapped = false;
|
|
do {
|
|
swapped = false;
|
|
for (let i = 0; i < sortedPackages.length - 1; i++) {
|
|
for (let j = i + 1; j < sortedPackages.length; j++) {
|
|
const a = sortedPackages[i];
|
|
const b = sortedPackages[j];
|
|
|
|
if (packages[a].dependencies.indexOf(b) != -1) {
|
|
// Swap them.
|
|
[sortedPackages[i], sortedPackages[i + 1]]
|
|
= [sortedPackages[i + 1], sortedPackages[i]];
|
|
swapped = true;
|
|
}
|
|
}
|
|
}
|
|
} while (swapped);
|
|
|
|
return sortedPackages;
|
|
}
|
|
|
|
|
|
function _build(logger: logging.Logger) {
|
|
logger.info('Building...');
|
|
const tsConfigPath = path.relative(process.cwd(), path.join(__dirname, '../tsconfig.json'));
|
|
// Load the Compiler Options.
|
|
const tsConfig = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
|
|
const parsedTsConfig = ts.parseJsonConfigFileContent(tsConfig.config, ts.sys, '.');
|
|
|
|
// Create the program and emit.
|
|
const program = ts.createProgram(parsedTsConfig.fileNames, parsedTsConfig.options);
|
|
const result = program.emit();
|
|
if (result.emitSkipped) {
|
|
logger.error(`TypeScript compiler failed:`);
|
|
const diagLogger = logger.createChild('diagnostics');
|
|
result.diagnostics.forEach(diagnostic => {
|
|
const messageText = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
|
|
|
if (diagnostic.file) {
|
|
const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start || 0);
|
|
const fileName = diagnostic.file.fileName;
|
|
const { line, character } = position;
|
|
diagLogger.error(`${fileName} (${line + 1},${character + 1}): ${messageText}`);
|
|
} else {
|
|
diagLogger.error(messageText);
|
|
}
|
|
});
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
export default function(argv: { local?: boolean, snapshot?: boolean }, logger: logging.Logger) {
|
|
_clean(logger);
|
|
|
|
const sortedPackages = _sortPackages();
|
|
_build(logger);
|
|
|
|
logger.info('Moving packages to dist/');
|
|
const packageLogger = logger.createChild('packages');
|
|
for (const packageName of sortedPackages) {
|
|
packageLogger.info(packageName);
|
|
const pkg = packages[packageName];
|
|
_recursiveCopy(pkg.build, pkg.dist, logger);
|
|
_rimraf(pkg.build);
|
|
}
|
|
|
|
logger.info('Copying resources...');
|
|
const resourceLogger = logger.createChild('resources');
|
|
for (const packageName of sortedPackages) {
|
|
resourceLogger.info(packageName);
|
|
const pkg = packages[packageName];
|
|
const pkgJson = pkg.packageJson;
|
|
const files = glob.sync(path.join(pkg.root, '**/*'), { dot: true, nodir: true });
|
|
const subSubLogger = resourceLogger.createChild(packageName);
|
|
subSubLogger.info(`${files.length} files total...`);
|
|
const resources = files
|
|
.map((fileName) => path.relative(pkg.root, fileName))
|
|
.filter(fileName => {
|
|
if (/(?:^|[\/\\])node_modules[\/\\]/.test(fileName)) {
|
|
return false;
|
|
}
|
|
|
|
// Schematics template files.
|
|
if (pkgJson['schematics'] &&
|
|
(fileName.match(/(\/|\\)files(\/|\\)/) || fileName.match(/(\/|\\)\w+-files(\/|\\)/))) {
|
|
return true;
|
|
}
|
|
|
|
if (fileName.endsWith('package.json')) {
|
|
return true;
|
|
}
|
|
|
|
// Remove Bazel files from NPM.
|
|
if (fileName.endsWith('BUILD')) {
|
|
return false;
|
|
}
|
|
|
|
// Skip sources.
|
|
if (fileName.endsWith('.ts') && !fileName.endsWith('.d.ts')) {
|
|
// Verify that it was actually built.
|
|
if (!fs.existsSync(path.join(pkg.dist, fileName).replace(/ts$/, 'js'))) {
|
|
subSubLogger.error(`\nSource found but compiled file not found: "${fileName}".`);
|
|
process.exit(2);
|
|
}
|
|
|
|
// Skip all sources.
|
|
return false;
|
|
}
|
|
|
|
// Skip tsconfig only.
|
|
if (fileName.endsWith('tsconfig.json')) {
|
|
return false;
|
|
}
|
|
|
|
// Skip files from gitignore.
|
|
if (_gitIgnoreMatch(fileName)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
subSubLogger.info(`${resources.length} resources...`);
|
|
resources.forEach(fileName => {
|
|
_copy(path.join(pkg.root, fileName), path.join(pkg.dist, fileName));
|
|
});
|
|
}
|
|
|
|
logger.info('Copying extra resources...');
|
|
for (const packageName of sortedPackages) {
|
|
const pkg = packages[packageName];
|
|
_copy(path.join(__dirname, '../LICENSE'), path.join(pkg.dist, 'LICENSE'));
|
|
}
|
|
|
|
logger.info('Removing spec files...');
|
|
const specLogger = logger.createChild('specfiles');
|
|
for (const packageName of sortedPackages) {
|
|
specLogger.info(packageName);
|
|
const pkg = packages[packageName];
|
|
const files = glob.sync(path.join(pkg.dist, '**/*_spec?(_large).@(js|d.ts)'));
|
|
specLogger.info(` ${files.length} spec files found...`);
|
|
files.forEach(fileName => _rm(fileName));
|
|
}
|
|
|
|
logger.info('Building ejs templates...');
|
|
const templateLogger = logger.createChild('templates');
|
|
const templateCompiler = require('@angular-devkit/core').template;
|
|
for (const packageName of sortedPackages) {
|
|
templateLogger.info(packageName);
|
|
const pkg = packages[packageName];
|
|
const files = glob.sync(path.join(pkg.dist, '**/*.ejs'));
|
|
templateLogger.info(` ${files.length} ejs files found...`);
|
|
files.forEach(fileName => {
|
|
const p = path.relative(
|
|
path.dirname(__dirname),
|
|
path.join(pkg.root, path.relative(pkg.dist, fileName)),
|
|
);
|
|
const fn = templateCompiler(fs.readFileSync(fileName).toString(), {
|
|
module: true,
|
|
sourceURL: p,
|
|
sourceMap: true,
|
|
sourceRoot: path.join(__dirname, '..'),
|
|
fileName: fileName.replace(/\.ejs$/, '.js'),
|
|
});
|
|
_rm(fileName);
|
|
fs.writeFileSync(fileName.replace(/\.ejs$/, '.js'), fn.source);
|
|
});
|
|
}
|
|
|
|
logger.info('Setting versions...');
|
|
|
|
const versionLogger = logger.createChild('versions');
|
|
for (const packageName of sortedPackages) {
|
|
versionLogger.info(packageName);
|
|
const pkg = packages[packageName];
|
|
const packageJsonPath = path.join(pkg.dist, 'package.json');
|
|
const packageJson = pkg.packageJson;
|
|
const version = pkg.version;
|
|
|
|
if (version) {
|
|
packageJson['version'] = version;
|
|
} else {
|
|
versionLogger.error('No version found... Only updating dependencies.');
|
|
}
|
|
|
|
for (const depName of Object.keys(packages)) {
|
|
const v = packages[depName].version;
|
|
for (const depKey of ['dependencies', 'peerDependencies', 'devDependencies']) {
|
|
const obj = packageJson[depKey] as JsonObject | null;
|
|
if (obj && obj[depName]) {
|
|
if (argv.local) {
|
|
obj[depName] = packages[depName].tar;
|
|
} else if (argv.snapshot) {
|
|
const pkg = packages[depName];
|
|
if (!pkg.snapshotRepo) {
|
|
versionLogger.error(
|
|
`Package ${JSON.stringify(depName)} is not published as a snapshot. `
|
|
+ `Fixing to current version ${v}.`,
|
|
);
|
|
obj[depName] = v;
|
|
} else {
|
|
obj[depName] = `github:${pkg.snapshotRepo}#${pkg.snapshotHash}`;
|
|
}
|
|
} else if ((obj[depName] as string).match(/\b0\.0\.0\b/)) {
|
|
obj[depName] = (obj[depName] as string).replace(/\b0\.0\.0\b/, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
}
|
|
|
|
logger.info('Tarring all packages...');
|
|
const tarLogger = logger.createChild('license');
|
|
Object.keys(packages).forEach(pkgName => {
|
|
const pkg = packages[pkgName];
|
|
tarLogger.info(`${pkgName} => ${pkg.tar}`);
|
|
_tar(pkg.tar, pkg.dist);
|
|
});
|
|
|
|
logger.info(`Done.`);
|
|
}
|