@freeappstore/compliance 0.3.0 → 0.6.0
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/brand-fonts.d.ts +2 -1
- package/dist/checks/brand-fonts.d.ts.map +1 -1
- package/dist/checks/brand-fonts.js +11 -7
- package/dist/checks/brand-fonts.js.map +1 -1
- package/dist/checks/brand-tokens.d.ts +22 -0
- package/dist/checks/brand-tokens.d.ts.map +1 -0
- package/dist/checks/brand-tokens.js +53 -0
- package/dist/checks/brand-tokens.js.map +1 -0
- package/dist/checks/bundle-size.d.ts +7 -1
- package/dist/checks/bundle-size.d.ts.map +1 -1
- package/dist/checks/bundle-size.js +35 -15
- package/dist/checks/bundle-size.js.map +1 -1
- 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 +20 -0
- package/dist/checks/dark-mode.d.ts.map +1 -0
- package/dist/checks/dark-mode.js +56 -0
- package/dist/checks/dark-mode.js.map +1 -0
- package/dist/checks/html-meta.d.ts +16 -0
- package/dist/checks/html-meta.d.ts.map +1 -0
- package/dist/checks/html-meta.js +51 -0
- package/dist/checks/html-meta.js.map +1 -0
- package/dist/checks/license-mit.d.ts +13 -0
- package/dist/checks/license-mit.d.ts.map +1 -0
- package/dist/checks/license-mit.js +45 -0
- package/dist/checks/license-mit.js.map +1 -0
- package/dist/checks/manifest.d.ts +2 -1
- package/dist/checks/manifest.d.ts.map +1 -1
- package/dist/checks/manifest.js +7 -12
- package/dist/checks/manifest.js.map +1 -1
- package/dist/checks/no-brand-overrides.d.ts +2 -1
- package/dist/checks/no-brand-overrides.d.ts.map +1 -1
- package/dist/checks/no-brand-overrides.js +94 -30
- package/dist/checks/no-brand-overrides.js.map +1 -1
- package/dist/checks/no-env-production.d.ts +16 -0
- package/dist/checks/no-env-production.d.ts.map +1 -0
- package/dist/checks/no-env-production.js +37 -0
- package/dist/checks/no-env-production.js.map +1 -0
- package/dist/checks/no-placeholders.d.ts +2 -1
- package/dist/checks/no-placeholders.d.ts.map +1 -1
- package/dist/checks/no-placeholders.js +11 -9
- package/dist/checks/no-placeholders.js.map +1 -1
- package/dist/checks/no-scroll.d.ts +2 -1
- package/dist/checks/no-scroll.d.ts.map +1 -1
- package/dist/checks/no-scroll.js +13 -47
- package/dist/checks/no-scroll.js.map +1 -1
- package/dist/checks/no-tracking.d.ts +2 -1
- package/dist/checks/no-tracking.d.ts.map +1 -1
- package/dist/checks/no-tracking.js +77 -21
- package/dist/checks/no-tracking.js.map +1 -1
- package/dist/checks/pwa-meta.d.ts +19 -0
- package/dist/checks/pwa-meta.d.ts.map +1 -0
- package/dist/checks/pwa-meta.js +42 -0
- package/dist/checks/pwa-meta.js.map +1 -0
- 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.d.ts +17 -0
- package/dist/checks/store-link.d.ts.map +1 -0
- package/dist/checks/store-link.js +36 -0
- package/dist/checks/store-link.js.map +1 -0
- package/dist/checks/unsafe-vh.d.ts +2 -1
- package/dist/checks/unsafe-vh.d.ts.map +1 -1
- package/dist/checks/unsafe-vh.js +25 -15
- package/dist/checks/unsafe-vh.js.map +1 -1
- package/dist/checks/viewport-support.d.ts +2 -1
- package/dist/checks/viewport-support.d.ts.map +1 -1
- package/dist/checks/viewport-support.js +13 -8
- package/dist/checks/viewport-support.js.map +1 -1
- package/dist/index.d.ts +28 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -19
- package/dist/index.js.map +1 -1
- package/dist/lib/file-source.d.ts +52 -0
- package/dist/lib/file-source.d.ts.map +1 -0
- package/dist/lib/file-source.js +156 -0
- package/dist/lib/file-source.js.map +1 -0
- package/dist/lib/gzip.d.ts +8 -0
- package/dist/lib/gzip.d.ts.map +1 -0
- package/dist/lib/gzip.js +26 -0
- package/dist/lib/gzip.js.map +1 -0
- package/dist/lib/project-type.d.ts +18 -0
- package/dist/lib/project-type.d.ts.map +1 -0
- package/dist/lib/project-type.js +34 -0
- package/dist/lib/project-type.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 +14 -8
- package/dist/live/index.js.map +1 -1
- package/package.json +10 -10
- package/LICENSE +0 -21
- package/dist/checks/brand-fonts.test.d.ts +0 -2
- package/dist/checks/brand-fonts.test.d.ts.map +0 -1
- package/dist/checks/brand-fonts.test.js +0 -45
- package/dist/checks/brand-fonts.test.js.map +0 -1
- package/dist/checks/bundle-size.test.d.ts +0 -2
- package/dist/checks/bundle-size.test.d.ts.map +0 -1
- package/dist/checks/bundle-size.test.js +0 -57
- package/dist/checks/bundle-size.test.js.map +0 -1
- package/dist/checks/manifest.test.d.ts +0 -2
- package/dist/checks/manifest.test.d.ts.map +0 -1
- package/dist/checks/manifest.test.js +0 -53
- package/dist/checks/manifest.test.js.map +0 -1
- package/dist/checks/no-brand-overrides.test.d.ts +0 -2
- package/dist/checks/no-brand-overrides.test.d.ts.map +0 -1
- package/dist/checks/no-brand-overrides.test.js +0 -102
- package/dist/checks/no-brand-overrides.test.js.map +0 -1
- package/dist/checks/no-placeholders.test.d.ts +0 -2
- package/dist/checks/no-placeholders.test.d.ts.map +0 -1
- package/dist/checks/no-placeholders.test.js +0 -46
- package/dist/checks/no-placeholders.test.js.map +0 -1
- package/dist/checks/no-scroll.test.d.ts +0 -2
- package/dist/checks/no-scroll.test.d.ts.map +0 -1
- package/dist/checks/no-scroll.test.js +0 -105
- package/dist/checks/no-scroll.test.js.map +0 -1
- package/dist/checks/no-tracking.test.d.ts +0 -2
- package/dist/checks/no-tracking.test.d.ts.map +0 -1
- package/dist/checks/no-tracking.test.js +0 -42
- package/dist/checks/no-tracking.test.js.map +0 -1
- package/dist/checks/unsafe-vh.test.d.ts +0 -2
- package/dist/checks/unsafe-vh.test.d.ts.map +0 -1
- package/dist/checks/unsafe-vh.test.js +0 -115
- package/dist/checks/unsafe-vh.test.js.map +0 -1
- package/dist/checks/viewport-support.test.d.ts +0 -2
- package/dist/checks/viewport-support.test.d.ts.map +0 -1
- package/dist/checks/viewport-support.test.js +0 -63
- package/dist/checks/viewport-support.test.js.map +0 -1
- package/dist/lib/walk.d.ts +0 -6
- package/dist/lib/walk.d.ts.map +0 -1
- package/dist/lib/walk.js +0 -35
- package/dist/lib/walk.js.map +0 -1
- package/dist/live/index.test.d.ts +0 -2
- package/dist/live/index.test.d.ts.map +0 -1
- package/dist/live/index.test.js +0 -259
- package/dist/live/index.test.js.map +0 -1
package/dist/checks/manifest.js
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
import { join } from 'node:path';
|
|
1
|
+
const MANIFEST_PATH = 'web/public/manifest.json';
|
|
3
2
|
/**
|
|
4
3
|
* Verifies the PWA manifest exists at the expected location and parses
|
|
5
4
|
* to JSON. Apps without a manifest can't be "Add to Home Screen"-d on
|
|
6
5
|
* mobile; required by the platform.
|
|
7
6
|
*/
|
|
8
|
-
export async function checkManifest(
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
raw = await readFile(path, 'utf8');
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
7
|
+
export async function checkManifest(source) {
|
|
8
|
+
const raw = await source.read(MANIFEST_PATH);
|
|
9
|
+
if (raw === null) {
|
|
15
10
|
return {
|
|
16
11
|
name: 'PWA manifest',
|
|
17
12
|
status: 'fail',
|
|
18
|
-
detail:
|
|
13
|
+
detail: `${MANIFEST_PATH} missing`,
|
|
19
14
|
suggestions: [
|
|
20
15
|
'Add a manifest.json with at least name, short_name, start_url, display, icons.',
|
|
21
16
|
'See template-standalone for a working example.',
|
|
@@ -30,7 +25,7 @@ export async function checkManifest(repoDir) {
|
|
|
30
25
|
return {
|
|
31
26
|
name: 'PWA manifest',
|
|
32
27
|
status: 'fail',
|
|
33
|
-
detail:
|
|
28
|
+
detail: `${MANIFEST_PATH} is not valid JSON`,
|
|
34
29
|
};
|
|
35
30
|
}
|
|
36
31
|
const required = ['name', 'short_name', 'start_url', 'display'];
|
|
@@ -43,6 +38,6 @@ export async function checkManifest(repoDir) {
|
|
|
43
38
|
suggestions: [`Add the ${missing.join(', ')} field(s) to manifest.json so installs work.`],
|
|
44
39
|
};
|
|
45
40
|
}
|
|
46
|
-
return { name: 'PWA manifest', status: 'pass', detail:
|
|
41
|
+
return { name: 'PWA manifest', status: 'pass', detail: MANIFEST_PATH };
|
|
47
42
|
}
|
|
48
43
|
//# sourceMappingURL=manifest.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/checks/manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/checks/manifest.ts"],"names":[],"mappings":"AAGA,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAEjD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAkB;IACpD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7C,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,aAAa,UAAU;YAClC,WAAW,EAAE;gBACX,gFAAgF;gBAChF,gDAAgD;aACjD;SACF,CAAC;IACJ,CAAC;IAED,IAAI,MAAwF,CAAC;IAC7F,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,aAAa,oBAAoB;SAC7C,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,CAAU,CAAC;IACzE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAC/C,WAAW,EAAE,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,8CAA8C,CAAC;SAC3F,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FileSource } from '../lib/file-source.js';
|
|
1
2
|
import type { CheckResult } from '../types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Apps and games on the platform must look consistent with the storefront
|
|
@@ -17,7 +18,7 @@ import type { CheckResult } from '../types.js';
|
|
|
17
18
|
* red, etc.). We don't try to gate every hex literal — too noisy and
|
|
18
19
|
* too many false positives.
|
|
19
20
|
*/
|
|
20
|
-
export declare function checkNoBrandOverrides(
|
|
21
|
+
export declare function checkNoBrandOverrides(source: FileSource): Promise<CheckResult>;
|
|
21
22
|
export declare function scanContent(filename: string, content: string, opts?: {
|
|
22
23
|
skipVarRedefs?: boolean;
|
|
23
24
|
}): string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-brand-overrides.d.ts","sourceRoot":"","sources":["../../src/checks/no-brand-overrides.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-brand-overrides.d.ts","sourceRoot":"","sources":["../../src/checks/no-brand-overrides.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAgDpF;AA2DD,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACrC,MAAM,EAAE,CAmDV"}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { extname, relative } from 'node:path';
|
|
3
|
-
import { walk } from '../lib/walk.js';
|
|
1
|
+
import { stripCommentsOnly, stripCssComments } from '../lib/strip.js';
|
|
4
2
|
/**
|
|
5
3
|
* Apps and games on the platform must look consistent with the storefront
|
|
6
4
|
* and with each other — brand colors, fonts, and layout rhythm come from
|
|
@@ -19,23 +17,30 @@ import { walk } from '../lib/walk.js';
|
|
|
19
17
|
* red, etc.). We don't try to gate every hex literal — too noisy and
|
|
20
18
|
* too many false positives.
|
|
21
19
|
*/
|
|
22
|
-
export async function checkNoBrandOverrides(
|
|
20
|
+
export async function checkNoBrandOverrides(source) {
|
|
23
21
|
const issues = [];
|
|
24
|
-
for await (const
|
|
25
|
-
const ext =
|
|
22
|
+
for await (const path of source.list()) {
|
|
23
|
+
const ext = extOf(path);
|
|
26
24
|
if (!SCANNED_EXTS.has(ext))
|
|
27
25
|
continue;
|
|
28
|
-
const
|
|
29
|
-
if (!
|
|
26
|
+
const raw = await source.read(path);
|
|
27
|
+
if (!raw)
|
|
30
28
|
continue;
|
|
31
|
-
|
|
29
|
+
// Strip comment bodies so a `// font-family: "Comic Sans"` note
|
|
30
|
+
// doesn't fire. Preserve string-literal contents — the JSX value
|
|
31
|
+
// `fontFamily: "Comic Sans"` IS a string literal and is the thing
|
|
32
|
+
// we're auditing. For .css/.scss use the css-only stripper (no `//`
|
|
33
|
+
// syntax at the language level).
|
|
34
|
+
const content = ext === '.css' || ext === '.scss'
|
|
35
|
+
? stripCssComments(raw)
|
|
36
|
+
: stripCommentsOnly(raw);
|
|
32
37
|
// Canonical theme file: the platform's CSS variables ARE defined here.
|
|
33
38
|
// Apps own this file post-scaffold (they can technically modify token
|
|
34
39
|
// values, and we accept the imperfection — the loud places where a
|
|
35
40
|
// brand override actually causes inconsistency are inline styles +
|
|
36
41
|
// per-component CSS, which this check still covers).
|
|
37
|
-
const isThemeFile =
|
|
38
|
-
const fileIssues = scanContent(
|
|
42
|
+
const isThemeFile = path === 'web/src/index.css' || path === 'web/src/main.css';
|
|
43
|
+
const fileIssues = scanContent(path, content, { skipVarRedefs: isThemeFile });
|
|
39
44
|
issues.push(...fileIssues);
|
|
40
45
|
// Cap at 10 — beyond that the user has bigger problems and we don't
|
|
41
46
|
// want a 200-line failure report.
|
|
@@ -118,10 +123,6 @@ const ALLOWED_FONT_TOKENS = new Set([
|
|
|
118
123
|
export function scanContent(filename, content, opts = {}) {
|
|
119
124
|
const out = [];
|
|
120
125
|
// 1. CSS variable overrides for protected tokens.
|
|
121
|
-
// Match `--accent: <something>;` outside of var() invocations.
|
|
122
|
-
// The regex looks for "<two dashes><name>:" near the start of a line,
|
|
123
|
-
// which is how CSS declarations look. var(--accent) won't match because
|
|
124
|
-
// there's a `(` not a `:`.
|
|
125
126
|
if (!opts.skipVarRedefs) {
|
|
126
127
|
const varDeclRe = /(?:^|[\s;{,])(--[a-z-]+)\s*:/gim;
|
|
127
128
|
let m;
|
|
@@ -133,27 +134,90 @@ export function scanContent(filename, content, opts = {}) {
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
|
-
// 2. font-family overrides.
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
137
|
+
// 2. font-family overrides.
|
|
138
|
+
// Capture the value of every font-family / fontFamily declaration, then
|
|
139
|
+
// extract candidate font names. JSX values can be ternaries
|
|
140
|
+
// (`fontFamily: isFoo ? "Fraunces" : "Manrope"`), so we pull out every
|
|
141
|
+
// quoted string in the value and check each one separately. If there are
|
|
142
|
+
// no quoted strings, the value is treated as a CSS-style comma list
|
|
143
|
+
// (`font-family: Comic Sans, serif`).
|
|
144
|
+
const fontHeadRe = /(font-family|fontFamily)\s*[:=]/g;
|
|
145
|
+
const quotedRe = /(['"`])([^'"`\n]+)\1/g;
|
|
140
146
|
let m;
|
|
141
|
-
while ((m =
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
while ((m = fontHeadRe.exec(content)) !== null) {
|
|
148
|
+
const isJsx = m[1] === 'fontFamily';
|
|
149
|
+
const start = m.index + m[0].length;
|
|
150
|
+
const value = sliceFontValue(content, start, isJsx);
|
|
151
|
+
const quotedStrings = [...value.matchAll(quotedRe)].map((x) => x[2]);
|
|
152
|
+
const candidates = quotedStrings.length > 0 ? quotedStrings : [value];
|
|
153
|
+
let flagged = false;
|
|
154
|
+
for (const cand of candidates) {
|
|
155
|
+
const tokens = cand
|
|
156
|
+
.split(',')
|
|
157
|
+
.map((t) => t.trim().replace(/^["']|["']$/g, '').toLowerCase())
|
|
158
|
+
.filter(Boolean);
|
|
159
|
+
for (const t of tokens) {
|
|
160
|
+
if (!ALLOWED_FONT_TOKENS.has(t)) {
|
|
161
|
+
const line = lineNumberAt(content, m.index);
|
|
162
|
+
out.push(`${filename}:${line} non-brand font "${t}"`);
|
|
163
|
+
flagged = true;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
152
166
|
}
|
|
167
|
+
if (flagged)
|
|
168
|
+
break;
|
|
153
169
|
}
|
|
154
170
|
}
|
|
155
171
|
return out;
|
|
156
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Walk forward from `start` until we hit the end of the font-family value.
|
|
175
|
+
*
|
|
176
|
+
* CSS values end at `;` or `}` or newline.
|
|
177
|
+
* JSX values end at `}` or at a `,` followed by another identifier+colon
|
|
178
|
+
* (the next style property). Commas inside quoted strings or before
|
|
179
|
+
* non-identifiers (e.g. font fallbacks) are part of the value.
|
|
180
|
+
*/
|
|
181
|
+
function sliceFontValue(content, start, isJsx) {
|
|
182
|
+
let i = start;
|
|
183
|
+
let quote = null;
|
|
184
|
+
while (i < content.length) {
|
|
185
|
+
const c = content[i];
|
|
186
|
+
if (quote) {
|
|
187
|
+
if (c === '\\') {
|
|
188
|
+
i += 2;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (c === quote)
|
|
192
|
+
quote = null;
|
|
193
|
+
i++;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
197
|
+
quote = c;
|
|
198
|
+
i++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (c === '\n' || c === ';' || c === '}')
|
|
202
|
+
break;
|
|
203
|
+
if (isJsx && c === ',') {
|
|
204
|
+
// Lookahead: is this `,` followed by `identifier:`? If yes, end of value.
|
|
205
|
+
let j = i + 1;
|
|
206
|
+
while (j < content.length && (content[j] === ' ' || content[j] === '\t'))
|
|
207
|
+
j++;
|
|
208
|
+
const rest = content.slice(j, j + 64);
|
|
209
|
+
if (/^[A-Za-z_$][\w$]*\s*:/.test(rest))
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
i++;
|
|
213
|
+
}
|
|
214
|
+
return content.slice(start, i);
|
|
215
|
+
}
|
|
216
|
+
function extOf(path) {
|
|
217
|
+
const dot = path.lastIndexOf('.');
|
|
218
|
+
const slash = path.lastIndexOf('/');
|
|
219
|
+
return dot > slash ? path.slice(dot).toLowerCase() : '';
|
|
220
|
+
}
|
|
157
221
|
function lineNumberAt(content, index) {
|
|
158
222
|
let n = 1;
|
|
159
223
|
for (let i = 0; i < index; i++)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-brand-overrides.js","sourceRoot":"","sources":["../../src/checks/no-brand-overrides.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-brand-overrides.js","sourceRoot":"","sources":["../../src/checks/no-brand-overrides.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FileSource } from '../lib/file-source.js';
|
|
2
|
+
import type { CheckResult } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* `.env.production` is a frequent accidental commit — Vite reads it at
|
|
5
|
+
* build time, and it's the file most likely to contain real API keys
|
|
6
|
+
* for the production deploy. The platform's templates instruct creators
|
|
7
|
+
* to use Cloudflare Pages env vars or Wrangler secrets instead. This
|
|
8
|
+
* check is a safety net: even if the creator's gitignore misses it, we
|
|
9
|
+
* fail compliance before publish.
|
|
10
|
+
*
|
|
11
|
+
* Scope: any `.env.production` file anywhere in the repo. We don't try
|
|
12
|
+
* to differentiate "this one's empty" from "this one has secrets" —
|
|
13
|
+
* the file shouldn't exist at all.
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkNoEnvProduction(source: FileSource): Promise<CheckResult>;
|
|
16
|
+
//# sourceMappingURL=no-env-production.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-env-production.d.ts","sourceRoot":"","sources":["../../src/checks/no-env-production.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBnF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.env.production` is a frequent accidental commit — Vite reads it at
|
|
3
|
+
* build time, and it's the file most likely to contain real API keys
|
|
4
|
+
* for the production deploy. The platform's templates instruct creators
|
|
5
|
+
* to use Cloudflare Pages env vars or Wrangler secrets instead. This
|
|
6
|
+
* check is a safety net: even if the creator's gitignore misses it, we
|
|
7
|
+
* fail compliance before publish.
|
|
8
|
+
*
|
|
9
|
+
* Scope: any `.env.production` file anywhere in the repo. We don't try
|
|
10
|
+
* to differentiate "this one's empty" from "this one has secrets" —
|
|
11
|
+
* the file shouldn't exist at all.
|
|
12
|
+
*/
|
|
13
|
+
export async function checkNoEnvProduction(source) {
|
|
14
|
+
const offenders = [];
|
|
15
|
+
for await (const path of source.list()) {
|
|
16
|
+
const base = path.split('/').pop() ?? '';
|
|
17
|
+
if (base === '.env.production')
|
|
18
|
+
offenders.push(path);
|
|
19
|
+
}
|
|
20
|
+
if (offenders.length === 0) {
|
|
21
|
+
return {
|
|
22
|
+
name: 'No .env.production',
|
|
23
|
+
status: 'pass',
|
|
24
|
+
detail: 'no production env files committed',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
name: 'No .env.production',
|
|
29
|
+
status: 'fail',
|
|
30
|
+
detail: `${offenders.length} .env.production file(s): ${offenders.join(', ')}`,
|
|
31
|
+
suggestions: [
|
|
32
|
+
'Delete the file and add `.env.production` to .gitignore.',
|
|
33
|
+
'Use Cloudflare Pages → Settings → Environment Variables for production secrets instead.',
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=no-env-production.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-env-production.js","sourceRoot":"","sources":["../../src/checks/no-env-production.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAkB;IAC3D,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QACzC,IAAI,IAAI,KAAK,iBAAiB;YAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,mCAAmC;SAC5C,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,6BAA6B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC9E,WAAW,EAAE;YACX,0DAA0D;YAC1D,yFAAyF;SAC1F;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FileSource } from '../lib/file-source.js';
|
|
1
2
|
import type { CheckResult } from '../types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Catches the case where APPNAME placeholders from the template were
|
|
@@ -5,5 +6,5 @@ import type { CheckResult } from '../types.js';
|
|
|
5
6
|
* but if someone clones the template manually and forgets the find/
|
|
6
7
|
* replace step, every page renders with `<title>APPNAME — …</title>`.
|
|
7
8
|
*/
|
|
8
|
-
export declare function checkNoPlaceholders(
|
|
9
|
+
export declare function checkNoPlaceholders(source: FileSource): Promise<CheckResult>;
|
|
9
10
|
//# sourceMappingURL=no-placeholders.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-placeholders.d.ts","sourceRoot":"","sources":["../../src/checks/no-placeholders.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-placeholders.d.ts","sourceRoot":"","sources":["../../src/checks/no-placeholders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoB/C;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBlF"}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { extname } from 'node:path';
|
|
3
|
-
import { walk } from '../lib/walk.js';
|
|
4
1
|
const TEXT_EXTS = new Set([
|
|
5
2
|
'.md',
|
|
6
3
|
'.txt',
|
|
@@ -24,14 +21,14 @@ const TEXT_EXTS = new Set([
|
|
|
24
21
|
* but if someone clones the template manually and forgets the find/
|
|
25
22
|
* replace step, every page renders with `<title>APPNAME — …</title>`.
|
|
26
23
|
*/
|
|
27
|
-
export async function checkNoPlaceholders(
|
|
24
|
+
export async function checkNoPlaceholders(source) {
|
|
28
25
|
const offenders = [];
|
|
29
|
-
for await (const
|
|
30
|
-
if (!TEXT_EXTS.has(
|
|
26
|
+
for await (const path of source.list()) {
|
|
27
|
+
if (!TEXT_EXTS.has(extOf(path)))
|
|
31
28
|
continue;
|
|
32
|
-
const content = await
|
|
33
|
-
if (content
|
|
34
|
-
offenders.push(
|
|
29
|
+
const content = await source.read(path);
|
|
30
|
+
if (content?.includes('APPNAME')) {
|
|
31
|
+
offenders.push(path);
|
|
35
32
|
if (offenders.length >= 5)
|
|
36
33
|
break; // don't spam, summarize
|
|
37
34
|
}
|
|
@@ -49,4 +46,9 @@ export async function checkNoPlaceholders(repoDir) {
|
|
|
49
46
|
],
|
|
50
47
|
};
|
|
51
48
|
}
|
|
49
|
+
function extOf(path) {
|
|
50
|
+
const dot = path.lastIndexOf('.');
|
|
51
|
+
const slash = path.lastIndexOf('/');
|
|
52
|
+
return dot > slash ? path.slice(dot).toLowerCase() : '';
|
|
53
|
+
}
|
|
52
54
|
//# sourceMappingURL=no-placeholders.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-placeholders.js","sourceRoot":"","sources":["../../src/checks/no-placeholders.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-placeholders.js","sourceRoot":"","sources":["../../src/checks/no-placeholders.ts"],"names":[],"mappings":"AAGA,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,KAAK;IACL,MAAM;IACN,OAAO;IACP,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,MAAM;CACP,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAkB;IAC1D,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAAE,SAAS;QAC1C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC;gBAAE,MAAM,CAAC,wBAAwB;QAC5D,CAAC;IACH,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,IAAI,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;IACjG,CAAC;IACD,OAAO;QACL,IAAI,EAAE,0BAA0B;QAChC,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,mCAAmC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACpH,WAAW,EAAE;YACX,0FAA0F;YAC1F,mFAAmF;SACpF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CAAC,IAAY;IACzB,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1D,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FileSource } from '../lib/file-source.js';
|
|
1
2
|
import type { CheckResult } from '../types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Games on FreeGameStore must fit the viewport — no horizontal or
|
|
@@ -20,5 +21,5 @@ import type { CheckResult } from '../types.js';
|
|
|
20
21
|
* detect "is this a game project" by looking for the @freeappstore/games
|
|
21
22
|
* dep, the canonical signal that the app uses GameShell.
|
|
22
23
|
*/
|
|
23
|
-
export declare function checkNoScroll(
|
|
24
|
+
export declare function checkNoScroll(source: FileSource): Promise<CheckResult>;
|
|
24
25
|
//# sourceMappingURL=no-scroll.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-scroll.d.ts","sourceRoot":"","sources":["../../src/checks/no-scroll.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-scroll.d.ts","sourceRoot":"","sources":["../../src/checks/no-scroll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAuE5E"}
|
package/dist/checks/no-scroll.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { extname, relative } from 'node:path';
|
|
3
|
-
import { walk } from '../lib/walk.js';
|
|
1
|
+
import { isGameProject } from '../lib/project-type.js';
|
|
4
2
|
/**
|
|
5
3
|
* Games on FreeGameStore must fit the viewport — no horizontal or
|
|
6
4
|
* vertical scroll at the document level. Static analysis catches the
|
|
@@ -22,9 +20,9 @@ import { walk } from '../lib/walk.js';
|
|
|
22
20
|
* detect "is this a game project" by looking for the @freeappstore/games
|
|
23
21
|
* dep, the canonical signal that the app uses GameShell.
|
|
24
22
|
*/
|
|
25
|
-
export async function checkNoScroll(
|
|
23
|
+
export async function checkNoScroll(source) {
|
|
26
24
|
// Skip apps — only games are subject to no-scroll.
|
|
27
|
-
if (!(await
|
|
25
|
+
if (!(await isGameProject(source))) {
|
|
28
26
|
return {
|
|
29
27
|
name: 'No scroll (games only)',
|
|
30
28
|
status: 'pass',
|
|
@@ -33,33 +31,27 @@ export async function checkNoScroll(repoDir) {
|
|
|
33
31
|
}
|
|
34
32
|
const issues = [];
|
|
35
33
|
let sawViewportLock = false;
|
|
36
|
-
for await (const
|
|
37
|
-
const ext =
|
|
34
|
+
for await (const path of source.list()) {
|
|
35
|
+
const ext = extOf(path);
|
|
38
36
|
if (!SCAN_EXTS.has(ext))
|
|
39
37
|
continue;
|
|
40
|
-
const content = await
|
|
38
|
+
const content = await source.read(path);
|
|
41
39
|
if (!content)
|
|
42
40
|
continue;
|
|
43
|
-
const rel = relative(repoDir, file);
|
|
44
|
-
// 1. Forbidden overflow declarations on root elements.
|
|
45
41
|
for (const re of FORBIDDEN_OVERFLOW) {
|
|
46
42
|
const m = re.exec(content);
|
|
47
43
|
if (m) {
|
|
48
44
|
const line = lineNumberAt(content, m.index);
|
|
49
|
-
issues.push(`${
|
|
45
|
+
issues.push(`${path}:${line} ${m[0]} — root scrolling not allowed in games`);
|
|
50
46
|
}
|
|
51
47
|
}
|
|
52
|
-
// 2. min-height on root elements (creates pages taller than viewport).
|
|
53
48
|
for (const re of FORBIDDEN_MIN_HEIGHT) {
|
|
54
49
|
const m = re.exec(content);
|
|
55
50
|
if (m) {
|
|
56
51
|
const line = lineNumberAt(content, m.index);
|
|
57
|
-
issues.push(`${
|
|
52
|
+
issues.push(`${path}:${line} ${m[0]} — use exact viewport height (100svh) instead`);
|
|
58
53
|
}
|
|
59
54
|
}
|
|
60
|
-
// 3. Look for at least one viewport-lock pattern. GameShell sets
|
|
61
|
-
// `height: 100svh` on a fixed wrapper; we accept any 100svh / 100vh
|
|
62
|
-
// declaration as evidence the game considered viewport sizing.
|
|
63
55
|
if (VIEWPORT_LOCK.test(content))
|
|
64
56
|
sawViewportLock = true;
|
|
65
57
|
if (issues.length >= 8)
|
|
@@ -96,42 +88,16 @@ export async function checkNoScroll(repoDir) {
|
|
|
96
88
|
};
|
|
97
89
|
}
|
|
98
90
|
const SCAN_EXTS = new Set(['.css', '.scss', '.tsx', '.ts', '.jsx', '.js', '.html']);
|
|
99
|
-
// Match `overflow: scroll` and `overflow: auto` on html/body selectors,
|
|
100
|
-
// or as bare global rules in CSS. Conservative: only flags when paired
|
|
101
|
-
// with html/body — avoids false positives for component-scoped overflow.
|
|
102
|
-
//
|
|
103
|
-
// IMPORTANT: no `g` flag. Module-scoped regexes with `g` share lastIndex
|
|
104
|
-
// across parallel test runs and produce flaky matches; we only need
|
|
105
|
-
// first-match-per-file anyway.
|
|
106
91
|
const FORBIDDEN_OVERFLOW = [
|
|
107
92
|
/(?:^|[\s,{])(?:html|body)\s*\{[^}]*overflow\s*:\s*(?:scroll|auto)/im,
|
|
108
93
|
/(?:^|[\s,{])(?:html|body)\s*\{[^}]*overflow-(?:x|y)\s*:\s*(?:scroll|auto)/im,
|
|
109
94
|
];
|
|
110
|
-
const FORBIDDEN_MIN_HEIGHT = [
|
|
111
|
-
/(?:^|[\s,{])(?:html|body)\s*\{[^}]*min-height\s*:\s*100vh/im,
|
|
112
|
-
];
|
|
95
|
+
const FORBIDDEN_MIN_HEIGHT = [/(?:^|[\s,{])(?:html|body)\s*\{[^}]*min-height\s*:\s*100vh/im];
|
|
113
96
|
const VIEWPORT_LOCK = /(?:height|max-height)\s*:\s*100(?:s?vh)|GameShell|@freeappstore\/games/i;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for await (const file of walk(repoDir)) {
|
|
119
|
-
const base = file.split('/').pop() ?? '';
|
|
120
|
-
if (base !== 'package.json')
|
|
121
|
-
continue;
|
|
122
|
-
const content = await readFile(file, 'utf8').catch(() => '');
|
|
123
|
-
if (/@freeappstore\/games/.test(content))
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
for await (const file of walk(repoDir)) {
|
|
127
|
-
const ext = extname(file).toLowerCase();
|
|
128
|
-
if (ext !== '.tsx' && ext !== '.ts')
|
|
129
|
-
continue;
|
|
130
|
-
const content = await readFile(file, 'utf8').catch(() => '');
|
|
131
|
-
if (/from\s+['"]@freeappstore\/games['"]/.test(content))
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
|
-
return false;
|
|
97
|
+
function extOf(path) {
|
|
98
|
+
const dot = path.lastIndexOf('.');
|
|
99
|
+
const slash = path.lastIndexOf('/');
|
|
100
|
+
return dot > slash ? path.slice(dot).toLowerCase() : '';
|
|
135
101
|
}
|
|
136
102
|
function lineNumberAt(content, index) {
|
|
137
103
|
let n = 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-scroll.js","sourceRoot":"","sources":["../../src/checks/no-scroll.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-scroll.js","sourceRoot":"","sources":["../../src/checks/no-scroll.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FileSource } from '../lib/file-source.js';
|
|
1
2
|
import type { CheckResult } from '../types.js';
|
|
2
|
-
export declare function checkNoTracking(
|
|
3
|
+
export declare function checkNoTracking(source: FileSource): Promise<CheckResult>;
|
|
3
4
|
//# sourceMappingURL=no-tracking.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-tracking.d.ts","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"
|
|
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"}
|