@wix/origami-lint 1.34.31 → 1.34.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
// The "data-values" subject of the converter rules (converter 0.80 at c8ed79b): astValue.ts 99, 123, 133; parseJsx.ts 915, 1223,
|
|
2
2
|
// 1227, 1231, 1235, 1255, 1268, 1289, 1697; projectData.ts 214, 225, 281, 435, 443; enrichImage.ts 94, 105.
|
|
3
3
|
// Converter 0.81 (checked at 0.84, 2c0e21c): the tag's import, not a `type` attr, decides if a component takes data attributes; `type` is data.
|
|
4
|
+
// Tracker KIT-richText (converter 0.87): a rich text's type is Builder.RichText.
|
|
5
|
+
// Tracker OUR-003 (origami-transforms mediaValidation): an <Image>'s literal image uri is a Wix media id.
|
|
6
|
+
// Converter 0.87 (68c71e4): `children` is a data key.
|
|
4
7
|
import { isDeepStrictEqual } from 'node:util';
|
|
5
8
|
import { DATA_KEY, EVENT_ATTRIBUTE, RESERVED_ATTRIBUTES, RESERVED_ENTRY_KEYS, bare, getAttribute, getExpression, getKey, getName, getObject, getProperties, getCallbackKind, getProperty, isBound, isOneEditAway, isReadByImport, isReservedAttribute, ownerOf, } from "../../rules-api/index.js";
|
|
6
9
|
import { defineRule } from "./define-rule.js";
|
|
7
|
-
const CHECKS = ['attribute_without_braces', 'non_literal_data', 'duplicate_element_prop', 'invalid_element_props_object', 'invalid_element_props_entry', 'invalid_data_object', 'invalid_element_props_key', 'unsupported_element_props_key', 'mixed_data_wrapper', 'duplicate_data_key', 'bare_document_href', 'missing_document_id', 'inconsistent_image_url', 'misplaced_image_url_call'];
|
|
10
|
+
const CHECKS = ['attribute_without_braces', 'non_literal_data', 'duplicate_element_prop', 'invalid_element_props_object', 'invalid_element_props_entry', 'invalid_data_object', 'invalid_element_props_key', 'unsupported_element_props_key', 'mixed_data_wrapper', 'duplicate_data_key', 'bare_document_href', 'missing_document_id', 'inconsistent_image_url', 'misplaced_image_url_call', 'invalid_rich_text_type', 'invalid_image_media_id'];
|
|
8
11
|
/** The names the import never reads as data (dataKeys.ts). The React name is joined as the converter does: Wix Standards fails the build on it in any file. */
|
|
9
12
|
const EXCLUDED_DATA_KEYS = [
|
|
10
13
|
'_slotsId', '_mapId', 'hidden', 'class', 'title', 'role', 'tabindex', 'draggable', 'lang', 'dir', 'accesskey', 'contenteditable', 'spellcheck',
|
|
11
14
|
'translate', 'autofocus', 'inputmode', 'enterkeyhint', 'popover', 'inert', 'part', 'nonce', 'is', 'itemscope', 'itemtype', 'itemprop', 'itemid',
|
|
12
|
-
'itemref', 'tabIndex', 'contentEditable', 'accessKey', 'autoFocus', 'inputMode', 'enterKeyHint', 'spellCheck', '
|
|
15
|
+
'itemref', 'tabIndex', 'contentEditable', 'accessKey', 'autoFocus', 'inputMode', 'enterKeyHint', 'spellCheck', 'styleRef', 'key',
|
|
13
16
|
'ref', ['dangerouslySet', 'InnerHTML'].join(''), '__proto__', 'constructor', 'prototype',
|
|
14
17
|
];
|
|
15
18
|
/** An elementProps entry's name is a CSS identifier (parseJsx.ts 1231): it becomes a class. */
|
|
@@ -18,9 +21,14 @@ const CSS_IDENTIFIER = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/;
|
|
|
18
21
|
const RESERVED_OBJECT_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
19
22
|
/** An image object's `type` (enrichImage.ts). */
|
|
20
23
|
const IMAGE_TYPE = 'Builder.Image';
|
|
24
|
+
/** A rich text's `type`, and the key the export writes it under (projectData.ts rebuildRichText reads `html` only on this type). */
|
|
25
|
+
const RICH_TEXT_TYPE = 'Builder.RichText';
|
|
26
|
+
const RICH_TEXT_KEY = 'richText';
|
|
21
27
|
/** The media identifiers the export writes: an image's (`wix:image://v1/<uri>/<name>#originWidth=…`), a document's (`wix:document://v1/ugd/<file>/<name>`). */
|
|
22
28
|
const IMAGE_PREFIX = 'wix:image://v1/';
|
|
23
29
|
const DOCUMENT_PREFIX = 'wix:document://v1/';
|
|
30
|
+
/** A Wix media id, `117a96_1cff23187eeb4050a2a691bcdcad6502~mv2.jpg`: an account prefix, an underscore and a file name (mediaValidation/index.js). */
|
|
31
|
+
const WIX_MEDIA_ID = /^[A-Za-z0-9]+_[A-Za-z0-9_~.-]+\.[A-Za-z0-9]+$/;
|
|
24
32
|
/** The reserved attributes this rule reads as data. The strings are component-attributes', `className` and `bindings` the bindings rule's. */
|
|
25
33
|
const READ_AS_DATA = ['data', 'props', 'elementProps'];
|
|
26
34
|
const FULL = { menus: true, media: true, jsx: true };
|
|
@@ -41,8 +49,9 @@ export default defineRule({
|
|
|
41
49
|
},
|
|
42
50
|
});
|
|
43
51
|
// ── the callback's steps ──
|
|
44
|
-
/**
|
|
52
|
+
/** Any <Image> in the file: its media id, as the build checks it. Then a component written out under Structure: its data values, then what the import builds from the ones it read. */
|
|
45
53
|
function checkJSXElementDataValues(input) {
|
|
54
|
+
checkImageMediaId(input);
|
|
46
55
|
if (!input.rulesApi.getComponent(input.node) || !isReadByImport(input.node))
|
|
47
56
|
return;
|
|
48
57
|
const elementProps = getAttribute(input.node, 'elementProps');
|
|
@@ -54,6 +63,7 @@ function checkJSXElementDataValues(input) {
|
|
|
54
63
|
checkDataKeyOnce(input, read.has('data') ? checkDataCarrier(input) : null);
|
|
55
64
|
checkDocumentLinks(input, read);
|
|
56
65
|
checkImageUrls(input, read);
|
|
66
|
+
checkRichTextTypes(input, read);
|
|
57
67
|
}
|
|
58
68
|
// ── the steps ──
|
|
59
69
|
const say = ({ report }, node, check, message) => report({ check, loc: node.loc, message });
|
|
@@ -253,8 +263,8 @@ function hasDocumentId(identifier) {
|
|
|
253
263
|
function checkImageUrls(input, read) {
|
|
254
264
|
const urls = new Set();
|
|
255
265
|
const values = dataValues(input).filter(([, path]) => read.has(attributeOf(path)));
|
|
256
|
-
for (const [value, path] of values) {
|
|
257
|
-
for (const { node, path: at } of objectsIn(value, path)) {
|
|
266
|
+
for (const [value, path, key] of values) {
|
|
267
|
+
for (const { node, path: at } of objectsIn(value, path, key)) {
|
|
258
268
|
if (valueOf(getProperty(node, 'type')?.value ?? null) !== IMAGE_TYPE)
|
|
259
269
|
continue;
|
|
260
270
|
const url = getProperty(node, 'url');
|
|
@@ -274,6 +284,32 @@ function checkImageUrls(input, read) {
|
|
|
274
284
|
if (stray)
|
|
275
285
|
say(input, stray, 'misplaced_image_url_call', `${owner(input)} has \`media.getCroppedImageUrl(…)\` outside an image's url. Write it only as the \`url\` of a \`{ type: 'Builder.Image', … }\` object.`);
|
|
276
286
|
}
|
|
287
|
+
/** KIT-richText: with another `type`, or none, the import takes the rich text but keeps its text empty (live import, 2026-10-26). */
|
|
288
|
+
function checkRichTextTypes(input, read) {
|
|
289
|
+
const values = dataValues(input).filter(([, path]) => read.has(attributeOf(path)));
|
|
290
|
+
for (const [value, path, valueKey] of values) {
|
|
291
|
+
for (const { node, path: at, key } of objectsIn(value, path, valueKey)) {
|
|
292
|
+
if (key !== RICH_TEXT_KEY)
|
|
293
|
+
continue;
|
|
294
|
+
const type = getProperty(node, 'type');
|
|
295
|
+
if (type && valueOf(type.value) === RICH_TEXT_TYPE)
|
|
296
|
+
continue;
|
|
297
|
+
const written = type ? `has \`type: ${textOf(input)(type.value)}\`` : 'has no `type`';
|
|
298
|
+
say(input, type ?? node, 'invalid_rich_text_type', `${owner(input)}'s \`${at}\` ${written}. Write \`type: '${RICH_TEXT_TYPE}'\`: with another type, or none, the import keeps its text empty (tracker KIT-richText).`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/** OUR-003: `yarn build` fails on an <Image> (by tag name, anywhere in the file) whose literal `image.uri` isn't a Wix media id; the import takes it, but the image is broken. */
|
|
303
|
+
function checkImageMediaId(input) {
|
|
304
|
+
const image = getObject(getExpression(getAttribute(input.node, 'image')));
|
|
305
|
+
if (input.rulesApi.getTagName(input.node) !== 'Image' || !image)
|
|
306
|
+
return;
|
|
307
|
+
const uri = getProperty(image, 'uri');
|
|
308
|
+
const value = uri && bare(uri.value);
|
|
309
|
+
if (value?.type !== 'Literal' || typeof value.value !== 'string' || WIX_MEDIA_ID.test(value.value))
|
|
310
|
+
return;
|
|
311
|
+
say(input, value, 'invalid_image_media_id', `${owner(input)}'s \`image.uri\` '${value.value}' isn't a Wix media id, and \`yarn build\` fails on it. Run \`wix origami upload-media\` to upload it (tracker OUR-003).`);
|
|
312
|
+
}
|
|
277
313
|
function imageCallsIn(input, dataValuesRead, read) {
|
|
278
314
|
const values = dataValuesRead.map(([value]) => value);
|
|
279
315
|
const props = read.has('props') ? getExpression(getAttribute(input.node, 'props')) : null;
|
|
@@ -465,29 +501,30 @@ function derivedImageArguments(image) {
|
|
|
465
501
|
];
|
|
466
502
|
}
|
|
467
503
|
const sameArguments = (call, expected) => isDeepStrictEqual(call.arguments.map(valueOf), expected);
|
|
468
|
-
function objectsIn(node, path) {
|
|
504
|
+
function objectsIn(node, path, key) {
|
|
469
505
|
if (node === null)
|
|
470
506
|
return [];
|
|
471
507
|
const value = bare(node);
|
|
472
508
|
if (value.type === 'ArrayExpression')
|
|
473
|
-
return value.elements.flatMap((element, index) => (element && element.type !== 'SpreadElement' ? objectsIn(element, `${path}[${index}]
|
|
509
|
+
return value.elements.flatMap((element, index) => (element && element.type !== 'SpreadElement' ? objectsIn(element, `${path}[${index}]`, null) : []));
|
|
474
510
|
if (value.type !== 'ObjectExpression')
|
|
475
511
|
return [];
|
|
476
|
-
return [{ node: value, path }, ...getProperties(value).flatMap((property) => objectsIn(property.value, `${path}.${getKey(property)}
|
|
512
|
+
return [{ node: value, path, key }, ...getProperties(value).flatMap((property) => objectsIn(property.value, `${path}.${getKey(property)}`, getKey(property)))];
|
|
477
513
|
}
|
|
514
|
+
/** Each data value: the value, its path as a message writes it, and the key that holds it. */
|
|
478
515
|
function dataValues(input) {
|
|
479
516
|
const element = input.node;
|
|
480
|
-
const values = dataAttributes(input).map((attribute) => [getExpression(attribute), getName(attribute)]).filter(([value]) => value);
|
|
517
|
+
const values = dataAttributes(input).map((attribute) => [getExpression(attribute), getName(attribute), getName(attribute)]).filter(([value]) => value);
|
|
481
518
|
const data = getExpression(getAttribute(element, 'data'));
|
|
482
519
|
if (data)
|
|
483
|
-
values.push([data, 'data']);
|
|
520
|
+
values.push([data, 'data', 'data']);
|
|
484
521
|
for (const { entry, path } of getEntries(getObject(getExpression(getAttribute(element, 'elementProps'))))) {
|
|
485
522
|
const at = `elementProps.${path.join('.')}`;
|
|
486
523
|
for (const property of entryDataKeys(entry))
|
|
487
|
-
values.push([property.value, `${at}.${getKey(property)}
|
|
524
|
+
values.push([property.value, `${at}.${getKey(property)}`, getKey(property)]);
|
|
488
525
|
const entryData = getProperty(entry, 'data');
|
|
489
526
|
if (entryData)
|
|
490
|
-
values.push([entryData.value, `${at}.data
|
|
527
|
+
values.push([entryData.value, `${at}.data`, 'data']);
|
|
491
528
|
}
|
|
492
529
|
return values;
|
|
493
530
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/origami-lint",
|
|
3
|
-
"version": "1.34.
|
|
3
|
+
"version": "1.34.32",
|
|
4
4
|
"description": "Offline lint for an exported Origami site: lints the pages that changed since the last passing run",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
]
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
|
-
"falconPackageHash": "
|
|
55
|
+
"falconPackageHash": "85567c0c24100afc6ec763f714be471d941b2f00b00ec2182eb63324"
|
|
56
56
|
}
|