feat: Yarn PnP support

master
DanSnow 2020-08-23 23:02:58 +08:00
parent b3fef941b6
commit 379e06e3c3
No known key found for this signature in database
GPG Key ID: 2B25949174178EBD
6 changed files with 277 additions and 139 deletions

115
package-lock.json generated
View File

@ -2512,12 +2512,13 @@
} }
}, },
"find-up": { "find-up": {
"version": "3.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true, "dev": true,
"requires": { "requires": {
"locate-path": "^3.0.0" "locate-path": "^6.0.0",
"path-exists": "^4.0.0"
} }
}, },
"for-in": { "for-in": {
@ -4827,13 +4828,12 @@
} }
}, },
"locate-path": { "locate-path": {
"version": "3.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true, "dev": true,
"requires": { "requires": {
"p-locate": "^3.0.0", "p-locate": "^5.0.0"
"path-exists": "^3.0.0"
} }
}, },
"lodash": { "lodash": {
@ -5319,12 +5319,23 @@
} }
}, },
"p-locate": { "p-locate": {
"version": "3.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true, "dev": true,
"requires": { "requires": {
"p-limit": "^2.0.0" "p-limit": "^3.0.2"
},
"dependencies": {
"p-limit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
"integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
}
} }
}, },
"p-try": { "p-try": {
@ -5376,9 +5387,9 @@
"dev": true "dev": true
}, },
"path-exists": { "path-exists": {
"version": "3.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true "dev": true
}, },
"path-is-absolute": { "path-is-absolute": {
@ -5484,6 +5495,42 @@
"dev": true, "dev": true,
"requires": { "requires": {
"find-up": "^3.0.0" "find-up": "^3.0.0"
},
"dependencies": {
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
}
} }
}, },
"pn": { "pn": {
@ -7243,6 +7290,42 @@
"which-module": "^2.0.0", "which-module": "^2.0.0",
"y18n": "^4.0.0", "y18n": "^4.0.0",
"yargs-parser": "^13.1.2" "yargs-parser": "^13.1.2"
},
"dependencies": {
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
}
} }
}, },
"yargs-parser": { "yargs-parser": {

View File

@ -189,6 +189,7 @@
"dset": "^2.0.1", "dset": "^2.0.1",
"esm": "^3.2.25", "esm": "^3.2.25",
"fast-glob": "^3.2.4", "fast-glob": "^3.2.4",
"find-up": "^5.0.0",
"glob-exec": "^0.1.1", "glob-exec": "^0.1.1",
"globalyzer": "^0.1.4", "globalyzer": "^0.1.4",
"globrex": "^0.1.2", "globrex": "^0.1.2",
@ -216,5 +217,6 @@
"vscode-languageclient": "^5.2.1", "vscode-languageclient": "^5.2.1",
"vscode-languageserver": "^5.2.1", "vscode-languageserver": "^5.2.1",
"vscode-uri": "^2.1.1" "vscode-uri": "^2.1.1"
} },
"dependencies": {}
} }

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

View File

