@khanacademy/perseus-linter 0.2.4 → 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.
Files changed (133) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +18 -0
  3. package/dist/es/index.js +277 -407
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.d.ts +7 -2
  6. package/dist/index.js +281 -398
  7. package/dist/index.js.flow +18 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/proptypes.d.ts +9 -0
  10. package/dist/proptypes.js.flow +17 -0
  11. package/dist/rule.d.ts +170 -0
  12. package/dist/rule.js.flow +86 -0
  13. package/dist/rules/absolute-url.d.ts +3 -0
  14. package/dist/rules/absolute-url.js.flow +9 -0
  15. package/dist/rules/all-rules.d.ts +2 -0
  16. package/dist/rules/all-rules.js.flow +9 -0
  17. package/dist/rules/blockquoted-math.d.ts +3 -0
  18. package/dist/rules/blockquoted-math.js.flow +9 -0
  19. package/dist/rules/blockquoted-widget.d.ts +3 -0
  20. package/dist/rules/blockquoted-widget.js.flow +9 -0
  21. package/dist/rules/double-spacing-after-terminal.d.ts +3 -0
  22. package/dist/rules/double-spacing-after-terminal.js.flow +9 -0
  23. package/dist/rules/extra-content-spacing.d.ts +3 -0
  24. package/dist/rules/extra-content-spacing.js.flow +9 -0
  25. package/dist/rules/heading-level-1.d.ts +3 -0
  26. package/dist/rules/heading-level-1.js.flow +9 -0
  27. package/dist/rules/heading-level-skip.d.ts +3 -0
  28. package/dist/rules/heading-level-skip.js.flow +9 -0
  29. package/dist/rules/heading-sentence-case.d.ts +3 -0
  30. package/dist/rules/heading-sentence-case.js.flow +9 -0
  31. package/dist/rules/heading-title-case.d.ts +3 -0
  32. package/dist/rules/heading-title-case.js.flow +9 -0
  33. package/dist/rules/image-alt-text.d.ts +3 -0
  34. package/dist/rules/image-alt-text.js.flow +9 -0
  35. package/dist/rules/image-in-table.d.ts +3 -0
  36. package/dist/rules/image-in-table.js.flow +9 -0
  37. package/dist/rules/image-spaces-around-urls.d.ts +3 -0
  38. package/dist/rules/image-spaces-around-urls.js.flow +9 -0
  39. package/dist/rules/image-widget.d.ts +3 -0
  40. package/dist/rules/image-widget.js.flow +9 -0
  41. package/dist/rules/link-click-here.d.ts +3 -0
  42. package/dist/rules/link-click-here.js.flow +9 -0
  43. package/dist/rules/lint-utils.d.ts +2 -0
  44. package/dist/rules/lint-utils.js.flow +8 -0
  45. package/dist/rules/long-paragraph.d.ts +3 -0
  46. package/dist/rules/long-paragraph.js.flow +9 -0
  47. package/dist/rules/math-adjacent.d.ts +3 -0
  48. package/dist/rules/math-adjacent.js.flow +9 -0
  49. package/dist/rules/math-align-extra-break.d.ts +3 -0
  50. package/dist/rules/math-align-extra-break.js.flow +9 -0
  51. package/dist/rules/math-align-linebreaks.d.ts +3 -0
  52. package/dist/rules/math-align-linebreaks.js.flow +9 -0
  53. package/dist/rules/math-empty.d.ts +3 -0
  54. package/dist/rules/math-empty.js.flow +9 -0
  55. package/dist/rules/math-font-size.d.ts +3 -0
  56. package/dist/rules/math-font-size.js.flow +9 -0
  57. package/dist/rules/math-frac.d.ts +3 -0
  58. package/dist/rules/math-frac.js.flow +9 -0
  59. package/dist/rules/math-nested.d.ts +3 -0
  60. package/dist/rules/math-nested.js.flow +9 -0
  61. package/dist/rules/math-starts-with-space.d.ts +3 -0
  62. package/dist/rules/math-starts-with-space.js.flow +9 -0
  63. package/dist/rules/math-text-empty.d.ts +3 -0
  64. package/dist/rules/math-text-empty.js.flow +9 -0
  65. package/dist/rules/math-without-dollars.d.ts +3 -0
  66. package/dist/rules/math-without-dollars.js.flow +9 -0
  67. package/dist/rules/nested-lists.d.ts +3 -0
  68. package/dist/rules/nested-lists.js.flow +9 -0
  69. package/dist/rules/profanity.d.ts +3 -0
  70. package/dist/rules/profanity.js.flow +9 -0
  71. package/dist/rules/table-missing-cells.d.ts +3 -0
  72. package/dist/rules/table-missing-cells.js.flow +9 -0
  73. package/dist/rules/unbalanced-code-delimiters.d.ts +3 -0
  74. package/dist/rules/unbalanced-code-delimiters.js.flow +9 -0
  75. package/dist/rules/unescaped-dollar.d.ts +3 -0
  76. package/dist/rules/unescaped-dollar.js.flow +9 -0
  77. package/dist/rules/widget-in-table.d.ts +3 -0
  78. package/dist/rules/widget-in-table.js.flow +9 -0
  79. package/dist/selector.d.ts +108 -0
  80. package/dist/selector.js.flow +31 -0
  81. package/dist/tree-transformer.d.ts +205 -0
  82. package/dist/tree-transformer.js.flow +253 -0
  83. package/dist/types.d.ts +6 -0
  84. package/dist/types.js.flow +12 -0
  85. package/package.json +4 -4
  86. package/src/__tests__/{matcher_test.js → matcher.test.ts} +60 -60
  87. package/src/__tests__/{rule_test.js → rule.test.ts} +13 -5
  88. package/src/__tests__/{rules_test.js → rules.test.ts} +99 -39
  89. package/src/__tests__/{selector-parser_test.js → selector-parser.test.ts} +1 -2
  90. package/src/__tests__/{tree-transformer_test.js → tree-transformer.test.ts} +39 -41
  91. package/src/{index.js → index.ts} +21 -23
  92. package/src/{proptypes.js → proptypes.ts} +4 -14
  93. package/src/{rule.js → rule.ts} +45 -38
  94. package/src/rules/{absolute-url.js → absolute-url.ts} +4 -5
  95. package/src/rules/all-rules.ts +71 -0
  96. package/src/rules/{blockquoted-math.js → blockquoted-math.ts} +3 -4
  97. package/src/rules/{blockquoted-widget.js → blockquoted-widget.ts} +3 -4
  98. package/src/rules/{double-spacing-after-terminal.js → double-spacing-after-terminal.ts} +3 -4
  99. package/src/rules/{extra-content-spacing.js → extra-content-spacing.ts} +3 -4
  100. package/src/rules/{heading-level-1.js → heading-level-1.ts} +3 -4
  101. package/src/rules/{heading-level-skip.js → heading-level-skip.ts} +3 -4
  102. package/src/rules/{heading-sentence-case.js → heading-sentence-case.ts} +3 -4
  103. package/src/rules/{heading-title-case.js → heading-title-case.ts} +11 -6
  104. package/src/rules/{image-alt-text.js → image-alt-text.ts} +3 -4
  105. package/src/rules/{image-in-table.js → image-in-table.ts} +3 -4
  106. package/src/rules/{image-spaces-around-urls.js → image-spaces-around-urls.ts} +3 -4
  107. package/src/rules/{image-widget.js → image-widget.ts} +3 -4
  108. package/src/rules/{link-click-here.js → link-click-here.ts} +3 -4
  109. package/src/rules/{lint-utils.js → lint-utils.ts} +1 -2
  110. package/src/rules/{long-paragraph.js → long-paragraph.ts} +3 -4
  111. package/src/rules/{math-adjacent.js → math-adjacent.ts} +3 -4
  112. package/src/rules/{math-align-extra-break.js → math-align-extra-break.ts} +3 -4
  113. package/src/rules/{math-align-linebreaks.js → math-align-linebreaks.ts} +3 -4
  114. package/src/rules/{math-empty.js → math-empty.ts} +3 -4
  115. package/src/rules/{math-font-size.js → math-font-size.ts} +3 -4
  116. package/src/rules/{math-frac.js → math-frac.ts} +3 -4
  117. package/src/rules/{math-nested.js → math-nested.ts} +3 -4
  118. package/src/rules/{math-starts-with-space.js → math-starts-with-space.ts} +3 -4
  119. package/src/rules/{math-text-empty.js → math-text-empty.ts} +3 -4
  120. package/src/rules/{math-without-dollars.js → math-without-dollars.ts} +3 -4
  121. package/src/rules/{nested-lists.js → nested-lists.ts} +3 -4
  122. package/src/rules/{profanity.js → profanity.ts} +3 -4
  123. package/src/rules/{table-missing-cells.js → table-missing-cells.ts} +3 -4
  124. package/src/rules/{unbalanced-code-delimiters.js → unbalanced-code-delimiters.ts} +3 -4
  125. package/src/rules/{unescaped-dollar.js → unescaped-dollar.ts} +3 -4
  126. package/src/rules/{widget-in-table.js → widget-in-table.ts} +3 -4
  127. package/src/{selector.js → selector.ts} +12 -13
  128. package/src/{tree-transformer.js → tree-transformer.ts} +24 -24
  129. package/src/types.ts +7 -0
  130. package/tsconfig.json +12 -0
  131. package/tsconfig.tsbuildinfo +1 -0
  132. package/src/rules/all-rules.js +0 -72
  133. package/src/types.js +0 -10
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable no-useless-escape */
2
- // @flow
3
2
  // Return the portion of a URL between // and /. This is the authority
