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 JSON5 = require('json5'); 1761847f8eSopenharmony_ciconst path = require('path'); 1861847f8eSopenharmony_ciconst fs = require('fs'); 1961847f8eSopenharmony_ci 2061847f8eSopenharmony_ciclass Project { 2161847f8eSopenharmony_ci constructor(projectPath, nonProject) { 2261847f8eSopenharmony_ci this.projectPath = projectPath; 2361847f8eSopenharmony_ci this.nonProject = nonProject; 2461847f8eSopenharmony_ci this.logTag = 'Project'; 2561847f8eSopenharmony_ci } 2661847f8eSopenharmony_ci 2761847f8eSopenharmony_ci getPath() { 2861847f8eSopenharmony_ci return this.projectPath; 2961847f8eSopenharmony_ci } 3061847f8eSopenharmony_ci 3161847f8eSopenharmony_ci getProfile() { 3261847f8eSopenharmony_ci if (!this.profile) { 3361847f8eSopenharmony_ci const buildProfilePath = path.resolve(this.projectPath, 'build-profile.json5'); 3461847f8eSopenharmony_ci if (!fs.existsSync(buildProfilePath)) { 3561847f8eSopenharmony_ci Logger.error(this.logTag, 'build-profile.json5 can\'t be found, is it an openharmony project?'); 3661847f8eSopenharmony_ci return this.profile; 3761847f8eSopenharmony_ci } 3861847f8eSopenharmony_ci const profileContent = fs.readFileSync(buildProfilePath, 'utf-8'); 3961847f8eSopenharmony_ci try { 4061847f8eSopenharmony_ci this.profile = JSON5.parse(profileContent); 4161847f8eSopenharmony_ci } catch (ex) { 4261847f8eSopenharmony_ci Logger.error(this.logTag, `parse build-profile.json error: ${JSON.stringify(ex)}`); 4361847f8eSopenharmony_ci } 4461847f8eSopenharmony_ci } 4561847f8eSopenharmony_ci return this.profile; 4661847f8eSopenharmony_ci } 4761847f8eSopenharmony_ci 4861847f8eSopenharmony_ci getAppSdkVersion() { 4961847f8eSopenharmony_ci const profile = this.getProfile(); 5061847f8eSopenharmony_ci 5161847f8eSopenharmony_ci if (!profile) { 5261847f8eSopenharmony_ci return undefined; 5361847f8eSopenharmony_ci } 5461847f8eSopenharmony_ci 5561847f8eSopenharmony_ci if (!profile.app) { 5661847f8eSopenharmony_ci return undefined; 5761847f8eSopenharmony_ci } 5861847f8eSopenharmony_ci 5961847f8eSopenharmony_ci if (profile.app.compileSdkVersion) { 6061847f8eSopenharmony_ci return profile.app.compileSdkVersion; 6161847f8eSopenharmony_ci } 6261847f8eSopenharmony_ci 6361847f8eSopenharmony_ci if (profile.app.products) { 6461847f8eSopenharmony_ci const compileSdkVersion = profile.app.products[0].compileSdkVersion; 6561847f8eSopenharmony_ci if (typeof compileSdkVersion === 'number') { 6661847f8eSopenharmony_ci return compileSdkVersion; 6761847f8eSopenharmony_ci } 6861847f8eSopenharmony_ci const version = compileSdkVersion.match(/\((.+)\)/g)[0].replace(/\(|\)/g, ''); 6961847f8eSopenharmony_ci return version; 7061847f8eSopenharmony_ci } 7161847f8eSopenharmony_ci return undefined; 7261847f8eSopenharmony_ci } 7361847f8eSopenharmony_ci 7461847f8eSopenharmony_ci getAppSdkPath() { 7561847f8eSopenharmony_ci if (this.sdkPath) { 7661847f8eSopenharmony_ci return this.sdkPath; 7761847f8eSopenharmony_ci } 7861847f8eSopenharmony_ci const localPropertiesPath = path.resolve(this.projectPath, 'local.properties'); 7961847f8eSopenharmony_ci if (!fs.existsSync(localPropertiesPath)) { 8061847f8eSopenharmony_ci Logger.error(this.logTag, 'unable to get the sdk path of the project, specify it using the --sdk or --sdkRoot'); 8161847f8eSopenharmony_ci return this.sdkPath; 8261847f8eSopenharmony_ci } 8361847f8eSopenharmony_ci const properties = this.parseProperty(localPropertiesPath); 8461847f8eSopenharmony_ci this.sdkPath = properties.get('sdk.dir'); 8561847f8eSopenharmony_ci return this.sdkPath; 8661847f8eSopenharmony_ci } 8761847f8eSopenharmony_ci 8861847f8eSopenharmony_ci parseProperty(propertyFilePath) { 8961847f8eSopenharmony_ci const properties = fs.readFileSync(propertyFilePath, 'utf-8'); 9061847f8eSopenharmony_ci const lines = properties.split('\n'); 9161847f8eSopenharmony_ci const propertyRegExp = new RegExp(/(.*)=(.*)/); 9261847f8eSopenharmony_ci const map = new Map(); 9361847f8eSopenharmony_ci lines.forEach((line) => { 9461847f8eSopenharmony_ci if (line.startsWith('#')) { 9561847f8eSopenharmony_ci return; 9661847f8eSopenharmony_ci } 9761847f8eSopenharmony_ci const expArray = line.match(propertyRegExp); 9861847f8eSopenharmony_ci const MATCHED_RESULT_NUMBER = 3; 9961847f8eSopenharmony_ci const KEY_INDEX = 1; 10061847f8eSopenharmony_ci const VALUE_INDEX = 2; 10161847f8eSopenharmony_ci if (expArray && expArray.length === MATCHED_RESULT_NUMBER) { 10261847f8eSopenharmony_ci map.set(expArray[KEY_INDEX].trim(), expArray[VALUE_INDEX].trim()); 10361847f8eSopenharmony_ci } 10461847f8eSopenharmony_ci }); 10561847f8eSopenharmony_ci return map; 10661847f8eSopenharmony_ci } 10761847f8eSopenharmony_ci 10861847f8eSopenharmony_ci /** 10961847f8eSopenharmony_ci * 获取应用的源码列表 11061847f8eSopenharmony_ci * 11161847f8eSopenharmony_ci * @returns 11261847f8eSopenharmony_ci */ 11361847f8eSopenharmony_ci getAppSources(isIncludeTest) { 11461847f8eSopenharmony_ci if (this.nonProject) { 11561847f8eSopenharmony_ci return this.getNonProjectAppSources(); 11661847f8eSopenharmony_ci } 11761847f8eSopenharmony_ci const profile = this.getProfile(); 11861847f8eSopenharmony_ci if (!profile || !profile.modules || profile.modules.length === 0) { 11961847f8eSopenharmony_ci return new Set(); 12061847f8eSopenharmony_ci } 12161847f8eSopenharmony_ci const moduleSrcPaths = []; 12261847f8eSopenharmony_ci profile.modules.forEach((module) => { 12361847f8eSopenharmony_ci if (module.srcPath) { 12461847f8eSopenharmony_ci moduleSrcPaths.push(path.resolve(this.projectPath, module.srcPath)); 12561847f8eSopenharmony_ci } 12661847f8eSopenharmony_ci }); 12761847f8eSopenharmony_ci const appSources = []; 12861847f8eSopenharmony_ci moduleSrcPaths.forEach((moduleSrc) => { 12961847f8eSopenharmony_ci appSources.push(...this.getModuleSource(moduleSrc, isIncludeTest)); 13061847f8eSopenharmony_ci }); 13161847f8eSopenharmony_ci return new Set(appSources); 13261847f8eSopenharmony_ci } 13361847f8eSopenharmony_ci 13461847f8eSopenharmony_ci getNonProjectAppSources() { 13561847f8eSopenharmony_ci Logger.info(this.logTag, 'find source files in non-project'); 13661847f8eSopenharmony_ci const appSources = []; 13761847f8eSopenharmony_ci this.listSourceFiles(this.projectPath, appSources); 13861847f8eSopenharmony_ci return new Set(appSources); 13961847f8eSopenharmony_ci } 14061847f8eSopenharmony_ci 14161847f8eSopenharmony_ci getModuleSource(modulePath, isIncludeTest) { 14261847f8eSopenharmony_ci const sourceSets = ['src/main/ets']; 14361847f8eSopenharmony_ci if (isIncludeTest) { 14461847f8eSopenharmony_ci sourceSets.push(...['src/ohosTest/ets']); 14561847f8eSopenharmony_ci } 14661847f8eSopenharmony_ci const sources = []; 14761847f8eSopenharmony_ci sourceSets.forEach((sourcePath) => { 14861847f8eSopenharmony_ci const srcPath = path.resolve(modulePath, sourcePath); 14961847f8eSopenharmony_ci this.listSourceFiles(srcPath, sources); 15061847f8eSopenharmony_ci }); 15161847f8eSopenharmony_ci if (sources.length === 0) { 15261847f8eSopenharmony_ci Logger.info(this.logTag, `can't find source file in ${this.projectPath}`); 15361847f8eSopenharmony_ci } 15461847f8eSopenharmony_ci return sources; 15561847f8eSopenharmony_ci } 15661847f8eSopenharmony_ci 15761847f8eSopenharmony_ci listSourceFiles(srcPath, dest) { 15861847f8eSopenharmony_ci if (fs.existsSync(srcPath)) { 15961847f8eSopenharmony_ci Logger.info(this.logTag, `find source code in ${srcPath}`); 16061847f8eSopenharmony_ci FileSystem.listFiles(srcPath, (filePath) => { 16161847f8eSopenharmony_ci const fileName = path.basename(filePath); 16261847f8eSopenharmony_ci return fileName.endsWith('.ts') || fileName.endsWith('.ets'); 16361847f8eSopenharmony_ci }, dest); 16461847f8eSopenharmony_ci } 16561847f8eSopenharmony_ci } 16661847f8eSopenharmony_ci} 16761847f8eSopenharmony_ci 16861847f8eSopenharmony_ciclass Sdk { 16961847f8eSopenharmony_ci 17061847f8eSopenharmony_ci /** 17161847f8eSopenharmony_ci * 17261847f8eSopenharmony_ci * @param {Project} project 应用工程对象 17361847f8eSopenharmony_ci * @param {string} sdkEtsPath 指向sdk中ets目录的路径 17461847f8eSopenharmony_ci * @param {string} sdkRoot sdk根目录 17561847f8eSopenharmony_ci */ 17661847f8eSopenharmony_ci constructor(project, sdkEtsPath, sdkRoot) { 17761847f8eSopenharmony_ci this.project = project; 17861847f8eSopenharmony_ci this.sdkEtsPath = sdkEtsPath; 17961847f8eSopenharmony_ci this.sdkRoot = sdkRoot; 18061847f8eSopenharmony_ci } 18161847f8eSopenharmony_ci 18261847f8eSopenharmony_ci getPath() { 18361847f8eSopenharmony_ci if (this.sdkEtsPath) { 18461847f8eSopenharmony_ci return this.sdkEtsPath; 18561847f8eSopenharmony_ci } 18661847f8eSopenharmony_ci if (this.sdkApiRoot) { 18761847f8eSopenharmony_ci return this.sdkApiRoot; 18861847f8eSopenharmony_ci } 18961847f8eSopenharmony_ci const sdkVersion = this.project.getAppSdkVersion(); 19061847f8eSopenharmony_ci const sdkDir = this.sdkRoot || this.project.getAppSdkPath(); 19161847f8eSopenharmony_ci if (sdkVersion && sdkDir) { 19261847f8eSopenharmony_ci this.sdkApiRoot = path.resolve(sdkDir, `${sdkVersion}`, 'ets'); 19361847f8eSopenharmony_ci } 19461847f8eSopenharmony_ci return this.sdkApiRoot; 19561847f8eSopenharmony_ci } 19661847f8eSopenharmony_ci 19761847f8eSopenharmony_ci /** 19861847f8eSopenharmony_ci * 获取SDK的d.ts文件列表 19961847f8eSopenharmony_ci * 20061847f8eSopenharmony_ci * @param {string} sdkRoot 20161847f8eSopenharmony_ci * @returns 20261847f8eSopenharmony_ci */ 20361847f8eSopenharmony_ci getApiLibs() { 20461847f8eSopenharmony_ci if (this.apiLibs) { 20561847f8eSopenharmony_ci return this.apiLibs; 20661847f8eSopenharmony_ci } 20761847f8eSopenharmony_ci this.apiLibs = []; 20861847f8eSopenharmony_ci this.listDtsFiles('api', this.apiLibs); 20961847f8eSopenharmony_ci return this.apiLibs; 21061847f8eSopenharmony_ci } 21161847f8eSopenharmony_ci 21261847f8eSopenharmony_ci getComponentLibs() { 21361847f8eSopenharmony_ci if (this.componentLibs) { 21461847f8eSopenharmony_ci return this.componentLibs; 21561847f8eSopenharmony_ci } 21661847f8eSopenharmony_ci this.componentLibs = []; 21761847f8eSopenharmony_ci this.listDtsFiles('component', this.componentLibs); 21861847f8eSopenharmony_ci return this.componentLibs; 21961847f8eSopenharmony_ci } 22061847f8eSopenharmony_ci 22161847f8eSopenharmony_ci getESLibs(libPath) { 22261847f8eSopenharmony_ci if (!process.env.bundleMode) { 22361847f8eSopenharmony_ci return []; 22461847f8eSopenharmony_ci } 22561847f8eSopenharmony_ci Logger.info('Sdk', `find ES libs in ${libPath}`); 22661847f8eSopenharmony_ci if (this.esLibs) { 22761847f8eSopenharmony_ci return this.esLibs; 22861847f8eSopenharmony_ci } 22961847f8eSopenharmony_ci this.esLibs = []; 23061847f8eSopenharmony_ci FileSystem.listFiles(libPath, (filePath) => path.basename(filePath).endsWith('.d.ts'), this.esLibs); 23161847f8eSopenharmony_ci FileSystem.listFiles(libPath, (filePath) => path.basename(filePath).endsWith('.d.ets'), this.esLibs); 23261847f8eSopenharmony_ci return this.esLibs; 23361847f8eSopenharmony_ci } 23461847f8eSopenharmony_ci 23561847f8eSopenharmony_ci listDtsFiles(dir, dest) { 23661847f8eSopenharmony_ci const sdkRoot = this.getPath(); 23761847f8eSopenharmony_ci if (!sdkRoot) { 23861847f8eSopenharmony_ci return; 23961847f8eSopenharmony_ci } 24061847f8eSopenharmony_ci const subDir = path.resolve(sdkRoot, dir); 24161847f8eSopenharmony_ci FileSystem.listFiles(subDir, (filePath) => path.basename(filePath).endsWith('.d.ts'), dest); 24261847f8eSopenharmony_ci FileSystem.listFiles(subDir, (filePath) => path.basename(filePath).endsWith('.d.ets'), dest); 24361847f8eSopenharmony_ci } 24461847f8eSopenharmony_ci} 24561847f8eSopenharmony_ci 24661847f8eSopenharmony_ciclass FileSystem { 24761847f8eSopenharmony_ci static listFiles(dir, filter, dest) { 24861847f8eSopenharmony_ci const files = fs.readdirSync(dir); 24961847f8eSopenharmony_ci files.forEach((element) => { 25061847f8eSopenharmony_ci const filePath = path.join(dir, element); 25161847f8eSopenharmony_ci const status = fs.statSync(filePath); 25261847f8eSopenharmony_ci if (status.isDirectory()) { 25361847f8eSopenharmony_ci this.listFiles(filePath, filter, dest); 25461847f8eSopenharmony_ci } else if (filter(filePath)) { 25561847f8eSopenharmony_ci dest.push(this.convertToPosixPath(filePath)); 25661847f8eSopenharmony_ci } 25761847f8eSopenharmony_ci }); 25861847f8eSopenharmony_ci } 25961847f8eSopenharmony_ci 26061847f8eSopenharmony_ci static convertToPosixPath(filePath) { 26161847f8eSopenharmony_ci return filePath.split(path.sep).join(path.posix.sep); 26261847f8eSopenharmony_ci } 26361847f8eSopenharmony_ci 26461847f8eSopenharmony_ci static isInDirectory(parentDir, subPath) { 26561847f8eSopenharmony_ci const relative = path.relative(parentDir, subPath); 26661847f8eSopenharmony_ci return (relative === '' || !relative.startsWith('..')) && !path.isAbsolute(relative); 26761847f8eSopenharmony_ci } 26861847f8eSopenharmony_ci 26961847f8eSopenharmony_ci static listAllAppDirs(parentDir) { 27061847f8eSopenharmony_ci const dest = []; 27161847f8eSopenharmony_ci this.listDirectory(parentDir, dest, (filePath) => { 27261847f8eSopenharmony_ci const buildProfilePath = path.resolve(filePath, 'build-profile.json5'); 27361847f8eSopenharmony_ci if (!fs.existsSync(buildProfilePath)) { 27461847f8eSopenharmony_ci return false; 27561847f8eSopenharmony_ci } 27661847f8eSopenharmony_ci const profileContent = fs.readFileSync(buildProfilePath, 'utf-8'); 27761847f8eSopenharmony_ci const profile = JSON5.parse(profileContent); 27861847f8eSopenharmony_ci return profile.app && profile.modules; 27961847f8eSopenharmony_ci }, (filePath) => { 28061847f8eSopenharmony_ci return filePath; 28161847f8eSopenharmony_ci }); 28261847f8eSopenharmony_ci return dest; 28361847f8eSopenharmony_ci } 28461847f8eSopenharmony_ci 28561847f8eSopenharmony_ci static listDirectory(dir, dest, filter, visitChildren) { 28661847f8eSopenharmony_ci const files = fs.readdirSync(dir); 28761847f8eSopenharmony_ci files.forEach((element) => { 28861847f8eSopenharmony_ci const filePath = path.join(dir, element); 28961847f8eSopenharmony_ci const status = fs.statSync(filePath); 29061847f8eSopenharmony_ci if (status.isDirectory()) { 29161847f8eSopenharmony_ci if (filter(filePath)) { 29261847f8eSopenharmony_ci dest.push(filePath); 29361847f8eSopenharmony_ci } else if (visitChildren(filePath)) { 29461847f8eSopenharmony_ci this.listDirectory(filePath, dest, filter, visitChildren); 29561847f8eSopenharmony_ci } 29661847f8eSopenharmony_ci } 29761847f8eSopenharmony_ci }); 29861847f8eSopenharmony_ci } 29961847f8eSopenharmony_ci} 30061847f8eSopenharmony_ci 30161847f8eSopenharmony_ciclass Logger { 30261847f8eSopenharmony_ci static INFO = 0; 30361847f8eSopenharmony_ci static WARN = 1; 30461847f8eSopenharmony_ci static ERROR = 2; 30561847f8eSopenharmony_ci static logs = ''; 30661847f8eSopenharmony_ci static LEVEL_NAME = new Map([ 30761847f8eSopenharmony_ci [this.INFO, 'I'], 30861847f8eSopenharmony_ci [this.WARN, 'W'], 30961847f8eSopenharmony_ci [this.ERROR, 'E'] 31061847f8eSopenharmony_ci ]); 31161847f8eSopenharmony_ci 31261847f8eSopenharmony_ci static info(tag, message) { 31361847f8eSopenharmony_ci this.wrap(this.INFO, tag, message); 31461847f8eSopenharmony_ci } 31561847f8eSopenharmony_ci 31661847f8eSopenharmony_ci static warn(tag, message) { 31761847f8eSopenharmony_ci this.wrap(this.WARN, tag, message); 31861847f8eSopenharmony_ci } 31961847f8eSopenharmony_ci 32061847f8eSopenharmony_ci static error(tag, message) { 32161847f8eSopenharmony_ci this.wrap(this.ERROR, tag, message); 32261847f8eSopenharmony_ci } 32361847f8eSopenharmony_ci 32461847f8eSopenharmony_ci static wrap(level, tag, message) { 32561847f8eSopenharmony_ci const timeStamp = `${this.formatDate(Date.now(), 'Y-M-D H:m:s:x')}`; 32661847f8eSopenharmony_ci const logMessage = `${timeStamp} ${this.getLevelName(level)} [${tag}] ${message}`; 32761847f8eSopenharmony_ci console.log(logMessage); 32861847f8eSopenharmony_ci } 32961847f8eSopenharmony_ci 33061847f8eSopenharmony_ci static flush(output) { 33161847f8eSopenharmony_ci const logName = path.resolve(output, `${this.formatDate(Date.now(), 'Y-M-D-Hmsx')}.log`); 33261847f8eSopenharmony_ci fs.writeFileSync(logName, this.logs); 33361847f8eSopenharmony_ci this.info('Logger', `log is in ${logName}`); 33461847f8eSopenharmony_ci } 33561847f8eSopenharmony_ci 33661847f8eSopenharmony_ci static getLevelName(level) { 33761847f8eSopenharmony_ci if (this.LEVEL_NAME.has(level)) { 33861847f8eSopenharmony_ci return this.LEVEL_NAME.get(level); 33961847f8eSopenharmony_ci } 34061847f8eSopenharmony_ci return this.LEVEL_NAME.get(this.INFO); 34161847f8eSopenharmony_ci } 34261847f8eSopenharmony_ci 34361847f8eSopenharmony_ci static formatDate(time, format) { 34461847f8eSopenharmony_ci const date = new Date(time); 34561847f8eSopenharmony_ci const year = date.getFullYear(); 34661847f8eSopenharmony_ci const month = date.getMonth() + 1; 34761847f8eSopenharmony_ci const day = date.getDate(); 34861847f8eSopenharmony_ci const hour = date.getHours(); 34961847f8eSopenharmony_ci const min = date.getMinutes(); 35061847f8eSopenharmony_ci const sec = date.getSeconds(); 35161847f8eSopenharmony_ci const mis = date.getMilliseconds(); 35261847f8eSopenharmony_ci let dateStr = format.replace('Y', `${year}`); 35361847f8eSopenharmony_ci dateStr = dateStr.replace('M', `${month}`); 35461847f8eSopenharmony_ci dateStr = dateStr.replace('D', `${day}`); 35561847f8eSopenharmony_ci dateStr = dateStr.replace('H', `${hour}`); 35661847f8eSopenharmony_ci dateStr = dateStr.replace('m', `${min}`); 35761847f8eSopenharmony_ci dateStr = dateStr.replace('s', `${sec}`); 35861847f8eSopenharmony_ci dateStr = dateStr.replace('x', `${mis}`); 35961847f8eSopenharmony_ci return dateStr; 36061847f8eSopenharmony_ci } 36161847f8eSopenharmony_ci} 36261847f8eSopenharmony_ci 36361847f8eSopenharmony_ciexports.Project = Project; 36461847f8eSopenharmony_ciexports.Sdk = Sdk; 36561847f8eSopenharmony_ciexports.FileSystem = FileSystem; 36661847f8eSopenharmony_ciexports.Logger = Logger;