1
1
// @ts -check
2
2
import * as fs from 'fs/promises'
3
- import { createRequire } from 'module'
4
3
import * as path from 'path'
5
4
import { pathToFileURL } from 'url'
6
5
import clearModule from 'clear-module'
@@ -18,11 +17,9 @@ import loadConfigFallback from 'tailwindcss/loadConfig'
18
17
import resolveConfigFallback from 'tailwindcss/resolveConfig'
19
18
import type { RequiredConfig } from 'tailwindcss/types/config.js'
20
19
import { expiringMap } from './expiring-map.js'
21
- import { resolveFrom , resolveIn } from './resolve'
20
+ import { resolveCssFrom , resolveJsFrom } from './resolve'
22
21
import type { ContextContainer } from './types'
23
22
24
- let localRequire = createRequire ( import . meta. url )
25
-
26
23
let sourceToPathMap = new Map < string , string | null > ( )
27
24
let sourceToEntryMap = new Map < string , string | null > ( )
28
25
let pathToContextMap = expiringMap < string | null , ContextContainer > ( 10_000 )
@@ -107,7 +104,7 @@ async function loadTailwindConfig(
107
104
let tailwindConfig : RequiredConfig = { content : [ ] }
108
105
109
106
try {
110
- let pkgFile = resolveIn ( 'tailwindcss/package.json' , [ baseDir ] )
107
+ let pkgFile = resolveJsFrom ( baseDir , 'tailwindcss/package.json' )
111
108
let pkgDir = path . dirname ( pkgFile )
112
109
113
110
try {
@@ -151,29 +148,40 @@ async function loadTailwindConfig(
151
148
* Create a loader function that can load plugins and config files relative to
152
149
* the CSS file that uses them. However, we don't want missing files to prevent
153
150
* everything from working so we'll let the error handler decide how to proceed.
154
- *
155
- * @param {object } param0
156
- * @returns
157
151
*/
158
152
function createLoader < T > ( {
153
+ legacy,
159
154
filepath,
160
155
onError,
161
156
} : {
157
+ legacy : boolean
162
158
filepath : string
163
- onError : ( id : string , error : unknown ) => T
159
+ onError : ( id : string , error : unknown , resourceType : string ) => T
164
160
} ) {
165
- let baseDir = path . dirname ( filepath )
166
161
let cacheKey = `${ + Date . now ( ) } `
167
162
168
- return async function loadFile ( id : string ) {
163
+ async function loadFile ( id : string , base : string , resourceType : string ) {
169
164
try {
170
- let resolved = resolveFrom ( baseDir , id )
165
+ let resolved = resolveJsFrom ( base , id )
166
+
171
167
let url = pathToFileURL ( resolved )
172
168
url . searchParams . append ( 't' , cacheKey )
173
169
174
170
return await import ( url . href ) . then ( ( m ) => m . default ?? m )
175
171
} catch ( err ) {
176
- return onError ( id , err )
172
+ return onError ( id , err , resourceType )
173
+ }
174
+ }
175
+
176
+ if ( legacy ) {
177
+ let baseDir = path . dirname ( filepath )
178
+ return ( id : string ) => loadFile ( id , baseDir , 'module' )
179
+ }
180
+
181
+ return async ( id : string , base : string , resourceType : string ) => {
182
+ return {
183
+ base,
184
+ module : await loadFile ( id , base , resourceType ) ,
177
185
}
178
186
}
179
187
}
@@ -184,7 +192,8 @@ async function loadV4(
184
192
entryPoint : string | null ,
185
193
) {
186
194
// Import Tailwind — if this is v4 it'll have APIs we can use directly
187
- let pkgPath = resolveIn ( 'tailwindcss' , [ baseDir ] )
195
+ let pkgPath = resolveJsFrom ( baseDir , 'tailwindcss' )
196
+
188
197
let tw = await import ( pathToFileURL ( pkgPath ) . toString ( ) )
189
198
190
199
// This is not Tailwind v4
@@ -195,15 +204,63 @@ async function loadV4(
195
204
// If the user doesn't define an entrypoint then we use the default theme
196
205
entryPoint = entryPoint ?? `${ pkgDir } /theme.css`
197
206
207
+ let importBasePath = path . dirname ( entryPoint )
208
+
198
209
// Resolve imports in the entrypoint to a flat CSS tree
199
210
let css = await fs . readFile ( entryPoint , 'utf-8' )
200
- let resolveImports = postcss ( [ postcssImport ( ) ] )
201
- let result = await resolveImports . process ( css , { from : entryPoint } )
211
+
212
+ // Determine if the v4 API supports resolving `@import`
213
+ let supportsImports = false
214
+ try {
215
+ await tw . __unstable__loadDesignSystem ( '@import "./empty";' , {
216
+ loadStylesheet : ( ) => {
217
+ supportsImports = true
218
+ return {
219
+ base : importBasePath ,
220
+ content : '' ,
221
+ }
222
+ } ,
223
+ } )
224
+ } catch { }
225
+
226
+ if ( ! supportsImports ) {
227
+ let resolveImports = postcss ( [ postcssImport ( ) ] )
228
+ let result = await resolveImports . process ( css , { from : entryPoint } )
229
+ css = result . css
230
+ }
202
231
203
232
// Load the design system and set up a compatible context object that is
204
233
// usable by the rest of the plugin
205
- let design = await tw . __unstable__loadDesignSystem ( result . css , {
234
+ let design = await tw . __unstable__loadDesignSystem ( css , {
235
+ base : importBasePath ,
236
+
237
+ // v4.0.0-alpha.25+
238
+ loadModule : createLoader ( {
239
+ legacy : false ,
240
+ filepath : entryPoint ,
241
+ onError : ( id , err , resourceType ) => {
242
+ console . error ( `Unable to load ${ resourceType } : ${ id } ` , err )
243
+
244
+ if ( resourceType === 'config' ) {
245
+ return { }
246
+ } else if ( resourceType === 'plugin' ) {
247
+ return ( ) => { }
248
+ }
249
+ } ,
250
+ } ) ,
251
+
252
+ loadStylesheet : async ( id : string , base : string ) => {
253
+ let resolved = resolveCssFrom ( base , id )
254
+
255
+ return {
256
+ base : path . dirname ( resolved ) ,
257
+ content : await fs . readFile ( resolved , 'utf-8' ) ,
258
+ }
259
+ } ,
260
+
261
+ // v4.0.0-alpha.24 and below
206
262
loadPlugin : createLoader ( {
263
+ legacy : true ,
207
264
filepath : entryPoint ,
208
265
onError ( id , err ) {
209
266
console . error ( `Unable to load plugin: ${ id } ` , err )
@@ -213,6 +270,7 @@ async function loadV4(
213
270
} ) ,
214
271
215
272
loadConfig : createLoader ( {
273
+ legacy : true ,
216
274
filepath : entryPoint ,
217
275
onError ( id , err ) {
218
276
console . error ( `Unable to load config: ${ id } ` , err )
0 commit comments