From 6abd03efa484f616d9a828edd64617236e6ead77 Mon Sep 17 00:00:00 2001 From: Andrew Dassonville Date: Tue, 25 Jun 2024 18:01:00 -0700 Subject: [PATCH 1/5] Add support for nested CSS variables --- .../src/CSSVariableManager.ts | 91 +++++++++++++------ .../src/CacheManager.ts | 30 +++--- .../unit-tests/CSSVariableManager.test.ts | 8 +- 3 files changed, 82 insertions(+), 47 deletions(-) diff --git a/packages/css-variables-language-server/src/CSSVariableManager.ts b/packages/css-variables-language-server/src/CSSVariableManager.ts index c642102..1936ecb 100644 --- a/packages/css-variables-language-server/src/CSSVariableManager.ts +++ b/packages/css-variables-language-server/src/CSSVariableManager.ts @@ -9,7 +9,6 @@ import path from 'path'; import postcssSCSS from 'postcss-scss'; import postcssLESS from 'postcss-less'; import CacheManager from './CacheManager'; -import isColor from './utils/isColor'; import { culoriColorToVscodeColor } from './utils/culoriColorToVscodeColor'; export type CSSSymbol = { @@ -55,7 +54,7 @@ const getAST = (filePath: string, content: string) => { if (fileExtension === '.less') { return postcssLESS.parse(content); } - + if (fileExtension === '.scss') { return postcssSCSS.parse(content); } @@ -66,6 +65,54 @@ const getAST = (filePath: string, content: string) => { export default class CSSVariableManager { private cacheManager = new CacheManager(); + private resolveCachedVariables = () => { + for (const filePath of this.cacheManager.getFiles()) { + this.cacheManager.getAll(filePath).forEach((variable, key) => { + this.setCssVariable(key, this.resolveRecursiveVariables(variable.symbol.value), filePath, variable.definition.range); + }); + } + }; + + public resolveRecursiveVariables = (value: string) => { + for (let i = 0; i < 20; i++) { + const variableReference = value.match(/^var\(\s*([a-zA-Z0-9-]+)\s*\)$/)?.[1]; + if (variableReference) { + const newValue = this.cacheManager.get(variableReference)?.symbol?.value; + if (newValue) { + value = newValue; + } else { + break; + } + } else { + break; + } + } + + return value; + }; + + public setCssVariable = (prop: string, value: string, filePath: string, range: Range) => { + const variable: CSSVariable = { + symbol: { + name: prop, + value: value, + }, + definition: { + uri: filePath, + range: range, + }, + }; + + const culoriColor = culori.parse(value); + + if (culoriColor) { + variable.color = culoriColorToVscodeColor(culoriColor); + } + + // add to cache + this.cacheManager.set(filePath, prop, variable); + }; + public parseCSSVariablesFromText = async ({ content, filePath, @@ -117,36 +164,20 @@ export default class CSSVariableManager { ast.walkDecls((decl) => { if (decl.prop.startsWith('--')) { - const variable: CSSVariable = { - symbol: { - name: decl.prop, - value: decl.value, - }, - definition: { - uri: fileURI, - range: Range.create( - Position.create( - decl.source.start.line - 1, - decl.source.start.column - 1 - ), - Position.create( - decl.source.end.line - 1, - decl.source.end.column - 1 - ) - ), - }, - }; - - const culoriColor = culori.parse(decl.value); - - if (culoriColor) { - variable.color = culoriColorToVscodeColor(culoriColor); - } - - // add to cache - this.cacheManager.set(filePath, decl.prop, variable); + this.setCssVariable(decl.prop, decl.value, fileURI,Range.create( + Position.create( + decl.source.start.line - 1, + decl.source.start.column - 1 + ), + Position.create( + decl.source.end.line - 1, + decl.source.end.column - 1 + ) + )); } }); + + this.resolveCachedVariables(); } catch (error) { console.error(filePath); } diff --git a/packages/css-variables-language-server/src/CacheManager.ts b/packages/css-variables-language-server/src/CacheManager.ts index 3fa8953..79c1a39 100644 --- a/packages/css-variables-language-server/src/CacheManager.ts +++ b/packages/css-variables-language-server/src/CacheManager.ts @@ -1,6 +1,6 @@ /** * Cache Manager - * + * * { * src/styles/variables.css: { * }, @@ -14,37 +14,45 @@ export default class CacheManager { private cachedVariables: Map> = new Map(); private allVariables: Map = new Map(); - + public get(key: string, filePath?: string) { if (filePath) { - return this.cachedVariables[filePath]?.get(key); + return this.cachedVariables.get(filePath)?.get(key); } return this.allVariables?.get(key); } - public getAll() { + public getAll(filePath?: string) { + if (filePath) { + return this.cachedVariables.get(filePath); + } + return this.allVariables; } + public getFiles() { + return this.cachedVariables.keys(); + } + public set(filePath: string, key: string, value: T) { - if (!this.cachedVariables[filePath]) { - this.cachedVariables[filePath] = new Map(); + if (!this.cachedVariables.get(filePath)) { + this.cachedVariables.set(filePath, new Map()); } - this.allVariables?.set(key, value); - this.cachedVariables[filePath].set(key, value); + this.allVariables.set(key, value); + this.cachedVariables.get(filePath).set(key, value); } public clearFileCache(filePath: string) { - this.cachedVariables[filePath]?.forEach((_, key) => { + this.cachedVariables.get(filePath)?.forEach((_, key) => { this.allVariables?.delete(key); }); - this.cachedVariables[filePath]?.clear(); + this.cachedVariables.get(filePath)?.clear(); } public clearAllCache() { this.allVariables?.clear(); this.cachedVariables.clear(); } -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/unit-tests/CSSVariableManager.test.ts b/packages/css-variables-language-server/src/tests/unit-tests/CSSVariableManager.test.ts index f2d293e..4137701 100644 --- a/packages/css-variables-language-server/src/tests/unit-tests/CSSVariableManager.test.ts +++ b/packages/css-variables-language-server/src/tests/unit-tests/CSSVariableManager.test.ts @@ -16,17 +16,13 @@ async function runTest( expect(allVars.get('--h2').symbol.value).toEqual('22px'); expect(allVars.get('--h3').symbol.value).toEqual('18px'); expect(allVars.get('--text-base').symbol.value).toEqual('16px'); - expect(allVars.get('--carousel-bg').symbol.value).toEqual( - 'var(--main-bg-color)' - ); + expect(allVars.get('--carousel-bg').symbol.value).toEqual('brown'); expect(allVars.get('--child-main-bg-color').symbol.value).toEqual('brown'); expect(allVars.get('--child-h1').symbol.value).toEqual('26px'); expect(allVars.get('--child-h2').symbol.value).toEqual('22px'); expect(allVars.get('--child-h3').symbol.value).toEqual('18px'); // expect(allVars.get('--child-text-base').symbol.value).toEqual('16px'); - expect(allVars.get('--child-carousel-bg').symbol.value).toEqual( - 'var(--main-bg-color)' - ); + expect(allVars.get('--child-carousel-bg').symbol.value).toEqual('brown'); if (typeof additionalChecks === 'function') { await additionalChecks(allVars); From f27f5561c16f58936bbd90c36e5988f33ba47a48 Mon Sep 17 00:00:00 2001 From: Andrew Dassonville Date: Tue, 25 Jun 2024 18:01:16 -0700 Subject: [PATCH 2/5] Bump culori --- package-lock.json | 35 +++++++++++-------- .../package.json | 2 +- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c34742..2d52a21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3336,11 +3336,6 @@ "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==", "dev": true }, - "node_modules/culori": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/culori/-/culori-0.20.1.tgz", - "integrity": "sha512-jNZDmufWx4vCHW2fTb62sarHEeIF3WWrUYIv4ZpoQnN2vQU6IRPz1Ra9QnsHUKzdb5lppSuLsdB72rMmBMAd+A==" - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -10298,11 +10293,11 @@ } }, "packages/css-variables-language-server": { - "version": "2.6.4", + "version": "2.7.0", "license": "MIT", "dependencies": { "axios": "^0.27.2", - "culori": "0.20.1", + "culori": "^4.0.1", "fast-glob": "^3.2.7", "less": "^4.1.3", "line-column": "^1.0.2", @@ -10315,7 +10310,7 @@ "vscode-uri": "^3.0.3" }, "bin": { - "css-variables-language-server": "bin/index.mjs" + "css-variables-language-server": "bin/index.js" }, "devDependencies": { "@types/jest": "^28.1.8", @@ -10332,6 +10327,14 @@ "typescript": "^4.8.2" } }, + "packages/css-variables-language-server/node_modules/culori": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/culori/-/culori-4.0.1.tgz", + "integrity": "sha512-LSnjA6HuIUOlkfKVbzi2OlToZE8OjFi667JWN9qNymXVXzGDmvuP60SSgC+e92sd7B7158f7Fy3Mb6rXS5EDPw==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "packages/css-variables-server": { "version": "1.0.0", "extraneous": true, @@ -10355,7 +10358,7 @@ } }, "packages/vscode-css-variables": { - "version": "2.6.5", + "version": "2.7.1", "devDependencies": { "@types/mocha": "^9.1.1", "@types/node": "^18.7.13", @@ -12921,7 +12924,7 @@ "@typescript-eslint/eslint-plugin": "^5.35.1", "@typescript-eslint/parser": "^5.35.1", "axios": "^0.27.2", - "culori": "0.20.1", + "culori": "^4.0.1", "eslint": "^8.23.0", "eslint-config-airbnb-typescript": "^17.0.0", "fast-glob": "^3.2.7", @@ -12939,6 +12942,13 @@ "vscode-languageserver": "^7.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-uri": "^3.0.3" + }, + "dependencies": { + "culori": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/culori/-/culori-4.0.1.tgz", + "integrity": "sha512-LSnjA6HuIUOlkfKVbzi2OlToZE8OjFi667JWN9qNymXVXzGDmvuP60SSgC+e92sd7B7158f7Fy3Mb6rXS5EDPw==" + } } }, "css-what": { @@ -12977,11 +12987,6 @@ "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==", "dev": true }, - "culori": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/culori/-/culori-0.20.1.tgz", - "integrity": "sha512-jNZDmufWx4vCHW2fTb62sarHEeIF3WWrUYIv4ZpoQnN2vQU6IRPz1Ra9QnsHUKzdb5lppSuLsdB72rMmBMAd+A==" - }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/packages/css-variables-language-server/package.json b/packages/css-variables-language-server/package.json index 2aa9c06..fc886cd 100644 --- a/packages/css-variables-language-server/package.json +++ b/packages/css-variables-language-server/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "axios": "^0.27.2", - "culori": "0.20.1", + "culori": "^4.0.1", "fast-glob": "^3.2.7", "less": "^4.1.3", "line-column": "^1.0.2", From 94148c5fa13ab1743673fe1a802524c37473fd21 Mon Sep 17 00:00:00 2001 From: Andrew Dassonville Date: Tue, 25 Jun 2024 18:03:18 -0700 Subject: [PATCH 3/5] Add changeset --- .changeset/thick-drinks-pump.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/thick-drinks-pump.md diff --git a/.changeset/thick-drinks-pump.md b/.changeset/thick-drinks-pump.md new file mode 100644 index 0000000..e5a16c8 --- /dev/null +++ b/.changeset/thick-drinks-pump.md @@ -0,0 +1,5 @@ +--- +"css-variables-language-server": minor +--- + +Add support for nested CSS variables. From 4aeef67dc28974f6305ac923b0c18752bda39b2a Mon Sep 17 00:00:00 2001 From: Andrew Dassonville Date: Tue, 25 Jun 2024 18:10:14 -0700 Subject: [PATCH 4/5] Add additional tests --- .../src/tests/fixtures/css-nested/child/child.css | 2 +- .../src/tests/fixtures/css-nested/main.css | 3 ++- .../src/tests/fixtures/import-url/child/child.css | 2 +- .../src/tests/fixtures/import-url/main.css | 3 ++- .../src/tests/fixtures/less-nested/child/child.less | 2 +- .../src/tests/fixtures/less-nested/main.less | 3 ++- .../src/tests/fixtures/mixed-nested/child/child1.less | 2 +- .../src/tests/fixtures/mixed-nested/child/child2.css | 2 +- .../src/tests/fixtures/mixed-nested/main.scss | 3 ++- .../fixtures/scss-nested/stylesheets/bootstrap/_variables.scss | 1 + .../src/tests/fixtures/tailwindcss-nested/main.css | 3 ++- .../src/tests/unit-tests/CSSVariableManager.test.ts | 1 + 12 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/css-variables-language-server/src/tests/fixtures/css-nested/child/child.css b/packages/css-variables-language-server/src/tests/fixtures/css-nested/child/child.css index 45a8a3f..0eef8e8 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/css-nested/child/child.css +++ b/packages/css-variables-language-server/src/tests/fixtures/css-nested/child/child.css @@ -40,4 +40,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/css-nested/main.css b/packages/css-variables-language-server/src/tests/fixtures/css-nested/main.css index 2347183..c33775b 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/css-nested/main.css +++ b/packages/css-variables-language-server/src/tests/fixtures/css-nested/main.css @@ -5,6 +5,7 @@ --h3: 18px; --text-base: 16px; --carousel-bg: var(--main-bg-color); + --carousel-bg-nested: var(--carousel-bg); } .one { @@ -39,4 +40,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/import-url/child/child.css b/packages/css-variables-language-server/src/tests/fixtures/import-url/child/child.css index 45a8a3f..0eef8e8 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/import-url/child/child.css +++ b/packages/css-variables-language-server/src/tests/fixtures/import-url/child/child.css @@ -40,4 +40,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/import-url/main.css b/packages/css-variables-language-server/src/tests/fixtures/import-url/main.css index 9433bf4..d5ac79d 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/import-url/main.css +++ b/packages/css-variables-language-server/src/tests/fixtures/import-url/main.css @@ -8,6 +8,7 @@ --h3: 18px; --text-base: 16px; --carousel-bg: var(--main-bg-color); + --carousel-bg-nested: var(--carousel-bg); } } @@ -43,4 +44,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/less-nested/child/child.less b/packages/css-variables-language-server/src/tests/fixtures/less-nested/child/child.less index 45a8a3f..0eef8e8 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/less-nested/child/child.less +++ b/packages/css-variables-language-server/src/tests/fixtures/less-nested/child/child.less @@ -40,4 +40,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/less-nested/main.less b/packages/css-variables-language-server/src/tests/fixtures/less-nested/main.less index 2347183..c33775b 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/less-nested/main.less +++ b/packages/css-variables-language-server/src/tests/fixtures/less-nested/main.less @@ -5,6 +5,7 @@ --h3: 18px; --text-base: 16px; --carousel-bg: var(--main-bg-color); + --carousel-bg-nested: var(--carousel-bg); } .one { @@ -39,4 +40,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/child/child1.less b/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/child/child1.less index 091446d..11d61bd 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/child/child1.less +++ b/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/child/child1.less @@ -36,4 +36,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/child/child2.css b/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/child/child2.css index e498ec3..bde3fdc 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/child/child2.css +++ b/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/child/child2.css @@ -37,4 +37,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/main.scss b/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/main.scss index 2347183..c33775b 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/main.scss +++ b/packages/css-variables-language-server/src/tests/fixtures/mixed-nested/main.scss @@ -5,6 +5,7 @@ --h3: 18px; --text-base: 16px; --carousel-bg: var(--main-bg-color); + --carousel-bg-nested: var(--carousel-bg); } .one { @@ -39,4 +40,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/fixtures/scss-nested/stylesheets/bootstrap/_variables.scss b/packages/css-variables-language-server/src/tests/fixtures/scss-nested/stylesheets/bootstrap/_variables.scss index 5153e1b..e57a881 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/scss-nested/stylesheets/bootstrap/_variables.scss +++ b/packages/css-variables-language-server/src/tests/fixtures/scss-nested/stylesheets/bootstrap/_variables.scss @@ -11,6 +11,7 @@ $bootstrap-sass-asset-helper: false !default; --h3: 18px; --text-base: 16px; --carousel-bg: var(--main-bg-color); + --carousel-bg-nested: var(--carousel-bg); } //== Colors diff --git a/packages/css-variables-language-server/src/tests/fixtures/tailwindcss-nested/main.css b/packages/css-variables-language-server/src/tests/fixtures/tailwindcss-nested/main.css index c68987a..8085260 100644 --- a/packages/css-variables-language-server/src/tests/fixtures/tailwindcss-nested/main.css +++ b/packages/css-variables-language-server/src/tests/fixtures/tailwindcss-nested/main.css @@ -6,6 +6,7 @@ --h3: 18px; --text-base: 16px; --carousel-bg: var(--main-bg-color); + --carousel-bg-nested: var(--carousel-bg); } } @@ -41,4 +42,4 @@ .five { background-color: var(--main-bg-color); -} \ No newline at end of file +} diff --git a/packages/css-variables-language-server/src/tests/unit-tests/CSSVariableManager.test.ts b/packages/css-variables-language-server/src/tests/unit-tests/CSSVariableManager.test.ts index 4137701..546daf2 100644 --- a/packages/css-variables-language-server/src/tests/unit-tests/CSSVariableManager.test.ts +++ b/packages/css-variables-language-server/src/tests/unit-tests/CSSVariableManager.test.ts @@ -17,6 +17,7 @@ async function runTest( expect(allVars.get('--h3').symbol.value).toEqual('18px'); expect(allVars.get('--text-base').symbol.value).toEqual('16px'); expect(allVars.get('--carousel-bg').symbol.value).toEqual('brown'); + expect(allVars.get('--carousel-bg-nested').symbol.value).toEqual('brown'); expect(allVars.get('--child-main-bg-color').symbol.value).toEqual('brown'); expect(allVars.get('--child-h1').symbol.value).toEqual('26px'); expect(allVars.get('--child-h2').symbol.value).toEqual('22px'); From b915fe72362d9a4b61faa2a7e2639601907f4f5c Mon Sep 17 00:00:00 2001 From: Andrew Dassonville Date: Tue, 25 Jun 2024 18:52:28 -0700 Subject: [PATCH 5/5] Cleanup function call --- .../src/CSSVariableManager.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/css-variables-language-server/src/CSSVariableManager.ts b/packages/css-variables-language-server/src/CSSVariableManager.ts index 1936ecb..cb88900 100644 --- a/packages/css-variables-language-server/src/CSSVariableManager.ts +++ b/packages/css-variables-language-server/src/CSSVariableManager.ts @@ -164,16 +164,21 @@ export default class CSSVariableManager { ast.walkDecls((decl) => { if (decl.prop.startsWith('--')) { - this.setCssVariable(decl.prop, decl.value, fileURI,Range.create( - Position.create( - decl.source.start.line - 1, - decl.source.start.column - 1 - ), - Position.create( - decl.source.end.line - 1, - decl.source.end.column - 1 + this.setCssVariable( + decl.prop, + decl.value, + fileURI, + Range.create( + Position.create( + decl.source.start.line - 1, + decl.source.start.column - 1 + ), + Position.create( + decl.source.end.line - 1, + decl.source.end.column - 1 + ) ) - )); + ); } });