@khanacademy/perseus-linter 0.3.10 → 0.3.12

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.
Files changed (53) hide show
  1. package/dist/es/index.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/package.json +7 -4
  4. package/.eslintrc.js +0 -12
  5. package/CHANGELOG.md +0 -148
  6. package/src/README.md +0 -41
  7. package/src/__tests__/matcher.test.ts +0 -498
  8. package/src/__tests__/rule.test.ts +0 -110
  9. package/src/__tests__/rules.test.ts +0 -548
  10. package/src/__tests__/selector-parser.test.ts +0 -51
  11. package/src/__tests__/tree-transformer.test.ts +0 -444
  12. package/src/index.ts +0 -281
  13. package/src/proptypes.ts +0 -19
  14. package/src/rule.ts +0 -419
  15. package/src/rules/absolute-url.ts +0 -23
  16. package/src/rules/all-rules.ts +0 -71
  17. package/src/rules/blockquoted-math.ts +0 -9
  18. package/src/rules/blockquoted-widget.ts +0 -9
  19. package/src/rules/double-spacing-after-terminal.ts +0 -11
  20. package/src/rules/extra-content-spacing.ts +0 -11
  21. package/src/rules/heading-level-1.ts +0 -13
  22. package/src/rules/heading-level-skip.ts +0 -19
  23. package/src/rules/heading-sentence-case.ts +0 -10
  24. package/src/rules/heading-title-case.ts +0 -68
  25. package/src/rules/image-alt-text.ts +0 -20
  26. package/src/rules/image-in-table.ts +0 -9
  27. package/src/rules/image-spaces-around-urls.ts +0 -34
  28. package/src/rules/image-widget.ts +0 -49
  29. package/src/rules/link-click-here.ts +0 -10
  30. package/src/rules/lint-utils.ts +0 -47
  31. package/src/rules/long-paragraph.ts +0 -13
  32. package/src/rules/math-adjacent.ts +0 -9
  33. package/src/rules/math-align-extra-break.ts +0 -10
  34. package/src/rules/math-align-linebreaks.ts +0 -42
  35. package/src/rules/math-empty.ts +0 -9
  36. package/src/rules/math-font-size.ts +0 -11
  37. package/src/rules/math-frac.ts +0 -9
  38. package/src/rules/math-nested.ts +0 -10
  39. package/src/rules/math-starts-with-space.ts +0 -11
  40. package/src/rules/math-text-empty.ts +0 -9
  41. package/src/rules/math-without-dollars.ts +0 -13
  42. package/src/rules/nested-lists.ts +0 -10
  43. package/src/rules/profanity.ts +0 -9
  44. package/src/rules/table-missing-cells.ts +0 -19
  45. package/src/rules/unbalanced-code-delimiters.ts +0 -13
  46. package/src/rules/unescaped-dollar.ts +0 -9
  47. package/src/rules/widget-in-table.ts +0 -9
  48. package/src/selector.ts +0 -504
  49. package/src/tree-transformer.ts +0 -583
  50. package/src/types.ts +0 -7
  51. package/src/version.ts +0 -10
  52. package/tsconfig-build.json +0 -12
  53. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,444 +0,0 @@
