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