diff --git a/compiler/babel.config.js b/compiler/babel.config.js index 1c8aa70516ca7db5bda81d75107c87ef219eea50..ed99ab39af3f8807fe5b91fa5256351c050a42fb 100644 --- a/compiler/babel.config.js +++ b/compiler/babel.config.js @@ -16,7 +16,7 @@ module.exports = function(api) { api.cache(true); - const presets = ['@babel/preset-env', '@babel/typescript']; + const presets = ['@babel/typescript']; const plugins = [ '@babel/plugin-transform-modules-commonjs', '@babel/plugin-proposal-class-properties', diff --git a/compiler/src/compile_info.ts b/compiler/src/compile_info.ts index c279652a042560616524acb5b1902d17f59e08c6..1d35935defed4caeebe79deb98ab92420a063814 100644 --- a/compiler/src/compile_info.ts +++ b/compiler/src/compile_info.ts @@ -13,6 +13,7 @@ * limitations under the License. */ +import * as ts from 'typescript'; import Stats from 'webpack/lib/Stats'; import Compiler from 'webpack/lib/Compiler'; import Compilation from 'webpack/lib/Compilation'; @@ -28,23 +29,23 @@ import CachedSource from 'webpack-sources/lib/CachedSource'; import ConcatSource from 'webpack-sources/lib/ConcatSource'; import { - BUILDIN_STYLE_NAMES, EXTEND_ATTRIBUTE, STYLES_ATTRIBUTE } from './component_map'; import { transformLog } from './process_ui_syntax'; -import { - dollarCollection, - componentCollection, - moduleCollection -} from './validate_ui_syntax'; -import { decoratorParamSet } from './process_component_member'; -import { appComponentCollection } from './process_component_build'; import { projectConfig } from '../main'; import { circularFile } from './utils'; +import { moduleCollection } from './validate_ui_syntax'; import { MODULE_SHARE_PATH, BUILD_SHARE_PATH } from './pre_define'; import { COMMON_ATTRS } from './component_map'; +import { + createLanguageService, + dollarCollection, + appComponentCollection, + decoratorParamsCollection +} from './ets_checker'; + configure({ appenders: { 'ETS': {type: 'stderr', layout: {type: 'messagePassThrough'}}}, categories: {'default': {appenders: ['ETS'], level: 'info'}} @@ -85,7 +86,7 @@ export class ResultStates { compiler.hooks.compilation.tap('SourcemapFixer', compilation => { compilation.hooks.afterProcessAssets.tap('SourcemapFixer', assets => { Reflect.ownKeys(assets).forEach(key => { - if (/\.map$/.test(key.toString())) { + if (/\.map$/.test(key.toString()) && assets[key]._value) { assets[key]._value = assets[key]._value.toString().replace('.ets?entry', '.ets'); } }); @@ -139,17 +140,45 @@ export class ResultStates { }); }); + compiler.hooks.run.tapPromise('make', async(compiler) => { + const rootFileNames: string[] = []; + Object.values(projectConfig.entryObj).forEach((fileName: string) => { + rootFileNames.push(fileName.replace('?entry', '')); + }); + const languageService: ts.LanguageService = createLanguageService(rootFileNames); + const rootProgram: ts.Program = languageService.getProgram(); + props.push(...dollarCollection, ...decoratorParamsCollection); + let allDiagnostics: ts.Diagnostic[] = rootProgram + .getSyntacticDiagnostics() + .concat(rootProgram.getSemanticDiagnostics()) + .concat(rootProgram.getDeclarationDiagnostics()); + allDiagnostics = allDiagnostics.filter((item) => { + return this.validateError(ts.flattenDiagnosticMessageText(item.messageText, '\n')); + }); + this.mErrorCount += allDiagnostics.length; + allDiagnostics.forEach((diagnostic: ts.Diagnostic) => { + const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); + if (diagnostic.file) { + const { line, character }: ts.LineAndCharacter = + diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); + logger.error(this.red, + `ETS:ERROR File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}\n ${message}\n`); + } else { + logger.error(this.red, `ETS:ERROR: ${message}`); + } + }); + }); + compiler.hooks.done.tap('Result States', (stats: Stats) => { this.mStats = stats; this.warningCount = 0; this.noteCount = 0; if (this.mStats.compilation.errors) { - this.mErrorCount = this.mStats.compilation.errors.length; + this.mErrorCount += this.mStats.compilation.errors.length; } if (this.mStats.compilation.warnings) { this.mWarningCount = this.mStats.compilation.warnings.length; } - props.push(...dollarCollection, ...decoratorParamSet, ...BUILDIN_STYLE_NAMES); this.printResult(); }); @@ -254,12 +283,10 @@ export class ResultStates { } private validateError(message: string): boolean { const propInfoReg: RegExp = /Cannot find name\s*'(\$?\$?[_a-zA-Z0-9]+)'/; - const componentNameReg: RegExp = /'typeof\s*(\$?[_a-zA-Z0-9]+)' is not callable/; const stateInfoReg: RegExp = /Property\s*'(\$[_a-zA-Z0-9]+)' does not exist on type/; const extendInfoReg: RegExp = /Property\s*'([_a-zA-Z0-9]+)' does not exist on type\s*'([_a-zA-Z0-9]+)(Attribute|Interface)'\./; - if (this.matchMessage(message, props.concat([...STYLES_ATTRIBUTE]), propInfoReg) || - this.matchMessage(message, [...componentCollection.customComponents], componentNameReg) || + if (this.matchMessage(message, props, propInfoReg) || this.matchMessage(message, props, stateInfoReg) || this.matchMessage(message, EXTEND_ATTRIBUTE, extendInfoReg, true) || this.matchMessage(message, [...STYLES_ATTRIBUTE, ...COMMON_ATTRS], extendInfoReg)) { @@ -288,7 +315,7 @@ export class ResultStates { } private filterModuleError(message: string): string { if (/You may need an additional loader/.test(message) && transformLog && transformLog.sourceFile) { - const fileName: string = transformLog.sourceFile.fileName.replace(/.ts$/, ''); + const fileName: string = transformLog.sourceFile.fileName; const errorInfos: string[] = message.split('You may need an additional loader to handle the result of these loaders.'); if (errorInfos && errorInfos.length > 1 && errorInfos[1]) { message = `ERROR in ${fileName}\n The following syntax is incorrect.${errorInfos[1]}`; diff --git a/compiler/src/component_map.ts b/compiler/src/component_map.ts index 615a675f923216b4b940906dc3878ff1f288e3ba..9cd3fa78eaa2fddb1d23f18ebe63e13d0784f034 100644 --- a/compiler/src/component_map.ts +++ b/compiler/src/component_map.ts @@ -81,7 +81,7 @@ export const STYLES_ATTRIBUTE: Set = new Set(); export const INTERFACE_NODE_SET: Set = new Set(); export const JS_BIND_COMPONENTS: Set = new Set([ - ...GESTURE_TYPE_NAMES, 'Gesture', + 'ForEach', 'LazyForEach', ...GESTURE_TYPE_NAMES, 'Gesture', 'PanGestureOption', 'CustomDialogController', 'Storage', 'Scroller', 'SwiperController', 'TabsController', 'CalendarController', 'AbilityController', 'VideoController', 'WebController', 'XComponentController', 'CanvasRenderingContext2D', 'CanvasGradient', 'ImageBitmap', 'ImageData', diff --git a/compiler/src/ets_checker.ts b/compiler/src/ets_checker.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb1762ed16818087683ed0d36e926c64b0aa2274 --- /dev/null +++ b/compiler/src/ets_checker.ts @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import path from 'path'; +import * as ts from 'typescript'; + +import { projectConfig } from '../main'; +import { processSystemApi } from './validate_ui_syntax'; +import { + INNER_COMPONENT_MEMBER_DECORATORS, + COMPONENT_IF, + COMPONENT_DECORATORS_PARAMS +} from './pre_define'; +import { JS_BIND_COMPONENTS } from './component_map'; + +function readDeaclareFiles(): string[] { + const declarationsFileNames: string[] = []; + fs.readdirSync(path.resolve(__dirname, '../declarations')) + .forEach((fileName: string) => { + if (/\.d\.ts$/.test(fileName)) { + declarationsFileNames.push(path.resolve(__dirname, '../declarations', fileName)); + } + }); + return declarationsFileNames; +} + +export function createLanguageService(rootFileNames: string[]): ts.LanguageService { + const compilerOptions: ts.CompilerOptions = ts.readConfigFile( + path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions; + Object.assign(compilerOptions, { + 'moduleResolution': ts.ModuleResolutionKind.NodeJs, + 'target': ts.ScriptTarget.ES2017, + 'baseUrl': path.resolve(projectConfig.projectPath), + 'paths': { + '*': [ + '*', + '../../../../../*' + ] + }, + 'lib': [ + 'lib.es2020.d.ts' + ] + }); + const files: ts.MapLike<{ version: number }> = {}; + const servicesHost: ts.LanguageServiceHost = { + getScriptFileNames: () => [...rootFileNames, ...readDeaclareFiles()], + getScriptVersion: fileName => + files[fileName] && files[fileName].version.toString(), + getScriptSnapshot: fileName => { + if (!fs.existsSync(fileName)) { + return undefined; + } + if (/(? process.cwd(), + getCompilationSettings: () => compilerOptions, + getDefaultLibFileName: options => ts.getDefaultLibFilePath(options), + fileExists: ts.sys.fileExists, + readFile: ts.sys.readFile, + readDirectory: ts.sys.readDirectory, + resolveModuleNames(moduleNames: string[], containingFile: string): ts.ResolvedModuleFull[] { + const resolvedModules: ts.ResolvedModuleFull[] = []; + for (const moduleName of moduleNames) { + const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, { + fileExists(fileName: string): boolean { + return ts.sys.fileExists(fileName); + }, + readFile(fileName: string): string | undefined { + return ts.sys.readFile(fileName); + } + }); + if (result.resolvedModule) { + resolvedModules.push(result.resolvedModule); + } else if (/^@(system|ohos)/.test(moduleName.trim())) { + const modulePath: string = path.resolve(__dirname, '../../../api/common', moduleName + '.d.ts'); + if (ts.sys.fileExists(modulePath)) { + resolvedModules.push(getResolveModule(modulePath, '.d.ts')); + } + } else if (/\.ets$/.test(moduleName)) { + const modulePath: string = path.resolve(path.dirname(containingFile), moduleName); + if (ts.sys.fileExists(modulePath)) { + resolvedModules.push(getResolveModule(modulePath, '.ets')); + } + } else if (/\.ts$/.test(moduleName)) { + const modulePath: string = path.resolve(path.dirname(containingFile), moduleName); + if (ts.sys.fileExists(modulePath)) { + resolvedModules.push(getResolveModule(modulePath, '.ts')); + } + } + } + if (moduleNames.length !== resolvedModules.length) { + const length: number = moduleNames.length - resolvedModules.length; + for (let i = 0; i < length; i++) { + resolvedModules.push(null); + } + } + return resolvedModules; + }, + directoryExists: ts.sys.directoryExists, + getDirectories: ts.sys.getDirectories + }; + return ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); +} + +function getResolveModule(modulePath: string, type): ts.ResolvedModuleFull { + return { + resolvedFileName: modulePath, + isExternalLibraryImport: false, + extension: type + }; +} + +export const dollarCollection: Set = new Set(); +export const appComponentCollection: Set = new Set(); +export const decoratorParamsCollection: Set = new Set(); + +function checkUISyntax(source: string, fileName: string): void { + if (/\.ets$/.test(fileName)) { + if (path.basename(fileName) !== 'app.ets') { + const sourceFile: ts.SourceFile = ts.createSourceFile(fileName, source, + ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); + collectComponents(sourceFile); + parseAllNode(sourceFile, sourceFile); + } + } +} + +function collectComponents(node: ts.SourceFile): void { + // @ts-ignore + if (node.identifiers && node.identifiers.size) { + // @ts-ignore + for (const key of node.identifiers.keys()) { + if (JS_BIND_COMPONENTS.has(key)) { + appComponentCollection.add(key); + } + } + } +} + +function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile): void { + if (ts.isStructDeclaration(node)) { + if (node.members) { + node.members.forEach(item => { + if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) { + const propertyName: string = item.name.getText(); + if (item.decorators && item.decorators.length) { + for (let i = 0; i < item.decorators.length; i++) { + const decoratorName: string = item.decorators[i].getText().replace(/\(.*\)$/, '').trim(); + if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { + dollarCollection.add('$' + propertyName); + } + if (isDecoratorCollection(item.decorators[i], decoratorName)) { + decoratorParamsCollection.add(item.decorators[i].expression.arguments[0].getText()); + } + } + } + } + }); + } + } + if (ts.isIfStatement(node)) { + appComponentCollection.add(COMPONENT_IF); + } + node.getChildren().forEach((item: ts.Node) => parseAllNode(item, sourceFileNode)); +} + +function isDecoratorCollection(item: ts.Decorator, decoratorName: string): boolean { + return COMPONENT_DECORATORS_PARAMS.has(decoratorName) && + item.expression.arguments && item.expression.arguments.length && + ts.isIdentifier(item.expression.arguments[0]); +} + +function processDraw(source: string): string { + const reg: RegExp = /new\s+(Circle|Ellipse|Rect|Path)/g; + return source.replace(reg, (item:string, item1: string) => { + return '\xa0'.repeat(item.length - item1.length) + item1; + }); +} diff --git a/compiler/src/pre_define.ts b/compiler/src/pre_define.ts index 2acba62299d9d81131c222cfa7747e1cc6f63557..3192d1270a195768fb82943d8ec7c219f0c9d893 100644 --- a/compiler/src/pre_define.ts +++ b/compiler/src/pre_define.ts @@ -38,6 +38,9 @@ export const COMPONENT_OBJECT_LINK_DECORATOR: string = '@ObjectLink'; export const COMPONENT_WATCH_DECORATOR: string = '@Watch'; export const COMPONENT_BUILDERPARAM_DECORATOR: string = '@BuilderParam'; +export const COMPONENT_DECORATORS_PARAMS: Set = new Set([COMPONENT_CONSUME_DECORATOR, + COMPONENT_STORAGE_PROP_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR, COMPONENT_PROVIDE_DECORATOR, + COMPONENT_WATCH_DECORATOR]); export const INNER_COMPONENT_DECORATORS: Set = new Set([COMPONENT_DECORATOR_ENTRY, COMPONENT_DECORATOR_PREVIEW, COMPONENT_DECORATOR_COMPONENT, COMPONENT_DECORATOR_CUSTOM_DIALOG]); export const INNER_COMPONENT_MEMBER_DECORATORS: Set = new Set([COMPONENT_STATE_DECORATOR, diff --git a/compiler/src/process_component_build.ts b/compiler/src/process_component_build.ts index d8db918fd416a9323798ec6d057af6ac9af0115c..35ab50b85b8891b8cde7fde18629a640494b3196 100644 --- a/compiler/src/process_component_build.ts +++ b/compiler/src/process_component_build.ts @@ -88,8 +88,6 @@ import { projectConfig } from '../main'; import { transformLog, contextGlobal } from './process_ui_syntax'; import { props } from './compile_info'; -export const appComponentCollection: Set = new Set(); - export function processComponentBuild(node: ts.MethodDeclaration, log: LogInfo[]): ts.MethodDeclaration { let newNode: ts.MethodDeclaration; @@ -130,27 +128,15 @@ export function processComponentBlock(node: ts.Block, isLazy: boolean, log: LogI function validateRootNode(node: ts.MethodDeclaration, log: LogInfo[]): boolean { let isValid: boolean = false; - if (node.body.statements.length < 4) { - switch (node.body.statements.length) { - case 1: - if (validateFirstNode(node.body.statements[0])) { - isValid = true; - } - break; - case 2: - if (validateFirstNode(node.body.statements[0]) && - validateBlockNode(node.body.statements[1])) { - isValid = true; - } - break; - case 3: - if (validateFirstNode(node.body.statements[0]) && - validateBlockNode(node.body.statements[1]) && - validateSecondNode(node.body.statements[2])) { - isValid = true; - } - break; + if (node.body.statements.length === 1) { + const statement: ts.Node = node.body.statements[0]; + if (ts.isIfStatement(statement) || ts.isExpressionStatement(statement) && statement.expression && + (ts.isEtsComponentExpression(statement.expression) || ts.isCallExpression(statement.expression)) && + validateEtsComponentNode(statement.expression)) { + isValid = true; } + } else { + isValid = false; } if (!isValid) { log.push({ @@ -162,6 +148,19 @@ function validateRootNode(node: ts.MethodDeclaration, log: LogInfo[]): boolean { return isValid; } +function validateEtsComponentNode(node: ts.CallExpression | ts.EtsComponentExpression) { + let childNode: ts.Node = node; + while (ts.isCallExpression(childNode) && childNode.expression && + ts.isPropertyAccessExpression(childNode.expression) && childNode.expression.expression) { + childNode = childNode.expression.expression; + } + if (ts.isEtsComponentExpression(childNode)) { + return true; + } else { + return false; + } +} + export function processComponentChild(node: ts.Block | ts.SourceFile, newStatements: ts.Statement[], log: LogInfo[]): void { if (node.statements.length) { @@ -174,8 +173,8 @@ export function processComponentChild(node: ts.Block | ts.SourceFile, newStateme newStatements, log, name); break; case ComponentType.customComponent: - if (index + 1 < array.length && ts.isBlock(array[index + 1])) { - if (processExpressionStatementChange(item, array[index + 1] as ts.Block, log)) { + if (item.expression && ts.isEtsComponentExpression(item.expression) && item.expression.body) { + if (processExpressionStatementChange(item, item.expression.body, log)) { item = processExpressionStatementChange(item, array[index + 1] as ts.Block, log); } } @@ -190,7 +189,6 @@ export function processComponentChild(node: ts.Block | ts.SourceFile, newStateme break; } } else if (ts.isIfStatement(item)) { - appComponentCollection.add(COMPONENT_IF); processIfStatement(item, newStatements, log); } else if (!ts.isBlock(item)) { log.push({ @@ -247,6 +245,29 @@ function processBlockToExpression(node: ts.ExpressionStatement, nextNode: ts.Blo return node; } +type EtsComponentResult = { + etsComponentNode: ts.EtsComponentExpression; + hasAttr: boolean; +} + +function parseEtsComponentExpression(node: ts.ExpressionStatement): EtsComponentResult { + let etsComponentNode: ts.EtsComponentExpression; + let hasAttr: boolean = false; + let temp: any = node.expression; + while (temp) { + if (ts.isCallExpression(temp) && temp.expression && + ts.isPropertyAccessExpression(temp.expression)) { + hasAttr = true; + } + if (ts.isEtsComponentExpression(temp)) { + etsComponentNode = temp; + break; + } + temp = temp.expression; + } + return { etsComponentNode: etsComponentNode, hasAttr: hasAttr }; +} + function processInnerComponent(node: ts.ExpressionStatement, index: number, arr: ts.Statement[], newStatements: ts.Statement[], log: LogInfo[], name: string): void { const res: CreateResult = createComponent(node, COMPONENT_CREATE_FUNCTION); @@ -255,7 +276,7 @@ function processInnerComponent(node: ts.ExpressionStatement, index: number, arr: const posOfNode: ts.LineAndCharacter = transformLog.sourceFile.getLineAndCharacterOfPosition(getRealNodePos(node)); const projectPath: string = projectConfig.projectPath; - const curFileName: string = transformLog.sourceFile.fileName.replace(/.ts$/, ''); + const curFileName: string = transformLog.sourceFile.fileName; const debugInfo: string = `${path.relative(projectPath, curFileName).replace(/\\+/g, '/')}` + `(${posOfNode.line + 1}:${posOfNode.character + 1})`; @@ -265,7 +286,8 @@ function processInnerComponent(node: ts.ExpressionStatement, index: number, arr: ts.factory.createNodeArray([ts.factory.createStringLiteral(debugInfo)]))); newStatements.push(debugNode); } - if (index + 1 < arr.length && ts.isBlock(arr[index + 1])) { + const etsComponentResult: EtsComponentResult = parseEtsComponentExpression(node); + if (etsComponentResult.etsComponentNode.body && ts.isBlock(etsComponentResult.etsComponentNode.body)) { if (res.isButton) { if (projectConfig.isPreview) { newStatements.splice(-2, 1, createComponent(node, COMPONENT_CREATE_CHILD_FUNCTION).newNode); @@ -273,11 +295,10 @@ function processInnerComponent(node: ts.ExpressionStatement, index: number, arr: newStatements.splice(-1, 1, createComponent(node, COMPONENT_CREATE_CHILD_FUNCTION).newNode); } } - if (index + 2 < arr.length && ts.isExpressionStatement(arr[index + 2]) && - isAttributeNode(arr[index + 2] as ts.ExpressionStatement)) { - bindComponentAttr(arr[index + 2] as ts.ExpressionStatement, res.identifierNode, newStatements, log); + if (etsComponentResult.hasAttr) { + bindComponentAttr(node, res.identifierNode, newStatements, log); } - processComponentChild(arr[index + 1] as ts.Block, newStatements, log); + processComponentChild(etsComponentResult.etsComponentNode.body, newStatements, log); } else { bindComponentAttr(node, res.identifierNode, newStatements, log); } @@ -488,7 +509,8 @@ function createComponent(node: ts.ExpressionStatement, type: string): CreateResu while (temp && !ts.isIdentifier(temp) && temp.expression) { temp = temp.expression; } - if (temp && temp.parent && ts.isCallExpression(temp.parent) && ts.isIdentifier(temp)) { + if (temp && temp.parent && (ts.isCallExpression(temp.parent) || + ts.isEtsComponentExpression(temp.parent)) && ts.isIdentifier(temp)) { if (temp.getText() === COMPONENT_BUTTON && type !== COMPONENT_POP_FUNCTION) { res.isButton = true; identifierNode = type === COMPONENT_CREATE_CHILD_FUNCTION @@ -622,7 +644,7 @@ function processDragStartBuilder(node: ts.CallExpression): ts.CallExpression { // @ts-ignore for (let i = 0; i < node.arguments[0].body.statements.length; i++) { // @ts-ignore - let statement: ts.Statement = node.arguments[0].body.statements[i]; + const statement: ts.Statement = node.arguments[0].body.statements[i]; newStatements.push(checkStatement(statement)); } node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ts.factory.updateArrowFunction( @@ -644,7 +666,7 @@ function checkStatement(statement: ts.Statement): ts.Statement { if (ts.isObjectLiteralExpression(statement.expression)) { const newProperties: ts.ObjectLiteralElementLike[] = []; for (let j = 0; j < statement.expression.properties.length; j++) { - let property: ts.ObjectLiteralElementLike = statement.expression.properties[j]; + const property: ts.ObjectLiteralElementLike = statement.expression.properties[j]; checkProperty(property); newProperties.push(property); } @@ -838,18 +860,31 @@ function addComponentAttr(temp: any, node: ts.Identifier, lastStatement: any, if (!COMMON_ATTRS.has(propName)) { validateStateStyleSyntax(temp, log); } - if (isGlobalStyles) { - for (let i = 0; i < temp.arguments.length; i++) { - temp.arguments[i] = traverseStylesAttr(temp.arguments[i]); - } - } } + temp = loopEtsComponent(temp, isStylesAttr, isGlobalStyles); statements.push(ts.factory.createExpressionStatement( createFunction(identifierNode, node, temp.arguments))); lastStatement.kind = true; } } +function loopEtsComponent(temp: any, isStylesAttr: boolean, isGlobalStyles: boolean): ts.Node { + temp.arguments.forEach((item: ts.Node, index: number) => { + if (isStylesAttr && isGlobalStyles) { + temp.arguments[index] = traverseStylesAttr(item); + } + if (ts.isNewExpression(item) && item.expression && ts.isEtsComponentExpression( + item.expression)) { + temp.arguments[index] = ts.factory.updateNewExpression(item, item.expression.expression, + undefined, item.expression.arguments); + } else if (ts.isEtsComponentExpression(item)) { + temp.arguments[index] = ts.factory.createCallExpression(item.expression, + undefined, item.arguments); + } + }); + return temp; +} + function classifyArgumentsNum(args: any, argumentsArr: ts.Expression[], propName: string, identifierNode: ts.Identifier): void { if (propName === BIND_POPUP && args.length === 2) { @@ -1018,7 +1053,8 @@ export function getName(node: ts.ExpressionStatement): string { let temp: any = node.expression; let name: string; while (temp) { - if (ts.isIdentifier(temp) && temp.parent && ts.isCallExpression(temp.parent)) { + if (ts.isIdentifier(temp) && temp.parent && (ts.isCallExpression(temp.parent) || + ts.isEtsComponentExpression(temp.parent))) { name = temp.escapedText.toString(); break; } else if (ts.isPropertyAccessExpression(temp) && temp.name && ts.isIdentifier(temp.name) && @@ -1044,46 +1080,6 @@ export function isAttributeNode(node: ts.ExpressionStatement): boolean { return BUILDIN_STYLE_NAMES.has(name); } -function validateFirstNode(node: ts.Statement): boolean { - const isEntryComponent: boolean = - componentCollection.entryComponent === componentCollection.currentClassName; - if (isEntryComponent && validateEntryComponent(node) || - !isEntryComponent && validateCustomComponent(node)) { - return true; - } - return false; -} - -function validateEntryComponent(node: ts.Statement): boolean { - if (ts.isExpressionStatement(node) && BUILDIN_CONTAINER_COMPONENT.has(getName(node))) { - return true; - } - return false; -} - -function validateCustomComponent(node: ts.Statement): boolean { - if (ts.isIfStatement(node) || - ts.isExpressionStatement(node) && (INNER_COMPONENT_NAMES.has(getName(node)) || - componentCollection.customComponents.has(getName(node)))) { - return true; - } - return false; -} - -function validateBlockNode(node: ts.Statement): boolean { - if (ts.isBlock(node)) { - return true; - } - return false; -} - -function validateSecondNode(node: ts.Statement): boolean { - if (ts.isExpressionStatement(node) && isAttributeNode(node)) { - return true; - } - return false; -} - enum ComponentType { innerComponent, customComponent, @@ -1092,10 +1088,26 @@ enum ComponentType { builderParamMethod } +function isEtsComponent(node: ts.ExpressionStatement): boolean { + let isEtsComponent: boolean = false; + let temp: any = node.expression; + while (temp) { + if (ts.isEtsComponentExpression(temp)) { + isEtsComponent = true; + } + temp = temp.expression; + } + return isEtsComponent; +} + function getComponentType(node: ts.ExpressionStatement, log: LogInfo[], name: string): ComponentType { - if (INNER_COMPONENT_NAMES.has(name)) { - return ComponentType.innerComponent; + if (isEtsComponent(node)) { + if (componentCollection.customComponents.has(name)) { + return ComponentType.customComponent; + } else { + return ComponentType.innerComponent; + } } else if (componentCollection.customComponents.has(name)) { return ComponentType.customComponent; } else if (name === COMPONENT_FOREACH || name === COMPONENT_LAZYFOREACH) { diff --git a/compiler/src/process_component_class.ts b/compiler/src/process_component_class.ts index 38f0c8ef8d0a622afd6fad45bc594c182bd788fe..105bcf344c61e2a708e7ea27782da06a6fed6cff 100644 --- a/compiler/src/process_component_class.ts +++ b/compiler/src/process_component_class.ts @@ -31,8 +31,6 @@ import { OBSERVED_PROPERTY_SIMPLE, COMPONENT_BUILD_FUNCTION, BASE_COMPONENT_NAME, - ATTRIBUTE_ANIMATETO, - GLOBAL_CONTEXT, CREATE_CONSTRUCTOR_PARAMS, COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, COMPONENT_CONSTRUCTOR_DELETE_PARAMS, @@ -86,12 +84,12 @@ import { hasDecorator } from './utils'; -export function processComponentClass(node: ts.ClassDeclaration, context: ts.TransformationContext, +export function processComponentClass(node: ts.StructDeclaration, context: ts.TransformationContext, log: LogInfo[], program: ts.Program): ts.ClassDeclaration { validateInheritClass(node, log); const memberNode: ts.ClassElement[] = processMembers(node.members, node.name, context, log, program, checkPreview(node)); - return ts.factory.updateClassDeclaration(node, undefined, node.modifiers, node.name, + return ts.factory.createClassDeclaration(undefined, node.modifiers, node.name, node.typeParameters, updateHeritageClauses(node), memberNode); } @@ -335,7 +333,7 @@ function getGeometryReaderFunctionBlock(node: ts.ArrowFunction | ts.FunctionExpr return processComponentBlock(blockNode, false, log); } -function updateHeritageClauses(node: ts.ClassDeclaration): ts.NodeArray { +function updateHeritageClauses(node: ts.StructDeclaration): ts.NodeArray { const result:ts.HeritageClause[] = []; const heritageClause:ts.HeritageClause = ts.factory.createHeritageClause( ts.SyntaxKind.ExtendsKeyword, @@ -482,7 +480,7 @@ function validateBuildMethodCount(buildCount: BuildCount, parentComponentName: t } } -function validateInheritClass(node: ts.ClassDeclaration, log: LogInfo[]): void { +function validateInheritClass(node: ts.StructDeclaration, log: LogInfo[]): void { if (node.heritageClauses) { log.push({ type: LogType.ERROR, diff --git a/compiler/src/process_component_constructor.ts b/compiler/src/process_component_constructor.ts index 36f56baa33f1625bd7efaa4c19c954e28a904ca5..dda7bfbc0850aab4f45ef3fe01b9cb68d0506cba 100644 --- a/compiler/src/process_component_constructor.ts +++ b/compiler/src/process_component_constructor.ts @@ -20,9 +20,7 @@ import { COMPONENT_CONSTRUCTOR_PARENT, COMPONENT_CONSTRUCTOR_PARAMS, COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, - COMPONENT_WATCH_FUNCTION, - BASE_COMPONENT_NAME, - INTERFACE_NAME_SUFFIX + COMPONENT_WATCH_FUNCTION } from './pre_define'; export function getInitConstructor(members: ts.NodeArray): ts.ConstructorDeclaration { @@ -32,12 +30,11 @@ export function getInitConstructor(members: ts.NodeArray): ts.Construct if (ctorNode) { ctorNode = updateConstructor(ctorNode, [], [], true); } - return ctorNode; + return initConstructorParams(ctorNode); } export function updateConstructor(ctorNode: ts.ConstructorDeclaration, - para: ts.ParameterDeclaration[], addStatements: ts.Statement[], - isSuper: boolean = false, isAdd: boolean = false, parentComponentName?: ts.Identifier): + para: ts.ParameterDeclaration[], addStatements: ts.Statement[], isSuper: boolean = false): ts.ConstructorDeclaration { let modifyPara: ts.ParameterDeclaration[]; if (para && para.length) { @@ -58,46 +55,28 @@ export function updateConstructor(ctorNode: ts.ConstructorDeclaration, } } if (ctorNode) { - let ctorPara: ts.ParameterDeclaration[] | ts.NodeArray = - modifyPara || ctorNode.parameters; - if (isAdd) { - ctorPara = addParamsType(ctorNode, modifyPara, parentComponentName); - } ctorNode = ts.factory.updateConstructorDeclaration(ctorNode, ctorNode.decorators, - ctorNode.modifiers, ctorPara, ts.factory.createBlock(modifyBody || ctorNode.body.statements, true)); + ctorNode.modifiers, modifyPara || ctorNode.parameters, + ts.factory.createBlock(modifyBody || ctorNode.body.statements, true)); } return ctorNode; } -function addParamsType(ctorNode: ts.ConstructorDeclaration, modifyPara: ts.ParameterDeclaration[], - parentComponentName: ts.Identifier): ts.ParameterDeclaration[] { - const tsPara: ts.ParameterDeclaration[] | ts.NodeArray = - modifyPara || ctorNode.parameters; - const newTSPara: ts.ParameterDeclaration[] = []; - tsPara.forEach((item) => { - let parameter: ts.ParameterDeclaration = item; - switch (item.getText()) { - case COMPONENT_CONSTRUCTOR_ID + '?': - parameter = ts.factory.updateParameterDeclaration(item, item.decorators, item.modifiers, - item.dotDotDotToken, item.name, item.questionToken, - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), item.initializer); - break; - case COMPONENT_CONSTRUCTOR_PARENT + '?': - parameter = ts.factory.createParameterDeclaration(item.decorators, item.modifiers, - item.dotDotDotToken, item.name, item.questionToken, - ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(BASE_COMPONENT_NAME), undefined), - item.initializer); - break; - case COMPONENT_CONSTRUCTOR_PARAMS + '?': - parameter = ts.factory.updateParameterDeclaration(item, item.decorators, item.modifiers, - item.dotDotDotToken, item.name, item.questionToken, - ts.factory.createTypeReferenceNode(ts.factory.createIdentifier( - parentComponentName.getText() + INTERFACE_NAME_SUFFIX), undefined), item.initializer); - break; - } - newTSPara.push(parameter); +function initConstructorParams(node: ts.ConstructorDeclaration): ts.ConstructorDeclaration { + const paramNames: string[] = [COMPONENT_CONSTRUCTOR_ID, COMPONENT_CONSTRUCTOR_PARENT, + COMPONENT_CONSTRUCTOR_PARAMS]; + const newParameters: ts.ParameterDeclaration[] = Array.from(node.parameters); + if (newParameters.length !== 0) { + // @ts-ignore + newParameters.splice(0, newParameters.length); + } + paramNames.forEach((paramName: string) => { + // @ts-ignore + newParameters.push(ts.factory.createParameterDeclaration(undefined, undefined, undefined, + ts.factory.createIdentifier(paramName), undefined, undefined, undefined)); }); - return newTSPara; + return ts.factory.updateConstructorDeclaration(node, undefined, node.modifiers, newParameters, + node.body); } export function addConstructor(ctorNode: any, watchMap: Map, @@ -129,5 +108,5 @@ export function addConstructor(ctorNode: any, watchMap: Map, ts.factory.createThis(), ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UPDATE_PARAMS)), undefined, [ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARAMS)])); return updateConstructor(updateConstructor(ctorNode, [], [callSuperStatement], true), [], - [updateWithValueParamsStatement, ...watchStatements], false, true, parentComponentName); + [updateWithValueParamsStatement, ...watchStatements], false); } diff --git a/compiler/src/process_ui_syntax.ts b/compiler/src/process_ui_syntax.ts index e3221796c198461c2af668be4c13f3bd4de13940..f0607768283566222eed63899b6e2418ac4ebf7f 100644 --- a/compiler/src/process_ui_syntax.ts +++ b/compiler/src/process_ui_syntax.ts @@ -48,15 +48,13 @@ import { getName, isAttributeNode, processComponentBlock, - bindComponentAttr, - appComponentCollection + bindComponentAttr } from './process_component_build'; import { BUILDIN_CONTAINER_COMPONENT, BUILDIN_STYLE_NAMES, CUSTOM_BUILDER_METHOD, EXTEND_ATTRIBUTE, - JS_BIND_COMPONENTS, INNER_STYLE_FUNCTION, GLOBAL_STYLE_FUNCTION, INTERFACE_NODE_SET @@ -73,13 +71,11 @@ export function processUISyntax(program: ts.Program, ut = false): Function { return (node: ts.SourceFile) => { pagesDir = path.resolve(path.dirname(node.fileName)); if (process.env.compiler === BUILD_ON) { - if (!ut && (path.basename(node.fileName) === 'app.ets.ts' || !/\.ets\.ts$/.test(node.fileName))) { + if (!ut && (path.basename(node.fileName) === 'app.ets' || /\.ts$/.test(node.fileName))) { node = ts.visitEachChild(node, processResourceNode, context); return node; } - collectComponents(node); transformLog.sourceFile = node; - validateSourceFileNode(node); node = createEntryNode(node, context); node = ts.visitEachChild(node, processAllNodes, context); GLOBAL_STYLE_FUNCTION.forEach((block, styleName) => { @@ -100,8 +96,7 @@ export function processUISyntax(program: ts.Program, ut = false): Function { function processAllNodes(node: ts.Node): ts.Node { if (ts.isImportDeclaration(node) || ts.isImportEqualsDeclaration(node)) { processImport(node, pagesDir, transformLog.errors); - } else if (ts.isClassDeclaration(node) && node.name && - componentCollection.customComponents.has(node.name.getText())) { + } else if (ts.isStructDeclaration(node)) { componentCollection.currentClassName = node.name.getText(); node = processComponentClass(node, context, transformLog.errors, program); componentCollection.currentClassName = null; @@ -161,18 +156,6 @@ export function processUISyntax(program: ts.Program, ut = false): Function { }; } -function collectComponents(node: ts.SourceFile): void { - // @ts-ignore - if (node.identifiers && node.identifiers.size) { - // @ts-ignore - for (const key of node.identifiers.keys()) { - if (JS_BIND_COMPONENTS.has(key)) { - appComponentCollection.add(key); - } - } - } -} - function isResource(node: ts.Node): boolean { return ts.isCallExpression(node) && ts.isIdentifier(node.expression) && (node.expression.escapedText.toString() === RESOURCE || diff --git a/compiler/src/result_process.ts b/compiler/src/result_process.ts index fb0269ad4b64546e0c524cee5e380250a612b012..9b6e18f3adc9c26f1e2e92262b2ba4711ba2b50c 100644 --- a/compiler/src/result_process.ts +++ b/compiler/src/result_process.ts @@ -52,7 +52,7 @@ module.exports = function resultProcess(source: string, map: any): void { item.line = undefined; item.column = undefined; } - item.fileName = sourceFile.fileName.replace(/.ts$/, ''); + item.fileName = sourceFile.fileName; return item; }); emitLogInfo(this, logInfos); diff --git a/compiler/src/utils.ts b/compiler/src/utils.ts index c7a7700bbf4cc6e5436220aa60a69f42568229be..39a17cd24efee7a94a70104cea5a0c1999d2a509 100644 --- a/compiler/src/utils.ts +++ b/compiler/src/utils.ts @@ -112,8 +112,8 @@ class ComponentInfo { export const componentInfo: ComponentInfo = new ComponentInfo(); -export function hasDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration | ts.ClassDeclaration, - decortorName: string): boolean { +export function hasDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration | + ts.StructDeclaration | ts.ClassDeclaration, decortorName: string): boolean { if (node.decorators && node.decorators.length) { for (let i = 0; i < node.decorators.length; i++) { if (node.decorators[i].getText() === decortorName) { @@ -205,4 +205,4 @@ function mkDir(path_: string): void { mkDir(parent); } fs.mkdirSync(path_); -} +} diff --git a/compiler/src/validate_ui_syntax.ts b/compiler/src/validate_ui_syntax.ts index 5dd4474de06c4905656f6b5f87e39dea53bcb593..6b8ad29b53d4c10fb80bf1cadc638d71b4c26d9d 100644 --- a/compiler/src/validate_ui_syntax.ts +++ b/compiler/src/validate_ui_syntax.ts @@ -22,8 +22,6 @@ import { COMPONENT_DECORATOR_PREVIEW, COMPONENT_DECORATOR_COMPONENT, COMPONENT_DECORATOR_CUSTOM_DIALOG, - STRUCT, - CLASS, NATIVE_MODULE, SYSTEM_PLUGIN, OHOS_PLUGIN, @@ -38,9 +36,6 @@ import { COMPONENT_PROVIDE_DECORATOR, COMPONENT_CONSUME_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR, - COMPONENT_CONSTRUCTOR_ID, - COMPONENT_CONSTRUCTOR_PARENT, - COMPONENT_CONSTRUCTOR_PARAMS, COMPONENT_OBSERVED_DECORATOR, STYLES, VALIDATE_MODULE, @@ -137,7 +132,7 @@ function checkComponentDecorator(source: string, filePath: string, fileQuery: string): LogInfo[] | null { const log: LogInfo[] = []; const sourceFile: ts.SourceFile = ts.createSourceFile(filePath, source, - ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); if (sourceFile && sourceFile.statements && sourceFile.statements.length) { const result: DecoratorResult = { entryCount: 0, @@ -151,26 +146,17 @@ function checkComponentDecorator(source: string, filePath: string, if (ts.isEnumDeclaration(item) && item.name) { enumCollection.add(item.name.getText()); } - if (isStruct(item)) { - if (index + 1 < arr.length && ts.isExpressionStatement(arr[index + 1]) && - // @ts-ignore - arr[index + 1].expression && ts.isIdentifier(arr[index + 1].expression)) { - if (ts.isExportAssignment(item) && hasComponentDecorator(item)) { - checkDecorators(item, result, arr[index + 1] as ts.ExpressionStatement, log, sourceFile); - } else if (index > 0 && hasComponentDecorator(arr[index - 1])) { - checkDecorators(arr[index - 1] as ts.MissingDeclaration, result, - arr[index + 1] as ts.ExpressionStatement, log, sourceFile); + if (ts.isStructDeclaration(item)) { + if (item.name && ts.isIdentifier(item.name)) { + if (item.decorators && item.decorators.length) { + checkDecorators(item.decorators, result, item.name, log, sourceFile); } else { - // @ts-ignore - const pos: number = item.expression.getStart(); const message: string = `A struct should use decorator '@Component'.`; - addLog(LogType.WARN, message, pos, log, sourceFile); + addLog(LogType.WARN, message, item.getStart(), log, sourceFile); } } else { - // @ts-ignore - const pos: number = item.expression.getStart(); const message: string = `A struct must have a name.`; - addLog(LogType.ERROR, message, pos, log, sourceFile); + addLog(LogType.WARN, message, item.getStart(), log, sourceFile); } } if (ts.isMissingDeclaration(item)) { @@ -245,33 +231,17 @@ export function isCustomDialogClass(node: ts.Node): boolean { return false; } -function isStruct(node: ts.Node): boolean { - if ((ts.isExpressionStatement(node) || ts.isExportAssignment(node)) && - node.expression && ts.isIdentifier(node.expression) && node.expression.getText() === STRUCT) { - return true; - } - return false; -} - -function hasComponentDecorator(node: ts.Node): boolean { - if ((ts.isMissingDeclaration(node) || ts.isExportAssignment(node)) && - node.decorators && node.decorators.length) { - return true; - } - return false; -} - interface DecoratorResult { entryCount: number; previewCount: number; } -function checkDecorators(node: ts.MissingDeclaration | ts.ExportAssignment, result: DecoratorResult, - component: ts.ExpressionStatement, log: LogInfo[], sourceFile: ts.SourceFile): void { +function checkDecorators(decorators: ts.NodeArray, result: DecoratorResult, + component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile): void { let hasComponentDecorator: boolean = false; const componentName: string = component.getText(); - node.decorators.forEach((element) => { - const name: string = element.getText().replace(/\((.|\n)*\)/, '').trim(); + decorators.forEach((element) => { + const name: string = element.getText(); if (INNER_COMPONENT_DECORATORS.has(name)) { componentCollection.customComponents.add(componentName); switch (name) { @@ -316,14 +286,14 @@ function checkDecorators(node: ts.MissingDeclaration | ts.ExportAssignment, resu function checkUISyntax(filePath: string, allComponentNames: Set, content: string, log: LogInfo[]): void { const sourceFile: ts.SourceFile = ts.createSourceFile(filePath, content, - ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); visitAllNode(sourceFile, sourceFile, allComponentNames, log); } function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponentNames: Set, log: LogInfo[]) { checkAllNode(node, allComponentNames, sourceFileNode, log); - if (ts.isClassDeclaration(node) && node.name && ts.isIdentifier(node.name)) { + if (ts.isStructDeclaration(node) && node.name && ts.isIdentifier(node.name)) { collectComponentProps(node); } if (ts.isMethodDeclaration(node) && hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) { @@ -335,7 +305,7 @@ function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponent function checkAllNode(node: ts.Node, allComponentNames: Set, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { if (ts.isExpressionStatement(node) && node.expression && ts.isIdentifier(node.expression) && - allComponentNames.has(node.expression.escapedText.toString())) { + allComponentNames.has(node.expression.escapedText.toString())) { const pos: number = node.expression.getStart(); const message: string = `The component name must be followed by parentheses, like '${node.expression.getText()}()'.`; @@ -629,7 +599,7 @@ function isNonspecificChildIfStatement(node: ts.Node, specificChildSet: Set, +function traversalComponentProps(node: ts.StructDeclaration, propertys: Set, regulars: Set, states: Set, links: Set, props: Set, storageProps: Set, storageLinks: Set, provides: Set, consumes: Set, objectLinks: Set): void { @@ -736,13 +706,6 @@ export interface ReplaceResult { export function sourceReplace(source: string, sourcePath: string): ReplaceResult { let content: string = source; const log: LogInfo[] = []; - // replace struct->class - content = content.replace( - new RegExp('\\b' + STRUCT + '\\b.+\\{', 'g'), item => { - item = item.replace(new RegExp('\\b' + STRUCT + '\\b', 'g'), `${CLASS} `); - return `${item} constructor(${COMPONENT_CONSTRUCTOR_ID}?, ${COMPONENT_CONSTRUCTOR_PARENT}?, ${COMPONENT_CONSTRUCTOR_PARAMS}?) {}`; - }); - content = preprocessExtend(content, sourcePath, log); // process @system. content = processSystemApi(content); diff --git a/compiler/test/test.js b/compiler/test/test.js index 0a6506aa049f1c30b938579f647b273fe18e35e8..99f5e2417daef7c30dfdfd472f52f1b23821834a 100644 --- a/compiler/test/test.js +++ b/compiler/test/test.js @@ -38,11 +38,14 @@ function expectActual(name, filePath) { process.env.compiler = BUILD_ON; const afterProcess = sourceReplace(source); validateUISyntax(source, afterProcess.content, `${name}.ts`); + const compilerOptions = ts.readConfigFile( + path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions; + Object.assign(compilerOptions, { + "sourceMap": false, + }); const result = ts.transpileModule(afterProcess.content, { - compilerOptions: { - "target": ts.ScriptTarget.ES2017 - }, - fileName: `${name}.ts`, + compilerOptions: compilerOptions, + fileName: `${name}.ets`, transformers: { before: [processUISyntax(null, true)] } }); componentInfo.id = 0; diff --git a/compiler/test/ut/component/customComponent.ts b/compiler/test/ut/component/customComponent.ts index bdf19913bc88f4cef950109df91c2663342ab961..90046eecbbac1875dc08e169985d66c90d853aca 100644 --- a/compiler/test/ut/component/customComponent.ts +++ b/compiler/test/ut/component/customComponent.ts @@ -25,8 +25,7 @@ struct Parent { Row() { Child({ stateProperty: this.regularToState, propProperty: this.stateToProp, - regularProperty: this.regularToRegular, - linkProperty: this.$stateToLink + regularProperty: this.regularToRegular, linkProperty: this.__stateToLink }) } } diff --git a/compiler/test/ut/struct/struct_02.ts b/compiler/test/ut/struct/struct_02.ts index 175a1085d27a9c70852b6fe7277399ad305e9d3f..2ddd86ac96fd2fe8fa4ede4ebbcca03c7cd51e8d 100644 --- a/compiler/test/ut/struct/struct_02.ts +++ b/compiler/test/ut/struct/struct_02.ts @@ -20,9 +20,17 @@ struct MyComponent { }` exports.expectResult = -`class MyComponent { - constructor(compilerAssignedUniqueChildId, parent, params) { } - build() { +`class MyComponent extends View { + constructor(compilerAssignedUniqueChildId, parent, params) { + super(compilerAssignedUniqueChildId, parent); + this.updateWithValueParams(params); + } + updateWithValueParams(params) { + } + aboutToBeDeleted() { + SubscriberManager.Get().delete(this.id()); + } + render() { } } ` diff --git a/compiler/tsconfig.json b/compiler/tsconfig.json index 822f29fa5e395235e355b680fbc867dba842c092..86df754ce5d64203f9e2e20d4c5de0a9903c59c4 100644 --- a/compiler/tsconfig.json +++ b/compiler/tsconfig.json @@ -1,6 +1,547 @@ { "compileOnSave": false, "compilerOptions": { + "ets": { + "render": { + "method": ["build", "pageTransition"], + "decorator": "Builder" + }, + "components": [ + "AbilityComponent", + "ActionSheet", + "AlphabetIndexer", + "Animator", + "Badge", + "Blank", + "Button", + "Calendar", + "Camera", + "Canvas", + "Checkbox", + "CheckboxGroup", + "Circle", + "ColorPicker", + "ColorPickerDialog", + "Column", + "ColumnSplit", + "Counter", + "DataPanel", + "DatePicker", + "Divider", + "Ellipse", + "Flex", + "FormComponent", + "FrictionMotion", + "Gauge", + "GeometryView", + "Grid", + "GridItem", + "GridContainer", + "Hyperlink", + "Image", + "ImageAnimator", + "Line", + "List", + "ListItem", + "LoadingProgress", + "Marquee", + "Menu", + "Navigation", + "Navigator", + "Option", + "PageTransitionEnter", + "PageTransitionExit", + "Panel", + "Path", + "PatternLock", + "Piece", + "PluginComponent", + "Polygon", + "Polyline", + "Progress", + "QRCode", + "Radio", + "Rating", + "Rect", + "Refresh", + "Row", + "RowSplit", + "Scroll", + "ScrollBar", + "ScrollMotion", + "Search", + "Section", + "Select", + "Shape", + "Sheet", + "SideBarContainer", + "Slider", + "Span", + "SpringMotion", + "SpringProp", + "Stack", + "Stepper", + "StepperItem", + "Swiper", + "TabContent", + "Tabs", + "Text", + "TextPicker", + "TextClock", + "TextArea", + "TextInput", + "TextTimer", + "Toggle", + "Video", + "Web", + "XComponent" + ], + "extend": { + "decorator": "Extend", + "components": [ + { + "name": "AbilityComponent", + "type": "AbilityComponentAttribute", + "instance": "AbilityComponentInstance" + }, + { + "name": "ActionSheet", + "type": "ActionSheetAttribute", + "instance": "ActionSheetInstance" + }, + { + "name": "AlphabetIndexer", + "type": "AlphabetIndexerAttribute", + "instance": "AlphabetIndexerInstance" + }, + { + "name": "Animator", + "type": "AnimatorAttribute", + "instance": "AnimatorInstance" + }, + { + "name": "Badge", + "type": "BadgeAttribute", + "instance": "BadgeInstance" + }, + { + "name": "Blank", + "type": "BlankAttribute", + "instance": "BlankInstance" + }, + { + "name": "Button", + "type": "ButtonAttribute", + "instance": "ButtonInstance" + }, + { + "name": "Calendar", + "type": "CalendarAttribute", + "instance": "CalendarInstance" + }, + { + "name": "Camera", + "type": "CameraAttribute", + "instance": "CameraInstance" + }, + { + "name": "Canvas", + "type": "CanvasAttribute", + "instance": "CanvasInstance" + }, + { + "name": "Checkbox", + "type": "CheckboxAttribute", + "instance": "CheckboxInstance" + }, + { + "name": "CheckboxGroup", + "type": "CheckboxGroupAttribute", + "instance": "CheckboxGroupInstance" + }, + { + "name": "Circle", + "type": "CircleAttribute", + "instance": "CircleInstance" + }, + { + "name": "ColorPicker", + "type": "ColorPickerAttribute", + "instance": "ColorPickerInstance" + }, + { + "name": "ColorPickerDialog", + "type": "ColorPickerDialogAttribute", + "instance": "ColorPickerDialogInstance" + }, + { + "name": "Column", + "type": "ColumnAttribute", + "instance": "ColumnInstance" + }, + { + "name": "ColumnSplit", + "type": "ColumnSplitAttribute", + "instance": "ColumnSplitInstance" + }, + { + "name": "Counter", + "type": "CounterAttribute", + "instance": "CounterInstance" + }, + { + "name": "DataPanel", + "type": "DataPanelAttribute", + "instance": "DataPanelInstance" + }, + { + "name": "DatePicker", + "type": "DatePickerAttribute", + "instance": "DatePickerInstance" + }, + { + "name": "Divider", + "type": "DividerAttribute", + "instance": "DividerInstance" + }, + { + "name": "Ellipse", + "type": "EllipseAttribute", + "instance": "EllipseInstance" + }, + { + "name": "Flex", + "type": "FlexAttribute", + "instance": "FlexInstance" + }, + { + "name": "FormComponent", + "type": "FormComponentAttribute", + "instance": "FormComponentInstance" + }, + { + "name": "FrictionMotion", + "type": "FrictionMotionAttribute", + "instance": "FrictionMotionInstance" + }, + { + "name": "Gauge", + "type": "GaugeAttribute", + "instance": "GaugeInstance" + }, + { + "name": "GeometryView", + "type": "GeometryViewAttribute", + "instance": "GeometryViewInstance" + }, + { + "name": "Grid", + "type": "GridAttribute", + "instance": "GridInstance" + }, + { + "name": "GridItem", + "type": "GridItemAttribute", + "instance": "GridItemInstance" + }, + { + "name": "GridContainer", + "type": "GridContainerAttribute", + "instance": "GridContainerInstance" + }, + { + "name": "Hyperlink", + "type": "HyperlinkAttribute", + "instance": "HyperlinkInstance" + }, + { + "name": "Image", + "type": "ImageAttribute", + "instance": "ImageInstance" + }, + { + "name": "ImageAnimator", + "type": "ImageAnimatorAttribute", + "instance": "ImageAnimatorInstance" + }, + { + "name": "Line", + "type": "LineAttribute", + "instance": "LineInstance" + }, + { + "name": "List", + "type": "ListAttribute", + "instance": "ListInstance" + }, + { + "name": "ListItem", + "type": "ListItemAttribute", + "instance": "ListItemInstance" + }, + { + "name": "LoadingProgress", + "type": "LoadingProgressAttribute", + "instance": "LoadingProgressInstance" + }, + { + "name": "Marquee", + "type": "MarqueeAttribute", + "instance": "MarqueeInstance" + }, + { + "name": "Menu", + "type": "MenuAttribute", + "instance": "MenuInstance" + }, + { + "name": "Navigation", + "type": "NavigationAttribute", + "instance": "NavigationInstance" + }, + { + "name": "Navigator", + "type": "NavigatorAttribute", + "instance": "NavigatorInstance" + }, + { + "name": "Option", + "type": "OptionAttribute", + "instance": "OptionInstance" + }, + { + "name": "PageTransitionEnter", + "type": "PageTransitionEnterAttribute", + "instance": "PageTransitionEnterInstance" + }, + { + "name": "PageTransitionExit", + "type": "PageTransitionExitAttribute", + "instance": "PageTransitionExitInstance" + }, + { + "name": "Panel", + "type": "PanelAttribute", + "instance": "PanelInstance" + }, + { + "name": "Path", + "type": "PathAttribute", + "instance": "PathInstance" + }, + { + "name": "PatternLock", + "type": "PatternLockAttribute", + "instance": "PatternLockInstance" + }, + { + "name": "Piece", + "type": "PieceAttribute", + "instance": "PieceInstance" + }, + { + "name": "PluginComponent", + "type": "PluginComponentAttribute", + "instance": "PluginComponentInstance" + }, + { + "name": "Polygon", + "type": "PolygonAttribute", + "instance": "PolygonInstance" + }, + { + "name": "Polyline", + "type": "PolylineAttribute", + "instance": "PolylineInstance" + }, + { + "name": "Progress", + "type": "ProgressAttribute", + "instance": "ProgressInstance" + }, + { + "name": "QRCode", + "type": "QRCodeAttribute", + "instance": "QRCodeInstance" + }, + { + "name": "Radio", + "type": "RadioAttribute", + "instance": "RadioInstance" + }, + { + "name": "Rating", + "type": "RatingAttribute", + "instance": "RatingInstance" + }, + { + "name": "Rect", + "type": "RectAttribute", + "instance": "RectInstance" + }, + { + "name": "Refresh", + "type": "RefreshAttribute", + "instance": "RefreshInstance" + }, + { + "name": "Row", + "type": "RowAttribute", + "instance": "RowInstance" + }, + { + "name": "RowSplit", + "type": "RowSplitAttribute", + "instance": "RowSplitInstance" + }, + { + "name": "Scroll", + "type": "ScrollAttribute", + "instance": "ScrollInstance" + }, + { + "name": "ScrollBar", + "type": "ScrollBarAttribute", + "instance": "ScrollBarInstance" + }, + { + "name": "ScrollMotion", + "type": "ScrollMotionAttribute", + "instance": "ScrollMotionInstance" + }, + { + "name": "Search", + "type": "SearchAttribute", + "instance": "SearchInstance" + }, + { + "name": "Section", + "type": "SectionAttribute", + "instance": "SectionInstance" + }, + { + "name": "Select", + "type": "SelectAttribute", + "instance": "SelectInstance" + }, + { + "name": "Shape", + "type": "ShapeAttribute", + "instance": "ShapeInstance" + }, + { + "name": "Sheet", + "type": "SheetAttribute", + "instance": "SheetInstance" + }, + { + "name": "SideBarContainer", + "type": "SideBarContainerAttribute", + "instance": "SideBarContainerInstance" + }, + { + "name": "Slider", + "type": "SliderAttribute", + "instance": "SliderInstance" + }, + { + "name": "Span", + "type": "SpanAttribute", + "instance": "SpanInstance" + }, + { + "name": "SpringMotion", + "type": "SpringMotionAttribute", + "instance": "SpringMotionInstance" + }, + { + "name": "SpringProp", + "type": "SpringPropAttribute", + "instance": "SpringPropInstance" + }, + { + "name": "Stack", + "type": "StackAttribute", + "instance": "StackInstance" + }, + { + "name": "Stepper", + "type": "StepperAttribute", + "instance": "StepperInstance" + }, + { + "name": "StepperItem", + "type": "StepperItemAttribute", + "instance": "StepperItemInstance" + }, + { + "name": "Swiper", + "type": "SwiperAttribute", + "instance": "SwiperInstance" + }, + { + "name": "TabContent", + "type": "TabContentAttribute", + "instance": "TabContentInstance" + }, + { + "name": "Tabs", + "type": "TabsAttribute", + "instance": "TabsInstance" + }, + { + "name": "Text", + "type": "TextAttribute", + "instance": "TextInstance" + }, + { + "name": "TextPicker", + "type": "TextPickerAttribute", + "instance": "TextPickerInstance" + }, + { + "name": "TextClock", + "type": "TextClockAttribute", + "instance": "TextClockInstance" + }, + { + "name": "TextArea", + "type": "TextAreaAttribute", + "instance": "TextAreaInstance" + }, + { + "name": "TextInput", + "type": "TextInputAttribute", + "instance": "TextInputInstance" + }, + { + "name": "TextTimer", + "type": "TextTimerAttribute", + "instance": "TextTimerInstance" + }, + { + "name": "Toggle", + "type": "ToggleAttribute", + "instance": "ToggleInstance" + }, + { + "name": "Video", + "type": "VideoAttribute", + "instance": "VideoInstance" + }, + { + "name": "Web", + "type": "WebAttribute", + "instance": "WebInstance" + }, + { + "name": "XComponent", + "type": "XComponentAttribute", + "instance": "XComponentInstance" + }, + ] + }, + }, "allowJs": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true, @@ -11,6 +552,7 @@ "experimentalDecorators": true, "moduleResolution": "node", "resolveJsonModule": true, + "skipLibCheck": true, "sourceMap": true, "module": "commonjs", "target": "es2017", diff --git a/compiler/webpack.config.js b/compiler/webpack.config.js index dacd227894d093a3b5a5aa88ef9cb5d69c98dd8b..8cf334f77867abd3fe5642b5f23b252267a39d5c 100644 --- a/compiler/webpack.config.js +++ b/compiler/webpack.config.js @@ -40,6 +40,9 @@ function initConfig(config) { const projectPath = path.resolve(projectConfig.projectPath); Object.assign(config, { entry: projectConfig.entryObj, + cache: { + type: "filesystem" + }, watch: watchMode, watchOptions: { aggregateTimeout: 10, @@ -67,8 +70,8 @@ function initConfig(config) { { loader: 'ts-loader', options: { - appendTsSuffixTo: [/\.ets$/], onlyCompileBundledFiles: true, + transpileOnly: true, configFile: path.resolve(__dirname, 'tsconfig.json'), getCustomTransformers(program) { return {