Skip to content

Instantly share code, notes, and snippets.

@jgoz
Last active August 29, 2015 13:59
Show Gist options
  • Save jgoz/10501188 to your computer and use it in GitHub Desktop.
Save jgoz/10501188 to your computer and use it in GitHub Desktop.
Typescript loader for webpack (WIP)
"use strict";
var fs = require("fs");
var loaderUtils = require("loader-utils");
var path = require("path");
var ts = require("typescript-api");
function buildSettings(options, debug, outFile) {
var settings = new ts.CompilationSettings();
if (options) {
var target = (options.target || "es3").toLowerCase();
settings.codeGenTarget = (target === "es3" ? 0 : (target === "es5" ? 1 : options.target));
var module = (options.module || "").toLowerCase();
settings.moduleGenTarget = (module === "commonjs" ? 1 : (module === "amd" ? 2 : 0));
settings.noResolve = options.noResolve === true;
settings.noImplicitAny = options.warnImplicitAny !== true;
}
settings.mapSourceFiles = debug;
settings.outFileOption = outFile;
//settings.syntacticErrors = true;
//settings.semanticErrors = true;
return ts.ImmutableCompilationSettings.fromCompilationSettings(settings);
}
function compile(batchCompiler, settings) {
var compiler = new ts.TypeScriptCompiler(new ts.NullLogger(), settings);
batchCompiler.resolvedFiles.forEach(function (resolvedFile) {
var sourceFile = this.getSourceFile(resolvedFile.path);
compiler.addFile(resolvedFile.path, sourceFile.scriptSnapshot, sourceFile.byteOrderMark, 0, false, resolvedFile.referencedFiles);
}, batchCompiler);
var it = compiler.compile(function (path) {
return batchCompiler.resolvePath(path);
});
var output = [];
while (it.moveNext()) {
var current = it.current();
var result = {};
for (var i = 0; i < current.outputFiles.length; i++) {
var outputFile = current.outputFiles[i];
if (!outputFile) { continue; }
if (outputFile.fileType === 1) { // SourceMap
result.map = outputFile.text;
} else {
// HACK: strip inline sourcemap info, as this will be included
// at the bottom of the output bundle
result.content = outputFile.text.replace(/\/\/# sourceMappingURL=.+\.map/, "");
result.name = outputFile.name;
}
}
if (result.content) {
output.push(result);
}
}
var errors = [];
batchCompiler.resolvedFiles.forEach(function (resolvedFile) {
var diagnostics = compiler.getSyntacticDiagnostics(resolvedFile.path)
.concat(compiler.getSemanticDiagnostics(resolvedFile.path));
diagnostics.forEach(function (d) {
errors.push({ path: resolvedFile.path, message: d.text(), line: d.line() + 1, character: d.character() + 1 });
});
});
return { output: output, errors: errors };
}
function resolve(batchCompiler, filePath) {
var result = [];
var content, output;
batchCompiler.resolvedFiles = [];
batchCompiler.inputFiles = [filePath];
batchCompiler.resolve();
batchCompiler.resolvedFiles.forEach(function (rf) {
if (!/\.d\.ts$/.test(rf.path)) {
content = fs.readFileSync(rf.path, {encoding: 'utf8'});
result.push({ path: rf.path, source: content });
}
});
return result;
}
function getBasename(filePath) {
return path.join(path.dirname(filePath), path.basename(filePath, path.extname(filePath)));
}
module.exports = function typescriptLoader(source) {
if (this.cacheable) { this.cacheable(); }
var tsRequest = loaderUtils.getRemainingRequest(this);
var jsRequest = loaderUtils.getCurrentRequest(this);
var query = loaderUtils.parseQuery(this.query);
var settings = buildSettings(query, this.debug, jsRequest);
var inputFiles = [{ path: tsRequest, source: source }];
var batchCompiler = new ts.BatchCompiler(ts.IO);
batchCompiler.logger = new ts.NullLogger();
batchCompiler.compilationSettings = settings;
resolve(batchCompiler, tsRequest);
var result = compile(batchCompiler, settings);
result.errors.forEach(function(error) {
this.emitError(
error.message.replace(/\.$/, "") + " in " +
path.relative(this.options.context, error.path) + " " +
error.line + ":" + error.character
);
}, this);
var expectedBasename = getBasename(tsRequest);
result.output.forEach(function(output) {
var outputBasename = getBasename(output.name);
if (outputBasename === expectedBasename) {
var map = JSON.parse(output.map);
map.sourcesContent = [source];
map.sourceRoot = "ts/" + path.relative(this.options.context, path.dirname(tsRequest));
this.callback(null, output.content, map);
} else {
this.addDependency(getBasename(output.name) + ".ts");
}
}, this);
};
module.exports.seperable = true;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment