This tool can be run from anywhere in the aio folder as: ```sh yarn create-example <example-name> ``` It will create some basic scaffold files to get the example started. After creation the developer should then use `yarn boilerplate:add` or similar to ensure that the example can be run and tested. You can optionally provide an absolute path to a pre-existing CLI project and it will copy over appropriate files (ignoring boilerplate) to the newly created example. ```sh yarn create-example <example-name> /path/to/other/cli/project ``` Fixes #39275 PR Close #39283
		
			
				
	
	
		
			140 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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 {EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, SHARED_PATH, STACKBLITZ_CONFIG_FILENAME} =
 | |
|     require('./constants');
 | |
| 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 <name> [source]',
 | |
|               [
 | |
|                 'Create a new <name> 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(options.source) : BASIC_SOURCE_PATH;
 | |
|   console.log('Copying files from', sourcePath);
 | |
|   copyExampleFiles(sourcePath, examplePath, exampleName);
 | |
| 
 | |
|   console.log(`The new "${exampleName}" example has been created.`);
 | |
|   console.log('Now run "yarn boilerplate:add" to set it up for development.');
 | |
|   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) {
 | |
|   ensureExamplePath(examplePath);
 | |
|   writeExampleConfigFile(examplePath);
 | |
|   writeStackBlitzFile(exampleName, examplePath);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 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`, which are not ignored by the `.gitignore` file in the
 | |
|  * `EXAMPLES_BASE_PATH`, to the `examplePath`.
 | |
|  */
 | |
| function copyExampleFiles(sourcePath, examplePath, exampleName) {
 | |
|   const gitIgnoreSource = getGitIgnore(sourcePath);
 | |
|   const gitIgnoreExamples = getGitIgnore(EXAMPLES_BASE_PATH);
 | |
| 
 | |
|   // 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))
 | |
|           // Filter out files that match the examples directory .gitignore rules
 | |
|           .filter(filePath => !gitIgnoreExamples.ignores(path.join(exampleName, 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; |