Add SPA session validation and buglist, update migration docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-12-03 21:28:08 +00:00
parent 9be3dfc14e
commit cff287e870
24169 changed files with 10223 additions and 7120 deletions

279
node_modules/rollup-plugin-dts/dist/rollup-plugin-dts.cjs generated vendored Normal file → Executable file
View File

@@ -344,7 +344,7 @@ class NamespaceFixer {
!ts.isObjectLiteralExpression(obj)) {
continue;
}
const exports = [];
const exports$1 = [];
for (const prop of obj.properties) {
if (!ts.isPropertyAssignment(prop) ||
!(ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) ||
@@ -354,7 +354,7 @@ class NamespaceFixer {
if (prop.name.text === "__proto__") {
continue;
}
exports.push({
exports$1.push({
exportedName: prop.name.text,
localName: prop.initializer.getText(),
});
@@ -362,7 +362,7 @@ class NamespaceFixer {
// sort in reverse order, since we will do string manipulation
namespaces.unshift({
name,
exports,
exports: exports$1,
location,
});
}
@@ -536,6 +536,51 @@ function convertExpression(node) {
end: node.name.getEnd(),
});
}
if (ts.isObjectLiteralExpression(node)) {
return withStartEnd({
type: "ObjectExpression",
properties: node.properties.map((prop) => {
if (ts.isPropertyAssignment(prop)) {
return withStartEnd({
type: "Property",
key: ts.isIdentifier(prop.name) ? createIdentifier(prop.name) : convertExpression(prop.name),
value: convertExpression(prop.initializer),
kind: "init",
method: false,
shorthand: false,
computed: ts.isComputedPropertyName(prop.name),
}, prop);
}
else if (ts.isShorthandPropertyAssignment(prop)) {
return withStartEnd({
type: "Property",
key: createIdentifier(prop.name),
value: createIdentifier(prop.name),
kind: "init",
method: false,
shorthand: true,
computed: false,
}, prop);
}
else {
throw new UnsupportedSyntaxError(prop, "Unsupported property type in object literal");
}
}),
}, node);
}
if (ts.isArrayLiteralExpression(node)) {
return withStartEnd({
type: "ArrayExpression",
elements: node.elements.map((elem) => {
if (ts.isExpression(elem)) {
return convertExpression(elem);
}
else {
throw new UnsupportedSyntaxError(elem, "Unsupported element type in array literal");
}
}),
}, node);
}
if (ts.isIdentifier(node)) {
return createIdentifier(node);
}
@@ -590,23 +635,18 @@ class LanguageService {
}
class TypeOnlyFixer {
constructor(fileName, rawCode, sourcemap) {
this.sourcemap = sourcemap;
constructor(fileName, rawCode) {
this.DEBUG = !!process.env.DTS_EXPORTS_FIXER_DEBUG;
this.types = new Set();
this.values = new Set();
this.typeHints = new Map();
this.reExportTypeHints = new Map();
this.typeofValueReferences = new Set();
this.importNodes = [];
this.exportNodes = [];
this.rawCode = rawCode;
this.source = parse(fileName, rawCode);
this.code = new MagicString(rawCode);
}
setValueReferences(valueReferences) {
this.typeofValueReferences = valueReferences;
}
fix() {
this.analyze(this.source.statements);
if (this.typeHints.size || this.reExportTypeHints.size) {
@@ -618,8 +658,7 @@ class TypeOnlyFixer {
}
return this.types.size
? {
code: this.code.toString(),
map: this.sourcemap ? this.code.generateMap() : null,
magicCode: this.code,
}
: {
code: this.rawCode,
@@ -824,9 +863,6 @@ class TypeOnlyFixer {
return (typeHintCount && typeHintCount + 1 >= referenceCount);
}
isTypeOnly(name) {
if (this.typeofValueReferences.has(name)) {
return false;
}
return this.typeHints.has(name)
|| (this.types.has(name) && !this.values.has(name));
}
@@ -839,6 +875,16 @@ function getNodeIndent(node) {
return ' '.repeat(match?.[1]?.length || 0);
}
function preProcessNamespaceBody(body, code, sourceFile) {
for (const stmt of body.statements) {
// Safely call the new context-aware function on all children
fixModifiers(code, stmt);
// Recurse for nested namespaces
if (ts.isModuleDeclaration(stmt) && stmt.body && ts.isModuleBlock(stmt.body)) {
preProcessNamespaceBody(stmt.body, code);
}
}
}
/**
* The pre-process step has the following goals:
* - [x] Fixes the "modifiers", removing any `export` modifier and adding any
@@ -927,6 +973,9 @@ function preProcess({ sourceFile, isEntry, isJSON }) {
}
// duplicate exports of namespaces
if (ts.isModuleDeclaration(node)) {
if (node.body && ts.isModuleBlock(node.body)) {
preProcessNamespaceBody(node.body, code);
}
duplicateExports(code, node);
}
fixModifiers(code, node);
@@ -1013,6 +1062,18 @@ function preProcess({ sourceFile, isEntry, isJSON }) {
*/
// transformTypeOnlyImport(node);
// transformTypeOnlyExport(node);
// Handle export default with object/array literals
// These need to be converted to named declarations so Rollup can track references within them
if (ts.isExportAssignment(node) && !node.isExportEquals) {
if (ts.isObjectLiteralExpression(node.expression) || ts.isArrayLiteralExpression(node.expression)) {
if (!defaultExport) {
defaultExport = uniqName("export_default");
}
// Replace "export default" with "declare var export_default ="
code.overwrite(node.getStart(), node.expression.getStart(), `declare var ${defaultExport} = `);
continue;
}
}
if (!matchesModifier(node, ts.ModifierFlags.ExportDefault)) {
continue;
}
@@ -1184,26 +1245,31 @@ function fixModifiers(code, node) {
if (!ts.canHaveModifiers(node)) {
return;
}
let hasDeclare = false;
const needsDeclare = ts.isEnumDeclaration(node) ||
ts.isClassDeclaration(node) ||
ts.isFunctionDeclaration(node) ||
ts.isModuleDeclaration(node) ||
ts.isVariableStatement(node);
for (const mod of node.modifiers ?? []) {
switch (mod.kind) {
case ts.SyntaxKind.ExportKeyword: // fall through
case ts.SyntaxKind.DefaultKeyword:
// TODO: be careful about that `+ 1`
code.remove(mod.getStart(), mod.getEnd() + 1);
break;
case ts.SyntaxKind.DeclareKeyword:
hasDeclare = true;
const isTopLevel = node.parent.kind === ts.SyntaxKind.SourceFile;
if (isTopLevel) {
// For top-level statements, remove `export`/`default` and ensure `declare` exists
let hasDeclare = false;
const needsDeclare = ts.isEnumDeclaration(node) ||
ts.isClassDeclaration(node) ||
ts.isFunctionDeclaration(node) ||
ts.isModuleDeclaration(node) ||
ts.isVariableStatement(node);
for (const mod of node.modifiers ?? []) {
switch (mod.kind) {
case ts.SyntaxKind.ExportKeyword: // fall through
case ts.SyntaxKind.DefaultKeyword:
// TODO: be careful about that `+ 1`
code.remove(mod.getStart(), mod.getEnd() + 1);
break;
case ts.SyntaxKind.DeclareKeyword:
hasDeclare = true;
}
}
if (needsDeclare && !hasDeclare) {
code.appendRight(node.getStart(), "declare ");
}
}
if (needsDeclare && !hasDeclare) {
code.appendRight(node.getStart(), "declare ");
}
// For statements inside namespaces, preserve all modifiers (including export)
}
function duplicateExports(code, module) {
if (!module.body || !ts.isModuleBlock(module.body)) {
@@ -1256,7 +1322,6 @@ const IGNORE_TYPENODES = new Set([
]);
class DeclarationScope {
constructor({ id, range }) {
this.valueReferences = new Set();
/**
* As we walk the AST, we need to keep track of type variable bindings that
* shadow the outer identifiers. To achieve this, we keep a stack of scopes,
@@ -1287,12 +1352,6 @@ class DeclarationScope {
const name = id.getText();
this.scopes[this.scopes.length - 1]?.add(name);
}
markAsValue(name) {
this.valueReferences.add(name);
}
getValueReferences() {
return this.valueReferences;
}
pushReference(id) {
let name;
// We convert references from TS AST to ESTree
@@ -1504,12 +1563,6 @@ class DeclarationScope {
if (ts.isTypeQueryNode(node)) {
const reference = this.convertEntityName(node.exprName);
this.pushReference(reference);
if (reference.type === "Identifier") {
this.markAsValue(reference.name);
}
else if (reference.type === "MemberExpression" && reference.object.type === "Identifier") {
this.markAsValue(reference.object.name);
}
this.convertTypeArguments(node);
return;
}
@@ -1574,6 +1627,28 @@ class DeclarationScope {
}
continue;
}
if (ts.isImportDeclaration(stmt)) {
if (stmt.importClause) {
if (stmt.importClause.name) {
this.pushTypeVariable(stmt.importClause.name);
}
if (stmt.importClause.namedBindings) {
if (ts.isNamespaceImport(stmt.importClause.namedBindings)) {
this.pushTypeVariable(stmt.importClause.namedBindings.name);
}
else {
for (const el of stmt.importClause.namedBindings.elements) {
this.pushTypeVariable(el.name);
}
}
}
}
continue;
}
if (ts.isImportEqualsDeclaration(stmt)) {
this.pushTypeVariable(stmt.name);
continue;
}
if (ts.isExportDeclaration(stmt)) ;
else {
throw new UnsupportedSyntaxError(stmt, "namespace child (hoisting) not supported yet");
@@ -1614,6 +1689,18 @@ class DeclarationScope {
// noop
continue;
}
// handle imports in the walking pass
if (ts.isImportDeclaration(stmt)) {
// noop, already handled by hoisting
continue;
}
if (ts.isImportEqualsDeclaration(stmt)) {
if (ts.isEntityName(stmt.moduleReference)) {
this.pushReference(this.convertEntityName(stmt.moduleReference));
}
// we ignore `import foo = require(...)` as that is a module import
continue;
}
if (ts.isExportDeclaration(stmt)) {
if (stmt.exportClause) {
if (ts.isNamespaceExport(stmt.exportClause)) {
@@ -1640,22 +1727,15 @@ function convert({ sourceFile }) {
class Transformer {
constructor(sourceFile) {
this.sourceFile = sourceFile;
this.allTypeReferences = new Set();
this.declarations = new Map();
this.ast = createProgram(sourceFile);
for (const stmt of sourceFile.statements) {
this.convertStatement(stmt);
}
for (const scope of this.declarations.values()) {
for (const valueRef of scope.getValueReferences()) {
this.allTypeReferences.add(valueRef);
}
}
}
transform() {
return {
ast: this.ast,
valueReferences: this.allTypeReferences,
};
}
pushStatement(node) {
@@ -1795,6 +1875,36 @@ class Transformer {
}
const scope = this.createDeclaration(node, decl.name);
scope.convertTypeNode(decl.type);
// Track references in the initializer (e.g., for object literals)
if (decl.initializer) {
this.trackExpressionReferences(decl.initializer, scope);
}
}
}
// Helper to track identifier references in expressions
trackExpressionReferences(expr, scope) {
if (ts.isIdentifier(expr)) {
scope.pushIdentifierReference(expr);
}
else if (ts.isObjectLiteralExpression(expr)) {
for (const prop of expr.properties) {
if (ts.isShorthandPropertyAssignment(prop)) {
scope.pushIdentifierReference(prop.name);
}
else if (ts.isPropertyAssignment(prop)) {
this.trackExpressionReferences(prop.initializer, scope);
}
}
}
else if (ts.isArrayLiteralExpression(expr)) {
for (const elem of expr.elements) {
if (ts.isExpression(elem)) {
this.trackExpressionReferences(elem, scope);
}
}
}
else if (ts.isPropertyAccessExpression(expr)) {
this.trackExpressionReferences(expr.expression, scope);
}
}
convertExportDeclaration(node) {
@@ -1909,6 +2019,43 @@ class Transformer {
}
}
class RelativeModuleDeclarationFixer {
constructor(fileName, code, sourcemap, name) {
this.sourcemap = sourcemap;
this.DEBUG = !!process.env.DTS_EXPORTS_FIXER_DEBUG;
this.relativeModuleDeclarations = [];
this.source = parse(fileName, code.toString());
this.code = code;
this.name = name || "./index";
}
fix() {
this.analyze(this.source.statements);
for (const node of this.relativeModuleDeclarations) {
const start = node.getStart();
const end = node.getEnd();
const quote = node.name.kind === ts.SyntaxKind.StringLiteral && "singleQuote" in node.name && node.name.singleQuote
? "'"
: '"';
const code = `declare module ${quote}${this.name}${quote} ${node.body.getText()}`;
this.code.overwrite(start, end, code);
}
return {
code: this.code.toString(),
map: this.relativeModuleDeclarations.length && this.sourcemap ? this.code.generateMap() : null,
};
}
analyze(nodes) {
for (const node of nodes) {
if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body) && /^\.\.?\//.test(node.name.text)) {
if (this.DEBUG) {
console.log(`Found relative module declaration: ${node.name.text} in ${this.source.fileName}`);
}
this.relativeModuleDeclarations.push(node);
}
}
}
}
/**
* This is the *transform* part of `rollup-plugin-dts`.
*
@@ -1930,7 +2077,6 @@ class Transformer {
const transform = () => {
const allTypeReferences = new Map();
const allFileReferences = new Map();
const allValueReferences = new Map();
return {
name: "dts-transform",
options({ onLog, ...options }) {
@@ -1985,7 +2131,6 @@ const transform = () => {
code = preprocessed.code.toString();
sourceFile = parse(fileName, code);
const converted = convert({ sourceFile });
allValueReferences.set(sourceFile.fileName, converted.valueReferences);
if (process.env.DTS_DUMP_AST) {
console.log(fileName);
console.log(code);
@@ -1998,14 +2143,10 @@ const transform = () => {
const fixer = new NamespaceFixer(source);
const typeReferences = new Set();
const fileReferences = new Set();
const valueReferences = new Set();
for (const fileName of Object.keys(chunk.modules)) {
for (const ref of allTypeReferences.get(fileName.split("\\").join("/")) || []) {
typeReferences.add(ref);
}
for (const ref of allValueReferences.get(fileName.split("\\").join("/")) || []) {
valueReferences.add(ref);
}
for (const ref of allFileReferences.get(fileName.split("\\").join("/")) || []) {
if (ref.startsWith(".")) {
// Need absolute path of the target file here
@@ -2030,9 +2171,10 @@ const transform = () => {
if (!code) {
code += "\nexport { };";
}
const typeOnlyFixer = new TypeOnlyFixer(chunk.fileName, code, !!options.sourcemap);
typeOnlyFixer.setValueReferences(typeReferences);
return typeOnlyFixer.fix();
const typeOnlyFixer = new TypeOnlyFixer(chunk.fileName, code);
const typesFixed = typeOnlyFixer.fix();
const relativeModuleDeclarationFixed = new RelativeModuleDeclarationFixer(chunk.fileName, "magicCode" in typesFixed && typesFixed.magicCode ? typesFixed.magicCode : new MagicString(code), !!options.sourcemap, "./" + (options.file && options.file !== "-" ? path__namespace.basename(options.file, ".d.ts") : "index"));
return relativeModuleDeclarationFixed.fix();
},
};
};
@@ -2044,7 +2186,8 @@ function writeBlock(codes) {
}
const TS_EXTENSIONS = /\.([cm]ts|[tj]sx?)$/;
function getModule({ entries, programs, resolvedOptions: { compilerOptions, tsconfig } }, fileName, code) {
function getModule({ entries, programs, resolvedOptions }, fileName, code) {
const { compilerOptions, tsconfig } = resolvedOptions;
// Create any `ts.SourceFile` objects on-demand for ".d.ts" modules,
// but only when there are zero ".ts" entry points.
if (!programs.length && DTS_EXTENSIONS.test(fileName)) {
@@ -2077,6 +2220,18 @@ function getModule({ entries, programs, resolvedOptions: { compilerOptions, tsco
};
}
else if (ts.sys.fileExists(fileName)) {
// For .d.ts files from external libraries (node_modules), return just the code without creating a program.
// The programs.find() above returns false for external library files (line 52-54), causing
// existingProgram to be undefined even though the file exists in a program. Without this check,
// we would create a new program for each external .d.ts file, causing memory exhaustion
// with large packages like type-fest (170+ files → 170+ programs → OOM).
if (programs.length > 0 && DTS_EXTENSIONS.test(fileName)) {
// Apply this optimization when bundling external packages via includeExternal or respectExternal
const shouldBundleExternal = resolvedOptions.includeExternal.length > 0 || resolvedOptions.respectExternal;
if (shouldBundleExternal) {
return { code };
}
}
const newProgram = createProgram$1(fileName, compilerOptions, tsconfig);
programs.push(newProgram);
// we created hte program from this fileName, so the source file must exist :P

0
node_modules/rollup-plugin-dts/dist/rollup-plugin-dts.d.cts generated vendored Normal file → Executable file
View File

0
node_modules/rollup-plugin-dts/dist/rollup-plugin-dts.d.mts generated vendored Normal file → Executable file
View File

279
node_modules/rollup-plugin-dts/dist/rollup-plugin-dts.mjs generated vendored Normal file → Executable file
View File

@@ -320,7 +320,7 @@ class NamespaceFixer {
!ts.isObjectLiteralExpression(obj)) {
continue;
}
const exports = [];
const exports$1 = [];
for (const prop of obj.properties) {
if (!ts.isPropertyAssignment(prop) ||
!(ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) ||
@@ -330,7 +330,7 @@ class NamespaceFixer {
if (prop.name.text === "__proto__") {
continue;
}
exports.push({
exports$1.push({
exportedName: prop.name.text,
localName: prop.initializer.getText(),
});
@@ -338,7 +338,7 @@ class NamespaceFixer {
// sort in reverse order, since we will do string manipulation
namespaces.unshift({
name,
exports,
exports: exports$1,
location,
});
}
@@ -512,6 +512,51 @@ function convertExpression(node) {
end: node.name.getEnd(),
});
}
if (ts.isObjectLiteralExpression(node)) {
return withStartEnd({
type: "ObjectExpression",
properties: node.properties.map((prop) => {
if (ts.isPropertyAssignment(prop)) {
return withStartEnd({
type: "Property",
key: ts.isIdentifier(prop.name) ? createIdentifier(prop.name) : convertExpression(prop.name),
value: convertExpression(prop.initializer),
kind: "init",
method: false,
shorthand: false,
computed: ts.isComputedPropertyName(prop.name),
}, prop);
}
else if (ts.isShorthandPropertyAssignment(prop)) {
return withStartEnd({
type: "Property",
key: createIdentifier(prop.name),
value: createIdentifier(prop.name),
kind: "init",
method: false,
shorthand: true,
computed: false,
}, prop);
}
else {
throw new UnsupportedSyntaxError(prop, "Unsupported property type in object literal");
}
}),
}, node);
}
if (ts.isArrayLiteralExpression(node)) {
return withStartEnd({
type: "ArrayExpression",
elements: node.elements.map((elem) => {
if (ts.isExpression(elem)) {
return convertExpression(elem);
}
else {
throw new UnsupportedSyntaxError(elem, "Unsupported element type in array literal");
}
}),
}, node);
}
if (ts.isIdentifier(node)) {
return createIdentifier(node);
}
@@ -566,23 +611,18 @@ class LanguageService {
}
class TypeOnlyFixer {
constructor(fileName, rawCode, sourcemap) {
this.sourcemap = sourcemap;
constructor(fileName, rawCode) {
this.DEBUG = !!process.env.DTS_EXPORTS_FIXER_DEBUG;
this.types = new Set();
this.values = new Set();
this.typeHints = new Map();
this.reExportTypeHints = new Map();
this.typeofValueReferences = new Set();
this.importNodes = [];
this.exportNodes = [];
this.rawCode = rawCode;
this.source = parse(fileName, rawCode);
this.code = new MagicString(rawCode);
}
setValueReferences(valueReferences) {
this.typeofValueReferences = valueReferences;
}
fix() {
this.analyze(this.source.statements);
if (this.typeHints.size || this.reExportTypeHints.size) {
@@ -594,8 +634,7 @@ class TypeOnlyFixer {
}
return this.types.size
? {
code: this.code.toString(),
map: this.sourcemap ? this.code.generateMap() : null,
magicCode: this.code,
}
: {
code: this.rawCode,
@@ -800,9 +839,6 @@ class TypeOnlyFixer {
return (typeHintCount && typeHintCount + 1 >= referenceCount);
}
isTypeOnly(name) {
if (this.typeofValueReferences.has(name)) {
return false;
}
return this.typeHints.has(name)
|| (this.types.has(name) && !this.values.has(name));
}
@@ -815,6 +851,16 @@ function getNodeIndent(node) {
return ' '.repeat(match?.[1]?.length || 0);
}
function preProcessNamespaceBody(body, code, sourceFile) {
for (const stmt of body.statements) {
// Safely call the new context-aware function on all children
fixModifiers(code, stmt);
// Recurse for nested namespaces
if (ts.isModuleDeclaration(stmt) && stmt.body && ts.isModuleBlock(stmt.body)) {
preProcessNamespaceBody(stmt.body, code);
}
}
}
/**
* The pre-process step has the following goals:
* - [x] Fixes the "modifiers", removing any `export` modifier and adding any
@@ -903,6 +949,9 @@ function preProcess({ sourceFile, isEntry, isJSON }) {
}
// duplicate exports of namespaces
if (ts.isModuleDeclaration(node)) {
if (node.body && ts.isModuleBlock(node.body)) {
preProcessNamespaceBody(node.body, code);
}
duplicateExports(code, node);
}
fixModifiers(code, node);
@@ -989,6 +1038,18 @@ function preProcess({ sourceFile, isEntry, isJSON }) {
*/
// transformTypeOnlyImport(node);
// transformTypeOnlyExport(node);
// Handle export default with object/array literals
// These need to be converted to named declarations so Rollup can track references within them
if (ts.isExportAssignment(node) && !node.isExportEquals) {
if (ts.isObjectLiteralExpression(node.expression) || ts.isArrayLiteralExpression(node.expression)) {
if (!defaultExport) {
defaultExport = uniqName("export_default");
}
// Replace "export default" with "declare var export_default ="
code.overwrite(node.getStart(), node.expression.getStart(), `declare var ${defaultExport} = `);
continue;
}
}
if (!matchesModifier(node, ts.ModifierFlags.ExportDefault)) {
continue;
}
@@ -1160,26 +1221,31 @@ function fixModifiers(code, node) {
if (!ts.canHaveModifiers(node)) {
return;
}
let hasDeclare = false;
const needsDeclare = ts.isEnumDeclaration(node) ||
ts.isClassDeclaration(node) ||
ts.isFunctionDeclaration(node) ||
ts.isModuleDeclaration(node) ||
ts.isVariableStatement(node);
for (const mod of node.modifiers ?? []) {
switch (mod.kind) {
case ts.SyntaxKind.ExportKeyword: // fall through
case ts.SyntaxKind.DefaultKeyword:
// TODO: be careful about that `+ 1`
code.remove(mod.getStart(), mod.getEnd() + 1);
break;
case ts.SyntaxKind.DeclareKeyword:
hasDeclare = true;
const isTopLevel = node.parent.kind === ts.SyntaxKind.SourceFile;
if (isTopLevel) {
// For top-level statements, remove `export`/`default` and ensure `declare` exists
let hasDeclare = false;
const needsDeclare = ts.isEnumDeclaration(node) ||
ts.isClassDeclaration(node) ||
ts.isFunctionDeclaration(node) ||
ts.isModuleDeclaration(node) ||
ts.isVariableStatement(node);
for (const mod of node.modifiers ?? []) {
switch (mod.kind) {
case ts.SyntaxKind.ExportKeyword: // fall through
case ts.SyntaxKind.DefaultKeyword:
// TODO: be careful about that `+ 1`
code.remove(mod.getStart(), mod.getEnd() + 1);
break;
case ts.SyntaxKind.DeclareKeyword:
hasDeclare = true;
}
}
if (needsDeclare && !hasDeclare) {
code.appendRight(node.getStart(), "declare ");
}
}
if (needsDeclare && !hasDeclare) {
code.appendRight(node.getStart(), "declare ");
}
// For statements inside namespaces, preserve all modifiers (including export)
}
function duplicateExports(code, module) {
if (!module.body || !ts.isModuleBlock(module.body)) {
@@ -1232,7 +1298,6 @@ const IGNORE_TYPENODES = new Set([
]);
class DeclarationScope {
constructor({ id, range }) {
this.valueReferences = new Set();
/**
* As we walk the AST, we need to keep track of type variable bindings that
* shadow the outer identifiers. To achieve this, we keep a stack of scopes,
@@ -1263,12 +1328,6 @@ class DeclarationScope {
const name = id.getText();
this.scopes[this.scopes.length - 1]?.add(name);
}
markAsValue(name) {
this.valueReferences.add(name);
}
getValueReferences() {
return this.valueReferences;
}
pushReference(id) {
let name;
// We convert references from TS AST to ESTree
@@ -1480,12 +1539,6 @@ class DeclarationScope {
if (ts.isTypeQueryNode(node)) {
const reference = this.convertEntityName(node.exprName);
this.pushReference(reference);
if (reference.type === "Identifier") {
this.markAsValue(reference.name);
}
else if (reference.type === "MemberExpression" && reference.object.type === "Identifier") {
this.markAsValue(reference.object.name);
}
this.convertTypeArguments(node);
return;
}
@@ -1550,6 +1603,28 @@ class DeclarationScope {
}
continue;
}
if (ts.isImportDeclaration(stmt)) {
if (stmt.importClause) {
if (stmt.importClause.name) {
this.pushTypeVariable(stmt.importClause.name);
}
if (stmt.importClause.namedBindings) {
if (ts.isNamespaceImport(stmt.importClause.namedBindings)) {
this.pushTypeVariable(stmt.importClause.namedBindings.name);
}
else {
for (const el of stmt.importClause.namedBindings.elements) {
this.pushTypeVariable(el.name);
}
}
}
}
continue;
}
if (ts.isImportEqualsDeclaration(stmt)) {
this.pushTypeVariable(stmt.name);
continue;
}
if (ts.isExportDeclaration(stmt)) ;
else {
throw new UnsupportedSyntaxError(stmt, "namespace child (hoisting) not supported yet");
@@ -1590,6 +1665,18 @@ class DeclarationScope {
// noop
continue;
}
// handle imports in the walking pass
if (ts.isImportDeclaration(stmt)) {
// noop, already handled by hoisting
continue;
}
if (ts.isImportEqualsDeclaration(stmt)) {
if (ts.isEntityName(stmt.moduleReference)) {
this.pushReference(this.convertEntityName(stmt.moduleReference));
}
// we ignore `import foo = require(...)` as that is a module import
continue;
}
if (ts.isExportDeclaration(stmt)) {
if (stmt.exportClause) {
if (ts.isNamespaceExport(stmt.exportClause)) {
@@ -1616,22 +1703,15 @@ function convert({ sourceFile }) {
class Transformer {
constructor(sourceFile) {
this.sourceFile = sourceFile;
this.allTypeReferences = new Set();
this.declarations = new Map();
this.ast = createProgram(sourceFile);
for (const stmt of sourceFile.statements) {
this.convertStatement(stmt);
}
for (const scope of this.declarations.values()) {
for (const valueRef of scope.getValueReferences()) {
this.allTypeReferences.add(valueRef);
}
}
}
transform() {
return {
ast: this.ast,
valueReferences: this.allTypeReferences,
};
}
pushStatement(node) {
@@ -1771,6 +1851,36 @@ class Transformer {
}
const scope = this.createDeclaration(node, decl.name);
scope.convertTypeNode(decl.type);
// Track references in the initializer (e.g., for object literals)
if (decl.initializer) {
this.trackExpressionReferences(decl.initializer, scope);
}
}
}
// Helper to track identifier references in expressions
trackExpressionReferences(expr, scope) {
if (ts.isIdentifier(expr)) {
scope.pushIdentifierReference(expr);
}
else if (ts.isObjectLiteralExpression(expr)) {
for (const prop of expr.properties) {
if (ts.isShorthandPropertyAssignment(prop)) {
scope.pushIdentifierReference(prop.name);
}
else if (ts.isPropertyAssignment(prop)) {
this.trackExpressionReferences(prop.initializer, scope);
}
}
}
else if (ts.isArrayLiteralExpression(expr)) {
for (const elem of expr.elements) {
if (ts.isExpression(elem)) {
this.trackExpressionReferences(elem, scope);
}
}
}
else if (ts.isPropertyAccessExpression(expr)) {
this.trackExpressionReferences(expr.expression, scope);
}
}
convertExportDeclaration(node) {
@@ -1885,6 +1995,43 @@ class Transformer {
}
}
class RelativeModuleDeclarationFixer {
constructor(fileName, code, sourcemap, name) {
this.sourcemap = sourcemap;
this.DEBUG = !!process.env.DTS_EXPORTS_FIXER_DEBUG;
this.relativeModuleDeclarations = [];
this.source = parse(fileName, code.toString());
this.code = code;
this.name = name || "./index";
}
fix() {
this.analyze(this.source.statements);
for (const node of this.relativeModuleDeclarations) {
const start = node.getStart();
const end = node.getEnd();
const quote = node.name.kind === ts.SyntaxKind.StringLiteral && "singleQuote" in node.name && node.name.singleQuote
? "'"
: '"';
const code = `declare module ${quote}${this.name}${quote} ${node.body.getText()}`;
this.code.overwrite(start, end, code);
}
return {
code: this.code.toString(),
map: this.relativeModuleDeclarations.length && this.sourcemap ? this.code.generateMap() : null,
};
}
analyze(nodes) {
for (const node of nodes) {
if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body) && /^\.\.?\//.test(node.name.text)) {
if (this.DEBUG) {
console.log(`Found relative module declaration: ${node.name.text} in ${this.source.fileName}`);
}
this.relativeModuleDeclarations.push(node);
}
}
}
}
/**
* This is the *transform* part of `rollup-plugin-dts`.
*
@@ -1906,7 +2053,6 @@ class Transformer {
const transform = () => {
const allTypeReferences = new Map();
const allFileReferences = new Map();
const allValueReferences = new Map();
return {
name: "dts-transform",
options({ onLog, ...options }) {
@@ -1961,7 +2107,6 @@ const transform = () => {
code = preprocessed.code.toString();
sourceFile = parse(fileName, code);
const converted = convert({ sourceFile });
allValueReferences.set(sourceFile.fileName, converted.valueReferences);
if (process.env.DTS_DUMP_AST) {
console.log(fileName);
console.log(code);
@@ -1974,14 +2119,10 @@ const transform = () => {
const fixer = new NamespaceFixer(source);
const typeReferences = new Set();
const fileReferences = new Set();
const valueReferences = new Set();
for (const fileName of Object.keys(chunk.modules)) {
for (const ref of allTypeReferences.get(fileName.split("\\").join("/")) || []) {
typeReferences.add(ref);
}
for (const ref of allValueReferences.get(fileName.split("\\").join("/")) || []) {
valueReferences.add(ref);
}
for (const ref of allFileReferences.get(fileName.split("\\").join("/")) || []) {
if (ref.startsWith(".")) {
// Need absolute path of the target file here
@@ -2006,9 +2147,10 @@ const transform = () => {
if (!code) {
code += "\nexport { };";
}
const typeOnlyFixer = new TypeOnlyFixer(chunk.fileName, code, !!options.sourcemap);
typeOnlyFixer.setValueReferences(typeReferences);
return typeOnlyFixer.fix();
const typeOnlyFixer = new TypeOnlyFixer(chunk.fileName, code);
const typesFixed = typeOnlyFixer.fix();
const relativeModuleDeclarationFixed = new RelativeModuleDeclarationFixer(chunk.fileName, "magicCode" in typesFixed && typesFixed.magicCode ? typesFixed.magicCode : new MagicString(code), !!options.sourcemap, "./" + (options.file && options.file !== "-" ? path.basename(options.file, ".d.ts") : "index"));
return relativeModuleDeclarationFixed.fix();
},
};
};
@@ -2020,7 +2162,8 @@ function writeBlock(codes) {
}
const TS_EXTENSIONS = /\.([cm]ts|[tj]sx?)$/;
function getModule({ entries, programs, resolvedOptions: { compilerOptions, tsconfig } }, fileName, code) {
function getModule({ entries, programs, resolvedOptions }, fileName, code) {
const { compilerOptions, tsconfig } = resolvedOptions;
// Create any `ts.SourceFile` objects on-demand for ".d.ts" modules,
// but only when there are zero ".ts" entry points.
if (!programs.length && DTS_EXTENSIONS.test(fileName)) {
@@ -2053,6 +2196,18 @@ function getModule({ entries, programs, resolvedOptions: { compilerOptions, tsco
};
}
else if (ts.sys.fileExists(fileName)) {
// For .d.ts files from external libraries (node_modules), return just the code without creating a program.
// The programs.find() above returns false for external library files (line 52-54), causing
// existingProgram to be undefined even though the file exists in a program. Without this check,
// we would create a new program for each external .d.ts file, causing memory exhaustion
// with large packages like type-fest (170+ files → 170+ programs → OOM).
if (programs.length > 0 && DTS_EXTENSIONS.test(fileName)) {
// Apply this optimization when bundling external packages via includeExternal or respectExternal
const shouldBundleExternal = resolvedOptions.includeExternal.length > 0 || resolvedOptions.respectExternal;
if (shouldBundleExternal) {
return { code };
}
}
const newProgram = createProgram$1(fileName, compilerOptions, tsconfig);
programs.push(newProgram);
// we created hte program from this fileName, so the source file must exist :P

View File

@@ -1,6 +1,6 @@
{
"name": "rollup-plugin-dts",
"version": "6.2.3",
"version": "6.3.0",
"description": "A rollup plugin that will bundle up your .d.ts definition files.",
"keywords": [
"rollup-plugin",
@@ -52,11 +52,11 @@
"@types/babel__code-frame": "^7.0.6",
"@types/d3-drag": "^3.0.7",
"@types/estree": "1.0.8",
"@types/node": "^24.2.1",
"@types/react": "^19.1.9",
"@types/node": "^24.10.1",
"@types/react": "^19.2.6",
"c8": "^10.1.3",
"rollup": "4.46.2",
"typescript": "5.9.2"
"rollup": "4.53.3",
"typescript": "5.9.3"
},
"peerDependencies": {
"rollup": "^3.29.4 || ^4",
@@ -66,6 +66,6 @@
"@babel/code-frame": "^7.27.1"
},
"dependencies": {
"magic-string": "^0.30.17"
"magic-string": "^0.30.21"
}
}