paperlint 14.1.0 → 14.3.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/CLAUDE.md +6 -1
- package/dist/skills/build-label-page/scripts/label-page-core.d.ts +70 -32
- package/docs/testing.md +12 -0
- package/eslint.config.mjs +17 -0
- package/package.json +2 -1
- package/skills/build-label-page/SKILL.md +19 -3
- package/skills/build-label-page/SKILL.md.spec.ts +18 -2
- package/skills/build-label-page/references/closed-choice-generator.md +86 -8
- package/skills/build-label-page/scripts/fixtures/v7-cards-items.json +138 -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 +277 -1
- package/skills/build-label-page/scripts/label-page-browser.test.ts +761 -10
- package/skills/build-label-page/scripts/label-page-core.js +311 -67
- package/skills/build-label-page/scripts/label-page-core.test.ts +49 -10
- package/skills/build-label-page/scripts/label-page-core.ts +511 -87
- package/skills/build-label-page/scripts/label-page-presets.test.ts +1 -1
- package/skills/build-label-page/scripts/label-page.js +5 -0
- package/skills/build-label-page/scripts/label-page.ts +5 -0
- package/skills/build-label-page/scripts/page-template.html +488 -62
|
@@ -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 {
|
|
@@ -85,11 +86,32 @@ const CARD = {
|
|
|
85
86
|
situation: { summary: "S", question: "Q?" },
|
|
86
87
|
excerpt: {
|
|
87
88
|
file: "src/a.ts",
|
|
89
|
+
lang: "TypeScript",
|
|
88
90
|
url: "https://example.com/a.ts",
|
|
89
|
-
|
|
91
|
+
gutter_header: "depth",
|
|
92
|
+
lines: [{ n: 1, text: "", mark: true, gutter: "1", context: false }],
|
|
93
|
+
annotation: "a `Term` here",
|
|
90
94
|
caption: "C",
|
|
91
95
|
mark_label: "M",
|
|
92
96
|
},
|
|
97
|
+
quote: { text: "Q with Term", source: "`R.md`, line 1" },
|
|
98
|
+
terms: [
|
|
99
|
+
{
|
|
100
|
+
term: "Term",
|
|
101
|
+
note: [
|
|
102
|
+
{
|
|
103
|
+
title: "T",
|
|
104
|
+
says: "S",
|
|
105
|
+
quote: "Q",
|
|
106
|
+
code: "x = 1",
|
|
107
|
+
example: "E",
|
|
108
|
+
url: "https://example.com/t",
|
|
109
|
+
url_label: "U",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
answer_notes: { label: { yes: "like A" } },
|
|
93
115
|
references: {
|
|
94
116
|
summary: "S",
|
|
95
117
|
entries: [
|
|
@@ -133,6 +155,8 @@ const FULL: DocObject = (() => {
|
|
|
133
155
|
QUESTIONS[1],
|
|
134
156
|
],
|
|
135
157
|
labeller_knows: ["python", "go", "rust"],
|
|
158
|
+
answerers: ["author", "second"],
|
|
159
|
+
min_seconds: 30,
|
|
136
160
|
familiarity: true,
|
|
137
161
|
familiarity_question: "How well do you know {group}?",
|
|
138
162
|
labels: {
|
|
@@ -142,9 +166,14 @@ const FULL: DocObject = (() => {
|
|
|
142
166
|
line_part: "{line} {part}/{of}",
|
|
143
167
|
repeat: "R",
|
|
144
168
|
practice_example: "P",
|
|
169
|
+
hidden: "H {from}-{to}",
|
|
170
|
+
hidden_one: "H {from}",
|
|
171
|
+
wait: "W {s}",
|
|
145
172
|
},
|
|
146
173
|
practice: PRACTICE.map((p, n) => (n === 0 ? { ...p, ...CARD } : p)),
|
|
147
|
-
items: ITEMS.map((i, n) =>
|
|
174
|
+
items: ITEMS.map((i, n) =>
|
|
175
|
+
n === 0 ? { ...i, ...CARD, answerers: ["author"] } : i,
|
|
176
|
+
),
|
|
148
177
|
}),
|
|
149
178
|
);
|
|
150
179
|
assert.ok(isObject(doc));
|
|
@@ -275,6 +304,21 @@ function badCases({ node, member }: Leaf): readonly BadCase[] {
|
|
|
275
304
|
return [...wrongType, ...extra];
|
|
276
305
|
}
|
|
277
306
|
|
|
307
|
+
/** `doc` with its first item's own answerers taken out. */
|
|
308
|
+
function withoutItemAnswerers(doc: DocObject): DocObject {
|
|
309
|
+
const out = put(doc, ["items", 0, "answerers"], undefined);
|
|
310
|
+
assert.ok(isObject(out));
|
|
311
|
+
return out;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* A field whose absence takes another with it: familiarity_question asks nothing unless familiarity
|
|
315
|
+
* is on, and an item's answerers name the page's.
|
|
316
|
+
*/
|
|
317
|
+
const RELAX: Partial<Record<string, (doc: DocObject) => DocObject>> = {
|
|
318
|
+
familiarity: (doc) => R.omit(doc, ["familiarity_question"]),
|
|
319
|
+
answerers: withoutItemAnswerers,
|
|
320
|
+
};
|
|
321
|
+
|
|
278
322
|
/** Spoil `leaf` each way in `badCases`; every one must stop the build and name the path. */
|
|
279
323
|
function expectRefused(leaf: Leaf): void {
|
|
280
324
|
badCases(leaf).forEach(([label, value], i) => {
|
|
@@ -290,16 +334,9 @@ function expectRefused(leaf: Leaf): void {
|
|
|
290
334
|
});
|
|
291
335
|
if (!leaf.node.required && !leaf.member) {
|
|
292
336
|
const absent = spoil(leaf.path, undefined);
|
|
293
|
-
// familiarity_question asks nothing unless familiarity is on: the two leave together.
|
|
294
337
|
const r = tryBuild(
|
|
295
338
|
`schema-${leaf.path.replace(/\W/g, "_")}-absent`,
|
|
296
|
-
leaf.path
|
|
297
|
-
? Object.fromEntries(
|
|
298
|
-
Object.entries(absent).filter(
|
|
299
|
-
([k]) => k !== "familiarity_question",
|
|
300
|
-
),
|
|
301
|
-
)
|
|
302
|
-
: absent,
|
|
339
|
+
(RELAX[leaf.path] ?? R.identity())(absent),
|
|
303
340
|
);
|
|
304
341
|
assert.equal(r.status, 0, `${leaf.path} is optional: ${r.out}`);
|
|
305
342
|
}
|
|
@@ -313,6 +350,8 @@ const DOCUMENTED_FIELDS = [
|
|
|
313
350
|
"seed",
|
|
314
351
|
"groups",
|
|
315
352
|
"labeller_knows",
|
|
353
|
+
"answerers",
|
|
354
|
+
"min_seconds",
|
|
316
355
|
"familiarity",
|
|
317
356
|
"familiarity_question",
|
|
318
357
|
"questions",
|