Skip to content

Commit

Permalink
fix: Should be able to parse to extract values template string (#77)
Browse files Browse the repository at this point in the history
* fix: Should be able to parse to extract values template string

* fix: updated unit test code

* fix: updated unit test code
  • Loading branch information
baiwusanyu-c authored Jun 16, 2023
1 parent 6560cfb commit 40f4077
Show file tree
Hide file tree
Showing 13 changed files with 2,285 additions and 2,427 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": "@baiwusanyu",
"rules": {
"no-console": ["warn", { "allow": ["log"] }]
"no-console": ["warn", { "allow": ["log"] }],
"no-template-curly-in-string": "off"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
},
"scripts": {
"init": "pnpm i",
"lint:fix": "eslint --fix ./ --ext .vue,.js,.ts,.jsx,.tsx,.json ",
"lint:fix": "eslint --cache --fix ./ --ext .vue,.js,.ts,.jsx,.tsx,.json ",
"dev": "pnpm run --filter @unplugin-vue-cssvars/build dev",
"build": "pnpm run clean && pnpm run --filter @unplugin-vue-cssvars/build build",
"play:vite:server": "pnpm run --filter @unplugin-vue-cssvars/play-vite server",
Expand Down
1 change: 0 additions & 1 deletion packages/core/inject/inject-cssvars.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import hash from 'hash-sum'
import { type MagicStringBase } from 'magic-string-ast'
import type { IParseSFCRes, TMatchVariable } from '../parser'
Expand Down
1 change: 0 additions & 1 deletion packages/core/parser/__test__/parser-compiled-sfc.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import { beforeEach, describe, expect, test } from 'vitest'
import { parse } from '@babel/parser'
import {
Expand Down
146 changes: 146 additions & 0 deletions packages/core/parser/__test__/parser-vbind-m.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { describe, expect, test } from 'vitest'
import { parseCssVars } from '../parser-vbind-m'

describe('analyze css vbind', () => {
test('Should be able to parse to extract the v-bind-m value', () => {
const source = `
.test {
color: v-bind-m(color);
}
`
const expected = ['color']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to parse single quoted values', () => {
const source = `
.test {
color: v-bind-m('color');
}
`
const expected = ['color']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to parse double quoted values', () => {
const source = `
.test {
color: v-bind-m("color");
}
`
const expected = ['color']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to parse the value of the template string', () => {
const source = `
.test {
color: v-bind-m(\`\${v}\`);
background-image: v-bind-m('\`url('\${bgUrl}')\`');
}
`

const expected = ['`${v}`', '`url(\'${bgUrl}\')`']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to parse extract v-bind-m values in nested', () => {
const source = `
.parent {
.child {
color: v-bind-m(color);
}
}
`
const expected = ['color']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to parse extract v-bind-m values when ignoring single line comments', () => {
const source = `
.test {
color: v-bind-m(color); // this is a comment
}
`
const expected = ['color']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to parse extract v-bind-m values when ignoring multi-line comments', () => {
const source = `
.test {
color: v-bind-m(color); /* this is a
multi-line
comment */
}
`
const expected = ['color']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to extract multiple v-bind-m values in analysis', () => {
const source = `
.test {
color: v-bind-m(color1);
background-color: v-bind-m(color2);
}
`
const expected = ['color1', 'color2']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should only analyze to extract unique values', () => {
const source = `
.test {
color: v-bind-m(color1);
background-color: v-bind-m(color1);
}
`
const expected = ['color1']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to parse to extract values inside nested parentheses', () => {
const source = `
.test {
color: v-bind-m(((color1)));
}
`
const expected = ['((color1))']
expect(parseCssVars([source])).toMatchObject(expected)
})

test('Should be able to parse to extract values template string', () => {
const source = '.test{ color: v-bind-m(`${v}`);\n background-image: v-bind-m("`url(\'${bgUrl}\')`");}'
const expected = ['`${v}`', "`url('${bgUrl}')`"]
expect(parseCssVars([source])).toMatchObject(expected)
})

test('the right parenthesis is missing', () => {
const source = `
.test {
v-bind-m(color1;
}
`
expect(parseCssVars([source])).toMatchObject([])
})

test('the left parenthesis is missing', () => {
const source = `
.test {
v-bind-m color1);
}
`
expect(parseCssVars([source])).toMatchObject([])
})

test('should be able to parse incomplete expressions', () => {
const source = `
.test {
font-weight: v-bind-m("count.toString(");
font-weight: v-bind-m(xxx);
}
`
expect(parseCssVars([source])).toMatchObject(['count.toString(', 'xxx'])
})
})
88 changes: 0 additions & 88 deletions packages/core/parser/__test__/parser-vbindm.spec.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/core/parser/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from './parser-variable'
export * from './parser-import'
export * from './parser-vbindm'
export * from './parser-vbind-m'
export { parserCompiledSfc } from './parser-compiled-sfc'
export type { IParseSFCRes } from './parser-compiled-sfc'
84 changes: 84 additions & 0 deletions packages/core/parser/parser-vbind-m.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Implementation from vue
* https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/style/cssVars.ts
*/
const enum LexerState {
inParens,
inSingleQuoteString,
inDoubleQuoteString,
}

function lexBinding(content: string, start: number): number | null {
let state: LexerState = LexerState.inParens
let parenDepth = 0

for (let i = start; i < content.length; i++) {
const char = content.charAt(i)
switch (state) {
case LexerState.inParens:
if (char === '\'') {
state = LexerState.inSingleQuoteString
} else if (char === '"') {
state = LexerState.inDoubleQuoteString
} else if (char === '(') {
parenDepth++
} else if (char === ')') {
if (parenDepth > 0)
parenDepth--
else
return i
}
break
case LexerState.inSingleQuoteString:
if (char === '\'')
state = LexerState.inParens

break
case LexerState.inDoubleQuoteString:
if (char === '"')
state = LexerState.inParens

break
}
}
return null
}

function normalizeExpression(exp: string) {
exp = exp.trim()
if (
(exp[0] === '\'' && exp[exp.length - 1] === '\'')
|| (exp[0] === '"' && exp[exp.length - 1] === '"')
)
return exp.slice(1, -1)

return exp
}

const vBindRE = /v-bind-m\s*\(/g

export function parseCssVars(
styles: string[],
hook?: {
getIndex(start: number, end: number): void
},
): string[] {
const vars: string[] = []
styles.forEach((style) => {
let match: RegExpExecArray | null = null
// ignore v-bind() in comments /* ... */
const content = style.replace(/\/\*([\s\S]*?)\*\//g, '')

while ((match = vBindRE.exec(content))) {
const start = match.index + match[0].length
const end = lexBinding(content, start)
if (end !== null) {
hook && hook.getIndex(start, end)
const variable = normalizeExpression(content.slice(start, end))
if (!vars.includes(variable))
vars.push(variable)
}
}
})
return vars
}
Loading

0 comments on commit 40f4077

Please sign in to comment.