{"version":3,"file":"ast.cjs","names":[],"sources":["../../src/ast.ts"],"sourcesContent":["import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nimport * as t from '@babel/types'\nimport {\n  deadCodeElimination as _deadCodeElimination,\n  findReferencedIdentifiers,\n} from 'babel-dead-code-elimination'\nimport type { GeneratorOptions, GeneratorResult } from '@babel/generator'\nimport type { ParseResult, ParserOptions } from '@babel/parser'\nimport type * as _babel_types from '@babel/types'\n\nexport type ParseAstOptions = ParserOptions & {\n  code: string\n  filename?: string\n}\n\nexport type ParseAstResult = ParseResult<_babel_types.File>\nexport function parseAst({\n  code,\n  filename,\n  sourceFilename,\n  plugins,\n  ...opts\n}: ParseAstOptions): ParseAstResult {\n  const inferredFilename = filename ?? sourceFilename\n  return parse(code, {\n    plugins: plugins ?? getDefaultParserPluginsForFilename(inferredFilename),\n    sourceType: 'module',\n    sourceFilename,\n    ...opts,\n  })\n}\n\nfunction getDefaultParserPluginsForFilename(\n  filename: string | undefined,\n): NonNullable<ParserOptions['plugins']> {\n  const plugins: NonNullable<ParserOptions['plugins']> = [\n    'typescript',\n    'explicitResourceManagement',\n    'importAttributes',\n    'deprecatedImportAssert',\n    ['decorators', { decoratorsBeforeExport: true }],\n    'decoratorAutoAccessors',\n  ]\n\n  if (!isPlainTypeScriptFile(filename)) {\n    plugins.unshift('jsx')\n  }\n\n  return plugins\n}\n\nfunction isPlainTypeScriptFile(filename: string | undefined): boolean {\n  if (!filename) {\n    return false\n  }\n\n  return /\\.[cm]?ts(?:$|[?#])/.test(filename)\n}\n\nlet generate = _generate\n\nif ('default' in generate) {\n  generate = generate.default as typeof generate\n}\ntype GenerateFromAstOptions = GeneratorOptions &\n  Required<Pick<GeneratorOptions, 'sourceFileName' | 'filename'>>\nexport function generateFromAst(\n  ast: _babel_types.Node,\n  opts?: GenerateFromAstOptions,\n): GeneratorResult {\n  return generate(\n    ast,\n    opts\n      ? { importAttributesKeyword: 'with', sourceMaps: true, ...opts }\n      : undefined,\n  )\n}\nexport type { GeneratorResult } from '@babel/generator'\n\n/**\n * Strips TypeScript type-only exports and imports from an AST.\n *\n * This is necessary because babel-dead-code-elimination doesn't handle\n * TypeScript type exports/imports. When a type export references an import\n * that pulls in server-only code, the dead code elimination won't remove\n * that import because it sees the type as still referencing it.\n *\n * This function removes:\n * - `export type Foo = ...`\n * - `export interface Foo { ... }`\n * - `export type { Foo } from './module'`\n * - `export type * from './module'`\n * - Type specifiers in mixed exports: `export { value, type Foo }` -> `export { value }`\n * - `import type { Foo } from './module'`\n * - Type specifiers in mixed imports: `import { value, type Foo } from './module'` -> `import { value }`\n *\n * Note: Non-exported type/interface declarations are preserved as they may be\n * used as type annotations within the code.\n *\n * @param ast - The Babel AST (or ParseResult) to mutate\n */\nexport function stripTypeExports(ast: ParseResult<_babel_types.File>): void {\n  // Filter the program body to remove type-only nodes\n  ast.program.body = ast.program.body.filter((node) => {\n    // Handle export declarations\n    if (t.isExportNamedDeclaration(node)) {\n      // Remove entire export if it's a type-only export\n      // e.g., `export type Foo = string`, `export interface Bar {}`, `export type { X } from './y'`\n      if (node.exportKind === 'type') {\n        return false\n      }\n\n      // For value exports with mixed specifiers, filter out type-only specifiers\n      // e.g., `export { value, type TypeOnly }` -> `export { value }`\n      if (node.specifiers.length > 0) {\n        node.specifiers = node.specifiers.filter((specifier) => {\n          if (t.isExportSpecifier(specifier)) {\n            return specifier.exportKind !== 'type'\n          }\n          return true\n        })\n\n        // If all specifiers were removed, remove the entire export declaration\n        // (unless it has a declaration like `export const x = 1`)\n        if (node.specifiers.length === 0 && !node.declaration) {\n          return false\n        }\n      }\n    }\n\n    // Handle type-only export-all declarations\n    // e.g., `export type * from './module'`\n    if (t.isExportAllDeclaration(node)) {\n      if (node.exportKind === 'type') {\n        return false\n      }\n    }\n\n    // Handle import declarations\n    if (t.isImportDeclaration(node)) {\n      // Remove entire import if it's a type-only import\n      // e.g., `import type { Foo } from './module'`\n      if (node.importKind === 'type') {\n        return false\n      }\n\n      // For value imports with mixed specifiers, filter out type-only specifiers\n      // e.g., `import { value, type TypeOnly } from './module'` -> `import { value }`\n      if (node.specifiers.length > 0) {\n        node.specifiers = node.specifiers.filter((specifier) => {\n          if (t.isImportSpecifier(specifier)) {\n            return specifier.importKind !== 'type'\n          }\n          return true\n        })\n\n        // If all specifiers were removed, remove the entire import declaration\n        if (node.specifiers.length === 0) {\n          return false\n        }\n      }\n    }\n\n    return true\n  })\n}\n\n// Re-export findReferencedIdentifiers from babel-dead-code-elimination\nexport { findReferencedIdentifiers }\n\n/**\n * Performs dead code elimination on the AST, with TypeScript type stripping.\n *\n * This is a wrapper around babel-dead-code-elimination that first strips\n * TypeScript type-only exports and imports. This is necessary because\n * babel-dead-code-elimination doesn't handle type exports, which can cause\n * imports to be retained when they're only referenced by type exports.\n *\n * @param ast - The Babel AST to mutate\n * @param candidates - Optional set of identifier paths to consider for removal.\n *                     If provided, only these identifiers will be candidates for removal.\n *                     This should be the result of `findReferencedIdentifiers(ast)` called\n *                     before any AST transformations.\n */\nexport function deadCodeElimination(\n  ast: ParseResult<_babel_types.File>,\n  candidates?: ReturnType<typeof findReferencedIdentifiers>,\n): void {\n  // First strip TypeScript type-only exports and imports\n  stripTypeExports(ast)\n\n  // Then run the original dead code elimination\n  _deadCodeElimination(ast, candidates)\n}\n"],"mappings":";;;;;;;;AAiBA,SAAgB,SAAS,EACvB,MACA,UACA,gBACA,SACA,GAAG,QAC+B;AAElC,SAAA,GAAA,cAAA,OAAa,MAAM;EACjB,SAAS,WAAW,mCAFG,YAAY,eAEqC;EACxE,YAAY;EACZ;EACA,GAAG;EACJ,CAAC;;AAGJ,SAAS,mCACP,UACuC;CACvC,MAAM,UAAiD;EACrD;EACA;EACA;EACA;EACA,CAAC,cAAc,EAAE,wBAAwB,MAAM,CAAC;EAChD;EACD;AAED,KAAI,CAAC,sBAAsB,SAAS,CAClC,SAAQ,QAAQ,MAAM;AAGxB,QAAO;;AAGT,SAAS,sBAAsB,UAAuC;AACpE,KAAI,CAAC,SACH,QAAO;AAGT,QAAO,sBAAsB,KAAK,SAAS;;AAG7C,IAAI,WAAW,iBAAA;AAEf,IAAI,aAAa,SACf,YAAW,SAAS;AAItB,SAAgB,gBACd,KACA,MACiB;AACjB,QAAO,SACL,KACA,OACI;EAAE,yBAAyB;EAAQ,YAAY;EAAM,GAAG;EAAM,GAC9D,KAAA,EACL;;;;;;;;;;;;;;;;;;;;;;;;AA0BH,SAAgB,iBAAiB,KAA2C;AAE1E,KAAI,QAAQ,OAAO,IAAI,QAAQ,KAAK,QAAQ,SAAS;AAEnD,MAAI,aAAE,yBAAyB,KAAK,EAAE;AAGpC,OAAI,KAAK,eAAe,OACtB,QAAO;AAKT,OAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,SAAK,aAAa,KAAK,WAAW,QAAQ,cAAc;AACtD,SAAI,aAAE,kBAAkB,UAAU,CAChC,QAAO,UAAU,eAAe;AAElC,YAAO;MACP;AAIF,QAAI,KAAK,WAAW,WAAW,KAAK,CAAC,KAAK,YACxC,QAAO;;;AAOb,MAAI,aAAE,uBAAuB,KAAK;OAC5B,KAAK,eAAe,OACtB,QAAO;;AAKX,MAAI,aAAE,oBAAoB,KAAK,EAAE;AAG/B,OAAI,KAAK,eAAe,OACtB,QAAO;AAKT,OAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,SAAK,aAAa,KAAK,WAAW,QAAQ,cAAc;AACtD,SAAI,aAAE,kBAAkB,UAAU,CAChC,QAAO,UAAU,eAAe;AAElC,YAAO;MACP;AAGF,QAAI,KAAK,WAAW,WAAW,EAC7B,QAAO;;;AAKb,SAAO;GACP;;;;;;;;;;;;;;;;AAoBJ,SAAgB,oBACd,KACA,YACM;AAEN,kBAAiB,IAAI;AAGrB,EAAA,GAAA,4BAAA,qBAAqB,KAAK,WAAW"}