diff --git a/README.md b/README.md index 1d18a7a..5264d42 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ Please note that no options are required. However, depending on your configurati | `dotenvOptions` | `{}` | Provides options for [`dotenv`](https://github.com/motdotla/dotenv#options). Note that this plugin only accepts a `string` value for `path`. | | `postcssOptions` | `{}` | See [`postcssOptions`](#postcssOptions) below. | | `rendererOptions` | `{}` | See [`rendererOptions`](#rendererOptions) below. | +| `caching` | `"never"` | See [`caching`](#caching) below | ```json { @@ -240,6 +241,19 @@ This is experimental, and may not always work as expected. It currently supports > For convenience, `loadPaths` for Sass are extended, not replaced. The defaults are the path of the current file, and `'node_modules'`. +#### `caching` + +Enables or disables caching. + +`"always"` will cache everything. This is overly agressive, and will cause stale types when CSS imported by CSS is modified. +The cache key is the filename of the CSS file directly imported by TypeScript. Restarting the TypeScript server will clear the cache. + +`"never"` (default) regenerates types for every CSS file, every time any file is changed. This causes significant latency in editor +operations (autocomplete, import suggestions, etc) in large projects, even when editing non-CSS files. + +Unfortunately, there's no balanced option or correct invalidation possible, due to typescript and the various CSS tools having +different module loading systems. + ### Visual Studio Code #### Recommended usage diff --git a/src/index.ts b/src/index.ts index 403b863..204886f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import { getDtsSnapshot } from './helpers/getDtsSnapshot'; import { createLogger } from './helpers/logger'; import { getProcessor } from './helpers/getProcessor'; import { filterPlugins } from './helpers/filterPlugins'; +import { IScriptSnapshot } from 'typescript/lib/tsserverlibrary'; const getPostCssConfigPlugins = (directory: string) => { try { @@ -35,6 +36,7 @@ const init: tsModule.server.PluginModuleFactory = ({ typescript: ts }) => { const logger = createLogger(info); const directory = info.project.getCurrentDirectory(); const compilerOptions = info.project.getCompilerOptions(); + const scriptSnapshotCache = new WeakMap(); const languageServiceHost = {} as Partial; @@ -132,8 +134,16 @@ const init: tsModule.server.PluginModuleFactory = ({ typescript: ts }) => { }; languageServiceHost.getScriptSnapshot = (fileName) => { - if (isCSS(fileName) && fs.existsSync(fileName)) { - return getDtsSnapshot( + const existingSnapshot = + info.languageServiceHost.getScriptSnapshot(fileName); + if (!isCSS(fileName) || !fs.existsSync(fileName)) return existingSnapshot; + + const cachedSnapshot = + existingSnapshot && scriptSnapshotCache.get(existingSnapshot); + if (cachedSnapshot && options.caching === 'always') { + return cachedSnapshot; + } else { + const newSnapshot = getDtsSnapshot( ts, processor, fileName, @@ -142,8 +152,10 @@ const init: tsModule.server.PluginModuleFactory = ({ typescript: ts }) => { compilerOptions, directory, ); + if (existingSnapshot) + scriptSnapshotCache.set(existingSnapshot, newSnapshot); + return newSnapshot; } - return info.languageServiceHost.getScriptSnapshot(fileName); }; const createModuleResolver = diff --git a/src/options.ts b/src/options.ts index 3417f4e..440d466 100644 --- a/src/options.ts +++ b/src/options.ts @@ -35,8 +35,11 @@ export interface Options { /** @deprecated To align with naming in other projects. */ postCssOptions?: PostcssOptions; rendererOptions?: RendererOptions; + caching?: CachingOptions; } +export type CachingOptions = 'always' | 'never'; + export type ClassnameTransformOptions = | 'asIs' | 'camelCase'