Merge pull request #1698 from alibaba/feat/0228

feat: 出码引擎 1.0.8
This commit is contained in:
刘菊萍(絮黎) 2023-03-14 11:07:44 +08:00 committed by GitHub
commit 41753de24a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 412 additions and 171 deletions

View File

@ -15,7 +15,6 @@ module.exports = {
'no-prototype-builtins': 1,
'no-useless-constructor': 1,
'no-empty-function': 1,
'@typescript-eslint/member-ordering': 0,
'lines-between-class-members': 0,
'no-await-in-loop': 0,
'no-plusplus': 0,
@ -35,23 +34,24 @@ module.exports = {
'@typescript-eslint/indent': 0,
'import/no-cycle': 0,
'@typescript-eslint/no-shadow': 0,
"@typescript-eslint/method-signature-style": 0,
"@typescript-eslint/consistent-type-assertions": 0,
"@typescript-eslint/no-useless-constructor": 0,
'@typescript-eslint/method-signature-style': 0,
'@typescript-eslint/consistent-type-assertions': 0,
'@typescript-eslint/no-useless-constructor': 0,
'@typescript-eslint/dot-notation': 0, // for lint performance
'@typescript-eslint/restrict-plus-operands': 0, // for lint performance
'no-unexpected-multiline': 1,
'no-multiple-empty-lines': ['error', { "max": 1 }],
'no-multiple-empty-lines': ['error', { max: 1 }],
'lines-around-comment': ['error', {
"beforeBlockComment": true,
"afterBlockComment": false,
"afterLineComment": false,
"allowBlockStart": true,
beforeBlockComment: true,
afterBlockComment: false,
afterLineComment: false,
allowBlockStart: true,
}],
"no-unused-vars": ['error', { "destructuredArrayIgnorePattern": "^_" }],
"@typescript-eslint/member-ordering": [
"error",
{ "default": ["signature", "field", "constructor", "method"] }
'comma-dangle': ['error', 'always-multiline'],
'@typescript-eslint/member-ordering': [
'error',
{ default: ['signature', 'field', 'constructor', 'method'] }
],
}
'no-unused-vars': ['error', { "destructuredArrayIgnorePattern": "^_" }]
},
};

View File

@ -71,7 +71,7 @@
},
"lifeCycles": {
"componentDidMount": {
"type": "JSExpression",
"type": "JSFunction",
"value": "function() { console.log('componentDidMount'); }"
}
},
@ -91,7 +91,7 @@
"isSync": true
},
"dataHandler": {
"type": "JSExpression",
"type": "JSFunction",
"value": "function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data;\n}"
}
},
@ -105,13 +105,13 @@
"isSync": true
},
"dataHandler": {
"type": "JSExpression",
"type": "JSFunction",
"value": "function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data.result;\n}"
}
}
],
"dataHandler": {
"type": "JSExpression",
"type": "JSFunction",
"value": "function (dataMap) {\n console.info(\"All datasources loaded:\", dataMap);\n}"
}
},

View File

@ -71,7 +71,7 @@
},
lifeCycles: {
componentDidMount: {
type: 'JSExpression',
type: 'JSFunction',
value: "function() { console.log('componentDidMount'); }",
},
},
@ -91,7 +91,7 @@
isSync: true,
},
dataHandler: {
type: 'JSExpression',
type: 'JSFunction',
value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data;\n}',
},
},
@ -105,13 +105,13 @@
isSync: true,
},
dataHandler: {
type: 'JSExpression',
type: 'JSFunction',
value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data.result;\n}',
},
},
],
dataHandler: {
type: 'JSExpression',
type: 'JSFunction',
value: 'function (dataMap) {\n console.info("All datasources loaded:", dataMap);\n}',
},
},

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-code-generator",
"version": "1.0.7",
"version": "1.0.8",
"description": "出码引擎 for LowCode Engine",
"license": "MIT",
"main": "lib/index.js",

View File

