UNPKG

rollup-plugin-dts

Version:

A rollup plugin that will bundle up your .d.ts definition files.

3,319 lines 143 kB
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var path = require('node:path');
var node_module = require('node:module');
var remapping = require('@jridgewell/remapping');
var MagicString = require('magic-string');
var fs = require('node:fs/promises');
var sourcemapCodec = require('@jridgewell/sourcemap-codec');
var convert$1 = require('convert-source-map');

var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
function _interopNamespaceDefault(e) {
  var n = Object.create(null);
  if (e) {
    Object.keys(e).forEach(function (k) {
      if (k !== 'default') {
        var d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: function () { return e[k]; }
        });
      }
    });
  }
  n.default = e;
  return Object.freeze(n);
}

var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);

// This module replaces `import ts from "typescript"` in the bundled output
// (see the typescript-shim plugin in rollup.config.ts).
//
// TypeScript 7 (the native compiler) does not ship a compiler API — it only
// exports `version`. Until the new API arrives in TypeScript 7.1+, users on
// typescript@7 need the `@typescript/typescript6` compatibility package,
// which provides the TypeScript 6.0 API.
//
// `require` is used instead of static imports so that a missing or API-less
// `typescript` package can be detected and recovered from at runtime.

const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('rollup-plugin-dts.cjs', document.baseURI).href)));

function loadTypeScript() {
  let ts;
  try {
    ts = require$1("typescript");
  } catch {
    // not installed, or an ESM-only entry point that this Node version cannot `require`
  }
  if (ts && typeof ts.createProgram === "function") {
    return ts;
  }
  try {
    return require$1("@typescript/typescript6");
  } catch {
    if (ts) {
      throw new Error(
        `rollup-plugin-dts requires the TypeScript compiler API. The installed typescript@${ts.version} does not provide it, ` +
          "and the `@typescript/typescript6` fallback is not installed.\n" +
          "TypeScript 7 does not ship a compiler API, so please additionally install the compatibility package:\n" +
          "  npm install -D @typescript/typescript6",
      );
    }
    throw new Error(
      "rollup-plugin-dts requires the TypeScript compiler API, but the `typescript` package could not be loaded.\n" +
        "Please install typescript 4.5 - 6.x, or typescript 7 together with the `@typescript/typescript6` compatibility package:\n" +
        "  npm install -D typescript",
    );
  }
}

var ts = loadTypeScript();

function resolveDefaultOptions(options) {
    return {
        ...options,
        compilerOptions: options.compilerOptions ?? {},
        respectExternal: options.respectExternal ?? false,
        includeExternal: options.includeExternal ?? [],
        sourcemap: options.sourcemap ?? false,
    };
}

const DTS_EXTENSIONS = /\.d\.(c|m)?tsx?$/;
const JSON_EXTENSIONS = /\.json$/;
const SUPPORTED_EXTENSIONS = /((\.d)?\.(c|m)?(t|j)sx?|\.json)$/;
function trimExtension(path) {
    return path.replace(SUPPORTED_EXTENSIONS, "");
}
function getDeclarationId(path) {
    return path.replace(SUPPORTED_EXTENSIONS, ".d.ts");
}
function parse(fileName, code) {
    return ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true);
}

const formatHost = {
    getCurrentDirectory: () => ts.sys.getCurrentDirectory(),
    getNewLine: () => ts.sys.newLine,
    getCanonicalFileName: ts.sys.useCaseSensitiveFileNames ? (f) => f : (f) => f.toLowerCase(),
};
const DEFAULT_OPTIONS = {
    // Ensure ".d.ts" modules are generated
    declaration: true,
    // Skip ".js" generation
    noEmit: false,
    emitDeclarationOnly: true,
    // Skip code generation when error occurs
    noEmitOnError: true,
    // Avoid extra work
    checkJs: false,
    declarationMap: false,
    skipLibCheck: true,
    // Ensure TS2742 errors are visible
    preserveSymlinks: true,
    // Ensure we can parse the latest code
    target: ts.ScriptTarget.ESNext,
    // Allows importing `*.json`
    resolveJsonModule: true,
};
const configByPath = new Map();
const logCache = (...args) => (process.env.DTS_LOG_CACHE ? console.log("[cache]", ...args) : null);
/**
 * Caches the config for every path between two given paths.
 *
 * It starts from the first path and walks up the directory tree until it reaches the second path.
 */
function cacheConfig([fromPath, toPath], config) {
    logCache(fromPath);
    configByPath.set(fromPath, config);
    while (fromPath !== toPath &&
        // make sure we're not stuck in an infinite loop
        fromPath !== path__namespace.dirname(fromPath)) {
        fromPath = path__namespace.dirname(fromPath);
        logCache("up", fromPath);
        if (configByPath.has(fromPath))
            return logCache("has", fromPath);
        configByPath.set(fromPath, config);
    }
}
function getCompilerOptions(input, overrideOptions, overrideConfigPath, enableDeclarationMap) {
    const compilerOptions = {
        ...DEFAULT_OPTIONS,
        ...overrideOptions,
        // When plugin's sourcemap option is explicitly true, force declarationMap
        // regardless of user's compilerOptions setting.
        // When sourcemap is false/undefined, respect user's compilerOptions.declarationMap.
        ...(enableDeclarationMap === true && { declarationMap: true }),
    };
    let dirName = path__namespace.dirname(input);
    let dtsFiles = [];
    // if a custom config is provided we'll use that as the cache key since it will always be used
    const cacheKey = overrideConfigPath || dirName;
    if (!configByPath.has(cacheKey)) {
        logCache("miss", cacheKey);
        const configPath = overrideConfigPath
            ? path__namespace.resolve(process.cwd(), overrideConfigPath)
            : ts.findConfigFile(dirName, ts.sys.fileExists);
        if (!configPath) {
            return { dtsFiles, dirName, compilerOptions };
        }
        const inputDirName = dirName;
        dirName = path__namespace.dirname(configPath);
        const { config, error } = ts.readConfigFile(configPath, ts.sys.readFile);
        if (error) {
            console.error(ts.formatDiagnostic(error, formatHost));
            return { dtsFiles, dirName, compilerOptions };
        }
        logCache("tsconfig", config);
        const configContents = ts.parseJsonConfigFileContent(config, ts.sys, dirName);
        if (overrideConfigPath) {
            // if a custom config is provided, we always only use that one
            cacheConfig([overrideConfigPath, overrideConfigPath], configContents);
        }
        else {
            // cache the config for all directories between input and resolved config path
            cacheConfig([inputDirName, dirName], configContents);
        }
    }
    else {
        logCache("HIT", cacheKey);
    }
    const { fileNames, options, errors } = configByPath.get(cacheKey);
    dtsFiles = fileNames.filter((name) => DTS_EXTENSIONS.test(name));
    if (errors.length) {
        console.error(ts.formatDiagnostics(errors, formatHost));
        return { dtsFiles, dirName, compilerOptions };
    }
    return {
        dtsFiles,
        dirName,
        compilerOptions: {
            ...options,
            ...compilerOptions,
        },
    };
}
function createProgram$1(fileName, overrideOptions, tsconfig, enableDeclarationMap) {
    const { dtsFiles, compilerOptions } = getCompilerOptions(fileName, overrideOptions, tsconfig, enableDeclarationMap);
    return ts.createProgram([fileName].concat(Array.from(dtsFiles)), compilerOptions, ts.createCompilerHost(compilerOptions, true));
}
function createPrograms(input, overrideOptions, tsconfig, enableDeclarationMap) {
    const programs = [];
    const dtsFiles = new Set();
    let inputs = [];
    let dirName = "";
    let compilerOptions = {};
    for (let main of input) {
        if (DTS_EXTENSIONS.test(main)) {
            continue;
        }
        main = path__namespace.resolve(main);
        const options = getCompilerOptions(main, overrideOptions, tsconfig, enableDeclarationMap);
        options.dtsFiles.forEach(dtsFiles.add, dtsFiles);
        if (!inputs.length) {
            inputs.push(main);
            ({ dirName, compilerOptions } = options);
            continue;
        }
        if (options.dirName === dirName) {
            inputs.push(main);
        }
        else {
            const host = ts.createCompilerHost(compilerOptions, true);
            const program = ts.createProgram(inputs.concat(Array.from(dtsFiles)), compilerOptions, host);
            programs.push(program);
            inputs = [main];
            ({ dirName, compilerOptions } = options);
        }
    }
    if (inputs.length) {
        const host = ts.createCompilerHost(compilerOptions, true);
        const program = ts.createProgram(inputs.concat(Array.from(dtsFiles)), compilerOptions, host);
        programs.push(program);
    }
    return programs;
}

function getCodeFrame() {
    let codeFrameColumns = undefined;
    try {
        ({ codeFrameColumns } = require("@babel/code-frame"));
        return codeFrameColumns;
    }
    catch {
        try {
            const esmRequire = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('rollup-plugin-dts.cjs', document.baseURI).href)));
            ({ codeFrameColumns } = esmRequire("@babel/code-frame"));
            return codeFrameColumns;
        }
        catch { }
    }
    return undefined;
}
function getLocation(node) {
    const sourceFile = node.getSourceFile();
    const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
    const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
    return {
        start: { line: start.line + 1, column: start.character + 1 },
        end: { line: end.line + 1, column: end.character + 1 },
    };
}
function frameNode(node) {
    const codeFrame = getCodeFrame();
    const sourceFile = node.getSourceFile();
    const code = sourceFile.getFullText();
    const location = getLocation(node);
    if (codeFrame) {
        return ("\n" +
            codeFrame(code, location, {
                highlightCode: true,
            }));
    }
    else {
        return `\n${location.start.line}:${location.start.column}: \`${node.getFullText().trim()}\``;
    }
}
class UnsupportedSyntaxError extends Error {
    constructor(node, message = "Syntax not yet supported") {
        super(`${message}\n${frameNode(node)}`);
    }
}

class NamespaceFixer {
    sourceFile;
    constructor(sourceFile) {
        this.sourceFile = sourceFile;
    }
    findNamespaces() {
        const namespaces = [];
        const items = {};
        for (const node of this.sourceFile.statements) {
            const location = {
                start: node.getStart(),
                end: node.getEnd(),
            };
            // Well, this is a big hack:
            // For some global `namespace` and `module` declarations, we generate
            // some fake IIFE code, so rollup can correctly scan its scope.
            // However, rollup will then insert bogus semicolons,
            // these `EmptyStatement`s, which are a syntax error and we want to
            // remove them. Well, we do that here…
            if (ts.isEmptyStatement(node)) {
                namespaces.unshift({
                    name: "",
                    exports: [],
                    location,
                });
                continue;
            }
            // When generating multiple chunks, rollup links those via import
            // statements, obviously. But rollup uses full filenames with typescript extension,
            // which typescript does not like. So make sure to change those to javascript extension here.
            // `.d.ts` -> `.js`
            // `.d.cts` -> `.cjs`
            // `.d.mts` -> `.mjs`
            if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
                node.moduleSpecifier &&
                ts.isStringLiteral(node.moduleSpecifier)) {
                const { text } = node.moduleSpecifier;
                if (text.startsWith(".") && (text.endsWith(".d.ts") || text.endsWith(".d.cts") || text.endsWith(".d.mts"))) {
                    const start = node.moduleSpecifier.getStart() + 1; // +1 to account for the quote
                    const end = node.moduleSpecifier.getEnd() - 1; // -1 to account for the quote
                    namespaces.unshift({
                        name: "",
                        exports: [],
                        location: {
                            start,
                            end,
                        },
                        textBeforeCodeAfter: text
                            .replace(/\.d\.ts$/, ".js")
                            .replace(/\.d\.cts$/, ".cjs")
                            .replace(/\.d\.mts$/, ".mjs"),
                    });
                }
            }
            // Remove redundant `{ Foo as Foo }` exports from a namespace which we
            // added in pre-processing to fix up broken renaming
            if (ts.isModuleDeclaration(node) && node.body) {
                let body = node.body;
                // Traverse dotted namespace chain (e.g. `namespace A.B.C {}`)
                while (ts.isModuleDeclaration(body) && body.body) {
                    body = body.body;
                }
                if (!ts.isModuleBlock(body)) {
                    continue;
                }
                for (const stmt of body.statements) {
                    if (ts.isExportDeclaration(stmt) && stmt.exportClause) {
                        if (ts.isNamespaceExport(stmt.exportClause)) {
                            continue;
                        }
                        for (const decl of stmt.exportClause.elements) {
                            if (decl.propertyName && decl.propertyName.getText() == decl.name.getText()) {
                                namespaces.unshift({
                                    name: "",
                                    exports: [],
                                    location: {
                                        start: decl.propertyName.getEnd(),
                                        end: decl.name.getEnd(),
                                    },
                                });
                            }
                        }
                    }
                }
            }
            if (ts.isClassDeclaration(node)) {
                items[node.name.getText()] = { type: "class", generics: node.typeParameters };
            }
            else if (ts.isFunctionDeclaration(node)) {
                // a function has generics, but these don’t need to be specified explicitly,
                // since functions are treated as values.
                items[node.name.getText()] = { type: "function" };
            }
            else if (ts.isInterfaceDeclaration(node)) {
                items[node.name.getText()] = { type: "interface", generics: node.typeParameters };
            }
            else if (ts.isTypeAliasDeclaration(node)) {
                items[node.name.getText()] = { type: "type", generics: node.typeParameters };
            }
            else if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name)) {
                items[node.name.getText()] = { type: "namespace" };
            }
            else if (ts.isEnumDeclaration(node)) {
                items[node.name.getText()] = { type: "enum" };
            }
            if (!ts.isVariableStatement(node)) {
                continue;
            }
            const { declarations } = node.declarationList;
            if (declarations.length !== 1) {
                continue;
            }
            const decl = declarations[0];
            const name = decl.name.getText();
            if (!decl.initializer || !ts.isCallExpression(decl.initializer)) {
                items[name] = { type: "var" };
                continue;
            }
            const obj = decl.initializer.arguments[0];
            if (!decl.initializer.expression.getFullText().includes("/*#__PURE__*/Object.freeze") ||
                !ts.isObjectLiteralExpression(obj)) {
                continue;
            }
            const exports = [];
            for (const prop of obj.properties) {
                if (!ts.isPropertyAssignment(prop) ||
                    !(ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) ||
                    (prop.name.text !== "__proto__" && !ts.isIdentifier(prop.initializer))) {
                    throw new UnsupportedSyntaxError(prop, "Expected a property assignment");
                }
                if (prop.name.text === "__proto__") {
                    continue;
                }
                exports.push({
                    exportedName: prop.name.text,
                    localName: prop.initializer.getText(),
                });
            }
            // sort in reverse order, since we will do string manipulation
            namespaces.unshift({
                name,
                exports,
                location,
            });
        }
        return { namespaces, itemTypes: items };
    }
    fix() {
        let code = this.sourceFile.getFullText();
        const { namespaces, itemTypes } = this.findNamespaces();
        for (const ns of namespaces) {
            const codeAfter = code.slice(ns.location.end);
            code = code.slice(0, ns.location.start);
            for (const { exportedName, localName } of ns.exports) {
                if (exportedName === localName) {
                    const { type, generics } = itemTypes[localName] || {};
                    if (type === "interface" || type === "type") {
                        // an interface is just a type
                        const typeParams = renderTypeParams(generics);
                        code += `type ${ns.name}_${exportedName}${typeParams.in} = ${localName}${typeParams.out};\n`;
                    }
                    else if (type === "enum" || type === "class") {
                        // enums and classes are both types and values
                        const typeParams = renderTypeParams(generics);
                        code += `type ${ns.name}_${exportedName}${typeParams.in} = ${localName}${typeParams.out};\n`;
                        code += `declare const ${ns.name}_${exportedName}: typeof ${localName};\n`;
                    }
                    else if (type === "namespace") {
                        // namespaces may contain both types and values
                        code += `import ${ns.name}_${exportedName} = ${localName};\n`;
                    }
                    else {
                        // functions and vars are just values
                        code += `declare const ${ns.name}_${exportedName}: typeof ${localName};\n`;
                    }
                }
            }
            if (ns.name) {
                code += `declare namespace ${ns.name} {\n`;
                code += `  export {\n`;
                for (const { exportedName, localName } of ns.exports) {
                    if (exportedName === localName) {
                        code += `    ${ns.name}_${exportedName} as ${exportedName},\n`;
                    }
                    else {
                        code += `    ${localName} as ${exportedName},\n`;
                    }
                }
                code += `  };\n`;
                code += `}`;
            }
            code += ns.textBeforeCodeAfter ?? "";
            code += codeAfter;
        }
        return code;
    }
}
function renderTypeParams(typeParameters) {
    if (!typeParameters || !typeParameters.length) {
        return { in: "", out: "" };
    }
    return {
        in: `<${typeParameters.map((param) => param.getText()).join(", ")}>`,
        out: `<${typeParameters.map((param) => param.name.getText()).join(", ")}>`,
    };
}

