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