@markuplint/parser-utils 3.0.0-canary.5 → 3.0.0-dev.177

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.
@@ -0,0 +1,27 @@
1
+ import type { MLASTHTMLAttr } from '@markuplint/ml-ast';
2
+ type ParseAttrOptions = {
3
+ readonly booleanish?: boolean;
4
+ readonly valueDelimiters?: readonly ValueDelimiter[];
5
+ readonly equal?: string;
6
+ };
7
+ type ValueDelimiter = {
8
+ readonly start: string;
9
+ readonly end: string;
10
+ };
11
+ export declare const defaultValueDelimiters: readonly ValueDelimiter[];
12
+ export declare function parseAttr(raw: string, offset: number, html: string, options?: ParseAttrOptions): MLASTHTMLAttr;
13
+ export declare function tokenize(
14
+ raw: string,
15
+ options?: ParseAttrOptions,
16
+ ): {
17
+ beforeName: string;
18
+ name: string;
19
+ afterName: string;
20
+ equal: string;
21
+ beforeValue: string;
22
+ startQuote: string;
23
+ value: string;
24
+ endQuote: string;
25
+ afterAttr: string;
26
+ };
27
+ export {};
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tokenize = exports.parseAttr = exports.defaultValueDelimiters = void 0;
4
+ const create_token_1 = require("./create-token");
5
+ exports.defaultValueDelimiters = [
6
+ {
7
+ start: "'",
8
+ end: "'",
9
+ },
10
+ {
11
+ start: '"',
12
+ end: '"',
13
+ },
14
+ ];
15
+ const defaultEqual = '=';
16
+ const spaceRegex = /^\s$/;
17
+ function parseAttr(raw, offset, html, options) {
18
+ const tokens = tokenize(raw, options);
19
+ tokens.beforeName;
20
+ const attrToken = (0, create_token_1.createTokenFromRawCode)(raw, offset, html);
21
+ const spacesBeforeName = (0, create_token_1.tokenizer)(tokens.beforeName, attrToken.startLine, attrToken.startCol, attrToken.startOffset);
22
+ const name = (0, create_token_1.tokenizer)(tokens.name, spacesBeforeName.endLine, spacesBeforeName.endCol, spacesBeforeName.endOffset);
23
+ const spacesBeforeEqual = (0, create_token_1.tokenizer)(tokens.afterName, name.endLine, name.endCol, name.endOffset);
24
+ const equal = (0, create_token_1.tokenizer)(tokens.equal, spacesBeforeEqual.endLine, spacesBeforeEqual.endCol, spacesBeforeEqual.endOffset);
25
+ const spacesAfterEqual = (0, create_token_1.tokenizer)(tokens.beforeValue, equal.endLine, equal.endCol, equal.endOffset);
26
+ const startQuote = (0, create_token_1.tokenizer)(tokens.startQuote, spacesAfterEqual.endLine, spacesAfterEqual.endCol, spacesAfterEqual.endOffset);
27
+ const value = (0, create_token_1.tokenizer)(tokens.value, startQuote.endLine, startQuote.endCol, startQuote.endOffset);
28
+ const endQuote = (0, create_token_1.tokenizer)(tokens.endQuote, value.endLine, value.endCol, value.endOffset);
29
+ const attr = {
30
+ type: 'html-attr',
31
+ uuid: (0, create_token_1.uuid)(),
32
+ raw: attrToken.raw,
33
+ startOffset: attrToken.startOffset,
34
+ endOffset: attrToken.endOffset,
35
+ startLine: attrToken.startLine,
36
+ endLine: attrToken.endLine,
37
+ startCol: attrToken.startCol,
38
+ endCol: attrToken.endCol,
39
+ spacesBeforeName,
40
+ name,
41
+ spacesBeforeEqual,
42
+ equal,
43
+ spacesAfterEqual,
44
+ startQuote,
45
+ value,
46
+ endQuote,
47
+ isDuplicatable: false,
48
+ nodeName: name.raw,
49
+ parentNode: null,
50
+ nextNode: null,
51
+ prevNode: null,
52
+ isFragment: false,
53
+ isGhost: false,
54
+ };
55
+ return attr;
56
+ }
57
+ exports.parseAttr = parseAttr;
58
+ function tokenize(raw, options) {
59
+ var _a, _b, _c;
60
+ const valueDelimiters = (_a = options === null || options === void 0 ? void 0 : options.valueDelimiters) !== null && _a !== void 0 ? _a : exports.defaultValueDelimiters;
61
+ const equalDelimiter = (_b = options === null || options === void 0 ? void 0 : options.equal) !== null && _b !== void 0 ? _b : defaultEqual;
62
+ let state = 'b-name';
63
+ const charactors = raw.split('');
64
+ let beforeName = '';
65
+ let name = '';
66
+ let afterName = '';
67
+ let equal = '';
68
+ let valueDelimiter = null;
69
+ let beforeValue = '';
70
+ let startQuote = '';
71
+ let value = '';
72
+ let endQuote = '';
73
+ let afterAttr = '';
74
+ while (charactors.length > 0) {
75
+ const charactor = charactors.shift();
76
+ if (state === 'b-name') {
77
+ if (spaceRegex.test(charactor)) {
78
+ beforeName += charactor;
79
+ continue;
80
+ }
81
+ name += charactor;
82
+ state = 'name';
83
+ continue;
84
+ }
85
+ if (state === 'name') {
86
+ if (equalDelimiter === charactor) {
87
+ equal = equalDelimiter;
88
+ state = 'value-start';
89
+ continue;
90
+ }
91
+ if (spaceRegex.test(charactor)) {
92
+ afterName += charactor;
93
+ state = 'a-name';
94
+ continue;
95
+ }
96
+ name += charactor;
97
+ continue;
98
+ }
99
+ if (state === 'a-name') {
100
+ if (equalDelimiter === charactor) {
101
+ equal = equalDelimiter;
102
+ state = 'value-start';
103
+ continue;
104
+ }
105
+ if (spaceRegex.test(charactor)) {
106
+ afterName += charactor;
107
+ continue;
108
+ }
109
+ break;
110
+ }
111
+ if (state === 'value-start') {
112
+ if (spaceRegex.test(charactor)) {
113
+ beforeValue += charactor;
114
+ continue;
115
+ }
116
+ valueDelimiter = (_c = valueDelimiters.find(d => d.start === charactor)) !== null && _c !== void 0 ? _c : null;
117
+ if (valueDelimiter) {
118
+ startQuote += valueDelimiter.start;
119
+ }
120
+ else {
121
+ value += beforeValue + charactor;
122
+ beforeValue = '';
123
+ }
124
+ state = 'value';
125
+ continue;
126
+ }
127
+ if (state !== 'value') {
128
+ throw new Error('ParseError: unknown parse state in the attribute');
129
+ }
130
+ value += charactor;
131
+ }
132
+ if (valueDelimiter) {
133
+ const endQuoteIndex = value.lastIndexOf(valueDelimiter.end);
134
+ endQuote = value.slice(endQuoteIndex, endQuoteIndex + 1);
135
+ afterAttr = value.slice(endQuoteIndex + 1);
136
+ value = value.slice(0, endQuoteIndex);
137
+ }
138
+ return {
139
+ beforeName,
140
+ name,
141
+ afterName,
142
+ equal,
143
+ beforeValue,
144
+ startQuote,
145
+ value,
146
+ endQuote,
147
+ afterAttr,
148
+ };
149
+ }
150
+ exports.tokenize = tokenize;
@@ -1,13 +1,21 @@
1
1
  export declare class ParserError extends Error {
2
- readonly col: number;
3
- readonly line: number;
4
- name: string;
5
- readonly nodeName: string | null;
6
- readonly raw: string;
7
- constructor(message: string, { line, col, raw, nodeName, }: {
8
- line?: number;
9
- col?: number;
10
- raw?: string;
11
- nodeName?: string | null;
12
- });
2
+ readonly col: number;
3
+ readonly line: number;
4
+ name: string;
5
+ readonly nodeName: string | null;
6
+ readonly raw: string;
7
+ constructor(
8
+ message: string,
9
+ {
10
+ line,
11
+ col,
12
+ raw,
13
+ nodeName,
14
+ }: {
15
+ readonly line?: number;
16
+ readonly col?: number;
17
+ readonly raw?: string;
18
+ readonly nodeName?: string | null;
19
+ },
20
+ );
13
21
  }
