@shopify/shop-minis-react 0.3.0 → 0.3.1
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/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/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/config.cjs +6 -0
- package/eslint/index.cjs +2 -0
- package/eslint/rules/prefer-sdk-hooks.cjs +131 -0
- package/package.json +4 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __module as r } from "../../../../../../../_virtual/
|
|
1
|
+
import { __module as r } from "../../../../../../../_virtual/index11.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;
|
package/eslint/config.cjs
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
// Import the plugin directly so consumers don't need to install it separately
|
|
10
|
+
const reactPlugin = require('eslint-plugin-react')
|
|
11
|
+
|
|
10
12
|
const shopMinisPlugin = require('./index.cjs')
|
|
11
13
|
|
|
12
14
|
module.exports = {
|
|
@@ -23,10 +25,14 @@ module.exports = {
|
|
|
23
25
|
},
|
|
24
26
|
plugins: {
|
|
25
27
|
'shop-minis': shopMinisPlugin,
|
|
28
|
+
react: reactPlugin,
|
|
26
29
|
},
|
|
27
30
|
rules: {
|
|
31
|
+
'no-console': 'warn',
|
|
32
|
+
'react/no-danger': 'error',
|
|
28
33
|
'shop-minis/no-internal-imports': 'error',
|
|
29
34
|
'shop-minis/prefer-sdk-components': 'warn',
|
|
35
|
+
'shop-minis/prefer-sdk-hooks': 'warn',
|
|
30
36
|
'shop-minis/validate-manifest': 'error',
|
|
31
37
|
},
|
|
32
38
|
}
|
package/eslint/index.cjs
CHANGED
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
const noInternalImports = require('./rules/no-internal-imports.cjs')
|
|
8
8
|
const preferSdkComponents = require('./rules/prefer-sdk-components.cjs')
|
|
9
|
+
const preferSdkHooks = require('./rules/prefer-sdk-hooks.cjs')
|
|
9
10
|
const validateManifest = require('./rules/validate-manifest.cjs')
|
|
10
11
|
|
|
11
12
|
module.exports = {
|
|
12
13
|
rules: {
|
|
13
14
|
'no-internal-imports': noInternalImports,
|
|
14
15
|
'prefer-sdk-components': preferSdkComponents,
|
|
16
|
+
'prefer-sdk-hooks': preferSdkHooks,
|
|
15
17
|
'validate-manifest': validateManifest,
|
|
16
18
|
},
|
|
17
19
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule to prefer SDK hooks over native browser APIs
|
|
3
|
+
* @fileoverview Enforce using Shop Minis SDK hooks instead of native browser APIs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
meta: {
|
|
8
|
+
type: 'suggestion',
|
|
9
|
+
docs: {
|
|
10
|
+
description:
|
|
11
|
+
'Prefer Shop Minis SDK hooks over native browser APIs for better compatibility and functionality',
|
|
12
|
+
category: 'Best Practices',
|
|
13
|
+
recommended: true,
|
|
14
|
+
},
|
|
15
|
+
messages: {
|
|
16
|
+
preferAsyncStorage:
|
|
17
|
+
'Use useAsyncStorage from @shopify/shop-minis-react instead of localStorage. The SDK hook provides async storage that works reliably in the Shop mini-app environment.',
|
|
18
|
+
preferSecureStorage:
|
|
19
|
+
'Use useSecureStorage from @shopify/shop-minis-react instead of localStorage for sensitive data. The SDK hook provides encrypted storage.',
|
|
20
|
+
},
|
|
21
|
+
schema: [
|
|
22
|
+
{
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
apis: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
description: 'Map of native APIs to SDK hooks',
|
|
28
|
+
additionalProperties: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
additionalProperties: false,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
create(context) {
|
|
39
|
+
// Default API mappings
|
|
40
|
+
const defaultApis = {
|
|
41
|
+
localStorage: 'useAsyncStorage',
|
|
42
|
+
sessionStorage: 'useAsyncStorage',
|
|
43
|
+
// Future additions will go here:
|
|
44
|
+
// navigator.geolocation: 'useGeolocation',
|
|
45
|
+
// window.history: 'useNavigation',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Get user configuration or use defaults
|
|
49
|
+
const options = context.options[0] || {}
|
|
50
|
+
const apiMap = {
|
|
51
|
+
...defaultApis,
|
|
52
|
+
...(options.apis || {}),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
MemberExpression(node) {
|
|
57
|
+
// Check for direct access: localStorage.getItem()
|
|
58
|
+
if (node.object.type === 'Identifier' && apiMap[node.object.name]) {
|
|
59
|
+
const apiName = node.object.name
|
|
60
|
+
const sdkHook = apiMap[apiName]
|
|
61
|
+
|
|
62
|
+
context.report({
|
|
63
|
+
node: node.object,
|
|
64
|
+
messageId: 'preferAsyncStorage',
|
|
65
|
+
data: {
|
|
66
|
+
nativeApi: apiName,
|
|
67
|
+
sdkHook,
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check for global access: window.localStorage or globalThis.localStorage
|
|
74
|
+
if (
|
|
75
|
+
node.object.type === 'MemberExpression' &&
|
|
76
|
+
node.object.object.type === 'Identifier' &&
|
|
77
|
+
(node.object.object.name === 'window' ||
|
|
78
|
+
node.object.object.name === 'globalThis') &&
|
|
79
|
+
node.object.property.type === 'Identifier' &&
|
|
80
|
+
apiMap[node.object.property.name]
|
|
81
|
+
) {
|
|
82
|
+
const apiName = node.object.property.name
|
|
83
|
+
const sdkHook = apiMap[apiName]
|
|
84
|
+
|
|
85
|
+
context.report({
|
|
86
|
+
node: node.object,
|
|
87
|
+
messageId: 'preferAsyncStorage',
|
|
88
|
+
data: {
|
|
89
|
+
nativeApi: apiName,
|
|
90
|
+
sdkHook,
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Also catch direct references to localStorage/sessionStorage
|
|
97
|
+
Identifier(node) {
|
|
98
|
+
// Only flag if it's being used, not just referenced in imports
|
|
99
|
+
const parent = node.parent
|
|
100
|
+
|
|
101
|
+
// Skip if it's part of an import statement
|
|
102
|
+
if (
|
|
103
|
+
parent.type === 'ImportSpecifier' ||
|
|
104
|
+
parent.type === 'ImportDefaultSpecifier'
|
|
105
|
+
) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Skip if it's already part of a MemberExpression (handled above)
|
|
110
|
+
if (parent.type === 'MemberExpression' && parent.object === node) {
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check if this is a direct reference to localStorage/sessionStorage
|
|
115
|
+
if (!apiMap[node.name]) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Skip if it's being declared as a variable
|
|
120
|
+
if (parent.type === 'VariableDeclarator' && parent.init === node) {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
context.report({
|
|
125
|
+
node,
|
|
126
|
+
messageId: 'preferAsyncStorage',
|
|
127
|
+
})
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
}
|
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.3.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -50,19 +50,20 @@
|
|
|
50
50
|
"@types/react-window": "1.8.8",
|
|
51
51
|
"@types/url-parse": "1.4.9",
|
|
52
52
|
"@types/video.js": "7.3.58",
|
|
53
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
53
54
|
"@vitejs/plugin-react": "4.5.1",
|
|
54
55
|
"class-variance-authority": "0.7.1",
|
|
55
56
|
"clsx": "2.1.1",
|
|
56
57
|
"color": "4.2.3",
|
|
57
58
|
"embla-carousel-react": "8.6.0",
|
|
59
|
+
"eslint": "^8.57.0",
|
|
60
|
+
"eslint-plugin-react": "^7.37.5",
|
|
58
61
|
"js-base64": "3.7.7",
|
|
59
62
|
"lodash": "4.17.21",
|
|
60
63
|
"lucide-react": "0.513.0",
|
|
61
64
|
"motion": "12.17.3",
|
|
62
65
|
"next-themes": "0.4.6",
|
|
63
66
|
"radix-ui": "1.4.2",
|
|
64
|
-
"eslint": "^8.57.0",
|
|
65
|
-
"@typescript-eslint/parser": "^7.0.0",
|
|
66
67
|
"react-intersection-observer": "9.13.1",
|
|
67
68
|
"react-resizable-panels": "3.0.2",
|
|
68
69
|
"react-router": "7.7.0",
|