Skip to content

Commit 8764dc0

Browse files
Perform a soft reload of some project state when editing theme files (tailwindlabs#918)
* Perform a soft reload of some project state when editing theme files * Format --------- Co-authored-by: Kris Braun <kris.braun@gmail.com>
1 parent c67c85f commit 8764dc0

File tree

2 files changed

+101
-29
lines changed

2 files changed

+101
-29
lines changed

packages/tailwindcss-language-server/src/projects.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ export interface ProjectService {
108108
onCodeAction(params: CodeActionParams): Promise<CodeAction[]>
109109
onDocumentLinks(params: DocumentLinkParams): DocumentLink[]
110110
sortClassLists(classLists: string[]): string[]
111+
112+
reload(): Promise<void>
111113
}
112114

113115
export enum DocumentSelectorPriority {
@@ -975,6 +977,42 @@ export async function createProjectService(
975977
enable() {
976978
enabled = true
977979
},
980+
981+
async reload() {
982+
if (!state.v4) return
983+
984+
console.log('---- RELOADING DESIGN SYSTEM ----')
985+
let css = await readCssFile(state.configPath)
986+
let designSystem = await loadDesignSystem(
987+
state.modules.tailwindcss.module,
988+
state.configPath,
989+
css,
990+
)
991+
992+
// TODO: This is weird and should be changed
993+
// We use Object.create so no global state is mutated until necessary
994+
let pseudoState = Object.create(state, {
995+
designSystem: {
996+
value: designSystem,
997+
},
998+
})
999+
1000+
let classList = designSystem.getClassList().map((className) => {
1001+
return [
1002+
className[0],
1003+
{
1004+
...className[1],
1005+
color: getColor(pseudoState, className[0]),
1006+
},
1007+
]
1008+
})
1009+
1010+
state.designSystem = designSystem
1011+
state.classList = classList as any
1012+
1013+
console.log('---- RELOADED ----')
1014+
},
1015+
9781016
state,
9791017
documentSelector() {
9801018
return documentSelector

packages/tailwindcss-language-server/src/tw.ts

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export class TW {
177177
isDefaultVersion: false,
178178
},
179179
}
180-
},
180+
}
181181
)
182182
} else {
183183
console.log("Searching for Tailwind CSS projects in the workspace's folders.")
@@ -224,9 +224,10 @@ export class TW {
224224
console.log(`[Global] Creating projects: ${JSON.stringify(workspaceDescription)}`)
225225

226226
const onDidChangeWatchedFiles = async (
227-
changes: Array<{ file: string; type: FileChangeType }>,
227+
changes: Array<{ file: string; type: FileChangeType }>
228228
): Promise<void> => {
229229
let needsRestart = false
230+
let needsSoftRestart = false
230231

231232
changeLoop: for (let change of changes) {
232233
let normalizedFilename = normalizePath(change.file)
@@ -242,12 +243,10 @@ export class TW {
242243
for (let [, project] of this.projects) {
243244
let twVersion = require('tailwindcss/package.json').version
244245
try {
245-
let v = require(
246-
resolveFrom(
247-
path.dirname(project.projectConfig.configPath),
248-
'tailwindcss/package.json',
249-
),
250-
).version
246+
let v = require(resolveFrom(
247+
path.dirname(project.projectConfig.configPath),
248+
'tailwindcss/package.json'
249+
)).version
251250
if (typeof v === 'string') {
252251
twVersion = v
253252
}
@@ -259,6 +258,20 @@ export class TW {
259258
}
260259
}
261260

261+
for (let [, project] of this.projects) {
262+
if (!project.state.v4) continue
263+
264+
let reloadableFiles = [
265+
project.projectConfig.configPath,
266+
...project.projectConfig.config.entries.map((entry) => entry.path),
267+
]
268+
269+
if (!changeAffectsFile(normalizedFilename, reloadableFiles)) continue
270+
271+
needsSoftRestart = true
272+
break changeLoop
273+
}
274+
262275
let isCssFile = minimatch(normalizedFilename, `**/${CSS_GLOB}`, {
263276
dot: true,
264277
})
@@ -300,6 +313,15 @@ export class TW {
300313
return
301314
}
302315

316+
if (needsSoftRestart) {
317+
try {
318+
await this.softRestart()
319+
} catch {
320+
this.restart()
321+
}
322+
return
323+
}
324+
303325
for (let [, project] of this.projects) {
304326
project.onFileEvents(changes)
305327
}
@@ -316,11 +338,11 @@ export class TW {
316338
.filter(
317339
(change, changeIndex, changes) =>
318340
changes.findIndex((c) => c.file === change.file && c.type === change.type) ===
319-
changeIndex,
341+
changeIndex
320342
)
321343

322344
await onDidChangeWatchedFiles(normalizedChanges)
323-
}),
345+
})
324346
)
325347

326348
let disposable = await this.connection.client.register(
@@ -331,7 +353,7 @@ export class TW {
331353
{ globPattern: `**/${PACKAGE_LOCK_GLOB}` },
332354
{ globPattern: `**/${CSS_GLOB}` },
333355
],
334-
},
356+
}
335357
)
336358

