Skip to content

Commit 6e741ac

Browse files
committed
Add support for new loadModule and loadStylesheet APIs
1 parent 2cb390a commit 6e741ac

File tree

2 files changed

+85
-9
lines changed

2 files changed

+85
-9
lines changed

src/config.ts

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import loadConfigFallback from 'tailwindcss/loadConfig'
1717
import resolveConfigFallback from 'tailwindcss/resolveConfig'
1818
import type { RequiredConfig } from 'tailwindcss/types/config.js'
1919
import { expiringMap } from './expiring-map.js'
20-
import { resolveJsFrom } from './resolve'
20+
import { resolveCssFrom, resolveJsFrom } from './resolve'
2121
import type { ContextContainer } from './types'
2222

2323
let sourceToPathMap = new Map<string, string | null>()
@@ -153,15 +153,17 @@ async function loadTailwindConfig(
153153
* @returns
154154
*/
155155
function createLoader<T>({
156+
legacy,
156157
filepath,
157158
onError,
158159
}: {
160+
legacy: boolean
159161
filepath: string
160-
onError: (id: string, error: unknown) => T
162+
onError: (id: string, error: unknown, resourceType: string) => T
161163
}) {
162164
let cacheKey = `${+Date.now()}`
163165

164-
async function loadFile(id: string, base: string) {
166+
async function loadFile(id: string, base: string, resourceType: string) {
165167
try {
166168
let resolved = resolveJsFrom(base, id)
167169

@@ -170,12 +172,21 @@ function createLoader<T>({
170172

171173
return await import(url.href).then((m) => m.default ?? m)
172174
} catch (err) {
173-
return onError(id, err)
175+
return onError(id, err, resourceType)
174176
}
175177
}
176178

177-
let baseDir = path.dirname(filepath)
178-
return (id: string) => loadFile(id, baseDir)
179+
if (legacy) {
180+
let baseDir = path.dirname(filepath)
181+
return (id: string) => loadFile(id, baseDir, 'module')
182+
}
183+
184+
return async (id: string, base: string, resourceType: string) => {
185+
return {
186+
base,
187+
module: await loadFile(id, base, resourceType),
188+
}
189+
}
179190
}
180191

181192
async function loadV4(
@@ -201,16 +212,63 @@ async function loadV4(
201212
// If the user doesn't define an entrypoint then we use the default theme
202213
entryPoint = entryPoint ?? `${pkgDir}/theme.css`
203214

215+
let importBasePath = path.dirname(entryPoint)
216+
204217
// Resolve imports in the entrypoint to a flat CSS tree
205218
let css = await fs.readFile(entryPoint, 'utf-8')
206-
let resolveImports = postcss([postcssImport()])
207-
let result = await resolveImports.process(css, { from: entryPoint })
208-
css = result.css
219+
220+
// Determine if the v4 API supports resolving `@import`
221+
let supportsImports = false
222+
try {
223+
await tw.__unstable__loadDesignSystem('@import "./empty";', {
224+
loadStylesheet: () => {
225+
supportsImports = true
226+
return {
227+
base: importBasePath,
228+
content: '',
229+
}
230+
},
231+
})
232+
} catch {}
233+
234+
if (!supportsImports) {
235+
let resolveImports = postcss([postcssImport()])
236+
let result = await resolveImports.process(css, { from: entryPoint })
237+
css = result.css
238+
}
209239

210240
// Load the design system and set up a compatible context object that is
211241
// usable by the rest of the plugin
212242
let design = await tw.__unstable__loadDesignSystem(css, {
243+
base: importBasePath,
244+
245+
// v4.0.0-alpha.25+
246+
loadModule: createLoader({
247+
legacy: false,
248+
filepath: entryPoint,
249+
onError: (id, err, resourceType) => {
250+
console.error(`Unable to load ${resourceType}: ${id}`, err)
251+
252+
if (resourceType === 'config') {
253+
return {}
254+
} else if (resourceType === 'plugin') {
255+
return () => {}
256+
}
257+
},
258+
}),
259+
260+
loadStylesheet: async (id: string, base: string) => {
261+
let resolved = resolveCssFrom(base, id)
262+
263+
return {
264+
base: path.dirname(resolved),
265+
content: await fs.readFile(resolved, 'utf-8'),
266+
}
267+
},
268+
269+
// v4.0.0-alpha.24 and below
213270
loadPlugin: createLoader({
271+
legacy: true,
214272
filepath: entryPoint,
215273
onError(id, err) {
216274
console.error(`Unable to load plugin: ${id}`, err)
@@ -220,6 +278,7 @@ async function loadV4(
220278
}),
221279

222280
loadConfig: createLoader({
281+
legacy: true,
223282
filepath: entryPoint,
224283
onError(id, err) {
225284
console.error(`Unable to load config: ${id}`, err)

src/resolve.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ const esmResolver = ResolverFactory.createResolver({
1111
conditionNames: ['node', 'import', 'require'],
1212
})
1313

14+
const cssResolver = ResolverFactory.createResolver({
15+
fileSystem: new CachedInputFileSystem(fs, 30_000),
16+
useSyncFileSystemCalls: true,
17+
extensions: ['.css'],
18+
mainFields: ['style'],
19+
conditionNames: ['style'],
20+
})
21+
1422
// This is a long-lived cache for resolved modules whether they exist or not
1523
// Because we're compatible with a large number of plugins, we need to check
1624
// for the existence of a module before attempting to import it. This cache
@@ -48,3 +56,12 @@ export function resolveJsFrom(base: string, id: string): string {
4856
`Cannot find module "${id}" (searching relative to: "${base}")`,
4957
)
5058
}
59+
60+
export function resolveCssFrom(base: string, id: string) {
61+
let result = cssResolver.resolveSync({}, base, id)
62+
if (result) return result
63+
64+
throw new Error(
65+
`Cannot find CSS file "${id}" (searching relative to: "${base}")`,
66+
)
67+
}

0 commit comments

Comments
 (0)