master
Brad Cornes 2020-11-24 13:27:19 +00:00
commit 321098dac4
5 changed files with 193 additions and 105 deletions

View File

@ -185,6 +185,7 @@
"dset": "^2.0.1",
"esm": "^3.2.25",
"fast-glob": "^3.2.4",
"find-up": "^5.0.0",
"glob-exec": "^0.1.1",
"import-from": "^3.0.0",
"jest": "^25.5.4",

View File

@ -0,0 +1,71 @@
import * as path from 'path'
import Module from 'module'
import findUp from 'find-up'
import resolveFrom from 'resolve-from'
import importFrom from 'import-from'
export function withUserEnvironment(base, cb) {
const pnpPath = findUp.sync('.pnp.js', { cwd: base })
if (pnpPath) {
return withPnpEnvironment(pnpPath, cb)
}
return withNonPnpEnvironment(base, cb)
}
function withPnpEnvironment(pnpPath, cb) {
const basePath = path.dirname(pnpPath)
// pnp will patch `module` and `fs` to load package in pnp environment
// backup the functions which will be patched here
const originalModule = Object.create(null)
originalModule._load = Module._load
originalModule._resolveFilename = Module._resolveFilename
originalModule._findPath = Module._findPath
const pnpapi = __non_webpack_require__(pnpPath)
// get into pnp environment
pnpapi.setup()
// restore the patched function, we can not load any package after called this
const restore = () => Object.assign(Module, originalModule)
const pnpResolve = (request, from = basePath) => {
return pnpapi.resolveRequest(request, from + '/')
}
const pnpRequire = (request, from) => {
return __non_webpack_require__(pnpResolve(request, from))
}
const res = cb({ resolve: pnpResolve, require: pnpRequire })
// check if it return a thenable
if (res != null && res.then) {
return res.then(
(x) => {
restore()
return x
},
(err) => {
restore()
throw err
}
)
}
restore()
return res
}
function withNonPnpEnvironment(base, cb) {
return cb({
require(request, from = base) {
return importFrom(from, request)
},
resolve(request, from = base) {
return resolveFrom(from, request)
},
})
}

View File