let IDs = 1;
/**
 * Create a new `Program` for the given `node`:
 */
function createProgram(node) {
    return withStartEnd({
        type: "Program",
        sourceType: "module",
        body: [],
    }, { start: node.getFullStart(), end: node.getEnd() });
}
/**
 * Creates a reference to `id`:
 * `_ = ${id}`
 */
function createReference(id) {
    const ident = {
        type: "Identifier",
        name: String(IDs++),
    };
    return {
        ident,
        expr: {
            type: "AssignmentPattern",
            left: ident,
            right: id,
        },
    };
}
function createIdentifier(node) {
    return withStartEnd({
        type: "Identifier",
        name: node.text,
    }, node);
}
/**
 * Create a new Scope which is always included
 * `(function (_ = MARKER) {})()`
 */
function createIIFE(range) {
    const fn = withStartEnd({
        type: "FunctionExpression",
        id: null,
        params: [],
        body: { type: "BlockStatement", body: [] },
    }, range);
    const iife = withStartEnd({
        type: "ExpressionStatement",
        expression: {
            type: "CallExpression",
            callee: { type: "Identifier", name: String(IDs++) },
            arguments: [fn],
            optional: false,
        },
    }, range);
    return { fn, iife };
}
/**
 * Create a dummy ReturnStatement with an ArrayExpression:
 * `return [];`
 */
function createReturn() {
    const expr = {
        type: "ArrayExpression",
        elements: [],
    };
    return {
        expr,
        stmt: {
            type: "ReturnStatement",
            argument: expr,
        },
    };
}
/**
 * Create a new Declaration and Scope for `id`:
 * `function ${id}(_ = MARKER) {}`
 */
