start werk
This commit is contained in:
+180
@@ -0,0 +1,180 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const path = require("path");
|
||||
const constants = require("./constants");
|
||||
const instances_1 = require("./instances");
|
||||
const utils_1 = require("./utils");
|
||||
function makeAfterCompile(instance, configFilePath) {
|
||||
let getCompilerOptionDiagnostics = true;
|
||||
let checkAllFilesForErrors = true;
|
||||
return (compilation, callback) => {
|
||||
// Don't add errors for child compilations
|
||||
if (compilation.compiler.isChild()) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
removeTSLoaderErrors(compilation.errors);
|
||||
provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath);
|
||||
getCompilerOptionDiagnostics = false;
|
||||
const modules = determineModules(compilation);
|
||||
const filesToCheckForErrors = determineFilesToCheckForErrors(checkAllFilesForErrors, instance);
|
||||
checkAllFilesForErrors = false;
|
||||
const filesWithErrors = new Map();
|
||||
provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance);
|
||||
provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation);
|
||||
instance.filesWithErrors = filesWithErrors;
|
||||
instance.modifiedFiles = null;
|
||||
instance.projectsMissingSourceMaps = new Set();
|
||||
callback();
|
||||
};
|
||||
}
|
||||
exports.makeAfterCompile = makeAfterCompile;
|
||||
/**
|
||||
* handle compiler option errors after the first compile
|
||||
*/
|
||||
function provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath) {
|
||||
if (getCompilerOptionDiagnostics) {
|
||||
const { languageService, loaderOptions, compiler, program } = instance;
|
||||
const errorsToAdd = utils_1.formatErrors(program === undefined
|
||||
? languageService.getCompilerOptionsDiagnostics()
|
||||
: program.getOptionsDiagnostics(), loaderOptions, instance.colors, compiler, { file: configFilePath || 'tsconfig.json' }, compilation.compiler.context);
|
||||
compilation.errors.push(...errorsToAdd);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* build map of all modules based on normalized filename
|
||||
* this is used for quick-lookup when trying to find modules
|
||||
* based on filepath
|
||||
*/
|
||||
function determineModules(compilation) {
|
||||
return compilation.modules.reduce((modules, module) => {
|
||||
if (module.resource) {
|
||||
const modulePath = path.normalize(module.resource);
|
||||
const existingModules = modules.get(modulePath);
|
||||
if (existingModules !== undefined) {
|
||||
if (existingModules.indexOf(module) === -1) {
|
||||
existingModules.push(module);
|
||||
}
|
||||
}
|
||||
else {
|
||||
modules.set(modulePath, [module]);
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
}, new Map());
|
||||
}
|
||||
function determineFilesToCheckForErrors(checkAllFilesForErrors, instance) {
|
||||
const { files, modifiedFiles, filesWithErrors, otherFiles } = instance;
|
||||
// calculate array of files to check
|
||||
const filesToCheckForErrors = new Map();
|
||||
if (checkAllFilesForErrors) {
|
||||
// check all files on initial run
|
||||
for (const [filePath, file] of files) {
|
||||
filesToCheckForErrors.set(filePath, file);
|
||||
}
|
||||
for (const [filePath, file] of otherFiles) {
|
||||
filesToCheckForErrors.set(filePath, file);
|
||||
}
|
||||
}
|
||||
else if (modifiedFiles !== null && modifiedFiles !== undefined) {
|
||||
// check all modified files, and all dependants
|
||||
for (const modifiedFileName of modifiedFiles.keys()) {
|
||||
utils_1.collectAllDependants(instance.reverseDependencyGraph, modifiedFileName).forEach(fileName => {
|
||||
const fileToCheckForErrors = files.get(fileName) || otherFiles.get(fileName);
|
||||
filesToCheckForErrors.set(fileName, fileToCheckForErrors);
|
||||
});
|
||||
}
|
||||
}
|
||||
// re-check files with errors from previous build
|
||||
if (filesWithErrors !== undefined) {
|
||||
for (const [fileWithErrorName, fileWithErrors] of filesWithErrors) {
|
||||
filesToCheckForErrors.set(fileWithErrorName, fileWithErrors);
|
||||
}
|
||||
}
|
||||
return filesToCheckForErrors;
|
||||
}
|
||||
function provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance) {
|
||||
const { compiler, program, languageService, files, loaderOptions, compilerOptions, otherFiles } = instance;
|
||||
const filePathRegex = compilerOptions.checkJs === true
|
||||
? constants.dtsTsTsxJsJsxRegex
|
||||
: constants.dtsTsTsxRegex;
|
||||
for (const filePath of filesToCheckForErrors.keys()) {
|
||||
if (filePath.match(filePathRegex) === null) {
|
||||
continue;
|
||||
}
|
||||
const sourceFile = program === undefined ? undefined : program.getSourceFile(filePath);
|
||||
// If the source file is undefined, that probably means it’s actually part of an unbuilt project reference,
|
||||
// which will have already produced a more useful error than the one we would get by proceeding here.
|
||||
// If it’s undefined and we’re not using project references at all, I guess carry on so the user will
|
||||
// get a useful error about which file was unexpectedly missing.
|
||||
if (utils_1.isUsingProjectReferences(instance) && sourceFile === undefined) {
|
||||
continue;
|
||||
}
|
||||
const errors = program === undefined
|
||||
? [
|
||||
...languageService.getSyntacticDiagnostics(filePath),
|
||||
...languageService.getSemanticDiagnostics(filePath)
|
||||
]
|
||||
: [
|
||||
...program.getSyntacticDiagnostics(sourceFile),
|
||||
...program.getSemanticDiagnostics(sourceFile)
|
||||
];
|
||||
if (errors.length > 0) {
|
||||
const fileWithError = files.get(filePath) || otherFiles.get(filePath);
|
||||
filesWithErrors.set(filePath, fileWithError);
|
||||
}
|
||||
// if we have access to a webpack module, use that
|
||||
const associatedModules = modules.get(filePath);
|
||||
if (associatedModules !== undefined) {
|
||||
associatedModules.forEach(module => {
|
||||
// remove any existing errors
|
||||
removeTSLoaderErrors(module.errors);
|
||||
// append errors
|
||||
const formattedErrors = utils_1.formatErrors(errors, loaderOptions, instance.colors, compiler, { module }, compilation.compiler.context);
|
||||
module.errors.push(...formattedErrors);
|
||||
compilation.errors.push(...formattedErrors);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// otherwise it's a more generic error
|
||||
const formattedErrors = utils_1.formatErrors(errors, loaderOptions, instance.colors, compiler, { file: filePath }, compilation.compiler.context);
|
||||
compilation.errors.push(...formattedErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* gather all declaration files from TypeScript and output them to webpack
|
||||
*/
|
||||
function provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation) {
|
||||
for (const filePath of filesToCheckForErrors.keys()) {
|
||||
if (filePath.match(constants.tsTsxRegex) === null) {
|
||||
continue;
|
||||
}
|
||||
const outputFiles = instances_1.getEmitOutput(instance, filePath);
|
||||
const declarationFiles = outputFiles.filter(outputFile => outputFile.name.match(constants.dtsDtsxOrDtsDtsxMapRegex));
|
||||
declarationFiles.forEach(declarationFile => {
|
||||
const assetPath = path.relative(compilation.compiler.outputPath, declarationFile.name);
|
||||
compilation.assets[assetPath] = {
|
||||
source: () => declarationFile.text,
|
||||
size: () => declarationFile.text.length
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* handle all other errors. The basic approach here to get accurate error
|
||||
* reporting is to start with a "blank slate" each compilation and gather
|
||||
* all errors from all files. Since webpack tracks errors in a module from
|
||||
* compilation-to-compilation, and since not every module always runs through
|
||||
* the loader, we need to detect and remove any pre-existing errors.
|
||||
*/
|
||||
function removeTSLoaderErrors(errors) {
|
||||
let index = -1;
|
||||
let length = errors.length;
|
||||
while (++index < length) {
|
||||
if (errors[index].loaderSource === 'ts-loader') {
|
||||
errors.splice(index--, 1);
|
||||
length--;
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const semver = require("semver");
|
||||
const typescript = require("typescript");
|
||||
function getCompiler(loaderOptions, log) {
|
||||
let compiler;
|
||||
let errorMessage;
|
||||
let compilerDetailsLogMessage;
|
||||
let compilerCompatible = false;
|
||||
try {
|
||||
compiler = require(loaderOptions.compiler);
|
||||
}
|
||||
catch (e) {
|
||||
errorMessage =
|
||||
loaderOptions.compiler === 'typescript'
|
||||
? 'Could not load TypeScript. Try installing with `yarn add typescript` or `npm install typescript`. If TypeScript is installed globally, try using `yarn link typescript` or `npm link typescript`.'
|
||||
: `Could not load TypeScript compiler with NPM package name \`${loaderOptions.compiler}\`. Are you sure it is correctly installed?`;
|
||||
}
|
||||
if (errorMessage === undefined) {
|
||||
compilerDetailsLogMessage = `ts-loader: Using ${loaderOptions.compiler}@${compiler.version}`;
|
||||
compilerCompatible = false;
|
||||
if (loaderOptions.compiler === 'typescript') {
|
||||
if (compiler.version !== undefined &&
|
||||
semver.gte(compiler.version, '2.4.1')) {
|
||||
// don't log yet in this case, if a tsconfig.json exists we want to combine the message
|
||||
compilerCompatible = true;
|
||||
}
|
||||
else {
|
||||
log.logError(`${compilerDetailsLogMessage}. This version is incompatible with ts-loader. Please upgrade to the latest version of TypeScript.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.logWarning(`${compilerDetailsLogMessage}. This version may or may not be compatible with ts-loader.`);
|
||||
}
|
||||
}
|
||||
return {
|
||||
compiler,
|
||||
compilerCompatible,
|
||||
compilerDetailsLogMessage,
|
||||
errorMessage
|
||||
};
|
||||
}
|
||||
exports.getCompiler = getCompiler;
|
||||
function getCompilerOptions(configParseResult) {
|
||||
const compilerOptions = Object.assign({}, configParseResult.options, {
|
||||
skipLibCheck: true,
|
||||
suppressOutputPathCheck: true // This is why: https://github.com/Microsoft/TypeScript/issues/7363
|
||||
});
|
||||
// if `module` is not specified and not using ES6+ target, default to CJS module output
|
||||
if (compilerOptions.module === undefined &&
|
||||
(compilerOptions.target !== undefined &&
|
||||
compilerOptions.target < typescript.ScriptTarget.ES2015)) {
|
||||
compilerOptions.module = typescript.ModuleKind.CommonJS;
|
||||
}
|
||||
return compilerOptions;
|
||||
}
|
||||
exports.getCompilerOptions = getCompilerOptions;
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const path = require("path");
|
||||
const utils_1 = require("./utils");
|
||||
function getConfigFile(compiler, colors, loader, loaderOptions, compilerCompatible, log, compilerDetailsLogMessage) {
|
||||
const configFilePath = findConfigFile(compiler, path.dirname(loader.resourcePath), loaderOptions.configFile);
|
||||
let configFileError;
|
||||
let configFile;
|
||||
if (configFilePath !== undefined) {
|
||||
if (compilerCompatible) {
|
||||
log.logInfo(`${compilerDetailsLogMessage} and ${configFilePath}`);
|
||||
}
|
||||
else {
|
||||
log.logInfo(`ts-loader: Using config file at ${configFilePath}`);
|
||||
}
|
||||
configFile = compiler.readConfigFile(configFilePath, compiler.sys.readFile);
|
||||
if (configFile.error !== undefined) {
|
||||
configFileError = utils_1.formatErrors([configFile.error], loaderOptions, colors, compiler, { file: configFilePath }, loader.context)[0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (compilerCompatible) {
|
||||
log.logInfo(compilerDetailsLogMessage);
|
||||
}
|
||||
configFile = {
|
||||
config: {
|
||||
compilerOptions: {},
|
||||
files: []
|
||||
}
|
||||
};
|
||||
}
|
||||
if (configFileError === undefined) {
|
||||
configFile.config.compilerOptions = Object.assign({}, configFile.config.compilerOptions, loaderOptions.compilerOptions);
|
||||
}
|
||||
return {
|
||||
configFilePath,
|
||||
configFile,
|
||||
configFileError
|
||||
};
|
||||
}
|
||||
exports.getConfigFile = getConfigFile;
|
||||
/**
|
||||
* Find a tsconfig file by name or by path.
|
||||
* By name, the tsconfig.json is found using the same method as `tsc`, starting in the current
|
||||
* directory and continuing up the parent directory chain.
|
||||
* By path, the file will be found by resolving the given path relative to the requesting entry file.
|
||||
*
|
||||
* @param compiler The TypeScript compiler instance
|
||||
* @param requestDirPath The directory in which the entry point requesting the tsconfig.json lies
|
||||
* @param configFile The tsconfig file name to look for or a path to that file
|
||||
* @return The absolute path to the tsconfig file, undefined if none was found.
|
||||
*/
|
||||
function findConfigFile(compiler, requestDirPath, configFile) {
|
||||
// If `configFile` is an absolute path, return it right away
|
||||
if (path.isAbsolute(configFile)) {
|
||||
return compiler.sys.fileExists(configFile) ? configFile : undefined;
|
||||
}
|
||||
// If `configFile` is a relative path, resolve it.
|
||||
// We define a relative path as: starts with
|
||||
// one or two dots + a common directory delimiter
|
||||
if (configFile.match(/^\.\.?(\/|\\)/) !== null) {
|
||||
const resolvedPath = path.resolve(requestDirPath, configFile);
|
||||
return compiler.sys.fileExists(resolvedPath) ? resolvedPath : undefined;
|
||||
// If `configFile` is a file name, find it in the directory tree
|
||||
}
|
||||
else {
|
||||
while (true) {
|
||||
const fileName = path.join(requestDirPath, configFile);
|
||||
if (compiler.sys.fileExists(fileName)) {
|
||||
return fileName;
|
||||
}
|
||||
const parentPath = path.dirname(requestDirPath);
|
||||
if (parentPath === requestDirPath) {
|
||||
break;
|
||||
}
|
||||
requestDirPath = parentPath;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
function getConfigParseResult(compiler, configFile, basePath) {
|
||||
const configParseResult = compiler.parseJsonConfigFileContent(configFile.config, compiler.sys, basePath);
|
||||
return configParseResult;
|
||||
}
|
||||
exports.getConfigParseResult = getConfigParseResult;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const os = require("os");
|
||||
exports.EOL = os.EOL;
|
||||
exports.CarriageReturnLineFeed = '\r\n';
|
||||
exports.LineFeed = '\n';
|
||||
exports.CarriageReturnLineFeedCode = 0;
|
||||
exports.LineFeedCode = 1;
|
||||
exports.extensionRegex = /\.[^.]+$/;
|
||||
exports.tsxRegex = /\.tsx$/i;
|
||||
exports.tsTsxRegex = /\.ts(x?)$/i;
|
||||
exports.dtsDtsxOrDtsDtsxMapRegex = /\.d\.ts(x?)(\.map)?$/i;
|
||||
exports.dtsTsTsxRegex = /(\.d)?\.ts(x?)$/i;
|
||||
exports.dtsTsTsxJsJsxRegex = /((\.d)?\.ts(x?)|js(x?))$/i;
|
||||
exports.tsTsxJsJsxRegex = /\.tsx?$|\.jsx?$/i;
|
||||
exports.jsJsx = /\.js(x?)$/i;
|
||||
exports.jsJsxMap = /\.js(x?)\.map$/i;
|
||||
exports.jsonRegex = /\.json$/i;
|
||||
exports.nodeModules = /node_modules/i;
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
"use strict";
|
||||
const loaderUtils = require("loader-utils");
|
||||
const path = require("path");
|
||||
const constants = require("./constants");
|
||||
const instances_1 = require("./instances");
|
||||
const utils_1 = require("./utils");
|
||||
const webpackInstances = [];
|
||||
const loaderOptionsCache = {};
|
||||
/**
|
||||
* The entry point for ts-loader
|
||||
*/
|
||||
function loader(contents) {
|
||||
// tslint:disable-next-line:no-unused-expression strict-boolean-expressions
|
||||
this.cacheable && this.cacheable();
|
||||
const callback = this.async();
|
||||
const options = getLoaderOptions(this);
|
||||
const instanceOrError = instances_1.getTypeScriptInstance(options, this);
|
||||
if (instanceOrError.error !== undefined) {
|
||||
callback(new Error(instanceOrError.error.message));
|
||||
return;
|
||||
}
|
||||
return successLoader(this, contents, callback, options, instanceOrError.instance);
|
||||
}
|
||||
function successLoader(loaderContext, contents, callback, options, instance) {
|
||||
const rawFilePath = path.normalize(loaderContext.resourcePath);
|
||||
const filePath = options.appendTsSuffixTo.length > 0 || options.appendTsxSuffixTo.length > 0
|
||||
? utils_1.appendSuffixesIfMatch({
|
||||
'.ts': options.appendTsSuffixTo,
|
||||
'.tsx': options.appendTsxSuffixTo
|
||||
}, rawFilePath)
|
||||
: rawFilePath;
|
||||
const fileVersion = updateFileInCache(filePath, contents, instance);
|
||||
const referencedProject = utils_1.getAndCacheProjectReference(filePath, instance);
|
||||
if (referencedProject !== undefined) {
|
||||
const [relativeProjectConfigPath, relativeFilePath] = [
|
||||
path.relative(loaderContext.rootContext, referencedProject.sourceFile.fileName),
|
||||
path.relative(loaderContext.rootContext, filePath)
|
||||
];
|
||||
if (referencedProject.commandLine.options.outFile !== undefined) {
|
||||
throw new Error(`The referenced project at ${relativeProjectConfigPath} is using ` +
|
||||
`the outFile' option, which is not supported with ts-loader.`);
|
||||
}
|
||||
const jsFileName = utils_1.getAndCacheOutputJSFileName(filePath, referencedProject, instance);
|
||||
const relativeJSFileName = path.relative(loaderContext.rootContext, jsFileName);
|
||||
if (!instance.compiler.sys.fileExists(jsFileName)) {
|
||||
throw new Error(`Could not find output JavaScript file for input ` +
|
||||
`${relativeFilePath} (looked at ${relativeJSFileName}).\n` +
|
||||
`The input file is part of a project reference located at ` +
|
||||
`${relativeProjectConfigPath}, so ts-loader is looking for the ` +
|
||||
'project’s pre-built output on disk. Try running `tsc --build` ' +
|
||||
'to build project references.');
|
||||
}
|
||||
// Since the output JS file is being read from disk instead of using the
|
||||
// input TS file, we need to tell the loader that the compilation doesn’t
|
||||
// actually depend on the current file, but depends on the JS file instead.
|
||||
loaderContext.clearDependencies();
|
||||
loaderContext.addDependency(jsFileName);
|
||||
utils_1.validateSourceMapOncePerProject(instance, loaderContext, jsFileName, referencedProject);
|
||||
const mapFileName = jsFileName + '.map';
|
||||
const outputText = instance.compiler.sys.readFile(jsFileName);
|
||||
const sourceMapText = instance.compiler.sys.readFile(mapFileName);
|
||||
makeSourceMapAndFinish(sourceMapText, outputText, filePath, contents, loaderContext, options, fileVersion, callback);
|
||||
}
|
||||
else {
|
||||
const { outputText, sourceMapText } = options.transpileOnly
|
||||
? getTranspilationEmit(filePath, contents, instance, loaderContext)
|
||||
: getEmit(rawFilePath, filePath, instance, loaderContext);
|
||||
makeSourceMapAndFinish(sourceMapText, outputText, filePath, contents, loaderContext, options, fileVersion, callback);
|
||||
}
|
||||
}
|
||||
function makeSourceMapAndFinish(sourceMapText, outputText, filePath, contents, loaderContext, options, fileVersion, callback) {
|
||||
if (outputText === null || outputText === undefined) {
|
||||
const additionalGuidance = !options.allowTsInNodeModules && filePath.indexOf('node_modules') !== -1
|
||||
? ' By default, ts-loader will not compile .ts files in node_modules.\n' +
|
||||
'You should not need to recompile .ts files there, but if you really want to, use the allowTsInNodeModules option.\n' +
|
||||
'See: https://github.com/Microsoft/TypeScript/issues/12358'
|
||||
: '';
|
||||
throw new Error(`TypeScript emitted no output for ${filePath}.${additionalGuidance}`);
|
||||
}
|
||||
const { sourceMap, output } = makeSourceMap(sourceMapText, outputText, filePath, contents, loaderContext);
|
||||
// _module.meta is not available inside happypack
|
||||
if (!options.happyPackMode && loaderContext._module.buildMeta !== undefined) {
|
||||
// Make sure webpack is aware that even though the emitted JavaScript may be the same as
|
||||
// a previously cached version the TypeScript may be different and therefore should be
|
||||
// treated as new
|
||||
loaderContext._module.buildMeta.tsLoaderFileVersion = fileVersion;
|
||||
}
|
||||
callback(null, output, sourceMap);
|
||||
}
|
||||
/**
|
||||
* either retrieves loader options from the cache
|
||||
* or creates them, adds them to the cache and returns
|
||||
*/
|
||||
function getLoaderOptions(loaderContext) {
|
||||
// differentiate the TypeScript instance based on the webpack instance
|
||||
let webpackIndex = webpackInstances.indexOf(loaderContext._compiler);
|
||||
if (webpackIndex === -1) {
|
||||
webpackIndex = webpackInstances.push(loaderContext._compiler) - 1;
|
||||
}
|
||||
const loaderOptions = loaderUtils.getOptions(loaderContext) ||
|
||||
{};
|
||||
const instanceName = webpackIndex + '_' + (loaderOptions.instance || 'default');
|
||||
if (!loaderOptionsCache.hasOwnProperty(instanceName)) {
|
||||
loaderOptionsCache[instanceName] = new WeakMap();
|
||||
}
|
||||
const cache = loaderOptionsCache[instanceName];
|
||||
if (cache.has(loaderOptions)) {
|
||||
return cache.get(loaderOptions);
|
||||
}
|
||||
validateLoaderOptions(loaderOptions);
|
||||
const options = makeLoaderOptions(instanceName, loaderOptions);
|
||||
cache.set(loaderOptions, options);
|
||||
return options;
|
||||
}
|
||||
const validLoaderOptions = [
|
||||
'silent',
|
||||
'logLevel',
|
||||
'logInfoToStdOut',
|
||||
'instance',
|
||||
'compiler',
|
||||
'context',
|
||||
'configFile',
|
||||
'transpileOnly',
|
||||
'ignoreDiagnostics',
|
||||
'errorFormatter',
|
||||
'colors',
|
||||
'compilerOptions',
|
||||
'appendTsSuffixTo',
|
||||
'appendTsxSuffixTo',
|
||||
'onlyCompileBundledFiles',
|
||||
'happyPackMode',
|
||||
'getCustomTransformers',
|
||||
'reportFiles',
|
||||
'experimentalWatchApi',
|
||||
'allowTsInNodeModules',
|
||||
'experimentalFileCaching',
|
||||
'projectReferences',
|
||||
'resolveModuleName',
|
||||
'resolveTypeReferenceDirective'
|
||||
];
|
||||
/**
|
||||
* Validate the supplied loader options.
|
||||
* At present this validates the option names only; in future we may look at validating the values too
|
||||
* @param loaderOptions
|
||||
*/
|
||||
function validateLoaderOptions(loaderOptions) {
|
||||
const loaderOptionKeys = Object.keys(loaderOptions);
|
||||
// tslint:disable-next-line:prefer-for-of
|
||||
for (let i = 0; i < loaderOptionKeys.length; i++) {
|
||||
const option = loaderOptionKeys[i];
|
||||
const isUnexpectedOption = validLoaderOptions.indexOf(option) === -1;
|
||||
if (isUnexpectedOption) {
|
||||
throw new Error(`ts-loader was supplied with an unexpected loader option: ${option}
|
||||
|
||||
Please take a look at the options you are supplying; the following are valid options:
|
||||
${validLoaderOptions.join(' / ')}
|
||||
`);
|
||||
}
|
||||
}
|
||||
if (loaderOptions.context !== undefined &&
|
||||
!path.isAbsolute(loaderOptions.context)) {
|
||||
throw new Error(`Option 'context' has to be an absolute path. Given '${loaderOptions.context}'.`);
|
||||
}
|
||||
}
|
||||
function makeLoaderOptions(instanceName, loaderOptions) {
|
||||
const options = Object.assign({}, {
|
||||
silent: false,
|
||||
logLevel: 'WARN',
|
||||
logInfoToStdOut: false,
|
||||
compiler: 'typescript',
|
||||
configFile: 'tsconfig.json',
|
||||
context: undefined,
|
||||
transpileOnly: false,
|
||||
compilerOptions: {},
|
||||
appendTsSuffixTo: [],
|
||||
appendTsxSuffixTo: [],
|
||||
transformers: {},
|
||||
happyPackMode: false,
|
||||
colors: true,
|
||||
onlyCompileBundledFiles: false,
|
||||
reportFiles: [],
|
||||
// When the watch API usage stabilises look to remove this option and make watch usage the default behaviour when available
|
||||
experimentalWatchApi: false,
|
||||
allowTsInNodeModules: false,
|
||||
experimentalFileCaching: true
|
||||
}, loaderOptions);
|
||||
options.ignoreDiagnostics = utils_1.arrify(options.ignoreDiagnostics).map(Number);
|
||||
options.logLevel = options.logLevel.toUpperCase();
|
||||
options.instance = instanceName;
|
||||
// happypack can be used only together with transpileOnly mode
|
||||
options.transpileOnly = options.happyPackMode ? true : options.transpileOnly;
|
||||
return options;
|
||||
}
|
||||
/**
|
||||
* Either add file to the overall files cache or update it in the cache when the file contents have changed
|
||||
* Also add the file to the modified files
|
||||
*/
|
||||
function updateFileInCache(filePath, contents, instance) {
|
||||
let fileWatcherEventKind;
|
||||
// Update file contents
|
||||
let file = instance.files.get(filePath);
|
||||
if (file === undefined) {
|
||||
file = instance.otherFiles.get(filePath);
|
||||
if (file !== undefined) {
|
||||
instance.otherFiles.delete(filePath);
|
||||
instance.files.set(filePath, file);
|
||||
}
|
||||
else {
|
||||
if (instance.watchHost !== undefined) {
|
||||
fileWatcherEventKind = instance.compiler.FileWatcherEventKind.Created;
|
||||
}
|
||||
file = { version: 0 };
|
||||
instance.files.set(filePath, file);
|
||||
}
|
||||
instance.changedFilesList = true;
|
||||
}
|
||||
if (instance.watchHost !== undefined && contents === undefined) {
|
||||
fileWatcherEventKind = instance.compiler.FileWatcherEventKind.Deleted;
|
||||
}
|
||||
if (file.text !== contents) {
|
||||
file.version++;
|
||||
file.text = contents;
|
||||
instance.version++;
|
||||
if (instance.watchHost !== undefined &&
|
||||
fileWatcherEventKind === undefined) {
|
||||
fileWatcherEventKind = instance.compiler.FileWatcherEventKind.Changed;
|
||||
}
|
||||
}
|
||||
if (instance.watchHost !== undefined && fileWatcherEventKind !== undefined) {
|
||||
instance.hasUnaccountedModifiedFiles = true;
|
||||
instance.watchHost.invokeFileWatcher(filePath, fileWatcherEventKind);
|
||||
instance.watchHost.invokeDirectoryWatcher(path.dirname(filePath), filePath);
|
||||
}
|
||||
// push this file to modified files hash.
|
||||
if (instance.modifiedFiles === null || instance.modifiedFiles === undefined) {
|
||||
instance.modifiedFiles = new Map();
|
||||
}
|
||||
instance.modifiedFiles.set(filePath, file);
|
||||
return file.version;
|
||||
}
|
||||
function getEmit(rawFilePath, filePath, instance, loaderContext) {
|
||||
const outputFiles = instances_1.getEmitOutput(instance, filePath);
|
||||
loaderContext.clearDependencies();
|
||||
loaderContext.addDependency(rawFilePath);
|
||||
const allDefinitionFiles = [...instance.files.keys()].filter(defFilePath => defFilePath.match(constants.dtsDtsxOrDtsDtsxMapRegex));
|
||||
// Make this file dependent on *all* definition files in the program
|
||||
const addDependency = loaderContext.addDependency.bind(loaderContext);
|
||||
allDefinitionFiles.forEach(addDependency);
|
||||
// Additionally make this file dependent on all imported files
|
||||
const fileDependencies = instance.dependencyGraph[filePath];
|
||||
const additionalDependencies = fileDependencies === undefined
|
||||
? []
|
||||
: fileDependencies.map(({ resolvedFileName, originalFileName }) => {
|
||||
const projectReference = utils_1.getAndCacheProjectReference(resolvedFileName, instance);
|
||||
// In the case of dependencies that are part of a project reference,
|
||||
// the real dependency that webpack should watch is the JS output file.
|
||||
return projectReference !== undefined
|
||||
? utils_1.getAndCacheOutputJSFileName(resolvedFileName, projectReference, instance)
|
||||
: originalFileName;
|
||||
});
|
||||
if (additionalDependencies.length > 0) {
|
||||
additionalDependencies.forEach(addDependency);
|
||||
}
|
||||
loaderContext._module.buildMeta.tsLoaderDefinitionFileVersions = allDefinitionFiles
|
||||
.concat(additionalDependencies)
|
||||
.map(defFilePath => defFilePath +
|
||||
'@' +
|
||||
(instance.files.get(defFilePath) || { version: '?' }).version);
|
||||
const outputFile = outputFiles
|
||||
.filter(file => file.name.match(constants.jsJsx))
|
||||
.pop();
|
||||
const outputText = outputFile === undefined ? undefined : outputFile.text;
|
||||
const sourceMapFile = outputFiles
|
||||
.filter(file => file.name.match(constants.jsJsxMap))
|
||||
.pop();
|
||||
const sourceMapText = sourceMapFile === undefined ? undefined : sourceMapFile.text;
|
||||
return { outputText, sourceMapText };
|
||||
}
|
||||
/**
|
||||
* Transpile file
|
||||
*/
|
||||
function getTranspilationEmit(fileName, contents, instance, loaderContext) {
|
||||
const { outputText, sourceMapText, diagnostics } = instance.compiler.transpileModule(contents, {
|
||||
compilerOptions: Object.assign({}, instance.compilerOptions, { rootDir: undefined }),
|
||||
transformers: instance.transformers,
|
||||
reportDiagnostics: true,
|
||||
fileName
|
||||
});
|
||||
// _module.errors is not available inside happypack - see https://github.com/TypeStrong/ts-loader/issues/336
|
||||
if (!instance.loaderOptions.happyPackMode) {
|
||||
const errors = utils_1.formatErrors(diagnostics, instance.loaderOptions, instance.colors, instance.compiler, { module: loaderContext._module }, loaderContext.context);
|
||||
loaderContext._module.errors.push(...errors);
|
||||
}
|
||||
return { outputText, sourceMapText };
|
||||
}
|
||||
function makeSourceMap(sourceMapText, outputText, filePath, contents, loaderContext) {
|
||||
if (sourceMapText === undefined) {
|
||||
return { output: outputText, sourceMap: undefined };
|
||||
}
|
||||
return {
|
||||
output: outputText.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, ''),
|
||||
sourceMap: Object.assign(JSON.parse(sourceMapText), {
|
||||
sources: [loaderUtils.getRemainingRequest(loaderContext)],
|
||||
file: filePath,
|
||||
sourcesContent: [contents]
|
||||
})
|
||||
};
|
||||
}
|
||||
module.exports = loader;
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
"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);
|
||||
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 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,
|
||||
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
|
||||
});
|
||||
});
|
||||
}
|
||||
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,
|
||||
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;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const console_1 = require("console");
|
||||
var LogLevel;
|
||||
(function (LogLevel) {
|
||||
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
||||
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
||||
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
||||
})(LogLevel = exports.LogLevel || (exports.LogLevel = {}));
|
||||
const stderrConsole = new console_1.Console(process.stderr);
|
||||
const stdoutConsole = new console_1.Console(process.stdout);
|
||||
const doNothingLogger = (_message) => { };
|
||||
const makeLoggerFunc = (loaderOptions) => loaderOptions.silent
|
||||
? (_whereToLog, _message) => { }
|
||||
: (whereToLog, message) =>
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log.call(whereToLog, message);
|
||||
const makeExternalLogger = (loaderOptions, logger) => (message) => logger(loaderOptions.logInfoToStdOut ? stdoutConsole : stderrConsole, message);
|
||||
const makeLogInfo = (loaderOptions, logger, green) => LogLevel[loaderOptions.logLevel] <= LogLevel.INFO
|
||||
? (message) => logger(loaderOptions.logInfoToStdOut ? stdoutConsole : stderrConsole, green(message))
|
||||
: doNothingLogger;
|
||||
const makeLogError = (loaderOptions, logger, red) => LogLevel[loaderOptions.logLevel] <= LogLevel.ERROR
|
||||
? (message) => logger(stderrConsole, red(message))
|
||||
: doNothingLogger;
|
||||
const makeLogWarning = (loaderOptions, logger, yellow) => LogLevel[loaderOptions.logLevel] <= LogLevel.WARN
|
||||
? (message) => logger(stderrConsole, yellow(message))
|
||||
: doNothingLogger;
|
||||
function makeLogger(loaderOptions, colors) {
|
||||
const logger = makeLoggerFunc(loaderOptions);
|
||||
return {
|
||||
log: makeExternalLogger(loaderOptions, logger),
|
||||
logInfo: makeLogInfo(loaderOptions, logger, colors.green),
|
||||
logWarning: makeLogWarning(loaderOptions, logger, colors.yellow),
|
||||
logError: makeLogError(loaderOptions, logger, colors.red)
|
||||
};
|
||||
}
|
||||
exports.makeLogger = makeLogger;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
// tslint:disable-next-line:no-submodule-imports
|
||||
const node = require('enhanced-resolve/lib/node');
|
||||
function makeResolver(options) {
|
||||
return node.create.sync(options.resolve);
|
||||
}
|
||||
exports.makeResolver = makeResolver;
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const path = require("path");
|
||||
const constants = require("./constants");
|
||||
const resolver_1 = require("./resolver");
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* Create the TypeScript language service
|
||||
*/
|
||||
function makeServicesHost(scriptRegex, log, loader, instance, enableFileCaching, projectReferences) {
|
||||
const { compiler, compilerOptions, appendTsTsxSuffixesIfRequired, files, loaderOptions: { resolveModuleName: customResolveModuleName, resolveTypeReferenceDirective: customResolveTypeReferenceDirective } } = instance;
|
||||
const newLine = compilerOptions.newLine === constants.CarriageReturnLineFeedCode
|
||||
? constants.CarriageReturnLineFeed
|
||||
: compilerOptions.newLine === constants.LineFeedCode
|
||||
? constants.LineFeed
|
||||
: constants.EOL;
|
||||
// make a (sync) resolver that follows webpack's rules
|
||||
const resolveSync = resolver_1.makeResolver(loader._compiler.options);
|
||||
const readFileWithFallback = (filePath, encoding) => compiler.sys.readFile(filePath, encoding) || utils_1.readFile(filePath, encoding);
|
||||
const fileExists = (filePathToCheck) => compiler.sys.fileExists(filePathToCheck) ||
|
||||
utils_1.readFile(filePathToCheck) !== undefined;
|
||||
const moduleResolutionHost = {
|
||||
fileExists,
|
||||
readFile: readFileWithFallback,
|
||||
realpath: compiler.sys.realpath,
|
||||
directoryExists: compiler.sys.directoryExists
|
||||
};
|
||||
const clearCache = enableFileCaching ? addCache(moduleResolutionHost) : null;
|
||||
// loader.context seems to work fine on Linux / Mac regardless causes problems for @types resolution on Windows for TypeScript < 2.3
|
||||
const getCurrentDirectory = () => loader.context;
|
||||
const resolvers = makeResolvers(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective, customResolveModuleName, resolveSync, appendTsTsxSuffixesIfRequired, scriptRegex, instance);
|
||||
const servicesHost = {
|
||||
getProjectVersion: () => `${instance.version}`,
|
||||
getProjectReferences: () => projectReferences,
|
||||
getScriptFileNames: () => [...files.keys()].filter(filePath => filePath.match(scriptRegex)),
|
||||
getScriptVersion: (fileName) => {
|
||||
fileName = path.normalize(fileName);
|
||||
const file = files.get(fileName);
|
||||
return file === undefined ? '' : file.version.toString();
|
||||
},
|
||||
getScriptSnapshot: (fileName) => {
|
||||
// This is called any time TypeScript needs a file's text
|
||||
// We either load from memory or from disk
|
||||
fileName = path.normalize(fileName);
|
||||
let file = files.get(fileName);
|
||||
if (file === undefined) {
|
||||
const text = utils_1.readFile(fileName);
|
||||
if (text === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
file = { version: 0, text };
|
||||
files.set(fileName, file);
|
||||
}
|
||||
return compiler.ScriptSnapshot.fromString(file.text);
|
||||
},
|
||||
/**
|
||||
* getDirectories is also required for full import and type reference completions.
|
||||
* Without it defined, certain completions will not be provided
|
||||
*/
|
||||
getDirectories: compiler.sys.getDirectories,
|
||||
/**
|
||||
* For @types expansion, these two functions are needed.
|
||||
*/
|
||||
directoryExists: moduleResolutionHost.directoryExists,
|
||||
useCaseSensitiveFileNames: () => compiler.sys.useCaseSensitiveFileNames,
|
||||
realpath: moduleResolutionHost.realpath,
|
||||
// The following three methods are necessary for @types resolution from TS 2.4.1 onwards see: https://github.com/Microsoft/TypeScript/issues/16772
|
||||
fileExists: moduleResolutionHost.fileExists,
|
||||
readFile: moduleResolutionHost.readFile,
|
||||
readDirectory: compiler.sys.readDirectory,
|
||||
getCurrentDirectory,
|
||||
getCompilationSettings: () => compilerOptions,
|
||||
getDefaultLibFileName: (options) => compiler.getDefaultLibFilePath(options),
|
||||
getNewLine: () => newLine,
|
||||
trace: log.log,
|
||||
log: log.log,
|
||||
// used for (/// <reference types="...">) see https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/250#issuecomment-485061329
|
||||
resolveTypeReferenceDirectives: resolvers.resolveTypeReferenceDirectives,
|
||||
resolveModuleNames: resolvers.resolveModuleNames,
|
||||
getCustomTransformers: () => instance.transformers
|
||||
};
|
||||
return { servicesHost, clearCache };
|
||||
}
|
||||
exports.makeServicesHost = makeServicesHost;
|
||||
function makeResolvers(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective, customResolveModuleName, resolveSync, appendTsTsxSuffixesIfRequired, scriptRegex, instance) {
|
||||
const resolveTypeReferenceDirective = makeResolveTypeReferenceDirective(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective);
|
||||
const resolveTypeReferenceDirectives = (typeDirectiveNames, containingFile, _redirectedReference) => typeDirectiveNames.map(directive => resolveTypeReferenceDirective(directive, containingFile)
|
||||
.resolvedTypeReferenceDirective);
|
||||
const resolveModuleName = makeResolveModuleName(compiler, compilerOptions, moduleResolutionHost, customResolveModuleName);
|
||||
const resolveModuleNames = (moduleNames, containingFile, _reusedNames, _redirectedReference) => {
|
||||
const resolvedModules = moduleNames.map(moduleName => resolveModule(resolveSync, resolveModuleName, appendTsTsxSuffixesIfRequired, scriptRegex, moduleName, containingFile));
|
||||
populateDependencyGraphs(resolvedModules, instance, containingFile);
|
||||
return resolvedModules;
|
||||
};
|
||||
return {
|
||||
resolveTypeReferenceDirectives,
|
||||
resolveModuleNames
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Create the TypeScript Watch host
|
||||
*/
|
||||
function makeWatchHost(scriptRegex, log, loader, instance, projectReferences) {
|
||||
const { compiler, compilerOptions, appendTsTsxSuffixesIfRequired, files, otherFiles, loaderOptions: { resolveModuleName: customResolveModuleName, resolveTypeReferenceDirective: customResolveTypeReferenceDirective } } = instance;
|
||||
const newLine = compilerOptions.newLine === constants.CarriageReturnLineFeedCode
|
||||
? constants.CarriageReturnLineFeed
|
||||
: compilerOptions.newLine === constants.LineFeedCode
|
||||
? constants.LineFeed
|
||||
: constants.EOL;
|
||||
// make a (sync) resolver that follows webpack's rules
|
||||
const resolveSync = resolver_1.makeResolver(loader._compiler.options);
|
||||
const readFileWithFallback = (filePath, encoding) => compiler.sys.readFile(filePath, encoding) || utils_1.readFile(filePath, encoding);
|
||||
const moduleResolutionHost = {
|
||||
fileExists,
|
||||
readFile: readFileWithFallback,
|
||||
realpath: compiler.sys.realpath
|
||||
};
|
||||
// loader.context seems to work fine on Linux / Mac regardless causes problems for @types resolution on Windows for TypeScript < 2.3
|
||||
const getCurrentDirectory = () => loader.context;
|
||||
const watchedFiles = {};
|
||||
const watchedDirectories = {};
|
||||
const watchedDirectoriesRecursive = {};
|
||||
const resolvers = makeResolvers(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective, customResolveModuleName, resolveSync, appendTsTsxSuffixesIfRequired, scriptRegex, instance);
|
||||
const watchHost = {
|
||||
rootFiles: getRootFileNames(),
|
||||
options: compilerOptions,
|
||||
useCaseSensitiveFileNames: () => compiler.sys.useCaseSensitiveFileNames,
|
||||
getNewLine: () => newLine,
|
||||
getCurrentDirectory,
|
||||
getDefaultLibFileName: options => compiler.getDefaultLibFilePath(options),
|
||||
fileExists,
|
||||
readFile: readFileWithCachingText,
|
||||
directoryExists: dirPath => compiler.sys.directoryExists(path.normalize(dirPath)),
|
||||
getDirectories: dirPath => compiler.sys.getDirectories(path.normalize(dirPath)),
|
||||
readDirectory: (dirPath, extensions, exclude, include, depth) => compiler.sys.readDirectory(path.normalize(dirPath), extensions, exclude, include, depth),
|
||||
realpath: dirPath => compiler.sys.resolvePath(path.normalize(dirPath)),
|
||||
trace: logData => log.log(logData),
|
||||
watchFile,
|
||||
watchDirectory,
|
||||
// used for (/// <reference types="...">) see https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/250#issuecomment-485061329
|
||||
resolveTypeReferenceDirectives: resolvers.resolveTypeReferenceDirectives,
|
||||
resolveModuleNames: resolvers.resolveModuleNames,
|
||||
invokeFileWatcher,
|
||||
invokeDirectoryWatcher,
|
||||
updateRootFileNames: () => {
|
||||
instance.changedFilesList = false;
|
||||
if (instance.watchOfFilesAndCompilerOptions !== undefined) {
|
||||
instance.watchOfFilesAndCompilerOptions.updateRootFileNames(getRootFileNames());
|
||||
}
|
||||
},
|
||||
createProgram: projectReferences === undefined
|
||||
? compiler.createAbstractBuilder
|
||||
: createBuilderProgramWithReferences
|
||||
};
|
||||
return watchHost;
|
||||
function getRootFileNames() {
|
||||
return [...files.keys()].filter(filePath => filePath.match(scriptRegex));
|
||||
}
|
||||
function readFileWithCachingText(fileName, encoding) {
|
||||
fileName = path.normalize(fileName);
|
||||
const file = files.get(fileName) || otherFiles.get(fileName);
|
||||
if (file !== undefined) {
|
||||
return file.text;
|
||||
}
|
||||
const text = readFileWithFallback(fileName, encoding);
|
||||
if (text === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
otherFiles.set(fileName, { version: 0, text });
|
||||
return text;
|
||||
}
|
||||
function fileExists(fileName) {
|
||||
const filePath = path.normalize(fileName);
|
||||
return files.has(filePath) || compiler.sys.fileExists(filePath);
|
||||
}
|
||||
function invokeWatcherCallbacks(callbacks, fileName, eventKind) {
|
||||
if (callbacks !== undefined) {
|
||||
// The array copy is made to ensure that even if one of the callback removes the callbacks,
|
||||
// we dont miss any callbacks following it
|
||||
const cbs = callbacks.slice();
|
||||
for (const cb of cbs) {
|
||||
cb(fileName, eventKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
function invokeFileWatcher(fileName, eventKind) {
|
||||
fileName = path.normalize(fileName);
|
||||
invokeWatcherCallbacks(watchedFiles[fileName], fileName, eventKind);
|
||||
}
|
||||
function invokeDirectoryWatcher(directory, fileAddedOrRemoved) {
|
||||
directory = path.normalize(directory);
|
||||
invokeWatcherCallbacks(watchedDirectories[directory], fileAddedOrRemoved);
|
||||
invokeRecursiveDirectoryWatcher(directory, fileAddedOrRemoved);
|
||||
}
|
||||
function invokeRecursiveDirectoryWatcher(directory, fileAddedOrRemoved) {
|
||||
directory = path.normalize(directory);
|
||||
invokeWatcherCallbacks(watchedDirectoriesRecursive[directory], fileAddedOrRemoved);
|
||||
const basePath = path.dirname(directory);
|
||||
if (directory !== basePath) {
|
||||
invokeRecursiveDirectoryWatcher(basePath, fileAddedOrRemoved);
|
||||
}
|
||||
}
|
||||
function createWatcher(file, callbacks, callback) {
|
||||
file = path.normalize(file);
|
||||
const existing = callbacks[file];
|
||||
if (existing === undefined) {
|
||||
callbacks[file] = [callback];
|
||||
}
|
||||
else {
|
||||
existing.push(callback);
|
||||
}
|
||||
return {
|
||||
close: () => {
|
||||
// tslint:disable-next-line:no-shadowed-variable
|
||||
const existing = callbacks[file];
|
||||
if (existing !== undefined) {
|
||||
utils_1.unorderedRemoveItem(existing, callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function watchFile(fileName, callback, _pollingInterval) {
|
||||
return createWatcher(fileName, watchedFiles, callback);
|
||||
}
|
||||
function watchDirectory(fileName, callback, recursive) {
|
||||
return createWatcher(fileName, recursive === true ? watchedDirectoriesRecursive : watchedDirectories, callback);
|
||||
}
|
||||
function createBuilderProgramWithReferences(rootNames, options, host, oldProgram, configFileParsingDiagnostics) {
|
||||
const program = compiler.createProgram({
|
||||
rootNames: rootNames,
|
||||
options: options,
|
||||
host,
|
||||
oldProgram: oldProgram && oldProgram.getProgram(),
|
||||
configFileParsingDiagnostics,
|
||||
projectReferences
|
||||
});
|
||||
const builderProgramHost = host;
|
||||
return compiler.createAbstractBuilder(program, builderProgramHost, oldProgram, configFileParsingDiagnostics);
|
||||
}
|
||||
}
|
||||
exports.makeWatchHost = makeWatchHost;
|
||||
function makeResolveTypeReferenceDirective(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective) {
|
||||
if (customResolveTypeReferenceDirective === undefined) {
|
||||
return (directive, containingFile) => compiler.resolveTypeReferenceDirective(directive, containingFile, compilerOptions, moduleResolutionHost);
|
||||
}
|
||||
return (directive, containingFile) => customResolveTypeReferenceDirective(directive, containingFile, compilerOptions, moduleResolutionHost, compiler.resolveTypeReferenceDirective);
|
||||
}
|
||||
function isJsImplementationOfTypings(resolvedModule, tsResolution) {
|
||||
return (resolvedModule.resolvedFileName.endsWith('js') &&
|
||||
/\.d\.ts$/.test(tsResolution.resolvedFileName));
|
||||
}
|
||||
function resolveModule(resolveSync, resolveModuleName, appendTsTsxSuffixesIfRequired, scriptRegex, moduleName, containingFile) {
|
||||
let resolutionResult;
|
||||
try {
|
||||
const originalFileName = resolveSync(undefined, path.normalize(path.dirname(containingFile)), moduleName);
|
||||
const resolvedFileName = appendTsTsxSuffixesIfRequired(originalFileName);
|
||||
if (resolvedFileName.match(scriptRegex) !== null) {
|
||||
resolutionResult = { resolvedFileName, originalFileName };
|
||||
}
|
||||
// tslint:disable-next-line:no-empty
|
||||
}
|
||||
catch (e) { }
|
||||
const tsResolution = resolveModuleName(moduleName, containingFile);
|
||||
if (tsResolution.resolvedModule !== undefined) {
|
||||
const resolvedFileName = path.normalize(tsResolution.resolvedModule.resolvedFileName);
|
||||
const tsResolutionResult = {
|
||||
originalFileName: resolvedFileName,
|
||||
resolvedFileName,
|
||||
isExternalLibraryImport: tsResolution.resolvedModule.isExternalLibraryImport
|
||||
};
|
||||
return resolutionResult === undefined ||
|
||||
resolutionResult.resolvedFileName ===
|
||||
tsResolutionResult.resolvedFileName ||
|
||||
isJsImplementationOfTypings(resolutionResult, tsResolutionResult)
|
||||
? tsResolutionResult
|
||||
: resolutionResult;
|
||||
}
|
||||
return resolutionResult;
|
||||
}
|
||||
function makeResolveModuleName(compiler, compilerOptions, moduleResolutionHost, customResolveModuleName) {
|
||||
if (customResolveModuleName === undefined) {
|
||||
return (moduleName, containingFile) => compiler.resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost);
|
||||
}
|
||||
return (moduleName, containingFile) => customResolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost, compiler.resolveModuleName);
|
||||
}
|
||||
function populateDependencyGraphs(resolvedModules, instance, containingFile) {
|
||||
resolvedModules = resolvedModules.filter(mod => mod !== null && mod !== undefined);
|
||||
instance.dependencyGraph[path.normalize(containingFile)] = resolvedModules;
|
||||
resolvedModules.forEach(resolvedModule => {
|
||||
if (instance.reverseDependencyGraph[resolvedModule.resolvedFileName] ===
|
||||
undefined) {
|
||||
instance.reverseDependencyGraph[resolvedModule.resolvedFileName] = {};
|
||||
}
|
||||
instance.reverseDependencyGraph[resolvedModule.resolvedFileName][path.normalize(containingFile)] = true;
|
||||
});
|
||||
}
|
||||
const cacheableFunctions = [
|
||||
'fileExists',
|
||||
'directoryExists',
|
||||
'realpath'
|
||||
];
|
||||
function addCache(servicesHost) {
|
||||
const clearCacheFunctions = [];
|
||||
cacheableFunctions.forEach((functionToCache) => {
|
||||
const originalFunction = servicesHost[functionToCache];
|
||||
if (originalFunction !== undefined) {
|
||||
const cache = createCache(originalFunction);
|
||||
servicesHost[functionToCache] = cache.getCached;
|
||||
clearCacheFunctions.push(cache.clear);
|
||||
}
|
||||
});
|
||||
return () => clearCacheFunctions.forEach(clear => clear());
|
||||
}
|
||||
function createCache(originalFunction) {
|
||||
const cache = new Map();
|
||||
return {
|
||||
clear: () => {
|
||||
cache.clear();
|
||||
},
|
||||
getCached: (arg) => {
|
||||
let res = cache.get(arg);
|
||||
if (res !== undefined) {
|
||||
return res;
|
||||
}
|
||||
res = originalFunction(arg);
|
||||
cache.set(arg, res);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import * as webpack from 'webpack';
|
||||
import { TSInstance } from './interfaces';
|
||||
export declare function makeAfterCompile(instance: TSInstance, configFilePath: string | undefined): (compilation: webpack.compilation.Compilation, callback: () => void) => void;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import * as typescript from 'typescript';
|
||||
import { LoaderOptions } from './interfaces';
|
||||
import * as logger from './logger';
|
||||
export declare function getCompiler(loaderOptions: LoaderOptions, log: logger.Logger): {
|
||||
compiler: typeof typescript | undefined;
|
||||
compilerCompatible: boolean;
|
||||
compilerDetailsLogMessage: string | undefined;
|
||||
errorMessage: string | undefined;
|
||||
};
|
||||
export declare function getCompilerOptions(configParseResult: typescript.ParsedCommandLine): typescript.CompilerOptions;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { Chalk } from 'chalk';
|
||||
import * as typescript from 'typescript';
|
||||
import * as webpack from 'webpack';
|
||||
import { LoaderOptions, WebpackError } from './interfaces';
|
||||
import * as logger from './logger';
|
||||
interface ConfigFile {
|
||||
config?: any;
|
||||
error?: typescript.Diagnostic;
|
||||
}
|
||||
export declare function getConfigFile(compiler: typeof typescript, colors: Chalk, loader: webpack.loader.LoaderContext, loaderOptions: LoaderOptions, compilerCompatible: boolean, log: logger.Logger, compilerDetailsLogMessage: string): {
|
||||
configFilePath: string | undefined;
|
||||
configFile: ConfigFile;
|
||||
configFileError: WebpackError | undefined;
|
||||
};
|
||||
export declare function getConfigParseResult(compiler: typeof typescript, configFile: ConfigFile, basePath: string): typescript.ParsedCommandLine;
|
||||
export {};
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
export declare const EOL: string;
|
||||
export declare const CarriageReturnLineFeed = "\r\n";
|
||||
export declare const LineFeed = "\n";
|
||||
export declare const CarriageReturnLineFeedCode = 0;
|
||||
export declare const LineFeedCode = 1;
|
||||
export declare const extensionRegex: RegExp;
|
||||
export declare const tsxRegex: RegExp;
|
||||
export declare const tsTsxRegex: RegExp;
|
||||
export declare const dtsDtsxOrDtsDtsxMapRegex: RegExp;
|
||||
export declare const dtsTsTsxRegex: RegExp;
|
||||
export declare const dtsTsTsxJsJsxRegex: RegExp;
|
||||
export declare const tsTsxJsJsxRegex: RegExp;
|
||||
export declare const jsJsx: RegExp;
|
||||
export declare const jsJsxMap: RegExp;
|
||||
export declare const jsonRegex: RegExp;
|
||||
export declare const nodeModules: RegExp;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import * as webpack from 'webpack';
|
||||
import { LoaderOptions } from './interfaces';
|
||||
/**
|
||||
* The entry point for ts-loader
|
||||
*/
|
||||
declare function loader(this: webpack.loader.LoaderContext, contents: string): void;
|
||||
export = loader;
|
||||
/**
|
||||
* expose public types via declaration merging
|
||||
*/
|
||||
declare namespace loader {
|
||||
interface Options extends LoaderOptions {
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import * as typescript from 'typescript';
|
||||
import * as webpack from 'webpack';
|
||||
import { LoaderOptions, TSInstance, WebpackError } from './interfaces';
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export declare function getTypeScriptInstance(loaderOptions: LoaderOptions, loader: webpack.loader.LoaderContext): {
|
||||
instance?: TSInstance;
|
||||
error?: WebpackError;
|
||||
};
|
||||
export declare function getEmitOutput(instance: TSInstance, filePath: string): typescript.OutputFile[];
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
export { ModuleResolutionHost } from 'typescript';
|
||||
import * as typescript from 'typescript';
|
||||
import { Chalk } from 'chalk';
|
||||
export interface ErrorInfo {
|
||||
code: number;
|
||||
severity: Severity;
|
||||
content: string;
|
||||
file: string;
|
||||
line: number;
|
||||
character: number;
|
||||
context: string;
|
||||
}
|
||||
export declare type FileLocation = {
|
||||
line: number;
|
||||
character: number;
|
||||
};
|
||||
export interface WebpackError {
|
||||
module?: any;
|
||||
file?: string;
|
||||
message: string;
|
||||
location?: FileLocation;
|
||||
loaderSource: string;
|
||||
}
|
||||
export interface WebpackModule {
|
||||
resource: string;
|
||||
errors: WebpackError[];
|
||||
buildMeta: {
|
||||
tsLoaderFileVersion: number;
|
||||
tsLoaderDefinitionFileVersions: string[];
|
||||
};
|
||||
}
|
||||
export declare type ResolveSync = (context: string | undefined, path: string, moduleName: string) => string;
|
||||
export interface WatchHost extends typescript.WatchCompilerHostOfFilesAndCompilerOptions<typescript.BuilderProgram> {
|
||||
invokeFileWatcher(fileName: string, eventKind: typescript.FileWatcherEventKind): void;
|
||||
invokeDirectoryWatcher(directory: string, fileAddedOrRemoved: string): void;
|
||||
updateRootFileNames(): void;
|
||||
}
|
||||
export interface TSInstance {
|
||||
compiler: typeof typescript;
|
||||
compilerOptions: typescript.CompilerOptions;
|
||||
/** Used for Vue for the most part */
|
||||
appendTsTsxSuffixesIfRequired: (filePath: string) => string;
|
||||
loaderOptions: LoaderOptions;
|
||||
/**
|
||||
* a cache of all the files
|
||||
*/
|
||||
files: TSFiles;
|
||||
/**
|
||||
* contains the modified files - cleared each time after-compile is called
|
||||
*/
|
||||
modifiedFiles?: TSFiles | null;
|
||||
/**
|
||||
* Paths to project references that are missing source maps.
|
||||
* Cleared each time after-compile is called. Used to dedupe
|
||||
* warnings about source maps during a single compilation.
|
||||
*/
|
||||
projectsMissingSourceMaps?: Set<string>;
|
||||
languageService?: typescript.LanguageService | null;
|
||||
version?: number;
|
||||
dependencyGraph: DependencyGraph;
|
||||
reverseDependencyGraph: ReverseDependencyGraph;
|
||||
filesWithErrors?: TSFiles;
|
||||
transformers: typescript.CustomTransformers;
|
||||
colors: Chalk;
|
||||
otherFiles: TSFiles;
|
||||
watchHost?: WatchHost;
|
||||
watchOfFilesAndCompilerOptions?: typescript.WatchOfFilesAndCompilerOptions<typescript.BuilderProgram>;
|
||||
program?: typescript.Program;
|
||||
hasUnaccountedModifiedFiles?: boolean;
|
||||
changedFilesList?: boolean;
|
||||
}
|
||||
export interface LoaderOptionsCache {
|
||||
[name: string]: WeakMap<LoaderOptions, LoaderOptions>;
|
||||
}
|
||||
export interface TSInstances {
|
||||
[name: string]: TSInstance;
|
||||
}
|
||||
export interface DependencyGraph {
|
||||
[file: string]: ResolvedModule[] | undefined;
|
||||
}
|
||||
export interface ReverseDependencyGraph {
|
||||
[file: string]: {
|
||||
[file: string]: boolean;
|
||||
} | undefined;
|
||||
}
|
||||
export declare type LogLevel = 'INFO' | 'WARN' | 'ERROR';
|
||||
export declare type ResolveModuleName = (moduleName: string, containingFile: string, compilerOptions: typescript.CompilerOptions, moduleResolutionHost: typescript.ModuleResolutionHost) => typescript.ResolvedModuleWithFailedLookupLocations;
|
||||
export declare type CustomResolveModuleName = (moduleName: string, containingFile: string, compilerOptions: typescript.CompilerOptions, moduleResolutionHost: typescript.ModuleResolutionHost, parentResolver: ResolveModuleName) => typescript.ResolvedModuleWithFailedLookupLocations;
|
||||
export declare type CustomResolveTypeReferenceDirective = (typeDirectiveName: string, containingFile: string, compilerOptions: typescript.CompilerOptions, moduleResolutionHost: typescript.ModuleResolutionHost, parentResolver: typeof typescript.resolveTypeReferenceDirective) => typescript.ResolvedTypeReferenceDirectiveWithFailedLookupLocations;
|
||||
export interface LoaderOptions {
|
||||
silent: boolean;
|
||||
logLevel: LogLevel;
|
||||
logInfoToStdOut: boolean;
|
||||
instance: string;
|
||||
compiler: string;
|
||||
configFile: string;
|
||||
context: string;
|
||||
transpileOnly: boolean;
|
||||
ignoreDiagnostics: number[];
|
||||
reportFiles: string[];
|
||||
errorFormatter: (message: ErrorInfo, colors: Chalk) => string;
|
||||
onlyCompileBundledFiles: boolean;
|
||||
colors: boolean;
|
||||
compilerOptions: typescript.CompilerOptions;
|
||||
appendTsSuffixTo: RegExp[];
|
||||
appendTsxSuffixTo: RegExp[];
|
||||
happyPackMode: boolean;
|
||||
getCustomTransformers: string | ((program: typescript.Program) => typescript.CustomTransformers | undefined);
|
||||
experimentalWatchApi: boolean;
|
||||
allowTsInNodeModules: boolean;
|
||||
experimentalFileCaching: boolean;
|
||||
projectReferences: boolean;
|
||||
resolveModuleName: CustomResolveModuleName;
|
||||
resolveTypeReferenceDirective: CustomResolveTypeReferenceDirective;
|
||||
}
|
||||
export interface TSFile {
|
||||
text?: string;
|
||||
version: number;
|
||||
projectReference?: {
|
||||
/**
|
||||
* Undefined here means we’ve already checked and confirmed there is no
|
||||
* project reference for the file. Don’t bother checking again.
|
||||
*/
|
||||
project?: typescript.ResolvedProjectReference;
|
||||
outputFileName?: string;
|
||||
};
|
||||
}
|
||||
/** where key is filepath */
|
||||
export declare type TSFiles = Map<string, TSFile>;
|
||||
export interface ResolvedModule {
|
||||
originalFileName: string;
|
||||
resolvedFileName: string;
|
||||
resolvedModule?: ResolvedModule;
|
||||
isExternalLibraryImport?: boolean;
|
||||
}
|
||||
export declare type Severity = 'error' | 'warning';
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { Chalk } from 'chalk';
|
||||
import { LoaderOptions } from './interfaces';
|
||||
declare type LoggerFunc = (message: string) => void;
|
||||
export interface Logger {
|
||||
log: LoggerFunc;
|
||||
logInfo: LoggerFunc;
|
||||
logWarning: LoggerFunc;
|
||||
logError: LoggerFunc;
|
||||
}
|
||||
export declare enum LogLevel {
|
||||
INFO = 1,
|
||||
WARN = 2,
|
||||
ERROR = 3
|
||||
}
|
||||
export declare function makeLogger(loaderOptions: LoaderOptions, colors: Chalk): Logger;
|
||||
export {};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import * as webpack from 'webpack';
|
||||
import { ResolveSync } from './interfaces';
|
||||
export declare function makeResolver(options: webpack.Configuration): ResolveSync;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import * as typescript from 'typescript';
|
||||
import * as webpack from 'webpack';
|
||||
import { TSInstance, WatchHost } from './interfaces';
|
||||
import * as logger from './logger';
|
||||
export declare type Action = () => void;
|
||||
export interface ServiceHostWhichMayBeCacheable {
|
||||
servicesHost: typescript.LanguageServiceHost;
|
||||
clearCache: Action | null;
|
||||
}
|
||||
/**
|
||||
* Create the TypeScript language service
|
||||
*/
|
||||
export declare function makeServicesHost(scriptRegex: RegExp, log: logger.Logger, loader: webpack.loader.LoaderContext, instance: TSInstance, enableFileCaching: boolean, projectReferences?: ReadonlyArray<typescript.ProjectReference>): ServiceHostWhichMayBeCacheable;
|
||||
/**
|
||||
* Create the TypeScript Watch host
|
||||
*/
|
||||
export declare function makeWatchHost(scriptRegex: RegExp, log: logger.Logger, loader: webpack.loader.LoaderContext, instance: TSInstance, projectReferences?: ReadonlyArray<typescript.ProjectReference>): WatchHost;
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { Chalk } from 'chalk';
|
||||
import * as typescript from 'typescript';
|
||||
import * as webpack from 'webpack';
|
||||
import { DependencyGraph, LoaderOptions, ReverseDependencyGraph, TSInstance, WebpackError, WebpackModule } from './interfaces';
|
||||
/**
|
||||
* Take TypeScript errors, parse them and format to webpack errors
|
||||
* Optionally adds a file name
|
||||
*/
|
||||
export declare function formatErrors(diagnostics: ReadonlyArray<typescript.Diagnostic> | undefined, loaderOptions: LoaderOptions, colors: Chalk, compiler: typeof typescript, merge: {
|
||||
file?: string;
|
||||
module?: WebpackModule;
|
||||
}, context: string): WebpackError[];
|
||||
export declare function readFile(fileName: string, encoding?: string | undefined): string | undefined;
|
||||
export declare function makeError(message: string, file: string | undefined, location?: {
|
||||
line: number;
|
||||
character: number;
|
||||
}): WebpackError;
|
||||
export declare function appendSuffixIfMatch(patterns: RegExp[], filePath: string, suffix: string): string;
|
||||
export declare function appendSuffixesIfMatch(suffixDict: {
|
||||
[suffix: string]: RegExp[];
|
||||
}, filePath: string): string;
|
||||
export declare function unorderedRemoveItem<T>(array: T[], item: T): boolean;
|
||||
/**
|
||||
* Recursively collect all possible dependants of passed file
|
||||
*/
|
||||
export declare function collectAllDependants(reverseDependencyGraph: ReverseDependencyGraph, fileName: string, collected?: {
|
||||
[file: string]: boolean;
|
||||
}): string[];
|
||||
/**
|
||||
* Recursively collect all possible dependencies of passed file
|
||||
*/
|
||||
export declare function collectAllDependencies(dependencyGraph: DependencyGraph, filePath: string, collected?: {
|
||||
[file: string]: boolean;
|
||||
}): string[];
|
||||
export declare function arrify<T>(val: T | T[]): T[];
|
||||
export declare function ensureProgram(instance: TSInstance): typescript.Program | undefined;
|
||||
export declare function supportsProjectReferences(instance: TSInstance): true | undefined;
|
||||
export declare function isUsingProjectReferences(instance: TSInstance): boolean;
|
||||
/**
|
||||
* Gets the project reference for a file from the cache if it exists,
|
||||
* or gets it from TypeScript and caches it otherwise.
|
||||
*/
|
||||
export declare function getAndCacheProjectReference(filePath: string, instance: TSInstance): typescript.ResolvedProjectReference | undefined;
|
||||
export declare function validateSourceMapOncePerProject(instance: TSInstance, loader: webpack.loader.LoaderContext, jsFileName: string, project: typescript.ResolvedProjectReference): void;
|
||||
/**
|
||||
* Gets the output JS file path for an input file governed by a composite project.
|
||||
* Pulls from the cache if it exists; computes and caches the result otherwise.
|
||||
*/
|
||||
export declare function getAndCacheOutputJSFileName(inputFileName: string, projectReference: typescript.ResolvedProjectReference, instance: TSInstance): string;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import * as webpack from 'webpack';
|
||||
import { TSInstance } from './interfaces';
|
||||
/**
|
||||
* Make function which will manually update changed files
|
||||
*/
|
||||
export declare function makeWatchRun(instance: TSInstance): (compiler: webpack.Compiler, callback: () => void) => void;
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = require("fs");
|
||||
const micromatch = require("micromatch");
|
||||
const path = require("path");
|
||||
const typescript = require("typescript");
|
||||
const constants = require("./constants");
|
||||
/**
|
||||
* The default error formatter.
|
||||
*/
|
||||
function defaultErrorFormatter(error, colors) {
|
||||
const messageColor = error.severity === 'warning' ? colors.bold.yellow : colors.bold.red;
|
||||
return (colors.grey('[tsl] ') +
|
||||
messageColor(error.severity.toUpperCase()) +
|
||||
(error.file === ''
|
||||
? ''
|
||||
: messageColor(' in ') +
|
||||
colors.bold.cyan(`${error.file}(${error.line},${error.character})`)) +
|
||||
constants.EOL +
|
||||
messageColor(` TS${error.code}: ${error.content}`));
|
||||
}
|
||||
/**
|
||||
* Take TypeScript errors, parse them and format to webpack errors
|
||||
* Optionally adds a file name
|
||||
*/
|
||||
function formatErrors(diagnostics, loaderOptions, colors, compiler, merge, context) {
|
||||
return diagnostics === undefined
|
||||
? []
|
||||
: diagnostics
|
||||
.filter(diagnostic => {
|
||||
if (loaderOptions.ignoreDiagnostics.indexOf(diagnostic.code) !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (loaderOptions.reportFiles.length > 0 &&
|
||||
diagnostic.file !== undefined) {
|
||||
const relativeFileName = path.relative(context, diagnostic.file.fileName);
|
||||
const matchResult = micromatch([relativeFileName], loaderOptions.reportFiles);
|
||||
if (matchResult.length === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(diagnostic => {
|
||||
const file = diagnostic.file;
|
||||
const position = file === undefined
|
||||
? undefined
|
||||
: file.getLineAndCharacterOfPosition(diagnostic.start);
|
||||
const errorInfo = {
|
||||
code: diagnostic.code,
|
||||
severity: compiler.DiagnosticCategory[diagnostic.category].toLowerCase(),
|
||||
content: compiler.flattenDiagnosticMessageText(diagnostic.messageText, constants.EOL),
|
||||
file: file === undefined ? '' : path.normalize(file.fileName),
|
||||
line: position === undefined ? 0 : position.line + 1,
|
||||
character: position === undefined ? 0 : position.character + 1,
|
||||
context
|
||||
};
|
||||
const message = loaderOptions.errorFormatter === undefined
|
||||
? defaultErrorFormatter(errorInfo, colors)
|
||||
: loaderOptions.errorFormatter(errorInfo, colors);
|
||||
const error = makeError(message, merge.file === undefined ? errorInfo.file : merge.file, position === undefined
|
||||
? undefined
|
||||
: { line: errorInfo.line, character: errorInfo.character });
|
||||
return Object.assign(error, merge);
|
||||
});
|
||||
}
|
||||
exports.formatErrors = formatErrors;
|
||||
function readFile(fileName, encoding = 'utf8') {
|
||||
fileName = path.normalize(fileName);
|
||||
try {
|
||||
return fs.readFileSync(fileName, encoding);
|
||||
}
|
||||
catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
exports.readFile = readFile;
|
||||
function makeError(message, file, location) {
|
||||
return {
|
||||
message,
|
||||
location,
|
||||
file,
|
||||
loaderSource: 'ts-loader'
|
||||
};
|
||||
}
|
||||
exports.makeError = makeError;
|
||||
function appendSuffixIfMatch(patterns, filePath, suffix) {
|
||||
if (patterns.length > 0) {
|
||||
for (const regexp of patterns) {
|
||||
if (filePath.match(regexp) !== null) {
|
||||
return filePath + suffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
exports.appendSuffixIfMatch = appendSuffixIfMatch;
|
||||
function appendSuffixesIfMatch(suffixDict, filePath) {
|
||||
let amendedPath = filePath;
|
||||
for (const suffix in suffixDict) {
|
||||
amendedPath = appendSuffixIfMatch(suffixDict[suffix], amendedPath, suffix);
|
||||
}
|
||||
return amendedPath;
|
||||
}
|
||||
exports.appendSuffixesIfMatch = appendSuffixesIfMatch;
|
||||
function unorderedRemoveItem(array, item) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (array[i] === item) {
|
||||
// Fill in the "hole" left at `index`.
|
||||
array[i] = array[array.length - 1];
|
||||
array.pop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
exports.unorderedRemoveItem = unorderedRemoveItem;
|
||||
/**
|
||||
* Recursively collect all possible dependants of passed file
|
||||
*/
|
||||
function collectAllDependants(reverseDependencyGraph, fileName, collected = {}) {
|
||||
const result = {};
|
||||
result[fileName] = true;
|
||||
collected[fileName] = true;
|
||||
const dependants = reverseDependencyGraph[fileName];
|
||||
if (dependants !== undefined) {
|
||||
Object.keys(dependants).forEach(dependantFileName => {
|
||||
if (!collected[dependantFileName]) {
|
||||
collectAllDependants(reverseDependencyGraph, dependantFileName, collected).forEach(fName => (result[fName] = true));
|
||||
}
|
||||
});
|
||||
}
|
||||
return Object.keys(result);
|
||||
}
|
||||
exports.collectAllDependants = collectAllDependants;
|
||||
/**
|
||||
* Recursively collect all possible dependencies of passed file
|
||||
*/
|
||||
function collectAllDependencies(dependencyGraph, filePath, collected = {}) {
|
||||
const result = {};
|
||||
result[filePath] = true;
|
||||
collected[filePath] = true;
|
||||
const directDependencies = dependencyGraph[filePath];
|
||||
if (directDependencies !== undefined) {
|
||||
directDependencies.forEach(dependencyModule => {
|
||||
if (!collected[dependencyModule.originalFileName]) {
|
||||
collectAllDependencies(dependencyGraph, dependencyModule.resolvedFileName, collected).forEach(depFilePath => (result[depFilePath] = true));
|
||||
}
|
||||
});
|
||||
}
|
||||
return Object.keys(result);
|
||||
}
|
||||
exports.collectAllDependencies = collectAllDependencies;
|
||||
function arrify(val) {
|
||||
if (val === null || val === undefined) {
|
||||
return [];
|
||||
}
|
||||
return Array.isArray(val) ? val : [val];
|
||||
}
|
||||
exports.arrify = arrify;
|
||||
function ensureProgram(instance) {
|
||||
if (instance && instance.watchHost) {
|
||||
if (instance.hasUnaccountedModifiedFiles) {
|
||||
if (instance.changedFilesList) {
|
||||
instance.watchHost.updateRootFileNames();
|
||||
}
|
||||
if (instance.watchOfFilesAndCompilerOptions) {
|
||||
instance.program = instance.watchOfFilesAndCompilerOptions
|
||||
.getProgram()
|
||||
.getProgram();
|
||||
}
|
||||
instance.hasUnaccountedModifiedFiles = false;
|
||||
}
|
||||
return instance.program;
|
||||
}
|
||||
if (instance.languageService) {
|
||||
return instance.languageService.getProgram();
|
||||
}
|
||||
return instance.program;
|
||||
}
|
||||
exports.ensureProgram = ensureProgram;
|
||||
function supportsProjectReferences(instance) {
|
||||
const program = ensureProgram(instance);
|
||||
return program && !!program.getProjectReferences;
|
||||
}
|
||||
exports.supportsProjectReferences = supportsProjectReferences;
|
||||
function isUsingProjectReferences(instance) {
|
||||
if (instance.loaderOptions.projectReferences &&
|
||||
supportsProjectReferences(instance)) {
|
||||
const program = ensureProgram(instance);
|
||||
return Boolean(program && program.getProjectReferences());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
exports.isUsingProjectReferences = isUsingProjectReferences;
|
||||
/**
|
||||
* Gets the project reference for a file from the cache if it exists,
|
||||
* or gets it from TypeScript and caches it otherwise.
|
||||
*/
|
||||
function getAndCacheProjectReference(filePath, instance) {
|
||||
const file = instance.files.get(filePath);
|
||||
if (file !== undefined && file.projectReference) {
|
||||
return file.projectReference.project;
|
||||
}
|
||||
const projectReference = getProjectReferenceForFile(filePath, instance);
|
||||
if (file !== undefined) {
|
||||
file.projectReference = { project: projectReference };
|
||||
}
|
||||
return projectReference;
|
||||
}
|
||||
exports.getAndCacheProjectReference = getAndCacheProjectReference;
|
||||
function getResolvedProjectReferences(program) {
|
||||
const getProjectReferences = program.getResolvedProjectReferences ||
|
||||
program.getProjectReferences;
|
||||
if (getProjectReferences) {
|
||||
return getProjectReferences();
|
||||
}
|
||||
return;
|
||||
}
|
||||
function getProjectReferenceForFile(filePath, instance) {
|
||||
if (isUsingProjectReferences(instance)) {
|
||||
const program = ensureProgram(instance);
|
||||
return (program &&
|
||||
getResolvedProjectReferences(program).find(ref => (ref &&
|
||||
ref.commandLine.fileNames.some(file => path.normalize(file) === filePath)) ||
|
||||
false));
|
||||
}
|
||||
return;
|
||||
}
|
||||
function validateSourceMapOncePerProject(instance, loader, jsFileName, project) {
|
||||
const { projectsMissingSourceMaps = new Set() } = instance;
|
||||
if (!projectsMissingSourceMaps.has(project.sourceFile.fileName)) {
|
||||
instance.projectsMissingSourceMaps = projectsMissingSourceMaps;
|
||||
projectsMissingSourceMaps.add(project.sourceFile.fileName);
|
||||
const mapFileName = jsFileName + '.map';
|
||||
if (!instance.compiler.sys.fileExists(mapFileName)) {
|
||||
const [relativeJSPath, relativeProjectConfigPath] = [
|
||||
path.relative(loader.rootContext, jsFileName),
|
||||
path.relative(loader.rootContext, project.sourceFile.fileName)
|
||||
];
|
||||
loader.emitWarning(new Error('Could not find source map file for referenced project output ' +
|
||||
`${relativeJSPath}. Ensure the 'sourceMap' compiler option ` +
|
||||
`is enabled in ${relativeProjectConfigPath} to ensure Webpack ` +
|
||||
'can map project references to the appropriate source files.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.validateSourceMapOncePerProject = validateSourceMapOncePerProject;
|
||||
/**
|
||||
* Gets the output JS file path for an input file governed by a composite project.
|
||||
* Pulls from the cache if it exists; computes and caches the result otherwise.
|
||||
*/
|
||||
function getAndCacheOutputJSFileName(inputFileName, projectReference, instance) {
|
||||
const file = instance.files.get(inputFileName);
|
||||
if (file && file.projectReference && file.projectReference.outputFileName) {
|
||||
return file.projectReference.outputFileName;
|
||||
}
|
||||
const outputFileName = getOutputJavaScriptFileName(inputFileName, projectReference);
|
||||
if (file !== undefined) {
|
||||
file.projectReference = file.projectReference || {
|
||||
project: projectReference
|
||||
};
|
||||
file.projectReference.outputFileName = outputFileName;
|
||||
}
|
||||
return outputFileName;
|
||||
}
|
||||
exports.getAndCacheOutputJSFileName = getAndCacheOutputJSFileName;
|
||||
// Adapted from https://github.com/Microsoft/TypeScript/blob/45101491c0b077c509b25830ef0ee5f85b293754/src/compiler/tsbuild.ts#L305
|
||||
function getOutputJavaScriptFileName(inputFileName, projectReference) {
|
||||
const { options } = projectReference.commandLine;
|
||||
const projectDirectory = options.rootDir || path.dirname(projectReference.sourceFile.fileName);
|
||||
const relativePath = path.relative(projectDirectory, inputFileName);
|
||||
const outputPath = path.resolve(options.outDir || projectDirectory, relativePath);
|
||||
const newExtension = constants.jsonRegex.test(inputFileName)
|
||||
? '.json'
|
||||
: constants.tsxRegex.test(inputFileName) &&
|
||||
options.jsx === typescript.JsxEmit.Preserve
|
||||
? '.jsx'
|
||||
: '.js';
|
||||
return outputPath.replace(constants.extensionRegex, newExtension);
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const path = require("path");
|
||||
const constants = require("./constants");
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* Make function which will manually update changed files
|
||||
*/
|
||||
function makeWatchRun(instance) {
|
||||
// Called Before starting compilation after watch
|
||||
const lastTimes = new Map();
|
||||
const startTime = 0;
|
||||
return (compiler, callback) => {
|
||||
if (null === instance.modifiedFiles) {
|
||||
instance.modifiedFiles = new Map();
|
||||
}
|
||||
// startTime = startTime || watching.startTime;
|
||||
const times = compiler.fileTimestamps;
|
||||
for (const [filePath, date] of times) {
|
||||
if (date > (lastTimes.get(filePath) || startTime) &&
|
||||
filePath.match(constants.tsTsxJsJsxRegex) !== null) {
|
||||
continue;
|
||||
}
|
||||
lastTimes.set(filePath, date);
|
||||
updateFile(instance, filePath);
|
||||
}
|
||||
// On watch update add all known dts files expect the ones in node_modules
|
||||
// (skip @types/* and modules with typings)
|
||||
for (const filePath of instance.files.keys()) {
|
||||
if (filePath.match(constants.dtsDtsxOrDtsDtsxMapRegex) !== null &&
|
||||
filePath.match(constants.nodeModules) === null) {
|
||||
updateFile(instance, filePath);
|
||||
}
|
||||
}
|
||||
callback();
|
||||
};
|
||||
}
|
||||
exports.makeWatchRun = makeWatchRun;
|
||||
function updateFile(instance, filePath) {
|
||||
const nFilePath = path.normalize(filePath);
|
||||
const file = instance.files.get(nFilePath) || instance.otherFiles.get(nFilePath);
|
||||
if (file !== undefined) {
|
||||
file.text = utils_1.readFile(nFilePath) || '';
|
||||
file.version++;
|
||||
instance.version++;
|
||||
instance.modifiedFiles.set(nFilePath, file);
|
||||
if (instance.watchHost !== undefined) {
|
||||
instance.watchHost.invokeFileWatcher(nFilePath, instance.compiler.FileWatcherEventKind.Changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user