in apps/api-extractor/src/api/Extractor.ts [191:430]
public static invoke(extractorConfig: ExtractorConfig, options?: IExtractorInvokeOptions): ExtractorResult {
if (!options) {
options = {};
}
const localBuild: boolean = options.localBuild || false;
let compilerState: CompilerState | undefined;
if (options.compilerState) {
compilerState = options.compilerState;
} else {
compilerState = CompilerState.create(extractorConfig, options);
}
const messageRouter: MessageRouter = new MessageRouter({
workingPackageFolder: extractorConfig.packageFolder,
messageCallback: options.messageCallback,
messagesConfig: extractorConfig.messages || {},
showVerboseMessages: !!options.showVerboseMessages,
showDiagnostics: !!options.showDiagnostics,
tsdocConfiguration: extractorConfig.tsdocConfiguration
});
if (extractorConfig.tsdocConfigFile.filePath && !extractorConfig.tsdocConfigFile.fileNotFound) {
if (!Path.isEqual(extractorConfig.tsdocConfigFile.filePath, ExtractorConfig._tsdocBaseFilePath)) {
messageRouter.logVerbose(
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: object = MessageRouter.buildJsonDumpObject(
(compilerState.program as ts.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: TSDocConfigFile = TSDocConfigFile.loadFromParser(
extractorConfig.tsdocConfiguration
);
const serializedTSDocConfig: object = MessageRouter.buildJsonDumpObject(
combinedConfigFile.saveToObject()
);
messageRouter.logDiagnostic(JSON.stringify(serializedTSDocConfig, undefined, 2));
messageRouter.logDiagnosticFooter();
}
const collector: Collector = new Collector({
program: compilerState.program as ts.Program,
messageRouter,
extractorConfig: extractorConfig
});
collector.analyze();
DocCommentEnhancer.analyze(collector);
ValidationEnhancer.analyze(collector);
const modelBuilder: ApiModelGenerator = new ApiModelGenerator(collector);
const apiPackage: ApiPackage = modelBuilder.buildApiPackage();
if (messageRouter.showDiagnostics) {
messageRouter.logDiagnostic(''); // skip a line after any diagnostic messages
}
if (extractorConfig.docModelEnabled) {
messageRouter.logVerbose(
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: boolean = false;
if (extractorConfig.apiReportEnabled) {
const actualApiReportPath: string = extractorConfig.reportTempFilePath;
const actualApiReportShortPath: string = extractorConfig._getShortFilePath(
extractorConfig.reportTempFilePath
);
const expectedApiReportPath: string = extractorConfig.reportFilePath;
const expectedApiReportShortPath: string = extractorConfig._getShortFilePath(
extractorConfig.reportFilePath
);
const actualApiReportContent: string = ApiReportGenerator.generateReviewFileContent(collector);
// Write the actual file
FileSystem.writeFile(actualApiReportPath, actualApiReportContent, {
ensureFolderExists: true,
convertLineEndings: extractorConfig.newlineKind
});
// Compare it against the expected file
if (FileSystem.exists(expectedApiReportPath)) {
const expectedApiReportContent: string = FileSystem.readFile(expectedApiReportPath);
if (
!ApiReportGenerator.areEquivalentApiFileContents(actualApiReportContent, expectedApiReportContent)
) {
apiReportChanged = true;
if (!localBuild) {
// For a production build, issue a warning that will break the CI build.
messageRouter.logWarning(
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(
ConsoleMessageId.ApiReportCopied,
'You have changed the public API signature for this project.' +
` Updating ${expectedApiReportShortPath}`
);
FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, {
ensureFolderExists: true,
convertLineEndings: extractorConfig.newlineKind
});
}
} else {
messageRouter.logVerbose(
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(
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: string = path.dirname(expectedApiReportPath);
if (!FileSystem.exists(expectedApiReportFolder)) {
messageRouter.logError(
ConsoleMessageId.ApiReportFolderMissing,
'Unable to create the API report file. Please make sure the target folder exists:\n' +
expectedApiReportFolder
);
} else {
FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, {
convertLineEndings: extractorConfig.newlineKind
});
messageRouter.logWarning(
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,
DtsRollupKind.PublicRelease,
extractorConfig.newlineKind
);
Extractor._generateRollupDtsFile(
collector,
extractorConfig.betaTrimmedFilePath,
DtsRollupKind.BetaRelease,
extractorConfig.newlineKind
);
Extractor._generateRollupDtsFile(
collector,
extractorConfig.untrimmedFilePath,
DtsRollupKind.InternalRelease,
extractorConfig.newlineKind
);
}
if (extractorConfig.tsdocMetadataEnabled) {
// Write the tsdoc-metadata.json file for this project
PackageMetadataManager.writeTsdocMetadataFile(
extractorConfig.tsdocMetadataFilePath,
extractorConfig.newlineKind
);
}
// Show all the messages that we collected during analysis
messageRouter.handleRemainingNonConsoleMessages();
// Determine success
let succeeded: boolean;
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
});
}