utils/node_modules/@microsoft/api-extractor/lib/api/Extractor.js

293 lines
16 KiB
JavaScript
Raw Normal View History

2024-02-07 01:33:07 -05:00
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Extractor = exports.ExtractorResult = void 0;
const path = __importStar(require("path"));
const semver = __importStar(require("semver"));
const ts = __importStar(require("typescript"));
const resolve = __importStar(require("resolve"));
const node_core_library_1 = require("@rushstack/node-core-library");
const ExtractorConfig_1 = require("./ExtractorConfig");
const Collector_1 = require("../collector/Collector");
const DtsRollupGenerator_1 = require("../generators/DtsRollupGenerator");
const ApiModelGenerator_1 = require("../generators/ApiModelGenerator");
const ApiReportGenerator_1 = require("../generators/ApiReportGenerator");
const PackageMetadataManager_1 = require("../analyzer/PackageMetadataManager");
const ValidationEnhancer_1 = require("../enhancers/ValidationEnhancer");
const DocCommentEnhancer_1 = require("../enhancers/DocCommentEnhancer");
const CompilerState_1 = require("./CompilerState");
const MessageRouter_1 = require("../collector/MessageRouter");
const tsdoc_config_1 = require("@microsoft/tsdoc-config");
const SourceMapper_1 = require("../collector/SourceMapper");
/**
* This object represents the outcome of an invocation of API Extractor.
*
* @public
*/
class ExtractorResult {
/** @internal */
constructor(properties) {
this.compilerState = properties.compilerState;
this.extractorConfig = properties.extractorConfig;
this.succeeded = properties.succeeded;
this.apiReportChanged = properties.apiReportChanged;
this.errorCount = properties.errorCount;
this.warningCount = properties.warningCount;
}
}
exports.ExtractorResult = ExtractorResult;
/**
* The starting point for invoking the API Extractor tool.
* @public
*/
class Extractor {
/**
* Returns the version number of the API Extractor NPM package.
*/
static get version() {
return Extractor._getPackageJson().version;
}
/**
* Returns the package name of the API Extractor NPM package.
*/
static get packageName() {
return Extractor._getPackageJson().name;
}
static _getPackageJson() {
return node_core_library_1.PackageJsonLookup.loadOwnPackageJson(__dirname);
}
/**
* Load the api-extractor.json config file from the specified path, and then invoke API Extractor.
*/
static loadConfigAndInvoke(configFilePath, options) {
const extractorConfig = ExtractorConfig_1.ExtractorConfig.loadFileAndPrepare(configFilePath);
return Extractor.invoke(extractorConfig, options);
}
/**
* Invoke API Extractor using an already prepared `ExtractorConfig` object.
*/
static invoke(extractorConfig, options) {
if (!options) {
options = {};
}
const localBuild = options.localBuild || false;
let compilerState;
if (options.compilerState) {
compilerState = options.compilerState;
}
else {
compilerState = CompilerState_1.CompilerState.create(extractorConfig, options);
}
const sourceMapper = new SourceMapper_1.SourceMapper();
const messageRouter = new MessageRouter_1.MessageRouter({
workingPackageFolder: extractorConfig.packageFolder,
messageCallback: options.messageCallback,
messagesConfig: extractorConfig.messages || {},
showVerboseMessages: !!options.showVerboseMessages,
showDiagnostics: !!options.showDiagnostics,
tsdocConfiguration: extractorConfig.tsdocConfiguration,
sourceMapper
});
if (extractorConfig.tsdocConfigFile.filePath && !extractorConfig.tsdocConfigFile.fileNotFound) {
if (!node_core_library_1.Path.isEqual(extractorConfig.tsdocConfigFile.filePath, ExtractorConfig_1.ExtractorConfig._tsdocBaseFilePath)) {
messageRouter.logVerbose("console-using-custom-tsdoc-config" /* ConsoleMessageId.UsingCustomTSDocConfig */, 'Using custom TSDoc config from ' + extractorConfig.tsdocConfigFile.filePath);
}
}
this._checkCompilerCompatibility(extractorConfig, messageRouter);
if (messageRouter.showDiagnostics) {
messageRouter.logDiagnostic('');
messageRouter.logDiagnosticHeader('Final prepared ExtractorConfig');
messageRouter.logDiagnostic(extractorConfig.getDiagnosticDump());
messageRouter.logDiagnosticFooter();
messageRouter.logDiagnosticHeader('Compiler options');
const serializedCompilerOptions = MessageRouter_1.MessageRouter.buildJsonDumpObject(compilerState.program.getCompilerOptions());
messageRouter.logDiagnostic(JSON.stringify(serializedCompilerOptions, undefined, 2));
messageRouter.logDiagnosticFooter();
messageRouter.logDiagnosticHeader('TSDoc configuration');
// Convert the TSDocConfiguration into a tsdoc.json representation
const combinedConfigFile = tsdoc_config_1.TSDocConfigFile.loadFromParser(extractorConfig.tsdocConfiguration);
const serializedTSDocConfig = MessageRouter_1.MessageRouter.buildJsonDumpObject(combinedConfigFile.saveToObject());
messageRouter.logDiagnostic(JSON.stringify(serializedTSDocConfig, undefined, 2));
messageRouter.logDiagnosticFooter();
}
const collector = new Collector_1.Collector({
program: compilerState.program,
messageRouter,
extractorConfig: extractorConfig,
sourceMapper
});
collector.analyze();
DocCommentEnhancer_1.DocCommentEnhancer.analyze(collector);
ValidationEnhancer_1.ValidationEnhancer.analyze(collector);
const modelBuilder = new ApiModelGenerator_1.ApiModelGenerator(collector);
const apiPackage = modelBuilder.buildApiPackage();
if (messageRouter.showDiagnostics) {
messageRouter.logDiagnostic(''); // skip a line after any diagnostic messages
}
if (extractorConfig.docModelEnabled) {
messageRouter.logVerbose("console-writing-doc-model-file" /* ConsoleMessageId.WritingDocModelFile */, 'Writing: ' + extractorConfig.apiJsonFilePath);
apiPackage.saveToJsonFile(extractorConfig.apiJsonFilePath, {
toolPackage: Extractor.packageName,
toolVersion: Extractor.version,
newlineConversion: extractorConfig.newlineKind,
ensureFolderExists: true,
testMode: extractorConfig.testMode
});
}
let apiReportChanged = false;
if (extractorConfig.apiReportEnabled) {
const actualApiReportPath = extractorConfig.reportTempFilePath;
const actualApiReportShortPath = extractorConfig._getShortFilePath(extractorConfig.reportTempFilePath);
const expectedApiReportPath = extractorConfig.reportFilePath;
const expectedApiReportShortPath = extractorConfig._getShortFilePath(extractorConfig.reportFilePath);
const actualApiReportContent = ApiReportGenerator_1.ApiReportGenerator.generateReviewFileContent(collector);
// Write the actual file
node_core_library_1.FileSystem.writeFile(actualApiReportPath, actualApiReportContent, {
ensureFolderExists: true,
convertLineEndings: extractorConfig.newlineKind
});
// Compare it against the expected file
if (node_core_library_1.FileSystem.exists(expectedApiReportPath)) {
const expectedApiReportContent = node_core_library_1.FileSystem.readFile(expectedApiReportPath);
if (!ApiReportGenerator_1.ApiReportGenerator.areEquivalentApiFileContents(actualApiReportContent, expectedApiReportContent)) {
apiReportChanged = true;
if (!localBuild) {
// For a production build, issue a warning that will break the CI build.
messageRouter.logWarning("console-api-report-not-copied" /* ConsoleMessageId.ApiReportNotCopied */, 'You have changed the public API signature for this project.' +
` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` +
` or perform a local build (which does this automatically).` +
` See the Git repo documentation for more info.`);
}
else {
// For a local build, just copy the file automatically.
messageRouter.logWarning("console-api-report-copied" /* ConsoleMessageId.ApiReportCopied */, 'You have changed the public API signature for this project.' +
` Updating ${expectedApiReportShortPath}`);
node_core_library_1.FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, {
ensureFolderExists: true,
convertLineEndings: extractorConfig.newlineKind
});
}
}
else {
messageRouter.logVerbose("console-api-report-unchanged" /* ConsoleMessageId.ApiReportUnchanged */, `The API report is up to date: ${actualApiReportShortPath}`);
}
}
else {
// The target file does not exist, so we are setting up the API review file for the first time.
//
// NOTE: People sometimes make a mistake where they move a project and forget to update the "reportFolder"
// setting, which causes a new file to silently get written to the wrong place. This can be confusing.
// Thus we treat the initial creation of the file specially.
apiReportChanged = true;
if (!localBuild) {
// For a production build, issue a warning that will break the CI build.
messageRouter.logWarning("console-api-report-not-copied" /* ConsoleMessageId.ApiReportNotCopied */, 'The API report file is missing.' +
` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` +
` or perform a local build (which does this automatically).` +
` See the Git repo documentation for more info.`);
}
else {
const expectedApiReportFolder = path.dirname(expectedApiReportPath);
if (!node_core_library_1.FileSystem.exists(expectedApiReportFolder)) {
messageRouter.logError("console-api-report-folder-missing" /* ConsoleMessageId.ApiReportFolderMissing */, 'Unable to create the API report file. Please make sure the target folder exists:\n' +
expectedApiReportFolder);
}
else {
node_core_library_1.FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, {
convertLineEndings: extractorConfig.newlineKind
});
messageRouter.logWarning("console-api-report-created" /* ConsoleMessageId.ApiReportCreated */, 'The API report file was missing, so a new file was created. Please add this file to Git:\n' +
expectedApiReportPath);
}
}
}
}
if (extractorConfig.rollupEnabled) {
Extractor._generateRollupDtsFile(collector, extractorConfig.publicTrimmedFilePath, DtsRollupGenerator_1.DtsRollupKind.PublicRelease, extractorConfig.newlineKind);
Extractor._generateRollupDtsFile(collector, extractorConfig.alphaTrimmedFilePath, DtsRollupGenerator_1.DtsRollupKind.AlphaRelease, extractorConfig.newlineKind);
Extractor._generateRollupDtsFile(collector, extractorConfig.betaTrimmedFilePath, DtsRollupGenerator_1.DtsRollupKind.BetaRelease, extractorConfig.newlineKind);
Extractor._generateRollupDtsFile(collector, extractorConfig.untrimmedFilePath, DtsRollupGenerator_1.DtsRollupKind.InternalRelease, extractorConfig.newlineKind);
}
if (extractorConfig.tsdocMetadataEnabled) {
// Write the tsdoc-metadata.json file for this project
PackageMetadataManager_1.PackageMetadataManager.writeTsdocMetadataFile(extractorConfig.tsdocMetadataFilePath, extractorConfig.newlineKind);
}
// Show all the messages that we collected during analysis
messageRouter.handleRemainingNonConsoleMessages();
// Determine success
let succeeded;
if (localBuild) {
// For a local build, fail if there were errors (but ignore warnings)
succeeded = messageRouter.errorCount === 0;
}
else {
// For a production build, fail if there were any errors or warnings
succeeded = messageRouter.errorCount + messageRouter.warningCount === 0;
}
return new ExtractorResult({
compilerState,
extractorConfig,
succeeded,
apiReportChanged,
errorCount: messageRouter.errorCount,
warningCount: messageRouter.warningCount
});
}
static _checkCompilerCompatibility(extractorConfig, messageRouter) {
messageRouter.logInfo("console-preamble" /* ConsoleMessageId.Preamble */, `Analysis will use the bundled TypeScript version ${ts.version}`);
try {
const typescriptPath = resolve.sync('typescript', {
basedir: extractorConfig.projectFolder,
preserveSymlinks: false
});
const packageJsonLookup = new node_core_library_1.PackageJsonLookup();
const packageJson = packageJsonLookup.tryLoadNodePackageJsonFor(typescriptPath);
if (packageJson && packageJson.version && semver.valid(packageJson.version)) {
// Consider a newer MINOR release to be incompatible
const ourMajor = semver.major(ts.version);
const ourMinor = semver.minor(ts.version);
const theirMajor = semver.major(packageJson.version);
const theirMinor = semver.minor(packageJson.version);
if (theirMajor > ourMajor || (theirMajor === ourMajor && theirMinor > ourMinor)) {
messageRouter.logInfo("console-compiler-version-notice" /* ConsoleMessageId.CompilerVersionNotice */, `*** The target project appears to use TypeScript ${packageJson.version} which is newer than the` +
` bundled compiler engine; consider upgrading API Extractor.`);
}
}
}
catch (e) {
// The compiler detection heuristic is not expected to work in many configurations
}
}
static _generateRollupDtsFile(collector, outputPath, dtsKind, newlineKind) {
if (outputPath !== '') {
collector.messageRouter.logVerbose("console-writing-dts-rollup" /* ConsoleMessageId.WritingDtsRollup */, `Writing package typings: ${outputPath}`);
DtsRollupGenerator_1.DtsRollupGenerator.writeTypingsFile(collector, outputPath, dtsKind, newlineKind);
}
}
}
exports.Extractor = Extractor;
//# sourceMappingURL=Extractor.js.map