UNPKG

13.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.evalDeclarationValue = exports.processDeclarationValue = exports.resolveArgumentsValue = exports.functionWarnings = void 0;
4const custom_values_1 = require("./custom-values");
5const native_reserved_lists_1 = require("./native-reserved-lists");
6const stylable_utils_1 = require("./stylable-utils");
7const stylable_value_parsers_1 = require("./stylable-value-parsers");
8const utils_1 = require("./utils");
9const postcssValueParser = require('postcss-value-parser');
10exports.functionWarnings = {
11 FAIL_TO_EXECUTE_FORMATTER: (resolvedValue, message) => `failed to execute formatter "${resolvedValue}" with error: "${message}"`,
12 CYCLIC_VALUE: (cyclicChain) => `Cyclic value definition detected: "${cyclicChain
13 .map((s, i) => (i === cyclicChain.length - 1 ? '↻ ' : i === 0 ? '→ ' : '↪ ') + s)
14 .join('\n')}"`,
15 CANNOT_USE_AS_VALUE: (type, varName) => `${type} "${varName}" cannot be used as a variable`,
16 CANNOT_USE_JS_AS_VALUE: (varName) => `JavaScript import "${varName}" cannot be used as a variable`,
17 CANNOT_FIND_IMPORTED_VAR: (varName) => `cannot use unknown imported "${varName}"`,
18 MULTI_ARGS_IN_VALUE: (args) => `value function accepts only a single argument: "value(${args})"`,
19 COULD_NOT_RESOLVE_VALUE: (args) => `cannot resolve value function using the arguments provided: "${args}"`,
20 UNKNOWN_FORMATTER: (name) => `cannot find native function or custom formatter called ${name}`,
21 UNKNOWN_VAR: (name) => `unknown var "${name}"`,
22};
23function resolveArgumentsValue(options, transformer, meta, diagnostics, node, variableOverride, path, cssVarsMapping) {
24 const resolvedArgs = {};
25 for (const k in options) {
26 resolvedArgs[k] = evalDeclarationValue(transformer.resolver, options[k], meta, node, variableOverride, transformer.replaceValueHook, diagnostics, path, cssVarsMapping);
27 }
28 return resolvedArgs;
29}
30exports.resolveArgumentsValue = resolveArgumentsValue;
31function processDeclarationValue(resolver, value, meta, node, variableOverride, valueHook, diagnostics, passedThrough = [], cssVarsMapping, args = []) {
32 diagnostics = node ? diagnostics : undefined;
33 const customValues = custom_values_1.resolveCustomValues(meta, resolver);
34 const parsedValue = postcssValueParser(value);
35 parsedValue.walk((parsedNode) => {
36 const { type, value } = parsedNode;
37 switch (type) {
38 case 'function':
39 if (value === 'value') {
40 const parsedArgs = stylable_value_parsers_1.strategies.args(parsedNode).map((x) => x.value);
41 if (parsedArgs.length >= 1) {
42 const varName = parsedArgs[0];
43 const getArgs = parsedArgs
44 .slice(1)
45 .map((arg) => evalDeclarationValue(resolver, arg, meta, node, variableOverride, valueHook, diagnostics, passedThrough.concat(createUniqID(meta.source, varName)), cssVarsMapping));
46 if (variableOverride && variableOverride[varName]) {
47 return (parsedNode.resolvedValue = variableOverride[varName]);
48 }
49 const refUniqID = createUniqID(meta.source, varName);
50 if (passedThrough.includes(refUniqID)) {
51 // TODO: move diagnostic to original value usage instead of the end of the cyclic chain
52 return handleCyclicValues(passedThrough, refUniqID, diagnostics, node, value, parsedNode);
53 }
54 const varSymbol = meta.mappedSymbols[varName];
55 if (varSymbol && varSymbol._kind === 'var') {
56 const resolved = processDeclarationValue(resolver, utils_1.stripQuotation(varSymbol.text), meta, varSymbol.node, variableOverride, valueHook, diagnostics, passedThrough.concat(createUniqID(meta.source, varName)), cssVarsMapping, getArgs);
57 const { outputValue, topLevelType, typeError } = resolved;
58 if (diagnostics && node) {
59 const argsAsString = parsedArgs.join(', ');
60 if (typeError) {
61 diagnostics.warn(node, exports.functionWarnings.COULD_NOT_RESOLVE_VALUE(argsAsString));
62 }
63 else if (!topLevelType && parsedArgs.length > 1) {
64 diagnostics.warn(node, exports.functionWarnings.MULTI_ARGS_IN_VALUE(argsAsString));
65 }
66 }
67 parsedNode.resolvedValue = valueHook
68 ? valueHook(outputValue, varName, true, passedThrough)
69 : outputValue;
70 }
71 else if (varSymbol && varSymbol._kind === 'import') {
72 const resolvedVar = resolver.deepResolve(varSymbol);
73 if (resolvedVar && resolvedVar.symbol) {
74 const resolvedVarSymbol = resolvedVar.symbol;
75 if (resolvedVar._kind === 'css') {
76 if (resolvedVarSymbol._kind === 'var') {
77 const resolvedValue = evalDeclarationValue(resolver, utils_1.stripQuotation(resolvedVarSymbol.text), resolvedVar.meta, resolvedVarSymbol.node, variableOverride, valueHook, diagnostics, passedThrough.concat(createUniqID(meta.source, varName)), cssVarsMapping, getArgs);
78 parsedNode.resolvedValue = valueHook
79 ? valueHook(resolvedValue, varName, false, passedThrough)
80 : resolvedValue;
81 }
82 else {
83 const errorKind = resolvedVarSymbol._kind === 'class' &&
84 resolvedVarSymbol[stylable_value_parsers_1.valueMapping.root]
85 ? 'stylesheet'
86 : resolvedVarSymbol._kind;
87 if (diagnostics && node) {
88 diagnostics.warn(node, exports.functionWarnings.CANNOT_USE_AS_VALUE(errorKind, varName), { word: varName });
89 }
90 }
91 }
92 else if (resolvedVar._kind === 'js' && diagnostics && node) {
93 // ToDo: provide actual exported id (default/named as x)
94 diagnostics.warn(node, exports.functionWarnings.CANNOT_USE_JS_AS_VALUE(varName), {
95 word: varName,
96 });
97 }
98 }
99 else {
100 const namedDecl = varSymbol.import.rule.nodes.find((node) => {
101 return node.type === 'decl' && node.prop === stylable_value_parsers_1.valueMapping.named;
102 });
103 if (namedDecl && diagnostics && node) {
104 // ToDo: provide actual exported id (default/named as x)
105 diagnostics.error(node, exports.functionWarnings.CANNOT_FIND_IMPORTED_VAR(varName), { word: varName });
106 }
107 }
108 }
109 else if (diagnostics && node) {
110 diagnostics.warn(node, exports.functionWarnings.UNKNOWN_VAR(varName), {
111 word: varName,
112 });
113 }
114 }
115 }
116 else if (value === '') {
117 parsedNode.resolvedValue = stringifyFunction(value, parsedNode);
118 }
119 else {
120 if (customValues[value]) {
121 // no op resolved at the bottom
122 }
123 else if (value === 'url') {
124 // postcss-value-parser treats url differently:
125 // https://github.com/TrySound/postcss-value-parser/issues/34
126 }
127 else if (value === 'format') {
128 // perserve native format function quotation
129 parsedNode.resolvedValue = stringifyFunction(value, parsedNode, true);
130 }
131 else {
132 const formatterRef = meta.mappedSymbols[value];
133 const formatter = resolver.deepResolve(formatterRef);
134 const formatterArgs = stylable_value_parsers_1.getFormatterArgs(parsedNode);
135 if (formatter && formatter._kind === 'js') {
136 try {
137 parsedNode.resolvedValue = formatter.symbol.apply(null, formatterArgs);
138 if (valueHook && typeof parsedNode.resolvedValue === 'string') {
139 parsedNode.resolvedValue = valueHook(parsedNode.resolvedValue, { name: parsedNode.value, args: formatterArgs }, true, passedThrough);
140 }
141 }
142 catch (error) {
143 parsedNode.resolvedValue = stringifyFunction(value, parsedNode);
144 if (diagnostics && node) {
145 diagnostics.warn(node, exports.functionWarnings.FAIL_TO_EXECUTE_FORMATTER(parsedNode.resolvedValue, error.message), { word: node.value });
146 }
147 }
148 }
149 else if (value === 'var') {
150 const varWithPrefix = parsedNode.nodes[0].value;
151 if (stylable_utils_1.isCSSVarProp(varWithPrefix)) {
152 if (cssVarsMapping && cssVarsMapping[varWithPrefix]) {
153 parsedNode.nodes[0].value = cssVarsMapping[varWithPrefix];
154 }
155 }
156 // handle default values
157 if (parsedNode.nodes.length > 2) {
158 parsedNode.resolvedValue = stringifyFunction(value, parsedNode);
159 }
160 }
161 else if (native_reserved_lists_1.isCssNativeFunction(value)) {
162 parsedNode.resolvedValue = stringifyFunction(value, parsedNode);
163 }
164 else if (diagnostics && node) {
165 parsedNode.resolvedValue = stringifyFunction(value, parsedNode);
166 diagnostics.warn(node, exports.functionWarnings.UNKNOWN_FORMATTER(value), {
167 word: value,
168 });
169 }
170 }
171 }
172 break;
173 default: {
174 return postcssValueParser.stringify(parsedNode);
175 }
176 }
177 }, true);
178 let outputValue = '';
179 let topLevelType = null;
180 let typeError = null;
181 for (const n of parsedValue.nodes) {
182 if (n.type === 'function') {
183 const matchingType = customValues[n.value];
184 if (matchingType) {
185 topLevelType = matchingType.evalVarAst(n, customValues);
186 try {
187 outputValue += matchingType.getValue(args, topLevelType, n, customValues);
188 }
189 catch (e) {
190 typeError = e;
191 // catch broken variable resolutions
192 }
193 }
194 else {
195 outputValue += stylable_value_parsers_1.getStringValue([n]);
196 }
197 }
198 else {
199 outputValue += stylable_value_parsers_1.getStringValue([n]);
200 }
201 }
202 return { outputValue, topLevelType, typeError };
203 // }
204 // TODO: handle calc (parse internals but maintain expression)
205 // TODO: check this thing. native function that accent our function dose not work
206 // e.g: calc(getVarName())
207}
208exports.processDeclarationValue = processDeclarationValue;
209function evalDeclarationValue(resolver, value, meta, node, variableOverride, valueHook, diagnostics, passedThrough = [], cssVarsMapping, args = []) {
210 return processDeclarationValue(resolver, value, meta, node, variableOverride, valueHook, diagnostics, passedThrough, cssVarsMapping, args).outputValue;
211}
212exports.evalDeclarationValue = evalDeclarationValue;
213function handleCyclicValues(passedThrough, refUniqID, diagnostics, node, value, parsedNode) {
214 const cyclicChain = passedThrough.map((variable) => variable || '');
215 cyclicChain.push(refUniqID);
216 if (diagnostics && node) {
217 diagnostics.warn(node, exports.functionWarnings.CYCLIC_VALUE(cyclicChain), {
218 word: refUniqID,
219 });
220 }
221 return stringifyFunction(value, parsedNode);
222}
223function stringifyFunction(name, parsedNode, perserveQuotes = false) {
224 return `${name}(${stylable_value_parsers_1.getFormatterArgs(parsedNode, false, undefined, perserveQuotes).join(', ')})`;
225}
226function createUniqID(source, varName) {
227 return `${source}: ${varName}`;
228}
229//# sourceMappingURL=functions.js.map
\No newline at end of file