@shopify/shop-minis-react 0.2.2 → 0.2.3

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,5 +1,5 @@
1
- var e = { exports: {} };
1
+ var r = {};
2
2
  export {
3
- e as __module
3
+ r as __exports
4
4
  };
5
5
  //# sourceMappingURL=index10.js.map
@@ -1,5 +1,6 @@
1
- var r = {};
1
+ import { __require as r } from "../shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js";
2
+ var i = r();
2
3
  export {
3
- r as __exports
4
+ i as s
4
5
  };
5
6
  //# sourceMappingURL=index4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index4.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"index4.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -1,6 +1,5 @@
1
- import { __require as r } from "../shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js";
2
- var i = r();
1
+ var r = {};
3
2
  export {
4
- i as s
3
+ r as __exports
5
4
  };
6
5
  //# sourceMappingURL=index7.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index7.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"index7.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,5 +1,5 @@
1
- var r = {};
1
+ var e = { exports: {} };
2
2
  export {
3
- r as __exports
3
+ e as __module
4
4
  };
5
5
  //# sourceMappingURL=index9.js.map
@@ -1,4 +1,4 @@
1
- import { s as r } from "../../../../../../../../_virtual/index7.js";
1
+ import { s as r } from "../../../../../../../../_virtual/index4.js";
2
2
  function s() {
3
3
  return r.useSyncExternalStore(
4
4
  e,
@@ -1,4 +1,4 @@
1
- import { __exports as r } from "../../../../../../../../_virtual/index9.js";
1
+ import { __exports as r } from "../../../../../../../../_virtual/index10.js";
2
2
  import { __require as a } from "./dom.js";
3
3
  import { __require as o } from "./dom-parser.js";
4
4
  var i;
@@ -1,4 +1,4 @@
1
- import { __exports as i } from "../../../../../../_virtual/index4.js";
1
+ import { __exports as i } from "../../../../../../_virtual/index7.js";
2
2
  var c;
3
3
  function d() {
4
4
  if (c) return i;
@@ -1,4 +1,4 @@
1
- import { __module as r } from "../../../../../../../_virtual/index10.js";
1
+ import { __module as r } from "../../../../../../../_virtual/index9.js";
2
2
  import { __require as o } from "../cjs/use-sync-external-store-shim.production.js";
3
3
  import { __require as i } from "../cjs/use-sync-external-store-shim.development.js";
4
4
  var e;
@@ -12,7 +12,6 @@ module.exports = {
12
12
  category: 'Best Practices',
13
13
  recommended: true,
14
14
  },
15
- fixable: 'code',
16
15
  messages: {
17
16
  preferSdkComponent:
18
17
  'Use <{{sdkComponent}}> from @shopify/shop-minis-react instead of <{{nativeElement}}>. The SDK component provides optimized performance, consistent styling, and additional features.',
@@ -52,9 +51,6 @@ module.exports = {
52
51
  ...(options.components || {}),
53
52
  }
54
53
 
55
- // eslint-disable-next-line @shopify/prefer-module-scope-constants
56
- const SDK_PACKAGE = '@shopify/shop-minis-react'
57
-
58
54
  return {
59
55
  JSXOpeningElement(node) {
60
56
  const elementName = node.name.name
@@ -70,81 +66,6 @@ module.exports = {
70
66
  nativeElement: elementName,
71
67
  sdkComponent,
72
68
  },
73
- fix(fixer) {
74
- const sourceCode = context.getSourceCode()
75
- const openingElement = node
76
- const jsxElement = node.parent
77
-
78
- // Get the closing element if it exists
79
- const closingElement = jsxElement.closingElement
80
-
81
- const fixes = []
82
-
83
- // Fix opening tag
84
- const openingTagStart = openingElement.name.range[0]
85
- const openingTagEnd = openingElement.name.range[1]
86
- fixes.push(
87
- fixer.replaceTextRange(
88
- [openingTagStart, openingTagEnd],
89
- sdkComponent
90
- )
91
- )
92
-
93
- // Fix closing tag if it exists (not self-closing)
94
- if (closingElement) {
95
- const closingTagStart = closingElement.name.range[0]
96
- const closingTagEnd = closingElement.name.range[1]
97
- fixes.push(
98
- fixer.replaceTextRange(
99
- [closingTagStart, closingTagEnd],
100
- sdkComponent
101
- )
102
- )
103
- }
104
-
105
- // Add import if it doesn't exist
106
- const program = sourceCode.ast
107
- const imports = program.body.filter(
108
- importNode => importNode.type === 'ImportDeclaration'
109
- )
110
-
111
- // Check if we already import from the SDK
112
- const sdkImport = imports.find(
113
- importNode => importNode.source.value === SDK_PACKAGE
114
- )
115
-
116
- if (sdkImport) {
117
- // Check if this component is already imported
118
- const hasComponent = sdkImport.specifiers.some(
119
- spec =>
120
- spec.type === 'ImportSpecifier' &&
121
- spec.imported.name === sdkComponent
122
- )
123
-
124
- if (!hasComponent) {
125
- // Add component to existing import
126
- const lastSpecifier =
127
- sdkImport.specifiers[sdkImport.specifiers.length - 1]
128
- fixes.push(
129
- fixer.insertTextAfter(lastSpecifier, `, ${sdkComponent}`)
130
- )
131
- }
132
- } else {
133
- // Add new import statement
134
- const firstNode = program.body[0]
135
- const importStatement = `import {${sdkComponent}} from '${SDK_PACKAGE}'\n`
136
-
137
- if (firstNode) {
138
- fixes.push(fixer.insertTextBefore(firstNode, importStatement))
139
- } else {
140
- fixes.push(
141
- fixer.insertTextAfterRange([0, 0], importStatement)
142
- )
143
- }
144
- }
145
-
146
- return fixes
147
- },
148
69
  })
149
70
  }
150
71
  },
@@ -9,6 +9,14 @@ const path = require('path')
9
9
  // Load the hook-scopes map
10
10
  const hookScopesMap = require('../../generated-hook-maps/hook-scopes-map.json')
11
11
 
12
+ // Load the component-scopes map (may not exist yet)
13
+ let componentScopesMap = {}
14
+ try {
15
+ componentScopesMap = require('../../generated-hook-maps/component-scopes-map.json')
16
+ } catch (err) {
17
+ // Component scopes map doesn't exist yet, that's okay
18
+ }
19
+
12
20
  // Module-level cache for manifest.json to avoid repeated file I/O
13
21
  // ESLint reuses the same rule module across all files, so this cache
14
22
  // persists for the entire lint run, dramatically improving performance
@@ -73,7 +81,7 @@ module.exports = {
73
81
  fixable: 'code',
74
82
  messages: {
75
83
  missingScope:
76
- 'Hook "{{hookName}}" requires scope "{{scope}}" in src/manifest.json. Add "{{scope}}" to the "scopes" array.',
84
+ '{{source}} requires scope "{{scope}}" in src/manifest.json. Add "{{scope}}" to the "scopes" array.',
77
85
  missingPermission:
78
86
  '{{reason}} requires permission "{{permission}}" in src/manifest.json. Add "{{permission}}" to the "permissions" array.',
79
87
  missingTrustedDomain:
@@ -93,6 +101,7 @@ module.exports = {
93
101
  let manifest = null
94
102
  let manifestParseError = null
95
103
  const usedHooks = new Set()
104
+ const usedComponents = new Set()
96
105
  const requiredPermissions = new Set()
97
106
  const requiredDomains = new Set()
98
107
  const fixedIssues = new Set()
@@ -171,6 +180,35 @@ module.exports = {
171
180
  })
172
181
  })
173
182
  }
183
+
184
+ // Check if it's a component that requires scopes
185
+ // We need to check all possible paths where the component might be defined
186
+ const possibleComponentPaths = Object.keys(
187
+ componentScopesMap
188
+ ).filter(componentPath => {
189
+ // Extract the component name from the path (e.g., "commerce/add-to-cart" -> "AddToCartButton")
190
+ // We'll check if the import name matches common component naming patterns
191
+ const pathParts = componentPath.split('/')
192
+ const fileName = pathParts[pathParts.length - 1]
193
+ // Convert kebab-case or snake_case to PascalCase
194
+ const componentName = fileName
195
+ .split(/[-_]/)
196
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
197
+ .join('')
198
+ return (
199
+ componentName === importedName || fileName === importedName
200
+ )
201
+ })
202
+
203
+ if (possibleComponentPaths.length > 0) {
204
+ possibleComponentPaths.forEach(componentPath => {
205
+ usedComponents.add({
206
+ path: componentPath,
207
+ name: importedName,
208
+ node,
209
+ })
210
+ })
211
+ }
174
212
  }
