Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/tailwindcss-intellisense/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
71 changes: 71 additions & 0 deletions packages/tailwindcss-intellisense/src/class-names/environment.js
Original file line number Diff line number Diff line change
@@ -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)
},
})
}
38 changes: 14 additions & 24 deletions packages/tailwindcss-intellisense/src/class-names/getPlugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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()
Expand Down
162 changes: 98 additions & 64 deletions packages/tailwindcss-intellisense/src/class-names/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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 (
Expand All @@ -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,
Expand All @@ -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']
Expand All @@ -94,45 +76,68 @@ export default async function getClassNames(

hook.unwatch()

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()
}

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
}

const resolvedConfig = resolveConfig({ cwd: configDir, config })
const browserslist = browserslistModule
? browserslistModule(undefined, {
path: configDir,
})
: []
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()
}

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
}

const resolvedConfig = resolveConfig({ cwd: configDir, config })
const browserslist = browserslistModule
? browserslistModule(undefined, {
path: configDir,
})
: []

return {
base,
components,
utilities,
resolvedConfig,
postcss,
browserslist,
}
}
)

return {
version,
Expand All @@ -154,7 +159,6 @@ export default async function getClassNames(
browserslist,
}),
modules: {
tailwindcss,
postcss,
},
featureFlags,
Expand Down Expand Up @@ -193,11 +197,41 @@ export default async function getClassNames(
let result
try {
result = await run()
} catch (_) {
} catch (e) {
console.log(e)
return null
}

watch([result.configPath, ...result.dependencies])

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 })
})
}
Loading