Skip to content

Commit

Permalink
feat(core): expose css custom properties api (#2943)
Browse files Browse the repository at this point in the history
  • Loading branch information
idoros committed Mar 12, 2024
1 parent 01bf04c commit 15f83ee
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 4 deletions.
57 changes: 55 additions & 2 deletions packages/core/src/features/css-custom-property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ import {
generateScopedCSSVar,
atPropertyValidationWarnings,
} from '../helpers/css-custom-property';
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, { WordNode } from 'postcss-value-parser';
Expand Down Expand Up @@ -315,6 +320,54 @@ export function addCSSProperty({
});
}

const UNKNOWN_LOCATION = Object.freeze({
offset: -1,
line: -1,
column: -1,
});

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) {
const parsed = postcssValueParser(decl.value);
parsed.walk((node) => {
Expand Down
16 changes: 15 additions & 1 deletion packages/core/src/features/st-symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ function createState(clone?: State): State {
container: clone ? { ...clone.byType.container } : {},
var: clone ? { ...clone.byType.var } : {},
},
symbolToAst: new WeakMap(),
};
}

Expand Down Expand Up @@ -113,6 +114,7 @@ interface State {
byType: {
[T in keyof SymbolMap]: Record<string, SymbolMap[T]>;
};
symbolToAst: WeakMap<StylableSymbol, postcss.Node>;
}

const dataKey = plugableRecord.key<State>('mappedSymbols');
Expand Down Expand Up @@ -173,7 +175,10 @@ export function addSymbol({
safeRedeclare?: boolean;
localName?: string;
}) {
const { byNS, byNSFlat, byType } = plugableRecord.getUnsafe(context.meta.data, dataKey);
const { byNS, byNSFlat, byType, symbolToAst } = plugableRecord.getUnsafe(
context.meta.data,
dataKey
);
const name = localName || symbol.name;
const typeTable = byType[symbol._kind];
const nsName = NAMESPACES[symbol._kind];
Expand All @@ -187,9 +192,18 @@ export function addSymbol({
byNS[nsName].push({ name, symbol, ast: node, safeRedeclare });
byNSFlat[nsName][name] = symbol;
typeTable[name] = symbol;
node && symbolToAst.set(symbol, node);
return symbol;
}

export function getSymbolAstNode(
meta: StylableMeta,
symbol: StylableSymbol
): postcss.Node | undefined {
const { symbolToAst } = plugableRecord.getUnsafe(meta.data, dataKey);
return symbolToAst.get(symbol);
}

export function reportRedeclare(context: FeatureContext) {
const { byNS } = plugableRecord.getUnsafe(context.meta.data, dataKey);
for (const symbols of Object.values(byNS)) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/stylable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export class Stylable {
public resolver: StylableResolver;
public stModule = new STImport.StylablePublicApi(this);
public stScope = new STScope.StylablePublicApi(this);
public cssCustomProperty = new CSSCustomProperty.StylablePublicApi(this);
public stVar = new STVar.StylablePublicApi(this);
public stMixin = new STMixin.StylablePublicApi(this);
public cssClass = new CSSClass.StylablePublicApi(this);
Expand Down
129 changes: 128 additions & 1 deletion packages/core/test/features/css-custom-property.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1170,4 +1174,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: '<color>';
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: '<color>';\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;`,
});
});
});
});
});

0 comments on commit 15f83ee

Please sign in to comment.