175
213
  })
176
214
  }
@@ -408,6 +446,7 @@ module.exports = {
408
446
  if (
409
447
  manifestParseError &&
410
448
  (usedHooks.size > 0 ||
449
+ usedComponents.size > 0 ||
411
450
  requiredPermissions.size > 0 ||
412
451
  requiredDomains.size > 0)
413
452
  ) {
@@ -447,6 +486,47 @@ module.exports = {
447
486
  })
448
487
  })
449
488
 
489
+ // Check scopes for components
490
+ usedComponents.forEach(
491
+ ({path: componentPath, name: componentName, node}) => {
492
+ const componentData = componentScopesMap[componentPath]
493
+ if (
494
+ !componentData ||
495
+ !componentData.scopes ||
496
+ componentData.scopes.length === 0
497
+ ) {
498
+ return
499
+ }
500
+
501
+ const requiredScopes = componentData.scopes
502
+
503
+ if (!manifest) {
504
+ issues.push({
505
+ messageId: 'manifestNotFound',
506
+ data: {
507
+ hookName: componentName,
508
+ scopes: requiredScopes.join(', '),
509
+ },
510
+ })
511
+ return
512
+ }
513
+
514
+ const manifestScopes = manifest.scopes || []
515
+
516
+ requiredScopes.forEach(requiredScope => {
517
+ if (!manifestScopes.includes(requiredScope)) {
518
+ issues.push({
519
+ type: 'scope',
520
+ scope: requiredScope,
521
+ componentName,
522
+ componentPath,
523
+ node,
524
+ })
525
+ }
526
+ })
527
+ }
528
+ )
529
+
450
530
  // Check permissions
