Files
rspade_system/node_modules/rollup-plugin-dts/dist/rollup-plugin-dts.mjs
2025-12-03 21:28:08 +00:00

2363 lines
94 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as path from 'node:path';
import ts from 'typescript';
import { createRequire } from 'node:module';
import MagicString from 'magic-string';
function resolveDefaultOptions(options) {
return {
...options,
compilerOptions: options.compilerOptions ?? {},
respectExternal: options.respectExternal ?? false,
includeExternal: options.includeExternal ?? [],
};
}
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.dirname(fromPath)) {
fromPath = path.dirname(fromPath);
logCache("up", fromPath);
if (configByPath.has(fromPath))
return logCache("has", fromPath);
configByPath.set(fromPath, config);
}
}
function getCompilerOptions(input, overrideOptions, overrideConfigPath) {
const compilerOptions = { ...DEFAULT_OPTIONS, ...overrideOptions };
let dirName = path.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.resolve(process.cwd(), overrideConfigPath)
: ts.findConfigFile(dirName, ts.sys.fileExists);
if (!configPath) {
return { dtsFiles, dirName, compilerOptions };
}
const inputDirName = dirName;
dirName = path.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) {
const { dtsFiles, compilerOptions } = getCompilerOptions(fileName, overrideOptions, tsconfig);
return ts.createProgram([fileName].concat(Array.from(dtsFiles)), compilerOptions, ts.createCompilerHost(compilerOptions, true));
}
function createPrograms(input, overrideOptions, tsconfig) {
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.resolve(main);
const options = getCompilerOptions(main, overrideOptions, tsconfig);
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 = createRequire(import.meta.url);
({ 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 {
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 && ts.isModuleBlock(node.body)) {
for (const stmt of node.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 dont 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$1 = [];
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$1.push({
exportedName: prop.name.text,
localName: prop.initializer.getText(),
});
}
// sort in reverse order, since we will do string manipulation
namespaces.unshift({
name,
exports: exports$1,
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.getText(),
}, 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 {
constructor(code) {
this.fileName = "index.d.ts";
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 {
constructor(fileName, rawCode) {
this.DEBUG = !!process.env.DTS_EXPORTS_FIXER_DEBUG;
this.types = new Set();
this.values = new Set();
this.typeHints = new Map();
this.reExportTypeHints = new Map();
this.importNodes = [];
this.exportNodes = [];
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(element.getText());
}
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) {
this.DEBUG && console.log(node.getText(), node.kind);
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.DEBUG && console.log(`${node.name.getFullText()} is a type`);
this.types.add(node.name.text);
continue;
}
if (ts.isTypeAliasDeclaration(node)) {
const alias = node.name.text;
this.DEBUG && console.log(`${node.name.getFullText()} is a type`);
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.DEBUG && console.log(`${declaration.name.getFullText()} is a value (from var statement)`);
this.values.add(declaration.name.text);
}
}
}
else {
if (node.name) {
this.DEBUG && console.log(`${node.name.getFullText()} is a value (from declaration)`);
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.DEBUG && console.log(`${node.name.getFullText()} is a value (from module declaration)`);
this.values.add(node.name.text);
}
this.analyze(node.getChildren());
continue;
}
this.DEBUG && console.log("unhandled statement", node.getFullText(), node.kind);
}
}
// 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 preProcessNamespaceBody(body, code, sourceFile) {
for (const stmt of body.statements) {
// Safely call the new context-aware function on all children
fixModifiers(code, stmt);
// Recurse for nested namespaces
if (ts.isModuleDeclaration(stmt) && stmt.body && ts.isModuleBlock(stmt.body)) {
preProcessNamespaceBody(stmt.body, code);
}
}
}
/**
* The pre-process step has the following goals:
* - [x] Fixes the "modifiers", removing any `export` modifier and adding any
* 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 cant 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`.
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)) {
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);
}
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 || !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 {
constructor({ id, range }) {
/**
* 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.
*/
this.scopes = [];
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;
}
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 {
constructor(sourceFile) {
this.sourceFile = sourceFile;
this.declarations = new Map();
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);
}
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,
};
}
}
class RelativeModuleDeclarationFixer {
constructor(fileName, code, sourcemap, name) {
this.sourcemap = sourcemap;
this.DEBUG = !!process.env.DTS_EXPORTS_FIXER_DEBUG;
this.relativeModuleDeclarations = [];
this.source = parse(fileName, code.toString());
this.code = code;
this.name = name || "./index";
}
fix() {
this.analyze(this.source.statements);
for (const node of this.relativeModuleDeclarations) {
const start = node.getStart();
const end = node.getEnd();
const quote = node.name.kind === ts.SyntaxKind.StringLiteral && "singleQuote" in node.name && node.name.singleQuote
? "'"
: '"';
const code = `declare module ${quote}${this.name}${quote} ${node.body.getText()}`;
this.code.overwrite(start, end, code);
}
return {
code: this.code.toString(),
map: this.relativeModuleDeclarations.length && this.sourcemap ? this.code.generateMap() : null,
};
}
analyze(nodes) {
for (const node of nodes) {
if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body) && /^\.\.?\//.test(node.name.text)) {
if (this.DEBUG) {
console.log(`Found relative module declaration: ${node.name.text} in ${this.source.fileName}`);
}
this.relativeModuleDeclarations.push(node);
}
}
}
}
/**
* This is the *transform* part of `rollup-plugin-dts`.
*
* 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 = () => {
const allTypeReferences = new Map();
const allFileReferences = new Map();
return {
name: "dts-transform",
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) {
// `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));
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));
}
return { code, ast: converted.ast, map: preprocessed.code.generateMap() };
},
renderChunk(inputCode, chunk, options) {
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.join(path.dirname(fileName), ref);
const chunkFolder = (options.file && path.dirname(options.file)) ||
(chunk.facadeModuleId && path.dirname(chunk.facadeModuleId)) ||
".";
let targetRelPath = path.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();
const relativeModuleDeclarationFixed = new RelativeModuleDeclarationFixer(chunk.fileName, "magicCode" in typesFixed && typesFixed.magicCode ? typesFixed.magicCode : new MagicString(code), !!options.sourcemap, "./" + (options.file && options.file !== "-" ? path.basename(options.file, ".d.ts") : "index"));
return relativeModuleDeclarationFixed.fix();
},
};
};
function writeBlock(codes) {
if (codes.length) {
return codes.join("\n") + "\n";
}
return "";
}
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);
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 transformPlugin = transform();
const ctx = { entries: [], programs: [], resolvedOptions: resolveDefaultOptions(options) };
return {
name: "dts",
// pass outputOptions & renderChunk hooks to the inner transform plugin
outputOptions: transformPlugin.outputOptions,
renderChunk: transformPlugin.renderChunk,
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.isAbsolute(filename)) {
name = path.basename(name);
}
else {
name = path.normalize(name);
}
options.input[name] = filename;
}
}
ctx.programs = createPrograms(Object.values(input), ctx.resolvedOptions.compilerOptions, ctx.resolvedOptions.tsconfig);
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.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);
let generated;
const { emitSkipped, diagnostics } = module.program.emit(module.source, (_, declarationText) => {
generated = transformPlugin.transform.call(this, declarationText, declarationId);
}, 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.");
}
}
return generated;
};
// 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.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.resolve(path.dirname(importer), source) : source;
resolvedCompilerOptions = getCompilerOptions(resolvedSource, ctx.resolvedOptions.compilerOptions, ctx.resolvedOptions.tsconfig).compilerOptions;
}
// resolve this via typescript
const { resolvedModule } = ts.resolveModuleName(source, importer, 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.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.resolve(resolvedModule.resolvedFileName) };
}
},
};
};
export { plugin as default, plugin as dts };