function createDeclaration(id, range) {
    return withStartEnd({
        type: "FunctionDeclaration",
        id: withStartEnd({
            type: "Identifier",
            name: ts.idText(id),
        }, id),
        params: [],
        body: { type: "BlockStatement", body: [] },
    }, range);
}
function convertExpression(node) {
    if (ts.isLiteralExpression(node)) {
        return { type: "Literal", value: node.text };
    }
    if (ts.isPropertyAccessExpression(node)) {
        if (ts.isPrivateIdentifier(node.name)) {
            throw new UnsupportedSyntaxError(node.name);
        }
        return withStartEnd({
            type: "MemberExpression",
            computed: false,
            optional: false,
            object: convertExpression(node.expression),
            property: convertExpression(node.name),
        }, {
            start: node.expression.getStart(),
            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);
    }
    else if (node.kind == ts.SyntaxKind.NullKeyword) {
        return { type: "Literal", value: null };
    }
    else {
        throw new UnsupportedSyntaxError(node);
    }
}
function withStartEnd(esNode, nodeOrRange) {
    const range = "start" in nodeOrRange ? nodeOrRange : { start: nodeOrRange.getStart(), end: nodeOrRange.getEnd() };
    return Object.assign(esNode, range);
}
function matchesModifier(node, flags) {
    const nodeFlags = ts.getCombinedModifierFlags(node);
    return (nodeFlags & flags) === flags;
}

class LanguageService {
    fileName = "index.d.ts";
    service;
    constructor(code) {
        const serviceHost = {
            getCompilationSettings: () => ({
                noEmit: true,
                noResolve: true,
                skipLibCheck: true,
                declaration: false,
                checkJs: false,
                declarationMap: false,
                target: ts.ScriptTarget.ESNext,
            }),
            getScriptFileNames: () => [this.fileName],
            getScriptVersion: () => "1",
            getScriptSnapshot: (fileName) => fileName === this.fileName
                ? ts.ScriptSnapshot.fromString(code)
                : undefined,
            getCurrentDirectory: () => "",
            getDefaultLibFileName: () => "",
            fileExists: (fileName) => fileName === this.fileName,
            readFile: (fileName) => fileName === this.fileName ? code : undefined,
        };
        this.service = ts.createLanguageService(serviceHost, ts.createDocumentRegistry(undefined, ""), ts.LanguageServiceMode.PartialSemantic);
    }
    findReferenceCount(node) {
        const referencedSymbols = this.service.findReferences(this.fileName, node.getStart());
        if (!referencedSymbols?.length) {
            return 0;
        }
        return referencedSymbols.reduce((total, symbol) => total + symbol.references.length, 0);
    }
}

class TypeOnlyFixer {
    rawCode;
    code;
    source;
    service;
    types = new Set();
    values = new Set();
    typeHints = new Map();
    reExportTypeHints = new Map();
    importNodes = [];
    exportNodes = [];
    constructor(fileName, rawCode) {
        this.rawCode = rawCode;
        this.source = parse(fileName, rawCode);
        this.code = new MagicString(rawCode);
    }
    fix() {
        this.analyze(this.source.statements);
        if (this.typeHints.size || this.reExportTypeHints.size) {
            this.service = new LanguageService(this.rawCode);
            this.importNodes.forEach((node) => this.fixTypeOnlyImport(node));
        }
        if (this.types.size) {
            this.exportNodes.forEach((node) => this.fixTypeOnlyExport(node));
        }
        return this.types.size
            ? {
                magicCode: this.code,
            }
            : {
                code: this.rawCode,
                map: null,
            };
    }
    fixTypeOnlyImport(node) {
        let hasRemoved = false;
        const typeImports = [];
        const valueImports = [];
        const specifier = node.moduleSpecifier.getText();
        const nameNode = node.importClause.name;
        const namedBindings = node.importClause.namedBindings;
        if (nameNode) {
            const name = nameNode.text;
            if (this.isTypeOnly(name)) {
                if (this.isUselessImport(nameNode)) {
                    hasRemoved = true;
                }
                else {
                    // import A from 'a';    ->   import type A from 'a';
                    typeImports.push(`import type ${name} from ${specifier};`);
                }
            }
            else {
                valueImports.push(`import ${name} from ${specifier};`);
            }
        }
        if (namedBindings && ts.isNamespaceImport(namedBindings)) {
            const name = namedBindings.name.text;
            if (this.isTypeOnly(name)) {
                if (this.isUselessImport(namedBindings.name)) {
                    hasRemoved = true;
                }
                else {
                    // import * as A from 'a';   ->   import type * as A from 'a';
                    typeImports.push(`import type * as ${name} from ${specifier};`);
                }
            }
            else {
                valueImports.push(`import * as ${name} from ${specifier};`);
            }
        }
        if (namedBindings && ts.isNamedImports(namedBindings)) {
            const typeNames = [];
            const valueNames = [];
            for (const element of namedBindings.elements) {
                if (this.isTypeOnly(element.name.text)) {
                    if (this.isUselessImport(element.name)) {
                        hasRemoved = true;
                    }
                    else {
                        // import { A as B } from 'a';   ->   import type { A as B } from 'a';
                        typeNames.push(element.getText());
                    }
                }
                else {
                    valueNames.push(element.getText());
                }
            }
            if (typeNames.length) {
                typeImports.push(`import type { ${typeNames.join(', ')} } from ${specifier};`);
            }
            if (valueNames.length) {
                valueImports.push(`import { ${valueNames.join(', ')} } from ${specifier};`);
            }
        }
        if (typeImports.length || hasRemoved) {
            this.code.overwrite(node.getStart(), node.getEnd(), [...valueImports, ...typeImports].join(`\n${getNodeIndent(node)}`));
        }
    }
    fixTypeOnlyExport(node) {
        const typeExports = [];
        const valueExports = [];
        const specifier = node.moduleSpecifier?.getText();
        if (ts.isNamespaceExport(node.exportClause)) {
            const name = node.exportClause.name.text;
            if (this.isReExportTypeOnly(name)) {
                // export * as A from 'a';   ->   export type * as A from 'a';
                typeExports.push(`export type * as ${name} from ${specifier};`);
            }
            else {
                valueExports.push(`export * as ${name} from ${specifier};`);
            }
        }
        if (ts.isNamedExports(node.exportClause)) {
            const typeNames = [];
            const valueNames = [];
            for (const element of node.exportClause.elements) {
                const name = element.propertyName?.text || element.name.text;
                const isType = node.moduleSpecifier
                    ? this.isReExportTypeOnly(element.name.text)
                    : this.isTypeOnly(name);
                if (isType) {
                    // export { A as B } from 'a';   ->   export type { A as B } from 'a';
                    typeNames.push(getExportSpecifierBinding(element));
                }
                else {
                    // export { A as B };   ->   export { A as B };
                    valueNames.push(element.getText());
                }
            }
            if (typeNames.length) {
                typeExports.push(`export type { ${typeNames.join(', ')} }${specifier ? ` from ${specifier}` : ''};`);
            }
            if (valueNames.length) {
                valueExports.push(`export { ${valueNames.join(', ')} }${specifier ? ` from ${specifier}` : ''};`);
            }
        }
        if (typeExports.length) {
            this.code.overwrite(node.getStart(), node.getEnd(), [...valueExports, ...typeExports].join(`\n${getNodeIndent(node)}`));
        }
    }
    analyze(nodes) {
        for (const node of nodes) {
            if (ts.isImportDeclaration(node) && node.importClause) {
                this.importNodes.push(node);
                continue;
            }
            if (ts.isExportDeclaration(node) && node.exportClause) {
                this.exportNodes.push(node);
                continue;
            }
            if (ts.isInterfaceDeclaration(node)) {
                this.types.add(node.name.text);
                continue;
            }
            if (ts.isTypeAliasDeclaration(node)) {
                const alias = node.name.text;
                this.types.add(alias);
                /**
                 * TODO: type-only import/export fixer.
                 * Temporarily disable the type-only import/export transformation,
                 * because the current implementation is unsafe.
                 *
                 * Issue: https://github.com/Swatinem/rollup-plugin-dts/issues/340
                 */
                // if (ts.isTypeReferenceNode(node.type) && ts.isIdentifier(node.type.typeName)) {
                //   const reference = node.type.typeName.text;
                //   const aliasHint = parseTypeOnlyName(alias);
                //   if(aliasHint.isTypeOnly) {
                //     this.DEBUG && console.log(`${reference} is a type (from type-only hint)`);
                //     this.types.add(reference);
                //     this.typeHints.set(reference, (this.typeHints.get(reference) || 0) + 1);
                //     if(aliasHint.isReExport) {
                //       const reExportName = alias.split(TYPE_ONLY_RE_EXPORT)[0]!
                //       this.DEBUG && console.log(`${reExportName} is a type (from type-only re-export hint)`);
                //       this.reExportTypeHints.set(reExportName, (this.reExportTypeHints.get(reExportName) || 0) + 1);
                //     }
                //     this.code.remove(node.getStart(), node.getEnd());
                //   }
                // }
                continue;
            }
            if (ts.isEnumDeclaration(node) ||
                ts.isFunctionDeclaration(node) ||
                ts.isClassDeclaration(node) ||
                ts.isVariableStatement(node)) {
                if (ts.isVariableStatement(node)) {
                    for (const declaration of node.declarationList.declarations) {
                        if (ts.isIdentifier(declaration.name)) {
                            this.values.add(declaration.name.text);
                        }
                    }
                }
                else {
                    if (node.name) {
                        this.values.add(node.name.text);
                    }
                }
                continue;
            }
            if (ts.isModuleBlock(node)) {
                this.analyze(node.statements);
                continue;
            }
            if (ts.isModuleDeclaration(node)) {
                if (node.name && ts.isIdentifier(node.name)) {
                    this.values.add(node.name.text);
                }
                this.analyze(node.getChildren());
                continue;
            }
        }
    }
    // The type-hint statements may lead to redundant import statements.
    // After type-hint statements been removed,
    // it is better to also remove these redundant import statements as well.
    // Of course, this is not necessary since it won't cause issues,
    // but it can make the output bundles cleaner :)
    isUselessImport(node) {
        // `referenceCount` contains it self.
        const referenceCount = this.service.findReferenceCount(node);
        const typeHintCount = this.typeHints.get(node.text);
        return (typeHintCount && typeHintCount + 1 >= referenceCount);
    }
    isTypeOnly(name) {
        return this.typeHints.has(name)
            || (this.types.has(name) && !this.values.has(name));
    }
    isReExportTypeOnly(name) {
        return this.reExportTypeHints.has(name);
    }
}
function getNodeIndent(node) {
    const match = node.getFullText().match(/^(?:\n*)([ ]*)/);
    return ' '.repeat(match?.[1]?.length || 0);
}
function getExportSpecifierBinding(element) {
    if (element.propertyName) {
        return `${element.propertyName.getText()} as ${element.name.getText()}`;
    }
    return element.name.getText();
}

const RESOLVED_MODULE_PREFIX = "dts-resolved:";
const RESOLVED_MODULE_COMMENT = new RegExp(`\\/\\*${RESOLVED_MODULE_PREFIX}(.+?)\\*\\/`);
/** Encode a resolved absolute path as a marker comment for later chunk resolution. */
function encodeResolvedModule(absolutePath) {
    return `/*${RESOLVED_MODULE_PREFIX}${absolutePath}*/`;
}
/** Decode a resolved module marker comment from text. Returns the absolute path, or null if not found. */
function decodeResolvedModule(text) {
    return text.match(RESOLVED_MODULE_COMMENT)?.[1] ?? null;
}
/** Strip the resolved module marker comment. */
function stripResolvedModuleComment(text) {
    return text.replace(RESOLVED_MODULE_COMMENT, "");
}
/** Normalize paths for comparison (handle Windows backslashes) */
function normalizePath(p) {
    return p.split("\\").join("/");
}
/** Collect the names declared at the top level of an augmentation body. */
function getAugmentedNames(body) {
    const names = [];
    for (const statement of body.statements) {
        if ((ts.isInterfaceDeclaration(statement) ||
            ts.isClassDeclaration(statement) ||
            ts.isFunctionDeclaration(statement) ||
            ts.isEnumDeclaration(statement) ||
            ts.isTypeAliasDeclaration(statement)) &&
            statement.name) {
            names.push(statement.name.text);
        }
        else if (ts.isVariableStatement(statement)) {
            for (const declaration of statement.declarationList.declarations) {
                if (ts.isIdentifier(declaration.name)) {
                    names.push(declaration.name.text);
                }
            }
        }
    }
    return names;
}
/**
 * Rewrites relative `declare module './x'` specifiers in bundled output so they point
 * at the chunk containing the target module, keeping module augmentations reachable
 * for consumers.
 *
 * preprocess resolves each relative specifier lexically against its source file's
 * directory and stamps it with a `dts-resolved:` marker comment that rides through
 * bundling attached to its declaration; this fixer decodes the marker, locates the
 * target chunk via Rollup's chunk metadata, and overwrites the specifier with a
 * chunk-relative path.
 *
 * The rewrite is skipped (original specifier kept + warning) when:
 * - the target module is not part of any output chunk (orphaned files, asset-style
 *   declarations) — a dangling specifier stays inert, while rewriting it would merge
 *   the augmentation into the wrong module;
 * - the target chunk does not export every augmented name unchanged (aliased
 *   re-exports, `Foo` → `Foo$1` deconfliction) — augmentation matches by exported
 *   name, so retargeting the specifier alone would desynchronize the two.
 *
 * Scope: only `./`/`../` specifiers are handled. Bare package names must be preserved
 * verbatim, since consumers resolve them by name. tsconfig-paths aliases
 * (`declare module "@/foo"`) are a known limitation — they cannot be resolved
 * lexically. Supporting them would mean: in preprocess, for specifiers matching a
 * `compilerOptions.paths` pattern (never for other bare names), resolve with
 * `ts.resolveModuleName` against the declaring file's compiler options (synchronous,
 * unlike the plugin context's `resolve`), skip `isExternalLibraryImport` results, and
 * stamp the resolved file name; the extension probe below already bridges the
 * remaining `.d.ts`-versus-`.ts` module id gap.
 */
class ModuleDeclarationFixer {
    code;
    sourcemap;
    source;
    chunkFileName;
    moduleToChunk;
    chunks;
    warn;
    constructor(chunk, code, sourcemap, moduleToChunk, chunks, warn) {
        this.code = code;
        this.sourcemap = sourcemap;
        // Parse the MagicString's original text, not toString(): the code may already
        // carry TypeOnlyFixer edits, but overwrite() coordinates always refer to the
        // original string, so node positions must come from that same text
        this.source = parse(chunk.fileName, code.original);
        this.chunkFileName = chunk.fileName;
        this.moduleToChunk = moduleToChunk;
        this.chunks = chunks;
        this.warn = warn;
    }
    fix() {
        let modified = false;
        for (const node of this.source.statements) {
            if (!ts.isModuleDeclaration(node) || !node.body || !ts.isModuleBlock(node.body)) {
                continue;
            }
            const sourceText = this.source.getFullText();
            const textBetween = sourceText.slice(node.name.getEnd(), node.body.getStart());
            const absolutePath = decodeResolvedModule(textBetween);
            if (!absolutePath) {
                continue;
            }
            const target = this.findTargetChunk(absolutePath);
            let specifier = node.name.getText();
            if (target === null) {
                // Keep the original specifier; consumers resolve it relative to the emitted
                // chunk, where it will typically dangle (a no-op augmentation) rather than
                // merge into the wrong module the way a current-chunk rewrite would
                this.warn(`declare module ${specifier} (${absolutePath}) could not be resolved to any output chunk, keeping the original specifier`);
            }
            else if (!this.augmentationApplies(getAugmentedNames(node.body), target.moduleId, target.chunkFileName)) {
                // Module augmentation matches by the target module's exported names; when the
                // chunk renamed, dropped, or ambiguously exports an augmented name, retargeting
                // the specifier would merge the augmentation into the wrong declaration
                this.warn(`declare module ${specifier} (${absolutePath}) augments names that the target chunk does not export unchanged, keeping the original specifier`);
            }
            else {
                const quote = node.name.kind === ts.SyntaxKind.StringLiteral && "singleQuote" in node.name && node.name.singleQuote
                    ? "'"
                    : '"';
                specifier = `${quote}${this.formatChunkReference(target.chunkFileName, target.jsExt)}${quote}`;
            }
            const cleanedBetween = stripResolvedModuleComment(textBetween);
            this.code.overwrite(node.name.getStart(), node.body.getStart(), specifier + cleanedBetween);
            modified = true;
        }
        return {
            code: this.code.toString(),
            map: modified && this.sourcemap ? this.code.generateMap() : null,
        };
    }
    /**
     * Find the output chunk containing the module at an absolute path.
     * Returns null when the module is not part of any output chunk.
     */
    findTargetChunk(absolutePath) {
        // Detect JS extension from the resolved path (present when the source uses ESM-style specifiers)
        const jsExtMatch = absolutePath.match(/\.[cm]?js$/);
        const basePath = jsExtMatch ? absolutePath.slice(0, -jsExtMatch[0].length) : absolutePath;
        // Try all file extensions that could appear as module IDs in Rollup's chunk metadata,
        // probing both the path itself and a directory index
        const extensions = ["", ".d.ts", ".d.mts", ".d.cts", ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
        const possiblePaths = [];
        for (const base of [basePath, `${basePath}/index`]) {
            for (const ext of extensions) {
                possiblePaths.push(base + ext);
            }
        }
        // Find which chunk contains this module
        for (const possiblePath of possiblePaths) {
            const moduleId = normalizePath(possiblePath);
            const chunkFileName = this.moduleToChunk.get(moduleId);
            if (chunkFileName) {
                return { chunkFileName, moduleId, jsExt: jsExtMatch?.[0] };
            }
        }
        return null;
    }
    /**
     * Check that every augmented name is exported from the target chunk under its
     * original name. Augmentation matches by exported name, so a name the chunk
     * dropped, re-exported under an alias, or deconflicted (`Foo` → `Foo$1` when two
     * modules in the chunk export a `Foo`) would merge into the wrong declaration.
     */
    augmentationApplies(names, moduleId, chunkFileName) {
        const chunkInfo = this.chunks[chunkFileName];
        if (!chunkInfo) {
            return true;
        }
        const targetModule = Object.entries(chunkInfo.modules).find(([id]) => normalizePath(id) === moduleId)?.[1];
        return names.every((name) => {
            if (!targetModule?.renderedExports.includes(name) || !chunkInfo.exports.includes(name)) {
                return false;
            }
            // A `name$<digits>` sibling export means this name was deconflicted within the
            // chunk, and there is no way to tell which module's declaration kept the name
            const deconflicted = new RegExp(`^${name.replace(/\$/g, "\\$")}\\$\\d+$`);
            return !chunkInfo.exports.some((exportName) => deconflicted.test(exportName));
        });
    }
    /**
     * Format a chunk filename as a relative path from the current chunk.
     */
    formatChunkReference(chunkFileName, jsExt) {
        // Compute the relative path from the current chunk's directory to the target chunk
        const chunkDir = path__namespace.dirname(this.chunkFileName);
        let relativePath = normalizePath(path__namespace.relative(chunkDir, chunkFileName));
        // Strip declaration extension and apply JS extension if the source used one
        relativePath = relativePath.replace(/\.d\.[cm]?tsx?$/, "");
        if (jsExt) {
            relativePath += jsExt;
        }
        // Ensure it starts with "./"
        if (!relativePath.startsWith(".")) {
            relativePath = "./" + relativePath;
        }
        return relativePath;
    }
}

function preProcessNamespaceBody(body, code, sourceFile) {
    // Recurse through dotted namespace chain (e.g. `namespace A.B.C {}`)
    if (ts.isModuleDeclaration(body)) {
        if (body.body && (ts.isModuleBlock(body.body) || ts.isModuleDeclaration(body.body))) {
            preProcessNamespaceBody(body.body, code);
        }
        return;
    }
    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) || ts.isModuleDeclaration(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
 *   missing `declare` modifier.
 * - [x] Splitting compound `VariableStatement` into its parts.
 * - [x] Moving declarations for the same "name" to be next to each other.
 * - [x] Removing any triple-slash directives and recording them.
 * - [x] Create a synthetic name for any nameless "export default".
 * - [x] Resolve inline `import()` statements and generate top-level imports for
 *   them.
 * - [x] Generate a separate `export {}` statement for any item which had its
 *   modifiers rewritten.
 * - [ ] Duplicate the identifiers of a namespace `export`, so that renaming does
 *   not break it
 */
function preProcess({ sourceFile, isEntry, isJSON }) {
    const code = new MagicString(sourceFile.getFullText());
    // Only treat as global module if it's not an entry point,
    // otherwise the final output will be mismatched with the entry.
    const treatAsGlobalModule = !isEntry && isGlobalModule(sourceFile);
    /** All the names that are declared in the `SourceFile`. */
    const declaredNames = new Set();
    /** All the names that are exported. */
    const exportedNames = new Set();
    /** The name of the default export. */
    let defaultExport = "";
    /** Inlined exports from `fileId` -> <synthetic name>. */
    const inlineImports = new Map();
    /** The ranges that each name covers, for re-ordering. */
    const nameRanges = new Map();
    /**
     * Pass 1:
     *
     * - Remove statements that we can’t handle.
     * - Collect a `Set` of all the declared names.
     * - Collect a `Set` of all the exported names.
     * - Maybe collect the name of the default export if present.
     * - Fix the modifiers of all the items.
     * - Collect the ranges of each named statement.
     * - Duplicate the identifiers of a namespace `export`, so that renaming does
     *   not break it
     */
    for (const node of sourceFile.statements) {
        if (ts.isEmptyStatement(node)) {
            code.remove(node.getStart(), node.getEnd());
            continue;
        }
        if (ts.isImportDeclaration(node)) {
            if (!node.importClause) {
                continue;
            }
            if (node.importClause.name) {
                declaredNames.add(node.importClause.name.text);
            }
            if (node.importClause.namedBindings) {
                if (ts.isNamespaceImport(node.importClause.namedBindings)) {
                    declaredNames.add(node.importClause.namedBindings.name.text);
                }
                else {
                    node.importClause.namedBindings.elements
                        .forEach((element) => declaredNames.add(element.name.text));
                }
            }
        }
        else if (ts.isEnumDeclaration(node) ||
            ts.isFunctionDeclaration(node) ||
            ts.isInterfaceDeclaration(node) ||
            ts.isClassDeclaration(node) ||
            ts.isTypeAliasDeclaration(node) ||
            ts.isModuleDeclaration(node)) {
            // collect the declared name
            if (node.name) {
                const name = node.name.getText();
                declaredNames.add(name);
                // collect the exported name, maybe as `default`.
                // `declare global` blocks are global augmentations, not actual exports.
                if (node.flags & ts.NodeFlags.GlobalAugmentation) ;
                else if (matchesModifier(node, ts.ModifierFlags.ExportDefault)) {
                    defaultExport = name;
                }
                else if ((treatAsGlobalModule && ts.isIdentifier(node.name))
                    || matchesModifier(node, ts.ModifierFlags.Export)) {
                    exportedNames.add(name);
                }
                if (!(node.flags & ts.NodeFlags.GlobalAugmentation)) {
                    pushNamedNode(name, [getStart(node), getEnd(node)]);
                }
            }
            // duplicate exports of namespaces
            if (ts.isModuleDeclaration(node)) {
                if (node.body && (ts.isModuleBlock(node.body) || ts.isModuleDeclaration(node.body))) {
                    preProcessNamespaceBody(node.body, code);
                }
                duplicateExports(code, node);
            }
            fixModifiers(code, node);
        }
        else if (ts.isVariableStatement(node)) {
            const { declarations } = node.declarationList;
            // collect all the names, also check if they are exported
            const isExport = matchesModifier(node, ts.ModifierFlags.Export);
            for (const decl of node.declarationList.declarations) {
                if (ts.isIdentifier(decl.name)) {
                    const name = decl.name.getText();
                    declaredNames.add(name);
                    if (treatAsGlobalModule || isExport) {
                        exportedNames.add(name);
                    }
                }
            }
            fixModifiers(code, node);
            // collect the ranges for re-ordering
            if (declarations.length === 1) {
                const decl = declarations[0];
                if (ts.isIdentifier(decl.name)) {
                    pushNamedNode(decl.name.getText(), [getStart(node), getEnd(node)]);
                }
            }
            else {
                // we do reordering after splitting
                const decls = declarations.slice();
                const first = decls.shift();
                pushNamedNode(first.name.getText(), [getStart(node), first.getEnd()]);
                for (const decl of decls) {
                    if (ts.isIdentifier(decl.name)) {
                        pushNamedNode(decl.name.getText(), [decl.getFullStart(), decl.getEnd()]);
                    }
                }
            }
            // split the variable declaration into different statements
            const { flags } = node.declarationList;
            const type = flags & ts.NodeFlags.Let ? "let" : flags & ts.NodeFlags.Const ? "const" : "var";
            const prefix = `declare ${type} `;
            const list = node.declarationList
                .getChildren()
                .find((c) => c.kind === ts.SyntaxKind.SyntaxList)
                .getChildren();
            let commaPos = 0;
            for (const node of list) {
                if (node.kind === ts.SyntaxKind.CommaToken) {
                    commaPos = node.getStart();
                    code.remove(commaPos, node.getEnd());
                }
                else if (commaPos) {
                    code.appendLeft(commaPos, ";\n");
                    const start = node.getFullStart();
                    const slice = code.slice(start, node.getStart());
                    const whitespace = slice.length - slice.trimStart().length;
                    if (whitespace) {
                        code.overwrite(start, start + whitespace, prefix);
                    }
                    else {
                        code.appendLeft(start, prefix);
                    }
                }
            }
        }
    }
    /**
     * Pass 2:
     *
     * Now that we have a Set of all the declared names, we can use that to
     * generate and de-conflict names for the following steps:
     *
     * - Resolve all the inline imports.
     * - Give any name-less `default export` a name.
     */
    for (const node of sourceFile.statements) {
        // recursively check inline imports
        checkInlineImport(node);
        /**
         * TODO: type-only import/export fixer.
         * Temporarily disable the type-only import/export transformation,
         * because the current implementation is unsafe.
         *
         * Issue: https://github.com/Swatinem/rollup-plugin-dts/issues/340
         */
        // 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;
        }
        // only function and class can be default exported, and be missing a name
        if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) {
            if (node.name) {
                continue;
            }
            if (!defaultExport) {
                defaultExport = uniqName("export_default");
            }
            const children = node.getChildren();
            const idx = children.findIndex((node) => node.kind === ts.SyntaxKind.ClassKeyword || node.kind === ts.SyntaxKind.FunctionKeyword);
            const token = children[idx];
            const nextToken = children[idx + 1];
            const isPunctuation = nextToken.kind >= ts.SyntaxKind.FirstPunctuation && nextToken.kind <= ts.SyntaxKind.LastPunctuation;
            if (isPunctuation) {
                const addSpace = code.slice(token.getEnd(), nextToken.getStart()) != " ";
                code.appendLeft(nextToken.getStart(), `${addSpace ? " " : ""}${defaultExport}`);
            }
            else {
                code.appendRight(token.getEnd(), ` ${defaultExport}`);
            }
        }
    }
    // and re-order all the name ranges to be contiguous
    for (const ranges of nameRanges.values()) {
        // we have to move all the nodes in front of the *last* one, which is a bit
        // unintuitive but is a workaround for:
        // https://github.com/Rich-Harris/magic-string/issues/180
        const last = ranges.pop();
        const start = last[0];
        for (const node of ranges) {
            code.move(node[0], node[1], start);
        }
    }
    // render all the inline imports, and all the exports
    if (defaultExport) {
        code.append(`\nexport default ${defaultExport};\n`);
    }
    if (exportedNames.size) {
        code.append(`\nexport { ${[...exportedNames].join(", ")} };\n`);
    }
    if (isJSON && exportedNames.size) {
        /**
         * Add default export for JSON modules.
         *
         * The typescript compiler only generate named exports for each top-level key,
         * but we also need a default export for JSON modules in most cases.
         * This also aligns with the behavior of `@rollup/plugin-json`.
         */
        defaultExport = uniqName("export_default");
        code.append([
            `\ndeclare const ${defaultExport}: {`,
            [...exportedNames].map(name => `  ${name}: typeof ${name};`).join("\n"),
            `};`,
            `export default ${defaultExport};\n`
        ].join('\n'));
    }
    for (const [fileId, importName] of inlineImports.entries()) {
        code.prepend(`import * as ${importName} from "${fileId}";\n`);
    }
    const lineStarts = sourceFile.getLineStarts();
    // and collect/remove all the typeReferenceDirectives
    const typeReferences = new Set();
    for (const ref of sourceFile.typeReferenceDirectives) {
        typeReferences.add(ref.fileName);
        const { line } = sourceFile.getLineAndCharacterOfPosition(ref.pos);
        const start = lineStarts[line];
        let end = sourceFile.getLineEndOfPosition(ref.pos);
        if (code.slice(end, end + 1) === "\n") {
            end += 1;
        }
        code.remove(start, end);
    }
    // and collect/remove all the fileReferenceDirectives
    const fileReferences = new Set();
    for (const ref of sourceFile.referencedFiles) {
        fileReferences.add(ref.fileName);
        const { line } = sourceFile.getLineAndCharacterOfPosition(ref.pos);
        const start = lineStarts[line];
        let end = sourceFile.getLineEndOfPosition(ref.pos);
        if (code.slice(end, end + 1) === "\n") {
            end += 1;
        }
        code.remove(start, end);
    }
    // Resolve relative module declarations to absolute paths
    // This allows correct chunk resolution later even when files from different
    // directories are bundled together
    //
    // Deliberately limited to `./` / `../` specifiers: bare names (`declare module "vue"`)
    // must stay untouched since consumers resolve them by package name, and tsconfig-paths
    // aliases cannot be resolved lexically (see ModuleDeclarationFixer for the full
    // mechanism and what alias support would require)
    const sourceDir = path__namespace.dirname(sourceFile.fileName);
    for (const node of sourceFile.statements) {
        if (ts.isModuleDeclaration(node) &&
            node.body &&
            ts.isModuleBlock(node.body) &&
            ts.isStringLiteral(node.name) &&
            /^\.\.?\//.test(node.name.text)) {
            const resolvedPath = path__namespace.resolve(sourceDir, node.name.text);
            code.appendRight(node.name.getEnd(), encodeResolvedModule(resolvedPath));
        }
    }
    // Strip trailing sourceMappingURL comments so they don't leak into bundled output
    const fullText = sourceFile.getFullText();
    // Check EOF token's leading trivia (comment on its own line after last statement)
    // and last statement's trailing trivia (comment on same line as last statement)
    const eofTrivia = ts.getLeadingCommentRanges(fullText, sourceFile.endOfFileToken.getFullStart());
    const lastStatement = sourceFile.statements[sourceFile.statements.length - 1];
    const trailingTrivia = lastStatement
        ? ts.getTrailingCommentRanges(fullText, lastStatement.getEnd())
        : undefined;
    for (const comment of [...(eofTrivia ?? []), ...(trailingTrivia ?? [])]) {
        // Skip block comments — sourceMappingURL text inside /** */ must not be stripped
        if (comment.kind !== ts.SyntaxKind.SingleLineCommentTrivia)
            continue;
        const text = fullText.slice(comment.pos, comment.end);
        if (!/^\/\/[#@]\s*sourceMappingURL=/.test(text))
            continue;
        let start = comment.pos;
        if (start > 0 && fullText[start - 1] === "\n") {
            start -= 1;
        }
        code.remove(start, comment.end);
        break;
    }
    return {
        code,
        typeReferences,
        fileReferences,
    };
    function checkInlineImport(node) {
        ts.forEachChild(node, checkInlineImport);
        if (ts.isImportTypeNode(node)) {
            if (!ts.isLiteralTypeNode(node.argument) || !ts.isStringLiteral(node.argument.literal)) {
                throw new UnsupportedSyntaxError(node, "inline imports should have a literal argument");
            }
            const fileId = node.argument.literal.text;
            const children = node.getChildren();
            const start = children.find((t) => t.kind === ts.SyntaxKind.ImportKeyword).getStart();
            let end = node.getEnd();
            const token = children.find((t) => t.kind === ts.SyntaxKind.DotToken || t.kind === ts.SyntaxKind.LessThanToken);
            if (token) {
                end = token.getStart();
            }
            const importName = createNamespaceImport(fileId);
            code.overwrite(start, end, importName);
        }
    }
    function createNamespaceImport(fileId) {
        let importName = inlineImports.get(fileId);
        if (!importName) {
            importName = uniqName(getSafeName(fileId));
            inlineImports.set(fileId, importName);
        }
        return importName;
    }
    function uniqName(hint) {
        let name = hint;
        while (declaredNames.has(name)) {
            name = `_${name}`;
        }
        declaredNames.add(name);
        return name;
    }
    function pushNamedNode(name, range) {
        let nodes = nameRanges.get(name);
        if (!nodes) {
            nodes = [range];
            nameRanges.set(name, nodes);
        }
        else {
            const last = nodes[nodes.length - 1];
            if (last[1] === range[0]) {
                last[1] = range[1];
            }
            else {
                nodes.push(range);
            }
        }
    }
}
/**
 * If the `SourceFile` is a "global module":
 *
 * 1. Doesn't have any top-level `export {}` or `export default` statements,
 *    otherwise it's a "scoped module".
 *
 * 2. Should have at least one top-level `import` or `export` statement,
 *    otherwise it's not a module.
 *
 * Issue: https://github.com/Swatinem/rollup-plugin-dts/issues/334
 */
function isGlobalModule(sourceFile) {
    let isModule = false;
    for (const node of sourceFile.statements) {
        if (ts.isExportDeclaration(node) || ts.isExportAssignment(node)) {
            return false;
        }
        if (isModule || ts.isImportDeclaration(node) || matchesModifier(node, ts.ModifierFlags.Export)) {
            isModule = true;
        }
    }
    return isModule;
}
function fixModifiers(code, node) {
    // remove the `export` and `default` modifier, add a `declare` if its missing.
    if (!ts.canHaveModifiers(node)) {
        return;
    }
    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 ");
        }
    }
    // For statements inside namespaces, preserve all modifiers (including export)
}
function duplicateExports(code, module) {
    if (!module.body) {
        return;
    }
    // Recurse through dotted namespace chain
    if (ts.isModuleDeclaration(module.body)) {
        duplicateExports(code, module.body);
        return;
    }
    if (!ts.isModuleBlock(module.body)) {
        return;
    }
    for (const node of module.body.statements) {
        if (ts.isExportDeclaration(node) && node.exportClause) {
            if (ts.isNamespaceExport(node.exportClause)) {
                continue;
            }
            for (const decl of node.exportClause.elements) {
                if (!decl.propertyName) {
                    code.appendLeft(decl.name.getEnd(), ` as ${decl.name.getText()}`);
                }
            }
        }
    }
}
function getSafeName(fileId) {
    return fileId.replace(/[^a-zA-Z0-9_$]/g, () => "_");
}
function getStart(node) {
    const start = node.getFullStart();
    return start + (newlineAt(node, start) ? 1 : 0);
}
function getEnd(node) {
    const end = node.getEnd();
    return end + (newlineAt(node, end) ? 1 : 0);
}
function newlineAt(node, idx) {
    return node.getSourceFile().getFullText()[idx] === "\n";
}

const IGNORE_TYPENODES = new Set([
    ts.SyntaxKind.LiteralType,
    ts.SyntaxKind.VoidKeyword,
    ts.SyntaxKind.UnknownKeyword,
    ts.SyntaxKind.AnyKeyword,
    ts.SyntaxKind.BooleanKeyword,
    ts.SyntaxKind.NumberKeyword,
    ts.SyntaxKind.StringKeyword,
    ts.SyntaxKind.ObjectKeyword,
    ts.SyntaxKind.NullKeyword,
    ts.SyntaxKind.UndefinedKeyword,
    ts.SyntaxKind.SymbolKeyword,
    ts.SyntaxKind.NeverKeyword,
    ts.SyntaxKind.ThisKeyword,
    ts.SyntaxKind.ThisType,
    ts.SyntaxKind.BigIntKeyword,
]);
class DeclarationScope {
    declaration;
    iife;
    returnExpr;
    constructor({ id, range }) {
        if (id) {
            this.declaration = createDeclaration(id, range);
        }
        else {
            const { iife, fn } = createIIFE(range);
            this.iife = iife;
            this.declaration = fn;
        }
        const ret = createReturn();
        this.declaration.body.body.push(ret.stmt);
        this.returnExpr = ret.expr;
    }
    /**
     * 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,
     * represented as Sets of variable names.
     */
    scopes = [];
    pushScope() {
        this.scopes.push(new Set());
    }
    popScope(n = 1) {
        for (let i = 0; i < n; i++) {
            this.scopes.pop();
        }
    }
    pushTypeVariable(id) {
        const name = id.getText();
        this.scopes[this.scopes.length - 1]?.add(name);
    }
    pushReference(id) {
        let name;
        // We convert references from TS AST to ESTree
        // to hand them off to rollup.
        // This means we have to check the left-most identifier inside our scope
        // tree and avoid to create the reference in that case
        if (id.type === "Identifier") {
            name = id.name;
        }
        else if (id.type === "MemberExpression") {
            if (id.object.type === "Identifier") {
                name = id.object.name;
            }
        }
        if (name) {
            for (const scope of this.scopes) {
                if (scope.has(name)) {
                    return;
                }
            }
        }
        // `this` is a reserved keyword that retrains meaning in certain Type-only contexts, including classes
        if (name === "this")
            return;
        const { ident, expr } = createReference(id);
        this.declaration.params.push(expr);
        this.returnExpr.elements.push(ident);
    }
    pushIdentifierReference(id) {
        this.pushReference(createIdentifier(id));
    }
    convertEntityName(node) {
        if (ts.isIdentifier(node)) {
            return createIdentifier(node);
        }
        return withStartEnd({
            type: "MemberExpression",
            computed: false,
            optional: false,
            object: this.convertEntityName(node.left),
            property: createIdentifier(node.right),
        }, node);
    }
    convertPropertyAccess(node) {
        // hm, we only care about property access expressions here…
        if (!ts.isIdentifier(node.expression) && !ts.isPropertyAccessExpression(node.expression)) {
            throw new UnsupportedSyntaxError(node.expression);
        }
        if (ts.isPrivateIdentifier(node.name)) {
            throw new UnsupportedSyntaxError(node.name);
        }
        const object = ts.isIdentifier(node.expression)
            ? createIdentifier(node.expression)
            : this.convertPropertyAccess(node.expression);
        return withStartEnd({
            type: "MemberExpression",
            computed: false,
            optional: false,
            object,
            property: createIdentifier(node.name),
        }, node);
    }
    convertComputedPropertyName(node) {
        if (!node.name || !ts.isComputedPropertyName(node.name)) {
            return;
        }
        const { expression } = node.name;
        if (ts.isLiteralExpression(expression) || ts.isPrefixUnaryExpression(expression)) {
            return;
        }
        if (ts.isIdentifier(expression)) {
            return this.pushReference(createIdentifier(expression));
        }
        if (ts.isPropertyAccessExpression(expression)) {
            return this.pushReference(this.convertPropertyAccess(expression));
        }
        throw new UnsupportedSyntaxError(expression);
    }
    convertParametersAndType(node) {
        this.convertComputedPropertyName(node);
        const typeVariables = this.convertTypeParameters(node.typeParameters);
        for (const param of node.parameters) {
            this.convertTypeNode(param.type);
        }
        this.convertTypeNode(node.type);
        this.popScope(typeVariables);
    }
    convertHeritageClauses(node) {
        for (const heritage of node.heritageClauses || []) {
            for (const type of heritage.types) {
                this.pushReference(convertExpression(type.expression));
                this.convertTypeArguments(type);
            }
        }
    }
    convertTypeArguments(node) {
        if (!node.typeArguments) {
            return;
        }
        for (const arg of node.typeArguments) {
            this.convertTypeNode(arg);
        }
    }
    convertMembers(members) {
        for (const node of members) {
            if (ts.isPropertyDeclaration(node) || ts.isPropertySignature(node) || ts.isIndexSignatureDeclaration(node)) {
                if (ts.isPropertyDeclaration(node) && node.initializer && ts.isPropertyAccessExpression(node.initializer)) {
                    this.pushReference(this.convertPropertyAccess(node.initializer));
                }
                this.convertComputedPropertyName(node);
                this.convertTypeNode(node.type);
                continue;
            }
            if (ts.isMethodDeclaration(node) ||
                ts.isMethodSignature(node) ||
                ts.isConstructorDeclaration(node) ||
                ts.isConstructSignatureDeclaration(node) ||
                ts.isCallSignatureDeclaration(node) ||
                ts.isGetAccessorDeclaration(node) ||
                ts.isSetAccessorDeclaration(node)) {
                this.convertParametersAndType(node);
            }
            else {
                throw new UnsupportedSyntaxError(node);
            }
        }
    }
    convertTypeParameters(params) {
        if (!params) {
            return 0;
        }
        for (const node of params) {
            this.convertTypeNode(node.constraint);
            this.convertTypeNode(node.default);
            this.pushScope();
            this.pushTypeVariable(node.name);
        }
        return params.length;
    }
    convertTypeNode(node) {
        if (!node) {
            return;
        }
        if (IGNORE_TYPENODES.has(node.kind)) {
            return;
        }
        if (ts.isTypeReferenceNode(node)) {
            this.pushReference(this.convertEntityName(node.typeName));
            this.convertTypeArguments(node);
            return;
        }
        if (ts.isTypeLiteralNode(node)) {
            this.convertMembers(node.members);
            return;
        }
        if (ts.isArrayTypeNode(node)) {
            this.convertTypeNode(node.elementType);
            return;
        }
        if (ts.isTupleTypeNode(node)) {
            for (const type of node.elements) {
                this.convertTypeNode(type);
            }
            return;
        }
        if (ts.isNamedTupleMember(node) ||
            ts.isParenthesizedTypeNode(node) ||
            ts.isTypeOperatorNode(node) ||
            ts.isTypePredicateNode(node)) {
            this.convertTypeNode(node.type);
            return;
        }
        if (ts.isUnionTypeNode(node) || ts.isIntersectionTypeNode(node)) {
            for (const type of node.types) {
                this.convertTypeNode(type);
            }
            return;
        }
        if (ts.isMappedTypeNode(node)) {
            const { typeParameter, type, nameType } = node;
            this.convertTypeNode(typeParameter.constraint);
            this.pushScope();
            this.pushTypeVariable(typeParameter.name);
            this.convertTypeNode(type);
            if (nameType) {
                this.convertTypeNode(nameType);
            }
            this.popScope();
            return;
        }
        if (ts.isConditionalTypeNode(node)) {
            this.convertTypeNode(node.checkType);
            this.pushScope();
            this.convertTypeNode(node.extendsType);
            this.convertTypeNode(node.trueType);
            this.convertTypeNode(node.falseType);
            this.popScope();
            return;
        }
        if (ts.isIndexedAccessTypeNode(node)) {
            this.convertTypeNode(node.objectType);
            this.convertTypeNode(node.indexType);
            return;
        }
        if (ts.isFunctionOrConstructorTypeNode(node)) {
            this.convertParametersAndType(node);
            return;
        }
        if (ts.isTypeQueryNode(node)) {
            const reference = this.convertEntityName(node.exprName);
            this.pushReference(reference);
            this.convertTypeArguments(node);
            return;
        }
        if (ts.isRestTypeNode(node)) {
            this.convertTypeNode(node.type);
            return;
        }
        if (ts.isOptionalTypeNode(node)) {
            this.convertTypeNode(node.type);
            return;
        }
        if (ts.isTemplateLiteralTypeNode(node)) {
            for (const span of node.templateSpans) {
                this.convertTypeNode(span.type);
            }
            return;
        }
        if (ts.isInferTypeNode(node)) {
            const { typeParameter } = node;
            this.convertTypeNode(typeParameter.constraint);
            this.pushTypeVariable(typeParameter.name);
            return;
        }
        else {
            throw new UnsupportedSyntaxError(node);
        }
    }
    convertNamespace(node, relaxedModuleBlock = false) {
        this.pushScope();
        if (relaxedModuleBlock && node.body && ts.isModuleDeclaration(node.body)) {
            this.convertNamespace(node.body, true);
            return;
        }
        if (!node.body || !ts.isModuleBlock(node.body)) {
            throw new UnsupportedSyntaxError(node, `namespace must have a "ModuleBlock" body.`);
        }
        const { statements } = node.body;
        // first, hoist all the declarations for correct shadowing
        for (const stmt of statements) {
            if (ts.isEnumDeclaration(stmt) ||
                ts.isFunctionDeclaration(stmt) ||
                ts.isClassDeclaration(stmt) ||
                ts.isInterfaceDeclaration(stmt) ||
                ts.isTypeAliasDeclaration(stmt) ||
                ts.isModuleDeclaration(stmt)) {
                if (stmt.name && ts.isIdentifier(stmt.name)) {
                    this.pushTypeVariable(stmt.name);
                }
                else {
                    throw new UnsupportedSyntaxError(stmt, "non-Identifier name not supported");
                }
                continue;
            }
            if (ts.isVariableStatement(stmt)) {
                for (const decl of stmt.declarationList.declarations) {
                    if (ts.isIdentifier(decl.name)) {
                        this.pushTypeVariable(decl.name);
                    }
                    else {
                        throw new UnsupportedSyntaxError(decl, "non-Identifier name not supported");
                    }
                }
                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");
            }
        }
        // and then walk all the children like normal…
        for (const stmt of statements) {
            if (ts.isVariableStatement(stmt)) {
                for (const decl of stmt.declarationList.declarations) {
                    if (decl.type) {
                        this.convertTypeNode(decl.type);
                    }
                }
                continue;
            }
            if (ts.isFunctionDeclaration(stmt)) {
                this.convertParametersAndType(stmt);
                continue;
            }
            if (ts.isInterfaceDeclaration(stmt) || ts.isClassDeclaration(stmt)) {
                const typeVariables = this.convertTypeParameters(stmt.typeParameters);
                this.convertHeritageClauses(stmt);
                this.convertMembers(stmt.members);
                this.popScope(typeVariables);
                continue;
            }
            if (ts.isTypeAliasDeclaration(stmt)) {
                const typeVariables = this.convertTypeParameters(stmt.typeParameters);
                this.convertTypeNode(stmt.type);
                this.popScope(typeVariables);
                continue;
            }
            if (ts.isModuleDeclaration(stmt)) {
                this.convertNamespace(stmt, relaxedModuleBlock);
                continue;
            }
            if (ts.isEnumDeclaration(stmt)) {
                // 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)) {
                        throw new UnsupportedSyntaxError(stmt.exportClause);
                    }
                    for (const decl of stmt.exportClause.elements) {
                        const id = decl.propertyName || decl.name;
                        this.pushIdentifierReference(id);
                    }
                }
            }
            else {
                throw new UnsupportedSyntaxError(stmt, "namespace child (walking) not supported yet");
            }
        }
        this.popScope();
    }
}

