Skip to content

Commit d30fe6f

Browse files
author
Max Heiber
committed
add useFileCompiler setting
enables getting type-checking while enforcing --isolatedModules fix ivogabe#613
1 parent ba96804 commit d30fe6f

32 files changed

+324
-96
lines changed

lib/main.ts

Lines changed: 68 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ function getFinalTransformers(getCustomTransformers?: GetCustomTransformers): Fi
5959
return null;
6060
}
6161

62-
function getTypeScript(typescript: typeof ts) {
62+
function getTypeScript(settingsTs: typeof ts, gulpTsSettingsTs: typeof ts) {
63+
if (settingsTs && gulpTsSettingsTs && settingsTs !== gulpTsSettingsTs) {
64+
throw Error("settings.typescript and gulpTsSettings.typescript aren't equal. Please specify only one or use the same `typescript` for both.");
65+
}
66+
const typescript = settingsTs || gulpTsSettingsTs;
6367
if (typescript) return typescript;
6468
try {
6569
return require('typescript');
@@ -166,80 +170,99 @@ module compile {
166170
// Unsupported by gulp-typescript
167171
sourceRoot?: string; // Use sourceRoot in gulp-sourcemaps instead
168172
}
173+
174+
export interface GulpTsSettings {
175+
useFileCompiler?: boolean;
176+
typescript?: typeof ts;
177+
}
178+
169179
export type Project = _project.Project;
170180
export type CompileStream = _project.ICompileStream;
171181
export import reporter = _reporter;
172182

173-
export function createProject(tsConfigFileName: string, settings?: Settings): Project;
174-
export function createProject(settings?: Settings): Project;
175-
export function createProject(fileNameOrSettings?: string | Settings, settings?: Settings): Project {
183+
function _createProject({
184+
tsConfigFileName,
185+
settings,
186+
gulpTsSettings
187+
}: {
188+
tsConfigFileName: string | undefined;
189+
settings: Settings;
190+
gulpTsSettings: GulpTsSettings;
191+
}): Project {
176192
let finalTransformers: FinalTransformers;
177-
let tsConfigFileName: string = undefined;
178193
let tsConfigContent: TsConfig = undefined;
179194
let projectDirectory = process.cwd();
180195
let typescript: typeof ts;
181196
let compilerOptions: ts.CompilerOptions;
182197
let projectReferences: ReadonlyArray<ts.ProjectReference>;
183-
let fileName: string;
184198

185199
let rawConfig: any;
186200

187-
if (fileNameOrSettings !== undefined) {
188-
if (typeof fileNameOrSettings === 'string') {
189-
fileName = fileNameOrSettings;
190-
tsConfigFileName = path.resolve(process.cwd(), fileName);
191-
projectDirectory = path.dirname(tsConfigFileName);
192-
if (settings === undefined) settings = {};
193-
} else {
194-
settings = fileNameOrSettings || {};
195-
}
196-
197-
finalTransformers = getFinalTransformers(settings.getCustomTransformers);
201+
finalTransformers = getFinalTransformers(settings.getCustomTransformers);
198202

199-
typescript = getTypeScript(settings.typescript);
200-
settings = checkAndNormalizeSettings(settings);
203+
typescript = getTypeScript(settings.typescript, gulpTsSettings.typescript);
204+
settings = checkAndNormalizeSettings(settings);
201205

202-
const settingsResult = typescript.convertCompilerOptionsFromJson(settings, projectDirectory);
203-
204-
if (settingsResult.errors) {
205-
reportErrors(settingsResult.errors, typescript);
206-
}
206+
const settingsResult = typescript.convertCompilerOptionsFromJson(settings, projectDirectory);
207207

208-
compilerOptions = settingsResult.options;
208+
if (settingsResult.errors) {
209+
reportErrors(settingsResult.errors, typescript);
210+
}
209211

210-
if (fileName !== undefined) {
211-
let tsConfig = typescript.readConfigFile(tsConfigFileName, typescript.sys.readFile);
212-
if (tsConfig.error) {
213-
console.log(tsConfig.error.messageText);
214-
}
212+
compilerOptions = settingsResult.options;
215213

216-
let parsed: ts.ParsedCommandLine =
217-
typescript.parseJsonConfigFileContent(
218-
tsConfig.config || {},
219-
getTsconfigSystem(typescript),
220-
path.resolve(projectDirectory),
221-
compilerOptions,
222-
tsConfigFileName);
214+
if (tsConfigFileName !== undefined) {
215+
let tsConfig = typescript.readConfigFile(tsConfigFileName, typescript.sys.readFile);
216+
if (tsConfig.error) {
217+
console.log(tsConfig.error.messageText);
218+
}
223219

224-
rawConfig = parsed.raw;
220+
let parsed: ts.ParsedCommandLine =
221+
typescript.parseJsonConfigFileContent(
222+
tsConfig.config || {},
223+
getTsconfigSystem(typescript),
224+
path.resolve(projectDirectory),
225+
compilerOptions,
226+
tsConfigFileName);
225227

226-
tsConfigContent = parsed.raw;
228+
rawConfig = parsed.raw;
227229

228-
if (parsed.errors) {
229-
reportErrors(parsed.errors, typescript, [18003]);
230-
}
230+
tsConfigContent = parsed.raw;
231231

232-
compilerOptions = parsed.options;
233-
projectReferences = parsed.projectReferences;
232+
if (parsed.errors) {
233+
reportErrors(parsed.errors, typescript, [18003]);
234234
}
235+
236+
compilerOptions = parsed.options;
237+
projectReferences = parsed.projectReferences;
235238
}
236239

237240
normalizeCompilerOptions(compilerOptions, typescript);
238-
const project = _project.setupProject(projectDirectory, tsConfigFileName, rawConfig, tsConfigContent, compilerOptions, projectReferences, typescript, finalTransformers);
241+
const project = _project.setupProject(projectDirectory, tsConfigFileName, rawConfig, tsConfigContent, compilerOptions, projectReferences, typescript, finalTransformers, gulpTsSettings.useFileCompiler);
239242

240243
return project;
241244
}
242245

246+
export function createProject(tsConfigFileName: string, settings?: Settings, gulpTsSettings?: GulpTsSettings): Project;
247+
export function createProject(settings?: Settings, gulpTsSettings?: GulpTsSettings): Project;
248+
249+
export function createProject(fileNameOrSettings?: string | Settings, settingsOrGulpTsSettings?: Settings | GulpTsSettings, gulpTsSettingsParam?: GulpTsSettings): Project {
250+
// overload: createProject(tsConfigFileName, settings, gulpTsSettings)
251+
if (typeof fileNameOrSettings === 'string') {
252+
return _createProject({
253+
tsConfigFileName: fileNameOrSettings,
254+
settings: settingsOrGulpTsSettings || {},
255+
gulpTsSettings: gulpTsSettingsParam || {} }
256+
);
257+
}
258+
// overload: createProject(settings, gulpTsSettings)
259+
return _createProject({
260+
tsConfigFileName: undefined,
261+
settings: fileNameOrSettings || {},
262+
gulpTsSettings: settingsOrGulpTsSettings as GulpTsSettings || {} }
263+
);
264+
}
265+
243266
export function filter(...args: any[]) {
244267
utils.deprecate('ts.filter() is deprecated',
245268
'soon you can use tsProject.resolve()',

lib/project.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,24 @@ export interface ProjectInfo {
5050
reporter: Reporter;
5151
}
5252

53-
export function setupProject(projectDirectory: string, configFileName: string, rawConfig: any, config: TsConfig, options: ts.CompilerOptions, projectReferences: ReadonlyArray<ts.ProjectReference>, typescript: typeof ts, finalTransformers: FinalTransformers) {
53+
export function setupProject(projectDirectory: string, configFileName: string, rawConfig: any, config: TsConfig, options: ts.CompilerOptions, projectReferences: ReadonlyArray<ts.ProjectReference>, typescript: typeof ts, finalTransformers: FinalTransformers, useFileCompiler: boolean | undefined) {
5454
const input = new FileCache(typescript, options);
55-
const compiler: ICompiler = options.isolatedModules ? new FileCompiler() : new ProjectCompiler();
56-
let running = false;
55+
if (useFileCompiler && !options.isolatedModules) {
56+
throw Error("useFileCompiler: true can only be used if config.compilerOptions.isolatedModules is also set to true");
57+
}
58+
let compiler: ICompiler;
5759

58-
if (options.isolatedModules) {
60+
if (options.isolatedModules && (useFileCompiler === undefined || useFileCompiler === true)) {
61+
compiler = new FileCompiler();
5962
options.newLine = typescript.NewLineKind.LineFeed;
6063
options.sourceMap = false;
6164
options.declaration = false;
6265
options.inlineSourceMap = true;
63-
}
66+
}
67+
else {
68+
compiler = new ProjectCompiler();
69+
}
70+
let running = false;
6471

6572
const project: PartialProject = (reporter) => {
6673
if (running) {

readme.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Almost all options from TypeScript are supported.
4444
- `noEmitOnError` (boolean) - Do not emit outputs if any type checking errors were reported.
4545
- `noEmitHelpers` (boolean) - Do not generate custom helper functions like __extends in compiled output.
4646
- `preserveConstEnums` (boolean) - Do not erase const enum declarations in generated code.
47-
- `isolatedModules` (boolean) - Compiles files seperately and doesn't check types, which causes a big speed increase. You have to use gulp-plumber and TypeScript 1.5+.
47+
- `isolatedModules` (boolean) - By default, this option compiles files seperately and doesn't check types, which causes a big speed increase. You have to use gulp-plumber and TypeScript 1.5+. If you use the `createProject` API (see below) and specify `useFileCompiler: false`, the meaning changes to match that of `tsc`. That is, you *will* get type-checking at the cost of slower compilation. In addition, TypeScript will error on constructs that would prevent safe separate compilation.
4848
- `allowJs` (boolean) - Allow JavaScript files to be compiled.
4949
- `rootDir` - Specifies the root directory of input files. Only use to control the output directory structure with `outDir`.
5050

@@ -60,7 +60,8 @@ API overview
6060
gulp-typescript can be imported using `const ts = require('gulp-typescript');`. It provides the following functions:
6161

6262
- `ts(options?)` - Returns a gulp stream that compiles TypeScript files using the specified options.
63-
- `ts.createProject(options?)`, `ts.createProject(tsconfig filename, options?)` - Returns a project. The intended usage is to create a project outside of a task with `const tsProject = ts.createProject(..);`. Within a task, `tsProject()` can be used to compile a stream of TypeScript files.
63+
- `ts.createProject(options?)`, `ts.createProject(tsconfig filename?: string, options?: CompilerOptions, gulpTsOptions?: GulpTsOptions)` - Returns a project. The intended usage is to create a project outside of a task with `const tsProject = ts.createProject(..);`. Within a task, `tsProject()` can be used to compile a stream of TypeScript files. The shape of `GulpTsOptions` is `{ useFileCompiler: boolean, typescript: TS }`. `useFileCompiler` defaults to `true` when `isolatedModules` is `true`. Specify `useFileCompiler: false` to use the project compiler even if `isolatedModules` is `true`.
64+
> The important differences are that the project compiler does type checking but the file compiler is faster and more more memory-efficient. Using `isolatedModules` and `useFileCompiler: false` together does extra type-checking that ensures that `useFileCompiler: true` *would* be safe.
6465
- `tsProject.src()` - Returns a stream containing the source files (.ts) from a tsconfig file. It can only be used if you create a project with a `tsconfig.json` file. It is a replacement for `gulp.src(..)`.
6566

6667
Both `ts(..)` and `tsProject()` provide sub-streams that only contain the JavaScript or declaration files. An example is shown later in the readme.

release/main.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ declare module compile {
4141
[name: string]: any;
4242
sourceRoot?: string;
4343
}
44+
interface GulpTsSettings {
45+
useFileCompiler?: boolean;
46+
typescript?: typeof ts;
47+
}
4448
type Project = _project.Project;
4549
type CompileStream = _project.ICompileStream;
4650
export import reporter = _reporter;
47-
function createProject(tsConfigFileName: string, settings?: Settings): Project;
48-
function createProject(settings?: Settings): Project;
51+
function createProject(tsConfigFileName: string, settings?: Settings, gulpTsSettings?: GulpTsSettings): Project;
52+
function createProject(settings?: Settings, gulpTsSettings?: GulpTsSettings): Project;
4953
function filter(...args: any[]): void;
5054
}
5155
export = compile;

release/main.js

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ function getFinalTransformers(getCustomTransformers) {
4949
}
5050
return null;
5151
}
52-
function getTypeScript(typescript) {
52+
function getTypeScript(settingsTs, gulpTsSettingsTs) {
53+
if (settingsTs && gulpTsSettingsTs && settingsTs !== gulpTsSettingsTs) {
54+
throw Error("settings.typescript and gulpTsSettings.typescript aren't equal. Please specify only one or use the same `typescript` for both.");
55+
}
56+
const typescript = settingsTs || gulpTsSettingsTs;
5357
if (typescript)
5458
return typescript;
5559
try {
@@ -98,54 +102,56 @@ function reportErrors(errors, typescript, ignore = []) {
98102
}
99103
(function (compile) {
100104
compile.reporter = _reporter;
101-
function createProject(fileNameOrSettings, settings) {
105+
function _createProject({ tsConfigFileName, settings, gulpTsSettings }) {
102106
let finalTransformers;
103-
let tsConfigFileName = undefined;
104107
let tsConfigContent = undefined;
105108
let projectDirectory = process.cwd();
106109
let typescript;
107110
let compilerOptions;
108111
let projectReferences;
109-
let fileName;
110112
let rawConfig;
111-
if (fileNameOrSettings !== undefined) {
112-
if (typeof fileNameOrSettings === 'string') {
113-
fileName = fileNameOrSettings;
114-
tsConfigFileName = path.resolve(process.cwd(), fileName);
115-
projectDirectory = path.dirname(tsConfigFileName);
116-
if (settings === undefined)
117-
settings = {};
118-
}
119-
else {
120-
settings = fileNameOrSettings || {};
121-
}
122-
finalTransformers = getFinalTransformers(settings.getCustomTransformers);
123-
typescript = getTypeScript(settings.typescript);
124-
settings = checkAndNormalizeSettings(settings);
125-
const settingsResult = typescript.convertCompilerOptionsFromJson(settings, projectDirectory);
126-
if (settingsResult.errors) {
127-
reportErrors(settingsResult.errors, typescript);
113+
finalTransformers = getFinalTransformers(settings.getCustomTransformers);
114+
typescript = getTypeScript(settings.typescript, gulpTsSettings.typescript);
115+
settings = checkAndNormalizeSettings(settings);
116+
const settingsResult = typescript.convertCompilerOptionsFromJson(settings, projectDirectory);
117+
if (settingsResult.errors) {
118+
reportErrors(settingsResult.errors, typescript);
119+
}
120+
compilerOptions = settingsResult.options;
121+
if (tsConfigFileName !== undefined) {
122+
let tsConfig = typescript.readConfigFile(tsConfigFileName, typescript.sys.readFile);
123+
if (tsConfig.error) {
124+
console.log(tsConfig.error.messageText);
128125
}
129-
compilerOptions = settingsResult.options;
130-
if (fileName !== undefined) {
131-
let tsConfig = typescript.readConfigFile(tsConfigFileName, typescript.sys.readFile);
132-
if (tsConfig.error) {
133-
console.log(tsConfig.error.messageText);
134-
}
135-
let parsed = typescript.parseJsonConfigFileContent(tsConfig.config || {}, getTsconfigSystem(typescript), path.resolve(projectDirectory), compilerOptions, tsConfigFileName);
136-
rawConfig = parsed.raw;
137-
tsConfigContent = parsed.raw;
138-
if (parsed.errors) {
139-
reportErrors(parsed.errors, typescript, [18003]);
140-
}
141-
compilerOptions = parsed.options;
142-
projectReferences = parsed.projectReferences;
126+
let parsed = typescript.parseJsonConfigFileContent(tsConfig.config || {}, getTsconfigSystem(typescript), path.resolve(projectDirectory), compilerOptions, tsConfigFileName);
127+
rawConfig = parsed.raw;
128+
tsConfigContent = parsed.raw;
129+
if (parsed.errors) {
130+
reportErrors(parsed.errors, typescript, [18003]);
143131
}
132+
compilerOptions = parsed.options;
133+
projectReferences = parsed.projectReferences;
144134
}
145135
normalizeCompilerOptions(compilerOptions, typescript);
146-
const project = _project.setupProject(projectDirectory, tsConfigFileName, rawConfig, tsConfigContent, compilerOptions, projectReferences, typescript, finalTransformers);
136+
const project = _project.setupProject(projectDirectory, tsConfigFileName, rawConfig, tsConfigContent, compilerOptions, projectReferences, typescript, finalTransformers, gulpTsSettings.useFileCompiler);
147137
return project;
148138
}
139+
function createProject(fileNameOrSettings, settingsOrGulpTsSettings, gulpTsSettingsParam) {
140+
// overload: createProject(tsConfigFileName, settings, gulpTsSettings)
141+
if (typeof fileNameOrSettings === 'string') {
142+
return _createProject({
143+
tsConfigFileName: fileNameOrSettings,
144+
settings: settingsOrGulpTsSettings || {},
145+
gulpTsSettings: gulpTsSettingsParam || {}
146+
});
147+
}
148+
// overload: createProject(settings, gulpTsSettings)
149+
return _createProject({
150+
tsConfigFileName: undefined,
151+
settings: fileNameOrSettings || {},
152+
gulpTsSettings: settingsOrGulpTsSettings || {}
153+
});
154+
}
149155
compile.createProject = createProject;
150156
function filter(...args) {
151157
utils.deprecate('ts.filter() is deprecated', 'soon you can use tsProject.resolve()', 'Filters have been removed as of gulp-typescript 3.0.\nSoon tsProject.resolve() will be available as an alternative.\nSee https://github.com/ivogabe/gulp-typescript/issues/190.');

release/project.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface ProjectInfo {
2828
directory: string;
2929
reporter: Reporter;
3030
}
31-
export declare function setupProject(projectDirectory: string, configFileName: string, rawConfig: any, config: TsConfig, options: ts.CompilerOptions, projectReferences: ReadonlyArray<ts.ProjectReference>, typescript: typeof ts, finalTransformers: FinalTransformers): Project;
31+
export declare function setupProject(projectDirectory: string, configFileName: string, rawConfig: any, config: TsConfig, options: ts.CompilerOptions, projectReferences: ReadonlyArray<ts.ProjectReference>, typescript: typeof ts, finalTransformers: FinalTransformers, useFileCompiler: boolean | undefined): Project;
3232
export interface ICompileStream extends NodeJS.ReadWriteStream {
3333
js: stream.Readable;
3434
dts: stream.Readable;

release/project.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,23 @@ const reporter_1 = require("./reporter");
1818
const input_1 = require("./input");
1919
const output_1 = require("./output");
2020
const compiler_1 = require("./compiler");
21-
function setupProject(projectDirectory, configFileName, rawConfig, config, options, projectReferences, typescript, finalTransformers) {
21+
function setupProject(projectDirectory, configFileName, rawConfig, config, options, projectReferences, typescript, finalTransformers, useFileCompiler) {
2222
const input = new input_1.FileCache(typescript, options);
23-
const compiler = options.isolatedModules ? new compiler_1.FileCompiler() : new compiler_1.ProjectCompiler();
24-
let running = false;
25-
if (options.isolatedModules) {
23+
if (useFileCompiler && !options.isolatedModules) {
24+
throw Error("useFileCompiler: true can only be used if config.compilerOptions.isolatedModules is also set to true");
25+
}
26+
let compiler;
27+
if (options.isolatedModules && (useFileCompiler === undefined || useFileCompiler === true)) {
28+
compiler = new compiler_1.FileCompiler();
2629
options.newLine = typescript.NewLineKind.LineFeed;
2730
options.sourceMap = false;
2831
options.declaration = false;
2932
options.inlineSourceMap = true;
3033
}
34+
else {
35+
compiler = new compiler_1.ProjectCompiler();
36+
}
37+
let running = false;
3138
const project = (reporter) => {
3239
if (running) {
3340
throw new Error('gulp-typescript: A project cannot be used in two compilations at the same time. Create multiple projects with createProject instead.');
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
TypeScript error: test/useFileCompiler/excluded-dir/test.ts(1,1): error TS2304: Cannot find name 'notCompiled'.
2+
TypeScript error: test/useFileCompiler/excluded-file.ts(1,1): error TS2304: Cannot find name 'notCompiled'.
3+
{
4+
"transpileErrors": 0,
5+
"optionsErrors": 0,
6+
"syntaxErrors": 0,
7+
"globalErrors": 0,
8+
"semanticErrors": 2,
9+
"declarationErrors": 0,
10+
"emitErrors": 0,
11+
"emitSkipped": false
12+
}

test/baselines/useFileCompiler/2.3/js/excluded-dir/test.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/baselines/useFileCompiler/2.3/js/excluded-dir/test.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/baselines/useFileCompiler/2.3/js/excluded-file.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/baselines/useFileCompiler/2.3/js/excluded-file.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)