@ -2,8 +2,6 @@ import extractClassNames from './extractClassNames'
import Hook from './hook' import Hook from './hook'
import dlv from 'dlv' import dlv from 'dlv'
import dset from 'dset' import dset from 'dset'
import resolveFrom from 'resolve-from'
import importFrom from 'import-from'
import chokidar from 'chokidar' import chokidar from 'chokidar'
import semver from 'semver' import semver from 'semver'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
@ -15,28 +13,16 @@ import * as fs from 'fs'
import { getUtilityConfigMap } from './getUtilityConfigMap' import { getUtilityConfigMap } from './getUtilityConfigMap'
import glob from 'fast-glob' import glob from 'fast-glob'
import normalizePath from 'normalize-path' import normalizePath from 'normalize-path'
import { withUserEnvironment } from './environment'
function arraysEqual(arr1, arr2) { function arraysEqual(arr1, arr2) {
return ( return JSON.stringify(arr1.concat([]).sort()) === JSON.stringify(arr2.concat([]).sort())
JSON.stringify(arr1.concat([]).sort()) ===
JSON.stringify(arr2.concat([]).sort())
)
} }
const CONFIG_GLOB = const CONFIG_GLOB = '**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js'
'**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js'
export default async function getClassNames( export default async function getClassNames(cwd = process.cwd(), { onChange = () => {} } = {}) {
cwd = process.cwd(),
{ onChange = () => {} } = {}
) {
async function run() { async function run() {
let postcss
let tailwindcss
let browserslistModule
let version
let featureFlags = { future: [], experimental: [] }
const configPaths = ( const configPaths = (
await glob(CONFIG_GLOB, { await glob(CONFIG_GLOB, {
cwd, cwd,
@ -53,25 +39,9 @@ export default async function getClassNames(
invariant(configPaths.length > 0, 'No Tailwind CSS config found.') invariant(configPaths.length > 0, 'No Tailwind CSS config found.')
const configPath = configPaths[0] const configPath = configPaths[0]
const configDir = path.dirname(configPath) const configDir = path.dirname(configPath)
const tailwindBase = path.dirname( const { version, featureFlags = { future: [], experimental: [] } } = loadMeta(configDir)
resolveFrom(configDir, 'tailwindcss/package.json')
)
postcss = importFrom(tailwindBase, 'postcss')
tailwindcss = importFrom(configDir, 'tailwindcss')
version = importFrom(configDir, 'tailwindcss/package.json').version
try { const sepLocation = semver.gte(version, '0.99.0') ? ['separator'] : ['options', 'separator']
// this is not required
browserslistModule = importFrom(tailwindBase, 'browserslist')
} catch (_) {}
try {
featureFlags = importFrom(tailwindBase, './lib/featureFlags.js').default
} catch (_) {}
const sepLocation = semver.gte(version, '0.99.0')
? ['separator']
: ['options', 'separator']
let userSeperator let userSeperator
let userPurge let userPurge
let hook = Hook(fs.realpathSync(configPath), (exports) => { let hook = Hook(fs.realpathSync(configPath), (exports) => {
@ -94,22 +64,18 @@ export default async function getClassNames(
hook.unwatch() hook.unwatch()
const { base, components, utilities, resolvedConfig, browserslist, postcss } = await withPackages(
configDir,
async ({ postcss, tailwindcss, browserslistModule }) => {
let postcssResult let postcssResult
try { try {
postcssResult = await Promise.all( postcssResult = await Promise.all(
[ [semver.gte(version, '0.99.0') ? 'base' : 'preflight', 'components', 'utilities'].map((group) =>
semver.gte(version, '0.99.0') ? 'base' : 'preflight',
'components',
'utilities',
].map((group) =>
postcss([tailwindcss(configPath)]).process(`@tailwind ${group};`, { postcss([tailwindcss(configPath)]).process(`@tailwind ${group};`, {
from: undefined, from: undefined,
}) })
) )
) )
} catch (error) {
throw error
} finally { } finally {
hook.unhook() hook.unhook()
} }
@ -134,6 +100,17 @@ export default async function getClassNames(
}) })
: [] : []
return {
base,
components,
utilities,
resolvedConfig,
postcss,
browserslist,
}
}
)
return { return {
version, version,
configPath, configPath,
@ -154,7 +131,6 @@ export default async function getClassNames(
browserslist, browserslist,
}), }),
modules: { modules: {
tailwindcss,
postcss, postcss,
}, },
featureFlags, featureFlags,
@ -164,10 +140,7 @@ export default async function getClassNames(
let watcher let watcher
function watch(files = []) { function watch(files = []) {
unwatch() unwatch()
watcher = chokidar watcher = chokidar.watch(files, { cwd }).on('change', handleChange).on('unlink', handleChange)
.watch(files, { cwd })
.on('change', handleChange)
.on('unlink', handleChange)
} }
function unwatch() { function unwatch() {
if (watcher) { if (watcher) {
@ -193,7 +166,8 @@ export default async function getClassNames(
let result let result
try { try {
result = await run() result = await run()
} catch (_) { } catch (e) {
console.log(e)
return null return null
} }
@ -201,3 +175,31 @@ export default async function getClassNames(
return result return result
} }
function loadMeta(configDir) {
return withUserEnvironment(configDir, ({ require, resolve }) => {
const version = require('tailwindcss/package.json').version
let featureFlags
try {
featureFlags = require('tailwindcss/lib/featureFlags.js').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 * as path from 'path'
import decache from './decache' import decache from './decache'
import { withUserEnvironment } from './environment'
export default function resolveConfig({ cwd, config }) { export default function resolveConfig({ cwd, config }) {
const tailwindBase = path.dirname(
resolveFrom(cwd, 'tailwindcss/package.json')
)
let resolve = (x) => x
if (typeof config === 'string') { if (typeof config === 'string') {
if (!cwd) { if (!cwd) {
cwd = path.dirname(config) cwd = path.dirname(config)
@ -17,21 +11,18 @@ export default function resolveConfig({ cwd, config }) {
config = __non_webpack_require__(config) config = __non_webpack_require__(config)
} }
let resolve = (x) => x
withUserEnvironment(cwd, ({ require }) => {
try { try {
resolve = importFrom(tailwindBase, './resolveConfig.js') resolve = require('tailwindcss/resolveConfig.js')
} catch (_) { } catch (_) {
try { try {
const resolveConfig = importFrom( const resolveConfig = require('tailwindcss/lib/util/resolveConfig.js')
tailwindBase, const defaultConfig = require('tailwindcss/stubs/defaultConfig.stub.js')
'./lib/util/resolveConfig.js'
)
const defaultConfig = importFrom(
tailwindBase,
'./stubs/defaultConfig.stub.js'
)
resolve = (config) => resolveConfig([config, defaultConfig]) resolve = (config) => resolveConfig([config, defaultConfig])
} catch (_) {} } catch (_) {}
} }
})
return resolve(config) return resolve(config)
} }