diff --git a/packages/preset-umi/src/commands/generators/api.test.ts b/packages/preset-umi/src/commands/generators/api.test.ts new file mode 100644 index 000000000000..0d9985cc684e --- /dev/null +++ b/packages/preset-umi/src/commands/generators/api.test.ts @@ -0,0 +1,43 @@ +import { generateApiResKV } from './api'; + +test('api name: foo', () => { + expect(generateApiResKV('foo')).toEqual({ + key: '"foo"', + value: '"is working"', + }); +}); + +test('api name: bar/boo', () => { + expect(generateApiResKV('bar/foo')).toEqual({ + key: '"foo"', + value: '"is working"', + }); +}); + +test('api name: foo/[id]', () => { + expect(generateApiResKV('foo/[id]')).toEqual({ + key: '"fooId"', + value: 'req.params["id"]', + }); +}); + +test('api name: [param]', () => { + expect(generateApiResKV('[param]')).toEqual({ + key: '"param"', + value: 'req.params["param"]', + }); +}); + +test('api name: long/nest/foo/[param]', () => { + expect(generateApiResKV('long/nest/foo/[param]')).toEqual({ + key: '"fooParam"', + value: 'req.params["param"]', + }); +}); + +test('api name: [ spaced ]', () => { + expect(generateApiResKV('[ spaced ]')).toEqual({ + key: '"spaced"', + value: 'req.params["spaced"]', + }); +}); diff --git a/packages/preset-umi/src/commands/generators/api.ts b/packages/preset-umi/src/commands/generators/api.ts new file mode 100644 index 000000000000..9a87194bf3e3 --- /dev/null +++ b/packages/preset-umi/src/commands/generators/api.ts @@ -0,0 +1,74 @@ +import { lodash } from '@umijs/utils'; +import { join, parse } from 'path'; +import { TEMPLATES_DIR } from '../../constants'; +import { IApi } from '../../types'; +import { GeneratorHelper, trim } from './utils'; + +export default (api: IApi) => { + api.describe({ + key: 'generator:api', + }); + + api.registerGenerator({ + key: 'api', + name: 'Generator api', + async fn(opts) { + const h = new GeneratorHelper(api); + + let [_, ...apiNames] = opts.args._; + + if (apiNames.length === 0) { + let apiName = await h.ensureVariableWithQuestion(null, { + type: 'text', + message: 'please input your api name:', + initial: 'foo', + format: trim, + }); + + apiNames = [apiName]; + } + + for (const apiName of apiNames) { + const apiFileName = `${apiName}.ts`; + const base = join(api.paths.absSrcPath, 'api'); + + const target = join(base, apiFileName); + + const kv = generateApiResKV(apiName); + + await opts.generateFile({ + target, + path: API_TML, + baseDir: api.paths.absSrcPath, + data: kv, + }); + } + }, + }); +}; + +const API_TML = join(TEMPLATES_DIR, 'generate/api.ts.tpl'); + +export function generateApiResKV(apiName: string): { + key: string; + value: string; +} { + const { name, dir } = parse(apiName); + const match = name.match(/^\[\s*(\w+)\s*\]$/); + + const quoteStr = JSON.stringify; + + if (!match) { + return { key: quoteStr(name), value: quoteStr('is working') }; + } + + const paramName = match[1]; + + const { name: itemName } = parse(dir); + + const key = itemName + ? `${itemName}${lodash.capitalize(paramName)}` + : paramName; + + return { key: quoteStr(key), value: `req.params[${quoteStr(paramName)}]` }; +} diff --git a/packages/preset-umi/src/commands/generators/component.test.ts b/packages/preset-umi/src/commands/generators/component.test.ts new file mode 100644 index 000000000000..71ae17354a94 --- /dev/null +++ b/packages/preset-umi/src/commands/generators/component.test.ts @@ -0,0 +1,60 @@ +import { normalize } from 'path'; +import { ComponentGenerator } from './component'; + +test('generate component with single name', async () => { + const { generateFile } = await runGeneratorWith('foo'); + + expect(generateFile).toBeCalledTimes(2); + expect(generateFile).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + target: normalize('/my/src/path/components/Foo/index.ts'), + baseDir: normalize('/my/src/path'), + data: { compName: 'Foo' }, + }), + ); + expect(generateFile).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + target: normalize('/my/src/path/components/Foo/Foo.tsx'), + baseDir: normalize('/my/src/path'), + data: { compName: 'Foo' }, + }), + ); +}); + +test('test generate nested named component foo/bar/qux', async () => { + const { generateFile } = await runGeneratorWith('foo/bar/qux'); + + expect(generateFile).toBeCalledTimes(2); + expect(generateFile).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + target: normalize('/my/src/path/components/foo/bar/Qux/index.ts'), + data: { compName: 'Qux' }, + }), + ); + expect(generateFile).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + target: normalize('/my/src/path/components/foo/bar/Qux/Qux.tsx'), + data: { compName: 'Qux' }, + }), + ); +}); + +async function runGeneratorWith(name: string) { + const generateFile = jest.fn(); + + const cg = new ComponentGenerator({ + componentName: name, + srcPath: normalize('/my/src/path'), + generateFile, + }); + + await cg.run(); + + return { + generateFile, + }; +} diff --git a/packages/preset-umi/src/commands/generators/component.ts b/packages/preset-umi/src/commands/generators/component.ts new file mode 100644 index 000000000000..2b8431d4351a --- /dev/null +++ b/packages/preset-umi/src/commands/generators/component.ts @@ -0,0 +1,93 @@ +import { GeneratorType } from '@umijs/core'; +import { generateFile, lodash } from '@umijs/utils'; +import { join, parse } from 'path'; +import { TEMPLATES_DIR } from '../../constants'; +import { IApi } from '../../types'; +import { GeneratorHelper } from './utils'; + +export default (api: IApi) => { + api.describe({ + key: 'generator:component', + }); + + api.registerGenerator({ + key: 'component', + name: 'Generate Component', + type: GeneratorType.generate, + + fn: async (options) => { + const h = new GeneratorHelper(api); + options.generateFile; + + let componentNames = options.args._.slice(1); + + if (componentNames.length === 0) { + let name: string = ''; + name = await h.ensureVariableWithQuestion(name, { + type: 'text', + message: 'Please input you component Name', + hint: 'foo', + initial: 'foo', + format: (s) => s?.trim() || '', + }); + componentNames = [name]; + } + + for (const cn of componentNames) { + await new ComponentGenerator({ + srcPath: api.paths.absSrcPath, + generateFile, + componentName: cn, + }).run(); + } + }, + }); +}; + +export class ComponentGenerator { + private readonly name: string; + private readonly dir: string; + + constructor( + readonly opts: { + componentName: string; + srcPath: string; + generateFile: typeof generateFile; + }, + ) { + const { name, dir } = parse(this.opts.componentName); + this.name = name; + this.dir = dir; + } + + async run() { + const { srcPath, generateFile } = this.opts; + const capitalizeName = lodash.capitalize(this.name); + const base = join( + this.opts.srcPath, + 'components', + this.dir, + capitalizeName, + ); + + const indexFile = join(base, 'index.ts'); + const compFile = join(base, `${capitalizeName}.tsx`); + + await generateFile({ + target: indexFile, + path: INDEX_TPL, + baseDir: srcPath, + data: { compName: capitalizeName }, + }); + + await generateFile({ + target: compFile, + path: COMP_TPL, + baseDir: srcPath, + data: { compName: capitalizeName }, + }); + } +} + +const INDEX_TPL = join(TEMPLATES_DIR, 'generate/component/index.ts.tpl'); +const COMP_TPL = join(TEMPLATES_DIR, 'generate/component/component.tsx.tpl'); diff --git a/packages/preset-umi/src/commands/generators/mock.ts b/packages/preset-umi/src/commands/generators/mock.ts new file mode 100644 index 000000000000..2a48c53debe2 --- /dev/null +++ b/packages/preset-umi/src/commands/generators/mock.ts @@ -0,0 +1,37 @@ +import { GeneratorType } from '@umijs/core'; +import { join } from 'path'; +import { TEMPLATES_DIR } from '../../constants'; +import { IApi } from '../../types'; +import { GeneratorHelper, trim } from './utils'; + +export default (api: IApi) => { + api.describe({ + key: 'generator:mock', + }); + + api.registerGenerator({ + key: 'mock', + type: GeneratorType.generate, + name: 'Generate mock code snippet', + + fn: async (opts) => { + let [_, mockName] = opts.args._; + + const h = new GeneratorHelper(api); + + mockName = await h.ensureVariableWithQuestion(mockName, { + type: 'text', + message: 'please input your mock file name', + initial: 'mockName', + format: trim, + }); + + opts.generateFile({ + target: join(api.paths.cwd, 'mock', `${mockName}.ts`), + baseDir: api.paths.cwd, + path: join(TEMPLATES_DIR, 'generate/mock.ts.tpl'), + data: { mockName }, + }); + }, + }); +}; diff --git a/packages/preset-umi/src/commands/generators/page.ts b/packages/preset-umi/src/commands/generators/page.ts index 847ba316075d..81c325399d5c 100644 --- a/packages/preset-umi/src/commands/generators/page.ts +++ b/packages/preset-umi/src/commands/generators/page.ts @@ -1,6 +1,7 @@ import { GeneratorType } from '@umijs/core'; import { generateFile, prompts, randomColor } from '@umijs/utils'; import { join, parse } from 'path'; +import { TEMPLATES_DIR } from '../../constants'; import { IApi } from '../../types'; import { promptsExitWhenCancel } from './utils'; @@ -24,14 +25,8 @@ export default (api: IApi) => { }); }; -const INDEX_TPL_PATH = join( - __dirname, - '../../../templates/generate/page/index.tsx.tpl', -); -const LEES_TPL_PATH = join( - __dirname, - '../../../templates/generate/page/index.less.tpl', -); +const INDEX_TPL_PATH = join(TEMPLATES_DIR, 'generate/page/index.tsx.tpl'); +const LEES_TPL_PATH = join(TEMPLATES_DIR, 'generate/page/index.less.tpl'); const DEFAULT_PAGE_NAME = 'unTitledPage'; export class PageGenerator { diff --git a/packages/preset-umi/src/commands/generators/utils.ts b/packages/preset-umi/src/commands/generators/utils.ts index 6d2b25604b28..aff0b15d9b90 100644 --- a/packages/preset-umi/src/commands/generators/utils.ts +++ b/packages/preset-umi/src/commands/generators/utils.ts @@ -97,6 +97,22 @@ export class GeneratorHelper { }); logger.info(`Install dependencies with ${npmClient}`); } + + async ensureVariableWithQuestion( + v: V, + question: Omit, 'name'>, + ) { + if (!v) { + const res = await promptsExitWhenCancel({ + ...question, + name: 'variable', + }); + + return res.variable ? res.variable : question.initial; + } + + return v; + } } export function getUmiJsPlugin() { @@ -121,3 +137,7 @@ export function promptsExitWhenCancel( }, }); } + +export function trim(s?: string) { + return s?.trim() || ''; +} diff --git a/packages/preset-umi/src/index.ts b/packages/preset-umi/src/index.ts index 8557a560149c..8499ac69ed53 100644 --- a/packages/preset-umi/src/index.ts +++ b/packages/preset-umi/src/index.ts @@ -40,6 +40,9 @@ export default () => { require.resolve('./commands/generators/jest'), require.resolve('./commands/generators/tailwindcss'), require.resolve('./commands/generators/dva'), + require.resolve('./commands/generators/component'), + require.resolve('./commands/generators/mock'), + require.resolve('./commands/generators/api'), require.resolve('./commands/plugin'), require.resolve('./commands/verify-commit'), ], diff --git a/packages/preset-umi/templates/generate/api.ts.tpl b/packages/preset-umi/templates/generate/api.ts.tpl new file mode 100644 index 000000000000..e06a189f7d9e --- /dev/null +++ b/packages/preset-umi/templates/generate/api.ts.tpl @@ -0,0 +1,11 @@ +import { UmiApiRequest, UmiApiResponse } from "umi"; + +export default async function (req: UmiApiRequest, res: UmiApiResponse) { + switch (req.method) { + case 'GET': + res.json({ {{{key}}}: {{{value}}} }) + break; + default: + res.status(405).json({ error: 'Method not allowed' }) + } +} diff --git a/packages/preset-umi/templates/generate/component/component.tsx.tpl b/packages/preset-umi/templates/generate/component/component.tsx.tpl new file mode 100644 index 000000000000..b21b2896407e --- /dev/null +++ b/packages/preset-umi/templates/generate/component/component.tsx.tpl @@ -0,0 +1,5 @@ +import React from 'react' + +export default function {{{compName}}}() { + return
{{{compName}}} is a awesome component
+} diff --git a/packages/preset-umi/templates/generate/component/index.ts.tpl b/packages/preset-umi/templates/generate/component/index.ts.tpl new file mode 100644 index 000000000000..391f0b7dc58a --- /dev/null +++ b/packages/preset-umi/templates/generate/component/index.ts.tpl @@ -0,0 +1,4 @@ +import {{{compName}}} from './{{{compName}}}' + +export default {{{compName}}} +export * from './{{{compName}}}' diff --git a/packages/preset-umi/templates/generate/mock.ts.tpl b/packages/preset-umi/templates/generate/mock.ts.tpl new file mode 100644 index 000000000000..2fae8eb07fe8 --- /dev/null +++ b/packages/preset-umi/templates/generate/mock.ts.tpl @@ -0,0 +1,9 @@ +export default { + 'GET /api/{{{mockName}}}': (_req: any, res: any) => { + res.json({ + success: true, + data: {}, + errorCode: 0, + }); + }, +};