UNPKG

yeoman-generator

Version:

Rails-inspired generator system that provides scaffolding for your apps

225 lines (224 loc) • 11.5 kB
/* eslint max-params: [1, 6] */ import assert from 'node:assert'; import { splitPathOptions } from '../util/path-options.js'; /** * A replacer is a function, an array or null, so a plain object in its position is the options object. */ const isWriteJSONOptions = (value) => typeof value === 'object' && value !== null && !Array.isArray(value); const writeJSONOptionsToPositional = ({ replacer, space, ...options }) => [ replacer, space, options, ]; /** * Merge the generator's `editorMetadata` into mem-fs-editor options, an explicit `metadata` option takes precedence. */ function withEditorMetadata(generator, options) { const { editorMetadata } = generator; if (!editorMetadata) { return options; } if (options && 'isFile' in options) { // Deprecated `write(filepath, contents, stat)` signature. return { stat: options, metadata: editorMetadata }; } return { ...options, metadata: { ...editorMetadata, ...options?.metadata } }; } export class FsMixin { fs; readTemplate(...args) { const [filepath, options, ...remaining] = args; const [pathOptions, readOptions] = splitPathOptions(options); return this.fs.read(this.templatePath(filepath, pathOptions), readOptions, ...remaining); } /** * Copy file from templates folder to destination folder. * mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}. * Shortcut for this.fs!.copy(this.templatePath(from), this.destinationPath(to)) */ copyTemplate(...args) { const [from, to, options, ...remaining] = args; const [pathOptions, copyOptions] = splitPathOptions(options); return this.fs.copy(from, this.destinationPath(to, pathOptions), withEditorMetadata(this, { fromBasePath: this.templatePath(), ...copyOptions }), ...remaining); } /** * Copy file from templates folder to destination folder. * mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}. * Shortcut for this.fs!.copy(this.templatePath(from), this.destinationPath(to)) */ async copyTemplateAsync(...args) { const [from, to, options, ...remaining] = args; const [pathOptions, copyOptions] = splitPathOptions(options); const templatePath = (filepath) => this.templatePath(filepath, pathOptions); return this.fs.copyAsync(Array.isArray(from) ? from.map(filepath => templatePath(filepath)) : templatePath(from), this.destinationPath(to, pathOptions), withEditorMetadata(this, copyOptions), ...remaining); } readDestination(...args) { const [filepath, options, ...remaining] = args; const [pathOptions, readOptions] = splitPathOptions(options); return this.fs.read(this.destinationPath(filepath, pathOptions), readOptions, ...remaining); } readDestinationJSON(...args) { const [filepath, defaults, options, ...remaining] = args; return this.fs.readJSON(this.destinationPath(filepath, options ?? {}), defaults, options, ...remaining); } /** * Write file to destination folder * mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}. * Shortcut for this.fs!.write(this.destinationPath(filepath)). */ writeDestination(...args) { const [filepath, contents, options, ...remaining] = args; const [pathOptions, writeOptions] = splitPathOptions(options); return this.fs.write(this.destinationPath(filepath, pathOptions), contents, withEditorMetadata(this, writeOptions), ...remaining); } writeDestinationJSON(...args) { const [filepath, contents, ...rest] = args; // Normalize the `writeDestinationJSON(filepath, contents, options)` form to the positional one. const [replacer, space, options, ...remaining] = isWriteJSONOptions(rest[0]) ? writeJSONOptionsToPositional(rest[0]) : rest; const [pathOptions, writeOptions] = splitPathOptions(options); return this.fs.writeJSON(this.destinationPath(filepath, pathOptions), contents, replacer, space, withEditorMetadata(this, writeOptions), ...remaining); } /** * Delete file from destination folder * mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}. * Shortcut for this.fs!.delete(this.destinationPath(filepath)). */ deleteDestination(...args) { const [paths, options, ...remaining] = args; const [pathOptions, deleteOptions] = splitPathOptions(options); const destinationPath = (filepath) => this.destinationPath(filepath, pathOptions); return this.fs.delete(Array.isArray(paths) ? paths.map(filepath => destinationPath(filepath)) : destinationPath(paths), deleteOptions, ...remaining); } /** * Copy file from destination folder to another destination folder. * mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}. * Shortcut for this.fs!.copy(this.destinationPath(from), this.destinationPath(to)). */ copyDestination(...args) { const [from, to, options, ...remaining] = args; const [pathOptions, copyOptions] = splitPathOptions(options); return this.fs.copy(from, this.destinationPath(to, pathOptions), withEditorMetadata(this, { fromBasePath: this.destinationPath(), ...copyOptions }), ...remaining); } /** * Move file from destination folder to another destination folder. * mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}. * Shortcut for this.fs!.move(this.destinationPath(from), this.destinationPath(to)). */ moveDestination(...args) { const [from, to, options, ...remaining] = args; const [pathOptions, moveOptions] = splitPathOptions(options); return this.fs.move(this.destinationPath(from, pathOptions), this.destinationPath(to, pathOptions), { fromBasePath: this.destinationPath(), ...moveOptions }, ...remaining); } /** * Exists file on destination folder. * mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}. * Shortcut for this.fs!.exists(this.destinationPath(filepath)). */ existsDestination(...args) { const [filepath, options, ...remaining] = args; return this.fs.exists(this.destinationPath(filepath, options ?? {}), options, ...remaining); } renderTemplate(source = '', destination = source, templateData, copyOptions, compatOptions) { if (compatOptions || 'context' in (copyOptions ?? {})) { copyOptions = { ...compatOptions, transformOptions: copyOptions }; } if (templateData === undefined || typeof templateData === 'string') { templateData = this._templateData(templateData); } const [pathOptions, copyTplOptions] = splitPathOptions(copyOptions); source = Array.isArray(source) ? source : [source]; const templatePath = this.templatePath(...source, pathOptions); destination = Array.isArray(destination) ? destination : [destination]; const destinationPath = this.destinationPath(...destination, pathOptions); this.fs.copyTpl(templatePath, destinationPath, templateData, withEditorMetadata(this, { fromBasePath: this.templatePath(), ...copyTplOptions, transformOptions: { context: this, ...copyTplOptions?.transformOptions, }, })); } async renderTemplateAsync(source = '', destination = source, templateData, copyOptions, compatOptions) { if (compatOptions || 'context' in (copyOptions ?? {})) { copyOptions = { ...compatOptions, transformOptions: copyOptions }; } if (templateData === undefined || typeof templateData === 'string') { templateData = this._templateData(templateData); } const [pathOptions, copyTplOptions] = splitPathOptions(copyOptions); source = Array.isArray(source) ? source : [source]; const templatePath = this.templatePath(...source, pathOptions); destination = Array.isArray(destination) ? destination : [destination]; const destinationPath = this.destinationPath(...destination, pathOptions); return this.fs.copyTplAsync(templatePath, destinationPath, templateData, withEditorMetadata(this, { fromBasePath: this.templatePath(), ...copyTplOptions, transformOptions: { context: this, ...copyTplOptions?.transformOptions, }, })); } /** * Copy templates from templates folder to the destination. */ renderTemplates(templates, templateData) { assert.ok(Array.isArray(templates), 'Templates must an array'); if (templateData === undefined || typeof templateData === 'string') { templateData = this._templateData(templateData); } for (const template of templates) { const { templateData: eachData = templateData, source, destination } = template; if (!template.when || template.when(eachData, this)) { this.renderTemplate(source, destination, eachData, { fromBasePath: this.templatePath(), ...template.copyOptions, }); } } } /** * Copy templates from templates folder to the destination. * * @param templates - template file, relative to templatePath(), or absolute inside the source root (outside when the `allowTemplatesOutsideRoot` feature is enabled, inside the generator package when the `allowPackageTemplates` feature is enabled). * @param templateData - ejs data */ async renderTemplatesAsync(templates, templateData) { assert.ok(Array.isArray(templates), 'Templates must an array'); if (templateData === undefined || typeof templateData === 'string') { templateData = this._templateData(templateData); } return Promise.all(templates.map(async (template) => { const { templateData: eachData = templateData, source, destination } = template; if (!template.when || template.when(eachData, this)) { return this.renderTemplateAsync(source, destination, eachData, { fromBasePath: this.templatePath(), ...template.copyOptions, }); } return; })); } /** * Utility method to get a formatted data for templates. * * @param path - path to the storage key. * @return data to be passed to the templates. */ _templateData(path) { if (path) { return this.config.getPath(path); } const allConfig = this.config.getAll(); if (this.generatorConfig) { Object.assign(allConfig, this.generatorConfig.getAll()); } if (this.instanceConfig) { Object.assign(allConfig, this.instanceConfig.getAll()); } return allConfig; } }