@markuplint/parser-utils 4.7.0 → 4.7.2

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/CHANGELOG.md CHANGED
@@ -3,16 +3,21 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- # [4.7.0](https://github.com/markuplint/markuplint/compare/@markuplint/parser-utils@4.6.8...@markuplint/parser-utils@4.7.0) (2024-10-15)
6
+ ## [4.7.2](https://github.com/markuplint/markuplint/compare/@markuplint/parser-utils@4.7.1...@markuplint/parser-utils@4.7.2) (2024-10-28)
7
7
 
8
+ **Note:** Version bump only for package @markuplint/parser-utils
8
9
 
9
- ### Features
10
+ ## [4.7.1](https://github.com/markuplint/markuplint/compare/@markuplint/parser-utils@4.7.0...@markuplint/parser-utils@4.7.1) (2024-10-27)
10
11
 
11
- * **parser-utils:** expose `getOffsetsFromCode` function ([8ef7aec](https://github.com/markuplint/markuplint/commit/8ef7aec26d3198328c86ebeffaa0bd9c879a1f0e))
12
+ ### Performance Improvements
12
13
 
14
+ - **parser-utils:** adjusted siblings correction timing to reduce exponential complexity ([676357c](https://github.com/markuplint/markuplint/commit/676357c438df7545f472787c9032463f9fdba515))
13
15
 
16
+ # [4.7.0](https://github.com/markuplint/markuplint/compare/@markuplint/parser-utils@4.6.8...@markuplint/parser-utils@4.7.0) (2024-10-15)
14
17
 
18
+ ### Features
15
19
 
20
+ - **parser-utils:** expose `getOffsetsFromCode` function ([8ef7aec](https://github.com/markuplint/markuplint/commit/8ef7aec26d3198328c86ebeffaa0bd9c879a1f0e))
16
21
 
17
22
  ## [4.6.8](https://github.com/markuplint/markuplint/compare/@markuplint/parser-utils@4.6.7...@markuplint/parser-utils@4.6.8) (2024-10-14)
18
23
 
package/lib/debug.d.ts CHANGED
@@ -2,3 +2,8 @@ import type { MLASTNode } from '@markuplint/ml-ast';
2
2
  import debug from 'debug';
3
3
  export declare const log: debug.Debugger;
4
4
  export declare function domLog(nodeList: readonly (MLASTNode | null)[]): void;
5
+ export declare class PerformanceTimer {
6
+ #private;
7
+ push(name?: string): "" | undefined;
8
+ log(): void;
9
+ }
package/lib/debug.js CHANGED
@@ -1,6 +1,62 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _PerformanceTimer_logs, _PerformanceTimer_counter;
1
13
  import debug from 'debug';
2
14
  import { nodeListToDebugMaps } from './debugger.js';
3
15
  export const log = debug('ml-parser');
4
16
  export function domLog(nodeList) {
5
17
  log('Parse result: %O', nodeListToDebugMaps(nodeList, true));
6
18
  }
19
+ export class PerformanceTimer {
20
+ constructor() {
21
+ _PerformanceTimer_logs.set(this, []);
22
+ _PerformanceTimer_counter.set(this, -1);
23
+ }
24
+ push(name) {
25
+ var _a;
26
+ if (!log.enabled) {
27
+ return '';
28
+ }
29
+ __classPrivateFieldSet(this, _PerformanceTimer_counter, (_a = __classPrivateFieldGet(this, _PerformanceTimer_counter, "f"), _a++, _a), "f");
30
+ const now = performance.now();
31
+ const last = __classPrivateFieldGet(this, _PerformanceTimer_logs, "f").at(-1);
32
+ if (last && Number.isNaN(last[2])) {
33
+ last[2] = now;
34
+ }
35
+ name = name || `#${__classPrivateFieldGet(this, _PerformanceTimer_counter, "f")}`;
36
+ __classPrivateFieldGet(this, _PerformanceTimer_logs, "f").push([name, now, Number.NaN]);
37
+ }
38
+ log() {
39
+ if (!log.enabled) {
40
+ return;
41
+ }
42
+ this.push('end');
43
+ __classPrivateFieldGet(this, _PerformanceTimer_logs, "f").pop();
44
+ const map = new Map();
45
+ for (const content of __classPrivateFieldGet(this, _PerformanceTimer_logs, "f")) {
46
+ const diff = content[2] - content[1];
47
+ const name = content[0];
48
+ if (map.has(name)) {
49
+ const [total, count] = map.get(name);
50
+ map.set(name, [total + diff, count + 1]);
51
+ }
52
+ else {
53
+ map.set(name, [diff, 1]);
54
+ }
55
+ }
56
+ for (const [name, [total, count]] of map) {
57
+ const avg = total / count;
58
+ log.extend(name)('%dms (avg: %dms, count: %d)', total.toExponential(3), avg.toExponential(3), count);
59
+ }
60
+ }
61
+ }
62
+ _PerformanceTimer_logs = new WeakMap(), _PerformanceTimer_counter = new WeakMap();
package/lib/parser.js CHANGED
@@ -9,12 +9,12 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
- var _Parser_instances, _Parser_booleanish, _Parser_defaultState, _Parser_endTagType, _Parser_ignoreTags, _Parser_maskChar, _Parser_tagNameCaseSensitive, _Parser_selfCloseType, _Parser_spaceChars, _Parser_rawTextElements, _Parser_authoredElementName, _Parser_originalRawCode, _Parser_rawCode, _Parser_defaultDepth, _Parser_walkMethodSequentailPrevNode, _Parser_arrayize, _Parser_concatText, _Parser_concatTextNodes, _Parser_convertIntoInvalidNode, _Parser_createOffsetSpaces, _Parser_createRemnantNode, _Parser_exposeRemnantNodes, _Parser_getEndLocation, _Parser_orphanEndTagToBogusMark, _Parser_pairing, _Parser_parseEndTag, _Parser_parseStartTag, _Parser_parseTag, _Parser_removeChild, _Parser_removeDeprecatedNode, _Parser_removeOffsetSpaces, _Parser_reset, _Parser_setRawCode, _Parser_siblingsCorrection, _Parser_trimText;
12
+ var _Parser_instances, _Parser_booleanish, _Parser_defaultState, _Parser_endTagType, _Parser_ignoreTags, _Parser_maskChar, _Parser_tagNameCaseSensitive, _Parser_selfCloseType, _Parser_spaceChars, _Parser_rawTextElements, _Parser_authoredElementName, _Parser_originalRawCode, _Parser_rawCode, _Parser_defaultDepth, _Parser_walkMethodSequentailPrevNode, _Parser_arrayize, _Parser_concatText, _Parser_concatTextNodes, _Parser_convertIntoInvalidNode, _Parser_createOffsetSpaces, _Parser_createRemnantNode, _Parser_exposeRemnantNodes, _Parser_getEndLocation, _Parser_orphanEndTagToBogusMark, _Parser_pairing, _Parser_parseEndTag, _Parser_parseStartTag, _Parser_parseTag, _Parser_removeChild, _Parser_removeDeprecatedNode, _Parser_removeOffsetSpaces, _Parser_reset, _Parser_setRawCode, _Parser_trimText;
13
13
  import { isVoidElement as detectVoidElement } from '@markuplint/ml-spec';
14
14
  import { v4 as uuid } from 'uuid';
15
15
  import { attrTokenizer } from './attr-tokenizer.js';
16
16
  import { defaultSpaces } from './const.js';
17
- import { domLog } from './debug.js';
17
+ import { domLog, PerformanceTimer } from './debug.js';
18
18
  import { detectElementType } from './detect-element-type.js';
19
19
  import { AttrState, TagState } from './enums.js';
20
20
  import { getEndCol, getEndLine, getOffsetsFromCode, getPosition } from './get-location.js';
@@ -22,6 +22,7 @@ import { ignoreBlock, restoreNode } from './ignore-block.js';
22
22
  import { ignoreFrontMatter } from './ignore-front-matter.js';
23
23
  import { ParserError } from './parser-error.js';
24
24
  import { sortNodes } from './sort-nodes.js';
25
+ const timer = new PerformanceTimer();
25
26
  export class Parser {
26
27
  constructor(options, defaultState) {
27
28
  _Parser_instances.add(this);
@@ -96,28 +97,40 @@ export class Parser {
96
97
  try {
97
98
  // Initialize raw code
98
99
  __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, rawCode, rawCode);
100
+ timer.push('beforeParse');
101
+ const beforeParsedCode = this.beforeParse(this.rawCode, options);
99
102
  // Override raw code
100
- __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, this.beforeParse(this.rawCode, options));
103
+ __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, beforeParsedCode);
101
104
  __classPrivateFieldSet(this, _Parser_authoredElementName, options?.authoredElementName, "f");
102
105
  let frontMatter = null;
103
106
  if (options?.ignoreFrontMatter) {
107
+ timer.push('ignoreFrontMatter');
104
108
  const fm = ignoreFrontMatter(this.rawCode);
105
109
  __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, fm.code);
106
110
  frontMatter = fm.frontMatter;
107
111
  }
112
+ timer.push('ignoreBlock');
108
113
  const blocks = ignoreBlock(this.rawCode, __classPrivateFieldGet(this, _Parser_ignoreTags, "f"), __classPrivateFieldGet(this, _Parser_maskChar, "f"));
109
114
  __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_setRawCode).call(this, blocks.replaced);
115
+ timer.push('tokenize');
110
116
  const tokenized = this.tokenize(options);
111
117
  const ast = tokenized.ast;
112
118
  const isFragment = tokenized.isFragment;
113
119
  __classPrivateFieldSet(this, _Parser_defaultDepth, options?.depth ?? __classPrivateFieldGet(this, _Parser_defaultDepth, "f"), "f");
120
+ timer.push('traverse');
114
121
  const traversed = this.traverse(ast, null, __classPrivateFieldGet(this, _Parser_defaultDepth, "f"));
122
+ timer.push('afterTraverse');
115
123
  const nodeTree = this.afterTraverse([...traversed.childNodes, ...traversed.siblings]);
124
+ timer.push('flattenNodes');
116
125
  let nodeList = this.flattenNodes(nodeTree);
126
+ timer.push('afterFlattenNodes');
117
127
  nodeList = this.afterFlattenNodes(nodeList);
128
+ timer.push('restoreNode');
118
129
  nodeList = restoreNode(this, nodeList, blocks, false);
130
+ timer.push('afterParse');
119
131
  nodeList = this.afterParse(nodeList, options);
120
132
  if (frontMatter) {
133
+ timer.push('frontMatter');
121
134
  const newNodeList = [...nodeList];
122
135
  let firstText = '';
123
136
  const firstTextNode = newNodeList.shift();
@@ -142,6 +155,7 @@ export class Parser {
142
155
  }
143
156
  nodeList = [fmNode, ...newNodeList];
144
157
  }
158
+ timer.log();
145
159
  domLog(nodeList);
146
160
  __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_reset).call(this);
147
161
  return {
@@ -174,9 +188,22 @@ export class Parser {
174
188
  }
175
189
  const childNodes = [];
176
190
  const siblings = [];
191
+ const existence = new Set();
177
192
  for (const originNode of originNodes) {
193
+ timer.push('nodeize');
178
194
  const nodes = this.nodeize(originNode, parentNode, depth);
179
- const after = this.afterNodeize(nodes, parentNode, depth);
195
+ const filteredNodes = [];
196
+ for (const node of nodes) {
197
+ // Remove duplicated nodes
198
+ const id = `${node.startOffset}:${node.endOffset}:${node.nodeName}:${node.type}:${node.raw}`;
199
+ if (existence.has(id)) {
200
+ continue;
201
+ }
202
+ existence.add(id);
203
+ filteredNodes.push(node);
204
+ }
205
+ timer.push('afterNodeize');
206
+ const after = this.afterNodeize(filteredNodes, parentNode, depth);
180
207
  childNodes.push(...after.siblings);
181
208
  siblings.push(...after.ancestors);
182
209
  }
@@ -186,7 +213,10 @@ export class Parser {
186
213
  };
187
214
  }
188
215
  afterTraverse(nodeTree) {
189
- return __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_siblingsCorrection).call(this, nodeTree);
216
+ return Array.prototype.toSorted == null
217
+ ? // TODO: Use sort instead of toSorted until we end support for Node 18
218
+ [...nodeTree].sort(sortNodes)
219
+ : nodeTree.toSorted(sortNodes);
190
220
  }
191
221
  nodeize(originNode, parentNode, depth) {
192
222
  return [];
@@ -232,6 +262,7 @@ export class Parser {
232
262
  return nodeList;
233
263
  }
234
264
  visitDoctype(token) {
265
+ timer.push('visitDoctype');
235
266
  const node = {
236
267
  ...token,
237
268
  ...this.createToken(token),
@@ -241,6 +272,7 @@ export class Parser {
241
272
  return [node];
242
273
  }
243
274
  visitComment(token, options) {
275
+ timer.push('visitComment');
244
276
  const isBogus = options?.isBogus ?? !token.raw.startsWith('<!--');
245
277
  const node = {
246
278
  ...token,
@@ -252,6 +284,7 @@ export class Parser {
252
284
  return [node];
253
285
  }
254
286
  visitText(token, options) {
287
+ timer.push('visitText');
255
288
  const node = {
256
289
  ...token,
257
290
  ...this.createToken(token),
@@ -270,6 +303,7 @@ export class Parser {
270
303
  return [node];
271
304
  }
272
305
  visitElement(token, childNodes = [], options) {
306
+ timer.push('visitElement');
273
307
  const createEndTagToken = options?.createEndTagToken;
274
308
  const namelessFragment = options?.namelessFragment ?? false;
275
309
  const overwriteProps = options?.overwriteProps;
@@ -311,6 +345,7 @@ export class Parser {
311
345
  return [startTag, ...siblings];
312
346
  }
313
347
  visitPsBlock(token, childNodes = [], conditionalType = null, originBlockNode) {
348
+ timer.push('visitPsBlock');
314
349
  const block = {
315
350
  ...token,
316
351
  ...this.createToken(token),
@@ -335,6 +370,7 @@ export class Parser {
335
370
  return traversed.siblings;
336
371
  }
337
372
  visitSpreadAttr(token) {
373
+ timer.push('visitSpreadAttr');
338
374
  const raw = token.raw.trim();
339
375
  if (raw === '') {
340
376
  return null;
@@ -355,6 +391,7 @@ export class Parser {
355
391
  };
356
392
  }
357
393
  visitAttr(token, options) {
394
+ timer.push('visitAttr');
358
395
  const raw = token.raw;
359
396
  const quoteSet = options?.quoteSet;
360
397
  const startState = options?.startState ?? AttrState.BeforeName;
@@ -616,7 +653,10 @@ export class Parser {
616
653
  newChildNodes.splice(currentIndex, 1, appendingChild);
617
654
  }
618
655
  Object.assign(parentNode, {
619
- childNodes: __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_siblingsCorrection).call(this, newChildNodes),
656
+ childNodes: Array.prototype.toSorted == null
657
+ ? // TODO: Use sort instead of toSorted until we end support for Node 18
658
+ [...newChildNodes].sort(sortNodes)
659
+ : newChildNodes.toSorted(sortNodes),
620
660
  });
621
661
  }
622
662
  replaceChild(parentNode, oldChildNode, ...replacementChildNodes) {
@@ -773,12 +813,9 @@ _Parser_booleanish = new WeakMap(), _Parser_defaultState = new WeakMap(), _Parse
773
813
  newNodeList.push(node);
774
814
  }
775
815
  return newNodeList;
776
- }, _Parser_pairing = function _Parser_pairing(startTag, endTag, appendChild = true) {
816
+ }, _Parser_pairing = function _Parser_pairing(startTag, endTag) {
777
817
  Object.assign(startTag, { pairNode: endTag });
778
818
  Object.assign(endTag, { pairNode: startTag });
779
- if (!appendChild) {
780
- return;
781
- }
782
819
  this.appendChild(startTag.parentNode, endTag);
783
820
  }, _Parser_parseEndTag = function _Parser_parseEndTag(token, namelessFragment) {
784
821
  const parsed = __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_parseTag).call(this, token, true, false, namelessFragment);
@@ -1061,34 +1098,6 @@ _Parser_booleanish = new WeakMap(), _Parser_defaultState = new WeakMap(), _Parse
1061
1098
  }, _Parser_setRawCode = function _Parser_setRawCode(rawCode, originalRawCode) {
1062
1099
  __classPrivateFieldSet(this, _Parser_rawCode, rawCode, "f");
1063
1100
  __classPrivateFieldSet(this, _Parser_originalRawCode, originalRawCode ?? __classPrivateFieldGet(this, _Parser_originalRawCode, "f"), "f");
1064
- }, _Parser_siblingsCorrection = function _Parser_siblingsCorrection(nodes) {
1065
- const stack = new Set();
1066
- const newNodes = [];
1067
- const oldNodes = Array.prototype.toSorted == null
1068
- ? // TODO: Use sort instead of toSorted until we end support for Node 18
1069
- [...nodes].sort(sortNodes)
1070
- : nodes.toSorted(sortNodes);
1071
- const nameToLastOpenTag = {};
1072
- for (const node of oldNodes) {
1073
- const id = `${node.startOffset}::${node.nodeName}`;
1074
- if (stack.has(id)) {
1075
- continue;
1076
- }
1077
- stack.add(id);
1078
- if (node.type === 'endtag') {
1079
- const openTag = nameToLastOpenTag[node.nodeName];
1080
- if (openTag && !openTag.pairNode) {
1081
- __classPrivateFieldGet(this, _Parser_instances, "m", _Parser_pairing).call(this, openTag, node, false);
1082
- newNodes.push(node);
1083
- continue;
1084
- }
1085
- }
1086
- else if (node.type === 'starttag') {
1087
- nameToLastOpenTag[node.nodeName] = node;
1088
- }
1089
- newNodes.push(node);
1090
- }
1091
- return newNodes;
1092
1101
  }, _Parser_trimText = function _Parser_trimText(nodeList) {
1093
1102
  const newNodeList = [];
1094
1103
  let prevNode = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/parser-utils",
3
- "version": "4.7.0",
3
+ "version": "4.7.2",
4
4
  "description": "Utility module for markuplint parser plugin",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -28,9 +28,9 @@
28
28
  "clean": "tsc --build --clean"
29
29
  },
30
30
  "dependencies": {
31
- "@markuplint/ml-ast": "4.4.6",
32
- "@markuplint/ml-spec": "4.7.2",
33
- "@markuplint/types": "4.6.2",
31
+ "@markuplint/ml-ast": "4.4.8",
32
+ "@markuplint/ml-spec": "4.8.1",
33
+ "@markuplint/types": "4.6.4",
34
34
  "@types/uuid": "10.0.0",
35
35
  "debug": "4.3.7",
36
36
  "espree": "10.2.0",
@@ -38,7 +38,7 @@
38
38
  "uuid": "10.0.0"
39
39
  },
40
40
  "devDependencies": {
41
- "@typescript-eslint/typescript-estree": "8.9.0"
41
+ "@typescript-eslint/typescript-estree": "8.11.0"
42
42
  },
43
- "gitHead": "c8c82d36c2e48d191091cbc22ca1b99ed0704b9f"
43
+ "gitHead": "60a8a7149bd4441e89972ca21b90033afb05b89e"
44
44
  }