@@ -25,6 +25,7 @@ import {
25
25
HoverRequest ,
26
26
DidChangeWatchedFilesNotification ,
27
27
FileChangeType ,
28
+ Disposable ,
28
29
} from 'vscode-languageserver/node'
29
30
import { TextDocument } from 'vscode-languageserver-textdocument'
30
31
import { URI } from 'vscode-uri'
@@ -73,6 +74,7 @@ import { debounce } from 'debounce'
73
74
import { getModuleDependencies } from './util/getModuleDependencies'
74
75
import assert from 'assert'
75
76
// import postcssLoadConfig from 'postcss-load-config'
77
+ import * as parcel from './watcher/index.js'
76
78
77
79
const CONFIG_FILE_GLOB = '{tailwind,tailwind.config}.{js,cjs}'
78
80
const TRIGGER_CHARACTERS = [
@@ -151,6 +153,7 @@ function first<T>(...options: Array<() => T>): T {
151
153
interface ProjectService {
152
154
state : State
153
155
tryInit : ( ) => Promise < void >
156
+ dispose : ( ) => void
154
157
onUpdateSettings : ( settings : any ) => void
155
158
onHover ( params : TextDocumentPositionParams ) : Promise < Hover >
156
159
onCompletion ( params : CompletionParams ) : Promise < CompletionList >
@@ -167,6 +170,7 @@ async function createProjectService(
167
170
params : InitializeParams ,
168
171
documentService : DocumentService
169
172
) : Promise < ProjectService > {
173
+ const disposables : Disposable [ ] = [ ]
170
174
const state : State = {
171
175
enabled : false ,
172
176
editor : {
@@ -208,7 +212,13 @@ async function createProjectService(
208
212
const documentSettingsCache : Map < string , Settings > = new Map ( )
209
213
let registrations : Promise < BulkUnregistration >
210
214
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
+ ]
212
222
213
223
function onFileEvents ( changes : Array < { file : string ; type : FileChangeType } > ) : void {
214
224
let needsInit = false
@@ -217,22 +227,30 @@ async function createProjectService(
217
227
for ( let change of changes ) {
218
228
let file = normalizePath ( change . file )
219
229
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
+
220
242
if ( change . type === FileChangeType . Created ) {
221
243
needsInit = true
222
244
break
223
245
} else if ( change . type === FileChangeType . Changed ) {
224
- if ( ! state . enabled || minimatch ( file , '**/package.json' ) ) {
246
+ if ( ! state . enabled || isPackageFile ) {
225
247
needsInit = true
226
248
break
227
249
} else {
228
250
needsRebuild = true
229
251
}
230
252
} 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 ) {
236
254
needsInit = true
237
255
break
238
256
} else {
@@ -261,34 +279,59 @@ async function createProjectService(
261
279
connection . client . register ( DidChangeWatchedFilesNotification . type , {
262
280
watchers : [ { globPattern : `**/${ CONFIG_FILE_GLOB } ` } , { globPattern : '**/package.json' } ] ,
263
281
} )
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
+ } ,
270
294
{
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
+ ) ,
278
298
}
279
299
)
280
300
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
+
281
318
await new Promise < void > ( ( resolve ) => {
282
- watcher . on ( 'ready' , ( ) => resolve ( ) )
319
+ chokidarWatcher . on ( 'ready' , ( ) => resolve ( ) )
283
320
} )
284
321
285
- watcher
322
+ chokidarWatcher
286
323
. on ( 'add' , ( file ) => onFileEvents ( [ { file, type : FileChangeType . Created } ] ) )
287
324
. on ( 'change' , ( file ) => onFileEvents ( [ { file, type : FileChangeType . Changed } ] ) )
288
325
. on ( 'unlink' , ( file ) => onFileEvents ( [ { file, type : FileChangeType . Deleted } ] ) )
326
+
327
+ disposables . push ( {
328
+ dispose ( ) {
329
+ chokidarWatcher . close ( )
330
+ } ,
331
+ } )
289
332
}
290
333
291
- function registerCapabilities ( watchFiles ? : string [ ] ) : void {
334
+ function registerCapabilities ( watchFiles : string [ ] = [ ] ) : void {
292
335
if ( supportsDynamicRegistration ( connection , params ) ) {
293
336
if ( registrations ) {
294
337
registrations . then ( ( r ) => r . dispose ( ) )
@@ -310,7 +353,7 @@ async function createProjectService(
310
353
resolveProvider : true ,
311
354
triggerCharacters : [ ...TRIGGER_CHARACTERS , state . separator ] ,
312
355
} )
313
- if ( watchFiles ) {
356
+ if ( watchFiles . length > 0 ) {
314
357
capabilities . add ( DidChangeWatchedFilesNotification . type , {
315
358
watchers : watchFiles . map ( ( file ) => ( { globPattern : file } ) ) ,
316
359
} )
@@ -323,13 +366,13 @@ async function createProjectService(
323
366
function resetState ( ) : void {
324
367
clearAllDiagnostics ( state )
325
368
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' ) {
327
371
delete state [ key ]
328
372
}
329
373
} )
330
374
state . enabled = false
331
- registerCapabilities ( )
332
- // TODO reset watcher (remove config dependencies)
375
+ registerCapabilities ( state . dependencies )
333
376
}
334
377
335
378
async function tryInit ( ) {
@@ -813,10 +856,10 @@ async function createProjectService(
813
856
}
814
857
815
858
if ( state . dependencies ) {
816
- watcher ?. unwatch ( state . dependencies )
859
+ chokidarWatcher ?. unwatch ( state . dependencies )
817
860
}
818
861
state . dependencies = getModuleDependencies ( state . configPath )
819
- watcher ?. add ( state . dependencies )
862
+ chokidarWatcher ?. add ( state . dependencies )
820
863
821
864
state . configId = getConfigId ( state . configPath , state . dependencies )
822
865
@@ -837,6 +880,11 @@ async function createProjectService(
837
880
return {
838
881
state,
839
882
tryInit,
883
+ dispose ( ) {
884
+ for ( let { dispose } of disposables ) {
885
+ dispose ( )
886
+ }
887
+ } ,
840
888
onUpdateSettings ( settings : any ) : void {
841
889
documentSettingsCache . clear ( )
842
890
if ( state . enabled ) {
@@ -1279,7 +1327,9 @@ class TW {
1279
1327
}
1280
1328
1281
1329
dispose ( ) : void {
1282
- //
1330
+ for ( let [ , project ] of this . projects ) {
1331
+ project . dispose ( )
1332
+ }
1283
1333
}
1284
1334
}
1285
1335
0 commit comments