Skip to content

Commit

Permalink
Fix loading of built-in plugins when using an ESM or TypeScript confi…
Browse files Browse the repository at this point in the history
…g with the Standalone CLI (#12506)
  • Loading branch information
thecrypticace authored Dec 1, 2023
1 parent 317fba1 commit 6dfcdd1
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,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))
- [Oxide] Remove `autoprefixer` dependency ([#11315](https://github.com/tailwindlabs/tailwindcss/pull/11315))
- [Oxide] Fix source maps issue resulting in a crash ([#11319](https://github.com/tailwindlabs/tailwindcss/pull/11319))
- [Oxide] Fallback to RegEx based parser when using custom transformers or extractors ([#11335](https://github.com/tailwindlabs/tailwindcss/pull/11335))
Expand Down
8 changes: 8 additions & 0 deletions src/lib/load-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ??
Expand Down
50 changes: 50 additions & 0 deletions standalone-cli/patch-require.js
Original file line number Diff line number Diff line change
@@ -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))
}
}
41 changes: 33 additions & 8 deletions standalone-cli/standalone.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
let Module = require('module')
let origRequire = Module.prototype.require
let log = require('tailwindcss/lib/util/log').default

let localModules = {
Expand All @@ -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')

0 comments on commit 6dfcdd1

Please sign in to comment.