-
-
Notifications
You must be signed in to change notification settings - Fork 780
/
index.ts
128 lines (106 loc) · 3.47 KB
/
index.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { existsSync, promises as fs } from 'fs'
import { basename, dirname, relative, resolve } from 'pathe'
import fg from 'fast-glob'
import consola from 'consola'
import { cyan, dim, green } from 'colorette'
import { debounce } from 'perfect-debounce'
import type { UserConfig } from '@unocss/core'
import { createGenerator, toArray } from '@unocss/core'
import type { Theme } from '@unocss/preset-uno'
import { loadConfig } from '@unocss/config'
import { version } from '../package.json'
import { PrettyError, handleError } from './errors'
import { defaultConfig } from './config'
import type { CliOptions, ResolvedCliOptions } from './types'
const name = 'unocss'
export async function resolveOptions(options: CliOptions) {
if (!options.patterns?.length) {
throw new PrettyError(
`No glob patterns, try ${cyan(`${name} <path/to/**/*>`)}`,
)
}
return options as ResolvedCliOptions
}
export async function build(_options: CliOptions) {
const fileCache = new Map<string, string>()
const cwd = _options.cwd || process.cwd()
const options = await resolveOptions(_options)
const { config, sources: configSources } = await loadConfig<Theme, UserConfig<Theme>>(cwd, options.config)
const uno = createGenerator(
config,
defaultConfig,
)
const files = await fg(options.patterns, { cwd, absolute: true })
await Promise.all(
files.map(async (file) => {
fileCache.set(file, await fs.readFile(file, 'utf8'))
}),
)
consola.log(green(`${name} v${version}`))
consola.start(`UnoCSS ${options.watch ? 'in watch mode...' : 'for production...'}`)
const debouncedBuild = debounce(
async () => {
generate(options).catch(handleError)
},
100,
)
const startWatcher = async () => {
if (!options.watch)
return
const { watch } = await import('chokidar')
const { patterns } = options
const ignored = ['**/{.git,node_modules}/**']
const watcher = watch(patterns, {
ignoreInitial: true,
ignorePermissionErrors: true,
ignored,
cwd,
})
if (configSources.length)
watcher.add(configSources)
watcher.on('all', async (type, file) => {
if (configSources.includes(file)) {
uno.setConfig((await loadConfig<Theme, UserConfig<Theme>>()).config)
consola.info(`${cyan(basename(file))} changed, setting new config`)
}
else {
consola.log(`${green(type)} ${dim(file)}`)
const absolutePath = resolve(cwd, file)
if (type.startsWith('unlink'))
fileCache.delete(absolutePath)
else
fileCache.set(absolutePath, await fs.readFile(absolutePath, 'utf8'))
}
debouncedBuild()
})
consola.info(
`Watching for changes in ${
toArray(patterns)
.map(i => cyan(i))
.join(', ')}`,
)
}
await generate(options)
startWatcher()
async function generate(options: ResolvedCliOptions) {
const outFile = resolve(options.cwd || process.cwd(), options.outFile ?? 'uno.css')
const { css, matched } = await uno.generate(
[...fileCache].join('\n'),
{
preflights: options.preflights,
minify: options.minify,
},
)
const dir = dirname(outFile)
if (!existsSync(dir))
await fs.mkdir(dir, { recursive: true })
await fs.writeFile(outFile, css, 'utf-8')
if (!options.watch) {
consola.success(
`${[...matched].length} utilities generated to ${cyan(
relative(process.cwd(), outFile),
)}\n`,
)
}
}
}