katex 0.16.45 → 0.16.47

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 (60) hide show
  1. package/README.md +3 -3
  2. package/contrib/copy-tex/README.md +2 -2
  3. package/contrib/mathtex-script-type/README.md +5 -5
  4. package/contrib/mathtex-script-type/mathtex-script-type.js +2 -1
  5. package/contrib/mhchem/README.md +1 -1
  6. package/contrib/render-a11y-string/render-a11y-string.ts +7 -5
  7. package/contrib/render-a11y-string/test/render-a11y-string-spec.ts +0 -1
  8. package/dist/README.md +3 -3
  9. package/dist/contrib/mathtex-script-type.js +2 -1
  10. package/dist/contrib/mathtex-script-type.min.js +1 -1
  11. package/dist/contrib/mathtex-script-type.mjs +2 -1
  12. package/dist/contrib/render-a11y-string.js +47 -8
  13. package/dist/contrib/render-a11y-string.min.js +1 -1
  14. package/dist/contrib/render-a11y-string.mjs +27 -2
  15. package/dist/katex-swap.css +5 -2
  16. package/dist/katex-swap.min.css +1 -1
  17. package/dist/katex.css +5 -2
  18. package/dist/katex.js +520 -316
  19. package/dist/katex.min.css +1 -1
  20. package/dist/katex.min.js +1 -1
  21. package/dist/katex.mjs +492 -321
  22. package/package.json +15 -16
  23. package/src/Options.ts +29 -28
  24. package/src/Parser.ts +12 -15
  25. package/src/Settings.ts +177 -60
  26. package/src/atoms.ts +33 -0
  27. package/src/buildCommon.ts +54 -46
  28. package/src/buildHTML.ts +4 -3
  29. package/src/buildMathML.ts +54 -47
  30. package/src/defineEnvironment.ts +1 -1
  31. package/src/defineFunction.ts +10 -3
  32. package/src/delimiter.ts +17 -13
  33. package/src/domTree.ts +28 -23
  34. package/src/environments/array.ts +12 -6
  35. package/src/environments/cd.ts +9 -2
  36. package/src/fontMetrics.ts +10 -23
  37. package/src/fontMetricsData.d.ts +6 -1
  38. package/src/functions/arrow.ts +4 -5
  39. package/src/functions/delimsizing.ts +22 -16
  40. package/src/functions/enclose.ts +6 -6
  41. package/src/functions/environment.ts +7 -2
  42. package/src/functions/font.ts +13 -8
  43. package/src/functions/hbox.ts +2 -2
  44. package/src/functions/horizBrace.ts +4 -6
  45. package/src/functions/math.ts +1 -0
  46. package/src/functions/op.ts +10 -5
  47. package/src/functions/smash.ts +1 -1
  48. package/src/functions/styling.ts +17 -5
  49. package/src/functions/supsub.ts +6 -3
  50. package/src/functions/text.ts +7 -3
  51. package/src/parseNode.ts +7 -5
  52. package/src/stretchy.ts +14 -14
  53. package/src/styles/katex.scss +3 -1
  54. package/src/svgGeometry.ts +2 -2
  55. package/src/symbols.ts +11 -26
  56. package/src/tree.ts +11 -5
  57. package/src/types/fonts.ts +73 -0
  58. package/src/{types.ts → types/index.ts} +4 -10
  59. package/src/utils.ts +0 -1
  60. package/src/wide-character.ts +101 -55
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "katex",
3
- "version": "0.16.45",
3
+ "version": "0.16.47",
4
4
  "description": "Fast math typesetting for the web.",
5
5
  "main": "dist/katex.js",
6
6
  "types": "types/katex.d.ts",
@@ -58,7 +58,6 @@
58
58
  "packageManager": "yarn@4.1.1",
