Skip to content

Commit

Permalink
Feat: api mock component generator (umijs#558)
Browse files Browse the repository at this point in the history
* refactor: 🎨 use template dir constant

* feat: βž• component template files

* refactor: 🎨 add ensureWithQustion to helper

* feat: ✨ add component generator

* test: βœ… fix windows failure case

* fix: πŸ› use initial use default value

* feat: ✨ mock generator

* feat: ✨generate support  api

* fix: πŸ›  mock should stay at `paths.cwd`

* refactor: πŸ”₯ use only one way to demo mock defination

Co-authored-by: pshu <[email protected]>
  • Loading branch information
stormslowly and stormslowly authored Apr 8, 2022
1 parent 9b04d46 commit 97b9c97
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 8 deletions.
43 changes: 43 additions & 0 deletions packages/preset-umi/src/commands/generators/api.test.ts
Original file line number Diff line number Diff line change
@@ -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"]',
});
});
74 changes: 74 additions & 0 deletions packages/preset-umi/src/commands/generators/api.ts
Original file line number Diff line number Diff line change
@@ -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)}]` };
}
60 changes: 60 additions & 0 deletions packages/preset-umi/src/commands/generators/component.test.ts
Original file line number Diff line number Diff line change
@@ -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,
};
}
93 changes: 93 additions & 0 deletions packages/preset-umi/src/commands/generators/component.ts
Original file line number Diff line number Diff line change
@@ -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');
37 changes: 37 additions & 0 deletions packages/preset-umi/src/commands/generators/mock.ts
Original file line number Diff line number Diff line change
@@ -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 },
});
},
});
};
11 changes: 3 additions & 8 deletions packages/preset-umi/src/commands/generators/page.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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 {
Expand Down
20 changes: 20 additions & 0 deletions packages/preset-umi/src/commands/generators/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ export class GeneratorHelper {
});
logger.info(`Install dependencies with ${npmClient}`);
}

async ensureVariableWithQuestion<V>(
v: V,
question: Omit<prompts.PromptObject<'variable'>, 'name'>,
) {
if (!v) {
const res = await promptsExitWhenCancel({
...question,
name: 'variable',
});

return res.variable ? res.variable : question.initial;
}

return v;
}
}

export function getUmiJsPlugin() {
Expand All @@ -121,3 +137,7 @@ export function promptsExitWhenCancel<T extends string = string>(
},
});
}

export function trim(s?: string) {
return s?.trim() || '';
}
3 changes: 3 additions & 0 deletions packages/preset-umi/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
],
Expand Down
11 changes: 11 additions & 0 deletions packages/preset-umi/templates/generate/api.ts.tpl
Original file line number Diff line number Diff line change
@@ -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' })
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react'

export default function {{{compName}}}() {
return <div>{{{compName}}} is a awesome component</div>
}
Loading

0 comments on commit 97b9c97

Please sign in to comment.