161847f8eSopenharmony_ci/* 261847f8eSopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd. 361847f8eSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 461847f8eSopenharmony_ci * you may not use this file except in compliance with the License. 561847f8eSopenharmony_ci * You may obtain a copy of the License at 661847f8eSopenharmony_ci * 761847f8eSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 861847f8eSopenharmony_ci * 961847f8eSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 1061847f8eSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 1161847f8eSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1261847f8eSopenharmony_ci * See the License for the specific language governing permissions and 1361847f8eSopenharmony_ci * limitations under the License. 1461847f8eSopenharmony_ci */ 1561847f8eSopenharmony_ci 1661847f8eSopenharmony_ciconst { Project, Sdk, FileSystem, Logger } = require('./utils'); 1761847f8eSopenharmony_ciconst { ApiWriter, ApiExcelWriter } = require('./api_writer'); 1861847f8eSopenharmony_ciconst { SystemApiRecognizer } = require('./api_recognizer'); 1961847f8eSopenharmony_ciconst { ReporterFormat } = require('./configs'); 2061847f8eSopenharmony_ciconst ts = require('typescript'); 2161847f8eSopenharmony_ciconst fs = require('fs'); 2261847f8eSopenharmony_ciconst path = require('path'); 2361847f8eSopenharmony_ci 2461847f8eSopenharmony_ciclass ProgramFactory { 2561847f8eSopenharmony_ci setLibPath(libPath) { 2661847f8eSopenharmony_ci this.libPath = libPath; 2761847f8eSopenharmony_ci } 2861847f8eSopenharmony_ci 2961847f8eSopenharmony_ci getETSOptions(componentLibs) { 3061847f8eSopenharmony_ci const tsconfig = require('../tsconfig.json'); 3161847f8eSopenharmony_ci const etsConfig = tsconfig.compilerOptions.ets; 3261847f8eSopenharmony_ci etsConfig.libs = [...componentLibs]; 3361847f8eSopenharmony_ci return etsConfig; 3461847f8eSopenharmony_ci } 3561847f8eSopenharmony_ci 3661847f8eSopenharmony_ci createProgram(rootNames, apiLibs, componentLibs, esLibs) { 3761847f8eSopenharmony_ci const compilerOption = { 3861847f8eSopenharmony_ci target: ts.ScriptTarget.ES2017, 3961847f8eSopenharmony_ci ets: this.getETSOptions([]), 4061847f8eSopenharmony_ci allowJs: false, 4161847f8eSopenharmony_ci lib: [...apiLibs, ...componentLibs, ...esLibs], 4261847f8eSopenharmony_ci module: ts.ModuleKind.CommonJS, 4361847f8eSopenharmony_ci }; 4461847f8eSopenharmony_ci this.compilerHost = this.createCompilerHost({ 4561847f8eSopenharmony_ci resolveModuleName: (moduleName) => { 4661847f8eSopenharmony_ci return this.resolveModuleName(moduleName, apiLibs); 4761847f8eSopenharmony_ci }, 4861847f8eSopenharmony_ci }, compilerOption); 4961847f8eSopenharmony_ci 5061847f8eSopenharmony_ci if (this.libPath && fs.existsSync(this.libPath)) { 5161847f8eSopenharmony_ci Logger.info('ProgramFactory', `set default lib location: ${this.libPath}`); 5261847f8eSopenharmony_ci this.compilerHost.getDefaultLibLocation = () => { 5361847f8eSopenharmony_ci return this.libPath; 5461847f8eSopenharmony_ci }; 5561847f8eSopenharmony_ci } 5661847f8eSopenharmony_ci return ts.createProgram({ 5761847f8eSopenharmony_ci rootNames: [...rootNames], 5861847f8eSopenharmony_ci options: compilerOption, 5961847f8eSopenharmony_ci host: this.compilerHost, 6061847f8eSopenharmony_ci }); 6161847f8eSopenharmony_ci } 6261847f8eSopenharmony_ci 6361847f8eSopenharmony_ci resolveModuleName(moduleName, libs) { 6461847f8eSopenharmony_ci if (moduleName.startsWith('@')) { 6561847f8eSopenharmony_ci const moduleFileName = `${moduleName}.d.ts`; 6661847f8eSopenharmony_ci const etsModuleFileName = `${moduleName}.d.ets`; 6761847f8eSopenharmony_ci for (const lib of libs) { 6861847f8eSopenharmony_ci if (lib.endsWith(moduleFileName) || lib.endsWith(etsModuleFileName)) { 6961847f8eSopenharmony_ci return lib; 7061847f8eSopenharmony_ci } 7161847f8eSopenharmony_ci } 7261847f8eSopenharmony_ci } 7361847f8eSopenharmony_ci return undefined; 7461847f8eSopenharmony_ci } 7561847f8eSopenharmony_ci 7661847f8eSopenharmony_ci createCompilerHost(moduleResolver, compilerOption) { 7761847f8eSopenharmony_ci const compilerHost = ts.createCompilerHost(compilerOption); 7861847f8eSopenharmony_ci compilerHost.resolveModuleNames = this.getResolveModuleNames(moduleResolver); 7961847f8eSopenharmony_ci return compilerHost; 8061847f8eSopenharmony_ci } 8161847f8eSopenharmony_ci 8261847f8eSopenharmony_ci getResolveModuleNames(moduleResolver) { 8361847f8eSopenharmony_ci return (moduleNames, containingFile, reusedNames, redirectedReference, options) => { 8461847f8eSopenharmony_ci const resolvedModules = []; 8561847f8eSopenharmony_ci for (const moduleName of moduleNames) { 8661847f8eSopenharmony_ci const moduleLookupLocaton = ts.resolveModuleName(moduleName, containingFile, options, 8761847f8eSopenharmony_ci this.moduleLookupResolutionHost()); 8861847f8eSopenharmony_ci if (moduleLookupLocaton.resolvedModule) { 8961847f8eSopenharmony_ci resolvedModules.push(moduleLookupLocaton.resolvedModule); 9061847f8eSopenharmony_ci } else { 9161847f8eSopenharmony_ci const modulePath = moduleResolver.resolveModuleName(moduleName); 9261847f8eSopenharmony_ci const resolved = modulePath && ts.sys.fileExists(modulePath) ? 9361847f8eSopenharmony_ci { resolvedFileName: modulePath } : 9461847f8eSopenharmony_ci undefined; 9561847f8eSopenharmony_ci resolvedModules.push(resolved); 9661847f8eSopenharmony_ci } 9761847f8eSopenharmony_ci } 9861847f8eSopenharmony_ci return resolvedModules; 9961847f8eSopenharmony_ci }; 10061847f8eSopenharmony_ci } 10161847f8eSopenharmony_ci 10261847f8eSopenharmony_ci moduleLookupResolutionHost() { 10361847f8eSopenharmony_ci return { 10461847f8eSopenharmony_ci fileExists: (fileName) => { 10561847f8eSopenharmony_ci return fileName && ts.sys.fileExists(fileName); 10661847f8eSopenharmony_ci }, 10761847f8eSopenharmony_ci readFile: (fileName) => { 10861847f8eSopenharmony_ci ts.sys.readFile(fileName); 10961847f8eSopenharmony_ci }, 11061847f8eSopenharmony_ci }; 11161847f8eSopenharmony_ci } 11261847f8eSopenharmony_ci} 11361847f8eSopenharmony_ci 11461847f8eSopenharmony_ciclass ApiCollector { 11561847f8eSopenharmony_ci constructor(argv) { 11661847f8eSopenharmony_ci const appProject = argv.app ? argv.app : (argv.dir ? argv.dir : undefined); 11761847f8eSopenharmony_ci if (!appProject) { 11861847f8eSopenharmony_ci throw 'app not found'; 11961847f8eSopenharmony_ci } 12061847f8eSopenharmony_ci this.project = new Project(appProject, argv.dir !== undefined); 12161847f8eSopenharmony_ci this.sdk = new Sdk(this.project, argv.sdk, argv.sdkRoot); 12261847f8eSopenharmony_ci this.formatFlag = ReporterFormat.getFlag(argv.format); 12361847f8eSopenharmony_ci this.outputPath = !argv.output ? appProject : argv.output; 12461847f8eSopenharmony_ci this.logTag = 'ApiCollector'; 12561847f8eSopenharmony_ci this.debugFlag = argv.debug; 12661847f8eSopenharmony_ci this.noRepeat = argv.noRepeat ? true : false; 12761847f8eSopenharmony_ci } 12861847f8eSopenharmony_ci 12961847f8eSopenharmony_ci setLibPath(libPath) { 13061847f8eSopenharmony_ci this.libPath = libPath; 13161847f8eSopenharmony_ci if (libPath && !fs.existsSync(this.libPath)) { 13261847f8eSopenharmony_ci Logger.warn(this.logTag, `${libPath} is not exist`); 13361847f8eSopenharmony_ci } else { 13461847f8eSopenharmony_ci Logger.info(this.logTag, `set lib path ${libPath}`); 13561847f8eSopenharmony_ci } 13661847f8eSopenharmony_ci return this; 13761847f8eSopenharmony_ci } 13861847f8eSopenharmony_ci 13961847f8eSopenharmony_ci setIncludeTest(isIncludeTest) { 14061847f8eSopenharmony_ci this.isIncludeTest = isIncludeTest; 14161847f8eSopenharmony_ci return this; 14261847f8eSopenharmony_ci } 14361847f8eSopenharmony_ci 14461847f8eSopenharmony_ci async start() { 14561847f8eSopenharmony_ci const sdkPath = this.sdk.getPath(); 14661847f8eSopenharmony_ci if (!sdkPath || !fs.existsSync(sdkPath)) { 14761847f8eSopenharmony_ci return; 14861847f8eSopenharmony_ci } 14961847f8eSopenharmony_ci const handleFilePath = path.join(sdkPath, '/api/@internal/full/global.d.ts'); 15061847f8eSopenharmony_ci const originalContent = fs.readFileSync(handleFilePath, 'utf-8'); 15161847f8eSopenharmony_ci let newContent = originalContent.replace(/\import|export/g, ''); 15261847f8eSopenharmony_ci fs.writeFileSync(handleFilePath, newContent); 15361847f8eSopenharmony_ci Logger.info(this.logTag, `scan app ${this.project.getPath()}`); 15461847f8eSopenharmony_ci Logger.info(this.logTag, `sdk is in ${sdkPath}`); 15561847f8eSopenharmony_ci const apiLibs = this.sdk.getApiLibs(); 15661847f8eSopenharmony_ci const componentLibs = this.sdk.getComponentLibs(); 15761847f8eSopenharmony_ci const eslibs = this.sdk.getESLibs(this.libPath); 15861847f8eSopenharmony_ci const appSourceSet = this.project.getAppSources(this.isIncludeTest); 15961847f8eSopenharmony_ci const programFactory = new ProgramFactory(); 16061847f8eSopenharmony_ci programFactory.setLibPath(this.libPath); 16161847f8eSopenharmony_ci let program = programFactory.createProgram(appSourceSet, apiLibs, componentLibs, eslibs); 16261847f8eSopenharmony_ci 16361847f8eSopenharmony_ci if (this.debugFlag) { 16461847f8eSopenharmony_ci program.getSourceFiles().forEach((sf) => { 16561847f8eSopenharmony_ci Logger.info('ApiCollector', sf.fileName); 16661847f8eSopenharmony_ci }); 16761847f8eSopenharmony_ci } 16861847f8eSopenharmony_ci 16961847f8eSopenharmony_ci let systemApiRecognizer = new SystemApiRecognizer(sdkPath); 17061847f8eSopenharmony_ci systemApiRecognizer.setTypeChecker(program.getTypeChecker()); 17161847f8eSopenharmony_ci Logger.info(this.logTag, `start scanning ${this.project.getPath()}`); 17261847f8eSopenharmony_ci appSourceSet.forEach((appCodeFilePath) => { 17361847f8eSopenharmony_ci const canonicalFileName = programFactory.compilerHost.getCanonicalFileName(appCodeFilePath); 17461847f8eSopenharmony_ci const sourceFile = program.getSourceFileByPath(canonicalFileName); 17561847f8eSopenharmony_ci if (sourceFile) { 17661847f8eSopenharmony_ci if (this.debugFlag) { 17761847f8eSopenharmony_ci Logger.info(this.logTag, `scan ${sourceFile.fileName}`); 17861847f8eSopenharmony_ci } 17961847f8eSopenharmony_ci systemApiRecognizer.visitNode(sourceFile, sourceFile.fileName); 18061847f8eSopenharmony_ci } else { 18161847f8eSopenharmony_ci Logger.warn(this.logTag, `no sourceFile ${appCodeFilePath}`); 18261847f8eSopenharmony_ci } 18361847f8eSopenharmony_ci }); 18461847f8eSopenharmony_ci Logger.info(this.logTag, `end scan ${this.project.getPath()}`); 18561847f8eSopenharmony_ci const apiWriter = this.getApiWriter(); 18661847f8eSopenharmony_ci apiWriter.add(systemApiRecognizer.getApiInformations()); 18761847f8eSopenharmony_ci // avoid oom 18861847f8eSopenharmony_ci systemApiRecognizer = undefined; 18961847f8eSopenharmony_ci program = undefined; 19061847f8eSopenharmony_ci await apiWriter.flush(); 19161847f8eSopenharmony_ci fs.writeFileSync(handleFilePath, originalContent); 19261847f8eSopenharmony_ci } 19361847f8eSopenharmony_ci 19461847f8eSopenharmony_ci getApiWriter() { 19561847f8eSopenharmony_ci if (!this.apiWriter) { 19661847f8eSopenharmony_ci this.apiWriter = new ApiWriter(this.outputPath, this.formatFlag, this.noRepeat); 19761847f8eSopenharmony_ci } 19861847f8eSopenharmony_ci return this.apiWriter; 19961847f8eSopenharmony_ci } 20061847f8eSopenharmony_ci 20161847f8eSopenharmony_ci setApiWriter(apiWriter) { 20261847f8eSopenharmony_ci this.apiWriter = apiWriter; 20361847f8eSopenharmony_ci } 20461847f8eSopenharmony_ci} 20561847f8eSopenharmony_ci 20661847f8eSopenharmony_ciclass MultiProjectApiCollector { 20761847f8eSopenharmony_ci constructor(argv) { 20861847f8eSopenharmony_ci this.argv = argv; 20961847f8eSopenharmony_ci } 21061847f8eSopenharmony_ci 21161847f8eSopenharmony_ci setLibPath(libPath) { 21261847f8eSopenharmony_ci this.libPath = libPath; 21361847f8eSopenharmony_ci if (libPath && !fs.existsSync(this.libPath)) { 21461847f8eSopenharmony_ci Logger.warn(this.logTag, `${libPath} is not exist`); 21561847f8eSopenharmony_ci } else { 21661847f8eSopenharmony_ci Logger.info(this.logTag, `set lib path ${libPath}`); 21761847f8eSopenharmony_ci } 21861847f8eSopenharmony_ci return this; 21961847f8eSopenharmony_ci } 22061847f8eSopenharmony_ci 22161847f8eSopenharmony_ci setIncludeTest(isIncludeTest) { 22261847f8eSopenharmony_ci this.isIncludeTest = isIncludeTest; 22361847f8eSopenharmony_ci return this; 22461847f8eSopenharmony_ci } 22561847f8eSopenharmony_ci 22661847f8eSopenharmony_ci async start() { 22761847f8eSopenharmony_ci const allApps = FileSystem.listAllAppDirs(this.argv.appDir); 22861847f8eSopenharmony_ci if (allApps.length === 0) { 22961847f8eSopenharmony_ci Logger.info('MultiProjectApiCollector', `project not found in ${this.argv.appDir}`); 23061847f8eSopenharmony_ci return; 23161847f8eSopenharmony_ci } 23261847f8eSopenharmony_ci const output = !this.argv.output ? this.argv.appDir : this.argv.output; 23361847f8eSopenharmony_ci const apiExcelWriter = new ApiExcelWriter(output); 23461847f8eSopenharmony_ci apiExcelWriter.close(); 23561847f8eSopenharmony_ci allApps.forEach((app) => { 23661847f8eSopenharmony_ci if (app) { 23761847f8eSopenharmony_ci this.argv.app = app; 23861847f8eSopenharmony_ci const apiCollector = new ApiCollector(this.argv); 23961847f8eSopenharmony_ci apiCollector.setApiWriter(apiExcelWriter); 24061847f8eSopenharmony_ci apiCollector.setLibPath(this.libPath).setIncludeTest(this.isIncludeTest).start(); 24161847f8eSopenharmony_ci } 24261847f8eSopenharmony_ci }); 24361847f8eSopenharmony_ci apiExcelWriter.open(); 24461847f8eSopenharmony_ci await apiExcelWriter.flush(); 24561847f8eSopenharmony_ci } 24661847f8eSopenharmony_ci} 24761847f8eSopenharmony_ci 24861847f8eSopenharmony_ciexports.ApiCollector = ApiCollector; 24961847f8eSopenharmony_ciexports.MultiProjectApiCollector = MultiProjectApiCollector;