@khanacademy/perseus 0.2.7 → 0.2.8

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 (84) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +5 -0
  3. package/dist/es/index.js +3094 -5325
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.js +13597 -15834
  6. package/dist/index.js.map +1 -1
  7. package/package.json +4 -2
  8. package/src/__tests__/perseus-markdown_test.jsx +4 -4
  9. package/src/article-renderer.jsx +3 -4
  10. package/src/components/input-with-examples.jsx +3 -4
  11. package/src/components/sortable.jsx +4 -5
  12. package/src/hint-renderer.jsx +2 -3
  13. package/src/hints-renderer.jsx +2 -3
  14. package/src/item-renderer.jsx +2 -3
  15. package/src/not-gorgon.js +1 -2
  16. package/src/perseus-markdown.jsx +1 -2
  17. package/src/renderer.jsx +2 -3
  18. package/src/widget-container.jsx +1 -1
  19. package/src/widgets/categorizer.jsx +1 -1
  20. package/src/widgets/explanation.jsx +1 -1
  21. package/src/widgets/expression.jsx +1 -1
  22. package/src/widgets/graded-group-set.jsx +1 -1
  23. package/src/widgets/graded-group.jsx +1 -1
  24. package/src/widgets/group.jsx +1 -1
  25. package/src/widgets/image.jsx +1 -1
  26. package/src/widgets/input-number.jsx +1 -1
  27. package/src/widgets/matcher.jsx +4 -1
  28. package/src/widgets/matrix.jsx +1 -1
  29. package/src/widgets/numeric-input.jsx +1 -1
  30. package/src/widgets/orderer.jsx +4 -1
  31. package/src/widgets/passage-ref-target.jsx +4 -1
  32. package/src/widgets/passage.jsx +1 -1
  33. package/src/widgets/radio/widget.jsx +1 -1
  34. package/src/widgets/sequence.jsx +4 -1
  35. package/src/widgets/sorter.jsx +4 -1
  36. package/src/widgets/table.jsx +4 -1
  37. package/src/README.md +0 -35
  38. package/src/__tests__/__snapshots__/pure-markdown_test.js.snap +0 -113
  39. package/src/__tests__/pure-markdown_test.js +0 -552
  40. package/src/gorgon/README +0 -40
  41. package/src/gorgon/__tests__/matcher_test.js +0 -497
  42. package/src/gorgon/__tests__/rule_test.js +0 -101
  43. package/src/gorgon/__tests__/rules_test.js +0 -487
  44. package/src/gorgon/__tests__/selector-parser_test.js +0 -52
  45. package/src/gorgon/__tests__/tree-transformer_test.js +0 -446
  46. package/src/gorgon/gorgon.js +0 -290
  47. package/src/gorgon/proptypes.js +0 -29
  48. package/src/gorgon/rule.js +0 -413
  49. package/src/gorgon/rules/absolute-url.js +0 -24
  50. package/src/gorgon/rules/all-rules.js +0 -72
  51. package/src/gorgon/rules/blockquoted-math.js +0 -10
  52. package/src/gorgon/rules/blockquoted-widget.js +0 -10
  53. package/src/gorgon/rules/double-spacing-after-terminal.js +0 -12
  54. package/src/gorgon/rules/extra-content-spacing.js +0 -12
  55. package/src/gorgon/rules/heading-level-1.js +0 -14
  56. package/src/gorgon/rules/heading-level-skip.js +0 -20
  57. package/src/gorgon/rules/heading-sentence-case.js +0 -11
  58. package/src/gorgon/rules/heading-title-case.js +0 -63
  59. package/src/gorgon/rules/image-alt-text.js +0 -21
  60. package/src/gorgon/rules/image-in-table.js +0 -10
  61. package/src/gorgon/rules/image-spaces-around-urls.js +0 -35
  62. package/src/gorgon/rules/image-widget.js +0 -50
  63. package/src/gorgon/rules/link-click-here.js +0 -11
  64. package/src/gorgon/rules/lint-utils.js +0 -48
  65. package/src/gorgon/rules/long-paragraph.js +0 -14
  66. package/src/gorgon/rules/math-adjacent.js +0 -10
  67. package/src/gorgon/rules/math-align-extra-break.js +0 -11
  68. package/src/gorgon/rules/math-align-linebreaks.js +0 -43
  69. package/src/gorgon/rules/math-empty.js +0 -10
  70. package/src/gorgon/rules/math-font-size.js +0 -12
  71. package/src/gorgon/rules/math-frac.js +0 -10
  72. package/src/gorgon/rules/math-nested.js +0 -11
  73. package/src/gorgon/rules/math-starts-with-space.js +0 -12
  74. package/src/gorgon/rules/math-text-empty.js +0 -10
  75. package/src/gorgon/rules/math-without-dollars.js +0 -14
  76. package/src/gorgon/rules/nested-lists.js +0 -11
  77. package/src/gorgon/rules/profanity.js +0 -10
  78. package/src/gorgon/rules/table-missing-cells.js +0 -20
  79. package/src/gorgon/rules/unbalanced-code-delimiters.js +0 -14
  80. package/src/gorgon/rules/unescaped-dollar.js +0 -10
  81. package/src/gorgon/rules/widget-in-table.js +0 -10
  82. package/src/gorgon/selector.js +0 -506
  83. package/src/gorgon/tree-transformer.js +0 -588
  84. package/src/pure-markdown.js +0 -347
