Fix loading of built-in plugins when using an ESM or TypeScript config with the Standalone CLI (#12506)

This commit is contained in:
Jordan Pittman 2023-12-01 11:44:30 -05:00
parent 2dcb1fcd82
commit e26a1ba5f1
4 changed files with 92 additions and 8 deletions

View file

@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Don't crash when given applying a variant to a negated version of a simple utility ([#12514](https://github.com/tailwindlabs/tailwindcss/pull/12514))
- Fix support for slashes in arbitrary modifiers ([#12515](https://github.com/tailwindlabs/tailwindcss/pull/12515))
- Fix source maps of variant utilities that come from an `@layer` rule ([#12508](https://github.com/tailwindlabs/tailwindcss/pull/12508))
- 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))
## [3.3.5] - 2023-10-25

View file

@ -4,6 +4,14 @@ import { transform } from 'sucrase'
import { Config } from '../../types/config'
let jiti: ReturnType<typeof jitiFactory> | null = null
// @internal
// This WILL be removed in some future release
// If you rely on this your stuff WILL break
export function useCustomJiti(_jiti: ReturnType<typeof jitiFactory>) {
jiti = _jiti
}
function lazyJiti() {
return (
jiti ??

View file

@ -0,0 +1,50 @@
const Module = require('node:module')
/**
* @param {Record<string, any>} mods
*/
module.exports.patchRequire = function patchRequire(mods, parentCache) {
function wrapRequire(origRequire) {
return Object.assign(
function (id) {
// Patch require(…) to return the cached module
if (mods.hasOwnProperty(id)) {
return mods[id]
}
return origRequire.apply(this, arguments)
},
// Make sure we carry over other properties of the original require(…)
origRequire,
{
resolve(id) {
// Defer to the "parent" require cache when resolving the module
// This also requires that the module be provided as a "native module" to JITI
// The path returned here is VERY important as it ensures that the `isNativeRe` in JITI
// passes which is required for the module to be loaded via the native require(…) function
// Thankfully, the regex just means that it needs to be in a node_modules folder which is true
// even when bundled using Vercel's `pkg`
if (parentCache.hasOwnProperty(id)) {
return parentCache[id].filename
}
return origRequire.resolve.apply(this, arguments)
},
}
)
}
let origRequire = Module.prototype.require
let origCreateRequire = Module.createRequire
// We have to augment the default "require" in every module
Module.prototype.require = wrapRequire(origRequire)
// And any "require" created by the "createRequire" method
Module.createRequire = function () {
return wrapRequire(origCreateRequire.apply(this, arguments))
}
}

View file

@ -1,5 +1,3 @@
let Module = require('module')
let origRequire = Module.prototype.require
let log = require('tailwindcss/lib/util/log').default
let localModules = {
@ -25,11 +23,38 @@ let localModules = {
tailwindcss: require('tailwindcss'),
}
Module.prototype.require = function (id) {
if (localModules.hasOwnProperty(id)) {
return localModules[id]
}
return origRequire.apply(this, arguments)
}
// Swap out the default JITI implementation with one that has the built-in modules above preloaded as "native modules"
// NOTE: This uses a private, internal API of Tailwind CSS and is subject to change at any time
let { useCustomJiti } = require('tailwindcss/lib/lib/load-config')
let { transform } = require('sucrase')
useCustomJiti(() =>
require('jiti')(__filename, {
interopDefault: true,
nativeModules: Object.keys(localModules),
transform: (opts) => {
return transform(opts.source, {
transforms: ['typescript', 'imports'],
})
},
})
)
let { patchRequire } = require('./patch-require.js')
patchRequire(
// Patch require(…) to return the bundled modules above so they don't need to be installed
localModules,
// Create a require cache that maps module IDs to module objects
// This MUST be done before require is patched to handle caching
Object.fromEntries(
Object.keys(localModules).map((id) => [
id,
id === '@tailwindcss/line-clamp'
? `node_modules/@tailwindcss/line-clamp/`
: require.cache[require.resolve(id)],
])
)
)
require('tailwindcss/lib/cli')