1
- import TreeTransformer from "../tree-transformer";
2
-
3
- import type {TraversalState} from "../tree-transformer";
4
-
5
- describe("PerseusLinter tree transformer", () => {
6
- function clone(o) {
7
- return JSON.parse(JSON.stringify(o));
8
- }
9
-
10
- const tree1 = {
11
- id: 0,
12
- type: "root",
13
- content: [
14
- {id: 1, type: "text", content: "Hello, "},
15
- {
16
- id: 2,
17
- type: "em",
18
- content: {
19
- id: 3,
20
- type: "text",
21
- content: "World!",
22
- },
23
- },
24
- {
25
- id: 4,
26
- type: "list",
27
- items: [
28
- {id: 5, type: "text", content: "A"},
29
- {id: 6, type: "text", content: "B"},
30
- {id: 7, type: "text", content: "C"},
31
- ],
32
- },
33
- ],
34
- } as const;
35
-
36
- // These are three variants on the same tree where we use arrays
37
- // instead of single nodes. The tests below will be run over all
38
- // four variants, and should work the same for all.
39
- const tree2 = [clone(tree1)];
40
- const tree3 = clone(tree1);
41
- tree3.content[1].content = [tree3.content[1].content];
42
- const tree4 = clone(tree2);
43
- tree4[0].content[1].content = [tree4[0].content[1].content];
44
-
45
- const trees = [tree1, tree2, tree3, tree4];
46
-
47
- const postOrderTraversalOrder = [1, 3, 2, 5, 6, 7, 4, 0];
48
- const parentNodeIds = [-1, 0, 0, 2, 0, 4, 4, 4];
49
- const previousNodeIds = [-1, -1, 1, -1, 2, -1, 5, 6];
50
- const nextNodeIds = [-1, 2, 4, -1, -1, 6, 7, -1];
51
-
52
- // The first test will fill in this array mapping numbers to nodes
53
- // Then subsequent tests can use it
54
- const nodes: Array<any> = [];
55
-
56
- function getTraversalOrder(tree: any) {
57
- const order: Array<any> = [];
58
- new TreeTransformer(tree).traverse((n, state) => {
59
- // @ts-expect-error - TS2339 - Property 'id' does not exist on type 'TreeNode'.
60
- order.push(n.id);
61
- });
62
- return order;
63
- }
64
-
65
- trees.forEach((tree: any, treenum: number) => {
66
- it(
67
- "does post-order traversal of each node in the tree " + treenum,
68
- () => {
69
- const tt = new TreeTransformer(tree);
70
- const ids: Array<any> = [];
71
-
72
- tt.traverse((n: any) => {
73
- nodes[n.id] = n; // Remember the nodes by id for later tests
74
- ids.push(n.id);
75
- });
76
-
77
- // Post-order traversal means we visit the nodes on the way
78
- // back up, not on the way down.
79
- expect(ids).toEqual(postOrderTraversalOrder);
80
- },
81
- );
82
-
83
- it("tracks the current node " + treenum, () => {
84
- new TreeTransformer(tree).traverse((n, state) => {
85
- expect(state.currentNode()).toEqual(n);
86
- });
87
- });
88
-
89
- it("correctly gets the siblings for each node " + treenum, () => {
90
- new TreeTransformer(tree).traverse((n: any, state) => {
91
- const previd = previousNodeIds[n.id];
92
- expect(state.hasPreviousSibling()).toEqual(previd >= 0);
93
- expect(state.previousSibling()).toEqual(
94
- previd >= 0 ? nodes[previd] : null,
95
- );
96
-
97
- const nextid = nextNodeIds[n.id];
98
- expect(state.nextSibling()).toEqual(
99
- nextid >= 0 ? nodes[nextid] : null,
100
- );
101
- });
102
- });
103
-
104
- it("knows the ancestors for each node: " + treenum, () => {
105
- const ancestorsById = [
106
- [],
107
- [0],
108
- [0],
109
- [0, 2],
110
- [0],
111
- [0, 4],
112
- [0, 4],
113
- [0, 4],
114
- ];
115
- const ancestorTypesById = [
116
- [],
117
- ["root"],
118
- ["root"],
119
- ["root", "em"],
120
- ["root"],
121
- ["root", "list"],
122
- ["root", "list"],
123
- ["root", "list"],
124
- ];
125
-
126
- new TreeTransformer(tree).traverse((n: any, state) => {
127
- expect(state.hasParent()).toEqual(
128
- ancestorsById[n.id].length > 0,
129
- );
130
- const ancestorids = ancestorsById[n.id];
131
- expect(state.parent()).toEqual(
132
- nodes[ancestorids[ancestorids.length - 1]],
133
- );
134
- expect(state.ancestors()).toEqual(
135
- ancestorsById[n.id].map((id) => nodes[id]),
136
- );
137
- expect(ancestorsById[n.id].map((id) => nodes[id].type)).toEqual(
138
- ancestorTypesById[n.id],
139
- );
140
- });
141
- });
142
-
143
- it("computes the textContent for each node " + treenum, () => {
144
- const textContentForNode = [
145
- "Hello, World!ABC",
146
- "Hello, ",
147
- "World!",
148
- "World!",
149
- "ABC",
150
- "A",
151
- "B",
152
- "C",
153
- ];
154
-
155
- new TreeTransformer(tree).traverse((n: any, state, content) => {
156
- expect(content).toEqual(textContentForNode[n.id]);
157
- });
158
- });
159
-
160
- it("can remove the next sibling " + treenum, () => {
161
- const expectedTraversals = [
162
- // if a node has no next sibling, the test is a no-op
163
- postOrderTraversalOrder,
164
- [1, 5, 6, 7, 4, 0],
165
- [1, 3, 2, 0],
166
- postOrderTraversalOrder,
167
- postOrderTraversalOrder,
168
- [1, 3, 2, 5, 7, 4, 0],
169
- [1, 3, 2, 5, 6, 4, 0],
170
- postOrderTraversalOrder,
171
- ];
172
-
173
- // For each node in the tree
174
- for (let id = 0; id < nodes.length; id++) {
175
- // Start with a copy of the tree
176
- const copy = clone(tree);
177
-
178
- // Remove the next sibling of the node with this id
179
- new TreeTransformer(copy).traverse((n: any, state: any) => {
180
- if (n.id === id) {
181
- state.removeNextSibling();
182
- }
183
-
184
- // Ensure that we don't iterate the removed sibling
185
- expect(n.id).not.toEqual(nextNodeIds[id]);
186
- });
187
-
188
- // And then get the traversal order of the resulting tree
189
- const traversal = getTraversalOrder(copy);
190
-
191
- // Compare it to the expected value
192
- expect(traversal).toEqual(expectedTraversals[id]);
193
- }
194
- });
195
-
196
- it("won't try to replace the root of the tree " + treenum, () => {
197
- const copy = clone(tree);
198
- new TreeTransformer(copy).traverse((n, state) => {
199
- if (n === state.root) {
200
- expect(() => state.replace()).toThrow();
201
- }
202
- });
203
- });
204
-
205
- it("Can remove nodes " + treenum, () => {
206
- const expectedTraversals = [
207
- null,
208
- [3, 2, 5, 6, 7, 4, 0],
209
- [1, 5, 6, 7, 4, 0],
210
- [1, 2, 5, 6, 7, 4, 0],
211
- [1, 3, 2, 0],
212
- [1, 3, 2, 6, 7, 4, 0],
213
- [1, 3, 2, 5, 7, 4, 0],
214
- [1, 3, 2, 5, 6, 4, 0],
215
- ];
216
-
217
- // Loop through all the nodes except the root
218
- for (let id = 1; id < nodes.length; id++) {
219
- // Make a copy of the tree
220
- const copy = clone(tree);
221
- // Remove this node from it
222
- new TreeTransformer(copy).traverse((n: any, state: any) => {
223
- if (n.id === id) {
224
- state.replace();
225
- }
226
- });
227
-
228
- // Traverse what remains and see if we get what is expected
229
- expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
230
- }
231
- });
232
-
233
- it("Can replace nodes " + treenum, () => {
234
- const expectedTraversals = [
235
- null,
236
- [99, 3, 2, 5, 6, 7, 4, 0],
237
- [1, 99, 5, 6, 7, 4, 0],
238
- [1, 99, 2, 5, 6, 7, 4, 0],
239
- [1, 3, 2, 99, 0],
240
- [1, 3, 2, 99, 6, 7, 4, 0],
241
- [1, 3, 2, 5, 99, 7, 4, 0],
242
- [1, 3, 2, 5, 6, 99, 4, 0],
243
- ];
244
-
245
- // Loop through all the nodes except the root
246
- for (let id = 1; id < nodes.length; id++) {
247
- // Make a copy of the tree
248
- const copy = clone(tree);
249
- // Replace the node with a different one
250
- new TreeTransformer(copy).traverse((n: any, state) => {
251
- if (n.id === id) {
252
- state.replace({
253
- // @ts-expect-error - TS2345 - Argument of type '{ id: number; type: string; }' is not assignable to parameter of type 'TreeNode'.
254
- id: 99,
255
- type: "replacement",
256
- });
257
- }
258
-
259
- // Ensure that we don't traverse the new node
260
- expect(n.id).not.toEqual(99);
261
- });
262
-
263
- // Traverse what remains and see if we get what is expected
264
- expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
265
- }
266
- });
267
-
268
- it("Can reparent nodes " + treenum, () => {
269
- const expectedTraversals = [
270
- null,
271
- [1, 99, 3, 2, 5, 6, 7, 4, 0],
272
- [1, 3, 2, 99, 5, 6, 7, 4, 0],
273
- [1, 3, 99, 2, 5, 6, 7, 4, 0],
274
- [1, 3, 2, 5, 6, 7, 4, 99, 0],
275
- [1, 3, 2, 5, 99, 6, 7, 4, 0],
276
- [1, 3, 2, 5, 6, 99, 7, 4, 0],
277
- [1, 3, 2, 5, 6, 7, 99, 4, 0],
278
- ];
279
-
280
- // Loop through all the nodes except the root
281
- for (let id = 1; id < nodes.length; id++) {
282
- // Make a copy of the tree
283
- const copy = clone(tree);
284
- let count = 0;
285
- // Replace the node with a different one
286
- new TreeTransformer(copy).traverse((n: any, state) => {
287
- if (n.id === id) {
288
- // Ensure that we don't traverse the node more than once
289
- expect(++count).toEqual(1);
290
- state.replace({
291
- // @ts-expect-error - TS2345 - Argument of type '{ id: number; type: string; content: any; }' is not assignable to parameter of type 'TreeNode'.
292
- id: 99,
293
- type: "reparent",
294
- content: n,
295
- });
296
- }
297
-
298
- // Ensure that we don't traverse the new node
299
- expect(n.id).not.toEqual(99);
300
- });
301
-
302
- // Traverse what remains and see if we get what is expected
303
- expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
304
- }
305
- });
306
-
307
- it("Can replace nodes with an array of nodes " + treenum, () => {
308
- const expectedTraversals = [
309
- null,
310
- [99, 101, 100, 3, 2, 5, 6, 7, 4, 0],
311
- [1, 99, 101, 100, 5, 6, 7, 4, 0],
312
- [1, 99, 101, 100, 2, 5, 6, 7, 4, 0],
313
- [1, 3, 2, 99, 101, 100, 0],
314
- [1, 3, 2, 99, 101, 100, 6, 7, 4, 0],
315
- [1, 3, 2, 5, 99, 101, 100, 7, 4, 0],
316
- [1, 3, 2, 5, 6, 99, 101, 100, 4, 0],
317
- ];
318
-
319
- // Loop through all the nodes except the root
320
- for (let id = 1; id < nodes.length; id++) {
321
- // Make a copy of the tree
322
- const copy = clone(tree);
323
- // Replace the node with two new ones
324
- new TreeTransformer(copy).traverse((n: any, state) => {
325
- if (n.id === id) {
326
- // @ts-expect-error - TS2345 - Argument of type '({ id: number; type: string; } | { id: number; type: string; content: { id: number; type: string; }; })[]' is not assignable to parameter of type 'TreeNode'.
327
- state.replace([
328
- {
329
- id: 99,
330
- type: "replacement",
331
- },
332
- {
333
- id: 100,
334
- type: "replacement",
335
- content: {
336
- id: 101,
337
- type: "nested",
338
- },
339
- },
340
- ]);
341
- }
342
-
343
- // Ensure that we don't traverse any new nodes
344
- expect(n.id).not.toEqual(99);
345
- expect(n.id).not.toEqual(100);
346
- expect(n.id).not.toEqual(101);
347
- });
348
-
349
- // Traverse what remains and see if we get what is expected
350
- expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
351
- }
352
- });
353
-
354
- it("Can replace nodes with two nodes " + treenum, () => {
355
- const expectedTraversals = [
356
- null,
357
- [99, 101, 100, 3, 2, 5, 6, 7, 4, 0],
358
- [1, 99, 101, 100, 5, 6, 7, 4, 0],
359
- [1, 99, 101, 100, 2, 5, 6, 7, 4, 0],
360
- [1, 3, 2, 99, 101, 100, 0],
361
- [1, 3, 2, 99, 101, 100, 6, 7, 4, 0],
362
- [1, 3, 2, 5, 99, 101, 100, 7, 4, 0],
363
- [1, 3, 2, 5, 6, 99, 101, 100, 4, 0],
364
- ];
365
-
366
- // Loop through all the nodes except the root
367
- for (let id = 1; id < nodes.length; id++) {
368
- // Make a copy of the tree
369
- const copy = clone(tree);
370
- // Replace the node with two new ones
371
- new TreeTransformer(copy).traverse((n: any, state) => {
372
- if (n.id === id) {
373
- state.replace(
374
- {
375
- // @ts-expect-error - TS2345 - Argument of type '{ id: number; type: string; }' is not assignable to parameter of type 'TreeNode'.
376
- id: 99,
377
- type: "replacement",
378
- },
379
- {
380
- id: 100,
381
- type: "replacement",
382
- content: {
383
- id: 101,
384
- type: "nested",
385
- },
386
- },
387
- );
388
- }
389
-
390
- // Ensure that we don't traverse any new nodes
391
- expect(n.id).not.toEqual(99);
392
- expect(n.id).not.toEqual(100);
393
- expect(n.id).not.toEqual(101);
394
- });
395
-
396
- // Traverse what remains and see if we get what is expected
397
- expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
398
- }
399
- });
400
-
401
- it("goToParent() and goToPreviousSibling() work " + treenum, () => {
402
- // Traverse the tree, saving copies of the state object
403
- // for each node. Then check that goToParent() and
404
- // goToPreviousSibling() on each saved state object
405
- // modify the states in apprpriate ways.
406
- const states: Array<TraversalState> = [];
407
-
408
- const tt = new TreeTransformer(tree);
409
- tt.traverse((n: any, state, content) => {
410
- states[n.id] = state.clone();
411
- // Verify that a clone() and equal() work as expected
412
- expect(state.equals(states[n.id])).toBeTruthy();
413
- });
414
-
415
- // Check that goToPreviousSibling() works
416
- for (let n = 0; n < states.length; n++) {
417
- const state = states[n].clone();
418
- const previousNodeId = previousNodeIds[n];
419
- if (previousNodeId === -1) {
420
- expect(() => {
421
- state.goToPreviousSibling();
422
- }).toThrow();
423
- } else {
424
- state.goToPreviousSibling();
425
- expect(state.equals(states[previousNodeId])).toBeTruthy();
426
- }
427
- }
428
-
429
- // Check that goToParent() works
430
- for (let n = 0; n < states.length; n++) {
431
- const state = states[n].clone();
432
- const parentNodeId = parentNodeIds[n];
433
- if (parentNodeId === -1) {
434
- expect(() => {
435
- state.goToParent();
436
- }).toThrow();
437
- } else {
438
- state.goToParent();
439
- expect(state.equals(states[parentNodeId])).toBeTruthy();
440
- }
441
- }
442
- });
443
- });
444
- });