comment-parser 1.0.0 → 1.1.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 +13 -0
- package/README.md +26 -19
- package/browser/index.js +331 -272
- package/es6/index.d.ts +21 -4
- package/es6/index.js +21 -5
- package/es6/parser/block-parser.d.ts +18 -2
- package/es6/parser/block-parser.js +12 -12
- package/es6/parser/index.d.ts +4 -4
- package/es6/parser/index.js +25 -26
- package/es6/parser/source-parser.js +13 -15
- package/es6/parser/spec-parser.d.ts +1 -6
- package/es6/parser/spec-parser.js +4 -151
- package/es6/parser/tokenizers/description.d.ts +19 -0
- package/es6/parser/tokenizers/description.js +46 -0
- package/es6/parser/tokenizers/index.d.ts +7 -0
- package/es6/parser/tokenizers/index.js +1 -0
- package/es6/parser/tokenizers/name.d.ts +6 -0
- package/es6/parser/tokenizers/name.js +91 -0
- package/es6/parser/tokenizers/tag.d.ts +6 -0
- package/es6/parser/tokenizers/tag.js +24 -0
- package/es6/parser/tokenizers/type.d.ts +27 -0
- package/es6/parser/tokenizers/type.js +67 -0
- package/es6/stringifier/index.d.ts +2 -1
- package/es6/stringifier/index.js +1 -6
- package/es6/stringifier/inspect.d.ts +2 -0
- package/es6/stringifier/inspect.js +41 -0
- package/es6/transforms/align.js +48 -43
- package/es6/transforms/indent.js +11 -22
- package/es6/transforms/index.d.ts +0 -2
- package/es6/transforms/index.js +2 -10
- package/es6/util.d.ts +11 -0
- package/es6/util.js +25 -25
- package/jest.config.js +12 -11
- package/lib/index.d.ts +21 -4
- package/lib/index.js +24 -6
- package/lib/parser/block-parser.d.ts +18 -2
- package/lib/parser/block-parser.js +12 -12
- package/lib/parser/index.d.ts +4 -4
- package/lib/parser/index.js +26 -27
- package/lib/parser/source-parser.js +14 -16
- package/lib/parser/spec-parser.d.ts +1 -6
- package/lib/parser/spec-parser.js +4 -156
- package/lib/parser/tokenizers/description.d.ts +19 -0
- package/lib/parser/tokenizers/description.js +51 -0
- package/lib/parser/tokenizers/index.d.ts +7 -0
- package/lib/parser/tokenizers/index.js +2 -0
- package/lib/parser/tokenizers/name.d.ts +6 -0
- package/lib/parser/tokenizers/name.js +94 -0
- package/lib/parser/tokenizers/tag.d.ts +6 -0
- package/lib/parser/tokenizers/tag.js +27 -0
- package/lib/parser/tokenizers/type.d.ts +27 -0
- package/lib/parser/tokenizers/type.js +70 -0
- package/lib/stringifier/index.d.ts +2 -1
- package/lib/stringifier/index.js +1 -6
- package/lib/stringifier/inspect.d.ts +2 -0
- package/lib/stringifier/inspect.js +44 -0
- package/lib/transforms/align.js +50 -45
- package/lib/transforms/indent.js +12 -23
- package/lib/transforms/index.d.ts +0 -2
- package/lib/transforms/index.js +3 -13
- package/lib/util.d.ts +11 -0
- package/lib/util.js +27 -26
- package/migrate-1.0.md +7 -7
- package/package.json +1 -1
- package/src/index.ts +20 -2
- package/src/parser/block-parser.ts +19 -1
- package/src/parser/index.ts +22 -21
- package/src/parser/source-parser.ts +2 -2
- package/src/parser/spec-parser.ts +2 -170
- package/src/parser/tokenizers/description.ts +75 -0
- package/src/parser/tokenizers/index.ts +8 -0
- package/src/parser/tokenizers/name.ts +112 -0
- package/src/parser/tokenizers/tag.ts +30 -0
- package/src/parser/tokenizers/type.ts +92 -0
- package/src/stringifier/index.ts +3 -1
- package/src/stringifier/inspect.ts +64 -0
- package/src/transforms/align.ts +37 -12
- package/src/transforms/index.ts +0 -3
- package/src/util.ts +20 -0
- package/tests/e2e/examples.js +39 -2
- package/tests/e2e/examples.spec.js +4 -2
- package/tests/e2e/issue-109.spec.js +49 -0
- package/tests/e2e/issue-112.spec.js +20 -0
- package/tests/e2e/issue-113.spec.js +23 -0
- package/tests/e2e/issue-119.spec.js +29 -0
- package/tests/e2e/issue-120.spec.js +29 -0
- package/tests/e2e/issue-121.spec.js +102 -0
- package/tests/e2e/transforms.spec.js +5 -2
- package/tests/unit/inspect.spec.ts +36 -0
- package/tests/unit/{spacer.spec.ts → spacer-description-joiner.spec.ts} +6 -6
- package/tests/unit/spec-description-tokenizer.spec.ts +100 -7
- package/tests/unit/spec-name-tokenizer.spec.ts +113 -1
- package/tests/unit/spec-parser.spec.ts +6 -9
- package/tests/unit/spec-tag-tokenizer.spec.ts +1 -1
- package/tests/unit/spec-type-tokenizer.spec.ts +121 -1
- package/tests/unit/stringifier.spec.ts +0 -1
- package/tests/unit/transforms-align.spec.ts +80 -16
- package/tests/unit/util-rewire.spec.ts +107 -0
- package/tests/unit/util.spec.ts +0 -48
- package/tsconfig.es6.json +1 -1
- package/tsconfig.node.json +1 -1
- package/es6/parser/spacer.d.ts +0 -3
- package/es6/parser/spacer.js +0 -37
- package/lib/parser/spacer.d.ts +0 -3
- package/lib/parser/spacer.js +0 -40
- package/src/parser/spacer.ts +0 -45
package/lib/parser/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Tokenizer } from './spec-parser';
|
|
2
1
|
import { Block } from '../primitives';
|
|
3
|
-
import {
|
|
2
|
+
import { Tokenizer } from './tokenizers/index';
|
|
4
3
|
export interface Options {
|
|
5
4
|
startLine: number;
|
|
6
5
|
fence: string;
|
|
7
|
-
spacing: 'compact' | 'preserve'
|
|
6
|
+
spacing: 'compact' | 'preserve';
|
|
8
7
|
tokenizers: Tokenizer[];
|
|
9
8
|
}
|
|
10
|
-
export
|
|
9
|
+
export declare type Parser = (source: string) => Block[];
|
|
10
|
+
export default function getParser({ startLine, fence, spacing, tokenizers, }?: Partial<Options>): Parser;
|
package/lib/parser/index.js
CHANGED
|
@@ -1,42 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
const util_1 = require("../util");
|
|
4
|
+
const block_parser_1 = require("./block-parser");
|
|
5
|
+
const source_parser_1 = require("./source-parser");
|
|
6
|
+
const spec_parser_1 = require("./spec-parser");
|
|
7
|
+
const tag_1 = require("./tokenizers/tag");
|
|
8
|
+
const type_1 = require("./tokenizers/type");
|
|
9
|
+
const name_1 = require("./tokenizers/name");
|
|
10
|
+
const description_1 = require("./tokenizers/description");
|
|
11
|
+
function getParser({ startLine = 0, fence = '```', spacing = 'compact', tokenizers = [
|
|
12
|
+
tag_1.default(),
|
|
13
|
+
type_1.default(spacing),
|
|
14
|
+
name_1.default(),
|
|
15
|
+
description_1.default(spacing),
|
|
16
|
+
], } = {}) {
|
|
15
17
|
if (startLine < 0 || startLine % 1 > 0)
|
|
16
18
|
throw new Error('Invalid startLine');
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return line.tokens.description.trim() != '';
|
|
23
|
-
};
|
|
19
|
+
const parseSource = source_parser_1.default({ startLine });
|
|
20
|
+
const parseBlock = block_parser_1.default({ fence });
|
|
21
|
+
const parseSpec = spec_parser_1.default({ tokenizers });
|
|
22
|
+
const joinDescription = description_1.getJoiner(spacing);
|
|
23
|
+
const notEmpty = (line) => line.tokens.description.trim() != '';
|
|
24
24
|
return function (source) {
|
|
25
|
-
|
|
26
|
-
for (
|
|
27
|
-
|
|
28
|
-
var lines = parseSource(line);
|
|
25
|
+
const blocks = [];
|
|
26
|
+
for (const line of util_1.splitLines(source)) {
|
|
27
|
+
const lines = parseSource(line);
|
|
29
28
|
if (lines === null)
|
|
30
29
|
continue;
|
|
31
30
|
if (lines.find(notEmpty) === undefined)
|
|
32
31
|
continue;
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
const sections = parseBlock(lines);
|
|
33
|
+
const specs = sections.slice(1).map(parseSpec);
|
|
35
34
|
blocks.push({
|
|
36
|
-
description:
|
|
35
|
+
description: joinDescription(sections[0]),
|
|
37
36
|
tags: specs,
|
|
38
37
|
source: lines,
|
|
39
|
-
problems: specs.reduce(
|
|
38
|
+
problems: specs.reduce((acc, spec) => acc.concat(spec.problems), []),
|
|
40
39
|
});
|
|
41
40
|
}
|
|
42
41
|
return blocks;
|
|
@@ -1,46 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function getParser(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var num = startLine;
|
|
3
|
+
const primitives_1 = require("../primitives");
|
|
4
|
+
const util_1 = require("../util");
|
|
5
|
+
function getParser({ startLine = 0, } = {}) {
|
|
6
|
+
let block = null;
|
|
7
|
+
let num = startLine;
|
|
9
8
|
return function parseSource(source) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
_a = util_1.splitSpace(rest), tokens.start = _a[0], rest = _a[1];
|
|
9
|
+
let rest = source;
|
|
10
|
+
const tokens = util_1.seedTokens();
|
|
11
|
+
[tokens.start, rest] = util_1.splitSpace(rest);
|
|
14
12
|
if (block === null &&
|
|
15
13
|
rest.startsWith(primitives_1.Markers.start) &&
|
|
16
14
|
!rest.startsWith(primitives_1.Markers.nostart)) {
|
|
17
15
|
block = [];
|
|
18
16
|
tokens.delimiter = rest.slice(0, primitives_1.Markers.start.length);
|
|
19
17
|
rest = rest.slice(primitives_1.Markers.start.length);
|
|
20
|
-
|
|
18
|
+
[tokens.postDelimiter, rest] = util_1.splitSpace(rest);
|
|
21
19
|
}
|
|
22
20
|
if (block === null) {
|
|
23
21
|
num++;
|
|
24
22
|
return null;
|
|
25
23
|
}
|
|
26
|
-
|
|
24
|
+
const isClosed = rest.trimRight().endsWith(primitives_1.Markers.end);
|
|
27
25
|
if (tokens.delimiter === '' &&
|
|
28
26
|
rest.startsWith(primitives_1.Markers.delim) &&
|
|
29
27
|
!rest.startsWith(primitives_1.Markers.end)) {
|
|
30
28
|
tokens.delimiter = primitives_1.Markers.delim;
|
|
31
29
|
rest = rest.slice(primitives_1.Markers.delim.length);
|
|
32
|
-
|
|
30
|
+
[tokens.postDelimiter, rest] = util_1.splitSpace(rest);
|
|
33
31
|
}
|
|
34
32
|
if (isClosed) {
|
|
35
|
-
|
|
33
|
+
const trimmed = rest.trimRight();
|
|
36
34
|
tokens.end = rest.slice(trimmed.length - primitives_1.Markers.end.length);
|
|
37
35
|
rest = trimmed.slice(0, -primitives_1.Markers.end.length);
|
|
38
36
|
}
|
|
39
37
|
tokens.description = rest;
|
|
40
|
-
block.push({ number: num, source
|
|
38
|
+
block.push({ number: num, source, tokens });
|
|
41
39
|
num++;
|
|
42
40
|
if (isClosed) {
|
|
43
|
-
|
|
41
|
+
const result = block.slice();
|
|
44
42
|
block = null;
|
|
45
43
|
return result;
|
|
46
44
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { Line, Spec } from '../primitives';
|
|
2
|
-
import {
|
|
2
|
+
import { Tokenizer } from './tokenizers/index';
|
|
3
3
|
export declare type Parser = (source: Line[]) => Spec;
|
|
4
|
-
export declare type Tokenizer = (spec: Spec) => Spec;
|
|
5
4
|
export interface Options {
|
|
6
5
|
tokenizers: Tokenizer[];
|
|
7
6
|
}
|
|
8
7
|
export default function getParser({ tokenizers }: Options): Parser;
|
|
9
|
-
export declare function tagTokenizer(): Tokenizer;
|
|
10
|
-
export declare function typeTokenizer(): Tokenizer;
|
|
11
|
-
export declare function nameTokenizer(): Tokenizer;
|
|
12
|
-
export declare function descriptionTokenizer(join: Spacer): Tokenizer;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function getParser(_a) {
|
|
6
|
-
var tokenizers = _a.tokenizers;
|
|
3
|
+
const util_1 = require("../util");
|
|
4
|
+
function getParser({ tokenizers }) {
|
|
7
5
|
return function parseSpec(source) {
|
|
8
6
|
var _a;
|
|
9
|
-
|
|
10
|
-
for (
|
|
11
|
-
var tokenize = tokenizers_1[_i];
|
|
7
|
+
let spec = util_1.seedSpec({ source });
|
|
8
|
+
for (const tokenize of tokenizers) {
|
|
12
9
|
spec = tokenize(spec);
|
|
13
10
|
if ((_a = spec.problems[spec.problems.length - 1]) === null || _a === void 0 ? void 0 : _a.critical)
|
|
14
11
|
break;
|
|
@@ -17,152 +14,3 @@ function getParser(_a) {
|
|
|
17
14
|
};
|
|
18
15
|
}
|
|
19
16
|
exports.default = getParser;
|
|
20
|
-
function tagTokenizer() {
|
|
21
|
-
return function (spec) {
|
|
22
|
-
var tokens = spec.source[0].tokens;
|
|
23
|
-
var match = tokens.description.match(/\s*(@(\S+))(\s*)/);
|
|
24
|
-
if (match === null) {
|
|
25
|
-
spec.problems.push({
|
|
26
|
-
code: 'spec:tag:prefix',
|
|
27
|
-
message: 'tag should start with "@" symbol',
|
|
28
|
-
line: spec.source[0].number,
|
|
29
|
-
critical: true,
|
|
30
|
-
});
|
|
31
|
-
return spec;
|
|
32
|
-
}
|
|
33
|
-
tokens.tag = match[1];
|
|
34
|
-
tokens.postTag = match[3];
|
|
35
|
-
tokens.description = tokens.description.slice(match[0].length);
|
|
36
|
-
spec.tag = match[2];
|
|
37
|
-
return spec;
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
exports.tagTokenizer = tagTokenizer;
|
|
41
|
-
function typeTokenizer() {
|
|
42
|
-
return function (spec) {
|
|
43
|
-
var _a;
|
|
44
|
-
var res = '';
|
|
45
|
-
var curlies = 0;
|
|
46
|
-
var tokens = spec.source[0].tokens;
|
|
47
|
-
var source = tokens.description.trimLeft();
|
|
48
|
-
if (source[0] !== '{')
|
|
49
|
-
return spec;
|
|
50
|
-
for (var _i = 0, source_1 = source; _i < source_1.length; _i++) {
|
|
51
|
-
var ch = source_1[_i];
|
|
52
|
-
if (ch === '{')
|
|
53
|
-
curlies++;
|
|
54
|
-
if (ch === '}')
|
|
55
|
-
curlies--;
|
|
56
|
-
res += ch;
|
|
57
|
-
if (curlies === 0) {
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (curlies !== 0) {
|
|
62
|
-
spec.problems.push({
|
|
63
|
-
code: 'spec:type:unpaired-curlies',
|
|
64
|
-
message: 'unpaired curlies',
|
|
65
|
-
line: spec.source[0].number,
|
|
66
|
-
critical: true,
|
|
67
|
-
});
|
|
68
|
-
return spec;
|
|
69
|
-
}
|
|
70
|
-
spec.type = res.slice(1, -1);
|
|
71
|
-
tokens.type = res;
|
|
72
|
-
_a = util_1.splitSpace(source.slice(tokens.type.length)), tokens.postType = _a[0], tokens.description = _a[1];
|
|
73
|
-
return spec;
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
exports.typeTokenizer = typeTokenizer;
|
|
77
|
-
function nameTokenizer() {
|
|
78
|
-
return function (spec) {
|
|
79
|
-
var _a, _b;
|
|
80
|
-
var _c;
|
|
81
|
-
var tokens = spec.source[0].tokens;
|
|
82
|
-
var source = tokens.description.trimLeft();
|
|
83
|
-
var quotedGroups = source.split('"');
|
|
84
|
-
// if it starts with quoted group, assume it is a literal
|
|
85
|
-
if (quotedGroups.length > 1 &&
|
|
86
|
-
quotedGroups[0] === '' &&
|
|
87
|
-
quotedGroups.length % 2 === 1) {
|
|
88
|
-
spec.name = quotedGroups[1];
|
|
89
|
-
tokens.name = "\"" + quotedGroups[1] + "\"";
|
|
90
|
-
_a = util_1.splitSpace(source.slice(tokens.name.length)), tokens.postName = _a[0], tokens.description = _a[1];
|
|
91
|
-
return spec;
|
|
92
|
-
}
|
|
93
|
-
var brackets = 0;
|
|
94
|
-
var name = '';
|
|
95
|
-
var optional = false;
|
|
96
|
-
var defaultValue;
|
|
97
|
-
// assume name is non-space string or anything wrapped into brackets
|
|
98
|
-
for (var _i = 0, source_2 = source; _i < source_2.length; _i++) {
|
|
99
|
-
var ch = source_2[_i];
|
|
100
|
-
if (brackets === 0 && util_1.isSpace(ch))
|
|
101
|
-
break;
|
|
102
|
-
if (ch === '[')
|
|
103
|
-
brackets++;
|
|
104
|
-
if (ch === ']')
|
|
105
|
-
brackets--;
|
|
106
|
-
name += ch;
|
|
107
|
-
}
|
|
108
|
-
if (brackets !== 0) {
|
|
109
|
-
spec.problems.push({
|
|
110
|
-
code: 'spec:name:unpaired-brackets',
|
|
111
|
-
message: 'unpaired brackets',
|
|
112
|
-
line: spec.source[0].number,
|
|
113
|
-
critical: true,
|
|
114
|
-
});
|
|
115
|
-
return spec;
|
|
116
|
-
}
|
|
117
|
-
var nameToken = name;
|
|
118
|
-
if (name[0] === '[' && name[name.length - 1] === ']') {
|
|
119
|
-
optional = true;
|
|
120
|
-
name = name.slice(1, -1);
|
|
121
|
-
var parts = name.split('=');
|
|
122
|
-
name = parts[0].trim();
|
|
123
|
-
defaultValue = (_c = parts[1]) === null || _c === void 0 ? void 0 : _c.trim();
|
|
124
|
-
if (name === '') {
|
|
125
|
-
spec.problems.push({
|
|
126
|
-
code: 'spec:name:empty-name',
|
|
127
|
-
message: 'empty name',
|
|
128
|
-
line: spec.source[0].number,
|
|
129
|
-
critical: true,
|
|
130
|
-
});
|
|
131
|
-
return spec;
|
|
132
|
-
}
|
|
133
|
-
if (parts.length > 2) {
|
|
134
|
-
spec.problems.push({
|
|
135
|
-
code: 'spec:name:invalid-default',
|
|
136
|
-
message: 'invalid default value syntax',
|
|
137
|
-
line: spec.source[0].number,
|
|
138
|
-
critical: true,
|
|
139
|
-
});
|
|
140
|
-
return spec;
|
|
141
|
-
}
|
|
142
|
-
if (defaultValue === '') {
|
|
143
|
-
spec.problems.push({
|
|
144
|
-
code: 'spec:name:empty-default',
|
|
145
|
-
message: 'empty default value',
|
|
146
|
-
line: spec.source[0].number,
|
|
147
|
-
critical: true,
|
|
148
|
-
});
|
|
149
|
-
return spec;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
spec.optional = optional;
|
|
153
|
-
spec.name = name;
|
|
154
|
-
tokens.name = nameToken;
|
|
155
|
-
if (defaultValue !== undefined)
|
|
156
|
-
spec.default = defaultValue;
|
|
157
|
-
_b = util_1.splitSpace(source.slice(tokens.name.length)), tokens.postName = _b[0], tokens.description = _b[1];
|
|
158
|
-
return spec;
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
exports.nameTokenizer = nameTokenizer;
|
|
162
|
-
function descriptionTokenizer(join) {
|
|
163
|
-
return function (spec) {
|
|
164
|
-
spec.description = join(spec.source);
|
|
165
|
-
return spec;
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
exports.descriptionTokenizer = descriptionTokenizer;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Line } from '../../primitives';
|
|
2
|
+
import { Tokenizer } from './index';
|
|
3
|
+
/**
|
|
4
|
+
* Walks over provided lines joining description token into a single string.
|
|
5
|
+
* */
|
|
6
|
+
export declare type Joiner = (lines: Line[]) => string;
|
|
7
|
+
/**
|
|
8
|
+
* Shortcut for standard Joiners
|
|
9
|
+
* compact - strip surrounding whitespace and concat lines using a single string
|
|
10
|
+
* preserve - preserves original whitespace and line breaks as is
|
|
11
|
+
*/
|
|
12
|
+
export declare type Spacing = 'compact' | 'preserve' | Joiner;
|
|
13
|
+
/**
|
|
14
|
+
* Makes no changes to `spec.lines[].tokens` but joins them into `spec.description`
|
|
15
|
+
* following given spacing srtategy
|
|
16
|
+
* @param {Spacing} spacing tells how to handle the whitespace
|
|
17
|
+
*/
|
|
18
|
+
export default function descriptionTokenizer(spacing?: Spacing): Tokenizer;
|
|
19
|
+
export declare function getJoiner(spacing: Spacing): Joiner;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getJoiner = void 0;
|
|
4
|
+
const primitives_1 = require("../../primitives");
|
|
5
|
+
/**
|
|
6
|
+
* Makes no changes to `spec.lines[].tokens` but joins them into `spec.description`
|
|
7
|
+
* following given spacing srtategy
|
|
8
|
+
* @param {Spacing} spacing tells how to handle the whitespace
|
|
9
|
+
*/
|
|
10
|
+
function descriptionTokenizer(spacing = 'compact') {
|
|
11
|
+
const join = getJoiner(spacing);
|
|
12
|
+
return (spec) => {
|
|
13
|
+
spec.description = join(spec.source);
|
|
14
|
+
return spec;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
exports.default = descriptionTokenizer;
|
|
18
|
+
function getJoiner(spacing) {
|
|
19
|
+
if (spacing === 'compact')
|
|
20
|
+
return compactJoiner;
|
|
21
|
+
if (spacing === 'preserve')
|
|
22
|
+
return preserveJoiner;
|
|
23
|
+
return spacing;
|
|
24
|
+
}
|
|
25
|
+
exports.getJoiner = getJoiner;
|
|
26
|
+
function compactJoiner(lines) {
|
|
27
|
+
return lines
|
|
28
|
+
.map(({ tokens: { description } }) => description.trim())
|
|
29
|
+
.filter((description) => description !== '')
|
|
30
|
+
.join(' ');
|
|
31
|
+
}
|
|
32
|
+
const lineNo = (num, { tokens }, i) => tokens.type === '' ? num : i;
|
|
33
|
+
const getDescription = ({ tokens }) => (tokens.delimiter === '' ? tokens.start : tokens.postDelimiter.slice(1)) +
|
|
34
|
+
tokens.description;
|
|
35
|
+
function preserveJoiner(lines) {
|
|
36
|
+
if (lines.length === 0)
|
|
37
|
+
return '';
|
|
38
|
+
// skip the opening line with no description
|
|
39
|
+
if (lines[0].tokens.description === '' &&
|
|
40
|
+
lines[0].tokens.delimiter === primitives_1.Markers.start)
|
|
41
|
+
lines = lines.slice(1);
|
|
42
|
+
// skip the closing line with no description
|
|
43
|
+
const lastLine = lines[lines.length - 1];
|
|
44
|
+
if (lastLine !== undefined &&
|
|
45
|
+
lastLine.tokens.description === '' &&
|
|
46
|
+
lastLine.tokens.end.endsWith(primitives_1.Markers.end))
|
|
47
|
+
lines = lines.slice(0, -1);
|
|
48
|
+
// description starts at the last line of type definition
|
|
49
|
+
lines = lines.slice(lines.reduce(lineNo, 0));
|
|
50
|
+
return lines.map(getDescription).join('\n');
|
|
51
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Spec } from '../../primitives';
|
|
2
|
+
/**
|
|
3
|
+
* Splits `spect.lines[].token.description` into other tokens,
|
|
4
|
+
* and populates the spec.{tag, name, type, description}. Invoked in a chaing
|
|
5
|
+
* with other tokens, operations listed above can be moved to separate tokenizers
|
|
6
|
+
*/
|
|
7
|
+
export declare type Tokenizer = (spec: Spec) => Spec;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const util_1 = require("../../util");
|
|
4
|
+
const isQuoted = (s) => s && s.startsWith('"') && s.endsWith('"');
|
|
5
|
+
/**
|
|
6
|
+
* Splits remaining `spec.lines[].tokens.description` into `name` and `descriptions` tokens,
|
|
7
|
+
* and populates the `spec.name`
|
|
8
|
+
*/
|
|
9
|
+
function nameTokenizer() {
|
|
10
|
+
const typeEnd = (num, { tokens }, i) => tokens.type === '' ? num : i;
|
|
11
|
+
return (spec) => {
|
|
12
|
+
// look for the name in the line where {type} ends
|
|
13
|
+
const { tokens } = spec.source[spec.source.reduce(typeEnd, 0)];
|
|
14
|
+
const source = tokens.description.trimLeft();
|
|
15
|
+
const quotedGroups = source.split('"');
|
|
16
|
+
// if it starts with quoted group, assume it is a literal
|
|
17
|
+
if (quotedGroups.length > 1 &&
|
|
18
|
+
quotedGroups[0] === '' &&
|
|
19
|
+
quotedGroups.length % 2 === 1) {
|
|
20
|
+
spec.name = quotedGroups[1];
|
|
21
|
+
tokens.name = `"${quotedGroups[1]}"`;
|
|
22
|
+
[tokens.postName, tokens.description] = util_1.splitSpace(source.slice(tokens.name.length));
|
|
23
|
+
return spec;
|
|
24
|
+
}
|
|
25
|
+
let brackets = 0;
|
|
26
|
+
let name = '';
|
|
27
|
+
let optional = false;
|
|
28
|
+
let defaultValue;
|
|
29
|
+
// assume name is non-space string or anything wrapped into brackets
|
|
30
|
+
for (const ch of source) {
|
|
31
|
+
if (brackets === 0 && util_1.isSpace(ch))
|
|
32
|
+
break;
|
|
33
|
+
if (ch === '[')
|
|
34
|
+
brackets++;
|
|
35
|
+
if (ch === ']')
|
|
36
|
+
brackets--;
|
|
37
|
+
name += ch;
|
|
38
|
+
}
|
|
39
|
+
if (brackets !== 0) {
|
|
40
|
+
spec.problems.push({
|
|
41
|
+
code: 'spec:name:unpaired-brackets',
|
|
42
|
+
message: 'unpaired brackets',
|
|
43
|
+
line: spec.source[0].number,
|
|
44
|
+
critical: true,
|
|
45
|
+
});
|
|
46
|
+
return spec;
|
|
47
|
+
}
|
|
48
|
+
const nameToken = name;
|
|
49
|
+
if (name[0] === '[' && name[name.length - 1] === ']') {
|
|
50
|
+
optional = true;
|
|
51
|
+
name = name.slice(1, -1);
|
|
52
|
+
const parts = name.split('=');
|
|
53
|
+
name = parts[0].trim();
|
|
54
|
+
if (parts[1] !== undefined)
|
|
55
|
+
defaultValue = parts.slice(1).join('=').trim();
|
|
56
|
+
if (name === '') {
|
|
57
|
+
spec.problems.push({
|
|
58
|
+
code: 'spec:name:empty-name',
|
|
59
|
+
message: 'empty name',
|
|
60
|
+
line: spec.source[0].number,
|
|
61
|
+
critical: true,
|
|
62
|
+
});
|
|
63
|
+
return spec;
|
|
64
|
+
}
|
|
65
|
+
if (defaultValue === '') {
|
|
66
|
+
spec.problems.push({
|
|
67
|
+
code: 'spec:name:empty-default',
|
|
68
|
+
message: 'empty default value',
|
|
69
|
+
line: spec.source[0].number,
|
|
70
|
+
critical: true,
|
|
71
|
+
});
|
|
72
|
+
return spec;
|
|
73
|
+
}
|
|
74
|
+
// has "=" and is not a string, except for "=>"
|
|
75
|
+
if (!isQuoted(defaultValue) && /=(?!>)/.test(defaultValue)) {
|
|
76
|
+
spec.problems.push({
|
|
77
|
+
code: 'spec:name:invalid-default',
|
|
78
|
+
message: 'invalid default value syntax',
|
|
79
|
+
line: spec.source[0].number,
|
|
80
|
+
critical: true,
|
|
81
|
+
});
|
|
82
|
+
return spec;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
spec.optional = optional;
|
|
86
|
+
spec.name = name;
|
|
87
|
+
tokens.name = nameToken;
|
|
88
|
+
if (defaultValue !== undefined)
|
|
89
|
+
spec.default = defaultValue;
|
|
90
|
+
[tokens.postName, tokens.description] = util_1.splitSpace(source.slice(tokens.name.length));
|
|
91
|
+
return spec;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
exports.default = nameTokenizer;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Splits the `@prefix` from remaining `Spec.lines[].token.descrioption` into the `tag` token,
|
|
5
|
+
* and populates `spec.tag`
|
|
6
|
+
*/
|
|
7
|
+
function tagTokenizer() {
|
|
8
|
+
return (spec) => {
|
|
9
|
+
const { tokens } = spec.source[0];
|
|
10
|
+
const match = tokens.description.match(/\s*(@(\S+))(\s*)/);
|
|
11
|
+
if (match === null) {
|
|
12
|
+
spec.problems.push({
|
|
13
|
+
code: 'spec:tag:prefix',
|
|
14
|
+
message: 'tag should start with "@" symbol',
|
|
15
|
+
line: spec.source[0].number,
|
|
16
|
+
critical: true,
|
|
17
|
+
});
|
|
18
|
+
return spec;
|
|
19
|
+
}
|
|
20
|
+
tokens.tag = match[1];
|
|
21
|
+
tokens.postTag = match[3];
|
|
22
|
+
tokens.description = tokens.description.slice(match[0].length);
|
|
23
|
+
spec.tag = match[2];
|
|
24
|
+
return spec;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
exports.default = tagTokenizer;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Tokenizer } from './index';
|
|
2
|
+
/**
|
|
3
|
+
* Joiner is a function taking collected type token string parts,
|
|
4
|
+
* and joining them together. In most of the cases this will be
|
|
5
|
+
* a single piece like {type-name}, but type may go over multipe line
|
|
6
|
+
* ```
|
|
7
|
+
* @tag {function(
|
|
8
|
+
* number,
|
|
9
|
+
* string
|
|
10
|
+
* )}
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare type Joiner = (parts: string[]) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Shortcut for standard Joiners
|
|
16
|
+
* compact - trim surrounding space, replace line breaks with a single space
|
|
17
|
+
* preserve - concat as is
|
|
18
|
+
*/
|
|
19
|
+
export declare type Spacing = 'compact' | 'preserve' | Joiner;
|
|
20
|
+
/**
|
|
21
|
+
* Sets splits remaining `Spec.lines[].tokes.description` into `type` and `description`
|
|
22
|
+
* tokens and populates Spec.type`
|
|
23
|
+
*
|
|
24
|
+
* @param {Spacing} spacing tells how to deal with a whitespace
|
|
25
|
+
* for type values going over multiple lines
|
|
26
|
+
*/
|
|
27
|
+
export default function typeTokenizer(spacing?: Spacing): Tokenizer;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const util_1 = require("../../util");
|
|
4
|
+
/**
|
|
5
|
+
* Sets splits remaining `Spec.lines[].tokes.description` into `type` and `description`
|
|
6
|
+
* tokens and populates Spec.type`
|
|
7
|
+
*
|
|
8
|
+
* @param {Spacing} spacing tells how to deal with a whitespace
|
|
9
|
+
* for type values going over multiple lines
|
|
10
|
+
*/
|
|
11
|
+
function typeTokenizer(spacing = 'compact') {
|
|
12
|
+
const join = getJoiner(spacing);
|
|
13
|
+
return (spec) => {
|
|
14
|
+
let curlies = 0;
|
|
15
|
+
let lines = [];
|
|
16
|
+
for (const [i, { tokens }] of spec.source.entries()) {
|
|
17
|
+
let type = '';
|
|
18
|
+
if (i === 0 && tokens.description[0] !== '{')
|
|
19
|
+
return spec;
|
|
20
|
+
for (const ch of tokens.description) {
|
|
21
|
+
if (ch === '{')
|
|
22
|
+
curlies++;
|
|
23
|
+
if (ch === '}')
|
|
24
|
+
curlies--;
|
|
25
|
+
type += ch;
|
|
26
|
+
if (curlies === 0)
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
lines.push([tokens, type]);
|
|
30
|
+
if (curlies === 0)
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
if (curlies !== 0) {
|
|
34
|
+
spec.problems.push({
|
|
35
|
+
code: 'spec:type:unpaired-curlies',
|
|
36
|
+
message: 'unpaired curlies',
|
|
37
|
+
line: spec.source[0].number,
|
|
38
|
+
critical: true,
|
|
39
|
+
});
|
|
40
|
+
return spec;
|
|
41
|
+
}
|
|
42
|
+
const parts = [];
|
|
43
|
+
const offset = lines[0][0].postDelimiter.length;
|
|
44
|
+
for (const [i, [tokens, type]] of lines.entries()) {
|
|
45
|
+
if (type === '')
|
|
46
|
+
continue;
|
|
47
|
+
tokens.type = type;
|
|
48
|
+
if (i > 0) {
|
|
49
|
+
tokens.type = tokens.postDelimiter.slice(offset) + type;
|
|
50
|
+
tokens.postDelimiter = tokens.postDelimiter.slice(0, offset);
|
|
51
|
+
}
|
|
52
|
+
[tokens.postType, tokens.description] = util_1.splitSpace(tokens.description.slice(type.length));
|
|
53
|
+
parts.push(tokens.type);
|
|
54
|
+
}
|
|
55
|
+
parts[0] = parts[0].slice(1);
|
|
56
|
+
parts[parts.length - 1] = parts[parts.length - 1].slice(0, -1);
|
|
57
|
+
spec.type = join(parts);
|
|
58
|
+
return spec;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
exports.default = typeTokenizer;
|
|
62
|
+
const trim = (x) => x.trim();
|
|
63
|
+
function getJoiner(spacing) {
|
|
64
|
+
if (spacing === 'compact')
|
|
65
|
+
return (t) => t.map(trim).join('');
|
|
66
|
+
else if (spacing === 'preserve')
|
|
67
|
+
return (t) => t.join('\n');
|
|
68
|
+
else
|
|
69
|
+
return spacing;
|
|
70
|
+
}
|