15815213711
2024-08-26 67b8b6731811983447e053d4396b3708c14dfe3c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
'use strict';
 
var glob = require('glob');
var path = require('path');
var ts = require('typescript');
var fs = require('fs');
var os = require('os');
 
function _interopNamespaceDefault(e) {
  var n = Object.create(null);
  if (e) {
    Object.keys(e).forEach(function (k) {
      if (k !== 'default') {
        var d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: function () { return e[k]; }
        });
      }
    });
  }
  n.default = e;
  return Object.freeze(n);
}
 
var ts__namespace = /*#__PURE__*/_interopNamespaceDefault(ts);
 
function tsCompile(fileNames, options) {
    console.log("compiling:", fileNames);
    const program = ts__namespace.createProgram(fileNames, options);
    const sources = program
        .getSourceFiles()
        .map((f) => f.fileName)
        .filter((name) => !name.includes("node_modules"));
    const emitResult = program.emit();
    logDiagnostics(program, emitResult);
    return { localSources: sources, compiled: !emitResult.emitSkipped };
}
function logDiagnostics(program, emitResult) {
    const allDiagnostics = ts__namespace
        .getPreEmitDiagnostics(program)
        .concat(emitResult.diagnostics);
    allDiagnostics.forEach((diagnostic) => {
        if (diagnostic.file) {
            const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
            const message = ts__namespace.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
            const filePath = path.resolve(diagnostic.file.fileName);
            console.log(`tsc: (${filePath}:${line + 1}:${character + 1}): ${message}`);
        }
        else {
            console.log(ts__namespace.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
        }
    });
}
 
const fsRoot = path.parse(process.cwd()).root;
/** Return true if any files need compiling */
function needsCompile(srcGlobs, outDir) {
    const files = srcGlobs.flatMap((src) => glob.glob.sync(src));
    const srcDestPairs = compilationPairs(files, outDir);
    return anyOutDated(srcDestPairs);
}
/** Return true if all files exist on the filesystem */
function expectFilesExist(files) {
    const missing = files.find((file) => !fs.existsSync(file));
    if (missing) {
        return false;
    }
    return true;
}
/** @return path to the js file that will be produced by typescript compilation */
function jsOutFile(tsFile, outDir) {
    const tsAbsolutePath = path.resolve(tsFile);
    const tsAbsoluteDir = path.dirname(tsAbsolutePath);
    const dirFromRoot = path.relative(fsRoot, tsAbsoluteDir);
    const jsDir = path.join(outDir, dirFromRoot);
    const outFile = changeSuffix(path.basename(tsFile), ".js");
    return path.join(jsDir, outFile);
}
/*
We set rootDir to fsRoot for tsc compilation.
 
That means that the .js output files produced by typescript will be in a deep tree
of subdirectories mirroring the path from / to the source file.
  e.g. /home/lee/proj/foo.ts will output to outdir/home/proj/lee/foo.js.
 
We need to set a rootDir so that the output tree js files produced by typescript is
predictable prior to compilation. Without a rootDir, tsc will make an output tree that
is as short as possible depending on the imports used by the .ts files. Shorter is nice,
but the unpredictability breaks checks for on-demand compilation.
 
A .ts file can import from parent directories.
  e.g. import * from "../util".
So we use the file system root as the rootDir to be conservative in handling
potential parent directory imports.
*/
function compileIfNecessary(sources, outDir, strict = true) {
    const sourceSet = new Set([...sources, ...extendedSources(outDir)]);
    const allSources = [...sourceSet];
    if (needsCompile(allSources, outDir)) {
        const { compiled, localSources } = tsCompile(sources, {
            outDir,
            rootDir: fsRoot,
            module: ts.ModuleKind.CommonJS,
            moduleResolution: ts.ModuleResolutionKind.NodeJs,
            esModuleInterop: true,
            resolveJsonModule: true,
            skipLibCheck: true,
            strict,
            target: ts.ScriptTarget.ES2019,
            noImplicitAny: false,
            noEmitOnError: true,
        });
        if (compiled) {
            saveExtendedSources(outDir, localSources);
            linkNodeModules(outDir);
        }
        return compiled;
    }
    return true;
}
/** local sources used in last compilation, including imports */
function extendedSources(outDir) {
    const file = sourcesFile(outDir);
    if (!fs.existsSync(file)) {
        return [];
    }
    const lines = fs.readFileSync(file, "utf8");
    return lines.split("\n");
}
function sourcesFile(outDir) {
    return path.join(outDir, "_sources");
}
function saveExtendedSources(outDir, allSources) {
    const file = sourcesFile(outDir);
    fs.writeFileSync(file, allSources.join("\n"));
}
/** Put a link in the output directory to node_modules.
 */
function linkNodeModules(outDir) {
    /*
     * Note that this only puts a link to the single node_modules directory
     * that's closest by.
     *
     * But I think node's module resolution will search multiple
     * parent directories for multiple node_modules at runtime. So just one
     * node_modules link may be insufficient in some complicated cases.
     *
     * If supporting the more complicated case is worthwhile, we can consider
     * e.g. encoding a full list of node_modules and setting NODE_PATH instead
     * of the symlink approach here.
     */
    const nodeModules = nearestNodeModules(process.cwd());
    if (nodeModules) {
        const linkToModules = path.join(outDir, "node_modules");
        symLinkForce(nodeModules, linkToModules);
    }
}
/** create a symlink, replacing any existing linkfile */
function symLinkForce(existing, link) {
    if (fs.existsSync(link)) {
        if (!fs.lstatSync(link).isSymbolicLink()) {
            throw `symLinkForce refusing to unlink non-symlink ${link}`;
        }
        fs.unlinkSync(link);
    }
    fs.symlinkSync(existing, link);
}
/** @return the resolved path to the nearest node_modules file,
 * either in the provided directory or a parent.
 */
function nearestNodeModules(dir) {
    const resolvedDir = path.resolve(dir);
    const modulesFile = path.join(resolvedDir, "node_modules");
    if (fs.existsSync(modulesFile)) {
        return modulesFile;
    }
    else {
        const { dir: parent, root } = path.parse(resolvedDir);
        if (parent !== root) {
            return nearestNodeModules(parent);
        }
        else {
            return undefined;
        }
    }
}
/**
 * Compile a typescript config file to js if necessary (if the js
 * file doesn't exist or is older than the typescript file).
 *
 * @param tsFile path to ts config file
 * @param outDir directory to place the compiled js file
 * @returns the path to the compiled javascript config file,
 *   or undefined if the compilation fails.
 */
function compileConfigIfNecessary(tsFile, outDir, strict = true) {
    if (!fs.existsSync(tsFile)) {
        console.error("config file:", tsFile, " not found");
        return undefined;
    }
    const success = compileIfNecessary([tsFile], outDir, strict);
    if (!success) {
        return undefined;
    }
    return jsOutFile(tsFile, outDir);
}
function compilationPairs(srcFiles, outDir) {
    return srcFiles.map((tsFile) => {
        return [tsFile, jsOutFile(tsFile, outDir)];
    });
}
function anyOutDated(filePairs) {
    const found = filePairs.find(([srcPath, outPath]) => {
        if (!fs.existsSync(outPath)) {
            return true;
        }
        const srcTime = fs.statSync(srcPath).mtime;
        const outTime = fs.statSync(outPath).mtime;
        return srcTime > outTime;
    });
    return found !== undefined;
}
function changeSuffix(filePath, suffix) {
    const dir = path.dirname(filePath);
    const curSuffix = path.extname(filePath);
    const base = path.basename(filePath, curSuffix);
    return path.join(dir, base + suffix);
}
 
/** Load a typescript configuration file.
 * For speed, the typescript file is transpiled to javascript and cached.
 *
 * @param T type of default export value in the configuration file
 * @param outDir location to store the compiled javascript.
 *  Defaults to $HOME/.cache/config-file-ts/<ts-file-path>/
 * @returns the default exported value from the configuration file or undefined
 */
function loadTsConfig(tsFile, outDir, strict = true) {
    const realOutDir = outDir || defaultOutDir(tsFile);
    const jsConfig = compileConfigIfNecessary(tsFile, realOutDir, strict);
    if (!jsConfig) {
        return undefined;
    }
    const end = jsConfig.length - path.extname(jsConfig).length;
    const requirePath = jsConfig.slice(0, end);
    const config = require(requirePath);
    return config.default;
}
/** @return the directory that will be used to store transpilation output. */
function defaultOutDir(tsFile, programName = "config-file-ts") {
    const tsPath = path.resolve(tsFile);
    let smushedPath = tsPath
        .split(path.sep)
        .join("-")
        .slice(1);
    if (os.platform() === "win32") {
        smushedPath = smushedPath.replace(/^:/, "");
    }
    return path.join(os.homedir(), ".cache", programName, smushedPath);
}
 
exports.compileIfNecessary = compileIfNecessary;
exports.defaultOutDir = defaultOutDir;
exports.expectFilesExist = expectFilesExist;
exports.jsOutFile = jsOutFile;
exports.loadTsConfig = loadTsConfig;
exports.symLinkForce = symLinkForce;
//# sourceMappingURL=index.js.map