1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.resolveStateParam = exports.createAttributeState = exports.createStateWithParamClassName = exports.createBooleanStateClassName = exports.setStateToNode = exports.transformPseudoStateSelector = exports.validateStateArgument = exports.validateStateDefinition = exports.processPseudoStates = exports.stateErrors = exports.stateWithParamDelimiter = exports.booleanStateDelimiter = exports.stateMiddleDelimiter = void 0;
|
4 | const functions_1 = require("./functions");
|
5 | const native_reserved_lists_1 = require("./native-reserved-lists");
|
6 | const state_validators_1 = require("./state-validators");
|
7 | const stylable_utils_1 = require("./stylable-utils");
|
8 | const stylable_value_parsers_1 = require("./stylable-value-parsers");
|
9 | const stylable_value_parsers_2 = require("./stylable-value-parsers");
|
10 | const utils_1 = require("./utils");
|
11 | const isVendorPrefixed = require('is-vendor-prefixed');
|
12 | const postcssValueParser = require('postcss-value-parser');
|
13 | const { hasOwnProperty } = Object.prototype;
|
14 | exports.stateMiddleDelimiter = '-';
|
15 | exports.booleanStateDelimiter = '--';
|
16 | exports.stateWithParamDelimiter = exports.booleanStateDelimiter + exports.stateMiddleDelimiter;
|
17 | exports.stateErrors = {
|
18 | UNKNOWN_STATE_USAGE: (name) => `unknown pseudo-state "${name}"`,
|
19 | UNKNOWN_STATE_TYPE: (name, type) => `pseudo-state "${name}" defined with unknown type: "${type}"`,
|
20 | TOO_MANY_STATE_TYPES: (name, types) => `pseudo-state "${name}(${types.join(', ')})" definition must be of a single type`,
|
21 | NO_STATE_TYPE_GIVEN: (name) => `pseudo-state "${name}" expected a definition of a single type, but received none`,
|
22 | TOO_MANY_ARGS_IN_VALIDATOR: (name, validator, args) => `pseudo-state "${name}" expected "${validator}" validator to receive a single argument, but it received "${args.join(', ')}"`,
|
23 | STATE_STARTS_WITH_HYPHEN: (name) => `state "${name}" declaration cannot begin with a "${exports.stateMiddleDelimiter}" chararcter`,
|
24 | };
|
25 |
|
26 | function processPseudoStates(value, decl, diagnostics) {
|
27 | const mappedStates = {};
|
28 | const ast = postcssValueParser(value);
|
29 | const statesSplitByComma = stylable_value_parsers_1.groupValues(ast.nodes);
|
30 | statesSplitByComma.forEach((workingState) => {
|
31 | const [stateDefinition, ...stateDefault] = workingState;
|
32 | if (stateDefinition.value.startsWith('-')) {
|
33 | diagnostics.error(decl, exports.stateErrors.STATE_STARTS_WITH_HYPHEN(stateDefinition.value), {
|
34 | word: stateDefinition.value,
|
35 | });
|
36 | }
|
37 | if (stateDefinition.type === 'function') {
|
38 | resolveStateType(stateDefinition, mappedStates, stateDefault, diagnostics, decl);
|
39 | }
|
40 | else if (stateDefinition.type === 'word') {
|
41 | resolveBooleanState(mappedStates, stateDefinition);
|
42 | }
|
43 | else {
|
44 |
|
45 | }
|
46 | });
|
47 | return mappedStates;
|
48 | }
|
49 | exports.processPseudoStates = processPseudoStates;
|
50 | function resolveStateType(stateDefinition, mappedStates, stateDefault, diagnostics, decl) {
|
51 | if (stateDefinition.type === 'function' && stateDefinition.nodes.length === 0) {
|
52 | resolveBooleanState(mappedStates, stateDefinition);
|
53 | diagnostics.warn(decl, exports.stateErrors.NO_STATE_TYPE_GIVEN(stateDefinition.value), {
|
54 | word: decl.value,
|
55 | });
|
56 | return;
|
57 | }
|
58 | if (stateDefinition.nodes.length > 1) {
|
59 | diagnostics.warn(decl, exports.stateErrors.TOO_MANY_STATE_TYPES(stateDefinition.value, stylable_value_parsers_1.listOptions(stateDefinition)), { word: decl.value });
|
60 | }
|
61 | const paramType = stateDefinition.nodes[0];
|
62 | const stateType = {
|
63 | type: stateDefinition.nodes[0].value,
|
64 | arguments: [],
|
65 | defaultValue: postcssValueParser.stringify(stateDefault).trim(),
|
66 | };
|
67 | if (isCustomMapping(stateDefinition)) {
|
68 | mappedStates[stateDefinition.value] = stateType.type.trim().replace(/\\["']/g, '"');
|
69 | }
|
70 | else if (typeof stateType === 'object' && stateType.type === 'boolean') {
|
71 | resolveBooleanState(mappedStates, stateDefinition);
|
72 | return;
|
73 | }
|
74 | else if (paramType.type === 'function' && stateType.type in state_validators_1.systemValidators) {
|
75 | if (paramType.nodes.length > 0) {
|
76 | resolveArguments(paramType, stateType, stateDefinition.value, diagnostics, decl);
|
77 | }
|
78 | mappedStates[stateDefinition.value] = stateType;
|
79 | }
|
80 | else if (stateType.type in state_validators_1.systemValidators) {
|
81 | mappedStates[stateDefinition.value] = stateType;
|
82 | }
|
83 | else {
|
84 | diagnostics.warn(decl, exports.stateErrors.UNKNOWN_STATE_TYPE(stateDefinition.value, paramType.value), { word: paramType.value });
|
85 | }
|
86 | }
|
87 | function resolveArguments(paramType, stateType, name, diagnostics, decl) {
|
88 | const separatedByComma = stylable_value_parsers_1.groupValues(paramType.nodes);
|
89 | separatedByComma.forEach((group) => {
|
90 | const validator = group[0];
|
91 | if (validator.type === 'function') {
|
92 | const args = stylable_value_parsers_1.listOptions(validator);
|
93 | if (args.length > 1) {
|
94 | diagnostics.warn(decl, exports.stateErrors.TOO_MANY_ARGS_IN_VALIDATOR(name, validator.value, args), { word: decl.value });
|
95 | }
|
96 | else {
|
97 | stateType.arguments.push({
|
98 | name: validator.value,
|
99 | args,
|
100 | });
|
101 | }
|
102 | }
|
103 | else if (validator.type === 'string' || validator.type === 'word') {
|
104 | stateType.arguments.push(validator.value);
|
105 | }
|
106 | });
|
107 | }
|
108 | function isCustomMapping(stateDefinition) {
|
109 | return stateDefinition.nodes.length === 1 && stateDefinition.nodes[0].type === 'string';
|
110 | }
|
111 | function resolveBooleanState(mappedStates, stateDefinition) {
|
112 | const currentState = mappedStates[stateDefinition.type];
|
113 | if (!currentState) {
|
114 | mappedStates[stateDefinition.value] = null;
|
115 | }
|
116 | else {
|
117 |
|
118 | }
|
119 | }
|
120 |
|
121 | function validateStateDefinition(decl, meta, resolver, diagnostics) {
|
122 | if (decl.parent && decl.parent.type !== 'root') {
|
123 | const container = decl.parent;
|
124 | if (container.type !== 'atrule') {
|
125 | const sRule = container;
|
126 | if (sRule.selectorAst.nodes && sRule.selectorAst.nodes.length === 1) {
|
127 | const singleSelectorAst = sRule.selectorAst.nodes[0];
|
128 | const selectorChunk = singleSelectorAst.nodes;
|
129 | if (selectorChunk.length === 1 && selectorChunk[0].type === 'class') {
|
130 | const className = selectorChunk[0].name;
|
131 | const classMeta = meta.classes[className];
|
132 | const states = classMeta[stylable_value_parsers_2.valueMapping.states];
|
133 | if (classMeta && classMeta._kind === 'class' && states) {
|
134 | for (const stateName in states) {
|
135 |
|
136 | const state = states[stateName];
|
137 | if (state && typeof state === 'object') {
|
138 | const res = validateStateArgument(state, meta, state.defaultValue || '', resolver, diagnostics, sRule, true, !!state.defaultValue);
|
139 | if (res.errors) {
|
140 | res.errors.unshift(`pseudo-state "${stateName}" default value "${state.defaultValue}" failed validation:`);
|
141 | diagnostics.warn(decl, res.errors.join('\n'), {
|
142 | word: decl.value,
|
143 | });
|
144 | }
|
145 | }
|
146 | }
|
147 | }
|
148 | else {
|
149 |
|
150 | }
|
151 | }
|
152 | }
|
153 | }
|
154 | }
|
155 | }
|
156 | exports.validateStateDefinition = validateStateDefinition;
|
157 | function validateStateArgument(stateAst, meta, value, resolver, diagnostics, rule, validateDefinition, validateValue = true) {
|
158 | const resolvedValidations = {
|
159 | res: resolveParam(meta, resolver, diagnostics, rule, value || stateAst.defaultValue),
|
160 | errors: null,
|
161 | };
|
162 | const { type: paramType } = stateAst;
|
163 | const validator = state_validators_1.systemValidators[paramType];
|
164 | try {
|
165 | if (resolvedValidations.res || validateDefinition) {
|
166 | const { errors } = validator.validate(resolvedValidations.res, stateAst.arguments, resolveParam.bind(null, meta, resolver, diagnostics, rule), !!validateDefinition, validateValue);
|
167 | resolvedValidations.errors = errors;
|
168 | }
|
169 | }
|
170 | catch (error) {
|
171 |
|
172 | }
|
173 | return resolvedValidations;
|
174 | }
|
175 | exports.validateStateArgument = validateStateArgument;
|
176 | function transformPseudoStateSelector(meta, node, name, symbol, origin, originSymbol, resolver, diagnostics, rule) {
|
177 | let current = meta;
|
178 | let currentSymbol = symbol;
|
179 | if (originSymbol && symbol !== originSymbol) {
|
180 | current = origin;
|
181 | currentSymbol = originSymbol;
|
182 | }
|
183 | let found = false;
|
184 | while (current && currentSymbol) {
|
185 | if (currentSymbol._kind === 'class' || currentSymbol._kind === 'element') {
|
186 | const states = currentSymbol[stylable_value_parsers_2.valueMapping.states];
|
187 | const extend = currentSymbol[stylable_value_parsers_2.valueMapping.extends];
|
188 | const alias = currentSymbol.alias;
|
189 | if (states && hasOwnProperty.call(states, name)) {
|
190 | found = true;
|
191 | setStateToNode(states, meta, name, node, current.namespace, resolver, diagnostics, rule);
|
192 | break;
|
193 | }
|
194 | else if (extend) {
|
195 | if (current.mappedSymbols[extend.name] &&
|
196 | current.mappedSymbols[extend.name]._kind !== 'import') {
|
197 | const nextCurrentSymbol = current.mappedSymbols[extend.name];
|
198 | if (currentSymbol === nextCurrentSymbol) {
|
199 | break;
|
200 | }
|
201 | currentSymbol = nextCurrentSymbol;
|
202 | }
|
203 | else {
|
204 | const next = resolver.resolve(extend);
|
205 | if (next && next.meta) {
|
206 | currentSymbol = next.symbol;
|
207 | current = next.meta;
|
208 | }
|
209 | else {
|
210 | break;
|
211 | }
|
212 | }
|
213 | }
|
214 | else if (alias) {
|
215 | const next = resolver.resolve(alias);
|
216 | if (next && next.meta) {
|
217 | currentSymbol = next.symbol;
|
218 | current = next.meta;
|
219 | }
|
220 | else {
|
221 | break;
|
222 | }
|
223 | }
|
224 | else {
|
225 | break;
|
226 | }
|
227 | }
|
228 | else {
|
229 | break;
|
230 | }
|
231 | }
|
232 | if (!found && rule) {
|
233 | if (!native_reserved_lists_1.nativePseudoClasses.includes(name) && !isVendorPrefixed(name)) {
|
234 | diagnostics.warn(rule, exports.stateErrors.UNKNOWN_STATE_USAGE(name), { word: name });
|
235 | }
|
236 | }
|
237 | return meta;
|
238 | }
|
239 | exports.transformPseudoStateSelector = transformPseudoStateSelector;
|
240 | function setStateToNode(states, meta, name, node, namespace, resolver, diagnostics, rule) {
|
241 | const stateDef = states[name];
|
242 | if (stateDef === null) {
|
243 | node.type = 'class';
|
244 | node.name = createBooleanStateClassName(name, namespace);
|
245 | }
|
246 | else if (typeof stateDef === 'string') {
|
247 | node.type = 'invalid';
|
248 | node.value = stateDef;
|
249 | }
|
250 | else if (typeof stateDef === 'object') {
|
251 | resolveStateValue(meta, resolver, diagnostics, rule, node, stateDef, name, namespace);
|
252 | }
|
253 | }
|
254 | exports.setStateToNode = setStateToNode;
|
255 | function resolveStateValue(meta, resolver, diagnostics, rule, node, stateDef, name, namespace) {
|
256 | let actualParam = resolveParam(meta, resolver, diagnostics, rule, node.content || stateDef.defaultValue);
|
257 | const validator = state_validators_1.systemValidators[stateDef.type];
|
258 | let stateParamOutput;
|
259 | try {
|
260 | stateParamOutput = validator.validate(actualParam, stateDef.arguments, resolveParam.bind(null, meta, resolver, diagnostics, rule), false, true);
|
261 | }
|
262 | catch (e) {
|
263 |
|
264 | }
|
265 | if (stateParamOutput !== undefined) {
|
266 | if (stateParamOutput.res !== actualParam) {
|
267 | actualParam = stateParamOutput.res;
|
268 | }
|
269 | if (rule && stateParamOutput.errors) {
|
270 | stateParamOutput.errors.unshift(`pseudo-state "${name}" with parameter "${actualParam}" failed validation:`);
|
271 | diagnostics.warn(rule, stateParamOutput.errors.join('\n'), { word: actualParam });
|
272 | }
|
273 | }
|
274 | const strippedParam = utils_1.stripQuotation(actualParam);
|
275 | if (stylable_utils_1.isValidClassName(strippedParam)) {
|
276 | node.type = 'class';
|
277 | node.name = createStateWithParamClassName(name, namespace, strippedParam);
|
278 | }
|
279 | else {
|
280 | node.type = 'attribute';
|
281 | node.content = createAttributeState(name, namespace, strippedParam);
|
282 | }
|
283 | }
|
284 | function resolveParam(meta, resolver, diagnostics, rule, nodeContent) {
|
285 | const defaultStringValue = '';
|
286 | const param = nodeContent || defaultStringValue;
|
287 | return functions_1.evalDeclarationValue(resolver, param, meta, rule, undefined, undefined, diagnostics);
|
288 | }
|
289 | function createBooleanStateClassName(stateName, namespace) {
|
290 | return `${namespace}${exports.booleanStateDelimiter}${stateName}`;
|
291 | }
|
292 | exports.createBooleanStateClassName = createBooleanStateClassName;
|
293 | function createStateWithParamClassName(stateName, namespace, param) {
|
294 | return `${namespace}${exports.stateWithParamDelimiter}${stateName}${resolveStateParam(param)}`;
|
295 | }
|
296 | exports.createStateWithParamClassName = createStateWithParamClassName;
|
297 | function createAttributeState(stateName, namespace, param) {
|
298 | return `class~="${createStateWithParamClassName(stateName, namespace, param)}"`;
|
299 | }
|
300 | exports.createAttributeState = createAttributeState;
|
301 | function resolveStateParam(param) {
|
302 | if (stylable_utils_1.isValidClassName(param)) {
|
303 | return `${exports.stateMiddleDelimiter}${param.length}${exports.stateMiddleDelimiter}${param}`;
|
304 | }
|
305 | else {
|
306 | return `${exports.stateMiddleDelimiter}${param.length}${exports.stateMiddleDelimiter}${utils_1.stripQuotation(JSON.stringify(param).replace(/\s/gm, '_'))}`;
|
307 | }
|
308 | }
|
309 | exports.resolveStateParam = resolveStateParam;
|
310 |
|
\ | No newline at end of file |