From 2671e50237ae3bbf9fb3f8a4f4729e528350351b Mon Sep 17 00:00:00 2001 From: houhaoyu Date: Tue, 6 Sep 2022 22:01:38 +0800 Subject: [PATCH] houhaoyu@huawei.com MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 性能劣化+增量检查 Signed-off-by: houhaoyu Change-Id: I82693d8fc868514819a6cfe5e17036a6d91da69c --- compiler/src/compile_info.ts | 116 ++++++++++++++++++++++++++++++-- compiler/src/ets_checker.ts | 125 +++++++++++++++++++++++------------ compiler/webpack.config.js | 2 +- 3 files changed, 193 insertions(+), 50 deletions(-) diff --git a/compiler/src/compile_info.ts b/compiler/src/compile_info.ts index b9c116e..646e9b0 100644 --- a/compiler/src/compile_info.ts +++ b/compiler/src/compile_info.ts @@ -33,7 +33,6 @@ import { moduleCollection, useOSFiles } from './validate_ui_syntax'; -import { projectConfig } from '../main'; import { circularFile, mkDir, @@ -50,7 +49,10 @@ import { importModuleCollection, createWatchCompilerHost } from './ets_checker'; -import { globalProgram } from '../main'; +import { + globalProgram, + projectConfig +} from '../main'; configure({ appenders: { 'ETS': {type: 'stderr', layout: {type: 'messagePassThrough'}}}, @@ -69,6 +71,21 @@ interface Info { }; } +export interface CacheFileName { + mtimeMs: number, + children: string[], + parent: string[], + error: boolean +} + +interface NeedUpdateFlag { + flag: boolean; +} + +export let cache: Cache = {}; +export const shouldResolvedFiles: Set = new Set() +type Cache = Record; + export class ResultStates { private mStats: Stats; private mErrorCount: number = 0; @@ -171,7 +188,16 @@ export class ResultStates { createWatchCompilerHost(rootFileNames, this.printDiagnostic.bind(this), this.delayPrintLogCount.bind(this))); } else { - const languageService: ts.LanguageService = createLanguageService(rootFileNames); + let languageService: ts.LanguageService = null; + let cacheFile: string = null; + if (projectConfig.xtsMode) { + languageService = createLanguageService(rootFileNames); + } else { + cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache'); + cache = fs.existsSync(cacheFile) ? JSON.parse(fs.readFileSync(cacheFile).toString()) : {}; + const filterFiles: string[] = filterInput(rootFileNames); + languageService = createLanguageService(filterFiles); + } globalProgram.program = languageService.getProgram(); const allDiagnostics: ts.Diagnostic[] = globalProgram.program .getSyntacticDiagnostics() @@ -180,6 +206,9 @@ export class ResultStates { allDiagnostics.forEach((diagnostic: ts.Diagnostic) => { this.printDiagnostic(diagnostic); }); + if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) { + fs.writeFileSync(cacheFile, JSON.stringify(cache, null, 2)); + } } }); @@ -200,15 +229,26 @@ export class ResultStates { this.printResult(); }); - compiler.hooks.watchRun.tap('Listening State', (compiler: Compiler) => { - if (compiler.modifiedFiles) { - const isTsAndEtsFile: boolean = [...compiler.modifiedFiles].some((item: string) => { + compiler.hooks.watchRun.tap('Listening State', (comp: Compiler) => { + comp.modifiedFiles = comp.modifiedFiles || []; + comp.removedFiles = comp.removedFiles || []; + const watchModifiedFiles: string[] = [...comp.modifiedFiles]; + const watchRemovedFiles: string[] = [...comp.removedFiles]; + if (comp.modifiedFiles) { + const isTsAndEtsFile: boolean = [...comp.modifiedFiles].some((item: string) => { return /.(ts|ets)$/.test(item); }); if (!isTsAndEtsFile) { process.env.watchTs = 'end'; } } + const changedFiles: string[] = [...watchModifiedFiles, ...watchRemovedFiles]; + if (changedFiles.length) { + shouldResolvedFiles.clear(); + } + changedFiles.forEach((file) => { + this.judgeFileShouldResolved(file, shouldResolvedFiles) + }) }); if (!projectConfig.isPreview) { @@ -221,6 +261,25 @@ export class ResultStates { } } + private judgeFileShouldResolved(file: string, shouldResolvedFiles: Set): void { + if (shouldResolvedFiles.has(file)) { + return; + } + shouldResolvedFiles.add(file); + if (cache && cache[file] && cache[file].parent) { + cache[file].parent.forEach((item)=>{ + this.judgeFileShouldResolved(item, shouldResolvedFiles); + }) + cache[file].parent = []; + } + if (cache && cache[file] && cache[file].children) { + cache[file].children.forEach((item)=>{ + this.judgeFileShouldResolved(item, shouldResolvedFiles); + }) + cache[file].children = []; + } + } + private generateCollectionFile() { if (projectConfig.aceSuperVisualPath && fs.existsSync(projectConfig.aceSuperVisualPath)) { appComponentCollection.clear(); @@ -246,6 +305,9 @@ export class ResultStates { private printDiagnostic(diagnostic: ts.Diagnostic): void { const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); if (this.validateError(message)) { + if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) { + updateErrorFileCache(diagnostic); + } this.mErrorCount += 1; if (diagnostic.file) { const { line, character }: ts.LineAndCharacter = @@ -415,3 +477,45 @@ export class ResultStates { return message; } } + +function updateErrorFileCache(diagnostic: ts.Diagnostic): void { + if (diagnostic.file && cache[path.resolve(diagnostic.file.fileName)]) { + cache[path.resolve(diagnostic.file.fileName)].error = true; + } +} + +function filterInput(rootFileNames: string[]): string[] { + return rootFileNames.filter((file: string) => { + const needUpdate: NeedUpdateFlag = { flag: false }; + const alreadyCheckedFiles: Set = new Set(); + checkNeedUpdateFiles(path.resolve(file), needUpdate, alreadyCheckedFiles); + return needUpdate.flag; + }); +} + +function checkNeedUpdateFiles(file: string, needUpdate: NeedUpdateFlag, alreadyCheckedFiles: Set): void { + if (alreadyCheckedFiles.has(file)) { + return; + } else { + alreadyCheckedFiles.add(file); + } + + if (needUpdate.flag) { + return; + } + + const value: CacheFileName = cache[file]; + const mtimeMs: number = fs.statSync(file).mtimeMs; + if (value) { + if (value.error || value.mtimeMs !== mtimeMs) { + needUpdate.flag = true; + return; + } + for (let i = 0; i < value.children.length; ++i) { + checkNeedUpdateFiles(value.children[i], needUpdate, alreadyCheckedFiles); + } + } else { + cache[file] = { mtimeMs, children: [], parent: [], error: false }; + needUpdate.flag = true; + } +} diff --git a/compiler/src/ets_checker.ts b/compiler/src/ets_checker.ts index e0c42b3..a3f7af5 100644 --- a/compiler/src/ets_checker.ts +++ b/compiler/src/ets_checker.ts @@ -38,7 +38,12 @@ import { import { JS_BIND_COMPONENTS } from './component_map'; import { getName } from './process_component_build'; import { INNER_COMPONENT_NAMES } from './component_map'; -import { props } from './compile_info'; +import { + props, + CacheFileName, + cache, + shouldResolvedFiles +} from './compile_info'; function readDeaclareFiles(): string[] { const declarationsFileNames: string[] = []; @@ -112,54 +117,88 @@ export function createLanguageService(rootFileNames: string[]): ts.LanguageServi return ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); } +const resolvedModulesCache: Map = new Map(); + function 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)/i.test(moduleName.trim())) { - const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts'); - if (systemModules.includes(moduleName + '.d.ts') && ts.sys.fileExists(modulePath)) { - resolvedModules.push(getResolveModule(modulePath, '.d.ts')); + if (![...shouldResolvedFiles].length || shouldResolvedFiles.has(path.resolve(containingFile))) { + 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)/i.test(moduleName.trim())) { + const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts'); + if (systemModules.includes(moduleName + '.d.ts') && ts.sys.fileExists(modulePath)) { + resolvedModules.push(getResolveModule(modulePath, '.d.ts')); + } else { + resolvedModules.push(null); + } + } 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 { + resolvedModules.push(null); + } + } else if (/\.ts$/.test(moduleName)) { + const modulePath: string = path.resolve(path.dirname(containingFile), moduleName); + if (ts.sys.fileExists(modulePath)) { + resolvedModules.push(getResolveModule(modulePath, '.ts')); + } else { + resolvedModules.push(null); + } } else { - resolvedModules.push(null); - } - } 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 { - resolvedModules.push(null); - } - } else if (/\.ts$/.test(moduleName)) { - const modulePath: string = path.resolve(path.dirname(containingFile), moduleName); - if (ts.sys.fileExists(modulePath)) { - resolvedModules.push(getResolveModule(modulePath, '.ts')); - } else { - resolvedModules.push(null); - } - } else { - const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts'); - const suffix: string = /\.js$/.test(moduleName) ? '' : '.js'; - const jsModulePath: string = path.resolve(__dirname, '../node_modules', moduleName + suffix); - if (ts.sys.fileExists(modulePath)) { - resolvedModules.push(getResolveModule(modulePath, '.d.ts')); - } else if (ts.sys.fileExists(jsModulePath)) { - resolvedModules.push(getResolveModule(modulePath, '.js')); - } else { - resolvedModules.push(null); + const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts'); + const suffix: string = /\.js$/.test(moduleName) ? '' : '.js'; + const jsModulePath: string = path.resolve(__dirname, '../node_modules', moduleName + suffix); + if (ts.sys.fileExists(modulePath)) { + resolvedModules.push(getResolveModule(modulePath, '.d.ts')); + } else if (ts.sys.fileExists(jsModulePath)) { + resolvedModules.push(getResolveModule(modulePath, '.js')); + } else { + resolvedModules.push(null); + } } } + if (!projectConfig.xtsMode) { + createOrUpdateCache(resolvedModules, containingFile); + } + resolvedModulesCache[path.resolve(containingFile)] = resolvedModules; + return resolvedModules; } - return resolvedModules; + return resolvedModulesCache[path.resolve(containingFile)]; +} + +function createOrUpdateCache(resolvedModules: ts.ResolvedModuleFull[], containingFile: string): void { + const children: string[] = []; + const error: boolean = false; + resolvedModules.forEach(moduleObj => { + if (moduleObj && moduleObj.resolvedFileName && /(?