diff --git a/packages/tailwindcss-intellisense/package.json b/packages/tailwindcss-intellisense/package.json index 2d89910..70370d2 100755 --- a/packages/tailwindcss-intellisense/package.json +++ b/packages/tailwindcss-intellisense/package.json @@ -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", diff --git a/packages/tailwindcss-intellisense/src/class-names/environment.js b/packages/tailwindcss-intellisense/src/class-names/environment.js new file mode 100644 index 0000000..6364d55 --- /dev/null +++ b/packages/tailwindcss-intellisense/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/packages/tailwindcss-intellisense/src/class-names/getPlugins.js b/packages/tailwindcss-intellisense/src/class-names/getPlugins.js index 1fa3e50..5f3bf80 100644 --- a/packages/tailwindcss-intellisense/src/class-names/getPlugins.js +++ b/packages/tailwindcss-intellisense/src/class-names/getPlugins.js @@ -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() diff --git a/packages/tailwindcss-intellisense/src/class-names/index.js b/packages/tailwindcss-intellisense/src/class-names/index.js index 773faee..29a06fd 100644 --- a/packages/tailwindcss-intellisense/src/class-names/index.js +++ b/packages/tailwindcss-intellisense/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,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 }) + }) +} diff --git a/packages/tailwindcss-intellisense/src/class-names/resolveConfig.js b/packages/tailwindcss-intellisense/src/class-names/resolveConfig.js index 49a3154..6085a00 100644 --- a/packages/tailwindcss-intellisense/src/class-names/resolveConfig.js +++ b/packages/tailwindcss-intellisense/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,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) }