@stylable/core
Version:
CSS for Components
414 lines • 17.8 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveReferencedVarNames = exports.parseVarsFromExpr = exports.StylablePublicApi = exports.get = exports.hooks = exports.diagnostics = void 0;
const feature_1 = require("./feature");
const custom_values_1 = require("../custom-values");
const diagnostics_1 = require("./diagnostics");
const STSymbol = __importStar(require("./st-symbol"));
const stylable_resolver_1 = require("../stylable-resolver");
const functions_1 = require("../functions");
const rule_1 = require("../helpers/rule");
const selector_1 = require("../helpers/selector");
const value_1 = require("../helpers/value");
const string_1 = require("../helpers/string");
const process_declaration_functions_1 = require("../process-declaration-functions");
const diagnostics_2 = require("../diagnostics");
const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
exports.diagnostics = {
FORBIDDEN_DEF_IN_COMPLEX_SELECTOR: diagnostics_1.generalDiagnostics.FORBIDDEN_DEF_IN_COMPLEX_SELECTOR,
NO_VARS_DEF_IN_ST_SCOPE: (0, diagnostics_2.createDiagnosticReporter)('07002', 'error', () => `cannot define ":vars" inside of "@st-scope"`),
DEPRECATED_ST_FUNCTION_NAME: (0, diagnostics_2.createDiagnosticReporter)('07003', 'info', (name, alternativeName) => `"${name}" is deprecated, use "${alternativeName}"`),
CYCLIC_VALUE: (0, diagnostics_2.createDiagnosticReporter)('07004', 'error', (cyclicChain) => `Cyclic value definition detected: "${cyclicChain
.map((s, i) => (i === cyclicChain.length - 1 ? '↻ ' : i === 0 ? '→ ' : '↪ ') + s)
.join('\n')}"`),
MISSING_VAR_IN_VALUE: (0, diagnostics_2.createDiagnosticReporter)('07005', 'error', () => `invalid value() with no var identifier`),
COULD_NOT_RESOLVE_VALUE: (0, diagnostics_2.createDiagnosticReporter)('07006', 'error', (args) => `cannot resolve value function${args ? ` using the arguments provided: "${args}"` : ''}`),
MULTI_ARGS_IN_VALUE: (0, diagnostics_2.createDiagnosticReporter)('07007', 'error', (args) => `value function accepts only a single argument: "value(${args})"`),
CANNOT_USE_AS_VALUE: (0, diagnostics_2.createDiagnosticReporter)('07008', 'error', (type, varName) => `${type} "${varName}" cannot be used as a variable`),
CANNOT_USE_JS_AS_VALUE: (0, diagnostics_2.createDiagnosticReporter)('07009', 'error', (type, varName) => `JavaScript ${type} import "${varName}" cannot be used as a variable`),
UNKNOWN_VAR: (0, diagnostics_2.createDiagnosticReporter)('07010', 'error', (name) => `unknown var "${name}"`),
};
// HOOKS
exports.hooks = (0, feature_1.createFeature)({
analyzeSelectorNode({ context, node, rule }) {
if (node.type !== `pseudo_class` || node.value !== `vars`) {
return;
}
// make sure `:vars` is the only selector
if (rule.selector === `:vars`) {
if ((0, rule_1.isChildOfAtRule)(rule, `st-scope`)) {
context.diagnostics.report(exports.diagnostics.NO_VARS_DEF_IN_ST_SCOPE(), { node: rule });
}
else {
collectVarSymbols(context, rule);
}
// stop further walk into `:vars {}`
return selector_1.walkSelector.stopAll;
}
else {
context.diagnostics.report(exports.diagnostics.FORBIDDEN_DEF_IN_COMPLEX_SELECTOR(`:vars`), {
node: rule,
});
}
return;
},
prepareAST({ node, toRemove }) {
if (node.type === 'rule' && node.selector === ':vars') {
toRemove.push(node);
}
},
transformResolve({ context }) {
// Resolve local vars
const resolved = {};
const symbols = STSymbol.getAllByType(context.meta, `var`);
// Temporarily don't report issues here // ToDo: move reporting here (from value() transformation)
const noDaigContext = {
...context,
diagnostics: new diagnostics_2.Diagnostics(),
};
for (const name of Object.keys(symbols)) {
const symbol = symbols[name];
const evaluated = context.evaluator.evaluateValue(noDaigContext, {
// ToDo: change to `value(${name})` in order to fix overrides in exports
value: (0, string_1.stripQuotation)(symbol.text),
meta: context.meta,
node: symbol.node,
});
resolved[name] = evaluated;
}
return resolved;
},
transformValue({ context, node, data }) {
evaluateValueCall(context, node, data);
},
transformJSExports({ exports, resolved }) {
for (const [name, { topLevelType, outputValue }] of Object.entries(resolved)) {
exports.stVars[name] = topLevelType ? (0, custom_values_1.unbox)(topLevelType) : outputValue;
}
},
});
// API
function get(meta, name) {
return STSymbol.get(meta, name, `var`);
}
exports.get = get;
// Stylable StVar Public APIs
const UNKNOWN_LOCATION = Object.freeze({
offset: -1,
line: -1,
column: -1,
});
class StylablePublicApi {
constructor(stylable) {
this.stylable = stylable;
}
getComputed(meta) {
const topLevelDiagnostics = new diagnostics_2.Diagnostics();
const getResolvedSymbols = (0, stylable_resolver_1.createSymbolResolverWithCache)(this.stylable.resolver, topLevelDiagnostics);
const evaluator = new functions_1.StylableEvaluator({ getResolvedSymbols });
const { var: stVars } = getResolvedSymbols(meta);
const computed = {};
for (const [localName, resolvedVar] of Object.entries(stVars)) {
const diagnostics = new diagnostics_2.Diagnostics();
const { outputValue, topLevelType, runtimeValue } = evaluator.evaluateValue({
resolver: this.stylable.resolver,
evaluator,
meta,
diagnostics,
}, {
meta: resolvedVar.meta,
value: (0, string_1.stripQuotation)(resolvedVar.symbol.text),
node: resolvedVar.symbol.node,
});
const computedStVar = {
value: runtimeValue ?? outputValue,
input: topLevelType ?? (0, custom_values_1.unbox)(outputValue, false),
diagnostics,
source: {
meta: resolvedVar.meta,
start: resolvedVar.symbol.node.source?.start || UNKNOWN_LOCATION,
end: resolvedVar.symbol.node.source?.end || UNKNOWN_LOCATION,
},
};
computed[localName] = computedStVar;
}
return computed;
}
flatten(meta) {
const computed = this.getComputed(meta);
const flatStVars = [];
for (const [symbol, stVar] of Object.entries(computed)) {
flatStVars.push(...this.flatSingle(stVar.input, [symbol], stVar.source));
}
return flatStVars;
}
flatSingle(input, path, source) {
const currentVars = [];
if (input.flatValue) {
currentVars.push({
value: input.flatValue,
path,
source,
});
}
if (typeof input.value === `object` && input.value !== null) {
for (const [key, innerInput] of Object.entries(input.value)) {
currentVars.push(...this.flatSingle(typeof innerInput === 'string' ? (0, custom_values_1.boxString)(innerInput) : innerInput, [...path, key], source));
}
}
return currentVars;
}
}
exports.StylablePublicApi = StylablePublicApi;
function parseVarsFromExpr(expr) {
const nameSet = new Set();
(0, postcss_value_parser_1.default)(expr).walk((node) => {
if (node.type === 'function' && node.value === 'value') {
for (const argNode of node.nodes) {
switch (argNode.type) {
case 'word':
nameSet.add(argNode.value);
return;
case 'div':
if (argNode.value === ',') {
return;
}
}
}
}
});
return nameSet;
}
exports.parseVarsFromExpr = parseVarsFromExpr;
function collectVarSymbols(context, rule) {
rule.walkDecls((decl) => {
warnOnDeprecatedCustomValues(context, decl);
// check type annotation
let type = null;
const prev = decl.prev();
if (prev && prev.type === 'comment') {
const typeMatch = prev.text.match(/^@type (.+)$/);
if (typeMatch) {
type = typeMatch[1];
}
}
// add symbol
const name = decl.prop;
STSymbol.addSymbol({
context,
symbol: {
_kind: 'var',
name,
value: '',
text: decl.value,
node: decl,
valueType: type,
},
node: decl,
});
});
}
function warnOnDeprecatedCustomValues(context, decl) {
(0, process_declaration_functions_1.processDeclarationFunctions)(decl, (node) => {
if (node.type === 'nested-item' && custom_values_1.deprecatedStFunctions[node.name]) {
const { alternativeName } = custom_values_1.deprecatedStFunctions[node.name];
context.diagnostics.report(exports.diagnostics.DEPRECATED_ST_FUNCTION_NAME(node.name, alternativeName), {
node: decl,
word: node.name,
});
}
}, false);
}
function evaluateValueCall(context, parsedNode, data) {
const { stVarOverride, value, node } = data;
const passedThrough = context.passedThrough || [];
const parsedArgs = value_1.strategies.args(parsedNode).map((x) => x.value);
const varName = parsedArgs[0];
const restArgs = parsedArgs.slice(1);
// check var not empty
if (!varName) {
if (node) {
context.diagnostics.report(exports.diagnostics.MISSING_VAR_IN_VALUE(), {
node,
word: (0, value_1.getStringValue)(parsedNode),
});
}
}
else if (parsedArgs.length >= 1) {
// override with value
if (stVarOverride?.[varName]) {
parsedNode.resolvedValue = stVarOverride?.[varName];
return;
}
// check cyclic
const refUniqID = createUniqID(data.meta.source, varName);
if (passedThrough.includes(refUniqID)) {
// TODO: move diagnostic to original value usage instead of the end of the cyclic chain
handleCyclicValues(context, passedThrough, refUniqID, data.node, value, parsedNode);
return;
}
// resolve
const resolvedSymbols = context.getResolvedSymbols(data.meta);
const resolvedVar = resolvedSymbols.var[varName];
const resolvedVarSymbol = resolvedVar?.symbol;
const possibleNonSTVarSymbol = STSymbol.get(context.meta, varName);
if (resolvedVarSymbol) {
const { outputValue, topLevelType, typeError } = context.evaluator.evaluateValue({ ...context, passedThrough: passedThrough.concat(refUniqID) }, {
...data,
value: (0, string_1.stripQuotation)(resolvedVarSymbol.text),
args: restArgs,
node: resolvedVarSymbol.node,
meta: resolvedVar.meta,
rootArgument: varName,
initialNode: node,
});
// report errors
if (node) {
const argsAsString = parsedArgs.join(', ');
if (!typeError && !topLevelType && parsedArgs.length > 1) {
context.diagnostics.report(exports.diagnostics.MULTI_ARGS_IN_VALUE(argsAsString), {
node,
});
}
}
parsedNode.resolvedValue = context.evaluator.valueHook
? context.evaluator.valueHook(outputValue, varName, true, passedThrough)
: outputValue;
}
else if (possibleNonSTVarSymbol) {
const type = resolvedSymbols.mainNamespace[varName];
if (type === `js`) {
const deepResolve = resolvedSymbols.js[varName];
const jsValue = deepResolve.symbol;
if (typeof jsValue === 'string') {
parsedNode.resolvedValue = context.evaluator.valueHook
? context.evaluator.valueHook(jsValue, varName, false, passedThrough)
: jsValue;
}
else if (node) {
// unsupported Javascript value
// ToDo: provide actual exported id (default/named as x)
context.diagnostics.report(exports.diagnostics.CANNOT_USE_JS_AS_VALUE(typeof jsValue, varName), {
node,
word: varName,
});
}
}
else if (type) {
// report mismatch type
const deepResolve = resolvedSymbols[type][varName];
let finalResolve = {
_kind: `css`,
meta: data.meta,
symbol: possibleNonSTVarSymbol,
};
if (deepResolve instanceof Array) {
// take the deep resolved in order to
// print the actual mismatched type
finalResolve = deepResolve[deepResolve.length - 1];
}
else if (deepResolve._kind === `css`) {
finalResolve = deepResolve;
}
reportUnsupportedSymbolInValue(context, varName, finalResolve, node);
}
else if (node) {
// report unknown var
context.diagnostics.report(exports.diagnostics.UNKNOWN_VAR(varName), {
node,
word: varName,
});
}
}
else if (node) {
context.diagnostics.report(exports.diagnostics.UNKNOWN_VAR(varName), {
node,
word: varName,
});
}
}
}
function resolveReferencedVarNames(context, initialName) {
const refNames = new Set();
const varsToCheck = [
{ meta: context.meta, name: initialName },
];
const checked = new Set();
while (varsToCheck.length) {
const { meta, name } = varsToCheck.shift();
const contextualId = meta.source + '/' + name;
if (!checked.has(contextualId)) {
checked.add(contextualId);
refNames.add(name);
const symbol = STSymbol.get(meta, name);
switch (symbol?._kind) {
case 'var':
parseVarsFromExpr(symbol.text).forEach((refName) => varsToCheck.push({
meta,
name: refName,
}));
break;
case 'import': {
const resolved = context.resolver.deepResolve(symbol);
if (resolved?._kind === 'css' && resolved.symbol?._kind === 'var') {
varsToCheck.push({ meta: resolved.meta, name: resolved.symbol.name });
}
break;
}
}
}
}
return refNames;
}
exports.resolveReferencedVarNames = resolveReferencedVarNames;
function reportUnsupportedSymbolInValue(context, name, resolve, node) {
const symbol = resolve.symbol;
const errorKind = symbol._kind === 'class' && symbol[`
-st-root`] ? 'stylesheet' : symbol._kind;
if (node) {
context.diagnostics.report(exports.diagnostics.CANNOT_USE_AS_VALUE(errorKind, name), {
node,
word: name,
});
}
}
function handleCyclicValues(context, passedThrough, refUniqID, node, value, parsedNode) {
if (node) {
const cyclicChain = passedThrough.map((variable) => variable || '');
cyclicChain.push(refUniqID);
context.diagnostics.report(exports.diagnostics.CYCLIC_VALUE(cyclicChain), {
node,
word: refUniqID, // ToDo: check word is path+var and not var name
});
}
return (0, value_1.stringifyFunction)(value, parsedNode);
}
function createUniqID(source, varName) {
return `${source}: ${varName}`;
}
//# sourceMappingURL=st-var.js.map