build(aio): add support for using the locally built Angular packages for aio

This commit allows building angular.io against the locally built Angular
packages. It adds two new npm scripts:

- `setup-local`: Same as `setup`, but overwrites the Angular packages for both
  angular.io and the examples boilerplate with the locally built ones.
- `build-local`: Same as `build`, but uses `setup-local` instead of `setup`
  under the hood, thus overwriting installed Angular packages with locally built
  ones.

Fixes #18611
This commit is contained in:
Georgios Kalpakas 2017-08-16 03:34:53 +03:00 committed by Victor Berchet
parent bb1665cbd8
commit 7a965dc58f
8 changed files with 609 additions and 86 deletions

View File

@ -13,7 +13,11 @@ You should run all these tasks from the `angular/aio` folder.
Here are the most important tasks you might need to use:
* `yarn` - install all the dependencies.
* `yarn setup` - Install all the dependencies, boilerplate, plunkers, zips and runs dgeni on the docs.
* `yarn setup` - install all the dependencies, boilerplate, plunkers, zips and run dgeni on the docs.
* `yarn setup-local` - same as `setup`, but use the locally built Angular packages for aio and docs examples boilerplate.
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.

View File

@ -8,31 +8,38 @@
"scripts": {
"ng": "yarn check-env && ng",
"start": "yarn check-env && ng serve",
"prebuild": "yarn check-env && yarn setup",
"build": "ng build --target=production --environment=stable -sm --build-optimizer",
"postbuild": "yarn sw-manifest && yarn sw-copy",
"prebuild": "yarn setup",
"build": "yarn ~~build",
"prebuild-local": "yarn setup-local",
"build-local": "yarn ~~build",
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint",
"test": "yarn check-env && ng test",
"pree2e": "yarn check-env && yarn ~~update-webdriver",
"e2e": "ng e2e --no-webdriver-update",
"setup": "yarn && yarn build-ie-polyfills && yarn boilerplate:remove && yarn boilerplate:add && yarn generate-plunkers && yarn generate-zips && yarn docs",
"pretest-pwa-score-local": "yarn build",
"test-pwa-score-local": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score -- http://localhost:4200 90\"",
"presetup": "yarn ~~check-env && yarn install && yarn boilerplate:remove",
"setup": "node tools/ng-packages-installer restore . && yarn boilerplate:add",
"postsetup": "yarn build-ie-polyfills && yarn generate-plunkers && yarn generate-zips && yarn docs",
"presetup-local": "yarn presetup",
"setup-local": "node tools/ng-packages-installer overwrite . && yarn boilerplate:add -- --local",
"postsetup-local": "yarn postsetup",
"pretest-pwa-score-localhost": "yarn build",
"test-pwa-score-localhost": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score -- http://localhost:4200 90\"",
"test-pwa-score": "node scripts/test-pwa-score",
"example-e2e": "node ./tools/examples/run-example-e2e",
"example-e2e": "yarn check-ng-packages -- tools/examples/shared && node ./tools/examples/run-example-e2e",
"example-lint": "tslint -c \"content/examples/tslint.json\" \"content/examples/**/*.ts\" -e \"content/examples/styleguide/**/*.avoid.ts\"",
"deploy-preview": "scripts/deploy-preview.sh",
"deploy-production": "scripts/deploy-to-firebase.sh",
"check-env": "node scripts/check-environment",
"check-ng-packages": "node tools/ng-packages-installer check",
"check-env": "yarn ~~check-env",
"postcheck-env": "yarn check-ng-packages -- .",
"payload-size": "scripts/payload.sh",
"predocs": "rimraf src/generated/{docs,*.json}",
"docs": "dgeni ./tools/transforms/angular.io-package",
"docs-watch": "node tools/transforms/authors-package/watchr.js",
"docs-lint": "eslint --ignore-path=\"tools/transforms/.eslintignore\" tools/transforms",
"docs-test": "node tools/transforms/test.js",
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test",
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer.spec.js",
"serve-and-sync": "concurrently --kill-others \"yarn docs-watch\" \"yarn start\"",
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false",
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
"boilerplate:test": "node tools/examples/test.js",
@ -41,7 +48,10 @@
"sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json",
"sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/",
"postinstall": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/assets/js/lunr.min.js --source-map",
"build-ie-polyfills": "node node_modules/webpack/bin/webpack.js -p src/ie-polyfills.js src/generated/ie-polyfills.min.js"
"build-ie-polyfills": "node node_modules/webpack/bin/webpack.js -p src/ie-polyfills.js src/generated/ie-polyfills.min.js",
"~~check-env": "node scripts/check-environment",
"~~build": "ng build --target=production --environment=stable -sm --build-optimizer && yarn sw-manifest && yarn sw-copy",
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false"
},
"engines": {
"node": ">=6.9.5 <7.0.0",
@ -78,6 +88,7 @@
"@types/node": "~6.0.60",
"archiver": "^1.3.0",
"canonical-path": "^0.0.2",
"chalk": "^2.1.0",
"codelyzer": "~2.0.0",
"concurrently": "^3.4.0",
"cross-spawn": "^5.1.0",

View File

@ -4,6 +4,8 @@ const path = require('canonical-path');
const shelljs = require('shelljs');
const yargs = require('yargs');
const ngPackagesInstaller = require('../ng-packages-installer');
const SHARED_PATH = path.resolve(__dirname, 'shared');
const SHARED_NODE_MODULES_PATH = path.resolve(SHARED_PATH, 'node_modules');
const BOILERPLATE_BASE_PATH = path.resolve(SHARED_PATH, 'boilerplate');
@ -46,23 +48,6 @@ const BOILERPLATE_PATHS = {
]
};
const ANGULAR_DIST_PATH = path.resolve(__dirname, '../../../dist');
const ANGULAR_PACKAGES_PATH = path.resolve(ANGULAR_DIST_PATH, 'packages-dist');
const ANGULAR_PACKAGES = [
'animations',
'common',
'compiler',
'compiler-cli',
'core',
'forms',
'http',
'platform-browser',
'platform-browser-dynamic',
'platform-server',
'router',
'upgrade',
];
const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
class ExampleBoilerPlate {
@ -72,13 +57,8 @@ class ExampleBoilerPlate {
* @param useLocal if true then overwrite the Angular library files with locally built ones
*/
add(useLocal) {
// first install the shared node_modules
this.installNodeModules(SHARED_PATH);
// Replace the Angular packages with those from the dist folder, if necessary
if (useLocal) {
ANGULAR_PACKAGES.forEach(packageName => this.overridePackage(ANGULAR_PACKAGES_PATH, packageName));
}
// Install the shared `node_modules/` (if necessary overwrite Angular packages from npm with local ones).
this.installNodeModules(SHARED_PATH, useLocal);
// Get all the examples folders, indicated by those that contain a `example-config.json` file
const exampleFolders = this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules');
@ -118,15 +98,14 @@ class ExampleBoilerPlate {
.argv;
}
installNodeModules(basePath) {
installNodeModules(basePath, useLocal) {
shelljs.exec('yarn', {cwd: basePath});
}
overridePackage(basePath, packageName) {
const sourceFolder = path.resolve(basePath, packageName);
const destinationFolder = path.resolve(SHARED_NODE_MODULES_PATH, '@angular', packageName);
shelljs.rm('-rf', destinationFolder);
fs.copySync(sourceFolder, destinationFolder);
if (useLocal) {
ngPackagesInstaller.overwritePackages(basePath);
} else {
ngPackagesInstaller.restorePackages(basePath);
}
}
getFoldersContaining(basePath, filename, ignore) {

View File

@ -1,11 +1,15 @@
const exampleBoilerPlate = require('./example-boilerplate');
const shelljs = require('shelljs');
const path = require('canonical-path');
const fs = require('fs-extra');
const glob = require('glob');
const path = require('canonical-path');
const shelljs = require('shelljs');
const ngPackagesInstaller = require('../ng-packages-installer');
const exampleBoilerPlate = require('./example-boilerplate');
describe('example-boilerplate tool', () => {
describe('add', () => {
const sharedDir = path.resolve(__dirname, 'shared');
const sharedNodeModulesDir = path.resolve(sharedDir, 'node_modules');
const BPFiles = {
cli: 18,
systemjs: 7,
@ -14,41 +18,44 @@ describe('example-boilerplate tool', () => {
const exampleFolders = ['a/b', 'c/d'];
beforeEach(() => {
spyOn(exampleBoilerPlate, 'installNodeModules');
spyOn(exampleBoilerPlate, 'overridePackage');
spyOn(exampleBoilerPlate, 'getFoldersContaining').and.returnValue(exampleFolders);
spyOn(fs, 'ensureSymlinkSync');
spyOn(exampleBoilerPlate, 'copyFile');
spyOn(exampleBoilerPlate, 'getFoldersContaining').and.returnValue(exampleFolders);
spyOn(exampleBoilerPlate, 'installNodeModules');
spyOn(exampleBoilerPlate, 'loadJsonFile').and.returnValue({});
});
it('should install the node modules', () => {
it('should install the npm dependencies into `sharedDir` (and pass the `useLocal` argument through)', () => {
exampleBoilerPlate.add();
expect(exampleBoilerPlate.installNodeModules).toHaveBeenCalledWith(path.resolve(__dirname, 'shared'));
});
expect(exampleBoilerPlate.installNodeModules).toHaveBeenCalledWith(sharedDir, undefined);
exampleBoilerPlate.installNodeModules.calls.reset();
it('should override the Angular node_modules with the locally built Angular packages if `useLocal` is true', () => {
const numberOfAngularPackages = 12;
exampleBoilerPlate.add(true);
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledTimes(numberOfAngularPackages);
// for example
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'common');
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'core');
expect(exampleBoilerPlate.installNodeModules).toHaveBeenCalledWith(sharedDir, true);
exampleBoilerPlate.installNodeModules.calls.reset();
exampleBoilerPlate.add(false);
expect(exampleBoilerPlate.installNodeModules).toHaveBeenCalledWith(sharedDir, false);
});
it('should process all the example folders', () => {
const examplesDir = path.resolve(__dirname, '../../content/examples');
exampleBoilerPlate.add();
expect(exampleBoilerPlate.getFoldersContaining).toHaveBeenCalledWith(path.resolve(__dirname, '../../content/examples'), 'example-config.json', 'node_modules');
expect(exampleBoilerPlate.getFoldersContaining)
.toHaveBeenCalledWith(examplesDir, 'example-config.json', 'node_modules');
});
it('should symlink the node_modules', () => {
exampleBoilerPlate.add();
expect(fs.ensureSymlinkSync).toHaveBeenCalledTimes(exampleFolders.length);
expect(fs.ensureSymlinkSync).toHaveBeenCalledWith(path.resolve(__dirname, 'shared/node_modules'), path.resolve('a/b/node_modules'));
expect(fs.ensureSymlinkSync).toHaveBeenCalledWith(path.resolve(__dirname, 'shared/node_modules'), path.resolve('c/d/node_modules'));
expect(fs.ensureSymlinkSync).toHaveBeenCalledWith(sharedNodeModulesDir, path.resolve('a/b/node_modules'));
expect(fs.ensureSymlinkSync).toHaveBeenCalledWith(sharedNodeModulesDir, path.resolve('c/d/node_modules'));
});
it('should copy all the source boilerplate files for systemjs', () => {
const boilerplateDir = path.resolve(sharedDir, 'boilerplate');
exampleBoilerPlate.loadJsonFile.and.callFake(filePath => filePath.indexOf('a/b') !== -1 ? { projectType: 'systemjs' } : {})
exampleBoilerPlate.add();
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledTimes(
@ -57,19 +64,20 @@ describe('example-boilerplate tool', () => {
(BPFiles.common * exampleFolders.length)
);
// for example
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledWith(path.resolve(__dirname, 'shared/boilerplate/systemjs'), 'a/b', 'package.json');
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledWith(path.resolve(__dirname, 'shared/boilerplate/common'), 'a/b', 'src/styles.css');
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledWith(`${boilerplateDir}/systemjs`, 'a/b', 'package.json');
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledWith(`${boilerplateDir}/common`, 'a/b', 'src/styles.css');
});
it('should copy all the source boilerplate files for cli', () => {
const boilerplateDir = path.resolve(sharedDir, 'boilerplate');
exampleBoilerPlate.add();
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledTimes(
(BPFiles.cli * exampleFolders.length) +
(BPFiles.common * exampleFolders.length)
);
// for example
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledWith(path.resolve(__dirname, 'shared/boilerplate/cli'), 'a/b', 'package.json');
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledWith(path.resolve(__dirname, 'shared/boilerplate/common'), 'c/d', 'src/styles.css');
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledWith(`${boilerplateDir}/cli`, 'a/b', 'package.json');
expect(exampleBoilerPlate.copyFile).toHaveBeenCalledWith(`${boilerplateDir}/common`, 'c/d', 'src/styles.css');
});
it('should try to load the example config file', () => {
@ -89,27 +97,33 @@ describe('example-boilerplate tool', () => {
});
describe('installNodeModules', () => {
it('should run `yarn` in the base path', () => {
beforeEach(() => {
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', () => {
ngPackagesInstaller.overwritePackages.and.callFake(() => expect(shelljs.exec).toHaveBeenCalled());
exampleBoilerPlate.installNodeModules('some/base/path', true);
expect(ngPackagesInstaller.overwritePackages).toHaveBeenCalledWith('some/base/path');
expect(ngPackagesInstaller.restorePackages).not.toHaveBeenCalled();
});
describe('overridePackage', () => {
beforeEach(() => {
spyOn(shelljs, 'rm');
spyOn(fs, 'copySync');
});
it('should restore the Angular packages if `useLocal` is not true', () => {
exampleBoilerPlate.installNodeModules('some/base/path1');
expect(ngPackagesInstaller.restorePackages).toHaveBeenCalledWith('some/base/path1');
it('should remove the original package from the shared node_modules folder', () => {
exampleBoilerPlate.overridePackage('base/path', 'somePackage');
expect(shelljs.rm).toHaveBeenCalledWith('-rf', path.resolve(__dirname, 'shared/node_modules/@angular/somePackage'));
});
exampleBoilerPlate.installNodeModules('some/base/path2', false);
expect(ngPackagesInstaller.restorePackages).toHaveBeenCalledWith('some/base/path2');
it('should copy the source folder to the shared node_modules folder', () => {
exampleBoilerPlate.overridePackage('base/path', 'somePackage');
expect(fs.copySync).toHaveBeenCalledWith(path.resolve('base/path/somePackage'), path.resolve(__dirname, 'shared/node_modules/@angular/somePackage'));
expect(ngPackagesInstaller.overwritePackages).not.toHaveBeenCalled();
});
});

View File

@ -0,0 +1,235 @@
#!/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();
}

View File

@ -0,0 +1,286 @@
'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});
});
});
});

View File

@ -1085,7 +1085,7 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.0.0, chalk@^2.0.1:
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
dependencies:

View File

@ -18,12 +18,6 @@ source ${thisDir}/_travis-fold.sh
travisFoldEnd "test.aio.lint"
# Run unit tests for boilerplate tools
travisFoldStart "test.aio.boilerplate.unit"
yarn boilerplate:test
travisFoldEnd "test.aio.boilerplate.unit"
# Run unit tests
travisFoldStart "test.aio.unit"
yarn test -- --single-run
@ -38,7 +32,7 @@ source ${thisDir}/_travis-fold.sh
# Run PWA-score tests
travisFoldStart "test.aio.pwaScore"
yarn test-pwa-score-local
yarn test-pwa-score-localhost
travisFoldEnd "test.aio.pwaScore"