1 | "use strict";
|
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3 | if (k2 === undefined) k2 = k;
|
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5 | }) : (function(o, m, k, k2) {
|
6 | if (k2 === undefined) k2 = k;
|
7 | o[k2] = m[k];
|
8 | }));
|
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
11 | }) : function(o, v) {
|
12 | o["default"] = v;
|
13 | });
|
14 | var __importStar = (this && this.__importStar) || function (mod) {
|
15 | if (mod && mod.__esModule) return mod;
|
16 | var result = {};
|
17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
18 | __setModuleDefault(result, mod);
|
19 | return result;
|
20 | };
|
21 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
22 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
23 | };
|
24 | Object.defineProperty(exports, "__esModule", { value: true });
|
25 | exports.removeSTDirective = exports.StylableTransformer = exports.transformerWarnings = void 0;
|
26 | const path_1 = require("path");
|
27 | const postcss = __importStar(require("postcss"));
|
28 | const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
|
29 | const is_vendor_prefixed_1 = __importDefault(require("is-vendor-prefixed"));
|
30 | const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
|
31 | const custom_values_1 = require("./custom-values");
|
32 | const functions_1 = require("./functions");
|
33 | const native_reserved_lists_1 = require("./native-reserved-lists");
|
34 | const pseudo_states_1 = require("./pseudo-states");
|
35 | const selector_utils_1 = require("./selector-utils");
|
36 | const stylable_mixins_1 = require("./stylable-mixins");
|
37 | const stylable_resolver_1 = require("./stylable-resolver");
|
38 | const stylable_utils_1 = require("./stylable-utils");
|
39 | const stylable_value_parsers_1 = require("./stylable-value-parsers");
|
40 | const { hasOwnProperty } = Object.prototype;
|
41 | const USE_SCOPE_SELECTOR_2 = true;
|
42 | exports.transformerWarnings = {
|
43 | UNKNOWN_PSEUDO_ELEMENT(name) {
|
44 | return `unknown pseudo element "${name}"`;
|
45 | },
|
46 | IMPORT_ISNT_EXTENDABLE() {
|
47 | return 'import is not extendable';
|
48 | },
|
49 | CANNOT_EXTEND_UNKNOWN_SYMBOL(name) {
|
50 | return `cannot extend unknown symbol "${name}"`;
|
51 | },
|
52 | CANNOT_EXTEND_JS() {
|
53 | return 'JS import is not extendable';
|
54 | },
|
55 | KEYFRAME_NAME_RESERVED(name) {
|
56 | return `keyframes "${name}" is reserved`;
|
57 | },
|
58 | UNKNOWN_IMPORT_ALIAS(name) {
|
59 | return `cannot use alias for unknown import "${name}"`;
|
60 | },
|
61 | };
|
62 | class StylableTransformer {
|
63 | constructor(options) {
|
64 | this.metaParts = new WeakMap();
|
65 | this.diagnostics = options.diagnostics;
|
66 | this.delimiter = options.delimiter || '__';
|
67 | this.keepValues = options.keepValues || false;
|
68 | this.fileProcessor = options.fileProcessor;
|
69 | this.replaceValueHook = options.replaceValueHook;
|
70 | this.postProcessor = options.postProcessor;
|
71 | this.resolver = new stylable_resolver_1.StylableResolver(options.fileProcessor, options.requireModule);
|
72 | this.mode = options.mode || 'production';
|
73 | }
|
74 | transform(meta) {
|
75 | const metaExports = {
|
76 | classes: {},
|
77 | vars: {},
|
78 | stVars: {},
|
79 | keyframes: {},
|
80 | };
|
81 | const ast = this.resetTransformProperties(meta);
|
82 | this.resolver.validateImports(meta, this.diagnostics);
|
83 | meta.transformedScopes = validateScopes(this, meta);
|
84 | this.transformAst(ast, meta, metaExports);
|
85 | this.transformGlobals(ast, meta);
|
86 | meta.transformDiagnostics = this.diagnostics;
|
87 | const result = { meta, exports: metaExports };
|
88 | return this.postProcessor ? this.postProcessor(result, this) : result;
|
89 | }
|
90 | transformAst(ast, meta, metaExports, variableOverride, path = [], mixinTransform = false) {
|
91 | const keyframeMapping = this.scopeKeyframes(ast, meta);
|
92 | const cssVarsMapping = this.createCSSVarsMapping(ast, meta);
|
93 | ast.walkRules((rule) => {
|
94 | if (selector_utils_1.isChildOfAtRule(rule, 'keyframes')) {
|
95 | return;
|
96 | }
|
97 | rule.selector = this.scopeRule(meta, rule, metaExports && metaExports.classes);
|
98 | });
|
99 | ast.walkAtRules(/media$/, (atRule) => {
|
100 | atRule.params = functions_1.evalDeclarationValue(this.resolver, atRule.params, meta, atRule, variableOverride, this.replaceValueHook, this.diagnostics, path.slice(), undefined, undefined);
|
101 | });
|
102 | ast.walkDecls((decl) => {
|
103 | stylable_utils_1.getDeclStylable(decl).sourceValue = decl.value;
|
104 | if (stylable_utils_1.isCSSVarProp(decl.prop)) {
|
105 | decl.prop = this.getScopedCSSVar(decl, meta, cssVarsMapping);
|
106 | }
|
107 | switch (decl.prop) {
|
108 | case stylable_value_parsers_1.valueMapping.partialMixin:
|
109 | case stylable_value_parsers_1.valueMapping.mixin:
|
110 | break;
|
111 | case stylable_value_parsers_1.valueMapping.states:
|
112 | pseudo_states_1.validateStateDefinition(decl, meta, this.resolver, this.diagnostics);
|
113 | break;
|
114 | default:
|
115 | decl.value = functions_1.evalDeclarationValue(this.resolver, decl.value, meta, decl, variableOverride, this.replaceValueHook, this.diagnostics, path.slice(), cssVarsMapping, undefined);
|
116 | }
|
117 | });
|
118 | if (USE_SCOPE_SELECTOR_2) {
|
119 | if (!mixinTransform && meta.outputAst && this.mode === 'development') {
|
120 | this.addDevRules(meta);
|
121 | }
|
122 | }
|
123 | ast.walkRules((rule) => stylable_mixins_1.appendMixins(this, rule, meta, variableOverride || {}, cssVarsMapping, path));
|
124 | if (metaExports) {
|
125 | if (USE_SCOPE_SELECTOR_2) {
|
126 | Object.assign(metaExports.classes, this.exportClasses(meta));
|
127 | }
|
128 | else {
|
129 | this.exportRootClass(meta, metaExports.classes);
|
130 | }
|
131 | this.exportLocalVars(meta, metaExports.stVars, variableOverride);
|
132 | this.exportKeyframes(keyframeMapping, metaExports.keyframes);
|
133 | this.exportCSSVars(cssVarsMapping, metaExports.vars);
|
134 | }
|
135 | }
|
136 | exportLocalVars(meta, stVarsExport, variableOverride) {
|
137 | for (const varSymbol of meta.vars) {
|
138 | const { outputValue, topLevelType } = functions_1.processDeclarationValue(this.resolver, varSymbol.text, meta, varSymbol.node, variableOverride);
|
139 | stVarsExport[varSymbol.name] = topLevelType ? custom_values_1.unbox(topLevelType) : outputValue;
|
140 | }
|
141 | }
|
142 | exportCSSVars(cssVarsMapping, varsExport) {
|
143 | for (const varName of Object.keys(cssVarsMapping)) {
|
144 | varsExport[varName.slice(2)] = cssVarsMapping[varName];
|
145 | }
|
146 | }
|
147 | exportKeyframes(keyframeMapping, keyframesExport) {
|
148 | Object.assign(keyframesExport, keyframeMapping);
|
149 | }
|
150 | exportRootClass(meta, classesExport) {
|
151 | const classExports = {};
|
152 | this.handleClass(meta, {
|
153 | type: 'class',
|
154 | name: meta.mappedSymbols[meta.root].name,
|
155 | nodes: [],
|
156 | }, meta.mappedSymbols[meta.root].name, classExports);
|
157 | classesExport[meta.root] = classExports[meta.mappedSymbols[meta.root].name];
|
158 | }
|
159 | exportClass(meta, name, classSymbol, metaExports) {
|
160 | const scopedName = this.scope(name, meta.namespace);
|
161 | if (metaExports && !metaExports[name]) {
|
162 | const extend = classSymbol ? classSymbol[stylable_value_parsers_1.valueMapping.extends] : undefined;
|
163 | let exportedClasses = scopedName;
|
164 | if (extend && extend !== classSymbol) {
|
165 | let finalSymbol;
|
166 | let finalName;
|
167 | let finalMeta;
|
168 | if (extend._kind === 'class') {
|
169 | finalSymbol = extend;
|
170 | finalName = extend.name;
|
171 | finalMeta = meta;
|
172 | }
|
173 | else if (extend._kind === 'import') {
|
174 | const resolved = this.resolver.deepResolve(extend);
|
175 | if (resolved && resolved._kind === 'css' && resolved.symbol) {
|
176 | if (resolved.symbol._kind === 'class') {
|
177 | finalSymbol = resolved.symbol;
|
178 | finalName = resolved.symbol.name;
|
179 | finalMeta = resolved.meta;
|
180 | }
|
181 | else {
|
182 | const found = stylable_utils_1.findRule(meta.ast, '.' + classSymbol.name);
|
183 | if (found) {
|
184 | this.diagnostics.error(found, exports.transformerWarnings.IMPORT_ISNT_EXTENDABLE(), { word: found.value });
|
185 | }
|
186 | }
|
187 | }
|
188 | else if (resolved) {
|
189 | const found = stylable_utils_1.findRule(meta.ast, '.' + classSymbol.name);
|
190 | if (found) {
|
191 | if (!resolved.symbol) {
|
192 | this.diagnostics.error(found, exports.transformerWarnings.CANNOT_EXTEND_UNKNOWN_SYMBOL(found.value), { word: found.value });
|
193 | }
|
194 | else {
|
195 | this.diagnostics.error(found, exports.transformerWarnings.CANNOT_EXTEND_JS(), {
|
196 | word: found.value,
|
197 | });
|
198 | }
|
199 | }
|
200 | }
|
201 | }
|
202 | if (finalSymbol && finalName && finalMeta && !finalSymbol[stylable_value_parsers_1.valueMapping.root]) {
|
203 | const classExports = {};
|
204 | this.handleClass(finalMeta, { type: 'class', name: finalName, nodes: [] }, finalName, classExports);
|
205 | if (classExports[finalName]) {
|
206 | exportedClasses += ' ' + classExports[finalName];
|
207 | }
|
208 | else {
|
209 | console.error(`something went wrong when exporting '${finalName}', ` +
|
210 | `please file an issue in stylable. With specific use case`);
|
211 | }
|
212 | }
|
213 | }
|
214 | metaExports[name] = exportedClasses;
|
215 | }
|
216 | return scopedName;
|
217 | }
|
218 | scopeKeyframes(ast, meta) {
|
219 | ast.walkAtRules(/keyframes$/, (atRule) => {
|
220 | const name = atRule.params;
|
221 | if (~native_reserved_lists_1.reservedKeyFrames.indexOf(name)) {
|
222 | this.diagnostics.error(atRule, exports.transformerWarnings.KEYFRAME_NAME_RESERVED(name), {
|
223 | word: name,
|
224 | });
|
225 | }
|
226 | atRule.params = this.scope(name, meta.namespace);
|
227 | });
|
228 | const keyframesExports = {};
|
229 | Object.keys(meta.mappedKeyframes).forEach((key) => {
|
230 | const res = this.resolver.resolveKeyframes(meta, key);
|
231 | if (res) {
|
232 | keyframesExports[key] = this.scope(res.symbol.alias, res.meta.namespace);
|
233 | }
|
234 | });
|
235 | ast.walkDecls(/animation$|animation-name$/, (decl) => {
|
236 | const parsed = postcss_value_parser_1.default(decl.value);
|
237 | parsed.nodes.forEach((node) => {
|
238 | const scoped = keyframesExports[node.value];
|
239 | if (scoped) {
|
240 | node.value = scoped;
|
241 | }
|
242 | });
|
243 | decl.value = parsed.toString();
|
244 | });
|
245 | return keyframesExports;
|
246 | }
|
247 | createCSSVarsMapping(_ast, meta) {
|
248 | const cssVarsMapping = {};
|
249 |
|
250 | for (const imported of meta.imports) {
|
251 | for (const symbolName of Object.keys(imported.named)) {
|
252 | if (stylable_utils_1.isCSSVarProp(symbolName)) {
|
253 | const importedVar = this.resolver.deepResolve(meta.mappedSymbols[symbolName]);
|
254 | if (importedVar &&
|
255 | importedVar._kind === 'css' &&
|
256 | importedVar.symbol &&
|
257 | importedVar.symbol._kind === 'cssVar') {
|
258 | cssVarsMapping[symbolName] = importedVar.symbol.global
|
259 | ? importedVar.symbol.name
|
260 | : stylable_utils_1.generateScopedCSSVar(importedVar.meta.namespace, importedVar.symbol.name.slice(2));
|
261 | }
|
262 | }
|
263 | }
|
264 | }
|
265 |
|
266 | for (const localVarName of Object.keys(meta.cssVars)) {
|
267 | const cssVar = meta.cssVars[localVarName];
|
268 | if (!cssVarsMapping[localVarName]) {
|
269 | cssVarsMapping[localVarName] = cssVar.global
|
270 | ? localVarName
|
271 | : stylable_utils_1.generateScopedCSSVar(meta.namespace, localVarName.slice(2));
|
272 | }
|
273 | }
|
274 | return cssVarsMapping;
|
275 | }
|
276 | getScopedCSSVar(decl, meta, cssVarsMapping) {
|
277 | let prop = decl.prop;
|
278 | if (meta.cssVars[prop]) {
|
279 | prop = cssVarsMapping[prop];
|
280 | }
|
281 | return prop;
|
282 | }
|
283 | addGlobalsToMeta(selectorAst, meta) {
|
284 | if (!meta) {
|
285 | return;
|
286 | }
|
287 | for (const ast of selectorAst) {
|
288 | selector_utils_1.traverseNode(ast, (inner) => {
|
289 | if (inner.type === 'class') {
|
290 | meta.globals[inner.name] = true;
|
291 | }
|
292 | });
|
293 | }
|
294 | }
|
295 | transformGlobals(ast, meta) {
|
296 | ast.walkRules((r) => {
|
297 | const selectorAst = selector_utils_1.parseSelector(r.selector);
|
298 | selector_utils_1.traverseNode(selectorAst, (node) => {
|
299 | if (node.type === 'nested-pseudo-class' && node.name === 'global') {
|
300 | this.addGlobalsToMeta([node], meta);
|
301 | node.type = 'selector';
|
302 | return true;
|
303 | }
|
304 | return undefined;
|
305 | });
|
306 |
|
307 | r.selector = selector_utils_1.stringifySelector(selectorAst);
|
308 | });
|
309 | }
|
310 | resolveSelectorElements(meta, selector) {
|
311 | if (USE_SCOPE_SELECTOR_2) {
|
312 | return this.scopeSelector2(meta, selector, undefined, true).elements;
|
313 | }
|
314 | else {
|
315 | return this.scopeSelector(meta, selector, undefined, true).elements;
|
316 | }
|
317 | }
|
318 | scopeSelector(originMeta, selector, classesExport, calcPaths = false, rule) {
|
319 | let meta = originMeta;
|
320 | let current = meta;
|
321 | let symbol = null;
|
322 | let nestedSymbol;
|
323 | let originSymbol;
|
324 | const selectorAst = selector_utils_1.parseSelector(selector);
|
325 | const addedSelectors = [];
|
326 | const elements = selectorAst.nodes.map((selectorNode) => {
|
327 | const selectorElements = [];
|
328 | selector_utils_1.traverseNode(selectorNode, (node) => {
|
329 | const { name, type } = node;
|
330 | if (calcPaths &&
|
331 | (type === 'class' || type === 'element' || type === 'pseudo-element')) {
|
332 | selectorElements.push({
|
333 | name,
|
334 | type,
|
335 | resolved: this.resolver.resolveExtends(current, name, type === 'element', this),
|
336 | });
|
337 | }
|
338 | if (type === 'selector' || type === 'spacing' || type === 'operator') {
|
339 | if (nestedSymbol) {
|
340 | symbol = nestedSymbol;
|
341 | nestedSymbol = null;
|
342 | }
|
343 | else {
|
344 | meta = originMeta;
|
345 | current = originMeta;
|
346 | symbol = originMeta.classes[originMeta.root];
|
347 | originSymbol = symbol;
|
348 | }
|
349 | }
|
350 | else if (type === 'class') {
|
351 | const next = this.handleClass(current, node, name, classesExport, rule, originMeta);
|
352 | originSymbol = current.classes[name];
|
353 | symbol = next.symbol;
|
354 | current = next.meta;
|
355 | }
|
356 | else if (type === 'element') {
|
357 | const next = this.handleElement(current, node, name, originMeta);
|
358 | originSymbol = current.elements[name];
|
359 | symbol = next.symbol;
|
360 | current = next.meta;
|
361 | }
|
362 | else if (type === 'pseudo-element') {
|
363 | const next = this.handlePseudoElement(current, node, name, selectorNode, addedSelectors, rule, originMeta);
|
364 | originSymbol = current.classes[name];
|
365 | meta = current;
|
366 | symbol = next.symbol;
|
367 | current = next.meta;
|
368 | }
|
369 | else if (type === 'pseudo-class') {
|
370 | current = pseudo_states_1.transformPseudoStateSelector(current, node, name, symbol, meta, originSymbol, this.resolver, this.diagnostics, rule);
|
371 | }
|
372 | else if (type === 'nested-pseudo-class') {
|
373 | if (name === 'global') {
|
374 |
|
375 | return true;
|
376 | }
|
377 | nestedSymbol = symbol;
|
378 | }
|
379 | else if (type === 'invalid' && node.value === '&' && current.parent) {
|
380 | const origin = current.mappedSymbols[current.root];
|
381 | const next = this.handleClass(current, {
|
382 | type: 'class',
|
383 | nodes: [],
|
384 | name: origin.name,
|
385 | }, origin.name, undefined, undefined, originMeta);
|
386 | originSymbol = current.classes[origin.name];
|
387 | symbol = next.symbol;
|
388 | current = next.meta;
|
389 | }
|
390 |
|
391 | return undefined;
|
392 | });
|
393 | return selectorElements;
|
394 | });
|
395 | this.addAdditionalSelectors(addedSelectors, selectorAst);
|
396 | return {
|
397 | current,
|
398 | symbol,
|
399 | selectorAst,
|
400 | elements,
|
401 | selector: selector_utils_1.stringifySelector(selectorAst),
|
402 | };
|
403 | }
|
404 | addAdditionalSelectors(addedSelectors, selectorAst) {
|
405 | addedSelectors.forEach((s) => {
|
406 | const clone = lodash_clonedeep_1.default(s.selectorNode);
|
407 | const i = s.selectorNode.nodes.indexOf(s.node);
|
408 | if (i === -1) {
|
409 | throw new Error('not supported inside nested classes');
|
410 | }
|
411 | else {
|
412 | clone.nodes[i].value = s.customElementChunk;
|
413 | }
|
414 | selectorAst.nodes.push(clone);
|
415 | });
|
416 | }
|
417 | applyRootScoping(meta, selectorAst) {
|
418 | const scopedRoot = meta.mappedSymbols[meta.root][stylable_value_parsers_1.valueMapping.global] ||
|
419 | this.scope(meta.root, meta.namespace);
|
420 | selectorAst.nodes.forEach((selector) => {
|
421 | const first = selector.nodes[0];
|
422 |
|
423 | if (first &&
|
424 | (first.type === 'selector' || first.type === 'nested-pseudo-class') &&
|
425 | first.name === 'global') {
|
426 | return;
|
427 | }
|
428 |
|
429 | if (first && first.nodes === scopedRoot) {
|
430 | return;
|
431 | }
|
432 | if (first && first.before && first.before === '.' + scopedRoot) {
|
433 | return;
|
434 | }
|
435 | if (first && first.type === 'invalid' && first.value === '&') {
|
436 | return;
|
437 | }
|
438 | if (!first || first.name !== scopedRoot) {
|
439 | selector.nodes = [
|
440 | typeof scopedRoot !== 'string'
|
441 | ? { type: 'selector', nodes: scopedRoot, name: 'global' }
|
442 | : { type: 'class', name: scopedRoot, nodes: [] },
|
443 | { type: 'spacing', value: ' ', name: '', nodes: [] },
|
444 | ...selector.nodes,
|
445 | ];
|
446 | }
|
447 | });
|
448 | }
|
449 | scopeRule(meta, rule, _classesExport) {
|
450 | if (USE_SCOPE_SELECTOR_2) {
|
451 | return this.scopeSelector2(meta, rule.selector, undefined, false, rule).selector;
|
452 | }
|
453 | else {
|
454 | return this.scopeSelector(meta, rule.selector, _classesExport, false, rule).selector;
|
455 | }
|
456 | }
|
457 | handleClass(meta, node, name, classesExport, rule, originMeta) {
|
458 | const symbol = meta.classes[name];
|
459 | const extend = symbol ? symbol[stylable_value_parsers_1.valueMapping.extends] : undefined;
|
460 | if (!extend && symbol && symbol.alias) {
|
461 | let next = this.resolver.deepResolve(symbol.alias);
|
462 | if (next && next._kind === 'css' && next.symbol && next.symbol._kind === 'class') {
|
463 | const globalMappedNodes = next.symbol[stylable_value_parsers_1.valueMapping.global];
|
464 | if (globalMappedNodes) {
|
465 | node.before = '';
|
466 | node.type = 'selector';
|
467 | node.nodes = globalMappedNodes;
|
468 | this.addGlobalsToMeta(globalMappedNodes, originMeta);
|
469 | }
|
470 | else {
|
471 | node.name = this.exportClass(next.meta, next.symbol.name, next.symbol, classesExport);
|
472 | }
|
473 | if (next.symbol[stylable_value_parsers_1.valueMapping.extends]) {
|
474 | next = this.resolver.deepResolve(next.symbol[stylable_value_parsers_1.valueMapping.extends]);
|
475 | if (next && next._kind === 'css') {
|
476 | return next;
|
477 | }
|
478 | }
|
479 | else {
|
480 | return next;
|
481 | }
|
482 | }
|
483 | else if (rule) {
|
484 | this.diagnostics.error(rule, exports.transformerWarnings.UNKNOWN_IMPORT_ALIAS(name), {
|
485 | word: symbol.alias.name,
|
486 | });
|
487 | }
|
488 | }
|
489 | let scopedName = '';
|
490 | let globalScopedSelector = '';
|
491 | const globalMappedNodes = symbol && symbol[stylable_value_parsers_1.valueMapping.global];
|
492 | if (globalMappedNodes) {
|
493 | globalScopedSelector = selector_utils_1.stringifySelector({
|
494 | type: 'selector',
|
495 | name: '',
|
496 | nodes: globalMappedNodes,
|
497 | });
|
498 | }
|
499 | else {
|
500 | scopedName = this.exportClass(meta, name, symbol, classesExport);
|
501 | }
|
502 | if (globalScopedSelector) {
|
503 | node.before = '';
|
504 | node.type = 'selector';
|
505 | node.nodes = symbol[stylable_value_parsers_1.valueMapping.global] || [];
|
506 | this.addGlobalsToMeta(globalMappedNodes, originMeta);
|
507 | }
|
508 | else {
|
509 | node.name = scopedName;
|
510 | }
|
511 | const next = this.resolver.deepResolve(extend);
|
512 | if (next && next._kind === 'css' && next.symbol && next.symbol._kind === 'class') {
|
513 | if (this.mode === 'development' && rule && rule.selectorType === 'class') {
|
514 | rule.after(selector_utils_1.createWarningRule(next.symbol.name, this.scope(next.symbol.name, next.meta.namespace), path_1.basename(next.meta.source), name, this.scope(symbol.name, meta.namespace), path_1.basename(meta.source)));
|
515 | }
|
516 | return next;
|
517 | }
|
518 |
|
519 | if (extend && extend._kind === 'class') {
|
520 | if (extend === symbol && extend.alias) {
|
521 | const next = this.resolver.deepResolve(extend.alias);
|
522 | if (next && next._kind === 'css' && next.symbol) {
|
523 | return next;
|
524 | }
|
525 | }
|
526 | }
|
527 | return { _kind: 'css', meta, symbol };
|
528 | }
|
529 | handleElement(meta, node, name, originMeta) {
|
530 | const tRule = meta.elements[name];
|
531 | const extend = tRule ? meta.mappedSymbols[name] : undefined;
|
532 | const next = this.resolver.deepResolve(extend);
|
533 | if (next && next._kind === 'css' && next.symbol) {
|
534 | if (next.symbol._kind === 'class' && next.symbol[stylable_value_parsers_1.valueMapping.global]) {
|
535 | node.before = '';
|
536 | node.type = 'selector';
|
537 | node.nodes = next.symbol[stylable_value_parsers_1.valueMapping.global] || [];
|
538 | this.addGlobalsToMeta(node.nodes, originMeta);
|
539 | }
|
540 | else {
|
541 | node.type = 'class';
|
542 | node.name = this.scope(next.symbol.name, next.meta.namespace);
|
543 | }
|
544 |
|
545 |
|
546 | return next;
|
547 | }
|
548 | return { meta, symbol: tRule };
|
549 | }
|
550 | handlePseudoElement(meta, node, name, selectorNode, addedSelectors, rule, originMeta) {
|
551 | let next;
|
552 | const customSelector = meta.customSelectors[':--' + name];
|
553 | if (customSelector) {
|
554 | const rootRes = this.scopeSelector(meta, '.root', {}, false);
|
555 | const res = this.scopeSelector(meta, customSelector, {}, false);
|
556 | const rootEg = new RegExp('^\\s*' + rootRes.selector.replace(/\./, '\\.') + '\\s*');
|
557 | const selectors = res.selectorAst.nodes.map((sel) => selector_utils_1.stringifySelector(sel).trim().replace(rootEg, ''));
|
558 | if (selectors[0]) {
|
559 | node.type = 'invalid';
|
560 | node.before = ' ';
|
561 | node.value = selectors[0];
|
562 | }
|
563 | for (let i = 1 ; i < selectors.length; i++) {
|
564 | addedSelectors.push({
|
565 | selectorNode,
|
566 | node,
|
567 | customElementChunk: selectors[i],
|
568 | });
|
569 | }
|
570 | if (res.selectorAst.nodes.length === 1 && res.symbol) {
|
571 | return { _kind: 'css', meta: res.current, symbol: res.symbol };
|
572 | }
|
573 |
|
574 | return {
|
575 | _kind: 'css',
|
576 | meta,
|
577 | symbol: { _kind: 'element', name: '*' },
|
578 | };
|
579 | }
|
580 |
|
581 | let symbol = meta.mappedSymbols[name];
|
582 | let current = meta;
|
583 | while (!symbol) {
|
584 |
|
585 | const root = current.mappedSymbols[current.root];
|
586 | next = this.resolver.deepResolve(root[stylable_value_parsers_1.valueMapping.extends]);
|
587 | if (next && next._kind === 'css') {
|
588 | current = next.meta;
|
589 | symbol = next.meta.mappedSymbols[name];
|
590 | }
|
591 | else {
|
592 | break;
|
593 | }
|
594 | }
|
595 | if (symbol) {
|
596 | if (symbol._kind === 'class') {
|
597 | node.type = 'class';
|
598 | node.before = symbol[stylable_value_parsers_1.valueMapping.root] ? '' : ' ';
|
599 | next = this.resolver.deepResolve(symbol);
|
600 | if (symbol[stylable_value_parsers_1.valueMapping.global]) {
|
601 | node.type = 'selector';
|
602 | node.nodes = symbol[stylable_value_parsers_1.valueMapping.global] || [];
|
603 | this.addGlobalsToMeta(node.nodes, originMeta);
|
604 | }
|
605 | else {
|
606 | if (symbol.alias && !symbol[stylable_value_parsers_1.valueMapping.extends]) {
|
607 | if (next && next.meta && next.symbol) {
|
608 | node.name = this.scope(next.symbol.name, next.meta.namespace);
|
609 | }
|
610 | else {
|
611 |
|
612 | }
|
613 | }
|
614 | else {
|
615 | node.name = this.scope(symbol.name, current.namespace);
|
616 | }
|
617 | }
|
618 | if (next && next._kind === 'css') {
|
619 | return next;
|
620 | }
|
621 | }
|
622 | }
|
623 | else if (rule) {
|
624 | if (!native_reserved_lists_1.nativePseudoElements.includes(name) && !is_vendor_prefixed_1.default(name)) {
|
625 | this.diagnostics.warn(rule, exports.transformerWarnings.UNKNOWN_PSEUDO_ELEMENT(name), {
|
626 | word: name,
|
627 | });
|
628 | }
|
629 | }
|
630 | return { _kind: 'css', meta: current, symbol };
|
631 | }
|
632 | scope(name, namespace, delimiter = this.delimiter) {
|
633 | return namespace ? namespace + delimiter + name : name;
|
634 | }
|
635 | exportClasses(meta) {
|
636 | const locals = {};
|
637 | const metaParts = this.resolveMetaParts(meta);
|
638 | for (const [localName, resolved] of Object.entries(metaParts.class)) {
|
639 | const exportedClasses = this.getPartExports(resolved);
|
640 | locals[localName] = exportedClasses.join(' ');
|
641 | }
|
642 | return locals;
|
643 | }
|
644 |
|
645 | getPartExports(resolved) {
|
646 | const exportedClasses = [];
|
647 | let first = true;
|
648 | for (const { meta, symbol } of resolved) {
|
649 | if (!first && symbol[stylable_value_parsers_1.valueMapping.root]) {
|
650 | break;
|
651 | }
|
652 | first = false;
|
653 | if (symbol.alias && !symbol[stylable_value_parsers_1.valueMapping.extends]) {
|
654 | continue;
|
655 | }
|
656 | exportedClasses.push(this.scope(symbol.name, meta.namespace));
|
657 | }
|
658 | return exportedClasses;
|
659 | }
|
660 | scopeSelector2(originMeta, selector, _classesExport, _calcPaths = false, rule) {
|
661 | const context = new ScopeContext(originMeta, selector_utils_1.parseSelector(selector), rule || postcss.rule({ selector }));
|
662 | const targetSelectorAst = this.scopeSelectorAst(context);
|
663 | return {
|
664 | targetSelectorAst,
|
665 | selector: selector_utils_1.stringifySelector(targetSelectorAst),
|
666 | elements: context.elements,
|
667 | };
|
668 | }
|
669 | scopeSelectorAst(context) {
|
670 | const { originMeta, selectorAst } = context;
|
671 |
|
672 | const selectorListChunks = selector_utils_1.separateChunks2(selectorAst);
|
673 |
|
674 | context.metaParts = this.resolveMetaParts(originMeta);
|
675 |
|
676 | if (!context.currentAnchor) {
|
677 | context.initRootAnchor({
|
678 | name: originMeta.root,
|
679 | type: 'class',
|
680 | resolved: context.metaParts.class[originMeta.root],
|
681 | });
|
682 | }
|
683 |
|
684 | for (const selectorChunks of selectorListChunks) {
|
685 | context.elements.push([]);
|
686 | context.selectorIndex++;
|
687 | context.chunks = selectorChunks;
|
688 |
|
689 | for (const chunk of selectorChunks) {
|
690 | context.chunk = chunk;
|
691 |
|
692 | for (const node of chunk.nodes) {
|
693 | context.node = node;
|
694 |
|
695 | this.handleChunkNode(context);
|
696 | }
|
697 | }
|
698 | if (selectorListChunks.length - 1 > context.selectorIndex) {
|
699 | context.initRootAnchor({
|
700 | name: originMeta.root,
|
701 | type: 'class',
|
702 | resolved: context.metaParts.class[originMeta.root],
|
703 | });
|
704 | }
|
705 | }
|
706 | const outputAst = selector_utils_1.mergeChunks(selectorListChunks);
|
707 | context.additionalSelectors.forEach((addSelector) => outputAst.nodes.push(addSelector()));
|
708 | return outputAst;
|
709 | }
|
710 | handleChunkNode(context) {
|
711 | const { currentAnchor, metaParts, node, originMeta, transformGlobals, } = context;
|
712 | const { type, name } = node;
|
713 | if (type === 'class') {
|
714 | const resolved = metaParts.class[name] || [
|
715 |
|
716 | { _kind: 'css', meta: originMeta, symbol: { _kind: 'class', name } },
|
717 | ];
|
718 | context.setCurrentAnchor({ name, type: 'class', resolved });
|
719 | const { symbol, meta } = selector_utils_1.getOriginDefinition(resolved);
|
720 | this.scopeClassNode(symbol, meta, node, originMeta);
|
721 | }
|
722 | else if (type === 'element') {
|
723 | const resolved = metaParts.element[name] || [
|
724 |
|
725 | { _kind: 'css', meta: originMeta, symbol: { _kind: 'element', name } },
|
726 | ];
|
727 | context.setCurrentAnchor({ name, type: 'element', resolved });
|
728 |
|
729 | if (resolved && resolved.length > 1) {
|
730 | const { symbol, meta } = selector_utils_1.getOriginDefinition(resolved);
|
731 | this.scopeClassNode(symbol, meta, node, originMeta);
|
732 | }
|
733 | }
|
734 | else if (type === 'pseudo-element') {
|
735 | const len = currentAnchor.resolved.length;
|
736 | const lookupStartingPoint = len === 1 ? 0 : 1;
|
737 | let resolved;
|
738 | for (let i = lookupStartingPoint; i < len; i++) {
|
739 | const { symbol, meta } = currentAnchor.resolved[i];
|
740 | if (!symbol[stylable_value_parsers_1.valueMapping.root]) {
|
741 | continue;
|
742 | }
|
743 | const customSelector = meta.customSelectors[':--' + name];
|
744 | if (customSelector) {
|
745 | this.handleCustomSelector(customSelector, meta, context, name, node);
|
746 | return;
|
747 | }
|
748 | const requestedPart = meta.classes[name];
|
749 | if (symbol.alias || !requestedPart) {
|
750 |
|
751 | continue;
|
752 | }
|
753 | resolved = this.resolveMetaParts(meta).class[name];
|
754 |
|
755 | context.setCurrentAnchor({
|
756 | name,
|
757 | type: 'pseudo-element',
|
758 | resolved,
|
759 | });
|
760 | const resolvedPart = selector_utils_1.getOriginDefinition(resolved);
|
761 | node.before = resolvedPart.symbol[stylable_value_parsers_1.valueMapping.root] ? '' : ' ';
|
762 | this.scopeClassNode(resolvedPart.symbol, resolvedPart.meta, node, originMeta);
|
763 | break;
|
764 | }
|
765 | if (!resolved) {
|
766 |
|
767 | context.setCurrentAnchor({
|
768 | name,
|
769 | type: 'pseudo-element',
|
770 | resolved: [],
|
771 | });
|
772 | if (!native_reserved_lists_1.nativePseudoElements.includes(name) &&
|
773 | !is_vendor_prefixed_1.default(name) &&
|
774 | !this.isDuplicateStScopeDiagnostic(context)) {
|
775 | this.diagnostics.warn(context.rule, exports.transformerWarnings.UNKNOWN_PSEUDO_ELEMENT(name), {
|
776 | word: name,
|
777 | });
|
778 | }
|
779 | }
|
780 | }
|
781 | else if (type === 'pseudo-class') {
|
782 | let found = false;
|
783 | for (const { symbol, meta } of currentAnchor.resolved) {
|
784 | const states = symbol[stylable_value_parsers_1.valueMapping.states];
|
785 | if (states && hasOwnProperty.call(states, name)) {
|
786 | found = true;
|
787 | pseudo_states_1.setStateToNode(states, meta, name, node, meta.namespace, this.resolver, this.diagnostics, context.rule);
|
788 | break;
|
789 | }
|
790 | }
|
791 | if (!found &&
|
792 | !native_reserved_lists_1.nativePseudoClasses.includes(name) &&
|
793 | !is_vendor_prefixed_1.default(name) &&
|
794 | !this.isDuplicateStScopeDiagnostic(context)) {
|
795 | this.diagnostics.warn(context.rule, pseudo_states_1.stateErrors.UNKNOWN_STATE_USAGE(name), {
|
796 | word: name,
|
797 | });
|
798 | }
|
799 | }
|
800 | else if (type === 'nested-pseudo-class') {
|
801 | if (name === 'global') {
|
802 |
|
803 | if (transformGlobals) {
|
804 | node.type = 'selector';
|
805 | }
|
806 | }
|
807 | else {
|
808 | const nestedContext = context.createNestedContext({
|
809 | type: 'selectors',
|
810 | name: `${name}`,
|
811 | nodes: node.nodes,
|
812 | });
|
813 | this.scopeSelectorAst(nestedContext);
|
814 |
|
815 | context.elements[context.selectorIndex].push(...nestedContext.elements[0]);
|
816 | }
|
817 | }
|
818 | else if (type === 'invalid' && node.value === '&') {
|
819 | if ( originMeta.parent) {
|
820 | const origin = originMeta.mappedSymbols[originMeta.root];
|
821 | context.setCurrentAnchor({
|
822 | name: origin.name,
|
823 | type: 'class',
|
824 | resolved: metaParts.class[origin.name],
|
825 | });
|
826 | }
|
827 | }
|
828 | }
|
829 | isDuplicateStScopeDiagnostic(context) {
|
830 | var _a;
|
831 | const transformedScope = (_a = context.originMeta.transformedScopes) === null || _a === void 0 ? void 0 : _a[context.rule.stScopeSelector];
|
832 | if (transformedScope && context.chunks && context.chunk) {
|
833 | const currentChunkSelector = selector_utils_1.stringifySelector({
|
834 | type: 'selector',
|
835 | nodes: context.chunk.nodes,
|
836 | name: '',
|
837 | });
|
838 | const i = context.chunks.indexOf(context.chunk);
|
839 | for (const stScopeSelectorChunks of transformedScope) {
|
840 |
|
841 | if (i <= stScopeSelectorChunks.length) {
|
842 | for (const chunk of stScopeSelectorChunks) {
|
843 | const scopeChunkSelector = selector_utils_1.stringifySelector({
|
844 | type: 'selector',
|
845 | nodes: chunk.nodes,
|
846 | name: '',
|
847 | });
|
848 |
|
849 | if (scopeChunkSelector === currentChunkSelector) {
|
850 | return true;
|
851 | }
|
852 | }
|
853 | }
|
854 | }
|
855 | }
|
856 | return false;
|
857 | }
|
858 | handleCustomSelector(customSelector, meta, context, name, node) {
|
859 | const selectorListChunks = selector_utils_1.separateChunks2(selector_utils_1.parseSelector(customSelector));
|
860 | const hasSingleSelector = selectorListChunks.length === 1;
|
861 | removeFirstRootInEachSelectorChunk(selectorListChunks, meta);
|
862 | const internalContext = new ScopeContext(meta, selector_utils_1.mergeChunks(selectorListChunks), context.rule);
|
863 | const customAstSelectors = this.scopeSelectorAst(internalContext).nodes;
|
864 | customAstSelectors.forEach(trimLeftSelectorAst);
|
865 | if (hasSingleSelector && internalContext.currentAnchor) {
|
866 | context.setCurrentAnchor({
|
867 | name,
|
868 | type: 'pseudo-element',
|
869 | resolved: internalContext.currentAnchor.resolved,
|
870 | });
|
871 | }
|
872 | else {
|
873 |
|
874 | context.setCurrentAnchor({
|
875 | name,
|
876 | type: 'pseudo-element',
|
877 | resolved: anyElementAnchor(meta).resolved,
|
878 | });
|
879 | }
|
880 | Object.assign(node, customAstSelectors[0]);
|
881 |
|
882 | for (let i = 1; i < customAstSelectors.length; i++) {
|
883 | const selectorNode = context.selectorAst.nodes[context.selectorIndex];
|
884 | const nodeIndex = selectorNode.nodes.indexOf(node);
|
885 | context.additionalSelectors.push(lazyCreateSelector(customAstSelectors[i], selectorNode, nodeIndex));
|
886 | }
|
887 | }
|
888 | scopeClassNode(symbol, meta, node, originMeta) {
|
889 | if (symbol[stylable_value_parsers_1.valueMapping.global]) {
|
890 | const globalMappedNodes = symbol[stylable_value_parsers_1.valueMapping.global];
|
891 | node.type = 'selector';
|
892 | node.nodes = globalMappedNodes;
|
893 | this.addGlobalsToMeta(globalMappedNodes, originMeta);
|
894 | }
|
895 | else {
|
896 | node.type = 'class';
|
897 | node.name = this.scope(symbol.name, meta.namespace);
|
898 | }
|
899 | }
|
900 | resolveMetaParts(meta) {
|
901 | let metaParts = this.metaParts.get(meta);
|
902 | if (!metaParts) {
|
903 | const resolvedClasses = {};
|
904 | for (const className of Object.keys(meta.classes)) {
|
905 | resolvedClasses[className] = this.resolver.resolveExtends(meta, className, false, undefined, (res, extend) => {
|
906 | const decl = stylable_utils_1.findRule(meta.ast, '.' + className);
|
907 | if (decl) {
|
908 | if (res && res._kind === 'js') {
|
909 | this.diagnostics.error(decl, exports.transformerWarnings.CANNOT_EXTEND_JS(), {
|
910 | word: decl.value,
|
911 | });
|
912 | }
|
913 | else if (res && !res.symbol) {
|
914 | this.diagnostics.error(decl, exports.transformerWarnings.CANNOT_EXTEND_UNKNOWN_SYMBOL(extend.name), { word: decl.value });
|
915 | }
|
916 | else {
|
917 | this.diagnostics.error(decl, exports.transformerWarnings.IMPORT_ISNT_EXTENDABLE(), { word: decl.value });
|
918 | }
|
919 | }
|
920 | else {
|
921 | if (meta.classes[className] && meta.classes[className].alias) {
|
922 | meta.ast.walkRules(new RegExp('\\.' + className), (rule) => {
|
923 | this.diagnostics.error(rule, exports.transformerWarnings.UNKNOWN_IMPORT_ALIAS(className), { word: className });
|
924 | return false;
|
925 | });
|
926 | }
|
927 | }
|
928 | });
|
929 | }
|
930 | const resolvedElements = {};
|
931 | for (const k of Object.keys(meta.elements)) {
|
932 | resolvedElements[k] = this.resolver.resolveExtends(meta, k, true);
|
933 | }
|
934 | metaParts = { class: resolvedClasses, element: resolvedElements };
|
935 | this.metaParts.set(meta, metaParts);
|
936 | }
|
937 | return metaParts;
|
938 | }
|
939 | addDevRules(meta) {
|
940 | const metaParts = this.resolveMetaParts(meta);
|
941 | for (const [className, resolved] of Object.entries(metaParts.class)) {
|
942 | if (resolved.length > 1) {
|
943 | meta.outputAst.walkRules('.' + this.scope(className, meta.namespace), (rule) => {
|
944 | const a = resolved[0];
|
945 | const b = resolved[1];
|
946 | rule.after(selector_utils_1.createWarningRule(b.symbol.name, this.scope(b.symbol.name, b.meta.namespace), path_1.basename(b.meta.source), a.symbol.name, this.scope(a.symbol.name, a.meta.namespace), path_1.basename(a.meta.source), true));
|
947 | });
|
948 | }
|
949 | }
|
950 | }
|
951 | resetTransformProperties(meta) {
|
952 | meta.globals = {};
|
953 | meta.transformedScopes = null;
|
954 | return (meta.outputAst = meta.ast.clone());
|
955 | }
|
956 | }
|
957 | exports.StylableTransformer = StylableTransformer;
|
958 | function removeSTDirective(root) {
|
959 | const toRemove = [];
|
960 | root.walkRules((rule) => {
|
961 | if (rule.nodes && rule.nodes.length === 0) {
|
962 | toRemove.push(rule);
|
963 | return;
|
964 | }
|
965 | rule.walkDecls((decl) => {
|
966 | if (decl.prop.startsWith('-st-')) {
|
967 | toRemove.push(decl);
|
968 | }
|
969 | });
|
970 | if (rule.raws) {
|
971 | rule.raws = {
|
972 | after: '\n',
|
973 | };
|
974 | }
|
975 | });
|
976 | if (root.raws) {
|
977 | root.raws = {};
|
978 | }
|
979 | function removeRecursiveUpIfEmpty(node) {
|
980 | const parent = node.parent;
|
981 | node.remove();
|
982 | if (parent && parent.nodes && parent.nodes.length === 0) {
|
983 | removeRecursiveUpIfEmpty(parent);
|
984 | }
|
985 | }
|
986 | toRemove.forEach((node) => {
|
987 | removeRecursiveUpIfEmpty(node);
|
988 | });
|
989 | }
|
990 | exports.removeSTDirective = removeSTDirective;
|
991 | function validateScopes(transformer, meta) {
|
992 | const transformedScopes = {};
|
993 | for (const scope of meta.scopes) {
|
994 | const len = transformer.diagnostics.reports.length;
|
995 | const rule = postcss.rule({ selector: scope.params });
|
996 | const context = new ScopeContext(meta, selector_utils_1.parseSelector(rule.selector), rule);
|
997 | transformedScopes[rule.selector] = selector_utils_1.separateChunks2(transformer.scopeSelectorAst(context));
|
998 | const ruleReports = transformer.diagnostics.reports.splice(len);
|
999 | ruleReports.forEach(({ message, type, options: { word } = {} }) => {
|
1000 | if (type === 'error') {
|
1001 | transformer.diagnostics.error(scope, message, { word: word || scope.params });
|
1002 | }
|
1003 | else {
|
1004 | transformer.diagnostics.warn(scope, message, { word: word || scope.params });
|
1005 | }
|
1006 | });
|
1007 | }
|
1008 | return transformedScopes;
|
1009 | }
|
1010 | function removeFirstRootInEachSelectorChunk(selectorListChunks, meta) {
|
1011 | selectorListChunks.forEach((selectorChunks) => {
|
1012 | selectorChunks[0].nodes = selectorChunks[0].nodes.filter(({ type, name }) => {
|
1013 | return !(type === 'class' && name === meta.root);
|
1014 | });
|
1015 | });
|
1016 | }
|
1017 | function trimLeftSelectorAst(n, i = 0) {
|
1018 | if (n) {
|
1019 | if (n.type === 'spacing') {
|
1020 | n.value = '';
|
1021 | }
|
1022 | n.before = '';
|
1023 | trimLeftSelectorAst(n.nodes && n.nodes[0], i + 1);
|
1024 | if (i === 0) {
|
1025 | n.before = ' ';
|
1026 | }
|
1027 | }
|
1028 | }
|
1029 | function anyElementAnchor(meta) {
|
1030 | return {
|
1031 | type: 'element',
|
1032 | name: '*',
|
1033 | resolved: [{ _kind: 'css', meta, symbol: { _kind: 'element', name: '*' } }],
|
1034 | };
|
1035 | }
|
1036 | function lazyCreateSelector(customElementChunk, selectorNode, nodeIndex) {
|
1037 | if (nodeIndex === -1) {
|
1038 | throw new Error('not supported inside nested classes');
|
1039 | }
|
1040 | return () => {
|
1041 | const clone = lodash_clonedeep_1.default(selectorNode);
|
1042 | clone.nodes[nodeIndex].nodes = customElementChunk.nodes;
|
1043 | return clone;
|
1044 | };
|
1045 | }
|
1046 | class ScopeContext {
|
1047 | constructor(originMeta, selectorAst, rule) {
|
1048 | this.additionalSelectors = [];
|
1049 | this.selectorIndex = -1;
|
1050 | this.elements = [];
|
1051 | this.transformGlobals = false;
|
1052 | this.originMeta = originMeta;
|
1053 | this.selectorAst = selectorAst;
|
1054 | this.rule = rule;
|
1055 | }
|
1056 | initRootAnchor(anchor) {
|
1057 | this.currentAnchor = anchor;
|
1058 | }
|
1059 | setCurrentAnchor(anchor) {
|
1060 | if (this.selectorIndex !== undefined && this.selectorIndex !== -1) {
|
1061 | this.elements[this.selectorIndex].push(anchor);
|
1062 | }
|
1063 | this.currentAnchor = anchor;
|
1064 | }
|
1065 | createNestedContext(selectorAst) {
|
1066 | const ctx = new ScopeContext(this.originMeta, selectorAst, this.rule);
|
1067 | Object.assign(ctx, this);
|
1068 | ctx.selectorAst = selectorAst;
|
1069 | ctx.selectorIndex = -1;
|
1070 | ctx.elements = [];
|
1071 | ctx.additionalSelectors = [];
|
1072 | return ctx;
|
1073 | }
|
1074 | }
|
1075 |
|
\ | No newline at end of file |