|
1 | 1 | import { defineConfig } from 'tsdown'; |
| 2 | +import fs from 'node:fs'; |
2 | 3 | import path from 'node:path'; |
| 4 | +import {parseAst} from 'rolldown/parseAst'; |
| 5 | + |
| 6 | +type AstNode = { |
| 7 | + type?: string; |
| 8 | + start?: number; |
| 9 | + end?: number; |
| 10 | + name?: string; |
| 11 | + value?: unknown; |
| 12 | + callee?: AstNode; |
| 13 | + arguments?: AstNode[]; |
| 14 | + [key: string]: unknown; |
| 15 | +}; |
| 16 | + |
| 17 | +type Replacement = { |
| 18 | + start: number; |
| 19 | + end: number; |
| 20 | + value: string; |
| 21 | +}; |
| 22 | + |
| 23 | +function isNode(value: unknown): value is AstNode { |
| 24 | + return typeof value === 'object' && value !== null && typeof (value as AstNode).type === 'string'; |
| 25 | +} |
| 26 | + |
| 27 | +function isNodeArray(value: unknown): value is AstNode[] { |
| 28 | + return Array.isArray(value) && value.every(isNode); |
| 29 | +} |
| 30 | + |
| 31 | +function isBundledDependency(id: string): boolean { |
| 32 | + return id.includes('/node_modules/') || id.includes('\\node_modules\\'); |
| 33 | +} |
| 34 | + |
| 35 | +function isPackageJsonSpecifier(value: unknown): value is string { |
| 36 | + return typeof value === 'string' && value.startsWith('.') && path.basename(value) === 'package.json'; |
| 37 | +} |
| 38 | + |
| 39 | +function findPackageJsonRequires(node: AstNode, visitor: (node: AstNode, specifier: string) => void): void { |
| 40 | + if ( |
| 41 | + node.type === 'CallExpression' |
| 42 | + && node.callee?.type === 'Identifier' |
| 43 | + && (node.callee.name === 'cjsRequire' || node.callee.name === 'require') |
| 44 | + && node.arguments?.length === 1 |
| 45 | + && node.arguments[0]?.type === 'Literal' |
| 46 | + && isPackageJsonSpecifier(node.arguments[0].value) |
| 47 | + ) { |
| 48 | + visitor(node, node.arguments[0].value); |
| 49 | + } |
| 50 | + |
| 51 | + for (const value of Object.values(node)) { |
| 52 | + if (isNode(value)) { |
| 53 | + findPackageJsonRequires(value, visitor); |
| 54 | + } else if (isNodeArray(value)) { |
| 55 | + for (const child of value) { |
| 56 | + findPackageJsonRequires(child, visitor); |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +function applyReplacements(code: string, replacements: Replacement[]): string { |
| 63 | + return replacements |
| 64 | + .sort((first, second) => second.start - first.start) |
| 65 | + .reduce( |
| 66 | + (result, replacement) => ( |
| 67 | + result.slice(0, replacement.start) + replacement.value + result.slice(replacement.end) |
| 68 | + ), |
| 69 | + code, |
| 70 | + ); |
| 71 | +} |
| 72 | + |
| 73 | +function inlineBundledPackageJson() { |
| 74 | + return { |
| 75 | + name: 'inline-bundled-package-json', |
| 76 | + transform(code: string, id: string, meta: {ast?: AstNode} = {}) { |
| 77 | + if (!isBundledDependency(id) || !code.includes('package.json')) { |
| 78 | + return null; |
| 79 | + } |
| 80 | + |
| 81 | + const ast = meta.ast ?? parseAst(code, null, id) as AstNode; |
| 82 | + const replacements: Replacement[] = []; |
| 83 | + |
| 84 | + findPackageJsonRequires(ast, (node, specifier) => { |
| 85 | + if (node.start === undefined || node.end === undefined) { |
| 86 | + return; |
| 87 | + } |
| 88 | + |
| 89 | + const packageJsonPath = path.resolve(path.dirname(id), specifier); |
| 90 | + |
| 91 | + if (!fs.existsSync(packageJsonPath)) { |
| 92 | + return; |
| 93 | + } |
| 94 | + |
| 95 | + replacements.push({ |
| 96 | + start: node.start, |
| 97 | + end: node.end, |
| 98 | + value: JSON.stringify(JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))), |
| 99 | + }); |
| 100 | + }); |
| 101 | + |
| 102 | + return replacements.length > 0 ? applyReplacements(code, replacements) : null; |
| 103 | + }, |
| 104 | + }; |
| 105 | +} |
3 | 106 |
|
4 | 107 | export default defineConfig({ |
5 | 108 | entry: ['src/index.ts'], |
6 | 109 | format: 'cjs', |
7 | 110 | dts: true, |
8 | 111 | platform: 'node', |
| 112 | + plugins: [inlineBundledPackageJson()], |
9 | 113 | alias: { |
10 | 114 | // Redirect to a shim that statically resolves rules |
11 | 115 | // (the original uses `requireindex` which breaks in bundles) |
|
0 commit comments