From c2a7d631a13c4cb46f5e55c16cc511f7931b01c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Wed, 19 Aug 2020 17:21:17 +0800 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E8=BF=98=E5=8E=9F?= =?UTF-8?q?=E5=87=BA=E7=A0=81=E6=A8=A1=E5=9D=97=E7=9A=84=20solutions=20?= =?UTF-8?q?=E7=9A=84=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code-generator/src/index.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/code-generator/src/index.ts b/packages/code-generator/src/index.ts index 87df670ee..79e527354 100644 --- a/packages/code-generator/src/index.ts +++ b/packages/code-generator/src/index.ts @@ -6,16 +6,12 @@ import { createProjectBuilder } from './generator/ProjectBuilder'; import { createModuleBuilder } from './generator/ModuleBuilder'; import { createDiskPublisher } from './publisher/disk'; import { createZipPublisher } from './publisher/zip'; -// import createIceJsProjectBuilder from './solutions/icejs'; -// import createRecoreProjectBuilder from './solutions/recore'; +import createIceJsProjectBuilder from './solutions/icejs'; +import createRecoreProjectBuilder from './solutions/recore'; // 引入说明 import { REACT_CHUNK_NAME } from './plugins/component/react/const'; -import { - COMMON_CHUNK_NAME, - CLASS_DEFINE_CHUNK_NAME, - DEFAULT_LINK_AFTER, -} from './const/generator'; +import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from './const/generator'; // 引入通用插件组 import esmodule from './plugins/common/esmodule'; @@ -50,8 +46,8 @@ export default { createProjectBuilder, createModuleBuilder, solutions: { - // icejs: createIceJsProjectBuilder, - // recore: createRecoreProjectBuilder, + icejs: createIceJsProjectBuilder, + recore: createRecoreProjectBuilder, }, solutionParts: { icejs, From 27f0e1301010f991012ebfca1aef844bd58f542e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Wed, 19 Aug 2020 17:23:03 +0800 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=20Rax=20=E7=9A=84=20solutions=20=E7=9A=84=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code-generator/src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/code-generator/src/index.ts b/packages/code-generator/src/index.ts index 79e527354..e817d2277 100644 --- a/packages/code-generator/src/index.ts +++ b/packages/code-generator/src/index.ts @@ -8,6 +8,7 @@ import { createDiskPublisher } from './publisher/disk'; import { createZipPublisher } from './publisher/zip'; import createIceJsProjectBuilder from './solutions/icejs'; import createRecoreProjectBuilder from './solutions/recore'; +import createRaxAppProjectBuilder from './solutions/rax-app'; // 引入说明 import { REACT_CHUNK_NAME } from './plugins/component/react/const'; @@ -39,6 +40,7 @@ import * as utilsTemplateHelper from './utils/templateHelper'; // 引入内置解决方案模块 import icejs from './plugins/project/framework/icejs'; +import rax from './plugins/project/framework/rax'; export * from './types'; @@ -48,9 +50,11 @@ export default { solutions: { icejs: createIceJsProjectBuilder, recore: createRecoreProjectBuilder, + rax: createRaxAppProjectBuilder, }, solutionParts: { icejs, + rax, }, publishers: { disk: createDiskPublisher, From 475534f51f93c52f575c3be3739cad9845bfa838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Wed, 19 Aug 2020 17:25:16 +0800 Subject: [PATCH 03/14] =?UTF-8?q?test:=20=F0=9F=92=8D=20=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=9A=84=E6=97=B6=E5=80=99=E7=9B=B4=E6=8E=A5=E5=BC=95=E7=94=A8?= =?UTF-8?q?=20src=20=E5=AF=BC=E5=87=BA=E7=9A=84=20solutions.rax=20?= =?UTF-8?q?=E5=B0=B1=E8=A1=8C=E4=BA=86,=E4=B8=8D=E7=94=A8=E5=86=8D?= =?UTF-8?q?=E4=BB=8E=E5=AD=90=E7=9B=AE=E5=BD=95=E9=87=8C=E5=BC=95=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code-generator/test/rax-app.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/code-generator/test/rax-app.test.ts b/packages/code-generator/test/rax-app.test.ts index 6ac4ce363..e3a05f61c 100644 --- a/packages/code-generator/test/rax-app.test.ts +++ b/packages/code-generator/test/rax-app.test.ts @@ -7,8 +7,8 @@ import path from 'path'; import chalk from 'chalk'; import CodeGenerator from '../src'; -import createRaxAppBuilder from '../src/solutions/rax-app'; -import { IProjectSchema } from '../src/types/schema'; + +import type { IProjectSchema } from '../src/types/schema'; const TEST_CASES_DIR = path.join(__dirname, '../test-cases/rax-app'); @@ -42,7 +42,7 @@ function defineTest(caseDirName: string) { } async function exportProject(schemaJson: IProjectSchema, targetPath: string, projectName: string) { - const raxAppBuilder = createRaxAppBuilder(); + const raxAppBuilder = CodeGenerator.solutions.rax(); const result = await raxAppBuilder.generateProject(schemaJson); const publisher = CodeGenerator.publishers.disk(); await publisher.publish({ From facfa2afd6d6189c7ef46af8e78c09d368139f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Fri, 21 Aug 2020 10:44:38 +0800 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20Rax=20=E5=87=BA=E7=A0=81=E6=97=B6=E5=AF=B9=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E7=9A=84=E8=A1=A8=E8=BE=BE=E5=BC=8F=E7=9A=84=E5=8C=85=E8=A3=B9?= =?UTF-8?q?=E9=80=BB=E8=BE=91,=20=E5=AF=B9=E4=BA=8E=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E7=AE=80=E5=8D=95=E7=9A=84=E5=AE=89=E5=85=A8=E7=9A=84=E8=A1=A8?= =?UTF-8?q?=E8=BE=BE=E5=BC=8F=E4=B8=8D=E5=81=9A=E5=8C=85=E8=A3=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugins/component/rax/jsx.ts | 68 +++++++++++++++++-- .../src/utils/expressionParser.ts | 19 +++--- .../demo-project/src/pages/Home/index.jsx | 4 +- .../test-cases/rax-app/demo2/schema.json5 | 16 ++--- 4 files changed, 85 insertions(+), 22 deletions(-) diff --git a/packages/code-generator/src/plugins/component/rax/jsx.ts b/packages/code-generator/src/plugins/component/rax/jsx.ts index eb6c513af..a65ebba4e 100644 --- a/packages/code-generator/src/plugins/component/rax/jsx.ts +++ b/packages/code-generator/src/plugins/component/rax/jsx.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import changeCase from 'change-case'; +import { Expression, MemberExpression } from '@babel/types'; import { BuilderComponentPlugin, BuilderComponentPluginFactory, @@ -25,7 +26,11 @@ import { generateExpression } from '../../../utils/jsExpression'; import { CustomHandlerSet, generateUnknownType } from '../../../utils/compositeType'; import { IScopeBindings, ScopeBindings } from '../../../utils/ScopeBindings'; -import { parseExpressionConvertThis2Context, parseExpressionGetGlobalVariables } from '../../../utils/expressionParser'; +import { + parseExpression, + parseExpressionConvertThis2Context, + parseExpressionGetGlobalVariables, +} from '../../../utils/expressionParser'; type PluginConfig = { fileType: string; @@ -181,7 +186,62 @@ function transformLoopExpr(expr: string, handlers: CustomHandlerSet) { } function transformJsExpr(expr: string, handlers: CustomHandlerSet) { - return isLiteralAtomicExpr(expr) ? expr : `__$$eval(() => (${transformThis2Context(expr, handlers)}))`; + if (!expr) { + return 'undefined'; + } + + if (isLiteralAtomicExpr(expr)) { + return expr; + } + + const exprAst = parseExpression(expr); + switch (exprAst.type) { + // 对于下面这些比较安全的字面值,可以直接返回对应的表达式,而非包一层 + case 'BigIntLiteral': + case 'BooleanLiteral': + case 'DecimalLiteral': + case 'NullLiteral': + case 'NumericLiteral': + case 'RegExpLiteral': + case 'StringLiteral': + return expr; + + // 对于直接写个函数的,则不用再包下,因为这样不会抛出异常的 + case 'ArrowFunctionExpression': + case 'FunctionExpression': + return transformThis2Context(exprAst, handlers); + + // 对于直接访问 this.xxx, this.utils.xxx, this.state.xxx 的也不用再包下 + case 'MemberExpression': + if (isSimpleDirectlyAccessingThis(exprAst) || isSimpleDirectlyAccessingSafeProperties(exprAst)) { + return transformThis2Context(exprAst, handlers); + } + + break; + + default: + break; + } + + // 其他的都需要包一层 + return `__$$eval(() => (${transformThis2Context(exprAst, handlers)}))`; +} + +/** this.xxx */ +function isSimpleDirectlyAccessingThis(exprAst: MemberExpression) { + return !exprAst.computed && exprAst.object.type === 'ThisExpression'; +} + +/** this.state.xxx 和 this.utils.xxx 等安全的肯定应该存在的东东 */ +function isSimpleDirectlyAccessingSafeProperties(exprAst: MemberExpression): boolean { + return ( + !exprAst.computed && + exprAst.object.type === 'MemberExpression' && + exprAst.object.object.type === 'ThisExpression' && + !exprAst.object.computed && + exprAst.object.property.type === 'Identifier' && + /^(state|utils|constants|i18n)$/.test(exprAst.object.property.name) + ); } function isImportAliasDefineChunk( @@ -206,14 +266,14 @@ function isImportAliasDefineChunk( * 判断是否是原子类型的表达式 */ function isLiteralAtomicExpr(expr: string): boolean { - return expr === 'null' || expr === 'undefined' || expr === 'true' || expr === 'false' || /^\d+$/.test(expr); + return expr === 'null' || expr === 'undefined' || expr === 'true' || expr === 'false' || /^-?\d+(\.\d+)?$/.test(expr); } /** * 将所有的 this.xxx 替换为 __$$context.xxx * @param expr */ -function transformThis2Context(expr: string, customHandlers: CustomHandlerSet): string { +function transformThis2Context(expr: string | Expression, customHandlers: CustomHandlerSet): string { // return expr // .replace(/\bthis\.item\./g, () => 'item.') // .replace(/\bthis\.index\./g, () => 'index.') diff --git a/packages/code-generator/src/utils/expressionParser.ts b/packages/code-generator/src/utils/expressionParser.ts index 0b7755171..7a932f1b4 100644 --- a/packages/code-generator/src/utils/expressionParser.ts +++ b/packages/code-generator/src/utils/expressionParser.ts @@ -7,8 +7,8 @@ import { isIdentifier, Node } from '@babel/types'; import { OrderedSet } from './OrderedSet'; export class ParseError extends Error { - constructor(public readonly expr: string, public readonly detail: unknown) { - super(`Failed to parse expression "${expr}"`); + constructor(public readonly expr: string | t.Expression, public readonly detail: unknown) { + super(`Failed to parse expression "${typeof expr === 'string' ? expr : generate(expr)}"`); Object.setPrototypeOf(this, new.target.prototype); } } @@ -159,7 +159,7 @@ export function parseExpressionGetGlobalVariables( } export function parseExpressionConvertThis2Context( - expr: string, + expr: string | t.Expression, contextName: string = '__$$context', localVariables: string[] = [], ): string { @@ -168,7 +168,7 @@ export function parseExpressionConvertThis2Context( } try { - const exprAst = parser.parseExpression(expr); + const exprAst = typeof expr === 'string' ? parser.parseExpression(expr) : expr; const exprWrapAst = t.expressionStatement(exprAst); const fileAst = t.file(t.program([exprWrapAst])); @@ -229,11 +229,14 @@ export function parseExpressionConvertThis2Context( const { code } = generate(exprWrapAst.expression, { sourceMaps: false }); return code; } catch (e) { - // throw new ParseError(expr, e); - throw e; + throw new ParseError(expr, e); } } -function indent(level: number) { - return ' '.repeat(level); +export function parseExpression(expr: string) { + try { + return parser.parseExpression(expr); + } catch (e) { + throw new ParseError(expr, e); + } } diff --git a/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx b/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx index b1b50c437..f506deab2 100644 --- a/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx @@ -79,7 +79,7 @@ class Home$$Page extends Component { === User Info: === - {__$$eval(() => __$$context.state.user) && ( + {__$$context.state.user && ( __$$context.state.user.avatar) }} @@ -128,7 +128,7 @@ class Home$$Page extends Component { }); }} > - 点击次数:{__$$eval(() => __$$context.state.clickCount)}(点击加 1) + 点击次数:{__$$context.state.clickCount}(点击加 1) 操作提示: diff --git a/packages/code-generator/test-cases/rax-app/demo2/schema.json5 b/packages/code-generator/test-cases/rax-app/demo2/schema.json5 index 96818e695..336e37b29 100644 --- a/packages/code-generator/test-cases/rax-app/demo2/schema.json5 +++ b/packages/code-generator/test-cases/rax-app/demo2/schema.json5 @@ -87,7 +87,7 @@ isSync: true, }, dataHandler: { - type: 'JSFunction', + type: 'JSExpression', value: 'function (response) {\nif (!response.success){\n throw new Error(response.message);\n }\n return response.data;\n}', }, }, @@ -104,13 +104,13 @@ isSync: true, }, dataHandler: { - type: 'JSFunction', + type: 'JSExpression', value: 'function (response) {\nif (!response.success){\n throw new Error(response.message);\n }\n return response.data.result;\n}', }, }, ], dataHandler: { - type: 'JSFunction', + type: 'JSExpression', value: 'function (dataMap) {\n console.info("All datasources loaded:", dataMap);\n}', }, }, @@ -164,7 +164,7 @@ componentName: 'View', props: { onClick: { - type: 'JSFunction', + type: 'JSExpression', value: 'this.hello', }, }, @@ -210,7 +210,7 @@ props: { style: { flexDirection: 'row' }, onClick: { - type: 'JSFunction', + type: 'JSExpression', value: 'function(){ this.utils.recordEvent(`CLICK_ORDER`, this.order.title) }', }, }, @@ -260,7 +260,7 @@ componentName: 'View', props: { onClick: { - type: 'JSFunction', + type: 'JSExpression', value: 'function (){ this.setState({ clickCount: this.state.clickCount + 1 }) }', }, }, @@ -312,7 +312,7 @@ name: 'formatPrice', type: 'function', content: { - type: 'JSFunction', + type: 'JSExpression', value: 'function formatPrice(price, unit) { return Number(price).toFixed(2) + unit; }', }, }, @@ -321,7 +321,7 @@ name: 'recordEvent', type: 'function', content: { - type: 'JSFunction', + type: 'JSExpression', value: 'function recordEvent(eventName, eventDetail) { \n this.utils.Toast.show(`[EVENT]: ${eventName} ${eventDetail}`);\n console.log(`[EVENT]: ${eventName} (detail: %o) (user: %o)`, eventDetail, this.state.user); }', }, }, From 206a04eae864bfe10445893ef13f5b8cb2448848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Fri, 21 Aug 2020 10:47:01 +0800 Subject: [PATCH 05/14] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E5=AF=B9=20transformThis2Context=20=E7=9A=84=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code-generator/src/plugins/component/rax/jsx.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/code-generator/src/plugins/component/rax/jsx.ts b/packages/code-generator/src/plugins/component/rax/jsx.ts index a65ebba4e..7cdb0c661 100644 --- a/packages/code-generator/src/plugins/component/rax/jsx.ts +++ b/packages/code-generator/src/plugins/component/rax/jsx.ts @@ -274,6 +274,7 @@ function isLiteralAtomicExpr(expr: string): boolean { * @param expr */ function transformThis2Context(expr: string | Expression, customHandlers: CustomHandlerSet): string { + // 下面这种字符串替换的方式虽然简单直接,但是对于复杂场景会误匹配,故后期改成了解析 AST 然后修改 AST 最后再重新生成代码的方式 // return expr // .replace(/\bthis\.item\./g, () => 'item.') // .replace(/\bthis\.index\./g, () => 'index.') From 730387de95f4081db308ff743698e1c37fd3f6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Fri, 21 Aug 2020 11:02:42 +0800 Subject: [PATCH 06/14] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20=E5=8E=BB?= =?UTF-8?q?=E6=8E=89=E4=B8=80=E4=BA=9B=E6=84=8F=E8=A7=81=E5=A4=84=E7=90=86?= =?UTF-8?q?=E4=BA=86=E7=9A=84=20TODO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugins/component/rax/containerInitState.ts | 1 - .../src/plugins/component/rax/containerInjectUtils.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/packages/code-generator/src/plugins/component/rax/containerInitState.ts b/packages/code-generator/src/plugins/component/rax/containerInitState.ts index 8dc634c98..6c5421133 100644 --- a/packages/code-generator/src/plugins/component/rax/containerInitState.ts +++ b/packages/code-generator/src/plugins/component/rax/containerInitState.ts @@ -33,7 +33,6 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => if (ir.state) { const state = ir.state; const fields = Object.keys(state).map((stateName) => { - // TODO: 这里用什么 handlers? const [isString, value] = generateCompositeType(state[stateName], {}); return `${stateName}: ${isString ? `'${value}'` : value},`; }); diff --git a/packages/code-generator/src/plugins/component/rax/containerInjectUtils.ts b/packages/code-generator/src/plugins/component/rax/containerInjectUtils.ts index 5fda869f2..b4a4c52f2 100644 --- a/packages/code-generator/src/plugins/component/rax/containerInjectUtils.ts +++ b/packages/code-generator/src/plugins/component/rax/containerInjectUtils.ts @@ -43,14 +43,12 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start], }); - // TODO: Page methods... next.chunks.push({ type: ChunkType.STRING, fileType: cfg.fileType, name: CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod, // 绑定下上下文,这样在所有的 utils 里面都能通过 this.xxx 来访问上下文了 - // TODO: 要不要优化为通过 Proxy 的方式懒绑定? content: ` _defineUtils() { const utils = { From 166c70b94c58e2a3a511b6fe5f2a6f7433645919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Fri, 21 Aug 2020 11:03:10 +0800 Subject: [PATCH 07/14] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20=E6=95=B4?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugins/component/rax/jsx.ts | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/code-generator/src/plugins/component/rax/jsx.ts b/packages/code-generator/src/plugins/component/rax/jsx.ts index 7cdb0c661..9cb1e9558 100644 --- a/packages/code-generator/src/plugins/component/rax/jsx.ts +++ b/packages/code-generator/src/plugins/component/rax/jsx.ts @@ -36,30 +36,12 @@ type PluginConfig = { fileType: string; }; -// TODO: componentName 若并非大写字符打头,甚至并非是一个有效的 JS 标识符怎么办?? const pluginFactory: BuilderComponentPluginFactory = (config?) => { const cfg: PluginConfig = { fileType: FileType.JSX, ...config, }; - // 什么都不做的的话,会有 3 个问题: - // 1. 小程序出码的时候,循环变量没法拿到 - // 2. 小程序出码的时候,很容易出现 Uncaught TypeError: Cannot read property 'avatar' of undefined 这样的异常(如下图的 50 行) -- 因为若直接出码,Rax 构建到小程序的时候会立即计算所有在视图中用到的变量 - // 3. 通过 this.xxx 能拿到的东西太多了,而且自定义的 methods 可能会无意间破坏 Rax 框架或小程序框架在页面 this 上的东东 - // const transformers = { - // transformThis2Context: (expr: string) => expr, - // transformJsExpr: (expr: string) => expr, - // transformLoopExpr: (expr: string) => expr, - // }; - - // 不转换 this.xxx 到 __$$context.xxx 的话,依然会有上述的 1 和 3 的问题。 - // const transformers = { - // transformThis2Context: (expr: string) => expr, - // transformJsExpr: (expr: string) => (isLiteralAtomicExpr(expr) ? expr : `__$$eval(() => (${expr}))`), - // transformLoopExpr: (expr: string) => `__$$evalArray(() => (${expr}))`, - // }; - const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, @@ -76,12 +58,17 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => } }); + // 注意:这里其实隐含了一个假设:schema 中的 componentName 应该是一个有效的 JS 标识符,而且是大写字母打头的 const mapComponentNameToAliasOrKeepIt = (componentName: string) => componentsNameAliasMap.get(componentName) || componentName; // 然后过滤掉所有的别名 chunks next.chunks = next.chunks.filter((chunk) => !isImportAliasDefineChunk(chunk)); + // 如果直接按目前的 React 的方式之间出码 JSX 的话,会有 3 个问题: + // 1. 小程序出码的时候,循环变量没法拿到 + // 2. 小程序出码的时候,很容易出现 Uncaught TypeError: Cannot read property 'avatar' of undefined 这样的异常(如下图的 50 行) -- 因为若直接出码,Rax 构建到小程序的时候会立即计算所有在视图中用到的变量 + // 3. 通过 this.xxx 能拿到的东西太多了,而且自定义的 methods 可能会无意间破坏 Rax 框架或小程序框架在页面 this 上的东东 const customHandlers: CustomHandlerSet = { expression(this: CustomHandlerSet, input: JSExpression) { return transformJsExpr(generateExpression(input), this); From e78dae09a6ed52cb96515273d4b9956f98148150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Fri, 21 Aug 2020 11:04:06 +0800 Subject: [PATCH 08/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20globalStyle=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AE=9A=E5=88=B6=E6=A0=B7=E5=BC=8F=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=9A=84=E5=90=8E=E7=BC=80=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/rax/plugins/globalStyle.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/code-generator/src/plugins/project/framework/rax/plugins/globalStyle.ts b/packages/code-generator/src/plugins/project/framework/rax/plugins/globalStyle.ts index 78e409fd2..ce3689f2b 100644 --- a/packages/code-generator/src/plugins/project/framework/rax/plugins/globalStyle.ts +++ b/packages/code-generator/src/plugins/project/framework/rax/plugins/globalStyle.ts @@ -9,7 +9,18 @@ import { IProjectInfo, } from '../../../../../types'; -const pluginFactory: BuilderComponentPluginFactory = () => { +export type GlobalStylePluginConfig = { + fileType: string; +}; + +const pluginFactory: BuilderComponentPluginFactory = ( + config?: Partial, +) => { + const cfg: GlobalStylePluginConfig = { + fileType: FileType.SCSS, + ...config, + }; + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, @@ -19,7 +30,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { next.chunks.push({ type: ChunkType.STRING, - fileType: FileType.SCSS, // TODO: 样式文件的类型定制化? + fileType: cfg.fileType, name: COMMON_CHUNK_NAME.StyleDepsImport, content: ``, linkAfter: [], @@ -27,7 +38,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { next.chunks.push({ type: ChunkType.STRING, - fileType: FileType.SCSS, + fileType: cfg.fileType, name: COMMON_CHUNK_NAME.StyleCssContent, content: ` body { @@ -39,7 +50,7 @@ body { next.chunks.push({ type: ChunkType.STRING, - fileType: FileType.SCSS, + fileType: cfg.fileType, name: COMMON_CHUNK_NAME.StyleCssContent, content: ir.css || '', linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport], From 1970e75c28f1a709754dc0a79f5b352dc1fe7391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Fri, 21 Aug 2020 11:04:49 +0800 Subject: [PATCH 09/14] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E6=98=A5=E5=B8=8C=E7=9A=84=E8=A7=A3=E9=87=8A=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=20generateAttr=20=E4=B8=AD=E8=BF=99=E5=9D=97=E7=89=B9?= =?UTF-8?q?=E6=AE=8A=E5=A4=84=E7=90=86=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code-generator/src/utils/nodeToJSX.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/code-generator/src/utils/nodeToJSX.ts b/packages/code-generator/src/utils/nodeToJSX.ts index 46d992824..51bcd7a2d 100644 --- a/packages/code-generator/src/utils/nodeToJSX.ts +++ b/packages/code-generator/src/utils/nodeToJSX.ts @@ -44,7 +44,7 @@ export function generateAttr( return customHandlers.nodeAttr(attrName, attrValue); } - // TODO: 这两个为啥要特殊处理?? + // 为了规避一个上游 schema 引入的 bug if (attrName === 'initValue' || attrName === 'labelCol') { return []; } From 8d198bd2a9e8d9b225f840e24213b841f2cefce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Fri, 21 Aug 2020 13:01:22 +0800 Subject: [PATCH 10/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E4=B8=BA=20Rax=20?= =?UTF-8?q?=E5=87=BA=E7=A0=81=E5=A2=9E=E5=8A=A0=E5=AF=B9=20i18n=20?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/generator/ProjectBuilder.ts | 4 +- .../component/rax/containerInjectContext.ts | 47 +++++ .../src/plugins/component/rax/jsx.ts | 37 ++-- .../src/plugins/project/i18n.ts | 88 +++++----- .../demo1/expected/demo-project/src/i18n.js | 13 ++ .../demo-project/src/pages/Home/index.jsx | 25 +++ .../demo2/expected/demo-project/src/i18n.js | 13 ++ .../demo-project/src/pages/Home/index.jsx | 25 +++ .../demo3/expected/demo-project/src/i18n.js | 13 ++ .../demo-project/src/pages/Detail/index.jsx | 25 +++ .../demo-project/src/pages/Home/index.jsx | 25 +++ .../demo-project/src/pages/List/index.jsx | 25 +++ .../demo4/expected/demo-project/src/i18n.js | 13 ++ .../demo-project/src/pages/Home/index.jsx | 25 +++ .../demo5/expected/demo-project/.editorconfig | 12 ++ .../demo5/expected/demo-project/.eslintignore | 11 ++ .../demo5/expected/demo-project/.eslintrc.js | 3 + .../demo5/expected/demo-project/.gitignore | 17 ++ .../demo5/expected/demo-project/README.md | 15 ++ .../demo5/expected/demo-project/abc.json | 7 + .../demo5/expected/demo-project/build.json | 12 ++ .../demo5/expected/demo-project/package.json | 32 ++++ .../demo5/expected/demo-project/src/app.js | 6 + .../demo5/expected/demo-project/src/app.json | 11 ++ .../expected/demo-project/src/constants.js | 3 + .../demo-project/src/document/index.jsx | 25 +++ .../expected/demo-project/src/global.scss | 3 + .../demo5/expected/demo-project/src/i18n.js | 20 +++ .../demo-project/src/pages/Home/index.css | 0 .../demo-project/src/pages/Home/index.jsx | 162 ++++++++++++++++++ .../demo5/expected/demo-project/src/utils.js | 1 + .../test-cases/rax-app/demo5/schema.json5 | 73 ++++++++ 32 files changed, 735 insertions(+), 56 deletions(-) create mode 100644 packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/i18n.js create mode 100644 packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/i18n.js create mode 100644 packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/i18n.js create mode 100644 packages/code-generator/test-cases/rax-app/demo4/expected/demo-project/src/i18n.js create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.editorconfig create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.eslintignore create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.eslintrc.js create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.gitignore create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/README.md create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/abc.json create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/build.json create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/package.json create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/app.js create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/app.json create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/constants.js create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/document/index.jsx create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/global.scss create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/i18n.js create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/pages/Home/index.css create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/pages/Home/index.jsx create mode 100644 packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/utils.js create mode 100644 packages/code-generator/test-cases/rax-app/demo5/schema.json5 diff --git a/packages/code-generator/src/generator/ProjectBuilder.ts b/packages/code-generator/src/generator/ProjectBuilder.ts index f0a13e962..d00826993 100644 --- a/packages/code-generator/src/generator/ProjectBuilder.ts +++ b/packages/code-generator/src/generator/ProjectBuilder.ts @@ -159,8 +159,8 @@ export class ProjectBuilder implements IProjectBuilder { } // i18n? - if (parseResult.globalI18n && builders.i18n && this.template.slots.i18n) { - const { files } = await builders.i18n.generateModule(parseResult.globalI18n); + if (builders.i18n && this.template.slots.i18n) { + const { files } = await builders.i18n.generateModule(parseResult.project); buildResult.push({ path: this.template.slots.i18n.path, diff --git a/packages/code-generator/src/plugins/component/rax/containerInjectContext.ts b/packages/code-generator/src/plugins/component/rax/containerInjectContext.ts index 26fc15e36..3c993a2cb 100644 --- a/packages/code-generator/src/plugins/component/rax/containerInjectContext.ts +++ b/packages/code-generator/src/plugins/component/rax/containerInjectContext.ts @@ -32,6 +32,15 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], }); + // TODO: i18n 是可选的,如果没有 i18n 这个文件怎么办?该怎么判断? + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: COMMON_CHUNK_NAME.InternalDepsImport, + content: `import * as __$$i18n from '../../i18n';`, + linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], + }); + next.chunks.push({ type: ChunkType.STRING, fileType: cfg.fileType, @@ -78,6 +87,16 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => get constants() { return __$$constants; }, + get i18n() { + return self._i18n; + }, + getLocale() { + return __$$i18n.getLocale(); + }, + setLocale(locale) { + __$$i18n.setLocale(locale); + self.forceUpdate(); + }, ...this._methods, }; @@ -87,6 +106,34 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd], }); + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsVar, + content: ` + _i18n = this._createI18nDelegate(); + `, + linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod, + content: ` + _createI18nDelegate() { + return new Proxy( + {}, + { + get(target, prop) { + return __$$i18n.i18n(prop); + }, + }, + ); + } + `, + linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd], + }); return next; }; return plugin; diff --git a/packages/code-generator/src/plugins/component/rax/jsx.ts b/packages/code-generator/src/plugins/component/rax/jsx.ts index 9cb1e9558..c92f29f4f 100644 --- a/packages/code-generator/src/plugins/component/rax/jsx.ts +++ b/packages/code-generator/src/plugins/component/rax/jsx.ts @@ -182,17 +182,13 @@ function transformJsExpr(expr: string, handlers: CustomHandlerSet) { } const exprAst = parseExpression(expr); - switch (exprAst.type) { - // 对于下面这些比较安全的字面值,可以直接返回对应的表达式,而非包一层 - case 'BigIntLiteral': - case 'BooleanLiteral': - case 'DecimalLiteral': - case 'NullLiteral': - case 'NumericLiteral': - case 'RegExpLiteral': - case 'StringLiteral': - return expr; + // 对于下面这些比较安全的字面值,可以直接返回对应的表达式,而非包一层 + if (isSimpleStraightLiteral(exprAst)) { + return expr; + } + + switch (exprAst.type) { // 对于直接写个函数的,则不用再包下,因为这样不会抛出异常的 case 'ArrowFunctionExpression': case 'FunctionExpression': @@ -221,8 +217,11 @@ function isSimpleDirectlyAccessingThis(exprAst: MemberExpression) { /** this.state.xxx 和 this.utils.xxx 等安全的肯定应该存在的东东 */ function isSimpleDirectlyAccessingSafeProperties(exprAst: MemberExpression): boolean { + const isPropertySimpleStraight = + !exprAst.computed || (exprAst.property.type !== 'PrivateName' && isSimpleStraightLiteral(exprAst.property)); + return ( - !exprAst.computed && + isPropertySimpleStraight && exprAst.object.type === 'MemberExpression' && exprAst.object.object.type === 'ThisExpression' && !exprAst.object.computed && @@ -231,6 +230,22 @@ function isSimpleDirectlyAccessingSafeProperties(exprAst: MemberExpression): boo ); } +/** 判断是非是一些简单直接的字面值 */ +function isSimpleStraightLiteral(expr: Expression): boolean { + switch (expr.type) { + case 'BigIntLiteral': + case 'BooleanLiteral': + case 'DecimalLiteral': + case 'NullLiteral': + case 'NumericLiteral': + case 'RegExpLiteral': + case 'StringLiteral': + return true; + default: + return false; + } +} + function isImportAliasDefineChunk( chunk: ICodeChunk, ): chunk is ICodeChunk & { diff --git a/packages/code-generator/src/plugins/project/i18n.ts b/packages/code-generator/src/plugins/project/i18n.ts index 2ecc68cfb..c5dc0911d 100644 --- a/packages/code-generator/src/plugins/project/i18n.ts +++ b/packages/code-generator/src/plugins/project/i18n.ts @@ -1,5 +1,5 @@ import { COMMON_CHUNK_NAME } from '../../const/generator'; -import { generateCompositeType } from '../../utils/compositeType'; + import { BuilderComponentPlugin, BuilderComponentPluginFactory, @@ -16,53 +16,55 @@ const pluginFactory: BuilderComponentPluginFactory = () => { }; const ir = next.ir as IProjectInfo; - if (ir.i18n) { - const [, i18nStr] = generateCompositeType(ir.i18n, {}); - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileMainContent, - content: ` - const i18nConfig = ${i18nStr}; - let locale = 'en_US'; + const i18nStr = ir.i18n ? JSON.stringify(ir.i18n, null, 2) : '{}'; - const changeLocale = (target) => { - locale = target; - }; + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileMainContent, + content: ` + const i18nConfig = ${i18nStr}; - const i18n = key => i18nConfig && i18nConfig[locale] && i18nConfig[locale][key] || ''; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.ImportAliasDefine, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - ], - }); + let locale = 'en-US'; - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileExport, - content: ` - export { - changeLocale, - i18n, - }; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.ImportAliasDefine, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - COMMON_CHUNK_NAME.FileMainContent, - ], - }); - } + const getLocale = () => locale; + const setLocale = (target) => { + locale = target; + }; + + const i18n = key => i18nConfig && i18nConfig[locale] && i18nConfig[locale][key] || ''; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.ImportAliasDefine, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileExport, + content: ` + export { + getLocale, + setLocale, + i18n, + }; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.ImportAliasDefine, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + COMMON_CHUNK_NAME.FileMainContent, + ], + }); return next; }; return plugin; diff --git a/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/i18n.js b/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..705ba5495 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/i18n.js @@ -0,0 +1,13 @@ +const i18nConfig = {}; + +let locale = 'en-US'; + +const getLocale = () => locale; + +const setLocale = (target) => { + locale = target; +}; + +const i18n = (key) => (i18nConfig && i18nConfig[locale] && i18nConfig[locale][key]) || ''; + +export { getLocale, setLocale, i18n }; diff --git a/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/pages/Home/index.jsx b/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/pages/Home/index.jsx index fce1c34a9..e3e89c9b0 100644 --- a/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/pages/Home/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/pages/Home/index.jsx @@ -13,6 +13,8 @@ import { isMiniApp as __$$isMiniApp } from 'universal-env'; import __$$constants from '../../constants'; +import * as __$$i18n from '../../i18n'; + import __$$projectUtils from '../../utils'; import './index.css'; @@ -22,6 +24,8 @@ class Home$$Page extends Component { _context = this._createContext(); + _i18n = this._createI18nDelegate(); + _dataSourceConfig = this._defineDataSourceConfig(); _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this._context, { runtimeConfig: true }); @@ -74,12 +78,33 @@ class Home$$Page extends Component { get constants() { return __$$constants; }, + get i18n() { + return self._i18n; + }, + getLocale() { + return __$$i18n.getLocale(); + }, + setLocale(locale) { + __$$i18n.setLocale(locale); + self.forceUpdate(); + }, ...this._methods, }; return context; } + _createI18nDelegate() { + return new Proxy( + {}, + { + get(target, prop) { + return __$$i18n.i18n(prop); + }, + }, + ); + } + _defineDataSourceConfig() { const __$$context = this._context; return { list: [] }; diff --git a/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/i18n.js b/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..705ba5495 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/i18n.js @@ -0,0 +1,13 @@ +const i18nConfig = {}; + +let locale = 'en-US'; + +const getLocale = () => locale; + +const setLocale = (target) => { + locale = target; +}; + +const i18n = (key) => (i18nConfig && i18nConfig[locale] && i18nConfig[locale][key]) || ''; + +export { getLocale, setLocale, i18n }; diff --git a/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx b/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx index f506deab2..43c6ca91a 100644 --- a/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx @@ -19,6 +19,8 @@ import { isMiniApp as __$$isMiniApp } from 'universal-env'; import __$$constants from '../../constants'; +import * as __$$i18n from '../../i18n'; + import __$$projectUtils from '../../utils'; import './index.css'; @@ -45,6 +47,8 @@ class Home$$Page extends Component { _context = this._createContext(); + _i18n = this._createI18nDelegate(); + _dataSourceConfig = this._defineDataSourceConfig(); _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this._context, { runtimeConfig: true, @@ -171,12 +175,33 @@ class Home$$Page extends Component { get constants() { return __$$constants; }, + get i18n() { + return self._i18n; + }, + getLocale() { + return __$$i18n.getLocale(); + }, + setLocale(locale) { + __$$i18n.setLocale(locale); + self.forceUpdate(); + }, ...this._methods, }; return context; } + _createI18nDelegate() { + return new Proxy( + {}, + { + get(target, prop) { + return __$$i18n.i18n(prop); + }, + }, + ); + } + _defineDataSourceConfig() { const __$$context = this._context; return { diff --git a/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/i18n.js b/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..705ba5495 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/i18n.js @@ -0,0 +1,13 @@ +const i18nConfig = {}; + +let locale = 'en-US'; + +const getLocale = () => locale; + +const setLocale = (target) => { + locale = target; +}; + +const i18n = (key) => (i18nConfig && i18nConfig[locale] && i18nConfig[locale][key]) || ''; + +export { getLocale, setLocale, i18n }; diff --git a/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/Detail/index.jsx b/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/Detail/index.jsx index 1e104c597..c3f22edd7 100644 --- a/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/Detail/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/Detail/index.jsx @@ -17,6 +17,8 @@ import { isMiniApp as __$$isMiniApp } from 'universal-env'; import __$$constants from '../../constants'; +import * as __$$i18n from '../../i18n'; + import __$$projectUtils from '../../utils'; import './index.css'; @@ -28,6 +30,8 @@ class Detail$$Page extends Component { _context = this._createContext(); + _i18n = this._createI18nDelegate(); + _dataSourceConfig = this._defineDataSourceConfig(); _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this._context, { runtimeConfig: true }); @@ -85,12 +89,33 @@ class Detail$$Page extends Component { get constants() { return __$$constants; }, + get i18n() { + return self._i18n; + }, + getLocale() { + return __$$i18n.getLocale(); + }, + setLocale(locale) { + __$$i18n.setLocale(locale); + self.forceUpdate(); + }, ...this._methods, }; return context; } + _createI18nDelegate() { + return new Proxy( + {}, + { + get(target, prop) { + return __$$i18n.i18n(prop); + }, + }, + ); + } + _defineDataSourceConfig() { const __$$context = this._context; return { list: [] }; diff --git a/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/Home/index.jsx b/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/Home/index.jsx index cb2bd3bab..dbd0e81ad 100644 --- a/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/Home/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/Home/index.jsx @@ -17,6 +17,8 @@ import { isMiniApp as __$$isMiniApp } from 'universal-env'; import __$$constants from '../../constants'; +import * as __$$i18n from '../../i18n'; + import __$$projectUtils from '../../utils'; import './index.css'; @@ -28,6 +30,8 @@ class Home$$Page extends Component { _context = this._createContext(); + _i18n = this._createI18nDelegate(); + _dataSourceConfig = this._defineDataSourceConfig(); _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this._context, { runtimeConfig: true }); @@ -85,12 +89,33 @@ class Home$$Page extends Component { get constants() { return __$$constants; }, + get i18n() { + return self._i18n; + }, + getLocale() { + return __$$i18n.getLocale(); + }, + setLocale(locale) { + __$$i18n.setLocale(locale); + self.forceUpdate(); + }, ...this._methods, }; return context; } + _createI18nDelegate() { + return new Proxy( + {}, + { + get(target, prop) { + return __$$i18n.i18n(prop); + }, + }, + ); + } + _defineDataSourceConfig() { const __$$context = this._context; return { list: [] }; diff --git a/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/List/index.jsx b/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/List/index.jsx index bd95523fb..c4e62dd2d 100644 --- a/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/List/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo3/expected/demo-project/src/pages/List/index.jsx @@ -17,6 +17,8 @@ import { isMiniApp as __$$isMiniApp } from 'universal-env'; import __$$constants from '../../constants'; +import * as __$$i18n from '../../i18n'; + import __$$projectUtils from '../../utils'; import './index.css'; @@ -28,6 +30,8 @@ class List$$Page extends Component { _context = this._createContext(); + _i18n = this._createI18nDelegate(); + _dataSourceConfig = this._defineDataSourceConfig(); _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this._context, { runtimeConfig: true }); @@ -88,12 +92,33 @@ class List$$Page extends Component { get constants() { return __$$constants; }, + get i18n() { + return self._i18n; + }, + getLocale() { + return __$$i18n.getLocale(); + }, + setLocale(locale) { + __$$i18n.setLocale(locale); + self.forceUpdate(); + }, ...this._methods, }; return context; } + _createI18nDelegate() { + return new Proxy( + {}, + { + get(target, prop) { + return __$$i18n.i18n(prop); + }, + }, + ); + } + _defineDataSourceConfig() { const __$$context = this._context; return { list: [] }; diff --git a/packages/code-generator/test-cases/rax-app/demo4/expected/demo-project/src/i18n.js b/packages/code-generator/test-cases/rax-app/demo4/expected/demo-project/src/i18n.js new file mode 100644 index 000000000..705ba5495 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo4/expected/demo-project/src/i18n.js @@ -0,0 +1,13 @@ +const i18nConfig = {}; + +let locale = 'en-US'; + +const getLocale = () => locale; + +const setLocale = (target) => { + locale = target; +}; + +const i18n = (key) => (i18nConfig && i18nConfig[locale] && i18nConfig[locale][key]) || ''; + +export { getLocale, setLocale, i18n }; diff --git a/packages/code-generator/test-cases/rax-app/demo4/expected/demo-project/src/pages/Home/index.jsx b/packages/code-generator/test-cases/rax-app/demo4/expected/demo-project/src/pages/Home/index.jsx index 287c12ad2..0a93e8574 100644 --- a/packages/code-generator/test-cases/rax-app/demo4/expected/demo-project/src/pages/Home/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo4/expected/demo-project/src/pages/Home/index.jsx @@ -17,6 +17,8 @@ import { isMiniApp as __$$isMiniApp } from 'universal-env'; import __$$constants from '../../constants'; +import * as __$$i18n from '../../i18n'; + import __$$projectUtils from '../../utils'; import './index.css'; @@ -28,6 +30,8 @@ class Home$$Page extends Component { _context = this._createContext(); + _i18n = this._createI18nDelegate(); + _dataSourceConfig = this._defineDataSourceConfig(); _dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this._context, { runtimeConfig: true }); @@ -82,12 +86,33 @@ class Home$$Page extends Component { get constants() { return __$$constants; }, + get i18n() { + return self._i18n; + }, + getLocale() { + return __$$i18n.getLocale(); + }, + setLocale(locale) { + __$$i18n.setLocale(locale); + self.forceUpdate(); + }, ...this._methods, }; return context; } + _createI18nDelegate() { + return new Proxy( + {}, + { + get(target, prop) { + return __$$i18n.i18n(prop); + }, + }, + ); + } + _defineDataSourceConfig() { const __$$context = this._context; return { list: [] }; diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.editorconfig b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.editorconfig new file mode 100644 index 000000000..5760be583 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.editorconfig @@ -0,0 +1,12 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.eslintignore b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.eslintignore new file mode 100644 index 000000000..3b437e614 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.eslintignore @@ -0,0 +1,11 @@ +# 忽略目录 +build/ +tests/ +demo/ + +# node 覆盖率文件 +coverage/ + +# 忽略文件 +**/*-min.js +**/*.min.js diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.eslintrc.js b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.eslintrc.js new file mode 100644 index 000000000..e2a7c5b54 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ['rax'], +}; diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.gitignore b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.gitignore new file mode 100644 index 000000000..50a53dace --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/.gitignore @@ -0,0 +1,17 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +*~ +*.swp +*.log + +.DS_Store +.idea/ +.temp/ + +build/ +dist/ +lib/ +coverage/ +node_modules/ + +template.yml diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/README.md b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/README.md new file mode 100644 index 000000000..6eff85d41 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/README.md @@ -0,0 +1,15 @@ +# @ali/rax-component-demo + +## Getting Started + +### `npm run start` + +Runs the app in development mode. + +Open [http://localhost:9999](http://localhost:9999) to view it in the browser. + +The page will reload if you make edits. + +### `npm run build` + +Builds the app for production to the `build` folder. diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/abc.json b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/abc.json new file mode 100644 index 000000000..f9ee40f71 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/abc.json @@ -0,0 +1,7 @@ +{ + "type": "rax", + "builder": "@ali/builder-rax-v1", + "info": { + "raxVersion": "1.x" + } +} diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/build.json b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/build.json new file mode 100644 index 000000000..f3e9b9323 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/build.json @@ -0,0 +1,12 @@ +{ + "inlineStyle": false, + "plugins": [ + [ + "build-plugin-rax-app", + { + "targets": ["web", "miniapp"] + } + ], + "@ali/build-plugin-rax-app-def" + ] +} diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/package.json b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/package.json new file mode 100644 index 000000000..a42398577 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/package.json @@ -0,0 +1,32 @@ +{ + "name": "@ali/rax-app-demo", + "private": true, + "version": "1.0.0", + "scripts": { + "build": "build-scripts build", + "start": "build-scripts start", + "lint": "eslint --ext .js --ext .jsx ./" + }, + "dependencies": { + "@ali/lowcode-datasource-engine": "^0.1.0", + "universal-env": "^3.2.0", + "rax": "^1.1.0", + "rax-app": "^2.0.0", + "rax-document": "^0.1.0", + "rax-view": "^1.0.0", + "rax-text": "^1.0.0" + }, + "devDependencies": { + "build-plugin-rax-app": "^5.0.0", + "@alib/build-scripts": "^0.1.0", + "@typescript-eslint/eslint-plugin": "^2.11.0", + "@typescript-eslint/parser": "^2.11.0", + "babel-eslint": "^10.0.3", + "eslint": "^6.8.0", + "eslint-config-rax": "^0.1.0", + "eslint-plugin-import": "^2.20.0", + "eslint-plugin-module": "^0.1.0", + "eslint-plugin-react": "^7.18.0", + "@ali/build-plugin-rax-app-def": "^1.0.0" + } +} diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/app.js b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/app.js new file mode 100644 index 000000000..bc474c6e3 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/app.js @@ -0,0 +1,6 @@ +import { runApp } from 'rax-app'; +import appConfig from './app.json'; + +import './global.scss'; + +runApp(appConfig); diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/app.json b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/app.json new file mode 100644 index 000000000..63dec4d7d --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/app.json @@ -0,0 +1,11 @@ +{ + "routes": [ + { + "path": "/", + "source": "pages/Home/index" + } + ], + "window": { + "title": "Rax App Demo" + } +} diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/constants.js b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/constants.js new file mode 100644 index 000000000..ea766c9da --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/constants.js @@ -0,0 +1,3 @@ +const __$$constants = {}; + +export default __$$constants; diff --git a/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/document/index.jsx b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/document/index.jsx new file mode 100644 index 000000000..50569a9b6 --- /dev/null +++ b/packages/code-generator/test-cases/rax-app/demo5/expected/demo-project/src/document/index.jsx @@ -0,0 +1,25 @@ +import { createElement } from 'rax'; +import { Root, Style, Script } from 'rax-document'; + +function Document() { + return ( + + + + + Rax App Demo +