@ -8,5 +8,26 @@ export const CONTAINER_TYPE = {
export const SUPPORT_SCHEMA_VERSION_LIST = ['0.0.1', '1.0.0'];
// built-in slot names which have been handled in ProjectBuilder
export const BUILTIN_SLOT_NAMES = [
'pages',
'components',
'router',
'entry',
'appConfig',
'buildConfig',
'constants',
'utils',
'i18n',
'globalStyle',
'htmlEntry',
'packageJSON',
'demo',
];
export const isBuiltinSlotName = function (name: string) {
return BUILTIN_SLOT_NAMES.includes(name);
};
export * from './file';
export * from './generator';

View File

@ -62,10 +62,9 @@ export function createModuleBuilder(
if (options.postProcessors.length > 0) {
files = files.map((file) => {
let { content } = file;
const type = file.ext;
let { content, ext: type, name } = file;
options.postProcessors.forEach((processer) => {
content = processer(content, type);
content = processer(content, type, name);
});
return createResultFile(file.name, type, content);

View File

@ -16,6 +16,7 @@ import { createResultDir, addDirectory, addFile } from '../utils/resultHelper';
import { createModuleBuilder } from './ModuleBuilder';
import { ProjectPreProcessor, ProjectPostProcessor, IContextData } from '../types/core';
import { CodeGeneratorError } from '../types/error';
import { isBuiltinSlotName } from '../const';
interface IModuleInfo {
moduleName?: string;
@ -24,22 +25,31 @@ interface IModuleInfo {
}
export interface ProjectBuilderInitOptions {
/** 项目模板 */
template: IProjectTemplate;
/** 项目插件 */
plugins: IProjectPlugins;
/** 模块后置处理器 */
postProcessors: PostProcessor[];
/** Schema 解析器 */
schemaParser?: ISchemaParser;
/** 项目级别的前置处理器 */
projectPreProcessors?: ProjectPreProcessor[];
/** 项目级别的后置处理器 */
projectPostProcessors?: ProjectPostProcessor[];
/** 是否处于严格模式 */
inStrictMode?: boolean;
/** 一些额外的上下文数据 */
extraContextData?: Record<string, unknown>;
/**
* Hook which is used to customize original options, we can reorder/add/remove plugins/processors
* of the existing solution.
@ -126,6 +136,7 @@ export class ProjectBuilder implements IProjectBuilder {
const builders = this.createModuleBuilders({
extraContextData: {
projectRemark: parseResult?.project?.projectRemark,
template: this.template,
},
});
// Generator Code module
@ -263,7 +274,8 @@ export class ProjectBuilder implements IProjectBuilder {
});
}
// TODO: 更多 slots 的处理??是不是可以考虑把 template 中所有的 slots 都处理下?
// handle extra slots
await this.generateExtraSlots(builders, parseResult, buildResult);
// Post Process
const isSingleComponent = parseResult?.project?.projectRemark?.isSingleComponent;
@ -320,6 +332,22 @@ export class ProjectBuilder implements IProjectBuilder {
return builders;
}
private async generateExtraSlots(
builders: Record<string, IModuleBuilder>,
parseResult: IParseResult,
buildResult: IModuleInfo[],
) {
for (const slotName in this.template.slots) {
if (!isBuiltinSlotName(slotName)) {
const { files } = await builders[slotName].generateModule(parseResult);
buildResult.push({
path: this.template.slots[slotName].path,
files,
});
}
}
}
}
export function createProjectBuilder(initOptions: ProjectBuilderInitOptions): IProjectBuilder {

View File

@ -1,6 +1,6 @@
/**
* Schema
* API , API
* API API
* export { xxx } from 'xx' export * from 'xxx')
* API tests/public
*/
@ -51,6 +51,7 @@ export default {
},
plugins: {
common: {
/**
* ES Module
* @deprecated please use esModule

View File

@ -443,6 +443,7 @@ function buildPackageImport(
export interface PluginConfig {
fileType?: string; // 导出的文件类型
useAliasName?: boolean; // 是否使用 componentName 重命名组件 identifier
filter?: (deps: IDependency[]) => IDependency[]; // 支持过滤能力
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?: PluginConfig) => {
@ -460,7 +461,8 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?: Plu
const ir = next.ir as IWithDependency;
if (ir && ir.deps && ir.deps.length > 0) {
const packs = groupDepsByPack(ir.deps);
const deps = cfg.filter ? cfg.filter(ir.deps) : ir.deps;
const packs = groupDepsByPack(deps);
Object.keys(packs).forEach((pkg) => {
const chunks = buildPackageImport(pkg, packs[pkg], cfg.fileType, cfg.useAliasName);

View File

@ -13,6 +13,7 @@ import {
IContainerInfo,
} from '../../../types';
import { debug } from '../../../utils/debug';
import { isJSExpressionFn } from '../../../utils/common';
export interface PluginConfig {
fileType: string;
@ -49,6 +50,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
// 过滤掉非法数据(有些场景下会误传入空字符串或 null)
if (
!isJSFunction(lifeCycles[lifeCycleName]) &&
!isJSExpressionFn(lifeCycles[lifeCycleName]) &&
!isJSExpression(lifeCycles[lifeCycleName])
) {
return;

View File

@ -75,8 +75,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
// 注意这里其实隐含了一个假设schema 中的 componentName 应该是一个有效的 JS 标识符,而且是大写字母打头的
// FIXME: 为了快速修复临时加的逻辑,需要用 pre-process 的方式替代处理。
const mapComponentNameToAliasOrKeepIt = (componentName: string) =>
componentsNameAliasMap.get(componentName) || componentName;
const mapComponentNameToAliasOrKeepIt = (componentName: string) => componentsNameAliasMap.get(componentName) || componentName;
// 然后过滤掉所有的别名 chunks
next.chunks = next.chunks.filter((chunk) => !isImportAliasDefineChunk(chunk));

View File

@ -26,7 +26,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
// 将模块名转换成 PascalCase 的格式,并添加特定后缀,防止命名冲突
const componentClassName = ensureValidClassName(
`${changeCase.pascalCase(ir.moduleName)}$$Page`,
`${changeCase.pascalCase(ir.moduleName)}$$${ir.containerType}`,
);
next.chunks.push({
@ -43,6 +43,18 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
],
});
if (ir.containerType === 'Component') {
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: `static displayName = '${changeCase.pascalCase(ir.moduleName)}';`,
linkAfter: [
CLASS_DEFINE_CHUNK_NAME.Start,
],
});
}
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,

View File

@ -13,12 +13,12 @@ import {
} from '../../../types';
export interface PluginConfig {
fileType: string;
fileType?: string;
implementType: 'inConstructor' | 'insMember' | 'hooks';
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
const cfg: PluginConfig & { fileType: string } = {
fileType: FileType.JSX,
implementType: 'inConstructor',
...config,

View File

@ -29,6 +29,7 @@ import { generateCompositeType } from '../../../utils/compositeType';
import { parseExpressionConvertThis2Context } from '../../../utils/expressionParser';
import { isValidContainerType } from '../../../utils/schema';
import { REACT_CHUNK_NAME } from './const';
import { isJSExpressionFn } from '../../../utils/common';
export interface PluginConfig {
fileType?: string;
@ -37,6 +38,7 @@ export interface PluginConfig {
*
*/
datasourceConfig?: {
/** 数据源引擎的版本 */
engineVersion?: string;
@ -188,15 +190,15 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
export default pluginFactory;
function wrapAsFunction(value: IPublicTypeCompositeValue, scope: IScope): IPublicTypeCompositeValue {
if (isJSExpression(value) || isJSFunction(value)) {
if (isJSExpression(value) || isJSFunction(value) || isJSExpressionFn(value)) {
return {
type: 'JSExpression',
value: `function(){ return ((${value.value}))}`,
value: `function(){ return ((${value.value}))}.bind(this)`,
};
}
return {
type: 'JSExpression',
value: `function(){return((${generateCompositeType(value, scope)}))}`,
value: `function(){return((${generateCompositeType(value, scope)}))}.bind(this)`,
};
}

View File

@ -11,6 +11,7 @@ import {
FileType,
ICodeStruct,
} from '../../../types';
import { getSlotRelativePath } from '../../../utils/pathHelper';
export interface PluginConfig {
fileType: string;
@ -31,9 +32,8 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.InternalDepsImport,
// TODO: 下面这个路径有没有更好的方式来获取?而非写死
content: `
import * as __$$i18n from '../../i18n';
import * as __$$i18n from '${getSlotRelativePath({ contextData: next.contextData, from: 'components', to: 'i18n' })}';
`,
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport],
});

View File

@ -11,15 +11,18 @@ import {
FileType,
ICodeStruct,
IContainerInfo,
IProjectTemplate,
} from '../../../types';
import { getSlotRelativePath } from '../../../utils/pathHelper';
export interface PluginConfig {
fileType: string;
fileType?: string;
/** prefer using class property to define utils */
preferClassProperty?: boolean;
}
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
const cfg: PluginConfig & { fileType: string } = {
fileType: FileType.JSX,
...config,
};
@ -33,30 +36,27 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
next.contextData.useRefApi = true;
const useRef = !!ir.analyzeResult?.isUsingRef;
// const isSingleComponent = next.contextData?.projectRemark?.isSingleComponent;
// const template = next.contextData?.template;
// function getRelativeUtilsPath(template: IProjectTemplate, isSingleComponent: boolean) {
// let relativeUtilsPath = '../../utils';
// const utilsPath = template.slots.utils.path;
// if (ir.containerType === 'Component') {
// // TODO: isSingleComponent
// relativeUtilsPath = getRelativePath(template.slots.components.path.join('/'), utilsPath.join('/'));
// }
// return relativeUtilsPath;
// }
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.InternalDepsImport,
// TODO: 下面这个路径有没有更好的方式来获取?而非写死
content: `
import utils${useRef ? ', { RefsManager }' : ''} from '../../utils';
import utils${useRef ? ', { RefsManager }' : ''} from '${getSlotRelativePath({ contextData: next.contextData, from: 'components', to: 'utils' })}';
`,
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport],
});
if (cfg.preferClassProperty) {
// mode: class property
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: 'utils = utils;',
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsVar]],
});
} else {
// mode: assign in constructor
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
@ -64,6 +64,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
content: 'this.utils = utils;',
linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart],
});
}
if (useRef) {
next.chunks.push({

View File

@ -13,6 +13,7 @@ import {
IContainerInfo,
} from '../../../types';
import { isJSFunction, isJSExpression } from '@alilc/lowcode-types';
import { isJSExpressionFn } from '../../../utils/common';
export interface PluginConfig {
fileType: string;
@ -41,6 +42,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
// 过滤掉非法数据(有些场景下会误传入空字符串或 null)
if (
!isJSFunction(lifeCycles[lifeCycleName]) &&
!isJSExpressionFn(lifeCycles[lifeCycleName]) &&
!isJSExpression(lifeCycles[lifeCycleName])
) {
return null;

View File

@ -22,6 +22,8 @@ const factory: PostProcessorFactory<ProcessorConfig> = (config?: ProcessorConfig
let parser: prettier.BuiltInParserName | any;
if (fileType === 'js' || fileType === 'jsx') {
parser = 'babel';
} else if (fileType === 'json') {
parser = 'json-stringify';
} else if (PARSERS.indexOf(fileType) >= 0) {
parser = fileType;
} else if (cfg.customFileTypeParser[fileType]) {

View File

@ -1,20 +1,15 @@
import {
IPublicTypeJSONArray,
IPublicTypeJSONObject,
IPublicTypeCompositeArray,
IPublicTypeCompositeObject,
ResultDir,
IPublicTypeCompositeObject, IPublicTypeJSExpression,
IPublicTypeJSFunction, IPublicTypeJSONArray,
IPublicTypeJSONObject, IPublicTypeJSSlot, IPublicTypeNodeDataType,
IPublicTypeProjectSchema, ResultDir,
ResultFile,
IPublicTypeNodeDataType,
IPublicTypeProjectSchema,
IPublicTypeJSExpression,
IPublicTypeJSFunction,
IPublicTypeJSSlot,
} from '@alilc/lowcode-types';
import { IParseResult } from './intermediate';
import { IScopeBindings } from '../utils/ScopeBindings';
import type { ProjectBuilderInitOptions } from '../generator/ProjectBuilder';
import { IScopeBindings } from '../utils/ScopeBindings';
import { IParseResult } from './intermediate';
export enum FileType {
CSS = 'css',
@ -22,8 +17,10 @@ export enum FileType {
LESS = 'less',
HTML = 'html',
JS = 'js',
MJS = 'mjs',
JSX = 'jsx',
TS = 'ts',
MTS = 'mts',
TSX = 'tsx',
JSON = 'json',
MD = 'md',
@ -67,16 +64,16 @@ export interface ICodeStruct extends IBaseCodeStruct {
/** 上下文数据,用来在插件之间共享一些数据 */
export interface IContextData extends IProjectBuilderOptions {
/**
* 使 Ref API (this.$/this.$$)
* */
useRefApi?: boolean;
/**
*
*
*/
[key: string]: any;
/**
* 使 Ref API (this.$/this.$$)
* */
useRefApi?: boolean;
}
export type BuilderComponentPlugin = (initStruct: ICodeStruct) => Promise<ICodeStruct>;
@ -168,6 +165,7 @@ export interface IProjectBuilderOptions {
* - expr: 求值的表达式
*/
evalErrorsHandler?: string;
/**
* Hook which is used to customize original options, we can reorder/add/remove plugins/processors
* of the existing solution.
@ -180,7 +178,8 @@ export interface IProjectBuilder {
}
/** 项目级别的前置处理器 */
export type ProjectPreProcessor = (schema: IPublicTypeProjectSchema) => Promise<IPublicTypeProjectSchema> | IPublicTypeProjectSchema;
export type ProjectPreProcessor = (schema: IPublicTypeProjectSchema) =>
Promise<IPublicTypeProjectSchema> | IPublicTypeProjectSchema;
export interface ProjectPostProcessorOptions {
parseResult?: IParseResult;
@ -199,7 +198,7 @@ export type ProjectPostProcessor = (
export type PostProcessorFactory<T> = (config?: T) => PostProcessor;
/** 模块级别的后置处理器 */
export type PostProcessor = (content: string, fileType: string) => string;
export type PostProcessor = (content: string, fileType: string, name?: string) => string;
// TODO: temp interface, need modify
export interface IPluginOptions {

View File

@ -42,6 +42,7 @@ export interface IRouterInfo extends IWithDependency {
* project's remarks
*/
export interface ProjectRemark {
/** if current project only contain one container which type is `Component` */
isSingleComponent?: boolean;
}

View File

@ -1,5 +1,7 @@
import type { IPublicTypeJSExpression, IPublicTypeJSFunction } from '@alilc/lowcode-types';
import changeCase from 'change-case';
import short from 'short-uuid';
import { DependencyType, IDependency, IExternalDependency, IInternalDependency } from '../types';
// Doc: https://www.npmjs.com/package/change-case
@ -39,3 +41,19 @@ export function getStaticExprValue<T>(expr: string): T {
// eslint-disable-next-line no-new-func
return Function(`"use strict";return (${expr})`)();
}
export function isJSExpressionFn(data: any): data is IPublicTypeJSFunction {
return data?.type === 'JSExpression' && data?.extType === 'function';
}
export function isInternalDependency(
dependency: IDependency,
): dependency is IInternalDependency {
return dependency.dependencyType === DependencyType.Internal;
}
export function isExternalDependency(
dependency: IDependency,
): dependency is IExternalDependency {
return dependency.dependencyType === DependencyType.External;
}

View File

@ -16,6 +16,7 @@ import { generateExpression, generateFunction } from './jsExpression';
import { generateJsSlot } from './jsSlot';
import { executeFunctionStack } from './aopHelper';
import { parseExpressionGetKeywords } from './expressionParser';
import { isJSExpressionFn } from './common';
interface ILegaoVariable {
type: 'variable';
@ -159,7 +160,7 @@ function generateUnknownType(
return generateExpression(value, scope);
}
if (isJSFunction(value)) {
if (isJSFunction(value) || isJSExpressionFn(value)) {
if (options.handlers?.function) {
return executeFunctionStack(value, scope, options.handlers.function, genFunction, options);
}

View File

@ -2,14 +2,18 @@ import changeCase from 'change-case';
import type { IProjectInfo } from '../types/intermediate';
export interface DataSourceDependenciesConfig {
/** 数据源引擎的版本 */
engineVersion?: string;
/** 数据源引擎的包名 */
enginePackage?: string;
/** 数据源 handlers 的版本 */
handlersVersion?: {
[key: string]: string;
};
/** 数据源 handlers 的包名 */
handlersPackages?: {
[key: string]: string;

View File

@ -12,6 +12,7 @@ import * as version from './version';
import * as scope from './Scope';
import * as expressionParser from './expressionParser';
import * as dataSource from './dataSource';
import * as pathHelper from './pathHelper';
export {
common,
@ -27,4 +28,5 @@ export {
scope,
expressionParser,
dataSource,
pathHelper,
};

View File

@ -5,6 +5,7 @@ import * as t from '@babel/types';
import { IPublicTypeJSExpression, IPublicTypeJSFunction, isJSExpression, isJSFunction } from '@alilc/lowcode-types';
import { CodeGeneratorError, IScope } from '../types';
import { transformExpressionLocalRef, ParseError } from './expressionParser';
import { isJSExpressionFn } from './common';
function parseFunction(content: string): t.FunctionExpression | null {
try {
@ -78,8 +79,13 @@ function getBodyStatements(content: string) {
throw new Error('Can not find Function Statement');
}
export function isJsCode(value: unknown): boolean {
return isJSExpression(value) || isJSFunction(value);
/**
* 广 JSFunction
* @param value
* @returns
*/
export function isBroadJSFunction(value: unknown): boolean {
return isJSExpressionFn(value) || isJSFunction(value);
}
export function generateExpression(value: any, scope: IScope): string {
@ -96,6 +102,10 @@ export function generateExpression(value: any, scope: IScope): string {
throw new CodeGeneratorError('Not a JSExpression');
}
function getFunctionSource(cfg: IPublicTypeJSFunction): string {
return cfg.source || cfg.value || cfg.compiled;
}
export function generateFunction(
value: any,
config: {
@ -112,21 +122,26 @@ export function generateFunction(
isBindExpr: false,
},
) {
if (isJsCode(value)) {
if (isBroadJSFunction(value)) {
const functionCfg = value as IPublicTypeJSFunction;
const functionSource = getFunctionSource(functionCfg);
if (config.isMember) {
return transformFuncExpr2MethodMember(config.name || '', functionCfg.value);
return transformFuncExpr2MethodMember(config.name || '', functionSource);
}
if (config.isBlock) {
return getBodyStatements(functionCfg.value);
return getBodyStatements(functionSource);
}
if (config.isArrow) {
return getArrowFunction(functionCfg.value);
return getArrowFunction(functionSource);
}
if (config.isBindExpr) {
return `(${functionCfg.value}).bind(this)`;
return `(${functionSource}).bind(this)`;
}
return functionCfg.value;
return functionSource;
}
if (isJSExpression(value)) {
return value.value;
}
throw new CodeGeneratorError('Not a JSFunction or JSExpression');

View File

@ -126,7 +126,7 @@ function generateAttrs(
if (props) {
if (!Array.isArray(props)) {
Object.keys(props).forEach((propName: string) => {
pieces = pieces.concat(generateAttr(propName, props[propName], scope, config));
pieces = pieces.concat(generateAttr(propName, props[propName] as IPublicTypeCompositeValue, scope, config));
});
} else {
props.forEach((prop) => {

View File

@ -0,0 +1,41 @@
import { IContextData } from '../types';
function relativePath(from: string[], to: string[]): string[] {
const length = Math.min(from.length, to.length);
let samePartsLength = length;
for (let i = 0; i < length; i++) {
if (from[i] !== to[i]) {
samePartsLength = i;
break;
}
}
if (samePartsLength === 0) {
return to;
}
let outputParts = [];
for (let i = samePartsLength; i < from.length; i++) {
outputParts.push('..');
}
outputParts = [...outputParts, ...to.slice(samePartsLength)];
if (outputParts[0] !== '..') {
outputParts.unshift('.');
}
return outputParts;
}
export function getSlotRelativePath(options: {
contextData: IContextData;
from: string;
to: string;
}) {
const { contextData, from, to } = options;
const isSingleComponent = contextData?.extraContextData?.projectRemark?.isSingleComponent;
const template = contextData?.extraContextData?.template;
let toPath = template.slots[to].path;
toPath = [...toPath, template.slots[to].fileName!];
let fromPath = template.slots[from].path;
if (!isSingleComponent && ['components', 'pages'].indexOf(from) !== -1) {
fromPath = [...fromPath, 'pageName'];
}
return relativePath(fromPath, toPath).join('/');
}

View File

@ -13,6 +13,7 @@ import {
isJSFunction,
} from '@alilc/lowcode-types';
import { CodeGeneratorError } from '../types/error';
import { isJSExpressionFn } from './common';
export function isContainerSchema(x: any): x is IPublicTypeContainerSchema {
return (
@ -100,7 +101,7 @@ export function handleSubNodes<T>(
});
} else {
Object.values(child.props).forEach((value) => {
const childRes = handleCompositeValueInProps(value);
const childRes = handleCompositeValueInProps(value as IPublicTypeCompositeValue);
childrenRes.push(...childRes);
});
}
@ -128,6 +129,7 @@ export function handleSubNodes<T>(
// IPublicTypeCompositeObject
if (
!isJSExpression(value) &&
!isJSExpressionFn(value) &&
!isJSFunction(value) &&
typeof value === 'object' &&
value !== null

View File

@ -253,7 +253,6 @@ class Home$$Page extends Component {
if (!response.success) {
throw new Error(response.message);
}
return response.data;
},
isInit: true,
@ -280,7 +279,6 @@ class Home$$Page extends Component {
if (!response.success) {
throw new Error(response.message);
}
return response.data.result;
},
isInit: true,

View File

@ -31,9 +31,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -71,10 +71,10 @@ class Test$$Page extends React.Component {
type: 'urlParams',
isInit: function () {
return undefined;
},
}.bind(_this),
options: function () {
return undefined;
},
}.bind(_this),
},
{
id: 'user',
@ -85,17 +85,16 @@ class Test$$Page extends React.Component {
uri: 'https://shs.xxx.com/mock/1458/demo/user',
isSync: true,
};
},
}.bind(_this),
dataHandler: function (response) {
if (!response.data.success) {
throw new Error(response.data.message);
}
return response.data.data;
},
isInit: function () {
return undefined;
},
}.bind(_this),
},
{
id: 'orders',
@ -106,17 +105,16 @@ class Test$$Page extends React.Component {
uri: 'https://shs.xxx.com/mock/1458/demo/orders',
isSync: true,
};
},
}.bind(_this),
dataHandler: function (response) {
if (!response.data.success) {
throw new Error(response.data.message);
}
return response.data.data.result;
},
isInit: function () {
return undefined;
},
}.bind(_this),
},
],
dataHandler: function (dataMap) {

View File

@ -71,7 +71,7 @@
},
"lifeCycles": {
"componentDidMount": {
"type": "JSExpression",
"type": "JSFunction",
"value": "function() { console.log('componentDidMount'); }"
}
},

View File

@ -34,9 +34,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -67,10 +67,10 @@ class Aaaa$$Page extends React.Component {
return {
uri: '',
};
},
}.bind(_this),
isInit: function () {
return undefined;
},
}.bind(_this),
},
],
};

View File

@ -29,9 +29,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -71,7 +71,7 @@
},
"lifeCycles": {
"componentDidMount": {
"type": "JSExpression",
"type": "JSFunction",
"value": "function() { console.log('componentDidMount'); }"
}
},

View File

@ -29,9 +29,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -31,9 +31,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -76,7 +76,7 @@ class Test$$Page extends React.Component {
type: 'fetch',
isInit: function () {
return true;
},
}.bind(_this),
options: function () {
return {
params: {},
@ -86,7 +86,7 @@ class Test$$Page extends React.Component {
headers: {},
uri: 'https://mocks.xxx.com/mock/jjpin/user/list',
};
},
}.bind(_this),
id: 'users',
},
],

View File

@ -32,9 +32,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -31,9 +31,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -71,10 +71,10 @@ class Test$$Page extends React.Component {
type: 'urlParams',
isInit: function () {
return undefined;
},
}.bind(_this),
options: function () {
return undefined;
},
}.bind(_this),
},
{
id: 'user',
@ -85,17 +85,16 @@ class Test$$Page extends React.Component {
uri: 'https://shs.xxx.com/mock/1458/demo/user',
isSync: true,
};
},
}.bind(_this),
dataHandler: function (response) {
if (!response.data.success) {
throw new Error(response.data.message);
}
return response.data.data;
},
isInit: function () {
return undefined;
},
}.bind(_this),
},
{
id: 'orders',
@ -106,17 +105,16 @@ class Test$$Page extends React.Component {
uri: 'https://shs.xxx.com/mock/1458/demo/orders',
isSync: true,
};
},
}.bind(_this),
dataHandler: function (response) {
if (!response.data.success) {
throw new Error(response.data.message);
}
return response.data.data.result;
},
isInit: function () {
return undefined;
},
}.bind(_this),
},
],
dataHandler: function (dataMap) {

View File

@ -71,7 +71,7 @@
},
lifeCycles: {
componentDidMount: {
type: 'JSExpression',
type: 'JSFunction',
value: "function() { console.log('componentDidMount'); }",
},
},
@ -91,7 +91,7 @@
isSync: true,
},
dataHandler: {
type: 'JSExpression',
type: 'JSFunction',
value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data;\n}',
},
},
@ -105,13 +105,13 @@
isSync: true,
},
dataHandler: {
type: 'JSExpression',
type: 'JSFunction',
value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data.result;\n}',
},
},
],
dataHandler: {
type: 'JSExpression',
type: 'JSFunction',
value: 'function (dataMap) {\n console.info("All datasources loaded:", dataMap);\n}',
},
},

View File

@ -31,9 +31,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -151,6 +151,7 @@ class Test$$Page extends React.Component {
onOkModifyDialogThird() {
//
this.setState({
currentStep: 0,
isModifyDialogVisible: false,
@ -159,6 +160,7 @@ class Test$$Page extends React.Component {
onCancelModifyDialogThird() {
//
this.setState({
isModifyDialogVisible: false,
});

View File

@ -30,9 +30,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -63,10 +63,10 @@ class Example$$Page extends React.Component {
return {
uri: 'https://api.example.com/user/list',
};
},
}.bind(_this),
isInit: function () {
return undefined;
},
}.bind(_this),
},
],
};

View File

@ -30,9 +30,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -59,14 +59,14 @@ class $$Page extends React.Component {
id: 'todos',
isInit: function () {
return true;
},
}.bind(_this),
type: 'jsonp',
options: function () {
return {
method: 'GET',
uri: 'https://a0ee9135-6a7f-4c0f-a215-f0f247ad907d.mock.pstmn.io',
};
},
}.bind(_this),
dataHandler: function dataHandler(data) {
return data.data;
},

View File

@ -32,9 +32,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -65,7 +65,6 @@ class Test$$Page extends React.Component {
};
this.__jp__init();
this.statusDesc = {
0: '失败',
1: '成功',
@ -163,7 +162,6 @@ class Test$$Page extends React.Component {
if (!item) {
return '暂无结果';
}
const { channel, plat, version, status } = item;
return [channel, plat, version, status].join('-');
}

View File

@ -32,9 +32,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -67,7 +67,6 @@ class Test$$Page extends React.Component {
};
this.__jp__init();
this.statusDesc = {
0: '失败',
1: '成功',
@ -202,12 +201,10 @@ class Test$$Page extends React.Component {
componentDidMount() {
this.$ds.resolve('PROJECTS');
if (this.userTimeout) {
clearTimeout(this.userTimeout);
this.userTimeout = null;
}
if (this.projectTimeout) {
clearTimeout(this.projectTimeout);
this.projectTimeout = null;

View File

@ -25,9 +25,16 @@
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"ideMode": { "name": "ice-react" },
"iceworks": { "type": "react", "adapter": "adapter-react-v3" },
"engines": { "node": ">=8.0.0" },
"ideMode": {
"name": "ice-react"
},
"iceworks": {
"type": "react",
"adapter": "adapter-react-v3"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"

View File

@ -19,7 +19,12 @@ export function App() {
`;
exports[`postprocessor/prettier should works for json file 1`] = `
"{ \\"components\\": [\\"Button\\", \\"Block\\"] }
"{
\\"components\\": [
\\"Button\\",
\\"Block\\"
]
}
"
`;