@zvndev/darkmown 2.6.1-next.11.g82bb8d7 → 2.7.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/AGENTS.md +392 -77
- package/CHANGELOG.md +47 -0
- package/README.md +316 -49
- package/docs/cli.md +51 -17
- package/docs/spec-alignment.md +25 -4
- package/grammar/wd-directives.gbnf +13 -5
- package/package.json +7 -3
- package/site/_/base.skin +1 -1
- package/site/_/footer.wd +1 -1
- package/site/pages/api-errors/index.wd +81 -0
- package/site/pages/app.wd +1 -1
- package/site/pages/attr-binding/index.wd +84 -0
- package/site/pages/blog/darkmown-2-7-0.md +176 -0
- package/site/pages/blog/darkmown-2-7-0.skin +127 -0
- package/site/pages/blog/no-unsafe-eval.md +66 -0
- package/site/pages/blog/no-unsafe-eval.skin +127 -0
- package/site/pages/blog/zero-js-by-default.md +1 -1
- package/site/pages/branch-state/index.wd +68 -0
- package/site/pages/docs/index.wd +320 -19
- package/site/pages/index.wd +3 -1
- package/site/pages/playground/index.js +63 -56
- package/site/pages/pulse/index.wd +1 -1
- package/site/pages/settings/index.wd +97 -0
- package/site/pages/showcase/index.wd +37 -1
- package/site/pages/upload/index.wd +63 -0
- package/site/pages/url-state/index.wd +70 -0
- package/src/builder.js +222 -34
- package/src/catalog.js +217 -21
- package/src/cli.js +6 -3
- package/src/compiler/actions.js +80 -12
- package/src/compiler/body.js +104 -28
- package/src/compiler/collections.js +29 -13
- package/src/compiler/context.js +9 -2
- package/src/compiler/expr-ast.js +14 -0
- package/src/compiler/fetch.js +8 -4
- package/src/compiler/forms.js +133 -14
- package/src/compiler/frontmatter.js +28 -7
- package/src/compiler/includes.js +46 -6
- package/src/compiler/interpolation.js +56 -1
- package/src/compiler/loops.js +489 -35
- package/src/compiler/markdown.js +457 -44
- package/src/compiler/page.js +8 -3
- package/src/compiler/predicates.js +84 -14
- package/src/compiler/state.js +167 -18
- package/src/compiler/structure.js +159 -29
- package/src/errors.js +111 -3
- package/src/feeds.js +140 -7
- package/src/grammar.js +53 -10
- package/src/runtime.js +509 -130
- package/src/runtime.min.js +2 -0
- package/src/runtime.min.js.map +1 -0
- package/src/statics.js +46 -2
- package/src/templates/blog/README.md +2 -0
- package/src/templates/dashboard/README.md +2 -0
- package/src/templates/landing/README.md +2 -0
- package/src/templates/starter/README.md +1 -1
- package/src/templates/store/README.md +2 -0
- package/src/tools/validate.js +14 -4
- package/types/builder.d.ts +6 -2
- package/types/catalog.d.ts +12 -0
- package/types/compiler/actions.d.ts +1 -1
- package/types/compiler/body.d.ts +0 -3
- package/types/compiler/forms.d.ts +12 -1
- package/types/compiler/frontmatter.d.ts +8 -1
- package/types/compiler/includes.d.ts +16 -2
- package/types/compiler/interpolation.d.ts +23 -0
- package/types/compiler/loops.d.ts +47 -0
- package/types/compiler/markdown.d.ts +63 -1
- package/types/compiler/predicates.d.ts +7 -1
- package/types/compiler/state.d.ts +8 -2
- package/types/compiler/structure.d.ts +32 -1
- package/types/feeds.d.ts +68 -2
- package/types/statics.d.ts +25 -0
package/src/grammar.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// while staying small and maintainable.
|
|
19
19
|
// ---------------------------------------------------------------------------
|
|
20
20
|
|
|
21
|
-
import { directiveCatalog } from "./catalog.js";
|
|
21
|
+
import { CATALOG_ACTION_GRAMMAR, directiveCatalog } from "./catalog.js";
|
|
22
22
|
|
|
23
23
|
/** Slugify a directive token into a GBNF rule name (`@loop` → `dir-loop`). */
|
|
24
24
|
/** @type {Record<string, string>} */
|
|
@@ -57,6 +57,19 @@ const STRUCTURED = new Set(["@loop", ":if", ":button", ":::"]);
|
|
|
57
57
|
/** A GBNF alternation literal list, e.g. `"a" | "b"`. */
|
|
58
58
|
const alt = (/** @type {string[]} */ items) => items.map((s) => `"${s}"`).join(" | ");
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* One alternative of the `action-op` rule, from a catalog action's shape.
|
|
62
|
+
* `name++` (suffix, no space) vs `name toggle [v]` / `name append v` (infix).
|
|
63
|
+
* @param {{ token: string, place: "suffix" | "infix", operand: "none" | "required" | "optional" }} op
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
function actionAlternative({ token, place, operand }) {
|
|
67
|
+
if (place === "suffix") return `"${token}"`;
|
|
68
|
+
const tail =
|
|
69
|
+
operand === "required" ? " sp value" : operand === "optional" ? " ( sp value )?" : "";
|
|
70
|
+
return `sp "${token}"${tail}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
/**
|
|
61
74
|
* Generate the GBNF grammar for `.wd` directive lines from the directive catalog.
|
|
62
75
|
* @returns {string}
|
|
@@ -72,14 +85,25 @@ export function generateGrammar() {
|
|
|
72
85
|
"# Constrains a directive line (or a { path | pipe } interpolation). Do not edit by hand."
|
|
73
86
|
);
|
|
74
87
|
lines.push("");
|
|
75
|
-
|
|
88
|
+
// A `.wd` line is an opener, a BLOCK CLOSER (or mid-block marker), or an
|
|
89
|
+
// interpolation. The closers were missing, so constrained decoding could open
|
|
90
|
+
// an `@loop` it was then unable to close.
|
|
91
|
+
lines.push("root ::= directive | closer | binding");
|
|
76
92
|
lines.push(`directive ::= ${dirRules.join(" | ")}`);
|
|
93
|
+
lines.push(
|
|
94
|
+
'closer ::= "@endloop" | "@empty" | ":endif" | ":endform" | ":endcarousel" | ":::"' +
|
|
95
|
+
' | ":else if" sp condition ( sp joiner sp condition )* | ":else if" sp path | ":else"'
|
|
96
|
+
);
|
|
77
97
|
lines.push("");
|
|
78
98
|
|
|
79
99
|
// Structured directives.
|
|
80
100
|
lines.push('dir-loop ::= "@loop" sp operand sp "into" sp ident loop-clauses');
|
|
81
101
|
lines.push('dir-if ::= ":if" sp ( condition (sp joiner sp condition)* | path )');
|
|
82
|
-
|
|
102
|
+
// The label, then the accessibility attributes the compiler whitelists, then
|
|
103
|
+
// the arrow. Attributes are OPTIONAL and repeatable; the whitelist is spelled
|
|
104
|
+
// out here (rather than left to `restofline`) so a constrained model can emit
|
|
105
|
+
// `role`/`aria-…`/`title` and cannot invent `onclick=` or `class=`.
|
|
106
|
+
lines.push('dir-button ::= ":button" sp dqstring ( sp a11y-attr )* sp "->" sp action-chain');
|
|
83
107
|
lines.push('dir-container ::= ":::" restofline');
|
|
84
108
|
lines.push("");
|
|
85
109
|
|
|
@@ -91,23 +115,37 @@ export function generateGrammar() {
|
|
|
91
115
|
lines.push("");
|
|
92
116
|
|
|
93
117
|
// @loop clause vocabulary (mirrors the fixed clause order in the catalog).
|
|
118
|
+
// `sort by`, `offset` and `limit` each accept a REACTIVE form as well as a
|
|
119
|
+
// literal — `sort by { sortKey } { sortDir }` for clickable-header sort, and
|
|
120
|
+
// `limit pageSize` for a state-driven page size. The grammar used to allow
|
|
121
|
+
// only the literal form, which made those shipped features unreachable under
|
|
122
|
+
// constrained decoding.
|
|
94
123
|
lines.push("loop-clauses ::= ( sp loop-clause )*");
|
|
95
124
|
lines.push(
|
|
96
|
-
'loop-clause ::= where-clause | "sort by" sp
|
|
125
|
+
'loop-clause ::= where-clause | "sort by" sp sort-key ( sp sort-dir )? | "reverse"' +
|
|
126
|
+
' | "offset" sp count | "limit" sp count | "paginate" sp number | "sortable"'
|
|
97
127
|
);
|
|
128
|
+
lines.push("sort-key ::= path | statebinding");
|
|
129
|
+
lines.push('sort-dir ::= "asc" | "desc" | statebinding');
|
|
130
|
+
lines.push("count ::= number | ident");
|
|
131
|
+
lines.push('statebinding ::= "{" wsopt ident wsopt "}"');
|
|
98
132
|
lines.push('where-clause ::= "where" sp condition ( sp joiner sp condition )*');
|
|
99
|
-
|
|
133
|
+
// A leading `not` negates one condition (it is a PREFIX, not a joiner — the
|
|
134
|
+
// catalog lists it under predicateJoiners, so it is derived from there rather
|
|
135
|
+
// than filtered away).
|
|
136
|
+
const negate = cat.predicateJoiners.includes("not") ? '( "not" sp )? ' : "";
|
|
137
|
+
lines.push(`condition ::= ${negate}operand sp op sp operand`);
|
|
100
138
|
lines.push(`op ::= ${alt(cat.predicateOps.map((o) => o.name))}`);
|
|
101
139
|
lines.push(`joiner ::= ${alt(cat.predicateJoiners.filter((j) => j !== "not"))}`);
|
|
102
140
|
lines.push("");
|
|
103
141
|
|
|
104
|
-
// Button-action vocabulary
|
|
105
|
-
//
|
|
142
|
+
// Button-action vocabulary, DERIVED from the catalog's action ops (token +
|
|
143
|
+
// shape) so the grammar cannot list a different set from `llms.txt` and the
|
|
144
|
+
// `WD311` hint. It used to be one hand-written string; that is how `refetch`
|
|
145
|
+
// ended up in the grammar and nowhere else.
|
|
106
146
|
lines.push('action-chain ::= action ( wsopt ";" wsopt action )*');
|
|
107
147
|
lines.push("action ::= ident action-op");
|
|
108
|
-
lines.push(
|
|
109
|
-
'action-op ::= "++" | "--" | sp "+=" sp value | sp "-=" sp value | sp "=" sp value | sp "toggle" ( sp value )? | sp "append" sp value | sp "prepend" sp value | sp "remove" sp value | sp "clear" | sp "merge" sp ident | sp "delete" sp value | sp "reset" | sp "refetch"'
|
|
110
|
-
);
|
|
148
|
+
lines.push(`action-op ::= ${CATALOG_ACTION_GRAMMAR.map(actionAlternative).join(" | ")}`);
|
|
111
149
|
lines.push("");
|
|
112
150
|
|
|
113
151
|
// Interpolation binding with format pipes.
|
|
@@ -116,6 +154,11 @@ export function generateGrammar() {
|
|
|
116
154
|
lines.push(`pipe ::= ${alt(cat.formatPipes.map((p) => p.name))}`);
|
|
117
155
|
lines.push("");
|
|
118
156
|
|
|
157
|
+
// Accessibility attributes (`::: ` headers and `:button` share the whitelist).
|
|
158
|
+
lines.push('a11y-attr ::= a11y-name "=" dqstring');
|
|
159
|
+
lines.push('a11y-name ::= "role" | "title" | "aria-" [a-z] [a-z0-9-]*');
|
|
160
|
+
lines.push("");
|
|
161
|
+
|
|
119
162
|
// Terminals.
|
|
120
163
|
lines.push("operand ::= path | dqstring | number");
|
|
121
164
|
lines.push("value ::= dqstring | number | path");
|