Skip to content

Commit

Permalink
feat(cli): support for transformers (#1592)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
wkeylin and antfu committed Sep 18, 2022
1 parent e264f26 commit 3ab65f2
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 20 deletions.
3 changes: 3 additions & 0 deletions packages/cli/package.json
Expand Up @@ -43,6 +43,8 @@
"stub": "unbuild --stub"
},
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@rollup/pluginutils": "^4.2.1",
"@unocss/config": "workspace:*",
"@unocss/core": "workspace:*",
"@unocss/preset-uno": "workspace:*",
Expand All @@ -51,6 +53,7 @@
"colorette": "^2.0.19",
"consola": "^2.15.3",
"fast-glob": "^3.2.11",
"magic-string": "^0.26.3",
"pathe": "^0.3.5",
"perfect-debounce": "^0.1.3"
}
Expand Down
44 changes: 35 additions & 9 deletions packages/cli/src/index.ts
Expand Up @@ -4,9 +4,12 @@ import fg from 'fast-glob'
import consola from 'consola'
import { cyan, dim, green } from 'colorette'
import { debounce } from 'perfect-debounce'
import { createGenerator, toArray } from '@unocss/core'
import { toArray } from '@unocss/core'
import { loadConfig } from '@unocss/config'
import type { SourceCodeTransformerEnforce, UserConfig } from '@unocss/core'
import { version } from '../package.json'
import { createContext } from '../../shared-integration/src/context'
import { applyTransformers } from '../../shared-integration/src/transformers'
import { PrettyError, handleError } from './errors'
import { defaultConfig } from './config'
import type { CliOptions, ResolvedCliOptions } from './types'
Expand All @@ -30,10 +33,7 @@ export async function build(_options: CliOptions) {
const options = await resolveOptions(_options)
const { config, sources: configSources } = await loadConfig(cwd, options.config)

const uno = createGenerator(
config,
defaultConfig,
)
const ctx = createContext<UserConfig>(config, defaultConfig)

const files = await fg(options.patterns, { cwd, absolute: true })
await Promise.all(
Expand Down Expand Up @@ -74,7 +74,7 @@ export async function build(_options: CliOptions) {
const absolutePath = resolve(cwd, file)

if (configSources.includes(absolutePath)) {
uno.setConfig((await loadConfig()).config)
await ctx.reloadConfig()
consola.info(`${cyan(basename(file))} changed, setting new config`)
}
else {
Expand All @@ -99,12 +99,38 @@ export async function build(_options: CliOptions) {

await generate(options)

startWatcher().catch(handleError)
await startWatcher().catch(handleError)

function transformFiles(sources: { id: string; code: string; transformedCode?: string | undefined }[], enforce: SourceCodeTransformerEnforce = 'default') {
return Promise.all(
sources.map(({ id, code, transformedCode }) => new Promise<{ id: string; code: string; transformedCode: string | undefined }>((resolve) => {
applyTransformers(ctx, code, id, enforce)
.then((transformsRes) => {
resolve({ id, code, transformedCode: transformsRes?.code || transformedCode })
})
})))
}

async function generate(options: ResolvedCliOptions) {
const sourceCache = Array.from(fileCache).map(([id, code]) => ({ id, code }))

const outFile = resolve(options.cwd || process.cwd(), options.outFile ?? 'uno.css')
const { css, matched } = await uno.generate(
[...fileCache].join('\n'),

const preTransform = await transformFiles(sourceCache, 'pre')
const defaultTransform = await transformFiles(preTransform)
const postTransform = await transformFiles(defaultTransform, 'post')

// update source file
await Promise.all(
postTransform.filter(({ transformedCode }) => !!transformedCode)
.map(({ transformedCode, id }) => new Promise<void>((resolve) => {
if (existsSync(id))
fs.writeFile(id, transformedCode as string, 'utf-8').then(resolve)
})),
)

const { css, matched } = await ctx.uno.generate(
[...postTransform.map(({ code, transformedCode }) => transformedCode ?? code)].join('\n'),
{
preflights: options.preflights,
minify: options.minify,
Expand Down
19 changes: 13 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions test/__snapshots__/cli.test.ts.snap
Expand Up @@ -6,7 +6,20 @@ exports[`cli > builds uno.css 1`] = `
.p-4{padding:1rem;}"
`;

exports[`cli > supports directives transformer 1`] = `""`;

exports[`cli > supports directives transformer 2`] = `".btn-center{margin-top:0rem;margin-bottom:0rem;text-align:center;font-weight:500;}"`;

exports[`cli > supports unocss.config.js 1`] = `
"/* layer: shortcuts */
.box{margin-left:auto;margin-right:auto;max-width:80rem;border-radius:0.375rem;--un-bg-opacity:1;background-color:rgba(243,244,246,var(--un-bg-opacity));padding:1rem;--un-shadow:var(--un-shadow-inset) 0 1px 2px 0 var(--un-shadow-color, rgba(0,0,0,0.05));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}"
`;

exports[`cli > supports variantGroup transformer 1`] = `
"/* layer: default */
.border-red{--un-border-opacity:1;border-color:rgba(248,113,113,var(--un-border-opacity));}
.border-solid{border-style:solid;}
.p-4{padding:1rem;}"
`;

exports[`cli > supports variantGroup transformer 2`] = `"<div class=\\"p-4 border-solid border-red\\"></div>"`;
46 changes: 41 additions & 5 deletions test/cli.test.ts
Expand Up @@ -37,6 +37,34 @@ export default defineConfig({
expect(output).toMatchSnapshot()
})

it('supports variantGroup transformer', async () => {
const { output, transform } = await runCli({
'views/index.html': '<div class="p-4 border-(solid red)"></div>',
'unocss.config.js': `
import { defineConfig, transformerVariantGroup } from 'unocss'
export default defineConfig({
transformers: [transformerVariantGroup()]
})
`.trim(),
}, 'views/index.html')
expect(output).toMatchSnapshot()
expect(transform).toMatchSnapshot()
})

it('supports directives transformer', async () => {
const { output, transform } = await runCli({
'views/index.css': '.btn-center{@apply text-center my-0 font-medium;}',
'unocss.config.js': `
import { defineConfig, transformerDirectives } from 'unocss'
export default defineConfig({
transformers: [transformerDirectives()]
})
`.trim(),
}, 'views/index.css')
expect(output).toMatchSnapshot()
expect(transform).toMatchSnapshot()
})

it('uno.css exclude initialized class after changing file', async () => {
const fileName = 'views/index.html'
const initializedContent = '<div class="bg-blue"></div>'
Expand All @@ -55,7 +83,7 @@ export default defineConfig({
// polling until update
for (let i = 100; i >= 0; i--) {
await sleep(100)
const output = await readUnocssFile(testDir)
const output = await readFile(testDir)
if (i === 0 || output.includes('.bg-red')) {
expect(output).toContain('.bg-red')
break
Expand Down Expand Up @@ -89,17 +117,25 @@ function runAsyncChildProcess(cwd: string, ...args: string[]) {
return startCli(cwd, ['', '', ...args, '--no-preflights'])
}

function readUnocssFile(testDir: string) {
return fs.readFile(resolve(testDir, 'uno.css'), 'utf8')
function readFile(testDir: string, targetFile?: string) {
return fs.readFile(resolve(testDir, targetFile ?? 'uno.css'), 'utf8')
}

async function runCli(files: Record<string, string>) {
async function runCli(files: Record<string, string>, transformFile?: string) {
const testDir = getTestDir()

await initOutputFiles(testDir, files)
await runAsyncChildProcess(testDir, 'views/**/*')

const output = await readUnocssFile(testDir)
const output = await readFile(testDir)

if (transformFile) {
const transform = await readFile(testDir, transformFile)
return {
output,
transform,
}
}

return {
output,
Expand Down

0 comments on commit 3ab65f2

Please sign in to comment.