function convert({ sourceFile }) {
    const transformer = new Transformer(sourceFile);
    return transformer.transform();
}
class Transformer {
    sourceFile;
    ast;
    declarations = new Map();
    constructor(sourceFile) {
        this.sourceFile = sourceFile;
        this.ast = createProgram(sourceFile);
        for (const stmt of sourceFile.statements) {
            this.convertStatement(stmt);
        }
    }
    transform() {
        return {
            ast: this.ast,
        };
    }
    pushStatement(node) {
        this.ast.body.push(node);
    }
    createDeclaration(node, id) {
        const range = { start: node.getFullStart(), end: node.getEnd() };
        if (!id) {
            const scope = new DeclarationScope({ range });
            this.pushStatement(scope.iife);
            return scope;
        }
        const name = id.getText();
        // We have re-ordered and grouped declarations in `reorderStatements`,
        // so we can assume same-name statements are next to each other, so we just
        // bump the `end` range.
        const scope = new DeclarationScope({ id, range });
        const existingScope = this.declarations.get(name);
        if (existingScope) {
            existingScope.pushIdentifierReference(id);
            existingScope.declaration.end = range.end;
            // we possibly have other declarations, such as an ExportDeclaration in
            // between, which should also be updated to the correct start/end.
            const selfIdx = this.ast.body.findIndex((node) => node == existingScope.declaration);
            for (let i = selfIdx + 1; i < this.ast.body.length; i++) {
                const decl = this.ast.body[i];
                decl.start = decl.end = range.end;
            }
        }
        else {
            this.pushStatement(scope.declaration);
            this.declarations.set(name, scope);
        }
        return existingScope || scope;
    }
    convertStatement(node) {
        if (ts.isEnumDeclaration(node)) {
            return this.convertEnumDeclaration(node);
        }
        if (ts.isFunctionDeclaration(node)) {
            return this.convertFunctionDeclaration(node);
        }
        if (ts.isInterfaceDeclaration(node) || ts.isClassDeclaration(node)) {
            return this.convertClassOrInterfaceDeclaration(node);
        }
        if (ts.isTypeAliasDeclaration(node)) {
            return this.convertTypeAliasDeclaration(node);
        }
        if (ts.isVariableStatement(node)) {
            return this.convertVariableStatement(node);
        }
        if (ts.isExportDeclaration(node) || ts.isExportAssignment(node)) {
            return this.convertExportDeclaration(node);
        }
        if (ts.isModuleDeclaration(node)) {
            return this.convertNamespaceDeclaration(node);
        }
        if (node.kind === ts.SyntaxKind.NamespaceExportDeclaration) {
            // just ignore `export as namespace FOO` statements…
            return this.removeStatement(node);
        }
        if (ts.isImportDeclaration(node) || ts.isImportEqualsDeclaration(node)) {
            return this.convertImportDeclaration(node);
        }
        else {
            throw new UnsupportedSyntaxError(node);
        }
    }
    removeStatement(node) {
        this.pushStatement(withStartEnd({
            type: "ExpressionStatement",
            expression: { type: "Literal", value: "pls remove me" },
        }, node));
    }
    convertNamespaceDeclaration(node) {
        // we want to keep `declare global` augmentations, and we want to
        // pull in all the things referenced inside.
        // so for this case, we need to figure out some way so that rollup does
        // the right thing and not rename these…
        const isGlobalAugmentation = node.flags & ts.NodeFlags.GlobalAugmentation;
        if (isGlobalAugmentation || !ts.isIdentifier(node.name)) {
            const scope = this.createDeclaration(node);
            scope.convertNamespace(node, true);
            return;
        }
        const scope = this.createDeclaration(node, node.name);
        scope.pushIdentifierReference(node.name);
        scope.convertNamespace(node, true);
    }
    convertEnumDeclaration(node) {
        const scope = this.createDeclaration(node, node.name);
        scope.pushIdentifierReference(node.name);
    }
    convertFunctionDeclaration(node) {
        if (!node.name) {
            throw new UnsupportedSyntaxError(node, "FunctionDeclaration should have a name");
        }
        const scope = this.createDeclaration(node, node.name);
        scope.pushIdentifierReference(node.name);
        scope.convertParametersAndType(node);
    }
    convertClassOrInterfaceDeclaration(node) {
        if (!node.name) {
            throw new UnsupportedSyntaxError(node, "ClassDeclaration / InterfaceDeclaration should have a name");
        }
        const scope = this.createDeclaration(node, node.name);
        const typeVariables = scope.convertTypeParameters(node.typeParameters);
        scope.convertHeritageClauses(node);
        scope.convertMembers(node.members);
        scope.popScope(typeVariables);
    }
    convertTypeAliasDeclaration(node) {
        /**
         * TODO: type-only import/export fixer.
         * Temporarily disable the type-only import/export transformation,
         * because the current implementation is unsafe.
         *
         * Issue: https://github.com/Swatinem/rollup-plugin-dts/issues/340
         */
        // if(parseTypeOnlyName(node.name.text).isTypeOnly) {
        //   this.pushStatement(convertTypeOnlyHintStatement(node))
        //   return
        // }
        const scope = this.createDeclaration(node, node.name);
        const typeVariables = scope.convertTypeParameters(node.typeParameters);
        scope.convertTypeNode(node.type);
        scope.popScope(typeVariables);
    }
    convertVariableStatement(node) {
        const { declarations } = node.declarationList;
        if (declarations.length !== 1) {
            throw new UnsupportedSyntaxError(node, "VariableStatement with more than one declaration not yet supported");
        }
        for (const decl of declarations) {
            if (!ts.isIdentifier(decl.name)) {
                throw new UnsupportedSyntaxError(node, "VariableDeclaration must have a name");
            }
            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) {
        if (ts.isExportAssignment(node)) {
            this.pushStatement(withStartEnd({
                type: "ExportDefaultDeclaration",
                declaration: convertExpression(node.expression),
            }, node));
            return;
        }
        const source = node.moduleSpecifier ? convertExpression(node.moduleSpecifier) : undefined;
        if (!node.exportClause) {
            // export * from './other'
            this.pushStatement(withStartEnd({
                type: "ExportAllDeclaration",
                source,
                exported: null,
                attributes: [],
            }, node));
        }
        else if (ts.isNamespaceExport(node.exportClause)) {
            // export * as name from './other'
            this.pushStatement(withStartEnd({
                type: "ExportAllDeclaration",
                source,
                exported: createIdentifier(node.exportClause.name),
                attributes: [],
            }, node));
        }
        else {
            // export { name } from './other'
            const specifiers = [];
            for (const elem of node.exportClause.elements) {
                specifiers.push(this.convertExportSpecifier(elem));
            }
            this.pushStatement(withStartEnd({
                type: "ExportNamedDeclaration",
                declaration: null,
                specifiers,
                source,
                attributes: [],
            }, node));
        }
    }
    convertImportDeclaration(node) {
        if (ts.isImportEqualsDeclaration(node)) {
            if (ts.isEntityName(node.moduleReference)) {
                const scope = this.createDeclaration(node, node.name);
                scope.pushReference(scope.convertEntityName(node.moduleReference));
                return;
            }
            // assume its like `import default`
            if (!ts.isExternalModuleReference(node.moduleReference)) {
                throw new UnsupportedSyntaxError(node, "ImportEquals should have a literal source.");
            }
            this.pushStatement(withStartEnd({
                type: "ImportDeclaration",
                specifiers: [
                    {
                        type: "ImportDefaultSpecifier",
                        local: createIdentifier(node.name),
                    },
                ],
                source: convertExpression(node.moduleReference.expression),
                attributes: [],
            }, node));
            return;
        }
        const source = convertExpression(node.moduleSpecifier);
        const specifiers = node.importClause && node.importClause.namedBindings
            ? this.convertNamedImportBindings(node.importClause.namedBindings)
            : [];
        if (node.importClause && node.importClause.name) {
            specifiers.push({
                type: "ImportDefaultSpecifier",
                local: createIdentifier(node.importClause.name),
            });
        }
        this.pushStatement(withStartEnd({
            type: "ImportDeclaration",
            specifiers,
            source,
            attributes: [],
        }, node));
    }
    convertNamedImportBindings(node) {
        if (ts.isNamedImports(node)) {
            return node.elements.map((el) => {
                const local = createIdentifier(el.name);
                const imported = el.propertyName ? createIdentifier(el.propertyName) : local;
                return {
                    type: "ImportSpecifier",
                    local,
                    imported,
                };
            });
        }
        return [
            {
                type: "ImportNamespaceSpecifier",
                local: createIdentifier(node.name),
            },
        ];
    }
    convertExportSpecifier(node) {
        const exported = createIdentifier(node.name);
        return {
            type: "ExportSpecifier",
            exported: exported,
            local: node.propertyName ? createIdentifier(node.propertyName) : exported,
        };
    }
}

/**
 * Hydrate sparse sourcemap with detailed segments from input map.
 *
 * Problem: Rollup produces sparse mappings because this plugin uses a "virtual AST"
 * trick - it generates fake ESTree nodes with start/end positions but no token-level
 * detail. Standard sourcemap composition (@jridgewell/remapping) can only trace
 * existing segments through the chain, not add new ones.
 *
 * Solution: Use Rollup's sparse segments as "anchors" to find which source line
 * each output line came from, then copy ALL segments from that input line.
 * The column delta between anchor positions aligns the segments correctly.
 *
 * This enables Go-to-Definition to work for individual identifiers, not just line starts.
 */
function hydrateSourcemap(sparseMappings, inputMap, outputCode) {
    const sparseDecoded = sourcemapCodec.decode(sparseMappings);
    const inputDecoded = sourcemapCodec.decode(inputMap.mappings);
    const outputLines = outputCode.split("\n");
    const hydratedMappings = [];
    for (let outLine = 0; outLine < sparseDecoded.length; outLine += 1) {
        const sparseSegments = sparseDecoded[outLine];
        if (!sparseSegments || sparseSegments.length === 0) {
            hydratedMappings.push([]);
            continue;
        }
        // Use first mapped segment as anchor to find source line
        const anchor = sparseSegments.find((segment) => segment.length >= 4);
        if (!anchor) {
            hydratedMappings.push(sparseSegments);
            continue;
        }
        const [, srcIdx, srcLine] = anchor;
        if (srcIdx !== 0 || srcLine === undefined || srcLine < 0 || srcLine >= inputDecoded.length) {
            hydratedMappings.push(sparseSegments);
            continue;
        }
        const inputSegments = inputDecoded[srcLine];
        if (!inputSegments || inputSegments.length === 0) {
            hydratedMappings.push(sparseSegments);
            continue;
        }
        const anchorOutCol = anchor[0];
        const anchorSrcCol = anchor.length >= 4 ? anchor[3] : 0;
        const delta = anchorOutCol - (anchorSrcCol ?? 0);
        const outputLine = outputLines[outLine] || "";
        const hydratedSegments = [];
        for (const seg of inputSegments) {
            const adjustedCol = seg[0] + delta;
            // Sanity check: skip segments outside valid range
            if (adjustedCol < 0 || adjustedCol > outputLine.length)
                continue;
            if (seg.length === 5) {
                hydratedSegments.push([adjustedCol, seg[1], seg[2], seg[3], seg[4]]);
            }
            else if (seg.length === 4) {
                hydratedSegments.push([adjustedCol, seg[1], seg[2], seg[3]]);
            }
            else {
                hydratedSegments.push([adjustedCol]);
            }
        }
        // Sort by column (required by sourcemap spec)
        hydratedSegments.sort((a, b) => a[0] - b[0]);
        hydratedMappings.push(hydratedSegments);
    }
    return sourcemapCodec.encode(hydratedMappings);
}
// Load sourcemap from inline data URL, file reference, or external .map file
async function loadInputSourcemap(info) {
    const { fileName, originalCode, inputMapText } = info;
    // Use pre-loaded map if available (from TypeScript emit of .ts files)
    if (inputMapText) {
        try {
            return JSON.parse(inputMapText);
        }
        catch {
            return null;
        }
    }
    // Try inline sourcemap (base64 or url-encoded data URL)
    // Note: TypeScript never emits inline declaration maps, but other tools might.
    // This is defensive code - low cost since convert.fromSource returns null for TS maps.
    const inlineConverter = convert$1.fromSource(originalCode);
    if (inlineConverter) {
        return inlineConverter.toObject();
    }
    // Try file reference (//# sourceMappingURL=foo.map)
    const readMap = async (mapFile) => {
        // Strip query string or fragment if present (e.g., "index.d.ts.map?v=12345" or "index.d.ts.map#hash")
        // Note: TypeScript doesn't add these, but other build tools might. Defensive code - trivial cost.
        const urlWithoutQuery = mapFile.split(/[?#]/)[0];
        const mapFilePath = path__namespace.resolve(path__namespace.dirname(fileName), urlWithoutQuery);
        return fs.readFile(mapFilePath, "utf8");
    };
    try {
        const fileConverter = await convert$1.fromMapFileSource(originalCode, readMap);
        if (fileConverter) {
            return fileConverter.toObject();
        }
    }
    catch {
        // File not found or parse error, try external .map
    }
    // Try external .map file (no comment in source)
    try {
        const mapContent = await fs.readFile(fileName + ".map", "utf8");
        return JSON.parse(mapContent);
    }
    catch {
        return null;
    }
}

function rewritePortableSharedChunkImportsInBundle(bundle, warn) {
    const chunks = Object.values(bundle).filter(isOutputChunk);
    const sharedChunks = chunks.filter((chunk) => !chunk.isEntry);
    if (sharedChunks.length === 0)
        return;
    const entryChunks = chunks.filter((chunk) => chunk.isEntry);
    const chunkGraph = new Map(chunks.map((chunk) => [
        chunk.fileName,
        {
            exports: chunk.exports,
            isEntry: chunk.isEntry,
        },
    ]));
    const analyses = new Map(chunks.map((chunk) => [chunk.fileName, analyzeChunk(chunk, chunkGraph)]));
    for (const chunk of entryChunks) {
        const analysis = analyses.get(chunk.fileName);
        const magicCode = new MagicString(chunk.code);
        let hasChanges = false;
        const unresolvedSymbols = new Set();
        for (const statement of analysis.importStatements) {
            const sharedChunk = sharedChunks.find((candidate) => getChunkImportSpecifier(chunk.fileName, candidate.fileName) === statement.moduleSpecifier);
            if (!sharedChunk) {
                continue;
            }
            const keptSpecifiers = [];
            const rewrittenSpecifiers = new Map();
            for (const specifier of statement.specifiers) {
                if (hasPublicHostRoute(analysis, statement.moduleSpecifier, specifier.importedName)) {
                    keptSpecifiers.push(specifier);
                    continue;
                }
                const hostCandidate = pickHostCandidate(entryChunks, analyses, chunk.fileName, sharedChunk.fileName, specifier);
                if (!hostCandidate) {
                    keptSpecifiers.push(specifier);
                    unresolvedSymbols.add(specifier.localName);
                    continue;
                }
                const hostSpecifier = getChunkImportSpecifier(chunk.fileName, hostCandidate.chunk.fileName);
                const hostImportSpecifiers = rewrittenSpecifiers.get(hostSpecifier) || [];
                hostImportSpecifiers.push({
                    importedName: hostCandidate.exportedName,
                    isTypeOnly: specifier.isTypeOnly || hostCandidate.isTypeOnly,
                    localName: specifier.localName,
                });
                rewrittenSpecifiers.set(hostSpecifier, hostImportSpecifiers);
            }
            if (!rewrittenSpecifiers.size) {
                continue;
            }
            const replacementStatements = [];
            if (keptSpecifiers.length) {
                replacementStatements.push(buildImportStatement(statement, keptSpecifiers, statement.moduleSpecifier, statement.quote));
            }
            const hostSpecifiers = Array.from(rewrittenSpecifiers.keys()).sort(compareStrings);
            for (const hostSpecifier of hostSpecifiers) {
                replacementStatements.push(buildImportStatement(statement, rewrittenSpecifiers.get(hostSpecifier), hostSpecifier, statement.quote));
            }
            magicCode.overwrite(statement.statement.getStart(), statement.statement.getEnd(), replacementStatements.join("\n"));
            hasChanges = true;
        }
        if (hasChanges) {
            applyChunkEdits(chunk, magicCode);
        }
        if (unresolvedSymbols.size) {
            warn(formatUnresolvedSharedTypeWarning(chunk.fileName, unresolvedSymbols));
        }
    }
}
function analyzeChunk(chunk, chunkGraph) {
    const source = parse(chunk.fileName, chunk.code);
    const importStatements = [];
    const importedBindings = new Map();
    const hostRoutesByModule = new Map();
    const starExports = [];
    for (const statement of source.statements) {
        if (ts.isImportDeclaration(statement) &&
            !statement.importClause?.name &&
            ts.isStringLiteral(statement.moduleSpecifier) &&
            statement.importClause?.namedBindings &&
            ts.isNamedImports(statement.importClause.namedBindings)) {
            const moduleSpecifier = statement.moduleSpecifier.text;
            const quote = statement.moduleSpecifier.getText(source).startsWith('"') ? '"' : "'";
            const specifiers = statement.importClause.namedBindings.elements.map((element) => {
                const specifier = {
                    importedName: element.propertyName?.text || element.name.text,
                    isTypeOnly: element.isTypeOnly || statement.importClause?.isTypeOnly || false,
                    localName: element.name.text,
                };
                importedBindings.set(specifier.localName, {
                    importedName: specifier.importedName,
                    isTypeOnly: specifier.isTypeOnly,
                    moduleSpecifier,
                });
                return specifier;
            });
            importStatements.push({
                isTypeOnly: statement.importClause.isTypeOnly,
                moduleSpecifier,
                quote,
                specifiers,
                statement,
            });
            continue;
        }
        if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier || !ts.isStringLiteral(statement.moduleSpecifier)) {
            continue;
        }
        if (!statement.exportClause) {
            starExports.push(statement.moduleSpecifier.text);
            continue;
        }
        if (!ts.isNamedExports(statement.exportClause)) {
            continue;
        }
        for (const element of statement.exportClause.elements) {
            addHostExportRoute(hostRoutesByModule, statement.moduleSpecifier.text, element.propertyName?.text || element.name.text, {
                exportedName: element.name.text,
                isTypeOnly: statement.isTypeOnly || element.isTypeOnly,
            });
        }
    }
    for (const statement of source.statements) {
        if (!ts.isExportDeclaration(statement) ||
            statement.moduleSpecifier ||
            !statement.exportClause ||
            !ts.isNamedExports(statement.exportClause)) {
            continue;
        }
        for (const element of statement.exportClause.elements) {
            const localName = element.propertyName?.text || element.name.text;
            const binding = importedBindings.get(localName);
            if (!binding) {
                continue;
            }
            addHostExportRoute(hostRoutesByModule, binding.moduleSpecifier, binding.importedName, {
                exportedName: element.name.text,
                isTypeOnly: binding.isTypeOnly || statement.isTypeOnly || element.isTypeOnly,
            });
        }
    }
    for (const moduleSpecifier of starExports) {
        const sharedChunk = Array.from(chunkGraph.entries()).find(([candidateFileName, candidate]) => !candidate.isEntry && getChunkImportSpecifier(chunk.fileName, candidateFileName) === moduleSpecifier);
        if (!sharedChunk) {
            continue;
        }
        for (const exportedName of sharedChunk[1].exports) {
            if (exportedName === "default") {
                continue;
            }
            addHostExportRoute(hostRoutesByModule, moduleSpecifier, exportedName, {
                exportedName,
                isTypeOnly: false,
            });
        }
    }
    return {
        hostRoutesByModule,
        importStatements,
    };
}
const isOutputChunk = (chunk) => chunk.type === "chunk";
function pickHostCandidate(entryChunks, analyses, currentChunkFileName, sharedChunkFileName, specifier) {
    const candidates = [];
    for (const hostChunk of entryChunks) {
        if (hostChunk.fileName === currentChunkFileName) {
            continue;
        }
        const hostAnalysis = analyses.get(hostChunk.fileName);
        const sharedSpecifier = getChunkImportSpecifier(hostChunk.fileName, sharedChunkFileName);
        const routes = getHostExportRoutes(hostAnalysis, sharedSpecifier, specifier.importedName);
        for (const route of routes) {
            candidates.push({
                ...route,
                chunk: hostChunk,
            });
        }
    }
    candidates.sort((left, right) => {
        const leftMatchesLocal = left.exportedName === specifier.localName ? 0 : 1;
        const rightMatchesLocal = right.exportedName === specifier.localName ? 0 : 1;
        if (leftMatchesLocal !== rightMatchesLocal) {
            return leftMatchesLocal - rightMatchesLocal;
        }
        const fileNameOrder = compareStrings(left.chunk.fileName, right.chunk.fileName);
        if (fileNameOrder !== 0) {
            return fileNameOrder;
        }
        return compareStrings(left.exportedName, right.exportedName);
    });
    return candidates[0];
}
const getHostExportRoutes = (analysis, moduleSpecifier, importedName) => analysis.hostRoutesByModule.get(moduleSpecifier)?.get(importedName) || [];
const hasPublicHostRoute = (analysis, moduleSpecifier, importedName) => getHostExportRoutes(analysis, moduleSpecifier, importedName).length > 0;
const compareStrings = (left, right) => {
    if (left < right) {
        return -1;
    }
    if (left > right) {
        return 1;
    }
    return 0;
};
function addHostExportRoute(hostRoutesByModule, moduleSpecifier, importedName, route) {
    const routesByImportedName = hostRoutesByModule.get(moduleSpecifier) || new Map();
    const routes = routesByImportedName.get(importedName) || [];
    if (!routes.some((existing) => existing.exportedName === route.exportedName && existing.isTypeOnly === route.isTypeOnly)) {
        routes.push(route);
    }
    routesByImportedName.set(importedName, routes);
    hostRoutesByModule.set(moduleSpecifier, routesByImportedName);
}
function buildImportStatement(statement, specifiers, moduleSpecifier, quote) {
    const useTypeOnlyImport = statement.isTypeOnly || specifiers.every((specifier) => specifier.isTypeOnly);
    const importKeyword = useTypeOnlyImport ? "import type" : "import";
    const namedImports = specifiers
        .map((specifier) => {
        const prefix = !useTypeOnlyImport && specifier.isTypeOnly ? "type " : "";
        if (specifier.importedName === specifier.localName) {
            return `${prefix}${specifier.importedName}`;
        }
        return `${prefix}${specifier.importedName} as ${specifier.localName}`;
    })
        .join(", ");
    return `${importKeyword} { ${namedImports} } from ${quote}${moduleSpecifier}${quote};`;
}
function applyChunkEdits(chunk, code) {
    const nextCode = code.toString();
    if (nextCode === chunk.code) {
        return;
    }
    if (chunk.map) {
        const chunkFileName = normalizeChunkPath(chunk.fileName);
        const rewriteMap = code.generateMap({
            file: chunkFileName,
            hires: true,
            includeContent: true,
            source: chunkFileName,
        });
        const remapped = remapping(rewriteMap, (file) => {
            if (normalizeChunkPath(file) === chunkFileName) {
                return chunk.map;
            }
            return null;
        });
        chunk.map = {
            ...chunk.map,
            mappings: typeof remapped.mappings === "string" ? remapped.mappings : "",
            names: remapped.names || [],
            sources: remapped.sources,
        };
        delete chunk.map.sourcesContent;
    }
    chunk.code = nextCode;
}
const formatUnresolvedSharedTypeWarning = (chunkFileName, symbols) => {
    const symbolList = Array.from(symbols).sort().join(", ");
    return [
        `Entry "${chunkFileName}" still references private shared type exports with no public re-export: ${symbolList}.`,
        "rollup-plugin-dts will not invent new public exports for these types.",
        "Re-export them from a public entry to avoid downstream TS2742 errors.",
    ].join(" ");
};
const getChunkImportSpecifier = (fromChunkFileName, toChunkFileName) => {
    const fromDir = path__namespace.posix.dirname(normalizeChunkPath(fromChunkFileName));
    const toRuntimeFileName = getChunkRuntimeFileName(normalizeChunkPath(toChunkFileName));
    let relativePath = path__namespace.posix.relative(fromDir, toRuntimeFileName);
    if (!relativePath.startsWith(".")) {
        relativePath = `./${relativePath}`;
    }
    return relativePath;
};
const getChunkRuntimeFileName = (fileName) => {
    if (fileName.endsWith(".d.mts")) {
        return `${fileName.slice(0, -6)}.mjs`;
    }
    if (fileName.endsWith(".d.cts")) {
        return `${fileName.slice(0, -6)}.cjs`;
    }
    if (fileName.endsWith(".d.ts")) {
        return `${fileName.slice(0, -5)}.js`;
    }
    return `${fileName}.js`;
};
const normalizeChunkPath = (fileName) => fileName.replaceAll("\\", "/");

/**
 * This is the *transform* part of `rollup-plugin-dts`.
 *
 * It sets a few input and output options, and otherwise is the core part of the
 * plugin responsible for bundling `.d.ts` files.
 *
 * That itself is a multi-step process:
 *
 * 1. The plugin has a preprocessing step that moves code around and cleans it
 *    up a bit, so that later steps can work with it easier. See `preprocess.ts`.
 * 2. It then converts the TypeScript AST into a ESTree-like AST that rollup
 *    understands. See `Transformer.ts`.
 * 3. After rollup is finished, the plugin will postprocess the output in a
 *    `renderChunk` hook. As rollup usually outputs javascript, it can output
 *    some code that is invalid in the context of a `.d.ts` file. In particular,
 *    the postprocess convert any javascript code that was created for namespace
 *    exports into TypeScript namespaces. See `NamespaceFixer.ts`.
 */
const transform = (enableSourcemap) => {
    const allTypeReferences = new Map();
    const allFileReferences = new Map();
    // Track pending sourcemaps to load lazily in generateBundle
    const pendingSourcemaps = new Map();
    return {
        name: "dts-transform",
        buildStart() {
            // Clear state for watch mode rebuilds
            allTypeReferences.clear();
            allFileReferences.clear();
            pendingSourcemaps.clear();
        },
        options({ onLog, ...options }) {
            return {
                ...options,
                onLog(level, log, defaultHandler) {
                    if (level === "warn" && log.code === "CIRCULAR_DEPENDENCY") {
                        return;
                    }
                    if (onLog) {
                        onLog(level, log, defaultHandler);
                    }
                    else {
                        defaultHandler(level, log);
                    }
                },
                treeshake: {
                    moduleSideEffects: "no-external",
                    propertyReadSideEffects: true,
                    unknownGlobalSideEffects: false,
                },
            };
        },
        outputOptions(options) {
            return {
                ...options,
                chunkFileNames: options.chunkFileNames || "[name]-[hash].d.ts",
                entryFileNames: options.entryFileNames || "[name].d.ts",
                format: "es",
                exports: "named",
                compact: false,
                freeze: true,
                interop: "esModule",
                generatedCode: Object.assign({ symbols: false }, options.generatedCode),
                strict: false,
            };
        },
        transform(code, fileName, inputMapTextOrOptions) {
            // `fileName` may not match the name in the moduleIds,
            // as we generate the `fileName` manually in the previews step,
            // so we need to find the correct moduleId.
            const name = trimExtension(fileName);
            const moduleIds = this.getModuleIds();
            const moduleId = Array.from(moduleIds).find((id) => trimExtension(id) === name);
            const isEntry = Boolean(moduleId && this.getModuleInfo(moduleId)?.isEntry);
            const isJSON = Boolean(moduleId && JSON_EXTENSIONS.test(moduleId));
            const inputMapText = typeof inputMapTextOrOptions === "string" ? inputMapTextOrOptions : undefined;
            // Preserve original code for loadInputSourcemap() before preProcess strips sourceMappingURL
            const rawCode = code;
            let sourceFile = parse(fileName, code);
            const preprocessed = preProcess({ sourceFile, isEntry, isJSON });
            // `sourceFile.fileName` here uses forward slashes
            allTypeReferences.set(sourceFile.fileName, preprocessed.typeReferences);
            allFileReferences.set(sourceFile.fileName, preprocessed.fileReferences);
            code = preprocessed.code.toString();
            sourceFile = parse(fileName, code);
            const converted = convert({ sourceFile });
            if (process.env.DTS_DUMP_AST) {
                console.log(fileName);
                console.log(code);
                console.log(JSON.stringify(converted.ast.body, undefined, 2));
            }
            if (!enableSourcemap) {
                return { code, ast: converted.ast, map: null };
            }
            // hires:true generates per-character mappings instead of per-line.
            // Without this, Go-to-Definition jumps to line starts instead of identifiers.
            const map = preprocessed.code.generateMap({ hires: true, source: fileName });
            // Store info for lazy sourcemap loading in generateBundle
            // For .d.ts files: will look for external .d.ts.map file
            // For .ts files compiled via generateDts(): inputMapText contains the in-memory map
            if (DTS_EXTENSIONS.test(fileName)) {
                pendingSourcemaps.set(fileName, {
                    fileName,
                    originalCode: rawCode,
                    inputMapText,
                });
            }
            return { code, ast: converted.ast, map };
        },
        renderChunk(inputCode, chunk, options, meta) {
            const source = parse(chunk.fileName, inputCode);
            const fixer = new NamespaceFixer(source);
            const typeReferences = new Set();
            const fileReferences = 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 allFileReferences.get(fileName.split("\\").join("/")) || []) {
                    if (ref.startsWith(".")) {
                        // Need absolute path of the target file here
                        const absolutePathToOriginal = path__namespace.join(path__namespace.dirname(fileName), ref);
                        const chunkFolder = (options.file && path__namespace.dirname(options.file)) ||
                            (chunk.facadeModuleId && path__namespace.dirname(chunk.facadeModuleId)) ||
                            ".";
                        let targetRelPath = path__namespace.relative(chunkFolder, absolutePathToOriginal).split("\\").join("/");
                        if (targetRelPath[0] !== ".") {
                            targetRelPath = "./" + targetRelPath;
                        }
                        fileReferences.add(targetRelPath);
                    }
                    else {
                        fileReferences.add(ref);
                    }
                }
            }
            let code = writeBlock(Array.from(fileReferences, (ref) => `/// <reference path="${ref}" />`));
            code += writeBlock(Array.from(typeReferences, (ref) => `/// <reference types="${ref}" />`));
            code += fixer.fix();
            if (!code) {
                code += "\nexport { };";
            }
            const typeOnlyFixer = new TypeOnlyFixer(chunk.fileName, code);
            const typesFixed = typeOnlyFixer.fix();
            // Build mapping from module paths to chunk filenames
            const moduleToChunk = new Map();
            for (const [chunkFileName, chunkInfo] of Object.entries(meta.chunks)) {
                for (const moduleId of Object.keys(chunkInfo.modules)) {
                    moduleToChunk.set(moduleId.split("\\").join("/"), chunkFileName);
                }
            }
            const moduleDeclarationFixer = new ModuleDeclarationFixer(chunk, "magicCode" in typesFixed && typesFixed.magicCode ? typesFixed.magicCode : new MagicString(code), !!options.sourcemap, moduleToChunk, meta.chunks, (message) => this.warn(message));
            return moduleDeclarationFixer.fix();
        },
        async generateBundle(options, bundle) {
            rewritePortableSharedChunkImportsInBundle(bundle, (message) => this.warn(message));
            // Fix sourcemap sources to point to original .ts files
            // When input .d.ts files have associated .d.ts.map files pointing to original .ts sources,
            // we use sourcemap remapping to compose the transform's map with the input map
            if (!options.sourcemap)
                return;
            // Lazily load input sourcemaps in parallel now that we know sourcemaps are enabled
            const inputSourcemaps = new Map();
            const entries = Array.from(pendingSourcemaps.entries());
            const loadedMaps = await Promise.all(entries.map(async ([fileName, info]) => ({
                fileName,
                inputMap: await loadInputSourcemap(info),
            })));
            // Helper to detect URL paths (http://, https://, file://, etc.)
            // Requires :// to avoid matching Windows drive letters like C:\ or D:/
            const isUrl = (p) => /^[a-z][a-z0-9+.-]*:\/\//i.test(p);
            for (const { fileName, inputMap } of loadedMaps) {
                if (inputMap && inputMap.sources) {
                    const inputMapDir = path__namespace.dirname(fileName);
                    // Resolve sourceRoot: preserve URLs verbatim, resolve filesystem paths
                    let sourceRoot;
                    if (inputMap.sourceRoot) {
                        sourceRoot = isUrl(inputMap.sourceRoot)
                            ? inputMap.sourceRoot
                            : path__namespace.resolve(inputMapDir, inputMap.sourceRoot);
                    }
                    else {
                        sourceRoot = inputMapDir;
                    }
                    const sourceRootIsUrl = isUrl(sourceRoot);
                    inputSourcemaps.set(fileName, {
                        version: inputMap.version ?? 3,
                        sources: inputMap.sources.map((source) => {
                            if (source === null)
                                return null;
                            // URL sources pass through unchanged
                            if (isUrl(source))
                                return source;
                            // URL sourceRoot: use URL constructor for proper path resolution (handles ../ segments)
                            if (sourceRootIsUrl) {
                                const base = sourceRoot.endsWith("/") ? sourceRoot : sourceRoot + "/";
                                return new URL(source, base).toString();
                            }
                            // Filesystem paths: use path.resolve
                            return path__namespace.isAbsolute(source) ? source : path__namespace.resolve(sourceRoot, source);
                        }),
                        // Note: sourcesContent intentionally not copied.
                        // TypeScript's declaration maps never include sourcesContent, and tsserver's
                        // getDocumentPositionMapper() rejects maps that have it (returns identity mapper).
                        // https://github.com/microsoft/TypeScript/blob/b19a9da2a3b8f2a720d314d01258dd2bdc110fef/src/services/sourcemaps.ts#L226
                        mappings: inputMap.mappings,
                        names: inputMap.names,
                    });
                }
            }
            const outputDir = options.dir || (options.file ? path__namespace.dirname(options.file) : process.cwd());
            for (const chunk of Object.values(bundle)) {
                if (chunk.type !== "chunk" || !chunk.map)
                    continue;
                // Check if any sources have input sourcemaps that need to be composed
                // Sources in chunk.map are relative to the chunk's output location, not the outputDir
                const chunkDir = path__namespace.join(outputDir, path__namespace.dirname(chunk.fileName));
                // Normalize path to relative forward-slash format for sourcemaps
                // Must be relative to chunkDir since sourcemap paths are relative to the .map file location
                // URL sources pass through unchanged (e.g., https://..., file://...)
                const toRelativeSourcePath = (source) => {
                    if (isUrl(source))
                        return source;
                    const relative = path__namespace.isAbsolute(source) ? path__namespace.relative(chunkDir, source) : source;
                    return relative.replaceAll("\\", "/");
                };
                const toRelativeSourcePathOrNull = (source) => source === null ? null : toRelativeSourcePath(source);
                const sourcesToRemap = new Map();
                for (const source of chunk.map.sources) {
                    if (!source)
                        continue;
                    // Skip URL sources - they can't be remapped via filesystem lookup
                    // and path.resolve would mangle them
                    if (isUrl(source))
                        continue;
                    const absoluteSource = path__namespace.resolve(chunkDir, source);
                    let inputMap = inputSourcemaps.get(absoluteSource);
                    // For .ts inputs compiled via generateDts(), the input map is stored under the .d.ts key
                    // but Rollup's chunk.map.sources uses the original .ts module ID.
                    // Match getDeclarationId() behavior: all source extensions (.ts, .mts, .tsx, etc.) → .d.ts
                    if (!inputMap && /\.[cm]?[tj]sx?$/.test(absoluteSource) && !absoluteSource.endsWith(".d.ts")) {
                        const dtsPath = absoluteSource.replace(/\.[cm]?[tj]sx?$/, ".d.ts");
                        inputMap = inputSourcemaps.get(dtsPath);
                    }
                    if (inputMap) {
                        sourcesToRemap.set(absoluteSource, inputMap);
                    }
                }
                if (sourcesToRemap.size === 0) {
                    // Always strip sourcesContent for TypeScript compatibility
                    // (tsserver rejects maps with sourcesContent)
                    delete chunk.map.sourcesContent;
                    // Handle empty chunks (like "export {};") - Rollup produces empty sources
                    // but we want to preserve original source references from input map
                    if (chunk.map.sources.length === 0 && chunk.facadeModuleId) {
                        const inputMap = inputSourcemaps.get(chunk.facadeModuleId);
                        if (inputMap && inputMap.sources.length > 0) {
                            const newSources = inputMap.sources.map(toRelativeSourcePathOrNull);
                            chunk.map.sources = newSources;
                        }
                    }
                    // Update the sourcemap asset (Rollup creates it separately from chunk.map)
                    updateSourcemapAsset(bundle, chunk.fileName, {
                        sources: chunk.map.sources.map(toRelativeSourcePathOrNull),
                        mappings: chunk.map.mappings,
                        names: chunk.map.names || [],
                    });
                    continue;
                }
                // For single-source cases where the input sourcemap also has a single source,
                // just replace the source directly to preserve the transform's hires mappings
                // This is important for Go-to-Definition to work line-by-line
                const isSingleSource = chunk.map.sources.length === 1 && sourcesToRemap.size === 1;
                const singleInputMap = isSingleSource ? Array.from(sourcesToRemap.values())[0] : null;
                const canSimplyReplace = singleInputMap && singleInputMap.sources.length === 1;
                let newSources;
                let newMappings;
                let newNames;
                if (canSimplyReplace && singleInputMap) {
                    // Sparse-Anchor Hydration: Rollup's output map is sparse (few segments per line)
                    // because the plugin uses a virtual AST that doesn't preserve token positions.
                    // Standard remapping via @jridgewell/remapping can only trace existing segments,
                    // not add new ones - so we'd lose the per-identifier mappings from the input map.
                    // Instead, we use Rollup's sparse segments as "anchors" to determine which source
                    // line each output line came from, then copy all detailed segments from that line.
                    newSources = singleInputMap.sources.map(toRelativeSourcePathOrNull);
                    newMappings = hydrateSourcemap(chunk.map.mappings, singleInputMap, chunk.code);
                    newNames = singleInputMap.names || [];
                }
                else {
                    // Track visited files to prevent infinite recursion.
                    // When TypeScript compiles foo.ts → foo.d.ts + foo.d.ts.map, the map's sources
                    // point back to foo.ts. If we return the same map when resolving foo.ts,
                    // @jridgewell/remapping will recursively try to resolve foo.ts again → infinite loop.
                    const visitedFiles = new Set();
                    const remapped = remapping(chunk.map, (file) => {
                        // File paths from remapping are relative to the chunk's output location
                        const absolutePath = path__namespace.resolve(chunkDir, file);
                        // Prevent infinite recursion: if we've already returned a map for this file,
                        // return null to stop the chain (this file is the original source)
                        if (visitedFiles.has(absolutePath)) {
                            return null;
                        }
                        visitedFiles.add(absolutePath);
                        const inputMap = sourcesToRemap.get(absolutePath);
                        if (inputMap) {
                            return inputMap;
                        }
                        return null;
                    });
                    newSources = remapped.sources.map(toRelativeSourcePathOrNull);
                    newMappings = typeof remapped.mappings === "string" ? remapped.mappings : "";
                    newNames = remapped.names || [];
                }
                chunk.map.sources = newSources;
                delete chunk.map.sourcesContent;
                chunk.map.mappings = newMappings;
                chunk.map.names = newNames;
                updateSourcemapAsset(bundle, chunk.fileName, {
                    sources: newSources,
                    mappings: newMappings,
                    names: newNames,
                });
            }
        },
    };
};
function writeBlock(codes) {
    if (codes.length) {
        return codes.join("\n") + "\n";
    }
    return "";
}
function updateSourcemapAsset(bundle, chunkFileName, data) {
    const mapFileName = `${chunkFileName}.map`;
    const mapAsset = bundle[mapFileName];
    if (mapAsset && mapAsset.type === "asset") {
        mapAsset.source = JSON.stringify({
            version: 3,
            // file should be just the basename since the .map is in the same directory
            file: path__namespace.basename(chunkFileName),
            ...data,
        });
    }
}

