feat(api-extractor): support multiple entrypoints (#10829)

* feat(api-extractor): support multiple entrypoints

* chore: initial support in generateSplitDocumentation

* chore: bring in line with upstream

* refactor: multiple entrypoints in scripts

* fix: split docs

* feat: website

* fix: docs failing on next

* fix: don't include dtypes for now

* refactor: don't fetch entrypoint if there is none

---------

Co-authored-by: iCrawl <buechler.noel@outlook.com>
This commit is contained in:
Qjuh
2025-05-12 23:48:41 +02:00
committed by GitHub
parent 4f5e5c7c14
commit b3db92edfb
93 changed files with 2330 additions and 1956 deletions

View File

@@ -66,9 +66,12 @@ export class CompilerState {
);
}
const inputFilePaths: string[] = commandLine.fileNames.concat(extractorConfig.mainEntryPointFilePath);
const inputFilePaths: string[] = commandLine.fileNames.concat(
extractorConfig.mainEntryPointFilePath.filePath,
extractorConfig.additionalEntryPoints.map((ep) => ep.filePath),
);
if (options?.additionalEntryPoints) {
inputFilePaths.push(...options.additionalEntryPoints);
inputFilePaths.push(...options.additionalEntryPoints.map((entryPoint) => entryPoint));
}
// Append the entry points and remove any non-declaration files from the list

View File

