@khanacademy/perseus-linter 0.2.5 → 0.3.1
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/.eslintrc.js +1 -0
- package/CHANGELOG.md +19 -0
- package/dist/es/index.js +643 -588
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +281 -398
- package/dist/index.js.flow +18 -2
- package/dist/index.js.map +1 -1
- package/dist/proptypes.d.ts +9 -0
- package/dist/proptypes.js.flow +17 -0
- package/dist/rule.d.ts +170 -0
- package/dist/rule.js.flow +86 -0
- package/dist/rules/absolute-url.d.ts +3 -0
- package/dist/rules/absolute-url.js.flow +9 -0
- package/dist/rules/all-rules.d.ts +2 -0
- package/dist/rules/all-rules.js.flow +9 -0
- package/dist/rules/blockquoted-math.d.ts +3 -0
- package/dist/rules/blockquoted-math.js.flow +9 -0
- package/dist/rules/blockquoted-widget.d.ts +3 -0
- package/dist/rules/blockquoted-widget.js.flow +9 -0
- package/dist/rules/double-spacing-after-terminal.d.ts +3 -0
- package/dist/rules/double-spacing-after-terminal.js.flow +9 -0
- package/dist/rules/extra-content-spacing.d.ts +3 -0
- package/dist/rules/extra-content-spacing.js.flow +9 -0
- package/dist/rules/heading-level-1.d.ts +3 -0
- package/dist/rules/heading-level-1.js.flow +9 -0
- package/dist/rules/heading-level-skip.d.ts +3 -0
- package/dist/rules/heading-level-skip.js.flow +9 -0
- package/dist/rules/heading-sentence-case.d.ts +3 -0
- package/dist/rules/heading-sentence-case.js.flow +9 -0
- package/dist/rules/heading-title-case.d.ts +3 -0
- package/dist/rules/heading-title-case.js.flow +9 -0
- package/dist/rules/image-alt-text.d.ts +3 -0
- package/dist/rules/image-alt-text.js.flow +9 -0
- package/dist/rules/image-in-table.d.ts +3 -0
- package/dist/rules/image-in-table.js.flow +9 -0
- package/dist/rules/image-spaces-around-urls.d.ts +3 -0
- package/dist/rules/image-spaces-around-urls.js.flow +9 -0
- package/dist/rules/image-widget.d.ts +3 -0
- package/dist/rules/image-widget.js.flow +9 -0
- package/dist/rules/link-click-here.d.ts +3 -0
- package/dist/rules/link-click-here.js.flow +9 -0
- package/dist/rules/lint-utils.d.ts +2 -0
- package/dist/rules/lint-utils.js.flow +8 -0
- package/dist/rules/long-paragraph.d.ts +3 -0
- package/dist/rules/long-paragraph.js.flow +9 -0
- package/dist/rules/math-adjacent.d.ts +3 -0
- package/dist/rules/math-adjacent.js.flow +9 -0
- package/dist/rules/math-align-extra-break.d.ts +3 -0
- package/dist/rules/math-align-extra-break.js.flow +9 -0
- package/dist/rules/math-align-linebreaks.d.ts +3 -0
- package/dist/rules/math-align-linebreaks.js.flow +9 -0
- package/dist/rules/math-empty.d.ts +3 -0
- package/dist/rules/math-empty.js.flow +9 -0
- package/dist/rules/math-font-size.d.ts +3 -0
- package/dist/rules/math-font-size.js.flow +9 -0
- package/dist/rules/math-frac.d.ts +3 -0
- package/dist/rules/math-frac.js.flow +9 -0
- package/dist/rules/math-nested.d.ts +3 -0
- package/dist/rules/math-nested.js.flow +9 -0
- package/dist/rules/math-starts-with-space.d.ts +3 -0
- package/dist/rules/math-starts-with-space.js.flow +9 -0
- package/dist/rules/math-text-empty.d.ts +3 -0
- package/dist/rules/math-text-empty.js.flow +9 -0
- package/dist/rules/math-without-dollars.d.ts +3 -0
- package/dist/rules/math-without-dollars.js.flow +9 -0
- package/dist/rules/nested-lists.d.ts +3 -0
- package/dist/rules/nested-lists.js.flow +9 -0
- package/dist/rules/profanity.d.ts +3 -0
- package/dist/rules/profanity.js.flow +9 -0
- package/dist/rules/table-missing-cells.d.ts +3 -0
- package/dist/rules/table-missing-cells.js.flow +9 -0
- package/dist/rules/unbalanced-code-delimiters.d.ts +3 -0
- package/dist/rules/unbalanced-code-delimiters.js.flow +9 -0
- package/dist/rules/unescaped-dollar.d.ts +3 -0
- package/dist/rules/unescaped-dollar.js.flow +9 -0
- package/dist/rules/widget-in-table.d.ts +3 -0
- package/dist/rules/widget-in-table.js.flow +9 -0
- package/dist/selector.d.ts +108 -0
- package/dist/selector.js.flow +31 -0
- package/dist/tree-transformer.d.ts +205 -0
- package/dist/tree-transformer.js.flow +253 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js.flow +12 -0
- package/package.json +4 -4
- package/src/__tests__/{matcher_test.js → matcher.test.ts} +60 -60
- package/src/__tests__/{rule_test.js → rule.test.ts} +13 -5
- package/src/__tests__/{rules_test.js → rules.test.ts} +99 -39
- package/src/__tests__/{selector-parser_test.js → selector-parser.test.ts} +1 -2
- package/src/__tests__/{tree-transformer_test.js → tree-transformer.test.ts} +39 -41
- package/src/{index.js → index.ts} +21 -23
- package/src/{proptypes.js → proptypes.ts} +4 -14
- package/src/{rule.js → rule.ts} +45 -38
- package/src/rules/{absolute-url.js → absolute-url.ts} +4 -5
- package/src/rules/all-rules.ts +71 -0
- package/src/rules/{blockquoted-math.js → blockquoted-math.ts} +3 -4
- package/src/rules/{blockquoted-widget.js → blockquoted-widget.ts} +3 -4
- package/src/rules/{double-spacing-after-terminal.js → double-spacing-after-terminal.ts} +3 -4
- package/src/rules/{extra-content-spacing.js → extra-content-spacing.ts} +3 -4
- package/src/rules/{heading-level-1.js → heading-level-1.ts} +3 -4
- package/src/rules/{heading-level-skip.js → heading-level-skip.ts} +3 -4
- package/src/rules/{heading-sentence-case.js → heading-sentence-case.ts} +3 -4
- package/src/rules/{heading-title-case.js → heading-title-case.ts} +11 -6
- package/src/rules/{image-alt-text.js → image-alt-text.ts} +3 -4
- package/src/rules/{image-in-table.js → image-in-table.ts} +3 -4
- package/src/rules/{image-spaces-around-urls.js → image-spaces-around-urls.ts} +3 -4
- package/src/rules/{image-widget.js → image-widget.ts} +3 -4
- package/src/rules/{link-click-here.js → link-click-here.ts} +3 -4
- package/src/rules/{lint-utils.js → lint-utils.ts} +1 -2
- package/src/rules/{long-paragraph.js → long-paragraph.ts} +3 -4
- package/src/rules/{math-adjacent.js → math-adjacent.ts} +3 -4
- package/src/rules/{math-align-extra-break.js → math-align-extra-break.ts} +3 -4
- package/src/rules/{math-align-linebreaks.js → math-align-linebreaks.ts} +3 -4
- package/src/rules/{math-empty.js → math-empty.ts} +3 -4
- package/src/rules/{math-font-size.js → math-font-size.ts} +3 -4
- package/src/rules/{math-frac.js → math-frac.ts} +3 -4
- package/src/rules/{math-nested.js → math-nested.ts} +3 -4
- package/src/rules/{math-starts-with-space.js → math-starts-with-space.ts} +3 -4
- package/src/rules/{math-text-empty.js → math-text-empty.ts} +3 -4
- package/src/rules/{math-without-dollars.js → math-without-dollars.ts} +3 -4
- package/src/rules/{nested-lists.js → nested-lists.ts} +3 -4
- package/src/rules/{profanity.js → profanity.ts} +3 -4
- package/src/rules/{table-missing-cells.js → table-missing-cells.ts} +3 -4
- package/src/rules/{unbalanced-code-delimiters.js → unbalanced-code-delimiters.ts} +3 -4
- package/src/rules/{unescaped-dollar.js → unescaped-dollar.ts} +3 -4
- package/src/rules/{widget-in-table.js → widget-in-table.ts} +3 -4
- package/src/{selector.js → selector.ts} +12 -13
- package/src/{tree-transformer.js → tree-transformer.ts} +24 -24
- package/src/types.ts +7 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/src/rules/all-rules.js +0 -72
- package/src/types.js +0 -10
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flowtype definitions for data
|
|
3
|
+
* Generated by Flowgen from a Typescript Definition
|
|
4
|
+
* Flowgen v1.21.0
|
|
5
|
+
* @flow
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* TreeTransformer is a class for traversing and transforming trees. Create a
|
|
9
|
+
* TreeTransformer by passing the root node of the tree to the
|
|
10
|
+
* constructor. Then traverse that tree by calling the traverse() method. The
|
|
11
|
+
* argument to traverse() is a callback function that will be called once for
|
|
12
|
+
* each node in the tree. This is a post-order depth-first traversal: the
|
|
13
|
+
* callback is not called on the a way down, but on the way back up. That is,
|
|
14
|
+
* the children of a node are traversed before the node itself is.
|
|
15
|
+
*
|
|
16
|
+
* The traversal callback function is passed three arguments, the node being
|
|
17
|
+
* traversed, a TraversalState object, and the concatentated text content of
|
|
18
|
+
* the node and all of its descendants. The TraversalState object is the most
|
|
19
|
+
* most interesting argument: it has methods for querying the ancestors and
|
|
20
|
+
* siblings of the node, and for deleting or replacing the node. These
|
|
21
|
+
* transformation methods are why this class is a tree transformer and not
|
|
22
|
+
* just a tree traverser.
|
|
23
|
+
*
|
|
24
|
+
* A typical tree traversal looks like this:
|
|
25
|
+
*
|
|
26
|
+
* new TreeTransformer(root).traverse((node, state, content) => {
|
|
27
|
+
* let parent = state.parent();
|
|
28
|
+
* let previous = state.previousSibling();
|
|
29
|
+
* // etc.
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* The traverse() method descends through nodes and arrays of nodes and calls
|
|
33
|
+
* the traverse callback on each node on the way back up to the root of the
|
|
34
|
+
* tree. (Note that it only calls the callback on the nodes themselves, not
|
|
35
|
+
* any arrays that contain nodes.) A node is loosely defined as any object
|
|
36
|
+
* with a string-valued `type` property. Objects that do not have a type
|
|
37
|
+
* property are assumed to not be part of the tree and are not traversed. When
|
|
38
|
+
* traversing an array, all elements of the array are examined, and any that
|
|
39
|
+
* are nodes or arrays are recursively traversed. When traversing a node, all
|
|
40
|
+
* properties of the object are examined and any node or array values are
|
|
41
|
+
* recursively traversed. In typical parse trees, the children of a node are
|
|
42
|
+
* in a `children` or `content` array, but this class is designed to handle
|
|
43
|
+
* more general trees. The Perseus markdown parser, for example, produces
|
|
44
|
+
* nodes of type "table" that have children in the `header` and `cells`
|
|
45
|
+
* properties.
|
|
46
|
+
*
|
|
47
|
+
* CAUTION: the traverse() method does not make any attempt to detect
|
|
48
|
+
* cycles. If you call it on a cyclic graph instead of a tree, it will cause
|
|
49
|
+
* infinite recursion (or, more likely, a stack overflow).
|
|
50
|
+
*
|
|
51
|
+
* TODO(davidflanagan): it probably wouldn't be hard to detect cycles: when
|
|
52
|
+
* pushing a new node onto the containers stack we could just check that it
|
|
53
|
+
* isn't already there.
|
|
54
|
+
*
|
|
55
|
+
* If a node has a text-valued `content` property, it is taken to be the
|
|
56
|
+
* plain-text content of the node. The traverse() method concatenates these
|
|
57
|
+
* content strings and passes them to the traversal callback for each
|
|
58
|
+
* node. This means that the callback has access the full text content of its
|
|
59
|
+
* node and all of the nodes descendants.
|
|
60
|
+
*
|
|
61
|
+
* See the TraversalState class for more information on what information and
|
|
62
|
+
* methods are available to the traversal callback.
|
|
63
|
+
*/
|
|
64
|
+
export type TreeNode = {|
|
|
65
|
+
type: string,
|
|
66
|
+
|};
|
|
67
|
+
export type TraversalCallback = (
|
|
68
|
+
node: TreeNode,
|
|
69
|
+
state: TraversalState,
|
|
70
|
+
content: string
|
|
71
|
+
) => void;
|
|
72
|
+
declare export default class TreeTransformer {
|
|
73
|
+
root: TreeNode;
|
|
74
|
+
constructor(root: TreeNode): this;
|
|
75
|
+
static isNode(n: any): boolean;
|
|
76
|
+
static isTextNode(n: any): boolean;
|
|
77
|
+
traverse(f: TraversalCallback): void;
|
|
78
|
+
_traverse(
|
|
79
|
+
n: TreeNode | Array<TreeNode>,
|
|
80
|
+
state: TraversalState,
|
|
81
|
+
f: TraversalCallback
|
|
82
|
+
): string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* This class represents the state of a tree traversal. An instance is created
|
|
86
|
+
* by the traverse() method of the TreeTransformer class to maintain the state
|
|
87
|
+
* for that traversal, and the instance is passed to the traversal callback
|
|
88
|
+
* function for each node that is traversed. This class is not intended to be
|
|
89
|
+
* instantiated directly, but is exported so that its type can be used for
|
|
90
|
+
* Flow annotaions.
|
|
91
|
+
*/
|
|
92
|
+
declare export class TraversalState {
|
|
93
|
+
root: TreeNode;
|
|
94
|
+
_currentNode: TreeNode | null | void;
|
|
95
|
+
_containers: Stack<TreeNode | Array<TreeNode>>;
|
|
96
|
+
_indexes: Stack<string | number>;
|
|
97
|
+
_ancestors: Stack<TreeNode>;
|
|
98
|
+
constructor(root: TreeNode): this;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Return the current node in the traversal. Any time the traversal
|
|
102
|
+
* callback is called, this method will return the name value as the
|
|
103
|
+
* first argument to the callback.
|
|
104
|
+
*/
|
|
105
|
+
currentNode(): TreeNode;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Return the parent of the current node, if there is one, or null.
|
|
109
|
+
*/
|
|
110
|
+
parent(): TreeNode | null | void;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Return an array of ancestor nodes. The first element of this array is
|
|
114
|
+
* the same as this.parent() and the last element is the root node. If we
|
|
115
|
+
* are currently at the root node, the the returned array will be empty.
|
|
116
|
+
* This method makes a copy of the internal state, so modifications to the
|
|
117
|
+
* returned array have no effect on the traversal.
|
|
118
|
+
*/
|
|
119
|
+
ancestors(): $ReadOnlyArray<TreeNode>;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Return the next sibling of this node, if it has one, or null otherwise.
|
|
123
|
+
*/
|
|
124
|
+
nextSibling(): TreeNode | null | void;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Return the previous sibling of this node, if it has one, or null
|
|
128
|
+
* otherwise.
|
|
129
|
+
*/
|
|
130
|
+
previousSibling(): TreeNode | null | void;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Remove the next sibling node (if there is one) from the tree. Returns
|
|
134
|
+
* the removed sibling or null. This method makes it easy to traverse a
|
|
135
|
+
* tree and concatenate adjacent text nodes into a single node.
|
|
136
|
+
*/
|
|
137
|
+
removeNextSibling(): TreeNode | null | void;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Replace the current node in the tree with the specified nodes. If no
|
|
141
|
+
* nodes are passed, this is a node deletion. If one node (or array) is
|
|
142
|
+
* passed, this is a 1-for-1 replacement. If more than one node is passed
|
|
143
|
+
* then this is a combination of deletion and insertion. The new node or
|
|
144
|
+
* nodes will not be traversed, so this method can safely be used to
|
|
145
|
+
* reparent the current node node beneath a new parent.
|
|
146
|
+
*
|
|
147
|
+
* This method throws an error if you attempt to replace the root node of
|
|
148
|
+
* the tree.
|
|
149
|
+
*/
|
|
150
|
+
replace(...replacements: $ReadOnlyArray<TreeNode>): void;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Returns true if the current node has a previous sibling and false
|
|
154
|
+
* otherwise. If this method returns false, then previousSibling() will
|
|
155
|
+
* return null, and goToPreviousSibling() will throw an error.
|
|
156
|
+
*/
|
|
157
|
+
hasPreviousSibling(): boolean;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Modify this traversal state object to have the state it would have had
|
|
161
|
+
* when visiting the previous sibling. Note that you may want to use
|
|
162
|
+
* clone() to make a copy before modifying the state object like this.
|
|
163
|
+
* This mutator method is not typically used during ordinary tree
|
|
164
|
+
* traversals, but is used by the Selector class for matching multi-node
|
|
165
|
+
* selectors.
|
|
166
|
+
*/
|
|
167
|
+
goToPreviousSibling(): void;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Returns true if the current node has an ancestor and false otherwise.
|
|
171
|
+
* If this method returns false, then the parent() method will return
|
|
172
|
+
* null and goToParent() will throw an error
|
|
173
|
+
*/
|
|
174
|
+
hasParent(): boolean;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Modify this object to look like it will look when we (later) visit the
|
|
178
|
+
* parent node of this node. You should not modify the instance passed to
|
|
179
|
+
* the tree traversal callback. Instead, make a copy with the clone()
|
|
180
|
+
* method and modify that. This mutator method is not typically used
|
|
181
|
+
* during ordinary tree traversals, but is used by the Selector class for
|
|
182
|
+
* matching multi-node selectors that involve parent and ancestor
|
|
183
|
+
* selectors.
|
|
184
|
+
*/
|
|
185
|
+
goToParent(): void;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Return a new TraversalState object that is a copy of this one.
|
|
189
|
+
* This method is useful in conjunction with the mutating methods
|
|
190
|
+
* goToParent() and goToPreviousSibling().
|
|
191
|
+
*/
|
|
192
|
+
clone(): TraversalState;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Returns true if this TraversalState object is equal to that
|
|
196
|
+
* TraversalState object, or false otherwise. This method exists
|
|
197
|
+
* primarily for use by our unit tests.
|
|
198
|
+
*/
|
|
199
|
+
equals(that: TraversalState): boolean;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* This class is an internal utility that just treats an array as a stack
|
|
203
|
+
* and gives us a top() method so we don't have to write expressions like
|
|
204
|
+
* `ancestors[ancestors.length-1]`. The values() method automatically
|
|
205
|
+
* copies the internal array so we don't have to worry about client code
|
|
206
|
+
* modifying our internal stacks. The use of this Stack abstraction makes
|
|
207
|
+
* the TraversalState class simpler in a number of places.
|
|
208
|
+
*/
|
|
209
|
+
declare class Stack<T> {
|
|
210
|
+
stack: Array<T>;
|
|
211
|
+
constructor(array?: $ReadOnlyArray<T> | null): this;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Push a value onto the stack.
|
|
215
|
+
*/
|
|
216
|
+
push(v: T): void;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Pop a value off of the stack.
|
|
220
|
+
*/
|
|
221
|
+
pop(): T;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Return the top value of the stack without popping it.
|
|
225
|
+
*/
|
|
226
|
+
top(): T;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Return a copy of the stack as an array
|
|
230
|
+
*/
|
|
231
|
+
values(): $ReadOnlyArray<T>;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Return the number of elements in the stack
|
|
235
|
+
*/
|
|
236
|
+
size(): number;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Return a string representation of the stack
|
|
240
|
+
*/
|
|
241
|
+
toString(): string;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Return a shallow copy of the stack
|
|
245
|
+
*/
|
|
246
|
+
clone(): Stack<T>;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Compare this stack to another and return true if the contents of
|
|
250
|
+
* the two arrays are the same.
|
|
251
|
+
*/
|
|
252
|
+
equals(that: Stack<T>): boolean;
|
|
253
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flowtype definitions for data
|
|
3
|
+
* Generated by Flowgen from a Typescript Definition
|
|
4
|
+
* Flowgen v1.21.0
|
|
5
|
+
* @flow
|
|
6
|
+
*/
|
|
7
|
+
export type LinterContextProps = {|
|
|
8
|
+
contentType: string,
|
|
9
|
+
highlightLint: boolean,
|
|
10
|
+
paths: $ReadOnlyArray<string>,
|
|
11
|
+
stack: $ReadOnlyArray<string>,
|
|
12
|
+
|};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Linter engine for Perseus",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.3.1",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
},
|
|
17
17
|
"module": "dist/es/index.js",
|
|
18
18
|
"main": "dist/index.js",
|
|
19
|
-
"source": "src/index.
|
|
19
|
+
"source": "src/index.ts",
|
|
20
20
|
"scripts": {
|
|
21
21
|
"test": "bash -c 'yarn --silent --cwd \"../..\" test ${@:0} $($([[ ${@: -1} = -* ]] || [[ ${@: -1} = bash ]]) && echo $PWD)'"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@khanacademy/perseus-error": "^0.1
|
|
24
|
+
"@khanacademy/perseus-error": "^0.2.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@khanacademy/pure-markdown": "^0.1
|
|
27
|
+
"@khanacademy/pure-markdown": "^0.2.1",
|
|
28
28
|
"prop-types": "^15.6.1"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
/* These tests exercise the Selector.match() method and also test that we
|
|
3
2
|
* can integrate ../../perseus-markdown.js with ../tree-transform.js and
|
|
4
3
|
* ../selector.js
|
|
5
4
|
*/
|
|
6
5
|
import * as PureMarkdown from "@khanacademy/pure-markdown";
|
|
7
6
|
|
|
8
|
-
import Selector from "../selector
|
|
9
|
-
import TreeTransformer from "../tree-transformer
|
|
7
|
+
import Selector from "../selector";
|
|
8
|
+
import TreeTransformer from "../tree-transformer";
|
|
10
9
|
|
|
11
10
|
describe("PerseusLinter selector matching:", () => {
|
|
12
11
|
const markdown = `
|
|
@@ -33,14 +32,14 @@ C
|
|
|
33
32
|
const tt = new TreeTransformer(tree);
|
|
34
33
|
tt.traverse((n, state, content) => {
|
|
35
34
|
// The wildcard selector should match at every node
|
|
36
|
-
//
|
|
35
|
+
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'.
|
|
37
36
|
expect(selector.match(state)[0]).toEqual(n);
|
|
38
37
|
});
|
|
39
38
|
});
|
|
40
39
|
|
|
41
40
|
it("type-based matching works", () => {
|
|
42
41
|
const tree = parseTree();
|
|
43
|
-
const selectors = {};
|
|
42
|
+
const selectors: Record<string, any> = {};
|
|
44
43
|
const tt = new TreeTransformer(tree);
|
|
45
44
|
|
|
46
45
|
// Traverse the tree once and create a selector for every type
|
|
@@ -83,14 +82,13 @@ C
|
|
|
83
82
|
tt.traverse((n, state, content) => {
|
|
84
83
|
const match = selector.match(state);
|
|
85
84
|
const parent = state.parent();
|
|
86
|
-
//
|
|
87
|
-
// $FlowFixMe[incompatible-type]
|
|
85
|
+
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'.
|
|
88
86
|
if (n.type === "text" && parent.type === "paragraph") {
|
|
89
87
|
expect(Array.isArray(match)).toBeTruthy();
|
|
90
88
|
expect(match).toHaveLength(2);
|
|
91
|
-
//
|
|
89
|
+
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'.
|
|
92
90
|
expect(match[0]).toEqual(parent);
|
|
93
|
-
//
|
|
91
|
+
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'.
|
|
94
92
|
expect(match[1]).toEqual(n);
|
|
95
93
|
matchedText += content;
|
|
96
94
|
numMatches++;
|
|
@@ -118,16 +116,18 @@ C
|
|
|
118
116
|
const grandparent = ancestors.pop();
|
|
119
117
|
if (
|
|
120
118
|
n.type === "text" &&
|
|
119
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
121
120
|
parent.type === "em" &&
|
|
121
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
122
122
|
grandparent.type === "paragraph"
|
|
123
123
|
) {
|
|
124
124
|
expect(Array.isArray(match)).toBeTruthy();
|
|
125
125
|
expect(match).toHaveLength(3);
|
|
126
|
-
//
|
|
126
|
+
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'.
|
|
127
127
|
expect(match[0]).toEqual(grandparent);
|
|
128
|
-
//
|
|
128
|
+
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'.
|
|
129
129
|
expect(match[1]).toEqual(parent);
|
|
130
|
-
//
|
|
130
|
+
// @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'.
|
|
131
131
|
expect(match[2]).toEqual(n);
|
|
132
132
|
matchedText += content;
|
|
133
133
|
numMatches++;
|
|
@@ -152,11 +152,11 @@ C
|
|
|
152
152
|
if (match !== null) {
|
|
153
153
|
expect(Array.isArray(match)).toBeTruthy();
|
|
154
154
|
expect(match).toHaveLength(2);
|
|
155
|
-
//
|
|
155
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
156
156
|
expect(match[0].type).toEqual("paragraph");
|
|
157
|
-
//
|
|
157
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
158
158
|
expect(match[1].type).toEqual("text");
|
|
159
|
-
//
|
|
159
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
160
160
|
expect(match[1]).toEqual(n);
|
|
161
161
|
matchedText += content;
|
|
162
162
|
numMatches++;
|
|
@@ -179,13 +179,13 @@ C
|
|
|
179
179
|
if (match !== null) {
|
|
180
180
|
expect(Array.isArray(match)).toBeTruthy();
|
|
181
181
|
expect(match).toHaveLength(3);
|
|
182
|
-
//
|
|
182
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
183
183
|
expect(match[0].type).toEqual("paragraph");
|
|
184
|
-
//
|
|
184
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
185
185
|
expect(match[1].type).toEqual("em");
|
|
186
|
-
//
|
|
186
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
187
187
|
expect(match[2].type).toEqual("text");
|
|
188
|
-
//
|
|
188
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
189
189
|
expect(match[2]).toEqual(n);
|
|
190
190
|
matchedText += content;
|
|
191
191
|
numMatches++;
|
|
@@ -208,13 +208,13 @@ C
|
|
|
208
208
|
if (match !== null) {
|
|
209
209
|
expect(Array.isArray(match)).toBeTruthy();
|
|
210
210
|
expect(match).toHaveLength(2);
|
|
211
|
-
//
|
|
211
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
212
212
|
expect(match[0].type).toEqual("heading");
|
|
213
|
-
//
|
|
213
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
214
214
|
expect(match[0]).toEqual(state.previousSibling());
|
|
215
|
-
//
|
|
215
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
216
216
|
expect(match[1].type).toEqual("paragraph");
|
|
217
|
-
//
|
|
217
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
218
218
|
expect(match[1]).toEqual(n);
|
|
219
219
|
matchedText += content;
|
|
220
220
|
numMatches++;
|
|
@@ -237,15 +237,15 @@ C
|
|
|
237
237
|
if (match !== null) {
|
|
238
238
|
expect(Array.isArray(match)).toBeTruthy();
|
|
239
239
|
expect(match).toHaveLength(3);
|
|
240
|
-
//
|
|
240
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
241
241
|
expect(match[0].type).toEqual("heading");
|
|
242
|
-
//
|
|
242
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
243
243
|
expect(match[1].type).toEqual("paragraph");
|
|
244
|
-
//
|
|
244
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
245
245
|
expect(match[1]).toEqual(state.previousSibling());
|
|
246
|
-
//
|
|
246
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
247
247
|
expect(match[2].type).toEqual("paragraph");
|
|
248
|
-
//
|
|
248
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
249
249
|
expect(match[2]).toEqual(n);
|
|
250
250
|
matchedText += content;
|
|
251
251
|
numMatches++;
|
|
@@ -268,11 +268,11 @@ C
|
|
|
268
268
|
if (match !== null) {
|
|
269
269
|
expect(Array.isArray(match)).toBeTruthy();
|
|
270
270
|
expect(match).toHaveLength(2);
|
|
271
|
-
//
|
|
271
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
272
272
|
expect(match[0].type).toEqual("heading");
|
|
273
|
-
//
|
|
273
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
274
274
|
expect(match[1].type).toEqual("paragraph");
|
|
275
|
-
//
|
|
275
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
276
276
|
expect(match[1]).toEqual(n);
|
|
277
277
|
matchedText += content;
|
|
278
278
|
numMatches++;
|
|
@@ -295,13 +295,13 @@ C
|
|
|
295
295
|
if (match !== null) {
|
|
296
296
|
expect(Array.isArray(match)).toBeTruthy();
|
|
297
297
|
expect(match).toHaveLength(3);
|
|
298
|
-
//
|
|
298
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
299
299
|
expect(match[0].type).toEqual("heading");
|
|
300
|
-
//
|
|
300
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
301
301
|
expect(match[1].type).toEqual("paragraph");
|
|
302
|
-
//
|
|
302
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
303
303
|
expect(match[2].type).toEqual("paragraph");
|
|
304
|
-
//
|
|
304
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
305
305
|
expect(match[2]).toEqual(n);
|
|
306
306
|
matchedText += content;
|
|
307
307
|
numMatches++;
|
|
@@ -324,15 +324,15 @@ C
|
|
|
324
324
|
if (match !== null) {
|
|
325
325
|
expect(Array.isArray(match)).toBeTruthy();
|
|
326
326
|
expect(match).toHaveLength(3);
|
|
327
|
-
//
|
|
327
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
328
328
|
expect(match[0].type).toEqual("list");
|
|
329
|
-
//
|
|
329
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
330
330
|
expect(match[1].type).toEqual("paragraph");
|
|
331
|
-
//
|
|
331
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
332
332
|
expect(match[1]).toEqual(state.parent());
|
|
333
|
-
//
|
|
333
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
334
334
|
expect(match[2].type).toEqual("em");
|
|
335
|
-
//
|
|
335
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
336
336
|
expect(match[2]).toEqual(n);
|
|
337
337
|
matchedText += content;
|
|
338
338
|
numMatches++;
|
|
@@ -355,15 +355,15 @@ C
|
|
|
355
355
|
if (match !== null) {
|
|
356
356
|
expect(Array.isArray(match)).toBeTruthy();
|
|
357
357
|
expect(match).toHaveLength(3);
|
|
358
|
-
//
|
|
358
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
359
359
|
expect(match[0].type).toEqual("list");
|
|
360
|
-
//
|
|
360
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
361
361
|
expect(match[1].type).toEqual("paragraph");
|
|
362
|
-
//
|
|
362
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
363
363
|
expect(match[1]).toEqual(state.parent());
|
|
364
|
-
//
|
|
364
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
365
365
|
expect(match[2].type).toEqual("em");
|
|
366
|
-
//
|
|
366
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
367
367
|
expect(match[2]).toEqual(n);
|
|
368
368
|
matchedText += content;
|
|
369
369
|
numMatches++;
|
|
@@ -386,17 +386,17 @@ C
|
|
|
386
386
|
if (match !== null) {
|
|
387
387
|
expect(Array.isArray(match)).toBeTruthy();
|
|
388
388
|
expect(match).toHaveLength(3);
|
|
389
|
-
//
|
|
389
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
390
390
|
expect(match[0].type).toEqual("paragraph");
|
|
391
|
-
//
|
|
391
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
392
392
|
expect(match[0]).toEqual(state.parent());
|
|
393
|
-
//
|
|
393
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
394
394
|
expect(match[1].type).toEqual("em");
|
|
395
|
-
//
|
|
395
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
396
396
|
expect(match[1]).toEqual(state.previousSibling());
|
|
397
|
-
//
|
|
397
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
398
398
|
expect(match[2].type).toEqual("text");
|
|
399
|
-
//
|
|
399
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
400
400
|
expect(match[2]).toEqual(n);
|
|
401
401
|
matchedText += content;
|
|
402
402
|
numMatches++;
|
|
@@ -419,17 +419,17 @@ C
|
|
|
419
419
|
if (match !== null) {
|
|
420
420
|
expect(Array.isArray(match)).toBeTruthy();
|
|
421
421
|
expect(match).toHaveLength(3);
|
|
422
|
-
//
|
|
422
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
423
423
|
expect(match[0].type).toEqual("paragraph");
|
|
424
|
-
//
|
|
424
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
425
425
|
expect(match[0]).toEqual(state.parent());
|
|
426
|
-
//
|
|
426
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
427
427
|
expect(match[1].type).toEqual("em");
|
|
428
|
-
//
|
|
428
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
429
429
|
expect(match[1]).toEqual(state.previousSibling());
|
|
430
|
-
//
|
|
430
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
431
431
|
expect(match[2].type).toEqual("text");
|
|
432
|
-
//
|
|
432
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
433
433
|
expect(match[2]).toEqual(n);
|
|
434
434
|
matchedText += content;
|
|
435
435
|
numMatches++;
|
|
@@ -452,7 +452,7 @@ C
|
|
|
452
452
|
if (match !== null) {
|
|
453
453
|
expect(Array.isArray(match)).toBeTruthy();
|
|
454
454
|
expect(match).toHaveLength(1);
|
|
455
|
-
//
|
|
455
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
456
456
|
expect(match[0]).toEqual(n);
|
|
457
457
|
expect(
|
|
458
458
|
n.type === "paragraph" || n.type === "list",
|
|
@@ -479,11 +479,11 @@ C
|
|
|
479
479
|
expect(Array.isArray(match)).toBeTruthy();
|
|
480
480
|
if (n.type === "heading") {
|
|
481
481
|
expect(match).toHaveLength(1);
|
|
482
|
-
//
|
|
482
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
483
483
|
expect(match[0]).toEqual(n);
|
|
484
484
|
} else {
|
|
485
485
|
expect(match).toHaveLength(2);
|
|
486
|
-
//
|
|
486
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
487
487
|
expect(match[1]).toEqual(n);
|
|
488
488
|
expect(n.type).toEqual("text");
|
|
489
489
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
import * as PureMarkdown from "@khanacademy/pure-markdown";
|
|
3
2
|
|
|
4
|
-
import Rule from "../rule
|
|
5
|
-
import TreeTransformer from "../tree-transformer
|
|
3
|
+
import Rule from "../rule";
|
|
4
|
+
import TreeTransformer from "../tree-transformer";
|
|
6
5
|
|
|
7
6
|
describe("PerseusLinter lint Rules class", () => {
|
|
8
7
|
const markdown = `
|
|
@@ -59,7 +58,7 @@ the previous heading was level ${previousHeading.level}`;
|
|
|
59
58
|
},
|
|
60
59
|
];
|
|
61
60
|
|
|
62
|
-
let rules = [];
|
|
61
|
+
let rules: Array<never> | Array<Rule | any> = [];
|
|
63
62
|
|
|
64
63
|
function parseTree() {
|
|
65
64
|
return PureMarkdown.parse(markdown);
|
|
@@ -74,7 +73,16 @@ the previous heading was level ${previousHeading.level}`;
|
|
|
74
73
|
it("check() method", () => {
|
|
75
74
|
const tree = parseTree();
|
|
76
75
|
const tt = new TreeTransformer(tree);
|
|
77
|
-
const warnings
|
|
76
|
+
const warnings: Array<
|
|
77
|
+
| any
|
|
78
|
+
| {
|
|
79
|
+
end: number;
|
|
80
|
+
message: string;
|
|
81
|
+
rule: string;
|
|
82
|
+
severity?: number;
|
|
83
|
+
start: number;
|
|
84
|
+
}
|
|
85
|
+
> = [];
|
|
78
86
|
|
|
79
87
|
tt.traverse((node, state, content) => {
|
|
80
88
|
rules.forEach((r) => {
|