@khanacademy/perseus-linter 1.1.0 → 1.2.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/es/index.js CHANGED
@@ -804,12 +804,62 @@ var DoubleSpacingAfterTerminal = Rule.makeRule({
804
804
  any other kind of terminal punctuation.`
805
805
  });
806
806
 
807
+ function buttonNotInButtonSet(name, set) {
808
+ return `Answer requires a button not found in the button sets: ${name} (in ${set})`;
809
+ }
810
+ const stringToButtonSet = {
811
+ "\\sqrt": "prealgebra",
812
+ "\\sin": "trig",
813
+ "\\cos": "trig",
814
+ "\\tan": "trig",
815
+ "\\log": "logarithms",
816
+ "\\ln": "logarithms"
817
+ };
818
+
819
+ /**
820
+ * Rule to make sure that Expression questions that require
821
+ * a specific math symbol to answer have that math symbol
822
+ * available in the keypad (desktop learners can use a keyboard,
823
+ * but mobile learners must use the MathInput keypad)
824
+ */
825
+ var ExpressionWidget = Rule.makeRule({
826
+ name: "expression-widget",
827
+ severity: Rule.Severity.WARNING,
828
+ selector: "widget",
829
+ lint: function (state, content, nodes, match, context) {
830
+ var _context$widgets;
831
+ // This rule only looks at image widgets
832
+ if (state.currentNode().widgetType !== "expression") {
833
+ return;
834
+ }
835
+ const nodeId = state.currentNode().id;
836
+ if (!nodeId) {
837
+ return;
838
+ }
839
+
840
+ // If it can't find a definition for the widget it does nothing
841
+ const widget = context == null || (_context$widgets = context.widgets) == null ? void 0 : _context$widgets[nodeId];
842
+ if (!widget) {
843
+ return;
844
+ }
845
+ const answers = widget.options.answerForms;
846
+ const buttons = widget.options.buttonSets;
847
+ for (const answer of answers) {
848
+ for (const [str, set] of Object.entries(stringToButtonSet)) {
849
+ if (answer.value.includes(str) && !buttons.includes(set)) {
850
+ return buttonNotInButtonSet(str, set);
851
+ }
852
+ }
853
+ }
854
+ }
855
+ });
856
+
807
857
  var ExtraContentSpacing = Rule.makeRule({
808
858
  name: "extra-content-spacing",
809
859
  selector: "paragraph",
810
860
  pattern: /\s+$/,
811
861
  applies: function (context) {
812
- return context.contentType === "article";
862
+ return (context == null ? void 0 : context.contentType) === "article";
813
863
  },
814
864
  message: `No extra whitespace at the end of content blocks.`
815
865
  });
@@ -989,9 +1039,13 @@ var ImageWidget = Rule.makeRule({
989
1039
  if (state.currentNode().widgetType !== "image") {
990
1040
  return;
991
1041
  }
1042
+ const nodeId = state.currentNode().id;
1043
+ if (!nodeId) {
1044
+ return;
1045
+ }
992
1046
 
993
1047
  // If it can't find a definition for the widget it does nothing
994
- const widget = context && context.widgets && context.widgets[state.currentNode().id];
1048
+ const widget = context && context.widgets && context.widgets[nodeId];
995
1049
  if (!widget) {
996
1050
  return;
997
1051
  }
@@ -1075,7 +1129,7 @@ var MathAlignLinebreaks = Rule.makeRule({
1075
1129
  const index = text.indexOf("\\\\");
1076
1130
  if (index === -1) {
1077
1131
  // No more backslash pairs, so we found no lint
1078
- return null;
1132
+ return;
1079
1133
  }
1080
1134
  text = text.substring(index + 2);
1081
1135
 
@@ -1177,13 +1231,17 @@ var StaticWidgetInQuestionStem = Rule.makeRule({
1177
1231
  selector: "widget",
1178
1232
  lint: (state, content, nodes, match, context) => {
1179
1233
  var _context$widgets;
1180
- if (context.contentType !== "exercise") {
1234
+ if ((context == null ? void 0 : context.contentType) !== "exercise") {
1181
1235
  return;
1182
1236
  }
1183
1237
  if (context.stack.includes("hint")) {
1184
1238
  return;
1185
1239
  }
1186
- const widget = context == null || (_context$widgets = context.widgets) == null ? void 0 : _context$widgets[state.currentNode().id];
1240
+ const nodeId = state.currentNode().id;
1241
+ if (!nodeId) {
1242
+ return;
1243
+ }
1244
+ const widget = context == null || (_context$widgets = context.widgets) == null ? void 0 : _context$widgets[nodeId];
1187
1245
  if (!widget) {
1188
1246
  return;
1189
1247
  }
@@ -1240,7 +1298,7 @@ do not put widgets inside of tables.`
1240
1298
  });
1241
1299
 
1242
1300
  // TODO(davidflanagan):
1243
- var AllRules = [AbsoluteUrl, BlockquotedMath, BlockquotedWidget, DoubleSpacingAfterTerminal, 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];
1301
+ 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];
1244
1302
 
1245
1303
  /**
1246
1304
  * TreeTransformer is a class for traversing and transforming trees. Create a
@@ -1781,7 +1839,7 @@ class Stack {
1781
1839
 
1782
1840
  // This file is processed by a Rollup plugin (replace) to inject the production
1783
1841
  const libName = "@khanacademy/perseus-linter";
1784
- const libVersion = "1.1.0";
1842
+ const libVersion = "1.2.0";
1785
1843
  addLibraryVersionToPerseusDebug(libName, libVersion);
1786
1844
 
1787
1845
  // Define the shape of the linter context object that is passed through the