nx/packages/devkit/src/generators/generate-files.ts
Jason Jean 5e23c07077
fix(core): automatically add root to the project.json projects (#9977)
* fix(core): automatically add root to the project.json projects

* chore(core): move project-configuration generator utils to nx package

* fix(core): add migrations to remove root
2022-04-28 13:24:35 -04:00

136 lines
3.6 KiB
TypeScript

import { readFileSync, readdirSync, statSync } from 'fs';
import * as path from 'path';
import type { Tree } from 'nx/src/generators/tree';
import { logger } from 'nx/src/utils/logger';
const binaryExts = new Set([
// // Image types originally from https://github.com/sindresorhus/image-type/blob/5541b6a/index.js
'.jpg',
'.jpeg',
'.png',
'.gif',
'.webp',
'.flif',
'.cr2',
'.tif',
'.bmp',
'.jxr',
'.psd',
'.ico',
'.bpg',
'.jp2',
'.jpm',
'.jpx',
'.heic',
'.cur',
'.tgz',
// Java files
'.jar',
'.keystore',
// Font files
'.ttf',
'.otf',
'.woff',
'.woff2',
'.eot',
]);
/**
* Generates a folder of files based on provided templates.
*
* While doing so it performs two substitutions:
* - Substitutes segments of file names surrounded by __
* - Uses ejs to substitute values in templates
*
* Examples:
* ```typescript
* generateFiles(tree, path.join(__dirname , 'files'), './tools/scripts', {tmpl: '', name: 'myscript'})
* ```
* This command will take all the files from the `files` directory next to the place where the command is invoked from.
* It will replace all `__tmpl__` with '' and all `__name__` with 'myscript' in the file names, and will replace all
* `<%= name %>` with `myscript` in the files themselves.
* `tmpl: ''` is a common pattern. With it you can name files like this: `index.ts__tmpl__`, so your editor
* doesn't get confused about incorrect TypeScript files.
*
* @param tree - the file system tree
* @param srcFolder - the source folder of files (absolute path)
* @param target - the target folder (relative to the tree root)
* @param substitutions - an object of key-value pairs
*/
export function generateFiles(
tree: Tree,
srcFolder: string,
target: string,
substitutions: { [k: string]: any }
): void {
const ejs = require('ejs');
const files = allFilesInDir(srcFolder);
if (files.length === 0) {
throw new Error(
`generateFiles: No files found in "${srcFolder}". Are you sure you specified the correct path?`
);
} else {
files.forEach((filePath) => {
let newContent: Buffer | string;
const computedPath = computePath(
srcFolder,
target,
filePath,
substitutions
);
if (binaryExts.has(path.extname(filePath))) {
newContent = readFileSync(filePath);
} else {
const template = readFileSync(filePath, 'utf-8');
try {
newContent = ejs.render(template, substitutions, {});
} catch (e) {
logger.error(`Error in ${filePath.replace(`${tree.root}/`, '')}:`);
throw e;
}
}
tree.write(computedPath, newContent);
});
}
}
function computePath(
srcFolder: string,
target: string,
filePath: string,
substitutions: { [k: string]: any }
): string {
const relativeFromSrcFolder = path.relative(srcFolder, filePath);
let computedPath = path.join(target, relativeFromSrcFolder);
if (computedPath.endsWith('.template')) {
computedPath = computedPath.substring(0, computedPath.length - 9);
}
Object.entries(substitutions).forEach(([propertyName, value]) => {
computedPath = computedPath.split(`__${propertyName}__`).join(value);
});
return computedPath;
}
function allFilesInDir(parent: string): string[] {
let res: string[] = [];
try {
readdirSync(parent).forEach((c) => {
const child = path.join(parent, c);
try {
const s = statSync(child);
if (!s.isDirectory()) {
res.push(child);
} else if (s.isDirectory()) {
res = [...res, ...allFilesInDir(child)];
}
} catch {}
});
} catch {}
return res;
}