@@ -0,0 +1,7 @@
1
+ import type { MLASTNode } from '@markuplint/ml-ast';
2
+ /**
3
+ *
4
+ * @disruptive
5
+ * @param nodeOrders [Disruptive change]
6
+ */
7
+ export declare function removeDeprecatedNode(nodeOrders: MLASTNode[]): void;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeDeprecatedNode = void 0;
4
+ /**
5
+ *
6
+ * @disruptive
7
+ * @param nodeOrders [Disruptive change]
8
+ */
9
+ function removeDeprecatedNode(
10
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
11
+ nodeOrders) {
12
+ /**
13
+ * sorting
14
+ */
15
+ nodeOrders.sort((a, b) => {
16
+ if (a.isGhost || b.isGhost) {
17
+ return 0;
18
+ }
19
+ return a.startOffset - b.startOffset;
20
+ });
21
+ /**
22
+ * remove duplicated node
23
+ */
24
+ const stack = {};
25
+ const removeIndexes = [];
26
+ nodeOrders.forEach((node, i) => {
27
+ if (node.isGhost) {
28
+ return;
29
+ }
30
+ const id = `${node.startLine}:${node.startCol}:${node.endLine}:${node.endCol}`;
31
+ if (stack[id] != null) {
32
+ removeIndexes.push(i);
33
+ }
34
+ stack[id] = i;
35
+ });
36
+ let r = nodeOrders.length;
37
+ while (r-- > 0) {
38
+ if (removeIndexes.includes(r)) {
39
+ nodeOrders.splice(r, 1);
40
+ }
41
+ }
42
+ }
43
+ exports.removeDeprecatedNode = removeDeprecatedNode;
@@ -8,11 +8,17 @@ exports.siblingsCorrection = void 0;
8
8
  * @affects nodeList[].prevNode
