diff --git a/aio/content/examples/examples.bzl b/aio/content/examples/examples.bzl index c5f457bf6ef..bfccc58735c 100644 --- a/aio/content/examples/examples.bzl +++ b/aio/content/examples/examples.bzl @@ -190,13 +190,15 @@ def docs_example(name, test = True, test_tags = [], test_exec_properties = {}, f nodejs_test( name = "e2e", data = [ - ":%s" % name, - YARN_LABEL, "@aio_npm//@angular/build-tooling/bazel/browsers/chromium", "//aio/tools/examples:run-example-e2e", "//aio/tools:windows-chromium-path", # We install the whole node modules for runtime deps of e2e tests "@{workspace}//:node_modules_files".format(workspace = EXAMPLE_DEPS_WORKSPACE_NAME), + ], + data_for_expansion = [ + ":%s" % name, + YARN_LABEL, ] + select({ "//aio:aio_local_deps": LOCAL_PACKAGE_DEPS, "//conditions:default": [], diff --git a/aio/scripts/BUILD.bazel b/aio/scripts/BUILD.bazel index a7f08b1f9d5..d7f411322dd 100644 --- a/aio/scripts/BUILD.bazel +++ b/aio/scripts/BUILD.bazel @@ -22,7 +22,7 @@ nodejs_binary( js_library( name = "fast-serve-and-watch", srcs = [ - "fast-serve-and-watch.js", + "fast-serve-and-watch.mjs", ], deps = [ "//aio/tools/transforms/authors-package:watchdocs", @@ -84,9 +84,9 @@ nodejs_test( testonly = True, data = [ ":audit-web-app", - ":audit-web-app-script", "@aio_npm//shelljs", ], + data_for_expansion = [":audit-web-app-script"], entry_point = "test-aio-a11y.mjs", env = { "AUDIT_SCRIPT_PATH": "$(rootpath :audit-web-app-script)", diff --git a/aio/scripts/build-404-page.js b/aio/scripts/build-404-page.mjs similarity index 63% rename from aio/scripts/build-404-page.js rename to aio/scripts/build-404-page.mjs index 8eaa63daced..1215d5d0321 100644 --- a/aio/scripts/build-404-page.js +++ b/aio/scripts/build-404-page.mjs @@ -1,8 +1,8 @@ #!/usr/bin/env node // Imports -const {readFileSync, writeFileSync} = require('fs'); -const {join, resolve} = require('path'); +import {readFileSync, writeFileSync} from 'fs'; +import {join, resolve} from 'path'; // Constants const SOURCE_404_BODY_PATH = resolve(process.argv[2]); @@ -18,13 +18,16 @@ function _main() { const srcIndexContent = readFileSync(srcIndexPath, 'utf8'); const src404BodyContent = readFileSync(SOURCE_404_BODY_PATH, 'utf8').trim(); - const dst404PageContent = srcIndexContent - .replace(/()[\s\S]+(<\/body>)/, `$1\n ${src404BodyContent}\n$2`); + const dst404PageContent = srcIndexContent.replace( + /()[\s\S]+(<\/body>)/, + `$1\n ${src404BodyContent}\n$2` + ); if (dst404PageContent === srcIndexContent) { throw new Error( - 'Failed to generate \'404.html\'. ' + - 'The content of \'index.html\' does not match the expected pattern.'); + "Failed to generate '404.html'. " + + "The content of 'index.html' does not match the expected pattern." + ); } writeFileSync(DEST_404_PAGE_PATH, dst404PageContent); diff --git a/aio/scripts/build-ngsw-config.js b/aio/scripts/build-ngsw-config.mjs similarity index 60% rename from aio/scripts/build-ngsw-config.js rename to aio/scripts/build-ngsw-config.mjs index 08221c0f6fb..f5702c06314 100644 --- a/aio/scripts/build-ngsw-config.js +++ b/aio/scripts/build-ngsw-config.mjs @@ -1,13 +1,14 @@ // Imports -const {basename, dirname, resolve: resolvePath} = require('canonical-path'); -const {mkdirSync, readFileSync, writeFileSync} = require('fs'); -const {parse: parseJson} = require('json5'); - +import cpath from 'canonical-path'; +import {mkdirSync, readFileSync, writeFileSync} from 'fs'; +import json5 from 'json5'; +import url from 'url'; // Constants -const FIREBASE_CONFIG_PATH = resolvePath(__dirname, '../firebase.json'); -const NGSW_CONFIG_TEMPLATE_PATH = resolvePath(__dirname, '../ngsw-config.template.json'); -const NGSW_CONFIG_OUTPUT_PATH = resolvePath(__dirname, '../ngsw-config.json'); +const currentDir = cpath.dirname(url.fileURLToPath(import.meta.url)); +const FIREBASE_CONFIG_PATH = cpath.resolve(currentDir, '../firebase.json'); +const NGSW_CONFIG_TEMPLATE_PATH = cpath.resolve(currentDir, '../ngsw-config.template.json'); +const NGSW_CONFIG_OUTPUT_PATH = cpath.resolve(currentDir, '../ngsw-config.json'); // Run _main(); @@ -15,9 +16,7 @@ _main(); // Helpers function _main() { // Allow an alternative output path (used by bazel to output to output dir) - const ngswConfigOutputPath = process.argv.length > 2 - ? process.argv[2] - : NGSW_CONFIG_OUTPUT_PATH; + const ngswConfigOutputPath = process.argv.length > 2 ? process.argv[2] : NGSW_CONFIG_OUTPUT_PATH; const firebaseConfig = readJson(FIREBASE_CONFIG_PATH); const ngswConfig = readJson(NGSW_CONFIG_TEMPLATE_PATH); @@ -29,18 +28,21 @@ function _main() { if (regexBasedRedirects.length > 0) { throw new Error( 'The following redirects use `regex`, which is currently not supported by ' + - `${basename(__filename)}:` + - regexBasedRedirects.map(x => `\n - ${JSON.stringify(x)}`).join('')); + `${basename(__filename)}:` + + regexBasedRedirects.map((x) => `\n - ${JSON.stringify(x)}`).join('') + ); } // Check that there are no unsupported glob characters/patterns. - const redirectsWithUnsupportedGlobs = firebaseRedirects - .filter(({source}) => !/^(?:[/\w\-.*]|:\w+|@\([\w\-.|]+\))+$/.test(source)); + const redirectsWithUnsupportedGlobs = firebaseRedirects.filter( + ({source}) => !/^(?:[/\w\-.*]|:\w+|@\([\w\-.|]+\))+$/.test(source) + ); if (redirectsWithUnsupportedGlobs.length > 0) { throw new Error( 'The following redirects use glob characters/patterns that are currently not supported by ' + - `${basename(__filename)}:` + - redirectsWithUnsupportedGlobs.map(x => `\n - ${JSON.stringify(x)}`).join('')); + `${basename(__filename)}:` + + redirectsWithUnsupportedGlobs.map((x) => `\n - ${JSON.stringify(x)}`).join('') + ); } // Compute additional ignore glob patterns to be added to `navigationUrls`. @@ -48,11 +50,11 @@ function _main() { // Grab the redirect source glob. .map(({source}) => source) // Ignore redirects for files (since these are already ignored by the SW). - .filter(glob => /\/[^/.]*$/.test(glob)) + .filter((glob) => /\/[^/.]*$/.test(glob)) // Convert each Firebase-specific glob to a SW-compatible glob. - .map(glob => `!${glob.replace(/:\w+/g, '*').replace(/@(\([\w\-.|]+\))/g, '$1')}`) + .map((glob) => `!${glob.replace(/:\w+/g, '*').replace(/@(\([\w\-.|]+\))/g, '$1')}`) // Add optional trailing `/` for globs that don't end with `*`. - .map(glob => /\w$/.test(glob) ? `${glob}\/{0,1}` : glob) + .map((glob) => (/\w$/.test(glob) ? `${glob}\/{0,1}` : glob)) // Remove more specific globs that are covered by more generic ones. .filter((glob, _i, globs) => !isGlobRedundant(glob, globs)) // Sort the generated globs alphabetically. @@ -61,28 +63,29 @@ function _main() { // Add the additional `navigationUrls` globs and save the config. ngswConfig.navigationUrls.push(...additionalNavigationUrls); - mkdirSync(dirname(ngswConfigOutputPath), {recursive: true}); + mkdirSync(cpath.dirname(ngswConfigOutputPath), {recursive: true}); writeJson(ngswConfigOutputPath, ngswConfig); } function isGlobRedundant(glob, globs) { // Find all globs that could cover other globs. // For simplicity, we only consider globs ending with `/**`. - const genericGlobs = globs.filter(g => g.endsWith('/**')); + const genericGlobs = globs.filter((g) => g.endsWith('/**')); // A glob is redundant if it starts with the path of a potentially generic glob (excluding the // trailing `**`) followed by a word character or a `*`. // For example, `/foo/bar/baz` is covered (and thus made redundant) by `/foo/**`, but `/foo/{0,1}` // is not. - return genericGlobs.some(g => { + return genericGlobs.some((g) => { const pathPrefix = g.slice(0, -2); - return (glob !== g) && glob.startsWith(pathPrefix) && - /^[\w*]/.test(glob.slice(pathPrefix.length)); + return ( + glob !== g && glob.startsWith(pathPrefix) && /^[\w*]/.test(glob.slice(pathPrefix.length)) + ); }); } function readJson(filePath) { - return parseJson(readFileSync(filePath, 'utf8')); + return json5.parse(readFileSync(filePath, 'utf8')); } function writeJson(filePath, obj) { diff --git a/aio/scripts/fast-serve-and-watch.js b/aio/scripts/fast-serve-and-watch.js deleted file mode 100644 index d5649f428d3..00000000000 --- a/aio/scripts/fast-serve-and-watch.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - This script serves the aio app, watches for changes, - and runs a fast dgeni build on any changed files. -*/ - -const spawn = require("cross-spawn"); -const watchr = require("../tools/transforms/authors-package/watchr.js"); -const architectCli = require.resolve("@angular-devkit/architect-cli/bin/architect"); - -const serve = spawn(process.execPath, ['--preserve-symlinks', architectCli, "site:serve", "--open", "--poll=1000", "--live-reload", "--watch"], {stdio: "inherit"}); -serve.on("error", error => { - console.error("architect serve script failed"); - console.error(error); - process.exit(1); -}); -serve.on("close", code => { - console.error(`architect serve script exited with code ${code}`); - process.exit(1); -}) - -watchr.watch(true); \ No newline at end of file diff --git a/aio/scripts/fast-serve-and-watch.mjs b/aio/scripts/fast-serve-and-watch.mjs new file mode 100644 index 00000000000..36175a218ca --- /dev/null +++ b/aio/scripts/fast-serve-and-watch.mjs @@ -0,0 +1,33 @@ +/* + This script serves the aio app, watches for changes, + and runs a fast dgeni build on any changed files. +*/ + +import spawn from 'cross-spawn'; +import watchr from '../tools/transforms/authors-package/watchr.js'; +const architectCli = require.resolve('@angular-devkit/architect-cli/bin/architect'); + +const serve = spawn( + process.execPath, + [ + '--preserve-symlinks', + architectCli, + 'site:serve', + '--open', + '--poll=1000', + '--live-reload', + '--watch', + ], + {stdio: 'inherit'} +); +serve.on('error', (error) => { + console.error('architect serve script failed'); + console.error(error); + process.exit(1); +}); +serve.on('close', (code) => { + console.error(`architect serve script exited with code ${code}`); + process.exit(1); +}); + +watchr.watch(true); diff --git a/aio/scripts/local_server_test.bzl b/aio/scripts/local_server_test.bzl index a5b102b9d6c..afc044b0429 100644 --- a/aio/scripts/local_server_test.bzl +++ b/aio/scripts/local_server_test.bzl @@ -15,17 +15,19 @@ def local_server_test(name, entry_point, serve_target, data = [], args = [], **k nodejs_test( name = name, testonly = True, + data_for_expansion = [ + serve_target, + entry_point, + "@aio_npm//light-server/bin:light-server", + ], args = [ "$(rootpath @aio_npm//light-server/bin:light-server)", "$(rootpath %s)" % serve_target, "$(rootpath %s)" % entry_point, ] + args, data = [ - entry_point, - serve_target, "//aio/scripts:run-with-local-server.mjs", "@aio_npm//get-port", - "@aio_npm//light-server/bin:light-server", "@aio_npm//shelljs", "@aio_npm//tree-kill", ] + data, diff --git a/aio/tools/examples/BUILD.bazel b/aio/tools/examples/BUILD.bazel index 632f03407bf..f7f26830f20 100644 --- a/aio/tools/examples/BUILD.bazel +++ b/aio/tools/examples/BUILD.bazel @@ -13,8 +13,9 @@ exports_files([ js_library( name = "example-boilerplate-lib", srcs = [ - "constants.js", - "example-boilerplate.js", + "constants.mjs", + "example-boilerplate.mjs", + "example-boilerplate-cli.mjs", ], deps = [ "//aio/tools/examples/shared", @@ -29,20 +30,21 @@ js_library( nodejs_binary( name = "example-boilerplate", data = [":example-boilerplate-lib"], - entry_point = "example-boilerplate.js", + entry_point = "example-boilerplate-cli.js", ) jasmine_node_test( name = "example-boilerplate-test", - srcs = ["example-boilerplate.spec.js"], + srcs = ["example-boilerplate.spec.mjs"], deps = [":example-boilerplate-lib"], ) js_library( name = "create-example-lib", srcs = [ - "create-example.js", - "constants.js", + "create-example.mjs", + "create-example-cli.mjs", + "constants.mjs", ] + glob(["shared/**"]), deps = [ "@aio_npm//@bazel/buildozer", @@ -57,12 +59,12 @@ js_library( nodejs_binary( name = "create-example", data = [":create-example-lib"], - entry_point = "create-example.js", + entry_point = "create-example-cli.mjs", ) jasmine_node_test( name = "create-example-test", - srcs = ["create-example.spec.js"], + srcs = ["create-example.spec.mjs"], deps = [":create-example-lib"], ) diff --git a/aio/tools/examples/constants.js b/aio/tools/examples/constants.js deleted file mode 100644 index 8214abaad4a..00000000000 --- a/aio/tools/examples/constants.js +++ /dev/null @@ -1,14 +0,0 @@ -const path = require('canonical-path'); - -exports.RUNFILES_ROOT = path.resolve(process.env.RUNFILES, 'angular'); - -exports.getExamplesBasePath = function(root) { - return path.join(root, 'aio', 'content', 'examples'); -} - -exports.getSharedPath = function(root) { - return path.join(root, 'aio', 'tools', 'examples', 'shared'); -} - -exports.EXAMPLE_CONFIG_FILENAME = 'example-config.json'; -exports.STACKBLITZ_CONFIG_FILENAME = 'stackblitz.json'; diff --git a/aio/tools/examples/constants.mjs b/aio/tools/examples/constants.mjs new file mode 100644 index 00000000000..9de5169ab08 --- /dev/null +++ b/aio/tools/examples/constants.mjs @@ -0,0 +1,14 @@ +import path from 'canonical-path'; + +export const RUNFILES_ROOT = path.resolve(process.env.RUNFILES, 'angular'); + +export function getExamplesBasePath(root) { + return path.join(root, 'aio', 'content', 'examples'); +} + +export function getSharedPath(root) { + return path.join(root, 'aio', 'tools', 'examples', 'shared'); +} + +export const EXAMPLE_CONFIG_FILENAME = 'example-config.json'; +export const STACKBLITZ_CONFIG_FILENAME = 'stackblitz.json'; diff --git a/aio/tools/examples/create-example-cli.mjs b/aio/tools/examples/create-example-cli.mjs new file mode 100644 index 00000000000..d370b97774e --- /dev/null +++ b/aio/tools/examples/create-example-cli.mjs @@ -0,0 +1,3 @@ +import {main} from './create-example.mjs'; + +main(); diff --git a/aio/tools/examples/create-example.js b/aio/tools/examples/create-example.js deleted file mode 100644 index 7b34213fe88..00000000000 --- a/aio/tools/examples/create-example.js +++ /dev/null @@ -1,156 +0,0 @@ -const fs = require('fs-extra'); -const glob = require('glob'); -const ignore = require('ignore'); -const path = require('canonical-path'); -const shelljs = require('shelljs'); -const yargs = require('yargs'); -const buildozer = require('@bazel/buildozer'); -const {RUNFILES_ROOT, getExamplesBasePath, getSharedPath, EXAMPLE_CONFIG_FILENAME, STACKBLITZ_CONFIG_FILENAME} = - require('./constants'); - -// BUILD_WORKSPACE_DIRECTORY is set by Bazel when calling `bazel run` and points to the -// root of the source tree (e.g., for creating a new example in the source tree). Otherwise, -// we are in a test so use the runfiles root. -const PROJECT_ROOT = path.resolve(process.env.BUILD_WORKSPACE_DIRECTORY || RUNFILES_ROOT); -const EXAMPLES_BASE_PATH = getExamplesBasePath(PROJECT_ROOT); -const SHARED_PATH = getSharedPath(PROJECT_ROOT); - -const BASIC_SOURCE_PATH = path.resolve(SHARED_PATH, 'example-scaffold'); - -shelljs.set('-e'); - -if (require.main === module) { - const options = - yargs(process.argv.slice(2)) - .command( - '$0 [source]', - [ - 'Create a new example.', - '', - 'If [source] is provided then the relevant files from the CLI project at that path are copied into the example.', - ].join('\n')) - .strict() - .version(false) - .argv; - - const exampleName = options.name; - const examplePath = path.resolve(EXAMPLES_BASE_PATH, exampleName); - - console.log('Creating new example at', examplePath); - createEmptyExample(exampleName, examplePath); - - const sourcePath = - options.source !== undefined ? path.resolve(EXAMPLES_BASE_PATH, options.source) : BASIC_SOURCE_PATH; - console.log('Copying files from', sourcePath); - copyExampleFiles(sourcePath, examplePath, exampleName); - - buildozer.runWithOptions([{commands: [`set name ${exampleName}`], targets: [`//aio/content/examples/${exampleName}:%docs_example`]}], {cwd: PROJECT_ROOT}); - - console.log(`The new "${exampleName}" example has been created.`); - console.log(`To include this example, add a "${exampleName}" entry in aio/content/examples/examples.bzl`) - console.log( - 'You can find more info on working with docs examples in aio/tools/examples/README.md.') -} - -/** - * Create the directory and marker files for the new example. - */ -function createEmptyExample(exampleName, examplePath) { - validateExampleName(exampleName); - ensureExamplePath(examplePath); - writeExampleConfigFile(examplePath); - writeStackBlitzFile(exampleName, examplePath); -} - -function validateExampleName(exampleName) { - if (/\s/.test(exampleName)) { - throw new Error( - `Unable to create example. The example name contains spaces: '${exampleName}'` - ) - } -} - -/** - * Ensure that the new example directory exists. - */ -function ensureExamplePath(examplePath) { - if (fs.existsSync(examplePath)) { - throw new Error( - `Unable to create example. The path to the new example already exists: ${examplePath}`); - } - fs.ensureDirSync(examplePath); -} - -/** - * Write the `example-config.json` file to the new example. - */ -function writeExampleConfigFile(examplePath) { - fs.writeFileSync(path.resolve(examplePath, EXAMPLE_CONFIG_FILENAME), ''); -} - -/** - * Write the `stackblitz.json` file into the new example. - */ -function writeStackBlitzFile(exampleName, examplePath) { - const config = { - description: titleize(exampleName), - files: ['!**/*.d.ts', '!**/*.js', '!**/*.[1,2].*'], - tags: [exampleName.split('-')] - }; - fs.writeFileSync( - path.resolve(examplePath, STACKBLITZ_CONFIG_FILENAME), - JSON.stringify(config, null, 2) + '\n'); -} - -/** - * Copy all the files from the `sourcePath` to the `examplePath`, except for files - * ignored by the source example. - */ -function copyExampleFiles(sourcePath, examplePath, exampleName) { - const gitIgnoreSource = getGitIgnore(sourcePath); - - // Grab the files in the source folder and filter them based on the gitignore rules. - const sourceFiles = - glob.sync('**/*', { - cwd: sourcePath, - dot: true, - ignore: ['**/node_modules/**', '.git/**', '.gitignore'], - mark: true - }) - // Filter out the directories, leaving only files - .filter(filePath => !/\/$/.test(filePath)) - // Filter out files that match the source directory .gitignore rules - .filter(filePath => !gitIgnoreSource.ignores(filePath)) - - for (const sourceFile of sourceFiles) { - console.log(' - ', sourceFile); - const destPath = path.resolve(examplePath, sourceFile) - fs.ensureDirSync(path.dirname(destPath)); - fs.copySync(path.resolve(sourcePath, sourceFile), destPath); - } -} - -function getGitIgnore(directory) { - const gitIgnoreMatcher = ignore(); - const gitignoreFilePath = path.resolve(directory, '.gitignore'); - if (fs.existsSync(gitignoreFilePath)) { - const gitignoreFile = fs.readFileSync(gitignoreFilePath, 'utf8'); - gitIgnoreMatcher.add(gitignoreFile); - } - return gitIgnoreMatcher; -} - -/** - * Convert a kebab-case string to space separated Title Case string. - */ -function titleize(input) { - return input.replace( - /(-|^)(.)/g, (_, pre, char) => `${pre === '-' ? ' ' : ''}${char.toUpperCase()}`); -} - -exports.createEmptyExample = createEmptyExample; -exports.ensureExamplePath = ensureExamplePath; -exports.writeExampleConfigFile = writeExampleConfigFile; -exports.writeStackBlitzFile = writeStackBlitzFile; -exports.copyExampleFiles = copyExampleFiles; -exports.titleize = titleize; diff --git a/aio/tools/examples/create-example.mjs b/aio/tools/examples/create-example.mjs new file mode 100644 index 00000000000..f15c17dcbb4 --- /dev/null +++ b/aio/tools/examples/create-example.mjs @@ -0,0 +1,169 @@ +import fs from 'fs-extra'; +import glob from 'glob'; +import ignore from 'ignore'; +import path from 'canonical-path'; +import shelljs from 'shelljs'; +import yargs from 'yargs'; +import buildozer from '@bazel/buildozer'; + +import { + RUNFILES_ROOT, + getExamplesBasePath, + getSharedPath, + EXAMPLE_CONFIG_FILENAME, + STACKBLITZ_CONFIG_FILENAME, +} from './constants.mjs'; + +// BUILD_WORKSPACE_DIRECTORY is set by Bazel when calling `bazel run` and points to the +// root of the source tree (e.g., for creating a new example in the source tree). Otherwise, +// we are in a test so use the runfiles root. +const PROJECT_ROOT = path.resolve(process.env.BUILD_WORKSPACE_DIRECTORY || RUNFILES_ROOT); +const EXAMPLES_BASE_PATH = getExamplesBasePath(PROJECT_ROOT); +const SHARED_PATH = getSharedPath(PROJECT_ROOT); + +const BASIC_SOURCE_PATH = path.resolve(SHARED_PATH, 'example-scaffold'); + +shelljs.set('-e'); + +export function main() { + const options = yargs(process.argv.slice(2)) + .command( + '$0 [source]', + [ + 'Create a new example.', + '', + 'If [source] is provided then the relevant files from the CLI project at that path are copied into the example.', + ].join('\n') + ) + .strict() + .version(false).argv; + + const exampleName = options.name; + const examplePath = path.resolve(EXAMPLES_BASE_PATH, exampleName); + + console.log('Creating new example at', examplePath); + createEmptyExample(exampleName, examplePath); + + const sourcePath = + options.source !== undefined + ? path.resolve(EXAMPLES_BASE_PATH, options.source) + : BASIC_SOURCE_PATH; + console.log('Copying files from', sourcePath); + copyExampleFiles(sourcePath, examplePath, exampleName); + + buildozer.runWithOptions( + [ + { + commands: [`set name ${exampleName}`], + targets: [`//aio/content/examples/${exampleName}:%docs_example`], + }, + ], + {cwd: PROJECT_ROOT} + ); + + console.log(`The new "${exampleName}" example has been created.`); + console.log( + `To include this example, add a "${exampleName}" entry in aio/content/examples/examples.bzl` + ); + console.log( + 'You can find more info on working with docs examples in aio/tools/examples/README.md.' + ); +} + +/** + * Create the directory and marker files for the new example. + */ +export function createEmptyExample(exampleName, examplePath) { + validateExampleName(exampleName); + ensureExamplePath(examplePath); + writeExampleConfigFile(examplePath); + writeStackBlitzFile(exampleName, examplePath); +} + +function validateExampleName(exampleName) { + if (/\s/.test(exampleName)) { + throw new Error(`Unable to create example. The example name contains spaces: '${exampleName}'`); + } +} + +/** + * Ensure that the new example directory exists. + */ +export function ensureExamplePath(examplePath) { + if (fs.existsSync(examplePath)) { + throw new Error( + `Unable to create example. The path to the new example already exists: ${examplePath}` + ); + } + fs.ensureDirSync(examplePath); +} + +/** + * Write the `example-config.json` file to the new example. + */ +export function writeExampleConfigFile(examplePath) { + fs.writeFileSync(path.resolve(examplePath, EXAMPLE_CONFIG_FILENAME), ''); +} + +/** + * Write the `stackblitz.json` file into the new example. + */ +export function writeStackBlitzFile(exampleName, examplePath) { + const config = { + description: titleize(exampleName), + files: ['!**/*.d.ts', '!**/*.js', '!**/*.[1,2].*'], + tags: [exampleName.split('-')], + }; + fs.writeFileSync( + path.resolve(examplePath, STACKBLITZ_CONFIG_FILENAME), + JSON.stringify(config, null, 2) + '\n' + ); +} + +/** + * Copy all the files from the `sourcePath` to the `examplePath`, except for files + * ignored by the source example. + */ +export function copyExampleFiles(sourcePath, examplePath, exampleName) { + const gitIgnoreSource = getGitIgnore(sourcePath); + + // Grab the files in the source folder and filter them based on the gitignore rules. + const sourceFiles = glob + .sync('**/*', { + cwd: sourcePath, + dot: true, + ignore: ['**/node_modules/**', '.git/**', '.gitignore'], + mark: true, + }) + // Filter out the directories, leaving only files + .filter((filePath) => !/\/$/.test(filePath)) + // Filter out files that match the source directory .gitignore rules + .filter((filePath) => !gitIgnoreSource.ignores(filePath)); + + for (const sourceFile of sourceFiles) { + console.log(' - ', sourceFile); + const destPath = path.resolve(examplePath, sourceFile); + fs.ensureDirSync(path.dirname(destPath)); + fs.copySync(path.resolve(sourcePath, sourceFile), destPath); + } +} + +function getGitIgnore(directory) { + const gitIgnoreMatcher = ignore(); + const gitignoreFilePath = path.resolve(directory, '.gitignore'); + if (fs.existsSync(gitignoreFilePath)) { + const gitignoreFile = fs.readFileSync(gitignoreFilePath, 'utf8'); + gitIgnoreMatcher.add(gitignoreFile); + } + return gitIgnoreMatcher; +} + +/** + * Convert a kebab-case string to space separated Title Case string. + */ +export function titleize(input) { + return input.replace( + /(-|^)(.)/g, + (_, pre, char) => `${pre === '-' ? ' ' : ''}${char.toUpperCase()}` + ); +} diff --git a/aio/tools/examples/create-example.spec.js b/aio/tools/examples/create-example.spec.mjs similarity index 67% rename from aio/tools/examples/create-example.spec.js rename to aio/tools/examples/create-example.spec.mjs index 4b0bc009f10..ddb9200d8c3 100644 --- a/aio/tools/examples/create-example.spec.js +++ b/aio/tools/examples/create-example.spec.mjs @@ -1,18 +1,17 @@ -const path = require('canonical-path'); -const fs = require('fs-extra'); -const {glob} = require('glob'); +import path from 'canonical-path'; +import fs from 'fs-extra'; +import glob from 'glob'; -const {EXAMPLE_CONFIG_FILENAME, STACKBLITZ_CONFIG_FILENAME} = - require('./constants'); +import {EXAMPLE_CONFIG_FILENAME, STACKBLITZ_CONFIG_FILENAME} from './constants.mjs'; -const { +import { copyExampleFiles, createEmptyExample, ensureExamplePath, titleize, writeExampleConfigFile, - writeStackBlitzFile -} = require('./create-example'); + writeStackBlitzFile, +} from './create-example.mjs'; describe('create-example tool', () => { describe('createEmptyExample', () => { @@ -23,17 +22,19 @@ describe('create-example tool', () => { createEmptyExample('foo-bar', '/path/to/foo-bar'); expect(writeFileSpy).toHaveBeenCalledTimes(2); - expect(writeFileSpy) - .toHaveBeenCalledWith( - path.resolve(`/path/to/foo-bar/${EXAMPLE_CONFIG_FILENAME}`), jasmine.any(String)); - expect(writeFileSpy) - .toHaveBeenCalledWith( - path.resolve(`/path/to/foo-bar/${STACKBLITZ_CONFIG_FILENAME}`), jasmine.any(String)); + expect(writeFileSpy).toHaveBeenCalledWith( + path.resolve(`/path/to/foo-bar/${EXAMPLE_CONFIG_FILENAME}`), + jasmine.any(String) + ); + expect(writeFileSpy).toHaveBeenCalledWith( + path.resolve(`/path/to/foo-bar/${STACKBLITZ_CONFIG_FILENAME}`), + jasmine.any(String) + ); }); it('should fail if the example name contains spaces', () => { - expect(() => createEmptyExample('foo bar', '/path/to/foo-bar')).toThrowError( - `Unable to create example. The example name contains spaces: 'foo bar'` + expect(() => createEmptyExample('foo bar', '/path/to/foo-bar')).toThrowError( + `Unable to create example. The example name contains spaces: 'foo bar'` ); }); }); @@ -41,9 +42,9 @@ describe('create-example tool', () => { describe('ensureExamplePath', () => { it('should error if the path already exists', () => { spyOn(fs, 'existsSync').and.returnValue(true); - expect(() => ensureExamplePath('foo/bar')) - .toThrowError( - `Unable to create example. The path to the new example already exists: foo/bar`); + expect(() => ensureExamplePath('foo/bar')).toThrowError( + `Unable to create example. The path to the new example already exists: foo/bar` + ); }); it('should create the directory on disk', () => { @@ -66,23 +67,26 @@ describe('create-example tool', () => { it('should write a JSON file to disk', () => { const spy = spyOn(fs, 'writeFileSync'); writeStackBlitzFile('bar-bar', '/foo/bar-bar'); - expect(spy).toHaveBeenCalledWith(path.resolve(`/foo/bar-bar/${STACKBLITZ_CONFIG_FILENAME}`), [ - '{', - ' "description": "Bar Bar",', - ' "files": [', - ' "!**/*.d.ts",', - ' "!**/*.js",', - ' "!**/*.[1,2].*"', - ' ],', - ' "tags": [', - ' [', - ' "bar",', - ' "bar"', - ' ]', - ' ]', - '}', - '', - ].join('\n')); + expect(spy).toHaveBeenCalledWith( + path.resolve(`/foo/bar-bar/${STACKBLITZ_CONFIG_FILENAME}`), + [ + '{', + ' "description": "Bar Bar",', + ' "files": [', + ' "!**/*.d.ts",', + ' "!**/*.js",', + ' "!**/*.[1,2].*"', + ' ],', + ' "tags": [', + ' [', + ' "bar",', + ' "bar"', + ' ]', + ' ]', + '}', + '', + ].join('\n') + ); }); }); @@ -92,7 +96,7 @@ describe('create-example tool', () => { spyOn(console, 'log'); spyOn(fs, 'existsSync').and.returnValue(true); - const readFileSyncSpy = spyOn(fs, 'readFileSync').and.callFake(p => { + const readFileSyncSpy = spyOn(fs, 'readFileSync').and.callFake((p) => { switch (p) { case sourceGitIgnorePath: return '**/*.bad'; @@ -100,9 +104,7 @@ describe('create-example tool', () => { throw new Error('Unexpected path'); } }); - spyOn(glob, 'sync').and.returnValue([ - 'a/', 'a/b/', 'a/c', 'x.ts', 'x.bad' - ]); + spyOn(glob, 'sync').and.returnValue(['a/', 'a/b/', 'a/c', 'x.ts', 'x.bad']); const ensureDirSyncSpy = spyOn(fs, 'ensureDirSync'); const copySyncSpy = spyOn(fs, 'copySync'); diff --git a/aio/tools/examples/example-boilerplate-cli.mjs b/aio/tools/examples/example-boilerplate-cli.mjs new file mode 100644 index 00000000000..f2347550d69 --- /dev/null +++ b/aio/tools/examples/example-boilerplate-cli.mjs @@ -0,0 +1,3 @@ +import boilerplate from './example-boilerplate.mjs'; + +boilerplate.main(); diff --git a/aio/tools/examples/example-boilerplate.js b/aio/tools/examples/example-boilerplate.mjs similarity index 65% rename from aio/tools/examples/example-boilerplate.js rename to aio/tools/examples/example-boilerplate.mjs index f2ee0821870..6b3b0f0d185 100644 --- a/aio/tools/examples/example-boilerplate.js +++ b/aio/tools/examples/example-boilerplate.mjs @@ -1,10 +1,15 @@ -const fs = require('fs-extra'); -const glob = require('glob'); -const ignore = require('ignore'); -const path = require('canonical-path'); -const shelljs = require('shelljs'); -const yargs = require('yargs'); -const {RUNFILES_ROOT, getExamplesBasePath, getSharedPath, EXAMPLE_CONFIG_FILENAME} = require('./constants'); +import fs from 'fs-extra'; +import glob from 'glob'; +import ignore from 'ignore'; +import path from 'canonical-path'; +import shelljs from 'shelljs'; +import yargs from 'yargs'; +import { + RUNFILES_ROOT, + getExamplesBasePath, + getSharedPath, + EXAMPLE_CONFIG_FILENAME, +} from './constants.mjs'; const PROJECT_ROOT = RUNFILES_ROOT; const EXAMPLES_BASE_PATH = getExamplesBasePath(PROJECT_ROOT); @@ -19,19 +24,24 @@ class ExampleBoilerPlate { * Add boilerplate files to an example */ add(exampleFolder, outputDir) { - const gitignore = ignore().add(fs.readFileSync(path.resolve(BOILERPLATE_BASE_PATH, '.gitignore'), 'utf8')); + const gitignore = ignore().add( + fs.readFileSync(path.resolve(BOILERPLATE_BASE_PATH, '.gitignore'), 'utf8') + ); const exampleConfig = this.loadJsonFile(path.resolve(exampleFolder, EXAMPLE_CONFIG_FILENAME)); // Compute additional boilerplate files that should not be copied over for this specific example // This allows the example to override boilerplate files locally, perhaps to include doc-regions specific to the example. const overrideBoilerplate = exampleConfig['overrideBoilerplate'] || []; - const boilerplateIgnore = ignore().add(gitignore).add( - // Note that the `*` here is to skip over the boilerplate folder itself. - // E.g. if the override is `a/b` then we what to match `cli/a/b` and `i18n/a/b` etc. - overrideBoilerplate.map(p => path.join('*', p)) - ); - const isPathIgnored = absolutePath => boilerplateIgnore.ignores(path.relative(BOILERPLATE_BASE_PATH, absolutePath)); + const boilerplateIgnore = ignore() + .add(gitignore) + .add( + // Note that the `*` here is to skip over the boilerplate folder itself. + // E.g. if the override is `a/b` then we what to match `cli/a/b` and `i18n/a/b` etc. + overrideBoilerplate.map((p) => path.join('*', p)) + ); + const isPathIgnored = (absolutePath) => + boilerplateIgnore.ignores(path.relative(BOILERPLATE_BASE_PATH, absolutePath)); const boilerPlateType = exampleConfig.projectType || 'cli'; const boilerPlateBasePath = path.resolve(BOILERPLATE_BASE_PATH, boilerPlateType); @@ -54,16 +64,21 @@ class ExampleBoilerPlate { } listOverrides() { - const exampleFolders = - this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules'); + const exampleFolders = this.getFoldersContaining( + EXAMPLES_BASE_PATH, + EXAMPLE_CONFIG_FILENAME, + 'node_modules' + ); const overriddenFiles = []; - exampleFolders.forEach(exampleFolder => { + exampleFolders.forEach((exampleFolder) => { const exampleConfig = this.loadJsonFile(path.resolve(exampleFolder, EXAMPLE_CONFIG_FILENAME)); const overrideBoilerplate = exampleConfig['overrideBoilerplate'] || []; if (overrideBoilerplate.length > 0) { for (const file of overrideBoilerplate) { - overriddenFiles.push(path.relative(EXAMPLES_BASE_PATH, path.resolve(exampleFolder, file))); + overriddenFiles.push( + path.relative(EXAMPLES_BASE_PATH, path.resolve(exampleFolder, file)) + ); } } }); @@ -74,7 +89,9 @@ class ExampleBoilerPlate { console.log(` - ${file}`); } console.log(`(All these paths are relative to ${EXAMPLES_BASE_PATH}.)`); - console.log('If you are updating the boilerplate files then also consider updating these too.'); + console.log( + 'If you are updating the boilerplate files then also consider updating these too.' + ); } else { console.log('No boilerplate files have been overridden in examples.'); console.log('You are safe to update the boilerplate files.'); @@ -82,23 +99,31 @@ class ExampleBoilerPlate { } main() { - yargs.usage('$0 [args]') - .command('add ', 'create boilerplate for an example', yrgs => this.add(yrgs.argv._[1], yrgs.argv._[2])) - .command('list-overrides', 'list all the boilerplate files that have been overridden in examples', () => this.listOverrides()) - .demandCommand(1, 'Please supply a command from the list above') - .argv; + yargs(process.argv.slice(2)) + .usage('$0 [args]') + .command('add ', 'create boilerplate for an example', (yrgs) => + this.add(yrgs.argv._[1], yrgs.argv._[2]) + ) + .command( + 'list-overrides', + 'list all the boilerplate files that have been overridden in examples', + () => this.listOverrides() + ) + .demandCommand(1, 'Please supply a command from the list above').argv; } getFoldersContaining(basePath, filename, ignore) { const pattern = path.resolve(basePath, '**', filename); const ignorePattern = path.resolve(basePath, '**', ignore, '**'); - return glob.sync(pattern, {ignore: [ignorePattern]}).map(file => path.dirname(file)); + return glob.sync(pattern, {ignore: [ignorePattern]}).map((file) => path.dirname(file)); } - loadJsonFile(filePath) { return fs.readJsonSync(filePath, {throws: false}) || {}; } + loadJsonFile(filePath) { + return fs.readJsonSync(filePath, {throws: false}) || {}; + } copyDirectoryContents(srcDir, dstDir, isPathIgnored) { - shelljs.ls('-Al', srcDir).forEach(stat => { + shelljs.ls('-Al', srcDir).forEach((stat) => { const srcPath = path.resolve(srcDir, stat.name); const dstPath = path.resolve(dstDir, stat.name); @@ -125,9 +150,4 @@ class ExampleBoilerPlate { } } -module.exports = new ExampleBoilerPlate(); - -// If this file was run directly then run the main function, -if (require.main === module) { - module.exports.main(); -} +export default new ExampleBoilerPlate(); diff --git a/aio/tools/examples/example-boilerplate.spec.js b/aio/tools/examples/example-boilerplate.spec.mjs similarity index 75% rename from aio/tools/examples/example-boilerplate.spec.js rename to aio/tools/examples/example-boilerplate.spec.mjs index bb06e4d3d47..6baf24bd5ac 100644 --- a/aio/tools/examples/example-boilerplate.spec.js +++ b/aio/tools/examples/example-boilerplate.spec.mjs @@ -1,15 +1,15 @@ -const path = require('canonical-path'); -const fs = require('fs-extra'); -const glob = require('glob'); -const shelljs = require('shelljs'); +import path from 'canonical-path'; +import fs from 'fs-extra'; +import glob from 'glob'; +import shelljs from 'shelljs'; -const {RUNFILES_ROOT, getExamplesBasePath, getSharedPath} = require("./constants"); +import {RUNFILES_ROOT, getExamplesBasePath, getSharedPath} from './constants.mjs'; +import exampleBoilerPlate from './example-boilerplate.mjs'; const PROJECT_ROOT = RUNFILES_ROOT; const EXAMPLES_BASE_PATH = getExamplesBasePath(PROJECT_ROOT); const SHARED_PATH = getSharedPath(PROJECT_ROOT); -const exampleBoilerPlate = require('./example-boilerplate'); const outputDir = process.env.TEST_TMPDIR; // Bazel-provided temp dir describe('example-boilerplate tool', () => { @@ -25,7 +25,7 @@ describe('example-boilerplate tool', () => { it('should copy all the source boilerplate files for systemjs', () => { const boilerplateDir = path.resolve(sharedDir, 'boilerplate'); - exampleBoilerPlate.loadJsonFile.and.returnValue({ projectType: 'systemjs' }); + exampleBoilerPlate.loadJsonFile.and.returnValue({projectType: 'systemjs'}); exampleBoilerPlate.add(exampleFolder, outputDir); @@ -38,7 +38,7 @@ describe('example-boilerplate tool', () => { it('should copy all the source boilerplate files for cli', () => { const boilerplateDir = path.resolve(sharedDir, 'boilerplate'); - exampleBoilerPlate.loadJsonFile.and.returnValue({ projectType: 'cli' }); + exampleBoilerPlate.loadJsonFile.and.returnValue({projectType: 'cli'}); exampleBoilerPlate.add(exampleFolder, outputDir); @@ -64,7 +64,7 @@ describe('example-boilerplate tool', () => { it('should copy all the source boilerplate files for i18n (on top of the cli ones)', () => { const boilerplateDir = path.resolve(sharedDir, 'boilerplate'); - exampleBoilerPlate.loadJsonFile.and.returnValue({ projectType: 'i18n' }); + exampleBoilerPlate.loadJsonFile.and.returnValue({projectType: 'i18n'}); exampleBoilerPlate.add(exampleFolder, outputDir); @@ -78,7 +78,7 @@ describe('example-boilerplate tool', () => { it('should copy all the source boilerplate files for universal (on top of the cli ones)', () => { const boilerplateDir = path.resolve(sharedDir, 'boilerplate'); - exampleBoilerPlate.loadJsonFile.and.returnValue({ projectType: 'universal' }); + exampleBoilerPlate.loadJsonFile.and.returnValue({projectType: 'universal'}); exampleBoilerPlate.add(exampleFolder, outputDir); @@ -93,7 +93,7 @@ describe('example-boilerplate tool', () => { it('should not copy boilerplate files that match `overrideBoilerplate` in the example-config.json file', () => { const boilerplateDir = path.resolve(sharedDir, 'boilerplate'); exampleBoilerPlate.loadJsonFile.and.returnValue({ - 'overrideBoilerplate': [ 'c/d' ] + 'overrideBoilerplate': ['c/d'], }); exampleBoilerPlate.add(exampleFolder, outputDir); @@ -106,12 +106,18 @@ describe('example-boilerplate tool', () => { it('should try to load the example config file', () => { exampleBoilerPlate.add(exampleFolder, outputDir); expect(exampleBoilerPlate.loadJsonFile).toHaveBeenCalledTimes(1); - expect(exampleBoilerPlate.loadJsonFile).toHaveBeenCalledWith(path.resolve(`${exampleFolder}/example-config.json`)); + expect(exampleBoilerPlate.loadJsonFile).toHaveBeenCalledWith( + path.resolve(`${exampleFolder}/example-config.json`) + ); }); }); describe('copyDirectoryContents', () => { - const spyFnFor = fnName => (...args) => { callLog.push(`${fnName}(${args.join(', ')})`); }; + const spyFnFor = + (fnName) => + (...args) => { + callLog.push(`${fnName}(${args.join(', ')})`); + }; let isPathIgnoredSpy; let callLog; @@ -154,7 +160,7 @@ describe('example-boilerplate tool', () => { {name: 'file-1.txt', isDirectory: () => false}, {name: 'file-2.txt', isDirectory: () => false}, ]); - isPathIgnoredSpy.and.callFake(path => path.endsWith('file-1.txt')); + isPathIgnoredSpy.and.callFake((path) => path.endsWith('file-1.txt')); exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); @@ -186,18 +192,19 @@ describe('example-boilerplate tool', () => { it('should recursively copy sub-directories', () => { spyOn(shelljs, 'ls') - .withArgs('-Al', 'source/dir').and.returnValue([ + .withArgs('-Al', 'source/dir') + .and.returnValue([ {name: 'file-1.txt', isDirectory: () => false}, {name: 'sub-dir-1', isDirectory: () => true}, {name: 'file-2.txt', isDirectory: () => false}, ]) - .withArgs('-Al', path.resolve('source/dir/sub-dir-1')).and.returnValue([ + .withArgs('-Al', path.resolve('source/dir/sub-dir-1')) + .and.returnValue([ {name: 'file-3.txt', isDirectory: () => false}, {name: 'sub-dir-2', isDirectory: () => true}, ]) - .withArgs('-Al', path.resolve('source/dir/sub-dir-1/sub-dir-2')).and.returnValue([ - {name: 'file-4.txt', isDirectory: () => false}, - ]); + .withArgs('-Al', path.resolve('source/dir/sub-dir-1/sub-dir-2')) + .and.returnValue([{name: 'file-4.txt', isDirectory: () => false}]); exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); @@ -210,22 +217,22 @@ describe('example-boilerplate tool', () => { // Create `sub-dir-1` and recursively copy its contents. `mkdir(-p, ${path.resolve('destination/dir/sub-dir-1')})`, - // Copy `sub-dir-1/file-3.txt`. - `test(-f, ${path.resolve('destination/dir/sub-dir-1/file-3.txt')})`, - 'cp(' + - `${path.resolve('source/dir/sub-dir-1/file-3.txt')}, ` + - `${path.resolve('destination/dir/sub-dir-1')})`, - `chmod(444, ${path.resolve('destination/dir/sub-dir-1/file-3.txt')})`, + // Copy `sub-dir-1/file-3.txt`. + `test(-f, ${path.resolve('destination/dir/sub-dir-1/file-3.txt')})`, + 'cp(' + + `${path.resolve('source/dir/sub-dir-1/file-3.txt')}, ` + + `${path.resolve('destination/dir/sub-dir-1')})`, + `chmod(444, ${path.resolve('destination/dir/sub-dir-1/file-3.txt')})`, - // Create `sub-dir-1/sub-dir-2` and recursively copy its contents. - `mkdir(-p, ${path.resolve('destination/dir/sub-dir-1/sub-dir-2')})`, + // Create `sub-dir-1/sub-dir-2` and recursively copy its contents. + `mkdir(-p, ${path.resolve('destination/dir/sub-dir-1/sub-dir-2')})`, - // Copy `sub-dir-1/sub-dir-2/file-4.txt`. - `test(-f, ${path.resolve('destination/dir/sub-dir-1/sub-dir-2/file-4.txt')})`, - 'cp(' + - `${path.resolve('source/dir/sub-dir-1/sub-dir-2/file-4.txt')}, ` + - `${path.resolve('destination/dir/sub-dir-1/sub-dir-2')})`, - `chmod(444, ${path.resolve('destination/dir/sub-dir-1/sub-dir-2/file-4.txt')})`, + // Copy `sub-dir-1/sub-dir-2/file-4.txt`. + `test(-f, ${path.resolve('destination/dir/sub-dir-1/sub-dir-2/file-4.txt')})`, + 'cp(' + + `${path.resolve('source/dir/sub-dir-1/sub-dir-2/file-4.txt')}, ` + + `${path.resolve('destination/dir/sub-dir-1/sub-dir-2')})`, + `chmod(444, ${path.resolve('destination/dir/sub-dir-1/sub-dir-2/file-4.txt')})`, // Copy `file-2.txt`. `test(-f, ${path.resolve('destination/dir/file-2.txt')})`, @@ -236,18 +243,19 @@ describe('example-boilerplate tool', () => { it('should skip ignored directories', () => { spyOn(shelljs, 'ls') - .withArgs('-Al', 'source/dir').and.returnValue([ + .withArgs('-Al', 'source/dir') + .and.returnValue([ {name: 'file-1.txt', isDirectory: () => false}, {name: 'sub-dir-1', isDirectory: () => true}, ]) - .withArgs('-Al', path.resolve('source/dir/sub-dir-1')).and.returnValue([ + .withArgs('-Al', path.resolve('source/dir/sub-dir-1')) + .and.returnValue([ {name: 'file-2.txt', isDirectory: () => false}, {name: 'sub-dir-2', isDirectory: () => true}, ]) - .withArgs('-Al', path.resolve('source/dir/sub-dir-1/sub-dir-2')).and.returnValue([ - {name: 'file-3.txt', isDirectory: () => false}, - ]); - isPathIgnoredSpy.and.callFake(path => path.endsWith('sub-dir-1')); + .withArgs('-Al', path.resolve('source/dir/sub-dir-1/sub-dir-2')) + .and.returnValue([{name: 'file-3.txt', isDirectory: () => false}]); + isPathIgnoredSpy.and.callFake((path) => path.endsWith('sub-dir-1')); exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy); @@ -272,30 +280,42 @@ describe('example-boilerplate tool', () => { it('should list all files that are overridden in examples', () => { spyOn(exampleBoilerPlate, 'loadJsonFile').and.returnValues( - {"overrideBoilerplate": ["angular.json", "tsconfig.json"]}, // a/b/example-config.json - {"overrideBoilerplate": []}, // c/d/example-config.json - {}, // e/f/example-config.json + {'overrideBoilerplate': ['angular.json', 'tsconfig.json']}, // a/b/example-config.json + {'overrideBoilerplate': []}, // c/d/example-config.json + {} // e/f/example-config.json ); exampleBoilerPlate.listOverrides(); - expect(exampleBoilerPlate.getFoldersContaining) - .toHaveBeenCalledWith(examplesDir, 'example-config.json', 'node_modules'); - expect(console.log).toHaveBeenCalledWith('Boilerplate files that have been overridden in examples:'); + expect(exampleBoilerPlate.getFoldersContaining).toHaveBeenCalledWith( + examplesDir, + 'example-config.json', + 'node_modules' + ); + expect(console.log).toHaveBeenCalledWith( + 'Boilerplate files that have been overridden in examples:' + ); expect(console.log).toHaveBeenCalledWith(' - a/b/angular.json'); expect(console.log).toHaveBeenCalledWith(' - a/b/tsconfig.json'); expect(console.log).toHaveBeenCalledWith(`(All these paths are relative to ${examplesDir}.)`); - expect(console.log).toHaveBeenCalledWith('If you are updating the boilerplate files then also consider updating these too.'); + expect(console.log).toHaveBeenCalledWith( + 'If you are updating the boilerplate files then also consider updating these too.' + ); }); it('should display a helpful message if there are no overridden files', () => { spyOn(exampleBoilerPlate, 'loadJsonFile').and.returnValues( - {"overrideBoilerplate": null}, // a/b/example-config.json - {"overrideBoilerplate": []}, // c/d/example-config.json - {}, // e/f/example-config.json + {'overrideBoilerplate': null}, // a/b/example-config.json + {'overrideBoilerplate': []}, // c/d/example-config.json + {} // e/f/example-config.json ); exampleBoilerPlate.listOverrides(); - expect(exampleBoilerPlate.getFoldersContaining) - .toHaveBeenCalledWith(examplesDir, 'example-config.json', 'node_modules'); - expect(console.log).toHaveBeenCalledWith('No boilerplate files have been overridden in examples.'); + expect(exampleBoilerPlate.getFoldersContaining).toHaveBeenCalledWith( + examplesDir, + 'example-config.json', + 'node_modules' + ); + expect(console.log).toHaveBeenCalledWith( + 'No boilerplate files have been overridden in examples.' + ); expect(console.log).toHaveBeenCalledWith('You are safe to update the boilerplate files.'); }); }); @@ -303,18 +323,24 @@ describe('example-boilerplate tool', () => { describe('getFoldersContaining', () => { it('should use glob.sync', () => { spyOn(glob, 'sync').and.returnValue(['a/b/config.json', 'c/d/config.json']); - const result = exampleBoilerPlate.getFoldersContaining('base/path', 'config.json', 'node_modules'); - expect(glob.sync).toHaveBeenCalledWith(path.resolve('base/path/**/config.json'), { ignore: [path.resolve('base/path/**/node_modules/**')] }); + const result = exampleBoilerPlate.getFoldersContaining( + 'base/path', + 'config.json', + 'node_modules' + ); + expect(glob.sync).toHaveBeenCalledWith(path.resolve('base/path/**/config.json'), { + ignore: [path.resolve('base/path/**/node_modules/**')], + }); expect(result).toEqual(['a/b', 'c/d']); }); }); describe('loadJsonFile', () => { it('should use fs.readJsonSync', () => { - spyOn(fs, 'readJsonSync').and.returnValue({ some: 'value' }); + spyOn(fs, 'readJsonSync').and.returnValue({some: 'value'}); const result = exampleBoilerPlate.loadJsonFile('some/file'); expect(fs.readJsonSync).toHaveBeenCalledWith('some/file', {throws: false}); - expect(result).toEqual({ some: 'value' }); + expect(result).toEqual({some: 'value'}); }); it('should return an empty object if readJsonSync fails', () => { diff --git a/aio/tools/transforms/authors-package/BUILD.bazel b/aio/tools/transforms/authors-package/BUILD.bazel index ba9b56656ac..7fc8376e58b 100644 --- a/aio/tools/transforms/authors-package/BUILD.bazel +++ b/aio/tools/transforms/authors-package/BUILD.bazel @@ -9,7 +9,7 @@ js_library( ["**/*.js"], exclude = [ "**/*/spec.js", - "watchr.js", + "watchr.mjs", ], ), deps = [ @@ -25,7 +25,7 @@ js_library( js_library( name = "watchdocs", srcs = [ - "watchr.js", + "watchr.mjs", ], deps = [ ":authors-package", diff --git a/aio/tools/transforms/authors-package/watchr.js b/aio/tools/transforms/authors-package/watchr.js deleted file mode 100644 index 94b043c88a5..00000000000 --- a/aio/tools/transforms/authors-package/watchr.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint no-console: "off" */ -const watchr = require('watchr'); -const {relative} = require('canonical-path'); -const {generateDocs} = require('./index.js'); -const { PROJECT_ROOT, CONTENTS_PATH, API_SOURCE_PATH } = require('../config'); - -function listener(changeType, fullPath) { - try { - const relativePath = relative(PROJECT_ROOT, fullPath); - console.log('The file', relativePath, `was ${changeType}d at`, new Date().toUTCString()); - generateDocs(relativePath); - } catch(err) { - console.log('Error generating docs', err); - } -} - -function next(error) { - if (error) { - console.log(error); - } -} - -function watch() { - console.log('============================================================================'); - console.log('Started watching files in:'); - console.log(' - ', CONTENTS_PATH); - console.log(' - ', API_SOURCE_PATH); - console.log('Doc gen will automatically run on any change to a file in either directory.'); - console.log('============================================================================'); - - watchr.open(CONTENTS_PATH, listener, next); - watchr.open(API_SOURCE_PATH, listener, next); -} - -exports.watch = watch; \ No newline at end of file diff --git a/aio/tools/transforms/authors-package/watchr.mjs b/aio/tools/transforms/authors-package/watchr.mjs new file mode 100644 index 00000000000..72ca2878237 --- /dev/null +++ b/aio/tools/transforms/authors-package/watchr.mjs @@ -0,0 +1,35 @@ +/* eslint no-console: "off" */ +import watchr from 'watchr'; +import canonicalPath from 'canonical-path'; +import authorsPackage from './index.js'; +import config from '../config.js'; + +function listener(changeType, fullPath) { + try { + const relativePath = canonicalPath.relative(config.PROJECT_ROOT, fullPath); + console.log('The file', relativePath, `was ${changeType}d at`, new Date().toUTCString()); + authorsPackage.generateDocs(relativePath); + } catch (err) { + console.log('Error generating docs', err); + } +} + +function next(error) { + if (error) { + console.log(error); + } +} + +function watch() { + console.log('============================================================================'); + console.log('Started watching files in:'); + console.log(' - ', config.CONTENTS_PATH); + console.log(' - ', config.API_SOURCE_PATH); + console.log('Doc gen will automatically run on any change to a file in either directory.'); + console.log('============================================================================'); + + watchr.open(config.CONTENTS_PATH, listener, next); + watchr.open(config.API_SOURCE_PATH, listener, next); +} + +exports.watch = watch; diff --git a/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl b/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl index 4df086fec8b..967356fc737 100644 --- a/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl +++ b/packages/compiler-cli/test/compliance/partial/partial_compliance_goldens.bzl @@ -18,7 +18,7 @@ def partial_compliance_golden(filePath): name = generate_partial_name, testonly = True, data = data, - data_for_args = [filePath], + data_for_expansion = [filePath], visibility = [":__pkg__"], entry_point = "//packages/compiler-cli/test/compliance/partial:cli.ts", templated_args = ["$(execpath %s)" % filePath], diff --git a/tools/circular_dependency_test/index.bzl b/tools/circular_dependency_test/index.bzl index b3a2eab761f..e2e99b86ee6 100644 --- a/tools/circular_dependency_test/index.bzl +++ b/tools/circular_dependency_test/index.bzl @@ -17,7 +17,7 @@ def circular_dependency_test(name, deps, entry_point, **kwargs): name = name, data = ["@npm//madge"] + deps, entry_point = "@npm//:node_modules/madge/bin/cli.js", - data_for_args = [MADGE_CONFIG_LABEL], + data_for_expansion = [MADGE_CONFIG_LABEL], templated_args = [ "--circular", "--no-spinner", diff --git a/tools/esm-interop/nodejs-rules.bzl b/tools/esm-interop/nodejs-rules.bzl index 120316ffe9a..8b7c010d4e8 100644 --- a/tools/esm-interop/nodejs-rules.bzl +++ b/tools/esm-interop/nodejs-rules.bzl @@ -6,7 +6,7 @@ load("//tools/esm-interop:extract-esm-output.bzl", "extract_esm_outputs") def nodejs_binary( name, - data_for_args = [], + data_for_expansion = [], linker_enabled = False, npm_workspace = "npm", **kwargs): @@ -25,12 +25,12 @@ def nodejs_binary( extract_esm_outputs( name = "%s_esm_deps" % name, testonly = testonly, - deps = data, + deps = data + data_for_expansion, ) _nodejs_binary( name = name, - data = [":%s_esm_deps" % name] + data_for_args, + data = [":%s_esm_deps" % name] + data_for_expansion, testonly = testonly, entry_point = str(entry_point).replace(".js", ".mjs"), env = env, @@ -41,7 +41,7 @@ def nodejs_binary( def nodejs_test( name, - data_for_args = [], + data_for_expansion = [], linker_enabled = False, npm_workspace = "npm", **kwargs): @@ -58,12 +58,12 @@ def nodejs_test( extract_esm_outputs( name = "%s_esm_deps" % name, testonly = True, - deps = data, + deps = data + data_for_expansion, ) _nodejs_test( name = name, - data = [":%s_esm_deps" % name] + data_for_args, + data = [":%s_esm_deps" % name] + data_for_expansion, env = env, templated_args = templated_args, use_esm = True, diff --git a/tools/symbol-extractor/index.bzl b/tools/symbol-extractor/index.bzl index c76739c4b3e..a947335f397 100644 --- a/tools/symbol-extractor/index.bzl +++ b/tools/symbol-extractor/index.bzl @@ -23,7 +23,7 @@ def js_expected_symbol_test(name, src, golden, data = [], **kwargs): data = all_data, entry_point = entry_point, tags = kwargs.pop("tags", []) + ["symbol_extractor"], - data_for_args = [src, golden], + data_for_expansion = [src, golden], templated_args = ["$(rootpath %s)" % src, "$(rootpath %s)" % golden], **kwargs ) @@ -33,7 +33,7 @@ def js_expected_symbol_test(name, src, golden, data = [], **kwargs): testonly = True, data = all_data, entry_point = entry_point, - data_for_args = [src, golden], + data_for_expansion = [src, golden], templated_args = ["$(rootpath %s)" % src, "$(rootpath %s)" % golden, "--accept"], **kwargs )