236 lines
7.9 KiB
JavaScript
236 lines
7.9 KiB
JavaScript
|
#!/bin/env node
|
||
|
'use strict';
|
||
|
|
||
|
// Imports
|
||
|
const chalk = require('chalk');
|
||
|
const fs = require('fs-extra');
|
||
|
const path = require('canonical-path');
|
||
|
const shelljs = require('shelljs');
|
||
|
const yargs = require('yargs');
|
||
|
|
||
|
// Config
|
||
|
shelljs.set('-e');
|
||
|
|
||
|
// Constants
|
||
|
const ROOT_DIR = path.resolve(__dirname, '../..');
|
||
|
const PACKAGES_DIR = path.join(ROOT_DIR, 'packages');
|
||
|
const PACKAGES_DIST_DIR = path.join(ROOT_DIR, 'dist/packages-dist');
|
||
|
const NG_LOCAL_FILENAME = '.ng-local';
|
||
|
|
||
|
// Classes
|
||
|
class NgPackagesInstaller {
|
||
|
constructor() {
|
||
|
// Properties - Protected
|
||
|
|
||
|
/**
|
||
|
* A sorted list of Angular package names.
|
||
|
* (Detected as directories in '/packages/' that contain a top-level 'package.json' file.)
|
||
|
*/
|
||
|
this.ngPackages = shelljs.
|
||
|
find(PACKAGES_DIR).
|
||
|
map(path => path.slice(PACKAGES_DIR.length + 1)).
|
||
|
filter(path => /^[^/]+\/package.json$/.test(path)).
|
||
|
map(path => path.slice(0, -13)).
|
||
|
sort();
|
||
|
}
|
||
|
|
||
|
// Methods - Public
|
||
|
|
||
|
/**
|
||
|
* Check whether the Angular packages installed in the specified `rootDir`'s 'node_modules/' come from npm and print a
|
||
|
* warning if not.
|
||
|
* @param {string} rootDir - The root directory whose npm dependencies will be checked.
|
||
|
*/
|
||
|
checkPackages(rootDir) {
|
||
|
rootDir = path.resolve(rootDir);
|
||
|
const localPackages = this._findLocalPackages(rootDir);
|
||
|
|
||
|
if (localPackages.length) {
|
||
|
const relativeScriptPath = path.relative('.', __filename.replace(/\.js$/, ''));
|
||
|
const relativeRootDir = path.relative('.', rootDir) || '.';
|
||
|
const restoreCmd = `node ${relativeScriptPath} restore ${relativeRootDir}`;
|
||
|
|
||
|
// Log a warning.
|
||
|
console.warn(chalk.yellow([
|
||
|
'',
|
||
|
'!'.repeat(110),
|
||
|
'!!!',
|
||
|
'!!! WARNING',
|
||
|
'!!!',
|
||
|
`!!! The following packages have been overwritten in '${rootDir}/node_modules/' with the locally built ones:`,
|
||
|
'!!!',
|
||
|
...localPackages.map(pkg => `!!! - @angular/${pkg}`),
|
||
|
'!!!',
|
||
|
'!!! To restore the packages run:',
|
||
|
'!!!',
|
||
|
`!!! ${restoreCmd}`,
|
||
|
'!!!',
|
||
|
'!'.repeat(110),
|
||
|
'',
|
||
|
].join('\n')));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Overwrite the Angular packages installed in the specified `rootDir`'s 'node_modules/' with the locally built ones.
|
||
|
* @param {string} rootDir - The root directory whose npm dependencies will be overwritten.
|
||
|
*/
|
||
|
overwritePackages(rootDir) {
|
||
|
rootDir = path.resolve(rootDir);
|
||
|
const nodeModulesDir = path.join(rootDir, 'node_modules');
|
||
|
|
||
|
this.ngPackages.forEach(packageName => this._overwritePackage(packageName, nodeModulesDir));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that the Angular packages installed in the specified `rootDir`'s 'node_modules/' come from npm.
|
||
|
* (If necessary, re-install the Angular packages using `yarn`.)
|
||
|
* @param {string} rootDir - The root directory whose npm dependencies will be restored.
|
||
|
*/
|
||
|
restorePackages(rootDir) {
|
||
|
rootDir = path.resolve(rootDir);
|
||
|
const localPackages = this._findLocalPackages(rootDir);
|
||
|
|
||
|
if (localPackages.length) {
|
||
|
this._reinstallOverwrittenNodeModules(rootDir);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Methods - Protected
|
||
|
|
||
|
/**
|
||
|
* Find and return all Angular packages installed in the specified `rootDir`'s 'node_modules/' that have been
|
||
|
* overwritten with the locally built ones.
|
||
|
* @param {string} rootDir - The root directory whose npm dependencies will be checked.
|
||
|
* @return {string[]} - A list of overwritten package names.
|
||
|
*/
|
||
|
_findLocalPackages(rootDir) {
|
||
|
const nodeModulesDir = path.join(rootDir, 'node_modules');
|
||
|
const localPackages = this.ngPackages.filter(packageName => this._isLocalPackage(packageName, nodeModulesDir));
|
||
|
|
||
|
this._log(`Local packages found: ${localPackages.join(', ') || '-'}`);
|
||
|
|
||
|
return localPackages;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check whether an installed Angular package from `nodeModulesDir` has been overwritten with a
|
||
|
* locally built package.
|
||
|
* @param {string} packageName - The name of the package to check.
|
||
|
* @param {string} nodeModulesDir - The target `node_modules/` directory.
|
||
|
* @return {boolean} - True if the package has been overwritten or false otherwise.
|
||
|
*/
|
||
|
_isLocalPackage(packageName, nodeModulesDir) {
|
||
|
const targetPackageDir = path.join(nodeModulesDir, '@angular', packageName);
|
||
|
const localFlagFile = path.join(targetPackageDir, NG_LOCAL_FILENAME);
|
||
|
const isLocal = fs.existsSync(localFlagFile);
|
||
|
|
||
|
this._log(`Checking package '${packageName}' (${targetPackageDir})... local: ${isLocal}`);
|
||
|
|
||
|
return isLocal;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Log a message if the `debug` property is set to true.
|
||
|
* @param {string} message - The message to be logged.
|
||
|
*/
|
||
|
_log(message) {
|
||
|
if (this.debug) {
|
||
|
const indent = ' ';
|
||
|
console.info(`${indent}[${NgPackagesInstaller.name}]: ${message.split('\n').join(`\n${indent}`)}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse and validate the input and invoke the appropriate command.
|
||
|
*/
|
||
|
_main() {
|
||
|
const preCommand = argv => {
|
||
|
this.debug = argv.debug;
|
||
|
|
||
|
const availablePackages = this.ngPackages.map(pkg => `\n - @angular/${pkg}`).join('') || '-';
|
||
|
this._log(`Available Angular packages: ${availablePackages}`);
|
||
|
};
|
||
|
|
||
|
yargs.
|
||
|
usage('$0 <cmd> <args>').
|
||
|
command(
|
||
|
'check <projectDir> [--debug]',
|
||
|
'Check whether the Angular packages installed as dependencies of `projectDir` come from npm and print a ' +
|
||
|
'warning if not.',
|
||
|
{
|
||
|
debug: {describe: 'Print debug information.'}
|
||
|
},
|
||
|
argv => {
|
||
|
preCommand(argv);
|
||
|
this.checkPackages(argv.projectDir);
|
||
|
}).
|
||
|
command(
|
||
|
'overwrite <projectDir> [--debug]',
|
||
|
'Overwrite the Angular packages installed as dependencies of `projectDir` with the locally built ones.',
|
||
|
{
|
||
|
debug: {describe: 'Print debug information.'}
|
||
|
},
|
||
|
argv => {
|
||
|
preCommand(argv);
|
||
|
this.overwritePackages(argv.projectDir);
|
||
|
}).
|
||
|
command(
|
||
|
'restore <projectDir> [--debug]',
|
||
|
'Ensure that the Angular packages installed as dependencies of `projectDir` come from npm.',
|
||
|
{
|
||
|
debug: {describe: 'Print debug information.'}
|
||
|
},
|
||
|
argv => {
|
||
|
preCommand(argv);
|
||
|
this.restorePackages(argv.projectDir);
|
||
|
}).
|
||
|
demandCommand(1, 'Please supply a command from the list above.').
|
||
|
strict().
|
||
|
argv;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove an installed Angular package from `nodeModulesDir` and replace it with the locally built
|
||
|
* one. Mark the package by adding an `.ng-local` file in the target directory.
|
||
|
* @param {string} packageName - The name of the package to overwrite.
|
||
|
* @param {string} nodeModulesDir - The target `node_modules/` directory.
|
||
|
*/
|
||
|
_overwritePackage(packageName, nodeModulesDir) {
|
||
|
const sourcePackageDir = path.join(PACKAGES_DIST_DIR, packageName);
|
||
|
const targetPackageDir = path.join(nodeModulesDir, '@angular', packageName);
|
||
|
const localFlagFile = path.join(targetPackageDir, NG_LOCAL_FILENAME);
|
||
|
|
||
|
this._log(`Overwriting package '${packageName}' (${sourcePackageDir} --> ${targetPackageDir})...`);
|
||
|
|
||
|
if (fs.existsSync(targetPackageDir)) {
|
||
|
shelljs.rm('-rf', targetPackageDir);
|
||
|
fs.copySync(sourcePackageDir, targetPackageDir);
|
||
|
fs.writeFileSync(localFlagFile, '');
|
||
|
} else {
|
||
|
this._log(' Nothing to overwrite - the package is not installed...');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Re-install overwritten npm dependencies using `yarn`. Removes the `.yarn-integrity` file to ensure `yarn` detects
|
||
|
* the overwritten packages.
|
||
|
* @param {string} rootDir - The root directory whose npm dependencies will be re-installed.
|
||
|
*/
|
||
|
_reinstallOverwrittenNodeModules(rootDir) {
|
||
|
const installCmd = 'yarn install --check-files';
|
||
|
|
||
|
this._log(`Running '${installCmd}' in '${rootDir}'...`);
|
||
|
shelljs.exec(installCmd, {cwd: rootDir});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Exports
|
||
|
module.exports = new NgPackagesInstaller();
|
||
|
|
||
|
// Run
|
||
|
if (require.main === module) {
|
||
|
// This file was run directly; run the main function.
|
||
|
module.exports._main();
|
||
|
}
|