From d18fd513a4a09880e878252d76db59b5055c4a9c Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 15 Sep 2022 13:55:15 -0400 Subject: [PATCH] Add option to load config file relative to the input file --- src/featureFlags.js | 1 + src/lib/setupTrackingContext.js | 6 ++--- src/util/resolveConfigPath.js | 44 ++++++++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/featureFlags.js b/src/featureFlags.js index bd75108ea..fef84b149 100644 --- a/src/featureFlags.js +++ b/src/featureFlags.js @@ -15,6 +15,7 @@ let featureFlags = { 'optimizeUniversalDefaults', 'matchVariant', // 'variantGrouping', + 'resolveConfigRelativeToInput', ], } diff --git a/src/lib/setupTrackingContext.js b/src/lib/setupTrackingContext.js index 37b42cdba..14cd92984 100644 --- a/src/lib/setupTrackingContext.js +++ b/src/lib/setupTrackingContext.js @@ -35,8 +35,8 @@ function getCandidateFiles(context, tailwindConfig) { } // Get the config object based on a path -function getTailwindConfig(configOrPath) { - let userConfigPath = resolveConfigPath(configOrPath) +function getTailwindConfig(configOrPath, inputPath) { + let userConfigPath = resolveConfigPath(configOrPath, inputPath) if (userConfigPath !== null) { let [prevConfig, prevConfigHash, prevDeps, prevModified] = @@ -119,7 +119,7 @@ export default function setupTrackingContext(configOrPath) { return ({ tailwindDirectives, registerDependency }) => { return (root, result) => { let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] = - getTailwindConfig(configOrPath) + getTailwindConfig(configOrPath, result.opts.from) let contextDependencies = new Set(configDependencies) diff --git a/src/util/resolveConfigPath.js b/src/util/resolveConfigPath.js index 646a46fd4..f622f75f0 100644 --- a/src/util/resolveConfigPath.js +++ b/src/util/resolveConfigPath.js @@ -1,5 +1,6 @@ import fs from 'fs' import path from 'path' +import { flagEnabled } from '../featureFlags.js' function isObject(value) { return typeof value === 'object' && value !== null @@ -13,7 +14,42 @@ function isString(value) { return typeof value === 'string' || value instanceof String } -export default function resolveConfigPath(pathOrConfig) { +/** + * This will take a possibly-relative path to a config and resolve + * it relative to the input path IF that config file exists and + * has the `resolveConfigRelativeToInput` flag enabled. + * + * If that file does not exist, or the flag is disabled, it will + * resolve the path relative to the current working directory. + * + * @param {string|undefined} inputPath + * @param {string} configPath + * @returns {string} + */ +function pickResolvedPath(configPath, inputPath) { + if (path.isAbsolute(configPath)) { + return configPath + } + + if (inputPath) { + try { + // Use require.resolve so we can find config file in parent directories + let resolvedPath = require.resolve(configPath, { + paths: [inputPath], + }) + + let maybeConfig = require(resolvedPath) + + if (typeof maybeConfig === 'object' && flagEnabled(maybeConfig, 'resolveConfigRelativeToInput')) { + return resolvedPath + } + } catch (e) {} + } + + return path.resolve(configPath) +} + +export default function resolveConfigPath(pathOrConfig, inputPath) { // require('tailwindcss')({ theme: ..., variants: ... }) if (isObject(pathOrConfig) && pathOrConfig.config === undefined && !isEmpty(pathOrConfig)) { return null @@ -25,7 +61,7 @@ export default function resolveConfigPath(pathOrConfig) { pathOrConfig.config !== undefined && isString(pathOrConfig.config) ) { - return path.resolve(pathOrConfig.config) + return pickResolvedPath(pathOrConfig.config, inputPath) } // require('tailwindcss')({ config: { theme: ..., variants: ... } }) @@ -39,13 +75,13 @@ export default function resolveConfigPath(pathOrConfig) { // require('tailwindcss')('custom-config.js') if (isString(pathOrConfig)) { - return path.resolve(pathOrConfig) + return pickResolvedPath(pathOrConfig, inputPath) } // require('tailwindcss') for (const configFile of ['./tailwind.config.js', './tailwind.config.cjs']) { try { - const configPath = path.resolve(configFile) + const configPath = pickResolvedPath(configFile, inputPath) fs.accessSync(configPath) return configPath } catch (err) {}