From 33d7e7bda161f14ab31f410c9f57f9d9f9e9149e Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 8 Aug 2024 16:52:10 +0200 Subject: [PATCH 1/5] feat: in strapi project create plugin in plugin path --- src/cli/commands/plugin/init/action.ts | 763 +++++++++++++------------ src/cli/commands/utils/helpers.ts | 45 +- 2 files changed, 444 insertions(+), 364 deletions(-) diff --git a/src/cli/commands/plugin/init/action.ts b/src/cli/commands/plugin/init/action.ts index 9fba1e4..6d76434 100644 --- a/src/cli/commands/plugin/init/action.ts +++ b/src/cli/commands/plugin/init/action.ts @@ -6,6 +6,8 @@ import gitUrlParse from 'git-url-parse'; import path from 'node:path'; import { outdent } from 'outdent'; +import { dirContainsStrapiProject, logInstructions } from '../../utils/helpers'; + import { gitIgnoreFile } from './files/gitIgnore'; import type { CLIContext } from '../../../../types'; @@ -21,24 +23,49 @@ const USE_RC_VERSIONS: string[] = [ '@strapi/typescript-utils', ] as const; +// Store results of prompt answers (run by pack-up init) +// This is a limitation of pack-up; we cannot run the prompt and pass the answers in +let promptAnswers: any = []; + export default async ( packagePath: string, { silent, debug }: ActionOptions, { logger, cwd }: CLIContext ) => { try { + const isStrapi = dirContainsStrapiProject(cwd); + + // If the user entered a path, we will try to parse the plugin name from it so we can provide it as a suggestion for consistency + const parsedPath = path.parse(packagePath); + const suggestedPackageName = parsedPath.base; + const isPathPackageName = !packagePath.includes('/'); + const pluginPath = isStrapi && isPathPackageName ? `./src/plugins/${packagePath}` : packagePath; + + // + const template = getPluginTemplate({ suggestedPackageName }); + /** * Create the package // plugin */ await init({ - path: packagePath, + path: pluginPath, cwd, silent, debug, - template: PLUGIN_TEMPLATE, + template, }); - logger.info("Don't forget to enable your plugin in your configuration files."); + if (isStrapi) { + const pkgName = promptAnswers.find((option: any) => option?.name === 'pkgName')?.answer; + + const language = promptAnswers.find((option: any) => option?.name === 'typescript')?.answer + ? 'ts' + : 'js'; + + logger.info(logInstructions(pkgName, { language, path: pluginPath })); + } + + logger.info('Plugin generated successfully.'); } catch (err) { logger.error( 'There seems to be an unexpected error, try again with --debug for more information \n' @@ -101,436 +128,448 @@ interface PluginPackageJson { }; } -const PLUGIN_TEMPLATE = defineTemplate(async ({ logger, gitConfig, packagePath }) => { - let repo: { - source?: string; - owner?: string; - name?: string; - }; - - const [packageFolder] = packagePath.split(path.sep).slice(-1); - - if (!packagePath?.length || !packageFolder) { - throw new Error('Missing package path'); - } - - return { - prompts: [ - definePackageOption({ - name: 'repo', - type: 'text', - message: 'git url', - validate(val: unknown) { - if (!val) { - return true; - } +type PluginTemplateOptions = { + suggestedPackageName?: string; +}; - try { - const result = gitUrlParse(val as any); +const getPluginTemplate = ({ suggestedPackageName }: PluginTemplateOptions) => { + return defineTemplate(async ({ logger, gitConfig, packagePath }) => { + let repo: { + source?: string; + owner?: string; + name?: string; + }; - repo = { source: result.source, owner: result.owner, name: result.name }; + const [packageFolder] = packagePath.split(path.sep).slice(-1); - return true; - } catch (err) { - return 'invalid git url'; - } - }, - }), - definePackageOption({ - name: 'pkgName', - type: 'text', - message: 'plugin name', - initial: () => repo?.name ?? '', - validate(val: unknown) { - if (!val || typeof val !== 'string') { - return 'package name is required'; - } + if (!packagePath?.length || !packageFolder) { + throw new Error('Missing package path'); + } - const match = PACKAGE_NAME_REGEXP.exec(val); + return { + prompts: [ + definePackageOption({ + name: 'repo', + type: 'text', + message: 'git url', + validate(val: unknown) { + if (!val) { + return true; + } - if (!match) { - return 'invalid package name'; - } + try { + const result = gitUrlParse(val as any); - return true; - }, - }), - definePackageOption({ - name: 'displayName', - type: 'text', - message: 'plugin display name', - }), - definePackageOption({ - name: 'description', - type: 'text', - message: 'plugin description', - }), - definePackageOption({ - name: 'authorName', - type: 'text', - message: 'plugin author name', - initial: gitConfig?.user?.name, - }), - definePackageOption({ - name: 'authorEmail', - type: 'text', - message: 'plugin author email', - initial: gitConfig?.user?.email, - }), - definePackageOption({ - name: 'license', - type: 'text', - message: 'plugin license', - initial: 'MIT', - validate(v) { - if (!v) { - return 'license is required'; - } + repo = { source: result.source, owner: result.owner, name: result.name }; - return true; - }, - }), - definePackageOption({ - name: 'client-code', - type: 'confirm', - message: 'register with the admin panel?', - initial: true, - }), - definePackageOption({ - name: 'server-code', - type: 'confirm', - message: 'register with the server?', - initial: true, - }), - definePackageFeature({ - name: 'editorconfig', - initial: true, - optional: true, - }), - definePackageFeature({ - name: 'eslint', - initial: true, - optional: true, - }), - definePackageFeature({ - name: 'prettier', - initial: true, - optional: true, - }), - definePackageFeature({ - name: 'typescript', - initial: true, - optional: true, - }), - ], - async getFiles(answers) { - const author: string[] = []; - - const files: TemplateFile[] = []; - - // package.json - const pkgJson: PluginPackageJson = { - version: '0.0.0', - keywords: [], - type: 'commonjs', - exports: { - './package.json': './package.json', - }, - files: ['dist'], - scripts: { - build: 'strapi-plugin build', - watch: 'strapi-plugin watch', - 'watch:link': 'strapi-plugin watch:link', - verify: 'strapi-plugin verify', - }, - dependencies: {}, - devDependencies: { - /** - * We set * as a default version, but further down - * we try to resolve each package to their latest - * version, failing that we leave the fallback of *. - */ - '@strapi/strapi': '*', - '@strapi/sdk-plugin': '*', - prettier: '*', - }, - peerDependencies: { - // TODO: set this to 5.0.0 when Strapi 5 is released - '@strapi/strapi': '^5.0.0-beta', - '@strapi/sdk-plugin': '^5.0.0', - }, - strapi: { - kind: 'plugin', - }, - }; - - if (Array.isArray(answers)) { - for (const ans of answers) { - const { name, answer } = ans; - - switch (name) { - case 'pkgName': { - pkgJson.name = String(answer); - pkgJson.strapi.name = String(answer); - break; + return true; + } catch (err) { + return 'invalid git url'; } - case 'description': { - pkgJson.description = String(answer) ?? undefined; - pkgJson.strapi.description = String(answer) ?? undefined; - break; + }, + }), + definePackageOption({ + name: 'pkgName', + type: 'text', + message: 'plugin name', + initial: () => suggestedPackageName ?? repo?.name ?? '', + validate(val: unknown) { + if (!val || typeof val !== 'string') { + return 'package name is required'; } - case 'displayName': { - pkgJson.strapi.displayName = String(answer) ?? undefined; - break; - } - case 'authorName': { - author.push(String(answer)); - break; - } - case 'authorEmail': { - if (answer) { - author.push(`<${answer}>`); - } - break; - } - case 'license': { - pkgJson.license = String(answer); - break; - } - case 'client-code': { - if (answer) { - pkgJson.exports['./strapi-admin'] = { - source: './admin/src/index.js', - import: './dist/admin/index.mjs', - require: './dist/admin/index.js', - default: './dist/admin/index.js', - }; - - pkgJson.dependencies = { - ...pkgJson.dependencies, - '@strapi/design-system': '*', - '@strapi/icons': '*', - 'react-intl': '*', - }; - - pkgJson.devDependencies = { - ...pkgJson.devDependencies, - react: '*', - 'react-dom': '*', - 'react-router-dom': '*', - 'styled-components': '*', - }; - - pkgJson.peerDependencies = { - ...pkgJson.peerDependencies, - react: '^17.0.0 || ^18.0.0', - 'react-dom': '^17.0.0 || ^18.0.0', - 'react-router-dom': '^6.0.0', - 'styled-components': '^6.0.0', - }; - } - break; + const match = PACKAGE_NAME_REGEXP.exec(val); + + if (!match) { + return 'invalid package name'; } - case 'server-code': { - if (answer) { - pkgJson.exports['./strapi-server'] = { - source: './server/src/index.js', - import: './dist/server/index.mjs', - require: './dist/server/index.js', - default: './dist/server/index.js', - }; - - pkgJson.files.push('./strapi-server.js'); - - files.push({ - name: 'strapi-server.js', - contents: outdent` - 'use strict'; - - module.exports = require('./dist/server'); - `, - }); - } - break; + return true; + }, + }), + definePackageOption({ + name: 'displayName', + type: 'text', + message: 'plugin display name', + }), + definePackageOption({ + name: 'description', + type: 'text', + message: 'plugin description', + }), + definePackageOption({ + name: 'authorName', + type: 'text', + message: 'plugin author name', + initial: gitConfig?.user?.name, + }), + definePackageOption({ + name: 'authorEmail', + type: 'text', + message: 'plugin author email', + initial: gitConfig?.user?.email, + }), + definePackageOption({ + name: 'license', + type: 'text', + message: 'plugin license', + initial: 'MIT', + validate(v) { + if (!v) { + return 'license is required'; } - case 'typescript': { - const isTypescript = Boolean(answer); - if (isTypescript) { - if (isRecord(pkgJson.exports['./strapi-admin'])) { - pkgJson.exports['./strapi-admin'].source = './admin/src/index.ts'; + return true; + }, + }), + definePackageOption({ + name: 'client-code', + type: 'confirm', + message: 'register with the admin panel?', + initial: true, + }), + definePackageOption({ + name: 'server-code', + type: 'confirm', + message: 'register with the server?', + initial: true, + }), + definePackageFeature({ + name: 'editorconfig', + initial: true, + optional: true, + }), + definePackageFeature({ + name: 'eslint', + initial: true, + optional: true, + }), + definePackageFeature({ + name: 'prettier', + initial: true, + optional: true, + }), + definePackageFeature({ + name: 'typescript', + initial: true, + optional: true, + }), + ], + async getFiles(answers) { + const author: string[] = []; + + const files: TemplateFile[] = []; + + // package.json + const pkgJson: PluginPackageJson = { + version: '0.0.0', + keywords: [], + type: 'commonjs', + exports: { + './package.json': './package.json', + }, + files: ['dist'], + scripts: { + build: 'strapi-plugin build', + watch: 'strapi-plugin watch', + 'watch:link': 'strapi-plugin watch:link', + verify: 'strapi-plugin verify', + }, + dependencies: {}, + devDependencies: { + /** + * We set * as a default version, but further down + * we try to resolve each package to their latest + * version, failing that we leave the fallback of *. + */ + '@strapi/strapi': '*', + '@strapi/sdk-plugin': '*', + prettier: '*', + }, + peerDependencies: { + // TODO: set this to 5.0.0 when Strapi 5 is released + '@strapi/strapi': '^5.0.0-beta', + '@strapi/sdk-plugin': '^5.0.0', + }, + strapi: { + kind: 'plugin', + }, + }; + + if (Array.isArray(answers)) { + for (const ans of answers) { + const { name, answer } = ans; + switch (name) { + case 'pkgName': { + pkgJson.name = String(answer); + pkgJson.strapi.name = String(answer); + break; + } + case 'description': { + pkgJson.description = String(answer) ?? undefined; + pkgJson.strapi.description = String(answer) ?? undefined; + break; + } + case 'displayName': { + pkgJson.strapi.displayName = String(answer) ?? undefined; + break; + } + case 'authorName': { + author.push(String(answer)); + break; + } + case 'authorEmail': { + if (answer) { + author.push(`<${answer}>`); + } + break; + } + case 'license': { + pkgJson.license = String(answer); + break; + } + case 'client-code': { + if (answer) { pkgJson.exports['./strapi-admin'] = { - types: './dist/admin/src/index.d.ts', - ...pkgJson.exports['./strapi-admin'], + source: './admin/src/index.js', + import: './dist/admin/index.mjs', + require: './dist/admin/index.js', + default: './dist/admin/index.js', }; - pkgJson.scripts = { - ...pkgJson.scripts, - 'test:ts:front': 'run -T tsc -p admin/tsconfig.json', + pkgJson.dependencies = { + ...pkgJson.dependencies, + '@strapi/design-system': '*', + '@strapi/icons': '*', + 'react-intl': '*', }; pkgJson.devDependencies = { ...pkgJson.devDependencies, - '@types/react': '*', - '@types/react-dom': '*', + react: '*', + 'react-dom': '*', + 'react-router-dom': '*', + 'styled-components': '*', }; - const { adminTsconfigFiles } = await import('./files/typescript'); - - files.push(adminTsconfigFiles.tsconfigBuildFile, adminTsconfigFiles.tsconfigFile); + pkgJson.peerDependencies = { + ...pkgJson.peerDependencies, + react: '^17.0.0 || ^18.0.0', + 'react-dom': '^17.0.0 || ^18.0.0', + 'react-router-dom': '^6.0.0', + 'styled-components': '^6.0.0', + }; } - if (isRecord(pkgJson.exports['./strapi-server'])) { - pkgJson.exports['./strapi-server'].source = './server/src/index.ts'; - + break; + } + case 'server-code': { + if (answer) { pkgJson.exports['./strapi-server'] = { - types: './dist/server/src/index.d.ts', - ...pkgJson.exports['./strapi-server'], - }; - - pkgJson.scripts = { - ...pkgJson.scripts, - 'test:ts:back': 'run -T tsc -p server/tsconfig.json', + source: './server/src/index.js', + import: './dist/server/index.mjs', + require: './dist/server/index.js', + default: './dist/server/index.js', }; - const { serverTsconfigFiles } = await import('./files/typescript'); + pkgJson.files.push('./strapi-server.js'); - files.push( - serverTsconfigFiles.tsconfigBuildFile, - serverTsconfigFiles.tsconfigFile - ); + files.push({ + name: 'strapi-server.js', + contents: outdent` + 'use strict'; + + module.exports = require('./dist/server'); + `, + }); } - pkgJson.devDependencies = { - ...pkgJson.devDependencies, - '@strapi/typescript-utils': '*', - typescript: '*', - }; + break; } + case 'typescript': { + const isTypescript = Boolean(answer); + + if (isTypescript) { + if (isRecord(pkgJson.exports['./strapi-admin'])) { + pkgJson.exports['./strapi-admin'].source = './admin/src/index.ts'; + + pkgJson.exports['./strapi-admin'] = { + types: './dist/admin/src/index.d.ts', + ...pkgJson.exports['./strapi-admin'], + }; + + pkgJson.scripts = { + ...pkgJson.scripts, + 'test:ts:front': 'run -T tsc -p admin/tsconfig.json', + }; + + pkgJson.devDependencies = { + ...pkgJson.devDependencies, + '@types/react': '*', + '@types/react-dom': '*', + }; + + const { adminTsconfigFiles } = await import('./files/typescript'); + + files.push( + adminTsconfigFiles.tsconfigBuildFile, + adminTsconfigFiles.tsconfigFile + ); + } + + if (isRecord(pkgJson.exports['./strapi-server'])) { + pkgJson.exports['./strapi-server'].source = './server/src/index.ts'; + + pkgJson.exports['./strapi-server'] = { + types: './dist/server/src/index.d.ts', + ...pkgJson.exports['./strapi-server'], + }; + + pkgJson.scripts = { + ...pkgJson.scripts, + 'test:ts:back': 'run -T tsc -p server/tsconfig.json', + }; + + const { serverTsconfigFiles } = await import('./files/typescript'); + + files.push( + serverTsconfigFiles.tsconfigBuildFile, + serverTsconfigFiles.tsconfigFile + ); + } + + pkgJson.devDependencies = { + ...pkgJson.devDependencies, + '@strapi/typescript-utils': '*', + typescript: '*', + }; + } - /** - * This is where we add all the source files regardless - * of whether they are typescript or javascript. - */ - if (isRecord(pkgJson.exports['./strapi-admin'])) { - files.push({ - name: isTypescript ? 'admin/src/pluginId.ts' : 'admin/src/pluginId.js', - contents: outdent` + /** + * This is where we add all the source files regardless + * of whether they are typescript or javascript. + */ + if (isRecord(pkgJson.exports['./strapi-admin'])) { + files.push({ + name: isTypescript ? 'admin/src/pluginId.ts' : 'admin/src/pluginId.js', + contents: outdent` export const PLUGIN_ID = '${pkgJson.name!.replace(/^strapi-plugin-/i, '')}'; `, - }); + }); - if (isTypescript) { - const { adminTypescriptFiles } = await import('./files/admin'); + if (isTypescript) { + const { adminTypescriptFiles } = await import('./files/admin'); - files.push(...adminTypescriptFiles); - } else { - const { adminJavascriptFiles } = await import('./files/admin'); + files.push(...adminTypescriptFiles); + } else { + const { adminJavascriptFiles } = await import('./files/admin'); - files.push(...adminJavascriptFiles); + files.push(...adminJavascriptFiles); + } } - } - if (isRecord(pkgJson.exports['./strapi-server'])) { - if (isTypescript) { - const { serverTypescriptFiles } = await import('./files/server'); + if (isRecord(pkgJson.exports['./strapi-server'])) { + if (isTypescript) { + const { serverTypescriptFiles } = await import('./files/server'); - files.push(...serverTypescriptFiles(packageFolder)); - } else { - const { serverJavascriptFiles } = await import('./files/server'); + files.push(...serverTypescriptFiles(packageFolder)); + } else { + const { serverJavascriptFiles } = await import('./files/server'); - files.push(...serverJavascriptFiles(packageFolder)); + files.push(...serverJavascriptFiles(packageFolder)); + } } + + break; } + case 'eslint': { + if (answer) { + const { eslintIgnoreFile } = await import('./files/eslint'); - break; - } - case 'eslint': { - if (answer) { - const { eslintIgnoreFile } = await import('./files/eslint'); + files.push(eslintIgnoreFile); + } - files.push(eslintIgnoreFile); + break; } + case 'prettier': { + if (answer) { + const { prettierFile, prettierIgnoreFile } = await import('./files/prettier'); - break; - } - case 'prettier': { - if (answer) { - const { prettierFile, prettierIgnoreFile } = await import('./files/prettier'); - - files.push(prettierFile, prettierIgnoreFile); + files.push(prettierFile, prettierIgnoreFile); + } + break; } - break; - } - case 'editorconfig': { - if (answer) { - const { editorConfigFile } = await import('./files/editorConfig'); + case 'editorconfig': { + if (answer) { + const { editorConfigFile } = await import('./files/editorConfig'); - files.push(editorConfigFile); + files.push(editorConfigFile); + } + break; } - break; + default: + break; } - default: - break; } } - } - if (repo) { - pkgJson.repository = { - type: 'git', - url: `git+ssh://git@${repo.source}/${repo.owner}/${repo.name}.git`, - }; - pkgJson.bugs = { - url: `https://${repo.source}/${repo.owner}/${repo.name}/issues`, - }; - pkgJson.homepage = `https://${repo.source}/${repo.owner}/${repo.name}#readme`; - } - - pkgJson.author = author.filter(Boolean).join(' ') ?? undefined; - - try { - pkgJson.devDependencies = await resolveLatestVerisonOfDeps(pkgJson.devDependencies); - pkgJson.dependencies = await resolveLatestVerisonOfDeps(pkgJson.dependencies); - pkgJson.peerDependencies = await resolveLatestVerisonOfDeps(pkgJson.peerDependencies); - } catch (err) { - if (err instanceof Error) { - logger.error(err.message); - } else { - logger.error(err); + if (repo) { + pkgJson.repository = { + type: 'git', + url: `git+ssh://git@${repo.source}/${repo.owner}/${repo.name}.git`, + }; + pkgJson.bugs = { + url: `https://${repo.source}/${repo.owner}/${repo.name}/issues`, + }; + pkgJson.homepage = `https://${repo.source}/${repo.owner}/${repo.name}#readme`; } - } - files.push({ - name: 'package.json', - contents: outdent` + pkgJson.author = author.filter(Boolean).join(' ') ?? undefined; + + try { + pkgJson.devDependencies = await resolveLatestVersionOfDeps(pkgJson.devDependencies); + pkgJson.dependencies = await resolveLatestVersionOfDeps(pkgJson.dependencies); + pkgJson.peerDependencies = await resolveLatestVersionOfDeps(pkgJson.peerDependencies); + } catch (err) { + if (err instanceof Error) { + logger.error(err.message); + } else { + logger.error(err); + } + } + + files.push({ + name: 'package.json', + contents: outdent` ${JSON.stringify(pkgJson, null, 2)} `, - }); + }); - files.push({ - name: 'README.md', - contents: outdent` + files.push({ + name: 'README.md', + contents: outdent` # ${pkgJson.name} ${pkgJson.description ?? ''} `, - }); + }); - files.push(gitIgnoreFile); + files.push(gitIgnoreFile); - return files; - }, - }; -}); + // Save prompt answers so we have access to them after init + promptAnswers = answers; + + return files; + }, + }; + }); +}; const isRecord = (value: unknown): value is Record => Boolean(value) && !Array.isArray(value) && typeof value === 'object'; -const resolveLatestVerisonOfDeps = async ( +const resolveLatestVersionOfDeps = async ( deps: Record ): Promise> => { const latestDeps: Record = {}; diff --git a/src/cli/commands/utils/helpers.ts b/src/cli/commands/utils/helpers.ts index 8138884..4a4704b 100644 --- a/src/cli/commands/utils/helpers.ts +++ b/src/cli/commands/utils/helpers.ts @@ -1,6 +1,10 @@ +import chalk from 'chalk'; +import fs from 'fs'; +import path from 'path'; + import type { CLIContext } from '../../../types'; -const runAction = +export const runAction = (name: string, action: (...args: any[]) => Promise) => (ctx: CLIContext, ...args: unknown[]) => { const { logger } = ctx; @@ -14,4 +18,41 @@ const runAction = }); }; -export { runAction }; +export const dirContainsStrapiProject = (dir: string) => { + try { + const packageJsonPath = path.join(dir, 'package.json'); + const pkgJSON = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + return Boolean( + (pkgJSON.dependencies && pkgJSON.dependencies['@strapi/strapi']) || + (pkgJSON.devDependencies && pkgJSON.devDependencies['@strapi/strapi']) + ); + } catch (err) { + return false; + } +}; + +export const logInstructions = ( + pluginName: string, + { language, path: pluginPath }: { language: string; path?: string } +) => { + const maxLength = ` resolve: './src/plugins/${pluginName}'`.length; + const separator = Array(maxLength).fill('─').join(''); + + const exportInstruction = language === 'js' ? 'module.exports =' : 'export default'; + + return ` +You can now enable your plugin by adding the following in ${chalk.yellow( + `./config/plugins.${language}` + )} +${separator} +${exportInstruction} { + ${chalk.gray('// ...')} + ${chalk.green(`'${pluginName}'`)}: { + enabled: ${chalk.yellow(true)}, + resolve: ${chalk.yellow(pluginPath || `'./src/plugins/${pluginName}'`)} + }, + ${chalk.gray('// ...')} +} +${separator} +`; +}; From ddd9df574dea30c8e58f6122e557ad556a0d95c1 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 8 Aug 2024 16:54:07 +0200 Subject: [PATCH 2/5] chore: changeset --- .changeset/wet-files-repeat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wet-files-repeat.md diff --git a/.changeset/wet-files-repeat.md b/.changeset/wet-files-repeat.md new file mode 100644 index 0000000..a48366c --- /dev/null +++ b/.changeset/wet-files-repeat.md @@ -0,0 +1,5 @@ +--- +'@strapi/sdk-plugin': minor +--- + +In Strapi projects, generate plugin in plugins path and log config info From 2e1ca56d4a4e9262bf909d7a2da55461663b291b Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 8 Aug 2024 17:01:49 +0200 Subject: [PATCH 3/5] enhancement: make path required --- src/cli/commands/plugin/init/command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/commands/plugin/init/command.ts b/src/cli/commands/plugin/init/command.ts index b048500..725bf36 100644 --- a/src/cli/commands/plugin/init/command.ts +++ b/src/cli/commands/plugin/init/command.ts @@ -9,7 +9,7 @@ const command: StrapiCommand = ({ command: commanderCommand, ctx }) => { commanderCommand .command('init') .description('Create a new plugin at a given path') - .argument('[path]', 'path to the plugin', './src/plugins/my-plugin') + .argument('path', 'path to the plugin') .option('-d, --debug', 'Enable debugging mode with verbose logs', false) .option('--silent', "Don't log anything", false) .action((path, options) => { From 40b3f5039c1bda8ed29ba3fd1cd05a325f225fc7 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 8 Aug 2024 17:18:05 +0200 Subject: [PATCH 4/5] chore: fix quotes --- src/cli/commands/utils/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/commands/utils/helpers.ts b/src/cli/commands/utils/helpers.ts index 4a4704b..1d6e51d 100644 --- a/src/cli/commands/utils/helpers.ts +++ b/src/cli/commands/utils/helpers.ts @@ -49,7 +49,7 @@ ${exportInstruction} { ${chalk.gray('// ...')} ${chalk.green(`'${pluginName}'`)}: { enabled: ${chalk.yellow(true)}, - resolve: ${chalk.yellow(pluginPath || `'./src/plugins/${pluginName}'`)} + resolve: '${chalk.yellow(pluginPath || `./src/plugins/${pluginName}`)}' }, ${chalk.gray('// ...')} } From 69e29d8e5fbc822e2bb9da00d7749d8b6e477f05 Mon Sep 17 00:00:00 2001 From: Convly Date: Tue, 13 Aug 2024 11:46:54 +0200 Subject: [PATCH 5/5] chore: refactor helpers and plugin init logic --- src/cli/commands/plugin/init/action.ts | 33 +++++++++++++++----------- src/cli/commands/utils/helpers.ts | 7 +++--- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/cli/commands/plugin/init/action.ts b/src/cli/commands/plugin/init/action.ts index 6d76434..efa8fef 100644 --- a/src/cli/commands/plugin/init/action.ts +++ b/src/cli/commands/plugin/init/action.ts @@ -25,7 +25,7 @@ const USE_RC_VERSIONS: string[] = [ // Store results of prompt answers (run by pack-up init) // This is a limitation of pack-up; we cannot run the prompt and pass the answers in -let promptAnswers: any = []; +let promptAnswers: { name: string; answer: string | boolean }[] = []; export default async ( packagePath: string, @@ -33,13 +33,17 @@ export default async ( { logger, cwd }: CLIContext ) => { try { - const isStrapi = dirContainsStrapiProject(cwd); + // Make sure prompt answers are reset + promptAnswers = []; + + const isStrapiProject = dirContainsStrapiProject(cwd); // If the user entered a path, we will try to parse the plugin name from it so we can provide it as a suggestion for consistency const parsedPath = path.parse(packagePath); const suggestedPackageName = parsedPath.base; const isPathPackageName = !packagePath.includes('/'); - const pluginPath = isStrapi && isPathPackageName ? `./src/plugins/${packagePath}` : packagePath; + const pluginPath = + isStrapiProject && isPathPackageName ? `./src/plugins/${packagePath}` : packagePath; // const template = getPluginTemplate({ suggestedPackageName }); @@ -55,14 +59,15 @@ export default async ( template, }); - if (isStrapi) { - const pkgName = promptAnswers.find((option: any) => option?.name === 'pkgName')?.answer; - - const language = promptAnswers.find((option: any) => option?.name === 'typescript')?.answer + if (isStrapiProject) { + const pkgName = promptAnswers.find((option) => option.name === 'pkgName')?.answer; + const language = promptAnswers.find((option) => option.name === 'typescript')?.answer ? 'ts' : 'js'; - logger.info(logInstructions(pkgName, { language, path: pluginPath })); + if (typeof pkgName === 'string' && ['ts', 'js'].includes(language)) { + logger.info(logInstructions(pkgName, { language, path: pluginPath })); + } } logger.info('Plugin generated successfully.'); @@ -255,7 +260,7 @@ const getPluginTemplate = ({ suggestedPackageName }: PluginTemplateOptions) => { optional: true, }), ], - async getFiles(answers) { + async getFiles(answers = []) { const author: string[] = []; const files: TemplateFile[] = []; @@ -288,7 +293,7 @@ const getPluginTemplate = ({ suggestedPackageName }: PluginTemplateOptions) => { }, peerDependencies: { // TODO: set this to 5.0.0 when Strapi 5 is released - '@strapi/strapi': '^5.0.0-beta', + '@strapi/strapi': '^5.0.0-rc', '@strapi/sdk-plugin': '^5.0.0', }, strapi: { @@ -307,12 +312,12 @@ const getPluginTemplate = ({ suggestedPackageName }: PluginTemplateOptions) => { break; } case 'description': { - pkgJson.description = String(answer) ?? undefined; - pkgJson.strapi.description = String(answer) ?? undefined; + pkgJson.description = String(answer); + pkgJson.strapi.description = String(answer); break; } case 'displayName': { - pkgJson.strapi.displayName = String(answer) ?? undefined; + pkgJson.strapi.displayName = String(answer); break; } case 'authorName': { @@ -379,7 +384,7 @@ const getPluginTemplate = ({ suggestedPackageName }: PluginTemplateOptions) => { name: 'strapi-server.js', contents: outdent` 'use strict'; - + module.exports = require('./dist/server'); `, }); diff --git a/src/cli/commands/utils/helpers.ts b/src/cli/commands/utils/helpers.ts index 1d6e51d..f2f2f35 100644 --- a/src/cli/commands/utils/helpers.ts +++ b/src/cli/commands/utils/helpers.ts @@ -23,8 +23,7 @@ export const dirContainsStrapiProject = (dir: string) => { const packageJsonPath = path.join(dir, 'package.json'); const pkgJSON = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); return Boolean( - (pkgJSON.dependencies && pkgJSON.dependencies['@strapi/strapi']) || - (pkgJSON.devDependencies && pkgJSON.devDependencies['@strapi/strapi']) + pkgJSON.dependencies?.['@strapi/strapi'] || pkgJSON.devDependencies?.['@strapi/strapi'] ); } catch (err) { return false; @@ -33,7 +32,7 @@ export const dirContainsStrapiProject = (dir: string) => { export const logInstructions = ( pluginName: string, - { language, path: pluginPath }: { language: string; path?: string } + { language, path: pluginPath }: { language: string; path: string } ) => { const maxLength = ` resolve: './src/plugins/${pluginName}'`.length; const separator = Array(maxLength).fill('─').join(''); @@ -49,7 +48,7 @@ ${exportInstruction} { ${chalk.gray('// ...')} ${chalk.green(`'${pluginName}'`)}: { enabled: ${chalk.yellow(true)}, - resolve: '${chalk.yellow(pluginPath || `./src/plugins/${pluginName}`)}' + resolve: '${chalk.yellow(pluginPath)}' }, ${chalk.gray('// ...')} }