| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- import { cancel, intro, isCancel, multiselect, outro, text } from '@clack/prompts';
- import { camelCase, constantCase, paramCase, pascalCase } from 'change-case';
- import * as fs from 'fs-extra';
- import path from 'path';
- import { getCustomEntityName } from '../../../shared/shared-prompts';
- import { renderEntity } from '../../../shared/shared-scaffold/entity';
- import { Scaffolder } from '../../../utilities/scaffolder';
- import { renderAdminResolver, renderAdminResolverWithEntity } from './scaffold/api/admin.resolver';
- import { renderApiExtensions } from './scaffold/api/api-extensions';
- import { renderShopResolver, renderShopResolverWithEntity } from './scaffold/api/shop.resolver';
- import { renderConstants } from './scaffold/constants';
- import { renderPlugin } from './scaffold/plugin';
- import { renderService, renderServiceWithEntity } from './scaffold/services/service';
- import { renderTypes } from './scaffold/types';
- import { GeneratePluginOptions, NewPluginTemplateContext } from './types';
- const cancelledMessage = 'Plugin setup cancelled.';
- export async function newPlugin() {
- const options: GeneratePluginOptions = { name: '', customEntityName: '', pluginDir: '' } as any;
- intro('Scaffolding a new Vendure plugin!');
- if (!options.name) {
- const name = await text({
- message: 'What is the name of the plugin?',
- initialValue: '',
- 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 (isCancel(name)) {
- cancel(cancelledMessage);
- process.exit(0);
- } else {
- options.name = name;
- }
- }
- const features = await multiselect({
- message: 'Which features would you like to include? (use ↑, ↓, space to select)',
- options: [
- { value: 'customEntity', label: 'Custom entity' },
- { value: 'apiExtensions', label: 'GraphQL API extensions' },
- ],
- required: false,
- });
- if (Array.isArray(features)) {
- options.withCustomEntity = features.includes('customEntity');
- options.withApiExtensions = features.includes('apiExtensions');
- }
- if (options.withCustomEntity) {
- options.customEntityName = await getCustomEntityName(cancelledMessage);
- }
- const pluginDir = getPluginDirName(options.name);
- const confirmation = await 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 (isCancel(confirmation)) {
- cancel(cancelledMessage);
- process.exit(0);
- } else {
- options.pluginDir = confirmation;
- generatePlugin(options);
- }
- }
- export function generatePlugin(options: GeneratePluginOptions) {
- const nameWithoutPlugin = options.name.replace(/-?plugin$/i, '');
- const normalizedName = nameWithoutPlugin + '-plugin';
- const templateContext: NewPluginTemplateContext = {
- ...options,
- pluginName: pascalCase(normalizedName),
- pluginInitOptionsName: constantCase(normalizedName) + '_OPTIONS',
- service: {
- className: pascalCase(nameWithoutPlugin) + 'Service',
- instanceName: camelCase(nameWithoutPlugin) + 'Service',
- fileName: paramCase(nameWithoutPlugin) + '.service',
- },
- entity: {
- className: options.customEntityName,
- instanceName: camelCase(options.customEntityName),
- fileName: paramCase(options.customEntityName) + '.entity',
- },
- };
- const scaffolder = new Scaffolder<NewPluginTemplateContext>();
- scaffolder.addFile(renderPlugin, paramCase(nameWithoutPlugin) + '.plugin.ts');
- scaffolder.addFile(renderTypes, 'types.ts');
- scaffolder.addFile(renderConstants, 'constants.ts');
- if (options.withApiExtensions) {
- scaffolder.addFile(renderApiExtensions, 'api/api-extensions.ts');
- if (options.withCustomEntity) {
- scaffolder.addFile(renderShopResolverWithEntity, 'api/shop.resolver.ts');
- scaffolder.addFile(renderAdminResolverWithEntity, 'api/admin.resolver.ts');
- } else {
- scaffolder.addFile(renderShopResolver, 'api/shop.resolver.ts');
- scaffolder.addFile(renderAdminResolver, 'api/admin.resolver.ts');
- }
- }
- if (options.withCustomEntity) {
- scaffolder.addFile(renderEntity, `entities/${templateContext.entity.fileName}.ts`);
- scaffolder.addFile(renderServiceWithEntity, `services/${templateContext.service.fileName}.ts`);
- } else {
- scaffolder.addFile(renderService, `services/${templateContext.service.fileName}.ts`);
- }
- const pluginDir = options.pluginDir;
- scaffolder.createScaffold({
- dir: pluginDir,
- context: templateContext,
- });
- outro('✅ Plugin scaffolding complete!');
- }
- function getPluginDirName(name: string) {
- const cwd = process.cwd();
- const pathParts = cwd.split(path.sep);
- const currentlyInPluginsDir = pathParts[pathParts.length - 1] === 'plugins';
- const currentlyInRootDir = fs.pathExistsSync(path.join(cwd, 'package.json'));
- const nameWithoutPlugin = name.replace(/-?plugin$/i, '');
- if (currentlyInPluginsDir) {
- return path.join(cwd, paramCase(nameWithoutPlugin));
- }
- if (currentlyInRootDir) {
- return path.join(cwd, 'src', 'plugins', paramCase(nameWithoutPlugin));
- }
- return path.join(cwd, paramCase(nameWithoutPlugin));
- }
|