9
9
  * @affects nodeList[].nextNode
10
10
  */
11
- function siblingsCorrection(nodeList) {
11
+ function siblingsCorrection(
12
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
13
+ nodeList) {
14
+ var _a, _b;
12
15
  for (let i = 0; i < nodeList.length; i++) {
13
- const prevNode = nodeList[i - 1] || null;
16
+ const prevNode = (_a = nodeList[i - 1]) !== null && _a !== void 0 ? _a : null;
14
17
  const node = nodeList[i];
15
- const nextNode = nodeList[i + 1] || null;
18
+ if (!node) {
19
+ continue;
20
+ }
21
+ const nextNode = (_b = nodeList[i + 1]) !== null && _b !== void 0 ? _b : null;
16
22
  node.prevNode = prevNode;
17
23
  node.nextNode = nextNode;
18
24
  }
@@ -0,0 +1,7 @@
1
+ export interface N {
2
+ type: 'text' | 'starttag' | 'endtag' | 'comment' | 'boguscomment';
3
+ raw: string;
4
+ line: number;
5
+ col: number;
6
+ }
7
+ export default function tagSplitter(raw: string, line: number, col: number): N[];
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const const_1 = require("./const");
4
+ const parser_utils_1 = require("@markuplint/parser-utils");
5
+ function tagSplitter(raw, line, col) {
6
+ return withLocation(tagSplitterAsString(raw), line, col);
7
+ }
8
+ exports.default = tagSplitter;
9
+ function tagSplitterAsString(raw) {
10
+ const tagMatches = raw.match(const_1.reSplitterTag);
11
+ if (!tagMatches) {
12
+ return [raw];
13
+ }
14
+ const tokens = Array.from(tagMatches);
15
+ tokens.unshift(); // remove all match
16
+ const nodes = [];
17
+ let rest = raw;
18
+ for (const token of tokens) {
19
+ const index = rest.indexOf(token);
20
+ let length = token.length;
21
+ if (index > 0) {
22
+ const text = rest.slice(0, index);
23
+ nodes.push(text);
24
+ length += text.length;
25
+ }
26
+ nodes.push(token);
27
+ rest = rest.slice(length);
28
+ }
29
+ if (rest) {
30
+ nodes.push(rest);
31
+ }
32
+ return nodes;
33
+ }
34
+ function withLocation(nodes, line, col) {
35
+ const result = [];
36
+ for (const node of nodes) {
37
+ if (node[0] !== '<') {
38
+ result.push({
39
+ type: 'text',
40
+ raw: node,
41
+ line,
42
+ col,
43
+ });
44
+ }
45
+ else {
46
+ const label = node.slice(1).slice(0, -1);
47
+ if (const_1.reTagName.test(label)) {
48
+ result.push({
49
+ type: 'starttag',
50
+ raw: node,
51
+ line,
52
+ col,
53
+ });
54
+ }
55
+ else if (label[0] === '/') {
56
+ result.push({
57
+ type: 'endtag',
58
+ raw: node,
59
+ line,
60
+ col,
61
+ });
62
+ }
63
+ else if (label[0] === '!') {
64
+ result.push({
65
+ type: 'comment',
66
+ raw: node,
67
+ line,
68
+ col,
69
+ });
70
+ }
71
+ else if (label[0] === '?') {
72
+ result.push({
73
+ type: 'boguscomment',
74
+ raw: node,
75
+ line,
76
+ col,
77
+ });
78
+ }
79
+ else {
80
+ result.push({
81
+ type: 'text',
82
+ raw: node,
83
+ line,
84
+ col,
85
+ });
86
+ }
87
+ }
88
+ line = (0, parser_utils_1.getEndLine)(node, line);
89
+ col = (0, parser_utils_1.getEndCol)(node, col);
90
+ }
91
+ return result;
92
+ }
package/lib/types.d.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  export type Code = {
2
- type: string;
3
- index: number;
4
- startTag: string;
5
- taggedCode: string;
6
- endTag: string | null;
2
+ readonly type: string;
3
+ readonly index: number;
4
+ readonly startTag: string;
5
+ readonly taggedCode: string;
6
+ readonly endTag: string | null;
7
7
  };
8
8
  export type IgnoreTag = {
9
- type: string;
10
- start: RegExp;
11
- end: RegExp;
9
+ readonly type: string;
10
+ readonly start: Readonly<RegExp>;
11
+ readonly end: Readonly<RegExp>;
12
12
  };
13
13
  export type IgnoreBlock = {
14
- source: string;
15
- replaced: string;
16
- stack: Code[];
17
- maskChar: string;
14
+ readonly source: string;
15
+ readonly replaced: string;
16
+ readonly stack: readonly Code[];
17
+ readonly maskChar: string;
18
18
  };
package/lib/walker.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  import type { MLASTNode, Walker } from '@markuplint/ml-ast';
2
- export declare function walk(nodeList: MLASTNode[], walker: Walker, depth?: number): void;
2
+ export declare function walk(nodeList: readonly MLASTNode[], walker: Walker, depth?: number): void;
package/lib/walker.js CHANGED
@@ -1,14 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.walk = void 0;
4
- function walk(nodeList, walker, depth = 0) {
4
+ function walk(
5
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
6
+ nodeList, walker, depth = 0) {
5
7
  for (const node of nodeList) {
6
8
  walker(node, depth);
7
9
  if ('childNodes' in node) {
8
10
  if (node.type === 'endtag') {
9
11
  continue;
10
12
  }
11
- if (node.childNodes && node.childNodes.length) {
13
+ if (node.childNodes && node.childNodes.length > 0) {
12
14
  walk(node.childNodes, walker, depth + 1);
13
15
  }
14
16
  if ('pearNode' in node && node.pearNode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/parser-utils",
3
- "version": "3.0.0-canary.5+382d1365",
3
+ "version": "3.0.0-dev.177+e94f3601",
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>",
@@ -11,18 +11,23 @@
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
14
+ "typedoc": {
15
+ "entryPoint": "./src/index.ts"
16
+ },
14
17
  "scripts": {
15
18
  "build": "tsc",
16
19
  "clean": "tsc --build --clean"
17
20
  },
18
- "devDependencies": {
19
- "@types/uuid": "^8.3.4"
20
- },
21
21
  "dependencies": {
22
- "@markuplint/ml-ast": "3.0.0-canary.69+382d1365",
23
- "@markuplint/types": "3.0.0-canary.5+382d1365",
24
- "tslib": "^2.4.0",
25
- "uuid": "8"
22
+ "@markuplint/ml-ast": "3.0.0-dev.177+e94f3601",
23
+ "@markuplint/types": "3.0.0-dev.177+e94f3601",
24
+ "@types/uuid": "^9.0.0",
25
+ "tslib": "^2.4.1",
26
+ "type-fest": "^3.8.0",
27
+ "uuid": "^9.0.0"
28
+ },
29
+ "peerDependencies": {
30
+ "@markuplint/ml-core": "3.x"
26
31
  },
27
- "gitHead": "382d13653071bd02210d5430403d1a87c840398c"
32
+ "gitHead": "e94f3601bf93366d1a2c7385ff7235817d82f7c9"
28
33
  }
@@ -0,0 +1,17 @@
1
+ const { tokenizer } = require('../lib/create-token');
2
+
3
+ describe('tokenizer', () => {
4
+ it('empty', () => {
5
+ expect(tokenizer('', 1, 1, 0)).toEqual(
6
+ expect.objectContaining({
7
+ raw: '',
8
+ startLine: 1,
9
+ startCol: 1,
10
+ startOffset: 0,
11
+ endLine: 1,
12
+ endCol: 1,
13
+ endOffset: 0,
14
+ }),
15
+ );
16
+ });
17
+ });
@@ -0,0 +1,31 @@
1
+ const { isDocumentFragment, createTree } = require('@markuplint/html-parser');
2
+
3
+ const { flattenNodes } = require('../lib/flatten-nodes');
4
+
5
+ function toTree(rawCode) {
6
+ const isFragment = isDocumentFragment(rawCode);
7
+ return createTree(rawCode, isFragment, 0, 0, 0);
8
+ }
9
+
10
+ describe('flattenNodes', () => {
11
+ it('a node', () => {
12
+ const raw = '<div></div>';
13
+ const tree = toTree(raw);
14
+ const list = flattenNodes(tree, raw);
15
+ expect(list.length).toStrictEqual(2);
16
+ });
17
+
18
+ it('one depth', () => {
19
+ const raw = '<div><span></span></div>';
20
+ const tree = toTree(raw);
21
+ const list = flattenNodes(tree, raw);
22
+ expect(list.length).toStrictEqual(4);
23
+ });
24
+
25
+ it('two depth', () => {
26
+ const raw = '<div><span><span></span></span></div>';
27
+ const tree = toTree(raw);
28
+ const list = flattenNodes(tree, raw);
29
+ expect(list.length).toStrictEqual(6);
30
+ });
31
+ });
@@ -0,0 +1,38 @@
1
+ const { getEndCol, getEndLine } = require('../lib/get-location');
2
+
3
+ describe('getEndLine', () => {
4
+ it('empty', () => {
5
+ expect(getEndLine('', 1)).toBe(1);
6
+ expect(getEndLine('', 3)).toBe(3);
7
+ });
8
+
9
+ it('basic', () => {
10
+ expect(getEndLine('\n\n', 2)).toBe(4);
11
+ expect(getEndLine('\n\n', 4)).toBe(6);
12
+ expect(getEndLine('\n\n \n \n foo', 1)).toBe(5);
13
+ expect(getEndLine('\n\n \n \n foo', 6)).toBe(10);
14
+ });
15
+ });
16
+
17
+ describe('getEndCol', () => {
18
+ it('empty', () => {
19
+ expect(getEndCol('', 1)).toBe(1);
20
+ expect(getEndCol('', 3)).toBe(3);
21
+ });
22
+
23
+ it('basic', () => {
24
+ expect(getEndCol(' ', 2)).toBe(4);
25
+ expect(getEndCol(' ', 4)).toBe(6);
26
+ expect(getEndCol('foo bar', 1)).toBe(8);
27
+ expect(getEndCol('foo bar', 6)).toBe(13);
28
+ });
29
+
30
+ it('with line break', () => {
31
+ expect(getEndCol('foo bar\n', 4)).toBe(1);
32
+ expect(getEndCol('foo bar\n', 4)).toBe(1);
33
+ expect(getEndCol('foo bar\n ', 4)).toBe(3);
34
+ expect(getEndCol('foo bar\n ', 4)).toBe(3);
35
+ expect(getEndCol('foo bar\nfoo bar', 1)).toBe(8);
36
+ expect(getEndCol('foo bar\nfoo bar', 6)).toBe(8);
37
+ });
38
+ });