From 379e06e3c306926c73e2d0f368caa83eeb69e154 Mon Sep 17 00:00:00 2001 From: DanSnow Date: Sun, 23 Aug 2020 23:02:58 +0800 Subject: [PATCH 1/3] feat: Yarn PnP support --- package-lock.json | 115 +++++++++++++++++++---- package.json | 4 +- src/class-names/environment.js | 71 ++++++++++++++ src/class-names/getPlugins.js | 37 +++----- src/class-names/index.js | 156 ++++++++++++++++--------------- src/class-names/resolveConfig.js | 33 +++---- 6 files changed, 277 insertions(+), 139 deletions(-) create mode 100644 src/class-names/environment.js diff --git a/package-lock.json b/package-lock.json index 0132369..9dcd203 100755 --- a/package-lock.json +++ b/package-lock.json @@ -2512,12 +2512,13 @@ } }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, "for-in": { @@ -4827,13 +4828,12 @@ } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" } }, "lodash": { @@ -5319,12 +5319,23 @@ } }, "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==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "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": { @@ -5376,9 +5387,9 @@ "dev": true }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { @@ -5484,6 +5495,42 @@ "dev": true, "requires": { "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": { @@ -7243,6 +7290,42 @@ "which-module": "^2.0.0", "y18n": "^4.0.0", "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": { diff --git a/package.json b/package.json index ff6fe78..e6569ff 100755 --- a/package.json +++ b/package.json @@ -189,6 +189,7 @@ "dset": "^2.0.1", "esm": "^3.2.25", "fast-glob": "^3.2.4", + "find-up": "^5.0.0", "glob-exec": "^0.1.1", "globalyzer": "^0.1.4", "globrex": "^0.1.2", @@ -216,5 +217,6 @@ "vscode-languageclient": "^5.2.1", "vscode-languageserver": "^5.2.1", "vscode-uri": "^2.1.1" - } + }, + "dependencies": {} } diff --git a/src/class-names/environment.js b/src/class-names/environment.js new file mode 100644 index 0000000..6364d55 --- /dev/null +++ b/src/class-names/environment.js @@ -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) + }, + }) +} diff --git a/src/class-names/getPlugins.js b/src/class-names/getPlugins.js index 1fa3e50..23941fe 100644 --- a/src/class-names/getPlugins.js +++ b/src/class-names/getPlugins.js @@ -2,22 +2,18 @@ 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 }) => { + try { + return require('tailwindcss/lib/corePlugins.js').default({ + corePlugins: resolvedConfig.corePlugins, + }) + } catch (_) { + return [] + } + }) } export default function getPlugins(config) { @@ -34,19 +30,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() diff --git a/src/class-names/index.js b/src/class-names/index.js index 773faee..b7a63c6 100644 --- a/src/class-names/index.js +++ b/src/class-names/index.js @@ -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,28 +13,16 @@ 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 ( - JSON.stringify(arr1.concat([]).sort()) === - JSON.stringify(arr2.concat([]).sort()) - ) + return JSON.stringify(arr1.concat([]).sort()) === JSON.stringify(arr2.concat([]).sort()) } -const CONFIG_GLOB = - '**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js' +const CONFIG_GLOB = '**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js' -export default async function getClassNames( - cwd = process.cwd(), - { onChange = () => {} } = {} -) { +export default async function getClassNames(cwd = process.cwd(), { onChange = () => {} } = {}) { async function run() { - let postcss - let tailwindcss - let browserslistModule - let version - let featureFlags = { future: [], experimental: [] } - const configPaths = ( await glob(CONFIG_GLOB, { cwd, @@ -53,25 +39,9 @@ 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 + const { version, featureFlags = { future: [], experimental: [] } } = loadMeta(configDir) - try { - // 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'] + const sepLocation = semver.gte(version, '0.99.0') ? ['separator'] : ['options', 'separator'] let userSeperator let userPurge let hook = Hook(fs.realpathSync(configPath), (exports) => { @@ -94,45 +64,52 @@ 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, + }) + ) + ) + } 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 +131,6 @@ export default async function getClassNames( browserslist, }), modules: { - tailwindcss, postcss, }, featureFlags, @@ -164,10 +140,7 @@ export default async function getClassNames( let watcher function watch(files = []) { unwatch() - watcher = chokidar - .watch(files, { cwd }) - .on('change', handleChange) - .on('unlink', handleChange) + watcher = chokidar.watch(files, { cwd }).on('change', handleChange).on('unlink', handleChange) } function unwatch() { if (watcher) { @@ -193,7 +166,8 @@ export default async function getClassNames( let result try { result = await run() - } catch (_) { + } catch (e) { + console.log(e) return null } @@ -201,3 +175,31 @@ 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 { + 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 }) + }) +} diff --git a/src/class-names/resolveConfig.js b/src/class-names/resolveConfig.js index 49a3154..48aac87 100644 --- a/src/class-names/resolveConfig.js +++ b/src/class-names/resolveConfig.js @@ -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,18 @@ 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 }) => { 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('tailwindcss/resolveConfig.js') + } catch (_) { + try { + const resolveConfig = require('tailwindcss/lib/util/resolveConfig.js') + const defaultConfig = require('tailwindcss/stubs/defaultConfig.stub.js') + resolve = (config) => resolveConfig([config, defaultConfig]) + } catch (_) {} + } + }) return resolve(config) } From e713b2bed8d4ca04c1ce2c873cf6fce609b42a04 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Thu, 27 Aug 2020 13:31:41 +0100 Subject: [PATCH 2/3] Rethrow postcss error --- src/class-names/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/class-names/index.js b/src/class-names/index.js index b7a63c6..4ff3211 100644 --- a/src/class-names/index.js +++ b/src/class-names/index.js @@ -76,6 +76,8 @@ export default async function getClassNames(cwd = process.cwd(), { onChange = () }) ) ) + } catch (error) { + throw error } finally { hook.unhook() } From 7ba8b4d8f883b5f049666edcc31d7f71d0237141 Mon Sep 17 00:00:00 2001 From: DanSnow Date: Thu, 27 Aug 2020 20:39:00 +0800 Subject: [PATCH 3/3] fix: Add back pnpm support --- src/class-names/getPlugins.js | 5 +++-- src/class-names/index.js | 3 ++- src/class-names/resolveConfig.js | 9 +++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/class-names/getPlugins.js b/src/class-names/getPlugins.js index 23941fe..5f3bf80 100644 --- a/src/class-names/getPlugins.js +++ b/src/class-names/getPlugins.js @@ -5,9 +5,10 @@ import { isObject } from './isObject' import { withUserEnvironment } from './environment' export async function getBuiltInPlugins({ cwd, resolvedConfig }) { - return withUserEnvironment(cwd, ({ require }) => { + return withUserEnvironment(cwd, ({ require, resolve }) => { + const tailwindBase = path.dirname(resolve('tailwindcss/package.json')) try { - return require('tailwindcss/lib/corePlugins.js').default({ + return require('./lib/corePlugins.js', tailwindBase).default({ corePlugins: resolvedConfig.corePlugins, }) } catch (_) { diff --git a/src/class-names/index.js b/src/class-names/index.js index 4ff3211..60553ae 100644 --- a/src/class-names/index.js +++ b/src/class-names/index.js @@ -184,7 +184,8 @@ function loadMeta(configDir) { let featureFlags try { - featureFlags = require('tailwindcss/lib/featureFlags.js').default + const tailwindBase = path.dirname(resolve('tailwindcss/package.json')) + featureFlags = require('./lib/featureFlags.js', tailwindBase).default } catch (_) {} return { version, featureFlags } diff --git a/src/class-names/resolveConfig.js b/src/class-names/resolveConfig.js index 48aac87..6085a00 100644 --- a/src/class-names/resolveConfig.js +++ b/src/class-names/resolveConfig.js @@ -12,13 +12,14 @@ export default function resolveConfig({ cwd, config }) { } let resolve = (x) => x - withUserEnvironment(cwd, ({ require }) => { + withUserEnvironment(cwd, ({ require, resolve }) => { + const tailwindBase = path.dirname(resolve('tailwindcss/package.json')) try { - resolve = require('tailwindcss/resolveConfig.js') + resolve = require('./resolveConfig.js', tailwindBase) } catch (_) { try { - const resolveConfig = require('tailwindcss/lib/util/resolveConfig.js') - const defaultConfig = require('tailwindcss/stubs/defaultConfig.stub.js') + const resolveConfig = require('./lib/util/resolveConfig.js', tailwindBase) + const defaultConfig = require('./stubs/defaultConfig.stub.js', tailwindBase) resolve = (config) => resolveConfig([config, defaultConfig]) } catch (_) {} }