UNPKG

@stylable/core

Version:

CSS for Components

241 lines 14.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.evalDeclarationValue = exports.processDeclarationValue = exports.resolveArgumentsValue = exports.functionWarnings = void 0; const path_1 = require("path"); const postcss_value_parser_1 = __importDefault(require("postcss-value-parser")); const custom_values_1 = require("./custom-values"); const native_reserved_lists_1 = require("./native-reserved-lists"); const stylable_assets_1 = require("./stylable-assets"); const stylable_utils_1 = require("./stylable-utils"); const stylable_value_parsers_1 = require("./stylable-value-parsers"); const utils_1 = require("./utils"); exports.functionWarnings = { FAIL_TO_EXECUTE_FORMATTER: (resolvedValue, message) => `failed to execute formatter "${resolvedValue}" with error: "${message}"`, CYCLIC_VALUE: (cyclicChain) => `Cyclic value definition detected: "${cyclicChain .map((s, i) => (i === cyclicChain.length - 1 ? '↻ ' : i === 0 ? '→ ' : '↪ ') + s) .join('\n')}"`, CANNOT_USE_AS_VALUE: (type, varName) => `${type} "${varName}" cannot be used as a variable`, CANNOT_USE_JS_AS_VALUE: (varName) => `JavaScript import "${varName}" cannot be used as a variable`, CANNOT_FIND_IMPORTED_VAR: (varName) => `cannot use unknown imported "${varName}"`, MULTI_ARGS_IN_VALUE: (args) => `value function accepts only a single argument: "value(${args})"`, COULD_NOT_RESOLVE_VALUE: (args) => `cannot resolve value function using the arguments provided: "${args}"`, UNKNOWN_FORMATTER: (name) => `cannot find native function or custom formatter called ${name}`, UNKNOWN_VAR: (name) => `unknown var "${name}"`, }; function resolveArgumentsValue(options, transformer, meta, diagnostics, node, variableOverride, path, cssVarsMapping) { const resolvedArgs = {}; for (const k in options) { resolvedArgs[k] = evalDeclarationValue(transformer.resolver, options[k], meta, node, variableOverride, transformer.replaceValueHook, diagnostics, path, cssVarsMapping, undefined); } return resolvedArgs; } exports.resolveArgumentsValue = resolveArgumentsValue; function processDeclarationValue(resolver, value, meta, node, variableOverride, valueHook, diagnostics, passedThrough = [], cssVarsMapping, args = []) { diagnostics = node ? diagnostics : undefined; const customValues = custom_values_1.resolveCustomValues(meta, resolver); const parsedValue = postcss_value_parser_1.default(value); parsedValue.walk((parsedNode) => { const { type, value } = parsedNode; switch (type) { case 'function': if (value === 'value') { const parsedArgs = stylable_value_parsers_1.strategies.args(parsedNode).map((x) => x.value); if (parsedArgs.length >= 1) { const varName = parsedArgs[0]; const getArgs = parsedArgs .slice(1) .map((arg) => evalDeclarationValue(resolver, arg, meta, node, variableOverride, valueHook, diagnostics, passedThrough.concat(createUniqID(meta.source, varName)), cssVarsMapping, undefined)); if (variableOverride && variableOverride[varName]) { return (parsedNode.resolvedValue = variableOverride[varName]); } const refUniqID = createUniqID(meta.source, varName); if (passedThrough.includes(refUniqID)) { // TODO: move diagnostic to original value usage instead of the end of the cyclic chain return handleCyclicValues(passedThrough, refUniqID, diagnostics, node, value, parsedNode); } const varSymbol = meta.mappedSymbols[varName]; if (varSymbol && varSymbol._kind === 'var') { const resolved = processDeclarationValue(resolver, utils_1.stripQuotation(varSymbol.text), meta, varSymbol.node, variableOverride, valueHook, diagnostics, passedThrough.concat(createUniqID(meta.source, varName)), cssVarsMapping, getArgs); const { outputValue, topLevelType, typeError } = resolved; if (diagnostics && node) { const argsAsString = parsedArgs.join(', '); if (typeError) { diagnostics.warn(node, exports.functionWarnings.COULD_NOT_RESOLVE_VALUE(argsAsString)); } else if (!topLevelType && parsedArgs.length > 1) { diagnostics.warn(node, exports.functionWarnings.MULTI_ARGS_IN_VALUE(argsAsString)); } } parsedNode.resolvedValue = valueHook ? valueHook(outputValue, varName, true, passedThrough) : outputValue; } else if (varSymbol && varSymbol._kind === 'import') { const resolvedVar = resolver.deepResolve(varSymbol); if (resolvedVar && resolvedVar.symbol) { const resolvedVarSymbol = resolvedVar.symbol; if (resolvedVar._kind === 'css') { if (resolvedVarSymbol._kind === 'var') { const resolvedValue = evalDeclarationValue(resolver, utils_1.stripQuotation(resolvedVarSymbol.text), resolvedVar.meta, resolvedVarSymbol.node, variableOverride, valueHook, diagnostics, passedThrough.concat(createUniqID(meta.source, varName)), cssVarsMapping, getArgs); parsedNode.resolvedValue = valueHook ? valueHook(resolvedValue, varName, false, passedThrough) : resolvedValue; } else { const errorKind = resolvedVarSymbol._kind === 'class' && resolvedVarSymbol[stylable_value_parsers_1.valueMapping.root] ? 'stylesheet' : resolvedVarSymbol._kind; if (diagnostics && node) { diagnostics.warn(node, exports.functionWarnings.CANNOT_USE_AS_VALUE(errorKind, varName), { word: varName }); } } } else if (resolvedVar._kind === 'js' && diagnostics && node) { // ToDo: provide actual exported id (default/named as x) diagnostics.warn(node, exports.functionWarnings.CANNOT_USE_JS_AS_VALUE(varName), { word: varName, }); } } else { const namedDecl = varSymbol.import.rule.nodes.find((node) => { return node.type === 'decl' && node.prop === stylable_value_parsers_1.valueMapping.named; }); if (namedDecl && diagnostics && node) { // ToDo: provide actual exported id (default/named as x) diagnostics.error(node, exports.functionWarnings.CANNOT_FIND_IMPORTED_VAR(varName), { word: varName }); } } } else if (diagnostics && node) { diagnostics.warn(node, exports.functionWarnings.UNKNOWN_VAR(varName), { word: varName, }); } } } else if (value === '') { parsedNode.resolvedValue = stringifyFunction(value, parsedNode); } else { if (customValues[value]) { // no op resolved at the bottom } else if (value === 'url') { // postcss-value-parser treats url differently: // https://github.com/TrySound/postcss-value-parser/issues/34 const url = parsedNode.nodes[0]; if ((url.type === 'word' || url.type === 'string') && url.value.startsWith('~')) { const sourceDir = path_1.dirname(meta.source); url.value = stylable_assets_1.assureRelativeUrlPrefix(path_1.relative(sourceDir, resolver.resolvePath(url.value.slice(1), sourceDir)).replace(/\\/gm, '/')); } } else if (value === 'format') { // preserve native format function quotation parsedNode.resolvedValue = stringifyFunction(value, parsedNode, true); } else { const formatterRef = meta.mappedSymbols[value]; const formatter = resolver.deepResolve(formatterRef); const formatterArgs = stylable_value_parsers_1.getFormatterArgs(parsedNode); if (formatter && formatter._kind === 'js') { try { parsedNode.resolvedValue = formatter.symbol.apply(null, formatterArgs); if (valueHook && typeof parsedNode.resolvedValue === 'string') { parsedNode.resolvedValue = valueHook(parsedNode.resolvedValue, { name: parsedNode.value, args: formatterArgs }, true, passedThrough); } } catch (error) { parsedNode.resolvedValue = stringifyFunction(value, parsedNode); if (diagnostics && node) { diagnostics.warn(node, exports.functionWarnings.FAIL_TO_EXECUTE_FORMATTER(parsedNode.resolvedValue, error.message), { word: node.value }); } } } else if (value === 'var') { const varWithPrefix = parsedNode.nodes[0].value; if (stylable_utils_1.isCSSVarProp(varWithPrefix)) { if (cssVarsMapping && cssVarsMapping[varWithPrefix]) { parsedNode.nodes[0].value = cssVarsMapping[varWithPrefix]; } } // handle default values if (parsedNode.nodes.length > 2) { parsedNode.resolvedValue = stringifyFunction(value, parsedNode); } } else if (native_reserved_lists_1.isCssNativeFunction(value)) { parsedNode.resolvedValue = stringifyFunction(value, parsedNode); } else if (diagnostics && node) { parsedNode.resolvedValue = stringifyFunction(value, parsedNode); diagnostics.warn(node, exports.functionWarnings.UNKNOWN_FORMATTER(value), { word: value, }); } } } break; default: { return postcss_value_parser_1.default.stringify(parsedNode); } } return; }, true); let outputValue = ''; let topLevelType = null; let typeError = null; for (const n of parsedValue.nodes) { if (n.type === 'function') { const matchingType = customValues[n.value]; if (matchingType) { topLevelType = matchingType.evalVarAst(n, customValues); try { outputValue += matchingType.getValue(args, topLevelType, n, customValues); } catch (e) { typeError = e; // catch broken variable resolutions } } else { outputValue += stylable_value_parsers_1.getStringValue([n]); } } else { outputValue += stylable_value_parsers_1.getStringValue([n]); } } return { outputValue, topLevelType, typeError }; // } // TODO: handle calc (parse internals but maintain expression) // TODO: check this thing. native function that accent our function dose not work // e.g: calc(getVarName()) } exports.processDeclarationValue = processDeclarationValue; function evalDeclarationValue(resolver, value, meta, node, variableOverride, valueHook, diagnostics, passedThrough = [], cssVarsMapping, args = []) { return processDeclarationValue(resolver, value, meta, node, variableOverride, valueHook, diagnostics, passedThrough, cssVarsMapping, args).outputValue; } exports.evalDeclarationValue = evalDeclarationValue; function handleCyclicValues(passedThrough, refUniqID, diagnostics, node, value, parsedNode) { const cyclicChain = passedThrough.map((variable) => variable || ''); cyclicChain.push(refUniqID); if (diagnostics && node) { diagnostics.warn(node, exports.functionWarnings.CYCLIC_VALUE(cyclicChain), { word: refUniqID, }); } return stringifyFunction(value, parsedNode); } function stringifyFunction(name, parsedNode, perserveQuotes = false) { return `${name}(${stylable_value_parsers_1.getFormatterArgs(parsedNode, false, undefined, perserveQuotes).join(', ')})`; } function createUniqID(source, varName) { return `${source}: ${varName}`; } //# sourceMappingURL=functions.js.map