@freeappstore/compliance 0.6.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/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/viewport-support.d.ts +4 -1
- package/dist/checks/viewport-support.d.ts.map +1 -1
- package/dist/checks/viewport-support.js +27 -17
- package/dist/checks/viewport-support.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/package.json +2 -2
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import type { FileSource } from '../lib/file-source.js';
|
|
2
2
|
import type { CheckResult } from '../types.js';
|
|
3
3
|
/**
|
|
4
|
-
* Verifies the PWA manifest
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Verifies the PWA manifest declares the four required fields (name,
|
|
5
|
+
* short_name, start_url, display). Two valid sources, in order:
|
|
6
|
+
*
|
|
7
|
+
* 1. `web/public/manifest.json` — legacy static apps that ship their
|
|
8
|
+
* own manifest file.
|
|
9
|
+
* 2. The `manifest: { ... }` block inside `VitePWA({...})` in
|
|
10
|
+
* `web/vite.config.ts` — the path every game currently uses.
|
|
11
|
+
*
|
|
12
|
+
* Either is fine; both being missing fails.
|
|
7
13
|
*/
|
|
8
14
|
export declare function checkManifest(source: FileSource): Promise<CheckResult>;
|
|
9
15
|
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/checks/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/checks/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAoB5E"}
|
package/dist/checks/manifest.js
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
import { extractInlineManifest } from '../lib/inline-manifest.js';
|
|
2
|
+
const STATIC_MANIFEST_PATH = 'web/public/manifest.json';
|
|
3
|
+
const VITE_CONFIG_PATH = 'web/vite.config.ts';
|
|
2
4
|
/**
|
|
3
|
-
* Verifies the PWA manifest
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Verifies the PWA manifest declares the four required fields (name,
|
|
6
|
+
* short_name, start_url, display). Two valid sources, in order:
|
|
7
|
+
*
|
|
8
|
+
* 1. `web/public/manifest.json` — legacy static apps that ship their
|
|
9
|
+
* own manifest file.
|
|
10
|
+
* 2. The `manifest: { ... }` block inside `VitePWA({...})` in
|
|
11
|
+
* `web/vite.config.ts` — the path every game currently uses.
|
|
12
|
+
*
|
|
13
|
+
* Either is fine; both being missing fails.
|
|
6
14
|
*/
|
|
7
15
|
export async function checkManifest(source) {
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
return
|
|
11
|
-
name: 'PWA manifest',
|
|
12
|
-
status: 'fail',
|
|
13
|
-
detail: `${MANIFEST_PATH} missing`,
|
|
14
|
-
suggestions: [
|
|
15
|
-
'Add a manifest.json with at least name, short_name, start_url, display, icons.',
|
|
16
|
-
'See template-standalone for a working example.',
|
|
17
|
-
],
|
|
18
|
-
};
|
|
16
|
+
const staticRaw = await source.read(STATIC_MANIFEST_PATH);
|
|
17
|
+
if (staticRaw !== null) {
|
|
18
|
+
return validateStaticManifest(staticRaw);
|
|
19
19
|
}
|
|
20
|
+
const viteConfig = await source.read(VITE_CONFIG_PATH);
|
|
21
|
+
if (viteConfig !== null && /\bVitePWA\s*\(/.test(viteConfig)) {
|
|
22
|
+
return validateInlineManifest(viteConfig);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
name: 'PWA manifest',
|
|
26
|
+
status: 'fail',
|
|
27
|
+
detail: `no manifest source found (looked for ${STATIC_MANIFEST_PATH} and a VitePWA(...) block in ${VITE_CONFIG_PATH})`,
|
|
28
|
+
suggestions: [
|
|
29
|
+
'Add a VitePWA(...) plugin to web/vite.config.ts with an inline manifest:{...} block, OR',
|
|
30
|
+
'Add a static web/public/manifest.json with at least name, short_name, start_url, display, icons.',
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function validateStaticManifest(raw) {
|
|
20
35
|
let parsed;
|
|
21
36
|
try {
|
|
22
37
|
parsed = JSON.parse(raw);
|
|
@@ -25,7 +40,7 @@ export async function checkManifest(source) {
|
|
|
25
40
|
return {
|
|
26
41
|
name: 'PWA manifest',
|
|
27
42
|
status: 'fail',
|
|
28
|
-
detail: `${
|
|
43
|
+
detail: `${STATIC_MANIFEST_PATH} is not valid JSON`,
|
|
29
44
|
};
|
|
30
45
|
}
|
|
31
46
|
const required = ['name', 'short_name', 'start_url', 'display'];
|
|
@@ -34,10 +49,38 @@ export async function checkManifest(source) {
|
|
|
34
49
|
return {
|
|
35
50
|
name: 'PWA manifest',
|
|
36
51
|
status: 'warn',
|
|
37
|
-
detail: `missing fields: ${missing.join(', ')}`,
|
|
52
|
+
detail: `missing fields in ${STATIC_MANIFEST_PATH}: ${missing.join(', ')}`,
|
|
38
53
|
suggestions: [`Add the ${missing.join(', ')} field(s) to manifest.json so installs work.`],
|
|
39
54
|
};
|
|
40
55
|
}
|
|
41
|
-
return { name: 'PWA manifest', status: 'pass', detail:
|
|
56
|
+
return { name: 'PWA manifest', status: 'pass', detail: STATIC_MANIFEST_PATH };
|
|
57
|
+
}
|
|
58
|
+
function validateInlineManifest(viteConfig) {
|
|
59
|
+
const inline = extractInlineManifest(viteConfig);
|
|
60
|
+
if (!inline) {
|
|
61
|
+
return {
|
|
62
|
+
name: 'PWA manifest',
|
|
63
|
+
status: 'fail',
|
|
64
|
+
detail: `VitePWA(...) call found in ${VITE_CONFIG_PATH} but the manifest:{...} block could not be parsed`,
|
|
65
|
+
suggestions: [
|
|
66
|
+
'Add a top-level `manifest: { name, short_name, start_url, display, icons, ... }` to your VitePWA() options.',
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const required = ['name', 'short_name', 'start_url', 'display'];
|
|
71
|
+
const missing = required.filter((k) => typeof inline[k] !== 'string' || inline[k] === '');
|
|
72
|
+
if (missing.length > 0) {
|
|
73
|
+
return {
|
|
74
|
+
name: 'PWA manifest',
|
|
75
|
+
status: 'warn',
|
|
76
|
+
detail: `missing fields in inline manifest (${VITE_CONFIG_PATH}): ${missing.join(', ')}`,
|
|
77
|
+
suggestions: [`Add ${missing.join(', ')} to the manifest:{...} inside VitePWA({...}).`],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
name: 'PWA manifest',
|
|
82
|
+
status: 'pass',
|
|
83
|
+
detail: `inline in ${VITE_CONFIG_PATH} (name="${inline.name}", display="${inline.display}")`,
|
|
84
|
+
};
|
|
42
85
|
}
|
|
43
86
|
//# 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":"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"}
|
|
@@ -6,7 +6,7 @@ import type { CheckResult } from '../types.js';
|
|
|
6
6
|
* 92% of devices · portrait + landscape") and visitors know what to
|
|
7
7
|
* expect before they tap.
|
|
8
8
|
*
|
|
9
|
-
* Two declarations are required
|
|
9
|
+
* Two declarations are required:
|
|
10
10
|
*
|
|
11
11
|
* "orientation": "any" | "portrait" | "landscape" |
|
|
12
12
|
* "portrait-primary" | "landscape-primary"
|
|
@@ -19,6 +19,9 @@ import type { CheckResult } from '../types.js';
|
|
|
19
19
|
*
|
|
20
20
|
* If you genuinely don't care about orientation, set `"any"`. Don't
|
|
21
21
|
* leave it off — that's the only value that returns a fail.
|
|
22
|
+
*
|
|
23
|
+
* Read from either a static `web/public/manifest.json` or the inline
|
|
24
|
+
* `manifest:{...}` inside VitePWA(...) in `web/vite.config.ts`.
|
|
22
25
|
*/
|
|
23
26
|
export declare function checkViewportSupport(source: FileSource): Promise<CheckResult>;
|
|
24
27
|
//# sourceMappingURL=viewport-support.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport-support.d.ts","sourceRoot":"","sources":["../../src/checks/viewport-support.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"viewport-support.d.ts","sourceRoot":"","sources":["../../src/checks/viewport-support.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CA2DnF"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { extractInlineManifest } from '../lib/inline-manifest.js';
|
|
2
|
+
const STATIC_MANIFEST_PATH = 'web/public/manifest.json';
|
|
3
|
+
const VITE_CONFIG_PATH = 'web/vite.config.ts';
|
|
2
4
|
/**
|
|
3
5
|
* Apps and games must declare which screens and orientations they
|
|
4
6
|
* support, so the storefront can render a coverage badge ("works on
|
|
5
7
|
* 92% of devices · portrait + landscape") and visitors know what to
|
|
6
8
|
* expect before they tap.
|
|
7
9
|
*
|
|
8
|
-
* Two declarations are required
|
|
10
|
+
* Two declarations are required:
|
|
9
11
|
*
|
|
10
12
|
* "orientation": "any" | "portrait" | "landscape" |
|
|
11
13
|
* "portrait-primary" | "landscape-primary"
|
|
@@ -18,25 +20,17 @@ const MANIFEST_PATH = 'web/public/manifest.json';
|
|
|
18
20
|
*
|
|
19
21
|
* If you genuinely don't care about orientation, set `"any"`. Don't
|
|
20
22
|
* leave it off — that's the only value that returns a fail.
|
|
23
|
+
*
|
|
24
|
+
* Read from either a static `web/public/manifest.json` or the inline
|
|
25
|
+
* `manifest:{...}` inside VitePWA(...) in `web/vite.config.ts`.
|
|
21
26
|
*/
|
|
22
27
|
export async function checkViewportSupport(source) {
|
|
23
|
-
const
|
|
24
|
-
if (
|
|
25
|
-
return {
|
|
26
|
-
name: 'Viewport support',
|
|
27
|
-
status: 'fail',
|
|
28
|
-
detail: `${MANIFEST_PATH} not found`,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
let manifest;
|
|
32
|
-
try {
|
|
33
|
-
manifest = JSON.parse(raw);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
28
|
+
const manifest = await loadManifest(source);
|
|
29
|
+
if (!manifest) {
|
|
36
30
|
return {
|
|
37
31
|
name: 'Viewport support',
|
|
38
32
|
status: 'fail',
|
|
39
|
-
detail:
|
|
33
|
+
detail: `no manifest source found (looked for ${STATIC_MANIFEST_PATH} and a VitePWA(...) block in ${VITE_CONFIG_PATH})`,
|
|
40
34
|
};
|
|
41
35
|
}
|
|
42
36
|
const orientation = manifest.orientation;
|
|
@@ -63,7 +57,7 @@ export async function checkViewportSupport(source) {
|
|
|
63
57
|
detail: `${issues.length} viewport-declaration issue${issues.length === 1 ? '' : 's'}`,
|
|
64
58
|
suggestions: [
|
|
65
59
|
...issues,
|
|
66
|
-
'
|
|
60
|
+
'Add `"orientation": "any"` (or portrait/landscape) and `"min_viewport_width": 360` to the manifest — either in web/public/manifest.json or inside the manifest:{...} of VitePWA() in web/vite.config.ts.',
|
|
67
61
|
],
|
|
68
62
|
};
|
|
69
63
|
}
|
|
@@ -81,6 +75,22 @@ export async function checkViewportSupport(source) {
|
|
|
81
75
|
detail: `orientation=${orientation} · min ${minWidth}px`,
|
|
82
76
|
};
|
|
83
77
|
}
|
|
78
|
+
async function loadManifest(source) {
|
|
79
|
+
const staticRaw = await source.read(STATIC_MANIFEST_PATH);
|
|
80
|
+
if (staticRaw !== null) {
|
|
81
|
+
try {
|
|
82
|
+
return JSON.parse(staticRaw);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const viteConfig = await source.read(VITE_CONFIG_PATH);
|
|
89
|
+
if (viteConfig !== null && /\bVitePWA\s*\(/.test(viteConfig)) {
|
|
90
|
+
return extractInlineManifest(viteConfig);
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
84
94
|
const VALID_ORIENTATIONS = new Set([
|
|
85
95
|
'any',
|
|
86
96
|
'portrait',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport-support.js","sourceRoot":"","sources":["../../src/checks/viewport-support.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"viewport-support.js","sourceRoot":"","sources":["../../src/checks/viewport-support.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts the PWA manifest object that vite-plugin-pwa embeds inline in
|
|
3
|
+
* `web/vite.config.ts`. The plugin call looks like:
|
|
4
|
+
*
|
|
5
|
+
* VitePWA({
|
|
6
|
+
* registerType: 'autoUpdate',
|
|
7
|
+
* manifest: {
|
|
8
|
+
* name: 'My Game',
|
|
9
|
+
* short_name: 'My Game',
|
|
10
|
+
* start_url: '/',
|
|
11
|
+
* display: 'standalone',
|
|
12
|
+
* ...
|
|
13
|
+
* },
|
|
14
|
+
* })
|
|
15
|
+
*
|
|
16
|
+
* We don't run a full TS parser — that would pull in ts-morph or
|
|
17
|
+
* @babel/parser just for one field. Instead we balance braces from the
|
|
18
|
+
* `manifest:` keyword and pull primitive top-level keys out of the
|
|
19
|
+
* resulting block with focused regexes. Robust enough for the patterns
|
|
20
|
+
* the games actually use; falls back to `null` on anything weird.
|
|
21
|
+
*/
|
|
22
|
+
export interface InlineManifest {
|
|
23
|
+
name?: string | undefined;
|
|
24
|
+
short_name?: string | undefined;
|
|
25
|
+
start_url?: string | undefined;
|
|
26
|
+
display?: string | undefined;
|
|
27
|
+
orientation?: string | undefined;
|
|
28
|
+
min_viewport_width?: number | undefined;
|
|
29
|
+
icons?: unknown;
|
|
30
|
+
}
|
|
31
|
+
export declare function extractInlineManifest(viteConfig: string): InlineManifest | null;
|
|
32
|
+
//# sourceMappingURL=inline-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-manifest.d.ts","sourceRoot":"","sources":["../../src/lib/inline-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAmF/E"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts the PWA manifest object that vite-plugin-pwa embeds inline in
|
|
3
|
+
* `web/vite.config.ts`. The plugin call looks like:
|
|
4
|
+
*
|
|
5
|
+
* VitePWA({
|
|
6
|
+
* registerType: 'autoUpdate',
|
|
7
|
+
* manifest: {
|
|
8
|
+
* name: 'My Game',
|
|
9
|
+
* short_name: 'My Game',
|
|
10
|
+
* start_url: '/',
|
|
11
|
+
* display: 'standalone',
|
|
12
|
+
* ...
|
|
13
|
+
* },
|
|
14
|
+
* })
|
|
15
|
+
*
|
|
16
|
+
* We don't run a full TS parser — that would pull in ts-morph or
|
|
17
|
+
* @babel/parser just for one field. Instead we balance braces from the
|
|
18
|
+
* `manifest:` keyword and pull primitive top-level keys out of the
|
|
19
|
+
* resulting block with focused regexes. Robust enough for the patterns
|
|
20
|
+
* the games actually use; falls back to `null` on anything weird.
|
|
21
|
+
*/
|
|
22
|
+
export function extractInlineManifest(viteConfig) {
|
|
23
|
+
const start = viteConfig.search(/\bmanifest\s*:\s*\{/);
|
|
24
|
+
if (start < 0)
|
|
25
|
+
return null;
|
|
26
|
+
const openIdx = viteConfig.indexOf('{', start);
|
|
27
|
+
if (openIdx < 0)
|
|
28
|
+
return null;
|
|
29
|
+
// Balance braces to find the end of the manifest object literal.
|
|
30
|
+
// Handles nested objects (icons array entries) and quoted strings
|
|
31
|
+
// (so a `}` inside a quoted value isn't counted).
|
|
32
|
+
let depth = 0;
|
|
33
|
+
let inSingle = false;
|
|
34
|
+
let inDouble = false;
|
|
35
|
+
let inBacktick = false;
|
|
36
|
+
let escape = false;
|
|
37
|
+
let endIdx = -1;
|
|
38
|
+
for (let i = openIdx; i < viteConfig.length; i++) {
|
|
39
|
+
const ch = viteConfig[i];
|
|
40
|
+
if (escape) {
|
|
41
|
+
escape = false;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (ch === '\\') {
|
|
45
|
+
escape = true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (inSingle) {
|
|
49
|
+
if (ch === "'")
|
|
50
|
+
inSingle = false;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (inDouble) {
|
|
54
|
+
if (ch === '"')
|
|
55
|
+
inDouble = false;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (inBacktick) {
|
|
59
|
+
if (ch === '`')
|
|
60
|
+
inBacktick = false;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (ch === "'") {
|
|
64
|
+
inSingle = true;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (ch === '"') {
|
|
68
|
+
inDouble = true;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (ch === '`') {
|
|
72
|
+
inBacktick = true;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (ch === '{')
|
|
76
|
+
depth++;
|
|
77
|
+
else if (ch === '}') {
|
|
78
|
+
depth--;
|
|
79
|
+
if (depth === 0) {
|
|
80
|
+
endIdx = i;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (endIdx < 0)
|
|
86
|
+
return null;
|
|
87
|
+
const block = viteConfig.slice(openIdx + 1, endIdx);
|
|
88
|
+
const readString = (key) => {
|
|
89
|
+
const re = new RegExp(`(?:^|[\\s,])${key}\\s*:\\s*['"\`]([^'"\`]*)['"\`]`);
|
|
90
|
+
const m = block.match(re);
|
|
91
|
+
return m ? m[1] : undefined;
|
|
92
|
+
};
|
|
93
|
+
const readNumber = (key) => {
|
|
94
|
+
const re = new RegExp(`(?:^|[\\s,])${key}\\s*:\\s*(\\d+(?:\\.\\d+)?)`);
|
|
95
|
+
const m = block.match(re);
|
|
96
|
+
return m ? Number(m[1]) : undefined;
|
|
97
|
+
};
|
|
98
|
+
const hasIcons = /(?:^|[\s,])icons\s*:\s*\[/.test(block);
|
|
99
|
+
return {
|
|
100
|
+
name: readString('name'),
|
|
101
|
+
short_name: readString('short_name'),
|
|
102
|
+
start_url: readString('start_url'),
|
|
103
|
+
display: readString('display'),
|
|
104
|
+
orientation: readString('orientation'),
|
|
105
|
+
min_viewport_width: readNumber('min_viewport_width'),
|
|
106
|
+
icons: hasIcons || undefined,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=inline-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-manifest.js","sourceRoot":"","sources":["../../src/lib/inline-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAYH,MAAM,UAAU,qBAAqB,CAAC,UAAkB;IACtD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACvD,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/C,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7B,iEAAiE;IACjE,kEAAkE;IAClE,kDAAkD;IAClD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,CAAE,CAAC;QAC1B,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,KAAK,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,MAAM,GAAG,IAAI,CAAC;YACd,SAAS;QACX,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,EAAE,KAAK,GAAG;gBAAE,QAAQ,GAAG,KAAK,CAAC;YACjC,SAAS;QACX,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,EAAE,KAAK,GAAG;gBAAE,QAAQ,GAAG,KAAK,CAAC;YACjC,SAAS;QACX,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,EAAE,KAAK,GAAG;gBAAE,UAAU,GAAG,KAAK,CAAC;YACnC,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,GAAG,IAAI,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,GAAG,IAAI,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,UAAU,GAAG,IAAI,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;aACnB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACpB,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,MAAM,GAAG,CAAC,CAAC;gBACX,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;IAEpD,MAAM,UAAU,GAAG,CAAC,GAAW,EAAsB,EAAE;QACrD,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,eAAe,GAAG,iCAAiC,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9B,CAAC,CAAC;IACF,MAAM,UAAU,GAAG,CAAC,GAAW,EAAsB,EAAE;QACrD,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,eAAe,GAAG,6BAA6B,CAAC,CAAC;QACvE,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtC,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEzD,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC;QACxB,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC;QACpC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC;QAClC,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC;QAC9B,WAAW,EAAE,UAAU,CAAC,aAAa,CAAC;QACtC,kBAAkB,EAAE,UAAU,CAAC,oBAAoB,CAAC;QACpD,KAAK,EAAE,QAAQ,IAAI,SAAS;KAC7B,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freeappstore/compliance",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "Compliance checks for apps published on freeappstore.online
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "Compliance checks for apps published on freeappstore.online \u2014 used by the CLI for local feedback and by CI for gating.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|