Skip to content

Commit c8186dc

Browse files
committed
Merge pull request #113 from ivogabe/common-source-dir
Calculate common source directory for files outside the common base path. Fixes #112
2 parents f668bd8 + a15940a commit c8186dc

File tree

6 files changed

+156
-0
lines changed

6 files changed

+156
-0
lines changed

lib/compiler.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
import ts = require('typescript');
22
import path = require('path');
33
import gutil = require('gulp-util');
4+
import sourceMap = require('source-map');
45
import tsApi = require('./tsapi');
56
import input = require('./input');
67
import output = require('./output');
78
import host = require('./host');
89
import project = require('./project');
910
import filter = require('./filter');
11+
import utils = require('./utils');
1012

1113
export interface ICompiler {
1214
prepare(_project: project.Project): void;
1315
inputFile(file: input.File);
1416
inputDone();
17+
/**
18+
* Corrects the paths in the sourcemap.
19+
* Returns true when the file is located
20+
* under the base path.
21+
*/
22+
correctSourceMap(map: sourceMap.RawSourceMap): boolean;
1523
}
1624

1725
/**
@@ -90,6 +98,51 @@ export class ProjectCompiler implements ICompiler {
9098

9199
this.project.output.finish();
92100
}
101+
102+
private _commonBaseDiff: [number, string];
103+
/**
104+
* Calculates the difference between the common base directory calculated based on the base paths of the input files
105+
* and the common source directory calculated by TypeScript.
106+
*/
107+
private get commonBaseDiff(): [number, string] {
108+
if (this._commonBaseDiff) return this._commonBaseDiff;
109+
110+
const expected = this.project.input.commonBasePath;
111+
const real = this.project.input.commonSourceDirectory;
112+
113+
const length = real.length - expected.length;
114+
115+
if (length > 0) {
116+
return this._commonBaseDiff = [length, real.substring(real.length - length)];
117+
} else {
118+
return this._commonBaseDiff = [length, expected.substring(expected.length + length)];
119+
}
120+
}
121+
122+
correctSourceMap(map: sourceMap.RawSourceMap) {
123+
const [diffLength, diff] = this.commonBaseDiff;
124+
125+
if (diffLength < 0) {
126+
// There were files added outside of the common base.
127+
map.sources = map.sources.map<string>(fileName => {
128+
const full = utils.normalizePath(path.join(this.project.input.commonSourceDirectory, fileName));
129+
let relative = path.relative(utils.normalizePath(this.project.input.commonBasePath), full);
130+
if (relative.substring(0, 3) === '../') {
131+
132+
return undefined;
133+
}
134+
135+
if (relative.substring(0, 2) === './') {
136+
relative = relative.substring(2);
137+
}
138+
return full.substring(full.length - relative.length);
139+
}).filter(fileName => fileName !== undefined);
140+
141+
if (map.sources.length === 0) return false;
142+
}
143+
144+
return true;
145+
}
93146
}
94147

95148
// TODO: file-based compiler

lib/input.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@ export class FileDictionary {
139139
})
140140
.reduce(getCommonBasePath)
141141
}
142+
get commonSourceDirectory() {
143+
const fileNames = this.getFileNames();
144+
return fileNames
145+
.filter(fileName => fileName.substr(fileName.length - 5).toLowerCase() !== '.d.ts')
146+
.map(fileName => {
147+
const file = this.files[utils.normalizePath(fileName)];
148+
return path.dirname(file.fileNameNormalized);
149+
})
150+
.reduce(getCommonBasePath)
151+
}
142152
}
143153

