mirror of
https://github.com/strapi/strapi.git
synced 2025-08-27 10:15:59 +00:00
feat(strapi): add experimental plugin init command (#18970)
This commit is contained in:
parent
40ef34cbcd
commit
46e6ffbb44
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: plugin:build
|
||||
description: An in depth look at the plugin:build command of the Strapi CLI
|
||||
title: plugin:watch
|
||||
description: An in depth look at the plugin:watch command of the Strapi CLI
|
||||
tags:
|
||||
- CLI
|
||||
- commands
|
||||
|
35
docs/docs/docs/01-core/strapi/commands/plugin/03-init.md
Normal file
35
docs/docs/docs/01-core/strapi/commands/plugin/03-init.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
title: plugin:init
|
||||
description: An in depth look at the plugin:init command of the Strapi CLI
|
||||
tags:
|
||||
- CLI
|
||||
- commands
|
||||
- plugins
|
||||
- initialization
|
||||
---
|
||||
|
||||
The `plugin:init` command is used to create a plugin, by default in `src/plugins` – because this is the strapi CLI we assume we're in a user app by default. This is done by using `pack-up` underneath and a unique template configuration.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
strapi plugin:init [path]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```bash
|
||||
Create a new plugin at a given path.
|
||||
|
||||
Options:
|
||||
-d, --debug Enable debugging mode with verbose logs (default: false)
|
||||
--silent Don't log anything (default: false)
|
||||
-h, --help Display help for command
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
The command sequence can be visualised as follows:
|
||||
|
||||
- Ask the user a series of questions via prompts
|
||||
- Generate a plugin folder structure based on that template
|
@ -142,6 +142,8 @@
|
||||
"dotenv": "14.2.0",
|
||||
"execa": "5.1.1",
|
||||
"fs-extra": "10.0.0",
|
||||
"get-latest-version": "5.1.0",
|
||||
"git-url-parse": "13.1.0",
|
||||
"glob": "7.2.3",
|
||||
"http-errors": "1.8.1",
|
||||
"https-proxy-agent": "5.0.1",
|
||||
@ -163,6 +165,7 @@
|
||||
"node-schedule": "2.1.0",
|
||||
"open": "8.4.0",
|
||||
"ora": "5.4.1",
|
||||
"outdent": "0.8.0",
|
||||
"package-json": "7.0.0",
|
||||
"pkg-up": "3.1.0",
|
||||
"qs": "6.11.1",
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { RenderAdminArgs, renderAdmin, Store } from '@strapi/admin/strapi-admin';
|
||||
// @ts-expect-error – No types, yet.
|
||||
import contentTypeBuilder from '@strapi/plugin-content-type-builder/strapi-admin';
|
||||
import email from '@strapi/plugin-email/strapi-admin';
|
||||
// @ts-expect-error – No types, yet.
|
||||
|
@ -2,15 +2,18 @@ import boxen from 'boxen';
|
||||
import chalk from 'chalk';
|
||||
import { BuildCLIOptions, ConfigBundle, build } from '@strapi/pack-up';
|
||||
import { notifyExperimentalCommand } from '../../../utils/helpers';
|
||||
import { createLogger } from '../../../utils/logger';
|
||||
import { Export, loadPkg, validatePkg } from '../../../utils/pkg';
|
||||
import { CLIContext } from '../../../types';
|
||||
|
||||
interface ActionOptions extends BuildCLIOptions {
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
export default async ({ force, ...opts }: ActionOptions) => {
|
||||
const logger = createLogger({ debug: opts.debug, silent: opts.silent, timestamp: false });
|
||||
export default async (
|
||||
{ force, ...opts }: ActionOptions,
|
||||
_cmd: unknown,
|
||||
{ logger, cwd }: CLIContext
|
||||
) => {
|
||||
try {
|
||||
/**
|
||||
* ALWAYS set production for using plugin build CLI.
|
||||
@ -18,12 +21,10 @@ export default async ({ force, ...opts }: ActionOptions) => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
/**
|
||||
* Notify users this is an experimental command and get them to approve first
|
||||
* this can be opted out by setting the argument --yes
|
||||
* this can be opted out by setting the argument --force
|
||||
*/
|
||||
await notifyExperimentalCommand('plugin:build', { force });
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
const pkg = await loadPkg({ cwd, logger });
|
||||
const pkgJson = await validatePkg({ pkg });
|
||||
|
||||
|
@ -6,7 +6,7 @@ import action from './action';
|
||||
/**
|
||||
* `$ strapi plugin:build`
|
||||
*/
|
||||
const command: StrapiCommand = ({ command }) => {
|
||||
const command: StrapiCommand = ({ command, ctx }) => {
|
||||
command
|
||||
.command('plugin:build')
|
||||
.description('Bundle your strapi plugin for publishing.')
|
||||
@ -15,7 +15,7 @@ const command: StrapiCommand = ({ command }) => {
|
||||
.option('--silent', "Don't log anything", false)
|
||||
.option('--sourcemap', 'produce sourcemaps', false)
|
||||
.option('--minify', 'minify the output', false)
|
||||
.action(runAction('plugin:build', action));
|
||||
.action((...args) => runAction('plugin:build', action)(...args, ctx));
|
||||
};
|
||||
|
||||
export default command;
|
||||
|
545
packages/core/strapi/src/commands/actions/plugin/init/action.ts
Normal file
545
packages/core/strapi/src/commands/actions/plugin/init/action.ts
Normal file
@ -0,0 +1,545 @@
|
||||
import path from 'node:path';
|
||||
import boxen from 'boxen';
|
||||
import chalk from 'chalk';
|
||||
import getLatestVersion from 'get-latest-version';
|
||||
import gitUrlParse from 'git-url-parse';
|
||||
import {
|
||||
InitOptions,
|
||||
definePackageFeature,
|
||||
definePackageOption,
|
||||
defineTemplate,
|
||||
init,
|
||||
TemplateFile,
|
||||
} from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
import { notifyExperimentalCommand } from '../../../utils/helpers';
|
||||
|
||||
import { CLIContext } from '../../../types';
|
||||
import { gitIgnoreFile } from './files/gitIgnore';
|
||||
|
||||
interface ActionOptions extends Pick<InitOptions, 'silent' | 'debug'> {}
|
||||
|
||||
export default async (
|
||||
packagePath: string,
|
||||
{ silent, debug }: ActionOptions,
|
||||
_cmd: unknown,
|
||||
{ logger, cwd }: CLIContext
|
||||
) => {
|
||||
try {
|
||||
/**
|
||||
* Notify users this is an experimental command. We don't need to get them to approve first.
|
||||
*/
|
||||
await notifyExperimentalCommand('plugin:init', { force: true });
|
||||
|
||||
/**
|
||||
* Create the package // plugin
|
||||
*/
|
||||
await init({
|
||||
path: packagePath,
|
||||
cwd,
|
||||
silent,
|
||||
debug,
|
||||
template: PLUGIN_TEMPLATE,
|
||||
});
|
||||
|
||||
logger.info("Don't forget to enable your plugin in your configuration files.");
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
'There seems to be an unexpected error, try again with --debug for more information \n'
|
||||
);
|
||||
if (err instanceof Error && err.stack) {
|
||||
logger.log(
|
||||
chalk.red(
|
||||
boxen(err.stack, {
|
||||
padding: 1,
|
||||
align: 'left',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
const PACKAGE_NAME_REGEXP = /^(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)\/)?[a-z0-9-~][a-z0-9-._~]*$/i;
|
||||
|
||||
interface PackageExport {
|
||||
types?: string;
|
||||
require: string;
|
||||
import: string;
|
||||
source: string;
|
||||
default: string;
|
||||
}
|
||||
|
||||
interface PluginPackageJson {
|
||||
name?: string;
|
||||
description?: string;
|
||||
version?: string;
|
||||
keywords?: string[];
|
||||
type: 'commonjs';
|
||||
license?: string;
|
||||
repository?: {
|
||||
type: 'git';
|
||||
url: string;
|
||||
};
|
||||
bugs?: {
|
||||
url: string;
|
||||
};
|
||||
homepage?: string;
|
||||
author?: string;
|
||||
exports: {
|
||||
'./strapi-admin'?: PackageExport;
|
||||
'./strapi-server'?: PackageExport;
|
||||
'./package.json': `${string}.json`;
|
||||
};
|
||||
files: string[];
|
||||
scripts: Record<string, string>;
|
||||
dependencies: Record<string, string>;
|
||||
devDependencies: Record<string, string>;
|
||||
peerDependencies: Record<string, string>;
|
||||
strapi: {
|
||||
name?: string;
|
||||
displayName?: string;
|
||||
description?: string;
|
||||
kind: 'plugin';
|
||||
};
|
||||
}
|
||||
|
||||
const PLUGIN_TEMPLATE = defineTemplate(async ({ logger, gitConfig, packagePath }) => {
|
||||
let repo: {
|
||||
source?: string;
|
||||
owner?: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
const [packageFolder] = packagePath.split(path.sep).slice(-1);
|
||||
|
||||
return {
|
||||
prompts: [
|
||||
definePackageOption({
|
||||
name: 'repo',
|
||||
type: 'text',
|
||||
message: 'git url',
|
||||
validate(v) {
|
||||
if (!v) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = gitUrlParse(v);
|
||||
|
||||
repo = { source: result.source, owner: result.owner, name: result.name };
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
return 'invalid git url';
|
||||
}
|
||||
},
|
||||
}),
|
||||
definePackageOption({
|
||||
name: 'pkgName',
|
||||
type: 'text',
|
||||
message: 'plugin name',
|
||||
initial: () => repo?.name ?? '',
|
||||
validate(v) {
|
||||
if (!v) {
|
||||
return 'package name is required';
|
||||
}
|
||||
|
||||
const match = PACKAGE_NAME_REGEXP.exec(v);
|
||||
|
||||
if (!match) {
|
||||
return 'invalid package name';
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
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',
|
||||
},
|
||||
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': '*',
|
||||
prettier: '*',
|
||||
},
|
||||
peerDependencies: {
|
||||
'@strapi/strapi': '^4.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'] = {
|
||||
source: './src/admin/index.js',
|
||||
import: './dist/admin/index.mjs',
|
||||
require: './dist/admin/index.js',
|
||||
default: './dist/admin/index.js',
|
||||
};
|
||||
|
||||
pkgJson.dependencies = {
|
||||
...pkgJson.dependencies,
|
||||
'@strapi/helper-plugin': '*',
|
||||
'@strapi/design-system': '*',
|
||||
'@strapi/icons': '*',
|
||||
};
|
||||
|
||||
pkgJson.devDependencies = {
|
||||
...pkgJson.dependencies,
|
||||
react: '*',
|
||||
'react-dom': '*',
|
||||
'react-router-dom': '5.3.4',
|
||||
'styled-components': '5.3.3',
|
||||
};
|
||||
|
||||
pkgJson.peerDependencies = {
|
||||
...pkgJson.peerDependencies,
|
||||
react: '^17.0.0 || ^18.0.0',
|
||||
'react-dom': '^17.0.0 || ^18.0.0',
|
||||
'react-router-dom': '5.2.0',
|
||||
'styled-components': '5.2.1',
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'server-code': {
|
||||
if (answer) {
|
||||
pkgJson.exports['./strapi-server'] = {
|
||||
source: './src/server/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;
|
||||
}
|
||||
case 'typescript': {
|
||||
const isTypescript = Boolean(answer);
|
||||
|
||||
if (isTypescript) {
|
||||
if (isRecord(pkgJson.exports['./strapi-admin'])) {
|
||||
pkgJson.exports['./strapi-admin'].source = './src/admin/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': '*',
|
||||
'@types/react-router-dom': '5.3.3',
|
||||
'@types/styled-components': '5.1.26',
|
||||
};
|
||||
|
||||
const { adminTsconfigFiles } = await import('./files/typescript');
|
||||
|
||||
files.push(adminTsconfigFiles.tsconfigBuildFile, adminTsconfigFiles.tsconfigFile);
|
||||
}
|
||||
|
||||
if (isRecord(pkgJson.exports['./strapi-server'])) {
|
||||
pkgJson.exports['./strapi-server'].source = './src/server/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`
|
||||
export const PLUGIN_ID = '${pkgJson.name!.replace(/^strapi-plugin-/i, '')}';
|
||||
`,
|
||||
});
|
||||
|
||||
if (isTypescript) {
|
||||
const { adminTypescriptFiles } = await import('./files/admin');
|
||||
|
||||
files.push(...adminTypescriptFiles);
|
||||
} else {
|
||||
const { adminJavascriptFiles } = await import('./files/admin');
|
||||
|
||||
files.push(...adminJavascriptFiles);
|
||||
}
|
||||
}
|
||||
|
||||
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(...serverJavascriptFiles(packageFolder));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'eslint': {
|
||||
if (answer) {
|
||||
const { eslintIgnoreFile } = await import('./files/eslint');
|
||||
|
||||
files.push(eslintIgnoreFile);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'prettier': {
|
||||
if (answer) {
|
||||
const { prettierFile, prettierIgnoreFile } = await import('./files/prettier');
|
||||
|
||||
files.push(prettierFile, prettierIgnoreFile);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'editorconfig': {
|
||||
if (answer) {
|
||||
const { editorConfigFile } = await import('./files/editorConfig');
|
||||
|
||||
files.push(editorConfigFile);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
files.push({
|
||||
name: 'package.json',
|
||||
contents: outdent`
|
||||
${JSON.stringify(pkgJson, null, 2)}
|
||||
`,
|
||||
});
|
||||
|
||||
files.push({
|
||||
name: 'README.md',
|
||||
contents: outdent`
|
||||
# ${pkgJson.name}
|
||||
|
||||
${pkgJson.description ?? ''}
|
||||
`,
|
||||
});
|
||||
|
||||
files.push(gitIgnoreFile);
|
||||
|
||||
return files;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
Boolean(value) && !Array.isArray(value) && typeof value === 'object';
|
||||
|
||||
const resolveLatestVerisonOfDeps = async (
|
||||
deps: Record<string, string>
|
||||
): Promise<Record<string, string>> => {
|
||||
const latestDeps: Record<string, string> = {};
|
||||
|
||||
for (const [name, version] of Object.entries(deps)) {
|
||||
try {
|
||||
const latestVersion = await getLatestVersion(name, version);
|
||||
latestDeps[name] = latestVersion ? `^${latestVersion}` : '*';
|
||||
} catch (err) {
|
||||
latestDeps[name] = '*';
|
||||
}
|
||||
}
|
||||
|
||||
return latestDeps;
|
||||
};
|
@ -0,0 +1,18 @@
|
||||
import type { StrapiCommand } from '../../../types';
|
||||
import { runAction } from '../../../utils/helpers';
|
||||
import action from './action';
|
||||
|
||||
/**
|
||||
* `$ strapi plugin:init`
|
||||
*/
|
||||
const command: StrapiCommand = ({ command, ctx }) => {
|
||||
command
|
||||
.command('plugin:init')
|
||||
.description('Create a new plugin at a given path')
|
||||
.argument('[path]', 'path to the plugin', './src/plugins/my-plugin')
|
||||
.option('-d, --debug', 'Enable debugging mode with verbose logs', false)
|
||||
.option('--silent', "Don't log anything", false)
|
||||
.action((...args) => runAction('plugin:init', action)(...args, ctx));
|
||||
};
|
||||
|
||||
export default command;
|
@ -0,0 +1,286 @@
|
||||
import { TemplateFile } from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
const PLUGIN_ICON_CODE = outdent`
|
||||
import { Puzzle } from '@strapi/icons';
|
||||
|
||||
const PluginIcon = () => <Puzzle />;
|
||||
|
||||
export { PluginIcon };
|
||||
`;
|
||||
|
||||
const APP_CODE = outdent`
|
||||
import { AnErrorOccurred } from '@strapi/helper-plugin';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
|
||||
import { PLUGIN_ID } from '../pluginId';
|
||||
|
||||
import { HomePage } from './HomePage';
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<Switch>
|
||||
<Route path={\`/plugins/\${PLUGIN_ID}\`} component={HomePage} exact />
|
||||
<Route component={AnErrorOccurred} />
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
export { App };
|
||||
`;
|
||||
|
||||
const HOMEPAGE_CODE = outdent`
|
||||
import { Main } from '@strapi/design-system';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { getTranslation } from '../utils/getTranslation';
|
||||
|
||||
const HomePage = () => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<Main>
|
||||
<h1>Welcome to {formatMessage({ id: getTranslation("plugin.name") })}</h1>
|
||||
</Main>
|
||||
)
|
||||
}
|
||||
|
||||
export { HomePage };
|
||||
`;
|
||||
|
||||
const TYPESCRIPT: TemplateFile[] = [
|
||||
{
|
||||
name: 'admin/src/index.ts',
|
||||
contents: outdent`
|
||||
import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
||||
import { PLUGIN_ID } from './pluginId';
|
||||
import { Initializer } from './components/Initializer';
|
||||
import { PluginIcon } from './components/PluginIcon';
|
||||
|
||||
export default {
|
||||
register(app: any) {
|
||||
app.addMenuLink({
|
||||
to: \`/plugins/\${PluginIcon}\`,
|
||||
icon: PluginIcon,
|
||||
intlLabel: {
|
||||
id: \`\${PLUGIN_ID}.plugin.name\`,
|
||||
defaultMessage: PLUGIN_ID,
|
||||
},
|
||||
Component: async () => {
|
||||
const { App } = await import('./pages/App');
|
||||
|
||||
return App;
|
||||
},
|
||||
});
|
||||
|
||||
app.registerPlugin({
|
||||
id: PLUGIN_ID,
|
||||
initializer: Initializer,
|
||||
isReady: false,
|
||||
name: PLUGIN_ID,
|
||||
});
|
||||
},
|
||||
|
||||
async registerTrads(app: any) {
|
||||
const { locales } = app;
|
||||
|
||||
const importedTranslations = await Promise.all(
|
||||
(locales as string[]).map((locale) => {
|
||||
return import(\`./translations/\${locale}.json\`)
|
||||
.then(({ default: data }) => {
|
||||
return {
|
||||
data: prefixPluginTranslations(data, PLUGIN_ID),
|
||||
locale,
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
return {
|
||||
data: {},
|
||||
locale,
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
return importedTranslations;
|
||||
},
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/components/PluginIcon.tsx',
|
||||
contents: PLUGIN_ICON_CODE,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/components/Initializer.tsx',
|
||||
contents: outdent`
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { PLUGIN_ID } from '../pluginId';
|
||||
|
||||
type InitializerProps = {
|
||||
setPlugin: (id: string) => void;
|
||||
};
|
||||
|
||||
const Initializer = ({ setPlugin }: InitializerProps) => {
|
||||
const ref = useRef(setPlugin);
|
||||
|
||||
useEffect(() => {
|
||||
ref.current(PLUGIN_ID);
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export { Initializer };
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/pages/App.tsx',
|
||||
contents: APP_CODE,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/pages/HomePage.tsx',
|
||||
contents: HOMEPAGE_CODE,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/utils/getTranslation.ts',
|
||||
contents: outdent`
|
||||
import { PLUGIN_ID } from '../pluginId';
|
||||
|
||||
const getTranslation = (id: string) => \`\${PLUGIN_ID}.\${id}\`;
|
||||
|
||||
export { getTranslation };
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/translations/en.json',
|
||||
contents: outdent`
|
||||
{}
|
||||
`,
|
||||
},
|
||||
{
|
||||
/**
|
||||
* TODO: remove this when we release design-system V2
|
||||
*/
|
||||
name: 'admin/custom.d.ts',
|
||||
contents: outdent`
|
||||
declare module '@strapi/design-system/*';
|
||||
declare module '@strapi/design-system';
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
const JAVASCRIPT: TemplateFile[] = [
|
||||
{
|
||||
name: 'admin/src/index.js',
|
||||
contents: outdent`
|
||||
import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
||||
import { PLUGIN_ID } from './pluginId';
|
||||
import { Initializer } from './components/Initializer';
|
||||
import { PluginIcon } from './components/PluginIcon';
|
||||
|
||||
export default {
|
||||
register(app) {
|
||||
app.addMenuLink({
|
||||
to: \`/plugins/\${PluginIcon}\`,
|
||||
icon: PluginIcon,
|
||||
intlLabel: {
|
||||
id: \`\${PLUGIN_ID}.plugin.name\`,
|
||||
defaultMessage: PLUGIN_ID,
|
||||
},
|
||||
Component: async () => {
|
||||
const { App } = await import('./pages/App');
|
||||
|
||||
return App;
|
||||
},
|
||||
});
|
||||
|
||||
app.registerPlugin({
|
||||
id: PLUGIN_ID,
|
||||
initializer: Initializer,
|
||||
isReady: false,
|
||||
name: PLUGIN_ID,
|
||||
});
|
||||
},
|
||||
|
||||
async registerTrads(app) {
|
||||
const { locales } = app;
|
||||
|
||||
const importedTranslations = await Promise.all(
|
||||
locales.map((locale) => {
|
||||
return import(\`./translations/\${locale}.json\`)
|
||||
.then(({ default: data }) => {
|
||||
return {
|
||||
data: prefixPluginTranslations(data, PLUGIN_ID),
|
||||
locale,
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
return {
|
||||
data: {},
|
||||
locale,
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
return importedTranslations;
|
||||
},
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/components/PluginIcon.jsx',
|
||||
contents: PLUGIN_ICON_CODE,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/components/Initializer.jsx',
|
||||
contents: outdent`
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { PLUGIN_ID } from '../pluginId';
|
||||
|
||||
/**
|
||||
* @type {import('react').FC<{ setPlugin: (id: string) => void }>}
|
||||
*/
|
||||
const Initializer = ({ setPlugin }) => {
|
||||
const ref = useRef(setPlugin);
|
||||
|
||||
useEffect(() => {
|
||||
ref.current(PLUGIN_ID);
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export { Initializer };
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/pages/App.jsx',
|
||||
contents: APP_CODE,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/pages/HomePage.jsx',
|
||||
contents: HOMEPAGE_CODE,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/utils/getTranslation.js',
|
||||
contents: outdent`
|
||||
import { PLUGIN_ID } from '../pluginId';
|
||||
|
||||
const getTranslation = (id) => \`\${PLUGIN_ID}.\${id}\`;
|
||||
|
||||
export { getTranslation };
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'admin/src/translations/en.json',
|
||||
contents: outdent`
|
||||
{}
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
export { TYPESCRIPT as adminTypescriptFiles, JAVASCRIPT as adminJavascriptFiles };
|
@ -0,0 +1,26 @@
|
||||
import { TemplateFile } from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
const editorConfigFile: TemplateFile = {
|
||||
name: '.editorconfig',
|
||||
contents: outdent`
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[{package.json,*.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
`,
|
||||
};
|
||||
|
||||
export { editorConfigFile };
|
@ -0,0 +1,11 @@
|
||||
import { TemplateFile } from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
const eslintIgnoreFile: TemplateFile = {
|
||||
name: '.eslintignore',
|
||||
contents: outdent`
|
||||
dist
|
||||
`,
|
||||
};
|
||||
|
||||
export { eslintIgnoreFile };
|
@ -0,0 +1,34 @@
|
||||
import { TemplateFile } from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
const gitIgnoreFile: TemplateFile = {
|
||||
name: '.gitignore',
|
||||
contents: outdent`
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
# production
|
||||
dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env
|
||||
`,
|
||||
};
|
||||
|
||||
export { gitIgnoreFile };
|
@ -0,0 +1,25 @@
|
||||
import { TemplateFile } from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
const prettierFile: TemplateFile = {
|
||||
name: '.prettierrc',
|
||||
contents: outdent`
|
||||
{
|
||||
"endOfLine": 'lf',
|
||||
"tabWidth": 2,
|
||||
"printWidth": 100,
|
||||
"singleQuote": true,
|
||||
"trailingComma": 'es5',
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
const prettierIgnoreFile: TemplateFile = {
|
||||
name: '.prettierignore',
|
||||
contents: outdent`
|
||||
dist
|
||||
coverage
|
||||
`,
|
||||
};
|
||||
|
||||
export { prettierFile, prettierIgnoreFile };
|
@ -0,0 +1,360 @@
|
||||
import { TemplateFile } from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
const TYPESCRIPT = (pluginName: string): TemplateFile[] => [
|
||||
{
|
||||
name: 'server/src/index.ts',
|
||||
contents: outdent`
|
||||
/**
|
||||
* Application methods
|
||||
*/
|
||||
import bootstrap from './bootstrap';
|
||||
import destroy from './destroy';
|
||||
import register from './register';
|
||||
|
||||
/**
|
||||
* Plugin server methods
|
||||
*/
|
||||
import config from './config';
|
||||
import contentTypes from './content-types';
|
||||
import controllers from './controllers';
|
||||
import middlewares from './middlewares';
|
||||
import policies from './policies';
|
||||
import routes from './routes';
|
||||
import services from './services';
|
||||
|
||||
export default {
|
||||
bootstrap,
|
||||
destroy,
|
||||
register,
|
||||
|
||||
config,
|
||||
controllers,
|
||||
contentTypes,
|
||||
middlewares,
|
||||
policies,
|
||||
routes,
|
||||
services,
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/bootstrap.ts',
|
||||
contents: outdent`
|
||||
import type { Strapi } from '@strapi/strapi';
|
||||
|
||||
const bootstrap = ({ strapi }: { strapi: Strapi }) => {
|
||||
// bootstrap phase
|
||||
};
|
||||
|
||||
export default bootstrap;
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/destroy.ts',
|
||||
contents: outdent`
|
||||
import type { Strapi } from '@strapi/strapi';
|
||||
|
||||
const destroy = ({ strapi }: { strapi: Strapi }) => {
|
||||
// destroy phase
|
||||
};
|
||||
|
||||
export default destroy;
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/register.ts',
|
||||
contents: outdent`
|
||||
import type { Strapi } from '@strapi/strapi';
|
||||
|
||||
const register = ({ strapi }: { strapi: Strapi }) => {
|
||||
// register phase
|
||||
};
|
||||
|
||||
export default register;
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/config/index.ts',
|
||||
contents: outdent`
|
||||
export default {
|
||||
default: {},
|
||||
validator() {},
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/content-types/index.ts',
|
||||
contents: outdent`
|
||||
export default {};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/controllers/index.ts',
|
||||
contents: outdent`
|
||||
import controller from './controller';
|
||||
|
||||
export default {
|
||||
controller,
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/controllers/controller.ts',
|
||||
contents: outdent`
|
||||
import type { Strapi } from '@strapi/strapi';
|
||||
|
||||
const controller = ({ strapi }: { strapi: Strapi }) => ({
|
||||
index(ctx) {
|
||||
ctx.body = strapi
|
||||
.plugin('${pluginName}')
|
||||
// the name of the service file & the method.
|
||||
.service('service')
|
||||
.getWelcomeMessage();
|
||||
},
|
||||
});
|
||||
|
||||
export default controller
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/middlewares/index.ts',
|
||||
contents: outdent`
|
||||
export default {};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/policies/index.ts',
|
||||
contents: outdent`
|
||||
export default {};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/routes/index.ts',
|
||||
contents: outdent`
|
||||
export default [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/',
|
||||
// name of the controller file & the method.
|
||||
handler: 'controller.index',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/services/index.ts',
|
||||
contents: outdent`
|
||||
import service from './service';
|
||||
|
||||
export default {
|
||||
service,
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/services/service.ts',
|
||||
contents: outdent`
|
||||
import type { Strapi } from '@strapi/strapi';
|
||||
|
||||
const service = ({ strapi }: { strapi: Strapi }) => ({
|
||||
getWelcomeMessage() {
|
||||
return 'Welcome to Strapi 🚀';
|
||||
},
|
||||
});
|
||||
|
||||
export default service
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
const JAVASCRIPT = (pluginName: string): TemplateFile[] => [
|
||||
{
|
||||
name: 'server/src/index.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Application methods
|
||||
*/
|
||||
const bootstrap = require('./bootstrap');
|
||||
const destroy = require('./destroy');
|
||||
const register = require('./register');
|
||||
|
||||
/**
|
||||
* Plugin server methods
|
||||
*/
|
||||
const config = require('./config');
|
||||
const contentTypes = require('./content-types');
|
||||
const controllers = require('./controllers');
|
||||
const middlewares = require('./middlewares');
|
||||
const policies = require('./policies');
|
||||
const routes = require('./routes');
|
||||
const services = require('./services');
|
||||
|
||||
module.exports = {
|
||||
bootstrap,
|
||||
destroy,
|
||||
register,
|
||||
|
||||
config,
|
||||
controllers,
|
||||
contentTypes,
|
||||
middlewares,
|
||||
policies,
|
||||
routes,
|
||||
services,
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/bootstrap.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
const bootstrap = ({ strapi }) => {
|
||||
// bootstrap phase
|
||||
};
|
||||
|
||||
module.exports = bootstrap;
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/destroy.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
const destroy = ({ strapi }) => {
|
||||
// destroy phase
|
||||
};
|
||||
|
||||
module.exports = destroy;
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/register.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
const register = ({ strapi }) => {
|
||||
// register phase
|
||||
};
|
||||
|
||||
module.exports = register;
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/config/index.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
default: {},
|
||||
validator() {},
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/content-types/index.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
module.exports = {};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/controllers/index.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
const controller = require('./controller');
|
||||
|
||||
module.exports = {
|
||||
controller,
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/controllers/controller.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
const controller = ({ strapi }) => ({
|
||||
index(ctx) {
|
||||
ctx.body = strapi
|
||||
.plugin('${pluginName}')
|
||||
// the name of the service file & the method.
|
||||
.service('service')
|
||||
.getWelcomeMessage();
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = controller
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/middlewares/index.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
module.exports = {};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/policies/index.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
module.exports = {};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/routes/index.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/',
|
||||
// name of the controller file & the method.
|
||||
handler: 'controller.index',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/services/index.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
const service = require('./service');
|
||||
|
||||
module.exports = {
|
||||
service,
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'server/src/services/service.ts',
|
||||
contents: outdent`
|
||||
'use strict';
|
||||
|
||||
const service = ({ strapi }) => ({
|
||||
getWelcomeMessage() {
|
||||
return 'Welcome to Strapi 🚀';
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = service
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
export { TYPESCRIPT as serverTypescriptFiles, JAVASCRIPT as serverJavascriptFiles };
|
@ -0,0 +1,71 @@
|
||||
import { TemplateFile } from '@strapi/pack-up';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
interface TsConfigFiles {
|
||||
tsconfigFile: TemplateFile;
|
||||
tsconfigBuildFile: TemplateFile;
|
||||
}
|
||||
|
||||
const ADMIN: TsConfigFiles = {
|
||||
tsconfigFile: {
|
||||
name: 'admin/tsconfig.json',
|
||||
contents: outdent`
|
||||
{
|
||||
"extends": "@strapi/typescript-utils/tsconfigs/admin",
|
||||
"include": ["./src", "./custom.d.ts"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "../",
|
||||
"baseUrl": ".",
|
||||
},
|
||||
}
|
||||
`,
|
||||
},
|
||||
tsconfigBuildFile: {
|
||||
name: 'admin/tsconfig.build.json',
|
||||
contents: outdent`
|
||||
{
|
||||
"extends": "./tsconfig",
|
||||
"include": ["./src", "./custom.d.ts"],
|
||||
"exclude": ["**/*.test.ts", "**/*.test.tsx"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "../",
|
||||
"baseUrl": ".",
|
||||
"outDir": "./dist",
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
const SERVER: TsConfigFiles = {
|
||||
tsconfigFile: {
|
||||
name: 'server/tsconfig.json',
|
||||
contents: outdent`
|
||||
{
|
||||
"extends": "@strapi/typescript-utils/tsconfigs/server",
|
||||
"include": ["./src"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "../",
|
||||
"baseUrl": ".",
|
||||
},
|
||||
}
|
||||
`,
|
||||
},
|
||||
tsconfigBuildFile: {
|
||||
name: 'server/tsconfig.build.json',
|
||||
contents: outdent`
|
||||
{
|
||||
"extends": "./tsconfig",
|
||||
"include": ["./src"],
|
||||
"exclude": ["**/*.test.ts"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "../",
|
||||
"baseUrl": ".",
|
||||
"outDir": "./dist",
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
export { ADMIN as adminTsconfigFiles, SERVER as serverTsconfigFiles };
|
@ -2,23 +2,17 @@ import boxen from 'boxen';
|
||||
import chalk from 'chalk';
|
||||
import { ConfigBundle, WatchCLIOptions, watch } from '@strapi/pack-up';
|
||||
import { notifyExperimentalCommand } from '../../../utils/helpers';
|
||||
import { createLogger } from '../../../utils/logger';
|
||||
import { Export, loadPkg, validatePkg } from '../../../utils/pkg';
|
||||
import { CLIContext } from '../../../types';
|
||||
|
||||
interface ActionOptions extends WatchCLIOptions {
|
||||
force?: boolean;
|
||||
}
|
||||
interface ActionOptions extends WatchCLIOptions {}
|
||||
|
||||
export default async ({ force, ...opts }: ActionOptions) => {
|
||||
const logger = createLogger({ debug: opts.debug, silent: opts.silent, timestamp: false });
|
||||
export default async (opts: ActionOptions, _cmd: unknown, { cwd, logger }: CLIContext) => {
|
||||
try {
|
||||
/**
|
||||
* Notify users this is an experimental command and get them to approve first
|
||||
* this can be opted out by setting the argument --yes
|
||||
* Notify users this is an experimental command.
|
||||
*/
|
||||
await notifyExperimentalCommand('plugin:watch', { force });
|
||||
|
||||
const cwd = process.cwd();
|
||||
await notifyExperimentalCommand('plugin:watch', { force: true });
|
||||
|
||||
const pkg = await loadPkg({ cwd, logger });
|
||||
const pkgJson = await validatePkg({ pkg });
|
||||
|
@ -3,15 +3,15 @@ import { runAction } from '../../../utils/helpers';
|
||||
import action from './action';
|
||||
|
||||
/**
|
||||
* `$ strapi plugin:build`
|
||||
* `$ strapi plugin:watch`
|
||||
*/
|
||||
const command: StrapiCommand = ({ command }) => {
|
||||
const command: StrapiCommand = ({ command, ctx }) => {
|
||||
command
|
||||
.command('plugin:watch')
|
||||
.description('Watch & compile your strapi plugin for local development.')
|
||||
.option('-d, --debug', 'Enable debugging mode with verbose logs', false)
|
||||
.option('--silent', "Don't log anything", false)
|
||||
.action(runAction('plugin:watch', action));
|
||||
.action((...args) => runAction('plugin:watch', action)(...args, ctx));
|
||||
};
|
||||
|
||||
export default command;
|
||||
|
@ -27,6 +27,7 @@ import versionCommand from './actions/version/command';
|
||||
import watchAdminCommand from './actions/watch-admin/command';
|
||||
|
||||
import buildPluginCommand from './actions/plugin/build-command/command';
|
||||
import initPluginCommand from './actions/plugin/init/command';
|
||||
import watchPluginCommand from './actions/plugin/watch/command';
|
||||
|
||||
import { createLogger } from './utils/logger';
|
||||
@ -63,6 +64,7 @@ const strapiCommands = {
|
||||
* Plugins
|
||||
*/
|
||||
buildPluginCommand,
|
||||
initPluginCommand,
|
||||
watchPluginCommand,
|
||||
} as const;
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
{
|
||||
"extends": "tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true
|
||||
"noEmit": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"module": "ESNext"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
|
@ -19,4 +19,5 @@ export type {
|
||||
TemplateOrTemplateResolver,
|
||||
TemplateFeature,
|
||||
TemplateOption,
|
||||
TemplateFile,
|
||||
} from './node/templates/types';
|
||||
|
@ -58,3 +58,4 @@ const parseGlobalGitConfig = async (): Promise<GitConfig | null> => {
|
||||
};
|
||||
|
||||
export { parseGlobalGitConfig };
|
||||
export type { GitConfig };
|
||||
|
@ -5,6 +5,7 @@ import prettier, { Config as PrettierConfig } from 'prettier';
|
||||
import prompts from 'prompts';
|
||||
|
||||
import { isError } from '../core/errors';
|
||||
import { parseGlobalGitConfig } from '../core/git';
|
||||
import { Logger } from '../core/logger';
|
||||
|
||||
import { Template, TemplateFeature, TemplateOption, TemplateOrTemplateResolver } from './types';
|
||||
@ -27,9 +28,11 @@ const createPackageFromTemplate = async (
|
||||
) => {
|
||||
const { cwd, logger, template: templateOrResolver } = opts;
|
||||
|
||||
const gitConfig = await parseGlobalGitConfig();
|
||||
|
||||
const template =
|
||||
typeof templateOrResolver === 'function'
|
||||
? await templateOrResolver({ cwd, logger, packagePath })
|
||||
? await templateOrResolver({ cwd, logger, packagePath, gitConfig })
|
||||
: templateOrResolver;
|
||||
|
||||
logger.info('Creating a new package at: ', relative(cwd, packagePath));
|
||||
|
@ -3,7 +3,6 @@ import gitUrlParse from 'git-url-parse';
|
||||
import { outdent } from 'outdent';
|
||||
|
||||
import { isError } from '../../core/errors';
|
||||
import { parseGlobalGitConfig } from '../../core/git';
|
||||
import { PackageJson } from '../../core/pkg';
|
||||
import { definePackageFeature, definePackageOption, defineTemplate } from '../create';
|
||||
import { TemplateFile } from '../types';
|
||||
@ -14,9 +13,7 @@ import { prettierFile, prettierIgnoreFile } from './files/prettier';
|
||||
|
||||
const PACKAGE_NAME_REGEXP = /^(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)\/)?[a-z0-9-~][a-z0-9-._~]*$/i;
|
||||
|
||||
const defaultTemplate = defineTemplate(async ({ logger }) => {
|
||||
const gitConfig = await parseGlobalGitConfig();
|
||||
|
||||
const defaultTemplate = defineTemplate(async ({ logger, gitConfig }) => {
|
||||
let repo: {
|
||||
source?: string;
|
||||
owner?: string;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { PromptObject } from 'prompts';
|
||||
|
||||
import { GitConfig } from '../core/git';
|
||||
import { Logger } from '../core/logger';
|
||||
|
||||
interface TemplateFeature<T extends string = string> extends Pick<PromptObject<T>, 'initial'> {
|
||||
@ -45,6 +46,7 @@ interface Template {
|
||||
|
||||
interface TemplateContext {
|
||||
cwd: string;
|
||||
gitConfig: GitConfig | null;
|
||||
logger: Logger;
|
||||
packagePath: string;
|
||||
}
|
||||
|
@ -8476,6 +8476,8 @@ __metadata:
|
||||
eslint-config-custom: "npm:4.15.5"
|
||||
execa: "npm:5.1.1"
|
||||
fs-extra: "npm:10.0.0"
|
||||
get-latest-version: "npm:5.1.0"
|
||||
git-url-parse: "npm:13.1.0"
|
||||
glob: "npm:7.2.3"
|
||||
http-errors: "npm:1.8.1"
|
||||
https-proxy-agent: "npm:5.0.1"
|
||||
@ -8497,6 +8499,7 @@ __metadata:
|
||||
node-schedule: "npm:2.1.0"
|
||||
open: "npm:8.4.0"
|
||||
ora: "npm:5.4.1"
|
||||
outdent: "npm:0.8.0"
|
||||
package-json: "npm:7.0.0"
|
||||
pkg-up: "npm:3.1.0"
|
||||
qs: "npm:6.11.1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user