59
59
  "devDependencies": {
60
60
  "@babel/core": "^7.29.0",
61
- "@babel/eslint-parser": "^7.28.6",
62
61
  "@babel/plugin-proposal-class-properties": "^7.18.6",
63
62
  "@babel/plugin-transform-react-jsx": "^7.28.6",
64
63
  "@babel/plugin-transform-runtime": "^7.29.0",
@@ -67,13 +66,16 @@
67
66
  "@babel/preset-typescript": "^7.28.5",
68
67
  "@babel/register": "^7.28.6",
69
68
  "@babel/runtime": "^7.28.6",
69
+ "@eslint/eslintrc": "^3.3.5",
70
+ "@eslint/js": "^10.0.1",
70
71
  "@rollup/plugin-babel": "^5.3.1",
71
72
  "@rollup/plugin-commonjs": "^28.0.9",
72
73
  "@rollup/plugin-typescript": "^12.3.0",
73
74
  "@semantic-release/changelog": "^6.0.1",
74
75
  "@semantic-release/git": "^10.0.1",
75
- "@typescript-eslint/eslint-plugin": "^8.56.0",
76
- "@typescript-eslint/parser": "^8.56.0",
76
+ "@stylistic/stylelint-plugin": "^5.1.0",
77
+ "@typescript-eslint/eslint-plugin": "^8.59.1",
78
+ "@typescript-eslint/parser": "^8.59.1",
77
79
  "babel-jest": "^30.2.0",
78
80
  "babel-loader": "^8.2.5",
79
81
  "babel-plugin-istanbul": "^6.1.1",
@@ -81,16 +83,14 @@
81
83
  "babel-plugin-version-inline": "^1.0.0",
82
84
  "benchmark": "^2.1.4",
83
85
  "browserslist": "^4.21.3",
84
- "browserstack-local": "^1.5.1",
85
86
  "caniuse-lite": "^1.0.30001384",
86
87
  "css-loader": "^6.7.1",
87
88
  "cssnano": "^5.1.13",
88
- "eslint": "^8.23.0",
89
- "eslint-import-resolver-webpack": "^0.13.10",
89
+ "eslint": "^10.0.3",
90
90
  "eslint-plugin-actions": "^2.0.0",
91
- "eslint-plugin-import": "^2.26.0",
92
- "eslint-plugin-react": "^7.31.1",
91
+ "eslint-plugin-react": "^7.37.5",
93
92
  "fs-extra": "^10.1.0",
93
+ "globals": "^17.6.0",
94
94
  "got": "^11.8.5",
95
95
  "husky": "^4.3.8",
96
96
  "istanbul-lib-coverage": "^3.2.0",
@@ -113,7 +113,6 @@
113
113
  "postcss-loader": "^7.0.1",
114
114
  "postcss-preset-env": "^7.8.0",
115
115
  "postcss-scss": "^4.0.9",
116
- "prettier": "^2.7.1",
117
116
  "rimraf": "^3.0.2",
118
117
  "rollup": "^2.79.2",
119
118
  "sass": "^1.94.2",
@@ -122,16 +121,16 @@
122
121
  "semantic-release": "^19.0.5",
123
122
  "sri-toolbox": "^0.2.0",
124
123
  "style-loader": "^3.3.1",
125
- "stylelint": "^14.11.0",
126
- "stylelint-config-standard": "^28.0.0",
127
- "stylelint-scss": "^6.3.2",
124
+ "stylelint": "^17.9.1",
125
+ "stylelint-config-standard": "^40.0.0",
126
+ "stylelint-scss": "^7.0.0",
128
127
  "terser-webpack-plugin": "^5.3.6",
129
128
  "tslib": "^2.8.1",
130
129
  "typescript": "^5.9.3",
131
130
  "webpack": "^5.74.0",
132
- "webpack-bundle-analyzer": "^4.6.1",
133
- "webpack-cli": "^4.10.0",
134
- "webpack-dev-server": "^4.10.1",
131
+ "webpack-bundle-analyzer": "^5.0.0",
132
+ "webpack-cli": "^5.0.0",
133
+ "webpack-dev-server": "^5.0.0",
135
134
  "webpack-remove-empty-scripts": "^1.0.4"
136
135
  },
137
136
  "bin": "cli.js",
package/src/Options.ts CHANGED
@@ -6,8 +6,8 @@
6
6
  */
7
7
 
8
8
  import {getGlobalMetrics} from "./fontMetrics";
9
- import type {FontMetrics} from "./fontMetrics";
10
9
  import type {StyleInterface} from "./Style";
10
+ import type {FontMetrics, MathFont, TextFont, FontWeight, FontShape} from "./types/fonts";
11
11
 
