Skip to content

fix: Should be able to parse to extract values template string #77

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
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