From 521949cb3a35db3d8dfcb7e31559e3608d755d7b Mon Sep 17 00:00:00 2001 From: Ido Rosenthal Date: Mon, 4 Mar 2024 13:35:47 +0200 Subject: [PATCH] feat: api for custom props available in stylesheet - local name in context stylesheet - local name in definition stylesheet - transformed name - source location (meta, start, end) --- .../core/src/features/css-custom-property.ts | 46 ++++++- .../test/features/css-custom-property.spec.ts | 129 +++++++++++++++++- 2 files changed, 172 insertions(+), 3 deletions(-) diff --git a/packages/core/src/features/css-custom-property.ts b/packages/core/src/features/css-custom-property.ts index d77a2240e..e7879b959 100644 --- a/packages/core/src/features/css-custom-property.ts +++ b/packages/core/src/features/css-custom-property.ts @@ -11,9 +11,13 @@ import type { Stylable } from '../stylable'; import { validateAllowedNodesUntil, stringifyFunction } from '../helpers/value'; import { globalValue, GLOBAL_FUNC } from '../helpers/global'; import { plugableRecord } from '../helpers/plugable-record'; -import { createDiagnosticReporter } from '../diagnostics'; +import { createDiagnosticReporter, Diagnostics } from '../diagnostics'; import type { StylableMeta } from '../stylable-meta'; -import type { StylableResolver, CSSResolve } from '../stylable-resolver'; +import { + type StylableResolver, + type CSSResolve, + createSymbolResolverWithCache, +} from '../stylable-resolver'; import type * as postcss from 'postcss'; // ToDo: refactor out - parse once and pass to hooks import postcssValueParser from 'postcss-value-parser'; @@ -292,6 +296,44 @@ const UNKNOWN_LOCATION = { export class StylablePublicApi { constructor(private stylable: Stylable) {} + + public getProperties(meta: StylableMeta) { + const results: Record< + string, + { + meta: StylableMeta; + localName: string; + targetName: string; + source: { + meta: StylableMeta; + start: postcss.Position; + end: postcss.Position; + }; + } + > = {}; + + const topLevelDiagnostics = new Diagnostics(); + const getResolvedSymbols = createSymbolResolverWithCache( + this.stylable.resolver, + topLevelDiagnostics + ); + const { cssVar } = getResolvedSymbols(meta); + for (const [name, symbol] of Object.entries(cssVar)) { + const defAst = STSymbol.getSymbolAstNode(symbol.meta, symbol.symbol); + results[name] = { + meta: symbol.meta, + localName: symbol.symbol.name, + targetName: getTransformedName(symbol), + source: { + meta: symbol.meta, + start: defAst?.source?.start || UNKNOWN_LOCATION, + end: defAst?.source?.end || UNKNOWN_LOCATION, + }, + }; + } + + return results; + } } function analyzeDeclValueVarCalls(context: FeatureContext, decl: postcss.Declaration) { diff --git a/packages/core/test/features/css-custom-property.spec.ts b/packages/core/test/features/css-custom-property.spec.ts index 1f4827da5..8f1f93dd3 100644 --- a/packages/core/test/features/css-custom-property.spec.ts +++ b/packages/core/test/features/css-custom-property.spec.ts @@ -6,7 +6,11 @@ import { diagnosticBankReportToStrings, deindent, } from '@stylable/core-test-kit'; -import { expect } from 'chai'; +import chai, { expect } from 'chai'; +import chaiSubset from 'chai-subset'; +import type { StylableMeta } from '../../src'; + +chai.use(chaiSubset); const stImportDiagnostics = diagnosticBankReportToStrings(STImport.diagnostics); const stSymbolDiagnostics = diagnosticBankReportToStrings(STSymbol.diagnostics); @@ -1105,4 +1109,127 @@ describe(`features/css-custom-property`, () => { ); }); }); + describe('introspection', () => { + function expectSourceLocation({ + source: { meta, start, end }, + expected, + }: { + source: { meta: StylableMeta; start: { offset: number }; end: { offset: number } }; + expected: string; + }) { + const actualSrc = meta.sourceAst.toString().slice(start.offset, end.offset); + expect(actualSrc).to.eql(expected); + } + describe('getProperties', () => { + it('should resolve all local properties', () => { + const { stylable, sheets } = testStylableCore( + deindent(` + @property --defInAtRule { + syntax: ''; + initial-value: green; + inherits: false; + } + + .root { + --defineInPropName: green; + + color: var(--defineInDeclValue); + } + `) + ); + + const { meta } = sheets['/entry.st.css']; + + const properties = stylable.cssCustomProperty.getProperties(meta); + + expect(properties).to.containSubset({ + '--defInAtRule': { + meta, + localName: '--defInAtRule', + targetName: '--entry-defInAtRule', + }, + '--defineInPropName': { + meta, + localName: '--defineInPropName', + targetName: '--entry-defineInPropName', + }, + '--defineInDeclValue': { + meta, + localName: '--defineInDeclValue', + targetName: '--entry-defineInDeclValue', + }, + }); + expectSourceLocation({ + source: properties['--defInAtRule'].source, + expected: `@property --defInAtRule {\n syntax: '';\n initial-value: green;\n inherits: false;\n}`, + }); + expectSourceLocation({ + source: properties['--defineInPropName'].source, + expected: `--defineInPropName: green;`, + }); + expectSourceLocation({ + source: properties['--defineInDeclValue'].source, + expected: `color: var(--defineInDeclValue);`, + }); + }); + it('should resolve imported properties', () => { + const { stylable, sheets } = testStylableCore({ + 'deep.st.css': ` + .x { + --deep: red; + } + `, + 'proxy.st.css': ` + @st-import [--deep as --deepReassign1] from './deep.st.css'; + .x { + --proxy: var(--deepReassign1); + } + `, + 'entry.st.css': deindent(` + @st-import [--proxy as --proxyReassign, --deepReassign1 as --deepReassign2] from './proxy.st.css'; + + .x { + --local: green; + } + `), + }); + + const { meta } = sheets['/entry.st.css']; + const { meta: proxyMeta } = sheets['/proxy.st.css']; + const { meta: deepMeta } = sheets['/deep.st.css']; + + const properties = stylable.cssCustomProperty.getProperties(meta); + + expect(properties).to.containSubset({ + '--local': { + meta, + localName: '--local', + targetName: '--entry-local', + }, + '--proxyReassign': { + meta: proxyMeta, + localName: '--proxy', + targetName: '--proxy-proxy', + }, + '--deepReassign2': { + meta: deepMeta, + localName: '--deep', + targetName: '--deep-deep', + }, + }); + expectSourceLocation({ + source: properties['--local'].source, + expected: `--local: green;`, + }); + expectSourceLocation({ + source: properties['--proxyReassign'].source, + expected: `--proxy: var(--deepReassign1);`, + }); + expectSourceLocation({ + source: properties['--deepReassign2'].source, + expected: `--deep: red;`, + }); + }); + }); + }); });