@stylable/core
Version:
CSS for Components
690 lines • 33.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveStateParam = exports.createStateWithParamClassName = exports.createBooleanStateClassName = exports.isTemplateState = exports.transformPseudoClassToCustomState = exports.validateStateArgument = exports.validateRuleStateDefinition = exports.systemValidators = exports.validationErrors = exports.parseStateValue = exports.parsePseudoStates = exports.stateDiagnostics = exports.stateWithParamDelimiter = exports.booleanStateDelimiter = exports.stateMiddleDelimiter = void 0;
const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
const cssesc_1 = __importDefault(require("cssesc"));
const diagnostics_1 = require("../diagnostics");
const selector_1 = require("./selector");
const value_1 = require("./value");
const string_1 = require("./string");
const functions_1 = require("../functions");
const features_1 = require("../features");
const native_reserved_lists_1 = require("../native-reserved-lists");
const css_value_parser_1 = require("@tokey/css-value-parser");
const css_value_seeker_1 = require("./css-value-seeker");
exports.stateMiddleDelimiter = '-';
exports.booleanStateDelimiter = '--';
exports.stateWithParamDelimiter = exports.booleanStateDelimiter + exports.stateMiddleDelimiter;
exports.stateDiagnostics = {
MISSING_TYPE_OR_TEMPLATE: (0, diagnostics_1.createDiagnosticReporter)('08000', 'error', (name) => `pseudo-state "${name}" missing type or template`),
UNKNOWN_STATE_TYPE: (0, diagnostics_1.createDiagnosticReporter)('08002', 'error', (name, type) => `pseudo-state "${name}" defined with unknown type: "${type}"`),
TOO_MANY_STATE_TYPES: (0, diagnostics_1.createDiagnosticReporter)('08003', 'error', (name, types) => `pseudo-state "${name}(${types.join(', ')})" definition must be of a single type`),
NO_STATE_TYPE_GIVEN: (0, diagnostics_1.createDiagnosticReporter)('08005', 'warning', (name) => `pseudo-state "${name}" expected a definition of a single type, but received none`),
TOO_MANY_ARGS_IN_VALIDATOR: (0, diagnostics_1.createDiagnosticReporter)('08006', 'error', (name, validator, args) => `pseudo-state "${name}" expected "${validator}" validator to receive a single argument, but it received "${args.join(', ')}"`),
STATE_STARTS_WITH_HYPHEN: (0, diagnostics_1.createDiagnosticReporter)('08007', 'error', (name) => `state "${name}" declaration cannot begin with a "${exports.stateMiddleDelimiter}" character`),
RESERVED_NATIVE_STATE: (0, diagnostics_1.createDiagnosticReporter)('08008', 'warning', (name) => `state "${name}" is reserved for native pseudo-class`),
DEFAULT_PARAM_FAILS_VALIDATION: (0, diagnostics_1.createDiagnosticReporter)('08010', 'error', (stateName, defaultValue, errors) => `pseudo-state "${stateName}" default value "${defaultValue}" failed validation:\n${errors.join('\n')}`),
NO_STATE_ARGUMENT_GIVEN: (0, diagnostics_1.createDiagnosticReporter)('08004', 'error', (name, type) => `pseudo-state "${name}" expected argument of type "${type}" but got none`),
FAILED_STATE_VALIDATION: (0, diagnostics_1.createDiagnosticReporter)('08009', 'error', (name, actualParam, errors) => [
`pseudo-state "${name}" with parameter "${actualParam}" failed validation:`,
...errors,
].join('\n')),
TEMPLATE_MISSING_PLACEHOLDER: (0, diagnostics_1.createDiagnosticReporter)('08011', 'warning', (state, template) => `pseudo-state "${state}" template "${template}" is missing a placeholder, use "$0" to set the parameter insertion place`),
TEMPLATE_MULTI_PARAMETERS: (0, diagnostics_1.createDiagnosticReporter)('08012', 'error', (state) => `pseudo-state "${state}" template only supports a single parameter`),
TEMPLATE_MISSING_PARAMETER: (0, diagnostics_1.createDiagnosticReporter)('08013', 'error', (state) => `pseudo-state "${state}" template expected a parameter definition`),
UNSUPPORTED_MULTI_SELECTOR: (0, diagnostics_1.createDiagnosticReporter)('08014', 'error', (state, finalSelector) => `pseudo-state "${state}" resulted in an unsupported multi selector "${finalSelector}"`),
UNSUPPORTED_COMPLEX_SELECTOR: (0, diagnostics_1.createDiagnosticReporter)('08015', 'error', (state, finalSelector) => `pseudo-state "${state}" resulted in an unsupported complex selector "${finalSelector}"`),
INVALID_SELECTOR: (0, diagnostics_1.createDiagnosticReporter)('08016', 'error', (state, finalSelector) => `pseudo-state "${state}" resulted in an invalid selector "${finalSelector}"`),
UNSUPPORTED_INITIAL_SELECTOR: (0, diagnostics_1.createDiagnosticReporter)('08017', 'error', (state, finalSelector) => `pseudo-state "${state}" result cannot start with a type or universal selector "${finalSelector}"`),
NO_PARAM_REQUIRED: (0, diagnostics_1.createDiagnosticReporter)('08018', 'error', (name, param) => `pseudo-state "${name}" accepts no parameter, but received "${param}"`),
};
// parse
function parsePseudoStates(value, decl, diagnostics) {
const mappedStates = {};
const ast = (0, postcss_value_parser_1.default)(value);
const statesSplitByComma = (0, value_1.groupValues)(ast.nodes);
statesSplitByComma.forEach((workingState) => {
const [stateDefinition, ...stateDefault] = workingState;
const stateName = stateDefinition.value;
if (!validateStateName(stateName, diagnostics, decl)) {
return;
}
if (stateDefinition.type === 'function') {
resolveStateType(stateDefinition, mappedStates, stateDefault, diagnostics, decl);
}
else if (stateDefinition.type === 'word') {
resolveBooleanState(mappedStates, stateDefinition);
}
else {
// TODO: Invalid state, edge case needs warning
}
});
return mappedStates;
}
exports.parsePseudoStates = parsePseudoStates;
function validateStateName(name, diagnostics, node) {
if (name.startsWith('-')) {
diagnostics.report(exports.stateDiagnostics.STATE_STARTS_WITH_HYPHEN(name), {
node: node,
word: name,
});
}
else if (native_reserved_lists_1.reservedFunctionalPseudoClasses.includes(name)) {
diagnostics.report(exports.stateDiagnostics.RESERVED_NATIVE_STATE(name), {
node: node,
word: name,
});
return false;
}
return true;
}
function parseStateValue(value, node, diagnostics) {
let stateName = '';
let stateDef = null; /*boolean*/
let amountTaken = 0;
const customIdentResult = (0, css_value_seeker_1.findCustomIdent)(value, 0);
const [amountToName, nameNode] = customIdentResult[0]
? customIdentResult
: (0, css_value_seeker_1.findNextCallNode)(value, 0);
if (nameNode && validateStateName(nameNode.value, diagnostics, node)) {
amountTaken += amountToName;
stateName = nameNode.value;
// state with parameter
if (nameNode.type === 'call') {
// take all of the definition since default value takes the rest
amountTaken = value.length;
// ToDo: translate resolveStateType to tokey and remove the double parsing
const postcssStateValue = (0, postcss_value_parser_1.default)((0, css_value_parser_1.stringifyCSSValue)(value.slice(amountToName - 1)));
// get state definition
const [stateDefinition, ...stateDefault] = postcssStateValue.nodes;
const stateMap = {};
resolveStateType(stateDefinition, stateMap, stateDefault, diagnostics, node // ToDo: change to accept any postcss node
);
if (stateMap[stateName]) {
stateDef = stateMap[stateName];
}
}
}
if (stateName) {
return [amountTaken, stateDef];
}
return [0, undefined];
}
exports.parseStateValue = parseStateValue;
function resolveBooleanState(mappedStates, stateDefinition) {
const currentState = mappedStates[stateDefinition.value];
if (!currentState) {
mappedStates[stateDefinition.value] = null; // add boolean state
}
else {
// TODO: warn with such name already exists
}
}
function resolveStateType(stateDefinition, mappedStates, stateDefault, diagnostics, decl) {
const stateName = stateDefinition.value;
if (stateDefinition.nodes.length === 0) {
resolveBooleanState(mappedStates, stateDefinition);
diagnostics.report(exports.stateDiagnostics.NO_STATE_TYPE_GIVEN(stateName), {
node: decl,
word: decl.value,
});
return;
}
const { paramType, argsFirstNode, argsFullValue } = collectStateArgsDef(stateDefinition.nodes);
if (!paramType) {
diagnostics.report(exports.stateDiagnostics.MISSING_TYPE_OR_TEMPLATE(stateName), {
node: decl,
});
return;
}
if (paramType?.type === 'string') {
defineTemplateState(stateName, paramType, argsFirstNode, argsFullValue, mappedStates, diagnostics, decl);
}
else {
if (argsFullValue.length > 1) {
diagnostics.report(exports.stateDiagnostics.TOO_MANY_STATE_TYPES(stateName, argsFirstNode.map((argNode) => argNode ? postcss_value_parser_1.default.stringify(argNode) : '')), {
node: decl,
word: decl.value,
});
}
defineParamState(stateName, paramType, stateDefault, mappedStates, diagnostics, stateDefinition, decl);
}
}
function defineTemplateState(stateName, templateDef, argsFirstNode, argsFullValue, mappedStates, diagnostics, decl) {
const template = (0, string_1.stripQuotation)(postcss_value_parser_1.default.stringify(templateDef));
if (argsFullValue.length === 1) {
// simple template with no params
const selectorStr = template.trim().replace(/\\["']/g, '"');
const selectorAst = (0, selector_1.parseSelectorWithCache)(selectorStr, { clone: true });
if (!validateTemplateSelector({
stateName,
selectorStr,
selectorAst,
cssNode: decl,
diagnostics,
})) {
return;
}
else {
mappedStates[stateName] = selectorStr;
}
}
else if (argsFullValue.length === 2) {
// single parameter template
if (!template.includes('$0')) {
diagnostics.report(exports.stateDiagnostics.TEMPLATE_MISSING_PLACEHOLDER(stateName, template), {
node: decl,
word: template,
});
}
const paramFullDef = argsFullValue[1];
const paramTypeDef = argsFirstNode[1];
if (!paramTypeDef) {
diagnostics.report(exports.stateDiagnostics.TEMPLATE_MISSING_PARAMETER(stateName), {
node: decl,
});
return;
}
const param = createStateParamDef(stateName + ' parameter', paramTypeDef, paramFullDef.splice(paramFullDef.indexOf(paramTypeDef) + 1), diagnostics, decl);
if (!param) {
// UNKNOWN_STATE_TYPE reported in createStateParamDef
return;
}
const templateStateType = {
type: 'template',
template,
params: [param],
};
mappedStates[stateName] = templateStateType;
}
else {
// unsupported multiple params
diagnostics.report(exports.stateDiagnostics.TEMPLATE_MULTI_PARAMETERS(stateName), {
node: decl,
});
}
}
function defineParamState(stateName, paramType, stateDefault, mappedStates, diagnostics, stateDefinition, decl) {
if (paramType.value === 'boolean') {
// explicit boolean // ToDo: remove support
resolveBooleanState(mappedStates, stateDefinition);
}
else {
const stateParamDef = createStateParamDef(stateName, paramType, stateDefault, diagnostics, decl);
if (stateParamDef) {
mappedStates[stateName] = stateParamDef;
}
}
}
function createStateParamDef(stateName, typeDef, stateDefault, diagnostics, decl) {
const type = typeDef.value;
if (type in exports.systemValidators && (typeDef.type === 'function' || typeDef.type === 'word')) {
const stateType = {
type,
arguments: [],
defaultValue: postcss_value_parser_1.default
.stringify(stateDefault)
.trim(),
};
if (typeDef.type === 'function' && typeDef.nodes.length > 0) {
resolveArguments(typeDef, stateType, stateName, diagnostics, decl);
}
return stateType;
}
else {
const srcValue = postcss_value_parser_1.default.stringify(typeDef);
diagnostics.report(exports.stateDiagnostics.UNKNOWN_STATE_TYPE(stateName, srcValue), {
node: decl,
word: srcValue,
});
return;
}
}
function collectStateArgsDef(nodes) {
const argsFullValue = [];
const argsFirstNode = [];
let collectedArg = [];
let firstActualValue = undefined;
for (const node of nodes) {
if (node.type === 'div') {
argsFullValue.push(collectedArg);
argsFirstNode.push(firstActualValue);
collectedArg = [];
firstActualValue = undefined;
}
else {
collectedArg.push(node);
if (!firstActualValue && node.type !== 'space' && node.type !== 'comment') {
firstActualValue = node;
}
}
}
if (collectedArg.length) {
argsFullValue.push(collectedArg);
argsFirstNode.push(firstActualValue);
}
const paramType = argsFirstNode[0];
return { paramType, argsFullValue, argsFirstNode };
}
function resolveArguments(paramType, stateType, name, diagnostics, decl) {
const separatedByComma = (0, value_1.groupValues)(paramType.nodes);
separatedByComma.forEach((group) => {
const validator = group[0];
if (validator.type === 'function') {
const args = (0, value_1.listOptions)(validator);
if (args.length > 1) {
diagnostics.report(exports.stateDiagnostics.TOO_MANY_ARGS_IN_VALIDATOR(name, validator.value, args), {
node: decl,
word: decl.value,
});
}
else {
stateType.arguments.push({
name: validator.value,
args,
});
}
}
else if (validator.type === 'string' || validator.type === 'word') {
stateType.arguments.push(validator.value);
}
});
}
exports.validationErrors = {
string: {
STRING_TYPE_VALIDATION_FAILED: (actualParam) => `"${actualParam}" should be of type string`,
REGEX_VALIDATION_FAILED: (regex, actualParam) => `expected "${actualParam}" to match regex "${regex}"`,
CONTAINS_VALIDATION_FAILED: (shouldContain, actualParam) => `expected "${actualParam}" to contain string "${shouldContain}"`,
MIN_LENGTH_VALIDATION_FAILED: (length, actualParam) => `expected "${actualParam}" to be of length longer than or equal to ${length}`,
MAX_LENGTH_VALIDATION_FAILED: (length, actualParam) => `expected "${actualParam}" to be of length shorter than or equal to ${length}`,
UKNOWN_VALIDATOR: (name) => `encountered unknown string validator "${name}"`,
},
number: {
NUMBER_TYPE_VALIDATION_FAILED: (actualParam) => `expected "${actualParam}" to be of type number`,
MIN_VALIDATION_FAILED: (actualParam, min) => `expected "${actualParam}" to be larger than or equal to ${min}`,
MAX_VALIDATION_FAILED: (actualParam, max) => `expected "${actualParam}" to be lesser then or equal to ${max}`,
MULTIPLE_OF_VALIDATION_FAILED: (actualParam, multipleOf) => `expected "${actualParam}" to be a multiple of ${multipleOf}`,
UKNOWN_VALIDATOR: (name) => `encountered unknown number validator "${name}"`,
},
enum: {
ENUM_TYPE_VALIDATION_FAILED: (actualParam, options) => `expected "${actualParam}" to be one of the options: "${options.join(', ')}"`,
NO_OPTIONS_DEFINED: () => `expected enum to be defined with one option or more`,
},
};
exports.systemValidators = {
string: {
validate(value, validators, resolveParam, validateDefinition, validateValue) {
const res = value;
const errors = [];
if (validateValue && typeof value !== 'string') {
errors.push(exports.validationErrors.string.STRING_TYPE_VALIDATION_FAILED(value));
}
if (validators.length > 0) {
validators.forEach((validatorMeta) => {
if (typeof validatorMeta === 'object') {
if (this.subValidators && this.subValidators[validatorMeta.name]) {
const subValidator = this.subValidators[validatorMeta.name];
const validationRes = subValidator(value, resolveParam(validatorMeta.args[0]));
if (validateValue && validationRes.errors) {
errors.push(...validationRes.errors);
}
}
else if (validateDefinition) {
errors.push(exports.validationErrors.string.UKNOWN_VALIDATOR(validatorMeta.name));
}
}
});
}
return { res, errors: errors.length ? errors : null };
},
subValidators: {
regex: (value, regex) => {
const r = new RegExp(regex);
const valid = r.test(value);
return {
res: value,
errors: valid
? null
: [exports.validationErrors.string.REGEX_VALIDATION_FAILED(regex, value)],
};
},
contains: (value, checkedValue) => {
const valid = !!~value.indexOf(checkedValue);
return {
res: value,
errors: valid
? null
: [exports.validationErrors.string.CONTAINS_VALIDATION_FAILED(checkedValue, value)],
};
},
minLength: (value, length) => {
const valid = value.length >= Number(length);
return {
res: value,
errors: valid
? null
: [exports.validationErrors.string.MIN_LENGTH_VALIDATION_FAILED(length, value)],
};
},
maxLength: (value, length) => {
const valid = value.length <= Number(length);
return {
res: value,
errors: valid
? null
: [exports.validationErrors.string.MAX_LENGTH_VALIDATION_FAILED(length, value)],
};
},
},
},
number: {
validate(value, validators, resolveParam, validateDefinition, validateValue) {
const res = value;
const errors = [];
if (isNaN(value)) {
if (validateValue) {
errors.push(exports.validationErrors.number.NUMBER_TYPE_VALIDATION_FAILED(value));
}
}
else if (validators.length > 0) {
validators.forEach((validatorMeta) => {
if (typeof validatorMeta === 'object') {
if (this.subValidators && this.subValidators[validatorMeta.name]) {
const subValidator = this.subValidators[validatorMeta.name];
const validationRes = subValidator(value, resolveParam(validatorMeta.args[0]));
if (validateValue && validationRes.errors) {
errors.push(...validationRes.errors);
}
}
else if (validateDefinition) {
errors.push(exports.validationErrors.number.UKNOWN_VALIDATOR(validatorMeta.name));
}
}
});
}
return { res, errors: errors.length ? errors : null };
},
subValidators: {
min: (value, minValue) => {
const valid = Number(value) >= Number(minValue);
return {
res: value,
errors: valid
? null
: [exports.validationErrors.number.MIN_VALIDATION_FAILED(value, minValue)],
};
},
max: (value, maxValue) => {
const valid = Number(value) <= Number(maxValue);
return {
res: value,
errors: valid
? null
: [exports.validationErrors.number.MAX_VALIDATION_FAILED(value, maxValue)],
};
},
multipleOf: (value, multipleOf) => {
const valid = Number(value) % Number(multipleOf) === 0;
return {
res: value,
errors: valid
? null
: [
exports.validationErrors.number.MULTIPLE_OF_VALIDATION_FAILED(value, multipleOf),
],
};
},
},
},
enum: {
validate(value, options, resolveParam, validateDefinition, validateValue) {
const res = value;
const errors = [];
const stringOptions = [];
if (options.length) {
const isOneOf = options.some((option) => {
if (typeof option === 'string') {
stringOptions.push(option);
return resolveParam(option) === value;
}
return true;
});
if (validateValue && !isOneOf) {
errors.push(exports.validationErrors.enum.ENUM_TYPE_VALIDATION_FAILED(value, stringOptions));
}
}
else if (validateDefinition) {
errors.push(exports.validationErrors.enum.NO_OPTIONS_DEFINED());
}
return { res, errors: errors.length ? errors : null };
},
},
};
function validateRuleStateDefinition(selector, selectorNode, meta, resolver, diagnostics) {
const selectorAst = (0, selector_1.parseSelectorWithCache)(selector);
if (selectorAst.length && selectorAst.length === 1) {
const singleSelectorAst = selectorAst[0];
const selectorChunk = singleSelectorAst.nodes;
if (selectorChunk.length === 1 && selectorChunk[0].type === 'class') {
const className = selectorChunk[0].value;
const classMeta = features_1.CSSClass.get(meta, className);
const states = classMeta?.[`-st-states`];
if (states && classMeta._kind === 'class') {
for (const stateName in states) {
// TODO: Sort out types
const state = states[stateName];
if (state && typeof state === 'object') {
const stateParam = isTemplateState(state) ? state.params[0] : state;
const { errors } = validateStateArgument(stateParam, meta, stateParam.defaultValue || '', resolver, diagnostics, selectorNode, true, !!stateParam.defaultValue);
if (errors && selectorNode.nodes) {
for (const node of selectorNode.nodes) {
if (node.type === 'decl' && node.prop === `-st-states`) {
diagnostics.report(exports.stateDiagnostics.DEFAULT_PARAM_FAILS_VALIDATION(stateName, stateParam.defaultValue || '', errors), {
node: node,
word: node.value,
});
break;
}
}
}
}
}
}
else {
// TODO: error state on non-class
}
}
}
}
exports.validateRuleStateDefinition = validateRuleStateDefinition;
function validateStateArgument(stateAst, meta, value, resolver, diagnostics, selectorNode, validateDefinition, validateValue = true) {
const resolvedValidations = {
res: resolveParam(meta, resolver, diagnostics, selectorNode, value || stateAst.defaultValue),
errors: null,
};
const { type: paramType } = stateAst;
const validator = exports.systemValidators[paramType];
try {
if (resolvedValidations.res || validateDefinition) {
const { errors } = validator.validate(resolvedValidations.res, stateAst.arguments, resolveParam.bind(null, meta, resolver, diagnostics, selectorNode), !!validateDefinition, validateValue);
resolvedValidations.errors = errors;
}
}
catch (error) {
// TODO: warn about validation throwing exception
}
return resolvedValidations;
}
exports.validateStateArgument = validateStateArgument;
// transform
function transformPseudoClassToCustomState(stateDef, meta, name, stateNode, namespace, resolver, diagnostics, selectorNode) {
if (stateDef === null || typeof stateDef === 'string') {
if (stateNode.nodes && selectorNode) {
diagnostics.report(exports.stateDiagnostics.NO_PARAM_REQUIRED(name, (0, selector_1.stringifySelector)(stateNode.nodes)), {
node: selectorNode,
word: (0, selector_1.stringifySelector)(stateNode),
});
}
if (stateDef === null) {
// boolean
(0, selector_1.convertToClass)(stateNode).value = createBooleanStateClassName(name, namespace);
}
else {
// static template selector
// simply concat global mapped selector - ToDo: maybe change to 'selector'
(0, selector_1.convertToInvalid)(stateNode).value = stateDef;
}
delete stateNode.nodes;
}
else if (typeof stateDef === 'object') {
if (isTemplateState(stateDef)) {
convertTemplateState(meta, resolver, diagnostics, selectorNode, stateNode, stateDef, name);
}
else {
resolveStateValue(meta, resolver, diagnostics, selectorNode, stateNode, stateDef, name, namespace);
}
}
}
exports.transformPseudoClassToCustomState = transformPseudoClassToCustomState;
function isTemplateState(state) {
return !!state && typeof state === 'object' && state.type === 'template';
}
exports.isTemplateState = isTemplateState;
function createBooleanStateClassName(stateName, namespace) {
const escapedNamespace = (0, cssesc_1.default)(namespace, { isIdentifier: true });
return `${escapedNamespace}${exports.booleanStateDelimiter}${stateName}`;
}
exports.createBooleanStateClassName = createBooleanStateClassName;
function createStateWithParamClassName(stateName, namespace, param) {
const escapedNamespace = (0, cssesc_1.default)(namespace, { isIdentifier: true });
return `${escapedNamespace}${exports.stateWithParamDelimiter}${stateName}${resolveStateParam(param, true)}`;
}
exports.createStateWithParamClassName = createStateWithParamClassName;
function resolveStateParam(param, escape = false) {
const result = `${exports.stateMiddleDelimiter}${param.length}${exports.stateMiddleDelimiter}${param.replace(/\s/gm, '_')}`;
// adding/removing initial `s` to indicate that it's not the first param of the identifier
return escape ? (0, cssesc_1.default)(`s` + result, { isIdentifier: true }).slice(1) : result;
}
exports.resolveStateParam = resolveStateParam;
function convertTemplateState(meta, resolver, diagnostics, selectorNode, stateNode, stateParamDef, name) {
const paramStateDef = stateParamDef.params[0];
const resolvedParam = getParamInput(meta, resolver, diagnostics, selectorNode, stateNode, paramStateDef, name);
validateParam(meta, resolver, diagnostics, selectorNode, paramStateDef, resolvedParam, name);
const strippedParam = (0, string_1.stripQuotation)(resolvedParam);
transformMappedStateWithParam({
stateName: name,
template: stateParamDef.template,
param: strippedParam,
node: stateNode,
selectorNode: selectorNode,
diagnostics,
});
}
function getParamInput(meta, resolver, diagnostics, selectorNode, stateNode, stateParamDef, name) {
const inputValue = stateNode.nodes && stateNode.nodes.length ? (0, selector_1.stringifySelector)(stateNode.nodes) : ``;
const resolvedParam = resolveParam(meta, resolver, diagnostics, selectorNode, inputValue ? inputValue : stateParamDef.defaultValue);
if (selectorNode && !inputValue && !stateParamDef.defaultValue) {
diagnostics.report(exports.stateDiagnostics.NO_STATE_ARGUMENT_GIVEN(name, stateParamDef.type), {
node: selectorNode,
word: name,
});
}
return resolvedParam;
}
function validateParam(meta, resolver, diagnostics, selectorNode, stateParamDef, resolvedParam, name) {
const validator = exports.systemValidators[stateParamDef.type];
let stateParamOutput;
try {
stateParamOutput = validator.validate(resolvedParam, stateParamDef.arguments, resolveParam.bind(null, meta, resolver, diagnostics, selectorNode), false, true);
}
catch (e) {
// TODO: warn about validation throwing exception
}
if (stateParamOutput !== undefined) {
if (stateParamOutput.res !== resolvedParam) {
resolvedParam = stateParamOutput.res;
}
if (selectorNode && stateParamOutput.errors) {
diagnostics.report(exports.stateDiagnostics.FAILED_STATE_VALIDATION(name, resolvedParam, stateParamOutput.errors), {
node: selectorNode,
word: resolvedParam,
});
}
}
}
function resolveStateValue(meta, resolver, diagnostics, selectorNode, stateNode, stateParamDef, name, namespace) {
const resolvedParam = getParamInput(meta, resolver, diagnostics, selectorNode, stateNode, stateParamDef, name);
validateParam(meta, resolver, diagnostics, selectorNode, stateParamDef, resolvedParam, name);
const strippedParam = (0, string_1.stripQuotation)(resolvedParam);
(0, selector_1.convertToClass)(stateNode).value = createStateWithParamClassName(name, namespace, strippedParam);
delete stateNode.nodes;
}
function transformMappedStateWithParam({ stateName, template, param, node, selectorNode, diagnostics, }) {
const selectorStr = template.replace(/\$0/g, param);
const selectorAst = (0, selector_1.parseSelectorWithCache)(selectorStr, { clone: true });
if (!validateTemplateSelector({
stateName,
selectorStr,
selectorAst,
cssNode: selectorNode,
diagnostics,
})) {
return;
}
(0, selector_1.convertToSelector)(node).nodes = selectorAst[0].nodes;
}
function validateTemplateSelector({ stateName, selectorStr, selectorAst, cssNode, diagnostics, }) {
if (selectorAst.length > 1) {
if (cssNode) {
diagnostics.report(exports.stateDiagnostics.UNSUPPORTED_MULTI_SELECTOR(stateName, selectorStr), {
node: cssNode,
});
}
return false;
}
else {
const firstSelector = selectorAst[0].nodes.find(({ type }) => type !== 'comment');
if (firstSelector?.type === 'type' || firstSelector?.type === 'universal') {
if (cssNode) {
diagnostics.report(exports.stateDiagnostics.UNSUPPORTED_INITIAL_SELECTOR(stateName, selectorStr), {
node: cssNode,
});
}
return false;
}
let unexpectedSelector = undefined;
for (const node of selectorAst[0].nodes) {
if (node.type === 'combinator' || node.type === 'invalid') {
unexpectedSelector = node;
break;
}
}
if (unexpectedSelector) {
if (cssNode) {
switch (unexpectedSelector.type) {
case 'combinator':
diagnostics.report(exports.stateDiagnostics.UNSUPPORTED_COMPLEX_SELECTOR(stateName, selectorStr), {
node: cssNode,
});
break;
case 'invalid':
diagnostics.report(exports.stateDiagnostics.INVALID_SELECTOR(stateName, selectorStr), {
node: cssNode,
});
break;
}
}
return false;
}
}
return true;
}
function resolveParam(meta, resolver, diagnostics, node, nodeContent) {
const defaultStringValue = '';
const param = nodeContent || defaultStringValue;
return (0, functions_1.evalDeclarationValue)(resolver, param, meta, node, undefined, undefined, diagnostics);
}
//# sourceMappingURL=custom-state.js.map