Skip to content

Commit

Permalink
feat(core): support style queries
Browse files Browse the repository at this point in the history
  • Loading branch information
idoros committed Dec 29, 2023
1 parent 61a1847 commit b2dbd81
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 43 deletions.
129 changes: 88 additions & 41 deletions packages/core/src/features/css-contains.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createFeature, FeatureContext } from './feature';
import * as STSymbol from './st-symbol';
import * as STImport from './st-import';
import * as CSSCustomProperty from './css-custom-property';
import type { StylableMeta } from '../stylable-meta';
import { createDiagnosticReporter } from '../diagnostics';
import { plugableRecord } from '../helpers/plugable-record';
Expand Down Expand Up @@ -130,47 +131,71 @@ export const hooks = createFeature<{
}
},
analyzeAtRule({ context, atRule }) {
if (!atRule.nodes) {
// treat @container with no body as definition
const ast = valueParser(atRule.params).nodes;
let searching = true;
let name = '';
let global = false;
for (const node of ast) {
if (node.type === 'comment' || node.type === 'space') {
// do nothing
continue;
} else if (searching && node.type === 'word') {
name = node.value;
} else if (searching && node.type === 'function' && node.value === GLOBAL_FUNC) {
name = globalValueFromFunctionNode(node) || '';
global = true;
} else {
const def = valueParser.stringify(node);
context.diagnostics.report(diagnostics.UNEXPECTED_DEFINITION(def), {
node: atRule,
word: def,
});
break;
const ast = valueParser(atRule.params).nodes;
let name = '';
let global = false;
let searchForContainerName = true;
let searchForLogicalOp = false;
for (const node of ast) {
if (node.type === 'comment' || node.type === 'space') {
// do nothing
continue;
} else if (searchForContainerName && node.type === 'word') {
searchForContainerName = false;
searchForLogicalOp = true;
name = node.value;
} else if (
searchForContainerName &&
node.type === 'function' &&
node.value === GLOBAL_FUNC
) {
searchForContainerName = false;
searchForLogicalOp = true;
name = globalValueFromFunctionNode(node) || '';
global = true;
} else if (node.type === 'function' && node.value === 'style') {
searchForContainerName = false;
searchForLogicalOp = true;
// check for custom properties
for (const queryNode of node.nodes) {
if (queryNode.type === 'word' && queryNode.value.startsWith('--')) {
CSSCustomProperty.addCSSProperty({
context,
node: atRule,
name: queryNode.value,
global: context.meta.type === 'css',
final: false,
});
}
}
searching = false;
} else if (
node.type !== 'function' &&
(!searchForLogicalOp || (node.type === 'word' && !logicalOpNames[node.value]))
) {
const def = valueParser.stringify(node);
context.diagnostics.report(diagnostics.UNEXPECTED_DEFINITION(def), {
node: atRule,
word: def,
});
break;
}
if (name) {
if (invalidContainerNames[name]) {
context.diagnostics.report(diagnostics.INVALID_CONTAINER_NAME(name), {
node: atRule,
word: name,
});
}
addContainer({
context,
ast: atRule,
name,
importName: name,
global,
forceDefinition: true,
}
if (name && !atRule.nodes) {
// treat @container with no body as definition
if (invalidContainerNames[name]) {
context.diagnostics.report(diagnostics.INVALID_CONTAINER_NAME(name), {
node: atRule,
word: name,
});
}
addContainer({
context,
ast: atRule,
name,
importName: name,
global,
forceDefinition: true,
});
}
},
transformResolve({ context }) {
Expand Down Expand Up @@ -220,10 +245,11 @@ export const hooks = createFeature<{
}
const ast = valueParser(atRule.params).nodes;
let changed = false;
let searchForContainerName = true;
search: for (const node of ast) {
if (node.type === 'comment' || node.type === 'space') {
// do nothing
} else if (node.type === 'word') {
} else if (node.type === 'word' && searchForContainerName) {
const resolve = resolved.record[node.value];
if (resolve) {
node.value = getTransformedName(resolve);
Expand All @@ -235,16 +261,31 @@ export const hooks = createFeature<{
});
}
break search;
} else if (node.type === 'function' && node.value === GLOBAL_FUNC) {
} else if (
node.type === 'function' &&
node.value === GLOBAL_FUNC &&
searchForContainerName
) {
const globalName = globalValueFromFunctionNode(node) || '';
if (globalName) {
changed = true;
const wordNode: WordNode = node as any;
wordNode.type = 'word';
wordNode.value = globalName;
}
} else {
break search;
} else if (node.type === 'function' && node.value === 'style') {
// check for custom properties
searchForContainerName = false;
for (const queryNode of node.nodes) {
if (queryNode.type === 'word' && queryNode.value.startsWith('--')) {
changed = true;
CSSCustomProperty.transformPropertyIdent(
context.meta,
queryNode,
context.getResolvedSymbols
);
}
}
}
}
if (changed) {
Expand All @@ -265,6 +306,12 @@ export const hooks = createFeature<{
});

const invalidContainerNames: Record<string, true> = {
none: true,
and: true,
not: true,
or: true,
};
const logicalOpNames: Record<string, true> = {
and: true,
not: true,
or: true,
Expand Down
79 changes: 77 additions & 2 deletions packages/core/test/features/css-contains.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { STSymbol, CSSContains } from '@stylable/core/dist/features';
import { STSymbol, CSSContains, CSSCustomProperty } from '@stylable/core/dist/features';
import {
testStylableCore,
shouldReportNoDiagnostics,
Expand Down Expand Up @@ -543,6 +543,61 @@ describe('features/css-contains', () => {
});
});
});
describe('custom-property', () => {
it('should register and transform style query custom properties', () => {
const { sheets } = testStylableCore(`
/* @atrule(single-standalone) style(--entry-x) */
@container style(--x) {}
/* @atrule(multi) style(--entry-y) and style(--entry-z) */
@container style(--y) and style(--z) {}
/* @atrule(value) style(--entry-y: green) or style(--entry-z > 50px) */
@container style(--y: green) or style(--z > 50px) {}
`);

const { meta } = sheets['/entry.st.css'];

shouldReportNoDiagnostics(meta);

expect(CSSCustomProperty.get(meta, `--x`), `--x symbol`).to.eql({
_kind: `cssVar`,
name: `--x`,
global: false,
alias: undefined,
});
expect(CSSCustomProperty.get(meta, `--y`), `--y symbol`).to.eql({
_kind: `cssVar`,
name: `--y`,
global: false,
alias: undefined,
});
expect(CSSCustomProperty.get(meta, `--z`), `--z symbol`).to.eql({
_kind: `cssVar`,
name: `--z`,
global: false,
alias: undefined,
});
});
it('should reference imported custom properties', () => {
const { sheets } = testStylableCore({
'/imported.st.css': `
@property --x;
@property st-global(--g);
`,
'/entry.st.css': `
@st-import [--x, --g] from './imported.st.css';
/* @atrule(single-standalone) style(--imported-x) or style(--g) */
@container style(--x) or style(--g) {}
`,
});

const { meta } = sheets['/entry.st.css'];

shouldReportNoDiagnostics(meta);
});
});
describe('st-mixin', () => {
it('should mix @container for nested mixin', () => {
const { sheets } = testStylableCore(
Expand Down Expand Up @@ -582,7 +637,7 @@ describe('features/css-contains', () => {
});
});
describe('native css', () => {
it('should not namespace', () => {
it('should not namespace container name', () => {
const { stylable } = testStylableCore({
'/native.css': deindent(`
.x {
Expand Down Expand Up @@ -616,5 +671,25 @@ describe('features/css-contains', () => {
// JS exports
expect(exports.containers, `JS export only locals`).to.eql({});
});
it('should not namespace style query custom properties', () => {
const { stylable } = testStylableCore({
'/native.css': deindent(`
/* @atrule style(--x) */
@container style(--x) {}
`),
'/entry.st.css': `
@st-import [--x] from './native.css';
/* @atrule style(--x) */
@container style(--x) {}
`,
});

const { meta: nativeMeta } = stylable.transform('/native.css');
const { meta } = stylable.transform('/entry.st.css');

shouldReportNoDiagnostics(nativeMeta);
shouldReportNoDiagnostics(meta);
});
});
});

0 comments on commit b2dbd81

Please sign in to comment.