@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.
- package/dist/_virtual/index10.js +2 -2
- package/dist/_virtual/index4.js +3 -2
- package/dist/_virtual/index4.js.map +1 -1
- package/dist/_virtual/index7.js +2 -3
- package/dist/_virtual/index7.js.map +1 -1
- package/dist/_virtual/index9.js +2 -2
- package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
- package/dist/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 +1 -1
- package/eslint/rules/prefer-sdk-components.cjs +0 -79
- package/eslint/rules/validate-manifest.cjs +86 -2
- package/generated-hook-maps/component-scopes-map.json +18 -0
- package/package.json +1 -1
package/dist/_virtual/index10.js
CHANGED
package/dist/_virtual/index4.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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":";;"}
|
package/dist/_virtual/index7.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
var i = r();
|
|
1
|
+
var r = {};
|
|
3
2
|
export {
|
|
4
|
-
|
|
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":";"}
|
package/dist/_virtual/index9.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __module as r } from "../../../../../../../_virtual/
|
|
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
|
-
'
|
|
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
|
-
|
|
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
|
+
}
|