Skip to content

Commit

Permalink
Patch createRequire() and loadConfig() to handle builtin modules in t…
Browse files Browse the repository at this point in the history
…he standalone CLI
  • Loading branch information
thecrypticace committed Nov 29, 2023
1 parent 817c466 commit 1d0d289
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 8 deletions.
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
52 changes: 52 additions & 0 deletions standalone-cli/patch-require.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const Module = require("node:module")

Check failure on line 1 in standalone-cli/patch-require.js

View workflow job for this annotation

GitHub Actions / build (18)

Replace `"node:module"` with `'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) {

Check failure on line 22 in standalone-cli/patch-require.js

View workflow job for this annotation

GitHub Actions / build (18)

Delete `·`
// 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)
}

Check failure on line 35 in standalone-cli/patch-require.js

View workflow job for this annotation

GitHub Actions / build (18)

Insert `,`
}
)
}

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(

Check failure on line 48 in standalone-cli/patch-require.js

View workflow job for this annotation

GitHub Actions / build (18)

Replace `⏎······origCreateRequire.apply(this,·arguments)⏎····` with `origCreateRequire.apply(this,·arguments)`
origCreateRequire.apply(this, arguments)
)
}
}
39 changes: 31 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,36 @@ 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")

Check failure on line 28 in standalone-cli/standalone.js

View workflow job for this annotation

GitHub Actions / build (18)

Replace `"tailwindcss/lib/lib/load-config"` with `'tailwindcss/lib/lib/load-config'`
let { transform } = require('sucrase')

useCustomJiti(() => require('jiti')(__filename, {

Check failure on line 31 in standalone-cli/standalone.js

View workflow job for this annotation

GitHub Actions / build (18)

Insert `⏎·`
interopDefault: true,

Check failure on line 32 in standalone-cli/standalone.js

View workflow job for this annotation

GitHub Actions / build (18)

Insert `··`
nativeModules: Object.keys(localModules),

Check failure on line 33 in standalone-cli/standalone.js

View workflow job for this annotation

GitHub Actions / build (18)

Insert `··`
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 1d0d289

Please sign in to comment.