1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.getEmitOutput = exports.getEmitFromWatchHost = exports.getInputFileNameFromOutput = exports.getOutputFileNames = exports.forEachResolvedProjectReference = exports.buildSolutionReferences = exports.reportTranspileErrors = exports.initializeInstance = exports.getTypeScriptInstance = void 0;
|
4 | const chalk_1 = require("chalk");
|
5 | const fs = require("fs");
|
6 | const path = require("path");
|
7 | const webpack = require("webpack");
|
8 | const after_compile_1 = require("./after-compile");
|
9 | const compilerSetup_1 = require("./compilerSetup");
|
10 | const config_1 = require("./config");
|
11 | const constants_1 = require("./constants");
|
12 | const instance_cache_1 = require("./instance-cache");
|
13 | const logger = require("./logger");
|
14 | const servicesHost_1 = require("./servicesHost");
|
15 | const utils_1 = require("./utils");
|
16 | const watch_run_1 = require("./watch-run");
|
17 | const instancesBySolutionBuilderConfigs = new Map();
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | function getTypeScriptInstance(loaderOptions, loader) {
|
26 | const existing = instance_cache_1.getTSInstanceFromCache(loader._compiler, loaderOptions.instance);
|
27 | if (existing) {
|
28 | if (!existing.initialSetupPending) {
|
29 | utils_1.ensureProgram(existing);
|
30 | }
|
31 | return { instance: existing };
|
32 | }
|
33 | const colors = new chalk_1.default.constructor({ enabled: loaderOptions.colors });
|
34 | const log = logger.makeLogger(loaderOptions, colors);
|
35 | const compiler = compilerSetup_1.getCompiler(loaderOptions, log);
|
36 | if (compiler.errorMessage !== undefined) {
|
37 | return {
|
38 | error: utils_1.makeError(loaderOptions, colors.red(compiler.errorMessage), undefined),
|
39 | };
|
40 | }
|
41 | return successfulTypeScriptInstance(loaderOptions, loader, log, colors, compiler.compiler, compiler.compilerCompatible, compiler.compilerDetailsLogMessage);
|
42 | }
|
43 | exports.getTypeScriptInstance = getTypeScriptInstance;
|
44 | function createFilePathKeyMapper(compiler, loaderOptions) {
|
45 |
|
46 | const fileNameLowerCaseRegExp = /[^\u0130\u0131\u00DFa-z0-9\\/:\-_\. ]+/g;
|
47 | return utils_1.useCaseSensitiveFileNames(compiler, loaderOptions)
|
48 | ? pathResolve
|
49 | : toFileNameLowerCase;
|
50 | function pathResolve(x) {
|
51 | return path.resolve(x);
|
52 | }
|
53 | function toFileNameLowerCase(x) {
|
54 | const filePathKey = pathResolve(x);
|
55 | return fileNameLowerCaseRegExp.test(filePathKey)
|
56 | ? filePathKey.replace(fileNameLowerCaseRegExp, ch => ch.toLowerCase())
|
57 | : filePathKey;
|
58 | }
|
59 | }
|
60 | function successfulTypeScriptInstance(loaderOptions, loader, log, colors, compiler, compilerCompatible, compilerDetailsLogMessage) {
|
61 | const configFileAndPath = config_1.getConfigFile(compiler, colors, loader, loaderOptions, compilerCompatible, log, compilerDetailsLogMessage);
|
62 | if (configFileAndPath.configFileError !== undefined) {
|
63 | const { message, file } = configFileAndPath.configFileError;
|
64 | return {
|
65 | error: utils_1.makeError(loaderOptions, colors.red('error while reading tsconfig.json:' + constants_1.EOL + message), file),
|
66 | };
|
67 | }
|
68 | const { configFilePath, configFile } = configFileAndPath;
|
69 | const filePathKeyMapper = createFilePathKeyMapper(compiler, loaderOptions);
|
70 | if (configFilePath && loaderOptions.projectReferences) {
|
71 | const configFileKey = filePathKeyMapper(configFilePath);
|
72 | const existing = getExistingSolutionBuilderHost(configFileKey);
|
73 | if (existing) {
|
74 |
|
75 | instance_cache_1.setTSInstanceInCache(loader._compiler, loaderOptions.instance, existing);
|
76 | return { instance: existing };
|
77 | }
|
78 | }
|
79 | const module = loader._module;
|
80 | const basePath = loaderOptions.context || path.dirname(configFilePath || '');
|
81 | const configParseResult = config_1.getConfigParseResult(compiler, configFile, basePath, configFilePath, loaderOptions);
|
82 | if (configParseResult.errors.length > 0 && !loaderOptions.happyPackMode) {
|
83 | const errors = utils_1.formatErrors(configParseResult.errors, loaderOptions, colors, compiler, { file: configFilePath }, loader.context);
|
84 | |
85 |
|
86 |
|
87 |
|
88 | if (module.addError) {
|
89 | errors.forEach(error => module.addError(error));
|
90 | }
|
91 | else {
|
92 | module.errors.push(...errors);
|
93 | }
|
94 | return {
|
95 | error: utils_1.makeError(loaderOptions, colors.red('error while parsing tsconfig.json'), configFilePath),
|
96 | };
|
97 | }
|
98 | const compilerOptions = compilerSetup_1.getCompilerOptions(configParseResult);
|
99 | const rootFileNames = new Set();
|
100 | const files = new Map();
|
101 | const otherFiles = new Map();
|
102 | const appendTsTsxSuffixesIfRequired = loaderOptions.appendTsSuffixTo.length > 0 ||
|
103 | loaderOptions.appendTsxSuffixTo.length > 0
|
104 | ? (filePath) => utils_1.appendSuffixesIfMatch({
|
105 | '.ts': loaderOptions.appendTsSuffixTo,
|
106 | '.tsx': loaderOptions.appendTsxSuffixTo,
|
107 | }, filePath)
|
108 | : (filePath) => filePath;
|
109 | if (loaderOptions.transpileOnly) {
|
110 |
|
111 |
|
112 | const transpileInstance = {
|
113 | compiler,
|
114 | compilerOptions,
|
115 | appendTsTsxSuffixesIfRequired,
|
116 | loaderOptions,
|
117 | rootFileNames,
|
118 | files,
|
119 | otherFiles,
|
120 | version: 0,
|
121 | program: undefined,
|
122 | dependencyGraph: new Map(),
|
123 | transformers: {},
|
124 | colors,
|
125 | initialSetupPending: true,
|
126 | reportTranspileErrors: true,
|
127 | configFilePath,
|
128 | configParseResult,
|
129 | log,
|
130 | filePathKeyMapper,
|
131 | };
|
132 | instance_cache_1.setTSInstanceInCache(loader._compiler, loaderOptions.instance, transpileInstance);
|
133 | return { instance: transpileInstance };
|
134 | }
|
135 |
|
136 | let normalizedFilePath;
|
137 | try {
|
138 | const filesToLoad = loaderOptions.onlyCompileBundledFiles
|
139 | ? configParseResult.fileNames.filter(fileName => constants_1.dtsDtsxOrDtsDtsxMapRegex.test(fileName))
|
140 | : configParseResult.fileNames;
|
141 | filesToLoad.forEach(filePath => {
|
142 | normalizedFilePath = path.normalize(filePath);
|
143 | files.set(filePathKeyMapper(normalizedFilePath), {
|
144 | fileName: normalizedFilePath,
|
145 | text: fs.readFileSync(normalizedFilePath, 'utf-8'),
|
146 | version: 0,
|
147 | });
|
148 | rootFileNames.add(normalizedFilePath);
|
149 | });
|
150 | }
|
151 | catch (exc) {
|
152 | return {
|
153 | error: utils_1.makeError(loaderOptions, colors.red(`A file specified in tsconfig.json could not be found: ${normalizedFilePath}`), normalizedFilePath),
|
154 | };
|
155 | }
|
156 | const instance = {
|
157 | compiler,
|
158 | compilerOptions,
|
159 | appendTsTsxSuffixesIfRequired,
|
160 | loaderOptions,
|
161 | rootFileNames,
|
162 | files,
|
163 | otherFiles,
|
164 | languageService: null,
|
165 | version: 0,
|
166 | transformers: {},
|
167 | dependencyGraph: new Map(),
|
168 | colors,
|
169 | initialSetupPending: true,
|
170 | configFilePath,
|
171 | configParseResult,
|
172 | log,
|
173 | filePathKeyMapper,
|
174 | };
|
175 | instance_cache_1.setTSInstanceInCache(loader._compiler, loaderOptions.instance, instance);
|
176 | return { instance };
|
177 | }
|
178 | function getExistingSolutionBuilderHost(key) {
|
179 | const existing = instancesBySolutionBuilderConfigs.get(key);
|
180 | if (existing)
|
181 | return existing;
|
182 | for (const instance of instancesBySolutionBuilderConfigs.values()) {
|
183 | if (instance.solutionBuilderHost.configFileInfo.has(key)) {
|
184 | return instance;
|
185 | }
|
186 | }
|
187 | return undefined;
|
188 | }
|
189 |
|
190 |
|
191 | const addAssetHooks = !!webpack.version.match(/^4.*/)
|
192 | ? (loader, instance) => {
|
193 |
|
194 | loader._compiler.hooks.afterCompile.tapAsync('ts-loader', after_compile_1.makeAfterCompile(instance, true, true, instance.configFilePath));
|
195 | }
|
196 | : (loader, instance) => {
|
197 |
|
198 |
|
199 |
|
200 | loader._compiler.hooks.afterCompile.tapAsync('ts-loader', after_compile_1.makeAfterCompile(instance, false, true, instance.configFilePath));
|
201 |
|
202 | loader._compilation.hooks.afterProcessAssets.tap('ts-loader', (_) => {
|
203 | after_compile_1.makeAfterCompile(instance, true, false, instance.configFilePath)(loader._compilation, () => {
|
204 | return null;
|
205 | });
|
206 | });
|
207 |
|
208 |
|
209 |
|
210 | };
|
211 | function initializeInstance(loader, instance) {
|
212 | if (!instance.initialSetupPending) {
|
213 | return;
|
214 | }
|
215 | instance.initialSetupPending = false;
|
216 |
|
217 | let { getCustomTransformers: customerTransformers } = instance.loaderOptions;
|
218 | let getCustomTransformers = Function.prototype;
|
219 | if (typeof customerTransformers === 'function') {
|
220 | getCustomTransformers = customerTransformers;
|
221 | }
|
222 | else if (typeof customerTransformers === 'string') {
|
223 | try {
|
224 | customerTransformers = require(customerTransformers);
|
225 | }
|
226 | catch (err) {
|
227 | throw new Error(`Failed to load customTransformers from "${instance.loaderOptions.getCustomTransformers}": ${err.message}`);
|
228 | }
|
229 | if (typeof customerTransformers !== 'function') {
|
230 | throw new Error(`Custom transformers in "${instance.loaderOptions.getCustomTransformers}" should export a function, got ${typeof getCustomTransformers}`);
|
231 | }
|
232 | getCustomTransformers = customerTransformers;
|
233 | }
|
234 | if (instance.loaderOptions.transpileOnly) {
|
235 | const program = (instance.program =
|
236 | instance.configParseResult.projectReferences !== undefined
|
237 | ? instance.compiler.createProgram({
|
238 | rootNames: instance.configParseResult.fileNames,
|
239 | options: instance.configParseResult.options,
|
240 | projectReferences: instance.configParseResult.projectReferences,
|
241 | })
|
242 | : instance.compiler.createProgram([], instance.compilerOptions));
|
243 | instance.transformers = getCustomTransformers(program);
|
244 |
|
245 | if (instance.solutionBuilderHost) {
|
246 | addAssetHooks(loader, instance);
|
247 | loader._compiler.hooks.watchRun.tapAsync('ts-loader', watch_run_1.makeWatchRun(instance, loader));
|
248 | }
|
249 | }
|
250 | else {
|
251 | if (!loader._compiler.hooks) {
|
252 | throw new Error("You may be using an old version of webpack; please check you're using at least version 4");
|
253 | }
|
254 | if (instance.loaderOptions.experimentalWatchApi) {
|
255 | instance.log.logInfo('Using watch api');
|
256 |
|
257 | instance.watchHost = servicesHost_1.makeWatchHost(getScriptRegexp(instance), loader, instance, instance.configParseResult.projectReferences);
|
258 | instance.watchOfFilesAndCompilerOptions = instance.compiler.createWatchProgram(instance.watchHost);
|
259 | instance.builderProgram = instance.watchOfFilesAndCompilerOptions.getProgram();
|
260 | instance.program = instance.builderProgram.getProgram();
|
261 | instance.transformers = getCustomTransformers(instance.program);
|
262 | }
|
263 | else {
|
264 | instance.servicesHost = servicesHost_1.makeServicesHost(getScriptRegexp(instance), loader, instance, instance.configParseResult.projectReferences);
|
265 | instance.languageService = instance.compiler.createLanguageService(instance.servicesHost, instance.compiler.createDocumentRegistry());
|
266 | instance.transformers = getCustomTransformers(instance.languageService.getProgram());
|
267 | }
|
268 | addAssetHooks(loader, instance);
|
269 | loader._compiler.hooks.watchRun.tapAsync('ts-loader', watch_run_1.makeWatchRun(instance, loader));
|
270 | }
|
271 | }
|
272 | exports.initializeInstance = initializeInstance;
|
273 | function getScriptRegexp(instance) {
|
274 |
|
275 | if (instance.configParseResult.options.resolveJsonModule) {
|
276 |
|
277 | return instance.configParseResult.options.allowJs === true
|
278 | ? /\.tsx?$|\.json$|\.jsx?$/i
|
279 | : /\.tsx?$|\.json$/i;
|
280 | }
|
281 |
|
282 | return instance.configParseResult.options.allowJs === true
|
283 | ? /\.tsx?$|\.jsx?$/i
|
284 | : /\.tsx?$/i;
|
285 | }
|
286 | function reportTranspileErrors(instance, loader) {
|
287 | if (!instance.reportTranspileErrors) {
|
288 | return;
|
289 | }
|
290 | const module = loader._module;
|
291 | instance.reportTranspileErrors = false;
|
292 |
|
293 | if (!instance.loaderOptions.happyPackMode) {
|
294 | const solutionErrors = servicesHost_1.getSolutionErrors(instance, loader.context);
|
295 | const diagnostics = instance.program.getOptionsDiagnostics();
|
296 | const errors = utils_1.formatErrors(diagnostics, instance.loaderOptions, instance.colors, instance.compiler, { file: instance.configFilePath || 'tsconfig.json' }, loader.context);
|
297 | |
298 |
|
299 |
|
300 |
|
301 | if (module.addError) {
|
302 | [...solutionErrors, ...errors].forEach(error => module.addError(error));
|
303 | }
|
304 | else {
|
305 | module.errors.push(...solutionErrors, ...errors);
|
306 | }
|
307 | }
|
308 | }
|
309 | exports.reportTranspileErrors = reportTranspileErrors;
|
310 | function buildSolutionReferences(instance, loader) {
|
311 | if (!utils_1.supportsSolutionBuild(instance)) {
|
312 | return;
|
313 | }
|
314 | if (!instance.solutionBuilderHost) {
|
315 |
|
316 | instance.log.logInfo('Using SolutionBuilder api');
|
317 | const scriptRegex = getScriptRegexp(instance);
|
318 | instance.solutionBuilderHost = servicesHost_1.makeSolutionBuilderHost(scriptRegex, loader, instance);
|
319 | const solutionBuilder = instance.compiler.createSolutionBuilderWithWatch(instance.solutionBuilderHost, instance.configParseResult.projectReferences.map(ref => ref.path), { verbose: true });
|
320 | solutionBuilder.build();
|
321 | instance.solutionBuilderHost.ensureAllReferenceTimestamps();
|
322 | instancesBySolutionBuilderConfigs.set(instance.filePathKeyMapper(instance.configFilePath), instance);
|
323 | }
|
324 | else {
|
325 | instance.solutionBuilderHost.buildReferences();
|
326 | }
|
327 | }
|
328 | exports.buildSolutionReferences = buildSolutionReferences;
|
329 | function forEachResolvedProjectReference(resolvedProjectReferences, cb) {
|
330 | let seenResolvedRefs;
|
331 | return worker(resolvedProjectReferences);
|
332 | function worker(resolvedRefs) {
|
333 | if (resolvedRefs) {
|
334 | for (const resolvedRef of resolvedRefs) {
|
335 | if (!resolvedRef) {
|
336 | continue;
|
337 | }
|
338 | if (seenResolvedRefs &&
|
339 | seenResolvedRefs.some(seenRef => seenRef === resolvedRef)) {
|
340 |
|
341 | continue;
|
342 | }
|
343 | (seenResolvedRefs || (seenResolvedRefs = [])).push(resolvedRef);
|
344 | const result = cb(resolvedRef) || worker(resolvedRef.references);
|
345 | if (result) {
|
346 | return result;
|
347 | }
|
348 | }
|
349 | }
|
350 | return undefined;
|
351 | }
|
352 | }
|
353 | exports.forEachResolvedProjectReference = forEachResolvedProjectReference;
|
354 |
|
355 | function fileExtensionIs(fileName, ext) {
|
356 | return fileName.endsWith(ext);
|
357 | }
|
358 | function rootDirOfOptions(instance, configFile) {
|
359 | return (configFile.options.rootDir ||
|
360 | instance.compiler.getDirectoryPath(configFile.options.configFilePath));
|
361 | }
|
362 | function getOutputPathWithoutChangingExt(instance, inputFileName, configFile, ignoreCase, outputDir) {
|
363 | return outputDir
|
364 | ? instance.compiler.resolvePath(outputDir, instance.compiler.getRelativePathFromDirectory(rootDirOfOptions(instance, configFile), inputFileName, ignoreCase))
|
365 | : inputFileName;
|
366 | }
|
367 | function getOutputJSFileName(instance, inputFileName, configFile, ignoreCase) {
|
368 | if (configFile.options.emitDeclarationOnly) {
|
369 | return undefined;
|
370 | }
|
371 | const isJsonFile = fileExtensionIs(inputFileName, '.json');
|
372 | const outputFileName = instance.compiler.changeExtension(getOutputPathWithoutChangingExt(instance, inputFileName, configFile, ignoreCase, configFile.options.outDir), isJsonFile
|
373 | ? '.json'
|
374 | : fileExtensionIs(inputFileName, '.tsx') &&
|
375 | configFile.options.jsx === instance.compiler.JsxEmit.Preserve
|
376 | ? '.jsx'
|
377 | : '.js');
|
378 | return !isJsonFile ||
|
379 | instance.compiler.comparePaths(inputFileName, outputFileName, configFile.options.configFilePath, ignoreCase) !== instance.compiler.Comparison.EqualTo
|
380 | ? outputFileName
|
381 | : undefined;
|
382 | }
|
383 | function getOutputFileNames(instance, configFile, inputFileName) {
|
384 | const ignoreCase = !utils_1.useCaseSensitiveFileNames(instance.compiler, instance.loaderOptions);
|
385 | if (instance.compiler.getOutputFileNames) {
|
386 | return instance.compiler.getOutputFileNames(configFile, inputFileName, ignoreCase);
|
387 | }
|
388 | const outputs = [];
|
389 | const addOutput = (fileName) => fileName && outputs.push(fileName);
|
390 | const js = getOutputJSFileName(instance, inputFileName, configFile, ignoreCase);
|
391 | addOutput(js);
|
392 | if (!fileExtensionIs(inputFileName, '.json')) {
|
393 | if (js && configFile.options.sourceMap) {
|
394 | addOutput(`${js}.map`);
|
395 | }
|
396 | if ((configFile.options.declaration || configFile.options.composite) &&
|
397 | instance.compiler.hasTSFileExtension(inputFileName)) {
|
398 | const dts = instance.compiler.getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
|
399 | addOutput(dts);
|
400 | if (configFile.options.declarationMap) {
|
401 | addOutput(`${dts}.map`);
|
402 | }
|
403 | }
|
404 | }
|
405 | return outputs;
|
406 | }
|
407 | exports.getOutputFileNames = getOutputFileNames;
|
408 | function getInputFileNameFromOutput(instance, filePath) {
|
409 | if (filePath.match(constants_1.tsTsxRegex) && !fileExtensionIs(filePath, '.d.ts')) {
|
410 | return undefined;
|
411 | }
|
412 | if (instance.solutionBuilderHost) {
|
413 | return instance.solutionBuilderHost.getInputFileNameFromOutput(filePath);
|
414 | }
|
415 | const program = utils_1.ensureProgram(instance);
|
416 | return (program &&
|
417 | program.getResolvedProjectReferences &&
|
418 | forEachResolvedProjectReference(program.getResolvedProjectReferences(), ({ commandLine }) => {
|
419 | const { options, fileNames } = commandLine;
|
420 | if (!options.outFile && !options.out) {
|
421 | const input = fileNames.find(file => getOutputFileNames(instance, commandLine, file).find(name => path.resolve(name) === filePath));
|
422 | return input && path.resolve(input);
|
423 | }
|
424 | return undefined;
|
425 | }));
|
426 | }
|
427 | exports.getInputFileNameFromOutput = getInputFileNameFromOutput;
|
428 | function getEmitFromWatchHost(instance, filePath) {
|
429 | const program = utils_1.ensureProgram(instance);
|
430 | const builderProgram = instance.builderProgram;
|
431 | if (builderProgram && program) {
|
432 | if (filePath) {
|
433 | const existing = instance.watchHost.outputFiles.get(instance.filePathKeyMapper(filePath));
|
434 | if (existing) {
|
435 | return existing;
|
436 | }
|
437 | }
|
438 | const outputFiles = [];
|
439 | const writeFile = (fileName, text, writeByteOrderMark) => {
|
440 | if (fileName.endsWith('.tsbuildinfo')) {
|
441 | instance.watchHost.tsbuildinfo = {
|
442 | name: fileName,
|
443 | writeByteOrderMark,
|
444 | text,
|
445 | };
|
446 | }
|
447 | else {
|
448 | outputFiles.push({ name: fileName, writeByteOrderMark, text });
|
449 | }
|
450 | };
|
451 | const sourceFile = filePath ? program.getSourceFile(filePath) : undefined;
|
452 |
|
453 | while (true) {
|
454 | const result = builderProgram.emitNextAffectedFile(writeFile,
|
455 | undefined,
|
456 | false, instance.transformers);
|
457 | if (!result) {
|
458 | break;
|
459 | }
|
460 |
|
461 |
|
462 | if (result.affected === sourceFile) {
|
463 | instance.watchHost.outputFiles.set(instance.filePathKeyMapper(result.affected.fileName), outputFiles.slice());
|
464 | return outputFiles;
|
465 | }
|
466 | }
|
467 | }
|
468 | return undefined;
|
469 | }
|
470 | exports.getEmitFromWatchHost = getEmitFromWatchHost;
|
471 | function getEmitOutput(instance, filePath) {
|
472 | if (fileExtensionIs(filePath, instance.compiler.Extension.Dts)) {
|
473 | return [];
|
474 | }
|
475 | if (utils_1.isReferencedFile(instance, filePath)) {
|
476 | return instance.solutionBuilderHost.getOutputFilesFromReferencedProjectInput(filePath);
|
477 | }
|
478 | const program = utils_1.ensureProgram(instance);
|
479 | if (program !== undefined) {
|
480 | const sourceFile = program.getSourceFile(filePath);
|
481 | const outputFiles = [];
|
482 | const writeFile = (fileName, text, writeByteOrderMark) => outputFiles.push({ name: fileName, writeByteOrderMark, text });
|
483 | const outputFilesFromWatch = getEmitFromWatchHost(instance, filePath);
|
484 | if (outputFilesFromWatch) {
|
485 | return outputFilesFromWatch;
|
486 | }
|
487 | program.emit(sourceFile, writeFile,
|
488 | undefined,
|
489 | false, instance.transformers);
|
490 | return outputFiles;
|
491 | }
|
492 | else {
|
493 |
|
494 | return instance.languageService.getProgram().getSourceFile(filePath) ===
|
495 | undefined
|
496 | ? []
|
497 | : instance.languageService.getEmitOutput(filePath).outputFiles;
|
498 | }
|
499 | }
|
500 | exports.getEmitOutput = getEmitOutput;
|
501 |
|
\ | No newline at end of file |