ci(aio): use custom package.json to run with local distributables (#19511)
Closes #19388 PR Close #19511
This commit is contained in:
parent
9fe6363575
commit
d1a00459a8
|
@ -4,14 +4,11 @@ const path = require('canonical-path');
|
||||||
const shelljs = require('shelljs');
|
const shelljs = require('shelljs');
|
||||||
const yargs = require('yargs');
|
const yargs = require('yargs');
|
||||||
|
|
||||||
const ngPackagesInstaller = require('../ng-packages-installer');
|
|
||||||
|
|
||||||
const SHARED_PATH = path.resolve(__dirname, 'shared');
|
const SHARED_PATH = path.resolve(__dirname, 'shared');
|
||||||
const SHARED_NODE_MODULES_PATH = path.resolve(SHARED_PATH, 'node_modules');
|
const SHARED_NODE_MODULES_PATH = path.resolve(SHARED_PATH, 'node_modules');
|
||||||
const BOILERPLATE_BASE_PATH = path.resolve(SHARED_PATH, 'boilerplate');
|
const BOILERPLATE_BASE_PATH = path.resolve(SHARED_PATH, 'boilerplate');
|
||||||
const BOILERPLATE_COMMON_BASE_PATH = path.resolve(BOILERPLATE_BASE_PATH, 'common');
|
const BOILERPLATE_COMMON_BASE_PATH = path.resolve(BOILERPLATE_BASE_PATH, 'common');
|
||||||
const EXAMPLES_BASE_PATH = path.resolve(__dirname, '../../content/examples');
|
const EXAMPLES_BASE_PATH = path.resolve(__dirname, '../../content/examples');
|
||||||
const TESTING_BASE_PATH = path.resolve(EXAMPLES_BASE_PATH, 'testing');
|
|
||||||
|
|
||||||
const BOILERPLATE_PATHS = {
|
const BOILERPLATE_PATHS = {
|
||||||
cli: [
|
cli: [
|
||||||
|
@ -99,13 +96,9 @@ class ExampleBoilerPlate {
|
||||||
}
|
}
|
||||||
|
|
||||||
installNodeModules(basePath, useLocal) {
|
installNodeModules(basePath, useLocal) {
|
||||||
shelljs.exec('yarn', {cwd: basePath});
|
const tool = 'node tools/ng-packages-installer';
|
||||||
|
const command = useLocal ? 'overwrite' : 'restore';
|
||||||
if (useLocal) {
|
shelljs.exec([tool, command, basePath, '--debug'].join(' '));
|
||||||
ngPackagesInstaller.overwritePackages(basePath);
|
|
||||||
} else {
|
|
||||||
ngPackagesInstaller.restorePackages(basePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFoldersContaining(basePath, filename, ignore) {
|
getFoldersContaining(basePath, filename, ignore) {
|
||||||
|
|
|
@ -3,7 +3,6 @@ const fs = require('fs-extra');
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
const shelljs = require('shelljs');
|
const shelljs = require('shelljs');
|
||||||
|
|
||||||
const ngPackagesInstaller = require('../ng-packages-installer');
|
|
||||||
const exampleBoilerPlate = require('./example-boilerplate');
|
const exampleBoilerPlate = require('./example-boilerplate');
|
||||||
|
|
||||||
describe('example-boilerplate tool', () => {
|
describe('example-boilerplate tool', () => {
|
||||||
|
@ -99,31 +98,22 @@ describe('example-boilerplate tool', () => {
|
||||||
describe('installNodeModules', () => {
|
describe('installNodeModules', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(shelljs, 'exec');
|
spyOn(shelljs, 'exec');
|
||||||
spyOn(ngPackagesInstaller, 'overwritePackages');
|
|
||||||
spyOn(ngPackagesInstaller, 'restorePackages');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should run `yarn` in the base path', () => {
|
|
||||||
exampleBoilerPlate.installNodeModules('some/base/path');
|
|
||||||
expect(shelljs.exec).toHaveBeenCalledWith('yarn', { cwd: 'some/base/path' });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should overwrite the Angular packages if `useLocal` is true', () => {
|
it('should overwrite the Angular packages if `useLocal` is true', () => {
|
||||||
ngPackagesInstaller.overwritePackages.and.callFake(() => expect(shelljs.exec).toHaveBeenCalled());
|
|
||||||
|
|
||||||
exampleBoilerPlate.installNodeModules('some/base/path', true);
|
exampleBoilerPlate.installNodeModules('some/base/path', true);
|
||||||
expect(ngPackagesInstaller.overwritePackages).toHaveBeenCalledWith('some/base/path');
|
expect(shelljs.exec).toHaveBeenCalledWith('node tools/ng-packages-installer overwrite some/base/path --debug');
|
||||||
expect(ngPackagesInstaller.restorePackages).not.toHaveBeenCalled();
|
expect(shelljs.exec.calls.count()).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should restore the Angular packages if `useLocal` is not true', () => {
|
it('should restore the Angular packages if `useLocal` is not true', () => {
|
||||||
exampleBoilerPlate.installNodeModules('some/base/path1');
|
exampleBoilerPlate.installNodeModules('some/base/path1');
|
||||||
expect(ngPackagesInstaller.restorePackages).toHaveBeenCalledWith('some/base/path1');
|
expect(shelljs.exec).toHaveBeenCalledWith('node tools/ng-packages-installer restore some/base/path1 --debug');
|
||||||
|
|
||||||
exampleBoilerPlate.installNodeModules('some/base/path2', false);
|
exampleBoilerPlate.installNodeModules('some/base/path2', false);
|
||||||
expect(ngPackagesInstaller.restorePackages).toHaveBeenCalledWith('some/base/path2');
|
expect(shelljs.exec).toHaveBeenCalledWith('node tools/ng-packages-installer restore some/base/path2 --debug');
|
||||||
|
|
||||||
expect(ngPackagesInstaller.overwritePackages).not.toHaveBeenCalled();
|
expect(shelljs.exec.calls.count()).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,235 +0,0 @@
|
||||||
#!/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();
|
|
||||||
}
|
|
|
@ -1,286 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const path = require('canonical-path');
|
|
||||||
const shelljs = require('shelljs');
|
|
||||||
|
|
||||||
const installer = require('./ng-packages-installer');
|
|
||||||
|
|
||||||
describe('NgPackagesInstaller', () => {
|
|
||||||
const ngPackages = installer.ngPackages;
|
|
||||||
const rootDir = 'root/dir';
|
|
||||||
const absoluteRootDir = path.resolve(rootDir);
|
|
||||||
const nodeModulesDir = `${absoluteRootDir}/node_modules`;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
spyOn(fs, 'copySync');
|
|
||||||
spyOn(fs, 'existsSync');
|
|
||||||
spyOn(fs, 'writeFileSync');
|
|
||||||
spyOn(shelljs, 'exec');
|
|
||||||
spyOn(shelljs, 'rm');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
|
|
||||||
describe('.ngPackages', () => {
|
|
||||||
it('should include all package names', () => {
|
|
||||||
// For example...
|
|
||||||
expect(installer.ngPackages).toContain('common');
|
|
||||||
expect(installer.ngPackages).toContain('core');
|
|
||||||
expect(installer.ngPackages).toContain('router');
|
|
||||||
expect(installer.ngPackages).toContain('upgrade');
|
|
||||||
|
|
||||||
expect(installer.ngPackages).not.toContain('static');
|
|
||||||
expect(installer.ngPackages).not.toContain('upgrade/static');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should correspond to package directories with top-level \'package.json\' files', () => {
|
|
||||||
fs.existsSync.and.callThrough();
|
|
||||||
|
|
||||||
const packagesDir = path.resolve(__dirname, '../../packages');
|
|
||||||
|
|
||||||
installer.ngPackages.forEach(packageName => {
|
|
||||||
const packageJson = `${packagesDir}/${packageName}/package.json`;
|
|
||||||
expect(fs.existsSync(packageJson)).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
|
|
||||||
describe('checkPackages()', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
spyOn(console, 'warn');
|
|
||||||
spyOn(installer, '_findLocalPackages');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check whether there are any local Angular packages in the target directory', () => {
|
|
||||||
installer._findLocalPackages.and.returnValue([]);
|
|
||||||
|
|
||||||
installer.checkPackages(rootDir);
|
|
||||||
expect(installer._findLocalPackages).toHaveBeenCalledWith(absoluteRootDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not print a warning if all Angular packages come from npm', () => {
|
|
||||||
installer._findLocalPackages.and.returnValue([]);
|
|
||||||
|
|
||||||
installer.checkPackages(rootDir);
|
|
||||||
expect(console.warn).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when there are local Angular packages', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
installer._findLocalPackages.and.returnValue(['common', 'router']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should print a warning', () => {
|
|
||||||
installer.checkPackages(rootDir);
|
|
||||||
|
|
||||||
expect(console.warn).toHaveBeenCalled();
|
|
||||||
expect(console.warn.calls.mostRecent().args[0]).toContain('WARNING');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should list the local (i.e. overwritten) packages', () => {
|
|
||||||
installer.checkPackages(rootDir);
|
|
||||||
|
|
||||||
const warning = console.warn.calls.mostRecent().args[0];
|
|
||||||
expect(warning).toContain('@angular/common');
|
|
||||||
expect(warning).toContain('@angular/router');
|
|
||||||
expect(warning).not.toContain('@angular/core');
|
|
||||||
expect(warning).not.toContain('@angular/upgrade');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should mention the command to restore the Angular packages', () => {
|
|
||||||
// When run for the current working directory...
|
|
||||||
const dir1 = '.';
|
|
||||||
const restoreCmdRe1 = RegExp('\\bnode .*?ng-packages-installer restore \\.');
|
|
||||||
|
|
||||||
installer.checkPackages(dir1);
|
|
||||||
|
|
||||||
expect(console.warn.calls.argsFor(0)[0]).toMatch(restoreCmdRe1);
|
|
||||||
|
|
||||||
// When run for a different directory...
|
|
||||||
const dir2 = rootDir;
|
|
||||||
const restoreCmdRe2 = RegExp(`\\bnode .*?ng-packages-installer restore .*?${path.normalize(rootDir)}\\b`);
|
|
||||||
|
|
||||||
installer.checkPackages(dir2);
|
|
||||||
|
|
||||||
expect(console.warn.calls.argsFor(1)[0]).toMatch(restoreCmdRe2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('overwritePackages()', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
spyOn(installer, '_overwritePackage');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should override the Angular packages in the target directory with the locally built ones', () => {
|
|
||||||
installer.overwritePackages(rootDir);
|
|
||||||
expect(installer._overwritePackage).toHaveBeenCalledTimes(ngPackages.length);
|
|
||||||
|
|
||||||
ngPackages.forEach(packageName =>
|
|
||||||
expect(installer._overwritePackage).toHaveBeenCalledWith(packageName, nodeModulesDir));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('restorePackages()', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
spyOn(installer, '_findLocalPackages');
|
|
||||||
spyOn(installer, '_reinstallOverwrittenNodeModules');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check whether there are any local Angular packages in the target directory first', () => {
|
|
||||||
installer._findLocalPackages.and.callFake(() => {
|
|
||||||
expect(installer._reinstallOverwrittenNodeModules).not.toHaveBeenCalled();
|
|
||||||
return [];
|
|
||||||
});
|
|
||||||
|
|
||||||
installer.restorePackages(rootDir);
|
|
||||||
expect(installer._findLocalPackages).toHaveBeenCalledWith(absoluteRootDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should re-install dependencies from npm afterwards (if necessary)', () => {
|
|
||||||
// No local packages.
|
|
||||||
installer._findLocalPackages.and.returnValue([]);
|
|
||||||
|
|
||||||
installer.restorePackages(rootDir);
|
|
||||||
expect(installer._reinstallOverwrittenNodeModules).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
// All local packages.
|
|
||||||
installer._reinstallOverwrittenNodeModules.calls.reset();
|
|
||||||
installer._findLocalPackages.and.returnValue(ngPackages);
|
|
||||||
|
|
||||||
installer.restorePackages(rootDir);
|
|
||||||
expect(installer._reinstallOverwrittenNodeModules).toHaveBeenCalledWith(absoluteRootDir);
|
|
||||||
|
|
||||||
// Some local packages.
|
|
||||||
installer._reinstallOverwrittenNodeModules.calls.reset();
|
|
||||||
installer._findLocalPackages.and.returnValue(['common', 'core', 'router', 'upgrade']);
|
|
||||||
|
|
||||||
installer.restorePackages(rootDir);
|
|
||||||
expect(installer._reinstallOverwrittenNodeModules).toHaveBeenCalledWith(absoluteRootDir);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('_findLocalPackages()', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
spyOn(installer, '_isLocalPackage');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check all Angular packages', () => {
|
|
||||||
installer._findLocalPackages(absoluteRootDir);
|
|
||||||
|
|
||||||
ngPackages.forEach(packageName =>
|
|
||||||
expect(installer._isLocalPackage).toHaveBeenCalledWith(packageName, nodeModulesDir));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return an empty list if all Angular packages come from npm', () => {
|
|
||||||
installer._isLocalPackage.and.returnValue(false);
|
|
||||||
expect(installer._findLocalPackages(rootDir)).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return a list of all local (i.e. overwritten) Angular packages', () => {
|
|
||||||
const localPackages = ['common', 'core', 'router', 'upgrade'];
|
|
||||||
|
|
||||||
installer._isLocalPackage.and.callFake(packageName => localPackages.includes(packageName));
|
|
||||||
|
|
||||||
expect(installer._findLocalPackages(rootDir)).toEqual(localPackages);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('_isLocalPackage()', () => {
|
|
||||||
it('should check whether the specified package is local/overwritten', () => {
|
|
||||||
const targetPackageDir = `${rootDir}/@angular/somePackage`;
|
|
||||||
const localFlagFile = `${targetPackageDir}/.ng-local`;
|
|
||||||
|
|
||||||
installer._isLocalPackage('somePackage', rootDir);
|
|
||||||
expect(fs.existsSync).toHaveBeenCalledWith(localFlagFile);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return whether the specified package was local', () => {
|
|
||||||
fs.existsSync.and.returnValues(true, false);
|
|
||||||
|
|
||||||
expect(installer._isLocalPackage('somePackage', rootDir)).toBe(true);
|
|
||||||
expect(installer._isLocalPackage('somePackage', rootDir)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('_log()', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
spyOn(console, 'info');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
installer.debug = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should log a message to the console if the `debug` property is true', () => {
|
|
||||||
installer._log('foo');
|
|
||||||
expect(console.info).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
installer.debug = true;
|
|
||||||
installer._log('bar');
|
|
||||||
expect(console.info).toHaveBeenCalledWith(' [NgPackagesInstaller]: bar');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('_overwritePackage()', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
fs.existsSync.and.returnValue(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check whether the Angular package is installed', () => {
|
|
||||||
const targetPackageDir = `${rootDir}/@angular/somePackage`;
|
|
||||||
|
|
||||||
installer._overwritePackage('somePackage', rootDir);
|
|
||||||
expect(fs.existsSync).toHaveBeenCalledWith(targetPackageDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove the original package from the target directory', () => {
|
|
||||||
const targetPackageDir = `${rootDir}/@angular/somePackage`;
|
|
||||||
|
|
||||||
shelljs.rm.and.callFake(() => expect(fs.existsSync).toHaveBeenCalled());
|
|
||||||
|
|
||||||
installer._overwritePackage('somePackage', rootDir);
|
|
||||||
expect(shelljs.rm).toHaveBeenCalledWith('-rf', targetPackageDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should copy the source package directory to the target directory', () => {
|
|
||||||
const sourcePackageDir = path.resolve(__dirname, '../../dist/packages-dist/somePackage');
|
|
||||||
const targetPackageDir = `${rootDir}/@angular/somePackage`;
|
|
||||||
|
|
||||||
fs.copySync.and.callFake(() => expect(shelljs.rm).toHaveBeenCalled());
|
|
||||||
|
|
||||||
installer._overwritePackage('somePackage', rootDir);
|
|
||||||
expect(fs.copySync).toHaveBeenCalledWith(sourcePackageDir, targetPackageDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add an empty `.ng-local` file to the target directory', () => {
|
|
||||||
const targetPackageDir = `${rootDir}/@angular/somePackage`;
|
|
||||||
const localFlagFile = `${targetPackageDir}/.ng-local`;
|
|
||||||
|
|
||||||
fs.writeFileSync.and.callFake(() => expect(fs.copySync).toHaveBeenCalled());
|
|
||||||
|
|
||||||
installer._overwritePackage('somePackage', rootDir);
|
|
||||||
expect(fs.writeFileSync).toHaveBeenCalledWith(localFlagFile, '');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should do nothing if the Angular package is not installed', () => {
|
|
||||||
fs.existsSync.and.returnValue(false);
|
|
||||||
|
|
||||||
installer._overwritePackage('somePackage', rootDir);
|
|
||||||
|
|
||||||
expect(shelljs.rm).not.toHaveBeenCalled();
|
|
||||||
expect(fs.copySync).not.toHaveBeenCalled();
|
|
||||||
expect(fs.writeFileSync).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('_reinstallOverwrittenNodeModules()', () => {
|
|
||||||
it('should run `yarn install --check-files` in the specified directory', () => {
|
|
||||||
installer._reinstallOverwrittenNodeModules(rootDir);
|
|
||||||
expect(shelljs.exec).toHaveBeenCalledWith('yarn install --check-files', {cwd: rootDir});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('canonical-path');
|
||||||
|
const shelljs = require('shelljs');
|
||||||
|
const yargs = require('yargs');
|
||||||
|
|
||||||
|
const PACKAGE_JSON = 'package.json';
|
||||||
|
const LOCKFILE = 'yarn.lock';
|
||||||
|
const LOCAL_MARKER_PATH = 'node_modules/_local_.json';
|
||||||
|
const PACKAGE_JSON_REGEX = /^[^/]+\/package\.json$/;
|
||||||
|
|
||||||
|
const ANGULAR_ROOT_DIR = path.resolve(__dirname, '../../..');
|
||||||
|
const ANGULAR_DIST_PACKAGES = path.resolve(ANGULAR_ROOT_DIR, 'dist/packages-dist');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tool that can install Angular dependencies for a project from NPM or from the
|
||||||
|
* locally built distributables.
|
||||||
|
*
|
||||||
|
* This tool is used to change dependencies of the `aio` application and the example
|
||||||
|
* applications.
|
||||||
|
*/
|
||||||
|
class NgPackagesInstaller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new installer for a project in the specified directory.
|
||||||
|
*
|
||||||
|
* @param {string} projectDir - the path to the directory containing the project.
|
||||||
|
* @param {object} options - a hash of options for the install
|
||||||
|
* * `debug` (`boolean`) - whether to display debug messages.
|
||||||
|
* * `force` (`boolean`) - whether to force a local installation
|
||||||
|
* even if there is a local marker file.
|
||||||
|
* * `ignorePackages` (`string[]`) - a collection of names of packages
|
||||||
|
* that should not be copied over.
|
||||||
|
*/
|
||||||
|
constructor(projectDir, options = {}) {
|
||||||
|
this.debug = options.debug;
|
||||||
|
this.force = options.force;
|
||||||
|
this.projectDir = path.resolve(projectDir);
|
||||||
|
this.localMarkerPath = path.resolve(this.projectDir, LOCAL_MARKER_PATH);
|
||||||
|
|
||||||
|
this._log('Project directory:', this.projectDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the dependencies have been overridden with locally built
|
||||||
|
* Angular packages. This is done by checking for the `_local_.json` marker file.
|
||||||
|
* This will emit a warning to the console if the dependencies have been overridden.
|
||||||
|
*/
|
||||||
|
checkDependencies() {
|
||||||
|
if (this._checkLocalMarker()) {
|
||||||
|
this._printWarning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install locally built Angular dependencies, overriding the dependencies in the package.json
|
||||||
|
* This will also write a "marker" file (`_local_.json`), which contains the overridden package.json
|
||||||
|
* contents and acts as an indicator that dependencies have been overridden.
|
||||||
|
*/
|
||||||
|
installLocalDependencies() {
|
||||||
|
if (this._checkLocalMarker() !== true || this.force) {
|
||||||
|
const pathToPackageConfig = path.resolve(this.projectDir, PACKAGE_JSON);
|
||||||
|
const packages = this._getDistPackages();
|
||||||
|
const packageConfigFile = fs.readFileSync(pathToPackageConfig);
|
||||||
|
const packageConfig = JSON.parse(packageConfigFile);
|
||||||
|
|
||||||
|
const [dependencies, peers] = this._collectDependencies(packageConfig.dependencies || {}, packages);
|
||||||
|
const [devDependencies, devPeers] = this._collectDependencies(packageConfig.devDependencies || {}, packages);
|
||||||
|
|
||||||
|
this._assignPeerDependencies(peers, dependencies, devDependencies);
|
||||||
|
this._assignPeerDependencies(devPeers, dependencies, devDependencies);
|
||||||
|
|
||||||
|
const localPackageConfig = Object.assign(Object.create(null), packageConfig, { dependencies, devDependencies });
|
||||||
|
localPackageConfig.__angular = { local: true };
|
||||||
|
const localPackageConfigJson = JSON.stringify(localPackageConfig, null, 2);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._log(`Writing temporary local ${PACKAGE_JSON} to ${pathToPackageConfig}`);
|
||||||
|
fs.writeFileSync(pathToPackageConfig, localPackageConfigJson);
|
||||||
|
this._installDeps('--no-lockfile', '--check-files');
|
||||||
|
this._setLocalMarker(localPackageConfigJson);
|
||||||
|
} finally {
|
||||||
|
this._log(`Restoring original ${PACKAGE_JSON} to ${pathToPackageConfig}`);
|
||||||
|
fs.writeFileSync(pathToPackageConfig, packageConfigFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reinstall the original package.json depdendencies
|
||||||
|
* Yarn will also delete the local marker file for us.
|
||||||
|
*/
|
||||||
|
restoreNpmDependencies() {
|
||||||
|
this._installDeps('--check-files');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected helpers
|
||||||
|
|
||||||
|
_assignPeerDependencies(peerDependencies, dependencies, devDependencies) {
|
||||||
|
Object.keys(peerDependencies).forEach(key => {
|
||||||
|
// If there is already an equivalent dependency then override it - otherwise assign/override the devDependency
|
||||||
|
if (dependencies[key]) {
|
||||||
|
this._log(`Overriding dependency with peerDependency: ${key}: ${peerDependencies[key]}`);
|
||||||
|
dependencies[key] = peerDependencies[key];
|
||||||
|
} else {
|
||||||
|
this._log(`${devDependencies[key] ? 'Overriding' : 'Assigning'} devDependency with peerDependency: ${key}: ${peerDependencies[key]}`);
|
||||||
|
devDependencies[key] = peerDependencies[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_collectDependencies(dependencies, packages) {
|
||||||
|
const peerDependencies = Object.create(null);
|
||||||
|
const mergedDependencies = Object.assign(Object.create(null), dependencies);
|
||||||
|
|
||||||
|
Object.keys(dependencies).forEach(key => {
|
||||||
|
const sourcePackage = packages[key];
|
||||||
|
if (sourcePackage) {
|
||||||
|
// point the core Angular packages at the distributable folder
|
||||||
|
mergedDependencies[key] = `file:${ANGULAR_DIST_PACKAGES}/${key.replace('@angular/', '')}`;
|
||||||
|
this._log(`Overriding dependency with local package: ${key}: ${mergedDependencies[key]}`);
|
||||||
|
// grab peer dependencies
|
||||||
|
Object.keys(sourcePackage.peerDependencies || {})
|
||||||
|
// ignore peerDependencies which are already core Angular packages
|
||||||
|
.filter(key => !packages[key])
|
||||||
|
.forEach(key => peerDependencies[key] = sourcePackage.peerDependencies[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return [mergedDependencies, peerDependencies];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A hash of Angular package configs.
|
||||||
|
* (Detected as directories in '/packages/' that contain a top-level 'package.json' file.)
|
||||||
|
*/
|
||||||
|
_getDistPackages() {
|
||||||
|
const packageConfigs = Object.create(null);
|
||||||
|
this._log(`Angular distributable directory: ${ANGULAR_DIST_PACKAGES}.`);
|
||||||
|
shelljs
|
||||||
|
.find(ANGULAR_DIST_PACKAGES)
|
||||||
|
.map(filePath => filePath.slice(ANGULAR_DIST_PACKAGES.length + 1))
|
||||||
|
.filter(filePath => PACKAGE_JSON_REGEX.test(filePath))
|
||||||
|
.forEach(packagePath => {
|
||||||
|
const packageConfig = require(path.resolve(ANGULAR_DIST_PACKAGES, packagePath));
|
||||||
|
const packageName = `@angular/${packagePath.slice(0, -PACKAGE_JSON.length -1)}`;
|
||||||
|
packageConfigs[packageName] = packageConfig;
|
||||||
|
});
|
||||||
|
this._log('Found the following Angular distributables:', Object.keys(packageConfigs).map(key => `\n - ${key}`));
|
||||||
|
return packageConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
_installDeps(...options) {
|
||||||
|
const command = 'yarn install ' + options.join(' ');
|
||||||
|
this._log('Installing dependencies with:', command);
|
||||||
|
shelljs.exec(command, {cwd: this.projectDir});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message if the `debug` property is set to true.
|
||||||
|
* @param {...string[]} messages - The messages to be logged.
|
||||||
|
*/
|
||||||
|
_log(...messages) {
|
||||||
|
if (this.debug) {
|
||||||
|
const header = ` [${NgPackagesInstaller.name}]: `;
|
||||||
|
const indent = ' '.repeat(header.length);
|
||||||
|
const message = messages.join(' ');
|
||||||
|
console.info(`${header}${message.split('\n').join(`\n${indent}`)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_printWarning() {
|
||||||
|
const relativeScriptPath = path.relative('.', __filename.replace(/\.js$/, ''));
|
||||||
|
const absoluteProjectDir = path.resolve(this.projectDir);
|
||||||
|
const restoreCmd = `node ${relativeScriptPath} restore ${absoluteProjectDir}`;
|
||||||
|
|
||||||
|
// Log a warning.
|
||||||
|
console.warn(chalk.yellow([
|
||||||
|
'',
|
||||||
|
'!'.repeat(110),
|
||||||
|
'!!!',
|
||||||
|
'!!! WARNING',
|
||||||
|
'!!!',
|
||||||
|
`!!! The project at "${absoluteProjectDir}" is running against the local Angular build.`,
|
||||||
|
'!!!',
|
||||||
|
'!!! To restore the npm packages run:',
|
||||||
|
'!!!',
|
||||||
|
`!!! "${restoreCmd}"`,
|
||||||
|
'!!!',
|
||||||
|
'!'.repeat(110),
|
||||||
|
'',
|
||||||
|
].join('\n')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local marker helpers
|
||||||
|
|
||||||
|
_checkLocalMarker() {
|
||||||
|
this._log('Checking for local marker at', this.localMarkerPath);
|
||||||
|
return fs.existsSync(this.localMarkerPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setLocalMarker(contents) {
|
||||||
|
this._log('Writing local marker file to', this.localMarkerPath);
|
||||||
|
fs.writeFileSync(this.localMarkerPath, contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
shelljs.set('-e');
|
||||||
|
|
||||||
|
yargs
|
||||||
|
.usage('$0 <cmd> [args]')
|
||||||
|
|
||||||
|
.option('debug', { describe: 'Print additional debug information.', default: false })
|
||||||
|
.option('force', { describe: 'Force the command to execute even if not needed.', default: false })
|
||||||
|
|
||||||
|
.command('overwrite <projectDir> [--force] [--debug]', 'Install dependencies from the locally built Angular distributables.', () => {}, argv => {
|
||||||
|
const installer = new NgPackagesInstaller(argv.projectDir, argv);
|
||||||
|
installer.installLocalDependencies();
|
||||||
|
})
|
||||||
|
.command('restore <projectDir> [--debug]', 'Install dependencies from the npm registry.', () => {}, argv => {
|
||||||
|
const installer = new NgPackagesInstaller(argv.projectDir, argv);
|
||||||
|
installer.restoreNpmDependencies();
|
||||||
|
})
|
||||||
|
.command('check <projectDir> [--debug]', 'Check that dependencies came from npm. Otherwise display a warning message.', () => {}, argv => {
|
||||||
|
const installer = new NgPackagesInstaller(argv.projectDir, argv);
|
||||||
|
installer.checkDependencies();
|
||||||
|
})
|
||||||
|
.demandCommand(1, 'Please supply a command from the list above.')
|
||||||
|
.strict()
|
||||||
|
.wrap(yargs.terminalWidth())
|
||||||
|
.argv;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = NgPackagesInstaller;
|
||||||
|
if (require.main === module) {
|
||||||
|
main();
|
||||||
|
}
|
|
@ -0,0 +1,246 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('canonical-path');
|
||||||
|
const shelljs = require('shelljs');
|
||||||
|
|
||||||
|
const NgPackagesInstaller = require('./index');
|
||||||
|
|
||||||
|
describe('NgPackagesInstaller', () => {
|
||||||
|
const rootDir = 'root/dir';
|
||||||
|
const absoluteRootDir = path.resolve(rootDir);
|
||||||
|
const nodeModulesDir = path.resolve(absoluteRootDir, 'node_modules');
|
||||||
|
const packageJsonPath = path.resolve(absoluteRootDir, 'package.json');
|
||||||
|
const packagesDir = path.resolve(path.resolve(__dirname, '../../../dist/packages-dist'));
|
||||||
|
let installer;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(fs, 'existsSync');
|
||||||
|
spyOn(fs, 'readFileSync');
|
||||||
|
spyOn(fs, 'writeFileSync');
|
||||||
|
spyOn(shelljs, 'exec');
|
||||||
|
spyOn(shelljs, 'rm');
|
||||||
|
spyOn(console, 'log');
|
||||||
|
spyOn(console, 'warn');
|
||||||
|
installer = new NgPackagesInstaller(rootDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('checkDependencies()', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(installer, '_printWarning');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not print a warning if there is no _local_.json file', () => {
|
||||||
|
fs.existsSync.and.returnValue(false);
|
||||||
|
installer.checkDependencies();
|
||||||
|
expect(fs.existsSync).toHaveBeenCalledWith(path.resolve(rootDir, 'node_modules/_local_.json'));
|
||||||
|
expect(installer._printWarning).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should print a warning if there is a _local_.json file', () => {
|
||||||
|
fs.existsSync.and.returnValue(true);
|
||||||
|
installer.checkDependencies();
|
||||||
|
expect(fs.existsSync).toHaveBeenCalledWith(path.resolve(rootDir, 'node_modules/_local_.json'));
|
||||||
|
expect(installer._printWarning).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('installLocalDependencies()', () => {
|
||||||
|
let dummyNgPackages, dummyPackage, dummyPackageJson, expectedModifiedPackage, expectedModifiedPackageJson;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(installer, '_checkLocalMarker');
|
||||||
|
|
||||||
|
// These are the packages that are "found" in the dist directory
|
||||||
|
dummyNgPackages = {
|
||||||
|
'@angular/core': { peerDependencies: { rxjs: '5.0.1' } },
|
||||||
|
'@angular/common': { peerDependencies: { '@angular/core': '4.4.1' } },
|
||||||
|
'@angular/compiler': { },
|
||||||
|
'@angular/compiler-cli': { peerDependencies: { typescript: '^2.4.2', '@angular/compiler': '4.3.2' } }
|
||||||
|
};
|
||||||
|
spyOn(installer, '_getDistPackages').and.returnValue(dummyNgPackages);
|
||||||
|
|
||||||
|
// This is the package.json in the "test" folder
|
||||||
|
dummyPackage = {
|
||||||
|
dependencies: {
|
||||||
|
'@angular/core': '4.4.1',
|
||||||
|
'@angular/common': '4.4.1'
|
||||||
|
},
|
||||||
|
devDependencies: {
|
||||||
|
'@angular/compiler-cli': '4.4.1'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dummyPackageJson = JSON.stringify(dummyPackage);
|
||||||
|
fs.readFileSync.and.returnValue(dummyPackageJson);
|
||||||
|
|
||||||
|
// This is the package.json that is temporarily written to the "test" folder
|
||||||
|
// Note that the Angular (dev)dependencies have been modified to use a "file:" path
|
||||||
|
// And that the peerDependencies from `dummyNgPackages` have been added as (dev)dependencies.
|
||||||
|
expectedModifiedPackage = {
|
||||||
|
dependencies: {
|
||||||
|
'@angular/core': `file:${packagesDir}/core`,
|
||||||
|
'@angular/common': `file:${packagesDir}/common`
|
||||||
|
},
|
||||||
|
devDependencies: {
|
||||||
|
'@angular/compiler-cli': `file:${packagesDir}/compiler-cli`,
|
||||||
|
rxjs: '5.0.1',
|
||||||
|
typescript: '^2.4.2'
|
||||||
|
},
|
||||||
|
__angular: { local: true }
|
||||||
|
};
|
||||||
|
expectedModifiedPackageJson = JSON.stringify(expectedModifiedPackage, null, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when there is a local package marker', () => {
|
||||||
|
it('should not continue processing', () => {
|
||||||
|
installer._checkLocalMarker.and.returnValue(true);
|
||||||
|
installer.installLocalDependencies();
|
||||||
|
expect(installer._checkLocalMarker).toHaveBeenCalled();
|
||||||
|
expect(installer._getDistPackages).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when there is no local package marker', () => {
|
||||||
|
let log;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
log = [];
|
||||||
|
fs.writeFileSync.and.callFake((filePath, contents) => filePath === packageJsonPath && log.push(`writeFile: ${contents}`));
|
||||||
|
spyOn(installer, '_installDeps').and.callFake(() => log.push('installDeps:'));
|
||||||
|
spyOn(installer, '_setLocalMarker');
|
||||||
|
installer._checkLocalMarker.and.returnValue(false);
|
||||||
|
installer.installLocalDependencies();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the dist packages', () => {
|
||||||
|
expect(installer._checkLocalMarker).toHaveBeenCalled();
|
||||||
|
expect(installer._getDistPackages).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load the package.json', () => {
|
||||||
|
expect(fs.readFileSync).toHaveBeenCalledWith(packageJsonPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite package.json with modified config', () => {
|
||||||
|
expect(fs.writeFileSync).toHaveBeenCalledWith(packageJsonPath, expectedModifiedPackageJson);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should restore original package.json', () => {
|
||||||
|
expect(fs.writeFileSync).toHaveBeenCalledWith(packageJsonPath, dummyPackageJson);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite package.json, then install deps, then restore original package.json', () => {
|
||||||
|
expect(log).toEqual([
|
||||||
|
`writeFile: ${expectedModifiedPackageJson}`,
|
||||||
|
`installDeps:`,
|
||||||
|
`writeFile: ${dummyPackageJson}`
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the local marker file with the contents of the modified package.json', () => {
|
||||||
|
expect(installer._setLocalMarker).toHaveBeenCalledWith(expectedModifiedPackageJson);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('restoreNpmDependencies()', () => {
|
||||||
|
it('should run `yarn install --check-files` in the specified directory', () => {
|
||||||
|
spyOn(installer, '_installDeps');
|
||||||
|
installer.restoreNpmDependencies();
|
||||||
|
expect(installer._installDeps).toHaveBeenCalledWith('--check-files');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_getDistPackages', () => {
|
||||||
|
it('should include top level Angular packages', () => {
|
||||||
|
const ngPackages = installer._getDistPackages();
|
||||||
|
|
||||||
|
// For example...
|
||||||
|
expect(ngPackages['@angular/common']).toBeDefined();
|
||||||
|
expect(ngPackages['@angular/core']).toBeDefined();
|
||||||
|
expect(ngPackages['@angular/router']).toBeDefined();
|
||||||
|
expect(ngPackages['@angular/upgrade']).toBeDefined();
|
||||||
|
|
||||||
|
expect(ngPackages['@angular/upgrade/static']).not.toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_log()', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(console, 'info');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should assign the debug property from the options', () => {
|
||||||
|
installer = new NgPackagesInstaller(rootDir, { debug: true });
|
||||||
|
expect(installer.debug).toBe(true);
|
||||||
|
installer = new NgPackagesInstaller(rootDir, { });
|
||||||
|
expect(installer.debug).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log a message to the console if the `debug` property is true', () => {
|
||||||
|
installer._log('foo');
|
||||||
|
expect(console.info).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
installer.debug = true;
|
||||||
|
installer._log('bar');
|
||||||
|
expect(console.info).toHaveBeenCalledWith(' [NgPackagesInstaller]: bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_printWarning', () => {
|
||||||
|
it('should mention the message passed in the warning', () => {
|
||||||
|
installer._printWarning();
|
||||||
|
expect(console.warn.calls.argsFor(0)[0]).toContain('is running against the local Angular build');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mention the command to restore the Angular packages in any warning', () => {
|
||||||
|
// When run for the current working directory...
|
||||||
|
const dir1 = '.';
|
||||||
|
const restoreCmdRe1 = RegExp('\\bnode .*?ng-packages-installer/index restore ' + path.resolve(dir1));
|
||||||
|
installer = new NgPackagesInstaller(dir1);
|
||||||
|
installer._printWarning('');
|
||||||
|
expect(console.warn.calls.argsFor(0)[0]).toMatch(restoreCmdRe1);
|
||||||
|
|
||||||
|
// When run for a different directory...
|
||||||
|
const dir2 = rootDir;
|
||||||
|
const restoreCmdRe2 = RegExp(`\\bnode .*?ng-packages-installer/index restore .*?${path.resolve(dir1)}\\b`);
|
||||||
|
installer = new NgPackagesInstaller(dir2);
|
||||||
|
installer._printWarning('');
|
||||||
|
expect(console.warn.calls.argsFor(1)[0]).toMatch(restoreCmdRe2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_installDeps', () => {
|
||||||
|
it('should run yarn install with the given options', () => {
|
||||||
|
installer._installDeps('option-1', 'option-2');
|
||||||
|
expect(shelljs.exec).toHaveBeenCalledWith('yarn install option-1 option-2', { cwd: absoluteRootDir });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('local marker helpers', () => {
|
||||||
|
let installer;
|
||||||
|
beforeEach(() => {
|
||||||
|
installer = new NgPackagesInstaller(rootDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_checkLocalMarker', () => {
|
||||||
|
it ('should return true if the local marker file exists', () => {
|
||||||
|
fs.existsSync.and.returnValue(true);
|
||||||
|
expect(installer._checkLocalMarker()).toEqual(true);
|
||||||
|
expect(fs.existsSync).toHaveBeenCalledWith(path.resolve(nodeModulesDir, '_local_.json'));
|
||||||
|
fs.existsSync.calls.reset();
|
||||||
|
|
||||||
|
fs.existsSync.and.returnValue(false);
|
||||||
|
expect(installer._checkLocalMarker()).toEqual(false);
|
||||||
|
expect(fs.existsSync).toHaveBeenCalledWith(path.resolve(nodeModulesDir, '_local_.json'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_setLocalMarker', () => {
|
||||||
|
it('should create a local marker file', () => {
|
||||||
|
installer._setLocalMarker('test contents');
|
||||||
|
expect(fs.writeFileSync).toHaveBeenCalledWith(path.resolve(nodeModulesDir, '_local_.json'), 'test contents');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue