@khanacademy/perseus-linter 1.2.17 → 1.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/dist/index.js CHANGED
@@ -525,22 +525,27 @@ class SiblingCombinator extends SelectorCombinator {
525
525
  // This represents the type returned by String.match(). It is an
526
526
  // array of strings, but also has index:number and input:string properties.
527
527
  // TypeScript doesn't handle it well, so we punt and just use any.
528
+
528
529
  // This is the return type of the check() method of a Rule object
530
+
529
531
  // This is the return type of the lint detection function passed as the 4th
530
532
  // argument to the Rule() constructor. It can return null or a string or an
531
533
  // object containing a string and two numbers.
532
534
  // prettier-ignore
533
535
  // (prettier formats this in a way that ka-lint does not like)
536
+
534
537
  // This is the type of the lint detection function that the Rule() constructor
535
538
  // expects as its fourth argument. It is passed the TraversalState object and
536
539
  // content string that were passed to check(), and is also passed the array of
537
540
  // nodes returned by the selector match and the array of strings returned by
538
541
  // the pattern match. It should return null if no lint is detected or an
539
542
  // error message or an object contining an error message.
543
+
540
544
  // An optional check to verify whether or not a particular rule should
541
545
  // be checked by context. For example, some rules only apply in exercises,
542
546
  // and should never be applied to articles. Defaults to true, so if we
543
547
  // omit the applies function in a rule, it'll be tested everywhere.
548
+
544
549
  /**
545
550
  * A Rule object describes a Perseus lint rule. See the comment at the top of
546
551
  * this file for detailed description.
@@ -628,7 +633,6 @@ class Rule {
628
633
  if (!error) {
629
634
  return null; // No lint; we're done
630
635
  }
631
-
632
636
  if (typeof error === "string") {
633
637
  // If the lint function returned a string we assume it
634
638
  // applies to the entire content of the node and return it.
@@ -1010,6 +1014,28 @@ Whitespace in image URLs causes translation difficulties.`;
1010
1014
  }
1011
1015
  });
1012
1016
 
1017
+ var ImageUrlEmpty = Rule.makeRule({
1018
+ name: "image-url-empty",
1019
+ severity: Rule.Severity.ERROR,
1020
+ selector: "image",
1021
+ lint: function (state, content, nodes) {
1022
+ const image = nodes[0];
1023
+ const url = image.target;
1024
+
1025
+ // If no URL is provided, an infinite spinner will be shown in articles
1026
+ // overlaying the page where the image should be. This prevents the page
1027
+ // from fully loading. As a result, we check for URLS with all images.
1028
+ if (!url || !url.trim()) {
1029
+ return "Images should have a URL";
1030
+ }
1031
+
1032
+ // NOTE(TB): Ideally there would be a check to confirm the URL works
1033
+ // and leads to a valid resource, but fetching the URL would require
1034
+ // linting to be able to handle async functions, which it currently
1035
+ // cannot do.
1036
+ }
1037
+ });
1038
+
1013
1039
  // Normally we have one rule per file. But since our selector class
1014
1040
  // can't match specific widget types directly, this rule implements
1015
1041
  // a number of image widget related rules in one place. This should
@@ -1146,15 +1172,6 @@ var MathEmpty = Rule.makeRule({
1146
1172
  message: "Empty math: don't use $$ in your markdown."
1147
1173
  });
1148
1174
 
1149
- var MathFontSize = Rule.makeRule({
1150
- name: "math-font-size",
1151
- severity: Rule.Severity.GUIDELINE,
1152
- selector: "math, blockMath",
1153
- pattern: /\\(tiny|Tiny|small|large|Large|LARGE|huge|Huge|scriptsize|normalsize)\s*{/,
1154
- message: `Math font size:
1155
- Don't change the default font size with \\Large{} or similar commands`
1156
- });
1157
-
1158
1175
  var MathFrac = Rule.makeRule({
1159
1176
  name: "math-frac",
1160
1177
  severity: Rule.Severity.GUIDELINE,
@@ -1283,7 +1300,7 @@ do not put widgets inside of tables.`
1283
1300
  });
1284
1301
 
1285
1302
  // TODO(davidflanagan):
1286
- var AllRules = [AbsoluteUrl, BlockquotedMath, BlockquotedWidget, DoubleSpacingAfterTerminal, ExpressionWidget, ExtraContentSpacing, HeadingLevel1, HeadingLevelSkip, HeadingSentenceCase, HeadingTitleCase, ImageAltText, ImageInTable, LinkClickHere, LongParagraph, MathAdjacent, MathAlignExtraBreak, MathAlignLinebreaks, MathEmpty, MathFontSize, MathFrac, MathNested, MathStartsWithSpace, MathTextEmpty, NestedLists, StaticWidgetInQuestionStem, TableMissingCells, UnescapedDollar, WidgetInTable, MathWithoutDollars, UnbalancedCodeDelimiters, ImageSpacesAroundUrls, ImageWidget];
1303
+ var AllRules = [AbsoluteUrl, BlockquotedMath, BlockquotedWidget, DoubleSpacingAfterTerminal, ImageUrlEmpty, ExpressionWidget, ExtraContentSpacing, HeadingLevel1, HeadingLevelSkip, HeadingSentenceCase, HeadingTitleCase, ImageAltText, ImageInTable, LinkClickHere, LongParagraph, MathAdjacent, MathAlignExtraBreak, MathAlignLinebreaks, MathEmpty, MathFrac, MathNested, MathStartsWithSpace, MathTextEmpty, NestedLists, StaticWidgetInQuestionStem, TableMissingCells, UnescapedDollar, WidgetInTable, MathWithoutDollars, UnbalancedCodeDelimiters, ImageSpacesAroundUrls, ImageWidget];
1287
1304
 
1288
1305
  /**
1289
1306
  * TreeTransformer is a class for traversing and transforming trees. Create a
@@ -1345,9 +1362,11 @@ var AllRules = [AbsoluteUrl, BlockquotedMath, BlockquotedWidget, DoubleSpacingAf
1345
1362
 
1346
1363
  // TreeNode is the type of a node in a parse tree. The only real requirement is
1347
1364
  // that every node has a string-valued `type` property
1365
+
1348
1366
  // TraversalCallback is the type of the callback function passed to the
1349
1367
  // traverse() method. It is invoked with node, state, and content arguments
1350
1368
  // and is expected to return nothing.
1369
+
1351
1370
  // This is the TreeTransformer class described in detail at the
1352
1371
  // top of this file.
1353
1372
  class TreeTransformer {
@@ -1828,10 +1847,51 @@ class Stack {
1828
1847
  }
1829
1848
  }
1830
1849
 
1850
+ /**
1851
+ * Adds the given perseus library version information to the __perseus_debug__
1852
+ * object and ensures that the object is attached to `globalThis` (`window` in
1853
+ * browser environments).
1854
+ *
1855
+ * This allows each library to provide runtime version information to assist in
1856
+ * debugging in production environments.
1857
+ */
1858
+ const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
1859
+ // If the library version is the default value, then we don't want to
1860
+ // prefix it with a "v" to indicate that it is a version number.
1861
+ let prefix = "v";
1862
+ if (libraryVersion === "__lib_version__") {
1863
+ prefix = "";
1864
+ }
1865
+ const formattedVersion = `${prefix}${libraryVersion}`;
1866
+ if (typeof globalThis !== "undefined") {
1867
+ globalThis.__perseus_debug__ = globalThis.__perseus_debug__ ?? {};
1868
+ const existingVersionEntry = globalThis.__perseus_debug__[libraryName];
1869
+ if (existingVersionEntry) {
1870
+ // If we already have an entry and it doesn't match the registered
1871
+ // version, we morph the entry into an array and log a warning.
1872
+ if (existingVersionEntry !== formattedVersion) {
1873
+ // Existing entry might be an array already (oops, at least 2
1874
+ // versions of the library already loaded!).
1875
+ const allVersions = Array.isArray(existingVersionEntry) ? existingVersionEntry : [existingVersionEntry];
1876
+ allVersions.push(formattedVersion);
1877
+ globalThis.__perseus_debug__[libraryName] = allVersions;
1878
+
1879
+ // eslint-disable-next-line no-console
1880
+ console.warn(`Multiple versions of ${libraryName} loaded on this page: ${allVersions.sort().join(", ")}`);
1881
+ }
1882
+ } else {
1883
+ globalThis.__perseus_debug__[libraryName] = formattedVersion;
1884
+ }
1885
+ } else {
1886
+ // eslint-disable-next-line no-console
1887
+ console.warn(`globalThis not found found (${formattedVersion})`);
1888
+ }
1889
+ };
1890
+
1831
1891
  // This file is processed by a Rollup plugin (replace) to inject the production
1832
1892
  const libName = "@khanacademy/perseus-linter";
1833
- const libVersion = "1.2.17";
1834
- perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
1893
+ const libVersion = "1.3.0";
1894
+ addLibraryVersionToPerseusDebug(libName, libVersion);
1835
1895
 
1836
1896
  // Define the shape of the linter context object that is passed through the
1837
1897
  const linterContextProps = PropTypes__default["default"].shape({