@@ -1,347 +0,0 @@
1
- // @flow
2
- /**
3
- * Contains markdown related functions in pure javascript,
4
- * extracted from perseus-markdown.jsx
5
- */
6
- import SimpleMarkdown from "@khanacademy/simple-markdown";
7
-
8
- import Util from "./util.js";
9
-
10
- /**
11
- * This match function matches math in `$`s, such as:
12
- *
13
- * $y = x + 1$
14
- *
15
- * It functions roughly like the following regex:
16
- * /\$([^\$]*)\$/
17
- *
18
- * Unfortunately, math may have other `$`s inside it, as
19
- * long as they are inside `{` braces `}`, mostly for
20
- * `\text{ $math$ }`.
21
- *
22
- * To parse this, we can't use a regex, since we
23
- * should support arbitrary nesting (even though
24
- * MathJax actually only supports two levels of nesting
25
- * here, which we *could* parse with a regex).
26
- *
27
- * Non-regex matchers like this are now a first-class
28
- * concept in simple-markdown. Yay!
29
- *
30
- * This can also match block-math, which is math alone in a paragraph.
31
- */
32
- const mathMatcher = (source, state, isBlock) => {
33
- const length = source.length;
34
- let index = 0;
35
-
36
- // When looking for blocks, skip over leading spaces
37
- if (isBlock) {
38
- if (state.inline) {
39
- return null;
40
- }
41
- while (index < length && source[index] === " ") {
42
- index++;
43
- }
44
- }
45
-
46
- // Our source must start with a "$"
47
- if (!(index < length && source[index] === "$")) {
48
- return null;
49
- }
50
-
51
- index++;
52
- const startIndex = index;
53
- let braceLevel = 0;
54
-
55
- // Loop through the source, looking for a closing '$'
56
- // closing '$'s only count if they are not escaped with
57
- // a `\`, and we are not in nested `{}` braces.
58
- while (index < length) {
59
- const character = source[index];
60
-
61
- if (character === "\\") {
62
- // Consume both the `\` and the escaped char as a single
63
- // token.
64
- // This is so that the second `$` in `$\\$` closes
65
- // the math expression, since the first `\` is escaping
66
- // the second `\`, but the second `\` is not escaping
67
- // the second `$`.
68
- // This also handles the case of escaping `$`s or
69
- // braces `\{`
70
- index++;
71
- } else if (braceLevel <= 0 && character === "$") {
72
- let endIndex = index + 1;
73
- if (isBlock) {
74
- // Look for two trailing newlines after the closing `$`
75
- const match = /^(?: *\n){2,}/.exec(source.slice(endIndex));
76
- endIndex = match ? endIndex + match[0].length : null;
77
- }
78
-
79
- // Return an array that looks like the results of a
80
- // regex's .exec function:
81
- // capture[0] is the whole string
82
- // capture[1] is the first "paren" match, which is the
83
- // content of the math here, as if we wrote the regex
84
- // /\$([^\$]*)\$/
85
- if (endIndex) {
86
- return [
87
- source.substring(0, endIndex),
88
- source.substring(startIndex, index),
89
- ];
90
- }
91
- return null;
92
- } else if (character === "{") {
93
- braceLevel++;
94
- } else if (character === "}") {
95
- braceLevel--;
96
- } else if (character === "\n" && source[index - 1] === "\n") {
97
- // This is a weird case we supported in the old
98
- // math implementation--double newlines break
99
- // math. I'm preserving it for now because content
100
- // creators might have questions with single '$'s
101
- // in paragraphs...
102
- return null;
103
- }
104
-
105
- index++;
106
- }
107
-
108
- // we didn't find a closing `$`
109
- return null;
110
- };
111
- const mathMatch = (source: any, state: any): any =>
112
- mathMatcher(source, state, false);
113
- const blockMathMatch = (source: any, state: any): any =>
114
- mathMatcher(source, state, true);
115
-
116
- const TITLED_TABLE_REGEX = new RegExp(
117
- "^\\|\\| +(.*) +\\|\\| *\\n" +
118
- "(" +
119
- // The simple-markdown nptable regex, without
120
- // the leading `^`
121
- // $FlowFixMe[prop-missing]
122
- // $FlowFixMe[incompatible-use]
123
- SimpleMarkdown.defaultRules.nptable.match.regex.source.substring(1) +
124
- ")",
125
- );
126
-
127
- // $FlowFixMe[prop-missing]
128
- const crowdinJiptMatcher = SimpleMarkdown.blockRegex(/^(crwdns.*)\n\s*\n/);
129
-
130
- export const pureMarkdownRules = {
131
- // $FlowFixMe[prop-missing]
132
- ...SimpleMarkdown.defaultRules,
133
-
134
- // NOTE: basically ignored by JIPT. wraps everything at the outer layer
135
- columns: {
136
- order: -2,
137
- // $FlowFixMe[prop-missing]
138
- match: (SimpleMarkdown.blockRegex(
139
- /^([\s\S]*\n\n)={5,}\n\n([\s\S]*)/,
140
- ): any),
141
- parse: (capture: any, parse: any, state: any): any => {
142
- return {
143
- col1: parse(capture[1], state),
144
- col2: parse(capture[2], state),
145
- };
146
- },
147
- },
148
- crowdinId: {
149
- order: -1,
150
- match: (source: any, state: any, prevCapture: any): any => {
151
- // Only match on the just-in-place translation site
152
- if (state.isJipt) {
153
- return crowdinJiptMatcher(source, state, prevCapture);
154
- }
155
- return null;
156
- },
157
- parse: (capture: any, parse: any, state: any): any => ({
158
- id: capture[1],
159
- }),
160
- },
161
- // This is pretty much horrible, but we have a regex here to capture an
162
- // entire table + a title. capture[1] is the title. capture[2] of the
163
- // regex is a copy of the simple-markdown nptable regex. Then we turn
164
- // our capture[2] into tableCapture[0], and any further captures in
165
- // our table regex into tableCapture[1..], and we pass tableCapture to
166
- // our nptable regex
167
- titledTable: {
168
- // process immediately before nptables
169
- // $FlowFixMe[prop-missing]
170
- // $FlowFixMe[incompatible-use]
171
- order: SimpleMarkdown.defaultRules.nptable.order - 0.5,
172
- // $FlowFixMe[prop-missing]
173
- match: (SimpleMarkdown.blockRegex(TITLED_TABLE_REGEX): any),
174
- parse: (capture: any, parse: any, state: any): any => {
175
- // $FlowFixMe[prop-missing]
176
- const title = SimpleMarkdown.parseInline(parse, capture[1], state);
177
-
178
- // Remove our [0] and [1] captures, and pass the rest to
179
- // the nptable parser
180
- const tableCapture = capture.slice(2);
181
- // $FlowFixMe[prop-missing]
182
- // $FlowFixMe[incompatible-use]
183
- const table = SimpleMarkdown.defaultRules.nptable.parse(
184
- tableCapture,
185
- parse,
186
- state,
187
- );
188
- return {
189
- title: title,
190
- table: table,
191
- };
192
- },
193
- },
194
- widget: {
195
- // $FlowFixMe[prop-missing]
196
- // $FlowFixMe[incompatible-use]
197
- order: SimpleMarkdown.defaultRules.link.order - 0.75,
198
- // $FlowFixMe[prop-missing]
199
- match: (SimpleMarkdown.inlineRegex(Util.rWidgetRule): any),
200
- parse: (capture: any, parse: any, state: any): any => {
201
- return {
202
- id: capture[1],
203
- widgetType: capture[2],
204
- };
205
- },
206
- },
207
- blockMath: {
208
- // $FlowFixMe[prop-missing]
209
- // $FlowFixMe[incompatible-use]
210
- order: (SimpleMarkdown.defaultRules.codeBlock.order + 0.5: any),
211
- match: blockMathMatch,
212
- parse: (capture: any, parse: any, state: any): any => {
213
- return {
214
- content: capture[1],
215
- };
216
- },
217
- },
218
- math: {
219
- // $FlowFixMe[prop-missing]
220
- // $FlowFixMe[incompatible-use]
221
- order: SimpleMarkdown.defaultRules.link.order - 0.25,
222
- match: mathMatch,
223
- parse: (capture: any, parse: any, state: any): any => {
224
- return {
225
- content: capture[1],
226
- };
227
- },
228
- },
229
- unescapedDollar: {
230
- // $FlowFixMe[prop-missing]
231
- // $FlowFixMe[incompatible-use]
232
- order: SimpleMarkdown.defaultRules.link.order - 0.24,
233
- // $FlowFixMe[prop-missing]
234
- match: (SimpleMarkdown.inlineRegex(/^(?!\\)\$/): any),
235
- parse: (capture: any, parse: any, state: any): any => {
236
- return {};
237
- },
238
- },
239
- fence: {
240
- // $FlowFixMe[prop-missing]
241
- // $FlowFixMe[incompatible-use]
242
- ...SimpleMarkdown.defaultRules.fence,
243
- parse: (capture: any, parse: any, state: any): any => {
244
- // $FlowFixMe[prop-missing]
245
- // $FlowFixMe[incompatible-use]
246
- const node = SimpleMarkdown.defaultRules.fence.parse(
247
- capture,
248
- parse,
249
- state,
250
- );
251
-
252
- // support screenreader-only text with ```alt
253
- if (node.lang === "alt") {
254
- return {
255
- type: "codeBlock",
256
- lang: "alt",
257
- // default codeBlock parsing doesn't parse the contents.
258
- // We need to parse the contents for things like table
259
- // support :).
260
- // The \n\n is because the inside of the codeblock might
261
- // not end in double newlines for block rules, because
262
- // ordinarily we don't parse this :).
263
- content: parse(node.content + "\n\n", state),
264
- };
265
- }
266
- return node;
267
- },
268
- },
269
- blockQuote: {
270
- // $FlowFixMe[prop-missing]
271
- // $FlowFixMe[incompatible-use]
272
- ...SimpleMarkdown.defaultRules.blockQuote,
273
- // Replace the less restrictive blockquote regex from SimpleMarkdown
274
- // with a more restrictive one. The only difference should be that
275
- //
276
- // > A blockquote
277
- //
278
- // > Another blockquote
279
- //
280
- // will now match as two different blockQuotes instead of a single
281
- // blockquote with some paragraph breaks in it.
282
- //
283
- // The main motivation for doing this is to provide better support for
284
- // translators translating blockquotes with multiple paragraphs in
285
- // them. When translating articles, we split up paragraphs, translate
286
- // them separately, and then recombine them. We do this so that
287
- // translators don't have to translate an entire article at a time,
288
- // they can instead translate paragraph-by-paragraph.That system
289
- // doesn't understand blockquotes, so it will split up blockquotes into
290
- // more than one paragraph. A way to solve this would be to make that
291
- // system understand blockquotes, but then translators would have to
292
- // translate an entire, multi-paragraph blockquote at a time. Instead,
293
- // we choose to modify our blockquote matching to split up
294
- // multi-paragraph blockquotes into multiple blockquotes.
295
- //
296
- // There is also precedence for doing this splitting up in other
297
- // libraries, for instance CommonMark also splits up blockquotes with
298
- // empty lines into multiple blockquotes:
299
- // https://spec.commonmark.org/0.28/#example-205
300
- // $FlowFixMe[prop-missing]
301
- match: (SimpleMarkdown.blockRegex(
302
- /^ *>[^\n]+(\n( *>)?[^\n]+)*\n{2,}/,
303
- ): any),
304
- },
305
- list: {
306
- // $FlowFixMe[prop-missing]
307
- // $FlowFixMe[incompatible-use]
308
- ...SimpleMarkdown.defaultRules.list,
309
- match: (source: any, state: any, prevCapture: any): any => {
310
- // Since lists can contain double newlines and we have special
311
- // handling of double newlines while parsing jipt content, just
312
- // disable the list parser.
313
- if (state.isJipt) {
314
- return null;
315
- }
316
- // $FlowFixMe[prop-missing]
317
- // $FlowFixMe[incompatible-use]
318
- return SimpleMarkdown.defaultRules.list.match(
319
- source,
320
- state,
321
- prevCapture,
322
- );
323
- },
324
- },
325
- // The lint rule never actually matches anything.
326
- // We check for lint after parsing, and, if we find any, we
327
- // transform the tree to add lint nodes. This rule is here
328
- // just for the react() function
329
- lint: {
330
- order: 1000,
331
- match: (s: any): any => null,
332
- parse: (capture: any, parse: any, state: any): any => ({}),
333
- },
334
- };
335
-
336
- // $FlowFixMe[prop-missing]
337
- // $FlowFixMe[incompatible-call]
338
- const builtParser = SimpleMarkdown.parserFor(pureMarkdownRules);
339
-
340
- export const parse = (source: string, state: $FlowFixMe): $FlowFixMe => {
341
- const paragraphedSource = source + "\n\n";
342
-
343
- return builtParser(paragraphedSource, {
344
- ...state,
345
- inline: false,
346
- });
347
- };