4
3
  // portion which is usually just the hostname, but may also include
5
4
  // a username, password or port. We don't strip those things out because
@@ -37,7 +36,7 @@ const internalDomains = {
37
36
  "ka-mobile.s3.amazonaws.com": true,
38
37
  "ka-perseus-graphie.s3.amazonaws.com": true,
39
38
  "ka-perseus-images.s3.amazonaws.com": true,
40
- };
39
+ } as const;
41
40
 
42
41
  // Returns true if this URL is relative, or if it is an absolute
43
42
  // URL with one of the domains listed above as its hostname.
@@ -1,7 +1,6 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "long-paragraph",
6
5
  severity: Rule.Severity.GUIDELINE,
7
6
  selector: "paragraph",
@@ -11,4 +10,4 @@ export default (Rule.makeRule({
11
10
  This paragraph is ${content.length} characters long.
12
11
  Shorten it to 500 characters or fewer.`;
13
12
  },
14
- }): Rule);
13
+ }) as Rule;
@@ -1,10 +1,9 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-adjacent",
6
5
  severity: Rule.Severity.WARNING,
7
6
  selector: "blockMath+blockMath",
8
7
  message: `Adjacent math blocks:
9
8
  combine the blocks between \\begin{align} and \\end{align}`,
10
- }): Rule);
9
+ }) as Rule;
@@ -1,11 +1,10 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-align-extra-break",
6
5
  severity: Rule.Severity.WARNING,
7
6
  selector: "blockMath",
8
7
  pattern: /(\\{2,})\s*\\end{align}/,
9
8
  message: `Extra space at end of block:
10
9
  Don't end an align block with backslashes`,
11
- }): Rule);
10
+ }) as Rule;
@@ -1,7 +1,6 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-align-linebreaks",
6
5
  severity: Rule.Severity.WARNING,
