@mapled/cli 0.10.0 → 0.12.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/README.md +54 -3
- package/dist/commands.d.ts +9 -0
- package/dist/commands.js +103 -2
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +99 -1
- package/dist/index.js +11 -2
- package/dist/live.d.ts +156 -0
- package/dist/live.js +1509 -0
- package/dist/manifest.d.ts +14 -0
- package/dist/manifest.js +54 -2
- package/dist/pin.js +7 -0
- package/dist/report.d.ts +127 -0
- package/dist/report.js +425 -0
- package/dist/scan.d.ts +22 -2
- package/dist/scan.js +126 -23
- package/dist/schema.d.ts +3 -0
- package/dist/schema.js +2 -0
- package/package.json +2 -2
package/dist/scan.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { targetFor } from "./manifest.js";
|
|
4
|
+
import { sameCapabilities, targetFor } from "./manifest.js";
|
|
5
5
|
const SKIP_DIRS = new Set([
|
|
6
6
|
"node_modules",
|
|
7
7
|
".git",
|
|
@@ -33,7 +33,7 @@ export function loadTypeScript(dir) {
|
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
async function listFiles(dir, max) {
|
|
36
|
+
export async function listFiles(dir, max) {
|
|
37
37
|
const files = [];
|
|
38
38
|
let truncated = false;
|
|
39
39
|
const walk = async (rel) => {
|
|
@@ -73,7 +73,7 @@ async function listFiles(dir, max) {
|
|
|
73
73
|
const PAGE_EXT = /\.(tsx|jsx|ts|js|mjs|mdx|md|astro|svelte|vue)$/;
|
|
74
74
|
/** Next.js App Router segments: groups and slots vanish, params keep
|
|
75
75
|
the [param] form the manifest uses. */
|
|
76
|
-
function appRoute(segments) {
|
|
76
|
+
export function appRoute(segments) {
|
|
77
77
|
const kept = [];
|
|
78
78
|
for (const s of segments) {
|
|
79
79
|
if (s.startsWith("_"))
|
|
@@ -753,18 +753,88 @@ function scopeOf(ts, id) {
|
|
|
753
753
|
}
|
|
754
754
|
return id.getSourceFile();
|
|
755
755
|
}
|
|
756
|
+
// what JavaScript reads as an identifier: Unicode letters and digits, `$`, `_`, and the two joiners inside one
|
|
757
|
+
const ID_START = /^[\p{ID_Start}$_]$/u;
|
|
758
|
+
const ID_PART = /^[\p{ID_Continue}$\p{Join_Control}]$/u;
|
|
759
|
+
/** The identifier character at `at` as JavaScript reads it — itself, or a
|
|
760
|
+
`\u` escape that reads as one — and the index after it; null when none
|
|
761
|
+
is there. */
|
|
762
|
+
function identCharAt(text, at, start) {
|
|
763
|
+
const c = text.charCodeAt(at);
|
|
764
|
+
if (Number.isNaN(c))
|
|
765
|
+
return null;
|
|
766
|
+
const test = start ? ID_START : ID_PART;
|
|
767
|
+
if (c === 92) {
|
|
768
|
+
if (text[at + 1] !== "u")
|
|
769
|
+
return null;
|
|
770
|
+
const [read, next] = unescapeAt(text, at);
|
|
771
|
+
// `\u` without its hex digits is no escape
|
|
772
|
+
return next > at + 2 && test.test(read) ? [read, next] : null;
|
|
773
|
+
}
|
|
774
|
+
// ASCII by its code: letters, `$`, `_`, and digits after the first
|
|
775
|
+
if (c < 128)
|
|
776
|
+
return (c >= 97 && c <= 122) || (c >= 65 && c <= 90) || c === 36 || c === 95 || (!start && c >= 48 && c <= 57) ? [text[at], at + 1] : null;
|
|
777
|
+
const ch = String.fromCodePoint(text.codePointAt(at));
|
|
778
|
+
return test.test(ch) ? [ch, at + ch.length] : null;
|
|
779
|
+
}
|
|
780
|
+
// `\n`, `\t` … by the letter, as character codes
|
|
781
|
+
const SIMPLE_ESCAPES = { n: 10, r: 13, t: 9, b: 8, f: 12, v: 11 };
|
|
782
|
+
/** The escape at `at` (a backslash) as JavaScript reads it in a string —
|
|
783
|
+
`\x6c`, `l`, `\u{6c}`, `\154`, `\n`, a line continuation, any other
|
|
784
|
+
character as itself — and the index after it. */
|
|
785
|
+
function unescapeAt(text, at) {
|
|
786
|
+
const e = text[at + 1];
|
|
787
|
+
if (e === undefined)
|
|
788
|
+
return ["", at + 1];
|
|
789
|
+
const simple = SIMPLE_ESCAPES[e];
|
|
790
|
+
if (simple !== undefined)
|
|
791
|
+
return [String.fromCharCode(simple), at + 2];
|
|
792
|
+
const code = e.charCodeAt(0);
|
|
793
|
+
// a line continuation: the backslash and the line break read as nothing
|
|
794
|
+
if (e === "\r")
|
|
795
|
+
return ["", text[at + 2] === "\n" ? at + 3 : at + 2];
|
|
796
|
+
if (e === "\n" || code === 0x2028 || code === 0x2029)
|
|
797
|
+
return ["", at + 2];
|
|
798
|
+
if (e === "x" && /^[\da-f]{2}$/i.test(text.slice(at + 2, at + 4)))
|
|
799
|
+
return [String.fromCharCode(parseInt(text.slice(at + 2, at + 4), 16)), at + 4];
|
|
800
|
+
if (e === "u") {
|
|
801
|
+
if (text[at + 2] === "{") {
|
|
802
|
+
const close = text.indexOf("}", at + 3);
|
|
803
|
+
const hex = close === -1 ? "" : text.slice(at + 3, close);
|
|
804
|
+
if (/^[\da-f]{1,6}$/i.test(hex) && parseInt(hex, 16) <= 0x10ffff)
|
|
805
|
+
return [String.fromCodePoint(parseInt(hex, 16)), close + 1];
|
|
806
|
+
}
|
|
807
|
+
else if (/^[\da-f]{4}$/i.test(text.slice(at + 2, at + 6)))
|
|
808
|
+
return [String.fromCharCode(parseInt(text.slice(at + 2, at + 6), 16)), at + 6];
|
|
809
|
+
}
|
|
810
|
+
// a legacy octal escape: up to three digits, the value under 256
|
|
811
|
+
const octal = /^[0-3][0-7]{0,2}|^[4-7][0-7]?/.exec(text.slice(at + 1, at + 4));
|
|
812
|
+
if (octal)
|
|
813
|
+
return [String.fromCharCode(parseInt(octal[0], 8)), at + 1 + octal[0].length];
|
|
814
|
+
return [e, at + 2];
|
|
815
|
+
}
|
|
756
816
|
/** JavaScript tokens: identifiers, string literals and punctuation, with
|
|
757
817
|
comments and template bodies dropped. A regex literal after an
|
|
758
|
-
operator is skipped
|
|
818
|
+
operator is skipped — only one that closes on its line, and never the
|
|
819
|
+
`/>` after a JSX attribute's `}`; JSX text is read as code, so an apostrophe in
|
|
759
820
|
prose can swallow a stretch — a false negative, never a crash. */
|
|
760
821
|
export function tokenize(text) {
|
|
761
822
|
const out = [];
|
|
762
823
|
let i = 0;
|
|
763
824
|
const n = text.length;
|
|
764
825
|
const REGEX_BEFORE = new Set(["(", ",", "=", ":", "[", "!", "&", "|", "?", "{", "}", ";", "return", "typeof", "=>"]);
|
|
826
|
+
let nl = false;
|
|
827
|
+
const push = (token) => {
|
|
828
|
+
if (nl)
|
|
829
|
+
token.nl = true;
|
|
830
|
+
nl = false;
|
|
831
|
+
out.push(token);
|
|
832
|
+
};
|
|
765
833
|
while (i < n) {
|
|
766
834
|
const c = text[i];
|
|
767
835
|
if (c === " " || c === "\t" || c === "\n" || c === "\r") {
|
|
836
|
+
if (c === "\n" || c === "\r")
|
|
837
|
+
nl = true;
|
|
768
838
|
i++;
|
|
769
839
|
continue;
|
|
770
840
|
}
|
|
@@ -775,16 +845,22 @@ export function tokenize(text) {
|
|
|
775
845
|
}
|
|
776
846
|
if (c === "/" && text[i + 1] === "*") {
|
|
777
847
|
const end = text.indexOf("*/", i + 2);
|
|
848
|
+
// a comment across lines is a line break
|
|
849
|
+
if (/[\n\r]/.test(text.slice(i + 2, end === -1 ? n : end)))
|
|
850
|
+
nl = true;
|
|
778
851
|
i = end === -1 ? n : end + 2;
|
|
779
852
|
continue;
|
|
780
853
|
}
|
|
781
854
|
if (c === '"' || c === "'" || c === "`") {
|
|
782
855
|
let j = i + 1;
|
|
783
856
|
let value = "";
|
|
857
|
+
let escaped = false;
|
|
784
858
|
while (j < n && text[j] !== c) {
|
|
785
859
|
if (text[j] === "\\") {
|
|
786
|
-
|
|
787
|
-
|
|
860
|
+
const [read, next] = unescapeAt(text, j);
|
|
861
|
+
value += read;
|
|
862
|
+
j = next;
|
|
863
|
+
escaped = true;
|
|
788
864
|
continue;
|
|
789
865
|
}
|
|
790
866
|
if (c === "`" && text[j] === "$" && text[j + 1] === "{") {
|
|
@@ -801,29 +877,51 @@ export function tokenize(text) {
|
|
|
801
877
|
value += "${}";
|
|
802
878
|
continue;
|
|
803
879
|
}
|
|
804
|
-
if (c !== "`" && text[j] === "\n")
|
|
880
|
+
if (c !== "`" && (text[j] === "\n" || text[j] === "\r"))
|
|
805
881
|
break;
|
|
806
882
|
value += text[j];
|
|
807
883
|
j++;
|
|
808
884
|
}
|
|
809
|
-
|
|
885
|
+
const token = { kind: "string", value: c === "`" && value.includes("${}") ? "${}" : value };
|
|
886
|
+
if (escaped)
|
|
887
|
+
token.escaped = true;
|
|
888
|
+
push(token);
|
|
889
|
+
// cut off by a line break: the break stays the next token's
|
|
890
|
+
if (text[j] !== c) {
|
|
891
|
+
token.unclosed = true;
|
|
892
|
+
i = j;
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
810
895
|
i = j + 1;
|
|
811
896
|
continue;
|
|
812
897
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
898
|
+
// an identifier, escapes decoded: `end\u0070oint` is `endpoint`
|
|
899
|
+
const first = identCharAt(text, i, true);
|
|
900
|
+
if (first) {
|
|
901
|
+
let value = first[0];
|
|
902
|
+
let escaped = c === "\\";
|
|
903
|
+
let j = first[1];
|
|
904
|
+
for (let part = identCharAt(text, j, false); part; part = identCharAt(text, j, false)) {
|
|
905
|
+
if (text[j] === "\\")
|
|
906
|
+
escaped = true;
|
|
907
|
+
value += part[0];
|
|
908
|
+
j = part[1];
|
|
909
|
+
}
|
|
910
|
+
const token = { kind: "ident", value };
|
|
911
|
+
if (escaped)
|
|
912
|
+
token.escaped = true;
|
|
913
|
+
push(token);
|
|
818
914
|
i = j;
|
|
819
915
|
continue;
|
|
820
916
|
}
|
|
821
917
|
if (c === "/") {
|
|
822
918
|
const prev = out[out.length - 1];
|
|
823
|
-
|
|
919
|
+
// `/>` after `}` closes a JSX element whose last attribute is an expression
|
|
920
|
+
const selfClosing = prev?.kind === "punct" && prev.value === "}" && text[i + 1] === ">";
|
|
921
|
+
if (!selfClosing && (!prev || (prev.kind === "punct" && REGEX_BEFORE.has(prev.value)) || (prev.kind === "ident" && REGEX_BEFORE.has(prev.value)))) {
|
|
824
922
|
let j = i + 1;
|
|
825
923
|
let inClass = false;
|
|
826
|
-
while (j < n && (inClass || text[j] !== "/") && text[j] !== "\n") {
|
|
924
|
+
while (j < n && (inClass || text[j] !== "/") && text[j] !== "\n" && text[j] !== "\r") {
|
|
827
925
|
if (text[j] === "\\")
|
|
828
926
|
j++;
|
|
829
927
|
else if (text[j] === "[")
|
|
@@ -832,19 +930,22 @@ export function tokenize(text) {
|
|
|
832
930
|
inClass = false;
|
|
833
931
|
j++;
|
|
834
932
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
j++;
|
|
838
|
-
|
|
839
|
-
|
|
933
|
+
// a regex closes on its own line; a `/` that doesn't is the code's
|
|
934
|
+
if (text[j] === "/") {
|
|
935
|
+
j++;
|
|
936
|
+
while (j < n && /[a-z]/.test(text[j]))
|
|
937
|
+
j++; // flags
|
|
938
|
+
i = j;
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
840
941
|
}
|
|
841
942
|
}
|
|
842
943
|
if (c === "=" && text[i + 1] === ">") {
|
|
843
|
-
|
|
944
|
+
push({ kind: "punct", value: "=>" });
|
|
844
945
|
i += 2;
|
|
845
946
|
continue;
|
|
846
947
|
}
|
|
847
|
-
|
|
948
|
+
push({ kind: "punct", value: c });
|
|
848
949
|
i++;
|
|
849
950
|
}
|
|
850
951
|
return out;
|
|
@@ -1293,6 +1394,8 @@ export function mergeManifest(existing, scan, opts) {
|
|
|
1293
1394
|
pages: scan.pages,
|
|
1294
1395
|
bindings: out,
|
|
1295
1396
|
...(existing?.notes && existing.notes.length > 0 ? { notes: existing.notes } : {}),
|
|
1397
|
+
...(scan.capabilities ? { capabilities: scan.capabilities } : existing?.capabilities ? { capabilities: existing.capabilities } : {}),
|
|
1296
1398
|
};
|
|
1297
|
-
|
|
1399
|
+
const capabilitiesChanged = existing !== null && !sameCapabilities(existing.capabilities, manifest.capabilities);
|
|
1400
|
+
return { manifest, added, changed, unchanged, kept, dropped, capabilitiesChanged };
|
|
1298
1401
|
}
|
package/dist/schema.d.ts
CHANGED
|
@@ -41,6 +41,9 @@ export type SchemaField = {
|
|
|
41
41
|
accepts it under these too until they are removed. Not part of the
|
|
42
42
|
schema hash — a rename changes the key, which is. */
|
|
43
43
|
aliases?: string[];
|
|
44
|
+
/** Translated (§28.2): the field holds a value per language; the site
|
|
45
|
+
reads the default language's. Absent on older servers. */
|
|
46
|
+
localized?: boolean;
|
|
44
47
|
};
|
|
45
48
|
export type SchemaCollection = {
|
|
46
49
|
key: string;
|
package/dist/schema.js
CHANGED
|
@@ -31,6 +31,8 @@ export function schemaHash(schema) {
|
|
|
31
31
|
: null,
|
|
32
32
|
// present only for computed fields, so schemas without formulas hash as before
|
|
33
33
|
...(f.type === "computed" && f.computed ? { computed: { expression: f.computed.expression, resultType: f.computed.resultType } } : {}),
|
|
34
|
+
// present only for translated fields, so schemas without translations hash as before
|
|
35
|
+
...(f.localized === true ? { localized: true } : {}),
|
|
34
36
|
})),
|
|
35
37
|
}));
|
|
36
38
|
return createHash("sha256").update(JSON.stringify(projection)).digest("hex").slice(0, 12);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapled/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Mapled CLI — sign in, link a project, generate TypeScript types for its content, create forms and check the site's integration.",
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"description": "Mapled CLI — sign in, link a project, generate TypeScript types for its content, create forms, preview drafts and check the site's integration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://mapled.io",
|
|
7
7
|
"keywords": [
|