@bunup/dts 0.11.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +694 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# typeroll
|
|
2
|
+
|
|
3
|
+
A blazing-fast `.d.ts` bundler written in Bun, designed to generate and merge TypeScript declarations from an entry point into a single `index.d.ts`, with advanced features like splitting and minification.
|
|
4
|
+
|
|
5
|
+
Typeroll powers [Bunup's TypeScript declarations feature](https://bunup.dev/docs/guide/typescript-declarations). Learn more at [bunup.dev](https://bunup.dev/).
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
Typeroll leverages **Bun's native bundler** under the hood to achieve blazing-fast TypeScript declaration bundling. Since Bun's bundler is designed for JavaScript and outputs JavaScript (not built for types or `.d.ts` files), we employ some clever tricks to make Bun bundle TypeScript declarations while supporting advanced features like code splitting and minification.
|
|
10
|
+
|
|
11
|
+
### The Process
|
|
12
|
+
|
|
13
|
+
**1. Declaration Generation**
|
|
14
|
+
|
|
15
|
+
First, `typeroll` generates `.d.ts` files from TypeScript sources using **isolated declarations** via `oxc-transform`.
|
|
16
|
+
|
|
17
|
+
**2. FakeJS Transformation**
|
|
18
|
+
|
|
19
|
+
Here's where the magic happens: we convert `.d.ts` files into synthetic JavaScript modules ("FakeJS"). Each type declaration becomes a **token array** where:
|
|
20
|
+
- Type content is broken down into individual tokens
|
|
21
|
+
- **Identifiers are preserved as live JavaScript variables** (not strings)
|
|
22
|
+
- The array variable name matches the original type name
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
// Original TypeScript declaration:
|
|
26
|
+
export interface UserProfile extends BaseUser {
|
|
27
|
+
name: string;
|
|
28
|
+
age: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Transformed to FakeJS:
|
|
32
|
+
export var UserProfile = [
|
|
33
|
+
"interface", UserProfile, "extends", BaseUser, "{",
|
|
34
|
+
"name", ":", "string", ";",
|
|
35
|
+
"age", ":", "number", ";",
|
|
36
|
+
"}"
|
|
37
|
+
];
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**3. Bundling with Bun**
|
|
41
|
+
|
|
42
|
+
Bun treats these FakeJS modules as regular JavaScript, enabling it to:
|
|
43
|
+
- **Track usage** of types through variable references
|
|
44
|
+
- Apply **tree-shaking** to remove unused types
|
|
45
|
+
- Perform **code splitting** when shared types are detected
|
|
46
|
+
- Apply **name mangling** during minification
|
|
47
|
+
|
|
48
|
+
The key insight: since identifiers are live variables, when Bun renames a variable during minification, it automatically updates **all references** throughout the bundle.
|
|
49
|
+
|
|
50
|
+
**4. Rehydration**
|
|
51
|
+
|
|
52
|
+
After bundling, we convert the Bun-bundled FakeJS back to clean `.d.ts` declarations. The beauty is that any name mangling applied by Bun is perfectly preserved—if Bun renamed `UserInterface` to `a`, all references are consistently updated.
|
|
53
|
+
|
|
54
|
+
### The Result
|
|
55
|
+
|
|
56
|
+
This round-trip transformation gives us the best of both worlds: the performance and advanced features of Bun's JavaScript bundler applied to TypeScript declarations, resulting in optimized, tree-shaken, and properly split `.d.ts` files without relying on TypeScript's own emit pipeline.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { OxcError } from "oxc-transform";
|
|
2
|
+
type IsolatedDeclarationError = {
|
|
3
|
+
error: OxcError
|
|
4
|
+
file: string
|
|
5
|
+
content: string
|
|
6
|
+
};
|
|
7
|
+
declare function logIsolatedDeclarationErrors(errors: IsolatedDeclarationError[]): void;
|
|
8
|
+
type Naming = string | {
|
|
9
|
+
chunk: string
|
|
10
|
+
};
|
|
11
|
+
type Resolve = boolean | (string | RegExp)[];
|
|
12
|
+
/**
|
|
13
|
+
* Options for generating declaration file
|
|
14
|
+
*/
|
|
15
|
+
type GenerateDtsOptions = {
|
|
16
|
+
naming?: Naming
|
|
17
|
+
/**
|
|
18
|
+
* Path to the preferred tsconfig.json file
|
|
19
|
+
* By default, the closest tsconfig.json file will be used
|
|
20
|
+
*/
|
|
21
|
+
preferredTsConfigPath?: string
|
|
22
|
+
/**
|
|
23
|
+
* Controls which external modules should be resolved
|
|
24
|
+
* - `true` to resolve all external modules
|
|
25
|
+
* - Array of strings or RegExp to match specific modules
|
|
26
|
+
* - `false` or `undefined` to disable external resolution
|
|
27
|
+
*/
|
|
28
|
+
resolve?: Resolve
|
|
29
|
+
/**
|
|
30
|
+
* The directory where the plugin will look for the `tsconfig.json` file and `node_modules`
|
|
31
|
+
* By default, the current working directory will be used
|
|
32
|
+
*/
|
|
33
|
+
cwd?: string
|
|
34
|
+
/**
|
|
35
|
+
* Whether to split declaration files when multiple entrypoints import the same files,
|
|
36
|
+
* modules, or share types. When enabled, shared types will be extracted to separate
|
|
37
|
+
* .d.ts files, and other declaration files will import these shared files.
|
|
38
|
+
*
|
|
39
|
+
* This helps reduce bundle size by preventing duplication of type definitions
|
|
40
|
+
* across multiple entrypoints.
|
|
41
|
+
*/
|
|
42
|
+
splitting?: boolean
|
|
43
|
+
/**
|
|
44
|
+
* Whether to minify the generated declaration files to reduce the size of the declaration file.
|
|
45
|
+
*/
|
|
46
|
+
minify?: boolean
|
|
47
|
+
};
|
|
48
|
+
type GenerateDtsResultFile = {
|
|
49
|
+
/**
|
|
50
|
+
* The kind of declaration file.
|
|
51
|
+
* - 'entry-point': The declaration file for an entry point
|
|
52
|
+
* - 'chunk': A declaration file created when code splitting is enabled
|
|
53
|
+
*/
|
|
54
|
+
kind: "entry-point" | "chunk"
|
|
55
|
+
/**
|
|
56
|
+
* The entry point that was used to generate the declaration file.
|
|
57
|
+
*
|
|
58
|
+
* This will only be available if the kind is 'entry-point' and not for chunk declaration files.
|
|
59
|
+
*/
|
|
60
|
+
entrypoint: string | undefined
|
|
61
|
+
/**
|
|
62
|
+
* If the kind is 'chunk', this is the name of the chunk file.
|
|
63
|
+
*/
|
|
64
|
+
chunkFileName: string | undefined
|
|
65
|
+
/**
|
|
66
|
+
* The output path of the declaration file relative to the output directory.
|
|
67
|
+
* This is the directory where you want to save the declaration file.
|
|
68
|
+
* When saving the declaration file, you should use this path to save it
|
|
69
|
+
* in the output directory you decide.
|
|
70
|
+
*
|
|
71
|
+
* This is particularly useful when splitting is enabled, as some declaration
|
|
72
|
+
* files import chunk files. Saving to this path ensures the import paths
|
|
73
|
+
* are correct.
|
|
74
|
+
*
|
|
75
|
+
* This is the recommended approach when saving declaration files to the
|
|
76
|
+
* output directory.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* await Bun.write(`dist/${result.outputPath}`, result.dts)
|
|
80
|
+
*/
|
|
81
|
+
outputPath: string
|
|
82
|
+
/**
|
|
83
|
+
* The parsed parts of the output path, containing the file name and extension
|
|
84
|
+
* This is useful when you need to manipulate the file name or extension separately
|
|
85
|
+
*/
|
|
86
|
+
pathInfo: {
|
|
87
|
+
/** The output path relative to the output directory without the extension */
|
|
88
|
+
outputPathWithoutExtension: string
|
|
89
|
+
/** The file extension including the dot (e.g. '.d.ts') */
|
|
90
|
+
ext: string
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The generated declaration file
|
|
94
|
+
*/
|
|
95
|
+
dts: string
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Result of the generateDts function
|
|
99
|
+
*/
|
|
100
|
+
type GenerateDtsResult = {
|
|
101
|
+
/**
|
|
102
|
+
* The generated declaration files with their relevant information
|
|
103
|
+
*/
|
|
104
|
+
files: GenerateDtsResultFile[]
|
|
105
|
+
/**
|
|
106
|
+
* The errors that occurred during the generation
|
|
107
|
+
*/
|
|
108
|
+
errors: IsolatedDeclarationError[]
|
|
109
|
+
};
|
|
110
|
+
declare function generateDts(entrypoints: string[], options?: GenerateDtsOptions): Promise<GenerateDtsResult>;
|
|
111
|
+
export { logIsolatedDeclarationErrors, generateDts, IsolatedDeclarationError, GenerateDtsResult, GenerateDtsOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
// packages/dts/src/generate.ts
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { isolatedDeclaration } from "oxc-transform";
|
|
4
|
+
import { resolveTsImportPath } from "ts-import-resolver";
|
|
5
|
+
|
|
6
|
+
// packages/dts/src/constants.ts
|
|
7
|
+
var EMPTY_EXPORT = "export {};";
|
|
8
|
+
|
|
9
|
+
// packages/dts/src/errors.ts
|
|
10
|
+
class TyperollError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(`typeroll: ${message}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// packages/dts/src/fake-js.ts
|
|
17
|
+
import { parse } from "@babel/parser";
|
|
18
|
+
|
|
19
|
+
// packages/dts/src/re.ts
|
|
20
|
+
var IMPORT_TYPE_RE = /import\s+type\s+/g;
|
|
21
|
+
var EXPORT_TYPE_RE = /export\s+type\s+/g;
|
|
22
|
+
var IMPORT_EXPORT_NAMES_RE = /(import|export)\s*{([^}]*)}/g;
|
|
23
|
+
var IMPORT_EXPORT_WITH_DEFAULT_RE = /(import|export)(\s+[^{,]+,)?\s*{([^}]*)}/g;
|
|
24
|
+
var TYPE_WORD_RE = /\btype\s+/g;
|
|
25
|
+
var EXPORT_DEFAULT_RE = /\bexport\s+default\s+/g;
|
|
26
|
+
var EXPORT_RE = /\bexport\s+/g;
|
|
27
|
+
var TOKENIZE_RE = /(\s+|\/\/.*?(?:\n|$)|\/\*[\s\S]*?\*\/|[a-zA-Z_$][a-zA-Z0-9_$]*|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\d+(?:\.\d*)?(?:[eE][+-]?\d+)?|[(){}[\],.;:]|=>|&&|\|\||[=!<>]=?|\+\+|--|[-+*/%&|^!~?]|\.{3}|::|\.)/g;
|
|
28
|
+
var CAPITAL_LETTER_RE = /[A-Z]/;
|
|
29
|
+
var JS_RE = /\.[cm]?jsx?$/;
|
|
30
|
+
var TS_RE = /\.[cm]?tsx?$|\.d\.[cm]?ts$/;
|
|
31
|
+
var EXTENSION_REGEX = /\.(d\.(ts|cts|mts)|[cm]?[jt]s)$/;
|
|
32
|
+
var NODE_MODULES_RE = /node_modules/;
|
|
33
|
+
|
|
34
|
+
// packages/dts/src/ast.ts
|
|
35
|
+
function isLikelyVariableOrTypeName(token) {
|
|
36
|
+
return CAPITAL_LETTER_RE.test(token) && !token.startsWith("/*") && !token.startsWith("@") && !token.startsWith('"') && !token.startsWith("'") && !token.startsWith("`");
|
|
37
|
+
}
|
|
38
|
+
function isImportDeclaration(node) {
|
|
39
|
+
return node.type === "ImportDeclaration";
|
|
40
|
+
}
|
|
41
|
+
function removeExportSyntaxes(text) {
|
|
42
|
+
return text.replace(EXPORT_DEFAULT_RE, "").replace(EXPORT_RE, "");
|
|
43
|
+
}
|
|
44
|
+
function isExportAllDeclaration(node) {
|
|
45
|
+
return node.type === "ExportAllDeclaration";
|
|
46
|
+
}
|
|
47
|
+
function isReExportStatement(node) {
|
|
48
|
+
return node.type === "ExportNamedDeclaration" && !node.declaration;
|
|
49
|
+
}
|
|
50
|
+
function isSideEffectImport(node) {
|
|
51
|
+
return node.type === "ImportDeclaration" && node.specifiers.length === 0;
|
|
52
|
+
}
|
|
53
|
+
function hasExportModifier(node, text) {
|
|
54
|
+
return node.type.startsWith("Export") || text.trim().startsWith("export");
|
|
55
|
+
}
|
|
56
|
+
function hasDefaultExportModifier(node, text) {
|
|
57
|
+
return node.type === "ExportDefaultDeclaration" || text.trim().startsWith("export default");
|
|
58
|
+
}
|
|
59
|
+
function isDefaultReExport(node) {
|
|
60
|
+
return node.type === "ExportDefaultDeclaration" && node.declaration?.type === "Identifier";
|
|
61
|
+
}
|
|
62
|
+
function getName(node, source) {
|
|
63
|
+
if (!node)
|
|
64
|
+
return null;
|
|
65
|
+
if (node.type === "ExportNamedDeclaration" && node.declaration) {
|
|
66
|
+
return getName(node.declaration, source);
|
|
67
|
+
}
|
|
68
|
+
if (node.type === "ExportDefaultDeclaration" && node.declaration) {
|
|
69
|
+
if (node.declaration.type === "Identifier") {
|
|
70
|
+
return node.declaration.name;
|
|
71
|
+
}
|
|
72
|
+
return getName(node.declaration, source);
|
|
73
|
+
}
|
|
74
|
+
switch (node.type) {
|
|
75
|
+
case "TSInterfaceDeclaration":
|
|
76
|
+
case "TSTypeAliasDeclaration":
|
|
77
|
+
case "ClassDeclaration":
|
|
78
|
+
case "TSEnumDeclaration":
|
|
79
|
+
case "FunctionDeclaration":
|
|
80
|
+
case "TSDeclareFunction":
|
|
81
|
+
if (node.id && node.id.type === "Identifier") {
|
|
82
|
+
return node.id.name;
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case "TSModuleDeclaration":
|
|
86
|
+
if (node.id && node.id.type === "Identifier") {
|
|
87
|
+
return node.id.name;
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case "VariableDeclaration": {
|
|
91
|
+
const declarations = node.declarations;
|
|
92
|
+
if (declarations?.length === 1 && declarations[0].id?.type === "Identifier") {
|
|
93
|
+
return declarations[0].id.name;
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function getCommentText(comments) {
|
|
101
|
+
if (!comments)
|
|
102
|
+
return null;
|
|
103
|
+
return comments.map((comment) => {
|
|
104
|
+
return comment.type === "CommentBlock" ? `/*${comment.value}*/` : comment.type === "CommentLine" ? `//${comment.value}` : null;
|
|
105
|
+
}).join(`
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
108
|
+
function getAllImportNames(body) {
|
|
109
|
+
const importNames = [];
|
|
110
|
+
for (const statement of body) {
|
|
111
|
+
if (isImportDeclaration(statement)) {
|
|
112
|
+
const importDecl = statement;
|
|
113
|
+
if (importDecl.specifiers) {
|
|
114
|
+
for (const specifier of importDecl.specifiers) {
|
|
115
|
+
if (specifier.type === "ImportDefaultSpecifier") {
|
|
116
|
+
importNames.push(specifier.local.name);
|
|
117
|
+
} else if (specifier.type === "ImportSpecifier") {
|
|
118
|
+
importNames.push(specifier.local.name);
|
|
119
|
+
} else if (specifier.type === "ImportNamespaceSpecifier") {
|
|
120
|
+
importNames.push(specifier.local.name);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return importNames;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// packages/dts/src/utils.ts
|
|
130
|
+
import { existsSync } from "node:fs";
|
|
131
|
+
import { normalize } from "node:path";
|
|
132
|
+
import { loadConfig } from "coffi";
|
|
133
|
+
import { minify } from "oxc-minify";
|
|
134
|
+
import { isCI, isDevelopment } from "std-env";
|
|
135
|
+
function isTypeScriptFile(path) {
|
|
136
|
+
if (!path)
|
|
137
|
+
return false;
|
|
138
|
+
return TS_RE.test(path);
|
|
139
|
+
}
|
|
140
|
+
function returnPathIfExists(path) {
|
|
141
|
+
return existsSync(path) ? path : null;
|
|
142
|
+
}
|
|
143
|
+
function getExtension(filename) {
|
|
144
|
+
const match = filename.match(EXTENSION_REGEX);
|
|
145
|
+
if (!match)
|
|
146
|
+
return "";
|
|
147
|
+
const ext = match[0];
|
|
148
|
+
return ext;
|
|
149
|
+
}
|
|
150
|
+
function replaceExtension(filename, newExt) {
|
|
151
|
+
if (EXTENSION_REGEX.test(filename)) {
|
|
152
|
+
return filename.replace(EXTENSION_REGEX, newExt);
|
|
153
|
+
}
|
|
154
|
+
return filename + newExt;
|
|
155
|
+
}
|
|
156
|
+
function deleteExtension(filename) {
|
|
157
|
+
return filename.replace(EXTENSION_REGEX, "");
|
|
158
|
+
}
|
|
159
|
+
async function loadTsConfig(cwd, preferredPath) {
|
|
160
|
+
const config = await loadConfig({
|
|
161
|
+
name: "tsconfig",
|
|
162
|
+
extensions: [".json"],
|
|
163
|
+
preferredPath,
|
|
164
|
+
cwd
|
|
165
|
+
});
|
|
166
|
+
return config;
|
|
167
|
+
}
|
|
168
|
+
function getShortFilePath(filePath, maxLength = 3) {
|
|
169
|
+
const fileParts = filePath.split("/");
|
|
170
|
+
const shortPath = fileParts.slice(-maxLength).join("/");
|
|
171
|
+
return shortPath;
|
|
172
|
+
}
|
|
173
|
+
function generateRandomString(length = 10) {
|
|
174
|
+
return Array.from({ length }, () => String.fromCharCode(97 + Math.floor(Math.random() * 26))).join("");
|
|
175
|
+
}
|
|
176
|
+
function isDev() {
|
|
177
|
+
return isDevelopment || !isCI;
|
|
178
|
+
}
|
|
179
|
+
function isNullOrUndefined(value) {
|
|
180
|
+
return value === undefined || value === null;
|
|
181
|
+
}
|
|
182
|
+
function cleanPath(path) {
|
|
183
|
+
let cleaned = normalize(path).replace(/\\/g, "/");
|
|
184
|
+
cleaned = cleaned.replace(/^[a-zA-Z]:\//, "");
|
|
185
|
+
cleaned = cleaned.replace(/^\/+/, "");
|
|
186
|
+
cleaned = cleaned.replace(/\/+/g, "/");
|
|
187
|
+
return cleaned;
|
|
188
|
+
}
|
|
189
|
+
function getDeclarationExtensionFromJsExtension(ext) {
|
|
190
|
+
if (ext === ".mjs")
|
|
191
|
+
return ".d.mts";
|
|
192
|
+
if (ext === ".cjs")
|
|
193
|
+
return ".d.cts";
|
|
194
|
+
return ".d.ts";
|
|
195
|
+
}
|
|
196
|
+
async function getFilesFromGlobs(patterns, cwd) {
|
|
197
|
+
const includePatterns = patterns.filter((p) => !p.startsWith("!"));
|
|
198
|
+
const excludePatterns = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
|
|
199
|
+
const includedFiles = new Set;
|
|
200
|
+
for (const pattern of includePatterns) {
|
|
201
|
+
const glob = new Bun.Glob(pattern);
|
|
202
|
+
for await (const file of glob.scan(cwd)) {
|
|
203
|
+
includedFiles.add(file);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (excludePatterns.length > 0) {
|
|
207
|
+
for (const pattern of excludePatterns) {
|
|
208
|
+
const glob = new Bun.Glob(pattern);
|
|
209
|
+
for await (const file of glob.scan(cwd)) {
|
|
210
|
+
includedFiles.delete(file);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return Array.from(includedFiles);
|
|
215
|
+
}
|
|
216
|
+
function filterTypescriptFiles(files) {
|
|
217
|
+
return files.filter((file) => isTypeScriptFile(file));
|
|
218
|
+
}
|
|
219
|
+
function minifyDts(dts) {
|
|
220
|
+
return minify(`${generateRandomString()}.d.ts`, dts, {
|
|
221
|
+
codegen: {
|
|
222
|
+
removeWhitespace: true
|
|
223
|
+
},
|
|
224
|
+
mangle: false,
|
|
225
|
+
compress: false,
|
|
226
|
+
sourcemap: false
|
|
227
|
+
}).code;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// packages/dts/src/fake-js.ts
|
|
231
|
+
async function dtsToFakeJs(dtsContent) {
|
|
232
|
+
const parsed = parse(dtsContent, {
|
|
233
|
+
sourceType: "module",
|
|
234
|
+
plugins: ["typescript"]
|
|
235
|
+
});
|
|
236
|
+
const referencedNames = new Set;
|
|
237
|
+
const exportedNames = new Set;
|
|
238
|
+
const result = [];
|
|
239
|
+
for (const name of getAllImportNames(parsed.program.body)) {
|
|
240
|
+
referencedNames.add(name);
|
|
241
|
+
}
|
|
242
|
+
for (const statement of parsed.program.body) {
|
|
243
|
+
if (isNullOrUndefined(statement.start) || isNullOrUndefined(statement.end)) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const statementText = dtsContent.substring(statement.start, statement.end);
|
|
247
|
+
const name = getName(statement, dtsContent);
|
|
248
|
+
if (name) {
|
|
249
|
+
referencedNames.add(name);
|
|
250
|
+
}
|
|
251
|
+
const isDefaultExport = hasDefaultExportModifier(statement, statementText);
|
|
252
|
+
if (isDefaultExport) {
|
|
253
|
+
result.push(`export { ${name} as default };`);
|
|
254
|
+
if (isDefaultReExport(statement)) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (isImportDeclaration(statement) || isExportAllDeclaration(statement) || isReExportStatement(statement)) {
|
|
259
|
+
if (isSideEffectImport(statement)) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const jsImportExport = jsifyImportExport(statementText);
|
|
263
|
+
result.push(jsImportExport);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
let leadingComment = null;
|
|
267
|
+
leadingComment = getCommentText(statement.leadingComments);
|
|
268
|
+
let statementTextWithCommentsAttatched = `${leadingComment ? `${leadingComment}
|
|
269
|
+
` : ""}${statementText}`;
|
|
270
|
+
const isExported = hasExportModifier(statement, statementText);
|
|
271
|
+
if (isExported) {
|
|
272
|
+
statementTextWithCommentsAttatched = removeExportSyntaxes(statementTextWithCommentsAttatched);
|
|
273
|
+
}
|
|
274
|
+
const tokens = tokenizeText(statementTextWithCommentsAttatched, referencedNames);
|
|
275
|
+
const varName = name || generateRandomString();
|
|
276
|
+
result.push(`var ${varName} = [${tokens.join(", ")}];`);
|
|
277
|
+
if (isExported && !isDefaultExport && !exportedNames.has(varName)) {
|
|
278
|
+
result.push(`export { ${varName} };`);
|
|
279
|
+
exportedNames.add(varName);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return result.join(`
|
|
283
|
+
`);
|
|
284
|
+
}
|
|
285
|
+
async function fakeJsToDts(fakeJsContent) {
|
|
286
|
+
const parseResult = parse(fakeJsContent, {
|
|
287
|
+
sourceType: "module",
|
|
288
|
+
attachComment: false
|
|
289
|
+
});
|
|
290
|
+
const program = parseResult.program;
|
|
291
|
+
const resultParts = [];
|
|
292
|
+
for (const statement of program.body) {
|
|
293
|
+
if (isNullOrUndefined(statement.start) || isNullOrUndefined(statement.end)) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const statementText = fakeJsContent.substring(statement.start, statement.end);
|
|
297
|
+
if (isImportDeclaration(statement) || isExportAllDeclaration(statement) || isReExportStatement(statement)) {
|
|
298
|
+
if (isImportDeclaration(statement)) {
|
|
299
|
+
resultParts.push(statementText.replace(/.(?:mjs|cjs|js)\b/g, ""));
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
resultParts.push(statementText);
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (statement.type === "ExpressionStatement") {
|
|
306
|
+
const namespaceDecl = handleNamespace(statement);
|
|
307
|
+
if (namespaceDecl) {
|
|
308
|
+
resultParts.push(namespaceDecl);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (statement.type === "VariableDeclaration") {
|
|
313
|
+
for (const declaration of statement.declarations) {
|
|
314
|
+
if (declaration.init?.type === "ArrayExpression") {
|
|
315
|
+
const dtsContent = processTokenArray(declaration.init);
|
|
316
|
+
if (dtsContent) {
|
|
317
|
+
resultParts.push(dtsContent);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return resultParts.join(`
|
|
324
|
+
`);
|
|
325
|
+
}
|
|
326
|
+
function jsifyImportExport(text) {
|
|
327
|
+
let result = text.replace(IMPORT_TYPE_RE, "import ").replace(EXPORT_TYPE_RE, "export ").replace(IMPORT_EXPORT_NAMES_RE, (_, keyword, names) => `${keyword} {${names.replace(TYPE_WORD_RE, "")}}`);
|
|
328
|
+
result = result.replace(IMPORT_EXPORT_WITH_DEFAULT_RE, (_, keyword, defaultPart = "", names = "") => {
|
|
329
|
+
const cleanedNames = names.replace(TYPE_WORD_RE, "");
|
|
330
|
+
return `${keyword}${defaultPart}{${cleanedNames}}`;
|
|
331
|
+
});
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
function tokenizeText(text, referencedNames) {
|
|
335
|
+
const tokens = [];
|
|
336
|
+
let match;
|
|
337
|
+
TOKENIZE_RE.lastIndex = 0;
|
|
338
|
+
while (true) {
|
|
339
|
+
match = TOKENIZE_RE.exec(text);
|
|
340
|
+
if (match === null)
|
|
341
|
+
break;
|
|
342
|
+
const token = match[0];
|
|
343
|
+
if (isLikelyVariableOrTypeName(token) || referencedNames.has(token)) {
|
|
344
|
+
tokens.push(token);
|
|
345
|
+
} else {
|
|
346
|
+
tokens.push(JSON.stringify(escapeNewlinesAndTabs(token)));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return tokens;
|
|
350
|
+
}
|
|
351
|
+
function processTokenArray(arrayLiteral) {
|
|
352
|
+
if (arrayLiteral.type !== "ArrayExpression") {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
const tokens = [];
|
|
356
|
+
for (const element of arrayLiteral.elements) {
|
|
357
|
+
if (!element)
|
|
358
|
+
continue;
|
|
359
|
+
const processed = processTokenElement(element);
|
|
360
|
+
if (processed !== null) {
|
|
361
|
+
tokens.push(processed);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return tokens.join("");
|
|
365
|
+
}
|
|
366
|
+
function processTokenElement(element) {
|
|
367
|
+
if (element.type === "StringLiteral" && typeof element.value === "string") {
|
|
368
|
+
return unescapeNewlinesAndTabs(element.value);
|
|
369
|
+
}
|
|
370
|
+
if (element.type === "Identifier") {
|
|
371
|
+
return element.name;
|
|
372
|
+
}
|
|
373
|
+
if (element.type === "TemplateLiteral") {
|
|
374
|
+
const parts = [];
|
|
375
|
+
parts.push(unescapeNewlinesAndTabs(element.quasis[0]?.value?.raw || ""));
|
|
376
|
+
for (let i = 0;i < element.expressions.length; i++) {
|
|
377
|
+
const expr = element.expressions[i];
|
|
378
|
+
if (expr.type === "Identifier") {
|
|
379
|
+
parts.push(expr.name);
|
|
380
|
+
}
|
|
381
|
+
parts.push(unescapeNewlinesAndTabs(element.quasis[i + 1]?.value?.raw || ""));
|
|
382
|
+
}
|
|
383
|
+
return parts.join("");
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
function escapeNewlinesAndTabs(text) {
|
|
388
|
+
return text.replace(/\n/g, "__typeroll_intermediate_new__line__").replace(/\t/g, "__typeroll_intermediate__tab__");
|
|
389
|
+
}
|
|
390
|
+
function unescapeNewlinesAndTabs(text) {
|
|
391
|
+
return text.replace(/__typeroll_intermediate_new__line__/g, `
|
|
392
|
+
`).replace(/__typeroll_intermediate__tab__/g, "\t");
|
|
393
|
+
}
|
|
394
|
+
function handleNamespace(stmt) {
|
|
395
|
+
const expr = stmt.expression;
|
|
396
|
+
if (!expr || expr.type !== "CallExpression" || expr.callee?.type !== "Identifier" || expr.arguments?.length !== 2 || expr.arguments[0].type !== "Identifier" || expr.arguments[1].type !== "ObjectExpression") {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
const namespaceName = expr.arguments[0].name;
|
|
400
|
+
const properties = expr.arguments[1].properties.filter((prop) => prop.type === "ObjectProperty").map((prop) => {
|
|
401
|
+
if (prop.type === "ObjectProperty" && prop.key.type === "Identifier" && prop.value.type === "ArrowFunctionExpression" && prop.value.body.type === "Identifier") {
|
|
402
|
+
const keyName = prop.key.name;
|
|
403
|
+
const returnName = prop.value.body.name;
|
|
404
|
+
return keyName === returnName ? keyName : `${returnName} as ${keyName}`;
|
|
405
|
+
}
|
|
406
|
+
return null;
|
|
407
|
+
}).filter(Boolean);
|
|
408
|
+
if (properties.length === 0) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
return `declare namespace ${namespaceName} {
|
|
412
|
+
export { ${properties.join(", ")} };
|
|
413
|
+
}`;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// packages/dts/src/resolver.ts
|
|
417
|
+
import { dirname } from "node:path";
|
|
418
|
+
import process2 from "node:process";
|
|
419
|
+
import { ResolverFactory } from "oxc-resolver";
|
|
420
|
+
function createResolver({
|
|
421
|
+
tsconfig,
|
|
422
|
+
cwd = process2.cwd(),
|
|
423
|
+
resolveOption
|
|
424
|
+
}) {
|
|
425
|
+
const resolver = new ResolverFactory({
|
|
426
|
+
mainFields: ["types", "typings", "module", "main"],
|
|
427
|
+
conditionNames: ["types", "typings", "import", "require"],
|
|
428
|
+
extensions: [".d.ts", ".d.mts", ".d.cts", ".ts", ".mts", ".cts"],
|
|
429
|
+
tsconfig: tsconfig ? { configFile: tsconfig, references: "auto" } : undefined
|
|
430
|
+
});
|
|
431
|
+
const resolutionCache = new Map;
|
|
432
|
+
return (importSource, importer) => {
|
|
433
|
+
if (importSource === "bun")
|
|
434
|
+
return null;
|
|
435
|
+
const cacheKey = `${importSource}:${importer || ""}`;
|
|
436
|
+
if (resolutionCache.has(cacheKey)) {
|
|
437
|
+
return resolutionCache.get(cacheKey) || null;
|
|
438
|
+
}
|
|
439
|
+
let shouldResolve = false;
|
|
440
|
+
if (resolveOption !== undefined) {
|
|
441
|
+
if (typeof resolveOption === "boolean") {
|
|
442
|
+
shouldResolve = resolveOption;
|
|
443
|
+
} else if (Array.isArray(resolveOption)) {
|
|
444
|
+
shouldResolve = resolveOption.some((resolver2) => {
|
|
445
|
+
if (typeof resolver2 === "string") {
|
|
446
|
+
return resolver2 === importSource;
|
|
447
|
+
}
|
|
448
|
+
return resolver2.test(importSource);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (!shouldResolve) {
|
|
453
|
+
resolutionCache.set(cacheKey, null);
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
const directory = importer ? dirname(importer) : cwd;
|
|
457
|
+
const resolution = resolver.sync(directory, importSource);
|
|
458
|
+
if (!resolution.path) {
|
|
459
|
+
resolutionCache.set(cacheKey, null);
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
const resolved = resolution.path;
|
|
463
|
+
if (JS_RE.test(resolved)) {
|
|
464
|
+
const dts = returnPathIfExists(resolved.replace(JS_RE, ".d.ts")) || returnPathIfExists(resolved.replace(JS_RE, ".d.mts")) || returnPathIfExists(resolved.replace(JS_RE, ".d.cts"));
|
|
465
|
+
const result2 = isTypeScriptFile(dts) ? dts : null;
|
|
466
|
+
resolutionCache.set(cacheKey, result2);
|
|
467
|
+
return result2;
|
|
468
|
+
}
|
|
469
|
+
const result = isTypeScriptFile(resolved) ? resolved : null;
|
|
470
|
+
resolutionCache.set(cacheKey, result);
|
|
471
|
+
return result;
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// packages/dts/src/generate.ts
|
|
476
|
+
async function generateDts(entrypoints, options = {}) {
|
|
477
|
+
const { resolve, preferredTsConfigPath, naming } = options;
|
|
478
|
+
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
|
|
479
|
+
const tsconfig = await loadTsConfig(cwd, preferredTsConfigPath);
|
|
480
|
+
const nonAbsoluteEntrypoints = entrypoints.filter((entrypoint) => !path.isAbsolute(entrypoint));
|
|
481
|
+
const resolvedEntrypoints = await getFilesFromGlobs(nonAbsoluteEntrypoints, cwd);
|
|
482
|
+
const absoluteEntrypoints = entrypoints.filter((entrypoint) => path.isAbsolute(entrypoint));
|
|
483
|
+
if (!filterTypescriptFiles([...resolvedEntrypoints, ...absoluteEntrypoints]).length) {
|
|
484
|
+
throw new TyperollError("One or more of the entrypoints you provided do not exist. Please check that each entrypoint points to a valid file.");
|
|
485
|
+
}
|
|
486
|
+
const collectedErrors = [];
|
|
487
|
+
const resolver = createResolver({
|
|
488
|
+
cwd,
|
|
489
|
+
resolveOption: resolve,
|
|
490
|
+
tsconfig: tsconfig.filepath
|
|
491
|
+
});
|
|
492
|
+
const fakeJsPlugin = {
|
|
493
|
+
name: "fake-js",
|
|
494
|
+
setup(build) {
|
|
495
|
+
build.onResolve({ filter: /.*/ }, (args) => {
|
|
496
|
+
if (!NODE_MODULES_RE.test(args.importer)) {
|
|
497
|
+
const resolved = resolveTsImportPath({
|
|
498
|
+
importer: args.importer,
|
|
499
|
+
path: args.path,
|
|
500
|
+
cwd,
|
|
501
|
+
tsconfig: tsconfig.config
|
|
502
|
+
});
|
|
503
|
+
if (resolved && isTypeScriptFile(resolved)) {
|
|
504
|
+
return { path: resolved };
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const resolvedFromNodeModules = resolver(args.path, args.importer);
|
|
508
|
+
if (resolvedFromNodeModules) {
|
|
509
|
+
return { path: resolvedFromNodeModules };
|
|
510
|
+
}
|
|
511
|
+
return {
|
|
512
|
+
path: args.path,
|
|
513
|
+
external: true
|
|
514
|
+
};
|
|
515
|
+
});
|
|
516
|
+
build.onLoad({ filter: /\.(ts|tsx|d\.ts|d\.mts|d\.cts)$/ }, async (args) => {
|
|
517
|
+
const sourceText = await Bun.file(args.path).text();
|
|
518
|
+
const declarationResult = isolatedDeclaration(args.path, sourceText);
|
|
519
|
+
let fakeJsContent = "";
|
|
520
|
+
if (!collectedErrors.some((e) => e.file === args.path)) {
|
|
521
|
+
for (const error of declarationResult.errors) {
|
|
522
|
+
collectedErrors.push({
|
|
523
|
+
error,
|
|
524
|
+
file: args.path,
|
|
525
|
+
content: sourceText
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (declarationResult.code) {
|
|
530
|
+
fakeJsContent = await dtsToFakeJs(declarationResult.code);
|
|
531
|
+
} else {
|
|
532
|
+
fakeJsContent = EMPTY_EXPORT;
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
loader: "js",
|
|
536
|
+
contents: fakeJsContent
|
|
537
|
+
};
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
const result = await Bun.build({
|
|
542
|
+
entrypoints: [
|
|
543
|
+
...filterTypescriptFiles(resolvedEntrypoints).map((entry) => path.resolve(path.join(cwd, entry))),
|
|
544
|
+
...filterTypescriptFiles(absoluteEntrypoints)
|
|
545
|
+
],
|
|
546
|
+
format: "esm",
|
|
547
|
+
target: "node",
|
|
548
|
+
naming,
|
|
549
|
+
splitting: options.splitting,
|
|
550
|
+
plugins: [fakeJsPlugin],
|
|
551
|
+
packages: "external",
|
|
552
|
+
minify: options.minify,
|
|
553
|
+
throw: false
|
|
554
|
+
});
|
|
555
|
+
if (!result.success) {
|
|
556
|
+
throw new TyperollError(`DTS bundling failed: ${result.logs}`);
|
|
557
|
+
}
|
|
558
|
+
const outputs = result.outputs.filter((output) => output.kind === "chunk" || output.kind === "entry-point");
|
|
559
|
+
const bundledFiles = [];
|
|
560
|
+
for (const output of outputs) {
|
|
561
|
+
const bundledFakeJsContent = await output.text();
|
|
562
|
+
const dtsContent = await fakeJsToDts(bundledFakeJsContent);
|
|
563
|
+
const entrypoint = output.kind === "entry-point" ? entrypoints[bundledFiles.length] : undefined;
|
|
564
|
+
const chunkFileName = output.kind === "chunk" ? replaceExtension(path.basename(output.path), getDeclarationExtensionFromJsExtension(getExtension(output.path))) : undefined;
|
|
565
|
+
const outputPath = cleanPath(replaceExtension(cleanPath(output.path), getDeclarationExtensionFromJsExtension(getExtension(output.path))));
|
|
566
|
+
const treeshakedDts = isolatedDeclaration(`${generateRandomString()}.d.ts`, dtsContent);
|
|
567
|
+
if (!treeshakedDts.code.length && !treeshakedDts.errors.length) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (treeshakedDts.errors.length && !treeshakedDts.code) {
|
|
571
|
+
throw new TyperollError(`DTS treeshaking failed for ${entrypoint || outputPath}
|
|
572
|
+
|
|
573
|
+
${JSON.stringify(treeshakedDts.errors, null, 2)}`);
|
|
574
|
+
}
|
|
575
|
+
bundledFiles.push({
|
|
576
|
+
kind: output.kind === "entry-point" ? "entry-point" : "chunk",
|
|
577
|
+
entrypoint,
|
|
578
|
+
chunkFileName,
|
|
579
|
+
outputPath,
|
|
580
|
+
dts: options.minify ? minifyDts(treeshakedDts.code) : treeshakedDts.code,
|
|
581
|
+
pathInfo: {
|
|
582
|
+
outputPathWithoutExtension: deleteExtension(outputPath),
|
|
583
|
+
ext: getExtension(outputPath)
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
files: bundledFiles,
|
|
589
|
+
errors: collectedErrors
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
// packages/dts/src/isolated-decl-logger.ts
|
|
593
|
+
import pc from "picocolors";
|
|
594
|
+
var SEVERITY_CONFIG = {
|
|
595
|
+
dev: {
|
|
596
|
+
Error: { color: pc.yellow, prefix: "WARN" },
|
|
597
|
+
Warning: { color: pc.yellow, prefix: "WARN" },
|
|
598
|
+
Advice: { color: pc.blue, prefix: "ADVICE" },
|
|
599
|
+
default: { color: pc.blue, prefix: "WARN" }
|
|
600
|
+
},
|
|
601
|
+
prod: {
|
|
602
|
+
Error: { color: pc.red, prefix: "Error" },
|
|
603
|
+
Warning: { color: pc.yellow, prefix: "Warning" },
|
|
604
|
+
Advice: { color: pc.blue, prefix: "Advice" },
|
|
605
|
+
default: { color: pc.red, prefix: "Error" }
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
var ISOLATED_DECLARATION_ERRORS = {
|
|
609
|
+
TS9007: `Function requires an explicit return type, e.g., \`function foo(): ${pc.green("string")} { ... }\`.`,
|
|
610
|
+
TS9008: `Method requires an explicit return type, e.g., \`myMethod(): ${pc.green("number")} { ... }\`.`,
|
|
611
|
+
TS9009: "Ensure at least one accessor (getter/setter) has an explicit return type.",
|
|
612
|
+
TS9010: `Variable requires an explicit type annotation, e.g., \`let name: ${pc.green("string")} = "Bob";\`.`,
|
|
613
|
+
TS9011: `Function parameter requires an explicit type, e.g., \`(param: ${pc.green("number")}) => {}\`.`,
|
|
614
|
+
TS9012: `Class property requires an explicit type, e.g., \`class MyClass { id: ${pc.green("number")}; }\`.`,
|
|
615
|
+
TS9013: "Expression type cannot be inferred. Add a type annotation where this expression is assigned or used.",
|
|
616
|
+
TS9014: "Computed property names must be simple (e.g., string/number literals or basic identifiers).",
|
|
617
|
+
TS9015: "Either add an explicit type to the object or avoid spread.",
|
|
618
|
+
TS9016: "Either add an explicit type to the object or use full `key: value` syntax.",
|
|
619
|
+
TS9017: `For array type inference, use \`as const\` (e.g., \`[1, 2, 3] ${pc.green("as const")}\`).`,
|
|
620
|
+
TS9018: "Either add an explicit type to the array or avoid spread.",
|
|
621
|
+
TS9019: `Avoid direct export of destructured bindings. Instead, declare and then export, e.g., \`const { x } = obj; ${pc.green("export { x };")}\`.`,
|
|
622
|
+
TS9020: "Enum member initializers must be simple, constant values (like numbers or strings), not expressions or external references.",
|
|
623
|
+
TS9021: "The `extends` clause must refer to a direct class name, not an expression.",
|
|
624
|
+
TS9022: `Class expressions cannot infer types. Assign the class expression to an explicitly typed variable, e.g., \`const MyClass: ${pc.green("typeof OtherClass")} = class { ... };\`.`,
|
|
625
|
+
TS9023: `Properties assigned to functions must be explicitly declared on the function type/interface, e.g., \`interface MyFunc { (): void; ${pc.green("myProp: string;")} }\`.`,
|
|
626
|
+
TS9025: `Parameter can implicitly be \`undefined\`. Explicitly add \`| undefined\` to its type, e.g., \`param?: string\` becomes \`param: ${pc.green("string | undefined")}\`.`,
|
|
627
|
+
TS9037: `Default export requires an explicit type, e.g., \`const MyValue: ${pc.green("number")} = 42; export default MyValue;\`.`,
|
|
628
|
+
TS9038: "Computed property names in class/object literals must be simple (string/number literals or plain identifiers), not complex expressions.",
|
|
629
|
+
TS9039: "A type references a private class member (`{name}`). Private members cannot be part of publicly exposed types."
|
|
630
|
+
};
|
|
631
|
+
function logIsolatedDeclarationErrors(errors) {
|
|
632
|
+
if (!errors.length)
|
|
633
|
+
return;
|
|
634
|
+
const hasErrors = errors.some(({ error }) => error.severity === "Error");
|
|
635
|
+
console.log();
|
|
636
|
+
errors.forEach(logSingle);
|
|
637
|
+
if (hasErrors) {
|
|
638
|
+
if (!isDev())
|
|
639
|
+
process.exit(1);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
function logSingle({ error, file, content }) {
|
|
643
|
+
const label = error.labels?.[0];
|
|
644
|
+
const errorCode = extractErrorCode(error.message);
|
|
645
|
+
const errorMessage = ISOLATED_DECLARATION_ERRORS[errorCode];
|
|
646
|
+
if (!errorMessage)
|
|
647
|
+
return;
|
|
648
|
+
const position = label ? calculatePosition(content, label.start) : "";
|
|
649
|
+
const shortPath = getShortFilePath(file);
|
|
650
|
+
const { color, prefix } = getSeverityFormatting(error.severity);
|
|
651
|
+
const formattedMessage = `${color(prefix)} ${shortPath}${position}: ${errorCode} ${errorMessage}`;
|
|
652
|
+
const codeFrame = label ? generateOxcCodeFrame(content, label.start, label.end) : error.codeframe || "";
|
|
653
|
+
const helpMessage = error.helpMessage ? `
|
|
654
|
+
${pc.cyan("Help:")} ${error.helpMessage}` : "";
|
|
655
|
+
console.log(`${formattedMessage}${helpMessage}
|
|
656
|
+
|
|
657
|
+
${pc.gray(codeFrame)}
|
|
658
|
+
`);
|
|
659
|
+
}
|
|
660
|
+
function extractErrorCode(message) {
|
|
661
|
+
return message.split(":")[0];
|
|
662
|
+
}
|
|
663
|
+
function getSeverityFormatting(severity) {
|
|
664
|
+
const config = SEVERITY_CONFIG[isDev() ? "dev" : "prod"];
|
|
665
|
+
return config[severity] || config.default;
|
|
666
|
+
}
|
|
667
|
+
function calculatePosition(sourceText, labelStart) {
|
|
668
|
+
if (labelStart === undefined)
|
|
669
|
+
return "";
|
|
670
|
+
const lines = sourceText.slice(0, labelStart).split(`
|
|
671
|
+
`);
|
|
672
|
+
const lineNumber = lines.length;
|
|
673
|
+
const columnStart = lines[lines.length - 1].length + 1;
|
|
674
|
+
return ` (${lineNumber}:${columnStart})`;
|
|
675
|
+
}
|
|
676
|
+
function generateOxcCodeFrame(sourceText, start, end) {
|
|
677
|
+
const lines = sourceText.split(`
|
|
678
|
+
`);
|
|
679
|
+
const errorLine = sourceText.slice(0, start).split(`
|
|
680
|
+
`).length;
|
|
681
|
+
const lineContent = lines[errorLine - 1];
|
|
682
|
+
const lastNewlineIndex = sourceText.slice(0, start).lastIndexOf(`
|
|
683
|
+
`);
|
|
684
|
+
const startCol = start - lastNewlineIndex - 1;
|
|
685
|
+
const endCol = end ? Math.min(end - lastNewlineIndex - 1, lineContent.length) : startCol + 1;
|
|
686
|
+
const underlineLength = Math.max(1, endCol - startCol);
|
|
687
|
+
const arrowLine = " ".repeat(startCol) + pc.dim(pc.blue("⎯".repeat(underlineLength)));
|
|
688
|
+
return `${lineContent}
|
|
689
|
+
${arrowLine}`;
|
|
690
|
+
}
|
|
691
|
+
export {
|
|
692
|
+
logIsolatedDeclarationErrors,
|
|
693
|
+
generateDts
|
|
694
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bunup/dts",
|
|
3
|
+
"description": "An extremely fast TypeScript declaration file generator and bundler that outputs to a single file.",
|
|
4
|
+
"version": "0.11.19",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@babel/parser": "^7.28.0",
|
|
21
|
+
"coffi": "^0.1.31",
|
|
22
|
+
"oxc-minify": "^0.77.2",
|
|
23
|
+
"oxc-resolver": "^8.0.0",
|
|
24
|
+
"oxc-transform": "^0.67.0",
|
|
25
|
+
"picocolors": "^1.1.1",
|
|
26
|
+
"std-env": "^3.9.0",
|
|
27
|
+
"ts-import-resolver": "^0.1.23"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"author": "Arshad Yaseen <m@arshadyaseen.com> (https://arshadyaseen.com)",
|
|
31
|
+
"maintainers": [
|
|
32
|
+
{
|
|
33
|
+
"name": "Arshad Yaseen",
|
|
34
|
+
"email": "m@arshadyaseen.com",
|
|
35
|
+
"url": "https://arshadyaseen.com"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"keywords": [
|
|
39
|
+
"typescript",
|
|
40
|
+
"dts",
|
|
41
|
+
"declaration",
|
|
42
|
+
"declaration generation",
|
|
43
|
+
"bun",
|
|
44
|
+
"bun dts"
|
|
45
|
+
],
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/bunup/bunup.git"
|
|
49
|
+
},
|
|
50
|
+
"funding": "https://github.com/sponsors/arshad-yaseen",
|
|
51
|
+
"homepage": "https://bunup.dev",
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"typescript": ">=4.5.0"
|
|
54
|
+
},
|
|
55
|
+
"peerDependenciesMeta": {
|
|
56
|
+
"typescript": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@babel/types": "^7.28.2"
|
|
62
|
+
}
|
|
63
|
+
}
|