@@ -25,6 +25,7 @@ import {
2525 HoverRequest ,
2626 DidChangeWatchedFilesNotification ,
2727 FileChangeType ,
28+ Disposable ,
2829} from 'vscode-languageserver/node'
2930import { TextDocument } from 'vscode-languageserver-textdocument'
3031import { URI } from 'vscode-uri'
@@ -73,6 +74,7 @@ import { debounce } from 'debounce'
7374import { getModuleDependencies } from './util/getModuleDependencies'
7475import assert from 'assert'
7576// import postcssLoadConfig from 'postcss-load-config'
77+ import * as parcel from './watcher/index.js'
7678
7779const CONFIG_FILE_GLOB = '{tailwind,tailwind.config}.{js,cjs}'
7880const TRIGGER_CHARACTERS = [
@@ -151,6 +153,7 @@ function first<T>(...options: Array<() => T>): T {
151153interface ProjectService {
152154 state : State
153155 tryInit : ( ) => Promise < void >
156+ dispose : ( ) => void
154157 onUpdateSettings : ( settings : any ) => void
155158 onHover ( params : TextDocumentPositionParams ) : Promise < Hover >
156159 onCompletion ( params : CompletionParams ) : Promise < CompletionList >
@@ -167,6 +170,7 @@ async function createProjectService(
167170 params : InitializeParams ,
168171 documentService : DocumentService
169172) : Promise < ProjectService > {
173+ const disposables : Disposable [ ] = [ ]
170174 const state : State = {
171175 enabled : false ,
172176 editor : {
@@ -208,7 +212,13 @@ async function createProjectService(
208212 const documentSettingsCache : Map < string , Settings > = new Map ( )
209213 let registrations : Promise < BulkUnregistration >
210214
211- let watcher : FSWatcher
215+ let chokidarWatcher : FSWatcher
216+ let ignore = [
217+ '**/.git/objects/**' ,
218+ '**/.git/subtree-cache/**' ,
219+ '**/node_modules/**' ,
220+ '**/.hg/store/**' ,
221+ ]
212222
213223 function onFileEvents ( changes : Array < { file : string ; type : FileChangeType } > ) : void {
214224 let needsInit = false
@@ -217,22 +227,30 @@ async function createProjectService(
217227 for ( let change of changes ) {
218228 let file = normalizePath ( change . file )
219229
230+ for ( let ignorePattern of ignore ) {
231+ if ( minimatch ( file , ignorePattern ) ) {
232+ continue
233+ }
234+ }
235+
236+ let isConfigFile = minimatch ( file , `**/${ CONFIG_FILE_GLOB } ` )
237+ let isPackageFile = minimatch ( file , '**/package.json' )
238+ let isDependency = state . dependencies && state . dependencies . includes ( change . file )
239+
240+ if ( ! isConfigFile && ! isPackageFile && ! isDependency ) continue
241+
220242 if ( change . type === FileChangeType . Created ) {
221243 needsInit = true
222244 break
223245 } else if ( change . type === FileChangeType . Changed ) {
224- if ( ! state . enabled || minimatch ( file , '**/package.json' ) ) {
246+ if ( ! state . enabled || isPackageFile ) {
225247 needsInit = true
226248 break
227249 } else {
228250 needsRebuild = true
229251 }
230252 } else if ( change . type === FileChangeType . Deleted ) {
231- if (
232- ! state . enabled ||
233- minimatch ( file , '**/package.json' ) ||
234- minimatch ( file , `**/${ CONFIG_FILE_GLOB } ` )
235- ) {
253+ if ( ! state . enabled || isPackageFile || isConfigFile ) {
236254 needsInit = true
237255 break
238256 } else {
@@ -261,34 +279,59 @@ async function createProjectService(
261279 connection . client . register ( DidChangeWatchedFilesNotification . type , {
262280 watchers : [ { globPattern : `**/${ CONFIG_FILE_GLOB } ` } , { globPattern : '**/package.json' } ] ,
263281 } )
264- } else {
265- watcher = chokidar . watch (
266- [
267- normalizePath ( `${ folder } /**/${ CONFIG_FILE_GLOB } ` ) ,
268- normalizePath ( `${ folder } /**/package.json` ) ,
269- ] ,
282+ } else if ( parcel . getBinding ( ) ) {
283+ let typeMap = {
284+ create : FileChangeType . Created ,
285+ update : FileChangeType . Changed ,
286+ delete : FileChangeType . Deleted ,
287+ }
288+
289+ let subscription = await parcel . subscribe (
290+ folder ,
291+ ( err , events ) => {
292+ onFileEvents ( events . map ( ( event ) => ( { file : event . path , type : typeMap [ event . type ] } ) ) )
293+ } ,
270294 {
271- ignorePermissionErrors : true ,
272- ignoreInitial : true ,
273- ignored : [ '**/node_modules/**' ] ,
274- awaitWriteFinish : {
275- stabilityThreshold : 100 ,
276- pollInterval : 20 ,
277- } ,
295+ ignore : ignore . map ( ( ignorePattern ) =>
296+ path . resolve ( folder , ignorePattern . replace ( / ^ [ * / ] + / , '' ) . replace ( / [ * / ] + $ / , '' ) )
297+ ) ,
278298 }
279299 )
280300
301+ disposables . push ( {
302+ dispose ( ) {
303+ subscription . unsubscribe ( )
304+ } ,
305+ } )
306+ } else {
307+ chokidarWatcher = chokidar . watch ( [ `**/${ CONFIG_FILE_GLOB } ` , '**/package.json' ] , {
308+ cwd : folder ,
309+ ignorePermissionErrors : true ,
310+ ignoreInitial : true ,
311+ ignored : ignore ,
312+ awaitWriteFinish : {
313+ stabilityThreshold : 100 ,
314+ pollInterval : 20 ,
315+ } ,
316+ } )
317+
281318 await new Promise < void > ( ( resolve ) => {
282- watcher . on ( 'ready' , ( ) => resolve ( ) )
319+ chokidarWatcher . on ( 'ready' , ( ) => resolve ( ) )
283320 } )
284321
285- watcher
322+ chokidarWatcher
286323 . on ( 'add' , ( file ) => onFileEvents ( [ { file, type : FileChangeType . Created } ] ) )
287324 . on ( 'change' , ( file ) => onFileEvents ( [ { file, type : FileChangeType . Changed } ] ) )
288325 . on ( 'unlink' , ( file ) => onFileEvents ( [ { file, type : FileChangeType . Deleted } ] ) )
326+
327+ disposables . push ( {
328+ dispose ( ) {
329+ chokidarWatcher . close ( )
330+ } ,
331+ } )
289332 }
290333
291- function registerCapabilities ( watchFiles ? : string [ ] ) : void {
334+ function registerCapabilities ( watchFiles : string [ ] = [ ] ) : void {
292335 if ( supportsDynamicRegistration ( connection , params ) ) {
293336 if ( registrations ) {
294337 registrations . then ( ( r ) => r . dispose ( ) )
@@ -310,7 +353,7 @@ async function createProjectService(
310353 resolveProvider : true ,
311354 triggerCharacters : [ ...TRIGGER_CHARACTERS , state . separator ] ,
312355 } )
313- if ( watchFiles ) {
356+ if ( watchFiles . length > 0 ) {
314357 capabilities . add ( DidChangeWatchedFilesNotification . type , {
315358 watchers : watchFiles . map ( ( file ) => ( { globPattern : file } ) ) ,
316359 } )
@@ -323,13 +366,13 @@ async function createProjectService(
323366 function resetState ( ) : void {
324367 clearAllDiagnostics ( state )
325368 Object . keys ( state ) . forEach ( ( key ) => {
326- if ( key !== 'editor' ) {
369+ // Keep `dependencies` to ensure that they are still watched
370+ if ( key !== 'editor' && key !== 'dependencies' ) {
327371 delete state [ key ]
328372 }
329373 } )
330374 state . enabled = false
331- registerCapabilities ( )
332- // TODO reset watcher (remove config dependencies)
375+ registerCapabilities ( state . dependencies )
333376 }
334377
335378 async function tryInit ( ) {
@@ -813,10 +856,10 @@ async function createProjectService(
813856 }
814857
815858 if ( state . dependencies ) {
816- watcher ?. unwatch ( state . dependencies )
859+ chokidarWatcher ?. unwatch ( state . dependencies )
817860 }
818861 state . dependencies = getModuleDependencies ( state . configPath )
819- watcher ?. add ( state . dependencies )
862+ chokidarWatcher ?. add ( state . dependencies )
820863
821864 state . configId = getConfigId ( state . configPath , state . dependencies )
822865
@@ -837,6 +880,11 @@ async function createProjectService(
837880 return {
838881 state,
839882 tryInit,
883+ dispose ( ) {
884+ for ( let { dispose } of disposables ) {
885+ dispose ( )
886+ }
887+ } ,
840888 onUpdateSettings ( settings : any ) : void {
841889 documentSettingsCache . clear ( )
842890 if ( state . enabled ) {
@@ -1279,7 +1327,9 @@ class TW {
12791327 }
12801328
12811329 dispose ( ) : void {
1282- //
1330+ for ( let [ , project ] of this . projects ) {
1331+ project . dispose ( )
1332+ }
12831333 }
12841334}
12851335
0 commit comments