Skip to content

Commit b2d411d

Browse files
Added support for running the local copy of babel for a given project
1 parent fe1947f commit b2d411d

File tree

3 files changed

+189
-105
lines changed

3 files changed

+189
-105
lines changed

dist/main/lang/modules/building.js

Lines changed: 89 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ var path = require('path');
33
var fs = require('fs');
44
var fsUtil_1 = require("../../utils/fsUtil");
55
var utils_1 = require("../utils");
6-
var babel;
6+
var findup = require('findup');
7+
var babels = {};
8+
var babelConfigs = {};
79
exports.Not_In_Context = "/* NotInContext */";
810
function diagnosticToTSError(diagnostic) {
911
var filePath = diagnostic.file.fileName;
@@ -37,13 +39,14 @@ function emitFile(proj, filePath) {
3739
var sourceMapContents = {};
3840
output.outputFiles.forEach(function (o) {
3941
mkdirp.sync(path.dirname(o.name));
40-
var additionalEmits = runExternalTranspiler(filePath, sourceFile.text, o, proj, sourceMapContents);
41-
if (!sourceMapContents[o.name] && !proj.projectFile.project.compilerOptions.noEmit) {
42-
fs.writeFileSync(o.name, o.text, "utf8");
43-
}
44-
additionalEmits.forEach(function (a) {
45-
mkdirp.sync(path.dirname(a.name));
46-
fs.writeFileSync(a.name, a.text, "utf8");
42+
runExternalTranspiler(filePath, sourceFile.text, o, proj, sourceMapContents).then(function (additionalEmits) {
43+
if (!sourceMapContents[o.name] && !proj.projectFile.project.compilerOptions.noEmit) {
44+
fs.writeFileSync(o.name, o.text, "utf8");
45+
}
46+
additionalEmits.forEach(function (a) {
47+
mkdirp.sync(path.dirname(a.name));
48+
fs.writeFileSync(a.name, a.text, "utf8");
49+
});
4750
});
4851
});
4952
}
@@ -75,20 +78,60 @@ function getRawOutput(proj, filePath) {
7578
return output;
7679
}
7780
exports.getRawOutput = getRawOutput;
81+
function getBabelInstance(projectDirectory) {
82+
return new Promise(function (resolve) {
83+
if (!babels[projectDirectory]) {
84+
findup(projectDirectory, 'node_modules/babel-core', function (err, dir) {
85+
if (err) {
86+
findup(projectDirectory, 'node_modules/babel', function (err, dir) {
87+
if (err) {
88+
babels[projectDirectory] = require('babel');
89+
}
90+
else {
91+
babels[projectDirectory] = require(path.join(dir, 'node_modules/babel'));
92+
}
93+
resolve(babels[projectDirectory]);
94+
});
95+
}
96+
else {
97+
babels[projectDirectory] = require(path.join(dir, 'node_modules/babel-core'));
98+
resolve(babels[projectDirectory]);
99+
}
100+
});
101+
}
102+
else {
103+
resolve(babels[projectDirectory]);
104+
}
105+
}).then(function (babel) {
106+
return new Promise(function (resolve) {
107+
findup(projectDirectory, '.babelrc', function (err, dir) {
108+
if (err)
109+
resolve(babel);
110+
fs.readFile(path.join(dir, '.babelrc'), function (err, data) {
111+
try {
112+
babelConfigs[projectDirectory] = JSON.parse(data.toString());
113+
}
114+
catch (e) { }
115+
resolve(babel);
116+
});
117+
});
118+
});
119+
});
120+
}
78121
function runExternalTranspiler(sourceFileName, sourceFileText, outputFile, project, sourceMapContents) {
79122
if (!isJSFile(outputFile.name) && !isJSSourceMapFile(outputFile.name)) {
80-
return [];
123+
return Promise.resolve([]);
81124
}
82125
var settings = project.projectFile.project;
83126
var externalTranspiler = settings.externalTranspiler;
84127
if (!externalTranspiler) {
85-
return [];
128+
return Promise.resolve([]);
86129
}
87130
if (isJSSourceMapFile(outputFile.name)) {
88131
var sourceMapPayload = JSON.parse(outputFile.text);
89132
var jsFileName = fsUtil_1.consistentPath(path.resolve(path.dirname(outputFile.name), sourceMapPayload.file));
90133
sourceMapContents[outputFile.name] = { jsFileName: jsFileName, sourceMapPayload: sourceMapPayload };
91-
return [];
134+
return Promise.resolve([]);
92135
}
93136
if (typeof externalTranspiler === 'string') {
94137
externalTranspiler = {
@@ -98,43 +141,42 @@ function runExternalTranspiler(sourceFileName, sourceFileText, outputFile, proje
98141
}
99142
if (typeof externalTranspiler === 'object') {
100143
if (externalTranspiler.name.toLocaleLowerCase() === "babel") {
101-
if (!babel) {
102-
babel = require("babel");
103-
}
104-
var babelOptions = utils_1.assign({}, externalTranspiler.options || {}, {
105-
filename: outputFile.name
106-
});
107-
var sourceMapFileName = getJSMapNameForJSFile(outputFile.name);
108-
if (sourceMapContents[sourceMapFileName]) {
109-
babelOptions.inputSourceMap = sourceMapContents[sourceMapFileName].sourceMapPayload;
110-
var baseName = path.basename(sourceFileName);
111-
babelOptions.inputSourceMap.sources = [baseName];
112-
babelOptions.inputSourceMap.file = baseName;
113-
}
114-
if (settings.compilerOptions.sourceMap) {
115-
babelOptions.sourceMaps = true;
116-
}
117-
if (settings.compilerOptions.inlineSourceMap) {
118-
babelOptions.sourceMaps = "inline";
119-
}
120-
if (!settings.compilerOptions.removeComments) {
121-
babelOptions.comments = true;
122-
}
123-
var babelResult = babel.transform(outputFile.text, babelOptions);
124-
outputFile.text = babelResult.code;
125-
if (babelResult.map && settings.compilerOptions.sourceMap) {
126-
var additionalEmit = {
127-
name: sourceMapFileName,
128-
text: JSON.stringify(babelResult.map),
129-
writeByteOrderMark: settings.compilerOptions.emitBOM
130-
};
131-
if (additionalEmit.name === "") {
132-
console.warn("The TypeScript language service did not yet provide a .js.map name for file " + outputFile.name);
133-
return [];
144+
return getBabelInstance(project.projectFile.projectFileDirectory).then(function (babel) {
145+
var babelOptions = utils_1.assign(babelConfigs[project.projectFile.projectFileDirectory] || {}, externalTranspiler.options || {}, {
146+
filename: outputFile.name
147+
});
148+
var sourceMapFileName = getJSMapNameForJSFile(outputFile.name);
149+
if (sourceMapContents[sourceMapFileName]) {
150+
babelOptions.inputSourceMap = sourceMapContents[sourceMapFileName].sourceMapPayload;
151+
var baseName = path.basename(sourceFileName);
152+
babelOptions.inputSourceMap.sources = [baseName];
153+
babelOptions.inputSourceMap.file = baseName;
134154
}
135-
return [additionalEmit];
136-
}
137-
return [];
155+
if (settings.compilerOptions.sourceMap) {
156+
babelOptions.sourceMaps = true;
157+
}
158+
if (settings.compilerOptions.inlineSourceMap) {
159+
babelOptions.sourceMaps = "inline";
160+
}
161+
if (!settings.compilerOptions.removeComments) {
162+
babelOptions.comments = true;
163+
}
164+
var babelResult = babel.transform(outputFile.text, babelOptions);
165+
outputFile.text = babelResult.code;
166+
if (babelResult.map && settings.compilerOptions.sourceMap) {
167+
var additionalEmit = {
168+
name: sourceMapFileName,
169+
text: JSON.stringify(babelResult.map),
170+
writeByteOrderMark: settings.compilerOptions.emitBOM
171+
};
172+
if (additionalEmit.name === "") {
173+
console.warn("The TypeScript language service did not yet provide a .js.map name for file " + outputFile.name);
174+
return [];
175+
}
176+
return [additionalEmit];
177+
}
178+
return [];
179+
});
138180
}
139181
}
140182
function getJSMapNameForJSFile(jsFileName) {

lib/main/lang/modules/building.ts

Lines changed: 99 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import fs = require('fs');
55
import {pathIsRelative, makeRelativePath} from "../../tsconfig/tsconfig";
66
import {consistentPath} from "../../utils/fsUtil";
77
import {createMap, assign} from "../utils";
8+
var findup = require('findup');
89

910
/** Lazy loaded babel tanspiler */
10-
let babel: any;
11+
let babels: { [key: string]: any } = {};
12+
/** Store babel configurations from .babelrc */
13+
let babelConfigs: { [key: string]: any } = {};
1114

1215
/** If we get a compile request for a ts file that is not in project. We return a js file with the following content */
1316
export const Not_In_Context = "/* NotInContext */";
@@ -54,23 +57,23 @@ export function emitFile(proj: project.Project, filePath: string): EmitOutput {
5457
let sourceMapContents: { [index: string]: any } = {};
5558
output.outputFiles.forEach(o => {
5659
mkdirp.sync(path.dirname(o.name));
57-
let additionalEmits = runExternalTranspiler(
60+
runExternalTranspiler(
5861
filePath,
5962
sourceFile.text,
6063
o,
6164
proj,
6265
sourceMapContents
63-
);
64-
65-
if (!sourceMapContents[o.name] && !proj.projectFile.project.compilerOptions.noEmit) {
66-
// .js.map files will be written as an "additional emit" later.
67-
fs.writeFileSync(o.name, o.text, "utf8");
68-
}
66+
).then((additionalEmits) => {
67+
if (!sourceMapContents[o.name] && !proj.projectFile.project.compilerOptions.noEmit) {
68+
// .js.map files will be written as an "additional emit" later.
69+
fs.writeFileSync(o.name, o.text, "utf8");
70+
}
6971

70-
additionalEmits.forEach(a => {
71-
mkdirp.sync(path.dirname(a.name));
72-
fs.writeFileSync(a.name, a.text, "utf8");
73-
})
72+
additionalEmits.forEach(a => {
73+
mkdirp.sync(path.dirname(a.name));
74+
fs.writeFileSync(a.name, a.text, "utf8");
75+
});
76+
});
7477
});
7578
}
7679

@@ -103,27 +106,66 @@ export function getRawOutput(proj: project.Project, filePath: string): ts.EmitOu
103106
return output;
104107
}
105108

109+
function getBabelInstance(projectDirectory: string) {
110+
return new Promise<any>(resolve => {
111+
if (!babels[projectDirectory]) {
112+
findup(projectDirectory, 'node_modules/babel-core', function(err: any, dir: string) {
113+
if (err) {
114+
findup(projectDirectory, 'node_modules/babel', function(err: any, dir: string) {
115+
if (err) {
116+
babels[projectDirectory] = require('babel');
117+
} else {
118+
babels[projectDirectory] = require(path.join(dir, 'node_modules/babel'));
119+
}
120+
121+
resolve(babels[projectDirectory]);
122+
});
123+
} else {
124+
babels[projectDirectory] = require(path.join(dir, 'node_modules/babel-core'));
125+
resolve(babels[projectDirectory]);
126+
}
127+
});
128+
} else {
129+
resolve(babels[projectDirectory]);
130+
}
131+
}).then(babel => {
132+
return new Promise<any>(resolve => {
133+
findup(projectDirectory, '.babelrc', function(err: any, dir) {
134+
if (err) resolve(babel);
135+
136+
fs.readFile(path.join(dir, '.babelrc'), function(err, data) {
137+
try {
138+
babelConfigs[projectDirectory] = JSON.parse(data.toString());
139+
} catch (e) { }
140+
141+
resolve(babel);
142+
});
143+
});
144+
});
145+
});
146+
}
147+
106148
function runExternalTranspiler(sourceFileName: string,
107149
sourceFileText: string,
108150
outputFile: ts.OutputFile,
109151
project: project.Project,
110-
sourceMapContents: { [index: string]: any }): ts.OutputFile[] {
152+
sourceMapContents: { [index: string]: any }): Promise<ts.OutputFile[]> {
111153

112154
if (!isJSFile(outputFile.name) && !isJSSourceMapFile(outputFile.name)) {
113-
return [];
155+
return Promise.resolve([]);
114156
}
115157

116158
let settings = project.projectFile.project;
117159
let externalTranspiler = settings.externalTranspiler;
118160
if (!externalTranspiler) {
119-
return [];
161+
return Promise.resolve([]);
120162
}
121163

122164
if (isJSSourceMapFile(outputFile.name)) {
123165
let sourceMapPayload = JSON.parse(outputFile.text);
124166
let jsFileName = consistentPath(path.resolve(path.dirname(outputFile.name), sourceMapPayload.file));
125167
sourceMapContents[outputFile.name] = { jsFileName: jsFileName, sourceMapPayload };
126-
return [];
168+
return Promise.resolve([]);
127169
}
128170

129171
if (typeof externalTranspiler === 'string') {
@@ -136,54 +178,53 @@ function runExternalTranspiler(sourceFileName: string,
136178
// We need this type guard to narrow externalTranspiler's type
137179
if (typeof externalTranspiler === 'object') {
138180
if (externalTranspiler.name.toLocaleLowerCase() === "babel") {
139-
if (!babel) {
140-
babel = require("babel")
141-
}
142-
143-
let babelOptions: any = assign({}, externalTranspiler.options || {}, {
144-
filename: outputFile.name
145-
});
181+
return getBabelInstance(project.projectFile.projectFileDirectory).then((babel) => {
146182

147-
let sourceMapFileName = getJSMapNameForJSFile(outputFile.name);
183+
let babelOptions: any = assign(babelConfigs[project.projectFile.projectFileDirectory] || {}, externalTranspiler.options || {}, {
184+
filename: outputFile.name
185+
});
148186

149-
if (sourceMapContents[sourceMapFileName]) {
150-
babelOptions.inputSourceMap = sourceMapContents[sourceMapFileName].sourceMapPayload;
151-
let baseName = path.basename(sourceFileName);
152-
// NOTE: Babel generates invalid source map without consistent `sources` and `file`.
153-
babelOptions.inputSourceMap.sources = [baseName];
154-
babelOptions.inputSourceMap.file = baseName;
155-
}
156-
if (settings.compilerOptions.sourceMap) {
157-
babelOptions.sourceMaps = true;
158-
}
159-
if (settings.compilerOptions.inlineSourceMap) {
160-
babelOptions.sourceMaps = "inline";
161-
}
162-
if (!settings.compilerOptions.removeComments) {
163-
babelOptions.comments = true;
164-
}
187+
let sourceMapFileName = getJSMapNameForJSFile(outputFile.name);
165188

166-
let babelResult = babel.transform(outputFile.text, babelOptions);
167-
outputFile.text = babelResult.code;
168-
169-
if (babelResult.map && settings.compilerOptions.sourceMap) {
170-
let additionalEmit: ts.OutputFile = {
171-
name: sourceMapFileName,
172-
text: JSON.stringify(babelResult.map),
173-
writeByteOrderMark: settings.compilerOptions.emitBOM
174-
};
175-
176-
if (additionalEmit.name === "") {
177-
// can't emit a blank file name - this should only be reached if the TypeScript
178-
// language service returns the .js file before the .js.map file.
179-
console.warn(`The TypeScript language service did not yet provide a .js.map name for file ${outputFile.name}`);
180-
return [];
189+
if (sourceMapContents[sourceMapFileName]) {
190+
babelOptions.inputSourceMap = sourceMapContents[sourceMapFileName].sourceMapPayload;
191+
let baseName = path.basename(sourceFileName);
192+
// NOTE: Babel generates invalid source map without consistent `sources` and `file`.
193+
babelOptions.inputSourceMap.sources = [baseName];
194+
babelOptions.inputSourceMap.file = baseName;
195+
}
196+
if (settings.compilerOptions.sourceMap) {
197+
babelOptions.sourceMaps = true;
198+
}
199+
if (settings.compilerOptions.inlineSourceMap) {
200+
babelOptions.sourceMaps = "inline";
201+
}
202+
if (!settings.compilerOptions.removeComments) {
203+
babelOptions.comments = true;
181204
}
182205

183-
return [additionalEmit];
184-
}
206+
let babelResult = babel.transform(outputFile.text, babelOptions);
207+
outputFile.text = babelResult.code;
208+
209+
if (babelResult.map && settings.compilerOptions.sourceMap) {
210+
let additionalEmit: ts.OutputFile = {
211+
name: sourceMapFileName,
212+
text: JSON.stringify(babelResult.map),
213+
writeByteOrderMark: settings.compilerOptions.emitBOM
214+
};
215+
216+
if (additionalEmit.name === "") {
217+
// can't emit a blank file name - this should only be reached if the TypeScript
218+
// language service returns the .js file before the .js.map file.
219+
console.warn(`The TypeScript language service did not yet provide a .js.map name for file ${outputFile.name}`);
220+
return [];
221+
}
185222

186-
return [];
223+
return [additionalEmit];
224+
}
225+
226+
return [];
227+
});
187228
}
188229
}
189230

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"detect-indent": "^4.0.0",
4949
"emissary": "^1.3.3",
5050
"escape-html": "^1.0.1",
51+
"findup": "^0.1.5",
5152
"fuzzaldrin": "^2.1.0",
5253
"glob": "^5.0.15",
5354
"htmltojsx": "0.2.4",

0 commit comments

Comments
 (0)