Skip to content

[api-extractor] Allow configuration of external packages #5470

@Danielku15

Description

@Danielku15

Summary

I am having a monorepo setup where some files are included via code sharing (using typescript path mappings).

In such a setup API extractor detects the imported files from the other projects as internal and tries to crawl and bundle them into the final types and API.

But according to my build setup the imported files will be external references in the final output.

To mitigate this problem I want to tell API extractor somehow that these imports are external modules.

Details

  1. API extractor detects the external modules here:
    private _isExternalModulePath(
    importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration | ts.ImportTypeNode,
    moduleSpecifier: string
    ): boolean {
    let specifier: ts.TypeNode | ts.Expression | undefined = ts.isImportTypeNode(importOrExportDeclaration)
    ? importOrExportDeclaration.argument
    : importOrExportDeclaration.moduleSpecifier;
    if (specifier && ts.isLiteralTypeNode(specifier)) {
    specifier = specifier.literal;
    }
    const mode: ts.ModuleKind.CommonJS | ts.ModuleKind.ESNext | undefined =
    specifier && ts.isStringLiteralLike(specifier)
    ? TypeScriptInternals.getModeForUsageLocation(
    importOrExportDeclaration.getSourceFile(),
    specifier,
    this._program.getCompilerOptions()
    )
    : undefined;
    const resolvedModule: ts.ResolvedModuleFull | undefined = TypeScriptInternals.getResolvedModule(
    this._program,
    importOrExportDeclaration.getSourceFile(),
    moduleSpecifier,
    mode
    );
    if (resolvedModule === undefined) {
    // The TS compiler API `getResolvedModule` cannot resolve ambient modules. Thus, to match API Extractor's
    // previous behavior, simply treat all ambient modules as external. This bug is tracked by
    // https://github.com/microsoft/rushstack/issues/3335.
    return true;
    }
    // Either something like `jquery` or `@microsoft/api-extractor`.
    const packageName: string | undefined = resolvedModule.packageId?.name;
    if (packageName !== undefined && this._bundledPackageNames.has(packageName)) {
    return false;
    }
    if (resolvedModule.isExternalLibraryImport === undefined) {
    // This presumably means the compiler couldn't figure out whether the module was external, but we're not
    // sure how this can happen.
    throw new InternalError(
    `Cannot determine whether the module ${JSON.stringify(moduleSpecifier)} is external\n` +
    SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration)
    );
    }
    return resolvedModule.isExternalLibraryImport;
    }
  2. There is already an option to additionally include packages
  3. There is no option to force an import to be an external module.

Bundlers like Rollup/Rolldown have dedicated options like: https://rollupjs.org/configuration-options/#external to control the externals. I think a similar string+regex option could solve this.

Standard questions

Please answer these questions to help us investigate your issue more quickly:

Question Answer
@microsoft/api-extractor version? 7.53.1
Operating system? Wom
API Extractor scenario? rollups (.d.ts) -->
Would you consider contributing a PR? Yes
TypeScript compiler version? 5.9.3
Node.js version (node -v)? v24.11.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Needs triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions