1/* 2 * Copyright (c) 2024-2025 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 16import * as ts from 'ohos-typescript'; 17import * as fs from 'fs'; 18import Logger, { LOG_MODULE_TYPE } from './logger'; 19 20const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'json5parser'); 21 22export function fetchDependenciesFromFile(filePath: string): { 23 [k: string]: unknown; 24} { 25 if (!fs.existsSync(filePath)) { 26 return {}; 27 } 28 let configurationsText: string; 29 try { 30 configurationsText = fs.readFileSync(filePath, 'utf-8'); 31 } catch (error) { 32 logger.error(`Error reading file: ${error}`); 33 return {}; 34 } 35 const file = parseJsonText(configurationsText); 36 return file; 37} 38 39export function parseJsonText(text: string): { [k: string]: unknown } { 40 let file; 41 try { 42 file = ts.parseJsonText('', text); 43 } catch (error) { 44 logger.error(`Error parsing file: ${error}`); 45 return {}; 46 } 47 const rootObjectLiteralExpression = getRootObjectLiteral(file); 48 if (!rootObjectLiteralExpression) { 49 logger.error('The JSON5 file format is incorrect, rootObjectLiteralExpression is null.'); 50 return {}; 51 } 52 return parseObjectLiteralExpression(rootObjectLiteralExpression, file); 53} 54 55function getRootObjectLiteral(file: ts.JsonSourceFile): ts.ObjectLiteralExpression | undefined { 56 if (!file || !file.statements || !file.statements.length) { 57 logger.error('The JSON5 file format is incorrect, the root node statements is empty.'); 58 return undefined; 59 } 60 const expressionStatement = file.statements[0]; 61 if (expressionStatement.kind !== ts.SyntaxKind.ExpressionStatement) { 62 logger.error(`The JSON5 file format is incorrect, the first child node is not ExpressionStatement. kind: ${expressionStatement.kind}`); 63 return undefined; 64 } 65 const rootObjectLiteralExpression = (expressionStatement as ts.ExpressionStatement).expression; 66 if (!rootObjectLiteralExpression) { 67 logger.error('The JSON5 file format is incorrect, the first child node is empty.'); 68 return undefined; 69 } 70 71 if (rootObjectLiteralExpression.kind === ts.SyntaxKind.ObjectLiteralExpression) { 72 return rootObjectLiteralExpression as ts.ObjectLiteralExpression; 73 } 74 75 if (rootObjectLiteralExpression.kind === ts.SyntaxKind.ArrayLiteralExpression) { 76 const elements = (rootObjectLiteralExpression as ts.ArrayLiteralExpression).elements; 77 if (elements && elements.length && elements[0].kind === ts.SyntaxKind.ObjectLiteralExpression) { 78 return elements[0] as ts.ObjectLiteralExpression; 79 } 80 logger.error('The JSON5 file format is incorrect, the node ArrayLiteralExpression first element is not ObjectLiteralExpression.'); 81 } 82 logger.error('The JSON5 file format is incorrect.'); 83 return undefined; 84} 85 86function parsePropertyInitializer(node: ts.Expression, file: ts.JsonSourceFile): unknown { 87 if (node.kind === ts.SyntaxKind.StringLiteral) { 88 return (node as ts.StringLiteral).text; 89 } else if (node.kind === ts.SyntaxKind.NumericLiteral) { 90 return (node as ts.NumericLiteral).text; 91 } else if (node.kind === ts.SyntaxKind.PrefixUnaryExpression) { 92 return (node as ts.PrefixUnaryExpression).getText(file); 93 } else if (node.kind === ts.SyntaxKind.ArrayLiteralExpression) { 94 return parseArrayLiteral(node, file); 95 } else if (node.kind === ts.SyntaxKind.ObjectLiteralExpression) { 96 return parseObjectLiteralExpression(node as ts.ObjectLiteralExpression, file); 97 } else if (node.kind === ts.SyntaxKind.TrueKeyword) { 98 return true; 99 } else if (node.kind === ts.SyntaxKind.FalseKeyword) { 100 return false; 101 } 102 return undefined; 103} 104 105function parseArrayLiteral(node: ts.Expression, file: ts.JsonSourceFile): unknown[] { 106 const res: unknown[] = []; 107 (node as ts.ArrayLiteralExpression).elements.forEach(n => { 108 res.push(parsePropertyInitializer(n, file)); 109 }); 110 return res; 111} 112 113function parseObjectLiteralExpression(ObjectLiteralExpression: ts.ObjectLiteralExpression, file: ts.JsonSourceFile): { [k: string]: unknown } { 114 const res: { [k: string]: unknown } = {}; 115 ObjectLiteralExpression.properties.forEach(node => { 116 const propNode = node as ts.PropertyAssignment; 117 const key = (propNode.name as ts.Identifier).text; 118 const value = parsePropertyInitializer(propNode.initializer, file); 119 res[key] = value; 120 }); 121 return res; 122} 123