Skip to content

Commit 7fac133

Browse files
committed
Add support for new loadModule and loadStylesheet APIs
wip wip wip wip
1 parent 713729b commit 7fac133

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(
@@ -199,16 +210,63 @@ async function loadV4(
199210
// If the user doesn't define an entrypoint then we use the default theme
200211
entryPoint = entryPoint ?? `${pkgDir}/theme.css`
201212

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

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

220278
loadConfig: createLoader({
279+
legacy: true,
221280
filepath: entryPoint,
222281
onError(id, err) {
223282
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)