diff --git a/README.md b/README.md index 6ff2825c..c7a83279 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Options for the TypeScript checker (`typescript` option object). | `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`. | | `context` | `string` | `dirname(configuration.configFile)` | The base path for finding files specified in the `tsconfig.json`. Same as the `context` option from the [ts-loader](https://github.com/TypeStrong/ts-loader#context). Useful if you want to keep your `tsconfig.json` in an external package. Keep in mind that **not** having a `tsconfig.json` in your project root can cause different behaviour between `fork-ts-checker-webpack-plugin` and `tsc`. When using editors like `VS Code` it is advised to add a `tsconfig.json` file to the root of the project and extend the config file referenced in option `configFile`. | | `build` | `boolean` | `false` | The equivalent of the `--build` flag for the `tsc` command. | -| `mode` | `'readonly'` or `'write-tsbuildinfo'` or `'write-references'` | `'write-tsbuildinfo'` | If you use the `babel-loader`, it's recommended to use `write-references` mode to improve initial compilation time. If you use `ts-loader`, it's recommended to use `write-tsbuildinfo` mode to not overwrite files emitted by the `ts-loader`. | +| `mode` | `'readonly'` or `'write-tsbuildinfo'` or `'write-dts'` or `'write-references'` | `'write-tsbuildinfo'` | If you use the `babel-loader`, it's recommended to use `write-references` mode to improve initial compilation time. If you use `ts-loader`, it's recommended to use `write-tsbuildinfo` mode to not overwrite files emitted by the `ts-loader`. If you use `ts-loader` with `transpileOnly` flag set to `true`, use `'write-dts` to emit the type definition files. | | `diagnosticOptions` | `object` | `{ syntactic: false, semantic: true, declaration: false, global: false }` | Settings to select which diagnostics do we want to perform. | | `extensions` | `object` | `{}` | See [TypeScript extensions options](#typescript-extensions-options). | | `profile` | `boolean` | `false` | Measures and prints timings related to the TypeScript performance. | @@ -299,24 +299,37 @@ When we call this method with a [webpack compiler instance](https://webpack.js.o [tapable](https://github.com/webpack/tapable) hooks where you can pass in your callbacks. ```js -const webpack = require('webpack'); +// ./src/webpack/MyWebpackPlugin.js const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); -const compiler = webpack({ - // ... webpack config -}); +class MyWebpackPlugin { + apply(compiler) { + const hooks = ForkTsCheckerWebpackPlugin.getCompilerHooks(compiler); + + // log some message on waiting + hooks.waiting.tap('MyPlugin', () => { + console.log('waiting for issues'); + }); + // don't show warnings + hooks.issues.tap('MyPlugin', (issues) => + issues.filter((issue) => issue.severity === 'error') + ); + } +} -// optionally add the plugin to the compiler -// **don't do this if already added through configuration** -new ForkTsCheckerWebpackPlugin().apply(compiler); +module.exports = MyWebpackPlugin; -// now get the plugin hooks from compiler -const hooks = ForkTsCheckerWebpackPlugin.getCompilerHooks(compiler); +// webpack.config.js +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); +const MyWebpackPlugin = require('./src/webpack/MyWebpackPlugin'); -// say we want to show some message when plugin is waiting for issues results -hooks.waiting.tap('yourListenerName', () => { - console.log('waiting for issues'); -}); +module.exports = { + /* ... */ + plugins: [ + new ForkTsCheckerWebpackPlugin(), + new MyWebpackPlugin() + ] +}; ``` ## Typings @@ -334,7 +347,7 @@ yarn add --dev @types/webpack ## Profiling types resolution -Starting from TypeScript 4.1.0 (currently in beta), you can profile long type checks by +Starting from TypeScript 4.1.0, you can profile long type checks by setting "generateTrace" compiler option. This is an instruction from [microsoft/TypeScript#40063](https://github.com/microsoft/TypeScript/pull/40063): 1. Set "generateTrace": "{folderName}" in your `tsconfig.json` diff --git a/package.json b/package.json index d75e8918..bdfbc6c5 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,13 @@ }, "peerDependencies": { "typescript": ">3.6.0", - "webpack": "^5.11.0" + "webpack": "^5.11.0", + "vue-template-compiler": "*" + }, + "peerDependenciesMeta": { + "vue-template-compiler": { + "optional": true + } }, "devDependencies": { "@commitlint/config-conventional": "^13.1.0", diff --git a/src/plugin-options.json b/src/plugin-options.json index 0a27f494..6a7f6525 100644 --- a/src/plugin-options.json +++ b/src/plugin-options.json @@ -129,9 +129,10 @@ "enum": [ "readonly", "write-tsbuildinfo", + "write-dts", "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" + "description": "`readonly` keeps all emitted files in memory, `write-tsbuildinfo` which writes only .tsbuildinfo files, `write-dts` writes .tsbuildinfo and type definition files, and `write-references` which writes both .tsbuildinfo and referenced projects output" }, "compilerOptions": { "type": "object", diff --git a/src/typescript/type-script-worker-config.ts b/src/typescript/type-script-worker-config.ts index 1c2ac79d..199fcf58 100644 --- a/src/typescript/type-script-worker-config.ts +++ b/src/typescript/type-script-worker-config.ts @@ -16,7 +16,7 @@ interface TypeScriptWorkerConfig { configOverwrite: TypeScriptConfigOverwrite; build: boolean; context: string; - mode: 'readonly' | 'write-tsbuildinfo' | 'write-references'; + mode: 'readonly' | 'write-tsbuildinfo' | 'write-dts' | 'write-references'; diagnosticOptions: TypeScriptDiagnosticsOptions; extensions: { vue: TypeScriptVueExtensionConfig; diff --git a/src/typescript/type-script-worker-options.ts b/src/typescript/type-script-worker-options.ts index 07afed2e..241ebf04 100644 --- a/src/typescript/type-script-worker-options.ts +++ b/src/typescript/type-script-worker-options.ts @@ -8,7 +8,7 @@ type TypeScriptWorkerOptions = { configOverwrite?: TypeScriptConfigOverwrite; context?: string; build?: boolean; - mode?: 'readonly' | 'write-tsbuildinfo' | 'write-references'; + mode?: 'readonly' | 'write-tsbuildinfo' | 'write-dts' | 'write-references'; diagnosticOptions?: Partial; extensions?: { vue?: TypeScriptVueExtensionOptions; diff --git a/src/typescript/worker/get-issues-worker.ts b/src/typescript/worker/get-issues-worker.ts index 0088c4a6..6c66b656 100644 --- a/src/typescript/worker/get-issues-worker.ts +++ b/src/typescript/worker/get-issues-worker.ts @@ -33,10 +33,10 @@ const getIssuesWorker = async ( watching: boolean ): Promise => { system.invalidateCache(); + invalidateDependencies(); if (didConfigFileChanged({ changedFiles, deletedFiles })) { invalidateConfig(); - invalidateDependencies(); invalidateArtifacts(); invalidateDiagnostics(); @@ -46,7 +46,6 @@ const getIssuesWorker = async ( invalidateTsBuildInfo(); } else if (didRootFilesChanged()) { - invalidateDependencies(); invalidateArtifacts(); invalidateWatchProgramRootFileNames(); diff --git a/src/typescript/worker/lib/system.ts b/src/typescript/worker/lib/system.ts index 5024fcd9..3dabcb58 100644 --- a/src/typescript/worker/lib/system.ts +++ b/src/typescript/worker/lib/system.ts @@ -68,6 +68,9 @@ const ignoredPaths = ['/node_modules/.', '/.git', '/.#']; export const system: ControlledTypeScriptSystem = { ...typescript.sys, useCaseSensitiveFileNames: true, + realpath(path: string): string { + return getReadFileSystem(path).realPath(path); + }, fileExists(path: string): boolean { const stats = getReadFileSystem(path).readStats(path); @@ -278,8 +281,19 @@ function isArtifact(path: string) { } function getReadFileSystem(path: string) { - if (!isInitialRun && (mode === 'readonly' || mode === 'write-tsbuildinfo') && isArtifact(path)) { - return memFileSystem; + if ((mode === 'readonly' || mode === 'write-tsbuildinfo') && isArtifact(path)) { + if (isInitialRun && !memFileSystem.exists(path) && passiveFileSystem.exists(path)) { + // copy file to memory on initial run + const stats = passiveFileSystem.readStats(path); + if (stats?.isFile()) { + const content = passiveFileSystem.readFile(path); + if (content) { + memFileSystem.writeFile(path, content); + memFileSystem.updateTimes(path, stats.atime, stats.mtime); + } + } + return memFileSystem; + } } return passiveFileSystem; @@ -288,7 +302,9 @@ function getReadFileSystem(path: string) { function getWriteFileSystem(path: string) { if ( mode === 'write-references' || - (mode === 'write-tsbuildinfo' && path.endsWith('.tsbuildinfo')) + (mode === 'write-tsbuildinfo' && path.endsWith('.tsbuildinfo')) || + (mode === 'write-dts' && + ['.tsbuildinfo', '.d.ts', '.d.ts.map'].some((suffix) => path.endsWith(suffix))) ) { return realFileSystem; } diff --git a/test/e2e/fixtures/typescript-monorepo/packages/client/tsconfig.json b/test/e2e/fixtures/typescript-monorepo/packages/client/tsconfig.json index 79add424..da4c8120 100644 --- a/test/e2e/fixtures/typescript-monorepo/packages/client/tsconfig.json +++ b/test/e2e/fixtures/typescript-monorepo/packages/client/tsconfig.json @@ -1,12 +1,12 @@ { - "extends": "../../tsconfig", + "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "./src", "outDir": "./lib", + "tsBuildInfoFile": "lib/tsconfig.tsbuildinfo", "sourceRoot": "./src", "baseUrl": "./src" }, "references": [{ "path": "../shared" }], - "include": ["src"], - "exclude": ["node_modules", "lib"] + "include": ["src"] } diff --git a/test/e2e/fixtures/typescript-monorepo/packages/shared/tsconfig.json b/test/e2e/fixtures/typescript-monorepo/packages/shared/tsconfig.json index 18339ed2..4d0541ae 100644 --- a/test/e2e/fixtures/typescript-monorepo/packages/shared/tsconfig.json +++ b/test/e2e/fixtures/typescript-monorepo/packages/shared/tsconfig.json @@ -1,11 +1,11 @@ { - "extends": "../../tsconfig", + "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "./src", "outDir": "./lib", + "tsBuildInfoFile": "lib/tsconfig.tsbuildinfo", "sourceRoot": "./src", "baseUrl": "./src" }, - "include": ["src"], - "exclude": ["node_modules", "lib"] + "include": ["src"] } diff --git a/test/e2e/fixtures/typescript-monorepo/tsconfig.base.json b/test/e2e/fixtures/typescript-monorepo/tsconfig.base.json new file mode 100644 index 00000000..672bb4e9 --- /dev/null +++ b/test/e2e/fixtures/typescript-monorepo/tsconfig.base.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "lib": ["es5", "scripthost"], + "moduleResolution": "node", + "esModuleInterop": true, + "importHelpers": false, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "strict": true, + "baseUrl": ".", + "composite": true, + "incremental": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "rootDir": "./packages" + } +} diff --git a/test/e2e/fixtures/typescript-monorepo/tsconfig.json b/test/e2e/fixtures/typescript-monorepo/tsconfig.json index 7fc8dd08..74e94c36 100644 --- a/test/e2e/fixtures/typescript-monorepo/tsconfig.json +++ b/test/e2e/fixtures/typescript-monorepo/tsconfig.json @@ -1,22 +1,5 @@ { - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "lib": ["es5", "scripthost"], - "moduleResolution": "node", - "esModuleInterop": true, - "importHelpers": false, - "skipLibCheck": true, - "skipDefaultLibCheck": true, - "strict": true, - "baseUrl": ".", - "composite": true, - "incremental": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "rootDir": "./packages" - }, + "extends": "./tsconfig.base.json", "files": [], "references": [ { "path": "./packages/shared" }, diff --git a/test/e2e/type-script-solution-builder-api.spec.ts b/test/e2e/type-script-solution-builder-api.spec.ts index 6b707937..0de0fea4 100644 --- a/test/e2e/type-script-solution-builder-api.spec.ts +++ b/test/e2e/type-script-solution-builder-api.spec.ts @@ -9,7 +9,7 @@ describe('TypeScript SolutionBuilder API', () => { { async: false, typescript: '~3.8.0', mode: 'readonly' }, { async: true, typescript: '~3.8.0', mode: 'write-tsbuildinfo' }, { async: false, typescript: '~4.0.0', mode: 'write-references' }, - { async: true, typescript: '~4.3.0', mode: 'readonly' }, + { async: true, typescript: '~4.3.0', mode: 'write-dts' }, ])('reports semantic error for %p', async ({ async, typescript, mode }) => { await sandbox.load(path.join(__dirname, 'fixtures/typescript-monorepo')); await sandbox.install('yarn', { typescript }); @@ -104,31 +104,50 @@ describe('TypeScript SolutionBuilder API', () => { break; case 'write-tsbuildinfo': - expect(await sandbox.exists('packages/shared/tsconfig.tsbuildinfo')).toEqual(true); - expect(await sandbox.exists('packages/client/tsconfig.tsbuildinfo')).toEqual(true); - expect(await sandbox.exists('packages/shared/lib')).toEqual(false); - expect(await sandbox.exists('packages/client/lib')).toEqual(false); + expect(await sandbox.exists('packages/shared/lib/tsconfig.tsbuildinfo')).toEqual(true); + expect(await sandbox.exists('packages/client/lib/tsconfig.tsbuildinfo')).toEqual(true); + expect(await sandbox.exists('packages/shared/lib')).toEqual(true); + expect(await sandbox.exists('packages/client/lib')).toEqual(true); + expect(await sandbox.exists('packages/shared/lib/index.js')).toEqual(false); + expect(await sandbox.exists('packages/client/lib/index.js')).toEqual(false); + + expect(await sandbox.read('packages/shared/lib/tsconfig.tsbuildinfo')).not.toEqual(''); + expect(await sandbox.read('packages/client/lib/tsconfig.tsbuildinfo')).not.toEqual(''); + + await sandbox.remove('packages/shared/lib'); + await sandbox.remove('packages/client/lib'); + break; - expect(await sandbox.read('packages/shared/tsconfig.tsbuildinfo')).not.toEqual(''); - expect(await sandbox.read('packages/client/tsconfig.tsbuildinfo')).not.toEqual(''); + case 'write-dts': + expect(await sandbox.exists('packages/shared/lib/tsconfig.tsbuildinfo')).toEqual(true); + expect(await sandbox.exists('packages/client/lib/tsconfig.tsbuildinfo')).toEqual(true); + expect(await sandbox.exists('packages/shared/lib')).toEqual(true); + expect(await sandbox.exists('packages/client/lib')).toEqual(true); + expect(await sandbox.exists('packages/shared/lib/index.d.ts')).toEqual(true); + expect(await sandbox.exists('packages/shared/lib/index.d.ts.map')).toEqual(true); + expect(await sandbox.exists('packages/client/lib/index.d.ts')).toEqual(true); + expect(await sandbox.exists('packages/client/lib/index.d.ts.map')).toEqual(true); + expect(await sandbox.exists('packages/shared/lib/index.js')).toEqual(false); + expect(await sandbox.exists('packages/client/lib/index.js')).toEqual(false); - await sandbox.remove('packages/shared/tsconfig.tsbuildinfo'); - await sandbox.remove('packages/client/tsconfig.tsbuildinfo'); + expect(await sandbox.read('packages/shared/lib/tsconfig.tsbuildinfo')).not.toEqual(''); + expect(await sandbox.read('packages/client/lib/tsconfig.tsbuildinfo')).not.toEqual(''); + + await sandbox.remove('packages/shared/lib'); + await sandbox.remove('packages/client/lib'); break; case 'write-references': - expect(await sandbox.exists('packages/shared/tsconfig.tsbuildinfo')).toEqual(true); - expect(await sandbox.exists('packages/client/tsconfig.tsbuildinfo')).toEqual(true); + expect(await sandbox.exists('packages/shared/lib/tsconfig.tsbuildinfo')).toEqual(true); + expect(await sandbox.exists('packages/client/lib/tsconfig.tsbuildinfo')).toEqual(true); expect(await sandbox.exists('packages/shared/lib')).toEqual(true); expect(await sandbox.exists('packages/client/lib')).toEqual(true); expect(await sandbox.exists('packages/shared/lib/index.js')).toEqual(true); expect(await sandbox.exists('packages/client/lib/index.js')).toEqual(true); - expect(await sandbox.read('packages/shared/tsconfig.tsbuildinfo')).not.toEqual(''); - expect(await sandbox.read('packages/client/tsconfig.tsbuildinfo')).not.toEqual(''); + expect(await sandbox.read('packages/shared/lib/tsconfig.tsbuildinfo')).not.toEqual(''); + expect(await sandbox.read('packages/client/lib/tsconfig.tsbuildinfo')).not.toEqual(''); - await sandbox.remove('packages/shared/tsconfig.tsbuildinfo'); - await sandbox.remove('packages/client/tsconfig.tsbuildinfo'); await sandbox.remove('packages/shared/lib'); await sandbox.remove('packages/client/lib'); break; diff --git a/test/unit/typescript/type-script-worker-config.spec.ts b/test/unit/typescript/type-script-worker-config.spec.ts index 28b7a447..12e95884 100644 --- a/test/unit/typescript/type-script-worker-config.spec.ts +++ b/test/unit/typescript/type-script-worker-config.spec.ts @@ -75,6 +75,7 @@ describe('typescript/type-scripts-worker-config', () => { [{ build: true }, { ...configuration, build: true }], [{ mode: 'readonly' }, { ...configuration, mode: 'readonly' }], [{ mode: 'write-tsbuildinfo' }, { ...configuration, mode: 'write-tsbuildinfo' }], + [{ mode: 'write-dts' }, { ...configuration, mode: 'write-dts' }], [{ mode: 'write-references' }, { ...configuration, mode: 'write-references' }], [ { configOverwrite: { compilerOptions: { strict: true }, include: ['src'] } },