12
12
  const sizeStyleMap = [
13
13
  // Each element contains [textsize, scriptsize, scriptscriptsize].
@@ -35,18 +35,14 @@ const sizeAtStyle = function(size: number, style: StyleInterface): number {
35
35
  return style.size < 2 ? size : sizeStyleMap[size - 1][style.size - 1];
36
36
  };
37
37
 
38
- // In these types, "" (empty string) means "no change".
39
- export type FontWeight = "textbf" | "textmd" | "";
40
- export type FontShape = "textit" | "textup" | "";
41
-
42
38
  export type OptionsData = {
43
39
  style: StyleInterface;
44
40
  color?: string | undefined;
45
41
  size?: number;
46
42
  textSize?: number;
47
43
  phantom?: boolean;
48
- font?: string;
49
- fontFamily?: string;
44
+ font?: MathFont;
45
+ fontFamily?: TextFont;
50
46
  fontWeight?: FontWeight;
51
47
  fontShape?: FontShape;
52
48
  sizeMultiplier?: number;
@@ -70,8 +66,8 @@ class Options {
70
66
  // A font family applies to a group of fonts (i.e. SansSerif), while a font
71
67
  // represents a specific font (i.e. SansSerif Bold).
72
68
  // See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm
73
- font: string;
74
- fontFamily: string;
69
+ font: MathFont;
70
+ fontFamily: TextFont;
75
71
  fontWeight: FontWeight;
76
72
  fontShape: FontShape;
77
73
  sizeMultiplier: number;
@@ -92,8 +88,8 @@ class Options {
92
88
  this.phantom = !!data.phantom;
93
89
  this.font = data.font || "";
94
90
  this.fontFamily = data.fontFamily || "";
95
- this.fontWeight = data.fontWeight || '';
96
- this.fontShape = data.fontShape || '';
91
+ this.fontWeight = data.fontWeight || "";
92
+ this.fontShape = data.fontShape || "";
97
93
  this.sizeMultiplier = sizeMultipliers[this.size - 1];
98
94
  this.maxSize = data.maxSize;
99
95
  this.minRuleThickness = data.minRuleThickness;
@@ -171,8 +167,11 @@ class Options {
171
167
  havingBaseStyle(style: StyleInterface): Options {
172
168
  style = style || this.style.text();
173
169
  const wantSize = sizeAtStyle(Options.BASESIZE, style);
174
- if (this.size === wantSize && this.textSize === Options.BASESIZE
175
- && this.style === style) {
170
+ if (
171
+ this.size === wantSize &&
172
+ this.textSize === Options.BASESIZE &&
173
+ this.style === style
174
+ ) {
176
175
  return this;
177
176
  } else {
178
177
  return this.extend({
@@ -191,14 +190,14 @@ class Options {
191
190
  switch (this.style.id) {
192
191
  case 4:
193
192
  case 5:
194
- size = 3; // normalsize in scriptstyle
193
+ size = 3; // normalsize in scriptstyle
195
194
  break;
196
195
  case 6:
197
196
  case 7:
198
- size = 1; // normalsize in scriptscriptstyle
197
+ size = 1; // normalsize in scriptscriptstyle
199
198
  break;
200
199
  default:
201
- size = 6; // normalsize in textstyle or displaystyle
200
+ size = 6; // normalsize in textstyle or displaystyle
202
201
  }
203
202
  return this.extend({
204
203
  style: this.style.text(),
@@ -228,20 +227,15 @@ class Options {
228
227
  * Creates a new options object with the given math font or old text font.
229
228
  * @type {[type]}
230
229
  */
231
- withFont(font: string): Options {
232
- return this.extend({
233
- font,
234
- });
230
+ withFont(font: MathFont): Options {
231
+ return this.extend({font});
235
232
  }
236
233
 
237
234
  /**
238
235
  * Create a new options objects with the given fontFamily.
239
236
  */
240
- withTextFontFamily(fontFamily: string): Options {
241
- return this.extend({
242
- fontFamily,
243
- font: "",
244
- });
237
+ withTextFontFamily(fontFamily: TextFont): Options {
238
+ return this.extend({fontFamily, font: ""});
245
239
  }
246
240
 
247
241
  /**
@@ -270,7 +264,11 @@ class Options {
270
264
  */
271
265
  sizingClasses(oldOptions: Options): Array<string> {
272
266
  if (oldOptions.size !== this.size) {
273
- return ["sizing", "reset-size" + oldOptions.size, "size" + this.size];
267
+ return [
268
+ "sizing",
269
+ "reset-size" + oldOptions.size,
270
+ "size" + this.size,
271
+ ];
274
272
  } else {
275
273
  return [];
276
274
  }
@@ -282,7 +280,11 @@ class Options {
282
280
  */
283
281
  baseSizingClasses(): Array<string> {
284
282
  if (this.size !== Options.BASESIZE) {
285
- return ["sizing", "reset-size" + this.size, "size" + Options.BASESIZE];
283
+ return [
284
+ "sizing",
285
+ "reset-size" + this.size,
286
+ "size" + Options.BASESIZE,
287
+ ];
286
288
  } else {
287
289
  return [];
288
290
  }
@@ -298,7 +300,6 @@ class Options {
298
300
  return this._fontMetrics;
299
301
  }
300
302
 
301
-
302
303
  /**
303
304
  * Gets the CSS color of the current options object
304
305
  */
package/src/Parser.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /* eslint no-constant-condition:0 */
2
2
  import functions from "./functions";
3
3
  import MacroExpander, {implicitCommands} from "./MacroExpander";
4
- import symbols, {ATOMS, extraLatin} from "./symbols";
4
+ import symbols, {extraLatin} from "./symbols";
5
+ import {isAtom} from "./atoms";
5
6
  import {validUnit} from "./units";
6
7
  import {supportedCodepoint} from "./unicodeScripts";
7
8
  import ParseError from "./ParseError";
@@ -15,9 +16,9 @@ import {Token} from "./Token";
15
16
  import unicodeAccents from /*preval*/ "./unicodeAccents";
16
17
  import unicodeSymbols from /*preval*/ "./unicodeSymbols";
17
18
 
18
- import type {ParseNode, AnyParseNode, SymbolParseNode, UnsupportedCmdParseNode}
19
- from "./parseNode";
20
- import type {Atom, Group} from "./symbols";
19
+ import type {NodeType, ParseNode, AnyParseNode, SymbolParseNode,
20
+ UnsupportedCmdParseNode} from "./parseNode";
21
+ import type {Group} from "./atoms";
21
22
  import type {Mode, ArgType, BreakToken} from "./types";
22
23
  import type {FunctionContext, FunctionSpec} from "./defineFunction";
23
24
  import type {EnvSpec} from "./defineEnvironment";
@@ -521,7 +522,7 @@ export default class Parser {
521
522
  */
522
523
  parseArguments(
523
524
  func: string, // Should look like "\name" or "\begin{name}".
524
- funcData: FunctionSpec<any> | EnvSpec<any>,
525
+ funcData: FunctionSpec<NodeType> | EnvSpec<NodeType>,
525
526
  ): {
526
527
  args: AnyParseNode[];
527
528
  optArgs: (AnyParseNode | null | undefined)[];
@@ -579,13 +580,15 @@ export default class Parser {
579
580
  return this.parseArgumentGroup(optional, type);
580
581
  case "hbox": {
581
582
  // hbox argument type wraps the argument in the equivalent of
582
- // \hbox, which is like \text but switching to \textstyle size.
583
+ // \hbox, which is like \text but switching to \textstyle size
584
+ // and resetting math font.
583
585
  const group = this.parseArgumentGroup(optional, "text");
584
586
  return group != null ? {
585
587
  type: "styling",
586
588
  mode: group.mode,
587
589
  body: [group],
588
590
  style: "text", // simulate \textstyle
591
+ resetFont: true,
589
592
  } : null;
590
593
  }
591
594
  case "raw": {
@@ -974,26 +977,22 @@ export default class Parser {
974
977
  const group: Group = symbols[this.mode][text].group;
975
978
  const loc = SourceLocation.range(nucleus);
976
979
  let s: SymbolParseNode;
977
- if (ATOMS.hasOwnProperty(group)) {
978
- // TODO(ts)
979
- const family = group as Atom;
980
+ if (isAtom(group)) {
980
981
  s = {
981
982
  type: "atom",
982
983
  mode: this.mode,
983
- family,
984
+ family: group,
984
985
  loc,
985
986
  text,
986
987
  };
987
988
  } else {
988
- // TODO(ts)
989
989
  s = {
990
- type: group as Exclude<SymbolParseNode["type"], "atom">,
990
+ type: group,
991
991
  mode: this.mode,
992
992
  loc,
993
993
  text,
994
994
  };
995
995
  }
996
- // TODO(ts)
997
996
  symbol = s;
998
997
  } else if (text.charCodeAt(0) >= 0x80) { // no symbol for e.g. ^
999
998
  if (this.settings.strict) {
@@ -1045,12 +1044,10 @@ export default class Parser {
1045
1044
  label: command,
1046
1045
  isStretchy: false,
1047
1046
  isShifty: true,
1048
- // TODO(ts)
1049
1047
  base: symbol,
1050
1048
  };
1051
1049
  }
1052
1050
  }
1053
- // TODO(ts)
1054
1051
  return symbol;
1055
1052
  }
1056
1053
  }
package/src/Settings.ts CHANGED
@@ -52,56 +52,144 @@ export type AnyTrustContext = TrustContextTypes[keyof TrustContextTypes];
52
52
  export type TrustFunction = (context: AnyTrustContext) => boolean | null | undefined;
53
53
  export type SettingsOptions = Partial<Settings>;
54
54
 
55
- type EnumType = {
56
- enum: string[];
55
+ type EnumType<T extends string = string> = {
56
+ enum: T[];
57
57
  };
58
58
 
59
59
  type Type = "boolean" | "string" | "number" | "object" | "function" | EnumType;
60
- type Schema = {
61
- [key in keyof SettingsOptions]?: {
62
- /**
63
- * Allowed type(s) of the value.
64
- */
65
- type: Type | Type[];
66
- /**
67
- * The default value. If not specified, false for boolean, an empty string
68
- * for string, 0 for number, an empty object for object, or the first item
69
- * for enum will be used. If multiple types are allowed, the first allowed
70
- * type will be used for determining the default value.
71
- */
72
- default?: any;
73
- /**
74
- * The description.
75
- */
76
- description?: string;
77
- /**
78
- * The function to process the option.
79
- */
80
- processor?: (arg0: any) => any;
81
- /**
82
- * The command line argument. See Commander.js docs for more information.
83
- * If not specified, the name prefixed with -- will be used. Set false not
84
- * to add to the CLI.
85
- */
86
- cli?: string | false;
87
- /**
88
- * The default value for the CLI.
89
- */
90
- cliDefault?: any;
91
- /**
92
- * The description for the CLI. If not specified, the description for the
93
- * option will be used.
94
- */
95
- cliDescription?: string;
96
- /**
97
- * The custom argument processor for the CLI. See Commander.js docs for
98
- * more information.
99
- */
100
- cliProcessor?: (arg0: any, arg1: any) => any;
101
- };
60
+ /**
61
+ * Union of all values that appear as schema defaults, cliDefaults, or
62
+ * cliProcessor return values. StrictFunction / TrustFunction are
63
+ * option-value types, not default/schema values, so they are excluded.
64
+ */
65
+ type SettingsValue = boolean | string | number | MacroMap | string[];
66
+ type DefaultValue = Exclude<SettingsValue, string[]>;
67
+
68
+ type SchemaMetadata<K extends keyof SettingsOptions> = {
69
+ /**
70
+ * The description.
71
+ */
72
+ description?: string;
73
+ /**
74
+ * The function to process the option.
75
+ */
76
+ processor?: (value: Settings[K]) => Settings[K];
77
+ /**
78
+ * The command line argument. See Commander.js docs for more information.
79
+ * If not specified, the name prefixed with -- will be used. Set false not
80
+ * to add to the CLI.
81
+ */
82
+ cli?: string | false;
83
+ /**
84
+ * The default value for the CLI.
85
+ */
86
+ cliDefault?: SettingsValue;
87
+ /**
88
+ * The description for the CLI. If not specified, the description for the
89
+ * option will be used.
90
+ */
91
+ cliDescription?: string;
92
+ /**
93
+ * The custom argument processor for the CLI. See Commander.js docs for
94
+ * more information. Signature varies per setting (e.g. parseFloat,
95
+ * or (def, defs) => defs.push(def) for macros).
96
+ */
97
+ cliProcessor?: (...args: any[]) => SettingsValue;
98
+ };
99
+
100
+ type BooleanSchema<K extends keyof SettingsOptions> = SchemaMetadata<K> & {
101
+ /**
102
+ * Allowed type(s) of the value.
103
+ */
104
+ type: "boolean";
105
+ /**
106
+ * The default value. If not specified, false will be used.
107
+ */
108
+ default?: Extract<Settings[K], boolean>;
109
+ };
110
+
111
+ type StringSchema<K extends keyof SettingsOptions> = SchemaMetadata<K> & {
112
+ /**
113
+ * Allowed type(s) of the value.
114
+ */
115
+ type: "string";
116
+ /**
117
+ * The default value. If not specified, an empty string will be used.
118
+ */
119
+ default?: Extract<Settings[K], string>;
120
+ };
121
+
122
+ type NumberSchema<K extends keyof SettingsOptions> = SchemaMetadata<K> & {
123
+ /**
124
+ * Allowed type(s) of the value.
125
+ */
126
+ type: "number";
127
+ /**
128
+ * The default value. If not specified, 0 will be used.
129
+ */
130
+ default?: Extract<Settings[K], number>;
131
+ };
132
+
133
+ type ObjectSchema<K extends keyof SettingsOptions> = SchemaMetadata<K> & {
134
+ /**
135
+ * Allowed type(s) of the value.
136
+ */
137
+ type: "object";
138
+ /**
139
+ * The default value. If not specified, an empty object will be used.
140
+ */
141
+ default?: Extract<Settings[K], MacroMap>;
142
+ };
143
+
144
+ type FunctionSchema<K extends keyof SettingsOptions> = SchemaMetadata<K> & {
145
+ /**
146
+ * Allowed type(s) of the value.
147
+ */
148
+ type: "function";
149
+ /**
150
+ * Settings do not currently use function-valued defaults.
151
+ */
152
+ default?: never;
153
+ };
154
+
155
+ type EnumSchema<K extends keyof SettingsOptions> = SchemaMetadata<K> & {
156
+ /**
157
+ * Allowed type(s) of the value.
158
+ */
159
+ type: EnumType<Extract<Settings[K], string>>;
160
+ /**
161
+ * The default value. If not specified, the first enum value will be used.
162
+ */
163
+ default?: Extract<Settings[K], string>;
164
+ };
165
+
166
+ type SingleTypeSchema<K extends keyof SettingsOptions> =
167
+ | BooleanSchema<K>
168
+ | StringSchema<K>
169
+ | NumberSchema<K>
170
+ | ObjectSchema<K>
171
+ | FunctionSchema<K>
172
+ | EnumSchema<K>;
173
+
174
+ type MultiTypeSchema<K extends keyof SettingsOptions> = SchemaMetadata<K> & {
175
+ /**
176
+ * Allowed type(s) of the value.
177
+ */
178
+ type: [SingleTypeSchema<K>["type"], ...Array<SingleTypeSchema<K>["type"]>];
179
+ /**
180
+ * The default value. If not specified, the first allowed type determines
181
+ * the default value.
182
+ */
183
+ default?: Extract<Settings[K], DefaultValue>;
102
184
  };
103
185
 
104
- type SchemaEntry = NonNullable<Schema[keyof SettingsOptions]>;
186
+ type SchemaEntry<K extends keyof SettingsOptions> =
187
+ | SingleTypeSchema<K>
188
+ | MultiTypeSchema<K>;
189
+
190
+ type Schema = {
191
+ [key in keyof SettingsOptions]?: SchemaEntry<key>;
192
+ };
105
193
 
106
194
  // TODO: automatically generate documentation
107
195
  // TODO: check all properties on Settings exist
@@ -208,16 +296,18 @@ export const SETTINGS_SCHEMA: Schema = {
208
296
  },
209
297
  };
210
298
 
211
- function getDefaultValue(schema: SchemaEntry): any {
212
- if ("default" in schema) {
213
- return schema.default;
214
- }
215
- const type = schema.type;
216
- const defaultType = Array.isArray(type) ? type[0] : type;
217
- if (typeof defaultType !== 'string') {
218
- return defaultType.enum[0];
299
+ function getImplicitDefault(type: "boolean"): boolean;
300
+ function getImplicitDefault(type: "string"): string;
301
+ function getImplicitDefault(type: "number"): number;
302
+ function getImplicitDefault(type: "object"): MacroMap;
303
+ function getImplicitDefault(type: "function"): never;
304
+ function getImplicitDefault<T extends string>(type: EnumType<T>): T;
305
+ function getImplicitDefault(type: Type): DefaultValue;
306
+ function getImplicitDefault(type: Type): DefaultValue {
307
+ if (typeof type !== 'string') {
308
+ return type.enum[0];
219
309
  }
220
- switch (defaultType) {
310
+ switch (type) {
221
311
  case 'boolean':
222
312
  return false;
223
313
  case 'string':
@@ -226,9 +316,37 @@ function getDefaultValue(schema: SchemaEntry): any {
226
316
  return 0;
227
317
  case 'object':
228
318
  return {};
319
+ default:
320
+ throw new Error(
321
+ "Unexpected schema type; settings must declare an explicit default.");
229
322
  }
230
323
  }
231
324
 
325
+ function getDefaultValue<K extends keyof SettingsOptions>(
326
+ schema: SchemaEntry<K>,
327
+ ): Settings[K];
328
+ function getDefaultValue(schema: SchemaEntry<keyof SettingsOptions>): DefaultValue {
329
+ if (schema.default !== undefined) {
330
+ return schema.default;
331
+ }
332
+ const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
333
+ return getImplicitDefault(type);
334
+ }
335
+
336
+ function applySetting<K extends keyof SettingsOptions>(
337
+ target: Settings,
338
+ prop: K,
339
+ options: SettingsOptions,
340
+ schema: SchemaEntry<K>,
341
+ ) {
342
+ const optionValue = options[prop];
343
+ target[prop] = optionValue !== undefined
344
+ ? (schema.processor
345
+ ? schema.processor(optionValue)
346
+ : optionValue)
347
+ : getDefaultValue(schema);
348
+ }
349
+
232
350
  /**
233
351
  * The main Settings object
234
352
  *
@@ -259,12 +377,11 @@ export default class Settings {
259
377
  // allow null options
260
378
  options = options || {};
261
379
  for (const prop of Object.keys(SETTINGS_SCHEMA) as Array<keyof SettingsOptions>) {
262
- const schema = SETTINGS_SCHEMA[prop] as SchemaEntry;
263
- const optionValue = options[prop];
264
- // TODO: validate options
265
- (this as Record<string, unknown>)[prop] = optionValue !== undefined ?
266
- (schema.processor ? schema.processor(optionValue) : optionValue)
267
- : getDefaultValue(schema);
380
+ const schema = SETTINGS_SCHEMA[prop] as SchemaEntry<typeof prop> | undefined;
381
+ if (schema) {
382
+ // TODO: validate options
383
+ applySetting(this, prop, options, schema);
384
+ }
268
385
  }
269
386
  }
270
387
 
package/src/atoms.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Small module for atom-group constants and type guard. Kept separate from
3
+ * `symbols.ts` so that consumers (notably `contrib/render-a11y-string`) can
4
+ * pull in `isAtom` without dragging in the ~870-line symbol tables.
5
+ */
6
+
7
+ // Some of these have a "-token" suffix since these are also used as `ParseNode`
8
+ // types for raw text tokens, and we want to avoid conflicts with higher-level
9
+ // `ParseNode` types. These `ParseNode`s are constructed within `Parser` by
10
+ // looking up the `symbols` map.
11
+ export const ATOMS = {
12
+ "bin": 1,
13
+ "close": 1,
14
+ "inner": 1,
15
+ "open": 1,
16
+ "punct": 1,
17
+ "rel": 1,
18
+ };
19
+ export const NON_ATOMS = {
20
+ "accent-token": 1,
21
+ "mathord": 1,
22
+ "op-token": 1,
23
+ "spacing": 1,
24
+ "textord": 1,
25
+ };
26
+
27
+ export type Atom = keyof typeof ATOMS;
28
+ export type NonAtom = keyof typeof NON_ATOMS;
29
+ export type Group = Atom | NonAtom;
30
+
31
+ export function isAtom(value: string): value is Atom {
32
+ return value in ATOMS;
33
+ }