Skip to content

Commit e26a1ba

Browse files
committed
Fix loading of built-in plugins when using an ESM or TypeScript config with the Standalone CLI (#12506)
1 parent 2dcb1fc commit e26a1ba

File tree

4 files changed

+92
-8
lines changed

4 files changed

+92
-8
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Don't crash when given applying a variant to a negated version of a simple utility ([#12514](https://github.com/tailwindlabs/tailwindcss/pull/12514))
1717
- Fix support for slashes in arbitrary modifiers ([#12515](https://github.com/tailwindlabs/tailwindcss/pull/12515))
1818
- Fix source maps of variant utilities that come from an `@layer` rule ([#12508](https://github.com/tailwindlabs/tailwindcss/pull/12508))
19+
- Fix loading of built-in plugins when using an ESM or TypeScript config with the Standalone CLI ([#12506](https://github.com/tailwindlabs/tailwindcss/pull/12506))
1920

2021
## [3.3.5] - 2023-10-25
2122

src/lib/load-config.ts

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import { transform } from 'sucrase'
44
import { Config } from '../../types/config'
55

66
let jiti: ReturnType<typeof jitiFactory> | null = null
7+
8+
// @internal
9+
// This WILL be removed in some future release
10+
// If you rely on this your stuff WILL break
11+
export function useCustomJiti(_jiti: ReturnType<typeof jitiFactory>) {
12+
jiti = _jiti
13+
}
14+
715
function lazyJiti() {
816
return (
917
jiti ??

standalone-cli/patch-require.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const Module = require('node:module')
2+
3+
/**
4+
* @param {Record<string, any>} mods
5+
*/
6+
module.exports.patchRequire = function patchRequire(mods, parentCache) {
7+
function wrapRequire(origRequire) {
8+
return Object.assign(
9+
function (id) {
10+
// Patch require(…) to return the cached module
11+
if (mods.hasOwnProperty(id)) {
12+
return mods[id]
13+
}
14+
15+
return origRequire.apply(this, arguments)
16+
},
17+
18+
// Make sure we carry over other properties of the original require(…)
19+
origRequire,
20+
21+
{
22+
resolve(id) {
23+
// Defer to the "parent" require cache when resolving the module
24+
// This also requires that the module be provided as a "native module" to JITI
25+
26+
// The path returned here is VERY important as it ensures that the `isNativeRe` in JITI
27+
// passes which is required for the module to be loaded via the native require(…) function
28+
// Thankfully, the regex just means that it needs to be in a node_modules folder which is true
29+
// even when bundled using Vercel's `pkg`
30+
if (parentCache.hasOwnProperty(id)) {
31+
return parentCache[id].filename
32+
}
33+
34+
return origRequire.resolve.apply(this, arguments)
35+
},
36+
}
37+
)
38+
}
39+
40+
let origRequire = Module.prototype.require
41+
let origCreateRequire = Module.createRequire
42+
43+
// We have to augment the default "require" in every module
44+
Module.prototype.require = wrapRequire(origRequire)
45+
46+
// And any "require" created by the "createRequire" method
47+
Module.createRequire = function () {
48+
return wrapRequire(origCreateRequire.apply(this, arguments))
49+
}
50+
}

standalone-cli/standalone.js

+33-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
let Module = require('module')
2-
let origRequire = Module.prototype.require
31
let log = require('tailwindcss/lib/util/log').default
42

53
let localModules = {
@@ -25,11 +23,38 @@ let localModules = {
2523
tailwindcss: require('tailwindcss'),
2624
}
2725

28-
Module.prototype.require = function (id) {
29-
if (localModules.hasOwnProperty(id)) {
30-
return localModules[id]
31-
}
32-
return origRequire.apply(this, arguments)
33-
}
26+
// Swap out the default JITI implementation with one that has the built-in modules above preloaded as "native modules"
27+
// NOTE: This uses a private, internal API of Tailwind CSS and is subject to change at any time
28+
let { useCustomJiti } = require('tailwindcss/lib/lib/load-config')
29+
let { transform } = require('sucrase')
30+
31+
useCustomJiti(() =>
32+
require('jiti')(__filename, {
33+
interopDefault: true,
34+
nativeModules: Object.keys(localModules),
35+
transform: (opts) => {
36+
return transform(opts.source, {
37+
transforms: ['typescript', 'imports'],
38+
})
39+
},
40+
})
41+
)
42+
43+
let { patchRequire } = require('./patch-require.js')
44+
patchRequire(
45+
// Patch require(…) to return the bundled modules above so they don't need to be installed
46+
localModules,
47+
48+
// Create a require cache that maps module IDs to module objects
49+
// This MUST be done before require is patched to handle caching
50+
Object.fromEntries(
51+
Object.keys(localModules).map((id) => [
52+
id,
53+
id === '@tailwindcss/line-clamp'
54+
? `node_modules/@tailwindcss/line-clamp/`
55+
: require.cache[require.resolve(id)],
56+
])
57+
)
58+
)
3459

3560
require('tailwindcss/lib/cli')

0 commit comments

Comments
 (0)