refactor: 💡 Rax 的出码插件有很多地方与 React 不同,还是单独拎出来为好

This commit is contained in:
牧毅 2020-08-12 10:34:27 +08:00
parent 63d5b6e4b0
commit a66ec82879
10 changed files with 507 additions and 9 deletions

View File

@ -0,0 +1,31 @@
import { COMMON_CHUNK_NAME } from '../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: `import { createElement, Component } from 'rax';`,
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,9 @@
export const RAX_CHUNK_NAME = {
ClassRenderStart: 'RaxComponentClassRenderStart',
ClassRenderPre: 'RaxComponentClassRenderPre',
ClassRenderEnd: 'RaxComponentClassRenderEnd',
ClassRenderJSX: 'RaxComponentClassRenderJSX',
ClassDidMountStart: 'RaxComponentClassDidMountStart',
ClassDidMountEnd: 'RaxComponentClassDidMountEnd',
ClassDidMountContent: 'RaxComponentClassDidMountContent',
};

View File

@ -0,0 +1,108 @@
import changeCase from 'change-case';
import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import { RAX_CHUNK_NAME } from './const';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
// 将模块名转换成 PascalCase 的格式,并添加特定后缀,防止命名冲突
const componentClassName = `${changeCase.pascalCase(ir.moduleName)}$$Page`;
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.Start,
content: `class ${componentClassName} extends Component {`,
linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine,
],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.End,
content: `}`,
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start, RAX_CHUNK_NAME.ClassRenderEnd],
});
// next.chunks.push({
// type: ChunkType.STRING,
// fileType: FileType.JSX,
// name: CLASS_DEFINE_CHUNK_NAME.ConstructorStart,
// content: 'constructor(props, context) { super(props); ',
// linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start],
// });
// next.chunks.push({
// type: ChunkType.STRING,
// fileType: FileType.JSX,
// name: CLASS_DEFINE_CHUNK_NAME.ConstructorEnd,
// content: '}',
// linkAfter: [
// CLASS_DEFINE_CHUNK_NAME.ConstructorStart,
// CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
// ],
// });
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: RAX_CHUNK_NAME.ClassRenderStart,
content: 'render() {',
linkAfter: [
CLASS_DEFINE_CHUNK_NAME.Start,
CLASS_DEFINE_CHUNK_NAME.ConstructorEnd,
CLASS_DEFINE_CHUNK_NAME.InsMethod,
],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: RAX_CHUNK_NAME.ClassRenderEnd,
content: '}',
linkAfter: [
RAX_CHUNK_NAME.ClassRenderStart,
RAX_CHUNK_NAME.ClassRenderPre,
RAX_CHUNK_NAME.ClassRenderJSX,
],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.FileExport,
content: `export default ${componentClassName};`,
linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine,
CLASS_DEFINE_CHUNK_NAME.End,
],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,52 @@
import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import { generateCompositeType } from '../../../utils/compositeType';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
type PluginConfig = {
fileType: string;
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
if (ir.state) {
const state = ir.state;
const fields = Object.keys(state).map<string>(stateName => {
const [isString, value] = generateCompositeType(state[stateName]);
return `${stateName}: ${isString ? `'${value}'` : value},`;
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
content: `this.state = { ${fields.join('')} };`,
linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart],
});
}
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,64 @@
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { generateCompositeType } from '../../../utils/compositeType';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
type PluginConfig = {
fileType: string;
implementType: 'inConstructor' | 'insMember' | 'hooks';
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
implementType: 'inConstructor',
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
if (ir.state) {
const state = ir.state;
const fields = Object.keys(state).map<string>(stateName => {
const [isString, value] = generateCompositeType(state[stateName]);
return `${stateName}: ${isString ? `'${value}'` : value},`;
});
if (cfg.implementType === 'inConstructor') {
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
content: `this.state = { ${fields.join('')} };`,
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorContent]],
});
} else if (cfg.implementType === 'insMember') {
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: `state = { ${fields.join('')} };`,
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsVar]],
});
}
}
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,40 @@
import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../../types';
type PluginConfig = {
fileType: string;
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
// TODO: utils 怎么注入?
// next.chunks.push({
// type: ChunkType.STRING,
// fileType: cfg.fileType,
// name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
// content: `this.utils = utils;`,
// linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart],
// });
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,90 @@
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { RAX_CHUNK_NAME } from './const';
import {
getFuncExprBody,
transformFuncExpr2MethodMember,
} from '../../../utils/jsExpression';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
CodeGeneratorError,
FileType,
ICodeChunk,
ICodeStruct,
IContainerInfo,
IJSExpression,
} from '../../../types';
type PluginConfig = {
fileType: string;
exportNameMapping: Record<string, string>;
normalizeNameMapping: Record<string, string>;
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
exportNameMapping: {},
normalizeNameMapping: {},
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
if (ir.lifeCycles) {
const lifeCycles = ir.lifeCycles;
const chunks = Object.keys(lifeCycles).map<ICodeChunk>(lifeCycleName => {
const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName;
const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName;
if (normalizeName === 'constructor') {
return {
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
content: getFuncExprBody(
(lifeCycles[lifeCycleName] as IJSExpression).value,
),
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorStart]],
};
}
if (normalizeName === 'render') {
return {
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.ClassRenderPre,
content: getFuncExprBody(
(lifeCycles[lifeCycleName] as IJSExpression).value,
),
linkAfter: [RAX_CHUNK_NAME.ClassRenderStart],
};
}
return {
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsMethod,
content: transformFuncExpr2MethodMember(
exportName,
(lifeCycles[lifeCycleName] as IJSExpression).value,
),
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
};
});
next.chunks.push.apply(next.chunks, chunks);
}
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,54 @@
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { transformFuncExpr2MethodMember } from '../../../utils/jsExpression';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeChunk,
ICodeStruct,
IContainerInfo,
IJSExpression,
} from '../../../types';
type PluginConfig = {
fileType: string;
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
if (ir.methods) {
const methods = ir.methods;
const chunks = Object.keys(methods).map<ICodeChunk>(methodName => ({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsMethod,
content: transformFuncExpr2MethodMember(
methodName,
(methods[methodName] as IJSExpression).value,
),
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
}));
next.chunks.push.apply(next.chunks, chunks);
}
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,50 @@
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
import { RAX_CHUNK_NAME } from './const';
import { createReactNodeGenerator } from '../../../utils/nodeToJSX';
type PluginConfig = {
fileType: string;
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const generator = createReactNodeGenerator();
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
const jsxContent = generator(ir);
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.ClassRenderJSX,
content: `return ${jsxContent};`,
linkAfter: [
RAX_CHUNK_NAME.ClassRenderStart,
RAX_CHUNK_NAME.ClassRenderPre,
],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -3,13 +3,13 @@ import { IProjectBuilder } from '../types';
import { createProjectBuilder } from '../generator/ProjectBuilder';
import esModule from '../plugins/common/esmodule';
import containerClass from '../plugins/component/react/containerClass';
import containerInitState from '../plugins/component/react/containerInitState';
import containerInjectUtils from '../plugins/component/react/containerInjectUtils';
import containerLifeCycle from '../plugins/component/react/containerLifeCycle';
import containerMethod from '../plugins/component/react/containerMethod';
import jsx from '../plugins/component/react/jsx';
import reactCommonDeps from '../plugins/component/react/reactCommonDeps';
import containerClass from '../plugins/component/rax/containerClass';
import containerInitState from '../plugins/component/rax/containerInitState';
import containerInjectUtils from '../plugins/component/rax/containerInjectUtils';
import containerLifeCycle from '../plugins/component/rax/containerLifeCycle';
import containerMethod from '../plugins/component/rax/containerMethod';
import jsx from '../plugins/component/rax/jsx';
import commonDeps from '../plugins/component/rax/commonDeps';
import css from '../plugins/component/style/css';
import constants from '../plugins/project/constants';
import i18n from '../plugins/project/i18n';
@ -24,7 +24,7 @@ export default function createIceJsProjectBuilder(): IProjectBuilder {
template: raxApp.template,
plugins: {
components: [
reactCommonDeps(),
commonDeps(),
esModule({
fileType: 'jsx',
}),
@ -37,7 +37,7 @@ export default function createIceJsProjectBuilder(): IProjectBuilder {
css(),
],
pages: [
reactCommonDeps(),
commonDeps(),
esModule({
fileType: 'jsx',
}),