refactor: 💡 friendly nodeToJsx API & esmodule alias logic

This commit is contained in:
春希 2020-08-17 17:07:02 +08:00
parent e689d7f341
commit 7359d1d985
11 changed files with 98 additions and 85 deletions

View File

@ -171,5 +171,5 @@ function exportProject() {
// main();
// exportModule();
// exportProject();
demo();
exportProject();
// demo();

View File

@ -146,7 +146,7 @@ class SchemaParser implements ISchemaParser {
}
}
}
return [''];
return '';
},
},
{
@ -236,7 +236,7 @@ class SchemaParser implements ISchemaParser {
return handleSubNodes<string>(
children,
{
node: (i: NodeSchema) => [i.componentName],
node: (i: NodeSchema) => i.componentName,
},
{
rerun: true,

View File

@ -50,7 +50,12 @@ function groupDepsByPack(deps: IDependency[]): Record<string, IDependency[]> {
return depMap;
}
function buildPackageImport(pkg: string, deps: IDependency[], targetFileType: string): ICodeChunk[] {
function buildPackageImport(
pkg: string,
deps: IDependency[],
targetFileType: string,
useAliasName: boolean,
): ICodeChunk[] {
const chunks: ICodeChunk[] = [];
let defaultImport = '';
let defaultImportAs = '';
@ -60,20 +65,8 @@ function buildPackageImport(pkg: string, deps: IDependency[], targetFileType: st
const srcName = dep.exportName;
let targetName = dep.componentName || dep.exportName;
// 如果是自组件,则导出父组件,并且根据自组件命名规则,判断是否需要定义标识符
if (dep.subName) {
chunks.push({
type: ChunkType.STRING,
fileType: targetFileType,
name: COMMON_CHUNK_NAME.ImportAliasDefine,
content: `const ${targetName} = ${srcName}.${dep.subName};`,
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport],
ext: {
originalName: `${srcName}.${dep.subName}`,
aliasName: targetName,
dependency: dep,
},
});
if (targetName !== `${srcName}.${dep.subName}`) {
if (!isValidIdentifier(targetName)) {
throw new CodeGeneratorError(`Invalid Identifier [${targetName}]`);
@ -82,9 +75,14 @@ function buildPackageImport(pkg: string, deps: IDependency[], targetFileType: st
chunks.push({
type: ChunkType.STRING,
fileType: targetFileType,
name: COMMON_CHUNK_NAME.FileVarDefine,
name: COMMON_CHUNK_NAME.ImportAliasDefine,
content: `const ${targetName} = ${srcName}.${dep.subName};`,
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport],
ext: {
originalName: `${srcName}.${dep.subName}`,
aliasName: targetName,
dependency: dep,
},
});
}
@ -94,30 +92,35 @@ function buildPackageImport(pkg: string, deps: IDependency[], targetFileType: st
if (dep.destructuring) {
imports[srcName] = targetName;
} else if (defaultImport) {
// 有些时候,可能已经从某个包里引入了一个东东,但是希望能再起个别名,这时候用赋值语句代替
chunks.push({
type: ChunkType.STRING,
fileType: targetFileType,
name: COMMON_CHUNK_NAME.ImportAliasDefine,
content: `const ${targetName} = ${defaultImportAs};`,
linkAfter: [COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport],
ext: {
originalName: defaultImportAs,
aliasName: targetName,
dependency: dep,
},
});
throw new CodeGeneratorError(`[${pkg}] has more than one default export.`);
} else {
defaultImport = srcName;
defaultImportAs = targetName;
}
if (targetName !== srcName) {
chunks.push({
type: ChunkType.STRING,
fileType: targetFileType,
name: COMMON_CHUNK_NAME.ImportAliasDefine,
content: '',
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport],
ext: {
originalName: srcName,
aliasName: targetName,
dependency: dep,
},
});
}
});
const items = Object.keys(imports).map((src) => (src === imports[src] ? src : `${src} as ${imports[src]}`));
const items = Object.keys(imports).map((src) =>
src === imports[src] || !useAliasName ? src : `${src} as ${imports[src]}`,
);
const statementL = ['import'];
if (defaultImport) {
statementL.push(defaultImportAs);
statementL.push(useAliasName ? defaultImportAs : defaultImport);
if (items.length > 0) {
statementL.push(',');
}
@ -152,13 +155,15 @@ function buildPackageImport(pkg: string, deps: IDependency[], targetFileType: st
}
type PluginConfig = {
fileType: string;
fileType?: string; // 导出的文件类型
useAliasName?: boolean; // 是否使用 componentName 重命名组件 identifier
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?: PluginConfig) => {
const cfg: PluginConfig = {
const cfg = {
fileType: FileType.JS,
...config,
useAliasName: true,
...(config || {}),
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
@ -172,7 +177,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?: Plu
const packs = groupDepsByPack(ir.deps);
Object.keys(packs).forEach((pkg) => {
const chunks = buildPackageImport(pkg, packs[pkg], cfg.fileType);
const chunks = buildPackageImport(pkg, packs[pkg], cfg.fileType, cfg.useAliasName);
next.chunks.push(...chunks);
});
}

View File

@ -1,19 +1,11 @@
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { RAX_CHUNK_NAME } from './const';
// import { JSExpression } from '@ali/lowcode-types';
import { getFuncExprBody, transformFuncExpr2MethodMember } from '../../../utils/jsExpression';
// import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
// import { RAX_CHUNK_NAME } from './const';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
CodeGeneratorError,
FileType,
ICodeChunk,
ICodeStruct,
IContainerInfo,
JSExpression,
} from '../../../types';
// import { getFuncExprBody, transformFuncExpr2MethodMember } from '../../../utils/jsExpression';
import { BuilderComponentPlugin, BuilderComponentPluginFactory, ICodeStruct } from '../../../types';
type PluginConfig = {
fileType: string;
@ -22,12 +14,12 @@ type PluginConfig = {
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
exportNameMapping: {},
normalizeNameMapping: {},
...config,
};
// const cfg: PluginConfig = {
// fileType: FileType.JSX,
// exportNameMapping: {},
// normalizeNameMapping: {},
// ...config,
// };
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {

View File

@ -1,4 +1,4 @@
import { CLASS_DEFINE_CHUNK_NAME, COMMON_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import {
BuilderComponentPlugin,

View File

@ -28,7 +28,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
<meta name="viewport" content="width=device-width" />
<title>${ir.meta.name}</title>
<title>${ir?.meta?.name || 'Ice App'}</title>
</head>
<body>
<div id="app"></div>

View File

@ -34,7 +34,7 @@ function Document() {
name="viewport"
content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover"
/>
<title>${ir.meta.name}</title>
<title>${ir?.meta?.name || 'Rax App'}</title>
<Style />
</head>
<body>

View File

@ -27,7 +27,7 @@ export default function createIceJsProjectBuilder(): IProjectBuilder {
plugins: {
components: [
commonDeps(),
esModule({ fileType: 'jsx' }),
esModule({ fileType: 'jsx', useAliasName: false }),
containerClass(),
containerInitState(),
containerMethods(),

View File

@ -154,9 +154,12 @@ export interface HandlerSet<T> {
slot?: (input: JSSlot) => T;
node?: (input: NodeSchema) => T;
array?: (input: any[]) => T;
children?: (input: T[]) => T;
object?: (input: object) => T;
common?: (input: unknown) => T;
tagName?: (input: string) => T;
loopDataExpr?: (input: string) => T;
conditionExpr?: (input: string) => T;
}
export type ExtGeneratorPlugin = (ctx: INodeGeneratorContext, nodeItem: NodeSchema) => CodePiece[];
@ -170,6 +173,7 @@ export type NodeGenerator = (nodeItem: NodeDataType) => string;
export interface INodeGeneratorContext {
handlers: HandlerSet<string>;
plugins: ExtGeneratorPlugin[];
generator: NodeGenerator;
}

View File

@ -1,4 +1,4 @@
import { JSExpression, JSFunction, isJsExpression, isJsFunction } from '@ali/lowcode-types';
import { JSExpression, JSFunction, isJSExpression, isJSFunction } from '@ali/lowcode-types';
import { CodeGeneratorError } from '../types';
export function transformFuncExpr2MethodMember(methodName: string, functionBody: string): string {
@ -40,11 +40,11 @@ export function getArrowFunction(functionBody: string) {
}
export function isJsCode(value: unknown): boolean {
return isJsExpression(value) || isJsFunction(value);
return isJSExpression(value) || isJSFunction(value);
}
export function generateExpression(value: any): string {
if (isJsExpression(value)) {
if (isJSExpression(value)) {
return (value as JSExpression).value || 'null';
}

View File

@ -18,7 +18,6 @@ import {
CodePiece,
HandlerSet,
NodeGenerator,
ExtGeneratorPlugin,
INodeGeneratorContext,
NodeGeneratorConfig,
} from '../types';
@ -26,7 +25,7 @@ import {
import { generateCompositeType } from './compositeType';
// tslint:disable-next-line: no-empty
const noop = () => [];
const noop = () => undefined;
const handleChildrenDefaultOptions = {
rerun: false,
@ -47,19 +46,24 @@ export function handleSubNodes<T>(
if (Array.isArray(children)) {
const list: NodeData[] = children as NodeData[];
return list.map((child) => handleSubNodes(child, handlers, opt)).reduce((p, c) => p.concat(c), []);
} else if (isDOMText(children)) {
}
let result: T | undefined = undefined;
const childrenRes: T[] = [];
if (isDOMText(children)) {
const handler = handlers.string || handlers.common || noop;
return handler(children as string);
result = handler(children as string);
} else if (isJSExpression(children)) {
const handler = handlers.expression || handlers.common || noop;
return handler(children as JSExpression);
result = handler(children as JSExpression);
} else {
const handler = handlers.node || handlers.common || noop;
const child = children as NodeSchema;
let curRes = handler(child);
result = handler(child);
if (opt.rerun && child.children) {
const childRes = handleSubNodes(child.children, handlers, opt);
curRes = curRes.concat(childRes || []);
childrenRes.push(...childRes);
}
if (child.props) {
// FIXME: currently only support PropsMap
@ -69,11 +73,16 @@ export function handleSubNodes<T>(
.forEach((propName) => {
const soltVals = (childProps[propName] as JSSlot).value;
const childRes = handleSubNodes(soltVals, handlers, opt);
curRes = curRes.concat(childRes || []);
childrenRes.push(...childRes);
});
}
return curRes;
}
if (result !== undefined) {
childrenRes.unshift(result);
}
return childrenRes;
}
export function generateAttr(ctx: INodeGeneratorContext, attrName: string, attrValue: any): CodePiece[] {
@ -167,18 +176,19 @@ export function linkPieces(pieces: CodePiece[]): string {
export function generateNodeSchema(nodeItem: NodeSchema, ctx: INodeGeneratorContext): string {
let pieces: CodePiece[] = [];
plugins.forEach((p) => {
ctx.plugins.forEach((p) => {
pieces = pieces.concat(p(ctx, nodeItem));
});
pieces = pieces.concat(generateBasicNode(ctx, nodeItem));
pieces = pieces.concat(generateAttrs(ctx, nodeItem));
if (nodeItem.children && (nodeItem.children as unknown[]).length > 0) {
pieces = pieces.concat(
handleChildren<string>(ctx, nodeItem.children, handlers).map((l) => ({
type: PIECE_TYPE.CHILDREN,
value: l,
})),
);
if (nodeItem.children) {
const childrenStr = ctx.generator(nodeItem.children);
pieces.push({
type: PIECE_TYPE.CHILDREN,
value: childrenStr,
});
}
return linkPieces(pieces);
@ -191,6 +201,7 @@ export function generateNodeSchema(nodeItem: NodeSchema, ctx: INodeGeneratorCont
/**
* JSX React loop condition
* @type ExtGeneratorPlugin
*
* @export
* @param {NodeSchema} nodeItem UI
@ -251,15 +262,15 @@ export function generateReactCtrlLine(ctx: INodeGeneratorContext, nodeItem: Node
return pieces;
}
const handleArray = (v: string[]) => v.join('');
const handleChildren = (v: string[]) => v.join('');
export function createNodeGenerator(cfg: NodeGeneratorConfig = {}): NodeGenerator {
let ctx: INodeGeneratorContext = { handlers: {}, generator: () => '' };
let ctx: INodeGeneratorContext = { handlers: {}, plugins: [], generator: () => '' };
const generateNode = (nodeItem: NodeDataType): string => {
if (_.isArray(nodeItem)) {
const resList = nodeItem.map((n) => generateNode(n));
return (cfg?.handlers?.array || handleArray)(resList);
return (cfg?.handlers?.children || handleChildren)(resList);
}
if (isNodeSchema(nodeItem)) {
@ -281,6 +292,7 @@ export function createNodeGenerator(cfg: NodeGeneratorConfig = {}): NodeGenerato
ctx = {
handlers: cfg?.handlers || {},
plugins: cfg.plugins || [],
generator: generateNode,
};