@@ -70,6 +70,11 @@ export const enum ConsoleMessageId {
*/
UsingCustomTSDocConfig = 'console-using-custom-tsdoc-config',
/**
* "Generating ___ API report: ___"
*/
WritingApiReport = 'console-writing-api-report',
/**
* "Writing: ___"
*/

View File

@@ -26,7 +26,7 @@ import { ApiReportGenerator } from '../generators/ApiReportGenerator.js';
import { DtsRollupGenerator, DtsRollupKind } from '../generators/DtsRollupGenerator.js';
import { CompilerState } from './CompilerState.js';
import { ConsoleMessageId } from './ConsoleMessageId.js';
import { ExtractorConfig } from './ExtractorConfig.js';
import { ExtractorConfig, type IExtractorConfigApiReport } from './ExtractorConfig.js';
import type { ExtractorMessage } from './ExtractorMessage.js';
/**
@@ -263,14 +263,14 @@ export class Extractor {
DocCommentEnhancer.analyze(collector);
ValidationEnhancer.analyze(collector);
const modelBuilder: ApiModelGenerator = new ApiModelGenerator(collector);
const modelBuilder: ApiModelGenerator = new ApiModelGenerator(collector, extractorConfig);
const apiPackage: ApiPackage = modelBuilder.buildApiPackage();
if (messageRouter.showDiagnostics) {
messageRouter.logDiagnostic(''); // skip a line after any diagnostic messages
}
if (extractorConfig.docModelEnabled) {
if (modelBuilder.docModelEnabled) {
messageRouter.logVerbose(ConsoleMessageId.WritingDocModelFile, 'Writing: ' + extractorConfig.apiJsonFilePath);
apiPackage.saveToJsonFile(extractorConfig.apiJsonFilePath, {
toolPackage: Extractor.packageName,
@@ -282,93 +282,22 @@ export class Extractor {
});
}
let apiReportChanged = false;
function writeApiReport(reportConfig: IExtractorConfigApiReport): boolean {
return Extractor._writeApiReport(
collector,
extractorConfig,
messageRouter,
extractorConfig.reportTempFolder,
extractorConfig.reportFolder,
reportConfig,
localBuild,
);
}
let anyReportChanged = 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)) {
messageRouter.logVerbose(
ConsoleMessageId.ApiReportUnchanged,
`The API report is up to date: ${actualApiReportShortPath}`,
);
} else {
apiReportChanged = true;
if (localBuild) {
// 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 {
// 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 {
// 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) {
const expectedApiReportFolder: string = path.dirname(expectedApiReportPath);
if (FileSystem.exists(expectedApiReportFolder)) {
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,
);
} else {
messageRouter.logError(
ConsoleMessageId.ApiReportFolderMissing,
'Unable to create the API report file. Please make sure the target folder exists:\n' +
expectedApiReportFolder,
);
}
} else {
// 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.`,
);
}
for (const reportConfig of extractorConfig.reportConfigs) {
anyReportChanged = writeApiReport(reportConfig) || anyReportChanged;
}
}
@@ -421,12 +350,151 @@ export class Extractor {
compilerState,
extractorConfig,
succeeded,
apiReportChanged,
apiReportChanged: anyReportChanged,
errorCount: messageRouter.errorCount,
warningCount: messageRouter.warningCount,
});
}
/**
* Generates the API report at the specified release level, writes it to the specified file path, and compares
* the output to the existing report (if one exists).
*
* @param collector - The collector to get the entities from.
* @param extractorConfig - The configuration for extracting.
* @param messageRouter - The message router to use.
* @param reportTempDirectoryPath - The path to the directory under which the temp report file will be written prior
* to comparison with an existing report.
* @param reportDirectoryPath - The path to the directory under which the existing report file is located, and to
* which the new report will be written post-comparison.
* @param reportConfig - API report configuration, including its file name and {@link ApiReportVariant}.
* @param localBuild - Whether the report is made locally.
* @returns Whether or not the newly generated report differs from the existing report (if one exists).
*/
private static _writeApiReport(
collector: Collector,
extractorConfig: ExtractorConfig,
messageRouter: MessageRouter,
reportTempDirectoryPath: string,
reportDirectoryPath: string,
reportConfig: IExtractorConfigApiReport,
localBuild: boolean,
): boolean {
let apiReportChanged = false;
const actualApiReportPathWithoutExtension: string = path
.resolve(reportTempDirectoryPath, reportConfig.fileName)
.replace(/\.api\.md$/, '');
const expectedApiReportPathWithoutExtension: string = path
.resolve(reportDirectoryPath, reportConfig.fileName)
.replace(/\.api\.md$/, '');
const actualApiReportContentMap: Map<string, string> = ApiReportGenerator.generateReviewFileContent(
collector,
reportConfig.variant,
);
for (const [modulePath, actualApiReportContent] of actualApiReportContentMap) {
const actualEntryPointApiReportPath = `${actualApiReportPathWithoutExtension}${
modulePath ? '.' : ''
}${modulePath}.api.md`;
const actualEntryPointApiReportShortPath: string =
extractorConfig._getShortFilePath(actualEntryPointApiReportPath);
const expectedEntryPointApiReportPath = `${expectedApiReportPathWithoutExtension}${
modulePath ? '.' : ''
}${modulePath}.api.md`;
const expectedEntryPointApiReportShortPath: string = extractorConfig._getShortFilePath(
expectedEntryPointApiReportPath,
);
collector.messageRouter.logVerbose(
ConsoleMessageId.WritingApiReport,
`Generating ${reportConfig.variant} API report: ${expectedEntryPointApiReportPath}`,
);
// Write the actual file
FileSystem.writeFile(actualEntryPointApiReportPath, actualApiReportContent, {
ensureFolderExists: true,
convertLineEndings: extractorConfig.newlineKind,
});
// Compare it against the expected file
if (FileSystem.exists(expectedEntryPointApiReportPath)) {
const expectedApiReportContent: string = FileSystem.readFile(expectedEntryPointApiReportPath);
if (ApiReportGenerator.areEquivalentApiFileContents(actualApiReportContent, expectedApiReportContent)) {
messageRouter.logVerbose(
ConsoleMessageId.ApiReportUnchanged,
`The API report is up to date: ${actualEntryPointApiReportShortPath}`,
);
} else {
apiReportChanged = true;
if (localBuild) {
// For a local build, just copy the file automatically.
messageRouter.logWarning(
ConsoleMessageId.ApiReportCopied,
`You have changed the API signature for this project. Updating ${actualEntryPointApiReportShortPath}`,
);
FileSystem.writeFile(actualEntryPointApiReportPath, actualApiReportContent, {
ensureFolderExists: true,
convertLineEndings: extractorConfig.newlineKind,
});
} else {
// For a production build, issue a warning that will break the CI build.
messageRouter.logWarning(
ConsoleMessageId.ApiReportNotCopied,
'You have changed the API signature for this project.' +
` Please copy the file "${actualEntryPointApiReportShortPath}" to "${expectedEntryPointApiReportShortPath}",` +
` or perform a local build (which does this automatically).` +
` See the Git repo documentation for more info.`,
);
}
}
} 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) {
const expectedApiReportFolder: string = path.dirname(expectedEntryPointApiReportPath);
if (FileSystem.exists(expectedApiReportFolder)) {
FileSystem.writeFile(expectedEntryPointApiReportPath, 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' +
expectedEntryPointApiReportPath,
);
} else {
messageRouter.logError(
ConsoleMessageId.ApiReportFolderMissing,
'Unable to create the API report file. Please make sure the target folder exists:\n' +
expectedApiReportFolder,
);
}
} else {
// 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 "${actualEntryPointApiReportShortPath}" to "${expectedEntryPointApiReportShortPath}",` +
` or perform a local build (which does this automatically).` +
` See the Git repo documentation for more info.`,
);
}
}
}
return apiReportChanged;
}
private static _checkCompilerCompatibility(extractorConfig: ExtractorConfig, messageRouter: MessageRouter): void {
messageRouter.logInfo(ConsoleMessageId.Preamble, `Analysis will use the bundled TypeScript version ${ts.version}`);

View File

@@ -2,8 +2,8 @@
// See LICENSE in the project root for license information.
import * as path from 'node:path';
import { EnumMemberOrder } from '@discordjs/api-extractor-model';
import { TSDocConfiguration } from '@microsoft/tsdoc';
import { EnumMemberOrder, ReleaseTag } from '@discordjs/api-extractor-model';
import { TSDocConfiguration, TSDocTagDefinition } from '@microsoft/tsdoc';
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
import {
JsonFile,
@@ -17,14 +17,21 @@ import {
Path,
NewlineKind,
} from '@rushstack/node-core-library';
import { type IRigConfig, RigConfig } from '@rushstack/rig-package';
import type { MergeWithCustomizer } from 'lodash';
import cloneDeep from 'lodash/cloneDeep.js';
import merge from 'lodash/merge.js';
import mergeWith from 'lodash/mergeWith.js';
import * as resolve from 'resolve';
import { PackageMetadataManager } from '../analyzer/PackageMetadataManager.js';
import { MessageRouter } from '../collector/MessageRouter.js';
import type { IApiModelGenerationOptions } from '../generators/ApiModelGenerator';
import apiExtractorSchema from '../schemas/api-extractor.schema.json' assert { type: 'json' };
import type { IConfigFile, IExtractorMessagesConfig } from './IConfigFile.js';
import type {
ApiReportVariant,
IConfigApiReport,
IConfigFile,
IConfigEntryPoint,
IExtractorMessagesConfig,
} from './IConfigFile.js';
/**
* Tokens used during variable expansion of path fields from api-extractor.json.
@@ -66,8 +73,10 @@ export interface IExtractorConfigLoadForFolderOptions {
/**
* An already constructed `RigConfig` object. If omitted, then a new `RigConfig` object will be constructed.
*
* @deprecated this is unsupported in the discord.js version
*/
rigConfig?: IRigConfig;
rigConfig?: never;
/**
* The folder path to start from when searching for api-extractor.json.
@@ -147,14 +156,56 @@ export interface IExtractorConfigPrepareOptions {
tsdocConfigFile?: TSDocConfigFile;
}
/**
* Configuration for a single API report, including its {@link IExtractorConfigApiReport.variant}.
*
* @public
*/
export interface IExtractorConfigApiReport {
/**
* Name of the output report file.
*
* @remarks Relative to the configured report directory path.
*/
fileName: string;
/**
* Report variant.
* Determines which API items will be included in the report output, based on their tagged release levels.
*/
variant: ApiReportVariant;
}
/**
* Default {@link IConfigApiReport.reportVariants}
*/
const defaultApiReportVariants: readonly ApiReportVariant[] = ['complete'];
/**
* Default {@link IConfigApiReport.tagsToReport}.
*
* @remarks
* Note that this list is externally documented, and directly affects report output.
* Also note that the order of tags in this list is significant, as it determines the order of tags in the report.
* Any changes to this list should be considered breaking.
*/
const defaultTagsToReport: Readonly<Record<`@${string}`, boolean>> = {
'@sealed': true,
'@virtual': true,
'@override': true,
'@eventProperty': true,
'@deprecated': true,
};
interface IExtractorConfigParameters {
additionalEntryPoints: IConfigEntryPoint[];
alphaTrimmedFilePath: string;
apiJsonFilePath: string;
apiReportEnabled: boolean;
apiReportIncludeForgottenExports: boolean;
betaTrimmedFilePath: string;
bundledPackages: string[];
docModelEnabled: boolean;
docModelGenerationOptions: IApiModelGenerationOptions | undefined;
docModelIncludeForgottenExports: boolean;
enumMemberOrder: EnumMemberOrder;
mainEntryPointFilePath: string;
@@ -167,10 +218,12 @@ interface IExtractorConfigParameters {
projectFolder: string;
projectFolderUrl: string | undefined;
publicTrimmedFilePath: string;
reportFilePath: string;
reportTempFilePath: string;
reportConfigs: readonly IExtractorConfigApiReport[];
reportFolder: string;
reportTempFolder: string;
rollupEnabled: boolean;
skipLibCheck: boolean;
tagsToReport: Readonly<Record<`@${string}`, boolean>>;
testMode: boolean;
tsconfigFilePath: string;
tsdocConfigFile: TSDocConfigFile;
@@ -180,9 +233,22 @@ interface IExtractorConfigParameters {
untrimmedFilePath: string;
}
// Lodash merges array values by default, which is unintuitive for config files (and makes it impossible for derived configurations to overwrite arrays).
// For example, given a base config containing an array property with value ["foo", "bar"] and a derived config that specifies ["baz"] for that property, lodash will produce ["baz", "bar"], which is unintuitive.
// This customizer function ensures that arrays are always overwritten.
const mergeCustomizer: MergeWithCustomizer = (_objValue, srcValue) => {
if (Array.isArray(srcValue)) {
return srcValue;
}
// Fall back to default merge behavior.
return undefined;
};
/**
* The `ExtractorConfig` class loads, validates, interprets, and represents the api-extractor.json config file.
*
* @sealed
* @public
*/
export class ExtractorConfig {
@@ -230,7 +296,12 @@ export class ExtractorConfig {
/**
* {@inheritDoc IConfigFile.mainEntryPointFilePath}
*/
public readonly mainEntryPointFilePath: string;
public readonly mainEntryPointFilePath: IConfigEntryPoint;
/**
* {@inheritDoc IConfigFile.additionalEntryPoints}
*/
public readonly additionalEntryPoints: IConfigEntryPoint[];
/**
* {@inheritDoc IConfigFile.bundledPackages}
@@ -258,14 +329,48 @@ export class ExtractorConfig {
public readonly apiReportEnabled: boolean;
/**
* The `reportFolder` path combined with the `reportFileName`.
* List of configurations for report files to be generated.
*
* @remarks Derived from {@link IConfigApiReport.reportFileName} and {@link IConfigApiReport.reportVariants}.
*/
public readonly reportFilePath: string;
public readonly reportConfigs: readonly IExtractorConfigApiReport[];
/**
* The `reportTempFolder` path combined with the `reportFileName`.
* {@inheritDoc IConfigApiReport.reportFolder}
*/
public readonly reportTempFilePath: string;
public readonly reportFolder: string;
/**
* {@inheritDoc IConfigApiReport.reportTempFolder}
*/
public readonly reportTempFolder: string;
/**
* {@inheritDoc IConfigApiReport.tagsToReport}
*/
public readonly tagsToReport: Readonly<Record<`@${string}`, boolean>>;
/**
* Gets the file path for the "complete" (default) report configuration, if one was specified.
* Otherwise, returns an empty string.
*
* @deprecated Use {@link ExtractorConfig.reportConfigs} to access all report configurations.
*/
public get reportFilePath(): string {
const completeConfig: IExtractorConfigApiReport | undefined = this._getCompleteReportConfig();
return completeConfig === undefined ? '' : path.join(this.reportFolder, completeConfig.fileName);
}
/**
* Gets the temp file path for the "complete" (default) report configuration, if one was specified.
* Otherwise, returns an empty string.
*
* @deprecated Use {@link ExtractorConfig.reportConfigs} to access all report configurations.
*/
public get reportTempFilePath(): string {
const completeConfig: IExtractorConfigApiReport | undefined = this._getCompleteReportConfig();
return completeConfig === undefined ? '' : path.join(this.reportTempFolder, completeConfig.fileName);
}
/**
* {@inheritDoc IConfigApiReport.includeForgottenExports}
@@ -273,9 +378,11 @@ export class ExtractorConfig {
public readonly apiReportIncludeForgottenExports: boolean;
/**
* {@inheritDoc IConfigDocModel.enabled}
* If specified, the doc model is enabled and the specified options will be used.
*
* @beta
*/
public readonly docModelEnabled: boolean;
public readonly docModelGenerationOptions: IApiModelGenerationOptions | undefined;
/**
* {@inheritDoc IConfigDocModel.apiJsonFilePath}
@@ -363,37 +470,77 @@ export class ExtractorConfig {
*/
public readonly enumMemberOrder: EnumMemberOrder;
private constructor(parameters: IExtractorConfigParameters) {
this.projectFolder = parameters.projectFolder;
this.packageJson = parameters.packageJson;
this.packageFolder = parameters.packageFolder;
this.mainEntryPointFilePath = parameters.mainEntryPointFilePath;
this.bundledPackages = parameters.bundledPackages;
this.tsconfigFilePath = parameters.tsconfigFilePath;
this.overrideTsconfig = parameters.overrideTsconfig;
this.skipLibCheck = parameters.skipLibCheck;
this.apiReportEnabled = parameters.apiReportEnabled;
this.reportFilePath = parameters.reportFilePath;
this.reportTempFilePath = parameters.reportTempFilePath;
this.apiReportIncludeForgottenExports = parameters.apiReportIncludeForgottenExports;
this.docModelEnabled = parameters.docModelEnabled;
this.apiJsonFilePath = parameters.apiJsonFilePath;
this.docModelIncludeForgottenExports = parameters.docModelIncludeForgottenExports;
this.projectFolderUrl = parameters.projectFolderUrl;
this.rollupEnabled = parameters.rollupEnabled;
this.untrimmedFilePath = parameters.untrimmedFilePath;
this.alphaTrimmedFilePath = parameters.alphaTrimmedFilePath;
this.betaTrimmedFilePath = parameters.betaTrimmedFilePath;
this.publicTrimmedFilePath = parameters.publicTrimmedFilePath;
this.omitTrimmingComments = parameters.omitTrimmingComments;
this.tsdocMetadataEnabled = parameters.tsdocMetadataEnabled;
this.tsdocMetadataFilePath = parameters.tsdocMetadataFilePath;
this.tsdocConfigFile = parameters.tsdocConfigFile;
this.tsdocConfiguration = parameters.tsdocConfiguration;
this.newlineKind = parameters.newlineKind;
this.messages = parameters.messages;
this.testMode = parameters.testMode;
this.enumMemberOrder = parameters.enumMemberOrder;
private constructor({
projectFolder,
packageJson,
packageFolder,
mainEntryPointFilePath,
additionalEntryPoints,
bundledPackages,
tsconfigFilePath,
overrideTsconfig,
skipLibCheck,
apiReportEnabled,
apiReportIncludeForgottenExports,
reportConfigs,
reportFolder,
reportTempFolder,
tagsToReport,
docModelGenerationOptions,
apiJsonFilePath,
docModelIncludeForgottenExports,
projectFolderUrl,
rollupEnabled,
untrimmedFilePath,
alphaTrimmedFilePath,
betaTrimmedFilePath,
publicTrimmedFilePath,
omitTrimmingComments,
tsdocMetadataEnabled,
tsdocMetadataFilePath,
tsdocConfigFile,
tsdocConfiguration,
newlineKind,
messages,
testMode,
enumMemberOrder,
}: IExtractorConfigParameters) {
this.projectFolder = projectFolder;
this.packageJson = packageJson;
this.packageFolder = packageFolder;
this.mainEntryPointFilePath = {
modulePath: '',
filePath: mainEntryPointFilePath,
};
this.additionalEntryPoints = additionalEntryPoints;
this.bundledPackages = bundledPackages;
this.tsconfigFilePath = tsconfigFilePath;
this.overrideTsconfig = overrideTsconfig;
this.skipLibCheck = skipLibCheck;
this.apiReportEnabled = apiReportEnabled;
this.reportConfigs = reportConfigs;
this.reportFolder = reportFolder;
this.reportTempFolder = reportTempFolder;
this.tagsToReport = tagsToReport;
this.apiReportIncludeForgottenExports = apiReportIncludeForgottenExports;
this.docModelGenerationOptions = docModelGenerationOptions;
this.apiJsonFilePath = apiJsonFilePath;
this.docModelIncludeForgottenExports = docModelIncludeForgottenExports;
this.projectFolderUrl = projectFolderUrl;
this.rollupEnabled = rollupEnabled;
this.untrimmedFilePath = untrimmedFilePath;
this.alphaTrimmedFilePath = alphaTrimmedFilePath;
this.betaTrimmedFilePath = betaTrimmedFilePath;
this.publicTrimmedFilePath = publicTrimmedFilePath;
this.omitTrimmingComments = omitTrimmingComments;
this.tsdocMetadataEnabled = tsdocMetadataEnabled;
this.tsdocMetadataFilePath = tsdocMetadataFilePath;
this.tsdocConfigFile = tsdocConfigFile;
this.tsdocConfiguration = tsdocConfiguration;
this.newlineKind = newlineKind;
this.messages = messages;
this.testMode = testMode;
this.enumMemberOrder = enumMemberOrder;
}
/**
@@ -480,44 +627,8 @@ export class ExtractorConfig {
configFilename = path.join(baseFolder, ExtractorConfig.FILENAME);
if (!FileSystem.exists(configFilename)) {
// If We didn't find it in <packageFolder>/api-extractor.json or <packageFolder>/config/api-extractor.json
// then check for a rig package
if (packageFolder) {
let rigConfig: IRigConfig;
if (options.rigConfig) {
// The caller provided an already solved RigConfig. Double-check that it is for the right project.
if (!Path.isEqual(options.rigConfig.projectFolderPath, packageFolder)) {
throw new Error(
'The provided ILoadForFolderOptions.rigConfig is for the wrong project folder:\n' +
'\nExpected path: ' +
packageFolder +
'\nProvided path: ' +
options.rigConfig.projectFolderOriginalPath,
);
}
rigConfig = options.rigConfig;
} else {
rigConfig = RigConfig.loadForProjectFolder({
projectFolderPath: packageFolder,
});
}
if (rigConfig.rigFound) {
configFilename = path.join(rigConfig.getResolvedProfileFolder(), ExtractorConfig.FILENAME);
// If the "projectFolder" setting isn't specified in api-extractor.json, it defaults to the
// "<lookup>" token which will probe for the tsconfig.json nearest to the api-extractor.json path.
// But this won't work if api-extractor.json belongs to the rig. So instead "<lookup>" should be
// the "<packageFolder>" that referenced the rig.
projectFolderLookupToken = packageFolder;
}
}
if (!FileSystem.exists(configFilename)) {
// API Extractor does not seem to be configured for this folder
return undefined;
}
// API Extractor does not seem to be configured for this folder
return undefined;
}
}
@@ -618,7 +729,7 @@ export class ExtractorConfig {
ExtractorConfig._resolveConfigFileRelativePaths(baseConfig, currentConfigFolderPath);
// Merge extractorConfig into baseConfig, mutating baseConfig
merge(baseConfig, configObject);
mergeWith(baseConfig, configObject, mergeCustomizer);
configObject = baseConfig;
currentConfigFilePath = extendsField;
@@ -628,7 +739,7 @@ export class ExtractorConfig {
}
// Lastly, apply the defaults
configObject = merge(cloneDeep(ExtractorConfig._defaultConfig), configObject);
configObject = mergeWith(cloneDeep(ExtractorConfig._defaultConfig), configObject, mergeCustomizer);
ExtractorConfig.jsonSchema.validateObject(configObject, jsonFilePath);
@@ -653,6 +764,21 @@ export class ExtractorConfig {
);
}
if (configFile.additionalEntryPoints) {
const entryPointWithAbsolutePath: IConfigEntryPoint[] = [];
for (const entryPoint of configFile.additionalEntryPoints) {
const absoluteFilePath: string = ExtractorConfig._resolveConfigFileRelativePath(
'additionalEntryPoints',
entryPoint.filePath,
currentConfigFolderPath,
);
entryPointWithAbsolutePath.push({ ...entryPoint, filePath: absoluteFilePath });
}
configFile.additionalEntryPoints = entryPointWithAbsolutePath;
}
if (configFile.compiler?.tsconfigFilePath) {
configFile.compiler.tsconfigFilePath = ExtractorConfig._resolveConfigFileRelativePath(
'tsconfigFilePath',
@@ -885,6 +1011,25 @@ export class ExtractorConfig {
throw new Error('The "mainEntryPointFilePath" path does not exist: ' + mainEntryPointFilePath);
}
const additionalEntryPoints: IConfigEntryPoint[] = [];
for (const entryPoint of configObject.additionalEntryPoints || []) {
const absoluteEntryPointFilePath: string = ExtractorConfig._resolvePathWithTokens(
'entryPointFilePath',
entryPoint.filePath,
tokenContext,
);
if (!ExtractorConfig.hasDtsFileExtension(absoluteEntryPointFilePath)) {
throw new Error('The "additionalEntryPoints" value is not a declaration file: ' + absoluteEntryPointFilePath);
}
if (!FileSystem.exists(absoluteEntryPointFilePath)) {
throw new Error('The "additionalEntryPoints" path does not exist: ' + absoluteEntryPointFilePath);
}
additionalEntryPoints.push({ ...entryPoint, filePath: absoluteEntryPointFilePath });
}
const bundledPackages: string[] = configObject.bundledPackages ?? [];
for (const bundledPackage of bundledPackages) {
if (!PackageName.isValidName(bundledPackage)) {
@@ -908,51 +1053,92 @@ export class ExtractorConfig {
}
}
let apiReportEnabled = false;
let reportFilePath = '';
let reportTempFilePath = '';
let apiReportIncludeForgottenExports = false;
if (configObject.apiReport) {
apiReportEnabled = Boolean(configObject.apiReport.enabled);
const reportFilename: string = ExtractorConfig._expandStringWithTokens(
'reportFileName',
configObject.apiReport.reportFileName ?? '',
tokenContext,
);
if (!reportFilename) {
// A merged configuration should have this
throw new Error('The "reportFilename" setting is missing');
}
if (reportFilename.includes('/') || reportFilename.includes('\\')) {
// A merged configuration should have this
throw new Error(`The "reportFilename" setting contains invalid characters: "${reportFilename}"`);
}
const reportFolder: string = ExtractorConfig._resolvePathWithTokens(
'reportFolder',
configObject.apiReport.reportFolder,
tokenContext,
);
const reportTempFolder: string = ExtractorConfig._resolvePathWithTokens(
'reportTempFolder',
configObject.apiReport.reportTempFolder,
tokenContext,
);
reportFilePath = path.join(reportFolder, reportFilename);
reportTempFilePath = path.join(reportTempFolder, reportFilename);
apiReportIncludeForgottenExports = Boolean(configObject.apiReport.includeForgottenExports);
if (configObject.apiReport?.tagsToReport) {
_validateTagsToReport(configObject.apiReport.tagsToReport);
}
let docModelEnabled = false;
const apiReportEnabled: boolean = configObject.apiReport?.enabled ?? false;
const apiReportIncludeForgottenExports: boolean = configObject.apiReport?.includeForgottenExports ?? false;
let reportFolder: string = tokenContext.projectFolder;
let reportTempFolder: string = tokenContext.projectFolder;
const reportConfigs: IExtractorConfigApiReport[] = [];
let tagsToReport: Record<`@${string}`, boolean> = {};
if (apiReportEnabled) {
// Undefined case checked above where we assign `apiReportEnabled`
const apiReportConfig: IConfigApiReport = configObject.apiReport!;
const reportFileNameSuffix = '.api.md';
let reportFileNameBase: string;
if (apiReportConfig.reportFileName) {
if (apiReportConfig.reportFileName.includes('/') || apiReportConfig.reportFileName.includes('\\')) {
throw new Error(
`The "reportFileName" setting contains invalid characters: "${apiReportConfig.reportFileName}"`,
);
}
if (apiReportConfig.reportFileName.endsWith(reportFileNameSuffix)) {
// The system previously asked users to specify their filenames in a form containing the `.api.md` extension.
// This guidance has changed, but to maintain backwards compatibility, we will temporarily support input
// that ends with the `.api.md` extension specially, by stripping it out.
// This should be removed in version 8, possibly replaced with an explicit error to help users
// migrate their configs.
reportFileNameBase = apiReportConfig.reportFileName.slice(0, -reportFileNameSuffix.length);
} else {
// `.api.md` extension was not specified. Use provided file name base as is.
reportFileNameBase = apiReportConfig.reportFileName;
}
} else {
// Default value
reportFileNameBase = '<unscopedPackageName>';
}
const reportVariantKinds: readonly ApiReportVariant[] =
apiReportConfig.reportVariants ?? defaultApiReportVariants;
for (const reportVariantKind of reportVariantKinds) {
// Omit the variant kind from the "complete" report file name for simplicity and for backwards compatibility.
const fileNameWithTokens = `${reportFileNameBase}${
reportVariantKind === 'complete' ? '' : `.${reportVariantKind}`
}${reportFileNameSuffix}`;
const normalizedFileName: string = ExtractorConfig._expandStringWithTokens(
'reportFileName',
fileNameWithTokens,
tokenContext,
);
reportConfigs.push({
fileName: normalizedFileName,
variant: reportVariantKind,
});
}
if (apiReportConfig.reportFolder) {
reportFolder = ExtractorConfig._resolvePathWithTokens(
'reportFolder',
apiReportConfig.reportFolder,
tokenContext,
);
}
if (apiReportConfig.reportTempFolder) {
reportTempFolder = ExtractorConfig._resolvePathWithTokens(
'reportTempFolder',
apiReportConfig.reportTempFolder,
tokenContext,
);
}
tagsToReport = {
...defaultTagsToReport,
...apiReportConfig.tagsToReport,
};
}
let docModelGenerationOptions: IApiModelGenerationOptions | undefined;
let apiJsonFilePath = '';
let docModelIncludeForgottenExports = false;
let projectFolderUrl: string | undefined;
if (configObject.docModel) {
docModelEnabled = Boolean(configObject.docModel.enabled);
if (configObject.docModel?.enabled) {
apiJsonFilePath = ExtractorConfig._resolvePathWithTokens(
'apiJsonFilePath',
configObject.docModel.apiJsonFilePath,
@@ -960,6 +1146,43 @@ export class ExtractorConfig {
);
docModelIncludeForgottenExports = Boolean(configObject.docModel.includeForgottenExports);
projectFolderUrl = configObject.docModel.projectFolderUrl;
const releaseTagsToTrim: Set<ReleaseTag> = new Set<ReleaseTag>();
const releaseTagsToTrimOption: string[] = configObject.docModel.releaseTagsToTrim || ['@internal'];
for (const releaseTagToTrim of releaseTagsToTrimOption) {
let releaseTag: ReleaseTag;
switch (releaseTagToTrim) {
case '@internal': {
releaseTag = ReleaseTag.Internal;
break;
}
case '@alpha': {
releaseTag = ReleaseTag.Alpha;
break;
}
case '@beta': {
releaseTag = ReleaseTag.Beta;
break;
}
case '@public': {
releaseTag = ReleaseTag.Public;
break;
}
default: {
throw new Error(`The release tag "${releaseTagToTrim}" is not supported`);
}
}
releaseTagsToTrim.add(releaseTag);
}
docModelGenerationOptions = {
releaseTagsToTrim,
};
}
let tsdocMetadataEnabled = false;
@@ -1014,6 +1237,15 @@ export class ExtractorConfig {
if (configObject.dtsRollup) {
rollupEnabled = Boolean(configObject.dtsRollup.enabled);
// d.ts rollup is not supported when there are more than one entry points.
if (rollupEnabled && additionalEntryPoints.length > 0) {
throw new Error(
`It seems that you have dtsRollup enabled while you also have defined additionalEntryPoints.` +
`dtsRollup is not supported when there are multiple entry points in your package`,
);
}
untrimmedFilePath = ExtractorConfig._resolvePathWithTokens(
'untrimmedFilePath',
configObject.dtsRollup.untrimmedFilePath,
@@ -1057,15 +1289,18 @@ export class ExtractorConfig {
packageJson,
packageFolder,
mainEntryPointFilePath,
additionalEntryPoints,
bundledPackages,
tsconfigFilePath,
overrideTsconfig: configObject.compiler.overrideTsconfig,
skipLibCheck: Boolean(configObject.compiler.skipLibCheck),
apiReportEnabled,
reportFilePath,
reportTempFilePath,
reportConfigs,
reportFolder,
reportTempFolder,
apiReportIncludeForgottenExports,
docModelEnabled,
tagsToReport,
docModelGenerationOptions,
apiJsonFilePath,
docModelIncludeForgottenExports,
projectFolderUrl,
@@ -1121,6 +1356,13 @@ export class ExtractorConfig {
return new ExtractorConfig({ ...extractorConfigParameters, tsdocConfigFile, tsdocConfiguration });
}
/**
* Gets the report configuration for the "complete" (default) report configuration, if one was specified.
*/
private _getCompleteReportConfig(): IExtractorConfigApiReport | undefined {
return this.reportConfigs.find((x) => x.variant === 'complete');
}
private static _resolvePathWithTokens(
fieldName: string,
value: string | undefined,
@@ -1194,3 +1436,47 @@ export class ExtractorConfig {
throw new Error(`The "${fieldName}" value contains extra token characters ("<" or ">"): ${value}`);
}
}
const releaseTags: Set<string> = new Set(['@public', '@alpha', '@beta', '@internal']);
/**
* Validate {@link ExtractorConfig.tagsToReport}.
*/
function _validateTagsToReport(
tagsToReport: Record<string, boolean>,
): asserts tagsToReport is Record<`@${string}`, boolean> {
const includedReleaseTags: string[] = [];
const invalidTags: [string, string][] = []; // tag name, error
for (const tag of Object.keys(tagsToReport)) {
if (releaseTags.has(tag)) {
// If a release tags is specified, regardless of whether it is enabled, we will throw an error.
// Release tags must not be specified.
includedReleaseTags.push(tag);
}
// If the tag is invalid, generate an error string from the inner error message.
try {
TSDocTagDefinition.validateTSDocTagName(tag);
} catch (error) {
invalidTags.push([tag, (error as Error).message]);
}
}
const errorMessages: string[] = [];
for (const includedReleaseTag of includedReleaseTags) {
errorMessages.push(
`${includedReleaseTag}: Release tags are always included in API reports and must not be specified`,
);
}
for (const [invalidTag, innerError] of invalidTags) {
errorMessages.push(`${invalidTag}: ${innerError}`);
}
if (errorMessages.length > 0) {
const errorMessage: string = [`"tagsToReport" contained one or more invalid tags:`, ...errorMessages].join(
'\n\t- ',
);
throw new Error(errorMessage);
}
}

View File

@@ -4,6 +4,21 @@
import type { EnumMemberOrder } from '@discordjs/api-extractor-model';
import type { ExtractorLogLevel } from './ExtractorLogLevel.js';
/**
* Represents an entry point in the package
* Example:
* ```
* {
* modulePath: 'Shape/Square',
* filePath: './dist/Shape/Square/index.d.ts'
* }
* ```
*/
export interface IConfigEntryPoint {
filePath: string;
modulePath: string;
}
/**
* Determines how the TypeScript compiler engine will be invoked by API Extractor.
*
@@ -47,6 +62,13 @@ export interface IConfigCompiler {
tsconfigFilePath?: string;
}
/**
* The allowed variations of API reports.
*
* @public
*/
export type ApiReportVariant = 'alpha' | 'beta' | 'complete' | 'public';
/**
* Configures how the API report files (*.api.md) will be generated.
*
@@ -71,11 +93,19 @@ export interface IConfigApiReport {
includeForgottenExports?: boolean;
/**
* The filename for the API report files. It will be combined with `reportFolder` or `reportTempFolder` to produce
* a full output filename.
* The base filename for the API report files, to be combined with {@link IConfigApiReport.reportFolder} or
* {@link IConfigApiReport.reportTempFolder} to produce the full file path.
*
* @remarks
* The file extension should be ".api.md", and the string should not contain a path separator such as `\` or `/`.
* The `reportFileName` should not include any path separators such as `\` or `/`. The `reportFileName` should
* not include a file extension, since API Extractor will automatically append an appropriate file extension such
* as `.api.md`. If the {@link IConfigApiReport.reportVariants} setting is used, then the file extension includes
* the variant name, for example `my-report.public.api.md` or `my-report.beta.api.md`. The `complete` variant always
* uses the simple extension `my-report.api.md`.
*
* Previous versions of API Extractor required `reportFileName` to include the `.api.md` extension explicitly;
* for backwards compatibility, that is still accepted but will be discarded before applying the above rules.
* @defaultValue `<unscopedPackageName>`
*/
reportFileName?: string;
@@ -104,8 +134,62 @@ export interface IConfigApiReport {
* prepend a folder token such as `<projectFolder>`.
*/
reportTempFolder?: string;
/**
* The set of report variants to generate.
*
* @remarks
* To support different approval requirements for different API levels, multiple "variants" of the API report can
* be generated. The `reportVariants` setting specifies a list of variants to be generated. If omitted,
* by default only the `complete` variant will be generated, which includes all `@internal`, `@alpha`, `@beta`,
* and `@public` items. Other possible variants are `alpha` (`@alpha` + `@beta` + `@public`),
* `beta` (`@beta` + `@public`), and `public` (`@public only`).
*
* The resulting API report file names will be derived from the {@link IConfigApiReport.reportFileName}.
* @defaultValue `[ "complete" ]`
*/
reportVariants?: ApiReportVariant[];
/**
* Specifies a list of {@link https://tsdoc.org/ | TSDoc} tags that should be reported in the API report file for
* items whose documentation contains them.
*
* @remarks
* Tag names must begin with `@`.
*
* This list may include standard TSDoc tags as well as custom ones.
* For more information on defining custom TSDoc tags, see
* {@link https://api-extractor.com/pages/configs/tsdoc_json/#defining-your-own-tsdoc-tags | here}.
*
* Note that an item's release tag will always reported; this behavior cannot be overridden.
* @defaultValue `@sealed`, `@virtual`, `@override`, `@eventProperty`, and `@deprecated`
* @example Omitting default tags
* To omit the `@sealed` and `@virtual` tags from API reports, you would specify `tagsToReport` as follows:
* ```json
* "tagsToReport": {
* "@sealed": false,
* "@virtual": false
* }
* ```
* @example Including additional tags
* To include additional tags to the set included in API reports, you could specify `tagsToReport` like this:
* ```json
* "tagsToReport": {
* "@customTag": true
* }
* ```
* This will result in `@customTag` being included in addition to the default tags.
*/
tagsToReport?: Readonly<Record<`@${string}`, boolean>>;
}
/**
* The allowed release tags that can be used to mark API items.
*
* @public
*/
export type ReleaseTagForTrim = '@alpha' | '@beta' | '@internal' | '@public';
/**
* Configures how the doc model file (*.api.json) will be generated.
*
@@ -151,6 +235,13 @@ export interface IConfigDocModel {
* Can be omitted if you don't need source code links in your API documentation reference.
*/
projectFolderUrl?: string;
/**
* Specifies a list of release tags that will be trimmed from the doc model.
*
* @defaultValue `["@internal"]`
*/
releaseTagsToTrim?: ReleaseTagForTrim[];
}
/**
@@ -322,6 +413,11 @@ export interface IExtractorMessagesConfig {
* @public
*/
export interface IConfigFile {
/**
* support multiple entry points.
*/
additionalEntryPoints: IConfigEntryPoint[];
/**
* {@inheritDoc IConfigApiReport}
*/

View File

@@ -1,55 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import * as path from 'node:path';
import { StandardTags } from '@microsoft/tsdoc';
import { ExtractorConfig } from '../ExtractorConfig.js';
const testDataFolder: string = path.join(__dirname, 'test-data');
describe('Extractor-custom-tags', () => {
describe('should use a TSDocConfiguration', () => {
it.only("with custom TSDoc tags defined in the package's tsdoc.json", () => {
const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(
path.join(testDataFolder, 'custom-tsdoc-tags/api-extractor.json'),
);
const { tsdocConfiguration } = extractorConfig;
expect(tsdocConfiguration.tryGetTagDefinition('@block')).not.toBe(undefined);
expect(tsdocConfiguration.tryGetTagDefinition('@inline')).not.toBe(undefined);
expect(tsdocConfiguration.tryGetTagDefinition('@modifier')).not.toBe(undefined);
});
it.only("with custom TSDoc tags enabled per the package's tsdoc.json", () => {
const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(
path.join(testDataFolder, 'custom-tsdoc-tags/api-extractor.json'),
);
const { tsdocConfiguration } = extractorConfig;
const block = tsdocConfiguration.tryGetTagDefinition('@block')!;
const inline = tsdocConfiguration.tryGetTagDefinition('@inline')!;
const modifier = tsdocConfiguration.tryGetTagDefinition('@modifier')!;
expect(tsdocConfiguration.isTagSupported(block)).toBe(true);
expect(tsdocConfiguration.isTagSupported(inline)).toBe(true);
expect(tsdocConfiguration.isTagSupported(modifier)).toBe(false);
});
it.only("with standard tags and API Extractor custom tags defined and supported when the package's tsdoc.json extends API Extractor's tsdoc.json", () => {
const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(
path.join(testDataFolder, 'custom-tsdoc-tags/api-extractor.json'),
);
const { tsdocConfiguration } = extractorConfig;
expect(tsdocConfiguration.tryGetTagDefinition('@inline')).not.toBe(undefined);
expect(tsdocConfiguration.tryGetTagDefinition('@block')).not.toBe(undefined);
expect(tsdocConfiguration.tryGetTagDefinition('@modifier')).not.toBe(undefined);
for (const tag of StandardTags.allDefinitions.concat([
tsdocConfiguration.tryGetTagDefinition('@betaDocumentation')!,
tsdocConfiguration.tryGetTagDefinition('@internalRemarks')!,
tsdocConfiguration.tryGetTagDefinition('@preapproved')!,
])) {
expect(tsdocConfiguration.tagDefinitions.includes(tag));
expect(tsdocConfiguration.supportedTagDefinitions.includes(tag));
}
});
});
});

View File

@@ -1,36 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import * as path from 'node:path';
import { Path } from '@rushstack/node-core-library';
import { ExtractorConfig } from '../ExtractorConfig.js';
const testDataFolder: string = path.join(__dirname, 'test-data');
function expectEqualPaths(path1: string, path2: string): void {
if (!Path.isEqual(path1, path2)) {
fail('Expected paths to be equal:\npath1: ' + path1 + '\npath2: ' + path2);
}
}
// Tests for expanding the "<lookup>" token for the "projectFolder" setting in api-extractor.json
describe(`${ExtractorConfig.name}.${ExtractorConfig.loadFileAndPrepare.name}`, () => {
it.only('config-lookup1: looks up ./api-extractor.json', () => {
const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(
path.join(testDataFolder, 'config-lookup1/api-extractor.json'),
);
expectEqualPaths(extractorConfig.projectFolder, path.join(testDataFolder, 'config-lookup1'));
});
it.only('config-lookup2: looks up ./config/api-extractor.json', () => {
const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(
path.join(testDataFolder, 'config-lookup2/config/api-extractor.json'),
);
expectEqualPaths(extractorConfig.projectFolder, path.join(testDataFolder, 'config-lookup2'));
});
it.only('config-lookup3a: looks up ./src/test/config/api-extractor.json', () => {
const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(
path.join(testDataFolder, 'config-lookup3/src/test/config/api-extractor.json'),
);
expectEqualPaths(extractorConfig.projectFolder, path.join(testDataFolder, 'config-lookup3/src/test/'));
});
});

View File

@@ -1,17 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/index.d.ts",
"apiReport": {
"enabled": true
},
"docModel": {
"enabled": true
},
"dtsRollup": {
"enabled": true
}
}

View File

@@ -1,2 +0,0 @@
/* eslint-disable unicorn/no-empty-file */
// empty file

View File

@@ -1,4 +0,0 @@
{
"name": "config-lookup1",
"version": "1.0.0"
}

View File

@@ -1,25 +0,0 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"declaration": true,
"sourceMap": true,
"declarationMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"types": ["heft-jest", "node"],
"module": "commonjs",
"target": "es2017",
"lib": ["es2017"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "lib"]
}

View File

@@ -1,17 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/index.d.ts",
"apiReport": {
"enabled": true
},
"docModel": {
"enabled": true
},
"dtsRollup": {
"enabled": true
}
}

View File

@@ -1,2 +0,0 @@
/* eslint-disable unicorn/no-empty-file */
// empty file

View File

@@ -1,4 +0,0 @@
{
"name": "config-lookup2",
"version": "1.0.0"
}

View File

@@ -1,25 +0,0 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"declaration": true,
"sourceMap": true,
"declarationMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"types": ["heft-jest", "node"],
"module": "commonjs",
"target": "es2017",
"lib": ["es2017"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "lib"]
}

View File

@@ -1,17 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/index.d.ts",
"apiReport": {
"enabled": true
},
"docModel": {
"enabled": true
},
"dtsRollup": {
"enabled": true
}
}

View File

@@ -1,2 +0,0 @@
/* eslint-disable unicorn/no-empty-file */
// empty file

View File

@@ -1,4 +0,0 @@
{
"name": "config-lookup3",
"version": "1.0.0"
}

View File

@@ -1,17 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/index.d.ts",
"apiReport": {
"enabled": true
},
"docModel": {
"enabled": true
},
"dtsRollup": {
"enabled": true
}
}

View File

@@ -1,2 +0,0 @@
/* eslint-disable unicorn/no-empty-file */
// empty file

View File

@@ -1,25 +0,0 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"declaration": true,
"sourceMap": true,
"declarationMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"types": ["heft-jest", "node"],
"module": "commonjs",
"target": "es2017",
"lib": ["es2017"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "lib"]
}