7
6
  selector: "blockMath",
@@ -40,4 +39,4 @@ export default (Rule.makeRule({
40
39
  text = text.substring(nextpair[0].length);
41
40
  }
42
41
  },
43
- }): Rule);
42
+ }) as Rule;
@@ -1,10 +1,9 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-empty",
6
5
  severity: Rule.Severity.WARNING,
7
6
  selector: "math, blockMath",
8
7
  pattern: /^$/,
9
8
  message: "Empty math: don't use $$ in your markdown.",
10
- }): Rule);
9
+ }) as Rule;
@@ -1,7 +1,6 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-font-size",
6
5
  severity: Rule.Severity.GUIDELINE,
7
6
  selector: "math, blockMath",
@@ -9,4 +8,4 @@ export default (Rule.makeRule({
9
8
  /\\(tiny|Tiny|small|large|Large|LARGE|huge|Huge|scriptsize|normalsize)\s*{/,
10
9
  message: `Math font size:
11
10
  Don't change the default font size with \\Large{} or similar commands`,
12
- }): Rule);
11
+ }) as Rule;
@@ -1,10 +1,9 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-frac",
6
5
  severity: Rule.Severity.GUIDELINE,
7
6
  selector: "math, blockMath",
8
7
  pattern: /\\frac[ {]/,
9
8
  message: "Use \\dfrac instead of \\frac in your math expressions.",
10
- }): Rule);
9
+ }) as Rule;
@@ -1,11 +1,10 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-nested",
6
5
  severity: Rule.Severity.ERROR,
