import { __unstable__loadDesignSystem, compile } from '@tailwindcss/node'
import fs from 'node:fs/promises'
import path from 'node:path'
import { dirname } from 'path'
import type { Config } from 'tailwindcss'
import { fileURLToPath } from 'url'
import { loadModule } from '../../../@tailwindcss-node/src/compile'
import { resolveConfig } from '../../../tailwindcss/src/compat/config/resolve-config'
import type { DesignSystem } from '../../../tailwindcss/src/design-system'
import { error } from '../utils/renderer'
import { migratePrefix } from './codemods/prefix'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const css = String.raw
export async function prepareConfig(
configPath: string | null,
options: { base: string },
): Promise<{
designSystem: DesignSystem
globs: { base: string; pattern: string }[]
userConfig: Config
configFilePath: string
newPrefix: string | null
}> {
try {
if (configPath === null) {
configPath = await detectConfigPath(options.base)
}
let fullConfigPath = path.resolve(options.base, configPath)
let relative = path.relative(__dirname, fullConfigPath)
if (!relative.startsWith('.')) {
relative = './' + relative
}
let userConfig = await createResolvedUserConfig(fullConfigPath)
let newPrefix = userConfig.prefix ? migratePrefix(userConfig.prefix) : null
let input = css`
@import 'tailwindcss' ${newPrefix ? `prefix(${newPrefix})` : ''};
@config './${relative}';
`
let [compiler, designSystem] = await Promise.all([
compile(input, { base: __dirname, onDependency: () => {} }),
__unstable__loadDesignSystem(input, { base: __dirname }),
])
return {
designSystem,
globs: compiler.globs,
userConfig,
newPrefix,
configFilePath: fullConfigPath,
}
} catch (e: any) {
error('Could not load the configuration file: ' + e.message)
process.exit(1)
}
}
async function createResolvedUserConfig(fullConfigPath: string): Promise<Config> {
let [noopDesignSystem, unresolvedUserConfig] = await Promise.all([
__unstable__loadDesignSystem(
css`
@import 'tailwindcss';
`,
{ base: __dirname },
),
loadModule(fullConfigPath, __dirname, () => {}).then((result) => result.module) as Config,
])
return resolveConfig(noopDesignSystem, [
{ base: dirname(fullConfigPath), config: unresolvedUserConfig },
]).resolvedConfig as any
}
const DEFAULT_CONFIG_FILES = [
'./tailwind.config.js',
'./tailwind.config.cjs',
'./tailwind.config.mjs',
'./tailwind.config.ts',
'./tailwind.config.cts',
'./tailwind.config.mts',
]
async function detectConfigPath(base: string) {
for (let file of DEFAULT_CONFIG_FILES) {
let fullPath = path.resolve(base, file)
try {
await fs.access(fullPath)
return file
} catch {}
}
throw new Error(
'No configuration file found. Please provide a path to the Tailwind CSS v3 config file via the `--config` option.',
)
}