Skip to content

Commit fa7e390

Browse files
author
nickpape-msft
authored
Merge pull request microsoft#48 from Microsoft/nickpape/ts-config-provider
Refactor tsConfigProvider into a single class
2 parents 7611931 + abc4deb commit fa7e390

7 files changed

Lines changed: 158 additions & 65 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/gulp-core-build-typescript",
5+
"comment": "Refactor the API-Extractor to use the same config as the TypeScriptTask",
6+
"type": "minor"
7+
},
8+
{
9+
"packageName": "@microsoft/gulp-core-build",
10+
"comment": "Export the SchemaValidator",
11+
"type": "minor"
12+
}
13+
],
14+
"email": "nickpape@microsoft.com"
15+
}

gulp-core-build-typescript/src/ApiExtractorTask.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import * as gulp from 'gulp';
33
import * as mkdirp from 'mkdirp';
44
import * as os from 'os';
55
import * as path from 'path';
6-
import * as typescript from 'typescript';
76
import * as through from 'through2';
87
import * as gulpUtil from 'gulp-util';
98
import { GulpTask } from '@microsoft/gulp-core-build';
109
import { Analyzer, IApiAnalyzerOptions, ApiFileGenerator, ApiJsonGenerator } from '@microsoft/api-extractor';
10+
import { TypeScriptConfiguration } from './TypeScriptConfiguration';
11+
import * as typescript from 'typescript'; /* tslint:disable-line */
1112

