|
3 | 3 |
|
4 | 4 | import * as path from 'path'; |
5 | 5 | import * as ts from 'typescript'; |
| 6 | +import * as resolve from 'resolve'; |
6 | 7 | import lodash = require('lodash'); |
7 | 8 | import colors = require('colors'); |
8 | 9 |
|
@@ -173,11 +174,62 @@ export class Extractor { |
173 | 174 | * @param options - IExtractor options. |
174 | 175 | */ |
175 | 176 | public static processProjectFromConfigFile(jsonConfigFile: string, options?: IExtractorOptions): void { |
176 | | - const configObject: IExtractorConfig = JsonFile.loadAndValidate(jsonConfigFile, Extractor.jsonSchema); |
| 177 | + const configObject: IExtractorConfig = this.loadConfigObject(jsonConfigFile); |
177 | 178 | const extractor: Extractor = new Extractor(configObject, options); |
178 | 179 | extractor.processProject(); |
179 | 180 | } |
180 | 181 |
|
| 182 | + /** |
| 183 | + * Loads the api extractor config file in Extractor Config object. |
| 184 | + * The jsonConfigFile path specified is relative to project directory path. |
| 185 | + * @param jsonConfigFile - Path to api extractor json config file. |
| 186 | + */ |
| 187 | + public static loadConfigObject(jsonConfigFile: string): IExtractorConfig { |
| 188 | + // Set to keep track of config files which have been processed. |
| 189 | + const pathSet: Set<string> = new Set<string>(); |
| 190 | + // Get absolute path of config file. |
| 191 | + let currentConfigFilePath: string = path.resolve(process.cwd(), jsonConfigFile); |
| 192 | + pathSet.add(currentConfigFilePath); |
| 193 | + |
| 194 | + const originalConfigFileFolder: string = path.dirname(currentConfigFilePath); |
| 195 | + |
| 196 | + let extractorConfig: IExtractorConfig = JsonFile.load(jsonConfigFile); |
| 197 | + |
| 198 | + while (extractorConfig.extends) { |
| 199 | + if (extractorConfig.extends.match(/^\./)) { |
| 200 | + // If extends has relative path. |
| 201 | + // Populate the api extractor config path defined in extends relative to current config path. |
| 202 | + currentConfigFilePath = path.resolve(originalConfigFileFolder, extractorConfig.extends); |
| 203 | + } else { |
| 204 | + // If extends has package path. |
| 205 | + currentConfigFilePath = resolve.sync( |
| 206 | + extractorConfig.extends, |
| 207 | + { |
| 208 | + basedir: originalConfigFileFolder |
| 209 | + } |
| 210 | + ); |
| 211 | + } |
| 212 | + // Check if this file was already processed. |
| 213 | + if (pathSet.has(currentConfigFilePath)) { |
| 214 | + throw new Error(`The API Extractor config files contain a cycle. "${currentConfigFilePath}"` |
| 215 | + + ` is included twice. Please check the "extends" values in config files.`); |
| 216 | + } |
| 217 | + pathSet.add(currentConfigFilePath); |
| 218 | + |
| 219 | + // Remove extends property from config for current config. |
| 220 | + delete extractorConfig.extends; |
| 221 | + |
| 222 | + // Load the extractor config defined in extends property. |
| 223 | + const baseConfig: IExtractorConfig = JsonFile.load(currentConfigFilePath); |
| 224 | + lodash.merge(baseConfig, extractorConfig); |
| 225 | + extractorConfig = baseConfig; |
| 226 | + } |
| 227 | + |
| 228 | + // Validate if the extractor config generated adheres to schema. |
| 229 | + Extractor.jsonSchema.validateObject(extractorConfig, jsonConfigFile); |
| 230 | + return extractorConfig; |
| 231 | + } |
| 232 | + |
181 | 233 | private static _applyConfigDefaults(config: IExtractorConfig): IExtractorConfig { |
182 | 234 | // Use the provided config to override the defaults |
183 | 235 | const normalized: IExtractorConfig = lodash.merge( |
|
0 commit comments