@ -2,22 +2,19 @@ import * as path from 'path'
import stackTrace from 'stack-trace'
import pkgUp from 'pkg-up'
import { isObject } from './isObject'
import resolveFrom from 'resolve-from'
import importFrom from 'import-from'
import { withUserEnvironment } from './environment'
export async function getBuiltInPlugins({ cwd, resolvedConfig }) {
const tailwindBase = path.dirname(
resolveFrom(cwd, 'tailwindcss/package.json')
)
try {
// TODO: add v0 support ("generators")
return importFrom(tailwindBase, './lib/corePlugins.js').default({
corePlugins: resolvedConfig.corePlugins,
})
} catch (_) {
return []
}
return withUserEnvironment(cwd, ({ require, resolve }) => {
const tailwindBase = path.dirname(resolve('tailwindcss/package.json'))
try {
return require('./lib/corePlugins.js', tailwindBase).default({
corePlugins: resolvedConfig.corePlugins,
})
} catch (_) {
return []
}
})
}
export default function getPlugins(config) {
@ -34,19 +31,12 @@ export default function getPlugins(config) {
}
let contributes = {
theme: isObject(pluginConfig.theme)
? Object.keys(pluginConfig.theme)
: [],
variants: isObject(pluginConfig.variants)
? Object.keys(pluginConfig.variants)
: [],
theme: isObject(pluginConfig.theme) ? Object.keys(pluginConfig.theme) : [],
variants: isObject(pluginConfig.variants) ? Object.keys(pluginConfig.variants) : [],
}
const fn = plugin.handler || plugin
const fnName =
typeof fn.name === 'string' && fn.name !== 'handler' && fn.name !== ''
? fn.name
: null
const fnName = typeof fn.name === 'string' && fn.name !== 'handler' && fn.name !== '' ? fn.name : null
try {
fn()

View File

@ -2,8 +2,6 @@ import extractClassNames from './extractClassNames'
import Hook from './hook'
import dlv from 'dlv'
import dset from 'dset'
import resolveFrom from 'resolve-from'
import importFrom from 'import-from'
import chokidar from 'chokidar'
import semver from 'semver'
import invariant from 'tiny-invariant'
@ -15,6 +13,7 @@ import * as fs from 'fs'
import { getUtilityConfigMap } from './getUtilityConfigMap'
import glob from 'fast-glob'
import normalizePath from 'normalize-path'
import { withUserEnvironment } from './environment'
function arraysEqual(arr1, arr2) {
return (
@ -31,12 +30,6 @@ export default async function getClassNames(
{ onChange = () => {} } = {}
) {
async function run() {
let postcss
let tailwindcss
let browserslistModule
let version
let featureFlags = { future: [], experimental: [] }
const configPaths = (
await glob(CONFIG_GLOB, {
cwd,
@ -53,21 +46,10 @@ export default async function getClassNames(
invariant(configPaths.length > 0, 'No Tailwind CSS config found.')
const configPath = configPaths[0]
const configDir = path.dirname(configPath)
const tailwindBase = path.dirname(
resolveFrom(configDir, 'tailwindcss/package.json')
)
postcss = importFrom(tailwindBase, 'postcss')
tailwindcss = importFrom(configDir, 'tailwindcss')
version = importFrom(configDir, 'tailwindcss/package.json').version
try {
// this is not required
browserslistModule = importFrom(tailwindBase, 'browserslist')
} catch (_) {}
try {
featureFlags = importFrom(tailwindBase, './lib/featureFlags.js').default
} catch (_) {}
const {
version,
featureFlags = { future: [], experimental: [] },
} = loadMeta(configDir)
const sepLocation = semver.gte(version, '0.99.0')
? ['separator']
@ -94,45 +76,68 @@ export default async function getClassNames(
hook.unwatch()
let postcssResult
const {
base,
components,
utilities,
resolvedConfig,
browserslist,
postcss,
} = await withPackages(
configDir,
async ({ postcss, tailwindcss, browserslistModule }) => {
let postcssResult
try {
postcssResult = await Promise.all(
[
semver.gte(version, '0.99.0') ? 'base' : 'preflight',
'components',
'utilities',
].map((group) =>
postcss([tailwindcss(configPath)]).process(
`@tailwind ${group};`,
{
from: undefined,
}
)
)
)
} catch (error) {
throw error
} finally {
hook.unhook()
}
try {
postcssResult = await Promise.all(
[
semver.gte(version, '0.99.0') ? 'base' : 'preflight',
'components',
'utilities',
].map((group) =>
postcss([tailwindcss(configPath)]).process(`@tailwind ${group};`, {
from: undefined,
})
)
)
} catch (error) {
throw error
} finally {
hook.unhook()
}
const [base, components, utilities] = postcssResult
const [base, components, utilities] = postcssResult
if (typeof userSeperator !== 'undefined') {
dset(config, sepLocation, userSeperator)
} else {
delete config[sepLocation]
}
if (typeof userPurge !== 'undefined') {
config.purge = userPurge
} else {
delete config.purge
}
if (typeof userSeperator !== 'undefined') {
dset(config, sepLocation, userSeperator)
} else {
delete config[sepLocation]
}
if (typeof userPurge !== 'undefined') {
config.purge = userPurge
} else {
delete config.purge
}
const resolvedConfig = resolveConfig({ cwd: configDir, config })
const browserslist = browserslistModule
? browserslistModule(undefined, {
path: configDir,
})
: []
const resolvedConfig = resolveConfig({ cwd: configDir, config })
const browserslist = browserslistModule
? browserslistModule(undefined, {
path: configDir,
})
: []
return {
base,
components,
utilities,
resolvedConfig,
postcss,
browserslist,
}
}
)
return {
version,
@ -154,7 +159,6 @@ export default async function getClassNames(
browserslist,
}),
modules: {
tailwindcss,
postcss,
},
featureFlags,
@ -193,7 +197,8 @@ export default async function getClassNames(
let result
try {
result = await run()
} catch (_) {
} catch (e) {
console.log(e)
return null
}
@ -201,3 +206,32 @@ export default async function getClassNames(
return result
}
function loadMeta(configDir) {
return withUserEnvironment(configDir, ({ require, resolve }) => {
const version = require('tailwindcss/package.json').version
let featureFlags
try {
const tailwindBase = path.dirname(resolve('tailwindcss/package.json'))
featureFlags = require('./lib/featureFlags.js', tailwindBase).default
} catch (_) {}
return { version, featureFlags }
})
}
function withPackages(configDir, cb) {
return withUserEnvironment(configDir, async ({ require, resolve }) => {
const tailwindBase = path.dirname(resolve('tailwindcss/package.json'))
const postcss = require('postcss', tailwindBase)
const tailwindcss = require('tailwindcss')
let browserslistModule
try {
// this is not required
browserslistModule = require('browserslist', tailwindBase)
} catch (_) {}
return cb({ postcss, tailwindcss, browserslistModule })
})
}

View File

@ -1,14 +1,8 @@
import resolveFrom from 'resolve-from'
import importFrom from 'import-from'
import * as path from 'path'
import decache from './decache'
import { withUserEnvironment } from './environment'
export default function resolveConfig({ cwd, config }) {
const tailwindBase = path.dirname(
resolveFrom(cwd, 'tailwindcss/package.json')
)
let resolve = (x) => x
if (typeof config === 'string') {
if (!cwd) {
cwd = path.dirname(config)
@ -17,21 +11,19 @@ export default function resolveConfig({ cwd, config }) {
config = __non_webpack_require__(config)
}
try {
resolve = importFrom(tailwindBase, './resolveConfig.js')
} catch (_) {
let resolve = (x) => x
withUserEnvironment(cwd, ({ require, resolve }) => {
const tailwindBase = path.dirname(resolve('tailwindcss/package.json'))
try {
const resolveConfig = importFrom(
tailwindBase,
'./lib/util/resolveConfig.js'
)
const defaultConfig = importFrom(
tailwindBase,
'./stubs/defaultConfig.stub.js'
)
resolve = (config) => resolveConfig([config, defaultConfig])
} catch (_) {}
}
resolve = require('./resolveConfig.js', tailwindBase)
} catch (_) {
try {
const resolveConfig = require('./lib/util/resolveConfig.js', tailwindBase)
const defaultConfig = require('./stubs/defaultConfig.stub.js', tailwindBase)
resolve = (config) => resolveConfig([config, defaultConfig])
} catch (_) {}
}
})
return resolve(config)
}