From 45cc4b6ac7ead1628e0ad711064fc699110e1b61 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 23 Sep 2024 14:03:20 -0400 Subject: [PATCH 1/3] Refactor --- .../src/css/resolve-css-imports.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/tailwindcss-language-server/src/css/resolve-css-imports.ts b/packages/tailwindcss-language-server/src/css/resolve-css-imports.ts index cf0e130a..788b5eea 100644 --- a/packages/tailwindcss-language-server/src/css/resolve-css-imports.ts +++ b/packages/tailwindcss-language-server/src/css/resolve-css-imports.ts @@ -11,10 +11,7 @@ const resolver = createResolver({ const resolveImports = postcss([ postcssImport({ - resolve(id, basedir) { - let paths = resolver.resolveSync({}, basedir, id) - return paths ? paths : id - }, + resolve: (id, base) => resolveCssFrom(base, id), }), fixRelativePaths(), ]) @@ -22,3 +19,7 @@ const resolveImports = postcss([ export function resolveCssImports() { return resolveImports } + +export function resolveCssFrom(base: string, id: string) { + return resolver.resolveSync({}, base, id) || id +} From 92046621ee2618ce0e0cf7a2eac7ca00bd896c08 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 23 Sep 2024 14:05:01 -0400 Subject: [PATCH 2/3] Refactor --- .../src/util/v4/design-system.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/tailwindcss-language-server/src/util/v4/design-system.ts b/packages/tailwindcss-language-server/src/util/v4/design-system.ts index e4c5bd84..1550b97f 100644 --- a/packages/tailwindcss-language-server/src/util/v4/design-system.ts +++ b/packages/tailwindcss-language-server/src/util/v4/design-system.ts @@ -25,31 +25,31 @@ export async function isMaybeV4(css: string): Promise { * Create a loader function that can load plugins and config files relative to * the CSS file that uses them. However, we don't want missing files to prevent * everything from working so we'll let the error handler decide how to proceed. - * - * @param {object} param0 - * @returns */ function createLoader({ filepath, onError, }: { filepath: string - onError: (id: string, error: unknown) => T + onError: (id: string, error: unknown, resourceType: string) => T }) { - let baseDir = path.dirname(filepath) let cacheKey = `${+Date.now()}` - return async function loadFile(id: string) { + async function loadFile(id: string, base: string, resourceType: string) { try { - let resolved = resolveFrom(baseDir, id) + let resolved = resolveFrom(base, id) + let url = pathToFileURL(resolved) url.searchParams.append('t', cacheKey) return await import(url.href).then((m) => m.default ?? m) } catch (err) { - return onError(id, err) + return onError(id, err, resourceType) } } + + let baseDir = path.dirname(filepath) + return (id: string) => loadFile(id, baseDir, 'module') } export async function loadDesignSystem( From 20d7009c99571e7007ca5916bb9f1c07cf68fd91 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 23 Sep 2024 14:05:59 -0400 Subject: [PATCH 3/3] Add support for new `loadModule` and `loadStylesheet` APIs --- .../src/util/v4/design-system.ts | 66 +++++++++++++++++-- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/packages/tailwindcss-language-server/src/util/v4/design-system.ts b/packages/tailwindcss-language-server/src/util/v4/design-system.ts index 1550b97f..c2e94134 100644 --- a/packages/tailwindcss-language-server/src/util/v4/design-system.ts +++ b/packages/tailwindcss-language-server/src/util/v4/design-system.ts @@ -1,8 +1,9 @@ import type { DesignSystem } from '@tailwindcss/language-service/src/util/v4' import postcss from 'postcss' +import * as fs from 'node:fs/promises' import * as path from 'node:path' -import { resolveCssImports } from '../../css' +import { resolveCssFrom, resolveCssImports } from '../../css' import { resolveFrom } from '../resolveFrom' import { pathToFileURL } from 'tailwindcss-language-server/src/utils' @@ -27,9 +28,11 @@ export async function isMaybeV4(css: string): Promise { * everything from working so we'll let the error handler decide how to proceed. */ function createLoader({ + legacy, filepath, onError, }: { + legacy: boolean filepath: string onError: (id: string, error: unknown, resourceType: string) => T }) { @@ -48,8 +51,17 @@ function createLoader({ } } - let baseDir = path.dirname(filepath) - return (id: string) => loadFile(id, baseDir, 'module') + if (legacy) { + let baseDir = path.dirname(filepath) + return (id: string) => loadFile(id, baseDir, 'module') + } + + return async (id: string, base: string, resourceType: string) => { + return { + base, + module: await loadFile(id, base, resourceType), + } + } } export async function loadDesignSystem( @@ -65,14 +77,53 @@ export async function loadDesignSystem( return null } + let supportsImports = false + try { + await tailwindcss.__unstable__loadDesignSystem(css, { + loadStylesheet: async (id: string, base: string) => { + supportsImports = true + return { base, content: '' } + }, + }) + } catch {} + // Step 2: Use postcss to resolve `@import` rules in the CSS file - // TODO: What if someone is actively editing their config and introduces a syntax error? - // We don't want to necessarily throw away the knowledge that we have a v4 project. - let resolved = await resolveCssImports().process(css, { from: filepath }) + if (!supportsImports) { + let resolved = await resolveCssImports().process(css, { from: filepath }) + css = resolved.css + } // Step 3: Take the resolved CSS and pass it to v4's `loadDesignSystem` - let design: DesignSystem = await tailwindcss.__unstable__loadDesignSystem(resolved.css, { + let design: DesignSystem = await tailwindcss.__unstable__loadDesignSystem(css, { + base: path.dirname(filepath), + + // v4.0.0-alpha.25+ + loadModule: createLoader({ + legacy: false, + filepath, + onError: (id, err, resourceType) => { + console.error(`Unable to load ${resourceType}: ${id}`, err) + + if (resourceType === 'config') { + return {} + } else if (resourceType === 'plugin') { + return () => {} + } + }, + }), + + loadStylesheet: async (id: string, base: string) => { + let resolved = resolveCssFrom(base, id) + + return { + base: path.dirname(resolved), + content: await fs.readFile(resolved, 'utf-8'), + } + }, + + // v4.0.0-alpha.24 and below loadPlugin: createLoader({ + legacy: true, filepath, onError(id, err) { console.error(`Unable to load plugin: ${id}`, err) @@ -82,6 +133,7 @@ export async function loadDesignSystem( }), loadConfig: createLoader({ + legacy: true, filepath, onError(id, err) { console.error(`Unable to load config: ${id}`, err)