@freeappstore/compliance 0.5.0 → 0.6.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/checks/claude-md-slim.d.ts +4 -0
- package/dist/checks/claude-md-slim.d.ts.map +1 -0
- package/dist/checks/claude-md-slim.js +88 -0
- package/dist/checks/claude-md-slim.js.map +1 -0
- package/dist/checks/dark-mode.d.ts +1 -1
- package/dist/checks/dark-mode.d.ts.map +1 -1
- package/dist/checks/dark-mode.js +11 -3
- package/dist/checks/dark-mode.js.map +1 -1
- package/dist/checks/html-meta.d.ts.map +1 -1
- package/dist/checks/html-meta.js +7 -2
- package/dist/checks/html-meta.js.map +1 -1
- package/dist/checks/license-mit.d.ts.map +1 -1
- package/dist/checks/license-mit.js +8 -1
- package/dist/checks/license-mit.js.map +1 -1
- package/dist/checks/manifest.d.ts +9 -3
- package/dist/checks/manifest.d.ts.map +1 -1
- package/dist/checks/manifest.js +61 -18
- package/dist/checks/manifest.js.map +1 -1
- package/dist/checks/no-brand-overrides.d.ts.map +1 -1
- package/dist/checks/no-brand-overrides.js +83 -14
- package/dist/checks/no-brand-overrides.js.map +1 -1
- package/dist/checks/no-env-production.d.ts.map +1 -1
- package/dist/checks/no-env-production.js +5 -1
- package/dist/checks/no-env-production.js.map +1 -1
- package/dist/checks/no-placeholders.d.ts.map +1 -1
- package/dist/checks/no-placeholders.js +16 -3
- package/dist/checks/no-placeholders.js.map +1 -1
- package/dist/checks/no-scroll.js +1 -3
- package/dist/checks/no-scroll.js.map +1 -1
- package/dist/checks/no-tracking.d.ts.map +1 -1
- package/dist/checks/no-tracking.js +68 -16
- package/dist/checks/no-tracking.js.map +1 -1
- package/dist/checks/pwa-meta.d.ts.map +1 -1
- package/dist/checks/pwa-meta.js +6 -2
- package/dist/checks/pwa-meta.js.map +1 -1
- package/dist/checks/pwa-offline.d.ts +40 -0
- package/dist/checks/pwa-offline.d.ts.map +1 -0
- package/dist/checks/pwa-offline.js +388 -0
- package/dist/checks/pwa-offline.js.map +1 -0
- package/dist/checks/store-link.js +1 -1
- package/dist/checks/store-link.js.map +1 -1
- package/dist/checks/unsafe-vh.d.ts.map +1 -1
- package/dist/checks/unsafe-vh.js +18 -6
- package/dist/checks/unsafe-vh.js.map +1 -1
- package/dist/checks/viewport-support.d.ts +4 -1
- package/dist/checks/viewport-support.d.ts.map +1 -1
- package/dist/checks/viewport-support.js +29 -19
- package/dist/checks/viewport-support.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/file-source.d.ts.map +1 -1
- package/dist/lib/file-source.js +9 -1
- package/dist/lib/file-source.js.map +1 -1
- package/dist/lib/inline-manifest.d.ts +32 -0
- package/dist/lib/inline-manifest.d.ts.map +1 -0
- package/dist/lib/inline-manifest.js +109 -0
- package/dist/lib/inline-manifest.js.map +1 -0
- package/dist/lib/strip.d.ts +60 -0
- package/dist/lib/strip.d.ts.map +1 -0
- package/dist/lib/strip.js +169 -0
- package/dist/lib/strip.js.map +1 -0
- package/dist/live/index.d.ts.map +1 -1
- package/dist/live/index.js +5 -1
- package/dist/live/index.js.map +1 -1
- package/package.json +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-scroll.js","sourceRoot":"","sources":["../../src/checks/no-scroll.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"no-scroll.js","sourceRoot":"","sources":["../../src/checks/no-scroll.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-tracking.d.ts","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"no-tracking.d.ts","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAuE/C,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CA6C9E"}
|
|
@@ -1,25 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'
|
|
9
|
-
|
|
10
|
-
'
|
|
11
|
-
|
|
1
|
+
import { stripCommentsForExt } from '../lib/strip.js';
|
|
2
|
+
function wb(name) {
|
|
3
|
+
// word-boundary, case-insensitive: only matches the literal token surrounded
|
|
4
|
+
// by non-word chars (or string start/end). Avoids `segmentation`, `collideSegment`.
|
|
5
|
+
return new RegExp(`(?:^|[^a-zA-Z0-9_])${escapeForRegExp(name)}(?:$|[^a-zA-Z0-9_])`, 'i');
|
|
6
|
+
}
|
|
7
|
+
const TRACKERS = [
|
|
8
|
+
{ name: 'google-analytics', patterns: [/google-analytics/i, /googletagmanager\.com/i] },
|
|
9
|
+
// `gtag` as a bare token would catch nothing real, but the GA install snippet
|
|
10
|
+
// always calls it like `gtag('config', ...)` or `gtag('event', ...)` or
|
|
11
|
+
// declares `window.gtag = ...`. Match those, not the bare identifier.
|
|
12
|
+
{ name: 'gtag', patterns: [/\bgtag\s*\(\s*['"]/i, /window\.gtag\b/i, /gtag\.js/i] },
|
|
13
|
+
// Amplitude SDK shows up as @amplitude/* scoped package, amplitude.com host,
|
|
14
|
+
// or amplitude.init/track/getInstance call. Bare `amplitude` (the math term)
|
|
15
|
+
// is fine.
|
|
16
|
+
{
|
|
17
|
+
name: 'amplitude',
|
|
18
|
+
patterns: [
|
|
19
|
+
/@amplitude\//i,
|
|
20
|
+
/amplitude\.com/i,
|
|
21
|
+
/\bamplitude\s*\.\s*(?:init|track|getInstance|setUserId|logEvent)\b/i,
|
|
22
|
+
/require\(['"]amplitude/i,
|
|
23
|
+
/from\s+['"]amplitude/i,
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{ name: 'mixpanel', patterns: [wb('mixpanel')] },
|
|
27
|
+
// Segment SDK: scoped package, hostname, or window.analytics.track/identify.
|
|
28
|
+
// Bare `segment` (geometry term — ball-line-segment, drop-target row segment)
|
|
29
|
+
// is fine.
|
|
30
|
+
{
|
|
31
|
+
name: 'segment',
|
|
32
|
+
patterns: [
|
|
33
|
+
/@segment\//i,
|
|
34
|
+
/segment\.io/i,
|
|
35
|
+
/segment\.com\/analytics/i,
|
|
36
|
+
/cdn\.segment\.com/i,
|
|
37
|
+
/window\.analytics\s*\.\s*(?:track|identify|page|group)\b/i,
|
|
38
|
+
/\bAnalyticsBrowser\.load\b/i,
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{ name: 'hotjar', patterns: [wb('hotjar')] },
|
|
42
|
+
{ name: 'plausible', patterns: [wb('plausible')] },
|
|
43
|
+
{ name: 'posthog', patterns: [wb('posthog')] },
|
|
12
44
|
];
|
|
13
45
|
const SCAN_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.html', '.json']);
|
|
46
|
+
function escapeForRegExp(s) {
|
|
47
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
48
|
+
}
|
|
49
|
+
// Per-game compliance tests legitimately mention these tracker names as
|
|
50
|
+
// the banned list they assert NOT to find. Treating those as positives
|
|
51
|
+
// is the classic "check finds its own assertion" false positive.
|
|
52
|
+
function isSelfReferenceTestFile(path) {
|
|
53
|
+
return /(?:^|\/)(?:test|tests|__tests__)\//.test(path) && /compliance\.test\.[jt]sx?$/.test(path);
|
|
54
|
+
}
|
|
14
55
|
export async function checkNoTracking(source) {
|
|
15
56
|
const hits = [];
|
|
16
57
|
for await (const path of source.list()) {
|
|
17
|
-
|
|
58
|
+
const ext = extOf(path);
|
|
59
|
+
if (!SCAN_EXTS.has(ext))
|
|
60
|
+
continue;
|
|
61
|
+
if (isSelfReferenceTestFile(path))
|
|
18
62
|
continue;
|
|
19
|
-
const
|
|
20
|
-
if (!
|
|
63
|
+
const raw = await source.read(path);
|
|
64
|
+
if (!raw)
|
|
21
65
|
continue;
|
|
22
|
-
|
|
66
|
+
// Strip comments only — NOT string literals. Real tracker imports
|
|
67
|
+
// live inside strings (`import from "@amplitude/..."`,
|
|
68
|
+
// `<script src="https://www.google-analytics.com/...">`), so
|
|
69
|
+
// erasing string contents would erase the very evidence we need.
|
|
70
|
+
// A rare false positive remains if someone mentions a tracker name
|
|
71
|
+
// inside a non-import string literal; comment-stripping handles the
|
|
72
|
+
// common case (documentation comments).
|
|
73
|
+
const content = stripCommentsForExt(raw, ext);
|
|
74
|
+
const matches = TRACKERS.filter((t) => t.patterns.some((re) => re.test(content))).map((t) => t.name);
|
|
23
75
|
if (matches.length > 0) {
|
|
24
76
|
hits.push({ file: path, matches });
|
|
25
77
|
}
|
|
@@ -28,7 +80,7 @@ export async function checkNoTracking(source) {
|
|
|
28
80
|
return {
|
|
29
81
|
name: 'No tracking SDKs',
|
|
30
82
|
status: 'pass',
|
|
31
|
-
detail: `scanned for ${
|
|
83
|
+
detail: `scanned for ${TRACKERS.length} known trackers`,
|
|
32
84
|
};
|
|
33
85
|
}
|
|
34
86
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-tracking.js","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-tracking.js","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pwa-meta.d.ts","sourceRoot":"","sources":["../../src/checks/pwa-meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"pwa-meta.d.ts","sourceRoot":"","sources":["../../src/checks/pwa-meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAuB3E"}
|
package/dist/checks/pwa-meta.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stripHtmlComments } from '../lib/strip.js';
|
|
1
2
|
const HTML_PATH = 'web/index.html';
|
|
2
3
|
/**
|
|
3
4
|
* For an "Add to Home Screen" install on iOS Safari to launch the app
|
|
@@ -15,14 +16,17 @@ const HTML_PATH = 'web/index.html';
|
|
|
15
16
|
* is specifically the iOS install hint that lives in the HTML head.
|
|
16
17
|
*/
|
|
17
18
|
export async function checkPwaMeta(source) {
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
19
|
+
const rawHtml = await source.read(HTML_PATH);
|
|
20
|
+
if (rawHtml === null) {
|
|
20
21
|
return {
|
|
21
22
|
name: 'PWA meta tags',
|
|
22
23
|
status: 'fail',
|
|
23
24
|
detail: `${HTML_PATH} not found`,
|
|
24
25
|
};
|
|
25
26
|
}
|
|
27
|
+
// Strip HTML comments — `<!-- <meta name="apple-mobile-web-app-capable"> -->`
|
|
28
|
+
// doesn't tell iOS anything.
|
|
29
|
+
const html = stripHtmlComments(rawHtml);
|
|
26
30
|
if (/<meta[^>]*\bname\s*=\s*["'](?:apple-)?mobile-web-app-capable["']/i.test(html)) {
|
|
27
31
|
return { name: 'PWA meta tags', status: 'pass', detail: 'iOS install hint present' };
|
|
28
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pwa-meta.js","sourceRoot":"","sources":["../../src/checks/pwa-meta.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pwa-meta.js","sourceRoot":"","sources":["../../src/checks/pwa-meta.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAkB;IACnD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,SAAS,YAAY;SACjC,CAAC;IACJ,CAAC;IACD,8EAA8E;IAC9E,6BAA6B;IAC7B,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,mEAAmE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACvF,CAAC;IACD,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,+DAA+D;QACvE,WAAW,EAAE;YACX,+GAA+G;SAChH;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FileSource } from '../lib/file-source.js';
|
|
2
|
+
import type { CheckResult } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Two things this check enforces:
|
|
5
|
+
*
|
|
6
|
+
* 1. Platform mandate — every app on freeappstore.online MUST be an
|
|
7
|
+
* installable PWA (service worker registered). Triggered whenever
|
|
8
|
+
* a project has a `web/index.html` (the universal signal of a
|
|
9
|
+
* Vite app destined for the storefront). Hand-rolled
|
|
10
|
+
* `serviceWorker.register` counts; vite-plugin-pwa counts. Anything
|
|
11
|
+
* else fails. Non-app repos that don't ship a web entry are
|
|
12
|
+
* naturally exempt — they're not subject to compliance anyway.
|
|
13
|
+
*
|
|
14
|
+
* 2. Offline-correctness quality — among PWAs that ARE configured,
|
|
15
|
+
* the workbox setup actually has to work from the home screen
|
|
16
|
+
* while offline. Apps that ship a manifest but mis-configure the
|
|
17
|
+
* service worker install fine, then show a blank screen offline.
|
|
18
|
+
*
|
|
19
|
+
* Three quality failure modes this catches, all observed in the wild:
|
|
20
|
+
*
|
|
21
|
+
* 1. `maximumFileSizeToCacheInBytes` left at the workbox default (2 MB).
|
|
22
|
+
* Any bundle chunk above 2 MB is silently dropped from the precache.
|
|
23
|
+
* The SW serves index.html from cache, then the JS chunk 404s offline.
|
|
24
|
+
*
|
|
25
|
+
* 2. Google Fonts loaded via render-blocking <link> in index.html with
|
|
26
|
+
* no `runtimeCaching` rule. Browser HTTP cache works most of the
|
|
27
|
+
* time, but standalone-mode launches on iOS bypass it inconsistently
|
|
28
|
+
* — fonts fall back to system, and on cold first offline launch the
|
|
29
|
+
* <link> can stall paint for hundreds of ms.
|
|
30
|
+
*
|
|
31
|
+
* 3. Assets in extensions not covered by `globPatterns`. The default
|
|
32
|
+
* list is js/css/html/png/svg/ico/woff2. Any wasm, json, audio, etc.
|
|
33
|
+
* shipped under web/public/ isn't precached → offline 404.
|
|
34
|
+
*
|
|
35
|
+
* Also catches the inverse: index.html links a manifest but the config
|
|
36
|
+
* has no service worker at all → "installable" PWA that always needs
|
|
37
|
+
* network.
|
|
38
|
+
*/
|
|
39
|
+
export declare function checkPwaOffline(source: FileSource): Promise<CheckResult>;
|
|
40
|
+
//# sourceMappingURL=pwa-offline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pwa-offline.d.ts","sourceRoot":"","sources":["../../src/checks/pwa-offline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAwM9E"}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { stripCommentsAndStrings, stripHtmlComments } from '../lib/strip.js';
|
|
2
|
+
const VITE_CONFIG = 'web/vite.config.ts';
|
|
3
|
+
const INDEX_HTML = 'web/index.html';
|
|
4
|
+
const PUBLIC_DIR = 'web/public';
|
|
5
|
+
/**
|
|
6
|
+
* Two things this check enforces:
|
|
7
|
+
*
|
|
8
|
+
* 1. Platform mandate — every app on freeappstore.online MUST be an
|
|
9
|
+
* installable PWA (service worker registered). Triggered whenever
|
|
10
|
+
* a project has a `web/index.html` (the universal signal of a
|
|
11
|
+
* Vite app destined for the storefront). Hand-rolled
|
|
12
|
+
* `serviceWorker.register` counts; vite-plugin-pwa counts. Anything
|
|
13
|
+
* else fails. Non-app repos that don't ship a web entry are
|
|
14
|
+
* naturally exempt — they're not subject to compliance anyway.
|
|
15
|
+
*
|
|
16
|
+
* 2. Offline-correctness quality — among PWAs that ARE configured,
|
|
17
|
+
* the workbox setup actually has to work from the home screen
|
|
18
|
+
* while offline. Apps that ship a manifest but mis-configure the
|
|
19
|
+
* service worker install fine, then show a blank screen offline.
|
|
20
|
+
*
|
|
21
|
+
* Three quality failure modes this catches, all observed in the wild:
|
|
22
|
+
*
|
|
23
|
+
* 1. `maximumFileSizeToCacheInBytes` left at the workbox default (2 MB).
|
|
24
|
+
* Any bundle chunk above 2 MB is silently dropped from the precache.
|
|
25
|
+
* The SW serves index.html from cache, then the JS chunk 404s offline.
|
|
26
|
+
*
|
|
27
|
+
* 2. Google Fonts loaded via render-blocking <link> in index.html with
|
|
28
|
+
* no `runtimeCaching` rule. Browser HTTP cache works most of the
|
|
29
|
+
* time, but standalone-mode launches on iOS bypass it inconsistently
|
|
30
|
+
* — fonts fall back to system, and on cold first offline launch the
|
|
31
|
+
* <link> can stall paint for hundreds of ms.
|
|
32
|
+
*
|
|
33
|
+
* 3. Assets in extensions not covered by `globPatterns`. The default
|
|
34
|
+
* list is js/css/html/png/svg/ico/woff2. Any wasm, json, audio, etc.
|
|
35
|
+
* shipped under web/public/ isn't precached → offline 404.
|
|
36
|
+
*
|
|
37
|
+
* Also catches the inverse: index.html links a manifest but the config
|
|
38
|
+
* has no service worker at all → "installable" PWA that always needs
|
|
39
|
+
* network.
|
|
40
|
+
*/
|
|
41
|
+
export async function checkPwaOffline(source) {
|
|
42
|
+
const config = await source.read(VITE_CONFIG);
|
|
43
|
+
const html = await source.read(INDEX_HTML);
|
|
44
|
+
// Strip HTML comments before matching — `<!-- <link rel="manifest"> -->`
|
|
45
|
+
// is not a live install claim, and a commented-out fonts <link> is
|
|
46
|
+
// not loading anything.
|
|
47
|
+
const htmlCode = html === null ? null : stripHtmlComments(html);
|
|
48
|
+
const linksManifest = htmlCode !== null && /<link[^>]+rel\s*=\s*["']manifest["']/i.test(htmlCode);
|
|
49
|
+
const linksGoogleFonts = htmlCode !== null && /fonts\.(googleapis|gstatic)\.com/i.test(htmlCode);
|
|
50
|
+
// Strip comments and string-literal contents before any regex matching
|
|
51
|
+
// against the config text — otherwise `VitePWA(` in a comment or
|
|
52
|
+
// `"workbox: {"` in a string is treated as real code (false positive).
|
|
53
|
+
const configCode = config === null ? null : stripCommentsAndStrings(config);
|
|
54
|
+
const hasVitePwa = configCode !== null && /\bVitePWA\s*\(/.test(configCode);
|
|
55
|
+
// `injectManifest` strategy means the developer writes their own SW
|
|
56
|
+
// file (typically web/src/sw.ts). The `workbox` field doesn't apply
|
|
57
|
+
// — `injectManifest` config does — so the rest of our checks are
|
|
58
|
+
// inapplicable. Trust the dev's manual SW. (Matched against `config`
|
|
59
|
+
// — not `configCode` — because the string-stripping would erase
|
|
60
|
+
// `"injectManifest"` from the latter.)
|
|
61
|
+
const usesInjectManifest = config !== null && /strategies\s*:\s*["']injectManifest["']/.test(config);
|
|
62
|
+
// Hand-rolled SW registration (e.g. an inline <script> calling
|
|
63
|
+
// navigator.serviceWorker.register) is a legitimate alternative to
|
|
64
|
+
// vite-plugin-pwa. If it exists, we trust the dev to manage their own
|
|
65
|
+
// precache and limit ourselves to the install-claim check.
|
|
66
|
+
const hasManualSw = (html !== null && /serviceWorker\.register/.test(html)) ||
|
|
67
|
+
(await sourceHasSwRegistration(source));
|
|
68
|
+
const hasServiceWorker = hasVitePwa || hasManualSw;
|
|
69
|
+
// Platform mandate (freeappstore.online): every app must be an
|
|
70
|
+
// installable PWA. "Installable" at the static-check level means a
|
|
71
|
+
// service worker registers — VitePWA (which also injects the
|
|
72
|
+
// manifest link), an injectManifest setup, or hand-rolled register.
|
|
73
|
+
// Triggered by the presence of `web/index.html` (the universal signal
|
|
74
|
+
// of a Vite-built app). Repos that don't ship a web entry — admin
|
|
75
|
+
// tools, backends, agents — are naturally exempt; compliance isn't
|
|
76
|
+
// run against them anyway.
|
|
77
|
+
if (html !== null && !hasServiceWorker) {
|
|
78
|
+
return {
|
|
79
|
+
name: 'PWA offline correctness',
|
|
80
|
+
status: 'fail',
|
|
81
|
+
detail: 'platform mandate: every app on freeappstore.online must be an installable PWA, but no service worker is registered',
|
|
82
|
+
suggestions: [
|
|
83
|
+
'Add `vite-plugin-pwa` to web/devDependencies, then wire `VitePWA({...})` into vite.config.ts plugins. Mirror the canonical config used in the canonical scaffolds, etc.',
|
|
84
|
+
'Or, if you prefer to manage your own SW, register it from web/src/main.tsx with `navigator.serviceWorker.register("/sw.js")` and ship the SW yourself.',
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// "Installable" claim with no service worker — the worst failure mode.
|
|
89
|
+
// The PWA installs from the manifest but launches into a network fetch
|
|
90
|
+
// for `/` that 404s offline → blank screen on home screen.
|
|
91
|
+
if (linksManifest && !hasServiceWorker) {
|
|
92
|
+
return {
|
|
93
|
+
name: 'PWA offline correctness',
|
|
94
|
+
status: 'fail',
|
|
95
|
+
detail: 'index.html links a manifest but no service worker is registered → installable PWA that cannot load offline from home screen',
|
|
96
|
+
suggestions: [
|
|
97
|
+
'Install vite-plugin-pwa and add VitePWA({...}) to vite.config.ts plugins.',
|
|
98
|
+
'Or register a service worker manually (`navigator.serviceWorker.register("/sw.js")`).',
|
|
99
|
+
'Or drop the <link rel="manifest"> from index.html if this is not meant to be installable.',
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// No vite.config.ts at all — can't analyze further. Either not a Vite
|
|
104
|
+
// project, or PWA wiring lives elsewhere; either way, nothing for us
|
|
105
|
+
// to assert about workbox.
|
|
106
|
+
if (config === null) {
|
|
107
|
+
return {
|
|
108
|
+
name: 'PWA offline correctness',
|
|
109
|
+
status: 'pass',
|
|
110
|
+
detail: hasManualSw
|
|
111
|
+
? 'no vite.config.ts; hand-rolled service worker present'
|
|
112
|
+
: 'no web/vite.config.ts (not a Vite project)',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (!hasVitePwa) {
|
|
116
|
+
return {
|
|
117
|
+
name: 'PWA offline correctness',
|
|
118
|
+
status: 'pass',
|
|
119
|
+
detail: hasManualSw
|
|
120
|
+
? 'hand-rolled service worker; no install claim to verify'
|
|
121
|
+
: 'not a PWA (no VitePWA, no manifest link)',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// injectManifest: developer hand-writes the SW. Their `workbox` config
|
|
125
|
+
// (if any) is irrelevant; the analyzable surface lives in their SW
|
|
126
|
+
// source which we don't parse.
|
|
127
|
+
if (usesInjectManifest) {
|
|
128
|
+
return {
|
|
129
|
+
name: 'PWA offline correctness',
|
|
130
|
+
status: 'pass',
|
|
131
|
+
detail: 'VitePWA with injectManifest strategy — SW managed in src',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// Extract the workbox block. Match `workbox: { ... }` allowing nested
|
|
135
|
+
// braces. We pass `configCode` (comments/strings stripped) so the
|
|
136
|
+
// matcher can't false-positive on `"workbox: {"` inside a string, but
|
|
137
|
+
// we slice out of `config` so the returned substring still has its
|
|
138
|
+
// real string contents — otherwise downstream `globPatterns:
|
|
139
|
+
// ["..."]` regex would fail.
|
|
140
|
+
const workbox = extractBalancedBlock(config, configCode, /workbox\s*:\s*\{/);
|
|
141
|
+
if (workbox === null) {
|
|
142
|
+
return {
|
|
143
|
+
name: 'PWA offline correctness',
|
|
144
|
+
status: 'warn',
|
|
145
|
+
detail: 'VitePWA present but no workbox block parsed — defaults may leave assets unprecached',
|
|
146
|
+
suggestions: [
|
|
147
|
+
'Add a `workbox: { ... }` block with globPatterns, maximumFileSizeToCacheInBytes, and runtimeCaching.',
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const issues = [];
|
|
152
|
+
const suggestions = [];
|
|
153
|
+
// Issue 1: bundle-size cap. Default is 2 MiB; many real bundles exceed it.
|
|
154
|
+
// Also catch the inverse footgun: a value *lower* than the default
|
|
155
|
+
// (e.g. someone copy-pasted `1024` thinking it was MB), which silently
|
|
156
|
+
// makes precache worse.
|
|
157
|
+
const capValue = parseBundleCap(workbox);
|
|
158
|
+
const DEFAULT_CAP = 2 * 1024 * 1024;
|
|
159
|
+
if (capValue === null) {
|
|
160
|
+
issues.push('no maximumFileSizeToCacheInBytes (defaults to 2 MB — bigger chunks silently skipped from precache)');
|
|
161
|
+
suggestions.push('Set `maximumFileSizeToCacheInBytes: 10 * 1024 * 1024` so the main bundle is precached.');
|
|
162
|
+
}
|
|
163
|
+
else if (capValue < DEFAULT_CAP) {
|
|
164
|
+
issues.push(`maximumFileSizeToCacheInBytes is ${capValue} bytes — smaller than the workbox default (2 MB); any chunk above this is dropped from precache`);
|
|
165
|
+
suggestions.push('Raise to at least `10 * 1024 * 1024` (10 MB) so real bundles fit.');
|
|
166
|
+
}
|
|
167
|
+
// `VitePWA({ disable: true })` literal turns the plugin off in all
|
|
168
|
+
// environments, so the manifest link is broken. Only flag the
|
|
169
|
+
// unconditional literal — `disable: process.env.NODE_ENV !==
|
|
170
|
+
// "production"` is a common, correct dev-only pattern that we can't
|
|
171
|
+
// evaluate statically.
|
|
172
|
+
if (configCode && /\bdisable\s*:\s*true\b/.test(configCode)) {
|
|
173
|
+
issues.push('VitePWA({ disable: true }) literal — service worker will never register');
|
|
174
|
+
suggestions.push('Drop `disable: true`, or gate it behind a non-production check.');
|
|
175
|
+
}
|
|
176
|
+
// Issue 2: Google Fonts with no runtime caching.
|
|
177
|
+
if (linksGoogleFonts) {
|
|
178
|
+
const hasGoogleApisRule = /fonts\\?\.googleapis\\?\.com/.test(workbox);
|
|
179
|
+
const hasGstaticRule = /fonts\\?\.gstatic\\?\.com/.test(workbox);
|
|
180
|
+
// Workbox also lets you target by `request.destination` — a rule
|
|
181
|
+
// like `({request}) => request.destination === "font"` catches
|
|
182
|
+
// every font request including Google Fonts. Accept that as
|
|
183
|
+
// covering both endpoints rather than emitting a false warn.
|
|
184
|
+
const hasDestinationFontRule = /\bdestination\b/.test(workbox) && /["']font["']/.test(workbox);
|
|
185
|
+
const fontsCovered = (hasGoogleApisRule && hasGstaticRule) || hasDestinationFontRule;
|
|
186
|
+
if (!fontsCovered) {
|
|
187
|
+
issues.push('index.html loads Google Fonts but workbox has no runtimeCaching for fonts.googleapis.com / fonts.gstatic.com');
|
|
188
|
+
suggestions.push('Add runtimeCaching CacheFirst rules for /^https:\\/\\/fonts\\.googleapis\\.com/ and /^https:\\/\\/fonts\\.gstatic\\.com/.');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Issue 3: assets in public/ in extensions not covered by globPatterns.
|
|
192
|
+
// Workbox supports multiple patterns in the array; we union the
|
|
193
|
+
// extensions across all of them, otherwise a config like
|
|
194
|
+
// `globPatterns: ["**/*.{js,css}", "**/*.wasm"]` would look like it
|
|
195
|
+
// omits wasm.
|
|
196
|
+
const covered = extractCoveredExtensions(workbox);
|
|
197
|
+
if (covered !== null && source.listDir) {
|
|
198
|
+
const uncovered = await findUncoveredAssets(source, PUBLIC_DIR, covered);
|
|
199
|
+
if (uncovered.length > 0) {
|
|
200
|
+
const sample = uncovered.slice(0, 3).join(', ');
|
|
201
|
+
issues.push(`web/public/ has files in extensions not in globPatterns: ${sample}${uncovered.length > 3 ? ` (+${uncovered.length - 3} more)` : ''}`);
|
|
202
|
+
const newExts = [...new Set([...covered, ...uncovered.map(extOf)])].filter(Boolean).join(',');
|
|
203
|
+
suggestions.push(`Extend globPatterns to cover them, e.g. \`**/*.{${newExts}}\`.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (issues.length === 0) {
|
|
207
|
+
return {
|
|
208
|
+
name: 'PWA offline correctness',
|
|
209
|
+
status: 'pass',
|
|
210
|
+
detail: 'workbox precaches everything, fonts cached, bundle cap raised',
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
name: 'PWA offline correctness',
|
|
215
|
+
status: 'warn',
|
|
216
|
+
detail: issues.join('; '),
|
|
217
|
+
suggestions,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Parse the numeric value assigned to `maximumFileSizeToCacheInBytes`.
|
|
222
|
+
* Handles simple integer literals (`10485760`), underscore separators
|
|
223
|
+
* (`10_485_760`), and `A * B * C` arithmetic chains that workbox docs
|
|
224
|
+
* recommend (`10 * 1024 * 1024`). Returns null if the key isn't present
|
|
225
|
+
* or the value isn't a recognisable arithmetic literal.
|
|
226
|
+
*/
|
|
227
|
+
function parseBundleCap(workbox) {
|
|
228
|
+
const m = workbox.match(/maximumFileSizeToCacheInBytes\s*:\s*([^,\n}]+)/);
|
|
229
|
+
if (!m)
|
|
230
|
+
return null;
|
|
231
|
+
const expr = (m[1] ?? '').trim().replace(/_/g, '');
|
|
232
|
+
// Strict: only digits, *, whitespace. Anything else (variable
|
|
233
|
+
// reference, function call) → can't evaluate, treat as unknown.
|
|
234
|
+
if (!/^[\d*\s]+$/.test(expr))
|
|
235
|
+
return null;
|
|
236
|
+
const parts = expr.split('*').map((s) => Number(s.trim()));
|
|
237
|
+
if (parts.some((n) => !Number.isFinite(n) || n < 0))
|
|
238
|
+
return null;
|
|
239
|
+
return parts.reduce((a, b) => a * b, 1);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Extracts the `{ ... }` body that follows the first match of `opener`,
|
|
243
|
+
* walking the source character-by-character to balance braces.
|
|
244
|
+
*
|
|
245
|
+
* Two source views are required:
|
|
246
|
+
* - `src` — the real source, used for the returned substring.
|
|
247
|
+
* - `code` — the stripped view (comments/strings blanked) with the
|
|
248
|
+
* same character offsets as `src`. Used for matching the opener and
|
|
249
|
+
* counting braces, so that `}` in a string doesn't close the block.
|
|
250
|
+
*
|
|
251
|
+
* Regex alone can't handle this because workbox blocks contain nested
|
|
252
|
+
* objects (`options: { expiration: {...} }`) and regex doesn't balance.
|
|
253
|
+
*/
|
|
254
|
+
function extractBalancedBlock(src, code, opener) {
|
|
255
|
+
const m = code.match(opener);
|
|
256
|
+
if (!m || m.index === undefined)
|
|
257
|
+
return null;
|
|
258
|
+
let i = m.index + m[0].length; // start just after the `{`
|
|
259
|
+
let depth = 1;
|
|
260
|
+
const start = i;
|
|
261
|
+
while (i < code.length) {
|
|
262
|
+
const c = code[i];
|
|
263
|
+
if (c === '{')
|
|
264
|
+
depth++;
|
|
265
|
+
else if (c === '}') {
|
|
266
|
+
depth--;
|
|
267
|
+
if (depth === 0)
|
|
268
|
+
return src.slice(start, i);
|
|
269
|
+
}
|
|
270
|
+
i++;
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Parses every string literal in the workbox block, finds those that
|
|
276
|
+
* look like glob patterns with a brace-expansion (e.g.
|
|
277
|
+
* `**\/*.{js,css,wasm}`), and unions their extensions. Returns null if
|
|
278
|
+
* no `globPatterns:` key is present at all.
|
|
279
|
+
*
|
|
280
|
+
* Bracket-balances the array body on a string-stripped view so that
|
|
281
|
+
* glob bracket expressions like `**\/[abc]/*` don't truncate the array
|
|
282
|
+
* — that `]` lives inside a string literal and isn't the array's close.
|
|
283
|
+
*/
|
|
284
|
+
function extractCoveredExtensions(workbox) {
|
|
285
|
+
const workboxCode = stripCommentsAndStrings(workbox);
|
|
286
|
+
const keyMatch = workboxCode.match(/globPatterns\s*:\s*\[/);
|
|
287
|
+
if (!keyMatch || keyMatch.index === undefined)
|
|
288
|
+
return null;
|
|
289
|
+
// Walk the stripped view to find the matching `]` for this `[`.
|
|
290
|
+
let i = keyMatch.index + keyMatch[0].length;
|
|
291
|
+
const start = i;
|
|
292
|
+
let depth = 1;
|
|
293
|
+
while (i < workboxCode.length && depth > 0) {
|
|
294
|
+
const c = workboxCode[i];
|
|
295
|
+
if (c === '[')
|
|
296
|
+
depth++;
|
|
297
|
+
else if (c === ']')
|
|
298
|
+
depth--;
|
|
299
|
+
if (depth === 0)
|
|
300
|
+
break;
|
|
301
|
+
i++;
|
|
302
|
+
}
|
|
303
|
+
if (depth !== 0)
|
|
304
|
+
return null;
|
|
305
|
+
// Pull the real (un-stripped) array body so we still see the
|
|
306
|
+
// quoted patterns.
|
|
307
|
+
const arrayBody = workbox.slice(start, i);
|
|
308
|
+
const covered = new Set();
|
|
309
|
+
for (const m of arrayBody.matchAll(/["']([^"']+)["']/g)) {
|
|
310
|
+
const pattern = m[1] ?? '';
|
|
311
|
+
const brace = pattern.match(/\{([^}]+)\}/);
|
|
312
|
+
if (brace) {
|
|
313
|
+
for (const ext of brace[1].split(',')) {
|
|
314
|
+
covered.add(ext.trim().toLowerCase());
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
// Bare pattern like "**/*.wasm" — extract the extension after the
|
|
319
|
+
// last `.` (or the entire pattern if it's literally `**/*.wasm`).
|
|
320
|
+
const ext = pattern.match(/\.([a-zA-Z0-9]+)$/);
|
|
321
|
+
if (ext)
|
|
322
|
+
covered.add(ext[1].toLowerCase());
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return covered;
|
|
326
|
+
}
|
|
327
|
+
function extOf(filename) {
|
|
328
|
+
const dot = filename.lastIndexOf('.');
|
|
329
|
+
return dot === -1 ? '' : filename.slice(dot + 1).toLowerCase();
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Walks web/public/ (one level deep is enough — workbox precache
|
|
333
|
+
* resolves the pattern recursively, but most public assets sit at the
|
|
334
|
+
* top level or one level down), returns extensions not in `covered`.
|
|
335
|
+
* Ignores the manifest icons and favicons we already know are listed.
|
|
336
|
+
*/
|
|
337
|
+
async function findUncoveredAssets(source, dir, covered) {
|
|
338
|
+
if (!source.listDir)
|
|
339
|
+
return [];
|
|
340
|
+
const seen = new Set();
|
|
341
|
+
await walkPublic(source, dir, seen, covered, 0);
|
|
342
|
+
return [...seen].sort();
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Best-effort scan of web/src/ entry points for a manual
|
|
346
|
+
* `serviceWorker.register` call. Doesn't recurse into the whole src
|
|
347
|
+
* tree — we only care about top-level entry files (main, index,
|
|
348
|
+
* registerSW) where this conventionally lives.
|
|
349
|
+
*/
|
|
350
|
+
async function sourceHasSwRegistration(source) {
|
|
351
|
+
const candidates = [
|
|
352
|
+
'web/src/main.tsx',
|
|
353
|
+
'web/src/main.ts',
|
|
354
|
+
'web/src/index.tsx',
|
|
355
|
+
'web/src/index.ts',
|
|
356
|
+
'web/src/registerSW.ts',
|
|
357
|
+
'web/src/registerSW.js',
|
|
358
|
+
];
|
|
359
|
+
for (const p of candidates) {
|
|
360
|
+
const text = await source.read(p);
|
|
361
|
+
if (text !== null && /serviceWorker\.register/.test(text))
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
async function walkPublic(source, dir, seen, covered, depth) {
|
|
367
|
+
if (depth > 3)
|
|
368
|
+
return;
|
|
369
|
+
if (!source.listDir)
|
|
370
|
+
return;
|
|
371
|
+
const entries = await source.listDir(dir);
|
|
372
|
+
if (entries === null)
|
|
373
|
+
return;
|
|
374
|
+
for (const name of entries) {
|
|
375
|
+
const full = `${dir}/${name}`;
|
|
376
|
+
if (name.includes('.')) {
|
|
377
|
+
const ext = extOf(name);
|
|
378
|
+
if (!ext || covered.has(ext))
|
|
379
|
+
continue;
|
|
380
|
+
seen.add(name);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
// Probably a subdirectory — recurse. listDir returns null if it's actually a file.
|
|
384
|
+
await walkPublic(source, full, seen, covered, depth + 1);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
//# sourceMappingURL=pwa-offline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pwa-offline.js","sourceRoot":"","sources":["../../src/checks/pwa-offline.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ export async function checkStoreLink(source) {
|
|
|
19
19
|
if (!path.startsWith('web/src/'))
|
|
20
20
|
continue;
|
|
21
21
|
const content = await source.read(path);
|
|
22
|
-
if (content
|
|
22
|
+
if (content?.includes(domain)) {
|
|
23
23
|
return { name: 'Store link', status: 'pass', detail: `${domain} referenced in ${path}` };
|
|
24
24
|
}
|
|
25
25
|
}
|