1/* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15const path = require('path'); 16const fs = require('fs'); 17const ts = require('typescript'); 18 19let sourceFile = null; 20let lastNoteStr = ''; 21let lastNodeName = ''; 22 23function collectDeclaration(url) { 24 try { 25 const utPath = path.resolve(__dirname, url); 26 const utFiles = []; 27 readFile(utPath, utFiles); 28 tsTransform(utFiles, deleteSystemApi); 29 } catch (error) { 30 console.error("DELETE_SYSTEM_PLUGIN ERROR: ", error) 31 } 32} 33 34function tsTransform(utFiles, callback) { 35 utFiles.forEach((url) => { 36 if (/\.json/.test(url) || /index\-full\.d\.ts/.test(url) || /common\.d\.ts/.test(url)) { 37 const content = fs.readFileSync(url, 'utf-8'); 38 writeFile(url, content); 39 } else if (/\.d\.ts/.test(url)) { 40 const content = fs.readFileSync(url, 'utf-8'); 41 const fileName = path.basename(url).replace(/.d.ts/g, '.ts'); 42 ts.transpileModule(content, { 43 compilerOptions: { 44 "target": ts.ScriptTarget.ES2017 45 }, 46 fileName: fileName, 47 transformers: { before: [callback(url)] } 48 }); 49 } 50 }); 51} 52 53function readFile(dir, utFiles) { 54 try { 55 const files = fs.readdirSync(dir); 56 files.forEach((element) => { 57 const filePath = path.join(dir, element); 58 const status = fs.statSync(filePath); 59 if (status.isDirectory()) { 60 readFile(filePath, utFiles); 61 } else { 62 utFiles.push(filePath); 63 } 64 }) 65 } catch (e) { 66 console.log('ETS ERROR: ' + e); 67 } 68} 69 70function writeFile(url, data, option) { 71 if (fs.existsSync(outputPath)) { 72 fs.rmdirSync(outputPath, { recursive: true }); 73 } 74 const newFilePath = path.resolve(outputPath, path.relative(__dirname, url)); 75 fs.mkdir(path.relative(__dirname, path.dirname(newFilePath)), { recursive: true }, (err) => { 76 if (err) { 77 console.log(`ERROR FOR CREATE PATH ${err}`); 78 } else { 79 fs.writeFile(newFilePath, data, option, (err) => { 80 if (err) { 81 console.log(`ERROR FOR CREATE FILE ${err}`); 82 } 83 }) 84 } 85 }) 86} 87 88const globalModules = new Set(['GlobalResource', 'StateManagement', 'SpecialEvent']); 89 90function formatImportDeclaration(url) { 91 return (context) => { 92 const allIdentifierSet = new Set([]); 93 let copyrightMessage = ''; 94 let isCopyrightDeleted = false; 95 return (node) => { 96 sourceFile = node; 97 collectAllIdentifier(node); 98 node = formatAllNodes(node); 99 if (!isEmptyFile(node)) { 100 const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); 101 let result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); 102 if (isCopyrightDeleted) { 103 result = copyrightMessage + '\n' + result; 104 } 105 writeFile(url, result); 106 } 107 return node; 108 } 109 function collectAllIdentifier(node) { 110 if (ts.isSourceFile(node) && node.statements) { 111 node.statements.forEach(stat => { 112 if (!ts.isImportDeclaration(stat)) { 113 ts.visitEachChild(stat, collectAllNodes, context); 114 } 115 }); 116 } 117 } 118 function collectAllNodes(node) { 119 if (ts.isIdentifier(node)) { 120 allIdentifierSet.add(node.escapedText.toString()); 121 } 122 return ts.visitEachChild(node, collectAllNodes, context); 123 } 124 function formatAllNodes(node) { 125 if (ts.isSourceFile(node) && node.statements) { 126 const newStatements = []; 127 node.statements.forEach(statement => { 128 if (ts.isImportDeclaration(statement)) { 129 const clauseSet = new Set([]); 130 if (statement.importClause && ts.isImportClause(statement.importClause)) { 131 const clauseNode = statement.importClause; 132 if (!clauseNode.namedBindings && clauseNode.name && ts.isIdentifier(clauseNode.name)) { 133 clauseSet.add(clauseNode.name.escapedText.toString()); 134 } else if (clauseNode.namedBindings && clauseNode.namedBindings.name && 135 ts.isIdentifier(clauseNode.namedBindings.name)) { 136 clauseSet.add(clauseNode.namedBindings.name.escapedText.toString()); 137 } else if (clauseNode.namedBindings && clauseNode.namedBindings.elements) { 138 clauseNode.namedBindings.elements.forEach(ele => { 139 if (ele.name && ts.isIdentifier(ele.name)) { 140 clauseSet.add(ele.name.escapedText.toString()); 141 } 142 }); 143 } 144 } 145 const importSpecifier = statement.moduleSpecifier.getText().replace(/[\'\"]/g, ''); 146 const importSpecifierRealPath = path.resolve(url, `../${importSpecifier}.d.ts`); 147 if ((fs.existsSync(importSpecifierRealPath) || globalModules.has(importSpecifier)) && clauseSet.size > 0) { 148 const clasueCheckList = []; 149 let exsitClauseSet = new Set([]); 150 for (const clause of clauseSet) { 151 if (allIdentifierSet.has(clause)) { 152 exsitClauseSet.add(clause); 153 clasueCheckList.push('exist'); 154 } else { 155 clasueCheckList.push('non-exist'); 156 } 157 } 158 let hasExsitStatus = false; 159 let hasNonExsitStatus = false; 160 clasueCheckList.forEach(ele => { 161 if (ele === 'exist') { 162 hasExsitStatus = true; 163 } else { 164 hasNonExsitStatus = true; 165 } 166 }); 167 if (hasExsitStatus) { 168 if (hasNonExsitStatus) { 169 const newSpecifiers = []; 170 statement.importClause.namedBindings.elements.forEach(element => { 171 if (exsitClauseSet.has(element.name.escapedText.toString())) { 172 newSpecifiers.push(element); 173 } 174 }); 175 statement.importClause.namedBindings = ts.factory.updateNamedImports( 176 statement.importClause.namedBindings, newSpecifiers); 177 } 178 newStatements.push(statement); 179 } else if (hasCopyright(statement)) { 180 copyrightMessage = node.getFullText().replace(node.getText(), ''); 181 isCopyrightDeleted = true; 182 } 183 } else if (hasCopyright(statement)) { 184 copyrightMessage = node.getFullText().replace(node.getText(), ''); 185 isCopyrightDeleted = true; 186 } 187 } else { 188 newStatements.push(statement); 189 } 190 }); 191 node = ts.factory.updateSourceFile(node, newStatements); 192 } 193 return node; 194 } 195 function hasCopyright(node) { 196 return /http\:\/\/www\.apache\.org\/licenses\/LICENSE\-2\.0/g.test(node.getFullText().replace(node.getText(), '')); 197 } 198 } 199} 200 201function deleteSystemApi(url) { 202 return (context) => { 203 return (node) => { 204 sourceFile = node; 205 node = processSourceFile(node); 206 node = ts.visitEachChild(node, processAllNodes, context); 207 if (!isEmptyFile(node)) { 208 const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); 209 const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); 210 const fileName = path.basename(url).replace(/.d.ts/g, '.ts'); 211 ts.transpileModule(result, { 212 compilerOptions: { 213 "target": ts.ScriptTarget.ES2017 214 }, 215 fileName: fileName, 216 transformers: { before: [formatImportDeclaration(url)] } 217 }); 218 } 219 return node; 220 } 221 function processAllNodes(node) { 222 if (ts.isInterfaceDeclaration(node)) { 223 const newMembers = []; 224 node.members.forEach(member => { 225 if (!isSystemapi(member)) { 226 newMembers.push(member); 227 } 228 }); 229 node = ts.factory.updateInterfaceDeclaration(node, node.modifiers, node.name, 230 node.typeParameters, node.heritageClauses, newMembers); 231 } else if (ts.isClassDeclaration(node)) { 232 const newMembers = []; 233 node.members.forEach(member => { 234 if (!isSystemapi(member)) { 235 newMembers.push(member); 236 } 237 }); 238 node = ts.factory.updateClassDeclaration(node, node.modifiers, node.name, 239 node.typeParameters, node.heritageClauses, newMembers); 240 } else if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) { 241 const newStatements = []; 242 node.body.statements.forEach(statement => { 243 if (!isSystemapi(statement)) { 244 newStatements.push(statement); 245 } 246 }); 247 const newModuleBody = ts.factory.updateModuleBlock(node.body, newStatements); 248 node = ts.factory.updateModuleDeclaration(node, node.modifiers, node.name, newModuleBody); 249 } else if (ts.isEnumDeclaration(node)) { 250 const newMembers = []; 251 node.members.forEach(member => { 252 if (!isSystemapi(member)) { 253 newMembers.push(member); 254 } 255 }); 256 node = ts.factory.updateEnumDeclaration(node, node.modifiers, node.name, newMembers); 257 } 258 return ts.visitEachChild(node, processAllNodes, context); 259 } 260 function processSourceFile(node) { 261 const stateNamesSet = new Set([]); 262 const newStatements = []; 263 const newStatementsWithoutExport = []; 264 node.statements.forEach(statement => { 265 if (!isSystemapi(statement)) { 266 newStatements.push(statement); 267 } else if (ts.isModuleDeclaration(statement) && statement.name && ts.isIdentifier(statement.name)) { 268 stateNamesSet.add(statement.name.escapedText.toString()); 269 } 270 }); 271 newStatements.forEach(statement => { 272 if (!(ts.isExportAssignment(statement) && statement.expression && ts.isIdentifier(statement.expression) && 273 stateNamesSet.has(statement.expression.escapedText.toString()))) { 274 newStatementsWithoutExport.push(statement); 275 } 276 }); 277 return ts.factory.updateSourceFile(node, newStatementsWithoutExport, node.isDeclarationFile, 278 node.referencedFiles); 279 } 280 } 281} 282exports.deleteSystemApi = deleteSystemApi; 283 284function isSystemapi(node) { 285 const notesContent = node.getFullText().replace(node.getText(), "").replace(/[\s]/g, ""); 286 const notesArr = notesContent.split(/\/\*\*/); 287 const notesStr = notesArr[notesArr.length - 1]; 288 if (notesStr.length !== 0) { 289 if (ts.isFunctionDeclaration(node) || ts.isMethodSignature(node) || ts.isMethodDeclaration(node)) { 290 lastNodeName = node.name && node.name.escapedText ? node.name.escapedText.toString() : ""; 291 lastNoteStr = notesStr; 292 } 293 return /\@systemapi/g.test(notesStr); 294 } else { 295 if ((ts.isFunctionDeclaration(node) || ts.isMethodSignature(node) || ts.isMethodDeclaration(node)) && node.name && 296 node.name.escapedText.toString() !== "" && node.name.escapedText.toString() === lastNodeName) { 297 return /\@systemapi/g.test(lastNoteStr); 298 } else { 299 return false; 300 } 301 } 302} 303 304function isEmptyFile(node) { 305 if (ts.isSourceFile(node) && node.statements) { 306 for (let i = 0; i < node.statements.length; i++) { 307 const statement = node.statements[i]; 308 if (!ts.isImportDeclaration(statement)) { 309 return false 310 } 311 } 312 } 313 return true; 314} 315 316const apiSourcePath = '../api'; 317const outputPath = path.resolve(__dirname, 'output'); 318collectDeclaration(apiSourcePath);