451
531
  requiredPermissions.forEach(({permission, reason, node}) => {
452
532
  if (!manifest) {
@@ -513,11 +593,15 @@ module.exports = {
513
593
 
514
594
  // Only report if not fixed
515
595
  if (!fixedIssues.has(issueKey)) {
596
+ // Determine if this is from a hook or component
597
+ const sourceName = issue.hookName || issue.componentName
598
+ const sourceType = issue.hookName ? 'Hook' : 'Component'
599
+
516
600
  context.report({
517
601
  loc: {line: 1, column: 0},
518
602
  messageId: 'missingScope',
519
603
  data: {
520
- hookName: issue.hookName,
604
+ source: `${sourceType} "${sourceName}"`,
521
605
  scope: issue.scope,
522
606
  },
523
607
  })
@@ -0,0 +1,18 @@
1
+ {
2
+ "commerce/product-card": {
3
+ "scopes": [
4
+ "product_list:write"
5
+ ],
6
+ "hooks": [
7
+ "useSavedProductsActions"
8
+ ]
9
+ },
10
+ "commerce/product-link": {
11
+ "scopes": [
12
+ "product_list:write"
13
+ ],
14
+ "hooks": [
15
+ "useSavedProductsActions"
16
+ ]
17
+ }
18
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shopify/shop-minis-react",
3
3
  "license": "SEE LICENSE IN LICENSE.txt",
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "sideEffects": false,
6
6
  "type": "module",
7
7
  "engines": {