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:
parent
bb1665cbd8
commit
7a965dc58f
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue