UNPKG

@reyalp/debug-utils

Version:

ts transformers for debug

638 lines (637 loc) 35.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.transform = void 0; const path_1 = __importStar(require("path")); const resolve_1 = __importDefault(require("resolve")); const typescript_1 = __importDefault(require("typescript")); const logger_1 = require("./logger"); const package_name = '@reyalp/debug-utils'; const modules = { logger: { types: _enum('Logger', 'LogCallChain1', 'LogCallChain2'), rt: _enum('format', 'filter', 'inspect', 'write'), chained: _enum('msg', 'dump', 'trace'), methods: _enum('tag', 'error', 'warn', 'info', 'verbose', 'debug', 'silly'), levels: { error: logger_1.LogLevel.ERROR, warn: logger_1.LogLevel.WARN, info: logger_1.LogLevel.INFO, verbose: logger_1.LogLevel.VERBOSE, debug: logger_1.LogLevel.DEBUG, silly: logger_1.LogLevel.SILLY } }, introspect: _enum('introspect', 'quoteval') }; const transform = (program, opts, compiler) => (context) => { var _a, _b; const options = program.getCompilerOptions(); const common_dir = program.getCommonSourceDirectory(); const root_dirs = [...((_a = options.rootDirs) !== null && _a !== void 0 ? _a : []), (_b = options.rootDir) !== null && _b !== void 0 ? _b : '', common_dir.endsWith('/') ? common_dir : (common_dir + '/')].filter(Boolean); const printer = typescript_1.default.createPrinter({ omitTrailingSemicolon: true, removeComments: true, preserveSourceNewlines: false }); const checker = program.getTypeChecker(); const factory = context.factory; const host = typescript_1.default.createCompilerHost(program.getCompilerOptions()); return (source) => { var _a, _b, _c, _d; const source_dir = (0, path_1.parse)(source.fileName).dir; const resolved = typescript_1.default.resolveModuleName(package_name, source.fileName, program.getCompilerOptions(), host); const resolved_lib_index = (_b = (_a = resolved.resolvedModule) === null || _a === void 0 ? void 0 : _a.resolvedFileName) !== null && _b !== void 0 ? _b : resolve_1.default.sync(package_name, { basedir: source_dir }); const resolved_lib_dir = (0, path_1.parse)(resolved_lib_index).dir; if (opts === null || opts === void 0 ? void 0 : opts.progress) { console.log(`TSC: ${source.fileName.slice((_d = (_c = root_dirs.find(r => source.fileName.startsWith(r))) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0)}`); } void _debug_print_node; return typescript_1.default.visitNode(source, visit); // === SOURCE_LOCATION ============================================================================= function format_node_name(node, fallback = '<expr>') { return typescript_1.default.isPropertyAccessExpression(node) ? format_call_expr(node, fallback) : typescript_1.default.isCallExpression(node) ? format_prop_access_expr(node, fallback) : is_simple_node(node) ? printer.printNode(typescript_1.default.EmitHint.Expression, node, source) : (fallback + _debug_print_node(node)); } function format_prop_access_expr(node, fallback = '<expr>') { const fn = _strip_paren_down(node.expression); return typescript_1.default.isPropertyAccessExpression(fn) ? format_call_expr(fn, fallback) : format_node_name(fn, fallback); } function format_call_expr(node, fallback = '<expr>') { var _a; if (typescript_1.default.isCallExpression(node.expression)) { const lhs = (_a = format_prop_access_expr(node.expression, fallback)) !== null && _a !== void 0 ? _a : format_node_name(node, fallback); return lhs + '.' + format_node_name(node.name, fallback); } else { return format_node_name(node.expression, fallback) + '.' + format_node_name(node.name, fallback); } } function collect_source_location(node, ups) { const loc = typescript_1.default.getLineAndCharacterOfPosition(source, node.getStart(source)); const line = loc.line + 1; const column = loc.character + 1; const file = collect_file_info(source.fileName, root_dirs); function _mk_item(consume) { return (node) => { const { kind, name } = consume(node); const loc = typescript_1.default.getLineAndCharacterOfPosition(source, node.getStart(source)); const line = loc.line + 1; const column = loc.character + 1; return { kind, name, line, column }; }; } const spine = unwind(node, ups); const symbols = spine.map(_mk_item(root => { var _a, _b; if (!root) return { kind: 'toplevel', name: '<toplevel>' }; if (typescript_1.default.isSourceFile(root)) return { kind: 'toplevel', name: '<toplevel>' }; if (typescript_1.default.isArrowFunction(root)) return { kind: 'func', name: 'λ' }; if (typescript_1.default.isConstructorDeclaration(root)) return { kind: 'func', name: '(ctor)' }; if (typescript_1.default.isFunctionDeclaration(root)) return { kind: 'func', name: root.name ? format_node_name(root.name) : 'λ' }; if (typescript_1.default.isFunctionExpression(root)) return { kind: 'func', name: root.name ? format_node_name(root.name) : 'λ' }; if (typescript_1.default.isCallExpression(root)) return { kind: 'call', name: format_node_name(root.expression, '<call>') }; if (typescript_1.default.isArrayLiteralExpression(root)) return { kind: 'elem', name: '[]' }; if (typescript_1.default.isPropertyAssignment(root)) return { kind: 'prop', name: format_node_name(root.name, '<assign>') }; if (typescript_1.default.isPropertyDeclaration(root)) return { kind: 'prop', name: format_node_name(root.name, '<assign>') }; if (typescript_1.default.isClassDeclaration(root)) return { kind: 'class', name: (_b = (_a = root.name) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : '*Annoymous' }; if (typescript_1.default.isMethodDeclaration(root)) return { kind: 'func', name: format_node_name(root.name) }; if (typescript_1.default.isModuleDeclaration(root)) return { kind: 'namespace', name: format_node_name(root.name) }; if (typescript_1.default.isVariableDeclaration(root)) { if (typescript_1.default.isArrayBindingPattern(root.name)) return { kind: 'var', name: '[]' }; if (typescript_1.default.isObjectBindingPattern(root.name)) return { kind: 'var', name: '{}' }; if (typescript_1.default.isEmptyBindingPattern(root.name)) return { kind: 'var', name: '{}' }; return { kind: 'var', name: format_node_name(root.name) }; } if (typescript_1.default.isAssignmentExpression(root)) { if (typescript_1.default.isArrayLiteralExpression(root.left)) return { kind: 'var', name: '[]' }; return { kind: 'var', name: format_node_name(root.left, '<assign>') }; } return { kind: 'expr', name: format_node_name(root, `<${typescript_1.default.SyntaxKind[root.kind]}>`) }; })); const sympath = symbols.filter(c => c.kind !== 'toplevel').map(c => c.name).reverse().join('.') || '<toplevel>'; return { line, column, symbols, spine, file, sympath }; } function _flatten_source_location_expr_prop_chain(node) { const path = []; while (typescript_1.default.isPropertyAccessExpression(node)) { path.push(node.name.text); node = _strip_paren_down(node.expression); } // node should be ts.Identifier if (typescript_1.default.isIdentifier(node)) path.push(node.text); return path.reverse(); } function _is_source_location_expr(node) { if (_should_skip(node)) return false; const root = _get_prop_chain_root(node); if (!root) return false; const ty = checker.getTypeAtLocation(root); const tyname = checker.typeToString(ty); if (tyname !== 'SourceLocation') return false; const sym = checker.getSymbolAtLocation(root); // should be `source_location` if (!sym) return false; const decls = sym.getDeclarations(); if (!decls || decls.length === 0) return false; return _is_imported_from_this_package(decls[0]); } // path is something like `source_location` / `source_location.file.name` / `source_location.up`... function _create_source_location_expr(node, path) { const ups = path.slice(1).filter(t => t === 'up'); const trail = path.slice(ups.length + 1).join('.'); const info = collect_source_location(node, ups.length); const full = `${info.file.rel_path}:${info.line} ${info.sympath}`; const create_symbols = () => factory.createArrayLiteralExpression(info.symbols.map(s => factory.createObjectLiteralExpression([ factory.createPropertyAssignment('kind', factory.createStringLiteral(s.kind)), factory.createPropertyAssignment('name', factory.createStringLiteral(s.name)), factory.createPropertyAssignment('line', factory.createNumericLiteral(s.line)), factory.createPropertyAssignment('column', factory.createNumericLiteral(s.column)) ]))); const create_file = () => factory.createObjectLiteralExpression(Object.entries(info.file).map(([name, val]) => factory.createPropertyAssignment(name, factory.createStringLiteral(val)))); if (trail === 'symbols') return create_symbols(); if (trail === 'file') return create_file(); if (trail === 'sympath') return factory.createStringLiteral(info.sympath); if (trail === 'full') return factory.createStringLiteral(full); const { line, column } = info; for (const [name, val] of Object.entries({ line, column })) { if (trail === name) return factory.createNumericLiteral(val); } for (const [name, val] of Object.entries(info.file)) { if (trail === 'file.' + name) return factory.createStringLiteral(val); } return factory.createObjectLiteralExpression([ factory.createPropertyAssignment('line', factory.createNumericLiteral(line)), factory.createPropertyAssignment('column', factory.createNumericLiteral(column)), factory.createPropertyAssignment('sympath', factory.createStringLiteral(info.sympath)), factory.createPropertyAssignment('full', factory.createStringLiteral(full)), factory.createPropertyAssignment('symbols', create_symbols()), factory.createPropertyAssignment('file', create_file()) ]); } function _check_source_location_param_initializer(node) { const sig = checker.getResolvedSignature(node); if (!sig) return undefined; const decl = sig.getDeclaration(); if (!decl) return undefined; const parameters = decl.parameters; const index = parameters.findIndex(p => p.initializer && _is_source_location_expr(p.initializer)); if (index === -1) return undefined; return { index, initializer: parameters[index].initializer }; } function _try_transform_source_location_call_param(node) { if (!typescript_1.default.isCallExpression(node)) return undefined; const p = _check_source_location_param_initializer(node); if (!p) return undefined; const fn = visit(node.expression); const initializer = _strip_paren_down(p.initializer); const path = _flatten_source_location_expr_prop_chain(initializer); const sl = _create_source_location_expr(node, path); const args = _list(Math.max(node.arguments.length, p.index + 1)) .map(i => node.arguments[i] ? visit(node.arguments[i]) : factory.createVoidZero()) .map((expr, i) => i === p.index ? factory.createBinaryExpression(expr, typescript_1.default.SyntaxKind.QuestionQuestionToken, sl) : expr); return factory.updateCallExpression(node, fn, node.typeArguments, args); } function _try_transform_source_location_expr(node) { const parent = _strip_paren_up(node.parent); if (parent && typescript_1.default.isParameter(parent)) return undefined; if (!typescript_1.default.isExpression(node)) return undefined; if (!_is_source_location_expr(node)) return undefined; const path = _flatten_source_location_expr_prop_chain(node); // path[0] is always source_location (or it's alias) return _create_source_location_expr(node, path); } // ================================================================================================= // === INTROSPECTION =============================================================================== function _check_introspection_call(node) { var _a; const decl = (_a = checker.getResolvedSignature(node)) === null || _a === void 0 ? void 0 : _a.declaration; if (!decl) return undefined; if (!typescript_1.default.isFunctionDeclaration(decl)) return undefined; if (!decl.name) return undefined; if (!typescript_1.default.isIdentifier(decl.name)) return undefined; if (!modules.introspect[decl.name.text]) return undefined; const fn = node.expression; if (!typescript_1.default.isIdentifier(fn)) return undefined; const sym = checker.getSymbolAtLocation(fn); if (!sym) return undefined; const decls = sym.getDeclarations(); if (!decls || decls.length === 0) return undefined; if (!_is_imported_from_this_package(decls[0])) return undefined; return decl.name.text; } function _try_transform_introspection_call(node) { if (!typescript_1.default.isCallExpression(node)) return undefined; const fn = _check_introspection_call(node); if (!fn) return undefined; if (fn === modules.introspect.introspect) { const code = printer.printNode(typescript_1.default.EmitHint.Expression, node.arguments[0], source); const arg = visit(node.arguments[0]); return factory.createArrayLiteralExpression([factory.createStringLiteral(code), arg]); } else if (fn === modules.introspect.quoteval) { // quoteval: (expr, j, print) const code = printer.printNode(typescript_1.default.EmitHint.Expression, node.arguments[0], source); const args = node.arguments.map(visit); const j_fallback = factory.createStringLiteral(' = '); const j = args[1] ? factory.createBinaryExpression(args[1], typescript_1.default.SyntaxKind.QuestionQuestionToken, j_fallback) : j_fallback; const p_fallback = factory.createPropertyAccessExpression(node.expression, '_inspect'); const p_fn = args[2] ? factory.createBinaryExpression(args[2], typescript_1.default.SyntaxKind.QuestionQuestionToken, p_fallback) : p_fallback; const p = factory.createCallExpression(p_fn, undefined, [args[0]]); return factory.createTemplateExpression(factory.createTemplateHead(code), [ factory.createTemplateSpan(j, factory.createTemplateMiddle('')), factory.createTemplateSpan(p, factory.createTemplateTail('')) ]); } } function _validate_introspection_usage(node) { if (_should_skip(node)) return; if (!typescript_1.default.isIdentifier(node)) return; if (typescript_1.default.isImportSpecifier(node.parent)) return; const sym = checker.getSymbolAtLocation(node); if (!sym) return; const decls = sym.getDeclarations(); if (!decls || decls.length === 0) return; const decl = decls[0]; if (!typescript_1.default.isImportSpecifier(decl)) return; if (!_is_imported_from_this_package(decl)) return; const name = _import_names(decl); if (!modules.introspect[name]) return; if (typescript_1.default.isCallExpression(node)) return; compiler.addDiagnostic(_make_diag(node, 'invalid introspect usage', `only '${node.text}(...)' is allowed`)); } // ================================================================================================= // === LOGGER ====================================================================================== function _check_log_call(node) { var _a, _b; const fn = _strip_paren_down(node.expression); if (!typescript_1.default.isPropertyAccessExpression(fn)) return undefined; const lhs = fn.expression; const ty = checker.getTypeAtLocation(lhs); const tyname = checker.typeToString(ty); if (!modules.logger.types[tyname]) return undefined; if (!modules.logger.chained[fn.name.text] && !modules.logger.methods[fn.name.text]) return undefined; const decl = (_b = (_a = ty.symbol) === null || _a === void 0 ? void 0 : _a.getDeclarations()) === null || _b === void 0 ? void 0 : _b[0]; if (!decl) return undefined; let decl_source = decl.parent; while (decl_source && !typescript_1.default.isSourceFile(decl_source)) { decl_source = decl_source.parent; } return decl_source.fileName === (0, path_1.join)(resolved_lib_dir, 'logger.d.ts') ? tyname : undefined; } function _flatten_log_call_chain(node) { /* * CallExpression * / \ * PropertyAccessExpression (args) * / \ * CallExpression Identifier (method name) * ........ */ const chain = []; let start = node; while (typescript_1.default.isCallExpression(start)) { const ty = _check_log_call(start); if (!ty) break; const args = [...start.arguments]; const prop = _strip_paren_down(start.expression); if (!typescript_1.default.isPropertyAccessExpression(prop)) { // not possible... throw new Error(`call.parent is not PropertyAccessExpression!`); } chain.push({ name: prop.name.text, args }); start = prop.expression; } return { start, chain: chain.reverse() }; } function _log_format_arg(expr, rt) { if (typescript_1.default.isLiteralExpression(expr)) return expr; const node = visit(expr); const ty = checker.getTypeAtLocation(expr); if (ty.getFlags() & (typescript_1.default.TypeFlags.StringLike)) { return node; } if (ty.getFlags() & (typescript_1.default.TypeFlags.NumberLike | typescript_1.default.TypeFlags.BooleanLike | typescript_1.default.TypeFlags.EnumLike)) { return node; } return factory.createCallExpression(factory.createPropertyAccessExpression(rt, modules.logger.rt.inspect), undefined, [node, factory.createStringLiteral(checker.typeToString(ty)), rt]); } function _log_format_dump_arg(expr, rt) { return factory.createTemplateExpression(factory.createTemplateHead(`${printer.printNode(typescript_1.default.EmitHint.Expression, expr, source)} = `), [factory.createTemplateSpan(_log_format_arg(expr, rt), factory.createTemplateTail(''))]); } function _create_log_stmts(call, { chain, start } = _flatten_log_call_chain(call)) { var _a, _b; // <logger>.<method₁>(args₁).<method₂>(args₂)... // => // { // var logger = <logger>, rt = logger.rt, level = <level>, tags = <tags> // if (rt.filter(ll, tags, rt)) { // var file = <file>, line = <line>, name = <name> // rt.write(rt.format([...], ll, tags, file, line, name, rt), ll, tags, file, line, name, rt) // } // } const logger_var = typescript_1.default.isIdentifier(start) ? start : factory.createUniqueName('logger'); const rt_var = factory.createUniqueName('rt'); const tags_var = factory.createUniqueName('tags'); const level_var = factory.createUniqueName('log_level'); const extra_tags = chain.filter(c => c.name === modules.logger.methods.tag).flatMap(c => c.args).map((visit)); const log_level_call = chain.find(c => modules.logger.levels[c.name]); const log_level = log_level_call ? modules.logger.levels[log_level_call.name] : logger_1.LogLevel.INFO; const args = chain .filter(c => c.name !== modules.logger.methods.tag && c.name !== modules.logger.chained.trace) .flatMap(c => c.args.map(arg => c.name === modules.logger.chained.dump ? _log_format_dump_arg(arg, rt_var) : _log_format_arg(arg, rt_var))); const logger_tags = factory.createPropertyAccessExpression(logger_var, 'tags'); const tags_init = extra_tags.length === 0 ? logger_tags : factory.createArrayLiteralExpression([ factory.createSpreadElement(logger_tags), ...extra_tags ]); const decl_vars = factory.createVariableStatement(undefined, factory.createVariableDeclarationList([ ...typescript_1.default.isIdentifier(start) ? [] : [factory.createVariableDeclaration(logger_var, undefined, undefined, start)], factory.createVariableDeclaration(rt_var, undefined, undefined, factory.createPropertyAccessExpression(logger_var, 'rt')), factory.createVariableDeclaration(tags_var, undefined, undefined, tags_init), factory.createVariableDeclaration(level_var, undefined, undefined, factory.createNumericLiteral(log_level)) ])); const pred_expr = factory.createCallExpression(factory.createPropertyAccessExpression(rt_var, modules.logger.rt.filter), undefined, [level_var, tags_var, rt_var]); const info = collect_source_location(call, 1); const file_var = factory.createUniqueName('file'); const line_var = factory.createUniqueName('line'); const name_var = factory.createUniqueName('name'); const trace_cfg = (_b = (_a = chain.find(c => c.name === modules.logger.chained.trace)) === null || _a === void 0 ? void 0 : _a.args) === null || _b === void 0 ? void 0 : _b[0]; const trace_cfg_expr = trace_cfg && visit(trace_cfg); const wrap_trace_init = (expr) => !trace_cfg_expr ? expr : !typescript_1.default.isBooleanLiteral(trace_cfg_expr) ? factory.createConditionalExpression(trace_cfg_expr, undefined, expr, undefined, factory.createVoidZero()) : trace_cfg_expr.kind === typescript_1.default.SyntaxKind.TrueKeyword ? expr : factory.createVoidZero(); const write_decls = factory.createVariableStatement(undefined, factory.createVariableDeclarationList([ factory.createVariableDeclaration(file_var, undefined, undefined, wrap_trace_init(factory.createStringLiteral((0, path_1.join)(info.file.rel_dir, info.file.name)))), factory.createVariableDeclaration(line_var, undefined, undefined, wrap_trace_init(factory.createNumericLiteral(info.line))), factory.createVariableDeclaration(name_var, undefined, undefined, wrap_trace_init(factory.createStringLiteral(info.sympath))) ])); const trace_args = [level_var, tags_var, rt_var, file_var, line_var, name_var]; const format_call = factory.createCallExpression(factory.createPropertyAccessExpression(rt_var, modules.logger.rt.format), undefined, [factory.createArrayLiteralExpression(args), ...trace_args]); const write_call = factory.createCallExpression(factory.createPropertyAccessExpression(rt_var, modules.logger.rt.write), undefined, [format_call, ...trace_args]); const write_stmt = factory.createBlock([write_decls, factory.createExpressionStatement(write_call)]); const log_stmt = factory.createIfStatement(pred_expr, write_stmt); return [decl_vars, log_stmt]; } function _try_transform_log_call(node) { if (typescript_1.default.isExpressionStatement(node) && typescript_1.default.isCallExpression(node.expression) && _check_log_call(node.expression)) { return factory.createBlock(_create_log_stmts(node.expression)); } else if (typescript_1.default.isCallExpression(node) && _check_log_call(node)) { const call_chain = _flatten_log_call_chain(node); const logger_var = factory.createUniqueName('logger'); const decl = factory.createVariableDeclaration(logger_var, undefined, undefined, call_chain.start); return factory.createImmediatelyInvokedArrowFunction([ factory.createVariableStatement(undefined, [decl]), ..._create_log_stmts(node, { chain: call_chain.chain, start: logger_var }), factory.createReturnStatement(logger_var) ]); } return undefined; } // ================================================================================================= function _debug_print_node(node) { return `${printer.printNode(typescript_1.default.EmitHint.Unspecified, node, source)} :: ${typescript_1.default.SyntaxKind[node.kind]}`; } function _validate_import(node) { var _a; if (!typescript_1.default.isImportClause(node)) return; const nb = node.namedBindings; if (!node.name && (!nb || !typescript_1.default.isNamespaceImport(nb))) return; const specifier = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.moduleSpecifier; if (!typescript_1.default.isStringLiteral(specifier)) return; if (specifier.text !== package_name) return; compiler.addDiagnostic(_make_diag(node.parent, `module specifier`, `use import { ... } from '${package_name}' instead`)); } function validate_node(node) { _validate_import(node); _validate_introspection_usage(node); } function next(node) { return typescript_1.default.visitEachChild(node, visit, context); } function visit(node) { var _a, _b, _c, _d; validate_node(node); return ((_d = (_c = (_b = (_a = _try_transform_log_call(node)) !== null && _a !== void 0 ? _a : _try_transform_introspection_call(node)) !== null && _b !== void 0 ? _b : _try_transform_source_location_call_param(node)) !== null && _c !== void 0 ? _c : _try_transform_source_location_expr(node)) !== null && _d !== void 0 ? _d : next(node)); } }; }; exports.transform = transform; exports.default = exports.transform; const is_stop_root = (node) => typescript_1.default.isArrowFunction(node) || typescript_1.default.isFunctionExpression(node) || typescript_1.default.isFunctionDeclaration(node) || typescript_1.default.isModuleDeclaration(node) || typescript_1.default.isClassDeclaration(node) || typescript_1.default.isClassExpression(node) || typescript_1.default.isVariableDeclaration(node) || typescript_1.default.isMethodDeclaration(node) || typescript_1.default.isMethodDeclaration(node) || typescript_1.default.isPropertyDeclaration(node) || typescript_1.default.isPropertyAssignment(node) || typescript_1.default.isAssignmentExpression(node) || typescript_1.default.isConstructorDeclaration(node) || typescript_1.default.isParameter(node) || typescript_1.default.isCallExpression(node); function seek_stop_root(node) { while (node && node.parent && !is_stop_root(node)) { node = _strip_paren_up(node.parent); } return node; } function skip_stop_root(node) { while (node) { if (!typescript_1.default.isCallExpression(node)) return _strip_paren_up(node.parent); // node is CallExpression node = _strip_paren_up(node.parent); while (typescript_1.default.isPropertyAccessExpression(node)) { node = _strip_paren_up(node.parent); } } return node; } function unwind(node, ups) { const spine = []; while (node && node.parent) { spine.push(node = seek_stop_root(node)); node = skip_stop_root(node); } return spine.slice(ups); } function is_simple_node(node) { if (node.kind === typescript_1.default.SyntaxKind.ThisKeyword) return true; if (typescript_1.default.isIdentifier(node)) return true; if (typescript_1.default.isPrivateIdentifier(node)) return true; if (typescript_1.default.isNoSubstitutionTemplateLiteral(node)) return true; if (typescript_1.default.isStringLiteral(node)) return true; if (typescript_1.default.isNumericLiteral(node)) return true; if (typescript_1.default.isBigIntLiteral(node)) return true; if (typescript_1.default.isPropertyAccessExpression(node)) return is_simple_node(node.expression) && is_simple_node(node.name); if (typescript_1.default.isElementAccessExpression(node)) return is_simple_node(node.expression) && is_simple_node(node.argumentExpression); if (typescript_1.default.isCallExpression(node)) return is_simple_node(node.expression) && node.arguments.every(is_simple_node); if (typescript_1.default.isComputedPropertyName(node)) return is_simple_node(node.expression); return false; } function trim_leading_slash(s) { return (s !== '/' && s.startsWith('/')) ? s.slice(1) : s; } function collect_file_info(full_path, root_dirs) { var _a, _b; const p = path_1.default.parse(full_path); const rel = root_dirs.find(dir => full_path.startsWith(dir)); const full_dir = p.dir; const rel_path = trim_leading_slash(full_path.slice((_a = rel === null || rel === void 0 ? void 0 : rel.length) !== null && _a !== void 0 ? _a : 0)); const rel_dir = trim_leading_slash(full_dir.slice((_b = rel === null || rel === void 0 ? void 0 : rel.length) !== null && _b !== void 0 ? _b : 0)); const ext = p.ext; const name = p.name; const fullname = p.base; return { full_path, full_dir, rel_path, rel_dir, ext, name, fullname }; } function _get_prop_chain_root(node) { while (typescript_1.default.isPropertyAccessExpression(node)) { node = _strip_paren_down(node.expression); } return typescript_1.default.isIdentifier(node) ? node : undefined; } function _is_imported_from_this_package(decl) { if (!typescript_1.default.isImportSpecifier(decl)) return false; const import_decl = decl.parent.parent.parent; const specifier = import_decl.moduleSpecifier; if (!typescript_1.default.isStringLiteral(specifier)) return false; return specifier.text === package_name; } function _enum(...names) { return Object.fromEntries(names.map(k => [k, k])); } function _import_names(decl) { var _a; return ((_a = decl.propertyName) !== null && _a !== void 0 ? _a : decl.name).text; } function _make_diag(node, key, message, category = typescript_1.default.DiagnosticCategory.Error) { const code = `(${package_name})`; return typescript_1.default.createDiagnosticForNode(node, { code, key, category, message }); } function _list(length) { return Array.from({ length }).map((_, i) => i); } function _strip_paren_up(node) { while (node && typescript_1.default.isParenthesizedExpression(node)) { node = node.parent; } return node; } function _strip_paren_down(node) { while (typescript_1.default.isParenthesizedExpression(node)) { node = node.expression; } return node; } function _should_skip(node) { return !node.parent || typescript_1.default.isImportSpecifier(node.parent) || typescript_1.default.isJsxOpeningElement(node.parent) || typescript_1.default.isJsxClosingElement(node.parent) || typescript_1.default.isJsxSelfClosingElement(node.parent); }