1213
function writeStringToGulpUtilFile(content: string, filename: string = 'tempfile'): gulpUtil.File {
1314
return new gulpUtil.File({
@@ -86,27 +87,15 @@ export class ApiExtractorTask extends GulpTask<IApiExtractorTaskConfig> {
8687
const typingsFilePath: string = path.join(this.buildConfig.rootPath, 'typings/tsd.d.ts');
8788
const otherFiles: string[] = fsx.existsSync(typingsFilePath) ? [typingsFilePath] : [];
8889

90+
// tslint:disable-next-line:no-any
91+
const compilerOptions: typescript.CompilerOptions =
92+
TypeScriptConfiguration.getTypescriptOptions(this.buildConfig).compilerOptions;
93+
8994
const analyzerOptions: IApiAnalyzerOptions = {
9095
entryPointFile,
91-
// NOTE: Ideally these should be the same options from @microsoft/gulp-core-build-typescript,
92-
// however those options are generated at runtime by analyzing project files,
93-
// so some work would be required to export them. Also, the analyzer would run
94-
// faster if it could reuse the @microsoft/gulp-core-build-typescript AST rather than starting
95-
// from scratch. These are all reasons why it would be a good idea for api-extractor
96-
// to be integrated into @microsoft/gulp-core-build-typescript just like tslint. Unfortunately
97-
// @microsoft/gulp-core-build-typescript was moved to a Git repository that makes this impractical.
98-
compilerOptions: {
99-
target: typescript.ScriptTarget.ES5,
100-
module: typescript.ModuleKind.CommonJS,
101-
moduleResolution: typescript.ModuleResolutionKind.NodeJs,
102-
rootDir: this.buildConfig.rootPath,
103-
declaration: true,
104-
experimentalDecorators: true,
105-
jsx: typescript.JsxEmit.React,
106-
sourceMap: true
107-
},
108-
otherFiles: otherFiles
109-
} as any; /* tslint:disable-line:no-any */
96+
compilerOptions,
97+
otherFiles
98+
} as any; /* tslint:disable-line:no-any */
11099

111100
const analyzer: Analyzer = new Analyzer(
112101
(message: string, fileName: string, lineNumber: number): void => {
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import * as path from 'path';
2+
import assign = require('object-assign');
3+
import { SchemaValidator, IBuildConfig } from '@microsoft/gulp-core-build';
4+
import ts = require('gulp-typescript');
5+
import * as typescript from 'typescript';
6+
7+
export interface ITsConfigFile<T> {
8+
compilerOptions: T;
9+
}
10+
11+
/* tslint:disable:no-any */
12+
/*
13+
* A helper class which provides access to the TSConfig.json file for a particular project.
14+
* It also is a central place for managing the version of typescript which this project
15+
* should be built with.
16+
*/
17+
export class TypeScriptConfiguration {
18+
private static _baseTsConfig: ITsConfigFile<ts.Settings>;
19+
private static _typescript: any = require('typescript');
20+
21+
/**
22+
* Gets `gulp-typescript` version of the config (used by TypeScriptTask)
23+
* Returns a new object each time.
24+
*/
25+
public static getGulpTypescriptOptions(buildConfig: IBuildConfig): ITsConfigFile<ts.Settings> {
26+
const file: ITsConfigFile<ts.Settings> = assign({}, this._getTsConfigFile(buildConfig));
27+
assign(file.compilerOptions, {
28+
rootDir: buildConfig.rootPath,
29+
typescript: this.getTypescriptCompiler()
30+
});
31+
return file;
32+
}
33+
34+
/*
35+
* Gets the `typescript` version of the config (used by ApiExtractorTask)
36+
* Note: these differ slightly from the values in the tsconfig.json
37+
* Returns a new object each time.
38+
*
39+
* Specifically, the issue in the difference between:
40+
* typescript.CompilerOptions
41+
* &
42+
* ts.Settings
43+
*
44+
* Insofar as `ts.Settings` accepts (and requires) enums for certain options, rather than strings.
45+
* The clearest example is `moduleResolution` below.
46+
*/
47+
public static getTypescriptOptions(buildConfig: IBuildConfig): ITsConfigFile<typescript.CompilerOptions> {
48+
const oldConfig: ITsConfigFile<ts.Settings> = this.getGulpTypescriptOptions(buildConfig);
49+
const newConfig: ITsConfigFile<typescript.CompilerOptions> = oldConfig as any;
50+
51+
newConfig.compilerOptions.moduleResolution =
52+
oldConfig.compilerOptions.moduleResolution === 'node' ?
53+
typescript.ModuleResolutionKind.NodeJs : typescript.ModuleResolutionKind.Classic;
54+
55+
return newConfig;
56+
}
57+
58+
/**
59+
* Override the version of the typescript compiler
60+
*/
61+
public static setTypescriptCompiler(typescript: any): void {
62+
if (this._typescript) {
63+
throw new Error('The version of the typescript compiler should only be set once.');
64+
}
65+
if (this._baseTsConfig) {
66+
throw new Error('Set the version of the typescript compiler before tasks call getConfig()');
67+
}
68+
this._typescript = typescript;
69+
}
70+
71+
/**
72+
* Get the version of the typescript compiler which is to be used
73+
*/
74+
public static getTypescriptCompiler(): any {
75+
if (!this._typescript) {
76+
return require('typescript');
77+
}
78+
return this._typescript;
79+
}
80+
81+
/**
82+
* Helper function which reads the tsconfig.json (or provides one), and memoizes it
83+
*/
84+
private static _getTsConfigFile(config: IBuildConfig): ITsConfigFile<ts.Settings> {
85+
if (!this._baseTsConfig) {
86+
try {
87+
this._baseTsConfig = SchemaValidator.readCommentedJsonFile<any>(
88+
this._getConfigPath(config)
89+
);
90+
} catch (e) {
91+
/* no-op */
92+
}
93+
94+
if (!this._baseTsConfig) {
95+
this._baseTsConfig = {
96+
compilerOptions: {
97+
declaration: true,
98+
experimentalDecorators: true,
99+
jsx: 'react',
100+
moduleResolution: 'node',
101+
sourceMap: true,
102+
target: 'es5',
103+
noUnusedParameters: true,
104+
noUnusedLocals: true
105+
}
106+
};
107+
}
108+
}
109+
return this._baseTsConfig;
110+
}
111+
112+
/**
113+
* Extracts the path to the tsconfig.json based on the buildConfiguration
114+
*/
115+
private static _getConfigPath(buildConfig: IBuildConfig): string {
116+
return path.resolve(path.join(buildConfig.rootPath, 'tsconfig.json'));
117+
}
118+
}

gulp-core-build-typescript/src/TypeScriptTask.ts

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ts = require('gulp-typescript');
44
import * as path from 'path';
55

66
import { IBuildConfig } from '@microsoft/gulp-core-build';
7+
import { TypeScriptConfiguration } from './TypeScriptConfiguration';
78

89
interface ITypeScriptErrorObject {
910
diagnostic: {
@@ -46,18 +47,6 @@ export interface ITypeScriptTaskConfig {
4647
*/
4748
reporter?: ts.reporter.Reporter;
4849

49-
/**
50-
* Optional override for the TypeScript compiler.
51-
*/
52-
/* tslint:disable:no-any */
53-
typescript?: any;
54-
/* tslint:enable:no-any */
55-
56-
/**
57-
* Compiler options. Overrides values from the tsconfig.json
58-
*/
59-
compilerOptions?: ICompilerOptions;
60-
6150
/**
6251
* Removes comments from all generated `.js` files. Will **not** remove comments from generated `.d.ts` files.
6352
* Defaults to false.
@@ -84,7 +73,6 @@ export class TypeScriptTask extends GulpTask<ITypeScriptTaskConfig> {
8473
public name: string = 'typescript';
8574

8675
public taskConfig: ITypeScriptTaskConfig = {
87-
typescript: require('typescript'),
8876
failBuildOnErrors: true,
8977
reporter: {
9078
error: (error: ts.reporter.TypeScriptError): void => {
@@ -119,7 +107,6 @@ export class TypeScriptTask extends GulpTask<ITypeScriptTaskConfig> {
119107
],
120108
removeCommentsFromJavaScript: false,
121109
emitSourceMaps: true,
122-
compilerOptions: {},
123110
libDir: undefined,
124111
libAMDDir: undefined
125112
};
@@ -139,42 +126,23 @@ export class TypeScriptTask extends GulpTask<ITypeScriptTaskConfig> {
139126
errorCount: 0
140127
};
141128

142-
/* tslint:disable:no-any */
143-
let tsConfig: any = this.readJSONSync('tsconfig.json');
144-
/* tslint:enable:no-any */
145-
146-
// Set default config if no local tsconfig.json exists.
147-
if (!tsConfig) {
148-
tsConfig = {
149-
compilerOptions: {
150-
'declaration': true,
151-
'experimentalDecorators': true,
152-
'jsx': 'react',
153-
'moduleResolution': 'node',
154-
'sourceMap': true,
155-
'target': 'es5',
156-
'noUnusedParameters': true,
157-
'noUnusedLocals': true
158-
}
159-
};
160-
}
161-
162129
this._normalizeConfig();
163130

164131
// Log the compiler version for custom verisons.
165-
if (this.taskConfig.typescript && this.taskConfig.typescript.version) {
166-
this.log(`Using custom version: ${this.taskConfig.typescript.version}`);
132+
const typescript: any = TypeScriptConfiguration.getTypescriptCompiler(); // tslint:disable-line:no-any
133+
if (typescript && typescript.version) {
134+
this.log(`TypeScript version: ${typescript.version}`);
167135
}
168136

169-
const compilerOptions: ICompilerOptions = assign(
170-
{},
171-
tsConfig.compilerOptions,
172-
{
173-
module: 'commonjs',
174-
typescript: this.taskConfig.typescript
175-
},
176-
this.taskConfig.compilerOptions
177-
);
137+
// tslint:disable-next-line:no-any
138+
const compilerOptions: ICompilerOptions =
139+
TypeScriptConfiguration.getGulpTypescriptOptions(this.buildConfig).compilerOptions;
140+
141+
if (compilerOptions.module !== 'commonjs' && compilerOptions.module) {
142+
this.logWarning(`Your tsconfig.json file specifies a different "target" than expected. `
143+
+ `Expected: "commonjs". Actual: "${compilerOptions.module}". Using "commonjs" instead.`);
144+
compilerOptions.module = 'commonjs';
145+
}
178146

179147
this._tsProject = this._tsProject || ts.createProject(compilerOptions);
180148

gulp-core-build-typescript/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { RemoveTripleSlashReferenceTask } from './RemoveTripleSlashReferenceTask
55
import { IExecutable, parallel, serial } from '@microsoft/gulp-core-build';
66
import { ApiExtractorTask } from './ApiExtractorTask';
77

8+
export * from './TypeScriptConfiguration';
89
export { TypeScriptTask } from './TypeScriptTask';
910
export { ApiExtractorTask } from './ApiExtractorTask';
1011

gulp-core-build/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export * from './tasks/GenerateShrinkwrapTask';
2222
export * from './tasks/GulpTask';
2323
export * from './tasks/CleanTask';
2424
export * from './tasks/ValidateShrinkwrapTask';
25+
export * from './jsonUtilities/SchemaValidator';
2526

2627
/* tslint:disable:variable-name */
2728
require('es6-promise').polyfill();

gulp-core-build/src/jsonUtilities/SchemaValidator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/// <reference types="jju" />
2+
/// <reference types="z-schema" />
23

34
import * as os from 'os';
45
import * as fs from 'fs';

0 commit comments

Comments
 (0)