1 | /// <reference path="typings/typescript/typescript.d.ts"/>
|
2 | /// <reference path="typings/chalk/chalk.d.ts"/>
|
3 | /// <reference path="typings/node/node.d.ts"/>
|
4 | /// <reference path="typings/sanitize-filename/sanitize-filename.d.ts"/>
|
5 | var chalk = require("chalk");
|
6 | var fs = require("fs");
|
7 | var os = require("os");
|
8 | var path = require("path");
|
9 | var sanitize = require("sanitize-filename");
|
10 | var typescript = require("typescript");
|
11 | /**
|
12 | * Module Configuration Options
|
13 | */
|
14 | var Config;
|
15 | (function (Config) {
|
16 | Config[Config["EMIT_ERROR"] = 0] = "EMIT_ERROR";
|
17 | Config[Config["USE_CACHE"] = 1] = "USE_CACHE";
|
18 | Config[Config["COMPILER_OPTIONS"] = 2] = "COMPILER_OPTIONS";
|
19 | })(Config || (Config = {}));
|
20 | /**
|
21 | * Requires a TypeScript file
|
22 | * @param {Module} module The node module
|
23 | * @param {string} filename The module filename
|
24 | */
|
25 | function req(module, filename) {
|
26 | var options = compilerOptions();
|
27 | var out = dest(filename, options);
|
28 | if (!useCache() || isModified(filename, out)) {
|
29 | compile(filename, options);
|
30 | }
|
31 | module._compile(fs.readFileSync(out, "utf8"), filename);
|
32 | }
|
33 | require.extensions[".ts"] = req;
|
34 | /**
|
35 | * Checks the environment for whether or not we should emit an error. Defaults
|
36 | * to true.
|
37 | * @return {boolean} Whether or not to emit TypeScript errors
|
38 | */
|
39 | function emitError() {
|
40 | return env(0 /* EMIT_ERROR */, true, toBoolean);
|
41 | }
|
42 | /**
|
43 | * Checks the environment for whether or not we should use a build cache.
|
44 | * Defaults to true.
|
45 | * @return {boolean} Whether or not to use cached JavaScript files
|
46 | */
|
47 | function useCache() {
|
48 | return env(1 /* USE_CACHE */, true, toBoolean);
|
49 | }
|
50 | /**
|
51 | * TypeScript Default Configuration
|
52 | * @type {typescript.CompilerOptions}
|
53 | */
|
54 | var defaultCompilerOptions = {
|
55 | module: 1 /* CommonJS */,
|
56 | outDir: getCachePath(process.cwd()),
|
57 | target: 1 /* ES5 */
|
58 | };
|
59 | /**
|
60 | * Returns path to cache for source directory.
|
61 | * @param {string} directory Directory with source code
|
62 | * @return {string} Path with all special characters replaced with _ and
|
63 | * prepended path to temporary directory
|
64 | */
|
65 | function getCachePath(directory) {
|
66 | var sanitizeOptions = {
|
67 | replacement: "_"
|
68 | };
|
69 | var parts = directory.split(path.sep).map(function (p) { return sanitize(p, sanitizeOptions); });
|
70 | var cachePath = path.join.apply(null, parts);
|
71 | return path.join(os.tmpdir(), "typescript-register", cachePath);
|
72 | }
|
73 | /**
|
74 | * Checks the environment for JSON stringified compiler options. By default it
|
75 | * will compile TypeScript files into a scoped temp directory using ES5 and
|
76 | * CommonJS targets.
|
77 | * @return {typescript.CompilerOptions} The TypeScript compiler settings
|
78 | */
|
79 | function compilerOptions() {
|
80 | return env(2 /* COMPILER_OPTIONS */, defaultCompilerOptions, toOptions);
|
81 | }
|
82 | /**
|
83 | * Gets a value from env
|
84 | * @param {Config} config The configuration target
|
85 | * @param {T} fallback The default value
|
86 | * @param {Function} map The map function if the value exists
|
87 | * @return {T} The environment variable
|
88 | */
|
89 | function env(config, fallback, map) {
|
90 | var key = "TYPESCRIPT_REGISTER_" + Config[config];
|
91 | if (process.env.hasOwnProperty(key)) {
|
92 | return map(process.env[key]);
|
93 | }
|
94 | return fallback;
|
95 | }
|
96 | /**
|
97 | * Returns the JavaScript destination for the path
|
98 | * @param {string} filename The TypeScript filename
|
99 | * @param {typescript.CompilerOptiones} options The Compiler Options
|
100 | * @return {string} The JavaScript filepath
|
101 | */
|
102 | function dest(filename, options) {
|
103 | var basename = path.basename(filename, ".ts");
|
104 | var outDir = options.outDir || path.dirname(filename);
|
105 | return path.join(outDir, basename + ".js");
|
106 | }
|
107 | /**
|
108 | * Check whether or not the path is modified
|
109 | * @param {string} tsPath The path to the TypeScript file
|
110 | * @param {string} jsPath The path to the JavaScript file
|
111 | * @return {boolean} Whether or not the file is modified
|
112 | */
|
113 | function isModified(tsPath, jsPath) {
|
114 | try {
|
115 | var js = fs.statSync(jsPath).mtime;
|
116 | var ts = fs.statSync(tsPath).mtime;
|
117 | return ts > js;
|
118 | }
|
119 | catch (err) {
|
120 | return true;
|
121 | }
|
122 | }
|
123 | /**
|
124 | * Compiles the TypeScript file
|
125 | * @param {string} filename The root file to compile
|
126 | * @param {typescript.CompilerOptions} options The Compiler Options
|
127 | */
|
128 | function compile(filename, options) {
|
129 | var host = typescript.createCompilerHost(options);
|
130 | var program = typescript.createProgram([filename], options, host);
|
131 | var checker = program.getTypeChecker(true);
|
132 | var result = checker.emitFiles();
|
133 | if (emitError()) {
|
134 | checkErrors(program.getDiagnostics().concat(checker.getDiagnostics().concat(result.diagnostics)));
|
135 | }
|
136 | }
|
137 | /**
|
138 | * Converts a list of errors into something readable
|
139 | * @param {typescript.Diagnostic[]} errors The TypeScript Diagnostics
|
140 | * @return {Error} The Compiler Error
|
141 | */
|
142 | function checkErrors(errors) {
|
143 | if (errors.length === 0) {
|
144 | return;
|
145 | }
|
146 | errors.forEach(function (diagnostic) {
|
147 | var position = diagnostic.file.getLineAndCharacterFromPosition(diagnostic.start);
|
148 | console.error(chalk.bgRed("" + diagnostic.code), chalk.grey("" + diagnostic.file.filename + ", (" + position.line + "," + position.character + ")"), diagnostic.messageText);
|
149 | });
|
150 | throw new Error("TypeScript Compilation Errors");
|
151 | }
|
152 | /**
|
153 | * Converts a string to boolean
|
154 | * @param {string} value The string value
|
155 | * @return {boolean} Whether or not the string is "true"
|
156 | */
|
157 | function toBoolean(value) {
|
158 | return value === "true";
|
159 | }
|
160 | /**
|
161 | * Converts a string to TypeScript compiler options
|
162 | * @param {string} value The string value
|
163 | * @return {typescript.CompilerOptions} The TypeScript Compiler Options
|
164 | */
|
165 | function toOptions(value) {
|
166 | return JSON.parse(value);
|
167 | }
|