diff --git a/packages/code-generator/src/plugins/component/rax/commonDeps.ts b/packages/code-generator/src/plugins/component/rax/commonDeps.ts new file mode 100644 index 000000000..561bb0022 --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/commonDeps.ts @@ -0,0 +1,31 @@ +import { COMMON_CHUNK_NAME } from '../../../const/generator'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + FileType, + ICodeStruct, + IContainerInfo, +} from '../../../types'; + +const pluginFactory: BuilderComponentPluginFactory = () => { + 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; diff --git a/packages/code-generator/src/plugins/component/rax/const.ts b/packages/code-generator/src/plugins/component/rax/const.ts new file mode 100644 index 000000000..317b41fa8 --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/const.ts @@ -0,0 +1,9 @@ +export const RAX_CHUNK_NAME = { + ClassRenderStart: 'RaxComponentClassRenderStart', + ClassRenderPre: 'RaxComponentClassRenderPre', + ClassRenderEnd: 'RaxComponentClassRenderEnd', + ClassRenderJSX: 'RaxComponentClassRenderJSX', + ClassDidMountStart: 'RaxComponentClassDidMountStart', + ClassDidMountEnd: 'RaxComponentClassDidMountEnd', + ClassDidMountContent: 'RaxComponentClassDidMountContent', +}; diff --git a/packages/code-generator/src/plugins/component/rax/containerClass.ts b/packages/code-generator/src/plugins/component/rax/containerClass.ts new file mode 100644 index 000000000..2f6918a8e --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerClass.ts @@ -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 = () => { + 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; diff --git a/packages/code-generator/src/plugins/component/rax/containerDataSource.ts b/packages/code-generator/src/plugins/component/rax/containerDataSource.ts new file mode 100644 index 000000000..72d7efd6b --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerDataSource.ts @@ -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 = (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(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; diff --git a/packages/code-generator/src/plugins/component/rax/containerInitState.ts b/packages/code-generator/src/plugins/component/rax/containerInitState.ts new file mode 100644 index 000000000..50984c961 --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerInitState.ts @@ -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 = (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(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; diff --git a/packages/code-generator/src/plugins/component/rax/containerInjectUtils.ts b/packages/code-generator/src/plugins/component/rax/containerInjectUtils.ts new file mode 100644 index 000000000..b72eb8668 --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerInjectUtils.ts @@ -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 = (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; diff --git a/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts b/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts new file mode 100644 index 000000000..65d411242 --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts @@ -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; + normalizeNameMapping: Record; +} + +const pluginFactory: BuilderComponentPluginFactory = (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(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; diff --git a/packages/code-generator/src/plugins/component/rax/containerMethod.ts b/packages/code-generator/src/plugins/component/rax/containerMethod.ts new file mode 100644 index 000000000..9bba467da --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerMethod.ts @@ -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 = (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(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; diff --git a/packages/code-generator/src/plugins/component/rax/jsx.ts b/packages/code-generator/src/plugins/component/rax/jsx.ts new file mode 100644 index 000000000..f41aa9de1 --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/jsx.ts @@ -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 = (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; diff --git a/packages/code-generator/src/solutions/rax-app.ts b/packages/code-generator/src/solutions/rax-app.ts index e0e89b8c5..be7c55c31 100644 --- a/packages/code-generator/src/solutions/rax-app.ts +++ b/packages/code-generator/src/solutions/rax-app.ts @@ -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', }),