blob: 9f659c38027b23dd022fe3648022971f7a0130d1 [file] [log] [blame]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const chalk_1 = require("chalk");
const fs = require("fs");
const path = require("path");
const after_compile_1 = require("./after-compile");
const compilerSetup_1 = require("./compilerSetup");
const config_1 = require("./config");
const constants_1 = require("./constants");
const logger = require("./logger");
const servicesHost_1 = require("./servicesHost");
const utils_1 = require("./utils");
const watch_run_1 = require("./watch-run");
const instances = {};
/**
* The loader is executed once for each file seen by webpack. However, we need to keep
* a persistent instance of TypeScript that contains all of the files in the program
* along with definition files and options. This function either creates an instance
* or returns the existing one. Multiple instances are possible by using the
* `instance` property.
*/
function getTypeScriptInstance(loaderOptions, loader) {
if (instances.hasOwnProperty(loaderOptions.instance)) {
const instance = instances[loaderOptions.instance];
utils_1.ensureProgram(instance);
return { instance: instances[loaderOptions.instance] };
}
const colors = new chalk_1.default.constructor({ enabled: loaderOptions.colors });
const log = logger.makeLogger(loaderOptions, colors);
const compiler = compilerSetup_1.getCompiler(loaderOptions, log);
if (compiler.errorMessage !== undefined) {
return { error: utils_1.makeError(colors.red(compiler.errorMessage), undefined) };
}
return successfulTypeScriptInstance(loaderOptions, loader, log, colors, compiler.compiler, compiler.compilerCompatible, compiler.compilerDetailsLogMessage);
}
exports.getTypeScriptInstance = getTypeScriptInstance;
function successfulTypeScriptInstance(loaderOptions, loader, log, colors, compiler, compilerCompatible, compilerDetailsLogMessage) {
const configFileAndPath = config_1.getConfigFile(compiler, colors, loader, loaderOptions, compilerCompatible, log, compilerDetailsLogMessage);
if (configFileAndPath.configFileError !== undefined) {
const { message, file } = configFileAndPath.configFileError;
return {
error: utils_1.makeError(colors.red('error while reading tsconfig.json:' + constants_1.EOL + message), file)
};
}
const { configFilePath, configFile } = configFileAndPath;
const basePath = loaderOptions.context || path.dirname(configFilePath || '');
const configParseResult = config_1.getConfigParseResult(compiler, configFile, basePath, configFilePath, loaderOptions.projectReferences);
if (configParseResult.errors.length > 0 && !loaderOptions.happyPackMode) {
const errors = utils_1.formatErrors(configParseResult.errors, loaderOptions, colors, compiler, { file: configFilePath }, loader.context);
loader._module.errors.push(...errors);
return {
error: utils_1.makeError(colors.red('error while parsing tsconfig.json'), configFilePath)
};
}
const compilerOptions = compilerSetup_1.getCompilerOptions(configParseResult);
const rootFileNames = new Set();
const files = new Map();
const otherFiles = new Map();
const appendTsTsxSuffixesIfRequired = loaderOptions.appendTsSuffixTo.length > 0 ||
loaderOptions.appendTsxSuffixTo.length > 0
? (filePath) => utils_1.appendSuffixesIfMatch({
'.ts': loaderOptions.appendTsSuffixTo,
'.tsx': loaderOptions.appendTsxSuffixTo
}, filePath)
: (filePath) => filePath;
// same strategy as https://github.com/s-panferov/awesome-typescript-loader/pull/531/files
let { getCustomTransformers: customerTransformers } = loaderOptions;
let getCustomTransformers = Function.prototype;
if (typeof customerTransformers === 'function') {
getCustomTransformers = customerTransformers;
}
else if (typeof customerTransformers === 'string') {
try {
customerTransformers = require(customerTransformers);
}
catch (err) {
throw new Error(`Failed to load customTransformers from "${loaderOptions.getCustomTransformers}": ${err.message}`);
}
if (typeof customerTransformers !== 'function') {
throw new Error(`Custom transformers in "${loaderOptions.getCustomTransformers}" should export a function, got ${typeof getCustomTransformers}`);
}
getCustomTransformers = customerTransformers;
}
if (loaderOptions.transpileOnly) {
// quick return for transpiling
// we do need to check for any issues with TS options though
const program = configParseResult.projectReferences !== undefined
? compiler.createProgram({
rootNames: configParseResult.fileNames,
options: configParseResult.options,
projectReferences: configParseResult.projectReferences
})
: compiler.createProgram([], compilerOptions);
// happypack does not have _module.errors - see https://github.com/TypeStrong/ts-loader/issues/336
if (!loaderOptions.happyPackMode) {
const diagnostics = program.getOptionsDiagnostics();
const errors = utils_1.formatErrors(diagnostics, loaderOptions, colors, compiler, { file: configFilePath || 'tsconfig.json' }, loader.context);
loader._module.errors.push(...errors);
}
instances[loaderOptions.instance] = {
compiler,
compilerOptions,
appendTsTsxSuffixesIfRequired,
loaderOptions,
rootFileNames,
files,
otherFiles,
program,
dependencyGraph: {},
reverseDependencyGraph: {},
transformers: getCustomTransformers(program),
colors
};
return { instance: instances[loaderOptions.instance] };
}
// Load initial files (core lib files, any files specified in tsconfig.json)
let normalizedFilePath;
try {
const filesToLoad = loaderOptions.onlyCompileBundledFiles
? configParseResult.fileNames.filter(fileName => constants_1.dtsDtsxOrDtsDtsxMapRegex.test(fileName))
: configParseResult.fileNames;
filesToLoad.forEach(filePath => {
normalizedFilePath = path.normalize(filePath);
files.set(normalizedFilePath, {
text: fs.readFileSync(normalizedFilePath, 'utf-8'),
version: 0
});
rootFileNames.add(normalizedFilePath);
});
}
catch (exc) {
return {
error: utils_1.makeError(colors.red(`A file specified in tsconfig.json could not be found: ${normalizedFilePath}`), normalizedFilePath)
};
}
// if allowJs is set then we should accept js(x) files
const scriptRegex = configParseResult.options.allowJs === true
? /\.tsx?$|\.jsx?$/i
: /\.tsx?$/i;
const instance = (instances[loaderOptions.instance] = {
compiler,
compilerOptions,
appendTsTsxSuffixesIfRequired,
loaderOptions,
rootFileNames,
files,
otherFiles,
languageService: null,
version: 0,
transformers: {},
dependencyGraph: {},
reverseDependencyGraph: {},
modifiedFiles: null,
colors
});
if (!loader._compiler.hooks) {
throw new Error("You may be using an old version of webpack; please check you're using at least version 4");
}
if (loaderOptions.experimentalWatchApi && compiler.createWatchProgram) {
log.logInfo('Using watch api');
// If there is api available for watch, use it instead of language service
instance.watchHost = servicesHost_1.makeWatchHost(scriptRegex, log, loader, instance, configParseResult.projectReferences);
instance.watchOfFilesAndCompilerOptions = compiler.createWatchProgram(instance.watchHost);
instance.program = instance.watchOfFilesAndCompilerOptions
.getProgram()
.getProgram();
instance.transformers = getCustomTransformers(instance.program);
}
else {
const servicesHost = servicesHost_1.makeServicesHost(scriptRegex, log, loader, instance, loaderOptions.experimentalFileCaching, configParseResult.projectReferences);
instance.languageService = compiler.createLanguageService(servicesHost.servicesHost, compiler.createDocumentRegistry());
if (servicesHost.clearCache !== null) {
loader._compiler.hooks.watchRun.tap('ts-loader', servicesHost.clearCache);
}
instance.transformers = getCustomTransformers(instance.languageService.getProgram());
}
loader._compiler.hooks.afterCompile.tapAsync('ts-loader', after_compile_1.makeAfterCompile(instance, configFilePath));
loader._compiler.hooks.watchRun.tapAsync('ts-loader', watch_run_1.makeWatchRun(instance));
return { instance };
}
function getEmitOutput(instance, filePath) {
const program = utils_1.ensureProgram(instance);
if (program !== undefined) {
const outputFiles = [];
const writeFile = (fileName, text, writeByteOrderMark) => outputFiles.push({ name: fileName, writeByteOrderMark, text });
const sourceFile = program.getSourceFile(filePath);
// The source file will be undefined if it’s part of an unbuilt project reference
if (sourceFile !== undefined || !utils_1.isUsingProjectReferences(instance)) {
program.emit(sourceFile, writeFile,
/*cancellationToken*/ undefined,
/*emitOnlyDtsFiles*/ false, instance.transformers);
}
return outputFiles;
}
else {
// Emit Javascript
return instance.languageService.getProgram().getSourceFile(filePath) ===
undefined
? []
: instance.languageService.getEmitOutput(filePath).outputFiles;
}
}
exports.getEmitOutput = getEmitOutput;
//# sourceMappingURL=instances.js.map