const TS_EXTENSIONS = /\.([cm]ts|[tj]sx?)$/;
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)) {
        return { code };
    }
    const isEntry = entries.includes(fileName);
    // Rollup doesn't tell you the entry point of each module in the bundle,
    // so we need to ask every TypeScript program for the given filename.
    const existingProgram = programs.find((p) => {
        // Entry points may be in the other entry source files, but it can't emit from them.
        // So we should find the program about the entry point which is the root files.
        if (isEntry) {
            return p.getRootFileNames().includes(fileName);
        }
        else {
            const sourceFile = p.getSourceFile(fileName);
            if (sourceFile && p.isSourceFileFromExternalLibrary(sourceFile)) {
                return false;
            }
            return !!sourceFile;
        }
    });
    if (existingProgram) {
        // we know this exists b/c of the .filter above, so this non-null assertion is safe
        const source = existingProgram.getSourceFile(fileName);
        return {
            code: source?.getFullText(),
            source,
            program: existingProgram,
        };
    }
    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, resolvedOptions.sourcemap);
        programs.push(newProgram);
        // we created hte program from this fileName, so the source file must exist :P
        const source = newProgram.getSourceFile(fileName);
        return {
            code: source?.getFullText(),
            source,
            program: newProgram,
        };
    }
    else {
        // the file isn't part of an existing program and doesn't exist on disk
        return null;
    }
}
const plugin = (options = {}) => {
    const ctx = { entries: [], programs: [], resolvedOptions: resolveDefaultOptions(options) };
    const transformPlugin = transform(ctx.resolvedOptions.sourcemap);
    return {
        name: "dts",
        // pass outputOptions, renderChunk, and generateBundle hooks to the inner transform plugin
        outputOptions: transformPlugin.outputOptions,
        renderChunk: transformPlugin.renderChunk,
        generateBundle: transformPlugin.generateBundle,
        options(options) {
            let { input = [] } = options;
            if (!Array.isArray(input)) {
                input = typeof input === "string" ? [input] : Object.values(input);
            }
            else if (input.length > 1) {
                // when dealing with multiple unnamed inputs, transform the inputs into
                // an explicit object, which strips the file extension
                options.input = {};
                for (const filename of input) {
                    let name = trimExtension(filename);
                    if (path__namespace.isAbsolute(filename)) {
                        name = path__namespace.basename(name);
                    }
                    else {
                        name = path__namespace.normalize(name);
                    }
                    options.input[name] = filename;
                }
            }
            ctx.programs = createPrograms(Object.values(input), ctx.resolvedOptions.compilerOptions, ctx.resolvedOptions.tsconfig, ctx.resolvedOptions.sourcemap);
            return transformPlugin.options.call(this, options);
        },
        transform(code, id) {
            if (!TS_EXTENSIONS.test(id) && !JSON_EXTENSIONS.test(id)) {
                return null;
            }
            const watchFiles = (module) => {
                if (module.program) {
                    const sourceDirectory = path__namespace.dirname(id);
                    const sourceFilesInProgram = module.program
                        .getSourceFiles()
                        .map((sourceFile) => sourceFile.fileName)
                        .filter((fileName) => fileName.startsWith(sourceDirectory));
                    sourceFilesInProgram.forEach(this.addWatchFile);
                }
            };
            const handleDtsFile = () => {
                const module = getModule(ctx, id, code);
                if (module) {
                    watchFiles(module);
                    return transformPlugin.transform.call(this, module.code, id);
                }
                return null;
            };
            const treatTsAsDts = () => {
                const declarationId = getDeclarationId(id);
                const module = getModule(ctx, declarationId, code);
                if (module) {
                    watchFiles(module);
                    return transformPlugin.transform.call(this, module.code, declarationId);
                }
                return null;
            };
            const generateDts = () => {
                const module = getModule(ctx, id, code);
                if (!module || !module.source || !module.program)
                    return null;
                watchFiles(module);
                const declarationId = getDeclarationId(id);
                // Capture both .d.ts and .d.ts.map from TypeScript emit
                // Emit order: .d.ts.map first, .d.ts second (verified via experiments/ts-emit-test.ts)
                let declarationText;
                let declarationMapText;
                const { emitSkipped, diagnostics } = module.program.emit(module.source, (emitFileName, text) => {
                    if (emitFileName.endsWith(".map")) {
                        declarationMapText = text;
                    }
                    else {
                        declarationText = text;
                    }
                }, undefined, // cancellationToken
                true, // emitOnlyDtsFiles
                undefined, // customTransformers
                // @ts-ignore This is a private API for workers, should be safe to use as TypeScript Playground has used it for a long time.
                true);
                if (emitSkipped) {
                    const errors = diagnostics.filter((diag) => diag.category === ts.DiagnosticCategory.Error);
                    if (errors.length) {
                        console.error(ts.formatDiagnostics(errors, formatHost));
                        this.error("Failed to compile. Check the logs above.");
                    }
                }
                if (!declarationText)
                    return null;
                // Strip //# sourceMappingURL comment from declaration text since we handle the map via inputMapText
                // Otherwise Rollup would double-process: once from the comment, once from our transform map
                // Note: TypeScript's emit only produces external maps (never inline), so this is safe.
                // If a custom transformer were to inject an inline map, it would also be stripped.
                const cleanDeclarationText = declarationText.replace(/\n?\/\/# sourceMappingURL=[^\n]+/, "");
                // Pass declaration map text to transform for sourcemap hydration
                return transformPlugin.transform.call(this, cleanDeclarationText, declarationId, declarationMapText);
            };
            // if it's a .d.ts file, handle it as-is
            if (DTS_EXTENSIONS.test(id))
                return handleDtsFile();
            // if it's a json file, use the typescript compiler to generate the declarations,
            // requires `compilerOptions.resolveJsonModule: true`.
            // This is also commonly used with `@rollup/plugin-json` to import JSON files.
            if (JSON_EXTENSIONS.test(id))
                return generateDts();
            // first attempt to treat .ts files as .d.ts files, and otherwise use the typescript compiler to generate the declarations
            return treatTsAsDts() ?? generateDts();
        },
        resolveId(source, importer) {
            if (!importer) {
                // store the entry point, because we need to know which program to add the file
                ctx.entries.push(path__namespace.resolve(source));
                return;
            }
            // normalize directory separators to forward slashes, as apparently typescript expects that?
            importer = importer.split("\\").join("/");
            let resolvedCompilerOptions = ctx.resolvedOptions.compilerOptions;
            if (ctx.resolvedOptions.tsconfig) {
                // Here we have a chicken and egg problem.
                // `source` would be resolved by `ts.nodeModuleNameResolver` a few lines below, but
                // `ts.nodeModuleNameResolver` requires `compilerOptions` which we have to resolve here,
                // since we have a custom `tsconfig.json`.
                // So, we use Node's resolver algorithm so we can see where the request is coming from so we
                // can load the custom `tsconfig.json` from the correct path.
                const resolvedSource = source.startsWith(".") ? path__namespace.resolve(path__namespace.dirname(importer), source) : source;
                resolvedCompilerOptions = getCompilerOptions(resolvedSource, ctx.resolvedOptions.compilerOptions, ctx.resolvedOptions.tsconfig, ctx.resolvedOptions.sourcemap).compilerOptions;
            }
            // resolve this via typescript
            const { resolvedModule } = ts.resolveModuleName(source, importer, {
                // Default moduleResolution to node10 if not explicitly set. TS6 changed
                // the default from node10 to bundler, and bundler resolution picks .js
                // files over directories with .d.ts index files, breaking .d.ts bundling.
                moduleResolution: ts.ModuleResolutionKind.Node10,
                ...resolvedCompilerOptions,
            }, ts.sys);
            if (!resolvedModule) {
                return;
            }
            if (resolvedModule.isExternalLibraryImport && resolvedModule.packageId && ctx.resolvedOptions.includeExternal.includes(resolvedModule.packageId.name)) {
                // include types from specified external modules
                return { id: path__namespace.resolve(resolvedModule.resolvedFileName) };
            }
            else if (!ctx.resolvedOptions.respectExternal && resolvedModule.isExternalLibraryImport) {
                // here, we define everything else that comes from `node_modules` as `external`.
                return { id: source, external: true };
            }
            else {
                // using `path.resolve` here converts paths back to the system specific separators
                return { id: path__namespace.resolve(resolvedModule.resolvedFileName) };
            }
        },
    };
};

exports.default = plugin;
exports.dts = plugin;