From 3b6f96eab4aa05c8a9a99bba5ef2097598296282 Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Tue, 22 Mar 2022 19:11:04 +0200 Subject: [PATCH 01/10] fix(core): custom-value inside computed st-var --- packages/core/src/custom-values.ts | 18 ++++--- packages/core/src/features/st-var.ts | 6 +-- packages/core/test/features/st-var.spec.ts | 55 ++++++++++++++++++++++ 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/packages/core/src/custom-values.ts b/packages/core/src/custom-values.ts index 42f248125..4dc1bf9d5 100644 --- a/packages/core/src/custom-values.ts +++ b/packages/core/src/custom-values.ts @@ -15,13 +15,19 @@ export function box(type: Type, value: Value): Box>(boxed: B | string): any { +export function unbox>( + boxed: B | string, + customValues?: CustomTypes +): any { if (typeof boxed === 'string') { return boxed; - } else if (typeof boxed === 'object' && boxed.type && hasOwnProperty.call(boxed, 'value')) { - return cloneDeepWith(boxed.value, unbox); + } else if (typeof boxed === 'object' && boxed?.type) { + const customValue = customValues?.[boxed.type]; + let value = boxed.value; + if (customValue?.flattenValue) { + value = customValue.getValue([], boxed, null, customValues!); + } + return cloneDeepWith(value, (v) => unbox(v, customValues)); } } @@ -44,7 +50,7 @@ export interface CustomValueExtension { getValue( path: string[], value: Box, - node: ParsedValue, + node: ParsedValue | null, customTypes: CustomTypes ): string; } diff --git a/packages/core/src/features/st-var.ts b/packages/core/src/features/st-var.ts index 8fd073c85..04b33669f 100644 --- a/packages/core/src/features/st-var.ts +++ b/packages/core/src/features/st-var.ts @@ -154,17 +154,15 @@ export class StylablePublicApi { } ); - const customValue = customValues[topLevelType?.type]; const computedStVar: ComputedStVar = { /** * In case of custom value that could be flat, we will use the "outputValue" which is a flat value. */ - value: - topLevelType && !customValue?.flattenValue ? unbox(topLevelType) : outputValue, + value: topLevelType ? unbox(topLevelType, customValues) : outputValue, diagnostics, }; - if (customValue?.flattenValue) { + if (topLevelType) { computedStVar.input = unbox(topLevelType); } diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index 99df56364..306ef257c 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -1340,6 +1340,61 @@ describe(`features/st-var`, () => { }); }); + it('should get deep computed custom value st-var', () => { + const { stylable, sheets } = testStylableCore({ + '/entry.st.css': ` + @st-import [stBorder] from './st-border.js'; + + :vars { + map: st-map(border stBorder(1px, solid, red)); + } + `, + // Stylable custom value + '/st-border.js': ` + const { createCustomValue, CustomValueStrategy } = require("@stylable/core"); + exports.stBorder = createCustomValue({ + processArgs: (node, customTypes) => { + return CustomValueStrategy.args(node, customTypes); + }, + createValue: ([size, style, color]) => { + return { + size, + style, + color, + }; + }, + getValue: (value, index) => { + return value[index]; + }, + flattenValue: ({ value: { size, style, color } }) => { + return { + delimiter: ' ', + parts: [size, style, color], + }; + }, + }) + `, + }); + + const { meta } = sheets['/entry.st.css']; + const computedVars = stylable.stVar.getComputed(meta); + + expect(Object.keys(computedVars)).to.eql(['map']); + expect(computedVars.map).to.containSubset({ + value: { + border: '1px solid red', + }, + input: { + border: { + color: 'red', + size: '1px', + style: 'solid', + }, + }, + diagnostics: { reports: [] }, + }); + }); + it('should get imported computed st-vars', () => { const { stylable, sheets } = testStylableCore({ '/entry.st.css': ` From d63098556650014dc0ec10f4def74ac47d1e15b2 Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Wed, 23 Mar 2022 16:43:13 +0200 Subject: [PATCH 02/10] fix(core): add descriptive input for the stVar --- packages/core/src/custom-values.ts | 37 +++++++-- packages/core/src/features/st-var.ts | 8 +- packages/core/test/features/st-var.spec.ts | 95 +++++++++++++++++++--- 3 files changed, 120 insertions(+), 20 deletions(-) diff --git a/packages/core/src/custom-values.ts b/packages/core/src/custom-values.ts index 4dc1bf9d5..bc4b19147 100644 --- a/packages/core/src/custom-values.ts +++ b/packages/core/src/custom-values.ts @@ -6,12 +6,18 @@ import type { ParsedValue } from './types'; export interface Box { type: Type; value: Value; + flatValue: string | undefined; } -export function box(type: Type, value: Value): Box { +export function box( + type: Type, + value: Value, + flatValue?: string +): Box { return { type, value, + flatValue, }; } @@ -177,7 +183,19 @@ export function createCustomValue({ flattenValue, evalVarAst(fnNode: ParsedValue, customTypes: CustomTypes) { const args = processArgs(fnNode, customTypes); - return box(localTypeSymbol, createValue(args)); + const value = createValue(args); + let flatValue: string | undefined; + + if (flattenValue) { + flatValue = getFlatValue( + flattenValue, + box(localTypeSymbol, value), + fnNode, + customTypes + ); + } + + return box(localTypeSymbol, value, flatValue); }, getValue( path: string[], @@ -187,10 +205,7 @@ export function createCustomValue({ ): string { if (path.length === 0) { if (flattenValue) { - const { delimiter, parts } = flattenValue(obj); - return parts - .map((v) => getBoxValue([], v, fallbackNode, customTypes)) - .join(delimiter); + return getFlatValue(flattenValue, obj, fallbackNode, customTypes); } else { // TODO: add diagnostics return getStringValue([fallbackNode]); @@ -204,6 +219,16 @@ export function createCustomValue({ }; } +function getFlatValue( + flattenValue: FlattenValue, + obj: Box, + fallbackNode: ParsedValue, + customTypes: CustomTypes +) { + const { delimiter, parts } = flattenValue(obj); + return parts.map((v) => getBoxValue([], v, fallbackNode, customTypes)).join(delimiter); +} + export function getBoxValue( path: string[], value: string | Box, diff --git a/packages/core/src/features/st-var.ts b/packages/core/src/features/st-var.ts index 04b33669f..dbe96520c 100644 --- a/packages/core/src/features/st-var.ts +++ b/packages/core/src/features/st-var.ts @@ -1,5 +1,5 @@ import { createFeature, FeatureContext, FeatureTransformContext } from './feature'; -import { deprecatedStFunctions } from '../custom-values'; +import { Box, deprecatedStFunctions } from '../custom-values'; import { generalDiagnostics } from './diagnostics'; import * as STSymbol from './st-symbol'; import type { StylableMeta } from '../stylable-meta'; @@ -28,10 +28,12 @@ export interface VarSymbol { node: postcss.Node; } +type Input = Box | Array>; + export interface ComputedStVar { value: RuntimeStVar; diagnostics: Diagnostics; - input?: any; + input?: Input; } export const diagnostics = { @@ -163,7 +165,7 @@ export class StylablePublicApi { }; if (topLevelType) { - computedStVar.input = unbox(topLevelType); + computedStVar.input = topLevelType; } computed[localName] = computedStVar; diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index 306ef257c..ad3b58295 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -1284,7 +1284,42 @@ describe(`features/st-var`, () => { }); expect(computedVars.c).to.containSubset({ value: ['red', 'gold'], - input: undefined, + input: { + type: 'st-array', + value: ['red', 'gold'], + }, + diagnostics: { reports: [] }, + }); + }); + + it('should get deep computed complex st-vars', () => { + const { stylable, sheets } = testStylableCore(` + :vars { + map: st-map(a st-map(b red)); + } + `); + + const { meta } = sheets['/entry.st.css']; + const computedVars = stylable.stVar.getComputed(meta); + + expect(Object.keys(computedVars)).to.eql(['map']); + expect(computedVars.map).to.containSubset({ + value: { + a: { + b: 'red', + }, + }, + input: { + type: 'st-map', + value: { + a: { + type: 'st-map', + value: { + b: 'red', + }, + }, + }, + }, diagnostics: { reports: [] }, }); }); @@ -1332,9 +1367,13 @@ describe(`features/st-var`, () => { expect(computedVars.border).to.containSubset({ value: '1px solid red', input: { - color: 'red', - size: '1px', - style: 'solid', + type: 'stBorder', + flatValue: '1px solid red', + value: { + color: 'red', + size: '1px', + style: 'solid', + }, }, diagnostics: { reports: [] }, }); @@ -1346,7 +1385,8 @@ describe(`features/st-var`, () => { @st-import [stBorder] from './st-border.js'; :vars { - map: st-map(border stBorder(1px, solid, red)); + array: st-array(red, stBorder(1px, solid, blue)); + map: st-map(border stBorder(1px, solid, value(array, 0))); } `, // Stylable custom value @@ -1379,16 +1419,44 @@ describe(`features/st-var`, () => { const { meta } = sheets['/entry.st.css']; const computedVars = stylable.stVar.getComputed(meta); - expect(Object.keys(computedVars)).to.eql(['map']); + expect(Object.keys(computedVars)).to.eql(['array', 'map']); + expect(computedVars.array).to.containSubset({ + value: ['red', '1px solid blue'], + input: { + type: 'st-array', + flatValue: undefined, + value: [ + 'red', + { + type: 'stBorder', + flatValue: '1px solid blue', + value: { + color: 'blue', + size: '1px', + style: 'solid', + }, + }, + ], + }, + diagnostics: { reports: [] }, + }); expect(computedVars.map).to.containSubset({ value: { border: '1px solid red', }, input: { - border: { - color: 'red', - size: '1px', - style: 'solid', + type: 'st-map', + flatValue: undefined, + value: { + border: { + type: 'stBorder', + flatValue: '1px solid red', + value: { + color: 'red', + size: '1px', + style: 'solid', + }, + }, }, }, diagnostics: { reports: [] }, @@ -1428,7 +1496,12 @@ describe(`features/st-var`, () => { }); expect(computedVars.b).to.containSubset({ value: { a: 'red' }, - input: undefined, + input: { + type: 'st-map', + value: { + a: 'red', + }, + }, diagnostics: { reports: [] }, }); }); From 995a2063c43a7de080d9492c96a10478619b6eaf Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Sun, 27 Mar 2022 13:08:32 +0300 Subject: [PATCH 03/10] refactor: move `st-border` mock implementation --- packages/core/test/features/st-var.spec.ts | 79 ++++++++-------------- 1 file changed, 29 insertions(+), 50 deletions(-) diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index ad3b58295..0e641c873 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -10,6 +10,31 @@ import postcssValueParser from 'postcss-value-parser'; chai.use(chaiSubset); describe(`features/st-var`, () => { + const stBorderDefinitionMock = ` + const { createCustomValue, CustomValueStrategy } = require("@stylable/core"); + exports.stBorder = createCustomValue({ + processArgs: (node, customTypes) => { + return CustomValueStrategy.args(node, customTypes); + }, + createValue: ([size, style, color]) => { + return { + size, + style, + color, + }; + }, + getValue: (value, index) => { + return value[index]; + }, + flattenValue: ({ value: { size, style, color } }) => { + return { + delimiter: ' ', + parts: [size, style, color], + }; + }, + }) + `; + it(`should process :vars definitions`, () => { const { sheets } = testStylableCore(` /* @transform-remove */ @@ -1327,37 +1352,14 @@ describe(`features/st-var`, () => { it('should get computed custom value st-var', () => { const { stylable, sheets } = testStylableCore({ '/entry.st.css': ` - @st-import [stBorder] from './st-border.js'; + @st-import [stBorder as createBorder] from './st-border.js'; :vars { - border: stBorder(1px, solid, red); + border: createBorder(1px, solid, red); } `, // Stylable custom value - '/st-border.js': ` - const { createCustomValue, CustomValueStrategy } = require("@stylable/core"); - exports.stBorder = createCustomValue({ - processArgs: (node, customTypes) => { - return CustomValueStrategy.args(node, customTypes); - }, - createValue: ([size, style, color]) => { - return { - size, - style, - color, - }; - }, - getValue: (value, index) => { - return value[index]; - }, - flattenValue: ({ value: { size, style, color } }) => { - return { - delimiter: ' ', - parts: [size, style, color], - }; - }, - }) - `, + '/st-border.js': stBorderDefinitionMock, }); const { meta } = sheets['/entry.st.css']; @@ -1390,30 +1392,7 @@ describe(`features/st-var`, () => { } `, // Stylable custom value - '/st-border.js': ` - const { createCustomValue, CustomValueStrategy } = require("@stylable/core"); - exports.stBorder = createCustomValue({ - processArgs: (node, customTypes) => { - return CustomValueStrategy.args(node, customTypes); - }, - createValue: ([size, style, color]) => { - return { - size, - style, - color, - }; - }, - getValue: (value, index) => { - return value[index]; - }, - flattenValue: ({ value: { size, style, color } }) => { - return { - delimiter: ' ', - parts: [size, style, color], - }; - }, - }) - `, + '/st-border.js': stBorderDefinitionMock, }); const { meta } = sheets['/entry.st.css']; From 724340f18b9ad4cfbd0068b574e52f2ace963d51 Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Mon, 28 Mar 2022 20:40:32 +0300 Subject: [PATCH 04/10] fix(core): full input st-vars --- packages/core/src/custom-values.ts | 63 +++++---- packages/core/src/features/st-var.ts | 34 ++--- packages/core/src/functions.ts | 59 +++++++-- packages/core/src/stylable-transformer.ts | 1 + packages/core/test/features/st-var.spec.ts | 142 +++++++++++++++++---- 5 files changed, 213 insertions(+), 86 deletions(-) diff --git a/packages/core/src/custom-values.ts b/packages/core/src/custom-values.ts index bc4b19147..6f67e4de9 100644 --- a/packages/core/src/custom-values.ts +++ b/packages/core/src/custom-values.ts @@ -3,6 +3,12 @@ import postcssValueParser from 'postcss-value-parser'; import { getFormatterArgs, getNamedArgs, getStringValue } from './helpers/value'; import type { ParsedValue } from './types'; +export class ValueError extends Error { + constructor(message: string, public fallbackValue: string) { + super(message); + } +} + export interface Box { type: Type; value: Value; @@ -23,17 +29,19 @@ export function box( export function unbox>( boxed: B | string, - customValues?: CustomTypes + unboxPrimitives = true, + customValues?: CustomTypes, + node?: ParsedValue ): any { if (typeof boxed === 'string') { - return boxed; - } else if (typeof boxed === 'object' && boxed?.type) { + return unboxPrimitives ? boxed : box('string', boxed); + } else if (typeof boxed === 'object' && boxed !== null) { const customValue = customValues?.[boxed.type]; let value = boxed.value; - if (customValue?.flattenValue) { - value = customValue.getValue([], boxed, null, customValues!); + if (customValue?.flattenValue && node) { + value = customValue.getValue([], boxed, node, customValues!); } - return cloneDeepWith(value, (v) => unbox(v, customValues)); + return cloneDeepWith(value, (v) => unbox(v, unboxPrimitives, customValues, node)); } } @@ -51,20 +59,21 @@ export interface CustomValueExtension { valueAst: ParsedValue, customTypes: { [typeID: string]: CustomValueExtension; - } + }, + boxPrimitive?: boolean ): Box; getValue( path: string[], value: Box, - node: ParsedValue | null, + node: ParsedValue, customTypes: CustomTypes ): string; } function createStArrayCustomFunction() { return createCustomValue({ - processArgs: (node, customTypes) => { - return CustomValueStrategy.args(node, customTypes); + processArgs: (node, customTypes, boxPrimitive) => { + return CustomValueStrategy.args(node, customTypes, boxPrimitive); }, createValue: (args) => { return args; @@ -75,8 +84,8 @@ function createStArrayCustomFunction() { function createStMapCustomFunction() { return createCustomValue({ - processArgs: (node, customTypes) => { - return CustomValueStrategy.named(node, customTypes); + processArgs: (node, customTypes, boxPrimitive) => { + return CustomValueStrategy.named(node, customTypes, boxPrimitive); }, createValue: (args) => { return args; @@ -104,7 +113,7 @@ export const deprecatedStFunctions: Record }; export const CustomValueStrategy = { - args: (fnNode: ParsedValue, customTypes: CustomTypes) => { + args: (fnNode: ParsedValue, customTypes: CustomTypes, boxPrimitive?: boolean) => { const pathArgs = getFormatterArgs(fnNode); const outputArray = []; for (const arg of pathArgs) { @@ -112,13 +121,13 @@ export const CustomValueStrategy = { const ct = parsedArg.type === 'function' && parsedArg.value; const resolvedValue = typeof ct === 'string' && customTypes[ct] - ? customTypes[ct].evalVarAst(parsedArg, customTypes) - : arg; + ? customTypes[ct].evalVarAst(parsedArg, customTypes, boxPrimitive) + : unbox(arg, !boxPrimitive); outputArray.push(resolvedValue); } return outputArray; }, - named: (fnNode: ParsedValue, customTypes: CustomTypes) => { + named: (fnNode: ParsedValue, customTypes: CustomTypes, boxPrimitive?: boolean) => { const outputMap: BoxedValueMap = {}; const s = getNamedArgs(fnNode); for (const [prop, space, ...valueNodes] of s) { @@ -136,13 +145,13 @@ export const CustomValueStrategy = { if (!resolvedValue) { const ct = customTypes[valueNode.value]; if (valueNode.type === 'function' && ct) { - resolvedValue = ct.evalVarAst(valueNode, customTypes); + resolvedValue = ct.evalVarAst(valueNode, customTypes, boxPrimitive); } else { - resolvedValue = getStringValue(valueNode); + resolvedValue = unbox(getStringValue(valueNode), !boxPrimitive); } } } else { - resolvedValue = getStringValue(valueNodes); + resolvedValue = unbox(getStringValue(valueNodes), !boxPrimitive); } if (resolvedValue) { @@ -164,7 +173,7 @@ type FlattenValue = (v: Box) => { }; interface ExtensionApi { - processArgs: (fnNode: ParsedValue, customTypes: CustomTypes) => Args; + processArgs: (fnNode: ParsedValue, customTypes: CustomTypes, boxPrimitive?: boolean) => Args; createValue: (args: Args) => Value; getValue: (v: Value, key: string) => string | Box; flattenValue?: FlattenValue; @@ -181,8 +190,8 @@ export function createCustomValue({ register(localTypeSymbol: string) { return { flattenValue, - evalVarAst(fnNode: ParsedValue, customTypes: CustomTypes) { - const args = processArgs(fnNode, customTypes); + evalVarAst(fnNode: ParsedValue, customTypes: CustomTypes, boxPrimitive?: boolean) { + const args = processArgs(fnNode, customTypes, boxPrimitive); const value = createValue(args); let flatValue: string | undefined; @@ -207,8 +216,12 @@ export function createCustomValue({ if (flattenValue) { return getFlatValue(flattenValue, obj, fallbackNode, customTypes); } else { - // TODO: add diagnostics - return getStringValue([fallbackNode]); + const stringifiedValue = getStringValue([fallbackNode]); + + throw new ValueError( + `/* Error trying to flat -> */${stringifiedValue}`, + stringifiedValue + ); } } const value = getValue(obj.value, path[0]); @@ -239,6 +252,8 @@ export function getBoxValue( return value; } else if (value && customTypes[value.type]) { return customTypes[value.type].getValue(path, value, node, customTypes); + } else if (value.type === 'string') { + return (value as Box<'string', string>).value; } else { throw new Error('Unknown Type ' + JSON.stringify(value)); // return JSON.stringify(value); diff --git a/packages/core/src/features/st-var.ts b/packages/core/src/features/st-var.ts index dbe96520c..fb6e7c588 100644 --- a/packages/core/src/features/st-var.ts +++ b/packages/core/src/features/st-var.ts @@ -1,5 +1,5 @@ import { createFeature, FeatureContext, FeatureTransformContext } from './feature'; -import { Box, deprecatedStFunctions } from '../custom-values'; +import { unbox, Box, deprecatedStFunctions } from '../custom-values'; import { generalDiagnostics } from './diagnostics'; import * as STSymbol from './st-symbol'; import type { StylableMeta } from '../stylable-meta'; @@ -14,7 +14,6 @@ import type { ImmutablePseudoClass, PseudoClass } from '@tokey/css-selector-pars import type * as postcss from 'postcss'; import { processDeclarationFunctions } from '../process-declaration-functions'; import { Diagnostics } from '../diagnostics'; -import { unbox } from '../custom-values'; import type { ParsedValue } from '../types'; import type { Stylable } from '../stylable'; import type { RuntimeStVar } from '../stylable-transformer'; @@ -28,12 +27,12 @@ export interface VarSymbol { node: postcss.Node; } -type Input = Box | Array>; +export type Input = Box | Array>; export interface ComputedStVar { value: RuntimeStVar; diagnostics: Diagnostics; - input?: Input; + input: Input; } export const diagnostics = { @@ -49,8 +48,8 @@ export const diagnostics = { .map((s, i) => (i === cyclicChain.length - 1 ? '↻ ' : i === 0 ? '→ ' : '↪ ') + s) .join('\n')}"`, MISSING_VAR_IN_VALUE: () => `invalid value() with no var identifier`, - COULD_NOT_RESOLVE_VALUE: (args: string) => - `cannot resolve value function using the arguments provided: "${args}"`, + COULD_NOT_RESOLVE_VALUE: (args?: string) => + `cannot resolve value function${args ? ` using the arguments provided: "${args}"` : ''}`, MULTI_ARGS_IN_VALUE: (args: string) => `value function accepts only a single argument: "value(${args})"`, CANNOT_USE_AS_VALUE: (type: string, varName: string) => @@ -135,13 +134,13 @@ export class StylablePublicApi { topLevelDiagnostics ); - const { var: stVars, customValues } = getResolvedSymbols(meta); + const { var: stVars } = getResolvedSymbols(meta); const computed: Record = {}; for (const [localName, resolvedVar] of Object.entries(stVars)) { const diagnostics = new Diagnostics(); - const { outputValue, topLevelType } = evaluator.evaluateValue( + const { outputValue, topLevelType, runtimeValue } = evaluator.evaluateValue( { getResolvedSymbols, resolver: this.stylable.resolver, @@ -157,17 +156,11 @@ export class StylablePublicApi { ); const computedStVar: ComputedStVar = { - /** - * In case of custom value that could be flat, we will use the "outputValue" which is a flat value. - */ - value: topLevelType ? unbox(topLevelType, customValues) : outputValue, + value: runtimeValue ?? outputValue, + input: topLevelType ?? unbox(outputValue, false), diagnostics, }; - if (topLevelType) { - computedStVar.input = topLevelType; - } - computed[localName] = computedStVar; } @@ -286,17 +279,14 @@ function evaluateValueCall( args: restArgs, node: resolvedVarSymbol.node, meta: resolvedVar.meta, + rootArgument: varName, + evaluatorNode: node, } ); // report errors if (node) { const argsAsString = parsedArgs.join(', '); - if (typeError) { - context.diagnostics.warn( - node, - diagnostics.COULD_NOT_RESOLVE_VALUE(argsAsString) - ); - } else if (!topLevelType && parsedArgs.length > 1) { + if (!typeError && !topLevelType && parsedArgs.length > 1) { context.diagnostics.warn(node, diagnostics.MULTI_ARGS_IN_VALUE(argsAsString)); } } diff --git a/packages/core/src/functions.ts b/packages/core/src/functions.ts index 2866ab3f5..0d56f8a58 100644 --- a/packages/core/src/functions.ts +++ b/packages/core/src/functions.ts @@ -12,11 +12,12 @@ import { createSymbolResolverWithCache, MetaResolvedSymbols, } from './stylable-resolver'; -import type { replaceValueHook, StylableTransformer } from './stylable-transformer'; +import type { replaceValueHook, RuntimeStVar, StylableTransformer } from './stylable-transformer'; import { getFormatterArgs, getStringValue, stringifyFunction } from './helpers/value'; import type { ParsedValue } from './types'; import type { FeatureTransformContext } from './features/feature'; import { CSSCustomProperty, STVar } from './features'; +import { unbox, ValueError } from './custom-values'; export type ValueFormatter = (name: string) => string; export type ResolvedFormatter = Record; @@ -30,10 +31,13 @@ export interface EvalValueData { tsVarOverride?: Record | null; cssVarsMapping?: Record; args?: string[]; + rootArgument?: string; + evaluatorNode?: postcss.Node; } export interface EvalValueResult { topLevelType: any; + runtimeValue: RuntimeStVar; outputValue: string; typeError?: Error; } @@ -58,7 +62,9 @@ export class StylableEvaluator { context.diagnostics, data.passedThrough, data.cssVarsMapping, - data.args + data.args, + data.rootArgument, + data.evaluatorNode ); } } @@ -111,7 +117,9 @@ export function processDeclarationValue( diagnostics: Diagnostics = new Diagnostics(), passedThrough: string[] = [], cssVarsMapping: Record = {}, - args: string[] = [] + args: string[] = [], + rootArgument?: string, + evaluatorNode?: postcss.Node ): EvalValueResult { const evaluator = new StylableEvaluator({ tsVarOverride: variableOverride }); const resolvedSymbols = getResolvedSymbols(meta); @@ -137,6 +145,8 @@ export function processDeclarationValue( tsVarOverride: variableOverride, cssVarsMapping, args, + rootArgument, + evaluatorNode, }, node: parsedNode, }); @@ -204,6 +214,8 @@ export function processDeclarationValue( tsVarOverride: variableOverride, cssVarsMapping, args, + rootArgument, + evaluatorNode, }, node: parsedNode, }); @@ -220,23 +232,46 @@ export function processDeclarationValue( let outputValue = ''; let topLevelType = null; + let runtimeValue = null; let typeError: Error | undefined = undefined; for (const n of parsedValue.nodes) { if (n.type === 'function') { const matchingType = resolvedSymbols.customValues[n.value]; if (matchingType) { - topLevelType = matchingType.evalVarAst(n, resolvedSymbols.customValues); try { - outputValue += matchingType.getValue( - args, - topLevelType, - n, - resolvedSymbols.customValues - ); + topLevelType = matchingType.evalVarAst(n, resolvedSymbols.customValues, true); + runtimeValue = unbox(topLevelType, true, resolvedSymbols.customValues, n); + try { + outputValue += matchingType.getValue( + args, + topLevelType, + n, + resolvedSymbols.customValues + ); + } catch (error) { + if (error instanceof ValueError) { + outputValue += error.fallbackValue; + } else { + throw error; + } + } } catch (e) { typeError = e as Error; - // catch broken variable resolutions + + const invalidNode = evaluatorNode || node; + + if (invalidNode) { + diagnostics.warn( + invalidNode, + STVar.diagnostics.COULD_NOT_RESOLVE_VALUE( + [...(rootArgument ? [rootArgument] : []), ...args].join(', ') + ), + { word: value } + ); + } else { + // TODO: catch broken variable resolutions without a node + } } } else { outputValue += getStringValue([n]); @@ -245,7 +280,7 @@ export function processDeclarationValue( outputValue += getStringValue([n]); } } - return { outputValue, topLevelType, typeError }; + return { outputValue, topLevelType, typeError, runtimeValue }; } export function evalDeclarationValue( diff --git a/packages/core/src/stylable-transformer.ts b/packages/core/src/stylable-transformer.ts index c9a00874f..dbe0913fb 100644 --- a/packages/core/src/stylable-transformer.ts +++ b/packages/core/src/stylable-transformer.ts @@ -204,6 +204,7 @@ export class StylableTransformer { node: atRule, valueHook: this.replaceValueHook, passedThrough: path.slice(), + evaluatorNode: atRule, }).outputValue; } else if (name === 'property') { CSSCustomProperty.hooks.transformAtRuleNode({ diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index 0e641c873..b1a33585c 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -1299,19 +1299,38 @@ describe(`features/st-var`, () => { expect(Object.keys(computedVars)).to.eql(['a', 'b', 'c']); expect(computedVars.a).to.containSubset({ value: 'red', - input: undefined, + input: { + flatValue: undefined, + type: 'string', + value: 'red', + }, diagnostics: { reports: [] }, }); expect(computedVars.b).to.containSubset({ value: 'blue', - input: undefined, + input: { + flatValue: undefined, + type: 'string', + value: 'blue', + }, diagnostics: { reports: [] }, }); expect(computedVars.c).to.containSubset({ value: ['red', 'gold'], input: { type: 'st-array', - value: ['red', 'gold'], + value: [ + { + flatValue: undefined, + type: 'string', + value: 'red', + }, + { + flatValue: undefined, + type: 'string', + value: 'gold', + }, + ], }, diagnostics: { reports: [] }, }); @@ -1328,24 +1347,28 @@ describe(`features/st-var`, () => { const computedVars = stylable.stVar.getComputed(meta); expect(Object.keys(computedVars)).to.eql(['map']); - expect(computedVars.map).to.containSubset({ + expect(computedVars.map.diagnostics.reports.length).to.eql(0); + expect(computedVars.map.value).to.eql({ + a: { + b: 'red', + }, + }); + expect(computedVars.map.input).to.eql({ + type: 'st-map', + flatValue: undefined, value: { a: { - b: 'red', - }, - }, - input: { - type: 'st-map', - value: { - a: { - type: 'st-map', - value: { - b: 'red', + type: 'st-map', + flatValue: undefined, + value: { + b: { + flatValue: undefined, + type: 'string', + value: 'red', }, }, }, }, - diagnostics: { reports: [] }, }); }); @@ -1369,7 +1392,7 @@ describe(`features/st-var`, () => { expect(computedVars.border).to.containSubset({ value: '1px solid red', input: { - type: 'stBorder', + type: 'createBorder', flatValue: '1px solid red', value: { color: 'red', @@ -1387,8 +1410,12 @@ describe(`features/st-var`, () => { @st-import [stBorder] from './st-border.js'; :vars { - array: st-array(red, stBorder(1px, solid, blue)); - map: st-map(border stBorder(1px, solid, value(array, 0))); + array: st-array(blue, stBorder(1px, solid, blue)); + map: st-map( + border stBorder(value(array, 1, size), + solid, + value(array, 0)) + ); } `, // Stylable custom value @@ -1400,12 +1427,16 @@ describe(`features/st-var`, () => { expect(Object.keys(computedVars)).to.eql(['array', 'map']); expect(computedVars.array).to.containSubset({ - value: ['red', '1px solid blue'], + value: ['blue', '1px solid blue'], input: { type: 'st-array', flatValue: undefined, value: [ - 'red', + { + flatValue: undefined, + type: 'string', + value: 'blue', + }, { type: 'stBorder', flatValue: '1px solid blue', @@ -1421,7 +1452,7 @@ describe(`features/st-var`, () => { }); expect(computedVars.map).to.containSubset({ value: { - border: '1px solid red', + border: '1px solid blue', }, input: { type: 'st-map', @@ -1429,9 +1460,9 @@ describe(`features/st-var`, () => { value: { border: { type: 'stBorder', - flatValue: '1px solid red', + flatValue: '1px solid blue', value: { - color: 'red', + color: 'blue', size: '1px', style: 'solid', }, @@ -1465,12 +1496,20 @@ describe(`features/st-var`, () => { expect(Object.keys(computedVars)).to.eql(['imported', 'a', 'b']); expect(computedVars.imported).to.containSubset({ value: 'red', - input: undefined, + input: { + flatValue: undefined, + type: 'string', + value: 'red', + }, diagnostics: { reports: [] }, }); expect(computedVars.a).to.containSubset({ value: 'red', - input: undefined, + input: { + flatValue: undefined, + type: 'string', + value: 'red', + }, diagnostics: { reports: [] }, }); expect(computedVars.b).to.containSubset({ @@ -1503,17 +1542,29 @@ describe(`features/st-var`, () => { expect(Object.keys(computedVars)).to.eql(['validBefore', 'invalid', 'validAfter']); expect(computedVars.validBefore).to.containSubset({ value: 'red', - input: undefined, + input: { + flatValue: undefined, + type: 'string', + value: 'red', + }, diagnostics: { reports: [] }, }); expect(computedVars.validAfter).to.containSubset({ value: 'green', - input: undefined, + input: { + flatValue: undefined, + type: 'string', + value: 'green', + }, diagnostics: { reports: [] }, }); expect(computedVars.invalid).to.containSubset({ value: 'invalid-func(imported)', - input: undefined, + input: { + flatValue: undefined, + type: 'string', + value: 'invalid-func(imported)', + }, diagnostics: { reports: [ { @@ -1524,5 +1575,40 @@ describe(`features/st-var`, () => { }, }); }); + + it('should emit diagnostics only on invalid custom st-vars', () => { + const { stylable, sheets } = testStylableCore({ + '/entry.st.css': ` + @st-import [stBorder] from './st-border.js'; + + :vars { + border: stBorder(st-array(1px, 2px), solid, red); + } + `, + // Stylable custom value + '/st-border.js': stBorderDefinitionMock, + }); + + const { meta } = sheets['/entry.st.css']; + + const computedVars = stylable.stVar.getComputed(meta); + + expect(computedVars.border).to.containSubset({ + value: '', + input: { + flatValue: undefined, + type: 'string', + value: '', + }, + diagnostics: { + reports: [ + { + message: STVar.diagnostics.COULD_NOT_RESOLVE_VALUE(), + type: 'warning', + }, + ], + }, + }); + }); }); }); From cd18643702259e4e7c5562bd911f13d58c0eeda8 Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Mon, 28 Mar 2022 21:01:08 +0300 Subject: [PATCH 05/10] fix(core): add primitive value as flat value --- packages/core/src/custom-values.ts | 2 +- packages/core/test/features/st-var.spec.ts | 24 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/core/src/custom-values.ts b/packages/core/src/custom-values.ts index 6f67e4de9..1b02443d6 100644 --- a/packages/core/src/custom-values.ts +++ b/packages/core/src/custom-values.ts @@ -34,7 +34,7 @@ export function unbox>( node?: ParsedValue ): any { if (typeof boxed === 'string') { - return unboxPrimitives ? boxed : box('string', boxed); + return unboxPrimitives ? boxed : box('string', boxed, boxed); } else if (typeof boxed === 'object' && boxed !== null) { const customValue = customValues?.[boxed.type]; let value = boxed.value; diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index b1a33585c..5c24a49b6 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -1300,7 +1300,7 @@ describe(`features/st-var`, () => { expect(computedVars.a).to.containSubset({ value: 'red', input: { - flatValue: undefined, + flatValue: 'red', type: 'string', value: 'red', }, @@ -1309,7 +1309,7 @@ describe(`features/st-var`, () => { expect(computedVars.b).to.containSubset({ value: 'blue', input: { - flatValue: undefined, + flatValue: 'blue', type: 'string', value: 'blue', }, @@ -1321,12 +1321,12 @@ describe(`features/st-var`, () => { type: 'st-array', value: [ { - flatValue: undefined, + flatValue: 'red', type: 'string', value: 'red', }, { - flatValue: undefined, + flatValue: 'gold', type: 'string', value: 'gold', }, @@ -1362,7 +1362,7 @@ describe(`features/st-var`, () => { flatValue: undefined, value: { b: { - flatValue: undefined, + flatValue: 'red', type: 'string', value: 'red', }, @@ -1433,7 +1433,7 @@ describe(`features/st-var`, () => { flatValue: undefined, value: [ { - flatValue: undefined, + flatValue: 'blue', type: 'string', value: 'blue', }, @@ -1497,7 +1497,7 @@ describe(`features/st-var`, () => { expect(computedVars.imported).to.containSubset({ value: 'red', input: { - flatValue: undefined, + flatValue: 'red', type: 'string', value: 'red', }, @@ -1506,7 +1506,7 @@ describe(`features/st-var`, () => { expect(computedVars.a).to.containSubset({ value: 'red', input: { - flatValue: undefined, + flatValue: 'red', type: 'string', value: 'red', }, @@ -1543,7 +1543,7 @@ describe(`features/st-var`, () => { expect(computedVars.validBefore).to.containSubset({ value: 'red', input: { - flatValue: undefined, + flatValue: 'red', type: 'string', value: 'red', }, @@ -1552,7 +1552,7 @@ describe(`features/st-var`, () => { expect(computedVars.validAfter).to.containSubset({ value: 'green', input: { - flatValue: undefined, + flatValue: 'green', type: 'string', value: 'green', }, @@ -1561,7 +1561,7 @@ describe(`features/st-var`, () => { expect(computedVars.invalid).to.containSubset({ value: 'invalid-func(imported)', input: { - flatValue: undefined, + flatValue: 'invalid-func(imported)', type: 'string', value: 'invalid-func(imported)', }, @@ -1596,7 +1596,7 @@ describe(`features/st-var`, () => { expect(computedVars.border).to.containSubset({ value: '', input: { - flatValue: undefined, + flatValue: '', type: 'string', value: '', }, From e33e76a67a022cd328ffa26a5d1e28eb495e662d Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Tue, 29 Mar 2022 09:57:56 +0300 Subject: [PATCH 06/10] feat(core): flatten st-vars --- packages/core/src/features/st-var.ts | 46 ++ packages/core/src/index.ts | 2 +- packages/core/test/features/st-var.spec.ts | 785 ++++++++++++++------- 3 files changed, 559 insertions(+), 274 deletions(-) diff --git a/packages/core/src/features/st-var.ts b/packages/core/src/features/st-var.ts index fb6e7c588..4db4b8bc7 100644 --- a/packages/core/src/features/st-var.ts +++ b/packages/core/src/features/st-var.ts @@ -35,6 +35,12 @@ export interface ComputedStVar { input: Input; } +export interface FlatComputedStVar { + value: string; + symbol: string; + path: string[]; +} + export const diagnostics = { FORBIDDEN_DEF_IN_COMPLEX_SELECTOR: generalDiagnostics.FORBIDDEN_DEF_IN_COMPLEX_SELECTOR, NO_VARS_DEF_IN_ST_SCOPE() { @@ -166,6 +172,46 @@ export class StylablePublicApi { return computed; } + + public flatten(meta: StylableMeta) { + const computed = this.getComputed(meta); + + const flatStVars: FlatComputedStVar[] = []; + + for (const [symbol, stVar] of Object.entries(computed)) { + flatStVars.push(...this.flatSingle(stVar.input, symbol, [symbol])); + } + + return flatStVars; + } + + private flatSingle(input: Input, sourcePath: string, path: string[]) { + const currentVars: FlatComputedStVar[] = []; + + if (input.flatValue) { + currentVars.push({ + symbol: sourcePath, + value: input.flatValue, + path, + }); + } + + if (typeof input.value === `object` && input.value !== null) { + for (const [key, innerInput] of Object.entries(input.value)) { + currentVars.push( + ...this.flatSingle( + innerInput, + Array.isArray(input.value) + ? `${sourcePath}[${key}]` + : `${sourcePath}.${key}`, + [...path, key] + ) + ); + } + } + + return currentVars; + } } function collectVarSymbols(context: FeatureContext, rule: postcss.Rule) { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9c3ee770a..183909660 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -20,7 +20,7 @@ export type { } from './features'; export { reservedKeyFrames } from './features/css-keyframes'; export { scopeCSSVar } from './features/css-custom-property'; -export type { ComputedStVar } from './features/st-var'; +export type { ComputedStVar, FlatComputedStVar } from './features/st-var'; export { StylableProcessor, createEmptyMeta, diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index 5c24a49b6..db8cacc87 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -1284,330 +1284,569 @@ describe(`features/st-var`, () => { }); }); describe('introspection', () => { - it('should get computed st-vars', () => { - const { stylable, sheets } = testStylableCore(` - :vars { - a: red; - b: blue; - c: st-array(value(a), gold); - } - `); + describe('getComputed', () => { + it('should get computed st-vars', () => { + const { stylable, sheets } = testStylableCore(` + :vars { + a: red; + b: blue; + c: st-array(value(a), gold); + } + `); - const { meta } = sheets['/entry.st.css']; - const computedVars = stylable.stVar.getComputed(meta); - - expect(Object.keys(computedVars)).to.eql(['a', 'b', 'c']); - expect(computedVars.a).to.containSubset({ - value: 'red', - input: { - flatValue: 'red', - type: 'string', + const { meta } = sheets['/entry.st.css']; + const computedVars = stylable.stVar.getComputed(meta); + + expect(Object.keys(computedVars)).to.eql(['a', 'b', 'c']); + expect(computedVars.a).to.containSubset({ value: 'red', - }, - diagnostics: { reports: [] }, - }); - expect(computedVars.b).to.containSubset({ - value: 'blue', - input: { - flatValue: 'blue', - type: 'string', + input: { + flatValue: 'red', + type: 'string', + value: 'red', + }, + diagnostics: { reports: [] }, + }); + expect(computedVars.b).to.containSubset({ value: 'blue', - }, - diagnostics: { reports: [] }, - }); - expect(computedVars.c).to.containSubset({ - value: ['red', 'gold'], - input: { - type: 'st-array', - value: [ - { - flatValue: 'red', - type: 'string', - value: 'red', - }, - { - flatValue: 'gold', - type: 'string', - value: 'gold', - }, - ], - }, - diagnostics: { reports: [] }, + input: { + flatValue: 'blue', + type: 'string', + value: 'blue', + }, + diagnostics: { reports: [] }, + }); + expect(computedVars.c).to.containSubset({ + value: ['red', 'gold'], + input: { + type: 'st-array', + value: [ + { + flatValue: 'red', + type: 'string', + value: 'red', + }, + { + flatValue: 'gold', + type: 'string', + value: 'gold', + }, + ], + }, + diagnostics: { reports: [] }, + }); }); - }); - it('should get deep computed complex st-vars', () => { - const { stylable, sheets } = testStylableCore(` - :vars { - map: st-map(a st-map(b red)); - } - `); + it('should get deep computed complex st-vars', () => { + const { stylable, sheets } = testStylableCore(` + :vars { + map: st-map(a st-map(b red)); + } + `); - const { meta } = sheets['/entry.st.css']; - const computedVars = stylable.stVar.getComputed(meta); + const { meta } = sheets['/entry.st.css']; + const computedVars = stylable.stVar.getComputed(meta); - expect(Object.keys(computedVars)).to.eql(['map']); - expect(computedVars.map.diagnostics.reports.length).to.eql(0); - expect(computedVars.map.value).to.eql({ - a: { - b: 'red', - }, - }); - expect(computedVars.map.input).to.eql({ - type: 'st-map', - flatValue: undefined, - value: { + expect(Object.keys(computedVars)).to.eql(['map']); + expect(computedVars.map.diagnostics.reports.length).to.eql(0); + expect(computedVars.map.value).to.eql({ a: { + b: 'red', + }, + }); + expect(computedVars.map.input).to.eql({ + type: 'st-map', + flatValue: undefined, + value: { + a: { + type: 'st-map', + flatValue: undefined, + value: { + b: { + flatValue: 'red', + type: 'string', + value: 'red', + }, + }, + }, + }, + }); + }); + + it('should get computed custom value st-var', () => { + const { stylable, sheets } = testStylableCore({ + '/entry.st.css': ` + @st-import [stBorder as createBorder] from './st-border.js'; + + :vars { + border: createBorder(1px, solid, red); + } + `, + // Stylable custom value + '/st-border.js': stBorderDefinitionMock, + }); + + const { meta } = sheets['/entry.st.css']; + const computedVars = stylable.stVar.getComputed(meta); + + expect(Object.keys(computedVars)).to.eql(['border']); + expect(computedVars.border).to.containSubset({ + value: '1px solid red', + input: { + type: 'createBorder', + flatValue: '1px solid red', + value: { + color: 'red', + size: '1px', + style: 'solid', + }, + }, + diagnostics: { reports: [] }, + }); + }); + + it('should get deep computed custom value st-var', () => { + const { stylable, sheets } = testStylableCore({ + '/entry.st.css': ` + @st-import [stBorder] from './st-border.js'; + + :vars { + array: st-array(blue, stBorder(1px, solid, blue)); + map: st-map( + border stBorder(value(array, 1, size), + solid, + value(array, 0)) + ); + } + `, + // Stylable custom value + '/st-border.js': stBorderDefinitionMock, + }); + + const { meta } = sheets['/entry.st.css']; + const computedVars = stylable.stVar.getComputed(meta); + + expect(Object.keys(computedVars)).to.eql(['array', 'map']); + expect(computedVars.array).to.containSubset({ + value: ['blue', '1px solid blue'], + input: { + type: 'st-array', + flatValue: undefined, + value: [ + { + flatValue: 'blue', + type: 'string', + value: 'blue', + }, + { + type: 'stBorder', + flatValue: '1px solid blue', + value: { + color: 'blue', + size: '1px', + style: 'solid', + }, + }, + ], + }, + diagnostics: { reports: [] }, + }); + expect(computedVars.map).to.containSubset({ + value: { + border: '1px solid blue', + }, + input: { type: 'st-map', flatValue: undefined, value: { - b: { - flatValue: 'red', - type: 'string', - value: 'red', + border: { + type: 'stBorder', + flatValue: '1px solid blue', + value: { + color: 'blue', + size: '1px', + style: 'solid', + }, }, }, }, - }, + diagnostics: { reports: [] }, + }); }); - }); - it('should get computed custom value st-var', () => { - const { stylable, sheets } = testStylableCore({ - '/entry.st.css': ` - @st-import [stBorder as createBorder] from './st-border.js'; + it('should get imported computed st-vars', () => { + const { stylable, sheets } = testStylableCore({ + '/entry.st.css': ` + @st-import [imported-var as imported] from './imported.st.css'; + + :vars { + a: value(imported); + b: st-map(a value(imported)); + } + `, + 'imported.st.css': ` + :vars { + imported-var: red; + } + `, + }); - :vars { - border: createBorder(1px, solid, red); - } - `, - // Stylable custom value - '/st-border.js': stBorderDefinitionMock, - }); + const { meta } = sheets['/entry.st.css']; + const computedVars = stylable.stVar.getComputed(meta); - const { meta } = sheets['/entry.st.css']; - const computedVars = stylable.stVar.getComputed(meta); - - expect(Object.keys(computedVars)).to.eql(['border']); - expect(computedVars.border).to.containSubset({ - value: '1px solid red', - input: { - type: 'createBorder', - flatValue: '1px solid red', - value: { - color: 'red', - size: '1px', - style: 'solid', + expect(Object.keys(computedVars)).to.eql(['imported', 'a', 'b']); + expect(computedVars.imported).to.containSubset({ + value: 'red', + input: { + flatValue: 'red', + type: 'string', + value: 'red', }, - }, - diagnostics: { reports: [] }, + diagnostics: { reports: [] }, + }); + expect(computedVars.a).to.containSubset({ + value: 'red', + input: { + flatValue: 'red', + type: 'string', + value: 'red', + }, + diagnostics: { reports: [] }, + }); + expect(computedVars.b).to.containSubset({ + value: { a: 'red' }, + input: { + type: 'st-map', + value: { + a: 'red', + }, + }, + diagnostics: { reports: [] }, + }); }); - }); - it('should get deep computed custom value st-var', () => { - const { stylable, sheets } = testStylableCore({ - '/entry.st.css': ` - @st-import [stBorder] from './st-border.js'; + it('should emit diagnostics only on invalid computed st-vars', () => { + const { stylable, sheets } = testStylableCore( + ` + :vars { + validBefore: red; + invalid: invalid-func(imported); + validAfter: green; + } + ` + ); - :vars { - array: st-array(blue, stBorder(1px, solid, blue)); - map: st-map( - border stBorder(value(array, 1, size), - solid, - value(array, 0)) - ); - } - `, - // Stylable custom value - '/st-border.js': stBorderDefinitionMock, - }); + const { meta } = sheets['/entry.st.css']; - const { meta } = sheets['/entry.st.css']; - const computedVars = stylable.stVar.getComputed(meta); + const computedVars = stylable.stVar.getComputed(meta); - expect(Object.keys(computedVars)).to.eql(['array', 'map']); - expect(computedVars.array).to.containSubset({ - value: ['blue', '1px solid blue'], - input: { - type: 'st-array', - flatValue: undefined, - value: [ - { - flatValue: 'blue', - type: 'string', - value: 'blue', - }, - { - type: 'stBorder', - flatValue: '1px solid blue', - value: { - color: 'blue', - size: '1px', - style: 'solid', + expect(Object.keys(computedVars)).to.eql(['validBefore', 'invalid', 'validAfter']); + expect(computedVars.validBefore).to.containSubset({ + value: 'red', + input: { + flatValue: 'red', + type: 'string', + value: 'red', + }, + diagnostics: { reports: [] }, + }); + expect(computedVars.validAfter).to.containSubset({ + value: 'green', + input: { + flatValue: 'green', + type: 'string', + value: 'green', + }, + diagnostics: { reports: [] }, + }); + expect(computedVars.invalid).to.containSubset({ + value: 'invalid-func(imported)', + input: { + flatValue: 'invalid-func(imported)', + type: 'string', + value: 'invalid-func(imported)', + }, + diagnostics: { + reports: [ + { + message: functionWarnings.UNKNOWN_FORMATTER('invalid-func'), + type: 'warning', }, - }, - ], - }, - diagnostics: { reports: [] }, + ], + }, + }); }); - expect(computedVars.map).to.containSubset({ - value: { - border: '1px solid blue', - }, - input: { - type: 'st-map', - flatValue: undefined, - value: { - border: { - type: 'stBorder', - flatValue: '1px solid blue', - value: { - color: 'blue', - size: '1px', - style: 'solid', + + it('should emit diagnostics only on invalid custom st-vars', () => { + const { stylable, sheets } = testStylableCore({ + '/entry.st.css': ` + @st-import [stBorder] from './st-border.js'; + + :vars { + border: stBorder(st-array(1px, 2px), solid, red); + } + `, + // Stylable custom value + '/st-border.js': stBorderDefinitionMock, + }); + + const { meta } = sheets['/entry.st.css']; + + const computedVars = stylable.stVar.getComputed(meta); + + expect(computedVars.border).to.containSubset({ + value: '', + input: { + flatValue: '', + type: 'string', + value: '', + }, + diagnostics: { + reports: [ + { + message: STVar.diagnostics.COULD_NOT_RESOLVE_VALUE(), + type: 'warning', }, - }, + ], }, - }, - diagnostics: { reports: [] }, + }); }); }); - it('should get imported computed st-vars', () => { - const { stylable, sheets } = testStylableCore({ - '/entry.st.css': ` - @st-import [imported-var as imported] from './imported.st.css'; + describe('flatten', () => { + it('should flat simple st vars', () => { + const { stylable, sheets } = testStylableCore(` + :vars { + a: red; + b: blue; + } + `); - :vars { - a: value(imported); - b: st-map(a value(imported)); - } - `, - 'imported.st.css': ` - :vars { - imported-var: red; - } - `, - }); + const meta = sheets['/entry.st.css'].meta; + const flattenStVars = stylable.stVar.flatten(meta); - const { meta } = sheets['/entry.st.css']; - const computedVars = stylable.stVar.getComputed(meta); - - expect(Object.keys(computedVars)).to.eql(['imported', 'a', 'b']); - expect(computedVars.imported).to.containSubset({ - value: 'red', - input: { - flatValue: 'red', - type: 'string', - value: 'red', - }, - diagnostics: { reports: [] }, + expect(flattenStVars).to.eql([ + { + symbol: 'a', + value: 'red', + path: ['a'], + }, + { + symbol: 'b', + value: 'blue', + path: ['b'], + }, + ]); }); - expect(computedVars.a).to.containSubset({ - value: 'red', - input: { - flatValue: 'red', - type: 'string', - value: 'red', - }, - diagnostics: { reports: [] }, + it('should not flat native css function inside st vars', () => { + const { stylable, sheets } = testStylableCore(` + :vars { + a: linear-gradient(to right, red, blue); + } + `); + + const meta = sheets['/entry.st.css'].meta; + const flattenStVars = stylable.stVar.flatten(meta); + + expect(flattenStVars).to.eql([ + { + path: ['a'], + symbol: 'a', + value: 'linear-gradient(to right, red, blue)', + }, + ]); }); - expect(computedVars.b).to.containSubset({ - value: { a: 'red' }, - input: { - type: 'st-map', - value: { - a: 'red', + it('should flat imported simple st vars', () => { + const { stylable, sheets } = testStylableCore({ + '/entry.st.css': ` + @st-import [color as myColor] from './imported.st.css'; + .root { color: value(myColor); } + `, + '/imported.st.css': ` + :vars { + color: red; + } + `, + }); + + const meta = sheets['/entry.st.css'].meta; + const flattenStVars = stylable.stVar.flatten(meta); + + expect(flattenStVars).to.eql([ + { + symbol: 'myColor', + value: 'red', + path: ['myColor'], }, - }, - diagnostics: { reports: [] }, + ]); }); - }); - it('should emit diagnostics only on invalid computed st-vars', () => { - const { stylable, sheets } = testStylableCore( - ` - :vars { - validBefore: red; - invalid: invalid-func(imported); - validAfter: green; - } - ` - ); + it('should flat st-array st vars', () => { + const { stylable, sheets } = testStylableCore( + ` + :vars { + array: st-array(1px, 2px); + } - const { meta } = sheets['/entry.st.css']; + ` + ); - const computedVars = stylable.stVar.getComputed(meta); + const meta = sheets['/entry.st.css'].meta; + const flattenStVars = stylable.stVar.flatten(meta); - expect(Object.keys(computedVars)).to.eql(['validBefore', 'invalid', 'validAfter']); - expect(computedVars.validBefore).to.containSubset({ - value: 'red', - input: { - flatValue: 'red', - type: 'string', - value: 'red', - }, - diagnostics: { reports: [] }, - }); - expect(computedVars.validAfter).to.containSubset({ - value: 'green', - input: { - flatValue: 'green', - type: 'string', - value: 'green', - }, - diagnostics: { reports: [] }, + expect(flattenStVars).to.eql([ + { + symbol: 'array[0]', + value: '1px', + path: ['array', '0'], + }, + { + symbol: 'array[1]', + value: '2px', + path: ['array', '1'], + }, + ]); }); - expect(computedVars.invalid).to.containSubset({ - value: 'invalid-func(imported)', - input: { - flatValue: 'invalid-func(imported)', - type: 'string', - value: 'invalid-func(imported)', - }, - diagnostics: { - reports: [ - { - message: functionWarnings.UNKNOWN_FORMATTER('invalid-func'), - type: 'warning', - }, - ], - }, + + it('should flat st-map st vars', () => { + const { stylable, sheets } = testStylableCore( + ` + :vars { + map: st-map(first 1px,second 2px); + } + + ` + ); + + const meta = sheets['/entry.st.css'].meta; + const flattenStVars = stylable.stVar.flatten(meta); + + expect(flattenStVars).to.eql([ + { + symbol: 'map.first', + value: '1px', + path: ['map', 'first'], + }, + { + symbol: 'map.second', + value: '2px', + path: ['map', 'second'], + }, + ]); }); - }); - it('should emit diagnostics only on invalid custom st-vars', () => { - const { stylable, sheets } = testStylableCore({ - '/entry.st.css': ` - @st-import [stBorder] from './st-border.js'; + it('should flat custom value st vars', () => { + const { stylable, sheets } = testStylableCore({ + '/entry.st.css': ` + @st-import [stBorder] from './st-border.js'; + + :vars { + border: stBorder(1px, solid, red); + } + `, + '/st-border.js': stBorderDefinitionMock, + }); + + const meta = sheets['/entry.st.css'].meta; + const flattenStVars = stylable.stVar.flatten(meta); + expect(flattenStVars).to.eql([ + { + symbol: 'border', + value: '1px solid red', + path: ['border'], + }, + ]); + }); + it('should flat nested st-array st vars', () => { + const { stylable, sheets } = testStylableCore( + ` :vars { - border: stBorder(st-array(1px, 2px), solid, red); + nestedArray: st-array( + red, + st-array(red, green), + st-map(color1 gold, color2 blue), + ); } - `, - // Stylable custom value - '/st-border.js': stBorderDefinitionMock, + + ` + ); + + const meta = sheets['/entry.st.css'].meta; + const flattenStVars = stylable.stVar.flatten(meta); + + expect(flattenStVars).to.eql([ + { + symbol: 'nestedArray[0]', + value: 'red', + path: ['nestedArray', '0'], + }, + { + symbol: 'nestedArray[1][0]', + value: 'red', + path: ['nestedArray', '1', '0'], + }, + { + symbol: 'nestedArray[1][1]', + value: 'green', + path: ['nestedArray', '1', '1'], + }, + { + symbol: 'nestedArray[2].color1', + value: 'gold', + path: ['nestedArray', '2', 'color1'], + }, + { + symbol: 'nestedArray[2].color2', + value: 'blue', + path: ['nestedArray', '2', 'color2'], + }, + ]); }); + it('should flat nested st-map st vars', () => { + const { stylable, sheets } = testStylableCore( + ` + :vars { + nestedMap: st-map( + simple red, + array st-array(red, green), + map st-map(color1 gold, color2 blue) + ); + } - const { meta } = sheets['/entry.st.css']; + ` + ); - const computedVars = stylable.stVar.getComputed(meta); + const meta = sheets['/entry.st.css'].meta; + const flattenStVars = stylable.stVar.flatten(meta); - expect(computedVars.border).to.containSubset({ - value: '', - input: { - flatValue: '', - type: 'string', - value: '', - }, - diagnostics: { - reports: [ - { - message: STVar.diagnostics.COULD_NOT_RESOLVE_VALUE(), - type: 'warning', - }, - ], - }, + expect(flattenStVars).to.eql([ + { + symbol: 'nestedMap.simple', + value: 'red', + path: ['nestedMap', 'simple'], + }, + { + symbol: 'nestedMap.array[0]', + value: 'red', + path: ['nestedMap', 'array', '0'], + }, + { + symbol: 'nestedMap.array[1]', + value: 'green', + path: ['nestedMap', 'array', '1'], + }, + { + symbol: 'nestedMap.map.color1', + value: 'gold', + path: ['nestedMap', 'map', 'color1'], + }, + { + symbol: 'nestedMap.map.color2', + value: 'blue', + path: ['nestedMap', 'map', 'color2'], + }, + ]); }); }); }); From 4c50ac1b55cbd6260a08ef6e4ca9828bd19c0103 Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Tue, 29 Mar 2022 15:31:30 +0300 Subject: [PATCH 07/10] fix: PR rejects --- packages/core/src/custom-values.ts | 15 ++++-- packages/core/src/features/st-var.ts | 22 ++++---- packages/core/src/functions.ts | 16 +++--- packages/core/src/stylable-transformer.ts | 2 +- packages/core/test/features/st-var.spec.ts | 63 ++++++++++------------ 5 files changed, 58 insertions(+), 60 deletions(-) diff --git a/packages/core/src/custom-values.ts b/packages/core/src/custom-values.ts index 1b02443d6..73fa2797e 100644 --- a/packages/core/src/custom-values.ts +++ b/packages/core/src/custom-values.ts @@ -3,7 +3,7 @@ import postcssValueParser from 'postcss-value-parser'; import { getFormatterArgs, getNamedArgs, getStringValue } from './helpers/value'; import type { ParsedValue } from './types'; -export class ValueError extends Error { +export class CustomValueError extends Error { constructor(message: string, public fallbackValue: string) { super(message); } @@ -27,6 +27,10 @@ export function box( }; } +export function boxString(value: string) { + return box('st-string', value, value); +} + export function unbox>( boxed: B | string, unboxPrimitives = true, @@ -34,8 +38,8 @@ export function unbox>( node?: ParsedValue ): any { if (typeof boxed === 'string') { - return unboxPrimitives ? boxed : box('string', boxed, boxed); - } else if (typeof boxed === 'object' && boxed !== null) { + return unboxPrimitives ? boxed : boxString(boxed); + } else if (typeof boxed === 'object' && boxed) { const customValue = customValues?.[boxed.type]; let value = boxed.value; if (customValue?.flattenValue && node) { @@ -101,6 +105,7 @@ export const stTypes: CustomTypes = { stMap: createStMapCustomFunction().register('stMap'), 'st-array': createStArrayCustomFunction().register('st-array'), 'st-map': createStMapCustomFunction().register('st-map'), + 'st-string': createStMapCustomFunction().register('st-string'), } as const; export const deprecatedStFunctions: Record = { @@ -218,7 +223,7 @@ export function createCustomValue({ } else { const stringifiedValue = getStringValue([fallbackNode]); - throw new ValueError( + throw new CustomValueError( `/* Error trying to flat -> */${stringifiedValue}`, stringifiedValue ); @@ -252,7 +257,7 @@ export function getBoxValue( return value; } else if (value && customTypes[value.type]) { return customTypes[value.type].getValue(path, value, node, customTypes); - } else if (value.type === 'string') { + } else if (value.type === 'st-string') { return (value as Box<'string', string>).value; } else { throw new Error('Unknown Type ' + JSON.stringify(value)); diff --git a/packages/core/src/features/st-var.ts b/packages/core/src/features/st-var.ts index 4db4b8bc7..91cae8384 100644 --- a/packages/core/src/features/st-var.ts +++ b/packages/core/src/features/st-var.ts @@ -1,5 +1,5 @@ import { createFeature, FeatureContext, FeatureTransformContext } from './feature'; -import { unbox, Box, deprecatedStFunctions } from '../custom-values'; +import { unbox, Box, deprecatedStFunctions, boxString } from '../custom-values'; import { generalDiagnostics } from './diagnostics'; import * as STSymbol from './st-symbol'; import type { StylableMeta } from '../stylable-meta'; @@ -27,17 +27,19 @@ export interface VarSymbol { node: postcss.Node; } -export type Input = Box | Array>; +export type CustomValueInput = Box< + string, + CustomValueInput | Record | Array +>; export interface ComputedStVar { value: RuntimeStVar; diagnostics: Diagnostics; - input: Input; + input: CustomValueInput; } export interface FlatComputedStVar { value: string; - symbol: string; path: string[]; } @@ -179,18 +181,17 @@ export class StylablePublicApi { const flatStVars: FlatComputedStVar[] = []; for (const [symbol, stVar] of Object.entries(computed)) { - flatStVars.push(...this.flatSingle(stVar.input, symbol, [symbol])); + flatStVars.push(...this.flatSingle(stVar.input, [symbol])); } return flatStVars; } - private flatSingle(input: Input, sourcePath: string, path: string[]) { + private flatSingle(input: CustomValueInput, path: string[]) { const currentVars: FlatComputedStVar[] = []; if (input.flatValue) { currentVars.push({ - symbol: sourcePath, value: input.flatValue, path, }); @@ -200,10 +201,7 @@ export class StylablePublicApi { for (const [key, innerInput] of Object.entries(input.value)) { currentVars.push( ...this.flatSingle( - innerInput, - Array.isArray(input.value) - ? `${sourcePath}[${key}]` - : `${sourcePath}.${key}`, + typeof innerInput === 'string' ? boxString(innerInput) : innerInput, [...path, key] ) ); @@ -326,7 +324,7 @@ function evaluateValueCall( node: resolvedVarSymbol.node, meta: resolvedVar.meta, rootArgument: varName, - evaluatorNode: node, + initialNode: node, } ); // report errors diff --git a/packages/core/src/functions.ts b/packages/core/src/functions.ts index 0d56f8a58..fed554db3 100644 --- a/packages/core/src/functions.ts +++ b/packages/core/src/functions.ts @@ -17,7 +17,7 @@ import { getFormatterArgs, getStringValue, stringifyFunction } from './helpers/v import type { ParsedValue } from './types'; import type { FeatureTransformContext } from './features/feature'; import { CSSCustomProperty, STVar } from './features'; -import { unbox, ValueError } from './custom-values'; +import { unbox, CustomValueError } from './custom-values'; export type ValueFormatter = (name: string) => string; export type ResolvedFormatter = Record; @@ -32,7 +32,7 @@ export interface EvalValueData { cssVarsMapping?: Record; args?: string[]; rootArgument?: string; - evaluatorNode?: postcss.Node; + initialNode?: postcss.Node; } export interface EvalValueResult { @@ -64,7 +64,7 @@ export class StylableEvaluator { data.cssVarsMapping, data.args, data.rootArgument, - data.evaluatorNode + data.initialNode ); } } @@ -119,7 +119,7 @@ export function processDeclarationValue( cssVarsMapping: Record = {}, args: string[] = [], rootArgument?: string, - evaluatorNode?: postcss.Node + initialNode?: postcss.Node ): EvalValueResult { const evaluator = new StylableEvaluator({ tsVarOverride: variableOverride }); const resolvedSymbols = getResolvedSymbols(meta); @@ -146,7 +146,7 @@ export function processDeclarationValue( cssVarsMapping, args, rootArgument, - evaluatorNode, + initialNode, }, node: parsedNode, }); @@ -215,7 +215,7 @@ export function processDeclarationValue( cssVarsMapping, args, rootArgument, - evaluatorNode, + initialNode, }, node: parsedNode, }); @@ -250,7 +250,7 @@ export function processDeclarationValue( resolvedSymbols.customValues ); } catch (error) { - if (error instanceof ValueError) { + if (error instanceof CustomValueError) { outputValue += error.fallbackValue; } else { throw error; @@ -259,7 +259,7 @@ export function processDeclarationValue( } catch (e) { typeError = e as Error; - const invalidNode = evaluatorNode || node; + const invalidNode = initialNode || node; if (invalidNode) { diagnostics.warn( diff --git a/packages/core/src/stylable-transformer.ts b/packages/core/src/stylable-transformer.ts index dbe0913fb..32f974a65 100644 --- a/packages/core/src/stylable-transformer.ts +++ b/packages/core/src/stylable-transformer.ts @@ -204,7 +204,7 @@ export class StylableTransformer { node: atRule, valueHook: this.replaceValueHook, passedThrough: path.slice(), - evaluatorNode: atRule, + initialNode: atRule, }).outputValue; } else if (name === 'property') { CSSCustomProperty.hooks.transformAtRuleNode({ diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index db8cacc87..bef9b1606 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -1302,7 +1302,7 @@ describe(`features/st-var`, () => { value: 'red', input: { flatValue: 'red', - type: 'string', + type: 'st-string', value: 'red', }, diagnostics: { reports: [] }, @@ -1311,7 +1311,7 @@ describe(`features/st-var`, () => { value: 'blue', input: { flatValue: 'blue', - type: 'string', + type: 'st-string', value: 'blue', }, diagnostics: { reports: [] }, @@ -1323,12 +1323,12 @@ describe(`features/st-var`, () => { value: [ { flatValue: 'red', - type: 'string', + type: 'st-string', value: 'red', }, { flatValue: 'gold', - type: 'string', + type: 'st-string', value: 'gold', }, ], @@ -1364,7 +1364,7 @@ describe(`features/st-var`, () => { value: { b: { flatValue: 'red', - type: 'string', + type: 'st-string', value: 'red', }, }, @@ -1413,9 +1413,11 @@ describe(`features/st-var`, () => { :vars { array: st-array(blue, stBorder(1px, solid, blue)); map: st-map( - border stBorder(value(array, 1, size), - solid, - value(array, 0)) + border stBorder( + value(array, 1, size), + solid, + value(array, 0) + ) ); } `, @@ -1435,7 +1437,7 @@ describe(`features/st-var`, () => { value: [ { flatValue: 'blue', - type: 'string', + type: 'st-string', value: 'blue', }, { @@ -1499,7 +1501,7 @@ describe(`features/st-var`, () => { value: 'red', input: { flatValue: 'red', - type: 'string', + type: 'st-string', value: 'red', }, diagnostics: { reports: [] }, @@ -1508,7 +1510,7 @@ describe(`features/st-var`, () => { value: 'red', input: { flatValue: 'red', - type: 'string', + type: 'st-string', value: 'red', }, diagnostics: { reports: [] }, @@ -1545,7 +1547,7 @@ describe(`features/st-var`, () => { value: 'red', input: { flatValue: 'red', - type: 'string', + type: 'st-string', value: 'red', }, diagnostics: { reports: [] }, @@ -1554,7 +1556,7 @@ describe(`features/st-var`, () => { value: 'green', input: { flatValue: 'green', - type: 'string', + type: 'st-string', value: 'green', }, diagnostics: { reports: [] }, @@ -1563,7 +1565,7 @@ describe(`features/st-var`, () => { value: 'invalid-func(imported)', input: { flatValue: 'invalid-func(imported)', - type: 'string', + type: 'st-string', value: 'invalid-func(imported)', }, diagnostics: { @@ -1598,7 +1600,7 @@ describe(`features/st-var`, () => { value: '', input: { flatValue: '', - type: 'string', + type: 'st-string', value: '', }, diagnostics: { @@ -1627,12 +1629,10 @@ describe(`features/st-var`, () => { expect(flattenStVars).to.eql([ { - symbol: 'a', value: 'red', path: ['a'], }, { - symbol: 'b', value: 'blue', path: ['b'], }, @@ -1651,7 +1651,6 @@ describe(`features/st-var`, () => { expect(flattenStVars).to.eql([ { path: ['a'], - symbol: 'a', value: 'linear-gradient(to right, red, blue)', }, ]); @@ -1674,7 +1673,6 @@ describe(`features/st-var`, () => { expect(flattenStVars).to.eql([ { - symbol: 'myColor', value: 'red', path: ['myColor'], }, @@ -1696,12 +1694,10 @@ describe(`features/st-var`, () => { expect(flattenStVars).to.eql([ { - symbol: 'array[0]', value: '1px', path: ['array', '0'], }, { - symbol: 'array[1]', value: '2px', path: ['array', '1'], }, @@ -1723,12 +1719,10 @@ describe(`features/st-var`, () => { expect(flattenStVars).to.eql([ { - symbol: 'map.first', value: '1px', path: ['map', 'first'], }, { - symbol: 'map.second', value: '2px', path: ['map', 'second'], }, @@ -1752,10 +1746,21 @@ describe(`features/st-var`, () => { expect(flattenStVars).to.eql([ { - symbol: 'border', value: '1px solid red', path: ['border'], }, + { + value: '1px', + path: ['border', 'size'], + }, + { + value: 'solid', + path: ['border', 'style'], + }, + { + value: 'red', + path: ['border', 'color'], + }, ]); }); it('should flat nested st-array st vars', () => { @@ -1777,27 +1782,22 @@ describe(`features/st-var`, () => { expect(flattenStVars).to.eql([ { - symbol: 'nestedArray[0]', value: 'red', path: ['nestedArray', '0'], }, { - symbol: 'nestedArray[1][0]', value: 'red', path: ['nestedArray', '1', '0'], }, { - symbol: 'nestedArray[1][1]', value: 'green', path: ['nestedArray', '1', '1'], }, { - symbol: 'nestedArray[2].color1', value: 'gold', path: ['nestedArray', '2', 'color1'], }, { - symbol: 'nestedArray[2].color2', value: 'blue', path: ['nestedArray', '2', 'color2'], }, @@ -1822,27 +1822,22 @@ describe(`features/st-var`, () => { expect(flattenStVars).to.eql([ { - symbol: 'nestedMap.simple', value: 'red', path: ['nestedMap', 'simple'], }, { - symbol: 'nestedMap.array[0]', value: 'red', path: ['nestedMap', 'array', '0'], }, { - symbol: 'nestedMap.array[1]', value: 'green', path: ['nestedMap', 'array', '1'], }, { - symbol: 'nestedMap.map.color1', value: 'gold', path: ['nestedMap', 'map', 'color1'], }, { - symbol: 'nestedMap.map.color2', value: 'blue', path: ['nestedMap', 'map', 'color2'], }, From 8dd23ecc11cdf01843061cbe284b918477f690ca Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Tue, 29 Mar 2022 17:31:34 +0300 Subject: [PATCH 08/10] refactor(core): use unbox in getBoxValue --- packages/core/src/custom-values.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/core/src/custom-values.ts b/packages/core/src/custom-values.ts index 73fa2797e..1bac63279 100644 --- a/packages/core/src/custom-values.ts +++ b/packages/core/src/custom-values.ts @@ -253,12 +253,10 @@ export function getBoxValue( node: ParsedValue, customTypes: CustomTypes ): string { - if (typeof value === 'string') { - return value; + if (typeof value === 'string' || value.type === 'st-string') { + return unbox(value, true, customTypes); } else if (value && customTypes[value.type]) { return customTypes[value.type].getValue(path, value, node, customTypes); - } else if (value.type === 'st-string') { - return (value as Box<'string', string>).value; } else { throw new Error('Unknown Type ' + JSON.stringify(value)); // return JSON.stringify(value); From 2fbea0005d63ccd0b14de0e27739bf877a440d71 Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Wed, 30 Mar 2022 12:19:33 +0300 Subject: [PATCH 09/10] test(core): add skip test for reporting invalid input in custom values --- packages/core/test/features/st-var.spec.ts | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index bef9b1606..ae78120f3 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -673,6 +673,19 @@ describe(`features/st-var`, () => { } `); }); + it.skip('should report on invalid input', () => { + /** + * TODO: test invalid input in built-in custom values (st-map, st-array) + */ + testStylableCore(` + :vars { + /* @transform-error ${STVar.diagnostics.COULD_NOT_RESOLVE_VALUE( + `keyWithoutValue` + ) /** TODO - add custom diagnostic for this case */}*/ + keyWithoutValueMap: stMap(keyWithoutValue); + } + `); + }) it(`*** st-map and st-array contract test ***`, () => { const test = ({ label, @@ -1538,7 +1551,24 @@ describe(`features/st-var`, () => { ` ); +<<<<<<< Updated upstream const { meta } = sheets['/entry.st.css']; +======= + :vars { + array: st-array(blue, stBorder(1px, solid, blue)); + map: st-map( + border stBorder( + value(array, 1, size), + solid, + value(array, 0) + ) + ); + } + `, + // Stylable custom value + '/st-border.js': stBorderDefinitionMock, + }); +>>>>>>> Stashed changes const computedVars = stylable.stVar.getComputed(meta); From f6e3a12412c6aed1566b0ef9580fad355b693e9f Mon Sep 17 00:00:00 2001 From: Tzach Bonfil <45866571+tzachbon@users.noreply.github.com> Date: Wed, 30 Mar 2022 12:53:48 +0300 Subject: [PATCH 10/10] fix(core): remove merge conflict marker encountered --- packages/core/test/features/st-var.spec.ts | 27 +++++----------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/packages/core/test/features/st-var.spec.ts b/packages/core/test/features/st-var.spec.ts index ae78120f3..e595084c1 100644 --- a/packages/core/test/features/st-var.spec.ts +++ b/packages/core/test/features/st-var.spec.ts @@ -679,13 +679,15 @@ describe(`features/st-var`, () => { */ testStylableCore(` :vars { - /* @transform-error ${STVar.diagnostics.COULD_NOT_RESOLVE_VALUE( - `keyWithoutValue` - ) /** TODO - add custom diagnostic for this case */}*/ + /* @transform-error ${ + STVar.diagnostics.COULD_NOT_RESOLVE_VALUE( + `keyWithoutValue` + ) /** TODO - add custom diagnostic for this case */ + }*/ keyWithoutValueMap: stMap(keyWithoutValue); } `); - }) + }); it(`*** st-map and st-array contract test ***`, () => { const test = ({ label, @@ -1551,24 +1553,7 @@ describe(`features/st-var`, () => { ` ); -<<<<<<< Updated upstream const { meta } = sheets['/entry.st.css']; -======= - :vars { - array: st-array(blue, stBorder(1px, solid, blue)); - map: st-map( - border stBorder( - value(array, 1, size), - solid, - value(array, 0) - ) - ); - } - `, - // Stylable custom value - '/st-border.js': stBorderDefinitionMock, - }); ->>>>>>> Stashed changes const computedVars = stylable.stVar.getComputed(meta);