From fd42e6766fa09bb0007dc48f1ce42cf182e2f8e6 Mon Sep 17 00:00:00 2001 From: zernonia Date: Sat, 21 Oct 2023 16:06:35 +0800 Subject: [PATCH 1/3] refactor: transform css only in component --- packages/cli/package.json | 1 + .../utils/transformers/transform-css-vars.ts | 55 +++++++++---- pnpm-lock.yaml | 82 +++++++++++++++++-- 3 files changed, 117 insertions(+), 21 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 0bc0e2192..6539ed726 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -49,6 +49,7 @@ "@babel/core": "^7.22.17", "@babel/parser": "^7.22.16", "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/compiler-sfc": "^3.3.6", "chalk": "5.3.0", "commander": "^11.0.0", "cosmiconfig": "^8.3.6", diff --git a/packages/cli/src/utils/transformers/transform-css-vars.ts b/packages/cli/src/utils/transformers/transform-css-vars.ts index f99da6b84..f01571b39 100644 --- a/packages/cli/src/utils/transformers/transform-css-vars.ts +++ b/packages/cli/src/utils/transformers/transform-css-vars.ts @@ -1,5 +1,6 @@ -import { SyntaxKind } from 'ts-morph' import type * as z from 'zod' +import MagicString from 'magic-string' +import { parse, walk } from '@vue/compiler-sfc' import type { registryBaseColorSchema } from '@/src/utils/registry/schema' import type { Transformer } from '@/src/utils/transformers' @@ -9,23 +10,46 @@ export const transformCssVars: Transformer = async ({ baseColor, }) => { // No transform if using css variables. - if (config.tailwind?.cssVariables || !baseColor?.inlineColors) + if (config.tailwind?.cssVariables || !baseColor?.inlineColors || sourceFile.getFilePath().endsWith('ts')) return sourceFile - sourceFile.getDescendantsOfKind(SyntaxKind.StringLiteral).forEach((node) => { - const value = node.getText() + const parsed = parse(sourceFile.getText()) + const template = parsed.descriptor.template - if (value.includes('cn(')) { - const splitted = value.split('\'').map(i => applyColorMapping(i, baseColor.inlineColors)) - node.replaceWithText(`${splitted.join('\'')}`) - } - else if (value) { - const valueWithColorMapping = applyColorMapping( - value.replace(/"/g, ''), - baseColor.inlineColors, - ) - node.replaceWithText(`"${valueWithColorMapping.trim()}"`) + if (!template) + return sourceFile + + type ElementNode = typeof template.ast + type ElementNodeChildren = ElementNode['children'] + + const parseChildren = (children: ElementNodeChildren) => { + for (const child of children) { + if ('children' in child) + parseChildren(child.children as ElementNodeChildren) + + if ('props' in child) { + for (const prop of child.props) { + if ('arg' in prop && prop.arg && 'content' in prop.exp! && prop.exp) { + if ('content' in prop.arg && prop.arg?.content === 'class') { + const s = new MagicString(prop.exp.content) + s.replace(/'(.*?)'/g, (substring) => { + return applyColorMapping(substring, + baseColor.inlineColors, + ) + }) + sourceFile.replaceText([prop.exp.loc.start.offset, prop.exp.loc.end.offset], s.toString()) + } + } + } + } } + } + + walk(template?.ast, { + enter(node: ElementNode) { + if (node.children) + parseChildren(node.children) + }, }) return sourceFile @@ -104,5 +128,6 @@ export function applyColorMapping( lightMode.push(className) } - return `${lightMode.join(' ')} ${darkMode.join(' ').trim()}` + const combined = `${lightMode.join(' ').replace(/\'/g, '')} ${darkMode.join(' ').trim()}`.trim() + return `'${combined}'` } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c387551e..d8b07ed79 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,7 +137,7 @@ importers: version: 3.3.4 autoprefixer: specifier: ^10.4.16 - version: 10.4.16(postcss@8.4.30) + version: 10.4.16(postcss@8.4.31) lodash.template: specifier: ^4.5.0 version: 4.5.0 @@ -167,7 +167,7 @@ importers: version: 4.4.11(@types/node@20.7.0) vitepress: specifier: ^1.0.0-rc.20 - version: 1.0.0-rc.20(@algolia/client-search@4.20.0)(@types/node@20.7.0)(postcss@8.4.30)(search-insights@2.8.3) + version: 1.0.0-rc.20(@algolia/client-search@4.20.0)(@types/node@20.7.0)(postcss@8.4.31)(search-insights@2.8.3) vue-tsc: specifier: ^1.8.15 version: 1.8.15(typescript@5.2.2) @@ -186,6 +186,9 @@ importers: '@babel/plugin-transform-typescript': specifier: ^7.22.15 version: 7.22.15(@babel/core@7.23.0) + '@vue/compiler-sfc': + specifier: ^3.3.6 + version: 3.3.6 chalk: specifier: 5.3.0 version: 5.3.0 @@ -2709,12 +2712,28 @@ packages: estree-walker: 2.0.2 source-map-js: 1.0.2 + /@vue/compiler-core@3.3.6: + resolution: {integrity: sha512-2JNjemwaNwf+MkkatATVZi7oAH1Hx0B04DdPH3ZoZ8vKC1xZVP7nl4HIsk8XYd3r+/52sqqoz9TWzYc3yE9dqA==} + dependencies: + '@babel/parser': 7.23.0 + '@vue/shared': 3.3.6 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + dev: false + /@vue/compiler-dom@3.3.4: resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==} dependencies: '@vue/compiler-core': 3.3.4 '@vue/shared': 3.3.4 + /@vue/compiler-dom@3.3.6: + resolution: {integrity: sha512-1MxXcJYMHiTPexjLAJUkNs/Tw2eDf2tY3a0rL+LfuWyiKN2s6jvSwywH3PWD8bKICjfebX3GWx2Os8jkRDq3Ng==} + dependencies: + '@vue/compiler-core': 3.3.6 + '@vue/shared': 3.3.6 + dev: false + /@vue/compiler-sfc@3.3.4: resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==} dependencies: @@ -2729,12 +2748,34 @@ packages: postcss: 8.4.30 source-map-js: 1.0.2 + /@vue/compiler-sfc@3.3.6: + resolution: {integrity: sha512-/Kms6du2h1VrXFreuZmlvQej8B1zenBqIohP0690IUBkJjsFvJxY0crcvVRJ0UhMgSR9dewB+khdR1DfbpArJA==} + dependencies: + '@babel/parser': 7.23.0 + '@vue/compiler-core': 3.3.6 + '@vue/compiler-dom': 3.3.6 + '@vue/compiler-ssr': 3.3.6 + '@vue/reactivity-transform': 3.3.6 + '@vue/shared': 3.3.6 + estree-walker: 2.0.2 + magic-string: 0.30.5 + postcss: 8.4.31 + source-map-js: 1.0.2 + dev: false + /@vue/compiler-ssr@3.3.4: resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==} dependencies: '@vue/compiler-dom': 3.3.4 '@vue/shared': 3.3.4 + /@vue/compiler-ssr@3.3.6: + resolution: {integrity: sha512-QTIHAfDCHhjXlYGkUg5KH7YwYtdUM1vcFl/FxFDlD6d0nXAmnjizka3HITp8DGudzHndv2PjKVS44vqqy0vP4w==} + dependencies: + '@vue/compiler-dom': 3.3.6 + '@vue/shared': 3.3.6 + dev: false + /@vue/devtools-api@6.5.0: resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} @@ -2766,6 +2807,16 @@ packages: estree-walker: 2.0.2 magic-string: 0.30.3 + /@vue/reactivity-transform@3.3.6: + resolution: {integrity: sha512-RlJl4dHfeO7EuzU1iJOsrlqWyJfHTkJbvYz/IOJWqu8dlCNWtxWX377WI0VsbAgBizjwD+3ZjdnvSyyFW1YVng==} + dependencies: + '@babel/parser': 7.23.0 + '@vue/compiler-core': 3.3.6 + '@vue/shared': 3.3.6 + estree-walker: 2.0.2 + magic-string: 0.30.5 + dev: false + /@vue/reactivity@3.3.4: resolution: {integrity: sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==} dependencies: @@ -2796,6 +2847,10 @@ packages: /@vue/shared@3.3.4: resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==} + /@vue/shared@3.3.6: + resolution: {integrity: sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==} + dev: false + /@vue/typescript@1.8.15(typescript@5.2.2): resolution: {integrity: sha512-qWyanQKXOsK84S8rP7QBrqsvUdQ0nZABZmTjXMpb3ox4Bp5IbkscREA3OPUrkgl64mAxwwCzIWcOc3BPTCPjQw==} dependencies: @@ -3124,7 +3179,7 @@ packages: tslib: 2.6.2 dev: false - /autoprefixer@10.4.16(postcss@8.4.30): + /autoprefixer@10.4.16(postcss@8.4.31): resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -3136,7 +3191,7 @@ packages: fraction.js: 4.3.6 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.30 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true @@ -5974,6 +6029,13 @@ packages: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -6842,6 +6904,14 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + /potpack@1.0.2: resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} dev: false @@ -8311,7 +8381,7 @@ packages: fsevents: 2.3.3 dev: true - /vitepress@1.0.0-rc.20(@algolia/client-search@4.20.0)(@types/node@20.7.0)(postcss@8.4.30)(search-insights@2.8.3): + /vitepress@1.0.0-rc.20(@algolia/client-search@4.20.0)(@types/node@20.7.0)(postcss@8.4.31)(search-insights@2.8.3): resolution: {integrity: sha512-CykMUJ8JLxLcGWek0ew3wln4RYbsOd1+0YzXITTpajggpynm2S331TNkJVOkHrMRc6GYe3y4pS40GfgcW0ZwAw==} hasBin: true peerDependencies: @@ -8332,7 +8402,7 @@ packages: focus-trap: 7.5.3 mark.js: 8.11.1 minisearch: 6.1.0 - postcss: 8.4.30 + postcss: 8.4.31 shiki: 0.14.4 vite: 4.4.11(@types/node@20.7.0) vue: 3.3.4 From db766086e2e5c26e5497fb319f68942a365548c2 Mon Sep 17 00:00:00 2001 From: zernonia Date: Sat, 21 Oct 2023 16:53:31 +0800 Subject: [PATCH 2/3] refactor: more concise --- .../utils/transformers/transform-css-vars.ts | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/packages/cli/src/utils/transformers/transform-css-vars.ts b/packages/cli/src/utils/transformers/transform-css-vars.ts index f01571b39..7d3c6e75d 100644 --- a/packages/cli/src/utils/transformers/transform-css-vars.ts +++ b/packages/cli/src/utils/transformers/transform-css-vars.ts @@ -20,35 +20,32 @@ export const transformCssVars: Transformer = async ({ return sourceFile type ElementNode = typeof template.ast - type ElementNodeChildren = ElementNode['children'] - - const parseChildren = (children: ElementNodeChildren) => { - for (const child of children) { - if ('children' in child) - parseChildren(child.children as ElementNodeChildren) - - if ('props' in child) { - for (const prop of child.props) { - if ('arg' in prop && prop.arg && 'content' in prop.exp! && prop.exp) { - if ('content' in prop.arg && prop.arg?.content === 'class') { - const s = new MagicString(prop.exp.content) - s.replace(/'(.*?)'/g, (substring) => { - return applyColorMapping(substring, - baseColor.inlineColors, - ) - }) - sourceFile.replaceText([prop.exp.loc.start.offset, prop.exp.loc.end.offset], s.toString()) - } + type TemplateNode = ElementNode['children'][number] + + const parseNode = (node: ElementNode | TemplateNode) => { + if ('props' in node) { + for (const prop of node.props) { + // for component + if ('arg' in prop && prop.arg && 'content' in prop.exp! && prop.exp) { + if ('content' in prop.arg && prop.arg?.content === 'class') { + const s = new MagicString(prop.exp.content) + s.replace(/'(.*?)'/g, (substring) => { + return `'${applyColorMapping(substring, baseColor.inlineColors)}'` + }) + sourceFile.replaceText([prop.exp.loc.start.offset, prop.exp.loc.end.offset], s.toString()) } } } } + if ('children' in node) { + for (const child of node.children) + parseNode(child as TemplateNode) + } } walk(template?.ast, { enter(node: ElementNode) { - if (node.children) - parseChildren(node.children) + parseNode(node) }, }) @@ -129,5 +126,5 @@ export function applyColorMapping( } const combined = `${lightMode.join(' ').replace(/\'/g, '')} ${darkMode.join(' ').trim()}`.trim() - return `'${combined}'` + return `${combined}` } From 60d1e785661c527776d8ce3f11eebc77966c41f9 Mon Sep 17 00:00:00 2001 From: zernonia Date: Sat, 21 Oct 2023 18:00:19 +0800 Subject: [PATCH 3/3] fix: pipeline --- .../utils/transformers/transform-css-vars.ts | 44 +++++++------------ .../transform-cjs-to-esm.test.ts.snap | 7 +-- .../transform-css-vars.test.ts.snap | 2 +- 3 files changed, 20 insertions(+), 33 deletions(-) diff --git a/packages/cli/src/utils/transformers/transform-css-vars.ts b/packages/cli/src/utils/transformers/transform-css-vars.ts index 7d3c6e75d..97e2b6a88 100644 --- a/packages/cli/src/utils/transformers/transform-css-vars.ts +++ b/packages/cli/src/utils/transformers/transform-css-vars.ts @@ -1,6 +1,7 @@ import type * as z from 'zod' import MagicString from 'magic-string' import { parse, walk } from '@vue/compiler-sfc' +import { SyntaxKind } from 'ts-morph' import type { registryBaseColorSchema } from '@/src/utils/registry/schema' import type { Transformer } from '@/src/utils/transformers' @@ -19,34 +20,24 @@ export const transformCssVars: Transformer = async ({ if (!template) return sourceFile - type ElementNode = typeof template.ast - type TemplateNode = ElementNode['children'][number] - - const parseNode = (node: ElementNode | TemplateNode) => { - if ('props' in node) { - for (const prop of node.props) { - // for component - if ('arg' in prop && prop.arg && 'content' in prop.exp! && prop.exp) { - if ('content' in prop.arg && prop.arg?.content === 'class') { - const s = new MagicString(prop.exp.content) - s.replace(/'(.*?)'/g, (substring) => { - return `'${applyColorMapping(substring, baseColor.inlineColors)}'` - }) - sourceFile.replaceText([prop.exp.loc.start.offset, prop.exp.loc.end.offset], s.toString()) - } - } - } + sourceFile.getDescendantsOfKind(SyntaxKind.StringLiteral).forEach((node) => { + if (template.loc.start.offset >= node.getPos()) + return sourceFile + + const value = node.getText() + + const hasClosingDoubleQuote = value.match(/"/g)?.length === 2 + if (value.search('\'') === -1 && hasClosingDoubleQuote) { + const mapped = applyColorMapping(value.replace(/"/g, ''), baseColor.inlineColors) + node.replaceWithText(`"${mapped}"`) } - if ('children' in node) { - for (const child of node.children) - parseNode(child as TemplateNode) + else { + const s = new MagicString(value) + s.replace(/'(.*?)'/g, (substring) => { + return `'${applyColorMapping(substring.replace(/\'/g, ''), baseColor.inlineColors)}'` + }) + node.replaceWithText(s.toString()) } - } - - walk(template?.ast, { - enter(node: ElementNode) { - parseNode(node) - }, }) return sourceFile @@ -124,7 +115,6 @@ export function applyColorMapping( if (!lightMode.includes(className)) lightMode.push(className) } - const combined = `${lightMode.join(' ').replace(/\'/g, '')} ${darkMode.join(' ').trim()}`.trim() return `${combined}` } diff --git a/packages/cli/test/utils/__snapshots__/transform-cjs-to-esm.test.ts.snap b/packages/cli/test/utils/__snapshots__/transform-cjs-to-esm.test.ts.snap index 0e927a781..68143755f 100644 --- a/packages/cli/test/utils/__snapshots__/transform-cjs-to-esm.test.ts.snap +++ b/packages/cli/test/utils/__snapshots__/transform-cjs-to-esm.test.ts.snap @@ -1,8 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`handle tailwind config template correctly 1`] = ` -" -import animate from \\"tailwindcss-animate\\" +"import animate from \\"tailwindcss-animate\\" /** @type {import('tailwindcss').Config} */ export default { @@ -43,9 +42,7 @@ export default { `; exports[`handle tailwind config template correctly 2`] = ` -" - -import animate from \\"tailwindcss-animate\\" +"import animate from \\"tailwindcss-animate\\" /** @type {import('tailwindcss').Config} */ export default { diff --git a/packages/cli/test/utils/__snapshots__/transform-css-vars.test.ts.snap b/packages/cli/test/utils/__snapshots__/transform-css-vars.test.ts.snap index 9a57aa199..964b9aad6 100644 --- a/packages/cli/test/utils/__snapshots__/transform-css-vars.test.ts.snap +++ b/packages/cli/test/utils/__snapshots__/transform-css-vars.test.ts.snap @@ -17,6 +17,6 @@ exports[`transform css vars 2`] = ` exports[`transform css vars 3`] = ` " \\"" `;