mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
build(bazel): create AIO example playgrounds for manual testing
After the bazel migration, AIO examples are no longer fully formed in the source tree.
This commit is contained in:
committed by
Joey Perrott
parent
8df1deaad5
commit
500acfcf98
@@ -6,6 +6,7 @@
|
||||
node_modules
|
||||
dist
|
||||
aio/node_modules
|
||||
aio/content/example-playground
|
||||
aio/tools/examples/shared/node_modules
|
||||
aio/tools/examples/shared/example-scaffold
|
||||
packages/bazel/node_modules
|
||||
|
||||
@@ -42,6 +42,9 @@ testem.log
|
||||
# e2e
|
||||
protractor-results*.txt
|
||||
|
||||
# Example playground
|
||||
content/example-playground
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
@@ -30,6 +30,8 @@ Here are the most important tasks you might need to use:
|
||||
* `yarn docs-lint` - check that the doc gen code follows our style rules.
|
||||
|
||||
* `yarn create-example` - create a new example directory containing initial source files.
|
||||
* `yarn example-playground <exampleName>` - set up a playground to manually test an example combined with its boilerplate files
|
||||
- `--local` - link locally build Angular packages as deps
|
||||
|
||||
* `yarn example-e2e` - run all e2e tests for examples. Available options:
|
||||
- `--local`: run e2e tests against locally built Angular packages.
|
||||
|
||||
@@ -182,7 +182,7 @@ def docs_example(name, test = True, test_tags = [], test_exec_properties = {}):
|
||||
|
||||
# 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 AIO_EXAMPLE_PACKAGES]
|
||||
LOCAL_PACKAGE_ARGS = ["--localPackage=%s#$(rootpath %s)" % (dep, to_package_label(dep)) for dep in AIO_EXAMPLE_PACKAGES]
|
||||
|
||||
nodejs_test(
|
||||
name = "e2e",
|
||||
@@ -206,7 +206,7 @@ def docs_example(name, test = True, test_tags = [], test_exec_properties = {}):
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
configuration_env_vars = ["NG_BUILD_CACHE"],
|
||||
entry_point = "//aio/tools/examples:run-example-e2e",
|
||||
entry_point = "//aio/tools/examples:run-example-e2e.mjs",
|
||||
env = {
|
||||
"CHROME_BIN": "$(CHROMIUM)",
|
||||
"CHROMEDRIVER_BIN": "$(CHROMEDRIVER)",
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"example-e2e": "node --experimental-import-meta-resolve tools/examples/run-filtered-example-e2es.mjs",
|
||||
"example-list-overrides": "bazel run //aio/tools/examples:example-boilerplate list-overrides",
|
||||
"example-lint": "eslint content/examples",
|
||||
"example-playground": "node ./tools/examples/create-example-playground-wrapper.mjs",
|
||||
"deploy-production": "bazel run //aio/scripts/deploy-to-firebase",
|
||||
"check-env": "yarn ~~check-env",
|
||||
"payload-size": "scripts/payload.sh",
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
load("//tools:defaults.bzl", "nodejs_binary")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
|
||||
load("@aio_npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
|
||||
load("//aio/content/examples:examples.bzl", "EXAMPLES")
|
||||
load("//:packages.bzl", "AIO_EXAMPLE_PACKAGES", "to_package_label")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files([
|
||||
"run-example-e2e.mjs",
|
||||
])
|
||||
|
||||
EXAMPLE_BOILERPLATE_SRCS = [
|
||||
"example-boilerplate.js",
|
||||
"constants.js",
|
||||
@@ -58,15 +64,25 @@ jasmine_node_test(
|
||||
CREATE_EXAMPLE_SRCS + CREATE_EXAMPLE_DEPS,
|
||||
)
|
||||
|
||||
js_library(
|
||||
name = "example-sandbox",
|
||||
srcs = ["example-sandbox.mjs"],
|
||||
deps = [
|
||||
"@aio_npm//cjson",
|
||||
"@aio_npm//fs-extra",
|
||||
"@aio_npm//globby",
|
||||
],
|
||||
)
|
||||
|
||||
js_library(
|
||||
name = "run-example-e2e",
|
||||
srcs = [
|
||||
"run-example-e2e.mjs",
|
||||
],
|
||||
deps = [
|
||||
":example-sandbox",
|
||||
"@aio_npm//@bazel/runfiles",
|
||||
"@aio_npm//canonical-path",
|
||||
"@aio_npm//cjson",
|
||||
"@aio_npm//cross-spawn",
|
||||
"@aio_npm//fs-extra",
|
||||
"@aio_npm//get-port",
|
||||
@@ -76,3 +92,30 @@ js_library(
|
||||
"@aio_npm//yargs",
|
||||
],
|
||||
)
|
||||
|
||||
EXAMPLES_WITH_BOILERPLATE = ["//aio/content/examples/%s" % example for example in EXAMPLES]
|
||||
|
||||
LOCAL_PACKAGE_DEPS = [to_package_label(dep) for dep in AIO_EXAMPLE_PACKAGES]
|
||||
|
||||
LOCAL_PACKAGE_ARGS = ["--localPackage=%s#$(rootpath %s)" % (
|
||||
dep,
|
||||
to_package_label(dep),
|
||||
) for dep in AIO_EXAMPLE_PACKAGES]
|
||||
|
||||
nodejs_binary(
|
||||
name = "create-example-playground",
|
||||
args = select({
|
||||
# Hardcode package names/paths in args
|
||||
"//aio:aio_local_deps": LOCAL_PACKAGE_ARGS,
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
data = [
|
||||
":example-sandbox",
|
||||
"@aio_example_deps//:node_modules_files",
|
||||
"@aio_npm//yargs",
|
||||
] + EXAMPLES_WITH_BOILERPLATE + select({
|
||||
"//aio:aio_local_deps": LOCAL_PACKAGE_DEPS,
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
entry_point = "create-example-playground.mjs",
|
||||
)
|
||||
|
||||
@@ -140,6 +140,16 @@ By default the script will place basic scaffold files into the new example (from
|
||||
But you can also specify the path to a separate CLI project, from which the script will copy files that would not be considered "boilerplate".
|
||||
See the [Boilerplate overview](#boilerplate-overview) for more information.
|
||||
|
||||
### `create-example-playground.mjs`
|
||||
|
||||
The [create-example-playground.mjs](./create-example-playground.mjs) script combines example sources, boilerplate, and shared node_modules deps into git-ignored playground directory `content/example-playground/{{EXAMPLE}}` that can be used for manual testing. This should be invoked via the yarn script:
|
||||
|
||||
```bash
|
||||
yarn example-playground <exampleName> [--local]
|
||||
```
|
||||
|
||||
The `--local` flag links in locally-built angular packages as dependencies.
|
||||
|
||||
### Updating example dependencies
|
||||
|
||||
With every major Angular release, we update the examples to be on the latest version.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import shelljs from 'shelljs';
|
||||
import yargs from 'yargs'
|
||||
import {hideBin} from 'yargs/helpers';
|
||||
import {getNativeBinary as getNativeBazelBinary} from '@bazel/bazelisk';
|
||||
|
||||
shelljs.set('-e')
|
||||
shelljs.set('-v')
|
||||
|
||||
/**
|
||||
* Create an example playground with shared example deps and optionally linked local
|
||||
* angular packages in the source tree under content/examples/example-playground. This
|
||||
* is a wrapper around the equivalent bazel binary but adds the --local option to link
|
||||
* local packages.
|
||||
*
|
||||
* Usage: node ./tools/examples/create-example-playground-wrapper.mjs <example> [options]
|
||||
*
|
||||
* Args:
|
||||
* example: name of the example
|
||||
*
|
||||
* Flags:
|
||||
* --local: use locally built angular packages
|
||||
*/
|
||||
|
||||
const options = yargs(hideBin(process.argv))
|
||||
.command('$0 <example>', 'Set up a playground for <example> in the source tree for manual testing')
|
||||
.option('local', {default: false, type: 'boolean'})
|
||||
.version(false)
|
||||
.strict()
|
||||
.argv;
|
||||
|
||||
const cmd = [
|
||||
getNativeBazelBinary(),
|
||||
'run',
|
||||
'//aio/tools/examples:create-example-playground',
|
||||
'--',
|
||||
`--example=${options.example}`,
|
||||
];
|
||||
|
||||
if (options.local) {
|
||||
cmd.splice(2, 0, '--config=aio_local_deps');
|
||||
}
|
||||
|
||||
shelljs.exec(cmd.join(' '));
|
||||
@@ -0,0 +1,66 @@
|
||||
import path from 'node:path';
|
||||
import yargs from 'yargs';
|
||||
import {hideBin} from 'yargs/helpers'
|
||||
import {constructExampleSandbox} from './example-sandbox.mjs';
|
||||
|
||||
if (!process.env.BUILD_WORKSPACE_DIRECTORY) {
|
||||
console.error(
|
||||
'Not running script as part of `bazel run`.'
|
||||
)
|
||||
process.exit(1);
|
||||
}
|
||||
const sourceRoot = process.env.BUILD_WORKSPACE_DIRECTORY;
|
||||
const runfilesRoot = path.join(process.env.RUNFILES, 'angular');
|
||||
const playgroundRoot = path.join(sourceRoot, 'aio', 'content', 'example-playground');
|
||||
|
||||
/**
|
||||
* Create an example playground with shared example deps and optionally linked local
|
||||
* angular packages in the source tree under content/examples/example-playground. This
|
||||
* script is intended to only be run under bazel as it has the localPackage arguments
|
||||
* hardcoded into the binary via starlark.
|
||||
*
|
||||
* Usage: bazel run //aio/tools/examples:create-example-playground -- --example=<example>
|
||||
*
|
||||
* Args:
|
||||
* example: name of the example
|
||||
*/
|
||||
|
||||
async function main(args) {
|
||||
const options =
|
||||
yargs(args)
|
||||
// Note: localPackage not listed above in usage as it's hardcoded in by the nodejs_binary
|
||||
.option('localPackage', {
|
||||
array: true,
|
||||
type: 'string',
|
||||
default: [],
|
||||
describe: 'Locally built package to substitute, in the form `packageName#packagePath`'
|
||||
})
|
||||
.option('example', {
|
||||
type: 'string',
|
||||
describe: 'Name of the example'
|
||||
})
|
||||
.demandOption('example')
|
||||
.strict()
|
||||
.version(false)
|
||||
.argv;
|
||||
|
||||
const localPackages = options.localPackage.reduce((pkgs, pkgNameAndPath) => {
|
||||
const [pkgName, pkgPath] = pkgNameAndPath.split('#');
|
||||
pkgs[pkgName] = path.resolve(pkgPath);
|
||||
return pkgs;
|
||||
}, {});
|
||||
|
||||
const exampleName = options.example;
|
||||
|
||||
// Note: the example sources plus boilerplate are merged into a target named after the example
|
||||
const fullExamplePath = path.join(runfilesRoot, 'aio', 'content', 'examples', exampleName, exampleName);
|
||||
|
||||
const destPath = path.join(playgroundRoot, exampleName);
|
||||
const nodeModules = path.join(runfilesRoot, '..', 'aio_example_deps', 'node_modules');
|
||||
|
||||
await constructExampleSandbox(fullExamplePath, destPath, nodeModules, localPackages);
|
||||
|
||||
console.log(`A playground folder for ${exampleName} has been set up at\n\n ${destPath}\n`);
|
||||
}
|
||||
|
||||
(async () => await main(hideBin(process.argv)))();
|
||||
@@ -0,0 +1,172 @@
|
||||
import jsonc from 'cjson';
|
||||
import fs from 'fs-extra';
|
||||
import {globbySync} from 'globby';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
|
||||
// Construct a sandbox environment for an example, linking in shared example node_modules
|
||||
// and optionally linking in locally-built angular packages.
|
||||
export async function constructExampleSandbox(examplePath, destPath, nodeModulesPath, localPackages) {
|
||||
fs.rmSync(destPath, {
|
||||
recursive: true,
|
||||
force: true
|
||||
});
|
||||
fs.copySync(examplePath, destPath);
|
||||
|
||||
// Remove write protection as the example was copied from bazel output tree
|
||||
chmodSyncRec(destPath);
|
||||
|
||||
// Symlink shared example node_modules, substituting for locally built deps if requested
|
||||
await constructSymlinkedNodeModules(destPath, nodeModulesPath, localPackages);
|
||||
|
||||
// Add preserveSymlinks fixups to various files --- needed when linkin in local deps
|
||||
preserveSymlinksWhenUsingLocalPackages(localPackages, destPath);
|
||||
}
|
||||
|
||||
async function constructSymlinkedNodeModules(examplePath, exampleDepsNodeModules, localPackages) {
|
||||
const linkedNodeModules = path.resolve(examplePath, 'node_modules');
|
||||
fs.ensureDirSync(linkedNodeModules);
|
||||
|
||||
await Promise.all([
|
||||
linkExampleDeps(exampleDepsNodeModules, linkedNodeModules, localPackages),
|
||||
linkLocalDeps(exampleDepsNodeModules, linkedNodeModules, localPackages)
|
||||
]);
|
||||
|
||||
fs.copySync(path.join(exampleDepsNodeModules, '.bin'), path.join(linkedNodeModules, '.bin'));
|
||||
pointBinSymlinksToLocalPackages(linkedNodeModules, exampleDepsNodeModules, localPackages);
|
||||
}
|
||||
|
||||
function linkExampleDeps(exampleDepsNodeModules, linkedNodeModules, localPackages) {
|
||||
const exampleDepsPackages = getPackageNamesFromNodeModules(exampleDepsNodeModules);
|
||||
|
||||
return Promise.all(exampleDepsPackages
|
||||
.filter(pkgName => !(pkgName in localPackages))
|
||||
.map(pkgName => fs.ensureSymlink(
|
||||
path.join(exampleDepsNodeModules, 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
|
||||
});
|
||||
}
|
||||
|
||||
async function linkLocalDeps(exampleDepsNodeModules, linkedNodeModules, localPackages) {
|
||||
const hasNpmDepForPkg = await Promise.all(Object.keys(localPackages)
|
||||
.map(pkgName => fs.pathExists(path.join(exampleDepsNodeModules, pkgName))));
|
||||
|
||||
return Promise.all(Object.keys(localPackages).filter((pkgName, i) => hasNpmDepForPkg[i])
|
||||
.map(pkgName => fs.ensureSymlinkSync(localPackages[pkgName], path.join(linkedNodeModules, pkgName), 'dir')));
|
||||
}
|
||||
|
||||
// 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, exampleDepsNodeModules, localPackages) {
|
||||
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 => {
|
||||
// TODO: Swapping the symlink to ngc with the local package equivalent
|
||||
// doesn't seem to work. When ngc runs in upgrade-phonecat-2-hybrid via
|
||||
// the build:aot yarn script, node cannot resolve @angular/compiler.
|
||||
if (bin === 'ngc') {
|
||||
return;
|
||||
}
|
||||
const symlinkTarget = fs.readlinkSync(path.join(linkedNodeModules, '.bin', bin));
|
||||
for (const pkgName of Object.keys(localPackages)) {
|
||||
const binMightBeInLocalPackage = symlinkTarget.includes(path.join(exampleDepsNodeModules, 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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// When local packages are symlinked in, node has trouble resolving some peer deps. Setting
|
||||
// preserveSymlinks in relevant files 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. Add the preserveSymlinks options to various
|
||||
// files that are used by the cli and systemjs tests (and sometimes both).
|
||||
function preserveSymlinksWhenUsingLocalPackages(LOCAL_PACKAGES, appDir) {
|
||||
if (Object.keys(LOCAL_PACKAGES).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set preserveSymlinks in angular.json
|
||||
const angularJsonPath = path.join(appDir, 'angular.json');
|
||||
if (fs.existsSync(angularJsonPath)) {
|
||||
const angularJson = jsonc.load(angularJsonPath, {
|
||||
encoding: 'utf-8'
|
||||
});
|
||||
angularJson.projects['angular.io-example'].architect.build.options.preserveSymlinks = true;
|
||||
fs.writeFileSync(angularJsonPath, JSON.stringify(angularJson, undefined, 2));
|
||||
}
|
||||
|
||||
// Set preserveSymlinks in any tsconfig.json files
|
||||
const tsConfigPaths = globbySync([path.join(appDir, 'tsconfig*.json')]);
|
||||
for (const tsConfigPath of tsConfigPaths) {
|
||||
const tsConfig = jsonc.load(tsConfigPath, {
|
||||
encoding: 'utf-8'
|
||||
});
|
||||
const isRootConfig = !tsConfig.extends;
|
||||
if (isRootConfig) {
|
||||
tsConfig.compilerOptions.preserveSymlinks = true;
|
||||
fs.writeFileSync(tsConfigPath, JSON.stringify(tsConfig, undefined, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// Call rollup with --preserveSymlinks
|
||||
const packageJsonPath = path.join(appDir, 'package.json');
|
||||
const packageJson = jsonc.load(packageJsonPath, {
|
||||
encoding: 'utf-8'
|
||||
});
|
||||
if ('rollup' in packageJson.dependencies || 'rollup' in packageJson.devDependencies) {
|
||||
packageJson.scripts.rollup = 'rollup --preserveSymlinks';
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, undefined, 2));
|
||||
}
|
||||
}
|
||||
|
||||
function chmodSyncRec(dest) {
|
||||
// Glob patterns always use unix-style paths
|
||||
const allFilesPattern = path.join(dest, '**').replace(/\\/g, '/');
|
||||
|
||||
globbySync(allFilesPattern, {
|
||||
dot: true,
|
||||
onlyFiles: false
|
||||
}).forEach(file => fs.chmodSync(file, '755'));
|
||||
fs.chmodSync(dest, '755');
|
||||
}
|
||||
@@ -2,13 +2,13 @@ import path from 'canonical-path';
|
||||
import {spawn} from 'cross-spawn';
|
||||
import fs from 'fs-extra';
|
||||
import {globbySync} from 'globby';
|
||||
import jsonc from 'cjson';
|
||||
import os from 'os';
|
||||
import shelljs from 'shelljs';
|
||||
import treeKill from 'tree-kill';
|
||||
import yargs from 'yargs';
|
||||
import {hideBin} from 'yargs/helpers'
|
||||
import getPort from 'get-port';
|
||||
import {constructExampleSandbox} from "./example-sandbox.mjs";
|
||||
|
||||
shelljs.set('-e');
|
||||
|
||||
@@ -19,13 +19,17 @@ process.env.CHROME_BIN = adjustChromeBinPathForWindows();
|
||||
process.env.CHROME_BIN = path.resolve(process.env.CHROME_BIN);
|
||||
process.env.CHROMEDRIVER_BIN = path.resolve(process.env.CHROMEDRIVER_BIN);
|
||||
|
||||
const {argv} = yargs(hideBin(process.argv));
|
||||
const {argv} = yargs(hideBin(process.argv))
|
||||
.command("* <examplePath> <yarnPath> <exampleDepsWorkspaceName>")
|
||||
.option('localPackage', {array: true, type: 'string', default: [], describe: 'Locally built package to substitute, in the form `packageName#packagePath`'})
|
||||
.version(false)
|
||||
.strict();
|
||||
|
||||
const EXAMPLE_PATH = path.resolve(argv._[0]);
|
||||
const EXAMPLE_PATH = path.resolve(argv.examplePath);
|
||||
const NODE = process.execPath;
|
||||
const VENDORED_YARN = path.resolve(argv._[1]);
|
||||
const EXAMPLE_DEPS_WORKSPACE_NAME = argv._[2];
|
||||
const LOCAL_PACKAGES = argv._.slice(3).reduce((pkgs, pkgNameAndPath) => {
|
||||
const VENDORED_YARN = path.resolve(argv.yarnPath);
|
||||
const EXAMPLE_DEPS_WORKSPACE_NAME = argv.exampleDepsWorkspaceName;
|
||||
const LOCAL_PACKAGES = argv.localPackage.reduce((pkgs, pkgNameAndPath) => {
|
||||
const [pkgName, pkgPath] = pkgNameAndPath.split('#');
|
||||
pkgs[pkgName] = path.resolve(pkgPath);
|
||||
return pkgs;
|
||||
@@ -55,15 +59,17 @@ const MAX_NO_OUTPUT_TIMEOUT = 1000 * 60 * 5; // 5 minutes
|
||||
async function runE2e(examplePath) {
|
||||
const exampleName = path.basename(examplePath);
|
||||
const maxAttempts = argv.retry || 1;
|
||||
const exampleTestPath = generatePathForExampleTest(exampleName);
|
||||
|
||||
try {
|
||||
examplePath = createCopyOfExampleForTest(exampleName, examplePath);
|
||||
await constructNodeModules(examplePath);
|
||||
|
||||
|
||||
await constructExampleSandbox(examplePath, exampleTestPath, path.resolve('..', EXAMPLE_DEPS_WORKSPACE_NAME, 'node_modules'), LOCAL_PACKAGES);
|
||||
|
||||
let testFn;
|
||||
if (isSystemJsTest(examplePath)) {
|
||||
testFn = () => runE2eTestsSystemJS(exampleName, examplePath);
|
||||
} else if (isCliTest(examplePath)) {
|
||||
testFn = () => runE2eTestsCLI(exampleName, examplePath);
|
||||
if (isSystemJsTest(exampleTestPath)) {
|
||||
testFn = () => runE2eTestsSystemJS(exampleName, exampleTestPath);
|
||||
} else if (isCliTest(exampleTestPath)) {
|
||||
testFn = () => runE2eTestsCLI(exampleName, exampleTestPath);
|
||||
} else {
|
||||
throw new Error(`Unknown e2e test type for example ${exampleName}`);
|
||||
}
|
||||
@@ -73,7 +79,7 @@ async function runE2e(examplePath) {
|
||||
console.error(e);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
fs.rmSync(examplePath, {recursive: true, force: true});
|
||||
fs.rmSync(exampleTestPath, {recursive: true, force: true});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,18 +99,13 @@ async function attempt(testFn, maxAttempts) {
|
||||
}
|
||||
}
|
||||
|
||||
function createCopyOfExampleForTest(exampleName, examplePath) {
|
||||
function generatePathForExampleTest(exampleName) {
|
||||
// Note that bazel provides a writeable tmp dir for tests in the env var TEST_TMPDIR,
|
||||
// however we do not use it here as in non-sandboxed mode the temp dir sits under the
|
||||
// execroot, so yarn will find the .yarnrc in the root of the workspace. If there is ever
|
||||
// a version mismatch (e.g., if we use multiple vendored yarn versions) then this could
|
||||
// cause subtle errors. Instead, just use a temp dir that bazel doesn't know about.
|
||||
const testPath = fs.mkdtempSync(`${os.tmpdir()}${path.sep}${exampleName}-`)
|
||||
globbySync(['**'], {cwd: examplePath, dot: true}).forEach(file => {
|
||||
fs.copySync(path.join(examplePath, file), path.join(testPath, file));
|
||||
fs.chmodSync(path.join(testPath, file), '755');
|
||||
});
|
||||
return testPath;
|
||||
return fs.mkdtempSync(`${os.tmpdir()}${path.sep}${exampleName}-`)
|
||||
}
|
||||
|
||||
function isSystemJsTest(examplePath) {
|
||||
@@ -118,8 +119,6 @@ function isCliTest(examplePath) {
|
||||
async function runE2eTestsSystemJS(exampleName, appDir) {
|
||||
const config = loadExampleConfig(appDir);
|
||||
|
||||
preserveSymlinksWhenUsingLocalPackages(LOCAL_PACKAGES, appDir);
|
||||
|
||||
const runArgs = await overrideSystemJsExampleToUseRandomPort(config, appDir);
|
||||
|
||||
const appBuildSpawnInfo = spawnExt(NODE, [VENDORED_YARN, config.build], {cwd: appDir});
|
||||
@@ -220,82 +219,6 @@ function runProtractorAoT(exampleName, appDir) {
|
||||
return runProtractorSystemJS(exampleName, promise, appDir, aotRunSpawnInfo);
|
||||
}
|
||||
|
||||
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 => {
|
||||
// TODO: Swapping the symlink to ngc with the local package equivalent
|
||||
// doesn't seem to work. When ngc runs in upgrade-phonecat-2-hybrid via
|
||||
// the build:aot yarn script, node cannot resolve @angular/compiler.
|
||||
if (bin === "ngc") {
|
||||
return;
|
||||
}
|
||||
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
|
||||
// fileName; then shut down the example.
|
||||
// All protractor output is appended to the outputFile.
|
||||
@@ -315,8 +238,6 @@ function runE2eTestsCLI(exampleName, appDir) {
|
||||
}
|
||||
}
|
||||
|
||||
preserveSymlinksWhenUsingLocalPackages(LOCAL_PACKAGES, appDir);
|
||||
|
||||
// `--no-webdriver-update` is needed to preserve the ChromeDriver version already installed.
|
||||
const testCommands = config.tests || [{
|
||||
cmd: NODE,
|
||||
@@ -435,43 +356,4 @@ function adjustChromeBinPathForWindows() {
|
||||
return process.env.CHROME_BIN;
|
||||
}
|
||||
|
||||
// When local packages are symlinked in, node has trouble resolving some peer deps. Setting
|
||||
// preserveSymlinks in relevant files 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. Add the preserveSymlinks options to various
|
||||
// files that are used by the cli and systemjs tests (and sometimes both).
|
||||
function preserveSymlinksWhenUsingLocalPackages(LOCAL_PACKAGES, appDir) {
|
||||
if (Object.keys(LOCAL_PACKAGES).length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set preserveSymlinks in angular.json
|
||||
const angularJsonPath = path.join(appDir, 'angular.json');
|
||||
if (fs.existsSync(angularJsonPath)) {
|
||||
const angularJson = jsonc.load(angularJsonPath, {encoding: 'utf-8'});
|
||||
angularJson.projects['angular.io-example'].architect.build.options.preserveSymlinks = true;
|
||||
fs.writeFileSync(angularJsonPath, JSON.stringify(angularJson, undefined, 2));
|
||||
}
|
||||
|
||||
// Set preserveSymlinks in any tsconfig.json files
|
||||
const tsConfigPaths = globbySync([path.join(appDir, 'tsconfig*.json')]);
|
||||
for (let tsConfigPath of tsConfigPaths) {
|
||||
const tsConfig = jsonc.load(tsConfigPath, {encoding: 'utf-8'});
|
||||
const isRootConfig = !tsConfig.extends;
|
||||
if (isRootConfig) {
|
||||
tsConfig.compilerOptions.preserveSymlinks = true;
|
||||
fs.writeFileSync(tsConfigPath, JSON.stringify(tsConfig, undefined, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// Call rollup with --preserveSymlinks
|
||||
const packageJsonPath = path.join(appDir, 'package.json');
|
||||
const packageJson = jsonc.load(packageJsonPath, {encoding: 'utf-8'});
|
||||
if ('rollup' in packageJson.dependencies || 'rollup' in packageJson.devDependencies) {
|
||||
packageJson.scripts['rollup'] = 'rollup --preserveSymlinks';
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, undefined, 2));
|
||||
}
|
||||
}
|
||||
|
||||
runE2e(EXAMPLE_PATH);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler/test:__pkg__",
|
||||
@@ -66,6 +67,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ pkg_npm(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
@@ -48,6 +48,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/bazel/test/ng_package:__pkg__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
@@ -92,6 +93,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -133,6 +133,7 @@ pkg_npm(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
],
|
||||
|
||||
@@ -31,6 +31,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/language-service/test:__pkg__",
|
||||
|
||||
@@ -62,6 +62,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/bazel/test/ng_package:__pkg__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
@@ -121,8 +122,9 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
]) + [
|
||||
"PACKAGE.md",
|
||||
"global/index.ts",
|
||||
"global/PACKAGE.md",
|
||||
]),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
@@ -51,6 +52,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler-cli/test/diagnostics:__pkg__",
|
||||
@@ -64,6 +65,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -28,6 +28,7 @@ pkg_npm(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
||||
@@ -33,6 +33,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
],
|
||||
@@ -57,6 +58,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -39,6 +39,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler-cli/test:__pkg__",
|
||||
@@ -65,6 +66,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -50,6 +50,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
],
|
||||
@@ -75,6 +76,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -32,6 +32,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
"//packages/compiler-cli/integrationtest:__pkg__",
|
||||
"//packages/compiler-cli/test:__pkg__",
|
||||
@@ -69,6 +70,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -51,6 +51,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
@@ -74,6 +75,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
@@ -32,6 +32,7 @@ ng_package(
|
||||
visibility = [
|
||||
"//aio:__pkg__",
|
||||
"//aio/content/examples:__subpackages__",
|
||||
"//aio/tools/examples:__pkg__",
|
||||
"//integration:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
@@ -56,6 +57,5 @@ filegroup(
|
||||
srcs = glob([
|
||||
"*.ts",
|
||||
"src/**/*.ts",
|
||||
"PACKAGE.md",
|
||||
]),
|
||||
]) + ["PACKAGE.md"],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user