@plitzi/cli 0.38.8 → 0.38.9
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/CHANGELOG.md +64 -0
- package/README.md +13 -6
- package/dist/index.js +401 -117
- package/package.json +3 -3
- package/skills/plitzi-cli/SKILL.md +10 -4
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
11
11
|
import { createServer } from "node:http";
|
|
12
12
|
import tls from "node:tls";
|
|
13
13
|
import readline from "node:readline/promises";
|
|
14
|
-
import { EXPLAIN_KINDS, PAGE_SPEC_FIELDS, PAGE_TEXT_LIMIT, actionSpecFromEntry, actionToSource, authorSpace, blankTemplateFiles, catalogTemplateFiles, closest, comparePictures, compareTexts, darkScheme, dataIssues, elementTriggers, elementTypeNames,
|
|
14
|
+
import { EXPLAIN_KINDS, PAGE_SPEC_FIELDS, PAGE_TEXT_LIMIT, actionSpecFromEntry, actionToSource, authorSpace, blankTemplateFiles, catalogTemplateFiles, closest, comparePictures, compareTexts, darkScheme, dataIssues, elementTriggers, elementTypeNames, emptyTemplateFiles, explain, explainKindOf, explainList, explanationText, failedFlowText, importProbe, importedFiles, inspectDocument, inspectPage, isSuggestionCode, loadImages, locateClasses, locateElements, openPage, pageRegions, pageTexts, planFixes, readDevTools, refusalOf, unrollPage } from "@plitzi/sdk-authoring";
|
|
15
15
|
import { existsSync, readFileSync, readdirSync, realpathSync, statSync, watch } from "node:fs";
|
|
16
16
|
import { parseEnv, promisify } from "node:util";
|
|
17
17
|
import { lookup } from "node:dns/promises";
|
|
@@ -2418,7 +2418,7 @@ var projectScripts = (answers) => {
|
|
|
2418
2418
|
* runs what `build` emitted and carries no TypeScript at all.
|
|
2419
2419
|
*/
|
|
2420
2420
|
build: "tsc -p tsconfig.build.json",
|
|
2421
|
-
"start:prod": nodeWithEnv(`${v$3}/main.js`)
|
|
2421
|
+
"start:prod": `NODE_ENV=production ${nodeWithEnv(`${v$3}/main.js`)}`
|
|
2422
2422
|
} : {
|
|
2423
2423
|
start: "vite",
|
|
2424
2424
|
build: "vite build",
|
|
@@ -2512,7 +2512,7 @@ var YARN_IGNORES$1 = "\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases
|
|
|
2512
2512
|
* the CLI records about it, and is committed — a clone without it could not pull, push or upgrade.
|
|
2513
2513
|
*/
|
|
2514
2514
|
var gitignore$1 = ({ mode, packageManager }) => `node_modules\n${v$3}\n.env\ntmp\n${mode === "server" ? `${y$2}\n` : ""}${packageManager === "yarn" ? YARN_IGNORES$1 : ""}`;
|
|
2515
|
-
var startLine = ({ mode, packageManager, source }) => mode === "server" ? `\`${runCommand(packageManager, "start")}\` serves pages on http://127.0.0.1:8080 — or, when something else holds 8080, the next free port, which it prints and writes to \`${x$3}\` (set \`PORT\` to choose one). \`${runCommand(packageManager, "start:dev")}\` restarts on a save to the server's code; a save to a plugin swaps it in the open page${source === "local" ? ", and a save to the space reloads it" : ""}. In production, \`${runCommand(packageManager, "build")}\` once and run \`${runCommand(packageManager, "start:prod")}
|
|
2515
|
+
var startLine = ({ mode, packageManager, source }) => mode === "server" ? `\`${runCommand(packageManager, "start")}\` serves pages on http://127.0.0.1:8080 — or, when something else holds 8080, the next free port, which it prints and writes to \`${x$3}\` (set \`PORT\` to choose one). \`${runCommand(packageManager, "start:dev")}\` restarts on a save to the server's code; a save to a plugin swaps it in the open page${source === "local" ? ", and a save to the space reloads it" : ""}. In production, \`${runCommand(packageManager, "build")}\` once and run \`${runCommand(packageManager, "start:prod")}\`, which sets \`NODE_ENV=production\` itself: the compiled server, with no TypeScript in the process.` : `\`${runCommand(packageManager, "start")}\` runs Vite on http://127.0.0.1:5173, with hot module replacement.`;
|
|
2516
2516
|
var spaceSection = (answers) => {
|
|
2517
2517
|
if (answers.source === "cloud") return `## Where the space comes from
|
|
2518
2518
|
|
|
@@ -2634,7 +2634,7 @@ ${commands.join("\n")}
|
|
|
2634
2634
|
- **Settings are ${code(".env")}**, never committed; ${code(".env.example")} names them, committed — a new one goes in both. ${settingsNote}
|
|
2635
2635
|
${dataNote}
|
|
2636
2636
|
- **${code("public/")} is on the internet.** Every file in it is served to anyone who asks for it, as it is, the moment the project is deployed — no sign-in, no check. Never put in it a secret, a key, a ${code(".env")}, a private document, a database dump, or data only some visitors may read: that goes through a server action or a provider that checks who is asking.
|
|
2637
|
-
${serverNotes}- **Check a page in text first:** ${code(`${runCommand(answers.packageManager, "check")} -- / --width 1440,390`)} says whether every element is on screen, nothing overflows and the console is clean — and with ${code("--click <id>")}, what clicking one element changed, or that nothing did (${code("--fill <id>=<value>")} first, for a form) — a picture only when it says something is wrong: ${code(`${runCommand(answers.packageManager, "shot")} -- / --width 390`)} (add ${code("--scheme dark")}; ${code("--frames 4")} to see what moves; ${code("--compare <url>")} against another site: by section, and each text measured). ${run("visual")} runs the checks as tests.
|
|
2637
|
+
${serverNotes}- **Check a page in text first:** ${code(`${runCommand(answers.packageManager, "check")} -- / --width 1440,390`)} says whether every element is on screen, nothing overflows and the console is clean — and with ${code("--click <id>")}, what clicking one element changed, or that nothing did (${code("--fill <id>=<value>")} first, for a form) — a picture only when it says something is wrong: ${code(`${runCommand(answers.packageManager, "shot")} -- / --width 390`)} (add ${code("--scheme dark")}; ${code("--frames 4")} to see what moves, ${code("--click <id|selector> --sheet")} for an interaction; ${code("--compare <url>")} against another site: by section, and each text measured). ${run("visual")} runs the checks as tests.
|
|
2638
2638
|
- **What the page holds, in text:** ${code(`${runCommand(answers.packageManager, "check")} -- /products --state --element <id>`)} adds its state, every source by name and one element (what it reads, its own state, whether it is on screen); every check already lists the flows that failed. Read it instead of guessing from classes in the DOM.
|
|
2639
2639
|
|
|
2640
2640
|
## Do not read
|
|
@@ -2660,7 +2660,7 @@ What you leave behind is the next reader's problem — the user's, or the next a
|
|
|
2660
2660
|
- **Scratch goes in ${code(`tmp/`)}, or nowhere.** A one-off script, a dump, a picture to look at — never at the root or beside the source, where it reads as part of the project.
|
|
2661
2661
|
- **One of everything.** A look used twice is a class; a value used twice is a token; a block used twice is a component, and rows of data are one list. Change it where it is defined, and rename everywhere when you rename.
|
|
2662
2662
|
- **Files a reader can find.** One part per file, named after what it is, in the folder of its kind — the shape ${code("src/space/")} already has. Do not start a parallel layout of your own.
|
|
2663
|
-
- **Leave it passing.** ${run("verify")} runs it all and prints only what fails: ${local ? `${run("author")} with zero warnings, ${run("lint:space")} clean, ` : ""}${run("typecheck")}, ${run("lint")} and the format clean, and every page whole (${run("check")}, with the server up).
|
|
2663
|
+
- **Leave it passing.** ${run("verify")} runs it all and prints only what fails: ${local ? `${run("author")} with zero warnings, ${run("lint:space")} clean, ` : ""}${run("typecheck")}, ${run("lint")} and the format clean, and every page whole (${run("check")}, with the server up) — those for signed-in visitors too, once ${code(".env")} names an account to check them as: ${code("PLITZI_CHECK_USER")} and ${code("PLITZI_CHECK_PASSWORD")}.
|
|
2664
2664
|
|
|
2665
2665
|
## The rules that go wrong most
|
|
2666
2666
|
|
|
@@ -3226,7 +3226,10 @@ var spaceFiles = (answers) => {
|
|
|
3226
3226
|
[r$6]: authorScript()
|
|
3227
3227
|
};
|
|
3228
3228
|
if (answers.template === "blank") return {
|
|
3229
|
-
|
|
3229
|
+
...emptyTemplateFiles({
|
|
3230
|
+
name: answers.name,
|
|
3231
|
+
dir: i$6
|
|
3232
|
+
}),
|
|
3230
3233
|
[r$6]: authorScript(),
|
|
3231
3234
|
...answers.mode === "client" ? { "public/data/.gitkeep": "" } : {}
|
|
3232
3235
|
};
|
|
@@ -4425,9 +4428,9 @@ var __exportAll = (all, no_symbols) => {
|
|
|
4425
4428
|
var e$42 = "[a-zA-Z_][a-zA-Z0-9_]*(?:-[a-zA-Z0-9_]+)*";
|
|
4426
4429
|
var t$63 = `(?:${e$42}|\\d+)`;
|
|
4427
4430
|
var n$47 = `\\[\\s*(?:\\d+|'[^']*'|"[^"]*"|${e$42}(?:\\.${t$63})*)\\s*\\]`;
|
|
4428
|
-
var r$
|
|
4429
|
-
var i$28 = `\\{\\{\\s*${r$
|
|
4430
|
-
var a$24 = `(?:${`\\{\\{\\{\\s*${r$
|
|
4431
|
+
var r$36 = `${`${e$42}(?:${n$47})*(?:\\??\\.${t$63}(?:${n$47})*)*`}(?:\\s*\\?\\?\\s*[^|{}]+?)?(?:\\s*\\|\\s*[a-zA-Z_]+(?:\\([^)]*\\))?)*`;
|
|
4432
|
+
var i$28 = `\\{\\{\\s*${r$36}\\s*\\}\\}`;
|
|
4433
|
+
var a$24 = `(?:${`\\{\\{\\{\\s*${r$36}\\s*\\}\\}\\}`}|${i$28})`;
|
|
4431
4434
|
new RegExp(a$24, "g");
|
|
4432
4435
|
RegExp(`^${a$24}$`);
|
|
4433
4436
|
var e$41 = ["smooth", "auto"];
|
|
@@ -4437,8 +4440,8 @@ var t$61 = [
|
|
|
4437
4440
|
"end",
|
|
4438
4441
|
"nearest"
|
|
4439
4442
|
];
|
|
4440
|
-
var s$
|
|
4441
|
-
var r$
|
|
4443
|
+
var s$22 = "onScroll";
|
|
4444
|
+
var r$34 = {
|
|
4442
4445
|
type: "select",
|
|
4443
4446
|
description: "How it moves: \"smooth\" glides there, \"auto\" jumps.",
|
|
4444
4447
|
default: "smooth",
|
|
@@ -4516,7 +4519,7 @@ var a$22 = {
|
|
|
4516
4519
|
description: "How far down, the same way; negative goes up.",
|
|
4517
4520
|
default: ""
|
|
4518
4521
|
},
|
|
4519
|
-
behavior: r$
|
|
4522
|
+
behavior: r$34
|
|
4520
4523
|
}
|
|
4521
4524
|
},
|
|
4522
4525
|
scrollTo: {
|
|
@@ -4534,7 +4537,7 @@ var a$22 = {
|
|
|
4534
4537
|
description: "Where down, the same way. Empty stays.",
|
|
4535
4538
|
default: ""
|
|
4536
4539
|
},
|
|
4537
|
-
behavior: r$
|
|
4540
|
+
behavior: r$34
|
|
4538
4541
|
}
|
|
4539
4542
|
},
|
|
4540
4543
|
scrollIntoView: {
|
|
@@ -4554,7 +4557,7 @@ var a$22 = {
|
|
|
4554
4557
|
default: "nearest",
|
|
4555
4558
|
options: [...t$61]
|
|
4556
4559
|
},
|
|
4557
|
-
behavior: r$
|
|
4560
|
+
behavior: r$34
|
|
4558
4561
|
}
|
|
4559
4562
|
}
|
|
4560
4563
|
};
|
|
@@ -4958,7 +4961,7 @@ var n$44 = {
|
|
|
4958
4961
|
"border-bottom-left-radius": "4px",
|
|
4959
4962
|
"border-bottom-right-radius": "4px"
|
|
4960
4963
|
};
|
|
4961
|
-
var r$
|
|
4964
|
+
var r$33 = t$60()({
|
|
4962
4965
|
type: "button",
|
|
4963
4966
|
attributeValues: {
|
|
4964
4967
|
subType: e$39()([
|
|
@@ -5608,7 +5611,7 @@ var n$39 = {
|
|
|
5608
5611
|
"letter-spacing": "inherit",
|
|
5609
5612
|
cursor: "pointer"
|
|
5610
5613
|
};
|
|
5611
|
-
var r$
|
|
5614
|
+
var r$32 = t$60()({
|
|
5612
5615
|
type: "themeToggle",
|
|
5613
5616
|
attributeValues: { subType: e$39()(["switch", "segmented"]) },
|
|
5614
5617
|
triggers: { onThemeChange: {
|
|
@@ -5806,7 +5809,7 @@ var n$37 = {
|
|
|
5806
5809
|
outline: "none",
|
|
5807
5810
|
position: "relative"
|
|
5808
5811
|
};
|
|
5809
|
-
var r$
|
|
5812
|
+
var r$31 = {
|
|
5810
5813
|
...n$37,
|
|
5811
5814
|
"line-height": "20px"
|
|
5812
5815
|
};
|
|
@@ -5828,7 +5831,7 @@ var o$23 = {
|
|
|
5828
5831
|
color: "red",
|
|
5829
5832
|
"margin-top": "4px"
|
|
5830
5833
|
};
|
|
5831
|
-
var s$
|
|
5834
|
+
var s$20 = {
|
|
5832
5835
|
color: "red",
|
|
5833
5836
|
"margin-left": "4px"
|
|
5834
5837
|
};
|
|
@@ -5847,7 +5850,7 @@ var l$13 = {
|
|
|
5847
5850
|
"padding-left": "0px",
|
|
5848
5851
|
display: "block",
|
|
5849
5852
|
...c$18,
|
|
5850
|
-
"background-color": "
|
|
5853
|
+
"background-color": "transparent",
|
|
5851
5854
|
color: "inherit",
|
|
5852
5855
|
"line-height": "inherit",
|
|
5853
5856
|
"font-family": "inherit",
|
|
@@ -5868,7 +5871,7 @@ var u$13 = {
|
|
|
5868
5871
|
cursor: "inherit",
|
|
5869
5872
|
"box-shadow": "none"
|
|
5870
5873
|
};
|
|
5871
|
-
var d$
|
|
5874
|
+
var d$12 = {
|
|
5872
5875
|
height: "100%",
|
|
5873
5876
|
"margin-top": "0px",
|
|
5874
5877
|
"margin-right": "0px",
|
|
@@ -5888,11 +5891,11 @@ var d$13 = {
|
|
|
5888
5891
|
"font-weight": "inherit",
|
|
5889
5892
|
cursor: "pointer"
|
|
5890
5893
|
};
|
|
5891
|
-
var f$
|
|
5894
|
+
var f$10 = {
|
|
5892
5895
|
...n$37,
|
|
5893
5896
|
cursor: "pointer"
|
|
5894
5897
|
};
|
|
5895
|
-
var p$
|
|
5898
|
+
var p$11 = {
|
|
5896
5899
|
height: "20px",
|
|
5897
5900
|
width: "36px",
|
|
5898
5901
|
"margin-top": "0px",
|
|
@@ -5922,7 +5925,7 @@ var p$10 = {
|
|
|
5922
5925
|
appearance: "none",
|
|
5923
5926
|
cursor: "pointer"
|
|
5924
5927
|
};
|
|
5925
|
-
var m$
|
|
5928
|
+
var m$7 = (e, t, { field: n, icon: r } = {}) => ({
|
|
5926
5929
|
name: e,
|
|
5927
5930
|
displayMode: "desktop",
|
|
5928
5931
|
style: {
|
|
@@ -5931,7 +5934,7 @@ var m$6 = (e, t, { field: n, icon: r } = {}) => ({
|
|
|
5931
5934
|
...n ? { field: { default: n } } : {},
|
|
5932
5935
|
...r ? { icon: { default: r } } : {},
|
|
5933
5936
|
label: { default: a$21 },
|
|
5934
|
-
requiredMark: { default: s$
|
|
5937
|
+
requiredMark: { default: s$20 },
|
|
5935
5938
|
error: { default: o$23 }
|
|
5936
5939
|
}
|
|
5937
5940
|
});
|
|
@@ -5977,7 +5980,7 @@ var g$4 = t$60()({
|
|
|
5977
5980
|
autoFocus: !1,
|
|
5978
5981
|
disabled: !1,
|
|
5979
5982
|
options: [],
|
|
5980
|
-
required: !
|
|
5983
|
+
required: !1,
|
|
5981
5984
|
requiredMessage: "",
|
|
5982
5985
|
minLength: 0,
|
|
5983
5986
|
minLengthMessage: "",
|
|
@@ -6029,26 +6032,26 @@ var g$4 = t$60()({
|
|
|
6029
6032
|
displayMode: "desktop",
|
|
6030
6033
|
style: { base: { default: {} } },
|
|
6031
6034
|
subTypes: {
|
|
6032
|
-
hidden: m$
|
|
6033
|
-
text: m$
|
|
6034
|
-
number: m$
|
|
6035
|
-
email: m$
|
|
6036
|
-
password: m$
|
|
6035
|
+
hidden: m$7("Form Control Hidden", {}),
|
|
6036
|
+
text: m$7("Form Control Text", r$31, h$4),
|
|
6037
|
+
number: m$7("Form Control Number", r$31, h$4),
|
|
6038
|
+
email: m$7("Form Control Email", r$31, h$4),
|
|
6039
|
+
password: m$7("Form Control Password", r$31, {
|
|
6037
6040
|
...h$4,
|
|
6038
|
-
icon: d$
|
|
6041
|
+
icon: d$12
|
|
6039
6042
|
}),
|
|
6040
|
-
search: m$
|
|
6041
|
-
url: m$
|
|
6042
|
-
tel: m$
|
|
6043
|
-
date: m$
|
|
6044
|
-
time: m$
|
|
6045
|
-
color: m$
|
|
6046
|
-
textarea: m$
|
|
6047
|
-
...r$
|
|
6043
|
+
search: m$7("Form Control Search", r$31, h$4),
|
|
6044
|
+
url: m$7("Form Control URL", r$31, h$4),
|
|
6045
|
+
tel: m$7("Form Control Phone", r$31, h$4),
|
|
6046
|
+
date: m$7("Form Control Date", r$31, h$4),
|
|
6047
|
+
time: m$7("Form Control Time", r$31, h$4),
|
|
6048
|
+
color: m$7("Form Control Color", r$31, h$4),
|
|
6049
|
+
textarea: m$7("Form Control Textarea", {
|
|
6050
|
+
...r$31,
|
|
6048
6051
|
color: "inherit",
|
|
6049
6052
|
resize: "vertical"
|
|
6050
6053
|
}),
|
|
6051
|
-
select: m$
|
|
6054
|
+
select: m$7("Form Control Select", f$10, { field: u$13 }),
|
|
6052
6055
|
checkbox: {
|
|
6053
6056
|
name: "Form Control Checkbox",
|
|
6054
6057
|
displayMode: "desktop",
|
|
@@ -6061,7 +6064,7 @@ var g$4 = t$60()({
|
|
|
6061
6064
|
"margin-right": "4px"
|
|
6062
6065
|
} },
|
|
6063
6066
|
label: { default: i$25 },
|
|
6064
|
-
requiredMark: { default: s$
|
|
6067
|
+
requiredMark: { default: s$20 },
|
|
6065
6068
|
error: { default: o$23 }
|
|
6066
6069
|
}
|
|
6067
6070
|
},
|
|
@@ -6070,12 +6073,12 @@ var g$4 = t$60()({
|
|
|
6070
6073
|
displayMode: "desktop",
|
|
6071
6074
|
style: {
|
|
6072
6075
|
base: { default: {} },
|
|
6073
|
-
input: { default: p$
|
|
6076
|
+
input: { default: p$11 },
|
|
6074
6077
|
label: { default: {
|
|
6075
6078
|
...i$25,
|
|
6076
6079
|
"align-items": "center"
|
|
6077
6080
|
} },
|
|
6078
|
-
requiredMark: { default: s$
|
|
6081
|
+
requiredMark: { default: s$20 },
|
|
6079
6082
|
error: { default: o$23 }
|
|
6080
6083
|
}
|
|
6081
6084
|
}
|
|
@@ -6677,7 +6680,7 @@ var n$34 = t$60()({
|
|
|
6677
6680
|
definition: {
|
|
6678
6681
|
label: "Api Container",
|
|
6679
6682
|
type: "apiContainer",
|
|
6680
|
-
description: "Fetches data from a backend HTTP API (its `query`/`method`/`credentials`) and exposes the response as a data source ITS DESCENDANTS bind to (source `apiContainer_<id>.data`; only elements inside it can consume it). This is how you get backend data into the frontend. Its `mockData` prop is builder-only sample data — the published runtime fetches the real `query`, so always set a real query for production. With `cache: true` a browser request is kept for `staleTime` seconds (default 30) and shared with every provider asking the same thing; it is off unless set. A flow refreshes it with `performQuery` or the global `invalidateQueries` step, and `refreshSeconds` makes it ask again on its own every N seconds (either runtime; paused while the tab is hidden) — the way to keep a queue, feed or status board current without a plugin. Its `subType` (container tag) is empty by default, and then it renders NO element of its own: its children lay out directly in its parent, and any class, variant or style binding on it applies to nothing. To style the provider itself give it a tag (`subType: \"div\"`, `section`, …); otherwise style its parent or a child.",
|
|
6683
|
+
description: "Fetches data from a backend HTTP API (its `query`/`method`/`credentials`) and exposes the response as a data source ITS DESCENDANTS bind to (source `apiContainer_<id>.data`; only elements inside it can consume it). This is how you get backend data into the frontend. Its `mockData` prop is builder-only sample data — the published runtime fetches the real `query`, so always set a real query for production. With `cache: true` a browser request is kept for `staleTime` seconds (default 30) and shared with every provider asking the same thing; it is off unless set. A flow refreshes it with `performQuery` or the global `invalidateQueries` step, and `refreshSeconds` makes it ask again on its own every N seconds (either runtime; paused while the tab is hidden) — the way to keep a queue, feed or status board current without a plugin. A server provider (`runtime: \"server\"`) whose answer means the address shows nothing — a record not found — says so in `notFound`, a template against its answer (`{{ source.found == false }}`): the page is sent with status 404, rendered as written. Its `subType` (container tag) is empty by default, and then it renders NO element of its own: its children lay out directly in its parent, and any class, variant or style binding on it applies to nothing. To style the provider itself give it a tag (`subType: \"div\"`, `section`, …); otherwise style its parent or a child.",
|
|
6681
6684
|
items: [],
|
|
6682
6685
|
bindings: {},
|
|
6683
6686
|
styleSelectors: { base: "" },
|
|
@@ -6976,7 +6979,7 @@ var t$42 = t$60()({
|
|
|
6976
6979
|
settings: {}
|
|
6977
6980
|
}
|
|
6978
6981
|
});
|
|
6979
|
-
var r$
|
|
6982
|
+
var r$30 = t$60()({
|
|
6980
6983
|
type: "container",
|
|
6981
6984
|
attributeValues: {
|
|
6982
6985
|
subType: e$39()([
|
|
@@ -7093,11 +7096,11 @@ var B$2 = {
|
|
|
7093
7096
|
ApiContainer: n$34,
|
|
7094
7097
|
BlockHtml: t$59,
|
|
7095
7098
|
BlockJsx: t$58,
|
|
7096
|
-
Button: r$
|
|
7099
|
+
Button: r$33,
|
|
7097
7100
|
Carousel: t$43,
|
|
7098
7101
|
CarouselTrack: t$42,
|
|
7099
7102
|
Channel: n$33,
|
|
7100
|
-
Container: r$
|
|
7103
|
+
Container: r$30,
|
|
7101
7104
|
Custom: t$57,
|
|
7102
7105
|
DialogContainer: t$60()({
|
|
7103
7106
|
type: "dialogContainer",
|
|
@@ -7851,7 +7854,7 @@ var B$2 = {
|
|
|
7851
7854
|
}
|
|
7852
7855
|
}),
|
|
7853
7856
|
Text: t$51,
|
|
7854
|
-
ThemeToggle: r$
|
|
7857
|
+
ThemeToggle: r$32,
|
|
7855
7858
|
Video: t$44
|
|
7856
7859
|
};
|
|
7857
7860
|
/** Every declaration, read through the part of its shape this catalog uses. */
|
|
@@ -8370,6 +8373,16 @@ var AUTHORING_CODES = {
|
|
|
8370
8373
|
means: "a `runtime: 'server'` element inside a component: the page server resolves a page's and its layouts', never a component's",
|
|
8371
8374
|
fix: "put the provider on the page, around the instance, and hand the component its rows as a prop"
|
|
8372
8375
|
},
|
|
8376
|
+
"not-found-in-browser": {
|
|
8377
|
+
kind: "refused",
|
|
8378
|
+
means: "`notFound` on a provider asked from the browser — its answer arrives after the page was sent with its status",
|
|
8379
|
+
fix: "`runtime: 'server'` on the provider, or `visible` on the page's \"not found\" part instead"
|
|
8380
|
+
},
|
|
8381
|
+
"not-found-not-a-template": {
|
|
8382
|
+
kind: "refused",
|
|
8383
|
+
means: "`notFound` that is not one `{{ expression }}` against the answer — never `true`, so never a 404",
|
|
8384
|
+
fix: "`notFound: '{{ source.found == false }}'`"
|
|
8385
|
+
},
|
|
8373
8386
|
"server-data-in-browser": {
|
|
8374
8387
|
kind: "refused",
|
|
8375
8388
|
means: "a provider asking for the project's own data (`/data/…`, read by its server from `src/data/` and never served) from the browser",
|
|
@@ -8855,6 +8868,11 @@ var AUTHORING_CODES = {
|
|
|
8855
8868
|
means: "a colour written out in a class where the space has a token of that value — it stays put in the dark scheme",
|
|
8856
8869
|
fix: "`var(--token)` when it should follow the scheme; a token of one value of its own when it must stay the same in both"
|
|
8857
8870
|
},
|
|
8871
|
+
"not-found-page": {
|
|
8872
|
+
kind: "suggested",
|
|
8873
|
+
means: "a space with no page of its own for an address no other page answers — a plain one was added, in the home page’s layout, sent with status 404",
|
|
8874
|
+
fix: "a page whose slug is `'*'`: `{ id: 'not-found', name: 'Not found', slug: '*', layout: …, body: [...] }`"
|
|
8875
|
+
},
|
|
8858
8876
|
"unused-component": {
|
|
8859
8877
|
kind: "suggested",
|
|
8860
8878
|
means: "a component no page, layout or other component places",
|
|
@@ -9154,6 +9172,8 @@ var t$30 = {
|
|
|
9154
9172
|
TEXT_INDENT: "text-indent",
|
|
9155
9173
|
TEXT_TRANSFORM: "text-transform",
|
|
9156
9174
|
DIRECTION: "direction",
|
|
9175
|
+
WRITING_MODE: "writing-mode",
|
|
9176
|
+
TEXT_ORIENTATION: "text-orientation",
|
|
9157
9177
|
WHITE_SPACE: "white-space",
|
|
9158
9178
|
TEXT_WRAP: "text-wrap",
|
|
9159
9179
|
WORD_BREAK: "word-break",
|
|
@@ -9178,7 +9198,7 @@ Object.values(t$30).map((e) => ({
|
|
|
9178
9198
|
path: e,
|
|
9179
9199
|
label: e
|
|
9180
9200
|
}));
|
|
9181
|
-
t$30.DISPLAY, t$30.FLEX_WRAP, t$30.FLEX_DIRECTION, t$30.ALIGN_ITEMS, t$30.JUSTIFY_ITEMS, t$30.JUSTIFY_CONTENT, t$30.ALIGN_CONTENT, t$30.ROW_GAP, t$30.COLUMN_GAP, t$30.GRID_ROW_GAP, t$30.GRID_COLUMN_GAP, t$30.GRID_TEMPLATE_AREAS, t$30.GRID_TEMPLATE_COLUMNS, t$30.GRID_TEMPLATE_ROWS, t$30.GRID_AUTO_FLOW, t$30.GRID_AUTO_ROWS, t$30.GRID_AUTO_COLUMNS, t$30.GRID_ROW_START, t$30.GRID_COLUMN_START, t$30.GRID_ROW_END, t$30.GRID_COLUMN_END, t$30.ALIGN_SELF, t$30.JUSTIFY_SELF, t$30.ORDER, t$30.FLEX_GROW, t$30.FLEX_SHRINK, t$30.FLEX_BASIS, t$30.LIST_STYLE, t$30.LIST_STYLE_TYPE, t$30.LIST_STYLE_POSITION, t$30.LIST_STYLE_IMAGE, t$30.BACKGROUND_COLOR, t$30.BACKGROUND_IMAGE, t$30.BACKGROUND_POSITION, t$30.BACKGROUND_SIZE, t$30.BACKGROUND_CLIP, t$30.BACKGROUND_ORIGIN, t$30.BACKGROUND_REPEAT, t$30.BACKGROUND_ATTACHMENT, t$30.BACKGROUND_BLEND_MODE, t$30.MASK_IMAGE, t$30.MASK_SIZE, t$30.MASK_POSITION, t$30.MASK_REPEAT, t$30.MASK_COMPOSITE, t$30.WEBKIT_MASK_IMAGE, t$30.BORDER_TOP_STYLE, t$30.BORDER_TOP_WIDTH, t$30.BORDER_TOP_COLOR, t$30.BORDER_BOTTOM_STYLE, t$30.BORDER_BOTTOM_WIDTH, t$30.BORDER_BOTTOM_COLOR, t$30.BORDER_LEFT_STYLE, t$30.BORDER_LEFT_WIDTH, t$30.BORDER_LEFT_COLOR, t$30.BORDER_RIGHT_STYLE, t$30.BORDER_RIGHT_WIDTH, t$30.BORDER_RIGHT_COLOR, t$30.BORDER_TOP_LEFT_RADIUS, t$30.BORDER_TOP_RIGHT_RADIUS, t$30.BORDER_BOTTOM_LEFT_RADIUS, t$30.BORDER_BOTTOM_RIGHT_RADIUS, t$30.BORDER_COLLAPSE, t$30.BORDER_SPACING, t$30.TABLE_LAYOUT, t$30.OPACITY, t$30.VISIBILITY, t$30.CURSOR, t$30.TRANSITION, t$30.TRANSITION_PROPERTY, t$30.TRANSITION_DURATION, t$30.TRANSITION_TIMING_FUNCTION, t$30.TRANSITION_DELAY, t$30.TRANSITION_BEHAVIOR, t$30.ANIMATION, t$30.ANIMATION_NAME, t$30.ANIMATION_DURATION, t$30.ANIMATION_TIMING_FUNCTION, t$30.ANIMATION_DELAY, t$30.ANIMATION_ITERATION_COUNT, t$30.ANIMATION_DIRECTION, t$30.ANIMATION_FILL_MODE, t$30.ANIMATION_PLAY_STATE, t$30.ANIMATION_TIMELINE, t$30.ANIMATION_RANGE, t$30.BOX_SHADOW, t$30.FILTER, t$30.BACKDROP_FILTER, t$30.MIX_BLEND_MODE, t$30.ISOLATION, t$30.CLIP_PATH, t$30.TRANSFORM, t$30.TRANSFORM_ORIGIN, t$30.TRANSLATE, t$30.ROTATE, t$30.SCALE, t$30.TRANSFORM_STYLE, t$30.PERSPECTIVE, t$30.PERSPECTIVE_ORIGIN, t$30.BACKFACE_VISIBILITY, t$30.WILL_CHANGE, t$30.OUTLINE_WIDTH, t$30.OUTLINE_STYLE, t$30.OUTLINE_COLOR, t$30.OUTLINE_OFFSET, t$30.POINTER_EVENTS, t$30.USER_SELECT, t$30.TOUCH_ACTION, t$30.RESIZE, t$30.APPEARANCE, t$30.SCROLL_BEHAVIOR, t$30.OVERSCROLL_BEHAVIOR, t$30.SCROLL_SNAP_TYPE, t$30.SCROLL_SNAP_ALIGN, t$30.SCROLL_SNAP_STOP, t$30.SCROLL_PADDING_TOP, t$30.SCROLL_MARGIN_TOP, t$30.ACCENT_COLOR, t$30.CARET_COLOR, t$30.COLOR_SCHEME, t$30.POSITION, t$30.FLOAT, t$30.CLEAR, t$30.Z_INDEX, t$30.TOP, t$30.BOTTOM, t$30.LEFT, t$30.RIGHT, t$30.BOX_SIZING, t$30.ASPECT_RATIO, t$30.WIDTH, t$30.HEIGHT, t$30.MIN_WIDTH, t$30.MIN_HEIGHT, t$30.MAX_WIDTH, t$30.MAX_HEIGHT, t$30.OVERFLOW, t$30.OVERFLOW_X, t$30.OVERFLOW_Y, t$30.SCROLLBAR_WIDTH, t$30.SCROLLBAR_COLOR, t$30.CONTAINER_TYPE, t$30.CONTENT, t$30.CONTAINER_NAME, t$30.OBJECT_FIT, t$30.OBJECT_POSITION, t$30.COLUMN_WIDTH, t$30.COLUMN_COUNT, t$30.MARGIN_TOP, t$30.MARGIN_BOTTOM, t$30.MARGIN_LEFT, t$30.MARGIN_RIGHT, t$30.PADDING_TOP, t$30.PADDING_BOTTOM, t$30.PADDING_LEFT, t$30.PADDING_RIGHT, t$30.FONT_FAMILY, t$30.FONT_WEIGHT, t$30.FONT_SIZE, t$30.LINE_HEIGHT, t$30.COLOR, t$30.TEXT_ALIGN, t$30.FONT_STYLE, t$30.FONT_VARIANT, t$30.FONT_VARIANT_NUMERIC, t$30.FONT_FEATURE_SETTINGS, t$30.TEXT_DECORATION, t$30.TEXT_DECORATION_LINE, t$30.TEXT_DECORATION_COLOR, t$30.TEXT_DECORATION_STYLE, t$30.TEXT_DECORATION_THICKNESS, t$30.TEXT_UNDERLINE_OFFSET, t$30.LETTER_SPACING, t$30.WORD_SPACING, t$30.TEXT_INDENT, t$30.TEXT_TRANSFORM, t$30.DIRECTION, t$30.WHITE_SPACE, t$30.TEXT_WRAP, t$30.WORD_BREAK, t$30.OVERFLOW_WRAP, t$30.HYPHENS, t$30.VERTICAL_ALIGN, t$30.TEXT_OVERFLOW, t$30.TEXT_SHADOW, t$30.LINE_CLAMP, t$30.WEBKIT_LINE_CLAMP, t$30.WEBKIT_BOX_ORIENT, t$30.WEBKIT_TEXT_FILL_COLOR, t$30.WEBKIT_BACKGROUND_CLIP, t$30.BOX_DECORATION_BREAK, t$30.WEBKIT_BOX_DECORATION_BREAK, t$30.FILL, t$30.STROKE, t$30.STROKE_WIDTH;
|
|
9201
|
+
t$30.DISPLAY, t$30.FLEX_WRAP, t$30.FLEX_DIRECTION, t$30.ALIGN_ITEMS, t$30.JUSTIFY_ITEMS, t$30.JUSTIFY_CONTENT, t$30.ALIGN_CONTENT, t$30.ROW_GAP, t$30.COLUMN_GAP, t$30.GRID_ROW_GAP, t$30.GRID_COLUMN_GAP, t$30.GRID_TEMPLATE_AREAS, t$30.GRID_TEMPLATE_COLUMNS, t$30.GRID_TEMPLATE_ROWS, t$30.GRID_AUTO_FLOW, t$30.GRID_AUTO_ROWS, t$30.GRID_AUTO_COLUMNS, t$30.GRID_ROW_START, t$30.GRID_COLUMN_START, t$30.GRID_ROW_END, t$30.GRID_COLUMN_END, t$30.ALIGN_SELF, t$30.JUSTIFY_SELF, t$30.ORDER, t$30.FLEX_GROW, t$30.FLEX_SHRINK, t$30.FLEX_BASIS, t$30.LIST_STYLE, t$30.LIST_STYLE_TYPE, t$30.LIST_STYLE_POSITION, t$30.LIST_STYLE_IMAGE, t$30.BACKGROUND_COLOR, t$30.BACKGROUND_IMAGE, t$30.BACKGROUND_POSITION, t$30.BACKGROUND_SIZE, t$30.BACKGROUND_CLIP, t$30.BACKGROUND_ORIGIN, t$30.BACKGROUND_REPEAT, t$30.BACKGROUND_ATTACHMENT, t$30.BACKGROUND_BLEND_MODE, t$30.MASK_IMAGE, t$30.MASK_SIZE, t$30.MASK_POSITION, t$30.MASK_REPEAT, t$30.MASK_COMPOSITE, t$30.WEBKIT_MASK_IMAGE, t$30.BORDER_TOP_STYLE, t$30.BORDER_TOP_WIDTH, t$30.BORDER_TOP_COLOR, t$30.BORDER_BOTTOM_STYLE, t$30.BORDER_BOTTOM_WIDTH, t$30.BORDER_BOTTOM_COLOR, t$30.BORDER_LEFT_STYLE, t$30.BORDER_LEFT_WIDTH, t$30.BORDER_LEFT_COLOR, t$30.BORDER_RIGHT_STYLE, t$30.BORDER_RIGHT_WIDTH, t$30.BORDER_RIGHT_COLOR, t$30.BORDER_TOP_LEFT_RADIUS, t$30.BORDER_TOP_RIGHT_RADIUS, t$30.BORDER_BOTTOM_LEFT_RADIUS, t$30.BORDER_BOTTOM_RIGHT_RADIUS, t$30.BORDER_COLLAPSE, t$30.BORDER_SPACING, t$30.TABLE_LAYOUT, t$30.OPACITY, t$30.VISIBILITY, t$30.CURSOR, t$30.TRANSITION, t$30.TRANSITION_PROPERTY, t$30.TRANSITION_DURATION, t$30.TRANSITION_TIMING_FUNCTION, t$30.TRANSITION_DELAY, t$30.TRANSITION_BEHAVIOR, t$30.ANIMATION, t$30.ANIMATION_NAME, t$30.ANIMATION_DURATION, t$30.ANIMATION_TIMING_FUNCTION, t$30.ANIMATION_DELAY, t$30.ANIMATION_ITERATION_COUNT, t$30.ANIMATION_DIRECTION, t$30.ANIMATION_FILL_MODE, t$30.ANIMATION_PLAY_STATE, t$30.ANIMATION_TIMELINE, t$30.ANIMATION_RANGE, t$30.BOX_SHADOW, t$30.FILTER, t$30.BACKDROP_FILTER, t$30.MIX_BLEND_MODE, t$30.ISOLATION, t$30.CLIP_PATH, t$30.TRANSFORM, t$30.TRANSFORM_ORIGIN, t$30.TRANSLATE, t$30.ROTATE, t$30.SCALE, t$30.TRANSFORM_STYLE, t$30.PERSPECTIVE, t$30.PERSPECTIVE_ORIGIN, t$30.BACKFACE_VISIBILITY, t$30.WILL_CHANGE, t$30.OUTLINE_WIDTH, t$30.OUTLINE_STYLE, t$30.OUTLINE_COLOR, t$30.OUTLINE_OFFSET, t$30.POINTER_EVENTS, t$30.USER_SELECT, t$30.TOUCH_ACTION, t$30.RESIZE, t$30.APPEARANCE, t$30.SCROLL_BEHAVIOR, t$30.OVERSCROLL_BEHAVIOR, t$30.SCROLL_SNAP_TYPE, t$30.SCROLL_SNAP_ALIGN, t$30.SCROLL_SNAP_STOP, t$30.SCROLL_PADDING_TOP, t$30.SCROLL_MARGIN_TOP, t$30.ACCENT_COLOR, t$30.CARET_COLOR, t$30.COLOR_SCHEME, t$30.POSITION, t$30.FLOAT, t$30.CLEAR, t$30.Z_INDEX, t$30.TOP, t$30.BOTTOM, t$30.LEFT, t$30.RIGHT, t$30.BOX_SIZING, t$30.ASPECT_RATIO, t$30.WIDTH, t$30.HEIGHT, t$30.MIN_WIDTH, t$30.MIN_HEIGHT, t$30.MAX_WIDTH, t$30.MAX_HEIGHT, t$30.OVERFLOW, t$30.OVERFLOW_X, t$30.OVERFLOW_Y, t$30.SCROLLBAR_WIDTH, t$30.SCROLLBAR_COLOR, t$30.CONTAINER_TYPE, t$30.CONTENT, t$30.CONTAINER_NAME, t$30.OBJECT_FIT, t$30.OBJECT_POSITION, t$30.COLUMN_WIDTH, t$30.COLUMN_COUNT, t$30.MARGIN_TOP, t$30.MARGIN_BOTTOM, t$30.MARGIN_LEFT, t$30.MARGIN_RIGHT, t$30.PADDING_TOP, t$30.PADDING_BOTTOM, t$30.PADDING_LEFT, t$30.PADDING_RIGHT, t$30.FONT_FAMILY, t$30.FONT_WEIGHT, t$30.FONT_SIZE, t$30.LINE_HEIGHT, t$30.COLOR, t$30.TEXT_ALIGN, t$30.FONT_STYLE, t$30.FONT_VARIANT, t$30.FONT_VARIANT_NUMERIC, t$30.FONT_FEATURE_SETTINGS, t$30.TEXT_DECORATION, t$30.TEXT_DECORATION_LINE, t$30.TEXT_DECORATION_COLOR, t$30.TEXT_DECORATION_STYLE, t$30.TEXT_DECORATION_THICKNESS, t$30.TEXT_UNDERLINE_OFFSET, t$30.LETTER_SPACING, t$30.WORD_SPACING, t$30.TEXT_INDENT, t$30.TEXT_TRANSFORM, t$30.DIRECTION, t$30.WRITING_MODE, t$30.TEXT_ORIENTATION, t$30.WHITE_SPACE, t$30.TEXT_WRAP, t$30.WORD_BREAK, t$30.OVERFLOW_WRAP, t$30.HYPHENS, t$30.VERTICAL_ALIGN, t$30.TEXT_OVERFLOW, t$30.TEXT_SHADOW, t$30.LINE_CLAMP, t$30.WEBKIT_LINE_CLAMP, t$30.WEBKIT_BOX_ORIENT, t$30.WEBKIT_TEXT_FILL_COLOR, t$30.WEBKIT_BACKGROUND_CLIP, t$30.BOX_DECORATION_BREAK, t$30.WEBKIT_BOX_DECORATION_BREAK, t$30.FILL, t$30.STROKE, t$30.STROKE_WIDTH;
|
|
9182
9202
|
[...e$38];
|
|
9183
9203
|
/** Both binding forms as one list. The map form targets attributes, which is what nearly every binding does. */
|
|
9184
9204
|
var toBindingSpecs = (bind) => Array.isArray(bind) ? bind : Object.entries(bind).map(([to, source]) => ({
|
|
@@ -9517,11 +9537,11 @@ var e$27 = {
|
|
|
9517
9537
|
"motion-safe": "@media (prefers-reduced-motion: no-preference)"
|
|
9518
9538
|
};
|
|
9519
9539
|
var t$23 = Object.keys(e$27);
|
|
9520
|
-
var r$
|
|
9540
|
+
var r$18 = (t) => Object.hasOwn(e$27, t);
|
|
9521
9541
|
var i$17 = String.raw`\d+(?:\.\d+)?(?:px|rem|em|ch)`;
|
|
9522
9542
|
var a$15 = String.raw`\((?:min|max)-width:\s*${i$17}\)`;
|
|
9523
9543
|
var o$17 = new RegExp(String.raw`^container(?:\s+(?<name>-?[_a-zA-Z][\w-]*))?\s+(?<query>${a$15}(?:\s+and\s+${a$15})?)$`);
|
|
9524
|
-
var s$
|
|
9544
|
+
var s$15 = (e) => {
|
|
9525
9545
|
let t = o$17.exec(e.trim());
|
|
9526
9546
|
if (t?.groups) return {
|
|
9527
9547
|
name: t.groups.name,
|
|
@@ -9529,8 +9549,8 @@ var s$14 = (e) => {
|
|
|
9529
9549
|
};
|
|
9530
9550
|
};
|
|
9531
9551
|
var c$12 = (e) => {
|
|
9532
|
-
if (r$
|
|
9533
|
-
let t = s$
|
|
9552
|
+
if (r$18(e)) return e;
|
|
9553
|
+
let t = s$15(e);
|
|
9534
9554
|
if (t) return t.name ? `container ${t.name} ${t.query}` : `container ${t.query}`;
|
|
9535
9555
|
};
|
|
9536
9556
|
var e$26 = [
|
|
@@ -9573,7 +9593,7 @@ var o$16 = [
|
|
|
9573
9593
|
"line-height",
|
|
9574
9594
|
"vertical-align"
|
|
9575
9595
|
];
|
|
9576
|
-
var s$
|
|
9596
|
+
var s$14 = [
|
|
9577
9597
|
"font-family",
|
|
9578
9598
|
"font-size",
|
|
9579
9599
|
"font-style",
|
|
@@ -9600,16 +9620,16 @@ var l$8 = {
|
|
|
9600
9620
|
"color",
|
|
9601
9621
|
"content",
|
|
9602
9622
|
"direction",
|
|
9603
|
-
...s$
|
|
9623
|
+
...s$14
|
|
9604
9624
|
],
|
|
9605
9625
|
placeholder: [
|
|
9606
9626
|
...o$16,
|
|
9607
|
-
...s$
|
|
9627
|
+
...s$14,
|
|
9608
9628
|
...c$11
|
|
9609
9629
|
],
|
|
9610
9630
|
"first-line": [
|
|
9611
9631
|
...o$16,
|
|
9612
|
-
...s$
|
|
9632
|
+
...s$14,
|
|
9613
9633
|
...c$11
|
|
9614
9634
|
]
|
|
9615
9635
|
};
|
|
@@ -9617,7 +9637,7 @@ var u$7 = (e, t) => {
|
|
|
9617
9637
|
let n = l$8[e];
|
|
9618
9638
|
return !n || t.startsWith("--") || n.includes(t);
|
|
9619
9639
|
};
|
|
9620
|
-
var d$
|
|
9640
|
+
var d$7 = /* @__PURE__ */ new Set([
|
|
9621
9641
|
"none",
|
|
9622
9642
|
"normal",
|
|
9623
9643
|
"open-quote",
|
|
@@ -9629,8 +9649,8 @@ var d$8 = /* @__PURE__ */ new Set([
|
|
|
9629
9649
|
"unset",
|
|
9630
9650
|
"revert"
|
|
9631
9651
|
]);
|
|
9632
|
-
var f$
|
|
9633
|
-
var p$
|
|
9652
|
+
var f$5 = /^(?:counter|counters|attr|url|var|image-set|linear-gradient|radial-gradient|conic-gradient)\(/i;
|
|
9653
|
+
var p$8 = (e) => {
|
|
9634
9654
|
let t = [], n = "", r = "", i = 0;
|
|
9635
9655
|
for (let a of e) {
|
|
9636
9656
|
if (r) {
|
|
@@ -9648,9 +9668,9 @@ var p$7 = (e) => {
|
|
|
9648
9668
|
}
|
|
9649
9669
|
return n && t.push(n), r || i !== 0 ? void 0 : t;
|
|
9650
9670
|
};
|
|
9651
|
-
var m$
|
|
9652
|
-
let t = p$
|
|
9653
|
-
return t?.length ? t.every((e) => /^(["']).*\1$/s.test(e) || d$
|
|
9671
|
+
var m$4 = (e) => {
|
|
9672
|
+
let t = p$8(e.trim());
|
|
9673
|
+
return t?.length ? t.every((e) => /^(["']).*\1$/s.test(e) || d$7.has(e.toLowerCase()) || f$5.test(e) && e.endsWith(")")) : !1;
|
|
9654
9674
|
};
|
|
9655
9675
|
var e$25 = [
|
|
9656
9676
|
"hover",
|
|
@@ -9670,7 +9690,7 @@ var e$25 = [
|
|
|
9670
9690
|
"even"
|
|
9671
9691
|
];
|
|
9672
9692
|
var t$21 = "plitzi-component--hidden";
|
|
9673
|
-
var r$
|
|
9693
|
+
var r$16 = {
|
|
9674
9694
|
odd: 0,
|
|
9675
9695
|
even: 1,
|
|
9676
9696
|
first: 2,
|
|
@@ -9687,7 +9707,7 @@ var r$17 = {
|
|
|
9687
9707
|
disabled: 13,
|
|
9688
9708
|
hidden: 14
|
|
9689
9709
|
};
|
|
9690
|
-
var i$15 = (e) => Object.hasOwn(r$
|
|
9710
|
+
var i$15 = (e) => Object.hasOwn(r$16, e);
|
|
9691
9711
|
var l$7 = {
|
|
9692
9712
|
current: ":is([aria-current]:not([aria-current=\"false\"]),[aria-pressed=\"true\"],[aria-selected=\"true\"],[role=\"tabpanel\"]:not([hidden]))",
|
|
9693
9713
|
expanded: "[aria-expanded=\"true\"]",
|
|
@@ -9698,7 +9718,7 @@ var l$7 = {
|
|
|
9698
9718
|
even: ":nth-child(even)"
|
|
9699
9719
|
};
|
|
9700
9720
|
var u$6 = (e) => (i$15(e) ? l$7[e] : void 0) ?? `:${e}`;
|
|
9701
|
-
var p$
|
|
9721
|
+
var p$7 = (e) => e === ">";
|
|
9702
9722
|
var n$16 = (e) => {
|
|
9703
9723
|
let t = typeof e == "string" && e.trim() !== "" ? Number(e) : e;
|
|
9704
9724
|
return typeof t == "number" && Number.isInteger(t) && t >= 250 ? t : void 0;
|
|
@@ -9880,7 +9900,7 @@ var onInterval = (ms) => {
|
|
|
9880
9900
|
* and hide the arrow at the end already reached: `[named('row', onScroll()), setState({ key: 'atEnd', type:
|
|
9881
9901
|
* 'boolean', value: '{{ row.atEnd }}' })]`.
|
|
9882
9902
|
*/
|
|
9883
|
-
var onScroll = () => on(s$
|
|
9903
|
+
var onScroll = () => on(s$22);
|
|
9884
9904
|
var onPageLoad = () => on("onPageLoad");
|
|
9885
9905
|
/**
|
|
9886
9906
|
* The end of a server action this element started — the trigger a `detached` run needs, because that step returns
|
|
@@ -12094,7 +12114,7 @@ var toStates = (states) => {
|
|
|
12094
12114
|
};
|
|
12095
12115
|
var CLASS_NAME = /^-?[_a-zA-Z][\w-]*$/;
|
|
12096
12116
|
var toAncestors = (ancestors) => Object.entries(ancestors ?? {}).map(([name, ancestor]) => {
|
|
12097
|
-
if (!p$
|
|
12117
|
+
if (!p$7(name) && !CLASS_NAME.test(name)) throw new AuthoringError("ancestor-not-class", `The ancestor "${name}" is not a class name. An ancestor condition is keyed by a class that ancestor wears — \`[card.name]\` for a \`styles()\` declaration — or by \`'>'\` for the parent, whatever it wears.`);
|
|
12098
12118
|
return [name, toBlocks({
|
|
12099
12119
|
css: ancestor.css,
|
|
12100
12120
|
states: ancestor.states,
|
|
@@ -12132,7 +12152,7 @@ var assertPseudo = (pseudo, part) => {
|
|
|
12132
12152
|
for (const rules of ruleSets) for (const [property, value] of Object.entries(rules)) {
|
|
12133
12153
|
if (property === "content") {
|
|
12134
12154
|
if (!i$16.includes(pseudo)) throw new AuthoringError("style-pseudo-content", `\`content\` on \`::${pseudo}\` draws nothing: only ${i$16.map((name) => `\`::${name}\``).join(", ")} take one.`);
|
|
12135
|
-
if (!m$
|
|
12155
|
+
if (!m$4(String(value))) throw new AuthoringError("style-pseudo-content", `\`content: ${String(value)}\` on \`::${pseudo}\` is not text CSS can read, so nothing is drawn. Text goes in quotes inside the string — \`content: '"${String(value).replace(/"/g, "\\\"")}"'\` — and an empty box is \`'""'\`; \`counter()\`, \`attr()\` and \`url()\` are written as they are.`);
|
|
12136
12156
|
continue;
|
|
12137
12157
|
}
|
|
12138
12158
|
if (!u$7(pseudo, property)) throw new AuthoringError("style-pseudo-property", `\`${property}\` on \`::${pseudo}\` is dropped by every browser — \`::${pseudo}\` honours ${(l$8[pseudo] ?? []).map((name) => `\`${name}\``).join(", ")}. Style the element itself for the rest.`);
|
|
@@ -14305,6 +14325,33 @@ var footerRow = styles("footer-row", { css: {
|
|
|
14305
14325
|
gap: "8px"
|
|
14306
14326
|
}
|
|
14307
14327
|
} });
|
|
14328
|
+
container({
|
|
14329
|
+
id: "not-found-main",
|
|
14330
|
+
subType: "main",
|
|
14331
|
+
class: [shell$1, heroBand],
|
|
14332
|
+
children: [
|
|
14333
|
+
text$1("404", {
|
|
14334
|
+
id: "not-found-kicker",
|
|
14335
|
+
class: kicker
|
|
14336
|
+
}),
|
|
14337
|
+
heading("Page not found", {
|
|
14338
|
+
id: "not-found-title",
|
|
14339
|
+
subType: "h1",
|
|
14340
|
+
class: sectionTitle
|
|
14341
|
+
}),
|
|
14342
|
+
paragraph({
|
|
14343
|
+
id: "not-found-lede",
|
|
14344
|
+
class: bodyText,
|
|
14345
|
+
content: "There is nothing at this address — it may have moved, or the link has a typo."
|
|
14346
|
+
}),
|
|
14347
|
+
link({
|
|
14348
|
+
id: "not-found-home",
|
|
14349
|
+
href: "/",
|
|
14350
|
+
class: button,
|
|
14351
|
+
content: "Back to the home page"
|
|
14352
|
+
})
|
|
14353
|
+
]
|
|
14354
|
+
});
|
|
14308
14355
|
/**
|
|
14309
14356
|
* The space, declared: this file is the page, and it assembles the rest — the palette in `tokens.ts`, the classes in
|
|
14310
14357
|
* `theme.ts`, what the page says in `content.ts`. Everything else — element ids, style selector names, the
|
|
@@ -14614,6 +14661,17 @@ container({
|
|
|
14614
14661
|
})]
|
|
14615
14662
|
})]
|
|
14616
14663
|
});
|
|
14664
|
+
heading("Page not found", {
|
|
14665
|
+
id: "not-found-title",
|
|
14666
|
+
subType: "h1"
|
|
14667
|
+
}), paragraph({
|
|
14668
|
+
id: "not-found-lede",
|
|
14669
|
+
content: "There is nothing at this address."
|
|
14670
|
+
}), link({
|
|
14671
|
+
id: "not-found-home",
|
|
14672
|
+
href: "/",
|
|
14673
|
+
content: "Back to the home page"
|
|
14674
|
+
});
|
|
14617
14675
|
/**
|
|
14618
14676
|
* The space, declared — and nothing in it yet but what every space starts from: tokens for both themes, a layout the
|
|
14619
14677
|
* pages render in, and one page.
|
|
@@ -15211,6 +15269,14 @@ var builtPluginTypes = (folder) => existsSync(folder) ? readdirSync(folder, { wi
|
|
|
15211
15269
|
return [entry.name, ...e$32(pluginSchema) ? Object.keys(pluginSchema) : []];
|
|
15212
15270
|
}) : [];
|
|
15213
15271
|
/**
|
|
15272
|
+
* The stylesheets of the plugins under `folder` — every `.css` in a plugin's folder, at any depth — as their text, for
|
|
15273
|
+
* `authorSpace`'s `stylesheets`: the tokens a plugin's own CSS reads are tokens the space uses.
|
|
15274
|
+
*/
|
|
15275
|
+
var pluginStylesheets = (folder) => existsSync(folder) ? readdirSync(folder, {
|
|
15276
|
+
recursive: true,
|
|
15277
|
+
withFileTypes: true
|
|
15278
|
+
}).filter((entry) => entry.isFile() && entry.name.endsWith(".css")).map((entry) => path.join(entry.parentPath, entry.name)).sort().map((file) => readFileSync(file, "utf-8")) : [];
|
|
15279
|
+
/**
|
|
15214
15280
|
* `projectAuthoring` of the project at `root`, held to the same check: for a tool that works on a project from another
|
|
15215
15281
|
* folder — the CLI, run anywhere inside one. A project's own code calls `projectAuthoring()`.
|
|
15216
15282
|
*/
|
|
@@ -15221,6 +15287,7 @@ var projectAuthoringAt = async (root) => {
|
|
|
15221
15287
|
return {
|
|
15222
15288
|
plugins: await pluginDeclarations(path.join(at, o$1$1)),
|
|
15223
15289
|
pluginTypes: builtPluginTypes(path.join(at, u$2)),
|
|
15290
|
+
stylesheets: pluginStylesheets(path.join(at, o$1$1)),
|
|
15224
15291
|
...existsSync(serverData) ? { serverData: projectData(serverData) } : {},
|
|
15225
15292
|
data: publicData(path.join(at, p$1))
|
|
15226
15293
|
};
|
|
@@ -18285,11 +18352,43 @@ var move$1 = async (elementId, options) => {
|
|
|
18285
18352
|
].join("\n"));
|
|
18286
18353
|
};
|
|
18287
18354
|
//#endregion
|
|
18355
|
+
//#region src/commands/projectSettings.ts
|
|
18356
|
+
/**
|
|
18357
|
+
* What the project's processes start with: what its `.env` sets, under what the environment already has — as its
|
|
18358
|
+
* scripts read it (`--env-file-if-exists=.env`). Read, never loaded into this process's own environment.
|
|
18359
|
+
*/
|
|
18360
|
+
var projectSettings = async (root) => {
|
|
18361
|
+
const text = await fs.readFile(path.join(root, ".env"), "utf-8").catch(() => void 0);
|
|
18362
|
+
const parsed = text === void 0 ? {} : parseEnv(text);
|
|
18363
|
+
return {
|
|
18364
|
+
file: text !== void 0,
|
|
18365
|
+
value: (name) => process.env[name] ?? parsed[name] ?? ""
|
|
18366
|
+
};
|
|
18367
|
+
};
|
|
18368
|
+
//#endregion
|
|
18288
18369
|
//#region src/commands/check.ts
|
|
18289
|
-
/**
|
|
18370
|
+
/** The account `verify` signs in as for the pages that send a visitor to sign in: the project's, in `.env`. */
|
|
18371
|
+
var CHECK_USER_ENV = "PLITZI_CHECK_USER";
|
|
18372
|
+
/** Where the account's password is read: `.env` or the environment, never the command line, which a shell keeps. */
|
|
18290
18373
|
var CHECK_PASSWORD_ENV = "PLITZI_CHECK_PASSWORD";
|
|
18291
18374
|
/** The project's sign-in route: where `createServer({ auth })` mounts its flows unless told otherwise. */
|
|
18292
18375
|
var SIGN_IN_PATH = "/auth/login";
|
|
18376
|
+
/**
|
|
18377
|
+
* The account a check signs in as: the one named, or the project's own (`PLITZI_CHECK_USER`) — `undefined` when
|
|
18378
|
+
* neither is — with its password from `PLITZI_CHECK_PASSWORD`, each read from the environment over `.env`, as the
|
|
18379
|
+
* project's scripts read them.
|
|
18380
|
+
*/
|
|
18381
|
+
var checkAccount = async (root, username) => {
|
|
18382
|
+
const settings = await projectSettings(root);
|
|
18383
|
+
const named = username ?? settings.value("PLITZI_CHECK_USER");
|
|
18384
|
+
if (!named) return;
|
|
18385
|
+
const password = settings.value(CHECK_PASSWORD_ENV);
|
|
18386
|
+
if (!password) return { problem: `${named} signs in with the password in ${CHECK_PASSWORD_ENV}: set it in .env — never on the command line, which a shell keeps` };
|
|
18387
|
+
return {
|
|
18388
|
+
username: named,
|
|
18389
|
+
password
|
|
18390
|
+
};
|
|
18391
|
+
};
|
|
18293
18392
|
/** The space the project declares, authored. Only for one written here. */
|
|
18294
18393
|
var projectSpace = async (root) => {
|
|
18295
18394
|
const project = await loadProjectSpace(root);
|
|
@@ -18310,12 +18409,25 @@ var projectSpace = async (root) => {
|
|
|
18310
18409
|
* (`/p/:slug`), so the one meant is read off the list rather than guessed from letters.
|
|
18311
18410
|
*/
|
|
18312
18411
|
var noPageMessage = (handles, pathname) => `no page of the space answers at ${pathname}: its pages are ${Object.values(handles.pages).map((page) => page.path).join(", ")}`;
|
|
18412
|
+
/**
|
|
18413
|
+
* The space's "not found" page: a page whose slug is `*`, the page of every address under its folder that no other
|
|
18414
|
+
* page answers. Not every path ending in `*` — `update/*` is a page of its own that answers a whole subtree.
|
|
18415
|
+
*/
|
|
18416
|
+
var isNotFoundPage = (page) => page.slug === "*";
|
|
18417
|
+
/** A path whose last segment is `*`: it answers every address under it. */
|
|
18418
|
+
var isSplat = (path) => path.endsWith("/*");
|
|
18419
|
+
/** Where `verify` looks at a page for an address nothing answers: an address under its folder no page has. */
|
|
18420
|
+
var NOT_FOUND_PROBE = "plitzi-check-not-found";
|
|
18421
|
+
/** The page an address is answered by — as the router picks: every page with a path of its own first, then the "not found" pages, the deepest folder's before the space's. */
|
|
18313
18422
|
var pageFor = (handles, pathname) => {
|
|
18314
18423
|
const wanted = pathname.replace(/\/$/, "").split("/");
|
|
18315
|
-
|
|
18316
|
-
const
|
|
18317
|
-
|
|
18318
|
-
|
|
18424
|
+
const answers = (path) => {
|
|
18425
|
+
const splat = isSplat(path);
|
|
18426
|
+
const segments = (splat ? path.slice(0, -2) : path.replace(/\/$/, "")).split("/");
|
|
18427
|
+
return (splat ? wanted.length > segments.length : segments.length === wanted.length) && segments.every((segment, index) => segment.startsWith(":") || /^\{\{.*\}\}$/.test(segment) || segment === wanted[index]);
|
|
18428
|
+
};
|
|
18429
|
+
const pages = Object.values(handles.pages);
|
|
18430
|
+
return [...pages.filter((page) => !isNotFoundPage(page)), ...pages.filter(isNotFoundPage).sort((a, b) => b.path.length - a.path.length)].find((page) => answers(page.path))?.id;
|
|
18319
18431
|
};
|
|
18320
18432
|
/** The elements a piece of HTML carries, by the id each is marked with. */
|
|
18321
18433
|
var markedIn = (html) => new Set([...html.matchAll(/data-plitzi-el="([^"]+)"/g)].map(([, id]) => id));
|
|
@@ -18708,17 +18820,23 @@ var checkAt = async (browser, origin, pathname, width, scheme, authored, asked,
|
|
|
18708
18820
|
} });
|
|
18709
18821
|
if (!signedIn.ok()) return unchecked({
|
|
18710
18822
|
code: "not-signed-in",
|
|
18711
|
-
message: signedIn.status() === 404 ? `nothing answers at ${SIGN_IN_PATH}: --as signs in through the routes createServer({ auth }) mounts there` : `${account.username} was not signed in (${String(signedIn.status())}): ${(await signedIn.text()).slice(0, 200)} — check ${CHECK_PASSWORD_ENV}`,
|
|
18823
|
+
message: signedIn.status() === 404 ? `nothing answers at ${SIGN_IN_PATH}: --as signs in through the routes createServer({ auth }) mounts there` : `${account.username} was not signed in (${String(signedIn.status())}): ${(await signedIn.text()).slice(0, 200)} — check ${CHECK_PASSWORD_ENV} in .env`,
|
|
18712
18824
|
width
|
|
18713
18825
|
});
|
|
18714
18826
|
}
|
|
18715
18827
|
const consoleErrors = [];
|
|
18716
18828
|
const failedRequests = [];
|
|
18717
18829
|
page.on("pageerror", (error) => consoleErrors.push(error.message));
|
|
18830
|
+
const address = `${origin}${pathname}`;
|
|
18718
18831
|
page.on("console", (message) => {
|
|
18719
|
-
if (message.type() === "error") consoleErrors.push(message.text());
|
|
18832
|
+
if (message.type() === "error" && message.location().url !== address) consoleErrors.push(message.text());
|
|
18720
18833
|
});
|
|
18834
|
+
let sentWith;
|
|
18721
18835
|
page.on("response", (response) => {
|
|
18836
|
+
if (sentWith === void 0 && response.url() === address) {
|
|
18837
|
+
sentWith = response.status();
|
|
18838
|
+
return;
|
|
18839
|
+
}
|
|
18722
18840
|
if (response.status() >= 400 && response.url().startsWith(origin)) failedRequests.push(`${String(response.status())} ${response.url().slice(origin.length)}`);
|
|
18723
18841
|
});
|
|
18724
18842
|
if (handles && !pageFor(handles, pathname)) return {
|
|
@@ -18743,7 +18861,7 @@ var checkAt = async (browser, origin, pathname, width, scheme, authored, asked,
|
|
|
18743
18861
|
if (withoutTrailingSlash(landed) !== withoutTrailingSlash(new URL(`${origin}${pathname}`).pathname)) return {
|
|
18744
18862
|
...unchecked({
|
|
18745
18863
|
code: "redirected",
|
|
18746
|
-
message: account ? `${pathname} sent the browser to ${landed}, signed in as ${account.username}: that account may not see it` : `${pathname} sent the browser to ${landed} — what a page for signed-in visitors does: \`plitzi page check ${pathname} --as <username>\` signs in first, the password in ${CHECK_PASSWORD_ENV}`,
|
|
18864
|
+
message: account ? `${pathname} sent the browser to ${landed}, signed in as ${account.username}: that account may not see it` : `${pathname} sent the browser to ${landed} — what a page for signed-in visitors does: \`plitzi page check ${pathname} --as <username>\` signs in first, the password in ${CHECK_PASSWORD_ENV} in .env`,
|
|
18747
18865
|
width
|
|
18748
18866
|
}),
|
|
18749
18867
|
consoleErrors,
|
|
@@ -18773,8 +18891,18 @@ var checkAt = async (browser, origin, pathname, width, scheme, authored, asked,
|
|
|
18773
18891
|
...flow.on === void 0 ? {} : { elementId: flow.on },
|
|
18774
18892
|
width
|
|
18775
18893
|
}));
|
|
18894
|
+
const statusIssues = (handles && pageId ? isNotFoundPage(handles.pages[pageId]) : false) && sentWith !== 404 ? [{
|
|
18895
|
+
code: "page-status",
|
|
18896
|
+
message: `${pathname} shows the page for an address nothing answers, sent with status ${String(sentWith)}: a crawler reads it as a page that exists — the server should send it with 404`,
|
|
18897
|
+
width
|
|
18898
|
+
}] : sentWith !== void 0 && sentWith >= 400 && sentWith !== 404 ? [{
|
|
18899
|
+
code: "page-status",
|
|
18900
|
+
message: `${pathname} was sent with status ${String(sentWith)}`,
|
|
18901
|
+
width
|
|
18902
|
+
}] : [];
|
|
18776
18903
|
const issues = [
|
|
18777
18904
|
...noPage,
|
|
18905
|
+
...statusIssues,
|
|
18778
18906
|
...report.issues.map((issue) => ({
|
|
18779
18907
|
...issue,
|
|
18780
18908
|
width
|
|
@@ -18803,7 +18931,8 @@ var checkAt = async (browser, origin, pathname, width, scheme, authored, asked,
|
|
|
18803
18931
|
...!asked.state || devTools.sources === void 0 ? {} : { sources: devTools.sources },
|
|
18804
18932
|
...devTools.element === void 0 ? {} : { element: devTools.element },
|
|
18805
18933
|
...styled === void 0 ? {} : { styles: styled },
|
|
18806
|
-
...clicked === void 0 ? {} : { clicked }
|
|
18934
|
+
...clicked === void 0 ? {} : { clicked },
|
|
18935
|
+
...sentWith === 404 ? { notFound: true } : {}
|
|
18807
18936
|
};
|
|
18808
18937
|
};
|
|
18809
18938
|
/** What the page holds, when it was asked for: one compact line each, the shape of a source rather than its rows. */
|
|
@@ -18859,6 +18988,7 @@ var clickedText = (clicked) => {
|
|
|
18859
18988
|
var reportText$2 = (report, asked) => {
|
|
18860
18989
|
const head = `${report.path} at ${String(report.width)} px`;
|
|
18861
18990
|
const held = [
|
|
18991
|
+
...report.notFound ? [" · sent with status 404: the page for an address that shows nothing"] : [],
|
|
18862
18992
|
...listsText(report.lists),
|
|
18863
18993
|
...browserOnlyText(report),
|
|
18864
18994
|
...heldText(report, asked),
|
|
@@ -18879,27 +19009,27 @@ var reportText$2 = (report, asked) => {
|
|
|
18879
19009
|
].join("\n");
|
|
18880
19010
|
};
|
|
18881
19011
|
var withoutTrailingSlash = (path) => path.length > 1 ? path.replace(/\/+$/, "") : path;
|
|
18882
|
-
var check = async (
|
|
19012
|
+
var check = async (routes, options) => {
|
|
18883
19013
|
const project = await projectHere("whose page to check");
|
|
18884
19014
|
if (!project) return;
|
|
18885
|
-
const
|
|
18886
|
-
if (
|
|
18887
|
-
fail(`--as ${
|
|
19015
|
+
const account = options.as === void 0 ? void 0 : await checkAccount(project.root, options.as);
|
|
19016
|
+
if (account && "problem" in account) {
|
|
19017
|
+
fail(`--as ${account.problem}.`);
|
|
18888
19018
|
return;
|
|
18889
19019
|
}
|
|
18890
19020
|
if (options.fill && !options.click) {
|
|
18891
19021
|
fail("--fill fills fields for a --click: with nothing clicked, nothing they hold is sent anywhere.");
|
|
18892
19022
|
return;
|
|
18893
19023
|
}
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
|
|
18897
|
-
}
|
|
19024
|
+
if (options.click && routes.length > 1) {
|
|
19025
|
+
fail(`--click clicks one element of one page: ${String(routes.length)} were named.`);
|
|
19026
|
+
return;
|
|
19027
|
+
}
|
|
18898
19028
|
const asked = {
|
|
18899
19029
|
state: Boolean(options.state),
|
|
18900
19030
|
...options.element ? { element: options.element } : {}
|
|
18901
19031
|
};
|
|
18902
|
-
const reports = await checkRoutes(project,
|
|
19032
|
+
const reports = await checkRoutes(project, routes.length > 0 ? routes : ["/"], {
|
|
18903
19033
|
widths: options.width ?? [1440, 390],
|
|
18904
19034
|
asked,
|
|
18905
19035
|
ssr: Boolean(options.ssr),
|
|
@@ -18942,7 +19072,7 @@ var checkRoutes = async (project, routes, options) => {
|
|
|
18942
19072
|
var staticPaths = async (root) => {
|
|
18943
19073
|
const authored = await projectSpace(root);
|
|
18944
19074
|
if ("problem" in authored) return authored;
|
|
18945
|
-
return Object.values(authored.handles.pages).map((page) => page.path).filter((pathname) => pathname.split("/").every((segment) => !segment.startsWith(":") && !/\{\{.*\}\}/.test(segment)));
|
|
19075
|
+
return Object.values(authored.handles.pages).map((page) => isNotFoundPage(page) ? `${page.path.slice(0, -1)}${NOT_FOUND_PROBE}` : page.path).filter((pathname) => pathname.split("/").every((segment) => !segment.startsWith(":") && !/\{\{.*\}\}/.test(segment)));
|
|
18946
19076
|
};
|
|
18947
19077
|
//#endregion
|
|
18948
19078
|
//#region src/commands/functions.ts
|
|
@@ -23510,6 +23640,82 @@ var compareWith = async (browser, taken, target, view, regions, base) => {
|
|
|
23510
23640
|
diff: `${base}-diff.png`
|
|
23511
23641
|
};
|
|
23512
23642
|
};
|
|
23643
|
+
/**
|
|
23644
|
+
* The pictures on one sheet, each under what it shows, four to a row and each 360 px wide: runs in the page, so it is
|
|
23645
|
+
* self-contained — no import, nothing it closes over.
|
|
23646
|
+
*/
|
|
23647
|
+
function contactSheet(cells) {
|
|
23648
|
+
const CELL_WIDTH = 360;
|
|
23649
|
+
const LABEL = 24;
|
|
23650
|
+
const GAP = 12;
|
|
23651
|
+
const load = (src) => new Promise((resolve, reject) => {
|
|
23652
|
+
const image = new Image();
|
|
23653
|
+
image.onload = () => resolve(image);
|
|
23654
|
+
image.onerror = () => reject(/* @__PURE__ */ new Error("a picture of the sheet did not load"));
|
|
23655
|
+
image.src = src;
|
|
23656
|
+
});
|
|
23657
|
+
return Promise.all(cells.map((cell) => load(cell.src))).then((images) => {
|
|
23658
|
+
const columns = Math.min(4, images.length);
|
|
23659
|
+
const heightOf = (image) => Math.round(image.naturalHeight * CELL_WIDTH / image.naturalWidth);
|
|
23660
|
+
const cellHeight = Math.max(...images.map(heightOf));
|
|
23661
|
+
const rows = Math.ceil(images.length / columns);
|
|
23662
|
+
const canvas = document.createElement("canvas");
|
|
23663
|
+
canvas.width = GAP + columns * 372;
|
|
23664
|
+
canvas.height = GAP + rows * (LABEL + cellHeight + GAP);
|
|
23665
|
+
const context = canvas.getContext("2d");
|
|
23666
|
+
if (!context) throw new Error("no canvas to draw the sheet on");
|
|
23667
|
+
context.fillStyle = "#ffffff";
|
|
23668
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
23669
|
+
context.font = "13px ui-monospace, monospace";
|
|
23670
|
+
images.forEach((image, index) => {
|
|
23671
|
+
const x = GAP + index % columns * 372;
|
|
23672
|
+
const y = GAP + Math.floor(index / columns) * (LABEL + cellHeight + GAP);
|
|
23673
|
+
context.fillStyle = "#16181d";
|
|
23674
|
+
context.fillText(cells[index].label, x, y + 16);
|
|
23675
|
+
context.drawImage(image, x, y + LABEL, CELL_WIDTH, heightOf(image));
|
|
23676
|
+
context.strokeStyle = "#d0d4dc";
|
|
23677
|
+
context.strokeRect(x + .5, y + LABEL + .5, 359, heightOf(image) - 1);
|
|
23678
|
+
});
|
|
23679
|
+
return canvas.toDataURL("image/png");
|
|
23680
|
+
});
|
|
23681
|
+
}
|
|
23682
|
+
/**
|
|
23683
|
+
* Each element clicked in turn, and the page pictured after it — `frames` pictures `every` ms apart, or one. What a
|
|
23684
|
+
* click changed is measured against the picture before it: nothing at all is said, as a click that did nothing.
|
|
23685
|
+
*/
|
|
23686
|
+
var clickThrough = async (taken, how, clicks, { frames, every }) => {
|
|
23687
|
+
const moments = [{
|
|
23688
|
+
label: "before",
|
|
23689
|
+
png: taken.png
|
|
23690
|
+
}];
|
|
23691
|
+
const clicked = [];
|
|
23692
|
+
for (const target of clicks) {
|
|
23693
|
+
const element = taken.page.locator(selectorOf(target)).first();
|
|
23694
|
+
if (await element.count() === 0) return { problem: `${target} is not on the page: --click takes an element's name or a CSS selector.` };
|
|
23695
|
+
const before = moments[moments.length - 1].png;
|
|
23696
|
+
try {
|
|
23697
|
+
await element.click({ timeout: 3e3 });
|
|
23698
|
+
} catch (error) {
|
|
23699
|
+
return { problem: `${target} could not be clicked — hidden, covered or disabled: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}` };
|
|
23700
|
+
}
|
|
23701
|
+
for (let frame = 1; frame <= Math.max(1, frames); frame += 1) {
|
|
23702
|
+
await taken.page.waitForTimeout(every);
|
|
23703
|
+
moments.push({
|
|
23704
|
+
label: `${target} +${String(frame * every)} ms`,
|
|
23705
|
+
png: await capture(taken.page, how)
|
|
23706
|
+
});
|
|
23707
|
+
}
|
|
23708
|
+
const after = moments[moments.length - 1].png;
|
|
23709
|
+
clicked.push({
|
|
23710
|
+
target,
|
|
23711
|
+
changed: (await comparePictures(taken.page, dataUrl(before), dataUrl(after))).changed
|
|
23712
|
+
});
|
|
23713
|
+
}
|
|
23714
|
+
return {
|
|
23715
|
+
moments,
|
|
23716
|
+
clicked
|
|
23717
|
+
};
|
|
23718
|
+
};
|
|
23513
23719
|
var shot = async (route, options) => {
|
|
23514
23720
|
const project = await projectHere("whose page to picture");
|
|
23515
23721
|
if (!project) return;
|
|
@@ -23523,6 +23729,17 @@ var shot = async (route, options) => {
|
|
|
23523
23729
|
fail(browser.problem);
|
|
23524
23730
|
return;
|
|
23525
23731
|
}
|
|
23732
|
+
const clicks = options.click ?? [];
|
|
23733
|
+
if (options.compare && clicks.length > 0) {
|
|
23734
|
+
await browser.close();
|
|
23735
|
+
fail("`--compare` sets the page beside another site as it loads: leave out --click, or picture the click on its own.");
|
|
23736
|
+
return;
|
|
23737
|
+
}
|
|
23738
|
+
if (options.sheet && clicks.length === 0 && (options.frames ?? 0) <= 1) {
|
|
23739
|
+
await browser.close();
|
|
23740
|
+
fail("`--sheet` lays out the pictures a --click or --frames takes: with neither, there is one picture and no sheet.");
|
|
23741
|
+
return;
|
|
23742
|
+
}
|
|
23526
23743
|
if (options.compare && (options.clip || options.scrollTo || options.viewport)) {
|
|
23527
23744
|
await browser.close();
|
|
23528
23745
|
fail("`--compare` sets the whole page beside the other site, section by section: leave out --clip, --scroll-to and --viewport.");
|
|
@@ -23539,7 +23756,7 @@ var shot = async (route, options) => {
|
|
|
23539
23756
|
...options.clip ? { clip: options.clip } : {},
|
|
23540
23757
|
...options.scrollTo ? { scrollTo: options.scrollTo } : {},
|
|
23541
23758
|
...options.viewport ? { viewport: true } : {},
|
|
23542
|
-
still: !options.clip && !options.scrollTo && !options.viewport && (options.frames ?? 0) <= 1
|
|
23759
|
+
still: !options.clip && !options.scrollTo && !options.viewport && (options.frames ?? 0) <= 1 && clicks.length === 0
|
|
23543
23760
|
};
|
|
23544
23761
|
const how = framing(view);
|
|
23545
23762
|
const taken = await pictureOf(browser, `${where.origin}${pathname}`, view, where.origin);
|
|
@@ -23571,13 +23788,29 @@ var shot = async (route, options) => {
|
|
|
23571
23788
|
report.compare = compared;
|
|
23572
23789
|
}
|
|
23573
23790
|
const frames = options.frames ?? 0;
|
|
23574
|
-
|
|
23575
|
-
|
|
23791
|
+
const every = options.every ?? 500;
|
|
23792
|
+
let moments = [];
|
|
23793
|
+
if (clicks.length > 0) {
|
|
23794
|
+
const played = await clickThrough(taken, how, clicks, {
|
|
23795
|
+
frames,
|
|
23796
|
+
every
|
|
23797
|
+
});
|
|
23798
|
+
if ("problem" in played) {
|
|
23799
|
+
fail(played.problem);
|
|
23800
|
+
return;
|
|
23801
|
+
}
|
|
23802
|
+
moments = played.moments;
|
|
23803
|
+
report.clicked = played.clicked;
|
|
23804
|
+
} else if (frames > 1) {
|
|
23576
23805
|
const pictures = [taken.png];
|
|
23577
23806
|
for (let frame = 1; frame < frames; frame += 1) {
|
|
23578
23807
|
await taken.page.waitForTimeout(every);
|
|
23579
23808
|
pictures.push(await capture(taken.page, how));
|
|
23580
23809
|
}
|
|
23810
|
+
moments = pictures.map((png, frame) => ({
|
|
23811
|
+
label: frame === 0 ? "before" : `+${String(frame * every)} ms`,
|
|
23812
|
+
png
|
|
23813
|
+
}));
|
|
23581
23814
|
const diffs = [];
|
|
23582
23815
|
for (let frame = 1; frame < pictures.length; frame += 1) diffs.push(await comparePictures(taken.page, dataUrl(pictures[frame - 1]), dataUrl(pictures[frame]), { regions }));
|
|
23583
23816
|
const whole = how.kind === "element" ? options.clip ?? "element" : "viewport";
|
|
@@ -23592,6 +23825,24 @@ var shot = async (route, options) => {
|
|
|
23592
23825
|
still: moved.filter((region) => region.changed <= MOVING_PERCENT).map((region) => region.name)
|
|
23593
23826
|
};
|
|
23594
23827
|
}
|
|
23828
|
+
if (moments.length > 1 && options.sheet) {
|
|
23829
|
+
const sheet = await taken.page.evaluate(contactSheet, moments.map((moment) => ({
|
|
23830
|
+
label: moment.label,
|
|
23831
|
+
src: dataUrl(moment.png)
|
|
23832
|
+
})));
|
|
23833
|
+
report.sheet = `${base}-sheet.png`;
|
|
23834
|
+
await writeFile(report.sheet, Buffer.from(sheet.replace(/^data:image\/png;base64,/, ""), "base64"));
|
|
23835
|
+
} else if (moments.length > 1 && clicks.length > 0) {
|
|
23836
|
+
report.pictures = [];
|
|
23837
|
+
for (const [index, moment] of moments.slice(1).entries()) {
|
|
23838
|
+
const file = `${base}-${String(index + 1)}.png`;
|
|
23839
|
+
await writeFile(file, moment.png);
|
|
23840
|
+
report.pictures.push({
|
|
23841
|
+
label: moment.label,
|
|
23842
|
+
out: file
|
|
23843
|
+
});
|
|
23844
|
+
}
|
|
23845
|
+
}
|
|
23595
23846
|
if (options.json) {
|
|
23596
23847
|
console.log(JSON.stringify(report));
|
|
23597
23848
|
return;
|
|
@@ -23600,6 +23851,9 @@ var shot = async (route, options) => {
|
|
|
23600
23851
|
if (report.settled) console.log(chalk.dim(`${String(report.settled)} arrival${report.settled === 1 ? "" : "s"} waiting for the scroll shown as they end — --scroll-to <id> --viewport --frames 4 shows one arriving`));
|
|
23601
23852
|
if (report.unloaded) console.log(chalk.yellow(`${String(report.unloaded)} picture(s) had not loaded when the page was taken`));
|
|
23602
23853
|
if (report.compare) console.log(compareText(report.compare).join("\n"));
|
|
23854
|
+
for (const { target, changed } of report.clicked ?? []) console.log(changed > 0 ? `clicked ${target}: ${String(changed)}% of the picture changed` : chalk.yellow(`clicked ${target}: nothing changed — the click did nothing to see`));
|
|
23855
|
+
if (report.sheet) console.log(`sheet: ${report.sheet}`);
|
|
23856
|
+
for (const picture of report.pictures ?? []) console.log(`${picture.label}: ${picture.out}`);
|
|
23603
23857
|
if (report.frames) console.log(report.frames.moving.length > 0 ? `moving over ${String(report.frames.count)} frames: ${regionsText(report.frames.moving)}` : `nothing moved over ${String(report.frames.count)} frames, ${String(report.frames.every)} ms apart`);
|
|
23604
23858
|
} finally {
|
|
23605
23859
|
await browser.close();
|
|
@@ -23664,8 +23918,10 @@ var scriptsOf = async (root) => {
|
|
|
23664
23918
|
const scripts = e$3(parsed) && e$3(parsed.scripts) ? parsed.scripts : {};
|
|
23665
23919
|
return Object.fromEntries(Object.entries(scripts).filter((entry) => typeof entry[1] === "string"));
|
|
23666
23920
|
};
|
|
23921
|
+
/** A page the browser was sent away from — one for signed-in visitors, sent to sign in. */
|
|
23922
|
+
var redirected = (report) => report.issues.some((issue) => issue.code === "redirected");
|
|
23667
23923
|
/** A page the browser was sent away from, or that nothing answered: not looked at, so neither passing nor failing. */
|
|
23668
|
-
var unchecked = (report) => report.issues.some((issue) => issue.code === "
|
|
23924
|
+
var unchecked = (report) => redirected(report) || report.issues.some((issue) => issue.code === "no-answer");
|
|
23669
23925
|
var verify = async (options) => {
|
|
23670
23926
|
const project = await projectHere("to verify");
|
|
23671
23927
|
if (!project) return;
|
|
@@ -23703,29 +23959,53 @@ var verify = async (options) => {
|
|
|
23703
23959
|
} : pages));
|
|
23704
23960
|
if (failed) process.exitCode = 1;
|
|
23705
23961
|
};
|
|
23962
|
+
var NOT_STARTED = "start it (npm start) and verify again, or --no-pages to leave them";
|
|
23706
23963
|
var pagesChecked = async (project) => {
|
|
23707
23964
|
const paths = await staticPaths(project.root);
|
|
23708
23965
|
if ("problem" in paths) return paths;
|
|
23709
|
-
const
|
|
23966
|
+
const options = {
|
|
23710
23967
|
widths: WIDTHS,
|
|
23711
23968
|
asked: { state: false },
|
|
23712
23969
|
ssr: false
|
|
23713
|
-
});
|
|
23714
|
-
if ("problem" in reports) return { problem: `${reports.problem} — start it (npm start) and verify again, or --no-pages to leave them` };
|
|
23715
|
-
return {
|
|
23716
|
-
passing: reports.filter((report) => report.ok).length,
|
|
23717
|
-
failing: reports.filter((report) => !report.ok && !unchecked(report)),
|
|
23718
|
-
unchecked: [...new Set(reports.filter(unchecked).map((report) => report.path))]
|
|
23719
23970
|
};
|
|
23971
|
+
const reports = await checkRoutes(project, paths, options);
|
|
23972
|
+
if ("problem" in reports) return { problem: `${reports.problem} — ${NOT_STARTED}` };
|
|
23973
|
+
const sentAway = [...new Set(reports.filter(redirected).map((report) => report.path))];
|
|
23974
|
+
if (sentAway.length === 0) return summary(reports);
|
|
23975
|
+
const account = await checkAccount(project.root);
|
|
23976
|
+
if (!account || "problem" in account) return summary(reports, account ?? { none: true });
|
|
23977
|
+
const signedIn = await checkRoutes(project, sentAway, {
|
|
23978
|
+
...options,
|
|
23979
|
+
account
|
|
23980
|
+
});
|
|
23981
|
+
if ("problem" in signedIn) return { problem: `${signedIn.problem} — ${NOT_STARTED}` };
|
|
23982
|
+
return summary([...reports.filter((report) => !sentAway.includes(report.path)), ...signedIn], {
|
|
23983
|
+
as: account.username,
|
|
23984
|
+
checks: signedIn.filter((report) => !unchecked(report)).length
|
|
23985
|
+
});
|
|
23986
|
+
};
|
|
23987
|
+
var summary = (reports, signedIn) => ({
|
|
23988
|
+
passing: reports.filter((report) => report.ok).length,
|
|
23989
|
+
failing: reports.filter((report) => !report.ok && !unchecked(report)),
|
|
23990
|
+
unchecked: [...new Set(reports.filter(unchecked).map((report) => report.path))],
|
|
23991
|
+
...signedIn ? { signedIn } : {}
|
|
23992
|
+
});
|
|
23993
|
+
/** Why the pages left are not checked, and what checks them. */
|
|
23994
|
+
var uncheckedText = (pages) => {
|
|
23995
|
+
const list = pages.unchecked.join(", ");
|
|
23996
|
+
const signedIn = pages.signedIn;
|
|
23997
|
+
if (!signedIn || "none" in signedIn) return `- not checked — sent elsewhere (a page for signed-in visitors) or not answered: ${list} — name an account of the project in .env, ${CHECK_USER_ENV} and ${CHECK_PASSWORD_ENV}, and verify signs in for them`;
|
|
23998
|
+
if ("problem" in signedIn) return `- not checked — sent elsewhere (a page for signed-in visitors) or not answered: ${list} — ${signedIn.problem}`;
|
|
23999
|
+
return `- not checked, even signed in as ${signedIn.as}: ${list} — that account may not see them, or nothing answered`;
|
|
23720
24000
|
};
|
|
23721
24001
|
var verifyText = (results, pages) => [...results.flatMap((result) => result.ok ? [chalk.green(`✓ ${result.name}`)] : [
|
|
23722
24002
|
chalk.red(`✗ ${result.name}`),
|
|
23723
24003
|
...(result.said ?? []).map((line) => ` ${line}`),
|
|
23724
24004
|
...result.fix ? [` → ${result.fix}`] : []
|
|
23725
24005
|
]), ...pages === void 0 ? [chalk.yellow("- pages not checked: --no-pages")] : "waiting" in pages ? [chalk.yellow(`- not run until ${pages.after} passes: ${pages.waiting.join(", ")} (--keep-going runs them anyway)`)] : "problem" in pages ? [chalk.red(`✗ pages — ${pages.problem}`)] : [
|
|
23726
|
-
pages.failing.length === 0 ? chalk.green(`✓ pages — ${String(pages.passing)} checks at ${WIDTHS.join(" and ")} px`) : chalk.red(`✗ pages — ${String(pages.failing.length)} of ${String(pages.passing + pages.failing.length)} checks`),
|
|
24006
|
+
pages.failing.length === 0 ? chalk.green(`✓ pages — ${String(pages.passing)} checks at ${WIDTHS.join(" and ")} px${pages.signedIn && "as" in pages.signedIn ? `, ${String(pages.signedIn.checks)} of them signed in as ${pages.signedIn.as}` : ""}`) : chalk.red(`✗ pages — ${String(pages.failing.length)} of ${String(pages.passing + pages.failing.length)} checks`),
|
|
23727
24007
|
...pages.failing.map((report) => reportText$2(report, { state: false })),
|
|
23728
|
-
...pages.unchecked.length > 0 ? [chalk.yellow(
|
|
24008
|
+
...pages.unchecked.length > 0 ? [chalk.yellow(uncheckedText(pages))] : []
|
|
23729
24009
|
]].join("\n");
|
|
23730
24010
|
//#endregion
|
|
23731
24011
|
//#region src/doctor/projectModules.ts
|
|
@@ -24066,17 +24346,8 @@ var minimumSecret = async (root) => {
|
|
|
24066
24346
|
const minimum = e$3(actions) ? actions.MIN_SIGNING_SECRET_LENGTH : void 0;
|
|
24067
24347
|
return typeof minimum === "number" ? minimum : void 0;
|
|
24068
24348
|
};
|
|
24069
|
-
/** What the process starts with: what `.env` sets, under what the environment already has — as the server reads it. */
|
|
24070
|
-
var settingsOf = async (root) => {
|
|
24071
|
-
const text = await readOptional(path.join(root, ".env"));
|
|
24072
|
-
const parsed = text === void 0 ? {} : parseEnv(text);
|
|
24073
|
-
return {
|
|
24074
|
-
file: text !== void 0,
|
|
24075
|
-
value: (name) => process.env[name] ?? parsed[name] ?? ""
|
|
24076
|
-
};
|
|
24077
|
-
};
|
|
24078
24349
|
var envChecks = async ({ root, answers }) => {
|
|
24079
|
-
const settings = await
|
|
24350
|
+
const settings = await projectSettings(root);
|
|
24080
24351
|
const where = settings.file ? ".env" : "the environment (there is no .env)";
|
|
24081
24352
|
const findings = [];
|
|
24082
24353
|
if (answers.mode === "client") {
|
|
@@ -24682,6 +24953,13 @@ var NODE_SCRIPTS = [
|
|
|
24682
24953
|
"start:dev",
|
|
24683
24954
|
"author"
|
|
24684
24955
|
];
|
|
24956
|
+
/** The variables a script sets before its command — `NODE_ENV=production node …` — by name. */
|
|
24957
|
+
var assignmentsOf = (command) => Object.fromEntries([...(/^((?:\w+=\S*\s+)*)/.exec(command)?.[1] ?? "").matchAll(/(\w+)=(\S*)/g)].map(([, name, value]) => [name, value]));
|
|
24958
|
+
/** A script's words when its command runs Node — after any variable it sets first — and none when it runs anything else. */
|
|
24959
|
+
var nodeWords = (command) => {
|
|
24960
|
+
const words = (command ?? "").replace(/^(?:\w+=\S*\s+)*/, "").split(/\s+/);
|
|
24961
|
+
return words[0] === "node" ? words : [];
|
|
24962
|
+
};
|
|
24685
24963
|
/**
|
|
24686
24964
|
* What a script that runs Node needs on disk: the file it starts, and every folder `--watch-path` names — Node stops at
|
|
24687
24965
|
* start when one of them is not there, which is why each watched folder keeps a `.gitkeep`.
|
|
@@ -24691,7 +24969,7 @@ var scriptTargetChecks = async ({ root, manifest }) => {
|
|
|
24691
24969
|
const findings = [];
|
|
24692
24970
|
for (const name of NODE_SCRIPTS) {
|
|
24693
24971
|
const command = scripts[name];
|
|
24694
|
-
const words =
|
|
24972
|
+
const words = nodeWords(command).slice(1);
|
|
24695
24973
|
const watched = words.flatMap((word) => /^--watch-path=(.+)$/.exec(word)?.[1] ?? []);
|
|
24696
24974
|
const started = words.filter((word) => !word.startsWith("-")).at(-1);
|
|
24697
24975
|
for (const folder of watched) if (!await fs.stat(path.join(root, folder)).then(() => true, () => false)) findings.push(say$3.error("watch-path-missing", `The script ${name} watches ${folder}, which is not there: Node stops before it starts.`, {
|
|
@@ -24727,8 +25005,12 @@ var scriptEnvChecks = ({ answers, manifest }) => {
|
|
|
24727
25005
|
if (answers.mode !== "server") return [];
|
|
24728
25006
|
const scripts = stringsOf(manifest.scripts);
|
|
24729
25007
|
return SERVER_SCRIPTS.flatMap((name) => {
|
|
24730
|
-
const words =
|
|
25008
|
+
const words = nodeWords(scripts[name]);
|
|
24731
25009
|
if (words.length === 0) return [];
|
|
25010
|
+
if (name === "start:prod" && assignmentsOf(scripts[name]).NODE_ENV !== "production") return [say$3.warning("start-prod-not-production", "The script start:prod does not set NODE_ENV=production: wherever it runs without it, the server is a development one — its dev tools on, and the trace of every action in what a visitor is answered.", {
|
|
25011
|
+
file: "package.json",
|
|
25012
|
+
fix: `NODE_ENV=production before node in start:prod — ${UPGRADE_PACKAGES} rewrites a script the CLI wrote.`
|
|
25013
|
+
})];
|
|
24732
25014
|
const flagged = words.some((word) => ENV_FLAG.test(word));
|
|
24733
25015
|
const preloaded = words.some((word, index) => word === `--import=${ENV_PRELOAD}` || word === "--import" && words[index + 1] === ENV_PRELOAD);
|
|
24734
25016
|
const watched = words.some((word) => word.startsWith("--watch"));
|
|
@@ -25452,7 +25734,9 @@ var previousLines = (previous) => previous.length ? [
|
|
|
25452
25734
|
...previous.map((url) => ` - ${url}`),
|
|
25453
25735
|
"Read each (a claude.ai artifact: open it with your artifact tool, never by fetching the URL). Number the new",
|
|
25454
25736
|
"findings after the highest id there, and fill `changed` with every earlier finding this project touched:",
|
|
25455
|
-
"fixed, partly or still open — and how you saw it, on the versions above."
|
|
25737
|
+
"fixed, partly or still open — and how you saw it, on the versions above.",
|
|
25738
|
+
"A link can stop opening — an artifact deleted, or no longer shared. Say which one did not, ask the person for",
|
|
25739
|
+
"the highest id it had, and leave `changed` to the reports you could read."
|
|
25456
25740
|
] : ["No earlier report: number the findings from PZ-1."];
|
|
25457
25741
|
var feedbackBrief = (facts, file, previous) => [
|
|
25458
25742
|
"A report to Plitzi, for the developer to send as a link.",
|
|
@@ -26749,7 +27033,7 @@ var reportText = (report) => {
|
|
|
26749
27033
|
}
|
|
26750
27034
|
for (const [title, findings] of groups) lines.push(...fileText(title, findings));
|
|
26751
27035
|
if (report.findings.length > 0) lines.push(chalk.dim(`What each means and what to write instead: the docs of each code — ${docsOf(report)}.`));
|
|
26752
|
-
const suggested = [...new Set(report.findings.filter((each) => each.origin === "authoring").map((each) => each.code))];
|
|
27036
|
+
const suggested = [...new Set(report.findings.filter((each) => each.origin === "authoring" && each.file !== void 0).map((each) => each.code))];
|
|
26753
27037
|
if (suggested.length > 0) lines.push(chalk.dim(`Authoring's suggestions (${suggested.join(", ")}) are quieted on the element they are about — \`quiet: ['${suggested[0]}']\` — not by a \`plitzi-lint-disable\` comment, which silences only lint's own codes.`));
|
|
26754
27038
|
const summary = [
|
|
26755
27039
|
plural(counts.error, "error"),
|
|
@@ -26901,14 +27185,14 @@ program.command("login").description("Sign in, in your browser. The session is k
|
|
|
26901
27185
|
program.command("logout").description("Sign out: the session is revoked on the platform and forgotten here").action(() => logout());
|
|
26902
27186
|
program.command("whoami").description("Who the CLI is signed in as, and the space it works in").option("--json", "One object: { api, user, space }, for a tool or an agent").option(...API_OPTION).action((options) => whoami(options));
|
|
26903
27187
|
spaceGroup.command("use").description("Choose the space to work in, in your browser. One at a time: it replaces the one chosen before").option(...API_OPTION).action((options) => space$2(options));
|
|
26904
|
-
pageGroup.command("check").argument("[
|
|
27188
|
+
pageGroup.command("check").argument("[paths...]", "The pages: /about /writers, in one browser. The home page when left out").description("Whether a page of the running project is whole, in text: every element on screen, no broken image, no console error, no failed flow").addOption(widthsOption("The widths to check it at, separated by commas", [1440, 390])).addOption(schemeOption()).option("--state", "Also what the page holds: its state, and every source by name").option("--element <id>", "Also one element: what it reads, its own state, whether it is on screen").option("--ssr", "Also the HTML the server sent against the hydrated page: what a server provider sent late").option("--click <id>", "Click one element once the page settles, and say what it changed — or that nothing did").addOption(new Option("--fill <id=value>", "Fill a field before the click, as a visitor types in it — once per field; a box takes true or false").argParser(fill)).option("--as <username>", "Sign in first as this account, through the server’s /auth routes — the password from PLITZI_CHECK_PASSWORD, in .env").option("--json", "One object per page and width, for a tool or an agent").action((paths, options) => check(paths, options));
|
|
26905
27189
|
spaceGroup.command("fix").description("What authoring would fix in the space, as edits to your source: shown as a diff; with --write written, formatted and checked").option("--write", "Write the edits — kept only when the space authors with each fix gone and no problem added").option("--json", "The plan as one object, for a tool or an agent").action((options) => fix(options));
|
|
26906
27190
|
pageGroup.command("import").argument("<url>", "The page to start from: https://example.com/pricing").description("A page of a site you verified, as a place to start writing from: its tokens, the outline of its blocks per breakpoint, its lists as JSON — never its words").option("-o, --out <dir>", "Where it is written, inside the project", "src/imported").addOption(widthsOption("The widths it is measured at: the widest is desktop, then tablet and mobile", [
|
|
26907
27191
|
1440,
|
|
26908
27192
|
768,
|
|
26909
27193
|
390
|
|
26910
27194
|
])).option("-f, --force", "Write over what --out already holds").option("--account", "A site not served from this machine: ask your Plitzi account whether one of your spaces verified its domain (signs in)").option("--json", "One object, for a tool or an agent").option(...API_OPTION).option(...DRY_RUN_OPTION).action((url, options) => importPage(url, options));
|
|
26911
|
-
pageGroup.command("shot").argument("[path]", "The page: /about. The home page when left out").description("A picture of one page of the running project — and, asked, how it differs from another or what moves").addOption(new Option("--width <px>", "The viewport width").argParser(width).default(1280)).addOption(new Option("--height <px>", "The viewport height (the picture is the whole page unless --viewport, --scroll-to or --clip)").argParser(positiveInteger).default(800)).addOption(schemeOption()).option("-o, --out <file>", "Where the picture goes. tmp/shots/<page>-<width>-<scheme>.png by default").option("--compare <url>", "Another site: the same page there, side by side, and how much differs by section").addOption(new Option("--frames <n>", "Pictures taken one after another: which sections move (a marquee, an autoplay)").argParser(positiveInteger)).addOption(new Option("--every <ms>", "How far apart --frames are taken").argParser(positiveInteger).default(500)).option("--wait-for <element>", "An element to wait for first: its name (data-plitzi-el) or a CSS selector").option("--clip <element>", "Only this element — its name or a CSS selector — scrolled to wherever it is").option("--scroll-to <element>", "The page scrolled until this element is in view; the picture is the screen there").option("--viewport", "Only what fits the viewport, not the whole page").option("--reduced-motion", "As a visitor who asked for less motion").option("--json", "One object, for a tool or an agent").action((path, options) => shot(path, options));
|
|
27195
|
+
pageGroup.command("shot").argument("[path]", "The page: /about. The home page when left out").description("A picture of one page of the running project — and, asked, how it differs from another or what moves").addOption(new Option("--width <px>", "The viewport width").argParser(width).default(1280)).addOption(new Option("--height <px>", "The viewport height (the picture is the whole page unless --viewport, --scroll-to or --clip)").argParser(positiveInteger).default(800)).addOption(schemeOption()).option("-o, --out <file>", "Where the picture goes. tmp/shots/<page>-<width>-<scheme>.png by default").option("--compare <url>", "Another site: the same page there, side by side, and how much differs by section").addOption(new Option("--frames <n>", "Pictures taken one after another: which sections move (a marquee, an autoplay)").argParser(positiveInteger)).addOption(new Option("--every <ms>", "How far apart --frames are taken").argParser(positiveInteger).default(500)).option("--wait-for <element>", "An element to wait for first: its name (data-plitzi-el) or a CSS selector").option("--clip <element>", "Only this element — its name or a CSS selector — scrolled to wherever it is").option("--scroll-to <element>", "The page scrolled until this element is in view; the picture is the screen there").option("--viewport", "Only what fits the viewport, not the whole page").option("--click <element...>", "Click these in order once the page settles — each its name or a CSS selector, inside a plugin too — picturing what each changed (--frames pictures --every ms apart after each)").option("--sheet", "Every picture taken — before, after each click, each frame — on one contact sheet").option("--reduced-motion", "As a visitor who asked for less motion").option("--json", "One object, for a tool or an agent").action((path, options) => shot(path, options));
|
|
26912
27196
|
program.command("verify").description("Whether the project is left passing: author, lints, types, format and every page, only the problems said").option("--no-pages", "Leave the pages out — said as such").option("--keep-going", "Run every check even after one fails — by default the first failure ends the run").option("--json", "One object, for a tool or an agent").action((options) => verify(options));
|
|
26913
27197
|
elementGroup.command("where").argument("<query>", "An element’s id, a class it wears, or words it shows: hero-cta, navLink, \"Get started\"").description("Where the project’s code writes an element: the file, the line and the call itself").addOption(new Option("--by <reading>", "Read the query one way only, not the first of id, class and words that matches").choices(WHERE_READINGS)).option("--json", "One object, for a tool or an agent").action((query, options) => where(query, options));
|
|
26914
27198
|
elementGroup.command("edit").argument("<id>", "The element, by its id: hero-cta").description("Change an element’s attributes in the call that writes it, then check the space still authors").option("--set <key=value...>", "An attribute to write: content=\"Start free\", required=true").option("--remove <key...>", "An attribute to take out").option("--every", "The call writes other elements too, and the change is for every one").option("--json", "One object, for a tool or an agent").action((id, options) => edit(id, options));
|