UNPKG

@lenne.tech/cli

Version:

lenne.Tech CLI: lt

256 lines (255 loc) 11.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const path_1 = require("path"); const framework_detection_1 = require("../../lib/framework-detection"); const module_1 = __importDefault(require("./module")); /** * Create a new server object */ const NewCommand = { alias: ['o'], description: 'Create embedded object', hidden: false, name: 'object', run: (toolbox, options) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b; // Options: const { currentItem, objectsToAdd, preventExitProcess, referencesToAdd } = Object.assign({ currentItem: '', objectsToAdd: [], preventExitProcess: false, referencesToAdd: [] }, options); // Retrieve the tools we need const { config, filesystem, helper, parameters, print: { divider, error, info, spin, success }, prompt: { confirm }, server, strings: { camelCase, kebabCase, pascalCase }, system, template, } = toolbox; // Handle --help-json flag if (toolbox.tools.helpJson({ aliases: ['o'], configuration: 'commands.server.object.*', description: 'Create embedded object', name: 'object', options: [ { description: 'Object name', flag: '--name', required: true, type: 'string' }, { default: false, description: 'Skip lint fix after generation', flag: '--skipLint', required: false, type: 'boolean', }, { default: false, description: 'Preview what would be generated without creating files', flag: '--dryRun', required: false, type: 'boolean', }, ], propertyFlags: { attributes: [ { description: 'Property name', name: 'name', type: 'string' }, { description: 'Property type', name: 'type', type: 'string', values: ['string', 'number', 'boolean', 'bigint', 'Date', 'ObjectId', 'Json'], }, { description: 'Optional field', name: 'nullable', type: 'boolean' }, { description: 'Array of this type', name: 'array', type: 'boolean' }, { description: 'Enum type reference', name: 'enum', type: 'string' }, { description: 'Embedded object/schema reference', name: 'schema', type: 'string' }, { description: 'Reference module for ObjectId fields', name: 'reference', type: 'string' }, ], pattern: '--prop-<attribute>-<index>', }, })) { return; } // Start timer const timer = system.startTimer(); // Info if (currentItem) { info(`Create a new server object (with inputs) for ${currentItem}`); } else { info('Create a new server object (with inputs)'); } // Load configuration const ltConfig = config.loadConfig(); // Parse CLI arguments const { name: cliName, skipLint: cliSkipLint } = parameters.options; // Parse dry-run flag early const dryRun = parameters.options.dryRun || parameters.options['dry-run']; // Get name let name = cliName || currentItem || parameters.first; if (!name) { name = yield helper.getInput(currentItem || parameters.first, { initial: currentItem || '', name: 'object name', }); } if (!name) { return; } // Set up initial props (to pass into templates) const nameCamel = camelCase(name); const nameKebab = kebabCase(name); const namePascal = pascalCase(name); // Check if directory const cwd = filesystem.cwd(); const path = cwd.substr(0, cwd.lastIndexOf('src')); if (!filesystem.exists((0, path_1.join)(path, 'src'))) { info(''); error(`No src directory in "${path}".`); return; } const directory = (0, path_1.join)(path, 'src', 'server', 'common', 'objects', nameKebab); if (filesystem.exists(directory)) { info(''); error(`Module directory "${directory}" already exists.`); return; } // Dry-run mode: show what would happen and exit if (dryRun) { info(''); info(`Dry run: lt server object --name ${name}`); info(''); info('Files that would be created:'); info(` src/server/common/objects/${nameKebab}/${nameKebab}.object.ts`); info(` src/server/common/objects/${nameKebab}/${nameKebab}.input.ts`); info(` src/server/common/objects/${nameKebab}/${nameKebab}-create.input.ts`); info(''); return `dry-run object ${name}`; } // Parse properties from CLI or interactive mode const { props, refsSet, schemaSet } = yield toolbox.parseProperties({ objectsToAdd, referencesToAdd }); const generateSpinner = spin('Generate files'); const inputTemplate = server.propsForInput(props, { modelName: name, nullable: true }); const createTemplate = server.propsForInput(props, { create: true, modelName: name, nullable: false }); const objectTemplate = server.propsForModel(props, { modelName: name }); // Framework-import specifier (bare in npm mode, relative in vendored mode) const importFor = (target) => (0, framework_detection_1.getFrameworkImportSpecifier)(path, target); // nest-server-module/inputs/xxx.input.ts const inputTarget = (0, path_1.join)(directory, `${nameKebab}.input.ts`); yield template.generate({ props: { frameworkImport: importFor(inputTarget), imports: inputTemplate.imports, nameCamel, nameKebab, namePascal, props: inputTemplate.props, }, target: inputTarget, template: 'nest-server-object/template.input.ts.ejs', }); // nest-server-object/inputs/xxx-create.input.ts const createInputTarget = (0, path_1.join)(directory, `${nameKebab}-create.input.ts`); yield template.generate({ props: { frameworkImport: importFor(createInputTarget), imports: createTemplate.imports, nameCamel, nameKebab, namePascal, props: createTemplate.props, }, target: createInputTarget, template: 'nest-server-object/template-create.input.ts.ejs', }); // nest-server-module/xxx.model.ts const objectTarget = (0, path_1.join)(directory, `${nameKebab}.object.ts`); yield template.generate({ props: { frameworkImport: importFor(objectTarget), imports: objectTemplate.imports, mappings: objectTemplate.mappings, nameCamel, nameKebab, namePascal, props: objectTemplate.props, }, target: objectTarget, template: 'nest-server-object/template.object.ts.ejs', }); generateSpinner.succeed('Files generated'); // Print structured summary const summaryLines = []; summaryLines.push('--- Summary ---'); summaryLines.push(`Object: ${namePascal}`); summaryLines.push(`Location: src/server/common/objects/${nameKebab}/`); summaryLines.push(''); summaryLines.push('Created files:'); summaryLines.push(` + ${nameKebab}.object.ts`); summaryLines.push(` + ${nameKebab}.input.ts`); summaryLines.push(` + ${nameKebab}-create.input.ts`); summaryLines.push(''); const propKeys = Object.keys(props); if (propKeys.length > 0) { summaryLines.push('Properties:'); for (const key of propKeys) { const p = props[key]; const parts = []; if (p.isArray) parts.push('array'); if (p.nullable) parts.push('nullable'); const suffix = parts.length > 0 ? ` (${parts.join(', ')})` : ''; summaryLines.push(` - ${p.name}${p.nullable ? '?' : ''}: ${p.type}${p.isArray ? '[]' : ''}${suffix}`); } summaryLines.push(''); } summaryLines.push('Next steps:'); summaryLines.push(' 1. Add descriptions to @UnifiedField decorators'); summaryLines.push(' 2. Customize securityCheck() in object'); summaryLines.push('---'); info(summaryLines.join('\n')); info(''); // Lint fix with priority: CLI > config > global > default (false) const skipLint = config.getSkipLint({ cliValue: cliSkipLint, commandConfig: (_b = (_a = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _a === void 0 ? void 0 : _a.server) === null || _b === void 0 ? void 0 : _b.object, config: ltConfig, }); if (!skipLint) { if (yield confirm('Run lint fix?', true)) { yield system.run(toolbox.pm.run('lint:fix')); } } // We're done, so show what to do next info(''); success(`Generated ${namePascal}Object in ${helper.msToMinutesAndSeconds(timer())}m.`); info(''); // Add additional references if (referencesToAdd.length > 0) { divider(); const nextRef = referencesToAdd.shift().reference; yield module_1.default.run(toolbox, { currentItem: nextRef, objectsToAdd, preventExitProcess: true, referencesToAdd }); } // Add additional objects if (objectsToAdd.length > 0) { divider(); const nextObj = objectsToAdd.shift().object; yield NewCommand.run(toolbox, { currentItem: nextObj, objectsToAdd, preventExitProcess: true, referencesToAdd }); } // We're done, so show what to do next if (!preventExitProcess) { if (refsSet || schemaSet) { success('HINT: References / Schemata have been added, so it is necessary to add the corresponding imports!'); } if (!toolbox.parameters.options.fromGluegunMenu) { process.exit(); } } // For tests return `new object ${name}`; }), }; exports.default = NewCommand;