-
Notifications
You must be signed in to change notification settings - Fork 4.5k
/
Copy pathmigrate-config.ts
110 lines (92 loc) · 3.27 KB
/
migrate-config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import path from 'node:path'
import postcss, { AtRule, type Plugin } from 'postcss'
import { normalizePath } from '../../../../@tailwindcss-node/src/normalize-path'
import type { Stylesheet } from '../../stylesheet'
import type { JSConfigMigration } from '../config/migrate-js-config'
const ALREADY_INJECTED = new WeakMap<Stylesheet, string[]>()
export function migrateConfig(
sheet: Stylesheet,
{
configFilePath,
jsConfigMigration,
}: { configFilePath: string | null; jsConfigMigration: JSConfigMigration | null },
): Plugin {
function migrate() {
if (!sheet.isTailwindRoot) return
if (!configFilePath) return
let alreadyInjected = ALREADY_INJECTED.get(sheet)
if (alreadyInjected && alreadyInjected.includes(configFilePath)) {
return
} else if (alreadyInjected) {
alreadyInjected.push(configFilePath)
} else {
ALREADY_INJECTED.set(sheet, [configFilePath])
}
let root = sheet.root
// We don't have a sheet with a file path
if (!sheet.file) return
let cssConfig = new AtRule()
// Remove the `@config` directive if it exists and we couldn't migrate the
// config file.
if (jsConfigMigration !== null) {
root.walkAtRules('config', (node) => {
node.remove()
})
let css = '\n\n'
css += '\n@tw-bucket source {'
for (let source of jsConfigMigration.sources) {
let absolute = path.resolve(source.base, source.pattern)
css += `@source '${relativeToStylesheet(sheet, absolute)}';\n`
}
css += '}\n'
css += '\n@tw-bucket plugin {\n'
for (let plugin of jsConfigMigration.plugins) {
let relative =
plugin.path[0] === '.'
? relativeToStylesheet(sheet, path.resolve(plugin.base, plugin.path))
: plugin.path
if (plugin.options === null) {
css += `@plugin '${relative}';\n`
} else {
css += `@plugin '${relative}' {\n`
for (let [property, value] of Object.entries(plugin.options)) {
let cssValue = ''
if (typeof value === 'string') {
cssValue = quoteString(value)
} else if (Array.isArray(value)) {
cssValue = value
.map((v) => (typeof v === 'string' ? quoteString(v) : '' + v))
.join(', ')
} else {
cssValue = '' + value
}
css += ` ${property}: ${cssValue};\n`
}
css += '}\n' // @plugin
}
}
css += '}\n' // @tw-bucket
cssConfig.append(postcss.parse(css + jsConfigMigration.css))
}
// Inject the `@config` directive
root.append(cssConfig.nodes)
}
return {
postcssPlugin: '@tailwindcss/upgrade/migrate-config',
OnceExit: migrate,
}
}
function relativeToStylesheet(sheet: Stylesheet, absolute: string) {
if (!sheet.file) throw new Error('Can not find a path for the stylesheet')
let sheetPath = sheet.file
let relative = path.relative(path.dirname(sheetPath), absolute)
if (relative[0] !== '.') {
relative = `./${relative}`
}
// Ensure relative is a POSIX style path since we will merge it with the
// glob.
return normalizePath(relative)
}
function quoteString(value: string): string {
return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`
}