Rush StackShopBlogEvents
Skip to main content

Invoking API Extractor

Sounds great! So... how exactly do we enable API Extractor for a new project?

Invoking via the command-line

The simplest way to invoke API Extractor is via the command-line.

1. Configure the TypeScript compiler for your project

For this tutorial, suppose we have a hypothetical library project whose package.json file looks like this:

awesome-widgets/package.json

{
"name": "awesome-widgets",
"version": "1.0.0",
"main": "./lib/index.js",
"typings": "./lib/index.d.ts"
}

Here we assume the library's main entry point is awesome-widgets/src/index.ts, which compiles to produce the index.js and index.d.ts files seen above. In your tsconfig.json file, you should enable the following settings:

  • "declaration": true - This enables generation of the .d.ts files that API Extractor will analyze. By design, TypeScript source files are not directly analyzed, but instead must be first processed by your compiler.

  • "declarationMap": true - This enables generation of .d.ts.map files that allow API Extractor errors to be reported using line numbers from your original source files; without this, the error locations will instead refer to the generated .d.ts files.

Our example tsconfig.json file might look like this:

awesome-widgets/tsconfig.json

{
"$schema": "http://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"declaration": true,
"sourceMap": true,
"declarationMap": true,
"types": [
],
"lib": [
"es5"
],
"outDir": "lib"
},
"include": [
"src/**/*.ts"
]
}

2. Install API Extractor

To install the NPM package in your global environment, use a command like this:

$ npm install -g @microsoft/api-extractor

Assuming your PATH environment variable is set up correctly, now you should now be able to invoke the api-extractor tool from your shell.

3. Create a template config file

Next, we need to create a config file api-extractor.json for your project. The following command will create a template file that shows all settings and their default values:

$ api-extractor init

We recommend to use this template for your real config file. However, since the template is fairly verbose, in this tutorial we will show condensed files without the extra comments. This page explains each setting in depth.

Comments in JSON files

Strictly speaking, JSON was originally intended as a machine interchange format, and thus does not formally support code comments. Recently JSON has gained popularity as a human-edited config file format, which obviously requires comments. As such, most serious JSON libraries can handle comments without any trouble. (A notable exception is JSON.parse(); don't use that -- it cannot validate schemas and has poor error reporting.)

VS Code highlights JSON comments as errors by default, but it provides an optional "JSON with comments" mode. To enable this, add this line to VS Code's settings.json:

"files.associations": { "*.json": "jsonc" }

GitHub also highlights comments as errors by default. To fix that, add this line to your .gitattributes file:

*.json  linguist-language=JSON-with-Comments

For a discussion of some other possibilities, see issue #1088.

Our convention is to put config files in the "config" subfolder, so folder tree might look like this:

  • awesome-widgets/package.json
  • awesome-widgets/tsconfig.json
  • awesome-widgets/config/api-extractor.json
  • awesome-widgets/lib/index.d.js
  • awesome-widgets/lib/index.js.map
  • awesome-widgets/lib/index.d.ts
  • awesome-widgets/lib/index.d.ts.map
  • awesome-widgets/src/index.ts

If your project doesn't use the "config" subfolder convention, you can also put api-extractor.json in your project folder. API Extractor will look for it in both places.

In the next few pages, we'll look at the individual settings in more detail. For now, we should simply make sure that the mainEntryPointFilePath matches the "typings" field in our package.json file above. The template assigns it like this:

  "mainEntryPointFilePath": "<projectFolder>/lib/index.d.ts",

...which matches the package.json "typings" field above.

4. Running the tool

Now we're ready to invoke the api-extractor command line. For a local (non-production) build, you would use these shell commands:

$ cd awesome-widgets

# First invoke the TypeScript compiler to make the .d.ts files
$ tsc

# Next, we invoke API Extractor
$ api-extractor run --local --verbose

If you're having trouble, the --diagnostics option prints additional information that can help to diagnose the issue.

# Print troubleshooting logs
$ api-extractor run --local --diagnostics

Compiler version incompatibilities

When API Extractor invokes the compiler engine to analyze your project, it uses its own TypeScript version. It cannot use your toolchain's version because the compiler engine API may be incompatible. This sometimes causes API Extractor to report compiler errors due to differences in the system typings between TypeScript versions. You can avoid this by specifying the --typescript-compiler-folder command-line option (IExtractorInvokeOptions.typescriptCompilerFolder in the API). This enables API Extractor to use the system typings from your toolchain's TypeScript folder.

If the issue is that your toolchain uses a newer compiler release than API Extractor's engine, please open a GitHub issue requesting to upgrade API Extractor's compiler. We try to stay as current as possible.

Invoking from a build script

If your project is built using a custom toolchain that is coded in TypeScript, you can alternatively invoke the API Extractor engine programmatically.

There are a lot of options, but here's a bare bones example:

import * as path from 'path';
import { Extractor, ExtractorConfig, ExtractorResult } from '@microsoft/api-extractor';

const apiExtractorJsonPath: string = path.join(__dirname, '../config/api-extractor.json');

// Load and parse the api-extractor.json file
const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath);

// Invoke API Extractor
const extractorResult: ExtractorResult = Extractor.invoke(extractorConfig, {
// Equivalent to the "--local" command-line parameter
localBuild: true,

// Equivalent to the "--verbose" command-line parameter
showVerboseMessages: true
});

if (extractorResult.succeeded) {
console.log(`API Extractor completed successfully`);
process.exitCode = 0;
} else {
console.error(
`API Extractor completed with ${extractorResult.errorCount} errors` +
` and ${extractorResult.warningCount} warnings`
);
process.exitCode = 1;
}

If you invoke API Extractor multiple times for a single tsconfig.json environment, this approach also allows you to reuse the same CompilerState object across multiple invocations. This can be a significant performance optimization, since the TypeScript compiler analysis is relatively expensive. Take a look at the api-extractor-scenarios/src/runScenarios.ts test runner for a real world example of how to do this.

Reusing settings across projects

The api-extractor.json file contents are completely described by the IConfigFile interface, which you can use to construct the ExtractorConfig object. With this approach it's possible to avoid creating an api-extractor.json file entirely, but we generally recommend not to do that. When developers are troubleshooting problems, it's very useful to have your actual configuration represented in a standard config file that people can inspect and tinker with. Also, if you ever need to debug API Extractor itself, it's probably easier to debug the isolated api-extractor process than a complex toolchain, but you'll need an api-extractor.json file for that.

So... if you work in a modern monorepo with many different projects, how can you ensure they have consistent API Extractor settings without a lot of copy+pasting of api-extractor.json files? Following the convention of tsconfig.json and tslint.json, API Extractor supports an "extends" field that allows your api-extractor.json file to inherit its configuration from a shared template file. See here for details.

Now that we've got things running, let's look at how to configure the three different output types...