337359
this.disposables.push(disposable)
@@ -360,14 +382,14 @@ export class TW {
360382
base,
361383
(err, events) => {
362384
onDidChangeWatchedFiles(
363-
events.map((event) => ({ file: event.path, type: typeMap[event.type] })),
385+
events.map((event) => ({ file: event.path, type: typeMap[event.type] }))
364386
)
365387
},
366388
{
367389
ignore: ignore.map((ignorePattern) =>
368-
path.resolve(base, ignorePattern.replace(/^[*/]+/, '').replace(/[*/]+$/, '')),
390+
path.resolve(base, ignorePattern.replace(/^[*/]+/, '').replace(/[*/]+$/, ''))
369391
),
370-
},
392+
}
371393
)
372394

373395
this.disposables.push({
@@ -388,7 +410,7 @@ export class TW {
388410
stabilityThreshold: 100,
389411
pollInterval: 20,
390412
},
391-
},
413+
}
392414
)
393415

394416
await new Promise<void>((resolve) => {
@@ -399,17 +421,17 @@ export class TW {
399421
.on('add', (file) =>
400422
onDidChangeWatchedFiles([
401423
{ file: path.resolve(base, file), type: FileChangeType.Created },
402-
]),
424+
])
403425
)
404426
.on('change', (file) =>
405427
onDidChangeWatchedFiles([
406428
{ file: path.resolve(base, file), type: FileChangeType.Changed },
407-
]),
429+
])
408430
)
409431
.on('unlink', (file) =>
410432
onDidChangeWatchedFiles([
411433
{ file: path.resolve(base, file), type: FileChangeType.Deleted },
412-
]),
434+
])
413435
)
414436

415437
this.disposables.push({
@@ -433,9 +455,9 @@ export class TW {
433455
projectConfig,
434456
this.initializeParams,
435457
this.watchPatterns,
436-
configTailwindVersionMap.get(projectConfig.configPath),
437-
),
438-
),
458+
configTailwindVersionMap.get(projectConfig.configPath)
459+
)
460+
)
439461
)
440462

441463
// init projects for documents that are _already_ open
@@ -465,19 +487,19 @@ export class TW {
465487
for (let [, project] of this.projects) {
466488
project.onUpdateSettings(settings)
467489
}
468-
}),
490+
})
469491
)
470492

471493
this.disposables.push(
472494
this.connection.onShutdown(() => {
473495
this.dispose()
474-
}),
496+
})
475497
)
476498

477499
this.disposables.push(
478500
this.documentService.onDidChangeContent((change) => {
479501
this.getProject(change.document)?.provideDiagnostics(change.document)
480-
}),
502+
})
481503
)
482504

483505
this.disposables.push(
@@ -487,7 +509,7 @@ export class TW {
487509
project.enable()
488510
project.tryInit()
489511
}
490-
}),
512+
})
491513
)
492514
}
493515

@@ -501,7 +523,7 @@ export class TW {
501523
projectConfig: ProjectConfig,
502524
params: InitializeParams,
503525
watchPatterns: (patterns: string[]) => void,
504-
tailwindVersion: string,
526+
tailwindVersion: string
505527
): Promise<void> {
506528
let key = String(this.projectCounter++)
507529
const project = await createProjectService(
@@ -524,7 +546,7 @@ export class TW {
524546
() => this.refreshDiagnostics(),
525547
(patterns: string[]) => watchPatterns(patterns),
526548
tailwindVersion,
527-
this.settingsCache.get,
549+
this.settingsCache.get
528550
)
529551
this.projects.set(key, project)
530552

@@ -571,11 +593,11 @@ export class TW {
571593

572594
private onRequest(
573595
method: '@/tailwindCSS/sortSelection',
574-
params: { uri: string; classLists: string[] },
596+
params: { uri: string; classLists: string[] }
575597
): { error: string } | { classLists: string[] }
576598
private onRequest(
577599
method: '@/tailwindCSS/getProject',
578-
params: { uri: string },
600+
params: { uri: string }
579601
): { version: string } | null
580602
private onRequest(method: string, params: any): any {
581603
if (method === '@/tailwindCSS/sortSelection') {
@@ -776,6 +798,18 @@ export class TW {
776798
this.initPromise = undefined
777799
this.init()
778800
}
801+
802+
async softRestart(): Promise<void> {
803+
// Tell each v4 project to reload it's design system
804+
for (let [, project] of this.projects) {
805+
if (!project.state.v4) continue
806+
807+
// "soft"-reload the project
808+
try {
809+
await project.reload()
810+
} catch {}
811+
}
812+
}
779813
}
780814

781815
function supportsDynamicRegistration(params: InitializeParams): boolean {

0 commit comments

Comments
 (0)