UNPKG

@vendure/cli

Version:

A modern, headless ecommerce framework

260 lines 11.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createNewPluginCommand = void 0; exports.createNewPlugin = createNewPlugin; exports.generatePlugin = generatePlugin; const prompts_1 = require("@clack/prompts"); const change_case_1 = require("change-case"); const fs = __importStar(require("fs-extra")); const path_1 = __importDefault(require("path")); const cli_command_1 = require("../../../shared/cli-command"); const shared_prompts_1 = require("../../../shared/shared-prompts"); const vendure_config_ref_1 = require("../../../shared/vendure-config-ref"); const vendure_plugin_ref_1 = require("../../../shared/vendure-plugin-ref"); const ast_utils_1 = require("../../../utilities/ast-utils"); const utils_1 = require("../../../utilities/utils"); const add_api_extension_1 = require("../api-extension/add-api-extension"); const add_codegen_1 = require("../codegen/add-codegen"); const add_entity_1 = require("../entity/add-entity"); const add_job_queue_1 = require("../job-queue/add-job-queue"); const add_service_1 = require("../service/add-service"); const add_ui_extensions_1 = require("../ui-extensions/add-ui-extensions"); exports.createNewPluginCommand = new cli_command_1.CliCommand({ id: 'create-new-plugin', category: 'Plugin', description: 'Create a new Vendure plugin', run: (options) => createNewPlugin(options), }); const cancelledMessage = 'Plugin setup cancelled.'; async function createNewPlugin(options = {}) { if (options.name !== undefined && (typeof options.name !== 'string' || !options.name.trim())) { throw new Error('Plugin name is required. Usage: vendure add -p <plugin-name>'); } const isNonInteractive = !!options.name; if (!isNonInteractive) { (0, prompts_1.intro)('Adding a new Vendure plugin!'); } const { project, config } = await (0, shared_prompts_1.analyzeProject)({ cancelledMessage, config: options.config }); if (!options.name) { const name = await (0, prompts_1.text)({ message: 'What is the name of the plugin?', initialValue: 'my-new-feature', validate: input => { if (!/^[a-z][a-z-0-9]+$/.test(input)) { return 'The plugin name must be lowercase and contain only letters, numbers and dashes'; } }, }); if ((0, prompts_1.isCancel)(name)) { (0, prompts_1.cancel)(cancelledMessage); process.exit(0); } else { options.name = name; } } const existingPluginDir = findExistingPluginsDir(project); const pluginDir = getPluginDirName(options.name, existingPluginDir); if (isNonInteractive) { options.pluginDir = pluginDir; if (fs.existsSync(options.pluginDir)) { throw new Error(`A directory named "${options.pluginDir}" already exists.`); } } else { const confirmation = await (0, prompts_1.text)({ message: 'Plugin location', initialValue: pluginDir, placeholder: '', validate: input => { if (fs.existsSync(input)) { return `A directory named "${input}" already exists. Please specify a different directory.`; } }, }); if ((0, prompts_1.isCancel)(confirmation)) { (0, prompts_1.cancel)(cancelledMessage); process.exit(0); } options.pluginDir = confirmation; } const { plugin, modifiedSourceFiles } = await generatePlugin(project, options); const configSpinner = (0, prompts_1.spinner)(); configSpinner.start('Updating VendureConfig...'); await (0, utils_1.pauseForPromptDisplay)(); const vendureConfig = new vendure_config_ref_1.VendureConfigRef(project, config); vendureConfig.addToPluginsArray(`${plugin.name}.init({})`); (0, ast_utils_1.addImportsToFile)(vendureConfig.sourceFile, { moduleSpecifier: plugin.getSourceFile(), namedImports: [plugin.name], }); await vendureConfig.sourceFile.getProject().save(); configSpinner.stop('Updated VendureConfig'); if (isNonInteractive) { return { project, modifiedSourceFiles: [], }; } let done = false; const followUpCommands = [ add_entity_1.addEntityCommand, add_service_1.addServiceCommand, add_api_extension_1.addApiExtensionCommand, add_job_queue_1.addJobQueueCommand, add_ui_extensions_1.addUiExtensionsCommand, add_codegen_1.addCodegenCommand, ]; let allModifiedSourceFiles = [...modifiedSourceFiles]; while (!done) { const featureType = await (0, utils_1.withInteractiveTimeout)(async () => { var _a; return await (0, prompts_1.select)({ message: `Add features to ${(_a = options.name) !== null && _a !== void 0 ? _a : 'plugin'}?`, options: [ { value: 'no', label: "[Finish] No, I'm done!" }, ...followUpCommands.map(c => ({ value: c.id, label: `[${c.category}] ${c.description}`, })), ], }); }); if ((0, prompts_1.isCancel)(featureType)) { done = true; } if (featureType === 'no') { done = true; } else { const command = followUpCommands.find(c => c.id === featureType); try { const result = await command.run({ plugin }); allModifiedSourceFiles = result.modifiedSourceFiles; for (const sourceFile of allModifiedSourceFiles) { sourceFile.organizeImports(); } } catch (e) { prompts_1.log.error(`Error adding feature "${command.id}"`); prompts_1.log.error(e.stack); } } } return { project, modifiedSourceFiles: [], }; } async function generatePlugin(project, options) { var _a, _b, _c, _d; const nameWithoutPlugin = options.name.replace(/-?plugin$/i, ''); const normalizedName = nameWithoutPlugin + '-plugin'; const templateContext = Object.assign(Object.assign({}, options), { pluginName: (0, change_case_1.pascalCase)(normalizedName), pluginInitOptionsName: (0, change_case_1.constantCase)(normalizedName) + '_OPTIONS' }); const projectSpinner = (0, prompts_1.spinner)(); projectSpinner.start('Generating plugin scaffold...'); await (0, utils_1.pauseForPromptDisplay)(); const pluginFile = (0, ast_utils_1.createFile)(project, path_1.default.join(__dirname, 'templates/plugin.template.ts'), path_1.default.join(options.pluginDir, (0, change_case_1.paramCase)(nameWithoutPlugin) + '.plugin.ts')); const pluginClass = pluginFile.getClass('TemplatePlugin'); if (!pluginClass) { throw new Error('Could not find the plugin class in the generated file'); } (_a = pluginFile.getImportDeclaration('./constants.template')) === null || _a === void 0 ? void 0 : _a.setModuleSpecifier('./constants'); (_b = pluginFile.getImportDeclaration('./types.template')) === null || _b === void 0 ? void 0 : _b.setModuleSpecifier('./types'); pluginClass.rename(templateContext.pluginName); const typesFile = (0, ast_utils_1.createFile)(project, path_1.default.join(__dirname, 'templates/types.template.ts'), path_1.default.join(options.pluginDir, 'types.ts')); const constantsFile = (0, ast_utils_1.createFile)(project, path_1.default.join(__dirname, 'templates/constants.template.ts'), path_1.default.join(options.pluginDir, 'constants.ts')); (_c = constantsFile .getVariableDeclaration('TEMPLATE_PLUGIN_OPTIONS')) === null || _c === void 0 ? void 0 : _c.rename(templateContext.pluginInitOptionsName).set({ initializer: `Symbol('${templateContext.pluginInitOptionsName}')` }); (_d = constantsFile .getVariableDeclaration('loggerCtx')) === null || _d === void 0 ? void 0 : _d.set({ initializer: `'${templateContext.pluginName}'` }); projectSpinner.stop('Generated plugin scaffold'); await project.save(); return { modifiedSourceFiles: [pluginFile, typesFile, constantsFile], plugin: new vendure_plugin_ref_1.VendurePluginRef(pluginClass), }; } function findExistingPluginsDir(project) { const pluginClasses = (0, ast_utils_1.getPluginClasses)(project); if (pluginClasses.length === 0) { return; } if (pluginClasses.length === 1) { return { prefix: path_1.default.dirname(pluginClasses[0].getSourceFile().getDirectoryPath()), suffix: '' }; } const pluginDirs = pluginClasses.map(c => { return c.getSourceFile().getDirectoryPath(); }); const prefix = findCommonPath(pluginDirs); const suffixStartIndex = prefix.length; const rest = pluginDirs[0].substring(suffixStartIndex).replace(/^\//, '').split('/'); const suffix = rest.length > 1 ? rest.slice(1).join('/') : ''; return { prefix, suffix }; } function getPluginDirName(name, existingPluginDirPattern) { const cwd = process.cwd(); const nameWithoutPlugin = name.replace(/-?plugin$/i, ''); if (existingPluginDirPattern) { return path_1.default.join(existingPluginDirPattern.prefix, (0, change_case_1.paramCase)(nameWithoutPlugin), existingPluginDirPattern.suffix); } else { return path_1.default.join(cwd, 'src', 'plugins', (0, change_case_1.paramCase)(nameWithoutPlugin)); } } function findCommonPath(paths) { if (paths.length === 0) { return ''; } const pathSegmentsList = paths.map(p => p.split('/')); const minLength = Math.min(...pathSegmentsList.map(segments => segments.length)); const commonPath = []; for (let i = 0; i < minLength; i++) { const currentSegment = pathSegmentsList[0][i]; const isCommon = pathSegmentsList.every(segments => segments[i] === currentSegment); if (isCommon) { commonPath.push(currentSegment); } else { break; } } return commonPath.join('/'); } //# sourceMappingURL=create-new-plugin.js.map