2022-12-14 09:59:42 +00:00
|
|
|
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';
|
2022-09-22 21:15:08 +00:00
|
|
|
|
|
|
|
|
const PROJECT_ROOT = RUNFILES_ROOT;
|
|
|
|
|
const EXAMPLES_BASE_PATH = getExamplesBasePath(PROJECT_ROOT);
|
|
|
|
|
const SHARED_PATH = getSharedPath(PROJECT_ROOT);
|
2017-07-27 07:29:17 +00:00
|
|
|
|
2020-07-22 11:40:03 +00:00
|
|
|
const BOILERPLATE_BASE_PATH = path.resolve(SHARED_PATH, 'boilerplate');
|
|
|
|
|
const BOILERPLATE_CLI_PATH = path.resolve(BOILERPLATE_BASE_PATH, 'cli');
|
|
|
|
|
const BOILERPLATE_COMMON_PATH = path.resolve(BOILERPLATE_BASE_PATH, 'common');
|
2020-04-06 19:57:58 +00:00
|
|
|
|
2017-07-27 07:29:17 +00:00
|
|
|
class ExampleBoilerPlate {
|
|
|
|
|
/**
|
2022-06-02 22:18:34 +00:00
|
|
|
* Add boilerplate files to an example
|
2017-07-27 07:29:17 +00:00
|
|
|
*/
|
2022-09-22 21:15:08 +00:00
|
|
|
add(exampleFolder, outputDir) {
|
2022-12-14 09:59:42 +00:00
|
|
|
const gitignore = ignore().add(
|
|
|
|
|
fs.readFileSync(path.resolve(BOILERPLATE_BASE_PATH, '.gitignore'), 'utf8')
|
|
|
|
|
);
|
2017-07-27 07:29:17 +00:00
|
|
|
|
2022-06-02 22:18:34 +00:00
|
|
|
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'] || [];
|
2022-12-14 09:59:42 +00:00
|
|
|
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));
|
2022-06-02 22:18:34 +00:00
|
|
|
|
|
|
|
|
const boilerPlateType = exampleConfig.projectType || 'cli';
|
|
|
|
|
const boilerPlateBasePath = path.resolve(BOILERPLATE_BASE_PATH, boilerPlateType);
|
2022-09-22 21:15:08 +00:00
|
|
|
shelljs.mkdir('-p', outputDir);
|
2022-06-02 22:18:34 +00:00
|
|
|
|
|
|
|
|
// All example types other than `cli` and `systemjs` are based on `cli`. Copy over the `cli`
|
|
|
|
|
// boilerplate files first.
|
|
|
|
|
// (Some of these files might be later overwritten by type-specific files.)
|
|
|
|
|
if (boilerPlateType !== 'cli' && boilerPlateType !== 'systemjs') {
|
2022-09-22 21:15:08 +00:00
|
|
|
this.copyDirectoryContents(BOILERPLATE_CLI_PATH, outputDir, isPathIgnored);
|
2022-06-02 22:18:34 +00:00
|
|
|
}
|
2017-08-22 19:31:15 +00:00
|
|
|
|
2022-06-02 22:18:34 +00:00
|
|
|
// Copy the type-specific boilerplate files.
|
2022-09-22 21:15:08 +00:00
|
|
|
this.copyDirectoryContents(boilerPlateBasePath, outputDir, isPathIgnored);
|
2019-05-12 18:12:17 +00:00
|
|
|
|
2022-06-02 22:18:34 +00:00
|
|
|
// Copy the common boilerplate files (unless explicitly not used).
|
|
|
|
|
if (exampleConfig.useCommonBoilerplate !== false) {
|
2022-09-22 21:15:08 +00:00
|
|
|
this.copyDirectoryContents(BOILERPLATE_COMMON_PATH, outputDir, isPathIgnored);
|
2022-06-02 22:18:34 +00:00
|
|
|
}
|
2017-07-27 07:29:17 +00:00
|
|
|
}
|
|
|
|
|
|
2021-09-08 20:12:29 +00:00
|
|
|
listOverrides() {
|
2022-12-14 09:59:42 +00:00
|
|
|
const exampleFolders = this.getFoldersContaining(
|
|
|
|
|
EXAMPLES_BASE_PATH,
|
|
|
|
|
EXAMPLE_CONFIG_FILENAME,
|
|
|
|
|
'node_modules'
|
|
|
|
|
);
|
2021-09-08 20:12:29 +00:00
|
|
|
|
|
|
|
|
const overriddenFiles = [];
|
2022-12-14 09:59:42 +00:00
|
|
|
exampleFolders.forEach((exampleFolder) => {
|
2021-09-08 20:12:29 +00:00
|
|
|
const exampleConfig = this.loadJsonFile(path.resolve(exampleFolder, EXAMPLE_CONFIG_FILENAME));
|
|
|
|
|
const overrideBoilerplate = exampleConfig['overrideBoilerplate'] || [];
|
|
|
|
|
if (overrideBoilerplate.length > 0) {
|
|
|
|
|
for (const file of overrideBoilerplate) {
|
2022-12-14 09:59:42 +00:00
|
|
|
overriddenFiles.push(
|
|
|
|
|
path.relative(EXAMPLES_BASE_PATH, path.resolve(exampleFolder, file))
|
|
|
|
|
);
|
2021-09-08 20:12:29 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (overriddenFiles.length > 0) {
|
|
|
|
|
console.log(`Boilerplate files that have been overridden in examples:`);
|
|
|
|
|
for (const file of overriddenFiles) {
|
|
|
|
|
console.log(` - ${file}`);
|
|
|
|
|
}
|
|
|
|
|
console.log(`(All these paths are relative to ${EXAMPLES_BASE_PATH}.)`);
|
2022-12-14 09:59:42 +00:00
|
|
|
console.log(
|
|
|
|
|
'If you are updating the boilerplate files then also consider updating these too.'
|
|
|
|
|
);
|
2021-09-08 20:12:29 +00:00
|
|
|
} else {
|
|
|
|
|
console.log('No boilerplate files have been overridden in examples.');
|
|
|
|
|
console.log('You are safe to update the boilerplate files.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-27 07:29:17 +00:00
|
|
|
main() {
|
2022-12-14 09:59:42 +00:00
|
|
|
yargs(process.argv.slice(2))
|
|
|
|
|
.usage('$0 <cmd> [args]')
|
|
|
|
|
.command('add <exampleDir> <outputDir>', '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;
|
2017-07-27 07:29:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getFoldersContaining(basePath, filename, ignore) {
|
|
|
|
|
const pattern = path.resolve(basePath, '**', filename);
|
|
|
|
|
const ignorePattern = path.resolve(basePath, '**', ignore, '**');
|
2022-12-14 09:59:42 +00:00
|
|
|
return glob.sync(pattern, {ignore: [ignorePattern]}).map((file) => path.dirname(file));
|
2017-07-27 07:29:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-14 09:59:42 +00:00
|
|
|
loadJsonFile(filePath) {
|
|
|
|
|
return fs.readJsonSync(filePath, {throws: false}) || {};
|
|
|
|
|
}
|
2017-10-30 22:39:58 +00:00
|
|
|
|
2020-09-27 10:37:43 +00:00
|
|
|
copyDirectoryContents(srcDir, dstDir, isPathIgnored) {
|
2022-12-14 09:59:42 +00:00
|
|
|
shelljs.ls('-Al', srcDir).forEach((stat) => {
|
2020-07-22 11:40:03 +00:00
|
|
|
const srcPath = path.resolve(srcDir, stat.name);
|
|
|
|
|
const dstPath = path.resolve(dstDir, stat.name);
|
|
|
|
|
|
2020-09-27 10:37:43 +00:00
|
|
|
if (isPathIgnored(srcPath)) {
|
|
|
|
|
// `srcPath` is ignored (e.g. by a `.gitignore` file): Ignore it.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 11:40:03 +00:00
|
|
|
if (stat.isDirectory()) {
|
|
|
|
|
// `srcPath` is a directory: Recursively copy it to `dstDir`.
|
|
|
|
|
shelljs.mkdir('-p', dstPath);
|
2020-09-27 10:37:43 +00:00
|
|
|
return this.copyDirectoryContents(srcPath, dstPath, isPathIgnored);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `srcPath` is a file: Copy it to `dstDir`.
|
|
|
|
|
// (Also make the file non-writable to avoid accidental editing of boilerplate files).
|
|
|
|
|
if (shelljs.test('-f', dstPath)) {
|
|
|
|
|
// If the file already exists, ensure it is writable (so it can be overwritten).
|
|
|
|
|
shelljs.chmod(666, dstPath);
|
2020-07-22 11:40:03 +00:00
|
|
|
}
|
2020-09-27 10:37:43 +00:00
|
|
|
shelljs.cp(srcPath, dstDir);
|
|
|
|
|
shelljs.chmod(444, dstPath);
|
2020-07-22 11:40:03 +00:00
|
|
|
});
|
2017-10-30 22:39:58 +00:00
|
|
|
}
|
2017-07-27 07:29:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-14 09:59:42 +00:00
|
|
|
export default new ExampleBoilerPlate();
|