mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
build(bazel): refactor aio example e2es to fix windows performance
Use the same config flag to enable local vs npm deps as aio.
This commit is contained in:
committed by
Joey Perrott
parent
a36034a135
commit
8d24a91c6a
@@ -136,6 +136,27 @@ yarn_install(
|
||||
yarn_lock = "//aio:yarn.lock",
|
||||
)
|
||||
|
||||
yarn_install(
|
||||
name = "aio_example_deps",
|
||||
data = [
|
||||
YARN_LABEL,
|
||||
"//:.yarnrc",
|
||||
],
|
||||
# Disabled because, when False, yarn_install preserves the node_modules folder
|
||||
# with bin symlinks in the external repository. This is needed to link the shared
|
||||
# set of deps for example e2es.
|
||||
exports_directories_only = False,
|
||||
manual_build_file_contents = """\
|
||||
filegroup(
|
||||
name = "node_modules_with_bins",
|
||||
srcs = ["node_modules", "node_modules/.bin"],
|
||||
)
|
||||
""",
|
||||
package_json = "//aio/tools/examples/shared:package.json",
|
||||
yarn = YARN_LABEL,
|
||||
yarn_lock = "//aio/tools/examples/shared:yarn.lock",
|
||||
)
|
||||
|
||||
load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies")
|
||||
|
||||
aspect_bazel_lib_dependencies()
|
||||
|
||||
@@ -2,6 +2,7 @@ load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin")
|
||||
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
|
||||
load("//aio/tools:defaults.bzl", "nodejs_test")
|
||||
load("//:yarn.bzl", "YARN_LABEL")
|
||||
load("//:packages.bzl", "ALL_PACKAGES", "to_package_label")
|
||||
|
||||
# This map controls which examples are included and whether or not to generate
|
||||
# a stackblitz live examples and zip archives. Keys are the example name, and values
|
||||
@@ -177,36 +178,43 @@ def docs_example(name, test = True, test_tags = []):
|
||||
)
|
||||
|
||||
if test:
|
||||
# These node_modules deps are symlinked into each example. These tree
|
||||
# artifact folder names must still be "node_modules" despite the symlink
|
||||
# being named node_modules. Otherwise, some deps will fail to resolve.
|
||||
node_modules_deps = {
|
||||
"local": "//aio/tools/examples/shared:local/node_modules",
|
||||
"npm": "//aio/tools/examples/shared:node_modules",
|
||||
}
|
||||
EXAMPLE_DEPS_WORKSPACE_NAME = "aio_example_deps"
|
||||
|
||||
for [node_modules_source, node_modules_label] in node_modules_deps.items():
|
||||
nodejs_test(
|
||||
name = "e2e_%s" % node_modules_source,
|
||||
data = [
|
||||
":%s" % name,
|
||||
YARN_LABEL,
|
||||
node_modules_label,
|
||||
"@aio_npm//@angular/dev-infra-private/bazel/browsers/chromium",
|
||||
],
|
||||
args = [
|
||||
"$(rootpath :%s)" % name,
|
||||
"$(rootpath %s)" % node_modules_label,
|
||||
"$(rootpath %s)" % YARN_LABEL,
|
||||
],
|
||||
configuration_env_vars = ["NG_BUILD_CACHE"],
|
||||
entry_point = "//aio/tools/examples:run-example-e2e",
|
||||
env = {
|
||||
"CHROME_BIN": "$(CHROMIUM)",
|
||||
"CHROMEDRIVER_BIN": "$(CHROMEDRIVER)",
|
||||
},
|
||||
toolchains = [
|
||||
"@aio_npm//@angular/dev-infra-private/bazel/browsers/chromium:toolchain_alias",
|
||||
],
|
||||
tags = test_tags,
|
||||
)
|
||||
LOCAL_PACKAGE_DEPS = [to_package_label(dep) for dep in ALL_PACKAGES]
|
||||
|
||||
# Local package deps are passed as args to the test script in the form "@package/name#path/to/package"
|
||||
# for the script's convenience.
|
||||
LOCAL_PACKAGE_ARGS = ["%s#$(rootpath %s)" % (dep, to_package_label(dep)) for dep in ALL_PACKAGES]
|
||||
|
||||
nodejs_test(
|
||||
name = "e2e",
|
||||
data = [
|
||||
":%s" % name,
|
||||
YARN_LABEL,
|
||||
"@aio_npm//@angular/dev-infra-private/bazel/browsers/chromium",
|
||||
"//aio/tools/examples:run-example-e2e",
|
||||
# We install the whole node modules for runtime deps of e2e tests
|
||||
"@{workspace}//:node_modules_with_bins".format(workspace = EXAMPLE_DEPS_WORKSPACE_NAME),
|
||||
] + select({
|
||||
"//aio:aio_local_deps": LOCAL_PACKAGE_DEPS,
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
args = [
|
||||
"$(rootpath :%s)" % name,
|
||||
"$(rootpath %s)" % YARN_LABEL,
|
||||
EXAMPLE_DEPS_WORKSPACE_NAME,
|
||||
] + select({
|
||||
"//aio:aio_local_deps": LOCAL_PACKAGE_ARGS,
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
configuration_env_vars = ["NG_BUILD_CACHE"],
|
||||
entry_point = "//aio/tools/examples:run-example-e2e",
|
||||
env = {
|
||||
"CHROME_BIN": "$(CHROMIUM)",
|
||||
"CHROMEDRIVER_BIN": "$(CHROMEDRIVER)",
|
||||
},
|
||||
toolchains = [
|
||||
"@aio_npm//@angular/dev-infra-private/bazel/browsers/chromium:toolchain_alias",
|
||||
],
|
||||
tags = test_tags,
|
||||
)
|
||||
|
||||
+1
-1
@@ -117,8 +117,8 @@
|
||||
"@bazel/bazelisk": "^1.7.5",
|
||||
"@bazel/buildozer": "^5.1.0",
|
||||
"@bazel/jasmine": "^5.4.1",
|
||||
"@bazel/typescript": "5.3.1",
|
||||
"@bazel/runfiles": "5.4.2",
|
||||
"@bazel/typescript": "5.3.1",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/node": "^12.7.9",
|
||||
|
||||
@@ -17,7 +17,12 @@ export async function resolve(specifier, context, defaultResolve) {
|
||||
return defaultResolve(specifier, context, defaultResolve);
|
||||
}
|
||||
|
||||
const nodeModules = path.resolve('external', process.env.NODE_MODULES_WORKSPACE_NAME, 'node_modules');
|
||||
let nodeModules;
|
||||
if (isBazelRunOrTestAction()) {
|
||||
nodeModules = path.resolve('../', process.env.NODE_MODULES_WORKSPACE_NAME, 'node_modules');
|
||||
} else {
|
||||
nodeModules = path.resolve('external', process.env.NODE_MODULES_WORKSPACE_NAME, 'node_modules');
|
||||
}
|
||||
|
||||
const packageImport = parsePackageImport(specifier);
|
||||
const pathToNodeModule = path.join(nodeModules, packageImport.packageName);
|
||||
@@ -54,3 +59,7 @@ function resolvePackageLocalFilepath(packageImport, packageJson) {
|
||||
|
||||
return packageImport.pathInPackage || packageJson.module || packageJson.main || 'index.js';
|
||||
}
|
||||
|
||||
function isBazelRunOrTestAction() {
|
||||
return process.env.TEST_WORKSPACE || process.env.BUILD_WORKSPACE_DIRECTORY;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ js_library(
|
||||
deps = [
|
||||
"@aio_npm//@bazel/runfiles",
|
||||
"@aio_npm//canonical-path",
|
||||
"@aio_npm//cjson",
|
||||
"@aio_npm//cross-spawn",
|
||||
"@aio_npm//fs-extra",
|
||||
"@aio_npm//globby",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import path from 'canonical-path';
|
||||
import {spawn} from 'cross-spawn';
|
||||
import fs from 'fs-extra';
|
||||
import {sync as globbySync} from 'globby';
|
||||
import {globbySync} from 'globby';
|
||||
import jsonc from 'cjson';
|
||||
import os from 'os';
|
||||
import shelljs from 'shelljs';
|
||||
import treeKill from 'tree-kill';
|
||||
@@ -20,27 +21,42 @@ process.env.CHROMEDRIVER_BIN = path.resolve(process.env.CHROMEDRIVER_BIN);
|
||||
const {argv} = yargs(hideBin(process.argv));
|
||||
|
||||
const EXAMPLE_PATH = path.resolve(argv._[0]);
|
||||
const NODE_MODULES_PATH = path.resolve(argv._[1]);
|
||||
const NODE = process.execPath;
|
||||
const VENDORED_YARN = path.resolve(argv._[2]);
|
||||
const VENDORED_YARN = path.resolve(argv._[1]);
|
||||
const EXAMPLE_DEPS_WORKSPACE_NAME = argv._[2];
|
||||
const LOCAL_PACKAGES = argv._.slice(3).reduce((pkgs, pkgNameAndPath) => {
|
||||
const [pkgName, pkgPath] = pkgNameAndPath.split('#');
|
||||
pkgs[pkgName] = path.resolve(pkgPath);
|
||||
return pkgs;
|
||||
}, {});
|
||||
|
||||
const SJS_SPEC_FILENAME = 'e2e-spec.ts';
|
||||
const CLI_SPEC_FILENAME = 'e2e/src/app.e2e-spec.ts';
|
||||
const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
|
||||
const MAX_NO_OUTPUT_TIMEOUT = 1000 * 60 * 5; // 5 minutes
|
||||
|
||||
/**
|
||||
* Run Protractor End-to-End Tests for Doc Samples
|
||||
* Run Protractor End-to-End Tests for a Docs Example
|
||||
*
|
||||
* Usage: node run-example-e2e.mjs <examplePath> <yarnPath> <exampleDepsWorkspaceName> [localPackage...]
|
||||
*
|
||||
* Args:
|
||||
* examplePath: path to the example
|
||||
* yarnPath: path to a vendored version of yarn
|
||||
* exampleDepsWorkspaceName: name of bazel workspace containing example node_omodules
|
||||
* localPackages: a vararg of local packages to substitute in place npm deps, in the form @package/name#pathToPackage.
|
||||
*
|
||||
* Flags
|
||||
* --retry to retry failed tests (useful for overcoming flakes)
|
||||
* e.g. --retry 3 // To try each test up to 3 times.
|
||||
*/
|
||||
async function runE2e(examplePath, nodeModulesPath) {
|
||||
|
||||
async function runE2e(examplePath) {
|
||||
const exampleName = path.basename(examplePath);
|
||||
const maxAttempts = argv.retry || 1;
|
||||
try {
|
||||
examplePath = createCopyOfExampleForTest(exampleName, examplePath);
|
||||
symlinkNodeModules(examplePath, nodeModulesPath);
|
||||
await constructNodeModules(examplePath);
|
||||
|
||||
let testFn;
|
||||
if (isSystemJsTest(examplePath)) {
|
||||
@@ -165,8 +181,74 @@ function runProtractorAoT(exampleName, appDir) {
|
||||
return runProtractorSystemJS(exampleName, promise, appDir, aotRunSpawnInfo);
|
||||
}
|
||||
|
||||
function symlinkNodeModules(examplePath, nodeModulesPath) {
|
||||
fs.ensureSymlinkSync(nodeModulesPath, path.join(examplePath, 'node_modules'), 'dir');
|
||||
async function constructNodeModules(examplePath) {
|
||||
const linkedNodeModules = path.resolve(examplePath, 'node_modules');
|
||||
const exampleDepsNodeModules = path.resolve('..', EXAMPLE_DEPS_WORKSPACE_NAME, 'node_modules');
|
||||
fs.ensureDirSync(linkedNodeModules);
|
||||
|
||||
await Promise.all([
|
||||
linkExampleDeps(exampleDepsNodeModules, linkedNodeModules),
|
||||
linkLocalDeps(exampleDepsNodeModules, linkedNodeModules)
|
||||
]);
|
||||
|
||||
fs.copySync(path.join(exampleDepsNodeModules, '.bin'), path.join(linkedNodeModules, '.bin'));
|
||||
pointBinSymlinksToLocalPackages(linkedNodeModules);
|
||||
}
|
||||
|
||||
// The .bin folder is copied over from the original yarn_install repository, so the
|
||||
// bin symlinks point there. When we link local packages in place of their npm equivalent,
|
||||
// we need to alter those symlinks to point into the local package.
|
||||
function pointBinSymlinksToLocalPackages(linkedNodeModules) {
|
||||
if (os.platform() === 'win32') {
|
||||
// Bins on Windows are not symlinks; they are scripts that will invoke the bin
|
||||
// relative to their location. The relative path will already point to the symlinked
|
||||
// local package, so no further action is required.
|
||||
return;
|
||||
}
|
||||
const allNodeModuleBins = globbySync(['**'], {cwd: path.join(linkedNodeModules, '.bin'), onlyFiles: true});
|
||||
allNodeModuleBins.forEach(bin => {
|
||||
const symlinkTarget = fs.readlinkSync(path.join(linkedNodeModules, '.bin', bin));
|
||||
for (const pkgName of Object.keys(LOCAL_PACKAGES)) {
|
||||
const binMightBeInLocalPackage = symlinkTarget.includes(path.join(EXAMPLE_DEPS_WORKSPACE_NAME, 'node_modules', pkgName) + path.sep);
|
||||
if (binMightBeInLocalPackage) {
|
||||
const pathToBinWithinPackage = symlinkTarget.substring(symlinkTarget.indexOf(pkgName) + pkgName.length + path.sep.length);
|
||||
const binExistsInLocalPackage = fs.existsSync(path.join(linkedNodeModules, pkgName, pathToBinWithinPackage));
|
||||
if (binExistsInLocalPackage) {
|
||||
// Replace the copied bin symlink with one that points to the symlinked local package.
|
||||
fs.rmSync(path.join(linkedNodeModules, '.bin', bin));
|
||||
fs.ensureSymlinkSync(path.join('..', pkgName, pathToBinWithinPackage), path.join(linkedNodeModules, '.bin', bin));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function linkExampleDeps(exampleDepsNodeModules, linkedNodeModules) {
|
||||
const exampleDepsPackages = getPackageNamesFromNodeModules(exampleDepsNodeModules);
|
||||
|
||||
return Promise.all(exampleDepsPackages
|
||||
.filter(pkgName => !(pkgName in LOCAL_PACKAGES))
|
||||
.map(pkgName => fs.ensureSymlink(path.join(exampleDepsNodeModules, pkgName), path.join(linkedNodeModules, pkgName), 'dir'))
|
||||
);
|
||||
}
|
||||
|
||||
async function linkLocalDeps(exampleDepsNodeModules, linkedNodeModules) {
|
||||
const hasNpmDepForPkg = await Promise.all(Object.keys(LOCAL_PACKAGES).map(pkgName => fs.pathExists(path.join(exampleDepsNodeModules, pkgName))));
|
||||
|
||||
return Promise.all(Object.keys(LOCAL_PACKAGES).filter((pkgName, i) => hasNpmDepForPkg[i])
|
||||
.map(pkgName => fs.ensureSymlinkSync(LOCAL_PACKAGES[pkgName], path.join(linkedNodeModules, pkgName), 'dir')));
|
||||
}
|
||||
|
||||
function getPackageNamesFromNodeModules(nodeModulesPath) {
|
||||
return globbySync([
|
||||
'@*/*',
|
||||
'!@*$', // Exclude a namespace folder itself
|
||||
'(?!@)*',
|
||||
'!.bin',
|
||||
'!.yarn-integrity',
|
||||
'!_*'
|
||||
], {cwd: nodeModulesPath, onlyDirectories: true, dot: true});
|
||||
}
|
||||
|
||||
// Start the example in appDir; then run protractor with the specified
|
||||
@@ -188,6 +270,16 @@ function runE2eTestsCLI(exampleName, appDir) {
|
||||
}
|
||||
}
|
||||
|
||||
// When local packages are symlinked in, node has trouble resolving some peer deps. Setting
|
||||
// preserveSymlinks: true in angular.json fixes this. This isn't required without local
|
||||
// packages because in the worst case we would leak into the original Bazel repository
|
||||
// and it would still find a node_modules folder for resolution.
|
||||
if (Object.keys(LOCAL_PACKAGES).length > 0) {
|
||||
const angularJson = jsonc.load(path.join(appDir, 'angular.json'), {encoding: 'utf-8'});
|
||||
angularJson.projects['angular.io-example'].architect.build.options.preserveSymlinks = true;
|
||||
fs.writeFileSync(path.join(appDir, 'angular.json'), JSON.stringify(angularJson));
|
||||
}
|
||||
|
||||
// `--no-webdriver-update` is needed to preserve the ChromeDriver version already installed.
|
||||
const testCommands = config.tests || [{
|
||||
cmd: NODE,
|
||||
@@ -306,4 +398,5 @@ function adjustChromeBinPathForWindows() {
|
||||
return process.env.CHROME_BIN;
|
||||
}
|
||||
|
||||
runE2e(EXAMPLE_PATH, NODE_MODULES_PATH);
|
||||
runE2e(EXAMPLE_PATH);
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
load("//aio/tools/ng-packages-installer:node_modules.bzl", "node_modules")
|
||||
load("//:packages.bzl", "AIO_EXAMPLE_PACKAGES", "to_package_label")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
@@ -10,12 +7,3 @@ filegroup(
|
||||
exclude = ["BUILD.bazel"],
|
||||
),
|
||||
)
|
||||
|
||||
node_modules(
|
||||
name = "node_modules",
|
||||
)
|
||||
|
||||
node_modules(
|
||||
name = "local/node_modules",
|
||||
local_package_substitutions = [to_package_label(pkg) for pkg in AIO_EXAMPLE_PACKAGES],
|
||||
)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:jasmine/recommended',
|
||||
],
|
||||
env: {
|
||||
es2020: true,
|
||||
jasmine: true,
|
||||
node: true,
|
||||
},
|
||||
plugins: [
|
||||
'jasmine',
|
||||
],
|
||||
rules: {
|
||||
'indent': ['error', 2],
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
'max-len': ['error', 120],
|
||||
'quotes': ['error', 'single'],
|
||||
'semi': ['error', 'always'],
|
||||
'jasmine/new-line-before-expect': ['off'],
|
||||
},
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
load("//aio/tools:defaults.bzl", "nodejs_binary")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
js_library(
|
||||
name = "lib",
|
||||
srcs = ["index.js"],
|
||||
deps = [
|
||||
"@aio_npm//@yarnpkg/lockfile",
|
||||
"@aio_npm//canonical-path",
|
||||
"@aio_npm//fs-extra",
|
||||
"@aio_npm//semver",
|
||||
"@aio_npm//shelljs",
|
||||
"@aio_npm//yargs",
|
||||
],
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "ng-packages-installer",
|
||||
data = [
|
||||
":lib",
|
||||
],
|
||||
entry_point = ":lib",
|
||||
)
|
||||
@@ -1,336 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const lockfile = require('@yarnpkg/lockfile');
|
||||
const path = require('canonical-path');
|
||||
const semver = require('semver');
|
||||
const shelljs = require('shelljs');
|
||||
const yargs = require('yargs');
|
||||
|
||||
const PACKAGE_JSON = 'package.json';
|
||||
const YARN_LOCK = 'yarn.lock';
|
||||
|
||||
/**
|
||||
* A tool that creates a node_modules folder with optional dependencies from locally
|
||||
* built distributables.
|
||||
*
|
||||
* This tool is used to change dependencies of the `aio` application and the example
|
||||
* applications to point to locally build angular packages.
|
||||
*/
|
||||
class NgPackagesInstaller {
|
||||
|
||||
/**
|
||||
* Install node_modules for a project in the specified directory.
|
||||
*
|
||||
* @param {string} outputDir - path to output the node_modules directory from the bazel execroot.
|
||||
* @param {string} packageJson - path to package.json file from the bazel execroot.
|
||||
* @param {string} yarnLock - path to the yarn lockfile from the bazel execroot.
|
||||
* @param {object} options - a hash of options for the install:
|
||||
* * `debug` (`boolean`) - whether to display debug messages.
|
||||
* * `localPackages` (`string[]`) - list of paths to local packages to substitute for their third-party equivalents
|
||||
* * `modulesFolder` (`string`) - name of the resulting node_modules folder
|
||||
*/
|
||||
constructor(outputDir, packageJson, yarnLock, options = {}) {
|
||||
this.debug = this._parseBooleanArg(options.debug);
|
||||
this.buildPackages = this._parseBooleanArg(options.buildPackages);
|
||||
this.modulesFolder = options.modulesFolder;
|
||||
this.localPackages = options.localPackages || [];
|
||||
this.outputDir = path.resolve(outputDir);
|
||||
this.packageJson = path.resolve(packageJson);
|
||||
this.yarnLock = path.resolve(yarnLock);
|
||||
|
||||
this._log('Output directory:', this.outputDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install locally built dependencies, overriding the dependencies in the `package.json`.
|
||||
*/
|
||||
installLocalDependencies() {
|
||||
// Copy package.json, yarn.lock, and the locally-built packages to a temporary directory
|
||||
// to form node_modules.
|
||||
//
|
||||
// 1. We cannot use Yarn --modules-folder (it causes issues when resolving bins for postinstall scripts)
|
||||
// so we always need to put into $CWD/node_modules`
|
||||
// 2. To avoid conflicts with multiple such targets in the same Bazel package --> we construct
|
||||
// the folder in a temporary directory and copy it over to the destination directory.
|
||||
const tempDir = path.join(this.outputDir, this.modulesFolder + '_tmp');
|
||||
|
||||
const pathToPackageConfig = path.resolve(tempDir, PACKAGE_JSON);
|
||||
fs.copySync(this.packageJson, pathToPackageConfig)
|
||||
fs.chmodSync(path.join(tempDir, PACKAGE_JSON), '755');
|
||||
|
||||
const pathToLockfile = path.resolve(tempDir, YARN_LOCK);
|
||||
fs.copySync(this.yarnLock, pathToLockfile)
|
||||
fs.chmodSync(path.join(tempDir, YARN_LOCK), '755');
|
||||
|
||||
|
||||
const packageConfigFile = fs.readFileSync(pathToPackageConfig, 'utf8');
|
||||
const packageConfig = JSON.parse(packageConfigFile);
|
||||
const parsedLockfile = this._parseLockfile(pathToLockfile);
|
||||
|
||||
try {
|
||||
|
||||
if (this.localPackages.length) {
|
||||
const localPackagesDir = path.join(tempDir, 'local_packages');
|
||||
this._copyLocalPackagesTo(localPackagesDir);
|
||||
|
||||
const packages = this._getDistPackages(localPackagesDir);
|
||||
|
||||
// Overwrite local packages dependencies to other packages with local files.
|
||||
Object.keys(packages).forEach(key => {
|
||||
const pkg = packages[key];
|
||||
const tmpConfig = JSON.parse(JSON.stringify(pkg.config));
|
||||
|
||||
// Prevent accidental publishing of the package, if something goes wrong.
|
||||
tmpConfig.private = true;
|
||||
|
||||
// Overwrite project dependencies/devDependencies to packages with local files.
|
||||
['dependencies', 'devDependencies'].forEach(prop => {
|
||||
const deps = tmpConfig[prop] || {};
|
||||
Object.keys(deps).forEach(key2 => {
|
||||
const pkg2 = packages[key2];
|
||||
if (pkg2) {
|
||||
// point the local packages at the distributable folder
|
||||
deps[key2] = `file:${pkg2.packageDir}`;
|
||||
this._log(`Overriding dependency of local ${key} with local package: ${key2}: ${deps[key2]}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Overwrite the package's version to avoid version mismatch errors with the CLI.
|
||||
this._overwritePackageVersion(key, tmpConfig, packageConfig, parsedLockfile);
|
||||
|
||||
fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(tmpConfig, null, 2));
|
||||
});
|
||||
|
||||
const [dependencies, peers] = this._collectDependencies(packageConfig.dependencies || {}, packages);
|
||||
const [devDependencies, devPeers] = this._collectDependencies(packageConfig.devDependencies || {}, packages);
|
||||
|
||||
this._assignPeerDependencies(peers, dependencies, devDependencies, parsedLockfile);
|
||||
this._assignPeerDependencies(devPeers, dependencies, devDependencies, parsedLockfile);
|
||||
|
||||
const localPackageConfig = Object.assign(Object.create(null), packageConfig, { dependencies, devDependencies });
|
||||
localPackageConfig.__angular = { local: true };
|
||||
const localPackageConfigJson = JSON.stringify(localPackageConfig, null, 2);
|
||||
|
||||
this._log(`Writing temporary local ${PACKAGE_JSON} to ${pathToPackageConfig}`);
|
||||
fs.writeFileSync(pathToPackageConfig, localPackageConfigJson);
|
||||
}
|
||||
|
||||
this._installDeps('--pure-lockfile', '--cwd', tempDir);
|
||||
|
||||
// We change the name of the node_modules folder manually rather than using yarn's --modules-folder argument because it
|
||||
// doesn't work well with invoking .bin executables in yarn scripts (https://github.com/yarnpkg/yarn/issues/8134).
|
||||
fs.moveSync(path.join(tempDir, 'node_modules'), path.join(this.outputDir, this.modulesFolder), {overwrite: true});
|
||||
} finally {
|
||||
fs.rmSync(tempDir, {recursive: true});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinstall the original package.json dependencies
|
||||
* Yarn will also delete the local marker file for us.
|
||||
*/
|
||||
restoreNpmDependencies() {
|
||||
this._installDeps('--frozen-lockfile', '--check-files');
|
||||
}
|
||||
|
||||
|
||||
_copyLocalPackagesTo(dest) {
|
||||
for (let pkg of this.localPackages) {
|
||||
// Get the package name from the path: .../{packageName}/npm_package.
|
||||
const name = path.basename(path.resolve(pkg, ".."))
|
||||
fs.copySync(pkg, path.join(dest, name))
|
||||
fs.chmodSync(path.join(dest, name, PACKAGE_JSON), '755')
|
||||
}
|
||||
}
|
||||
|
||||
_assignPeerDependencies(peerDependencies, dependencies, devDependencies, parsedLockfile) {
|
||||
Object.keys(peerDependencies).forEach(key => {
|
||||
const peerDepRange = peerDependencies[key];
|
||||
|
||||
// Ignore peerDependencies whose range is already satisfied by current version in lockfile.
|
||||
const originalRange = dependencies[key] || devDependencies[key];
|
||||
const lockfileVersion = originalRange && parsedLockfile[`${key}@${originalRange}`].version;
|
||||
|
||||
if (lockfileVersion && semver.satisfies(lockfileVersion, peerDepRange)) return;
|
||||
|
||||
// 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}: ${peerDepRange}`);
|
||||
dependencies[key] = peerDepRange;
|
||||
} else {
|
||||
this._log(
|
||||
`${devDependencies[key] ? 'Overriding' : 'Assigning'} devDependency with peerDependency: ` +
|
||||
`${key}: ${peerDepRange}`);
|
||||
devDependencies[key] = peerDepRange;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_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:${sourcePackage.packageDir}`;
|
||||
this._log(`Overriding dependency with local package: ${key}: ${mergedDependencies[key]}`);
|
||||
// grab peer dependencies
|
||||
const sourcePackagePeerDeps = sourcePackage.config.peerDependencies || {};
|
||||
Object.keys(sourcePackagePeerDeps)
|
||||
// ignore peerDependencies which are already core Angular/Zone.js packages
|
||||
.filter(key => !packages[key])
|
||||
.forEach(key => peerDependencies[key] = sourcePackagePeerDeps[key]);
|
||||
}
|
||||
});
|
||||
|
||||
return [mergedDependencies, peerDependencies];
|
||||
}
|
||||
|
||||
/**
|
||||
* A hash of local package configs included with --local-packages
|
||||
*/
|
||||
_getDistPackages(containingDir) {
|
||||
const collectPackages = containingDir => {
|
||||
const packages = {};
|
||||
|
||||
for (const dirName of shelljs.ls(containingDir)) {
|
||||
const packageDir = path.resolve(containingDir, dirName);
|
||||
const packageJsonPath = path.join(packageDir, PACKAGE_JSON);
|
||||
const packageConfig = fs.existsSync(packageJsonPath) ? require(packageJsonPath) : null;
|
||||
const packageName = packageConfig && packageConfig.name;
|
||||
|
||||
if (!packageConfig) {
|
||||
// No `package.json` found - this directory is not a package.
|
||||
continue;
|
||||
} else if (!packageName) {
|
||||
// No `name` property in `package.json`. (This should never happen.)
|
||||
throw new Error(`Package '${packageDir}' specifies no name in its '${PACKAGE_JSON}'.`);
|
||||
}
|
||||
|
||||
packages[packageName] = {
|
||||
packageDir,
|
||||
packageJsonPath,
|
||||
config: packageConfig,
|
||||
};
|
||||
}
|
||||
|
||||
return packages;
|
||||
};
|
||||
|
||||
const packageConfigs = {
|
||||
...collectPackages(containingDir)
|
||||
};
|
||||
|
||||
this._log('Found the following distributables:', ...Object.keys(packageConfigs).map(key => `\n - ${key}`));
|
||||
return packageConfigs;
|
||||
}
|
||||
|
||||
_installDeps(...options) {
|
||||
const command = `${process.execPath} ${process.env.YARN} install ${options.join(' ')}`;
|
||||
this._log('Installing dependencies with:', command);
|
||||
shelljs.exec(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}`)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a package's version with the fake version based on the package's original version in the projects's
|
||||
* lockfile.
|
||||
*
|
||||
* **Background:**
|
||||
* This helps avoid version mismatch errors with the CLI.
|
||||
* Since the version set by bazel on the locally built packages is determined based on the latest tag for a commit on
|
||||
* the current branch, it is often the case that this version is older than what the current `@angular/cli` version is
|
||||
* compatible with (e.g. if the user has not fetched the latest tags from `angular/angular` or the branch has not been
|
||||
* rebased recently.
|
||||
*
|
||||
* @param {string} packageName - The name of the package we are updating (e.g. `'@angular/core'`).
|
||||
* @param {{[key: string]: any}} packageConfig - The package's parsed `package.json`.
|
||||
* @param {{[key: string]: any}} projectConfig - The project's parsed `package.json`.
|
||||
* @param {import('@yarnpkg/lockfile').LockFileObject} projectLockfile - The projects's parsed `yarn.lock`.
|
||||
*/
|
||||
_overwritePackageVersion(packageName, packageConfig, projectConfig, projectLockfile) {
|
||||
const projectVersionRange = (projectConfig.dependencies || {})[packageName] ||
|
||||
(projectConfig.devDependencies || {})[packageName];
|
||||
const projectVersion = (projectLockfile[`${packageName}@${projectVersionRange}`] || {}).version;
|
||||
|
||||
if (projectVersion !== undefined) {
|
||||
const newVersion = `${projectVersion}+locally-overwritten-by-ngPackagesInstaller`;
|
||||
this._log(`Overwriting the version of '${packageName}': ${packageConfig.version} --> ${newVersion}`);
|
||||
packageConfig.version = newVersion;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the value for a boolean cli argument/option. When passing an option multiple times, `yargs` parses it as an
|
||||
* array of boolean values. In that case, we only care about the last occurrence.
|
||||
*
|
||||
* This can be useful, for example, when one has a base command with the option turned on and another command
|
||||
* (building on top of the first one) turning the option off:
|
||||
* ```
|
||||
* "base-command": "my-script --foo --bar",
|
||||
* "no-bar-command": "yarn base-command --no-bar",
|
||||
* ```
|
||||
*/
|
||||
_parseBooleanArg(value) {
|
||||
return Array.isArray(value) ? value.pop() : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and return a `yarn.lock` file.
|
||||
*/
|
||||
_parseLockfile(lockfilePath) {
|
||||
const lockfileContent = fs.readFileSync(lockfilePath, 'utf8');
|
||||
const parsed = lockfile.parse(lockfileContent);
|
||||
|
||||
if (parsed.type !== 'success') {
|
||||
throw new Error(
|
||||
`[${NgPackagesInstaller.name}]: Error parsing lockfile '${lockfilePath}' (result type: ${parsed.type}).`);
|
||||
}
|
||||
|
||||
return parsed.object;
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
shelljs.set('-e');
|
||||
|
||||
const createInstaller = argv => {
|
||||
const {outputDir, packageJson, yarnLock, ...options} = argv;
|
||||
return new NgPackagesInstaller(outputDir, packageJson, yarnLock, options);
|
||||
};
|
||||
|
||||
/* eslint-disable max-len */
|
||||
yargs
|
||||
.usage('$0 [args]')
|
||||
.option('debug', { describe: 'Print additional debug information.', default: false })
|
||||
.option('local-packages', { describe: 'List of locally built packages that should be substituted in place of their npm equivalent.', default: [], array: true })
|
||||
.option('modules-folder', { describe: 'Name of the node_modules folder.', default: 'node_modules'})
|
||||
.command('* <outputDir> <packageJson> <yarnLock> [--debug] [--local-packages package1Path package2Path] [--modules-folder node_modules]', 'Install dependencies from the locally built Angular/Zone.js distributables.', () => {}, argv => {
|
||||
createInstaller(argv).installLocalDependencies();
|
||||
})
|
||||
.strict()
|
||||
.wrap(yargs.terminalWidth())
|
||||
.argv;
|
||||
/* eslint-enable max-len */
|
||||
}
|
||||
|
||||
module.exports = NgPackagesInstaller;
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
@@ -1,587 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const lockfile = require('@yarnpkg/lockfile');
|
||||
const path = require('canonical-path');
|
||||
const shelljs = require('shelljs');
|
||||
|
||||
const NgPackagesInstaller = require('./index');
|
||||
|
||||
describe('NgPackagesInstaller', () => {
|
||||
const projectDir = 'root/dir';
|
||||
const absoluteProjectDir = path.resolve(projectDir);
|
||||
const nodeModulesDir = path.resolve(absoluteProjectDir, 'node_modules');
|
||||
const packageJsonPath = path.resolve(absoluteProjectDir, 'package.json');
|
||||
const yarnLockPath = path.resolve(absoluteProjectDir, 'yarn.lock');
|
||||
const ngRootDir = path.resolve(__dirname, '../../..');
|
||||
const packagesDir = path.join(ngRootDir, 'dist/packages-dist');
|
||||
const aimwaDir = path.join(ngRootDir, 'dist/angular-in-memory-web-api-dist');
|
||||
const zoneJsDir = path.join(ngRootDir, 'dist/zone.js-dist');
|
||||
const toolsDir = path.join(ngRootDir, 'dist/tools/@angular');
|
||||
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(projectDir);
|
||||
});
|
||||
|
||||
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(projectDir, '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(projectDir, 'node_modules/_local_.json'));
|
||||
expect(installer._printWarning).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('installLocalDependencies()', () => {
|
||||
const copyJsonObj = obj => JSON.parse(JSON.stringify(obj));
|
||||
let dummyLocalPackages, dummyPackage, dummyPackageJson, expectedModifiedPackage, expectedModifiedPackageJson;
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(installer, '_checkLocalMarker');
|
||||
spyOn(installer, '_installDeps');
|
||||
spyOn(installer, '_setLocalMarker');
|
||||
|
||||
spyOn(installer, '_parseLockfile').and.returnValue({
|
||||
'rxjs@^6.3.0': {version: '6.3.3'},
|
||||
'rxjs-dev@^6.3.0': {version: '6.4.2'}
|
||||
});
|
||||
|
||||
// These are the packages that are "found" in the dist directory
|
||||
dummyLocalPackages = {
|
||||
'@angular/core': {
|
||||
packageDir: `${packagesDir}/core`,
|
||||
packageJsonPath: `${packagesDir}/core/package.json`,
|
||||
config: {
|
||||
peerDependencies: {
|
||||
'rxjs': '^6.4.0',
|
||||
'rxjs-dev': '^6.4.0',
|
||||
'some-package': '5.0.1',
|
||||
'zone.js': '~0.8.26'
|
||||
}
|
||||
}
|
||||
},
|
||||
'@angular/common': {
|
||||
packageDir: `${packagesDir}/common`,
|
||||
packageJsonPath: `${packagesDir}/common/package.json`,
|
||||
config: { peerDependencies: { '@angular/core': '4.4.4-1ab23cd4' } }
|
||||
},
|
||||
'@angular/compiler': {
|
||||
packageDir: `${packagesDir}/compiler`,
|
||||
packageJsonPath: `${packagesDir}/compiler/package.json`,
|
||||
config: { peerDependencies: { '@angular/common': '4.4.4-1ab23cd4' } }
|
||||
},
|
||||
'@angular/compiler-cli': {
|
||||
packageDir: `${toolsDir}/compiler-cli`,
|
||||
packageJsonPath: `${toolsDir}/compiler-cli/package.json`,
|
||||
config: {
|
||||
dependencies: { '@angular/tsc-wrapped': '4.4.4-1ab23cd4' },
|
||||
peerDependencies: { typescript: '^2.4.2', '@angular/compiler': '4.4.4-1ab23cd4' }
|
||||
}
|
||||
},
|
||||
'@angular/tsc-wrapped': {
|
||||
packageDir: `${toolsDir}/tsc-wrapped`,
|
||||
packageJsonPath: `${toolsDir}/tsc-wrapped/package.json`,
|
||||
config: {
|
||||
devDependencies: { '@angular/common': '4.4.4-1ab23cd4' },
|
||||
peerDependencies: { tsickle: '^1.4.0' }
|
||||
}
|
||||
},
|
||||
'angular-in-memory-web-api': {
|
||||
packageDir: `${aimwaDir}/angular-in-memory-web-api`,
|
||||
packageJsonPath: `${aimwaDir}/angular-in-memory-web-api/package.json`,
|
||||
config: {
|
||||
dependencies: { rxjs: '^6.3.0' }
|
||||
}
|
||||
},
|
||||
'zone.js': {
|
||||
packageDir: `${zoneJsDir}/zone.js`,
|
||||
packageJsonPath: `${zoneJsDir}/zone.js/package.json`,
|
||||
config: {
|
||||
devDependencies: { typescript: '^2.4.2' }
|
||||
}
|
||||
},
|
||||
};
|
||||
spyOn(installer, '_getDistPackages').and.callFake(() => copyJsonObj(dummyLocalPackages));
|
||||
|
||||
// This is the package.json in the "test" folder
|
||||
dummyPackage = {
|
||||
dependencies: {
|
||||
'@angular/core': '4.4.1',
|
||||
'@angular/common': '4.4.1',
|
||||
rxjs: '^6.3.0',
|
||||
'zone.js': '^0.8.26'
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/compiler-cli': '4.4.1',
|
||||
'angular-in-memory-web-api': '^0.11.0',
|
||||
'rxjs-dev': '^6.3.0'
|
||||
}
|
||||
};
|
||||
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/Zone.js (dev)dependencies have been modified to use a "file:" path
|
||||
// and that the peerDependencies from `dummyLocalPackages` have been updated or added as
|
||||
// (dev)dependencies (unless the current version in lockfile satisfies semver).
|
||||
//
|
||||
// For example, `rxjs-dev@6.4.2` (from lockfile) satisfies `rxjs-dev@^6.4.0` (from
|
||||
// `@angular/core`), thus `rxjs-dev: ^6.3.0` (from original `package.json`) is retained.
|
||||
// In contrast, `rxjs@6.3.3` (from lockfile) does not satisfy `rxjs@^6.4.0 (from
|
||||
// `@angular/core`), thus `rxjs: ^6.3.0` (from original `package.json`) is replaced with
|
||||
// `rxjs: ^6.4.0` (from `@angular/core`).
|
||||
expectedModifiedPackage = {
|
||||
dependencies: {
|
||||
'@angular/core': `file:${packagesDir}/core`,
|
||||
'@angular/common': `file:${packagesDir}/common`,
|
||||
'rxjs': '^6.4.0',
|
||||
'zone.js': `file:${zoneJsDir}/zone.js`,
|
||||
},
|
||||
devDependencies: {
|
||||
'@angular/compiler-cli': `file:${toolsDir}/compiler-cli`,
|
||||
'angular-in-memory-web-api': `file:${aimwaDir}/angular-in-memory-web-api`,
|
||||
'rxjs-dev': '^6.3.0',
|
||||
'some-package': '5.0.1',
|
||||
typescript: '^2.4.2'
|
||||
},
|
||||
__angular: { local: true }
|
||||
};
|
||||
expectedModifiedPackageJson = JSON.stringify(expectedModifiedPackage, null, 2);
|
||||
});
|
||||
|
||||
describe('when there is a local package marker', () => {
|
||||
beforeEach(() => installer._checkLocalMarker.and.returnValue(true));
|
||||
|
||||
it('should not continue processing', () => {
|
||||
installer.installLocalDependencies();
|
||||
expect(installer._checkLocalMarker).toHaveBeenCalledWith();
|
||||
expect(installer._getDistPackages).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should continue processing (without checking for local marker) if `force` is true', () => {
|
||||
installer.force = true;
|
||||
installer.installLocalDependencies();
|
||||
expect(installer._checkLocalMarker).not.toHaveBeenCalled();
|
||||
expect(installer._getDistPackages).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is no local package marker', () => {
|
||||
let log;
|
||||
|
||||
beforeEach(() => {
|
||||
log = [];
|
||||
fs.writeFileSync.and.callFake((filePath, contents) =>
|
||||
filePath === packageJsonPath && log.push(`writeFile: ${contents}`));
|
||||
installer._installDeps.and.callFake((...args) => log.push(`installDeps: ${args.join(' ')}`));
|
||||
installer._checkLocalMarker.and.returnValue(false);
|
||||
installer.installLocalDependencies();
|
||||
});
|
||||
|
||||
it('should parse the lockfile and get the dist packages', () => {
|
||||
expect(installer._checkLocalMarker).toHaveBeenCalledWith();
|
||||
expect(installer._parseLockfile).toHaveBeenCalledWith(yarnLockPath);
|
||||
expect(installer._getDistPackages).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should temporarily overwrite the package.json files of local Angular packages', () => {
|
||||
const pkgJsonPathFor = pkgName => dummyLocalPackages[pkgName].packageJsonPath;
|
||||
const pkgConfigFor = pkgName => copyJsonObj(dummyLocalPackages[pkgName].config);
|
||||
const overwriteConfigFor = (pkgName, newProps) => Object.assign(pkgConfigFor(pkgName), newProps);
|
||||
const stringifyConfig = config => JSON.stringify(config, null, 2);
|
||||
|
||||
const allArgs = fs.writeFileSync.calls.allArgs();
|
||||
const firstSevenArgs = allArgs.slice(0, 7);
|
||||
const lastSevenArgs = allArgs.slice(-7);
|
||||
|
||||
expect(firstSevenArgs).toEqual([
|
||||
[
|
||||
pkgJsonPathFor('@angular/core'),
|
||||
stringifyConfig(overwriteConfigFor('@angular/core', {private: true})),
|
||||
],
|
||||
[
|
||||
pkgJsonPathFor('@angular/common'),
|
||||
stringifyConfig(overwriteConfigFor('@angular/common', {private: true})),
|
||||
],
|
||||
[
|
||||
pkgJsonPathFor('@angular/compiler'),
|
||||
stringifyConfig(overwriteConfigFor('@angular/compiler', {private: true})),
|
||||
],
|
||||
[
|
||||
pkgJsonPathFor('@angular/compiler-cli'),
|
||||
stringifyConfig(overwriteConfigFor('@angular/compiler-cli', {
|
||||
private: true,
|
||||
dependencies: { '@angular/tsc-wrapped': `file:${toolsDir}/tsc-wrapped` },
|
||||
})),
|
||||
],
|
||||
[
|
||||
pkgJsonPathFor('@angular/tsc-wrapped'),
|
||||
stringifyConfig(overwriteConfigFor('@angular/tsc-wrapped', {
|
||||
private: true,
|
||||
devDependencies: { '@angular/common': `file:${packagesDir}/common` },
|
||||
})),
|
||||
],
|
||||
[
|
||||
pkgJsonPathFor('angular-in-memory-web-api'),
|
||||
stringifyConfig(overwriteConfigFor('angular-in-memory-web-api', {private: true})),
|
||||
],
|
||||
[
|
||||
pkgJsonPathFor('zone.js'),
|
||||
stringifyConfig(overwriteConfigFor('zone.js', {private: true})),
|
||||
],
|
||||
]);
|
||||
|
||||
expect(lastSevenArgs).toEqual([
|
||||
'@angular/core',
|
||||
'@angular/common',
|
||||
'@angular/compiler',
|
||||
'@angular/compiler-cli',
|
||||
'@angular/tsc-wrapped',
|
||||
'angular-in-memory-web-api',
|
||||
'zone.js',
|
||||
].map(pkgName => [pkgJsonPathFor(pkgName), stringifyConfig(pkgConfigFor(pkgName))]));
|
||||
});
|
||||
|
||||
it('should load the package.json', () => {
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith(packageJsonPath, 'utf8');
|
||||
});
|
||||
|
||||
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: --pure-lockfile --check-files',
|
||||
`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` in the specified directory, with the correct options', () => {
|
||||
spyOn(installer, '_installDeps');
|
||||
installer.restoreNpmDependencies();
|
||||
expect(installer._installDeps).toHaveBeenCalledWith('--frozen-lockfile', '--check-files');
|
||||
});
|
||||
});
|
||||
|
||||
describe('_buildDistPackages()', () => {
|
||||
// Call `_buildDistPackages()` with a mock `process.platform` value.
|
||||
const buildDistPackagesOnPlatform = platform => {
|
||||
const originalDescriptor = Object.getOwnPropertyDescriptor(process, 'platform');
|
||||
Object.defineProperty(process, 'platform', {...originalDescriptor, value: platform});
|
||||
installer._buildDistPackages();
|
||||
Object.defineProperty(process, 'platform', originalDescriptor);
|
||||
};
|
||||
|
||||
it('should build the local packages, when not on Windows', () => {
|
||||
const buildCmd = 'yarn -s build';
|
||||
|
||||
buildDistPackagesOnPlatform('linux');
|
||||
expect(shelljs.exec).toHaveBeenCalledWith(buildCmd, {cwd: ngRootDir});
|
||||
|
||||
shelljs.exec.calls.reset();
|
||||
|
||||
buildDistPackagesOnPlatform('darwin');
|
||||
expect(shelljs.exec).toHaveBeenCalledWith(buildCmd, {cwd: ngRootDir});
|
||||
|
||||
shelljs.exec.calls.reset();
|
||||
|
||||
buildDistPackagesOnPlatform('anythingButWindows :(');
|
||||
expect(shelljs.exec).toHaveBeenCalledWith(buildCmd, {cwd: ngRootDir});
|
||||
});
|
||||
|
||||
it('should print a warning, when on Windows', () => {
|
||||
buildDistPackagesOnPlatform('win32');
|
||||
const warning = console.warn.calls.argsFor(0)[0];
|
||||
|
||||
expect(shelljs.exec).not.toHaveBeenCalled();
|
||||
expect(warning).toContain(
|
||||
'Automatically building the local Angular/angular-in-memory-web-api/zone.js packages is currently not ' +
|
||||
'supported on Windows.');
|
||||
expect(warning).toContain('Git Bash for Windows');
|
||||
expect(warning).toContain('Windows Subsystem for Linux');
|
||||
expect(warning).toContain('Linux docker container or VM');
|
||||
});
|
||||
});
|
||||
|
||||
describe('_getDistPackages()', () => {
|
||||
beforeEach(() => {
|
||||
fs.existsSync.and.callThrough();
|
||||
spyOn(NgPackagesInstaller.prototype, '_buildDistPackages');
|
||||
});
|
||||
|
||||
it('should not build the local packages by default', () => {
|
||||
installer._getDistPackages();
|
||||
expect(installer._buildDistPackages).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should build the local packages, if `buildPackages` is true', () => {
|
||||
installer = new NgPackagesInstaller(projectDir, {buildPackages: true});
|
||||
installer._getDistPackages();
|
||||
expect(installer._buildDistPackages).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not build the local packages, if `buildPackages` is false', () => {
|
||||
installer = new NgPackagesInstaller(projectDir, {buildPackages: false});
|
||||
installer._getDistPackages();
|
||||
expect(installer._buildDistPackages).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should include top level Angular and Zone.js packages', () => {
|
||||
const localPackages = installer._getDistPackages();
|
||||
const expectedValue = jasmine.objectContaining({
|
||||
packageDir: jasmine.any(String),
|
||||
packageJsonPath: jasmine.any(String),
|
||||
config: jasmine.any(Object),
|
||||
});
|
||||
|
||||
// For example...
|
||||
expect(localPackages['@angular/common']).toEqual(expectedValue);
|
||||
expect(localPackages['@angular/core']).toEqual(expectedValue);
|
||||
expect(localPackages['@angular/router']).toEqual(expectedValue);
|
||||
expect(localPackages['@angular/upgrade']).toEqual(expectedValue);
|
||||
expect(localPackages['zone.js']).toEqual(expectedValue);
|
||||
|
||||
expect(localPackages['@angular/upgrade/static']).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should store each package\'s directory', () => {
|
||||
const localPackages = installer._getDistPackages();
|
||||
|
||||
// For example...
|
||||
expect(localPackages['@angular/core'].packageDir).toBe(path.join(packagesDir, 'core'));
|
||||
expect(localPackages['@angular/router'].packageDir).toBe(path.join(packagesDir, 'router'));
|
||||
expect(localPackages['zone.js'].packageDir).toBe(path.join(zoneJsDir, 'zone.js'));
|
||||
});
|
||||
|
||||
it('should not include packages that have been ignored', () => {
|
||||
installer = new NgPackagesInstaller(projectDir, { ignorePackages: ['@angular/router'] });
|
||||
const localPackages = installer._getDistPackages();
|
||||
|
||||
expect(localPackages['@angular/common']).toBeDefined();
|
||||
expect(localPackages['@angular/router']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_log()', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(console, 'info');
|
||||
});
|
||||
|
||||
it('should assign the debug property from the options', () => {
|
||||
installer = new NgPackagesInstaller(projectDir, { debug: true });
|
||||
expect(installer.debug).toBe(true);
|
||||
installer = new NgPackagesInstaller(projectDir, { });
|
||||
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('_overwritePackageVersion()', () => {
|
||||
it('should do nothing if the specified package is not a dependency', () => {
|
||||
const pkgConfig = {name: '@scope/missing', version: 'local-version'};
|
||||
const lockFile = {
|
||||
[`${pkgConfig.name}@project-range`]: {version: 'project-version'},
|
||||
};
|
||||
let projectConfig;
|
||||
|
||||
// No `dependencies`/`devDependencies` at all.
|
||||
projectConfig = {};
|
||||
installer._overwritePackageVersion(pkgConfig.name, pkgConfig, projectConfig, lockFile);
|
||||
expect(pkgConfig.version).toBe('local-version');
|
||||
|
||||
// Not listed in `dependencies`/`devDependencies`.
|
||||
projectConfig = {
|
||||
dependencies: {otherPackage: 'foo'},
|
||||
devDependencies: {yetAnotherPackage: 'bar'},
|
||||
};
|
||||
installer._overwritePackageVersion(pkgConfig.name, pkgConfig, projectConfig, lockFile);
|
||||
expect(pkgConfig.version).toBe('local-version');
|
||||
});
|
||||
|
||||
it('should do nothing if the specified package cannot be found in the lockfile', () => {
|
||||
const pkgConfig = {name: '@scope/missing', version: 'local-version'};
|
||||
const projectConfig = {
|
||||
dependencies: {[pkgConfig.name]: 'project-range'},
|
||||
};
|
||||
let lockFile;
|
||||
|
||||
// Package missing from lockfile.
|
||||
lockFile = {
|
||||
'otherPackage@someRange': {version: 'some-version'},
|
||||
};
|
||||
installer._overwritePackageVersion(pkgConfig.name, pkgConfig, projectConfig, lockFile);
|
||||
expect(pkgConfig.version).toBe('local-version');
|
||||
|
||||
// Package present in lockfile, but for a different version range.
|
||||
lockFile = {
|
||||
[`${pkgConfig.name}@other-range`]: {version: 'project-version'},
|
||||
};
|
||||
installer._overwritePackageVersion(pkgConfig.name, pkgConfig, projectConfig, lockFile);
|
||||
expect(pkgConfig.version).toBe('local-version');
|
||||
});
|
||||
|
||||
it('should overwrite the package version if it is a dependency and found in the lockfile', () => {
|
||||
const pkgConfig = {name: '@scope/found', version: 'local-version'};
|
||||
const lockFile = {
|
||||
[`${pkgConfig.name}@project-range-prod`]: {version: 'project-version-prod'},
|
||||
[`${pkgConfig.name}@project-range-dev`]: {version: 'project-version-dev'},
|
||||
};
|
||||
let projectConfig;
|
||||
|
||||
// Package in `dependencies`.
|
||||
projectConfig = {
|
||||
dependencies: {[pkgConfig.name]: 'project-range-prod'},
|
||||
};
|
||||
installer._overwritePackageVersion(pkgConfig.name, pkgConfig, projectConfig, lockFile);
|
||||
expect(pkgConfig.version).toBe('project-version-prod+locally-overwritten-by-ngPackagesInstaller');
|
||||
|
||||
// // Package in `devDependencies`.
|
||||
projectConfig = {
|
||||
devDependencies: {[pkgConfig.name]: 'project-range-dev'},
|
||||
};
|
||||
installer._overwritePackageVersion(pkgConfig.name, pkgConfig, projectConfig, lockFile);
|
||||
expect(pkgConfig.version).toBe('project-version-dev+locally-overwritten-by-ngPackagesInstaller');
|
||||
|
||||
// // Package in both `dependencies` and `devDependencies` (the former takes precedence).
|
||||
projectConfig = {
|
||||
devDependencies: {[pkgConfig.name]: 'project-range-dev'},
|
||||
dependencies: {[pkgConfig.name]: 'project-range-prod'},
|
||||
};
|
||||
installer._overwritePackageVersion(pkgConfig.name, pkgConfig, projectConfig, lockFile);
|
||||
expect(pkgConfig.version).toBe('project-version-prod+locally-overwritten-by-ngPackagesInstaller');
|
||||
});
|
||||
});
|
||||
|
||||
describe('_parseLockfile()', () => {
|
||||
let originalLockfileParseDescriptor;
|
||||
|
||||
beforeEach(() => {
|
||||
// Workaround for `lockfile.parse()` being non-writable.
|
||||
let parse = lockfile.parse;
|
||||
originalLockfileParseDescriptor = Object.getOwnPropertyDescriptor(lockfile, 'parse');
|
||||
Object.defineProperty(lockfile, 'parse', {
|
||||
get() { return parse; },
|
||||
set(newParse) { parse = newParse; },
|
||||
});
|
||||
|
||||
fs.readFileSync.and.returnValue('mock content');
|
||||
spyOn(lockfile, 'parse').and.returnValue({type: 'success', object: {foo: {version: 'bar'}}});
|
||||
});
|
||||
|
||||
afterEach(() => Object.defineProperty(lockfile, 'parse', originalLockfileParseDescriptor));
|
||||
|
||||
it('should parse the specified lockfile', () => {
|
||||
installer._parseLockfile('/foo/bar/yarn.lock');
|
||||
expect(fs.readFileSync).toHaveBeenCalledWith('/foo/bar/yarn.lock', 'utf8');
|
||||
expect(lockfile.parse).toHaveBeenCalledWith('mock content');
|
||||
});
|
||||
|
||||
it('should throw if parsing the lockfile fails', () => {
|
||||
lockfile.parse.and.returnValue({type: 'not success'});
|
||||
expect(() => installer._parseLockfile('/foo/bar/yarn.lock')).toThrowError(
|
||||
'[NgPackagesInstaller]: Error parsing lockfile \'/foo/bar/yarn.lock\' (result type: not success).');
|
||||
});
|
||||
|
||||
it('should return the parsed lockfile content as an object', () => {
|
||||
const parsed = installer._parseLockfile('/foo/bar/yarn.lock');
|
||||
expect(parsed).toEqual({foo: {version: '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/Zone.js 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 = projectDir;
|
||||
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: absoluteProjectDir });
|
||||
});
|
||||
});
|
||||
|
||||
describe('local marker helpers', () => {
|
||||
let installer;
|
||||
beforeEach(() => {
|
||||
installer = new NgPackagesInstaller(projectDir);
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,43 +0,0 @@
|
||||
# Copyright Google LLC All Rights Reserved.
|
||||
#
|
||||
# Use of this source code is governed by an MIT-style license that can be
|
||||
# found in the LICENSE file at https://angular.io/license
|
||||
"""Utilities to run yarn install as a build target"""
|
||||
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin")
|
||||
load("//:yarn.bzl", "YARN_LABEL", "YARN_PATH")
|
||||
|
||||
def node_modules(name = "node_modules", package_json = "package.json", yarn_lock = "yarn.lock", local_package_substitutions = []):
|
||||
"""Install node modules as a tree artifact.
|
||||
|
||||
Args:
|
||||
name: Optional name of the node_modules folder (default is node_modules)
|
||||
package_json: Optional label to package.json if not in the same package
|
||||
yarn_lock: Optional label to yarn.lock if not in the same package
|
||||
local_package_substitutions: Optional list of locally built packages to substitute
|
||||
for their npm equivalent
|
||||
"""
|
||||
npm_package_bin(
|
||||
name = name,
|
||||
args = [
|
||||
"$(RULEDIR)",
|
||||
"$(execpath %s)" % package_json,
|
||||
"$(execpath %s)" % yarn_lock,
|
||||
"--modules-folder",
|
||||
name,
|
||||
"--local-packages",
|
||||
] + [
|
||||
"$(execpath %s)" % pkg
|
||||
for pkg in local_package_substitutions
|
||||
],
|
||||
data = [
|
||||
YARN_LABEL,
|
||||
package_json,
|
||||
yarn_lock,
|
||||
] + local_package_substitutions,
|
||||
env = {
|
||||
"YARN": YARN_PATH,
|
||||
},
|
||||
output_dir = True,
|
||||
tool = "//aio/tools/ng-packages-installer",
|
||||
)
|
||||
@@ -28,7 +28,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler/test:__pkg__",
|
||||
|
||||
@@ -32,7 +32,7 @@ pkg_npm(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
@@ -47,7 +47,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/bazel/test/ng_package:__pkg__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
|
||||
@@ -132,7 +132,7 @@ pkg_npm(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
],
|
||||
|
||||
@@ -30,7 +30,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/language-service/test:__pkg__",
|
||||
|
||||
@@ -61,7 +61,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/bazel/test/ng_package:__pkg__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
|
||||
@@ -28,7 +28,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
@@ -28,7 +28,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler-cli/test/diagnostics:__pkg__",
|
||||
|
||||
@@ -27,7 +27,7 @@ pkg_npm(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
@@ -32,7 +32,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
],
|
||||
|
||||
@@ -38,7 +38,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler-cli/test:__pkg__",
|
||||
|
||||
@@ -49,7 +49,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
],
|
||||
|
||||
@@ -31,7 +31,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler-cli/test:__pkg__",
|
||||
|
||||
@@ -50,7 +50,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
@@ -31,7 +31,7 @@ ng_package(
|
||||
# Dependencies on the full npm_package cause long re-builds.
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/tools/examples/shared:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
Reference in New Issue
Block a user