paperlint 14.0.0 → 14.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/skills/build-label-page/scripts/label-page-core.d.ts +105 -25
- package/dist/skills/build-label-page/scripts/label-page-presets.d.ts +10 -0
- package/package.json +1 -1
- package/skills/build-label-page/SKILL.md +20 -2
- package/skills/build-label-page/SKILL.md.spec.ts +19 -1
- package/skills/build-label-page/references/closed-choice-generator.md +171 -5
- package/skills/build-label-page/scripts/fixtures/blocks-items.json +214 -0
- package/skills/build-label-page/scripts/fixtures/linter-config-witness-items.json +448 -0
- package/skills/build-label-page/scripts/label-page-answerers.test.ts +381 -0
- package/skills/build-label-page/scripts/label-page-blocks.test.ts +335 -0
- package/skills/build-label-page/scripts/label-page-browser.test.ts +637 -2
- package/skills/build-label-page/scripts/label-page-core.js +360 -70
- package/skills/build-label-page/scripts/label-page-core.test.ts +78 -11
- package/skills/build-label-page/scripts/label-page-core.ts +585 -107
- package/skills/build-label-page/scripts/label-page-html.test.ts +22 -0
- package/skills/build-label-page/scripts/label-page-presets.js +222 -0
- package/skills/build-label-page/scripts/label-page-presets.test.ts +175 -0
- package/skills/build-label-page/scripts/label-page-presets.ts +257 -0
- package/skills/build-label-page/scripts/label-page.js +13 -2
- package/skills/build-label-page/scripts/label-page.ts +13 -2
- package/skills/build-label-page/scripts/page-template.html +438 -44
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import assert from "node:assert/strict";
|
|
7
7
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
+
import * as R from "remeda";
|
|
9
10
|
import { test } from "vitest";
|
|
10
11
|
import { z } from "zod";
|
|
11
12
|
import {
|
|
@@ -29,7 +30,15 @@ import {
|
|
|
29
30
|
/** A node of the declared config schema, as `example-generator.py schema` prints it. */
|
|
30
31
|
const Node: z.ZodType<SchemaNode> = z.lazy(() =>
|
|
31
32
|
z.object({
|
|
32
|
-
kind: z.enum([
|
|
33
|
+
kind: z.enum([
|
|
34
|
+
"string",
|
|
35
|
+
"text",
|
|
36
|
+
"integer",
|
|
37
|
+
"boolean",
|
|
38
|
+
"map",
|
|
39
|
+
"array",
|
|
40
|
+
"object",
|
|
41
|
+
]),
|
|
33
42
|
required: z.boolean(),
|
|
34
43
|
pattern: z.string().optional(),
|
|
35
44
|
key_pattern: z.string().optional(),
|
|
@@ -42,7 +51,8 @@ const Node: z.ZodType<SchemaNode> = z.lazy(() =>
|
|
|
42
51
|
);
|
|
43
52
|
|
|
44
53
|
interface SchemaNode {
|
|
45
|
-
readonly kind:
|
|
54
|
+
readonly kind:
|
|
55
|
+
"string" | "text" | "integer" | "boolean" | "map" | "array" | "object";
|
|
46
56
|
readonly required: boolean;
|
|
47
57
|
readonly pattern?: string | undefined;
|
|
48
58
|
readonly key_pattern?: string | undefined;
|
|
@@ -73,6 +83,40 @@ const CARD = {
|
|
|
73
83
|
rules: [{ linter: "eslint", id: "no-console" }],
|
|
74
84
|
unit: { id: "L0", text: "One line.", mark: "One", part: 1, of: 1 },
|
|
75
85
|
readings: { a: "on", b: "off" },
|
|
86
|
+
situation: { summary: "S", question: "Q?" },
|
|
87
|
+
excerpt: {
|
|
88
|
+
file: "src/a.ts",
|
|
89
|
+
url: "https://example.com/a.ts",
|
|
90
|
+
lines: [{ n: 1, text: "", mark: true }],
|
|
91
|
+
caption: "C",
|
|
92
|
+
mark_label: "M",
|
|
93
|
+
},
|
|
94
|
+
references: {
|
|
95
|
+
summary: "S",
|
|
96
|
+
entries: [
|
|
97
|
+
{
|
|
98
|
+
label: "L",
|
|
99
|
+
title: "T",
|
|
100
|
+
fields: [{ label: "F", text: "X", style: "code" }],
|
|
101
|
+
url: "https://example.com/r",
|
|
102
|
+
url_label: "U",
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
more: { summary: "S", items: ["`src/b.ts`"], rest: "R" },
|
|
107
|
+
table: {
|
|
108
|
+
summary: "S",
|
|
109
|
+
note: "N",
|
|
110
|
+
rows: [
|
|
111
|
+
{
|
|
112
|
+
label: "L",
|
|
113
|
+
value: "V",
|
|
114
|
+
plain: "P",
|
|
115
|
+
mark: { tone: "yes", text: "Y" },
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
repeat: { group: "g", index: 1, of: 1 },
|
|
76
120
|
};
|
|
77
121
|
/** A config with EVERY optional field present, so every schema path has a value to spoil. */
|
|
78
122
|
const FULL: DocObject = (() => {
|
|
@@ -90,10 +134,22 @@ const FULL: DocObject = (() => {
|
|
|
90
134
|
QUESTIONS[1],
|
|
91
135
|
],
|
|
92
136
|
labeller_knows: ["python", "go", "rust"],
|
|
137
|
+
answerers: ["author", "second"],
|
|
138
|
+
min_seconds: 30,
|
|
93
139
|
familiarity: true,
|
|
94
140
|
familiarity_question: "How well do you know {group}?",
|
|
141
|
+
labels: {
|
|
142
|
+
place: "C",
|
|
143
|
+
body: "B",
|
|
144
|
+
line: "L",
|
|
145
|
+
line_part: "{line} {part}/{of}",
|
|
146
|
+
repeat: "R",
|
|
147
|
+
practice_example: "P",
|
|
148
|
+
},
|
|
95
149
|
practice: PRACTICE.map((p, n) => (n === 0 ? { ...p, ...CARD } : p)),
|
|
96
|
-
items: ITEMS.map((i, n) =>
|
|
150
|
+
items: ITEMS.map((i, n) =>
|
|
151
|
+
n === 0 ? { ...i, ...CARD, answerers: ["author"] } : i,
|
|
152
|
+
),
|
|
97
153
|
}),
|
|
98
154
|
);
|
|
99
155
|
assert.ok(isObject(doc));
|
|
@@ -187,6 +243,7 @@ function spoil(path: string, bad: Doc | undefined): DocObject {
|
|
|
187
243
|
|
|
188
244
|
const WRONG_TYPES: Readonly<Record<SchemaNode["kind"], readonly Doc[]>> = {
|
|
189
245
|
string: [null, 1, true, ["x"], {}, "", " "],
|
|
246
|
+
text: [null, 1, true, ["x"], {}],
|
|
190
247
|
integer: [null, "7", true, 7.5, []],
|
|
191
248
|
boolean: [null, "false", 0, [], {}],
|
|
192
249
|
map: [null, "x", ["x"], 1],
|
|
@@ -223,6 +280,21 @@ function badCases({ node, member }: Leaf): readonly BadCase[] {
|
|
|
223
280
|
return [...wrongType, ...extra];
|
|
224
281
|
}
|
|
225
282
|
|
|
283
|
+
/** `doc` with its first item's own answerers taken out. */
|
|
284
|
+
function withoutItemAnswerers(doc: DocObject): DocObject {
|
|
285
|
+
const out = put(doc, ["items", 0, "answerers"], undefined);
|
|
286
|
+
assert.ok(isObject(out));
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* A field whose absence takes another with it: familiarity_question asks nothing unless familiarity
|
|
291
|
+
* is on, and an item's answerers name the page's.
|
|
292
|
+
*/
|
|
293
|
+
const RELAX: Partial<Record<string, (doc: DocObject) => DocObject>> = {
|
|
294
|
+
familiarity: (doc) => R.omit(doc, ["familiarity_question"]),
|
|
295
|
+
answerers: withoutItemAnswerers,
|
|
296
|
+
};
|
|
297
|
+
|
|
226
298
|
/** Spoil `leaf` each way in `badCases`; every one must stop the build and name the path. */
|
|
227
299
|
function expectRefused(leaf: Leaf): void {
|
|
228
300
|
badCases(leaf).forEach(([label, value], i) => {
|
|
@@ -238,16 +310,9 @@ function expectRefused(leaf: Leaf): void {
|
|
|
238
310
|
});
|
|
239
311
|
if (!leaf.node.required && !leaf.member) {
|
|
240
312
|
const absent = spoil(leaf.path, undefined);
|
|
241
|
-
// familiarity_question asks nothing unless familiarity is on: the two leave together.
|
|
242
313
|
const r = tryBuild(
|
|
243
314
|
`schema-${leaf.path.replace(/\W/g, "_")}-absent`,
|
|
244
|
-
leaf.path
|
|
245
|
-
? Object.fromEntries(
|
|
246
|
-
Object.entries(absent).filter(
|
|
247
|
-
([k]) => k !== "familiarity_question",
|
|
248
|
-
),
|
|
249
|
-
)
|
|
250
|
-
: absent,
|
|
315
|
+
(RELAX[leaf.path] ?? R.identity())(absent),
|
|
251
316
|
);
|
|
252
317
|
assert.equal(r.status, 0, `${leaf.path} is optional: ${r.out}`);
|
|
253
318
|
}
|
|
@@ -261,6 +326,8 @@ const DOCUMENTED_FIELDS = [
|
|
|
261
326
|
"seed",
|
|
262
327
|
"groups",
|
|
263
328
|
"labeller_knows",
|
|
329
|
+
"answerers",
|
|
330
|
+
"min_seconds",
|
|
264
331
|
"familiarity",
|
|
265
332
|
"familiarity_question",
|
|
266
333
|
"questions",
|