View File

@@ -1,25 +0,0 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"declaration": true,
"sourceMap": true,
"declarationMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"types": ["heft-jest", "node"],
"module": "commonjs",
"target": "es2017",
"lib": ["es2017"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "lib"]
}

View File

@@ -1,17 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/index.d.ts",
"apiReport": {
"enabled": true
},
"docModel": {
"enabled": true
},
"dtsRollup": {
"enabled": true
}
}

View File

@@ -1,7 +0,0 @@
/* eslint-disable tsdoc/syntax */
/**
* @block
* @inline test
* @modifier
*/
interface CustomTagsTestInterface {}

View File

@@ -1,4 +0,0 @@
{
"name": "config-lookup1",
"version": "1.0.0"
}

View File

@@ -1,25 +0,0 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"declaration": true,
"sourceMap": true,
"declarationMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"types": ["heft-jest", "node"],
"module": "commonjs",
"target": "es2017",
"lib": ["es2017"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "lib"]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
"extends": ["../../../../../extends/tsdoc-base.json"],
"tagDefinitions": [
{
"tagName": "@block",
"syntaxKind": "block"
},
{
"tagName": "@inline",
"syntaxKind": "inline",
"allowMultiple": true
},
{
"tagName": "@modifier",
"syntaxKind": "modifier"
}
],
"supportForTags": {
"@block": true,
"@inline": true,
"@modifier": false
}
}