@wix/origami-lint 1.34.9 → 1.34.11

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,17 +1,17 @@
1
- import allowedImports from "./allowed-imports.js";
2
- import applyAndPresets from "./apply-and-presets.js";
3
- import bindings from "./bindings.js";
4
- import breakpoints from "./breakpoints.js";
5
- import cssFileNesting from "./css-file-nesting.js";
6
- import cssLayoutProperties from "./css-layout-properties.js";
7
- import cssSelectors from "./css-selectors.js";
8
- import dataValues from "./data-values.js";
9
- import globalStyles from "./global-styles.js";
10
- import noAbsoluteSection from "./no-absolute-section.js";
11
- import pageStructure from "./page-structure.js";
12
- import slotAttributes from "./slot-attributes.js";
13
- import themeFontIds from "./theme-font-ids.js";
14
- import themeRoot from "./theme-root.js";
15
- import noFreeCode from "./no-free-code.js";
1
+ import allowedImports from "./site-app/allowed-imports.js";
2
+ import applyAndPresets from "./site-app/apply-and-presets.js";
3
+ import bindings from "./site-app/bindings.js";
4
+ import breakpoints from "./site-app/breakpoints.js";
5
+ import cssFileNesting from "./site-app/css-file-nesting.js";
6
+ import cssLayoutProperties from "./site-app/css-layout-properties.js";
7
+ import cssSelectors from "./site-app/css-selectors.js";
8
+ import dataValues from "./site-app/data-values.js";
9
+ import globalStyles from "./site-app/global-styles.js";
10
+ import noAbsoluteSection from "./site-app/no-absolute-section.js";
11
+ import pageStructure from "./site-app/page-structure.js";
12
+ import slotAttributes from "./site-app/slot-attributes.js";
13
+ import themeFontIds from "./site-app/theme-font-ids.js";
14
+ import themeRoot from "./site-app/theme-root.js";
15
+ import noFreeCode from "./site-app/no-free-code.js";
16
16
  export const ruleRegistry = [allowedImports, noFreeCode, pageStructure, noAbsoluteSection, themeFontIds, slotAttributes, themeRoot, breakpoints, cssFileNesting, globalStyles, applyAndPresets, bindings, dataValues, cssLayoutProperties, cssSelectors];
17
- export { defineRule } from "./define-rule.js";
17
+ export { defineRule } from "./site-app/define-rule.js";
@@ -0,0 +1,2 @@
1
+ // What every rule has, whatever it runs on: a page rule (`Rule`, in site-app/) or a project rule (`ProjectRule`, in project/).
2
+ export {};
@@ -2,9 +2,9 @@
2
2
  // converter 0.80 a page's interactions live in servicesConfigs/interactions.ts, and structure.jsx can't use these packages (0.86
3
3
  // refuses a tag from them, parseJsx.ts:471, identity.ts:80; a call is free code, and fn() / ctx() refuse them as a source).
4
4
  import path from 'node:path';
5
- import { CSS_FILES, JSX_FILES } from "../page-model/index.js";
6
- import { BUILDERS, RUNTIME_ONLY_BUILDERS, RUNTIME_PACKAGE } from "../rules-api/index.js";
7
- import { GLOBAL_COMPONENTS, GLOBAL_COMPONENTS_FOLDER, GLOBAL_FOLDER, SITE_SRC } from "../shared/site-files-tree-mapping.js";
5
+ import { CSS_FILES, JSX_FILES } from "../../page-model/index.js";
6
+ import { BUILDERS, RUNTIME_ONLY_BUILDERS, RUNTIME_PACKAGE } from "../../rules-api/index.js";
7
+ import { GLOBAL_COMPONENTS, GLOBAL_COMPONENTS_FOLDER, GLOBAL_FOLDER, SITE_SRC } from "../../shared/site-files-tree-mapping.js";
8
8
  import { defineRule } from "./define-rule.js";
9
9
  /** The interactions packages, which the import reserves: structure.jsx imports nothing from them. Matched by source, not by name: `Interact` from @site/site-code is the site's own. */
10
10
  const INTERACTIONS_PACKAGES = ['interactReact', 'interact', 'motionPresets'];
