diff --git a/examples/fork-ts-checker-webpack-plugin/yarn.lock b/examples/fork-ts-checker-webpack-plugin/yarn.lock index cb11ecfcc..b898b148f 100644 --- a/examples/fork-ts-checker-webpack-plugin/yarn.lock +++ b/examples/fork-ts-checker-webpack-plugin/yarn.lock @@ -2293,9 +2293,9 @@ js-tokens@^3.0.2: integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= js-yaml@^3.7.0: - version "3.12.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" - integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" esprima "^4.0.0" diff --git a/src/config.ts b/src/config.ts index 7cb78e806..e30e97460 100644 --- a/src/config.ts +++ b/src/config.ts @@ -6,7 +6,7 @@ import * as webpack from 'webpack'; import { ConfigFile, LoaderOptions, WebpackError } from './interfaces'; import * as logger from './logger'; -import { formatErrors } from './utils'; +import { formatErrors, getPossiblyRenamedFilePath } from './utils'; export function getConfigFile( compiler: typeof typescript, @@ -122,11 +122,12 @@ export function getConfigParseResult( compiler: typeof typescript, configFile: ConfigFile, basePath: string, - configFilePath: string | undefined + configFilePath: string | undefined, + loaderOptions: LoaderOptions ) { const configParseResult = compiler.parseJsonConfigFileContent( configFile.config, - compiler.sys, + getSuffixAppendingParseConfigHost(compiler.sys, loaderOptions), basePath ); @@ -139,3 +140,49 @@ export function getConfigParseResult( return configParseResult; } + +function getSuffixAppendingParseConfigHost( + sys: typescript.System, + loaderOptions: LoaderOptions +): typescript.ParseConfigHost { + if ( + !loaderOptions.appendTsSuffixTo.length && + !loaderOptions.appendTsxSuffixTo.length + ) { + return sys; + } + + return { + useCaseSensitiveFileNames: sys.useCaseSensitiveFileNames, + fileExists: mapArg(sys.fileExists, transformFileName), + readFile: mapArg(sys.readFile, transformFileName), + readDirectory: (rootDir, extensions, ...rest) => { + const allFiles = sys.readDirectory(rootDir); + const customExtensions = allFiles.reduce((exts: string[], fileName) => { + const renamed = transformFileName(fileName); + if ( + renamed !== fileName && + extensions.indexOf(path.extname(renamed).toLowerCase()) > -1 + ) { + const ext = path.extname(fileName).toLowerCase(); + if (ext) { + exts.push(ext); + } + } + return exts; + }, []); + + return sys + .readDirectory(rootDir, extensions.concat(customExtensions), ...rest) + .map(transformFileName); + } + }; + + function transformFileName(rawFileName: string) { + return getPossiblyRenamedFilePath(rawFileName, loaderOptions); + } +} + +function mapArg(toWrap: (x: T) => U, wrapWith: (x: T) => T): (x: T) => U { + return x => toWrap(wrapWith(x)); +} diff --git a/src/index.ts b/src/index.ts index 5e189d3b5..1811a9672 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,11 +18,12 @@ import { TSInstance } from './interfaces'; import { - appendSuffixesIfMatch, arrify, formatErrors, getAndCacheOutputJSFileName, getAndCacheProjectReference, + getPossiblyRenamedFilePath, + isRootFileOrExempt, validateSourceMapOncePerProject } from './utils'; @@ -62,35 +63,15 @@ function successLoader( ) { const rawFilePath = path.normalize(loaderContext.resourcePath); - const filePath = - options.appendTsSuffixTo.length > 0 || options.appendTsxSuffixTo.length > 0 - ? appendSuffixesIfMatch( - { - '.ts': options.appendTsSuffixTo, - '.tsx': options.appendTsxSuffixTo - }, - rawFilePath - ) - : rawFilePath; + const filePath = getPossiblyRenamedFilePath(rawFilePath, options); const { version: fileVersion, changedFilesList } = updateFileInCache( filePath, contents, - instance + instance, + options ); - if (changedFilesList && !instance.rootFileNames.has(filePath)) { - reloadRootFileNamesFromConfig(loaderContext, instance); - if ( - !instance.rootFileNames.has(filePath) && - !options.onlyCompileBundledFiles - ) { - throw new Error( - `The file ${filePath} is not part of the project specified in tsconfig.json. Make sure it is covered by the fields 'include', 'exclude' and 'files'.` - ); - } - } - const referencedProject = getAndCacheProjectReference(filePath, instance); if (referencedProject !== undefined) { const [relativeProjectConfigPath, relativeFilePath] = [ @@ -155,6 +136,18 @@ function successLoader( callback ); } else { + if (changedFilesList && !isRootFileOrExempt(filePath, instance)) { + reloadRootFileNamesFromConfig(loaderContext, instance, options); + if ( + !instance.rootFileNames.has(filePath) && + !options.onlyCompileBundledFiles + ) { + throw new Error( + `The file ${filePath} is not part of the project specified in tsconfig.json. Make sure it is covered by the fields 'include', 'exclude' and 'files'.` + ); + } + } + const { outputText, sourceMapText } = options.transpileOnly ? getTranspilationEmit(filePath, contents, instance, loaderContext) : getEmit(rawFilePath, filePath, instance, loaderContext); @@ -178,28 +171,23 @@ function successLoader( */ function reloadRootFileNamesFromConfig( loaderContext: webpack.loader.LoaderContext, - instance: TSInstance + instance: TSInstance, + options: LoaderOptions ) { - const { - compiler, - basePath, - configFile, - configFilePath, - colors, - loaderOptions - } = instance; + const { compiler, basePath, configFile, configFilePath, colors } = instance; const configParseResult = getConfigParseResult( compiler, configFile, basePath, - configFilePath + configFilePath, + options ); - if (configParseResult.errors.length > 0 && !loaderOptions.happyPackMode) { + if (configParseResult.errors.length > 0 && !options.happyPackMode) { const errors = formatErrors( configParseResult.errors, - loaderOptions, + options, colors, compiler, { file: configFilePath }, @@ -211,7 +199,7 @@ function reloadRootFileNamesFromConfig( throw new Error(colors.red('error while parsing tsconfig.json')); } - updateInstanceRootFileNames(loaderOptions, instance, configParseResult); + updateInstanceRootFileNames(options, instance, configParseResult); return; } @@ -400,7 +388,8 @@ function makeLoaderOptions(instanceName: string, loaderOptions: LoaderOptions) { function updateFileInCache( filePath: string, contents: string, - instance: TSInstance + instance: TSInstance, + options: LoaderOptions ) { let fileWatcherEventKind: typescript.FileWatcherEventKind | undefined; let changedFilesList = false; @@ -432,6 +421,9 @@ function updateFileInCache( // if (!instance.rootFileNames.has(filePath)) { instance.version!++; + if (options.onlyCompileBundledFiles) { + instance.rootFileNames.add(filePath); + } } if (instance.watchHost !== undefined && contents === undefined) { diff --git a/src/instances.ts b/src/instances.ts index 820e83e7a..5401f2bbe 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -22,6 +22,7 @@ import { appendSuffixesIfMatch, ensureProgram, formatErrors, + getOriginalFilePath, isUsingProjectReferences, makeError } from './utils'; @@ -100,7 +101,8 @@ function successfulTypeScriptInstance( compiler, configFile, basePath, - configFilePath + configFilePath, + loaderOptions ); if (configParseResult.errors.length > 0 && !loaderOptions.happyPackMode) { @@ -254,7 +256,10 @@ function successfulTypeScriptInstance( instance.rootFileNames.forEach(filePath => { normalizedFilePath = path.normalize(filePath); files.set(normalizedFilePath, { - text: fs.readFileSync(normalizedFilePath, 'utf-8'), + text: fs.readFileSync( + getOriginalFilePath(normalizedFilePath, loaderOptions), + 'utf-8' + ), version: 0 }); }); diff --git a/src/servicesHost.ts b/src/servicesHost.ts index f54e9d71c..91fa9faf0 100644 --- a/src/servicesHost.ts +++ b/src/servicesHost.ts @@ -96,8 +96,7 @@ export function makeServicesHost( getProjectReferences: () => projectReferences, - getScriptFileNames: () => - [...files.keys()].filter(filePath => filePath.match(scriptRegex)), + getScriptFileNames: () => Array.from(instance.rootFileNames), getScriptVersion: (fileName: string) => { fileName = path.normalize(fileName); @@ -351,7 +350,7 @@ export function makeWatchHost( return watchHost; function getRootFileNames() { - return [...files.keys()].filter(filePath => filePath.match(scriptRegex)); + return Array.from(instance.rootFileNames); } function readFileWithCachingText(fileName: string, encoding?: string) { diff --git a/src/utils.ts b/src/utils.ts index a28318ccb..05d739232 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -136,6 +136,67 @@ export function makeError( }; } +export function getPossiblyRenamedFilePath( + rawFilePath: string, + loaderOptions: LoaderOptions +) { + return loaderOptions.appendTsSuffixTo.length > 0 || + loaderOptions.appendTsxSuffixTo.length > 0 + ? appendSuffixesIfMatch( + { + '.ts': loaderOptions.appendTsSuffixTo, + '.tsx': loaderOptions.appendTsxSuffixTo + }, + rawFilePath + ) + : rawFilePath; +} + +export function getOriginalFilePath( + possiblyRenamedFilePath: string, + loaderOptions: LoaderOptions +) { + if ( + loaderOptions.appendTsSuffixTo.length && + possiblyRenamedFilePath.endsWith(typescript.Extension.Ts) + ) { + const maybeOriginal = stripEnd( + possiblyRenamedFilePath, + typescript.Extension.Ts.length + ); + const renamedAgain = appendSuffixIfMatch( + loaderOptions.appendTsSuffixTo, + maybeOriginal, + typescript.Extension.Ts + ); + if (renamedAgain === possiblyRenamedFilePath) { + return maybeOriginal; + } + } + if ( + loaderOptions.appendTsxSuffixTo.length && + possiblyRenamedFilePath.endsWith(typescript.Extension.Tsx) + ) { + const maybeOriginal = stripEnd( + possiblyRenamedFilePath, + typescript.Extension.Tsx.length + ); + const renamedAgain = appendSuffixIfMatch( + loaderOptions.appendTsxSuffixTo, + maybeOriginal, + typescript.Extension.Tsx + ); + if (renamedAgain === possiblyRenamedFilePath) { + return maybeOriginal; + } + } + return possiblyRenamedFilePath; +} + +export function stripEnd(str: string, lengthToRemove: number) { + return str.slice(0, str.length - lengthToRemove); +} + export function appendSuffixIfMatch( patterns: RegExp[], filePath: string, @@ -255,6 +316,21 @@ export function ensureProgram(instance: TSInstance) { return instance.program; } +export function isRootFileOrExempt( + fileName: string, + instance: TSInstance +): boolean { + // node_modules checking handled separately, later + if ( + instance.rootFileNames.has(fileName) || + fileName.indexOf('node_modules') > -1 + ) { + return true; + } + + return false; +} + export function supportsProjectReferences(instance: TSInstance) { const program = ensureProgram(instance); return program && !!program.getProjectReferences; diff --git a/test/comparison-tests/appendSuffixTo/tsconfig.json b/test/comparison-tests/appendSuffixTo/tsconfig.json index 1db219bc0..b71270932 100644 --- a/test/comparison-tests/appendSuffixTo/tsconfig.json +++ b/test/comparison-tests/appendSuffixTo/tsconfig.json @@ -1,7 +1,4 @@ { "compilerOptions": { - }, - "files": [ - "index.vue.ts" - ] + } } diff --git a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/tsconfig.json b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/tsconfig.json index 0fa41fd51..0967ef424 100644 --- a/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/tsconfig.json +++ b/test/comparison-tests/nodeModulesMeaningfulErrorWhenImportingTs/tsconfig.json @@ -1,8 +1 @@ -{ - "compilerOptions": { - "include": [ - "node_modules", - "app.ts" - ] - } -} +{} diff --git a/test/execution-tests/3.0.1_projectReferences/tsconfig.json b/test/execution-tests/3.0.1_projectReferences/tsconfig.json index 57ce99826..b05bc05cd 100644 --- a/test/execution-tests/3.0.1_projectReferences/tsconfig.json +++ b/test/execution-tests/3.0.1_projectReferences/tsconfig.json @@ -1,6 +1,7 @@ { "files": [ - "./src/app.ts" + "./src/app.ts", + "./test/app.tests.ts" ], "references": [ { "path": "./lib" } diff --git a/test/execution-tests/3.0.1_projectReferences/yarn.lock b/test/execution-tests/3.0.1_projectReferences/yarn.lock index 2bf33ec6c..c83148e75 100644 --- a/test/execution-tests/3.0.1_projectReferences/yarn.lock +++ b/test/execution-tests/3.0.1_projectReferences/yarn.lock @@ -5,7 +5,9 @@ "@types/jasmine@^2.5.35": version "2.8.5" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.5.tgz#96e58872583fa80c7ea0dd29024b180d5e133678" + integrity sha512-mkrHFZTgOXkZhau36K628iKFkjbp11t/bHCkY4Mefu4R6McMg2FD9P3naBv/0Ygyn4sz8baColJp2gdmSekgiw== jasmine-core@^2.3.4: version "2.9.1" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.9.1.tgz#b6bbc1d8e65250d56f5888461705ebeeeb88f22f" + integrity sha1-trvB2OZSUNVvWIhGFwXr7uuI8i8= diff --git a/test/execution-tests/allowTsInNodeModules/tsconfig.json b/test/execution-tests/allowTsInNodeModules/tsconfig.json index 5d1fcc937..f1ef41bdc 100644 --- a/test/execution-tests/allowTsInNodeModules/tsconfig.json +++ b/test/execution-tests/allowTsInNodeModules/tsconfig.json @@ -3,9 +3,9 @@ "noEmitOnError": true }, "include": [ - "./node_modules/whitelistedModule" - ], - "files": [ - "./node_modules/whitelistedFiles/file.ts" + "./node_modules/whitelistedModule", + "./node_modules/whitelistedFiles", + "./src", + "./test" ] } \ No newline at end of file diff --git a/test/execution-tests/allowTsInNodeModules/yarn.lock b/test/execution-tests/allowTsInNodeModules/yarn.lock index a8e06fe04..74f4532c9 100644 --- a/test/execution-tests/allowTsInNodeModules/yarn.lock +++ b/test/execution-tests/allowTsInNodeModules/yarn.lock @@ -5,10 +5,12 @@ "@types/jasmine@^2.5.35": version "2.8.6" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.6.tgz#14445b6a1613cf4e05dd61c3c3256d0e95c0421e" + integrity sha512-clg9raJTY0EOo5pVZKX3ZlMjlYzVU73L71q5OV1jhE2Uezb7oF94jh4CvwrW6wInquQAdhOxJz5VDF2TLUGmmA== jasmine-core@^2.3.4: version "2.99.1" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.99.1.tgz#e6400df1e6b56e130b61c4bcd093daa7f6e8ca15" + integrity sha1-5kAN8ea1bhMLYcS80JPap/boyhU= "whitelistedFiles@file:../../testPackages/whitelistedFiles": version "1.0.0" diff --git a/test/execution-tests/optionsCaching/tsconfig.json b/test/execution-tests/optionsCaching/tsconfig.json index e61308eb4..ea813c904 100644 --- a/test/execution-tests/optionsCaching/tsconfig.json +++ b/test/execution-tests/optionsCaching/tsconfig.json @@ -3,5 +3,8 @@ "target": "esnext", "jsx": "react", "noEmitOnError": true - } + }, + "include": [ + "*" + ] } diff --git a/test/execution-tests/run-tests.js b/test/execution-tests/run-tests.js index 40daab4dd..ac6732b88 100644 --- a/test/execution-tests/run-tests.js +++ b/test/execution-tests/run-tests.js @@ -140,7 +140,7 @@ function runTests(testName) { console.log('running webpack compilation'); var webpackPath = path.resolve(__dirname, '../../node_modules/webpack/bin/webpack.js'); var program = debug ? 'node --inspect-brk=5858 ' + webpackPath : 'webpack'; - execSync(webpackPath + ' --bail', { cwd: testPath, stdio: 'inherit' }); + execSync(program + ' --bail', { cwd: testPath, stdio: 'inherit' }); passingTests.push(testName); } } diff --git a/yarn.lock b/yarn.lock index 7a3a94b91..d8c6ffc16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1353,8 +1353,9 @@ class-utils@^0.3.5: static-extend "^0.1.1" clean-css@4.1.x: - version "4.1.9" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.9.tgz#35cee8ae7687a49b98034f70de00c4edd3826301" + version "4.1.11" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a" + integrity sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo= dependencies: source-map "0.5.x" @@ -2941,7 +2942,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.13.1, js-yaml@^3.13.0: +js-yaml@3.13.1, js-yaml@^3.9.0: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -2949,10 +2950,10 @@ js-yaml@3.13.1, js-yaml@^3.13.0: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.9.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" - integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== +js-yaml@^3.13.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" esprima "^4.0.0"