7
6
  selector: "math, blockMath",
8
7
  pattern: /\\text{[^$}]*\$[^$}]*\$[^}]*}/,
9
8
  message: `Nested math:
10
9
  Don't nest math expressions inside \\text{} blocks`,
11
- }): Rule);
10
+ }) as Rule;
@@ -1,7 +1,6 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-starts-with-space",
6
5
  severity: Rule.Severity.GUIDELINE,
7
6
  selector: "math, blockMath",
@@ -9,4 +8,4 @@ export default (Rule.makeRule({
9
8
  message: `Math starts with space:
10
9
  math should not be indented. Do not begin math expressions with
11
10
  LaTeX space commands like ~, \\;, \\quad, or \\phantom`,
12
- }): Rule);
11
+ }) as Rule;
@@ -1,10 +1,9 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "math-text-empty",
6
5
  severity: Rule.Severity.WARNING,
7
6
  selector: "math, blockMath",
8
7
  pattern: /\\text{\s*}/,
9
8
  message: "Empty \\text{} block in math expression",
10
- }): Rule);
9
+ }) as Rule;
@@ -1,14 +1,13 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
3
  // Because no selector is specified, this rule only applies to text nodes.
5
4
  // Math and code hold their content directly and do not have text nodes
6
5
  // beneath them (unlike the HTML DOM) so this rule automatically does not
7
6
  // apply inside $$ or ``.
8
- export default (Rule.makeRule({
7
+ export default Rule.makeRule({
9
8
  name: "math-without-dollars",
10
9
  severity: Rule.Severity.GUIDELINE,
11
10
  pattern: /\\\w+{[^}]*}|{|}/,
12
11
  message: `This looks like LaTeX:
13
12
  did you mean to put it inside dollar signs?`,
14
- }): Rule);
13
+ }) as Rule;
@@ -1,11 +1,10 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "nested-lists",
6
5
  severity: Rule.Severity.WARNING,
7
6
  selector: "list list",
8
7
  message: `Nested lists:
9
8
  nested lists are hard to read on mobile devices;
10
9
  do not use additional indentation.`,
11
- }): Rule);
10
+ }) as Rule;
@@ -1,10 +1,9 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "profanity",
6
5
  // This list could obviously be expanded a lot, but I figured we
7
6
  // could start with https://en.wikipedia.org/wiki/Seven_dirty_words
8
7
  pattern: /\b(shit|piss|fuck|cunt|cocksucker|motherfucker|tits)\b/i,
9
8
  message: "Avoid profanity",
10
- }): Rule);
9
+ }) as Rule;
@@ -1,7 +1,6 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "table-missing-cells",
6
5
  severity: Rule.Severity.WARNING,
7
6
  selector: "table",
@@ -17,4 +16,4 @@ Row ${r + 1} has ${rowLengths[r]} cells.`;
17
16
  }
18
17
  }
19
18
  },
20
- }): Rule);
19
+ }) as Rule;
@@ -1,14 +1,13 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
3
  // Because no selector is specified, this rule only applies to text nodes.
5
4
  // Math and code hold their content directly and do not have text nodes
6
5
  // beneath them (unlike the HTML DOM) so this rule automatically does not
7
6
  // apply inside $$ or ``.
8
- export default (Rule.makeRule({
7
+ export default Rule.makeRule({
9
8
  name: "unbalanced-code-delimiters",
10
9
  severity: Rule.Severity.ERROR,
11
10
  pattern: /[`~]+/,