@@ -1,4 +1,4 @@
1
- import { createCssFileReporter, describeAtRule, getCssChildren, getDashedId, getSegmentClass, getSelectorSegments, isPresetProperty, isSafeCssIdentifier } from "../rules-api/index.js";
1
+ import { createCssFileReporter, describeAtRule, getCssChildren, getDashedId, getSegmentClass, getSelectorSegments, isPresetProperty, isSafeCssIdentifier } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  export default defineRule({
4
4
  name: 'apply-and-presets',
@@ -1,4 +1,4 @@
1
- import { BUILDERS, CALLBACK_PROP, EVENT_ATTRIBUTE, RESERVED_ENTRY_KEYS, RUNTIME_PACKAGE, TRANSFORM_BUILDERS, TYPED_BUILDERS, bare, getAttribute, getExpression, getKey, getCallbackKind, getName, getObject, getParamReadKind, getProperties, getProperty, isBound, isReservedAttribute, isStructure, ownerOf, } from "../rules-api/index.js";
1
+ import { BUILDERS, CALLBACK_PROP, EVENT_ATTRIBUTE, RESERVED_ENTRY_KEYS, RUNTIME_PACKAGE, TRANSFORM_BUILDERS, TYPED_BUILDERS, bare, getAttribute, getExpression, getKey, getCallbackKind, getName, getObject, getParamReadKind, getProperties, getProperty, isBound, isReservedAttribute, isStructure, ownerOf, } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  /** The converter's reserved sources (identity.ts `isReservedRegistry`) and the DM's own `document`: no fn()/ctx() names. */
4
4
  const RESERVED_FOR_BINDINGS = ['runtime', 'interactReact', 'interact', 'motionPresets', 'menu', 'document', 'core'];
@@ -63,7 +63,7 @@ function checkComponentBindings(at) {
63
63
  if (!rulesApi.hasDataAttributes(node))
64
64
  return;
65
65
  boundPropOnce(at);
66
- for (const [name, , value] of boundAttributes(node))
66
+ for (const [name, , value] of boundAttributes(node, at.input.rulesApi))
67
67
  prop(at, name, value, true);
68
68
  }
69
69
  /** One elementProps entry: its `bindings`, and the keys it binds. */
@@ -80,19 +80,48 @@ function checkEntryBindings(at, entry) {
80
80
  if (className)
81
81
  classNameConditions(entryAt, className, '`className`');
82
82
  entryPropBoundOnce(entryAt, object);
83
- for (const [key, property] of boundEntryKeys(object))
83
+ for (const [key, property] of boundEntryKeys(object, at.input.rulesApi))
84
84
  prop(entryAt, key, property.value, true);
85
85
  }
86
86
  /** A builder call in a structure attribute: it is imported, and so are the names it uses. */
87
87
  function checkCallExpressionImports(input) {
88
88
  if (input.node.callee.type !== 'Identifier' || !isInStructureAttribute(input.node))
89
89
  return;
90
+ if (isSvgCall(input.node, input.rulesApi))
91
+ return checkSvgArguments(input);
90
92
  if (!isBuilderImported(input, input.node.callee))
91
93
  return;
92
94
  const ctxHook = bindingNameImported(input, input.node.callee.name);
93
95
  if (ctxHook)
94
96
  ctxSourceHasName(input, ctxHook);
95
97
  }
98
+ function isSvgCall(node, rulesApi) {
99
+ if (node.callee.type !== 'Identifier')
100
+ return false;
101
+ const found = rulesApi.getImport(node.callee);
102
+ return found?.kind === 'runtime' && found.imported === 'svg';
103
+ }
104
+ function checkSvgArguments({ node, rulesApi, report }) {
105
+ const [url, uri] = node.arguments;
106
+ if (node.arguments.length === 2 && uri.type === 'Literal' && typeof uri.value === 'string' &&
107
+ url.type === 'MemberExpression' && !url.computed && url.property.type === 'Identifier' && url.property.name === 'url') {
108
+ const call = url.object;
109
+ if (call.type === 'CallExpression' && call.arguments.length === 1 &&
110
+ call.callee.type === 'MemberExpression' && !call.callee.computed &&
111
+ call.callee.property.type === 'Identifier' && call.callee.property.name === 'getShapeUrl' &&
112
+ call.callee.object.type === 'Identifier') {
113
+ const media = rulesApi.getImport(call.callee.object);
114
+ const [identifier] = call.arguments;
115
+ if (media?.kind === 'sdk' && media.imported === 'media' &&
116
+ identifier.type === 'Literal' && identifier.value === `wix:image://v1/${uri.value}`)
117
+ return;
118
+ }
119
+ }
120
+ report({
121
+ loc: node.loc,
122
+ message: tracked("An SVG media expression must be `svg(media.getShapeUrl('wix:image://v1/<uri>').url, '<uri>')`, with matching URI strings, svg imported from '@wix/origami-runtime' and media from '@wix/sdk'.", 'CONV-vectorArtExpression.ts:44'),
123
+ });
124
+ }
96
125
  /** Each builder's own row: the shape of its call, a key written twice in it. */
97
126
  const ROW_OF_BUILDER = { all: 'CONV-decode.ts:196', callback: 'CONV-astValue.ts:287', ctx: 'CONV-astValue.ts:276', fn: 'CONV-astValue.ts:287', is: 'CONV-decode.ts:172', raw: 'CONV-decode.ts:28' };
98
127
  const tracked = (message, row) => `${message.slice(0, -1)} (tracker ${row}).`;
@@ -152,7 +181,7 @@ function boundPropOnce(at) {
152
181
  const props = getBindingsProps(bindings && getExpression(bindings), rulesApi);
153
182
  if (!props)
154
183
  return;
155
- for (const [name, attribute] of boundAttributes(node)) {
184
+ for (const [name, attribute] of boundAttributes(node, rulesApi)) {
156
185
  const where = props.raw ? `bindings={raw({ properties: { ${name} } })}` : `bindings={{ props: { ${name} } }}`;
157
186
  if (props.names.has(name))
158
187
  say(at, attribute, `${at.owner} binds \`${name}\` twice: in \`${name}={…}\` and in \`${where}\`. Keep one.`, 'CONV-parseJsx.ts:753');
@@ -162,7 +191,7 @@ function entryPropBoundOnce(at, entry) {
162
191
  const props = getBindingsProps(getProperty(entry, 'bindings')?.value, at.input.rulesApi);
163
192
  if (!props)
164
193
  return;
165
- for (const [key, property] of boundEntryKeys(entry)) {
194
+ for (const [key, property] of boundEntryKeys(entry, at.input.rulesApi)) {
166
195
  const where = props.raw ? `bindings: raw({ properties: { ${key} } })` : `bindings.props.${key}`;
167
196
  if (props.names.has(key))
168
197
  say(at, property, `${at.owner} binds \`${key}\` twice: in \`${key}: …\` and in its \`${where}\`. Keep one.`, 'CONV-parseJsx.ts:753');
@@ -646,19 +675,19 @@ function runsAtRender(at, node) {
646
675
  return isInvoke(node) || getParamReadKind(node) ? describeIn(at, node) : null;
647
676
  }
648
677
  /** The data attributes bound to a value the import reads as a binding; not an `on…` attribute, which it doesn't read at all (OUR-031 reports it). */
649
- function boundAttributes(element) {
678
+ function boundAttributes(element, rulesApi) {
650
679
  return element.openingElement.attributes.flatMap((attribute) => {
651
680
  if (attribute.type !== 'JSXAttribute' || isReservedAttribute(getName(attribute)) || EVENT_ATTRIBUTE.test(getName(attribute)))
652
681
  return [];
653
682
  const value = getExpression(attribute);
654
- return isBound(value) ? [[getName(attribute), attribute, value]] : [];
683
+ return isBound(value) && !(value.type === 'CallExpression' && isSvgCall(value, rulesApi)) ? [[getName(attribute), attribute, value]] : [];
655
684
  });
656
685
  }
657
- function boundEntryKeys(entry) {
686
+ function boundEntryKeys(entry, rulesApi) {
658
687
  return getProperties(entry).flatMap((property) => {
659
688
  const key = getKey(property);
660
689
  const value = bare(property.value);
661
- return key !== null && !RESERVED_ENTRY_KEYS.includes(key) && isBound(value) ? [[key, property]] : [];
690
+ return key !== null && !RESERVED_ENTRY_KEYS.includes(key) && isBound(value) && !(value.type === 'CallExpression' && isSvgCall(value, rulesApi)) ? [[key, property]] : [];
662
691
  });
663
692
  }
664
693
  function getBindingsProps(node, rulesApi) {
@@ -1,4 +1,4 @@
1
- import { describeAtRule, reportCssProblem } from "../rules-api/index.js";
1
+ import { describeAtRule, reportCssProblem } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  /** The converter's shape for a declaration's name: a dashed identifier. */
4
4
  const DASHED_NAME = /^--[A-Za-z0-9_-]+$/;
@@ -1,4 +1,4 @@
1
- import { BREAKPOINT_AT_RULES, createCssFileReporter, describeAtRule, getCssChildren, ORIGAMI_TARGETS, PREAMBLE_LAYER } from "../rules-api/index.js";
1
+ import { BREAKPOINT_AT_RULES, createCssFileReporter, describeAtRule, getCssChildren, ORIGAMI_TARGETS, PREAMBLE_LAYER } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  /** The import's leading-import line (cssFileParser.ts `IMPORT_LINE_PATTERN`). */
4
4
  const IMPORT_LINE = /^@import '([^']+)';$/;
@@ -1,4 +1,4 @@
1
- import { CONTENT_TARGET, createCssFileReporter, getName, LAYER_ATTRIBUTES, OVERFLOW_TARGET } from "../rules-api/index.js";
1
+ import { CONTENT_TARGET, createCssFileReporter, getName, LAYER_ATTRIBUTES, OVERFLOW_TARGET } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  // ── the tables (propSpecs.ts) ──
4
4
  const keywords = (...values) => ({ values, allowed: values.join(', ') });
@@ -1,4 +1,4 @@
1
- import { BREAKPOINT_AT_RULES, createCssFileReporter, getSelectorSegments, isCssIdentifier, isSafeCssIdentifier, PREAMBLE_LAYER, readSegment } from "../rules-api/index.js";
1
+ import { BREAKPOINT_AT_RULES, createCssFileReporter, getSelectorSegments, isCssIdentifier, isSafeCssIdentifier, PREAMBLE_LAYER, readSegment } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  /** The at-rules whose rules the import reads: breakpoint blocks, and a `@mixin`'s `@result`. */
4
4
  const READ_AT_RULES = [...BREAKPOINT_AT_RULES, 'result'];
@@ -2,7 +2,7 @@
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
4
  import { isDeepStrictEqual } from 'node:util';
5
- import { DATA_KEY, EVENT_ATTRIBUTE, RESERVED_ATTRIBUTES, RESERVED_ENTRY_KEYS, bare, getAttribute, getExpression, getKey, getName, getObject, getProperties, getCallbackKind, getProperty, isBound, isOneEditAway, isReservedAttribute, isStructure, ownerOf, } from "../rules-api/index.js";
5
+ import { DATA_KEY, EVENT_ATTRIBUTE, RESERVED_ATTRIBUTES, RESERVED_ENTRY_KEYS, bare, getAttribute, getExpression, getKey, getName, getObject, getProperties, getCallbackKind, getProperty, isBound, isOneEditAway, isReservedAttribute, isStructure, ownerOf, } from "../../rules-api/index.js";
6
6
  import { defineRule } from "./define-rule.js";
7
7
  /** 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. */
8
8
  const EXCLUDED_DATA_KEYS = [
@@ -1,4 +1,4 @@
1
- import { BREAKPOINT_AT_RULES, createCssFileReporter, describeAtRule, getCssChildren, getDashedId, getSegmentClass, getSelectorSegments, isCssIdentifier, isSafeCssIdentifier } from "../rules-api/index.js";
1
+ import { BREAKPOINT_AT_RULES, createCssFileReporter, describeAtRule, getCssChildren, getDashedId, getSegmentClass, getSelectorSegments, isCssIdentifier, isSafeCssIdentifier } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  export default defineRule({
4
4
  name: 'global-styles',
@@ -1,4 +1,4 @@
1
- import { SECTIONS } from "../rules-api/index.js";
1
+ import { SECTIONS } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  export default defineRule({
4
4
  name: 'no-absolute-section',
@@ -1,4 +1,4 @@
1
- import { STRUCTURE } from "../rules-api/index.js";
1
+ import { STRUCTURE } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  const FREE_CODE = 'The import refuses this statement: a structure file holds only imports, `const` / `let` / `var` declarations and `export const Structure = () => …` (tracker CONV-parseJsx.ts:287).';
4
4
  const STRUCTURE_SHAPE = 'Declare Structure once, as `export const Structure = () => ( <Page …/> )`: an exported const arrow that returns the root element directly, with no `{ return … }`, fragment or condition around it (tracker CONV-parseJsx.ts:335).';
@@ -19,8 +19,8 @@
19
19
  // <Interaction> is no wrapper since converter 0.80 (interactions live in servicesConfigs/interactions.ts): allowed-imports
20
20
  // reports the tag, and it stays transparent here so the elements inside it aren't reported too. Context-provider wrappers join
21
21
  // once the page model lists the providers.
22
- import { FILE_START } from "../page-model/index.js";
23
- import { LAYER_ATTRIBUTES, SECTIONS, WRAPPERS } from "../rules-api/index.js";
22
+ import { FILE_START } from "../../page-model/index.js";
23
+ import { LAYER_ATTRIBUTES, SECTIONS, WRAPPERS } from "../../rules-api/index.js";
24
24
  import { defineRule } from "./define-rule.js";
25
25
  export default defineRule({
26
26
  name: 'page-structure',
@@ -5,7 +5,7 @@
5
5
  // - the root element has no `slot=`: it has no owner to be a slot of (CONV-fromBundle.ts:1312);
6
6
  // - two children of one owner don't share a slot key (CONV-slots.ts:555).
7
7
  // Children are the element children and the layer elements (backgroundLayer, pinnedLayer); <Remove> and <Interaction> are transparent.
8
- import { LAYER_ATTRIBUTES, WRAPPERS } from "../rules-api/index.js";
8
+ import { LAYER_ATTRIBUTES, WRAPPERS } from "../../rules-api/index.js";
9
9
  import { defineRule } from "./define-rule.js";
10
10
  export default defineRule({
11
11
  name: 'slot-attributes',
@@ -5,7 +5,7 @@
5
5
  // theme.css isn't a file oxlint lints, so the rule runs on each linted page (a theme change lints every page) and
6
6
  // marks the page's start; the message names the token's line in theme.css.
7
7
  import { fonts } from '@wix/origami-fonts';
8
- import { reportCssProblem } from "../rules-api/index.js";
8
+ import { reportCssProblem } from "../../rules-api/index.js";
9
9
  import { defineRule } from "./define-rule.js";
10
10
  const FONT_TOKEN = /^--wst-[\w-]+-font$/;
11
11
  // The size in a `font` shorthand, with its optional line height: the family follows it. The number's quantifiers
@@ -1,4 +1,4 @@
1
- import { describeAtRule, isPresetProperty, PREAMBLE_LAYER, reportCssProblem } from "../rules-api/index.js";
1
+ import { describeAtRule, isPresetProperty, PREAMBLE_LAYER, reportCssProblem } from "../../rules-api/index.js";
2
2
  import { defineRule } from "./define-rule.js";
3
3
  const SPX_PROPS = ['--spx-ref-width', '--spx-resolver-type'];
4
4
  const SPX_RESOLVERS = ['scale', 'fixed'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/origami-lint",
3
- "version": "1.34.9",
3
+ "version": "1.34.11",
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": "e6f85d6cff1c44bc12c185e0e668b0a6918157b33ebab5a72193f8e7"
55
+ "falconPackageHash": "6105be6143086aff6e6d4d5ab523a01350826d77e8c206618d240ec2"
56
56
  }