144154
export class FileCache {
@@ -213,6 +223,9 @@ export class FileCache {
213223
get commonBasePath() {
214224
return this.current.commonBasePath;
215225
}
226+
get commonSourceDirectory() {
227+
return this.current.commonSourceDirectory;
228+
}
216229

217230
isChanged(onlyGulp = false) {
218231
if (!this.previous) return true;

lib/output.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ export class Output {
7979
&& (file.content[OutputFileKind.Definitions] !== undefined || !this.project.options.declaration)) {
8080

8181
file.sourceMap = JSON.parse(file.content[OutputFileKind.SourceMap]);
82+
if (!this.project.compiler.correctSourceMap(file.sourceMap)) {
83+
file.skipPush = true;
84+
return;
85+
}
86+
8287
if (this.project.singleOutput) {
8388
file.original = this.project.input.firstSourceFile;
8489
file.sourceMapOrigins = this.project.input.getFileNames(true).map(fName => this.project.input.getFile(fName));
@@ -208,6 +213,12 @@ export class Output {
208213
}
209214

210215
private getError(info: ts.Diagnostic): reporter.TypeScriptError {
216+
if (info.code === 6059) {
217+
// "File '...' is not under 'rootDir' '...'. 'rootDir' is expected to contain all source files."
218+
// This is handled by ICompiler#correctSourceMap, so this error can be muted.
219+
return undefined;
220+
}
221+
211222
const err = <reporter.TypeScriptError> new Error();
212223
err.name = 'TypeScript error';
213224
err.diagnostic = info;
@@ -259,6 +270,8 @@ export class Output {
259270
this.error(this.getError(info));
260271
}
261272
error(error: reporter.TypeScriptError) {
273+
if (!error) return;
274+
262275
// Save errors for lazy compilation (if the next input is the same as the current),
263276
this.errors.push(error);
264277
// call reporter callback

release/compiler.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
var ts = require('typescript');
2+
var path = require('path');
23
var tsApi = require('./tsapi');
34
var output = require('./output');
45
var host = require('./host');
56
var filter = require('./filter');
7+
var utils = require('./utils');
68
/**
79
* Compiles a whole project, with full type checking
810
*/
@@ -58,6 +60,48 @@ var ProjectCompiler = (function () {
5860
}
5961
this.project.output.finish();
6062
};
63+
Object.defineProperty(ProjectCompiler.prototype, "commonBaseDiff", {
64+
/**
65+
* Calculates the difference between the common base directory calculated based on the base paths of the input files
66+
* and the common source directory calculated by TypeScript.
67+
*/
68+
get: function () {
69+
if (this._commonBaseDiff)
70+
return this._commonBaseDiff;
71+
var expected = this.project.input.commonBasePath;
72+
var real = this.project.input.commonSourceDirectory;
73+
var length = real.length - expected.length;
74+
if (length > 0) {
75+
return this._commonBaseDiff = [length, real.substring(real.length - length)];
76+
}
77+
else {
78+
return this._commonBaseDiff = [length, expected.substring(expected.length + length)];
79+
}
80+
},
81+
enumerable: true,
82+
configurable: true
83+
});
84+
ProjectCompiler.prototype.correctSourceMap = function (map) {
85+
var _this = this;
86+
var _a = this.commonBaseDiff, diffLength = _a[0], diff = _a[1];
87+
if (diffLength < 0) {
88+
// There were files added outside of the common base.
89+
map.sources = map.sources.map(function (fileName) {
90+
var full = utils.normalizePath(path.join(_this.project.input.commonSourceDirectory, fileName));
91+
var relative = path.relative(utils.normalizePath(_this.project.input.commonBasePath), full);
92+
if (relative.substring(0, 3) === '../') {
93+
return undefined;
94+
}
95+
if (relative.substring(0, 2) === './') {
96+
relative = relative.substring(2);
97+
}
98+
return full.substring(full.length - relative.length);
99+
}).filter(function (fileName) { return fileName !== undefined; });
100+
if (map.sources.length === 0)
101+
return false;
102+
}
103+
return true;
104+
};
61105
return ProjectCompiler;
62106
})();
63107
exports.ProjectCompiler = ProjectCompiler;

release/input.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,21 @@ var FileDictionary = (function () {
123123
enumerable: true,
124124
configurable: true
125125
});
126+
Object.defineProperty(FileDictionary.prototype, "commonSourceDirectory", {
127+
get: function () {
128+
var _this = this;
129+
var fileNames = this.getFileNames();
130+
return fileNames
131+
.filter(function (fileName) { return fileName.substr(fileName.length - 5).toLowerCase() !== '.d.ts'; })
132+
.map(function (fileName) {
133+
var file = _this.files[utils.normalizePath(fileName)];
134+
return path.dirname(file.fileNameNormalized);
135+
})
136+
.reduce(getCommonBasePath);
137+
},
138+
enumerable: true,
139+
configurable: true
140+
});
126141
return FileDictionary;
127142
})();
128143
exports.FileDictionary = FileDictionary;
@@ -193,6 +208,13 @@ var FileCache = (function () {
193208
enumerable: true,
194209
configurable: true
195210
});
211+
Object.defineProperty(FileCache.prototype, "commonSourceDirectory", {
212+
get: function () {
213+
return this.current.commonSourceDirectory;
214+
},
215+
enumerable: true,
216+
configurable: true
217+
});
196218
FileCache.prototype.isChanged = function (onlyGulp) {
197219
if (onlyGulp === void 0) { onlyGulp = false; }
198220
if (!this.previous)

release/output.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ var Output = (function () {
4949
&& file.content[OutputFileKind.SourceMap] !== undefined
5050
&& (file.content[OutputFileKind.Definitions] !== undefined || !this.project.options.declaration)) {
5151
file.sourceMap = JSON.parse(file.content[OutputFileKind.SourceMap]);
52+
if (!this.project.compiler.correctSourceMap(file.sourceMap)) {
53+
file.skipPush = true;
54+
return;
55+
}
5256
if (this.project.singleOutput) {
5357
file.original = this.project.input.firstSourceFile;
5458
file.sourceMapOrigins = this.project.input.getFileNames(true).map(function (fName) { return _this.project.input.getFile(fName); });
@@ -169,6 +173,11 @@ var Output = (function () {
169173
this.streamDts.push(null);
170174
};
171175
Output.prototype.getError = function (info) {
176+
if (info.code === 6059) {
177+
// "File '...' is not under 'rootDir' '...'. 'rootDir' is expected to contain all source files."
178+
// This is handled by ICompiler#correctSourceMap, so this error can be muted.
179+
return undefined;
180+
}
172181
var err = new Error();
173182
err.name = 'TypeScript error';
174183
err.diagnostic = info;
@@ -215,6 +224,8 @@ var Output = (function () {
215224
this.error(this.getError(info));
216225
};
217226
Output.prototype.error = function (error) {
227+
if (!error)
228+
return;
218229
// Save errors for lazy compilation (if the next input is the same as the current),
219230
this.errors.push(error);
220231
// call reporter callback

0 commit comments

Comments
 (0)