This commit is contained in:
Jordan Pittman 2022-09-21 13:32:04 -04:00
parent 6bd3327d8e
commit df35325e27
9 changed files with 72 additions and 16 deletions

View file

@ -10,6 +10,7 @@ let featureFlags = {
'hoverOnlyWhenSupported',
'respectDefaultRingColorOpacity',
'disableColorOpacityUtilitiesByDefault',
'relativeContentPathsByDefault',
],
experimental: [
'optimizeUniversalDefaults',

View file

@ -1,6 +1,7 @@
import setupTrackingContext from './lib/setupTrackingContext'
import processTailwindFeatures from './processTailwindFeatures'
import { env } from './lib/sharedState'
import { findAtConfigPath } from './lib/findAtConfigPath'
module.exports = function tailwindcss(configOrPath) {
return {
@ -13,6 +14,10 @@ module.exports = function tailwindcss(configOrPath) {
return root
},
function (root, result) {
// Use the path for the `@config` directive if it exists, otherwise use the
// path for the file being processed
configOrPath = findAtConfigPath(root, result) ?? configOrPath
let context = setupTrackingContext(configOrPath)
if (root.type === 'document') {

View file

@ -116,7 +116,7 @@ function resolveRelativePaths(context, contentPaths) {
let resolveFrom = []
// Resolve base paths relative to the config file (when possible) if the experimental flag is enabled
if (context.userConfigPath && context.postcssOptions?.experimental?.contextualPaths) {
if (context.userConfigPath && context.tailwindConfig.content.relative) {
resolveFrom = [path.dirname(context.userConfigPath)]
}

View file

@ -0,0 +1,33 @@
import path from 'path'
/**
* Find the @config at-rule in the given CSS AST and return the relative path to the config file
*
*
* @param {import('postcss').Root} root
* @param {import('postcss').Result} result
*/
export function findAtConfigPath(root, result) {
let configPath = null
let relativeTo = root.source.input.file ?? result.opts.from ?? null
root.walkAtRules('config', (rule) => {
if (relativeTo === undefined) {
throw rule.error(
'The `@config` at-rule cannot be used without a `from` option being set on the PostCSS config.'
)
}
if (configPath) {
throw rule.error('Only `@config` at-rule is allowed per file.')
}
configPath = rule.params.replace(/^['"]|['"]$/, '')
if (path.isAbsolute(configPath)) {
throw rule.error('The `@config` at-rule cannot be used with an absolute path.')
}
})
return configPath ? path.resolve(path.dirname(relativeTo), configPath) : null
}

View file

@ -940,8 +940,7 @@ export function getContext(
tailwindConfig,
userConfigPath,
tailwindConfigHash,
contextDependencies,
postcssOptions
contextDependencies
) {
let sourcePath = result.opts.from
let isConfigFile = userConfigPath !== null
@ -1003,7 +1002,6 @@ export function getContext(
Object.assign(context, {
userConfigPath,
postcssOptions,
})
trackModified([...contextDependencies], getFileModifiedMap(context))

View file

@ -27,7 +27,6 @@ function getCandidateFiles(context, tailwindConfig) {
// Get the config object based on a path
function getTailwindConfig(configOrPath, inputPath) {
let userConfigPath = resolveConfigPath(configOrPath, inputPath)
let postcssOptions = typeof configOrPath === 'object' && configOrPath !== null ? configOrPath : {}
if (userConfigPath !== null) {
let [prevConfig, prevConfigHash, prevDeps, prevModified] =
@ -47,7 +46,7 @@ function getTailwindConfig(configOrPath, inputPath) {
// It hasn't changed (based on timestamps)
if (!modified) {
return [prevConfig, userConfigPath, prevConfigHash, prevDeps, postcssOptions]
return [prevConfig, userConfigPath, prevConfigHash, prevDeps]
}
// It has changed (based on timestamps), or first run
@ -58,7 +57,7 @@ function getTailwindConfig(configOrPath, inputPath) {
newConfig = validateConfig(newConfig)
let newHash = hash(newConfig)
configPathCache.set(userConfigPath, [newConfig, newHash, newDeps, newModified])
return [newConfig, userConfigPath, newHash, newDeps, postcssOptions]
return [newConfig, userConfigPath, newHash, newDeps]
}
// It's a plain object, not a path
@ -68,7 +67,7 @@ function getTailwindConfig(configOrPath, inputPath) {
newConfig = validateConfig(newConfig)
return [newConfig, null, hash(newConfig), [], postcssOptions]
return [newConfig, null, hash(newConfig), []]
}
// DISABLE_TOUCH = TRUE
@ -79,7 +78,7 @@ function getTailwindConfig(configOrPath, inputPath) {
export default function setupTrackingContext(configOrPath) {
return ({ tailwindDirectives, registerDependency }) => {
return (root, result) => {
let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies, postcssOptions] =
let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] =
getTailwindConfig(configOrPath, result.opts.from)
let contextDependencies = new Set(configDependencies)
@ -108,8 +107,7 @@ export default function setupTrackingContext(configOrPath) {
tailwindConfig,
userConfigPath,
tailwindConfigHash,
contextDependencies,
postcssOptions
contextDependencies
)
let candidateFiles = getCandidateFiles(context, tailwindConfig)

View file

@ -56,9 +56,11 @@ export function normalizeConfig(config) {
// When `config.content` is an object
if (typeof config.content === 'object' && config.content !== null) {
// Only `files`, `extract` and `transform` can exist in `config.content`
// Only `files`, `relative`, `extract`, and `transform` can exist in `config.content`
if (
Object.keys(config.content).some((key) => !['files', 'extract', 'transform'].includes(key))
Object.keys(config.content).some(
(key) => !['files', 'extract', 'transform', 'relative'].includes(key)
)
) {
return false
}
@ -112,6 +114,14 @@ export function normalizeConfig(config) {
) {
return false
}
// `config.content.relative` is optional and can be a boolean
if (
typeof config.content.relative !== 'boolean' &&
typeof config.content.relative !== 'undefined'
) {
return false
}
}
return true
@ -154,6 +164,16 @@ export function normalizeConfig(config) {
// Normalize the `content`
config.content = {
relative: (() => {
let { content } = config
if (content?.relative) {
return content.relative
}
return config.future?.relativeContentPathsByDefault ?? false
})(),
files: (() => {
let { content, purge } = config

View file

@ -55,9 +55,9 @@ export default function resolveConfigPath(pathOrConfig, inputPath) {
pathOrConfig.config !== undefined &&
isString(pathOrConfig.config)
) {
if (pathOrConfig.experimental?.contextualPaths) {
return pickResolvedPath(pathOrConfig.config, inputPath)
}
// if (pathOrConfig.experimental?.contextualPaths) {
// return pickResolvedPath(pathOrConfig.config, inputPath)
// }
return path.resolve(pathOrConfig.config)
}

1
types/config.d.ts vendored
View file

@ -31,6 +31,7 @@ type ContentConfig =
| (FilePath | RawFile)[]
| {
files: (FilePath | RawFile)[]
relative?: boolean
extract?: ExtractorFn | { [extension: string]: ExtractorFn }
transform?: TransformerFn | { [extension: string]: TransformerFn }
}