paperlint 14.0.0 → 14.1.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 +68 -1
- 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 +6 -2
- package/skills/build-label-page/SKILL.md.spec.ts +5 -1
- package/skills/build-label-page/references/closed-choice-generator.md +129 -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-blocks.test.ts +335 -0
- package/skills/build-label-page/scripts/label-page-browser.test.ts +328 -2
- package/skills/build-label-page/scripts/label-page-core.js +154 -12
- package/skills/build-label-page/scripts/label-page-core.test.ts +54 -2
- package/skills/build-label-page/scripts/label-page-core.ts +253 -31
- 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 +8 -2
- package/skills/build-label-page/scripts/label-page.ts +8 -2
- package/skills/build-label-page/scripts/page-template.html +237 -30
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
{
|
|
2
|
+
"study": "demo-witness",
|
|
3
|
+
"preset": "linter-config-witness",
|
|
4
|
+
"kind": "labels",
|
|
5
|
+
"title": "Witness cards",
|
|
6
|
+
"intro": "Each card shows one requirement, one piece of real code and one question about that code. The linter settings behind the card are under «Settings behind this card»; you do not need them to answer.",
|
|
7
|
+
"seed": 11,
|
|
8
|
+
"groups": {
|
|
9
|
+
"all": "Witness cards"
|
|
10
|
+
},
|
|
11
|
+
"questions": [
|
|
12
|
+
{
|
|
13
|
+
"id": "witness",
|
|
14
|
+
"text": "Does the requirement forbid this line, or apply to this file?",
|
|
15
|
+
"short": "answer",
|
|
16
|
+
"options": [
|
|
17
|
+
{
|
|
18
|
+
"value": "covers",
|
|
19
|
+
"label": "Yes",
|
|
20
|
+
"icon": "✅",
|
|
21
|
+
"example": "«Use `===`» and the line `a == b`: yes, the requirement forbids it"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"value": "excludes",
|
|
25
|
+
"label": "No",
|
|
26
|
+
"icon": "🚫",
|
|
27
|
+
"example": "«No `console` in shipped code» and a test file: no, tests are not shipped"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"value": "open",
|
|
31
|
+
"label": "Unclear: the wording could be read either way",
|
|
32
|
+
"icon": "↔️",
|
|
33
|
+
"example": "«Use `===`» and the line `x == null`: unclear, a common exception the wording neither allows nor forbids"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"value": "unsure",
|
|
37
|
+
"label": "I can't judge (tell me why)",
|
|
38
|
+
"icon": "❔",
|
|
39
|
+
"example": "the code is in a language you cannot read: say so in the box",
|
|
40
|
+
"ask_why": "Why can't you judge it?"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"practice": [
|
|
46
|
+
{
|
|
47
|
+
"body": "Functions take at most 3 parameters",
|
|
48
|
+
"situation": {
|
|
49
|
+
"summary": "One setting allows up to 5 parameters, the other up to 3.",
|
|
50
|
+
"question": "Does «Functions take at most 3 parameters» forbid {line}?"
|
|
51
|
+
},
|
|
52
|
+
"example": {
|
|
53
|
+
"file": "src/orders/create.ts",
|
|
54
|
+
"lines": [
|
|
55
|
+
{
|
|
56
|
+
"n": 11,
|
|
57
|
+
"text": "",
|
|
58
|
+
"mark": false
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"n": 12,
|
|
62
|
+
"text": "export function createOrder(id, items, user, coupon) {",
|
|
63
|
+
"mark": true
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"n": 13,
|
|
67
|
+
"text": " return { id, items, user, coupon };",
|
|
68
|
+
"mark": false
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"message": "Function 'createOrder' has too many parameters (4). Maximum allowed is 3.",
|
|
72
|
+
"caught_by": "a"
|
|
73
|
+
},
|
|
74
|
+
"details": {
|
|
75
|
+
"a": [
|
|
76
|
+
{
|
|
77
|
+
"key": "option:max",
|
|
78
|
+
"raw": "3",
|
|
79
|
+
"plain": "at most 3 parameters",
|
|
80
|
+
"matches_example": true
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"b": [
|
|
84
|
+
{
|
|
85
|
+
"key": "option:max",
|
|
86
|
+
"raw": "5",
|
|
87
|
+
"plain": "at most 5 parameters",
|
|
88
|
+
"matches_example": false
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"rules": [
|
|
93
|
+
{
|
|
94
|
+
"linter": "eslint",
|
|
95
|
+
"id": "max-params",
|
|
96
|
+
"explanation": {
|
|
97
|
+
"source": "eslint docs",
|
|
98
|
+
"what": "Enforce a maximum number of parameters in function definitions.",
|
|
99
|
+
"example_bad": "function foo (bar, baz, qux, qxx) {}",
|
|
100
|
+
"example_good": "function foo (bar, baz, qux) {}",
|
|
101
|
+
"url": "https://eslint.org/docs/latest/rules/max-params"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"expected": {
|
|
106
|
+
"witness": "covers"
|
|
107
|
+
},
|
|
108
|
+
"why": "The words fix the number 3, and the marked line has 4 parameters."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"body": "Use `===`, never `==`",
|
|
112
|
+
"situation": {
|
|
113
|
+
"summary": "One setting allows `== null`, the other forbids every `==`.",
|
|
114
|
+
"question": "Does «Use `===`, never `==`» forbid {line}?"
|
|
115
|
+
},
|
|
116
|
+
"example": {
|
|
117
|
+
"file": "src/cache/lookup.ts",
|
|
118
|
+
"lines": [
|
|
119
|
+
{
|
|
120
|
+
"n": 41,
|
|
121
|
+
"text": " const entry = store.get(key);",
|
|
122
|
+
"mark": false
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"n": 42,
|
|
126
|
+
"text": " if (entry == null) return undefined;",
|
|
127
|
+
"mark": true
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
"message": "Expected '===' and instead saw '=='.",
|
|
131
|
+
"caught_by": "b"
|
|
132
|
+
},
|
|
133
|
+
"details": {
|
|
134
|
+
"a": [
|
|
135
|
+
{
|
|
136
|
+
"key": "option:null",
|
|
137
|
+
"raw": "\"ignore\"",
|
|
138
|
+
"plain": "allows `== null` and `!= null`",
|
|
139
|
+
"matches_example": false
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
"b": [
|
|
143
|
+
{
|
|
144
|
+
"key": "rule",
|
|
145
|
+
"raw": "\"always\"",
|
|
146
|
+
"plain": "every `==` and `!=`",
|
|
147
|
+
"matches_example": true
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
"rules": [
|
|
152
|
+
{
|
|
153
|
+
"linter": "eslint",
|
|
154
|
+
"id": "eqeqeq",
|
|
155
|
+
"explanation": {
|
|
156
|
+
"source": "none"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"expected": {
|
|
161
|
+
"witness": "covers"
|
|
162
|
+
},
|
|
163
|
+
"why": "«never `==`» names no exception, so `== null` is forbidden too."
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"body": "No `console` calls in shipped code",
|
|
167
|
+
"situation": {
|
|
168
|
+
"summary": "One setting checks test files, the other leaves them out.",
|
|
169
|
+
"question": "Should this requirement apply to test files?"
|
|
170
|
+
},
|
|
171
|
+
"example": {
|
|
172
|
+
"file": "test/report.test.ts",
|
|
173
|
+
"lines": [
|
|
174
|
+
{
|
|
175
|
+
"n": 7,
|
|
176
|
+
"text": " console.log(result);",
|
|
177
|
+
"mark": true
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"message": "Unexpected console statement.",
|
|
181
|
+
"caught_by": "a"
|
|
182
|
+
},
|
|
183
|
+
"details": {
|
|
184
|
+
"a": [
|
|
185
|
+
{
|
|
186
|
+
"key": "files",
|
|
187
|
+
"raw": "**/*.ts",
|
|
188
|
+
"plain": "every TypeScript file",
|
|
189
|
+
"matches_example": true
|
|
190
|
+
}
|
|
191
|
+
],
|
|
192
|
+
"b": [
|
|
193
|
+
{
|
|
194
|
+
"key": "files",
|
|
195
|
+
"raw": "**/*.ts",
|
|
196
|
+
"plain": "every TypeScript file",
|
|
197
|
+
"matches_example": true
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"key": "ignores",
|
|
201
|
+
"raw": "test/**",
|
|
202
|
+
"plain": "except the files under test/",
|
|
203
|
+
"matches_example": true
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
"rules": [
|
|
208
|
+
{
|
|
209
|
+
"linter": "eslint",
|
|
210
|
+
"id": "no-console",
|
|
211
|
+
"explanation": {
|
|
212
|
+
"source": "none"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
"expected": {
|
|
217
|
+
"witness": "excludes"
|
|
218
|
+
},
|
|
219
|
+
"why": "Test files are not shipped, so the words do not reach them."
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
"items": [
|
|
223
|
+
{
|
|
224
|
+
"source_id": "req-logging:tests",
|
|
225
|
+
"group": "all",
|
|
226
|
+
"body": "Never log with `print`; use the `logging` module",
|
|
227
|
+
"situation": {
|
|
228
|
+
"summary": "One setting checks the scripts/ folder, the other leaves it out.",
|
|
229
|
+
"question": "Should this requirement apply to the scripts/ folder?"
|
|
230
|
+
},
|
|
231
|
+
"example": {
|
|
232
|
+
"file": "scripts/seed_db.py",
|
|
233
|
+
"url": "https://example.com/demo-shop/blob/0000000/scripts/seed_db.py#L18",
|
|
234
|
+
"lines": [
|
|
235
|
+
{
|
|
236
|
+
"n": 16,
|
|
237
|
+
"text": "for row in rows:",
|
|
238
|
+
"mark": false
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"n": 17,
|
|
242
|
+
"text": " db.insert(row)",
|
|
243
|
+
"mark": false
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"n": 18,
|
|
247
|
+
"text": "print(f\"seeded {len(rows)} rows\")",
|
|
248
|
+
"mark": true
|
|
249
|
+
}
|
|
250
|
+
],
|
|
251
|
+
"message": "`print` found",
|
|
252
|
+
"caught_by": "b"
|
|
253
|
+
},
|
|
254
|
+
"more_like_this": {
|
|
255
|
+
"count": 3,
|
|
256
|
+
"files": [
|
|
257
|
+
"scripts/migrate.py",
|
|
258
|
+
"scripts/backfill_prices.py"
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
"details": {
|
|
262
|
+
"a": [
|
|
263
|
+
{
|
|
264
|
+
"key": "rule",
|
|
265
|
+
"raw": "T201",
|
|
266
|
+
"plain": "flags every call to `print`",
|
|
267
|
+
"matches_example": null
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"key": "ignores",
|
|
271
|
+
"raw": "scripts/**",
|
|
272
|
+
"plain": "except the files under scripts/",
|
|
273
|
+
"matches_example": true
|
|
274
|
+
}
|
|
275
|
+
],
|
|
276
|
+
"b": [
|
|
277
|
+
{
|
|
278
|
+
"key": "rule",
|
|
279
|
+
"raw": "T201",
|
|
280
|
+
"plain": "flags every call to `print`",
|
|
281
|
+
"matches_example": null
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
},
|
|
285
|
+
"rules": [
|
|
286
|
+
{
|
|
287
|
+
"linter": "ruff",
|
|
288
|
+
"id": "T201",
|
|
289
|
+
"explanation": {
|
|
290
|
+
"source": "ruff rule",
|
|
291
|
+
"what": "Checks for `print` statements.",
|
|
292
|
+
"why": "`print` statements used for debugging should be omitted from production code. They can lead to the accidental inclusion of sensitive information in logs, and are not configurable by clients, unlike `logging` statements.",
|
|
293
|
+
"example_bad": "def sum_less_than_four(a, b):\n print(f\"Calling with {a} and {b}\")\n return a + b < 4",
|
|
294
|
+
"example_good": "import logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n\ndef sum_less_than_four(a, b):\n logger.debug(\"Calling with %s and %s\", a, b)\n return a + b < 4",
|
|
295
|
+
"url": "https://docs.astral.sh/ruff/rules/print/"
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
],
|
|
299
|
+
"repeat": {
|
|
300
|
+
"group": "req-logging",
|
|
301
|
+
"index": 1,
|
|
302
|
+
"of": 2
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
"source_id": "req-logging:debug-flag",
|
|
307
|
+
"group": "all",
|
|
308
|
+
"body": "Never log with `print`; use the `logging` module",
|
|
309
|
+
"situation": {
|
|
310
|
+
"summary": "One setting allows `print` behind a debug flag, the other forbids every `print`.",
|
|
311
|
+
"question": "Does «Never log with `print`; use the `logging` module» forbid {line}?"
|
|
312
|
+
},
|
|
313
|
+
"example": {
|
|
314
|
+
"file": "shop/cart.py",
|
|
315
|
+
"lines": [
|
|
316
|
+
{
|
|
317
|
+
"n": 30,
|
|
318
|
+
"text": " if DEBUG:",
|
|
319
|
+
"mark": false
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"n": 31,
|
|
323
|
+
"text": " print(\"cart\", cart.id, cart.total)",
|
|
324
|
+
"mark": true
|
|
325
|
+
}
|
|
326
|
+
],
|
|
327
|
+
"message": "`print` found",
|
|
328
|
+
"caught_by": "a"
|
|
329
|
+
},
|
|
330
|
+
"details": {
|
|
331
|
+
"a": [
|
|
332
|
+
{
|
|
333
|
+
"key": "rule",
|
|
334
|
+
"raw": "T201",
|
|
335
|
+
"plain": "flags every call to `print`",
|
|
336
|
+
"matches_example": null
|
|
337
|
+
}
|
|
338
|
+
],
|
|
339
|
+
"b": [
|
|
340
|
+
{
|
|
341
|
+
"key": "rule",
|
|
342
|
+
"raw": "T201",
|
|
343
|
+
"plain": "flags every call to `print`",
|
|
344
|
+
"matches_example": null
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"key": "option:allow-debug",
|
|
348
|
+
"raw": "true",
|
|
349
|
+
"plain": "allows `print` inside `if DEBUG:`",
|
|
350
|
+
"matches_example": true
|
|
351
|
+
}
|
|
352
|
+
]
|
|
353
|
+
},
|
|
354
|
+
"rules": [
|
|
355
|
+
{
|
|
356
|
+
"linter": "ruff",
|
|
357
|
+
"id": "T201",
|
|
358
|
+
"explanation": {
|
|
359
|
+
"source": "none"
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
],
|
|
363
|
+
"repeat": {
|
|
364
|
+
"group": "req-logging",
|
|
365
|
+
"index": 2,
|
|
366
|
+
"of": 2
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
"source_id": "req-imports:barrel",
|
|
371
|
+
"group": "all",
|
|
372
|
+
"body": "Import from a package's public entry point, never from its internals",
|
|
373
|
+
"situation": {
|
|
374
|
+
"summary": "One setting forbids any path below a package's folder, the other only paths into src/internal/.",
|
|
375
|
+
"question": "Does «Import from a package's public entry point, never from its internals» forbid {line}?"
|
|
376
|
+
},
|
|
377
|
+
"example": {
|
|
378
|
+
"file": "apps/web/src/checkout/total.ts",
|
|
379
|
+
"lines": [
|
|
380
|
+
{
|
|
381
|
+
"n": 1,
|
|
382
|
+
"text": "import { money } from \"@demo/format\";",
|
|
383
|
+
"mark": false
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
"n": 2,
|
|
387
|
+
"text": "import { round } from \"@demo/format/src/round\";",
|
|
388
|
+
"mark": true
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"n": 3,
|
|
392
|
+
"text": "",
|
|
393
|
+
"mark": false
|
|
394
|
+
}
|
|
395
|
+
],
|
|
396
|
+
"message": "'@demo/format/src/round' import is restricted from being used by a pattern. Import from the package's entry point.",
|
|
397
|
+
"caught_by": "a"
|
|
398
|
+
},
|
|
399
|
+
"more_like_this": {
|
|
400
|
+
"count": 1,
|
|
401
|
+
"files": [
|
|
402
|
+
"apps/admin/src/report/sum.ts"
|
|
403
|
+
]
|
|
404
|
+
},
|
|
405
|
+
"details": {
|
|
406
|
+
"a": [
|
|
407
|
+
{
|
|
408
|
+
"key": "option:patterns",
|
|
409
|
+
"raw": "[{\"group\": [\"@demo/*/*\"], \"message\": \"Import from the package's entry point.\"}]",
|
|
410
|
+
"plain": "forbids any import path one or more folders below a `@demo/` package",
|
|
411
|
+
"matches_example": true
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
"key": "files",
|
|
415
|
+
"raw": "apps/**/*.ts",
|
|
416
|
+
"plain": "the TypeScript files under apps/",
|
|
417
|
+
"matches_example": true
|
|
418
|
+
}
|
|
419
|
+
],
|
|
420
|
+
"b": [
|
|
421
|
+
{
|
|
422
|
+
"key": "option:patterns",
|
|
423
|
+
"raw": "[{\"group\": [\"@demo/*/src/internal/**\"]}]",
|
|
424
|
+
"plain": "forbids only import paths into a package's src/internal/ folder",
|
|
425
|
+
"matches_example": false
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
"key": "files",
|
|
429
|
+
"raw": "apps/**/*.ts",
|
|
430
|
+
"plain": "the TypeScript files under apps/",
|
|
431
|
+
"matches_example": true
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
},
|
|
435
|
+
"rules": [
|
|
436
|
+
{
|
|
437
|
+
"linter": "eslint",
|
|
438
|
+
"id": "no-restricted-imports",
|
|
439
|
+
"explanation": {
|
|
440
|
+
"source": "config message",
|
|
441
|
+
"what": "Import from the package's entry point.",
|
|
442
|
+
"url": "https://eslint.org/docs/latest/rules/no-restricted-imports"
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
]
|
|
446
|
+
}
|
|
447
|
+
]
|
|
448
|
+
}
|