import { extname } from "pathe"; import { parse, parseExpression } from "@babel/parser"; //#region src/check.ts /** * Checks if the given node matches the specified type(s). * * @param node - The node to check. * @param types - The type(s) to match against. It can be a single type or an array of types. * @returns True if the node matches the specified type(s), false otherwise. */ function isTypeOf(node, types) { if (!node) return false; return [].concat(types).some((type) => { if (type === "Function") return isFunctionType(node); else if (type === "Literal") return isLiteralType(node); else if (type === "Expression") return isExpressionType(node); else return node.type === type; }); } /** * Checks if the given node is a CallExpression with the specified callee. * * @param node - The node to check. * @param test - The callee to compare against. It can be a string, an array of strings, or a function that takes a string and returns a boolean. * @returns True if the node is a CallExpression with the specified callee, false otherwise. */ function isCallOf(node, test) { return !!node && node.type === "CallExpression" && isIdentifierOf(node.callee, test); } /** * Checks if the given node is a TaggedTemplateExpression with the specified callee. * * @param node - The node to check. * @param test - The callee to compare against. It can be a string, an array of strings, or a function that takes a string and returns a boolean. * @returns True if the node is a TaggedTemplateExpression with the specified callee, false otherwise. */ function isTaggedFunctionCallOf(node, test) { return !!node && node.type === "TaggedTemplateExpression" && isIdentifierOf(node.tag, test); } /** * Checks if the given node is an Identifier with the specified name. * * @param node - The node to check. * @param test - The name to compare against. It can be a string or an array of strings. * @returns True if the node is an Identifier with the specified name, false otherwise. */ function isIdentifierOf(node, test) { return !!node && node.type === "Identifier" && match(node.name, test); } /** * Checks if the given node is a literal type. * * @param node - The node to check. * @returns True if the node is a literal type, false otherwise. */ function isLiteralType(node) { return !!node && node.type.endsWith("Literal"); } /** * Checks if the given node is a function type. * * @param node - The node to check. * @returns True if the node is a function type, false otherwise. */ function isFunctionType(node) { return !!node && !node.type.startsWith("TS") && /Function(?:Expression|Declaration)$|Method$/.test(node.type); } /** * Checks if the given node is a declaration type. * * @param node - The node to check. * @returns True if the node is a declaration type, false otherwise. */ function isDeclarationType(node) { if (!node) return false; switch (node.type) { case "FunctionDeclaration": case "VariableDeclaration": case "ClassDeclaration": case "ExportAllDeclaration": case "ExportDefaultDeclaration": case "ExportNamedDeclaration": case "ImportDeclaration": case "DeclareClass": case "DeclareFunction": case "DeclareInterface": case "DeclareModule": case "DeclareModuleExports": case "DeclareTypeAlias": case "DeclareOpaqueType": case "DeclareVariable": case "DeclareExportDeclaration": case "DeclareExportAllDeclaration": case "InterfaceDeclaration": case "OpaqueType": case "TypeAlias": case "EnumDeclaration": case "TSDeclareFunction": case "TSInterfaceDeclaration": case "TSTypeAliasDeclaration": case "TSEnumDeclaration": case "TSModuleDeclaration": return true; case "Placeholder": if (node.expectedNode === "Declaration") return true; } return false; } /** * Checks if the given node is an expression type. * * @param node - The node to check. * @returns True if the node is an expression type, false otherwise. */ function isExpressionType(node) { return !!node && (node.type.endsWith("Expression") || isLiteralType(node) || [ "Identifier", "MetaProperty", "Super", "Import", "JSXElement", "JSXFragment", "TopicReference", "PipelineBareFunction", "PipelinePrimaryTopicReference", "TSTypeAssertion" ].includes(node.type)); } function match(value, test) { if (Array.isArray(test)) return test.includes(value); if (typeof test === "function") return test(value); return value === test; } /* v8 ignore start */ /** * Checks if the input `node` is a reference to a bound variable. * * Copied from https://github.com/babel/babel/blob/main/packages/babel-types/src/validators/isReferenced.ts * * To avoid runtime dependency on `@babel/types` (which includes process references) * This file should not change very often in babel but we may need to keep it * up-to-date from time to time. * * @param node - The node to check. * @param parent - The parent node of the input `node`. * @param grandparent - The grandparent node of the input `node`. * @returns True if the input `node` is a reference to a bound variable, false otherwise. */ function isReferenced(node, parent, grandparent) { switch (parent.type) { case "MemberExpression": case "OptionalMemberExpression": if (parent.property === node) return !!parent.computed; return parent.object === node; case "JSXMemberExpression": return parent.object === node; case "VariableDeclarator": return parent.init === node; case "ArrowFunctionExpression": return parent.body === node; case "PrivateName": return false; case "ClassMethod": case "ClassPrivateMethod": case "ObjectMethod": if (parent.key === node) return !!parent.computed; return false; case "ObjectProperty": if (parent.key === node) return !!parent.computed; return !grandparent || grandparent.type !== "ObjectPattern"; case "ClassProperty": case "ClassAccessorProperty": if (parent.key === node) return !!parent.computed; return true; case "ClassPrivateProperty": return parent.key !== node; case "ClassDeclaration": case "ClassExpression": return parent.superClass === node; case "AssignmentExpression": return parent.right === node; case "AssignmentPattern": return parent.right === node; case "LabeledStatement": return false; case "CatchClause": return false; case "RestElement": return false; case "BreakStatement": case "ContinueStatement": return false; case "FunctionDeclaration": case "FunctionExpression": return false; case "ExportNamespaceSpecifier": case "ExportDefaultSpecifier": return false; case "ExportSpecifier": if (grandparent?.source) return false; return parent.local === node; case "ImportDefaultSpecifier": case "ImportNamespaceSpecifier": case "ImportSpecifier": return false; case "ImportAttribute": return false; case "JSXAttribute": return false; case "ObjectPattern": case "ArrayPattern": return false; case "MetaProperty": return false; case "ObjectTypeProperty": return parent.key !== node; case "TSEnumMember": return parent.id !== node; case "TSPropertySignature": if (parent.key === node) return !!parent.computed; return true; } return true; } /* v8 ignore end */ //#endregion //#region src/create.ts /** * Creates a string literal AST node. * * @param value - The value of the string literal. * @returns The string literal AST node. */ function createStringLiteral(value) { return { type: "StringLiteral", value, extra: { rawValue: value, raw: JSON.stringify(value) } }; } /** * Creates a TypeScript union type AST node. * * @param types - An array of TypeScript types. * @returns The TypeScript union type AST node. */ function createTSUnionType(types) { return { type: "TSUnionType", types }; } /** * Creates a TypeScript literal type AST node. * * @param literal - The literal value. * @returns The TypeScript literal type AST node. */ function createTSLiteralType(literal) { return { type: "TSLiteralType", literal }; } //#endregion //#region src/extract.ts /** * Extract identifiers of the given node. * @param node The node to extract. * @param identifiers The array to store the extracted identifiers. * @see https://github.com/vuejs/core/blob/1f6a1102aa09960f76a9af2872ef01e7da8538e3/packages/compiler-core/src/babelUtils.ts#L208 */ function extractIdentifiers(node, identifiers = []) { switch (node.type) { case "Identifier": identifiers.push(node); break; case "MemberExpression": { let object = node; while (object.type === "MemberExpression") object = object.object; identifiers.push(object); break; } case "ObjectPattern": for (const prop of node.properties) if (prop.type === "RestElement") extractIdentifiers(prop.argument, identifiers); else extractIdentifiers(prop.value, identifiers); break; case "ArrayPattern": node.elements.forEach((element) => { element && extractIdentifiers(element, identifiers); }); break; case "RestElement": extractIdentifiers(node.argument, identifiers); break; case "AssignmentPattern": extractIdentifiers(node.left, identifiers); break; } return identifiers; } //#endregion //#region src/lang.ts const REGEX_DTS = /\.d\.[cm]?ts(\?.*)?$/; const REGEX_LANG_TS = /^[cm]?tsx?$/; const REGEX_LANG_JSX = /^[cm]?[jt]sx$/; /** * Returns the language (extension name) of a given filename. * @param filename - The name of the file. * @returns The language of the file. */ function getLang(filename) { if (isDts(filename)) return "dts"; return extname(filename).replace(/^\./, "").replace(/\?.*$/, ""); } /** * Checks if a filename represents a TypeScript declaration file (.d.ts). * @param filename - The name of the file to check. * @returns A boolean value indicating whether the filename is a TypeScript declaration file. */ function isDts(filename) { return REGEX_DTS.test(filename); } /** * Checks if the given language (ts, mts, cjs, dts, tsx...) is TypeScript. * @param lang - The language to check. * @returns A boolean indicating whether the language is TypeScript. */ function isTs(lang) { return !!lang && (lang === "dts" || REGEX_LANG_TS.test(lang)); } //#endregion //#region src/loc.ts /** * Locates the trailing comma in the given code within the specified range. * * @param code - The code to search for the trailing comma. * @param start - The start index of the range to search within. * @param end - The end index of the range to search within. * @param comments - Optional array of comments to exclude from the search range. * @returns The index of the trailing comma, or -1 if not found. */ function locateTrailingComma(code, start, end, comments = []) { let i = start; while (i < end) { if (comments.some((c) => i >= c.start && i < c.end)) { i++; continue; } const char = code[i]; if (["}", ")"].includes(char)) return -1; if (char === ",") return i; i++; } return -1; } //#endregion //#region src/parse.ts function hasPlugin(plugins, plugin) { return plugins.some((p) => (Array.isArray(p) ? p[0] : p) === plugin); } /** * Gets the Babel parser options for the given language. * @param lang - The language of the code (optional). * @param options - The parser options (optional). * @returns The Babel parser options for the given language. */ function getBabelParserOptions(lang, options = {}) { const plugins = [...options.plugins || []]; if (isTs(lang)) { if (!hasPlugin(plugins, "typescript")) plugins.push(lang === "dts" ? ["typescript", { dts: true }] : "typescript"); if (!hasPlugin(plugins, "decorators") && !hasPlugin(plugins, "decorators-legacy")) plugins.push("decorators-legacy"); if (!hasPlugin(plugins, "importAttributes") && !hasPlugin(plugins, "importAssertions") && !hasPlugin(plugins, "deprecatedImportAssert")) plugins.push("importAttributes", "deprecatedImportAssert"); if (!hasPlugin(plugins, "explicitResourceManagement")) plugins.push("explicitResourceManagement"); if (REGEX_LANG_JSX.test(lang) && !hasPlugin(plugins, "jsx")) plugins.push("jsx"); } else if (!hasPlugin(plugins, "jsx")) plugins.push("jsx"); return { sourceType: "module", ...options, plugins }; } /** * Parses the given code using Babel parser. * * @param code - The code to parse. * @param lang - The language of the code (optional). * @param options - The parser options (optional). * @param options.cache - Whether to cache the result (optional). * @returns The parse result, including the program, errors, and comments. */ function babelParse(code, lang, { cache,...options } = {}) { let result; if (cache) result = parseCache.get(code); if (!result) { result = parse(code, getBabelParserOptions(lang, options)); if (cache) parseCache.set(code, result); } const { program, type,...rest } = result; return { ...program, ...rest }; } const parseCache = new Map(); /** * Parses the given code using the Babel parser as an expression. * * @template T - The type of the parsed AST node. * @param {string} code - The code to parse. * @param {string} [lang] - The language to parse. Defaults to undefined. * @param {ParserOptions} [options] - The options to configure the parser. Defaults to an empty object. * @returns {ParseResult} - The result of the parsing operation. */ function babelParseExpression(code, lang, options = {}) { return parseExpression(code, getBabelParserOptions(lang, options)); } //#endregion //#region src/resolve.ts /** * Resolves a string representation of the given node. * @param node The node to resolve. * @param computed Whether the node is computed or not. * @returns The resolved string representation of the node. */ function resolveString(node, computed = false) { if (typeof node === "string") return node; else if (node.type === "Identifier") { if (computed) throw new TypeError("Invalid Identifier"); return node.name; } else if (node.type === "PrivateName") return `#${node.id.name}`; else if (node.type === "ThisExpression") return "this"; else if (node.type === "Super") return "super"; return String(resolveLiteral(node)); } /** * Resolves the value of a literal node. * @param node The literal node to resolve. * @returns The resolved value of the literal node. */ function resolveLiteral(node) { switch (node.type) { case "TemplateLiteral": return resolveTemplateLiteral(node); case "NullLiteral": return null; case "BigIntLiteral": return BigInt(node.value); case "RegExpLiteral": return new RegExp(node.pattern, node.flags); case "BooleanLiteral": case "NumericLiteral": case "StringLiteral": return node.value; case "DecimalLiteral": return Number(node.value); } } /** * Resolves a template literal node into a string. * @param node The template literal node to resolve. * @returns The resolved string representation of the template literal. */ function resolveTemplateLiteral(node) { return node.quasis.reduce((prev, curr, idx) => { const expr = node.expressions[idx]; if (expr) { if (!isLiteralType(expr)) throw new TypeError("TemplateLiteral expression must be a literal"); return prev + curr.value.cooked + resolveLiteral(expr); } return prev + curr.value.cooked; }, ""); } /** * Resolves the identifier node into an array of strings. * @param node The identifier node to resolve. * @returns An array of resolved strings representing the identifier. * @throws TypeError If the identifier is invalid. */ function resolveIdentifier(node) { if (isTypeOf(node, [ "Identifier", "PrivateName", "ThisExpression", "Super" ])) return [resolveString(node)]; const left = node.type === "TSQualifiedName" ? node.left : node.object; const right = node.type === "TSQualifiedName" ? node.right : node.property; const computed = node.type === "TSQualifiedName" ? false : node.computed; if (isTypeOf(left, [ "Identifier", "MemberExpression", "ThisExpression", "Super", "TSQualifiedName" ])) { const keys = resolveIdentifier(left); if (isTypeOf(right, [ "Identifier", "PrivateName", "Literal" ])) keys.push(resolveString(right, computed)); else throw new TypeError("Invalid Identifier"); return keys; } throw new TypeError("Invalid Identifier"); } function tryResolveIdentifier(...args) { try { return resolveIdentifier(...args); } catch { return; } } function resolveObjectKey(node, raw = false) { const { key, computed } = node; switch (key.type) { case "StringLiteral": case "NumericLiteral": return raw ? key.extra.raw : key.value; case "Identifier": if (!computed) return raw ? `"${key.name}"` : key.name; throw "Cannot resolve computed Identifier"; default: throw new SyntaxError(`Unexpected node type: ${key.type}`); } } //#endregion //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/walker.js /** * @typedef { import('estree').Node} Node * @typedef {{ * skip: () => void; * remove: () => void; * replace: (node: Node) => void; * }} WalkerContext */ var WalkerBase = class { constructor() { /** @type {boolean} */ this.should_skip = false; /** @type {boolean} */ this.should_remove = false; /** @type {Node | null} */ this.replacement = null; /** @type {WalkerContext} */ this.context = { skip: () => this.should_skip = true, remove: () => this.should_remove = true, replace: (node) => this.replacement = node }; } /** * @template {Node} Parent * @param {Parent | null | undefined} parent * @param {keyof Parent | null | undefined} prop * @param {number | null | undefined} index * @param {Node} node */ replace(parent, prop, index, node) { if (parent && prop) if (index != null) /** @type {Array} */ parent[prop][index] = node; else /** @type {Node} */ parent[prop] = node; } /** * @template {Node} Parent * @param {Parent | null | undefined} parent * @param {keyof Parent | null | undefined} prop * @param {number | null | undefined} index */ remove(parent, prop, index) { if (parent && prop) if (index !== null && index !== void 0) /** @type {Array} */ parent[prop].splice(index, 1); else delete parent[prop]; } }; //#endregion //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/sync.js /** * @typedef { import('estree').Node} Node * @typedef { import('./walker.js').WalkerContext} WalkerContext * @typedef {( * this: WalkerContext, * node: Node, * parent: Node | null, * key: string | number | symbol | null | undefined, * index: number | null | undefined * ) => void} SyncHandler */ var SyncWalker = class extends WalkerBase { /** * * @param {SyncHandler} [enter] * @param {SyncHandler} [leave] */ constructor(enter, leave) { super(); /** @type {boolean} */ this.should_skip = false; /** @type {boolean} */ this.should_remove = false; /** @type {Node | null} */ this.replacement = null; /** @type {WalkerContext} */ this.context = { skip: () => this.should_skip = true, remove: () => this.should_remove = true, replace: (node) => this.replacement = node }; /** @type {SyncHandler | undefined} */ this.enter = enter; /** @type {SyncHandler | undefined} */ this.leave = leave; } /** * @template {Node} Parent * @param {Node} node * @param {Parent | null} parent * @param {keyof Parent} [prop] * @param {number | null} [index] * @returns {Node | null} */ visit(node, parent, prop, index) { if (node) { if (this.enter) { const _should_skip = this.should_skip; const _should_remove = this.should_remove; const _replacement = this.replacement; this.should_skip = false; this.should_remove = false; this.replacement = null; this.enter.call(this.context, node, parent, prop, index); if (this.replacement) { node = this.replacement; this.replace(parent, prop, index, node); } if (this.should_remove) this.remove(parent, prop, index); const skipped = this.should_skip; const removed = this.should_remove; this.should_skip = _should_skip; this.should_remove = _should_remove; this.replacement = _replacement; if (skipped) return node; if (removed) return null; } /** @type {keyof Node} */ let key; for (key in node) { /** @type {unknown} */ const value = node[key]; if (value && typeof value === "object") { if (Array.isArray(value)) { const nodes = value; for (let i = 0; i < nodes.length; i += 1) { const item = nodes[i]; if (isNode$1(item)) { if (!this.visit(item, node, key, i)) i--; } } } else if (isNode$1(value)) this.visit(value, node, key, null); } } if (this.leave) { const _replacement = this.replacement; const _should_remove = this.should_remove; this.replacement = null; this.should_remove = false; this.leave.call(this.context, node, parent, prop, index); if (this.replacement) { node = this.replacement; this.replace(parent, prop, index, node); } if (this.should_remove) this.remove(parent, prop, index); const removed = this.should_remove; this.replacement = _replacement; this.should_remove = _should_remove; if (removed) return null; } } return node; } }; /** * Ducktype a node. * * @param {unknown} value * @returns {value is Node} */ function isNode$1(value) { return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string"; } //#endregion //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/async.js /** * @typedef { import('estree').Node} Node * @typedef { import('./walker.js').WalkerContext} WalkerContext * @typedef {( * this: WalkerContext, * node: Node, * parent: Node | null, * key: string | number | symbol | null | undefined, * index: number | null | undefined * ) => Promise} AsyncHandler */ var AsyncWalker = class extends WalkerBase { /** * * @param {AsyncHandler} [enter] * @param {AsyncHandler} [leave] */ constructor(enter, leave) { super(); /** @type {boolean} */ this.should_skip = false; /** @type {boolean} */ this.should_remove = false; /** @type {Node | null} */ this.replacement = null; /** @type {WalkerContext} */ this.context = { skip: () => this.should_skip = true, remove: () => this.should_remove = true, replace: (node) => this.replacement = node }; /** @type {AsyncHandler | undefined} */ this.enter = enter; /** @type {AsyncHandler | undefined} */ this.leave = leave; } /** * @template {Node} Parent * @param {Node} node * @param {Parent | null} parent * @param {keyof Parent} [prop] * @param {number | null} [index] * @returns {Promise} */ async visit(node, parent, prop, index) { if (node) { if (this.enter) { const _should_skip = this.should_skip; const _should_remove = this.should_remove; const _replacement = this.replacement; this.should_skip = false; this.should_remove = false; this.replacement = null; await this.enter.call(this.context, node, parent, prop, index); if (this.replacement) { node = this.replacement; this.replace(parent, prop, index, node); } if (this.should_remove) this.remove(parent, prop, index); const skipped = this.should_skip; const removed = this.should_remove; this.should_skip = _should_skip; this.should_remove = _should_remove; this.replacement = _replacement; if (skipped) return node; if (removed) return null; } /** @type {keyof Node} */ let key; for (key in node) { /** @type {unknown} */ const value = node[key]; if (value && typeof value === "object") { if (Array.isArray(value)) { const nodes = value; for (let i = 0; i < nodes.length; i += 1) { const item = nodes[i]; if (isNode(item)) { if (!await this.visit(item, node, key, i)) i--; } } } else if (isNode(value)) await this.visit(value, node, key, null); } } if (this.leave) { const _replacement = this.replacement; const _should_remove = this.should_remove; this.replacement = null; this.should_remove = false; await this.leave.call(this.context, node, parent, prop, index); if (this.replacement) { node = this.replacement; this.replace(parent, prop, index, node); } if (this.should_remove) this.remove(parent, prop, index); const removed = this.should_remove; this.replacement = _replacement; this.should_remove = _should_remove; if (removed) return null; } } return node; } }; /** * Ducktype a node. * * @param {unknown} value * @returns {value is Node} */ function isNode(value) { return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string"; } //#endregion //#region node_modules/.pnpm/estree-walker@3.0.3/node_modules/estree-walker/src/index.js /** * @typedef {import('estree').Node} Node * @typedef {import('./sync.js').SyncHandler} SyncHandler * @typedef {import('./async.js').AsyncHandler} AsyncHandler */ /** * @param {Node} ast * @param {{ * enter?: SyncHandler * leave?: SyncHandler * }} walker * @returns {Node | null} */ function walk(ast, { enter, leave }) { const instance = new SyncWalker(enter, leave); return instance.visit(ast, null); } /** * @param {Node} ast * @param {{ * enter?: AsyncHandler * leave?: AsyncHandler * }} walker * @returns {Promise} */ async function asyncWalk(ast, { enter, leave }) { const instance = new AsyncWalker(enter, leave); return await instance.visit(ast, null); } //#endregion //#region src/walk.ts /** * Walks the AST and applies the provided handlers. * * @template T - The type of the AST node. * @param {T} node - The root node of the AST. * @param {WalkHandlers} hooks - The handlers to be applied during the walk. * @returns {T | null} - The modified AST node or null if the node is removed. */ const walkAST = walk; /** * Asynchronously walks the AST starting from the given node, * applying the provided handlers to each node encountered. * * @template T - The type of the AST node. * @param {T} node - The root node of the AST. * @param {WalkHandlers>} handlers - The handlers to be applied to each node. * @returns {Promise} - A promise that resolves to the modified AST or null if the AST is empty. */ const walkASTAsync = asyncWalk; /** * Walks through an ImportDeclaration node and populates the provided imports object. * * @param imports - The object to store the import bindings. * @param node - The ImportDeclaration node to walk through. */ function walkImportDeclaration(imports, node) { if (node.importKind === "type") return; const source = node.source.value; for (const specifier of node.specifiers) { const isType = specifier.type === "ImportSpecifier" && specifier.importKind === "type"; const local = specifier.local.name; const imported = specifier.type === "ImportSpecifier" ? resolveString(specifier.imported) : specifier.type === "ImportNamespaceSpecifier" ? "*" : "default"; imports[local] = { source, local, imported, specifier, isType }; } } /** * Walks through an ExportDeclaration node and populates the exports object with the relevant information. * @param exports - The object to store the export information. * @param node - The ExportDeclaration node to process. */ function walkExportDeclaration(exports, node) { let local; let exported; let isType; let source; let specifier; let declaration; function setExport() { exports[exported] = { source, local, exported, specifier, isType, declaration }; } if (node.type === "ExportNamedDeclaration") { if (node.specifiers.length > 0) for (const s of node.specifiers) { const isExportSpecifier = s.type === "ExportSpecifier"; isType = node.exportKind === "type" || isExportSpecifier && s.exportKind === "type"; local = isExportSpecifier ? s.local.name : s.type === "ExportNamespaceSpecifier" ? "*" : "default"; source = node.source ? node.source.value : null; exported = isExportSpecifier ? resolveString(s.exported) : s.exported.name; declaration = null; specifier = s; setExport(); } else if (node.specifiers.length === 0 && !!node.declaration) { if (node.declaration.type === "VariableDeclaration") for (const decl of node.declaration.declarations) { /* c8 ignore next 4 */ if (decl.id.type !== "Identifier") continue; local = resolveString(decl.id); source = null; exported = local; isType = node.exportKind === "type"; declaration = node.declaration; specifier = null; setExport(); } else if ("id" in node.declaration && node.declaration.id && node.declaration.id.type === "Identifier") { local = resolveString(node.declaration.id); source = null; exported = local; isType = node.exportKind === "type"; declaration = node.declaration; specifier = null; setExport(); } } return; } else if (node.type === "ExportDefaultDeclaration") { if (isExpressionType(node.declaration)) local = "name" in node.declaration ? node.declaration.name : "default"; else local = resolveString(node.declaration.id || "default"); source = null; exported = "default"; isType = false; declaration = node.declaration; specifier = null; } else { local = "*"; source = resolveString(node.source); exported = "*"; isType = node.exportKind === "type"; specifier = null; declaration = null; } setExport(); } //#endregion //#region src/scope.ts const extractors = { ArrayPattern(names, param) { for (const element of param.elements) if (element) extractors[element.type](names, element); }, AssignmentPattern(names, param) { extractors[param.left.type](names, param.left); }, Identifier(names, param) { names.push(param.name); }, MemberExpression() {}, ObjectPattern(names, param) { for (const prop of param.properties) if (prop.type === "RestElement") extractors.RestElement(names, prop); else extractors[prop.value.type](names, prop.value); }, RestElement(names, param) { extractors[param.argument.type](names, param.argument); } }; function extractAssignedNames(param) { const names = []; extractors[param.type](names, param); return names; } const blockDeclarations = { const: true, let: true }; /** * Represents a scope. */ var Scope = class { parent; isBlockScope; declarations; constructor(options = {}) { this.parent = options.parent; this.isBlockScope = !!options.block; this.declarations = Object.create(null); if (options.params) options.params.forEach((param) => { extractAssignedNames(param).forEach((name) => { this.declarations[name] = true; }); }); } addDeclaration(node, isBlockDeclaration, isVar) { if (!isBlockDeclaration && this.isBlockScope) this.parent.addDeclaration(node, isBlockDeclaration, isVar); else if (node.id) extractAssignedNames(node.id).forEach((name) => { this.declarations[name] = true; }); } contains(name) { return this.declarations[name] || (this.parent ? this.parent.contains(name) : false); } }; /** * Attaches scopes to the given AST * * @param ast - The AST to attach scopes to. * @param propertyName - The name of the property to attach the scopes to. Default is 'scope'. * @returns The root scope of the AST. */ function attachScopes(ast, propertyName = "scope") { let scope = new Scope(); walkAST(ast, { enter(node, parent) { if (/(?:Function|Class)Declaration/.test(node.type)) scope.addDeclaration(node, false, false); if (node.type === "VariableDeclaration") { const { kind } = node; const isBlockDeclaration = blockDeclarations[kind]; node.declarations.forEach((declaration) => { scope.addDeclaration(declaration, isBlockDeclaration, true); }); } let newScope; if (/Function/.test(node.type)) { const func = node; newScope = new Scope({ parent: scope, block: false, params: func.params }); if (func.type === "FunctionExpression" && func.id) newScope.addDeclaration(func, false, false); } if (/For(?:In|Of)?Statement/.test(node.type)) newScope = new Scope({ parent: scope, block: true }); if (node.type === "BlockStatement" && !/Function/.test(parent.type)) newScope = new Scope({ parent: scope, block: true }); if (node.type === "CatchClause") newScope = new Scope({ parent: scope, params: node.param ? [node.param] : [], block: true }); if (newScope) { Object.defineProperty(node, propertyName, { value: newScope, configurable: true }); scope = newScope; } }, leave(node) { if (node[propertyName]) scope = scope.parent; } }); return scope; } /* c8 ignore stop */ //#endregion //#region src/utils.ts const TS_NODE_TYPES = [ "TSAsExpression", "TSTypeAssertion", "TSNonNullExpression", "TSInstantiationExpression", "TSSatisfiesExpression" ]; /** * Unwraps a TypeScript node by recursively traversing the AST until a non-TypeScript node is found. * @param node - The TypeScript node to unwrap. * @returns The unwrapped node. */ function unwrapTSNode(node) { if (isTypeOf(node, TS_NODE_TYPES)) return unwrapTSNode(node.expression); else return node; } /** * Escapes a raw key by checking if it needs to be wrapped with quotes or not. * * @param rawKey - The raw key to escape. * @returns The escaped key. */ function escapeKey(rawKey) { if (String(+rawKey) === rawKey) return rawKey; try { const node = parseExpression(`({${rawKey}: 1})`); if (node.properties[0].key.type === "Identifier") return rawKey; } catch {} return JSON.stringify(rawKey); } //#endregion export { REGEX_DTS, REGEX_LANG_JSX, REGEX_LANG_TS, Scope, TS_NODE_TYPES, attachScopes, babelParse, babelParseExpression, createStringLiteral, createTSLiteralType, createTSUnionType, escapeKey, extractIdentifiers, getBabelParserOptions, getLang, isCallOf, isDeclarationType, isDts, isExpressionType, isFunctionType, isIdentifierOf, isLiteralType, isReferenced, isTaggedFunctionCallOf, isTs, isTypeOf, locateTrailingComma, parseCache, resolveIdentifier, resolveLiteral, resolveObjectKey, resolveString, resolveTemplateLiteral, tryResolveIdentifier, unwrapTSNode, walkAST, walkASTAsync, walkExportDeclaration, walkImportDeclaration };