12
11
  message: `Unbalanced code delimiters:
13
12
  code blocks should begin and end with the same type and number of delimiters`,
14
- }): Rule);
13
+ }) as Rule;
@@ -1,10 +1,9 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "unescaped-dollar",
6
5
  severity: Rule.Severity.ERROR,
7
6
  selector: "unescapedDollar",
8
7
  message: `Unescaped dollar sign:
9
8
  Dollar signs must appear in pairs or be escaped as \\$`,
10
- }): Rule);
9
+ }) as Rule;
@@ -1,10 +1,9 @@
1
- // @flow
2
- import Rule from "../rule.js";
1
+ import Rule from "../rule";
3
2
 
4
- export default (Rule.makeRule({
3
+ export default Rule.makeRule({
5
4
  name: "widget-in-table",
6
5
  severity: Rule.Severity.BULK_WARNING,
7
6
  selector: "table widget",
8
7
  message: `Widget in table:
9
8
  do not put widgets inside of tables.`,
10
- }): Rule);
9
+ }) as Rule;
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable no-useless-escape */
2
- // @flow
3
2
  /**
4
3
  * The Selector class implements a CSS-like system for matching nodes in a
5
4
  * parse tree based on the structure of the tree. Create a Selector object by
@@ -89,7 +88,7 @@
89
88
 
90
89
  import {Errors, PerseusError} from "@khanacademy/perseus-error";
91
90
 
92
- import type {TreeNode, TraversalState} from "./tree-transformer.js";
91
+ import type {TreeNode, TraversalState} from "./tree-transformer";
93
92
 
94
93
  /**
95
94
  * This is the base class for all Selector types. The key method that all
@@ -108,7 +107,7 @@ export default class Selector {
108
107
  * This is the base class so we just throw an exception. All Selector
109
108
  * subclasses must provide an implementation of this method.
110
109
  */
