wikiparser-node 1.19.0 → 1.20.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 (105) hide show
  1. package/README.md +3 -0
  2. package/bundle/bundle-es7.min.js +24 -31
  3. package/bundle/bundle-lsp.min.js +25 -98
  4. package/bundle/bundle.min.js +25 -32
  5. package/config/default.json +49 -3
  6. package/config/minimum.json +5 -1
  7. package/data/ext/math.json +660 -0
  8. package/dist/addon/table.js +24 -5
  9. package/dist/addon/transclude.js +4 -4
  10. package/dist/base.d.mts +1 -1
  11. package/dist/base.d.ts +1 -1
  12. package/dist/bin/config.js +26 -8
  13. package/dist/index.js +10 -1
  14. package/dist/internal.d.ts +2 -0
  15. package/dist/lib/document.d.ts +1 -0
  16. package/dist/lib/document.js +10 -1
  17. package/dist/lib/element.js +15 -21
  18. package/dist/lib/lsp.js +88 -58
  19. package/dist/lib/node.js +21 -24
  20. package/dist/lib/text.js +3 -4
  21. package/dist/lib/title.js +2 -1
  22. package/dist/mixin/attributesParent.js +1 -1
  23. package/dist/mixin/fixed.d.ts +1 -2
  24. package/dist/mixin/fixed.js +2 -3
  25. package/dist/mixin/gapped.d.ts +4 -0
  26. package/dist/mixin/gapped.js +24 -0
  27. package/dist/mixin/hidden.js +1 -1
  28. package/dist/mixin/multiLine.d.ts +4 -0
  29. package/dist/mixin/multiLine.js +33 -0
  30. package/dist/mixin/noEscape.js +1 -1
  31. package/dist/mixin/padded.d.ts +5 -0
  32. package/dist/mixin/padded.js +24 -0
  33. package/dist/mixin/singleLine.js +1 -1
  34. package/dist/mixin/sol.js +1 -1
  35. package/dist/mixin/syntax.js +3 -3
  36. package/dist/parser/braces.js +8 -12
  37. package/dist/parser/commentAndExt.js +18 -2
  38. package/dist/parser/magicLinks.js +2 -2
  39. package/dist/parser/selector.js +5 -2
  40. package/dist/src/arg.js +10 -13
  41. package/dist/src/atom.js +2 -7
  42. package/dist/src/attribute.js +6 -6
  43. package/dist/src/attributes.js +8 -12
  44. package/dist/src/commented.d.ts +26 -0
  45. package/dist/src/commented.js +52 -0
  46. package/dist/src/converter.js +3 -6
  47. package/dist/src/converterFlags.js +266 -223
  48. package/dist/src/converterRule.d.ts +2 -2
  49. package/dist/src/converterRule.js +2 -2
  50. package/dist/src/extLink.d.ts +2 -3
  51. package/dist/src/extLink.js +222 -175
  52. package/dist/src/gallery.d.ts +4 -5
  53. package/dist/src/gallery.js +177 -144
  54. package/dist/src/heading.js +11 -11
  55. package/dist/src/hidden.js +2 -7
  56. package/dist/src/imageParameter.d.ts +2 -2
  57. package/dist/src/imageParameter.js +6 -5
  58. package/dist/src/imagemap.d.ts +2 -2
  59. package/dist/src/imagemap.js +155 -123
  60. package/dist/src/imagemapLink.d.ts +2 -2
  61. package/dist/src/index.js +44 -20
  62. package/dist/src/link/base.d.ts +2 -3
  63. package/dist/src/link/base.js +13 -15
  64. package/dist/src/link/file.d.ts +2 -3
  65. package/dist/src/link/file.js +3 -3
  66. package/dist/src/link/galleryImage.d.ts +2 -3
  67. package/dist/src/link/galleryImage.js +7 -13
  68. package/dist/src/magicLink.js +7 -9
  69. package/dist/src/nested.d.ts +2 -2
  70. package/dist/src/nested.js +4 -7
  71. package/dist/src/nowiki/comment.js +2 -5
  72. package/dist/src/nowiki/doubleUnderscore.js +2 -5
  73. package/dist/src/nowiki/index.d.ts +2 -2
  74. package/dist/src/nowiki/index.js +2 -1
  75. package/dist/src/onlyinclude.js +6 -13
  76. package/dist/src/paramTag/index.d.ts +2 -2
  77. package/dist/src/paramTag/index.js +102 -66
  78. package/dist/src/parameter.d.ts +4 -5
  79. package/dist/src/parameter.js +13 -13
  80. package/dist/src/pre.d.ts +4 -5
  81. package/dist/src/pre.js +8 -17
  82. package/dist/src/syntax.d.ts +1 -1
  83. package/dist/src/syntax.js +2 -7
  84. package/dist/src/table/base.d.ts +2 -2
  85. package/dist/src/table/base.js +8 -8
  86. package/dist/src/table/index.js +1 -1
  87. package/dist/src/table/td.d.ts +3 -3
  88. package/dist/src/table/td.js +9 -9
  89. package/dist/src/table/tr.d.ts +2 -2
  90. package/dist/src/tagPair/ext.js +35 -30
  91. package/dist/src/tagPair/index.d.ts +1 -1
  92. package/dist/src/tagPair/index.js +2 -5
  93. package/dist/src/tagPair/translate.d.ts +38 -0
  94. package/dist/src/tagPair/translate.js +129 -0
  95. package/dist/src/transclude.js +5 -6
  96. package/dist/util/html.js +16 -1
  97. package/dist/util/lint.js +17 -30
  98. package/dist/util/sharable.js +29 -1
  99. package/dist/util/sharable.mjs +31 -3
  100. package/dist/util/string.js +14 -1
  101. package/extensions/dist/base.js +1 -1
  102. package/extensions/dist/lsp.js +13 -2
  103. package/package.json +4 -3
  104. package/extensions/es7/base.js +0 -320
  105. package/extensions/es7/lint.js +0 -97
