From f33a6daa41303ba477459ad92864ba421666d7f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ole=C5=9B?= Date: Wed, 25 Aug 2021 23:07:15 +0200 Subject: [PATCH] feat: port changes from main branch This commit contains fixes from the main branch --- .github/stale.yml | 23 ++- README.md | 5 +- src/ForkTsCheckerWebpackPlugin.ts | 46 +++--- src/ForkTsCheckerWebpackPluginOptions.json | 144 +++++++++--------- src/ForkTsCheckerWebpackPluginState.ts | 6 +- src/hooks/tapDoneToAsyncGetIssues.ts | 4 +- src/hooks/tapStartToConnectAndRunReporter.ts | 58 +++++-- src/hooks/tapStopToDisconnectReporter.ts | 11 +- src/reporter/FilesMatch.ts | 1 + .../reporter-rpc/ReporterRpcClient.ts | 17 ++- .../TypeScriptReporterOptions.ts | 31 ++-- .../extension/TypeScriptEmbeddedExtension.ts | 2 +- .../extension/TypeScriptExtension.ts | 2 +- .../issue/TypeScriptIssueFactory.ts | 2 +- .../profile/TypeScriptPerformance.ts | 2 +- .../reporter/ControlledTypeScriptSystem.ts | 17 ++- .../reporter/ControlledWatchCompilerHost.ts | 2 +- .../ControlledWatchSolutionBuilderHost.ts | 4 +- .../reporter/TypeScriptConfigurationParser.ts | 40 +++-- .../reporter/TypeScriptReporter.ts | 5 +- src/watch/InclusiveNodeWatchFileSystem.ts | 11 +- .../type-definitions/webpack.config.ts | 4 +- 22 files changed, 259 insertions(+), 178 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index e71ef490..dc90e5a1 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,8 +1,17 @@ -# Configuration for probot-stale - https://github.com/probot/stale - -# Number of days of inactivity before an Issue or Pull Request becomes stale +# Number of days of inactivity before an issue becomes stale daysUntilStale: 60 - -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 14 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/README.md b/README.md index bbd9dabe..918a4be7 100644 --- a/README.md +++ b/README.md @@ -88,10 +88,10 @@ Options passed to the plugin constructor will overwrite options from the cosmico | Name | Type | Default value | Description | | ----------------- | ---------------------------------- | ------------------------------------------------------------------ | ----------- | | `async` | `boolean` | `compiler.options.mode === 'development'` | If `true`, reports issues **after** webpack's compilation is done. Thanks to that it doesn't block the compilation. Used only in the `watch` mode. | -| `typescript` | `object` or `boolean` | `true` | If a `boolean`, it enables/disables TypeScript checker. If an `object`, see [TypeScript options](#typescript-options). | +| `typescript` | `object` | `{}` | See [TypeScript options](#typescript-options). | | `issue` | `object` | `{}` | See [Issues options](#issues-options). | | `formatter` | `string` or `object` or `function` | `codeframe` | Available formatters are `basic`, `codeframe` and a custom `function`. To [configure](https://babeljs.io/docs/en/babel-code-frame#options) `codeframe` formatter, pass object: `{ type: 'codeframe', options: { } }`. | -| `logger` | `object` | `{ infrastructure: 'silent', issues: 'console', devServer: true }` | Available loggers are `silent`, `console`, and `webpack-infrastructure`. Infrastructure logger prints additional information, issue logger prints `issues` in the `async` mode. If `devServer` is set to `false`, errors will not be reported to Webpack Dev Server. | +| `logger` | `object` | `{ infrastructure: 'silent', issues: 'console', devServer: true }` | Available loggers are `silent`, `console`, and `webpack-infrastructure`. Infrastructure logger prints additional information, issue logger prints `issues` in the `async` mode. If `devServer` is set to `false`, errors will not be reported to Webpack Dev Server. | ### TypeScript options @@ -99,7 +99,6 @@ Options for the TypeScript checker (`typescript` option object). | Name | Type | Default value | Description | | -------------------- | --------- | -------------------------------------------------------------------------------------------------------------- | ----------- | -| `enabled` | `boolean` | `true` | If `true`, it enables TypeScript checker. | | `memoryLimit` | `number` | `2048` | Memory limit for the checker process in MB. If the process exits with the allocation failed error, try to increase this number. | | `configFile` | `string` | `'tsconfig.json'` | Path to the `tsconfig.json` file (path relative to the `compiler.options.context` or absolute path) | | `configOverwrite` | `object` | `{ compilerOptions: { skipLibCheck: true, sourceMap: false, inlineSourceMap: false, declarationMap: false } }` | This configuration will overwrite configuration from the `tsconfig.json` file. Supported fields are: `extends`, `compilerOptions`, `include`, `exclude`, `files`, and `references`. | diff --git a/src/ForkTsCheckerWebpackPlugin.ts b/src/ForkTsCheckerWebpackPlugin.ts index 9103c665..26641e4f 100644 --- a/src/ForkTsCheckerWebpackPlugin.ts +++ b/src/ForkTsCheckerWebpackPlugin.ts @@ -9,7 +9,6 @@ import schema from './ForkTsCheckerWebpackPluginOptions.json'; import { ForkTsCheckerWebpackPluginOptions } from './ForkTsCheckerWebpackPluginOptions'; import { createForkTsCheckerWebpackPluginConfiguration } from './ForkTsCheckerWebpackPluginConfiguration'; import { createForkTsCheckerWebpackPluginState } from './ForkTsCheckerWebpackPluginState'; -import { composeReporterRpcClients, createAggregatedReporter, ReporterRpcClient } from './reporter'; import { assertTypeScriptSupport } from './typescript-reporter/TypeScriptSupport'; import { createTypeScriptReporterRpcClient } from './typescript-reporter/reporter/TypeScriptReporterRpcClient'; import { tapStartToConnectAndRunReporter } from './hooks/tapStartToConnectAndRunReporter'; @@ -27,9 +26,18 @@ class ForkTsCheckerWebpackPlugin { */ static readonly version: string = '{{VERSION}}'; // will be replaced by the @semantic-release/exec /** - * Default pool for the plugin concurrency limit + * Default pools for the plugin concurrency limit */ - static readonly pool: Pool = createPool(Math.max(1, os.cpus().length)); + static readonly issuesPool: Pool = createPool(Math.max(1, os.cpus().length)); + static readonly dependenciesPool: Pool = createPool(Math.max(1, os.cpus().length)); + + /** + * @deprecated Use ForkTsCheckerWebpackPlugin.issuesPool instead + */ + static get pool(): Pool { + // for backward compatibility + return ForkTsCheckerWebpackPlugin.issuesPool; + } private readonly options: ForkTsCheckerWebpackPluginOptions; @@ -54,26 +62,22 @@ class ForkTsCheckerWebpackPlugin { apply(compiler: webpack.Compiler) { const configuration = createForkTsCheckerWebpackPluginConfiguration(compiler, this.options); const state = createForkTsCheckerWebpackPluginState(); - const reporters: ReporterRpcClient[] = []; - - if (configuration.typescript.enabled) { - assertTypeScriptSupport(configuration.typescript); - reporters.push(createTypeScriptReporterRpcClient(configuration.typescript)); - } - if (reporters.length) { - const reporter = createAggregatedReporter(composeReporterRpcClients(reporters)); + assertTypeScriptSupport(configuration.typescript); + const issuesReporter = createTypeScriptReporterRpcClient(configuration.typescript); + const dependenciesReporter = createTypeScriptReporterRpcClient(configuration.typescript); - tapAfterEnvironmentToPatchWatching(compiler, state); - tapStartToConnectAndRunReporter(compiler, reporter, configuration, state); - tapAfterCompileToAddDependencies(compiler, configuration, state); - tapStopToDisconnectReporter(compiler, reporter, state); - tapErrorToLogMessage(compiler, configuration); - } else { - throw new Error( - `ForkTsCheckerWebpackPlugin is configured to not use any issue reporter. It's probably a configuration issue.` - ); - } + tapAfterEnvironmentToPatchWatching(compiler, state); + tapStartToConnectAndRunReporter( + compiler, + issuesReporter, + dependenciesReporter, + configuration, + state + ); + tapAfterCompileToAddDependencies(compiler, configuration, state); + tapStopToDisconnectReporter(compiler, issuesReporter, dependenciesReporter, state); + tapErrorToLogMessage(compiler, configuration); } } diff --git a/src/ForkTsCheckerWebpackPluginOptions.json b/src/ForkTsCheckerWebpackPluginOptions.json index 2468728f..8484d089 100644 --- a/src/ForkTsCheckerWebpackPluginOptions.json +++ b/src/ForkTsCheckerWebpackPluginOptions.json @@ -35,18 +35,26 @@ "additionalProperties": true } }, - "required": ["type"] + "required": [ + "type" + ] }, - "FormatterType": { + "FormatterType": { "type": "string", - "enum": ["basic", "codeframe"] + "enum": [ + "basic", + "codeframe" + ] }, "IssueMatch": { "type": "object", "properties": { "severity": { "type": "string", - "enum": ["error", "warning"] + "enum": [ + "error", + "warning" + ] }, "code": { "type": "string" @@ -84,7 +92,11 @@ }, "LoggerType": { "type": "string", - "enum": ["console", "webpack-infrastructure", "silent"] + "enum": [ + "console", + "webpack-infrastructure", + "silent" + ] }, "Logger": { "type": "object", @@ -101,81 +113,73 @@ } }, "TypeScriptReporterOptions": { - "oneOf": [ - { + "type": "object", + "properties": { + "memoryLimit": { + "type": "number", + "description": "Memory limit for TypeScript reporter process." + }, + "configFile": { + "type": "string", + "description": "Path to tsconfig.json. By default plugin uses context or process.cwd() to localize tsconfig.json file." + }, + "context": { + "type": "string", + "description": "The base path for finding files specified in the tsconfig.json. Same as context option from the ts-loader." + }, + "build": { "type": "boolean", - "description": "Enable TypeScript reporter." + "description": "The equivalent of the `--build` flag from the `tsc`." }, - { + "mode": { + "type": "string", + "enum": [ + "readonly", + "write-tsbuildinfo", + "write-references" + ], + "description": "`readonly` keeps all emitted files in memory, `write-tsbuildinfo` which writes only .tsbuildinfo files and `write-references` which writes both .tsbuildinfo and referenced projects output" + }, + "compilerOptions": { + "type": "object", + "description": "Custom compilerOptions to be passed to the TypeScript compiler.", + "additionalProperties": true + }, + "diagnosticOptions": { "type": "object", + "description": "Types of diagnostics to be reported.", "properties": { - "enabled": { - "type": "boolean", - "description": "Enable TypeScript reporter." + "syntactic": { + "type": "boolean" }, - "memoryLimit": { - "type": "number", - "description": "Memory limit for TypeScript reporter process." + "semantic": { + "type": "boolean" }, - "configFile": { - "type": "string", - "description": "Path to tsconfig.json. By default plugin uses context or process.cwd() to localize tsconfig.json file." - }, - "context": { - "type": "string", - "description": "The base path for finding files specified in the tsconfig.json. Same as context option from the ts-loader." + "declaration": { + "type": "boolean" }, - "build": { - "type": "boolean", - "description": "The equivalent of the `--build` flag from the `tsc`." - }, - "mode": { - "type": "string", - "enum": ["readonly", "write-tsbuildinfo", "write-references"], - "description": "`readonly` keeps all emitted files in memory, `write-tsbuildinfo` which writes only .tsbuildinfo files and `write-references` which writes both .tsbuildinfo and referenced projects output" - }, - "compilerOptions": { - "type": "object", - "description": "Custom compilerOptions to be passed to the TypeScript compiler.", - "additionalProperties": true - }, - "diagnosticOptions": { - "type": "object", - "description": "Types of diagnostics to be reported.", - "properties": { - "syntactic": { - "type": "boolean" - }, - "semantic": { - "type": "boolean" - }, - "declaration": { - "type": "boolean" - }, - "global": { - "type": "boolean" - } - } - }, - "extensions": { - "type": "object", - "properties": { - "vue": { - "$ref": "#/definitions/TypeScriptVueExtensionOptions" - } - } - }, - "profile": { - "type": "boolean", - "description": "Measures and prints timings related to the TypeScript performance." - }, - "typescriptPath": { - "type": "string", - "description": "If supplied this is a custom path where TypeScript can be found." + "global": { + "type": "boolean" } } + }, + "extensions": { + "type": "object", + "properties": { + "vue": { + "$ref": "#/definitions/TypeScriptVueExtensionOptions" + } + } + }, + "profile": { + "type": "boolean", + "description": "Measures and prints timings related to the TypeScript performance." + }, + "typescriptPath": { + "type": "string", + "description": "If supplied this is a custom path where TypeScript can be found." } - ] + } }, "TypeScriptVueExtensionOptions": { "oneOf": [ diff --git a/src/ForkTsCheckerWebpackPluginState.ts b/src/ForkTsCheckerWebpackPluginState.ts index a77149da..206ce8d5 100644 --- a/src/ForkTsCheckerWebpackPluginState.ts +++ b/src/ForkTsCheckerWebpackPluginState.ts @@ -3,7 +3,8 @@ import { FilesMatch, Report } from './reporter'; import { Issue } from './issue'; interface ForkTsCheckerWebpackPluginState { - reportPromise: Promise; + issuesReportPromise: Promise; + dependenciesReportPromise: Promise; issuesPromise: Promise; dependenciesPromise: Promise; lastDependencies: FilesMatch | undefined; @@ -14,7 +15,8 @@ interface ForkTsCheckerWebpackPluginState { function createForkTsCheckerWebpackPluginState(): ForkTsCheckerWebpackPluginState { return { - reportPromise: Promise.resolve(undefined), + issuesReportPromise: Promise.resolve(undefined), + dependenciesReportPromise: Promise.resolve(undefined), issuesPromise: Promise.resolve(undefined), dependenciesPromise: Promise.resolve(undefined), lastDependencies: undefined, diff --git a/src/hooks/tapDoneToAsyncGetIssues.ts b/src/hooks/tapDoneToAsyncGetIssues.ts index 2338d47a..858dc8aa 100644 --- a/src/hooks/tapDoneToAsyncGetIssues.ts +++ b/src/hooks/tapDoneToAsyncGetIssues.ts @@ -22,7 +22,7 @@ function tapDoneToAsyncGetIssues( return; } - const reportPromise = state.reportPromise; + const reportPromise = state.issuesReportPromise; const issuesPromise = state.issuesPromise; let issues: Issue[] | undefined; @@ -46,7 +46,7 @@ function tapDoneToAsyncGetIssues( return; } - if (reportPromise !== state.reportPromise) { + if (reportPromise !== state.issuesReportPromise) { // there is a newer report - ignore this one return; } diff --git a/src/hooks/tapStartToConnectAndRunReporter.ts b/src/hooks/tapStartToConnectAndRunReporter.ts index 7acdd945..8263bbab 100644 --- a/src/hooks/tapStartToConnectAndRunReporter.ts +++ b/src/hooks/tapStartToConnectAndRunReporter.ts @@ -12,7 +12,8 @@ import { ForkTsCheckerWebpackPlugin } from '../ForkTsCheckerWebpackPlugin'; function tapStartToConnectAndRunReporter( compiler: webpack.Compiler, - reporter: ReporterRpcClient, + issuesReporter: ReporterRpcClient, + dependenciesReporter: ReporterRpcClient, configuration: ForkTsCheckerWebpackPluginConfiguration, state: ForkTsCheckerWebpackPluginState ) { @@ -64,44 +65,72 @@ function tapStartToConnectAndRunReporter( } let resolveDependencies: (dependencies: FilesMatch | undefined) => void; - let rejectedDependencies: (error: Error) => void; + let rejectDependencies: (error: Error) => void; let resolveIssues: (issues: Issue[] | undefined) => void; let rejectIssues: (error: Error) => void; state.dependenciesPromise = new Promise((resolve, reject) => { resolveDependencies = resolve; - rejectedDependencies = reject; + rejectDependencies = reject; }); state.issuesPromise = new Promise((resolve, reject) => { resolveIssues = resolve; rejectIssues = reject; }); - const previousReportPromise = state.reportPromise; - state.reportPromise = ForkTsCheckerWebpackPlugin.pool.submit( + const previousIssuesReportPromise = state.issuesReportPromise; + const previousDependenciesReportPromise = state.dependenciesReportPromise; + + change = await hooks.start.promise(change, compilation); + + state.issuesReportPromise = ForkTsCheckerWebpackPlugin.issuesPool.submit( (done) => // eslint-disable-next-line no-async-promise-executor new Promise(async (resolve) => { - change = await hooks.start.promise(change, compilation); + try { + await issuesReporter.connect(); + + const previousReport = await previousIssuesReportPromise; + if (previousReport) { + await previousReport.close(); + } + + const report = await issuesReporter.getReport(change, state.watching); + resolve(report); + + report.getIssues().then(resolveIssues).catch(rejectIssues).finally(done); + } catch (error) { + if (error instanceof OperationCanceledError) { + hooks.canceled.call(compilation); + } else { + hooks.error.call(error, compilation); + } + resolve(undefined); + resolveIssues(undefined); + done(); + } + }) + ); + state.dependenciesReportPromise = ForkTsCheckerWebpackPlugin.dependenciesPool.submit( + (done) => + // eslint-disable-next-line no-async-promise-executor + new Promise(async (resolve) => { try { - await reporter.connect(); + await dependenciesReporter.connect(); - const previousReport = await previousReportPromise; + const previousReport = await previousDependenciesReportPromise; if (previousReport) { await previousReport.close(); } - const report = await reporter.getReport(change, state.watching); + const report = await dependenciesReporter.getReport(change, state.watching); resolve(report); report .getDependencies() .then(resolveDependencies) - .catch(rejectedDependencies) - .finally(() => { - // get issues after dependencies are resolved as it can be blocking - report.getIssues().then(resolveIssues).catch(rejectIssues).finally(done); - }); + .catch(rejectDependencies) + .finally(done); } catch (error) { if (error instanceof OperationCanceledError) { hooks.canceled.call(compilation); @@ -111,7 +140,6 @@ function tapStartToConnectAndRunReporter( resolve(undefined); resolveDependencies(undefined); - resolveIssues(undefined); done(); } }) diff --git a/src/hooks/tapStopToDisconnectReporter.ts b/src/hooks/tapStopToDisconnectReporter.ts index 44dfb7e2..a3429f69 100644 --- a/src/hooks/tapStopToDisconnectReporter.ts +++ b/src/hooks/tapStopToDisconnectReporter.ts @@ -4,22 +4,25 @@ import { ReporterRpcClient } from '../reporter'; function tapStopToDisconnectReporter( compiler: webpack.Compiler, - reporter: ReporterRpcClient, + issuesReporter: ReporterRpcClient, + dependenciesReporter: ReporterRpcClient, state: ForkTsCheckerWebpackPluginState ) { compiler.hooks.watchClose.tap('ForkTsCheckerWebpackPlugin', () => { - reporter.disconnect(); + issuesReporter.disconnect(); + dependenciesReporter.disconnect(); }); compiler.hooks.done.tap('ForkTsCheckerWebpackPlugin', async () => { if (!state.watching) { - await reporter.disconnect(); + await Promise.all([issuesReporter.disconnect(), dependenciesReporter.disconnect()]); } }); compiler.hooks.failed.tap('ForkTsCheckerWebpackPlugin', () => { if (!state.watching) { - reporter.disconnect(); + issuesReporter.disconnect(); + dependenciesReporter.disconnect(); } }); } diff --git a/src/reporter/FilesMatch.ts b/src/reporter/FilesMatch.ts index d5aeb665..aab8f2dc 100644 --- a/src/reporter/FilesMatch.ts +++ b/src/reporter/FilesMatch.ts @@ -1,6 +1,7 @@ interface FilesMatch { files: string[]; dirs: string[]; + excluded: string[]; extensions: string[]; } diff --git a/src/reporter/reporter-rpc/ReporterRpcClient.ts b/src/reporter/reporter-rpc/ReporterRpcClient.ts index 297f5f6e..4679a103 100644 --- a/src/reporter/reporter-rpc/ReporterRpcClient.ts +++ b/src/reporter/reporter-rpc/ReporterRpcClient.ts @@ -31,8 +31,16 @@ function createReporterRpcClient( await channel.open(); } if (!rpcClient.isConnected()) { - await rpcClient.connect(); - await rpcClient.dispatchCall(configure, configuration); + try { + await rpcClient.connect(); + await rpcClient.dispatchCall(configure, configuration); + } catch (error) { + // connect or configure was not successful - + // close the reporter and re-throw an error + await rpcClient.disconnect(); + await channel.close(); + throw error; + } } }, disconnect: async () => { @@ -77,11 +85,14 @@ function composeReporterRpcClients(clients: ReporterRpcClient[]): ReporterRpcCli new Set([...mergedDependencies.files, ...singleDependencies.files]) ), dirs: Array.from(new Set([...mergedDependencies.dirs, ...singleDependencies.dirs])), + excluded: Array.from( + new Set([...mergedDependencies.excluded, ...singleDependencies.excluded]) + ), extensions: Array.from( new Set([...mergedDependencies.extensions, ...singleDependencies.extensions]) ), }), - { files: [], dirs: [], extensions: [] } + { files: [], dirs: [], excluded: [], extensions: [] } ) ), getIssues: () => diff --git a/src/typescript-reporter/TypeScriptReporterOptions.ts b/src/typescript-reporter/TypeScriptReporterOptions.ts index 1a2c89e7..860efb22 100644 --- a/src/typescript-reporter/TypeScriptReporterOptions.ts +++ b/src/typescript-reporter/TypeScriptReporterOptions.ts @@ -2,22 +2,19 @@ import { TypeScriptDiagnosticsOptions } from './TypeScriptDiagnosticsOptions'; import { TypeScriptVueExtensionOptions } from './extension/vue/TypeScriptVueExtensionOptions'; import { TypeScriptConfigurationOverwrite } from './TypeScriptConfigurationOverwrite'; -type TypeScriptReporterOptions = - | boolean - | { - enabled?: boolean; - memoryLimit?: number; - configFile?: string; - configOverwrite?: TypeScriptConfigurationOverwrite; - context?: string; - build?: boolean; - mode?: 'readonly' | 'write-tsbuildinfo' | 'write-references'; - diagnosticOptions?: Partial; - extensions?: { - vue?: TypeScriptVueExtensionOptions; - }; - profile?: boolean; - typescriptPath?: string; - }; +type TypeScriptReporterOptions = { + memoryLimit?: number; + configFile?: string; + configOverwrite?: TypeScriptConfigurationOverwrite; + context?: string; + build?: boolean; + mode?: 'readonly' | 'write-tsbuildinfo' | 'write-references'; + diagnosticOptions?: Partial; + extensions?: { + vue?: TypeScriptVueExtensionOptions; + }; + profile?: boolean; + typescriptPath?: string; +}; export { TypeScriptReporterOptions }; diff --git a/src/typescript-reporter/extension/TypeScriptEmbeddedExtension.ts b/src/typescript-reporter/extension/TypeScriptEmbeddedExtension.ts index 71a1b222..0204397a 100644 --- a/src/typescript-reporter/extension/TypeScriptEmbeddedExtension.ts +++ b/src/typescript-reporter/extension/TypeScriptEmbeddedExtension.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import { extname } from 'path'; import { TypeScriptExtension } from './TypeScriptExtension'; import { Issue } from '../../issue'; diff --git a/src/typescript-reporter/extension/TypeScriptExtension.ts b/src/typescript-reporter/extension/TypeScriptExtension.ts index 9a6d4906..1b36c7d5 100644 --- a/src/typescript-reporter/extension/TypeScriptExtension.ts +++ b/src/typescript-reporter/extension/TypeScriptExtension.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import { Issue } from '../../issue'; import { FilesMatch } from '../../reporter'; diff --git a/src/typescript-reporter/issue/TypeScriptIssueFactory.ts b/src/typescript-reporter/issue/TypeScriptIssueFactory.ts index 2d54464d..66ba5a91 100644 --- a/src/typescript-reporter/issue/TypeScriptIssueFactory.ts +++ b/src/typescript-reporter/issue/TypeScriptIssueFactory.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import * as os from 'os'; import { deduplicateAndSortIssues, Issue, IssueLocation } from '../../issue'; diff --git a/src/typescript-reporter/profile/TypeScriptPerformance.ts b/src/typescript-reporter/profile/TypeScriptPerformance.ts index df3daa26..b26d4a81 100644 --- a/src/typescript-reporter/profile/TypeScriptPerformance.ts +++ b/src/typescript-reporter/profile/TypeScriptPerformance.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import { Performance } from '../../profile/Performance'; interface TypeScriptPerformance { diff --git a/src/typescript-reporter/reporter/ControlledTypeScriptSystem.ts b/src/typescript-reporter/reporter/ControlledTypeScriptSystem.ts index 735ca445..d71f7f32 100644 --- a/src/typescript-reporter/reporter/ControlledTypeScriptSystem.ts +++ b/src/typescript-reporter/reporter/ControlledTypeScriptSystem.ts @@ -1,5 +1,5 @@ -import * as ts from 'typescript'; -import { dirname } from 'path'; +import type * as ts from 'typescript'; +import { dirname, join } from 'path'; import { createPassiveFileSystem } from '../file-system/PassiveFileSystem'; import forwardSlash from '../../utils/path/forwardSlash'; import { createRealFileSystem } from '../file-system/RealFileSystem'; @@ -48,6 +48,7 @@ function createControlledTypeScriptSystem( let artifacts: FilesMatch = { files: [], dirs: [], + excluded: [], extensions: [], }; let isInitialRun = true; @@ -198,9 +199,7 @@ function createControlledTypeScriptSystem( controlledSystem.invokeFileDeleted(path); }, directoryExists(path: string): boolean { - const stats = getReadFileSystem(path).readStats(path); - - return !!stats && stats.isDirectory(); + return Boolean(getReadFileSystem(path).readStats(path)?.isDirectory()); }, createDirectory(path: string): void { getWriteFileSystem(path).createDir(path); @@ -210,7 +209,13 @@ function createControlledTypeScriptSystem( getDirectories(path: string): string[] { const dirents = getReadFileSystem(path).readDir(path); - return dirents.filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name); + return dirents + .filter( + (dirent) => + dirent.isDirectory() || + (dirent.isSymbolicLink() && controlledSystem.directoryExists(join(path, dirent.name))) + ) + .map((dirent) => dirent.name); }, getModifiedTime(path: string): Date | undefined { const stats = getReadFileSystem(path).readStats(path); diff --git a/src/typescript-reporter/reporter/ControlledWatchCompilerHost.ts b/src/typescript-reporter/reporter/ControlledWatchCompilerHost.ts index ec3b82eb..1776b6cd 100644 --- a/src/typescript-reporter/reporter/ControlledWatchCompilerHost.ts +++ b/src/typescript-reporter/reporter/ControlledWatchCompilerHost.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import { TypeScriptHostExtension } from '../extension/TypeScriptExtension'; import { ControlledTypeScriptSystem } from './ControlledTypeScriptSystem'; diff --git a/src/typescript-reporter/reporter/ControlledWatchSolutionBuilderHost.ts b/src/typescript-reporter/reporter/ControlledWatchSolutionBuilderHost.ts index f2abe2e8..f5139afb 100644 --- a/src/typescript-reporter/reporter/ControlledWatchSolutionBuilderHost.ts +++ b/src/typescript-reporter/reporter/ControlledWatchSolutionBuilderHost.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import { createControlledWatchCompilerHost } from './ControlledWatchCompilerHost'; import { TypeScriptHostExtension } from '../extension/TypeScriptExtension'; import { ControlledTypeScriptSystem } from './ControlledTypeScriptSystem'; @@ -59,7 +59,7 @@ function createControlledWatchSolutionBuilderHost(parsedConfiguration.fileNames); - if (typeof parsedConfiguration.options.configFilePath === 'string') { - files.add(parsedConfiguration.options.configFilePath); + const configFilePath = parsedConfiguration.options.configFilePath; + if (typeof configFilePath === 'string') { + files.add(configFilePath); } const dirs = new Set(Object.keys(parsedConfiguration.wildcardDirectories || {})); + const excluded = new Set( + (parsedConfiguration.raw?.exclude || []).map((path: string) => resolve(configFileContext, path)) + ); for (const projectReference of parsedConfiguration.projectReferences || []) { - const configFile = typescript.resolveProjectReferencePath(projectReference); - if (processedConfigFiles.includes(configFile)) { + const childConfigFilePath = typescript.resolveProjectReferencePath(projectReference); + const childConfigContext = dirname(childConfigFilePath); + if (processedConfigFiles.includes(childConfigFilePath)) { // handle circular dependencies continue; } - const parsedConfiguration = parseTypeScriptConfiguration( + const childParsedConfiguration = parseTypeScriptConfiguration( typescript, - configFile, - dirname(configFile), + childConfigFilePath, + childConfigContext, {}, parseConfigFileHost ); const childDependencies = getDependenciesFromTypeScriptConfiguration( typescript, - parsedConfiguration, + childParsedConfiguration, + childConfigContext, parseConfigFileHost, - [...processedConfigFiles, configFile] + [...processedConfigFiles, childConfigFilePath] ); childDependencies.files.forEach((file) => { files.add(file); @@ -92,6 +99,7 @@ function getDependenciesFromTypeScriptConfiguration( return { files: Array.from(files).map((file) => normalize(file)), dirs: Array.from(dirs).map((dir) => normalize(dir)), + excluded: Array.from(excluded).map((path) => normalize(path)), extensions: extensions, }; } @@ -108,10 +116,10 @@ function removeJsonExtension(path: string) { } } -function getTsBuildInfoEmitOutputFilePath(options: ts.CompilerOptions) { - if (typeof ts.getTsBuildInfoEmitOutputFilePath === 'function') { +function getTsBuildInfoEmitOutputFilePath(typescript: typeof ts, options: ts.CompilerOptions) { + if (typeof typescript.getTsBuildInfoEmitOutputFilePath === 'function') { // old TypeScript version doesn't provides this method - return ts.getTsBuildInfoEmitOutputFilePath(options); + return typescript.getTsBuildInfoEmitOutputFilePath(options); } // based on the implementation from typescript @@ -153,7 +161,10 @@ function getArtifactsFromTypeScriptConfiguration( if (parsedConfiguration.options.outFile) { files.add(resolve(configFileContext, parsedConfiguration.options.outFile)); } - const tsBuildInfoPath = getTsBuildInfoEmitOutputFilePath(parsedConfiguration.options); + const tsBuildInfoPath = getTsBuildInfoEmitOutputFilePath( + typescript, + parsedConfiguration.options + ); if (tsBuildInfoPath) { files.add(resolve(configFileContext, tsBuildInfoPath)); } @@ -200,6 +211,7 @@ function getArtifactsFromTypeScriptConfiguration( return { files: Array.from(files).map((file) => normalize(file)), dirs: Array.from(dirs).map((dir) => normalize(dir)), + excluded: [], extensions, }; } diff --git a/src/typescript-reporter/reporter/TypeScriptReporter.ts b/src/typescript-reporter/reporter/TypeScriptReporter.ts index a5a1c42b..1e3bea41 100644 --- a/src/typescript-reporter/reporter/TypeScriptReporter.ts +++ b/src/typescript-reporter/reporter/TypeScriptReporter.ts @@ -1,4 +1,4 @@ -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import path from 'path'; import { FilesMatch, Reporter } from '../../reporter'; import { createIssuesFromTsDiagnostics } from '../issue/TypeScriptIssueFactory'; @@ -170,6 +170,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration let dependencies = getDependenciesFromTypeScriptConfiguration( typescript, parsedConfiguration, + configuration.context, parseConfigFileHost ); @@ -481,7 +482,7 @@ function createTypeScriptReporter(configuration: TypeScriptReporterConfiguration ); } if (!program) { - program = ts.createProgram({ + program = typescript.createProgram({ rootNames: parsedConfiguration.fileNames, options: parsedConfiguration.options, projectReferences: parsedConfiguration.projectReferences, diff --git a/src/watch/InclusiveNodeWatchFileSystem.ts b/src/watch/InclusiveNodeWatchFileSystem.ts index 9be24410..8f441a27 100644 --- a/src/watch/InclusiveNodeWatchFileSystem.ts +++ b/src/watch/InclusiveNodeWatchFileSystem.ts @@ -9,7 +9,8 @@ import minimatch from 'minimatch'; const BUILTIN_IGNORED_DIRS = ['node_modules', '.git', '.yarn', '.pnp']; function createIsIgnored( - ignored: string | RegExp | (string | RegExp)[] | undefined + ignored: string | RegExp | (string | RegExp)[] | undefined, + excluded: string[] ): (path: string) => boolean { const ignoredPatterns = ignored ? (Array.isArray(ignored) ? ignored : [ignored]) : []; const ignoredFunctions = ignoredPatterns.map((pattern) => { @@ -23,6 +24,9 @@ function createIsIgnored( return () => false; } }); + ignoredFunctions.push((path: string) => + excluded.some((excludedPath) => path.startsWith(excludedPath)) + ); ignoredFunctions.push((path: string) => BUILTIN_IGNORED_DIRS.some((ignoredDir) => path.includes(`/${ignoredDir}/`)) ); @@ -58,7 +62,10 @@ class InclusiveNodeWatchFileSystem implements WatchFileSystem { callbackUndelayed ) => { clearFilesChange(this.compiler); - const isIgnored = createIsIgnored(options?.ignored); + const isIgnored = createIsIgnored( + options?.ignored, + this.pluginState.lastDependencies?.excluded || [] + ); // use standard watch file system for files and missing const standardWatcher = this.watchFileSystem.watch( diff --git a/test/e2e/fixtures/type-definitions/webpack.config.ts b/test/e2e/fixtures/type-definitions/webpack.config.ts index ce9ffeb2..293405e4 100644 --- a/test/e2e/fixtures/type-definitions/webpack.config.ts +++ b/test/e2e/fixtures/type-definitions/webpack.config.ts @@ -5,9 +5,7 @@ const config = { plugins: [ new ForkTsCheckerWebpackPlugin({ async: 'invalid_value', - typescript: { - enabled: true, - }, + typescript: {}, }), ], };