111
- match(state: TraversalState): ?$ReadOnlyArray<TreeNode> {
110
+ match(state: TraversalState): ReadonlyArray<TreeNode> | null | undefined {
112
111
  throw new PerseusError(
113
112
  "Selector subclasses must implement match()",
114
113
  Errors.NotAllowed,
@@ -135,7 +134,7 @@ export default class Selector {
135
134
  */
136
135
  class Parser {
137
136
  static TOKENS: RegExp; // We do lexing with a simple regular expression
138
- tokens: $ReadOnlyArray<string>; // The array of tokens
137
+ tokens: ReadonlyArray<string>; // The array of tokens
139
138
  tokenIndex: number; // Which token in the array we're looking at now
140
139
 
141
140
  constructor(s: string) {
@@ -300,14 +299,14 @@ class ParseError extends Error {
300
299
  * first.
301
300
  */
302
301
  class SelectorList extends Selector {
303
- selectors: $ReadOnlyArray<Selector>;
302
+ selectors: ReadonlyArray<Selector>;
304
303
 
305
- constructor(selectors: $ReadOnlyArray<Selector>) {
304
+ constructor(selectors: ReadonlyArray<Selector>) {
306
305
  super();
307
306
  this.selectors = selectors;
308
307
  }
309
308
 
310
- match(state: TraversalState): ?$ReadOnlyArray<TreeNode> {
309
+ match(state: TraversalState): ReadonlyArray<TreeNode> | null | undefined {
311
310
  for (let i = 0; i < this.selectors.length; i++) {
312
311
  const s = this.selectors[i];
313
312
  const result = s.match(state);
@@ -333,7 +332,7 @@ class SelectorList extends Selector {
333
332
  * matches any node.
334
333
  */
335
334
  class AnyNode extends Selector {
336
- match(state: TraversalState): ?$ReadOnlyArray<TreeNode> {
335
+ match(state: TraversalState): ReadonlyArray<TreeNode> | null | undefined {
337
336
  return [state.currentNode()];
338
337
  }
339
338
 
@@ -354,7 +353,7 @@ class TypeSelector extends Selector {
354
353
  this.type = type;
355
354
  }
356
355
 
357
- match(state: TraversalState): ?$ReadOnlyArray<TreeNode> {
356
+ match(state: TraversalState): ReadonlyArray<TreeNode> | null | undefined {
358
357
  const node = state.currentNode();
359
358
  if (node.type === this.type) {
360
359
  return [node];
@@ -394,7 +393,7 @@ class AncestorCombinator extends SelectorCombinator {
394
393
  super(left, right);
395
394
  }
396
395
 
397
- match(state: TraversalState): ?$ReadOnlyArray<TreeNode> {
396
+ match(state: TraversalState): ReadonlyArray<TreeNode> | null | undefined {
398
397
  const rightResult = this.right.match(state);
399
398
  if (rightResult) {
400
399
  state = state.clone();
@@ -424,7 +423,7 @@ class ParentCombinator extends SelectorCombinator {
424
423
  super(left, right);
425
424
  }
426
425
 
427
- match(state: TraversalState): ?$ReadOnlyArray<TreeNode> {
426
+ match(state: TraversalState): ReadonlyArray<TreeNode> | null | undefined {
428
427
  const rightResult = this.right.match(state);
429
428
  if (rightResult) {
430
429
  if (state.hasParent()) {
@@ -454,7 +453,7 @@ class PreviousCombinator extends SelectorCombinator {
454
453
  super(left, right);
455
454
  }
456
455
 
457
- match(state: TraversalState): ?$ReadOnlyArray<TreeNode> {
456
+ match(state: TraversalState): ReadonlyArray<TreeNode> | null | undefined {
458
457
  const rightResult = this.right.match(state);
459
458
  if (rightResult) {
460
459
  if (state.hasPreviousSibling()) {
@@ -484,7 +483,7 @@ class SiblingCombinator extends SelectorCombinator {
484
483
  super(left, right);
485
484
  }
486
485
 
487
- match(state: TraversalState): ?$ReadOnlyArray<TreeNode> {
486
+ match(state: TraversalState): ReadonlyArray<TreeNode> | null | undefined {
488
487
  const rightResult = this.right.match(state);
489
488
  if (rightResult) {
490
489
  state = state.clone();
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  /**
3
2
  * TreeTransformer is a class for traversing and transforming trees. Create a
4
3
  * TreeTransformer by passing the root node of the tree to the
@@ -61,7 +60,9 @@ import {Errors, PerseusError} from "@khanacademy/perseus-error";
61
60
 
62
61
  // TreeNode is the type of a node in a parse tree. The only real requirement is
63
62
  // that every node has a string-valued `type` property
64
- export type TreeNode = {type: string, ...};
63
+ export type TreeNode = {
64
+ type: string;
65
+ };
65
66
 
66
67
  // TraversalCallback is the type of the callback function passed to the
67
68
  // traverse() method. It is invoked with node, state, and content arguments
@@ -117,7 +118,6 @@ export default class TreeTransformer {
117
118
  // details. Note that this method uses the TraversalState object to store
118
119
  // information about the structure of the tree.
119
120
  _traverse(
120
- // eslint-disable-next-line ft-flow/no-mutable-array
121
121
  n: TreeNode | Array<TreeNode>,
122
122
  state: TraversalState,
123
123
  f: TraversalCallback,
@@ -126,7 +126,7 @@ export default class TreeTransformer {
126
126
  if (TreeTransformer.isNode(n)) {
127
127
  // If we were called on a node object, then we handle it
128
128
  // this way.
129
- const node = ((n: any): TreeNode); // safe cast; we just tested
129
+ const node = n as TreeNode; // safe cast; we just tested
130
130
 
131
131
  // Put the node on the stack before recursing on its children
132
132
  state._containers.push(node);
@@ -137,8 +137,9 @@ export default class TreeTransformer {
137
137
  // but other nodes types like "math" may also have content.
138
138
  // TODO(mdr): We found a new Flow error when upgrading:
139
139
  // "node.content (property `content` is missing in `TreeNode` [1].)"
140
- // $FlowFixMe[prop-missing](0.57.3->0.75.0)
140
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'content' does not exist on type 'TreeNode'.
141
141
  if (typeof node.content === "string") {
142
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'content' does not exist on type 'TreeNode'.
142
143
  content = node.content;
143
144
  }
144
145
 
@@ -204,7 +205,7 @@ export default class TreeTransformer {
204
205
  state._indexes.push(index);
205
206
  content += this._traverse(nodes[index], state, f);
206
207
  // Casting to convince Flow that this is a number
207
- index = ((state._indexes.pop(): any): number) + 1;
208
+ index = (state._indexes.pop() as number) + 1;
208
209
  }
209
210
 
210
211
  // Pop the array off the stack. Note, however, that we do not call
@@ -242,8 +243,7 @@ export class TraversalState {
242
243
  // elements, depending on whether we just recursed on an array or on a
243
244
  // node. This is hard for Flow to deal with, so you'll see a number of
244
245
  // Flow casts through the any type when working with these two properties.
245
- _currentNode: ?TreeNode;
246
- // eslint-disable-next-line ft-flow/no-mutable-array
246
+ _currentNode: TreeNode | null | undefined;
247
247
  _containers: Stack<TreeNode | Array<TreeNode>>;
248
248
  _indexes: Stack<string | number>;
249
249
  _ancestors: Stack<TreeNode>;
@@ -288,7 +288,7 @@ export class TraversalState {
288
288
  /**
289
289
  * Return the parent of the current node, if there is one, or null.
290
290
  */
291
- parent(): ?TreeNode {
291
+ parent(): TreeNode | null | undefined {
292
292
  return this._ancestors.top();
293
293
  }
294
294
 
@@ -299,14 +299,14 @@ export class TraversalState {
299
299
  * This method makes a copy of the internal state, so modifications to the
300
300
  * returned array have no effect on the traversal.
301
301
  */
302
- ancestors(): $ReadOnlyArray<TreeNode> {
302
+ ancestors(): ReadonlyArray<TreeNode> {
303
303
  return this._ancestors.values();
304
304
  }
305
305
 
306
306
  /**
307
307
  * Return the next sibling of this node, if it has one, or null otherwise.
308
308
  */
309
- nextSibling(): ?TreeNode {
309
+ nextSibling(): TreeNode | null | undefined {
310
310
  const siblings = this._containers.top();
311
311
 
312
312
  // If we're at the root of the tree or if the parent is an
@@ -316,7 +316,7 @@ export class TraversalState {
316
316
  }
317
317
 
318
318
  // The top index is a number because the top container is an array
319
- const index = ((this._indexes.top(): any): number);
319
+ const index = this._indexes.top() as number;
320
320
  if (siblings.length > index + 1) {
321
321
  return siblings[index + 1];
322
322
  }
@@ -327,7 +327,7 @@ export class TraversalState {
327
327
  * Return the previous sibling of this node, if it has one, or null
328
328
  * otherwise.
329
329
  */
330
- previousSibling(): ?TreeNode {
330
+ previousSibling(): TreeNode | null | undefined {
331
331
  const siblings = this._containers.top();
332
332
 
333
333
  // If we're at the root of the tree or if the parent is an
@@ -337,7 +337,7 @@ export class TraversalState {
337
337
  }
338
338
 
339
339
  // The top index is a number because the top container is an array
340
- const index = ((this._indexes.top(): any): number);
340
+ const index = this._indexes.top() as number;
341
341
  if (index > 0) {
342
342
  return siblings[index - 1];
343
343
  }
@@ -349,11 +349,11 @@ export class TraversalState {
349
349
  * the removed sibling or null. This method makes it easy to traverse a
350
350
  * tree and concatenate adjacent text nodes into a single node.
351
351
  */
352
- removeNextSibling(): ?TreeNode {
352
+ removeNextSibling(): TreeNode | null | undefined {
353
353
  const siblings = this._containers.top();
354
354
  if (siblings && Array.isArray(siblings)) {
355
355
  // top index is a number because top container is an array
356
- const index = ((this._indexes.top(): any): number);
356
+ const index = this._indexes.top() as number;
357
357
  if (siblings.length > index + 1) {
358
358
  return siblings.splice(index + 1, 1)[0];
359
359
  }
@@ -372,7 +372,7 @@ export class TraversalState {
372
372
  * This method throws an error if you attempt to replace the root node of
373
373
  * the tree.
374
374
  */
375
- replace(...replacements: $ReadOnlyArray<TreeNode>): void {
375
+ replace(...replacements: ReadonlyArray<TreeNode>): void {
376
376
  const parent = this._containers.top();
377
377
  if (!parent) {
378
378
  throw new PerseusError(
@@ -386,7 +386,7 @@ export class TraversalState {
386
386
  // or object property. This is hard for Flow, so we have to do some
387
387
  // unsafe casting and be careful when we use which cast version
388
388
  if (Array.isArray(parent)) {
389
- const index = ((this._indexes.top(): any): number);
389
+ const index = this._indexes.top() as number;
390
390
  // For an array parent we just splice the new nodes in
391
391
  parent.splice(index, 1, ...replacements);
392
392
  // Adjust the index to account for the changed array length.
@@ -394,7 +394,7 @@ export class TraversalState {
394
394
  this._indexes.pop();
395
395
  this._indexes.push(index + replacements.length - 1);
396
396
  } else {
397
- const property = ((this._indexes.top(): any): string);
397
+ const property = this._indexes.top() as string;
398
398
  // For an object parent we care how many new nodes there are
399
399
  if (replacements.length === 0) {
400
400
  // Deletion
@@ -417,7 +417,7 @@ export class TraversalState {
417
417
  hasPreviousSibling(): boolean {
418
418
  return (
419
419
  Array.isArray(this._containers.top()) &&
420
- ((this._indexes.top(): any): number) > 0
420
+ (this._indexes.top() as number) > 0
421
421
  );
422
422
  }
423
423
 
@@ -441,7 +441,7 @@ export class TraversalState {
441
441
  // Since we know that we have a previous sibling, we know that
442
442
  // the value on top of the stack is a number, but we have to do
443
443
  // this unsafe cast because Flow doesn't know that.
444
- const index = ((this._indexes.pop(): any): number);
444
+ const index = this._indexes.pop() as number;
445
445
  this._indexes.push(index - 1);
446
446
  }
447
447
 
@@ -527,10 +527,9 @@ export class TraversalState {
527
527
  * the TraversalState class simpler in a number of places.
528
528
  */
529
529
  class Stack<T> {
530
- // eslint-disable-next-line ft-flow/no-mutable-array
531
530
  stack: Array<T>;
532
531
 
533
- constructor(array: ?$ReadOnlyArray<T>) {
532
+ constructor(array?: ReadonlyArray<T> | null) {
534
533
  this.stack = array ? array.slice(0) : [];
535
534
  }
536
535
 
@@ -541,6 +540,7 @@ class Stack<T> {
541
540
 
542
541
  /** Pop a value off of the stack. */
543
542
  pop(): T {
543
+ // @ts-expect-error [FEI-5003] - TS2322 - Type 'T | undefined' is not assignable to type 'T'.
544
544
  return this.stack.pop();
545
545
  }
546
546
 
@@ -550,7 +550,7 @@ class Stack<T> {
550
550
  }
551
551
 
552
552
  /** Return a copy of the stack as an array */
553
- values(): $ReadOnlyArray<T> {
553
+ values(): ReadonlyArray<T> {
554
554
  return this.stack.slice(0);
555
555
  }
556
556
 
package/src/types.ts ADDED
@@ -0,0 +1,7 @@
1
+ export type LinterContextProps = {
2
+ contentType: string;
3
+ highlightLint: boolean;
4
+ paths: ReadonlyArray<string>;
5
+ stack: ReadonlyArray<string>;
6
+ // additional properties can be added to the context by widgets
7
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "exclude": ["dist", "**/*.test.*", "**/*.testdata.*", "**/*.stories.*", "**/*.cypress.*"],
3
+ "extends": "../tsconfig-shared.json",
4
+ "compilerOptions": {
5
+ "outDir": "./dist",
6
+ "rootDir": "src",
7
+ },
8
+ "references": [
9
+ {"path": "../perseus-error"},
10
+ {"path": "../pure-markdown"},
11
+ ]
12
+ }