@@ -36,7 +36,7 @@ transclude_1.TranscludeToken.prototype.setValue =
36
36
  const include = this.getAttribute('include'), config = this.getAttribute('config'), k = index_1.default.parse(key, include, undefined, config), v = index_1.default.parse(value, include, undefined, config),
37
37
  // @ts-expect-error abstract class
38
38
  token = debug_1.Shadow.run(() => new parameter_1.ParameterToken(undefined, undefined, config));
39
- token.firstChild.append(...k.childNodes);
39
+ token.firstChild.safeAppend(k.childNodes);
40
40
  token.lastChild.concat(v.childNodes); // eslint-disable-line unicorn/prefer-spread
41
41
  this.insertAt(token);
42
42
  };
@@ -49,7 +49,7 @@ transclude_1.TranscludeToken.prototype.replaceTemplate =
49
49
  }
50
50
  const { childNodes } = index_1.default
51
51
  .parse(title, this.getAttribute('include'), 2, this.getAttribute('config'));
52
- this.firstChild.replaceChildren(...childNodes);
52
+ this.firstChild.safeReplaceChildren(childNodes);
53
53
  };
54
54
  transclude_1.TranscludeToken.prototype.replaceModule =
55
55
  /** @implements */
@@ -66,7 +66,7 @@ transclude_1.TranscludeToken.prototype.replaceModule =
66
66
  return;
67
67
  }
68
68
  const { childNodes } = index_1.default.parse(title, this.getAttribute('include'), 2, config);
69
- this.childNodes[1].replaceChildren(...childNodes);
69
+ this.childNodes[1].safeReplaceChildren(childNodes);
70
70
  };
71
71
  transclude_1.TranscludeToken.prototype.replaceFunction =
72
72
  /** @implements */
@@ -86,7 +86,7 @@ transclude_1.TranscludeToken.prototype.replaceFunction =
86
86
  return;
87
87
  }
88
88
  const { childNodes } = index_1.default.parse(func, this.getAttribute('include'), 2, config);
89
- this.childNodes[2].replaceChildren(...childNodes);
89
+ this.childNodes[2].safeReplaceChildren(childNodes);
90
90
  };
91
91
  transclude_1.TranscludeToken.prototype.fixDuplication =
92
92
  /** @implements */
package/dist/base.d.mts CHANGED
@@ -18,7 +18,7 @@ export interface Config {
18
18
  readonly redirects?: [string, string][];
19
19
  }
20
20
  export type ConfigData = Omit<Config, 'excludes'>;
