Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions playground/theme/components/TestNuxt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ defineProps({
</template>

<style scoped lang="ts">
const test = keyFrames({
Comment thread
SGAMERyu marked this conversation as resolved.
Outdated
from: { transform: 'scale(1)' },
to: { transform: 'scale(1.5)' }
})

css({
variants: {
padded: {
Expand Down Expand Up @@ -49,6 +54,7 @@ css({
transition: 'box-shadow .1 ease-in-out',
color: '{color.white}',
boxShadow: (props) => `0 8px 0 {color.${props.color}.600}, 0 12px 16px rgba(0, 0, 0, .35)`,
animation: `${test} 1s linear infinite`,
span: {
fontFamily: '{font.secondary}',
display: 'inline-block',
Expand Down
54 changes: 45 additions & 9 deletions src/transforms/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import type { ASTNode } from 'ast-types'
import { defu } from 'defu'
import type { PinceauContext } from 'pinceau/types'
import { parse } from 'acorn'
import { resolveCssProperty, stringify } from '../utils'
import { resolveCssProperty, stringify, stringifyKeyFrames } from '../utils'
import { message } from '../utils/logger'
import { parseAst, printAst, visitAst } from '../utils/ast'
import { resolveComputedStyles } from './vue/computed'
import { hash } from 'ohash'

/**
* Stringify every call of css() into a valid Vue <style> declaration.
Expand All @@ -29,18 +30,20 @@ export const transformCssFunction = (
return ''
}

const declaration = resolveCssCallees(
const { keyFramesDeclaration, cssDeclaration } = resolveCssCallees(
code,
ast => evalCssDeclaration(ast, computedStyles),
)

// Handle variants and remove them from declaration
if (declaration && declaration?.variants) {
Object.assign(variants, defu(variants || {}, declaration?.variants || {}))
delete declaration.variants
if (cssDeclaration && cssDeclaration?.variants) {
Object.assign(variants, defu(variants || {}, cssDeclaration?.variants || {}))
delete cssDeclaration.variants
}

return stringify(declaration, (property: any, value: any, _style: any, _selectors: any) => resolveCssProperty(property, value, _style, _selectors, ctx, loc))
const animationText = stringifyKeyFrames(keyFramesDeclaration)

return animationText + stringify(cssDeclaration, (property: any, value: any, _style: any, _selectors: any) => resolveCssProperty(property, value, _style, _selectors, ctx, loc))
}

/**
Expand All @@ -61,16 +64,25 @@ export function castVariants(property: any, value: any) {
*/
export function resolveCssCallees(code: string, cb: (ast: ASTNode) => any): any {
const ast = parseAst(code)
let result: any = false
let cssDeclaration: any = false
let keyFramesDeclaration: any = false
visitAst(ast, {
visitCallExpression(path: any) {
if (path.value.callee.name === 'css') {
result = defu(result || {}, cb(path.value.arguments[0]))
cssDeclaration = defu(cssDeclaration || {}, cb(path.value.arguments[0]))
}
return this.traverse(path)
},
visitVariableDeclaration(path: any) {
if (path.value.declarations[0]?.init?.callee?.name === 'keyFrames') {
Comment thread
SGAMERyu marked this conversation as resolved.
Outdated
const animateName = path.value.declarations[0]?.id?.name
keyFramesDeclaration = defu(keyFramesDeclaration || {}, evalKeyFramesDeclaration(animateName, path.value.declarations[0]?.init?.arguments[0]))
}

return this.traverse(path)
}
})
return result
return { cssDeclaration, keyFramesDeclaration }
}

/**
Expand All @@ -94,3 +106,27 @@ export function evalCssDeclaration(cssAst: ASTNode, computedStyles: any = {}) {
return {}
}
}


export function evalKeyFramesDeclaration(name: string, keyFramesAst: ASTNode) {
try {
// eslint-disable-next-line no-eval
const _eval = eval
const keyFramesCode = printAst(keyFramesAst).code
const keyFrameName = hash(keyFramesCode)

// set animate name
_eval(`var ${name} = '${keyFrameName}'`)

// const transformed = transform({ source: recast.print(ast).code })
_eval(`var keyFramesDeclaration = {
'${keyFrameName}':${keyFramesCode},
}`)

// @ts-expect-error - Evaluated code
return keyFramesDeclaration
}
catch (e) {
return {}
}
}
13 changes: 13 additions & 0 deletions src/utils/stringify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,16 @@ export const stringify = (

return parse(value, [], [])
}

export const stringifyKeyFrames = (value) => {
let cssText = ''
Object.entries(value).forEach(([key, content]) => {
const animateRule: string[] = []
Object.entries(content).forEach(([key, value]) => {
animateRule.push(`${key} ${JSON.stringify(value).replace(/'|"/g, '')}`)
})
cssText = `@keyframes ${key} { ${animateRule.join(' ')} }`
})

return cssText || ''
}