@khanacademy/perseus-linter 0.2.5 → 0.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/.eslintrc.js +1 -0
- package/CHANGELOG.md +11 -0
- package/dist/es/index.js +277 -407
- 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
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import TreeTransformer from "./tree-transformer.js";
|
|
1
|
+
import Rule from "./rule";
|
|
2
|
+
import AllRules from "./rules/all-rules";
|
|
3
|
+
import TreeTransformer from "./tree-transformer";
|
|
5
4
|
|
|
6
|
-
export {linterContextProps, linterContextDefault} from "./proptypes
|
|
7
|
-
export type {LinterContextProps} from "./types
|
|
5
|
+
export {linterContextProps, linterContextDefault} from "./proptypes";
|
|
6
|
+
export type {LinterContextProps} from "./types";
|
|
8
7
|
|
|
9
|
-
const allLintRules:
|
|
8
|
+
const allLintRules: ReadonlyArray<any> = AllRules.filter(
|
|
10
9
|
(r) => r.severity < Rule.Severity.BULK_WARNING,
|
|
11
10
|
);
|
|
12
11
|
|
|
@@ -37,12 +36,12 @@ export {Rule, allLintRules as rules};
|
|
|
37
36
|
// online linting and batch linting.
|
|
38
37
|
//
|
|
39
38
|
export function runLinter(
|
|
40
|
-
tree:
|
|
41
|
-
context:
|
|
39
|
+
tree: any,
|
|
40
|
+
context: any,
|
|
42
41
|
highlight: boolean,
|
|
43
|
-
rules
|
|
44
|
-
):
|
|
45
|
-
const warnings = [];
|
|
42
|
+
rules: ReadonlyArray<any> = allLintRules,
|
|
43
|
+
): ReadonlyArray<any> {
|
|
44
|
+
const warnings: Array<any> = [];
|
|
46
45
|
const tt = new TreeTransformer(tree);
|
|
47
46
|
|
|
48
47
|
// The markdown parser often outputs adjacent text nodes. We
|
|
@@ -51,8 +50,7 @@ export function runLinter(
|
|
|
51
50
|
if (TreeTransformer.isTextNode(node)) {
|
|
52
51
|
let next = state.nextSibling();
|
|
53
52
|
while (TreeTransformer.isTextNode(next)) {
|
|
54
|
-
//
|
|
55
|
-
// $FlowFixMe[incompatible-use]
|
|
53
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'content' does not exist on type 'TreeNode'. | TS2533 - Object is possibly 'null' or 'undefined'. | TS2339 - Property 'content' does not exist on type 'TreeNode'.
|
|
56
54
|
node.content += next.content;
|
|
57
55
|
state.removeNextSibling();
|
|
58
56
|
next = state.nextSibling();
|
|
@@ -77,14 +75,14 @@ export function runLinter(
|
|
|
77
75
|
// issue too. But using JavaScript has its own downsides: there is
|
|
78
76
|
// risk that the linter JavaScript would interfere with
|
|
79
77
|
// widget-related Javascript.
|
|
80
|
-
let tableWarnings = [];
|
|
78
|
+
let tableWarnings: Array<never> = [];
|
|
81
79
|
let insideTable = false;
|
|
82
80
|
|
|
83
81
|
// Traverse through the nodes of the parse tree. At each node, loop
|
|
84
82
|
// through the array of lint rules and check whether there is a
|
|
85
83
|
// lint violation at that node.
|
|
86
84
|
tt.traverse((node, state, content) => {
|
|
87
|
-
const nodeWarnings = [];
|
|
85
|
+
const nodeWarnings: Array<any> = [];
|
|
88
86
|
|
|
89
87
|
// If our rule is only designed to be tested against a particular
|
|
90
88
|
// content type and we're not in that content type, we don't need to
|
|
@@ -99,7 +97,7 @@ export function runLinter(
|
|
|
99
97
|
const nodeContext = {
|
|
100
98
|
...context,
|
|
101
99
|
stack: stack.join("."),
|
|
102
|
-
};
|
|
100
|
+
} as const;
|
|
103
101
|
|
|
104
102
|
applicableRules.forEach((rule) => {
|
|
105
103
|
const warning = rule.check(node, state, content, nodeContext);
|
|
@@ -159,6 +157,7 @@ export function runLinter(
|
|
|
159
157
|
// this node, then we need to save the warnings for display
|
|
160
158
|
// on the table itself
|
|
161
159
|
if (insideTable && nodeWarnings.length) {
|
|
160
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
162
161
|
tableWarnings.push(...nodeWarnings);
|
|
163
162
|
}
|
|
164
163
|
|
|
@@ -186,6 +185,7 @@ export function runLinter(
|
|
|
186
185
|
// node under a new lint node and put the warnings there.
|
|
187
186
|
state.replace({
|
|
188
187
|
type: "lint",
|
|
188
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type '{ type: string; content: TreeNode; message: string; ruleName: any; blockHighlight: any; insideTable: boolean; severity: any; }' is not assignable to parameter of type 'TreeNode'.
|
|
189
189
|
content: node,
|
|
190
190
|
message: nodeWarnings.map((w) => w.message).join("\n\n"),
|
|
191
191
|
ruleName: nodeWarnings[0].rule,
|
|
@@ -219,7 +219,7 @@ export function runLinter(
|
|
|
219
219
|
// single line, so keeping them combined in that case might
|
|
220
220
|
// be the best thing, anyway.
|
|
221
221
|
//
|
|
222
|
-
//
|
|
222
|
+
// @ts-expect-error [FEI-5003] - TS2339 - Property 'content' does not exist on type 'TreeNode'.
|
|
223
223
|
const content = node.content; // Text nodes have content
|
|
224
224
|
const warning = nodeWarnings[0]; // There is only one warning.
|
|
225
225
|
// These are the lint boundaries within the content
|
|
@@ -228,7 +228,8 @@ export function runLinter(
|
|
|
228
228
|
const prefix = content.substring(0, start);
|
|
229
229
|
const lint = content.substring(start, end);
|
|
230
230
|
const suffix = content.substring(end);
|
|
231
|
-
|
|
231
|
+
// TODO(FEI-5003): Give this a real type.
|
|
232
|
+
const replacements: any[] = []; // What we'll replace the node with
|
|
232
233
|
|
|
233
234
|
// The prefix text node, if there is one
|
|
234
235
|
if (prefix) {
|
|
@@ -269,10 +270,7 @@ export function runLinter(
|
|
|
269
270
|
return warnings;
|
|
270
271
|
}
|
|
271
272
|
|
|
272
|
-
export function pushContextStack(
|
|
273
|
-
context: $FlowFixMe,
|
|
274
|
-
name: string,
|
|
275
|
-
): $FlowFixMe {
|
|
273
|
+
export function pushContextStack(context: any, name: string): any {
|
|
276
274
|
const stack = context.stack || [];
|
|
277
275
|
return {
|
|
278
276
|
...context,
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
// Define the shape of the linter context object that is passed through the
|
|
3
2
|
// tree with additional information about what we are checking.
|
|
4
|
-
|
|
5
3
|
import PropTypes from "prop-types";
|
|
6
4
|
|
|
7
|
-
import type {LinterContextProps} from "./types
|
|
5
|
+
import type {LinterContextProps} from "./types";
|
|
8
6
|
|
|
9
|
-
export const linterContextProps
|
|
10
|
-
contentType?: string,
|
|
11
|
-
highlightLint?: boolean,
|
|
12
|
-
// eslint-disable-next-line ft-flow/no-mutable-array
|
|
13
|
-
paths?: Array<string>,
|
|
14
|
-
// eslint-disable-next-line ft-flow/no-mutable-array
|
|
15
|
-
stack?: Array<string>,
|
|
16
|
-
...
|
|
17
|
-
}> = PropTypes.shape({
|
|
7
|
+
export const linterContextProps = PropTypes.shape({
|
|
18
8
|
contentType: PropTypes.string,
|
|
19
9
|
highlightLint: PropTypes.bool,
|
|
20
10
|
paths: PropTypes.arrayOf(PropTypes.string),
|
|
@@ -24,6 +14,6 @@ export const linterContextProps: React$PropType$Primitive<{
|
|
|
24
14
|
export const linterContextDefault: LinterContextProps = {
|
|
25
15
|
contentType: "",
|
|
26
16
|
highlightLint: false,
|
|
27
|
-
paths:
|
|
28
|
-
stack:
|
|
17
|
+
paths: [] as ReadonlyArray<any>,
|
|
18
|
+
stack: [] as ReadonlyArray<any>,
|
|
29
19
|
};
|
package/src/{rule.js → rule.ts}
RENAMED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
/**
|
|
3
2
|
* The Rule class represents a Perseus lint rule. A Rule instance has a check()
|
|
4
3
|
* method that takes the same (node, state, content) arguments that a
|
|
@@ -124,9 +123,9 @@
|
|
|
124
123
|
|
|
125
124
|
import {Errors, PerseusError} from "@khanacademy/perseus-error";
|
|
126
125
|
|
|
127
|
-
import Selector from "./selector
|
|
126
|
+
import Selector from "./selector";
|
|
128
127
|
|
|
129
|
-
import type {TraversalState, TreeNode} from "./tree-transformer
|
|
128
|
+
import type {TraversalState, TreeNode} from "./tree-transformer";
|
|
130
129
|
|
|
131
130
|
// This represents the type returned by String.match(). It is an
|
|
132
131
|
// array of strings, but also has index:number and input:string properties.
|
|
@@ -134,28 +133,29 @@ import type {TraversalState, TreeNode} from "./tree-transformer.js";
|
|
|
134
133
|
export type PatternMatchType = any;
|
|
135
134
|
|
|
136
135
|
// This is the return type of the check() method of a Rule object
|
|
137
|
-
export type RuleCheckReturnType =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
136
|
+
export type RuleCheckReturnType =
|
|
137
|
+
| {
|
|
138
|
+
rule: string;
|
|
139
|
+
message: string;
|
|
140
|
+
start: number;
|
|
141
|
+
end: number;
|
|
142
|
+
severity?: number;
|
|
143
|
+
}
|
|
144
|
+
| null
|
|
145
|
+
| undefined;
|
|
144
146
|
|
|
145
147
|
// This is the return type of the lint detection function passed as the 4th
|
|
146
148
|
// argument to the Rule() constructor. It can return null or a string or an
|
|
147
149
|
// object containing a string and two numbers.
|
|
148
150
|
// prettier-ignore
|
|
149
151
|
// (prettier formats this in a way that ka-lint does not like)
|
|
150
|
-
export type LintTesterReturnType =
|
|
151
|
-
string
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
end: number,
|
|
156
|
-
|});
|
|
152
|
+
export type LintTesterReturnType = string | {
|
|
153
|
+
message: string,
|
|
154
|
+
start: number,
|
|
155
|
+
end: number
|
|
156
|
+
} | null | undefined;
|
|
157
157
|
|
|
158
|
-
export type LintRuleContextObject =
|
|
158
|
+
export type LintRuleContextObject = any | null | undefined;
|
|
159
159
|
|
|
160
160
|
// This is the type of the lint detection function that the Rule() constructor
|
|
161
161
|
// expects as its fourth argument. It is passed the TraversalState object and
|
|
@@ -166,7 +166,7 @@ export type LintRuleContextObject = ?Object;
|
|
|
166
166
|
export type LintTester = (
|
|
167
167
|
state: TraversalState,
|
|
168
168
|
content: string,
|
|
169
|
-
selectorMatch:
|
|
169
|
+
selectorMatch: ReadonlyArray<TreeNode>,
|
|
170
170
|
patternMatch: PatternMatchType,
|
|
171
171
|
context: LintRuleContextObject,
|
|
172
172
|
) => LintTesterReturnType;
|
|
@@ -185,19 +185,19 @@ export default class Rule {
|
|
|
185
185
|
name: string; // The name of the rule
|
|
186
186
|
severity: number; // The severity of the rule
|
|
187
187
|
selector: Selector; // The specified selector or the DEFAULT_SELECTOR
|
|
188
|
-
pattern:
|
|
188
|
+
pattern: RegExp | null | undefined; // A regular expression if one was specified
|
|
189
189
|
lint: LintTester; // The lint-testing function or a default
|
|
190
190
|
applies: AppliesTester; // Checks to see if we should apply a rule or not
|
|
191
|
-
message:
|
|
191
|
+
message: string | null | undefined; // The error message for use with the default function
|
|
192
192
|
static DEFAULT_SELECTOR: Selector;
|
|
193
193
|
|
|
194
194
|
// The comment at the top of this file has detailed docs for
|
|
195
195
|
// this constructor and its arguments
|
|
196
196
|
constructor(
|
|
197
|
-
name:
|
|
198
|
-
severity:
|
|
199
|
-
selector:
|
|
200
|
-
pattern:
|
|
197
|
+
name: string | null | undefined,
|
|
198
|
+
severity: number | null | undefined,
|
|
199
|
+
selector: Selector | null | undefined,
|
|
200
|
+
pattern: RegExp | null | undefined,
|
|
201
201
|
lint: LintTester | string,
|
|
202
202
|
applies: AppliesTester,
|
|
203
203
|
) {
|
|
@@ -233,7 +233,7 @@ export default class Rule {
|
|
|
233
233
|
|
|
234
234
|
// A factory method for use with rules described in JSON files
|
|
235
235
|
// See the documentation at the start of this file for details.
|
|
236
|
-
static makeRule(options:
|
|
236
|
+
static makeRule(options: any): Rule {
|
|
237
237
|
return new Rule(
|
|
238
238
|
options.name,
|
|
239
239
|
options.severity,
|
|
@@ -312,7 +312,7 @@ export default class Rule {
|
|
|
312
312
|
start: error.start,
|
|
313
313
|
end: error.end,
|
|
314
314
|
};
|
|
315
|
-
} catch (e) {
|
|
315
|
+
} catch (e: any) {
|
|
316
316
|
// If the lint function threw an exception we handle that as
|
|
317
317
|
// a special type of lint. We want the user to see the lint
|
|
318
318
|
// warning in this case (even though it is out of their control)
|
|
@@ -339,10 +339,14 @@ ${e.stack}`,
|
|
|
339
339
|
_defaultLintFunction(
|
|
340
340
|
state: TraversalState,
|
|
341
341
|
content: string,
|
|
342
|
-
selectorMatch:
|
|
342
|
+
selectorMatch: ReadonlyArray<TreeNode>,
|
|
343
343
|
patternMatch: PatternMatchType,
|
|
344
344
|
context: LintRuleContextObject,
|
|
345
|
-
): {
|
|
345
|
+
): {
|
|
346
|
+
end: number;
|
|
347
|
+
message: string;
|
|
348
|
+
start: number;
|
|
349
|
+
} {
|
|
346
350
|
return {
|
|
347
351
|
message: this.message || "",
|
|
348
352
|
start: patternMatch.index,
|
|
@@ -365,7 +369,9 @@ ${e.stack}`,
|
|
|
365
369
|
// input "foo" ==> output /foo/
|
|
366
370
|
// input "/foo/i" ==> output /foo/i
|
|
367
371
|
//
|
|
368
|
-
static makePattern(
|
|
372
|
+
static makePattern(
|
|
373
|
+
pattern?: RegExp | string | null,
|
|
374
|
+
): RegExp | null | undefined {
|
|
369
375
|
if (!pattern) {
|
|
370
376
|
return null;
|
|
371
377
|
}
|
|
@@ -376,7 +382,8 @@ ${e.stack}`,
|
|
|
376
382
|
const lastSlash = pattern.lastIndexOf("/");
|
|
377
383
|
const expression = pattern.substring(1, lastSlash);
|
|
378
384
|
const flags = pattern.substring(lastSlash + 1);
|
|
379
|
-
|
|
385
|
+
// @ts-expect-error [FEI-5003] - TS2713 - Cannot access 'RegExp.flags' because 'RegExp' is a type, but not a namespace. Did you mean to retrieve the type of the property 'flags' in 'RegExp' with 'RegExp["flags"]'?
|
|
386
|
+
return new RegExp(expression, flags as RegExp.flags);
|
|
380
387
|
}
|
|
381
388
|
return new RegExp(pattern);
|
|
382
389
|
}
|
|
@@ -387,7 +394,7 @@ ${e.stack}`,
|
|
|
387
394
|
// want to simulate a match on the entire content string.
|
|
388
395
|
static FakePatternMatch(
|
|
389
396
|
input: string,
|
|
390
|
-
match:
|
|
397
|
+
match: string | null | undefined,
|
|
391
398
|
index: number,
|
|
392
399
|
): PatternMatchType {
|
|
393
400
|
const result: any = [match];
|
|
@@ -396,12 +403,12 @@ ${e.stack}`,
|
|
|
396
403
|
return result;
|
|
397
404
|
}
|
|
398
405
|
|
|
399
|
-
static Severity: {
|
|
400
|
-
BULK_WARNING: number
|
|
401
|
-
ERROR: number
|
|
402
|
-
GUIDELINE: number
|
|
403
|
-
WARNING: number
|
|
404
|
-
|
|
406
|
+
static Severity: {
|
|
407
|
+
BULK_WARNING: number;
|
|
408
|
+
ERROR: number;
|
|
409
|
+
GUIDELINE: number;
|
|
410
|
+
WARNING: number;
|
|
411
|
+
} = {
|
|
405
412
|
ERROR: 1,
|
|
406
413
|
WARNING: 2,
|
|
407
414
|
GUIDELINE: 3,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
import {getHostname} from "./lint-utils
|
|
3
|
+
import {getHostname} from "./lint-utils";
|
|
5
4
|
|
|
6
|
-
export default
|
|
5
|
+
export default Rule.makeRule({
|
|
7
6
|
name: "absolute-url",
|
|
8
7
|
severity: Rule.Severity.GUIDELINE,
|
|
9
8
|
selector: "link, image",
|
|
@@ -21,4 +20,4 @@ https://www.khanacademy.org URL prefix.
|
|
|
21
20
|
Use a relative URL beginning with / instead.`;
|
|
22
21
|
}
|
|
23
22
|
},
|
|
24
|
-
})
|
|
23
|
+
}) as Rule;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// TODO(davidflanagan):
|
|
2
|
+
// This should probably be converted to use import and to export
|
|
3
|
+
// and object that maps rule names to rules. Also, maybe this should
|
|
4
|
+
// be an auto-generated file with a script that updates it any time
|
|
5
|
+
// we add a new rule?
|
|
6
|
+
|
|
7
|
+
import AbsoluteUrl from "./absolute-url";
|
|
8
|
+
import BlockquotedMath from "./blockquoted-math";
|
|
9
|
+
import BlockquotedWidget from "./blockquoted-widget";
|
|
10
|
+
import DoubleSpacingAfterTerminal from "./double-spacing-after-terminal";
|
|
11
|
+
import ExtraContentSpacing from "./extra-content-spacing";
|
|
12
|
+
import HeadingLevel1 from "./heading-level-1";
|
|
13
|
+
import HeadingLevelSkip from "./heading-level-skip";
|
|
14
|
+
import HeadingSentenceCase from "./heading-sentence-case";
|
|
15
|
+
import HeadingTitleCase from "./heading-title-case";
|
|
16
|
+
import ImageAltText from "./image-alt-text";
|
|
17
|
+
import ImageInTable from "./image-in-table";
|
|
18
|
+
import ImageSpacesAroundUrls from "./image-spaces-around-urls";
|
|
19
|
+
import ImageWidget from "./image-widget";
|
|
20
|
+
import LinkClickHere from "./link-click-here";
|
|
21
|
+
import LongParagraph from "./long-paragraph";
|
|
22
|
+
import MathAdjacent from "./math-adjacent";
|
|
23
|
+
import MathAlignExtraBreak from "./math-align-extra-break";
|
|
24
|
+
import MathAlignLinebreaks from "./math-align-linebreaks";
|
|
25
|
+
import MathEmpty from "./math-empty";
|
|
26
|
+
import MathFontSize from "./math-font-size";
|
|
27
|
+
import MathFrac from "./math-frac";
|
|
28
|
+
import MathNested from "./math-nested";
|
|
29
|
+
import MathStartsWithSpace from "./math-starts-with-space";
|
|
30
|
+
import MathTextEmpty from "./math-text-empty";
|
|
31
|
+
import MathWithoutDollars from "./math-without-dollars";
|
|
32
|
+
import NestedLists from "./nested-lists";
|
|
33
|
+
import Profanity from "./profanity";
|
|
34
|
+
import TableMissingCells from "./table-missing-cells";
|
|
35
|
+
import UnbalancedCodeDelimiters from "./unbalanced-code-delimiters";
|
|
36
|
+
import UnescapedDollar from "./unescaped-dollar";
|
|
37
|
+
import WidgetInTable from "./widget-in-table";
|
|
38
|
+
|
|
39
|
+
export default [
|
|
40
|
+
AbsoluteUrl,
|
|
41
|
+
BlockquotedMath,
|
|
42
|
+
BlockquotedWidget,
|
|
43
|
+
DoubleSpacingAfterTerminal,
|
|
44
|
+
ExtraContentSpacing,
|
|
45
|
+
HeadingLevel1,
|
|
46
|
+
HeadingLevelSkip,
|
|
47
|
+
HeadingSentenceCase,
|
|
48
|
+
HeadingTitleCase,
|
|
49
|
+
ImageAltText,
|
|
50
|
+
ImageInTable,
|
|
51
|
+
LinkClickHere,
|
|
52
|
+
LongParagraph,
|
|
53
|
+
MathAdjacent,
|
|
54
|
+
MathAlignExtraBreak,
|
|
55
|
+
MathAlignLinebreaks,
|
|
56
|
+
MathEmpty,
|
|
57
|
+
MathFontSize,
|
|
58
|
+
MathFrac,
|
|
59
|
+
MathNested,
|
|
60
|
+
MathStartsWithSpace,
|
|
61
|
+
MathTextEmpty,
|
|
62
|
+
NestedLists,
|
|
63
|
+
TableMissingCells,
|
|
64
|
+
UnescapedDollar,
|
|
65
|
+
WidgetInTable,
|
|
66
|
+
Profanity,
|
|
67
|
+
MathWithoutDollars,
|
|
68
|
+
UnbalancedCodeDelimiters,
|
|
69
|
+
ImageSpacesAroundUrls,
|
|
70
|
+
ImageWidget,
|
|
71
|
+
];
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "blockquoted-math",
|
|
6
5
|
severity: Rule.Severity.WARNING,
|
|
7
6
|
selector: "blockQuote math, blockQuote blockMath",
|
|
8
7
|
message: `Blockquoted math:
|
|
9
8
|
math should not be indented.`,
|
|
10
|
-
})
|
|
9
|
+
}) as Rule;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "blockquoted-widget",
|
|
6
5
|
severity: Rule.Severity.WARNING,
|
|
7
6
|
selector: "blockQuote widget",
|
|
8
7
|
message: `Blockquoted widget:
|
|
9
8
|
widgets should not be indented.`,
|
|
10
|
-
})
|
|
9
|
+
}) as Rule;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/* eslint-disable no-useless-escape */
|
|
2
|
-
|
|
3
|
-
import Rule from "../rule.js";
|
|
2
|
+
import Rule from "../rule";
|
|
4
3
|
|
|
5
|
-
export default
|
|
4
|
+
export default Rule.makeRule({
|
|
6
5
|
name: "double-spacing-after-terminal",
|
|
7
6
|
severity: Rule.Severity.BULK_WARNING,
|
|
8
7
|
selector: "paragraph",
|
|
9
8
|
pattern: /[.!\?] {2}/i,
|
|
10
9
|
message: `Use a single space after a sentence-ending period, or
|
|
11
10
|
any other kind of terminal punctuation.`,
|
|
12
|
-
})
|
|
11
|
+
}) as Rule;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "extra-content-spacing",
|
|
6
5
|
selector: "paragraph",
|
|
7
6
|
pattern: /\s+$/,
|
|
@@ -9,4 +8,4 @@ export default (Rule.makeRule({
|
|
|
9
8
|
return context.contentType === "article";
|
|
10
9
|
},
|
|
11
10
|
message: `No extra whitespace at the end of content blocks.`,
|
|
12
|
-
})
|
|
11
|
+
}) as Rule;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "heading-level-1",
|
|
6
5
|
severity: Rule.Severity.WARNING,
|
|
7
6
|
selector: "heading",
|
|
@@ -11,4 +10,4 @@ export default (Rule.makeRule({
|
|
|
11
10
|
Begin headings with two or more # characters.`;
|
|
12
11
|
}
|
|
13
12
|
},
|
|
14
|
-
})
|
|
13
|
+
}) as Rule;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "heading-level-skip",
|
|
6
5
|
severity: Rule.Severity.WARNING,
|
|
7
6
|
selector: "heading ~ heading",
|
|
@@ -17,4 +16,4 @@ this heading is level ${currentHeading.level} but
|
|
|
17
16
|
the previous heading was level ${previousHeading.level}`;
|
|
18
17
|
}
|
|
19
18
|
},
|
|
20
|
-
})
|
|
19
|
+
}) as Rule;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "heading-sentence-case",
|
|
6
5
|
severity: Rule.Severity.GUIDELINE,
|
|
7
6
|
selector: "heading",
|
|
8
7
|
pattern: /^\W*[a-z]/, // first letter is lowercase
|
|
9
8
|
message: `First letter is lowercase:
|
|
10
9
|
the first letter of a heading should be capitalized.`,
|
|
11
|
-
})
|
|
10
|
+
}) as Rule;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
3
|
// These are 3-letter and longer words that we would not expect to be
|
|
5
4
|
// capitalized even in a title-case heading. See
|
|
6
5
|
// http://blog.apastyle.org/apastyle/2012/03/title-case-and-sentence-case-capitalization-in-apa-style.html
|
|
7
|
-
const littleWords = {
|
|
6
|
+
const littleWords = {
|
|
7
|
+
and: true,
|
|
8
|
+
nor: true,
|
|
9
|
+
but: true,
|
|
10
|
+
the: true,
|
|
11
|
+
for: true,
|
|
12
|
+
} as const;
|
|
8
13
|
|
|
9
|
-
function isCapitalized(word) {
|
|
14
|
+
function isCapitalized(word: any) {
|
|
10
15
|
const c = word[0];
|
|
11
16
|
return c === c.toUpperCase();
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
export default
|
|
19
|
+
export default Rule.makeRule({
|
|
15
20
|
name: "heading-title-case",
|
|
16
21
|
severity: Rule.Severity.GUIDELINE,
|
|
17
22
|
selector: "heading",
|
|
@@ -60,4 +65,4 @@ This heading appears to be in title-case, but should be sentence-case.
|
|
|
60
65
|
Only capitalize the first letter and proper nouns.`;
|
|
61
66
|
}
|
|
62
67
|
},
|
|
63
|
-
})
|
|
68
|
+
}) as Rule;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "image-alt-text",
|
|
6
5
|
severity: Rule.Severity.WARNING,
|
|
7
6
|
selector: "image",
|
|
@@ -18,4 +17,4 @@ for accessibility, all images should have descriptive alt text.
|
|
|
18
17
|
This image's alt text is only ${image.alt.length} characters long.`;
|
|
19
18
|
}
|
|
20
19
|
},
|
|
21
|
-
})
|
|
20
|
+
}) as Rule;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "image-in-table",
|
|
6
5
|
severity: Rule.Severity.BULK_WARNING,
|
|
7
6
|
selector: "table image",
|
|
8
7
|
message: `Image in table:
|
|
9
8
|
do not put images inside of tables.`,
|
|
10
|
-
})
|
|
9
|
+
}) as Rule;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "image-spaces-around-urls",
|
|
6
5
|
severity: Rule.Severity.ERROR,
|
|
7
6
|
selector: "image",
|
|
@@ -32,4 +31,4 @@ Whitespace in image URLs causes translation difficulties.`;
|
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
},
|
|
35
|
-
})
|
|
34
|
+
}) as Rule;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
3
|
// Normally we have one rule per file. But since our selector class
|
|
5
4
|
// can't match specific widget types directly, this rule implements
|
|
@@ -7,7 +6,7 @@ import Rule from "../rule.js";
|
|
|
7
6
|
// slightly increase efficiency, but it means that if there is more
|
|
8
7
|
// than one problem with an image widget, the user will only see one
|
|
9
8
|
// problem at a time.
|
|
10
|
-
export default
|
|
9
|
+
export default Rule.makeRule({
|
|
11
10
|
name: "image-widget",
|
|
12
11
|
severity: Rule.Severity.WARNING,
|
|
13
12
|
selector: "widget",
|
|
@@ -47,4 +46,4 @@ This image's alt text is only ${alt.trim().length} characters long.`;
|
|
|
47
46
|
Don't include math expressions in image captions.`;
|
|
48
47
|
}
|
|
49
48
|
},
|
|
50
|
-
})
|
|
49
|
+
}) as Rule;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import Rule from "../rule.js";
|
|
1
|
+
import Rule from "../rule";
|
|
3
2
|
|
|
4
|
-
export default
|
|
3
|
+
export default Rule.makeRule({
|
|
5
4
|
name: "link-click-here",
|
|
6
5
|
severity: Rule.Severity.WARNING,
|
|
7
6
|
selector: "link",
|
|
8
7
|
pattern: /click here/i,
|
|
9
8
|
message: `Inappropriate link text:
|
|
10
9
|
Do not use the words "click here" in links.`,
|
|
11
|
-
})
|
|
10
|
+
}) as Rule;
|