@shopify/shop-minis-cli 0.0.225 → 0.0.226
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/build/utils/minis-manifest.js +16 -0
- package/build/utils/minis-manifest.js.map +1 -1
- package/build/utils/scan-sdk-usage.d.ts +20 -0
- package/build/utils/scan-sdk-usage.js +185 -0
- package/build/utils/scan-sdk-usage.js.map +1 -0
- package/build/utils/scopes-validation.d.ts +54 -0
- package/build/utils/scopes-validation.js +163 -0
- package/build/utils/scopes-validation.js.map +1 -0
- package/package.json +1 -1
|
@@ -2,8 +2,10 @@ import { createRequire } from 'node:module';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
5
|
+
import { outputDebug } from '@shopify/cli-kit/node/output';
|
|
5
6
|
import { Validator } from 'jsonschema';
|
|
6
7
|
import { getMiniBasePath } from './path.js';
|
|
8
|
+
import { validateUnusedScopes } from './scopes-validation.js';
|
|
7
9
|
const require = createRequire(import.meta.url);
|
|
8
10
|
const manifestSchema = require('../schemas/manifest.schema.json');
|
|
9
11
|
export const DEFAULT_MANIFEST_PATH = path.join('src', 'manifest.json');
|
|
@@ -190,6 +192,20 @@ export function validateManifest(manifestPath) {
|
|
|
190
192
|
validationErrors.push('trusted_domains must be an array');
|
|
191
193
|
}
|
|
192
194
|
}
|
|
195
|
+
// Validate unused scopes
|
|
196
|
+
const miniPath = getMiniBasePath();
|
|
197
|
+
const scopesResult = validateUnusedScopes(miniPath, manifest);
|
|
198
|
+
if (scopesResult.skipped) {
|
|
199
|
+
outputDebug(`Scopes validation skipped: ${scopesResult.skipReason}`);
|
|
200
|
+
}
|
|
201
|
+
else if (!scopesResult.success) {
|
|
202
|
+
if (scopesResult.errors) {
|
|
203
|
+
validationErrors.push(...scopesResult.errors);
|
|
204
|
+
}
|
|
205
|
+
for (const scope of scopesResult.unusedScopes) {
|
|
206
|
+
validationErrors.push(`Unused scope: ${scope}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
193
209
|
return {
|
|
194
210
|
valid: result.valid && validationErrors.length === 0,
|
|
195
211
|
errors: validationErrors,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"minis-manifest.js","sourceRoot":"","sources":["../../src/utils/minis-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AACzC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,SAAS,CAAA;AAEjD,OAAO,EAAC,UAAU,EAAC,MAAM,6BAA6B,CAAA;AACtD,OAAO,EAAC,SAAS,EAAC,MAAM,YAAY,CAAA;AAIpC,OAAO,EAAC,eAAe,EAAC,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"minis-manifest.js","sourceRoot":"","sources":["../../src/utils/minis-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AACzC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,SAAS,CAAA;AAEjD,OAAO,EAAC,UAAU,EAAC,MAAM,6BAA6B,CAAA;AACtD,OAAO,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAA;AACxD,OAAO,EAAC,SAAS,EAAC,MAAM,YAAY,CAAA;AAIpC,OAAO,EAAC,eAAe,EAAC,MAAM,WAAW,CAAA;AACzC,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAA;AAE3D,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC9C,MAAM,cAAc,GAAG,OAAO,CAAC,iCAAiC,CAAC,CAAA;AAEjE,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC,CAAA;AAEtE,MAAM,UAAU,cAAc;IAC5B,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,qBAAqB,CAAC,CAAA;IAC5E,OAAO,UAAU,CAAC,gBAAgB,CAAC,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,YAAY,GAAG,qBAAqB;IAClE,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,YAAY,CAAC,CAAA;IACnE,IAAI,QAAa,CAAA;IACjB,IAAI;QACF,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;KACrC;IAAC,OAAO,GAAQ,EAAE;QACjB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;QAC9C,IAAI,YAAY,EAAE;YAChB,IAAI;gBACF,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;gBACnC,QAAQ,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA;aAC1D;YAAC,MAAM;gBACN,QAAQ,GAAG,IAAI,CAAA;aAChB;YAED,IAAI,QAAQ,KAAK,IAAI,EAAE;gBACrB,MAAM,IAAI,KAAK,CAAC,uCAAuC,YAAY,EAAE,CAAC,CAAA;aACvE;SACF;aAAM;YACL,IAAI,YAAY,GAAG,qCAAqC,YAAY,EAAE,CAAA;YACtE,IAAI,GAAG,EAAE,IAAI,KAAK,aAAa,EAAE;gBAC/B,YAAY,GAAG,wDAAwD,YAAY,IAAI,CAAA;aACxF;iBAAM,IAAI,GAAG,EAAE,IAAI,KAAK,kBAAkB,EAAE;gBAC3C,YAAY,GAAG,yEAAyE,YAAY,IAAI,CAAA;aACzG;YACD,MAAM,IAAI,UAAU,CAAC,mCAAmC,EAAE,YAAY,CAAC,CAAA;SACxE;KACF;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,YAAY,GAAG,qBAAqB,EAC1B,EAAE;IACZ,OAAO,eAAe,CAAC,YAAY,CAAC,EAAE,QAAQ,IAAI,EAAE,CAAA;AACtD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,QAAkB,EAClB,YAAY,GAAG,qBAAqB,EACpC,EAAE;IACF,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,YAAY,CAAC,CAAA;IACnE,MAAM,YAAY,GAAG,eAAe,CAAC,YAAY,CAAC,CAAA;IAElD,YAAY,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAEhC,aAAa,CACX,gBAAgB,EAChB,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAC5C;QACE,QAAQ,EAAE,OAAO;KAClB,CACF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,OAAwB,EACxB,YAAY,GAAG,qBAAqB,EACpC,EAAE;IACF,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,YAAY,CAAC,CAAA;IACnE,MAAM,YAAY,GAAG,eAAe,CAAC,YAAY,CAAC,CAAA;IAElD,YAAY,CAAC,OAAO,GAAG,OAAO,CAAA;IAE9B,aAAa,CACX,gBAAgB,EAChB,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAC5C;QACE,QAAQ,EAAE,OAAO;KAClB,CACF,CAAA;AACH,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,iBAAiB,CAAC,MAAc;IACvC,uBAAuB;IACvB,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;QAClC,OAAO,wBAAwB,CAAA;KAChC;IAED,kDAAkD;IAClD,4CAA4C;IAC5C,IAAI,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QACpC,OAAO,oCAAoC,CAAA;KAC5C;IAED,2EAA2E;IAC3E,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QACrB,OAAO,kCAAkC,CAAA;KAC1C;IAED,6EAA6E;IAC7E,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QAC3B,OAAO,oCAAoC,CAAA;KAC5C;IAED,wDAAwD;IACxD,IAAI,+BAA+B,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QAChD,OAAO,4DAA4D,CAAA;KACpE;IAED,6DAA6D;IAC7D,+EAA+E;IAC/E,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACtC,IAAI,UAAU,GAAG,CAAC,EAAE;QAClB,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;QACnD,kFAAkF;QAClF,IACE,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC;YAC1B,0BAA0B,CAAC,IAAI,CAAC,WAAW,CAAC;YAC5C,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,EAC1C;YACA,OAAO,2CAA2C,CAAA;SACnD;KACF;IAED,+EAA+E;IAC/E,6BAA6B;IAC7B,MAAM,cAAc,GAClB,yGAAyG,CAAA;IAE3G,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;IAC1C,IAAI,CAAC,KAAK,EAAE;QACV,OAAO,2DAA2D,CAAA;KACnE;IAED,MAAM,CAAC,EAAE,cAAc,EAAE,QAAQ,EAAE,AAAD,EAAG,AAAD,EAAG,AAAD,EAAG,QAAQ,CAAC,GAAG,KAAK,CAAA;IAE1D,iFAAiF;IACjF,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QAC3B,IAAI,cAAc,EAAE;YAClB,sEAAsE;YACtE,OAAO,0FAA0F,CAAA;SAClG;QACD,OAAO,4DAA4D,CAAA;KACpE;IAED,oDAAoD;IACpD,IAAI,QAAQ,EAAE;QACZ,uCAAuC;QACvC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;YAC3B,OAAO,sEAAsE,CAAA;SAC9E;QAED,qEAAqE;QACrE,IAAI,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;YACpC,gEAAgE;YAChE,MAAM,kBAAkB,GAAG,2CAA2C,CAAA;YACtE,IAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;gBACrC,OAAO,kCAAkC,CAAA;aAC1C;SACF;KACF;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,2BAA2B,CAAC,KAAa;IAChD,mEAAmE;IACnE,MAAM,qBAAqB,GAA2B;QACpD,kBAAkB,EAChB,4HAA4H;QAC9H,SAAS,EACP,qHAAqH;QACvH,MAAM,EACJ,mGAAmG;QACrG,IAAI,EAAE,qEAAqE;KAC5E,CAAA;IAED,+CAA+C;IAC/C,MAAM,qBAAqB,GAAG,KAAK,CAAC,KAAK,CACvC,sCAAsC,CACvC,CAAA;IACD,IAAI,qBAAqB,EAAE;QACzB,MAAM,SAAS,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAC1C,OAAO,qBAAqB,CAAC,SAAS,CAAC,IAAI,KAAK,CAAA;KACjD;IAED,iDAAiD;IACjD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,YAAqB;IACpD,MAAM,QAAQ,GAAG,eAAe,CAAC,YAAY,CAAC,CAAA;IAC9C,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAA;IACjC,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAA;IAE3D,MAAM,gBAAgB,GAAG;QACvB,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CACrC,2BAA2B,CAAC,eAAe,CAAC,KAAK,CAAC,CACnD;KACF,CAAA;IAED,2BAA2B;IAC3B,IAAI,QAAQ,CAAC,eAAe,EAAE;QAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE;YAC3C,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,MAAW,EAAE,EAAE;gBAC/C,+EAA+E;gBAC/E,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;oBAC9B,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAA;oBACvC,IAAI,KAAK,EAAE;wBACT,gBAAgB,CAAC,IAAI,CACnB,kCAAkC,MAAM,MAAM,KAAK,EAAE,CACtD,CAAA;qBACF;iBACF;YACH,CAAC,CAAC,CAAA;SACH;aAAM;YACL,gBAAgB,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;SAC1D;KACF;IAED,yBAAyB;IACzB,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAA;IAClC,MAAM,YAAY,GAAG,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAE7D,IAAI,YAAY,CAAC,OAAO,EAAE;QACxB,WAAW,CAAC,8BAA8B,YAAY,CAAC,UAAU,EAAE,CAAC,CAAA;KACrE;SAAM,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;QAChC,IAAI,YAAY,CAAC,MAAM,EAAE;YACvB,gBAAgB,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;SAC9C;QACD,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,YAAY,EAAE;YAC7C,gBAAgB,CAAC,IAAI,CAAC,iBAAiB,KAAK,EAAE,CAAC,CAAA;SAChD;KACF;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC;QACpD,MAAM,EAAE,gBAAgB;KACzB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK Import Scanning Utilities
|
|
3
|
+
*
|
|
4
|
+
* Scans a mini app's source code to find all imports from @shopify/shop-minis-react.
|
|
5
|
+
* Used by scope validation to determine which hooks/components are actually used.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Recursively find all TypeScript/TSX files in a directory
|
|
9
|
+
*/
|
|
10
|
+
export declare function findTsFiles(dir: string): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Parse a file and extract import names from @shopify/shop-minis-react
|
|
13
|
+
* Returns just the import names as strings (no metadata needed for validation)
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractImports(filePath: string): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Get unique SDK imports from a mini app directory.
|
|
18
|
+
* This is the main entry point for scope validation.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getUniqueSdkImports(miniPath: string): Set<string>;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK Import Scanning Utilities
|
|
3
|
+
*
|
|
4
|
+
* Scans a mini app's source code to find all imports from @shopify/shop-minis-react.
|
|
5
|
+
* Used by scope validation to determine which hooks/components are actually used.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
const SDK_PACKAGE = '@shopify/shop-minis-react';
|
|
10
|
+
/**
|
|
11
|
+
* Recursively find all TypeScript/TSX files in a directory
|
|
12
|
+
*/
|
|
13
|
+
export function findTsFiles(dir) {
|
|
14
|
+
const files = [];
|
|
15
|
+
function walkDir(currentDir) {
|
|
16
|
+
if (!fs.existsSync(currentDir)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
22
|
+
// Skip node_modules and build directories
|
|
23
|
+
if (entry.name === 'node_modules' ||
|
|
24
|
+
entry.name === 'dist' ||
|
|
25
|
+
entry.name === 'build' ||
|
|
26
|
+
entry.name === '.minis-cache') {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
walkDir(fullPath);
|
|
31
|
+
}
|
|
32
|
+
else if (entry.isFile() &&
|
|
33
|
+
(entry.name.endsWith('.ts') || entry.name.endsWith('.tsx'))) {
|
|
34
|
+
files.push(fullPath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (fs.existsSync(dir)) {
|
|
39
|
+
walkDir(dir);
|
|
40
|
+
}
|
|
41
|
+
return files;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Remove single-line comments from a line of code
|
|
45
|
+
*/
|
|
46
|
+
function removeComments(line) {
|
|
47
|
+
const commentIndex = line.indexOf('//');
|
|
48
|
+
if (commentIndex !== -1) {
|
|
49
|
+
return line.substring(0, commentIndex);
|
|
50
|
+
}
|
|
51
|
+
return line;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if we're inside a multi-line comment block
|
|
55
|
+
*/
|
|
56
|
+
function isInMultiLineComment(lines, currentIndex) {
|
|
57
|
+
let inComment = false;
|
|
58
|
+
for (let i = 0; i <= currentIndex; i++) {
|
|
59
|
+
const line = lines[i];
|
|
60
|
+
if (line.includes('/*')) {
|
|
61
|
+
inComment = true;
|
|
62
|
+
}
|
|
63
|
+
if (line.includes('*/')) {
|
|
64
|
+
inComment = false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return inComment;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse an individual import item and extract the original name
|
|
71
|
+
* Handles: "Foo", "Foo as Bar", "type Foo", "type Foo as Bar"
|
|
72
|
+
*/
|
|
73
|
+
function parseImportItem(item) {
|
|
74
|
+
const trimmed = item.trim();
|
|
75
|
+
// Handle "type Foo" or "type Foo as Bar"
|
|
76
|
+
const typeMatch = trimmed.match(/^type\s+(.+?)(?:\s+as\s+\w+)?$/);
|
|
77
|
+
if (typeMatch) {
|
|
78
|
+
const nameWithAlias = typeMatch[1];
|
|
79
|
+
const asMatch = nameWithAlias.match(/^(.+?)\s+as\s+/);
|
|
80
|
+
return asMatch ? asMatch[1].trim() : nameWithAlias.trim();
|
|
81
|
+
}
|
|
82
|
+
// Handle "Foo as Bar"
|
|
83
|
+
const asMatch = trimmed.match(/^(.+?)\s+as\s+/);
|
|
84
|
+
if (asMatch) {
|
|
85
|
+
return asMatch[1].trim();
|
|
86
|
+
}
|
|
87
|
+
return trimmed;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Parse a file and extract import names from @shopify/shop-minis-react
|
|
91
|
+
* Returns just the import names as strings (no metadata needed for validation)
|
|
92
|
+
*/
|
|
93
|
+
export function extractImports(filePath) {
|
|
94
|
+
const imports = [];
|
|
95
|
+
try {
|
|
96
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
97
|
+
const lines = content.split('\n');
|
|
98
|
+
const escapedPackage = SDK_PACKAGE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
99
|
+
for (let i = 0; i < lines.length; i++) {
|
|
100
|
+
if (isInMultiLineComment(lines, i)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const line = removeComments(lines[i]).trim();
|
|
104
|
+
if (!line)
|
|
105
|
+
continue;
|
|
106
|
+
// Check if this line starts an import/export statement
|
|
107
|
+
const importOrExportStart = /^(import|export)\s/.test(line);
|
|
108
|
+
if (!importOrExportStart) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Collect multi-line imports
|
|
112
|
+
let fullStatement = line;
|
|
113
|
+
let currentLine = i;
|
|
114
|
+
while (currentLine < lines.length - 1 &&
|
|
115
|
+
!fullStatement.match(/['"][;]?\s*$/)) {
|
|
116
|
+
currentLine++;
|
|
117
|
+
const nextLine = removeComments(lines[currentLine]).trim();
|
|
118
|
+
fullStatement = `${fullStatement} ${nextLine}`;
|
|
119
|
+
}
|
|
120
|
+
fullStatement = fullStatement.replace(/\s+/g, ' ').trim();
|
|
121
|
+
// Check if this import/export is from our SDK package
|
|
122
|
+
const importOrExportRegex = new RegExp(`(import|export).*from\\s*['"]${escapedPackage}['"]`);
|
|
123
|
+
if (!importOrExportRegex.test(fullStatement)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
// Match: import type { ... } from '...' or import { ... } from '...'
|
|
127
|
+
const namedImportMatch = fullStatement.match(new RegExp(`import\\s*(?:type\\s*)?{([^}]+)}\\s*from\\s*['"]${escapedPackage}['"]`));
|
|
128
|
+
if (namedImportMatch) {
|
|
129
|
+
const importedItems = namedImportMatch[1]
|
|
130
|
+
.split(',')
|
|
131
|
+
.map(parseImportItem)
|
|
132
|
+
.filter(Boolean);
|
|
133
|
+
imports.push(...importedItems);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// Match: import Something from '...'
|
|
137
|
+
const defaultImportMatch = fullStatement.match(new RegExp(`import\\s+(\\w+)\\s+from\\s*['"]${escapedPackage}['"]`));
|
|
138
|
+
if (defaultImportMatch) {
|
|
139
|
+
imports.push(defaultImportMatch[1]);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
// Match: import * as Something from '...'
|
|
143
|
+
const namespaceImportMatch = fullStatement.match(new RegExp(`import\\s*\\*\\s*as\\s+(\\w+)\\s*from\\s*['"]${escapedPackage}['"]`));
|
|
144
|
+
if (namespaceImportMatch) {
|
|
145
|
+
imports.push(`* as ${namespaceImportMatch[1]}`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
// Match: export { ... } from '...'
|
|
149
|
+
const reExportMatch = fullStatement.match(new RegExp(`export\\s*{([^}]+)}\\s*from\\s*['"]${escapedPackage}['"]`));
|
|
150
|
+
if (reExportMatch) {
|
|
151
|
+
const exportedItems = reExportMatch[1]
|
|
152
|
+
.split(',')
|
|
153
|
+
.map(parseImportItem)
|
|
154
|
+
.filter(Boolean);
|
|
155
|
+
imports.push(...exportedItems);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
// Match: export * from '...'
|
|
159
|
+
const reExportAllMatch = fullStatement.match(new RegExp(`export\\s*\\*\\s*from\\s*['"]${escapedPackage}['"]`));
|
|
160
|
+
if (reExportAllMatch) {
|
|
161
|
+
imports.push('* (re-export all)');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// Skip files that can't be read
|
|
167
|
+
}
|
|
168
|
+
return imports;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get unique SDK imports from a mini app directory.
|
|
172
|
+
* This is the main entry point for scope validation.
|
|
173
|
+
*/
|
|
174
|
+
export function getUniqueSdkImports(miniPath) {
|
|
175
|
+
const files = findTsFiles(miniPath);
|
|
176
|
+
const uniqueImports = new Set();
|
|
177
|
+
for (const file of files) {
|
|
178
|
+
const imports = extractImports(file);
|
|
179
|
+
for (const name of imports) {
|
|
180
|
+
uniqueImports.add(name);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return uniqueImports;
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=scan-sdk-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-sdk-usage.js","sourceRoot":"","sources":["../../src/utils/scan-sdk-usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Validates that all scopes declared in a mini's manifest.json are actually used
|
|
5
|
+
* by the hooks and components imported from @shopify/shop-minis-react.
|
|
6
|
+
*
|
|
7
|
+
* Ported from shop-minis-submissions/scripts/validate-mini-manifest.ts
|
|
8
|
+
*/
|
|
9
|
+
export interface ScopesValidationResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
unusedScopes: string[];
|
|
12
|
+
usedHooksAndComponents: string[];
|
|
13
|
+
/** True if validation was skipped (e.g., SDK maps not found) */
|
|
14
|
+
skipped: boolean;
|
|
15
|
+
/** Reason why validation was skipped */
|
|
16
|
+
skipReason?: string;
|
|
17
|
+
/** Validation errors (e.g., invalid scope types in manifest) */
|
|
18
|
+
errors?: string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Loads the hook-to-scopes mapping from the SDK in a Mini's node_modules
|
|
22
|
+
* Returns null if the SDK is not installed or the map file doesn't exist
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadHookScopesMap(miniPath: string): Record<string, string[]> | null;
|
|
25
|
+
/**
|
|
26
|
+
* Loads the component-to-scopes mapping from the SDK in a Mini's node_modules
|
|
27
|
+
* Returns a map keyed by component name (not file path) for easier lookup
|
|
28
|
+
* Returns null if the SDK is not installed or the map file doesn't exist
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadComponentScopesMap(miniPath: string): Record<string, {
|
|
31
|
+
scopes: string[];
|
|
32
|
+
hooks: string[];
|
|
33
|
+
}> | null;
|
|
34
|
+
/**
|
|
35
|
+
* Checks if a scope is required by any of the used hooks/components
|
|
36
|
+
*/
|
|
37
|
+
export declare function isScopeUsed(scope: string, usedItems: Set<string>, hookToScopes: Record<string, string[]>, componentToScopes: Record<string, {
|
|
38
|
+
scopes: string[];
|
|
39
|
+
hooks: string[];
|
|
40
|
+
}>): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Validates that all scopes declared in the manifest are actually used.
|
|
43
|
+
* Returns a result indicating success/failure and any unused scopes.
|
|
44
|
+
*
|
|
45
|
+
* This function gracefully handles missing SDK installations by skipping
|
|
46
|
+
* validation and returning a successful result.
|
|
47
|
+
*
|
|
48
|
+
* @param miniPath - Path to the mini app directory
|
|
49
|
+
* @param manifest - Already-loaded manifest content (avoids double-loading)
|
|
50
|
+
*/
|
|
51
|
+
export declare function validateUnusedScopes(miniPath: string, manifest: {
|
|
52
|
+
scopes?: unknown[];
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
}): ScopesValidationResult;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Validates that all scopes declared in a mini's manifest.json are actually used
|
|
5
|
+
* by the hooks and components imported from @shopify/shop-minis-react.
|
|
6
|
+
*
|
|
7
|
+
* Ported from shop-minis-submissions/scripts/validate-mini-manifest.ts
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { getUniqueSdkImports } from './scan-sdk-usage.js';
|
|
12
|
+
/**
|
|
13
|
+
* Loads the hook-to-scopes mapping from the SDK in a Mini's node_modules
|
|
14
|
+
* Returns null if the SDK is not installed or the map file doesn't exist
|
|
15
|
+
*/
|
|
16
|
+
export function loadHookScopesMap(miniPath) {
|
|
17
|
+
const mapPath = path.join(miniPath, 'node_modules', '@shopify', 'shop-minis-react', 'generated-hook-maps', 'hook-scopes-map.json');
|
|
18
|
+
if (!fs.existsSync(mapPath)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const mapContent = fs.readFileSync(mapPath, 'utf-8');
|
|
23
|
+
return JSON.parse(mapContent);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Loads the component-to-scopes mapping from the SDK in a Mini's node_modules
|
|
31
|
+
* Returns a map keyed by component name (not file path) for easier lookup
|
|
32
|
+
* Returns null if the SDK is not installed or the map file doesn't exist
|
|
33
|
+
*/
|
|
34
|
+
export function loadComponentScopesMap(miniPath) {
|
|
35
|
+
const mapPath = path.join(miniPath, 'node_modules', '@shopify', 'shop-minis-react', 'generated-hook-maps', 'component-scopes-map.json');
|
|
36
|
+
if (!fs.existsSync(mapPath)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const mapContent = fs.readFileSync(mapPath, 'utf-8');
|
|
41
|
+
const filePathMap = JSON.parse(mapContent);
|
|
42
|
+
// Build reverse map: componentName -> {scopes, hooks}
|
|
43
|
+
const componentNameMap = {};
|
|
44
|
+
for (const [filePath, data] of Object.entries(filePathMap)) {
|
|
45
|
+
if (data.name) {
|
|
46
|
+
// Use componentName field when available
|
|
47
|
+
componentNameMap[data.name] = {
|
|
48
|
+
scopes: data.scopes,
|
|
49
|
+
hooks: data.hooks,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// Fallback: derive component name from file path (for backward compatibility)
|
|
54
|
+
// commerce/product-card -> ProductCard
|
|
55
|
+
const fileName = filePath.split('/').pop() || '';
|
|
56
|
+
const componentName = fileName
|
|
57
|
+
.split('-')
|
|
58
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
59
|
+
.join('');
|
|
60
|
+
if (componentName) {
|
|
61
|
+
componentNameMap[componentName] = {
|
|
62
|
+
scopes: data.scopes,
|
|
63
|
+
hooks: data.hooks,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return componentNameMap;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Checks if a scope is required by any of the used hooks/components
|
|
76
|
+
*/
|
|
77
|
+
export function isScopeUsed(scope, usedItems, hookToScopes, componentToScopes) {
|
|
78
|
+
// Check if any used item (hook or component) requires this scope
|
|
79
|
+
for (const item of usedItems) {
|
|
80
|
+
// Check hooks
|
|
81
|
+
const hookScopes = hookToScopes[item];
|
|
82
|
+
if (hookScopes && hookScopes.includes(scope)) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
// Check components (now keyed by component name after loading)
|
|
86
|
+
const componentData = componentToScopes[item];
|
|
87
|
+
if (componentData &&
|
|
88
|
+
componentData.scopes &&
|
|
89
|
+
componentData.scopes.includes(scope)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Validates that all scopes declared in the manifest are actually used.
|
|
97
|
+
* Returns a result indicating success/failure and any unused scopes.
|
|
98
|
+
*
|
|
99
|
+
* This function gracefully handles missing SDK installations by skipping
|
|
100
|
+
* validation and returning a successful result.
|
|
101
|
+
*
|
|
102
|
+
* @param miniPath - Path to the mini app directory
|
|
103
|
+
* @param manifest - Already-loaded manifest content (avoids double-loading)
|
|
104
|
+
*/
|
|
105
|
+
export function validateUnusedScopes(miniPath, manifest) {
|
|
106
|
+
// If no scopes declared, nothing to validate
|
|
107
|
+
const declaredScopes = manifest.scopes;
|
|
108
|
+
if (!declaredScopes || declaredScopes.length === 0) {
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
unusedScopes: [],
|
|
112
|
+
usedHooksAndComponents: [],
|
|
113
|
+
skipped: false,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Scopes must be an array
|
|
117
|
+
if (!Array.isArray(declaredScopes)) {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
unusedScopes: [],
|
|
121
|
+
usedHooksAndComponents: [],
|
|
122
|
+
skipped: false,
|
|
123
|
+
errors: ['scopes must be an array'],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Load scope maps from SDK
|
|
127
|
+
const hookToScopes = loadHookScopesMap(miniPath);
|
|
128
|
+
const componentToScopes = loadComponentScopesMap(miniPath);
|
|
129
|
+
// If SDK maps are not available, skip validation gracefully
|
|
130
|
+
if (!hookToScopes) {
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
unusedScopes: [],
|
|
134
|
+
usedHooksAndComponents: [],
|
|
135
|
+
skipped: true,
|
|
136
|
+
skipReason: 'SDK scope maps not found. Make sure @shopify/shop-minis-react is installed.',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
// Use empty object if component scopes map doesn't exist (it's optional)
|
|
140
|
+
const componentScopes = componentToScopes || {};
|
|
141
|
+
// Scan mini source for SDK imports
|
|
142
|
+
const usedHooksAndComponents = getUniqueSdkImports(miniPath);
|
|
143
|
+
// Check each declared scope to see if it's actually used
|
|
144
|
+
const unusedScopes = [];
|
|
145
|
+
const errors = [];
|
|
146
|
+
for (const scope of declaredScopes) {
|
|
147
|
+
if (typeof scope !== 'string') {
|
|
148
|
+
errors.push(`scopes must contain only strings, found: ${typeof scope}`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (!isScopeUsed(scope, usedHooksAndComponents, hookToScopes, componentScopes)) {
|
|
152
|
+
unusedScopes.push(scope);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
success: unusedScopes.length === 0 && errors.length === 0,
|
|
157
|
+
unusedScopes,
|
|
158
|
+
usedHooksAndComponents: Array.from(usedHooksAndComponents),
|
|
159
|
+
skipped: false,
|
|
160
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=scopes-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopes-validation.js","sourceRoot":"","sources":["../../src/utils/scopes-validation.ts"],"names":[],"mappings":"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"}
|