21
- export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
21
+ export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'translate' | 'translate-attr' | 'translate-inner' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
22
22
  export declare const stages: {
23
23
  redirect: number;
24
24
  onlyinclude: number;
package/dist/base.d.ts CHANGED
@@ -18,7 +18,7 @@ export interface Config {
18
18
  readonly redirects?: [string, string][];
19
19
  }
20
20
  export type ConfigData = Omit<Config, 'excludes'>;
21
- export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
21
+ export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'translate' | 'translate-attr' | 'translate-inner' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
22
22
  export declare const stages: {
23
23
  redirect: number;
24
24
  onlyinclude: number;
@@ -8,6 +8,7 @@ const path_1 = __importDefault(require("path"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const assert_1 = __importDefault(require("assert"));
10
10
  const cm_1 = require("@bhsd/common/dist/cm");
11
+ const diff_1 = require("../util/diff");
11
12
  /**
12
13
  * Converts an array to an object.
13
14
  * @param config parser configuration
@@ -85,7 +86,7 @@ let mwConfig;
85
86
  */
86
87
  exports.default = async (site, url, force, internal) => {
87
88
  if (!site || !url) {
88
- console.error('Usage: npx getParserConfig <site> <script path> [force]');
89
+ (0, diff_1.error)('Usage: npx getParserConfig <site> <script path> [force]');
89
90
  process.exit(1);
90
91
  }
91
92
  else if (/(?:\.php|\/)$/u.test(url)) {
@@ -97,8 +98,11 @@ exports.default = async (site, url, force, internal) => {
97
98
  siprop: 'general|magicwords|functionhooks|namespaces|namespacealiases',
98
99
  format: 'json',
99
100
  formatversion: '2',
100
- }, { query: { general: { articlepath, variants }, magicwords, namespaces, namespacealiases, functionhooks, }, } = await (await fetch(`${url}/api.php?${new URLSearchParams(params).toString()}`)).json(), { query: { variables } } = await (await fetch(`${url}/api.php?${new URLSearchParams({ ...params, siprop: 'variables' }).toString()}`)).json();
101
+ }, { query: { general: { articlepath, variants }, magicwords, namespaces, namespacealiases, functionhooks, }, } = await (await fetch(`${url}/api.php?${new URLSearchParams(params).toString()}`)).json();
101
102
  eval(m); // eslint-disable-line no-eval
103
+ if (!mwConfig) {
104
+ throw new RangeError('Extension:CodeMirror is not installed!');
105
+ }
102
106
  const dir = path_1.default.join('..', '..', 'config'), ns = Object.entries(namespaces).filter(([id]) => filterGadget(id))
103
107
  .flatMap(([id, { name, canonical = '' }]) => [
104
108
  [id, name],
@@ -118,6 +122,12 @@ exports.default = async (site, url, force, internal) => {
118
122
  config.doubleUnderscore[0] = [];
119
123
  config.doubleUnderscore[1] = [];
120
124
  Object.assign(config.parserFunction[0], (0, cm_1.getConfig)(magicwords, ({ name }) => name === 'msgnw'));
125
+ config.parserFunction[2] = getAliases(magicwords, new Set(['msg', 'raw']));
126
+ config.parserFunction[3] = getAliases(magicwords, new Set(['subst', 'safesubst']));
127
+ if (!mwConfig.variableIDs) {
128
+ const { query: { variables } } = await (await fetch(`${url}/api.php?${new URLSearchParams({ ...params, siprop: 'variables' }).toString()}`)).json();
129
+ Object.assign(config, { variable: [...new Set([...variables, '='])] });
130
+ }
121
131
  if ('#choose' in config.parserFunction[0]) {
122
132
  delete config.parserFunction[0]['choose'];
123
133
  const i = config.variable.indexOf('choose');
@@ -125,17 +135,25 @@ exports.default = async (site, url, force, internal) => {
125
135
  config.variable.splice(i, 1);
126
136
  }
127
137
  }
128
- config.parserFunction[2] = getAliases(magicwords, new Set(['msg', 'raw']));
129
- config.parserFunction[3] = getAliases(magicwords, new Set(['subst', 'safesubst']));
130
- if (!config.variable) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition
131
- Object.assign(config, { variable: [...new Set([...variables, '='])] });
132
- }
133
138
  const file = path_1.default.join(__dirname, dir, `${site}.json`);
134
139
  if (force || !fs_1.default.existsSync(file)) {
135
140
  fs_1.default.writeFileSync(file, `${JSON.stringify(config, null, '\t')}\n`);
136
141
  }
137
142
  else if (!internal) {
138
- assert_1.default.deepStrictEqual(arrToObj(require(file)), arrToObj(config));
143
+ const oldConfig = arrToObj(require(file)), newConfig = arrToObj(config);
144
+ for (const [k, v] of Object.entries(newConfig)) {
145
+ try {
146
+ assert_1.default.deepStrictEqual(oldConfig[k], v);
147
+ }
148
+ catch (e) {
149
+ if (e instanceof assert_1.default.AssertionError) {
150
+ (0, diff_1.error)(`Configuration mismatch for "${k}"`);
151
+ delete e.actual;
152
+ delete e.expected;
153
+ }
154
+ throw e;
155
+ }
156
+ }
139
157
  }
140
158
  return config;
141
159
  };
package/dist/index.js CHANGED
@@ -76,7 +76,7 @@ const Parser = {
76
76
  return this.getConfig();
77
77
  }
78
78
  /* NOT FOR BROWSER ONLY END */
79
- const parserConfig = config ?? this.config, { doubleUnderscore,
79
+ const parserConfig = config ?? this.config, { doubleUnderscore, ext, parserFunction, variable,
80
80
  /* NOT FOR BROWSER */
81
81
  conversionTable, redirects, } = parserConfig;
82
82
  for (let i = 0; i < 2; i++) {
@@ -84,6 +84,15 @@ const Parser = {
84
84
  doubleUnderscore[i] = Object.keys(doubleUnderscore[i + 2]);
85
85
  }
86
86
  }
87
+ if (ext.includes('translate') && !variable.includes('translationlanguage')) {
88
+ variable.push('translationlanguage');
89
+ if (Array.isArray(parserFunction[1])) {
90
+ parserFunction[1].push('TRANSLATIONLANGUAGE');
91
+ }
92
+ else {
93
+ parserFunction[1]['TRANSLATIONLANGUAGE'] = 'translationlanguage';
94
+ }
95
+ }
87
96
  /* NOT FOR BROWSER */
88
97
  if (conversionTable) {
89
98
  this.conversionTable = new Map(conversionTable);
@@ -45,3 +45,5 @@ export type { NestedToken } from './src/nested';
45
45
  export type { GalleryToken } from './src/gallery';
46
46
  export type { ImagemapLinkToken } from './src/imagemapLink';
47
47
  export type { ImagemapToken } from './src/imagemap';
48
+ export type { CommentedToken } from './src/commented';
49
+ export type { TranslateToken } from './src/tagPair/translate';
@@ -10,6 +10,7 @@ export declare const MathJax: Promise<Jax | undefined>;
10
10
  export declare const jsonTags: string[];
11
11
  export declare const jsonLSP: import("vscode-json-languageservice").LanguageService | undefined;
12
12
  export declare const cssLSP: import("vscode-css-languageservice").LanguageService | undefined;
13
+ export declare const htmlData: import("vscode-html-languageservice").IHTMLDataProvider | undefined;
13
14
  export declare const stylelint: Promise<import("stylelint").PublicApi | undefined>;
14
15
  /** embedded document */
15
16
  declare class EmbeddedDocument implements TextDocument {
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.EmbeddedCSSDocument = exports.EmbeddedJSONDocument = exports.stylelint = exports.cssLSP = exports.jsonLSP = exports.jsonTags = exports.MathJax = void 0;
6
+ exports.EmbeddedCSSDocument = exports.EmbeddedJSONDocument = exports.stylelint = exports.htmlData = exports.cssLSP = exports.jsonLSP = exports.jsonTags = exports.MathJax = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const common_1 = require("@bhsd/common");
9
9
  /* NOT FOR BROWSER */
@@ -70,6 +70,15 @@ exports.cssLSP = (() => {
70
70
  return undefined;
71
71
  }
72
72
  })();
73
+ exports.htmlData = (() => {
74
+ try {
75
+ return require('vscode-html-languageservice')
76
+ .getDefaultHTMLDataProvider();
77
+ }
78
+ catch {
79
+ return undefined;
80
+ }
81
+ })();
73
82
  exports.stylelint = (async () => {
74
83
  try {
75
84
  return (await import('stylelint')).default;
@@ -46,7 +46,6 @@ const node_1 = require("./node");
46
46
  const fs_1 = __importDefault(require("fs"));
47
47
  const path_1 = __importDefault(require("path"));
48
48
  const constants_1 = require("../util/constants");
49
- const html_1 = require("../util/html");
50
49
  const readOnly_1 = require("../mixin/readOnly");
51
50
  /**
52
51
  * HTMLElement-like
@@ -262,7 +261,7 @@ let AstElement = (() => {
262
261
  * @param condition 条件
263
262
  */
264
263
  #getElementsBy(condition) {
265
- const descendants = [];
264
+ let descendants = [];
266
265
  for (const child of this.childNodes) {
267
266
  if (child.type === 'text') {
268
267
  continue;
@@ -270,7 +269,7 @@ let AstElement = (() => {
270
269
  else if (condition(child)) {
271
270
  descendants.push(child);
272
271
  }
273
- descendants.push(...child.#getElementsBy(condition));
272
+ descendants = [...descendants, ...child.#getElementsBy(condition)];
274
273
  }
275
274
  return descendants;
276
275
  }
@@ -291,6 +290,10 @@ let AstElement = (() => {
291
290
  * @param elements nodes to be inserted / 插入节点
292
291
  */
293
292
  append(...elements) {
293
+ this.safeAppend(elements);
294
+ }
295
+ /** @private */
296
+ safeAppend(elements) {
294
297
  for (const element of elements) {
295
298
  this.insertAt(element);
296
299
  }
@@ -302,10 +305,14 @@ let AstElement = (() => {
302
305
  * @param elements nodes to be inserted / 新的子节点
303
306
  */
304
307
  replaceChildren(...elements) {
308
+ this.safeReplaceChildren(elements);
309
+ }
310
+ /** @private */
311
+ safeReplaceChildren(elements) {
305
312
  for (let i = this.length - 1; i >= 0; i--) {
306
313
  this.removeAt(i);
307
314
  }
308
- this.append(...elements);
315
+ this.safeAppend(elements);
309
316
  }
310
317
  /**
311
318
  * Modify the text child node
@@ -406,7 +413,10 @@ let AstElement = (() => {
406
413
  for (let i = 0, cur = start + this.getAttribute('padding'); i < this.length; i++) {
407
414
  const child = this.childNodes[i];
408
415
  child.setAttribute('aIndex', cur);
409
- errors.push(...child.lint(cur, re));
416
+ const childErrors = child.lint(cur, re);
417
+ if (childErrors.length > 0) {
418
+ errors.push(...childErrors);
419
+ }
410
420
  cur += child.toString().length + this.getGaps(i);
411
421
  }
412
422
  return errors;
@@ -575,22 +585,6 @@ let AstElement = (() => {
575
585
  child.escape();
576
586
  }
577
587
  }
578
- /** @private */
579
- toHtmlInternal(opt) {
580
- for (const child of this.childNodes) {
581
- if (child.type === 'text') {
582
- child.removeBlankLines();
583
- }
584
- }
585
- for (let i = 0; i < this.length; i++) {
586
- const child = this.childNodes[i];
587
- if (child.is('list') || child.is('dd')) {
588
- child.getRange();
589
- }
590
- }
591
- this.normalize();
592
- return (0, html_1.html)(this.childNodes, '', opt);
593
- }
594
588
  };
595
589
  })();
596
590
  exports.AstElement = AstElement;
package/dist/lib/lsp.js CHANGED
@@ -24,7 +24,7 @@ const document_1 = require("./document");
24
24
  const cssRules = {
25
25
  'block-no-empty': null,
26
26
  'property-no-unknown': null,
27
- }, jsonSelector = document_1.jsonTags.map(s => `ext#${s}`).join(), mathSelector = ['math', 'chem', 'ce'].map(s => `ext#${s}`).join(), scores = new Map();
27
+ }, jsonSelector = document_1.jsonTags.map(s => `ext#${s}`).join(), mathTags = ['math', 'chem', 'ce'], mathSelector = mathTags.map(s => `ext#${s}`).join(), scores = new Map();
28
28
  let colors;
29
29
  /* NOT FOR BROWSER ONLY END */
30
30
  exports.tasks = new WeakMap();
@@ -286,22 +286,25 @@ class LanguageService {
286
286
  data;
287
287
  /* NOT FOR BROWSER ONLY */
288
288
  lilypond;
289
- /** @private */
290
- lilypondData;
289
+ #lilypondData;
290
+ #mathData;
291
+ #mathSet;
291
292
  /* NOT FOR BROWSER ONLY END */
292
293
  /** @param uri 任务标识 */
293
294
  constructor(uri) {
294
295
  exports.tasks.set(uri, this);
296
+ /* NOT FOR BROWSER ONLY */
297
+ const dataDir = path_1.default.join('..', '..', 'data'), extDir = path_1.default.join(dataDir, 'ext');
298
+ this.#lilypondData = require(path_1.default.join(extDir, 'score'));
299
+ this.#mathData = require(path_1.default.join(extDir, 'math'));
300
+ this.#mathSet = new Set(this.#mathData);
301
+ /* NOT FOR BROWSER ONLY END */
295
302
  Object.defineProperties(this, {
296
303
  config: { enumerable: false },
297
304
  data: {
298
- value: require(path_1.default.join('..', '..', 'data', 'signatures')),
299
- enumerable: false,
300
- },
301
- /* NOT FOR BROWSER ONLY */
302
- lilypondData: {
303
- value: require(path_1.default.join('..', '..', 'data', 'ext', 'score')),
304
305
  enumerable: false,
306
+ /* NOT FOR BROWSER ONLY */
307
+ value: require(path_1.default.join(dataDir, 'signatures')),
305
308
  },
306
309
  });
307
310
  }
@@ -412,14 +415,15 @@ class LanguageService {
412
415
  async provideDocumentColors(rgba, text, hsl = true) {
413
416
  const root = await this.#queue(text);
414
417
  /* NOT FOR BROWSER ONLY */
415
- /* eslint-disable require-atomic-updates */
416
- try {
417
- colors ??= new RegExp(String.raw `\b${Object.keys((await import('color-name')).default).join('|')}\b`, 'giu');
418
- }
419
- catch {
420
- colors = false;
421
- }
422
- /* eslint-enable require-atomic-updates */
418
+ colors ??= (async () => {
419
+ try {
420
+ return new RegExp(String.raw `\b${Object.keys((await import('color-name')).default).join('|')}\b`, 'giu');
421
+ }
422
+ catch {
423
+ return false;
424
+ }
425
+ })();
426
+ const re = await colors;
423
427
  /* NOT FOR BROWSER ONLY END */
424
428
  return root.querySelectorAll('attr-value,parameter-value,arg-default').reverse()
425
429
  .flatMap(token => {
@@ -436,14 +440,14 @@ class LanguageService {
436
440
  /* NOT FOR BROWSER ONLY END */
437
441
  }
438
442
  /* NOT FOR BROWSER ONLY */
439
- const isStyle = colors && type === 'attr-value' && parentNode.name === 'style';
443
+ const isStyle = re && type === 'attr-value' && parentNode.name === 'style';
440
444
  /* NOT FOR BROWSER ONLY END */
441
445
  return childNodes.filter((child) => child.type === 'text').reverse()
442
446
  .flatMap(child => {
443
447
  const { data } = child, parts = (0, common_1.splitColors)(data, hsl).filter(([, , , isColor]) => isColor);
444
448
  /* NOT FOR BROWSER ONLY */
445
449
  if (isStyle) {
446
- parts.push(...[...data.matchAll(colors)].map(({ index, 0: s }) => [s, index, index + s.length, true]));
450
+ parts.push(...[...data.matchAll(re)].map(({ index, 0: s }) => [s, index, index + s.length, true]));
447
451
  }
448
452
  /* NOT FOR BROWSER ONLY END */
449
453
  if (parts.length === 0) {
@@ -487,13 +491,13 @@ class LanguageService {
487
491
  this.config ??= index_1.default.getConfig();
488
492
  const { nsid, ext, html, parserFunction: [insensitive, sensitive, ...other], doubleUnderscore, protocol, img, } = this.config, tags = new Set([ext, html].flat(2));
489
493
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions, es-x/no-regexp-unicode-property-escapes
490
- /(?:<\/?(\w*)|(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)?|(__(?:(?!__)[\p{L}\d_])*)|(?<!\[)\[([a-z:/]*)|\[\[\s*(?:file|image)\s*:[^[\]{}<>]+\|([^[\]{}<>|=]*)|<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\8))?(?=\s))*)?\s(\w*))$/iu;
494
+ /(?:<\/?(\w*)|(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)?|(__(?:(?!__)[\p{L}\p{N}_])*)|(?<!\[)\[([a-z:/]*)|\[\[\s*(?:file|image)\s*:[^[\]{}<>]+\|([^[\]{}<>|=]*)|<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\8))?(?=\s))*)?\s(\w*))$/iu;
491
495
  const re = new RegExp('(?:' // eslint-disable-line prefer-template
492
496
  + String.raw `<(\/?\w*)` // tag
493
497
  + '|'
494
498
  + String.raw `(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)?` // braces and brackets
495
499
  + '|'
496
- + String.raw `(__(?:(?!__)[\p{L}\d_])*)` // behavior switch
500
+ + String.raw `(__(?:(?!__)[\p{L}\p{N}_])*)` // behavior switch
497
501
  + '|'
498
502
  + String.raw `(?<!\[)\[([a-z:/]*)` // protocol
499
503
  + '|'
@@ -692,23 +696,32 @@ class LanguageService {
692
696
  }
693
697
  const word = /\\?\b(?:\w|\b(?:->?|\.)|\bly:)+$/u.exec(curLine.slice(0, character))?.[0];
694
698
  if (word) {
695
- const { lilypondData } = this;
699
+ const data = this.#lilypondData;
696
700
  return word.startsWith('\\')
697
- ? getCompletion(lilypondData.filter(w => w.startsWith('\\')), 'Function', word, position)
701
+ ? getCompletion(data.filter(w => w.startsWith('\\')), 'Function', word, position)
698
702
  : [
699
- ...getCompletion(lilypondData.filter(w => /^[a-z]/u.test(w)), 'Variable', word, position),
700
- ...getCompletion(lilypondData.filter(w => /^[A-Z]/u.test(w)), 'Class', word, position),
703
+ ...getCompletion(data.filter(w => /^[a-z]/u.test(w)), 'Variable', word, position),
704
+ ...getCompletion(data.filter(w => /^[A-Z]/u.test(w)), 'Class', word, position),
701
705
  ];
702
706
  }
707
+ }
708
+ else if (type === 'ext-inner' && mathTags.includes(cur.name)) {
709
+ const word = /\\\w+$/u.exec(curLine.slice(0, character))?.[0];
710
+ if (word) {
711
+ const data = this.#mathData;
712
+ return getCompletion(cur.name === 'math' && parentNode.getAttr('chem') !== undefined
713
+ ? [...data, String.raw `\ce`]
714
+ : data, 'Function', word, position);
715
+ }
703
716
  /* NOT FOR BROWSER ONLY END */
704
717
  }
705
718
  else if ((0, exports.isAttr)(cur) && isHtmlAttr(parentNode)) {
706
- const data = lint_1.htmlData.provideValues(parentNode.tag, parentNode.name);
719
+ const data = (0, lint_1.provideValues)(parentNode.tag, parentNode.name);
707
720
  if (data.length === 0) {
708
721
  return undefined;
709
722
  }
710
723
  const val = this.#text.slice(cur.getAbsoluteIndex(), root.indexFromPos(line, character)).trimStart();
711
- return getCompletion(data.map(({ name }) => name), 'Value', val, position);
724
+ return getCompletion(data, 'Value', val, position);
712
725
  }
713
726
  return undefined;
714
727
  }
@@ -833,32 +846,42 @@ class LanguageService {
833
846
  }));
834
847
  }
835
848
  }
836
- const MathJax = await document_1.MathJax, mathDiagnostics = MathJax
837
- ? root.querySelectorAll(mathSelector)
838
- .map(({ selfClosing, innerText, lastChild, name }) => {
839
- if (selfClosing) {
840
- return [];
841
- }
849
+ const MathJax = await document_1.MathJax, data = this.#mathSet, mathDiagnostics = root.querySelectorAll(mathSelector)
850
+ .map(token => {
851
+ const { selfClosing, innerText, lastChild, name } = token;
852
+ if (selfClosing) {
853
+ return [];
854
+ }
855
+ const hasCe = name === 'math' && token.getAttr('chem') !== undefined, mathErrors = [...innerText.matchAll(/\\\w+/gu)]
856
+ .filter(([macro]) => !(hasCe && macro === String.raw `\ce` || data.has(macro)))
857
+ .map(({ 0: macro, index }) => {
858
+ const aIndex = lastChild.getAbsoluteIndex() + index;
859
+ return {
860
+ range: createRange(root, aIndex, aIndex + macro.length),
861
+ severity: 2,
862
+ source: 'MathJax',
863
+ code: 'UnknownMacro',
864
+ message: `Unknown macro "${macro}"`,
865
+ };
866
+ });
867
+ if (MathJax) {
842
868
  try {
843
869
  MathJax.tex2mml(name === 'math' ? innerText : String.raw `\ce{${innerText}}`);
844
- return [];
845
870
  }
846
871
  catch (e) {
847
872
  if (e && typeof e === 'object' && 'id' in e && 'message' in e) {
848
- return [
849
- {
850
- range: createNodeRange(lastChild),
851
- severity: 1,
852
- source: 'MathJax',
853
- code: e.id,
854
- message: e.message,
855
- },
856
- ];
873
+ mathErrors.push({
874
+ range: createNodeRange(lastChild),
875
+ severity: 1,
876
+ source: 'MathJax',
877
+ code: e.id,
878
+ message: e.message,
879
+ });
857
880
  }
858
- return [];
859
881
  }
860
- })
861
- : [];
882
+ }
883
+ return mathErrors;
884
+ });
862
885
  /* NOT FOR BROWSER ONLY END */
863
886
  return [
864
887
  diagnostics,
@@ -921,7 +944,10 @@ class LanguageService {
921
944
  if (document_1.jsonLSP) {
922
945
  for (const { selfClosing, lastChild } of root.querySelectorAll(jsonSelector)) {
923
946
  if (!selfClosing) {
924
- ranges.push(...document_1.jsonLSP.getFoldingRanges(new document_1.EmbeddedJSONDocument(root, lastChild)));
947
+ const foldingRanges = document_1.jsonLSP.getFoldingRanges(new document_1.EmbeddedJSONDocument(root, lastChild));
948
+ if (foldingRanges.length > 0) {
949
+ ranges.push(...foldingRanges);
950
+ }
925
951
  }
926
952
  }
927
953
  }
@@ -1184,10 +1210,10 @@ class LanguageService {
1184
1210
  const textDoc = new document_1.EmbeddedJSONDocument(root, offsetNode);
1185
1211
  return await document_1.jsonLSP.doHover(textDoc, position, textDoc.jsonDoc) ?? undefined;
1186
1212
  }
1187
- else if (lint_1.htmlData.provideTags && lint_1.htmlData.provideAttributes) {
1213
+ else if (document_1.htmlData) {
1188
1214
  if (type === 'html' && offset <= offsetNode.getRelativeIndex(0)
1189
1215
  || type === 'html-attr-dirty' && offset === 0 && parentNode.firstChild === offsetNode) {
1190
- const token = type === 'html' ? offsetNode : parentNode.parentNode, data = lint_1.htmlData.provideTags().find(({ name: n }) => n === token.name);
1216
+ const token = type === 'html' ? offsetNode : parentNode.parentNode, data = document_1.htmlData.provideTags().find(({ name: n }) => n === token.name);
1191
1217
  if (data?.description) {
1192
1218
  const start = positionAt(root, token.getAbsoluteIndex());
1193
1219
  return {
@@ -1203,7 +1229,7 @@ class LanguageService {
1203
1229
  }
1204
1230
  }
1205
1231
  else if (type === 'attr-key' && isHtmlAttr(parentNode)) {
1206
- const data = lint_1.htmlData.provideAttributes(parentNode.tag).find(({ name: n }) => n === parentNode.name);
1232
+ const data = document_1.htmlData.provideAttributes(parentNode.tag).find(({ name: n }) => n === parentNode.name);
1207
1233
  if (data?.description) {
1208
1234
  return {
1209
1235
  contents: data.description,
@@ -1279,16 +1305,20 @@ class LanguageService {
1279
1305
  * @param text source Wikitext / 源代码
1280
1306
  */
1281
1307
  async provideInlayHints(text) {
1282
- const hints = [], root = await this.#queue(text);
1308
+ const root = await this.#queue(text);
1309
+ let hints = [];
1283
1310
  for (const token of root.querySelectorAll('template,magic-word#invoke').reverse()) {
1284
1311
  const { type, childNodes } = token;
1285
- hints.push(...childNodes.slice(type === 'template' ? 1 : 3).filter(({ anon }) => anon)
1286
- .reverse()
1287
- .map((parameter) => ({
1288
- position: positionAt(root, parameter.getAbsoluteIndex()),
1289
- label: `${parameter.name}=`,
1290
- kind: 2,
1291
- })));
1312
+ hints = [
1313
+ ...hints,
1314
+ ...childNodes.slice(type === 'template' ? 1 : 3).filter(({ anon }) => anon)
1315
+ .reverse()
1316
+ .map((parameter) => ({
1317
+ position: positionAt(root, parameter.getAbsoluteIndex()),
1318
+ label: `${parameter.name}=`,
1319
+ kind: 2,
1320
+ })),
1321
+ ];
1292
1322
  }
1293
1323
  return hints;
1294
1324
  }
package/dist/lib/node.js CHANGED
@@ -302,20 +302,6 @@ class AstNode {
302
302
  ...this.getRootNode().posFromIndex(this.getAbsoluteIndex()),
303
303
  };
304
304
  }
305
- /** @private */
306
- seal(key, permanent) {
307
- /* NOT FOR BROWSER */
308
- if (!permanent) {
309
- this.#optional.add(key);
310
- }
311
- /* NOT FOR BROWSER END */
312
- Object.defineProperty(this, key, {
313
- enumerable: !permanent && Boolean(this[key]),
314
- configurable: true,
315
- /* NOT FOR BROWSER */
316
- writable: false,
317
- });
318
- }
319
305
  /**
320
306
  * Whether to be of a certain type
321
307
  *
@@ -344,6 +330,22 @@ class AstNode {
344
330
  this.#lines = value;
345
331
  });
346
332
  }
333
+ /* PRINT ONLY */
334
+ /** @private */
335
+ seal(key, permanent) {
336
+ /* NOT FOR BROWSER */
337
+ if (!permanent) {
338
+ this.#optional.add(key);
339
+ }
340
+ /* NOT FOR BROWSER END */
341
+ Object.defineProperty(this, key, {
342
+ enumerable: !permanent && Boolean(this[key]),
343
+ configurable: true,
344
+ /* NOT FOR BROWSER */
345
+ writable: false,
346
+ });
347
+ }
348
+ /* PRINT ONLY END */
347
349
  /* NOT FOR BROWSER */
348
350
  /* istanbul ignore next */
349
351
  /** @private */
@@ -374,13 +376,8 @@ class AstNode {
374
376
  }
375
377
  return true;
376
378
  }
377
- /**
378
- * 在当前节点前后插入兄弟节点
379
- * @param nodes 插入节点
380
- * @param offset 插入的相对位置
381
- * @throws `Error` 不存在父节点
382
- */
383
- #insertAdjacent(nodes, offset) {
379
+ /** @private */
380
+ insertAdjacent(nodes, offset) {
384
381
  const { parentNode } = this;
385
382
  /* istanbul ignore if */
386
383
  if (!parentNode) {
@@ -398,7 +395,7 @@ class AstNode {
398
395
  * @param nodes nodes to be inserted / 插入节点
399
396
  */
400
397
  after(...nodes) {
401
- this.#insertAdjacent(nodes, 1);
398
+ this.insertAdjacent(nodes, 1);
402
399
  }
403
400
  /**
404
401
  * Insert a batch of sibling nodes before the current node
@@ -407,7 +404,7 @@ class AstNode {
407
404
  * @param nodes nodes to be inserted / 插入节点
408
405
  */
409
406
  before(...nodes) {
410
- this.#insertAdjacent(nodes, 0);
407
+ this.insertAdjacent(nodes, 0);
411
408
  }
412
409
  /**
413
410
  * Remove the current node
@@ -424,7 +421,7 @@ class AstNode {
424
421
  * @param nodes nodes to be inserted / 插入节点
425
422
  */
426
423
  replaceWith(...nodes) {
427
- this.after(...nodes);
424
+ this.insertAdjacent(nodes, 1);
428
425
  this.remove();
429
426
  }
430
427
  /**