Skip to content

Commit 3e3b1ec

Browse files
committed
Merge pull request microsoft#957 from Microsoft/susurana/extendssupport
[api-extractor] Support "extends" field in api-extractor.json config file (cherry picked from commit 54eee36) # Conflicts: # common/reviews/api/api-extractor.api.ts
1 parent 45716f8 commit 3e3b1ec

6 files changed

Lines changed: 69 additions & 4 deletions

File tree

apps/api-extractor/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
"jju": "~1.3.0",
4141
"lodash": "~4.17.5",
4242
"typescript": "~3.1.6",
43-
"z-schema": "~3.18.3"
43+
"z-schema": "~3.18.3",
44+
"resolve": "1.8.1"
4445
},
4546
"devDependencies": {
4647
"@microsoft/rush-stack-compiler": "0.4.3",

apps/api-extractor/src/api/Extractor.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import * as path from 'path';
55
import * as ts from 'typescript';
6+
import * as resolve from 'resolve';
67
import lodash = require('lodash');
78
import colors = require('colors');
89

@@ -187,11 +188,62 @@ export class Extractor {
187188
* @param options - IExtractor options.
188189
*/
189190
public static processProjectFromConfigFile(jsonConfigFile: string, options?: IExtractorOptions): void {
190-
const configObject: IExtractorConfig = JsonFile.loadAndValidate(jsonConfigFile, Extractor.jsonSchema);
191+
const configObject: IExtractorConfig = this.loadConfigObject(jsonConfigFile);
191192
const extractor: Extractor = new Extractor(configObject, options);
192193
extractor.processProject();
193194
}
194195

196+
/**
197+
* Loads the api extractor config file in Extractor Config object.
198+
* The jsonConfigFile path specified is relative to project directory path.
199+
* @param jsonConfigFile - Path to api extractor json config file.
200+
*/
201+
public static loadConfigObject(jsonConfigFile: string): IExtractorConfig {
202+
// Set to keep track of config files which have been processed.
203+
const pathSet: Set<string> = new Set<string>();
204+
// Get absolute path of config file.
205+
let currentConfigFilePath: string = path.resolve(process.cwd(), jsonConfigFile);
206+
pathSet.add(currentConfigFilePath);
207+
208+
const originalConfigFileFolder: string = path.dirname(currentConfigFilePath);
209+
210+
let extractorConfig: IExtractorConfig = JsonFile.load(jsonConfigFile);
211+
212+
while (extractorConfig.extends) {
213+
if (extractorConfig.extends.match(/^\./)) {
214+
// If extends has relative path.
215+
// Populate the api extractor config path defined in extends relative to current config path.
216+
currentConfigFilePath = path.resolve(originalConfigFileFolder, extractorConfig.extends);
217+
} else {
218+
// If extends has package path.
219+
currentConfigFilePath = resolve.sync(
220+
extractorConfig.extends,
221+
{
222+
basedir: originalConfigFileFolder
223+
}
224+
);
225+
}
226+
// Check if this file was already processed.
227+
if (pathSet.has(currentConfigFilePath)) {
228+
throw new Error(`The API Extractor config files contain a cycle. "${currentConfigFilePath}"`
229+
+ ` is included twice. Please check the "extends" values in config files.`);
230+
}
231+
pathSet.add(currentConfigFilePath);
232+
233+
// Remove extends property from config for current config.
234+
delete extractorConfig.extends;
235+
236+
// Load the extractor config defined in extends property.
237+
const baseConfig: IExtractorConfig = JsonFile.load(currentConfigFilePath);
238+
lodash.merge(baseConfig, extractorConfig);
239+
extractorConfig = baseConfig;
240+
}
241+
242+
// Validate if the extractor config generated adheres to schema.
243+
Extractor.jsonSchema.validateObject(extractorConfig, jsonConfigFile);
244+
return extractorConfig;
245+
}
246+
195247
private static _applyConfigDefaults(config: IExtractorConfig): IExtractorConfig {
196248
// Use the provided config to override the defaults
197249
const normalized: IExtractorConfig = lodash.merge(

apps/api-extractor/src/api/IExtractorConfig.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,12 @@ export interface IExtractorDtsRollupConfig {
254254
* @public
255255
*/
256256
export interface IExtractorConfig {
257+
/**
258+
* Path to json config file from which config should extend.
259+
* The path specified in this field is relative to current config file path.
260+
*/
261+
extends?: string;
262+
257263
/**
258264
* Determines how the TypeScript compiler will be invoked.
259265
* The compiler.configType selects the type of configuration;

apps/api-extractor/src/cli/RunAction.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import * as colors from 'colors';
55
import * as os from 'os';
66
import * as path from 'path';
77
import {
8-
JsonFile,
98
PackageJsonLookup,
109
FileSystem,
1110
IPackageJson
@@ -134,7 +133,7 @@ export class RunAction extends CommandLineAction {
134133
console.log(`Using configuration from ${configFilename}` + os.EOL + os.EOL);
135134
}
136135

137-
const config: IExtractorConfig = JsonFile.loadAndValidate(configFilename, Extractor.jsonSchema);
136+
const config: IExtractorConfig = Extractor.loadConfigObject(configFilename);
138137
const extractor: Extractor = new Extractor(
139138
config,
140139
{

apps/api-extractor/src/schemas/api-extractor.schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
"type": "string"
99
},
1010

11+
"extends": {
12+
"description": "Optional field. Path to json config file from which config should extend",
13+
"type": "string"
14+
},
15+
1116
"compiler": {
1217
"description": "Determines how the TypeScript compiler will be invoked. The compiler.configType selects the type of configuration. Different options are available according to the configuration type.",
1318
"type": "object",

common/reviews/api/api-extractor.api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ class Extractor {
305305
analyzeProject(options?: IAnalyzeProjectOptions): void;
306306
static generateFilePathsForAnalysis(inputFilePaths: string[]): string[];
307307
static jsonSchema: JsonSchema;
308+
static loadConfigObject(jsonConfigFile: string): IExtractorConfig;
308309
static readonly packageName: string;
309310
processProject(options?: IAnalyzeProjectOptions): boolean;
310311
static processProjectFromConfigFile(jsonConfigFile: string, options?: IExtractorOptions): void;
@@ -464,6 +465,7 @@ interface IExtractorConfig {
464465
compiler: IExtractorTsconfigCompilerConfig | IExtractorRuntimeCompilerConfig;
465466
// @beta
466467
dtsRollup?: IExtractorDtsRollupConfig;
468+
extends?: string;
467469
policies?: IExtractorPoliciesConfig;
468470
project: IExtractorProjectConfig;
469471
validationRules?: IExtractorValidationRulesConfig;

0 commit comments

Comments
 (0)