roadmapsmith 0.9.3 → 0.9.5
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/README.md +20 -0
- package/bin/cli.js +254 -254
- package/package.json +56 -56
- package/src/config.js +219 -219
- package/src/generator/index.js +614 -614
- package/src/index.js +11 -11
- package/src/io.js +264 -264
- package/src/match.js +86 -86
- package/src/model.js +33 -33
- package/src/parser/index.js +100 -100
- package/src/renderer/professional.js +544 -544
- package/src/sync/index.js +1 -1
- package/src/templates/index.js +1 -1
- package/src/utils.js +142 -142
- package/src/validator/index.js +727 -641
- package/templates/roadmap.template.md +1 -1
package/src/sync/index.js
CHANGED
package/src/templates/index.js
CHANGED
package/src/utils.js
CHANGED
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const STOP_WORDS = new Set([
|
|
6
|
-
'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'for', 'from', 'in', 'into', 'is', 'it', 'of', 'on', 'or', 'that',
|
|
7
|
-
'the', 'to', 'with', 'this', 'these', 'those', 'via', 'per', 'task', 'tasks', 'phase', 'priority'
|
|
8
|
-
]);
|
|
9
|
-
|
|
10
|
-
function toPosix(input) {
|
|
11
|
-
return input.split(path.sep).join('/');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function slugify(text) {
|
|
15
|
-
return String(text || '')
|
|
16
|
-
.toLowerCase()
|
|
17
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
18
|
-
.replace(/^-+|-+$/g, '')
|
|
19
|
-
.replace(/-{2,}/g, '-') || 'task';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function normalizeText(text) {
|
|
23
|
-
return String(text || '')
|
|
24
|
-
.toLowerCase()
|
|
25
|
-
.replace(/[`*_~#>\[\](){}.!?,:;"']/g, ' ')
|
|
26
|
-
.replace(/\s+/g, ' ')
|
|
27
|
-
.trim();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function tokenize(text) {
|
|
31
|
-
return normalizeText(text)
|
|
32
|
-
.split(' ')
|
|
33
|
-
.filter(Boolean)
|
|
34
|
-
.filter((token) => !STOP_WORDS.has(token));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function uniqueBy(items, keyFn) {
|
|
38
|
-
const seen = new Set();
|
|
39
|
-
const result = [];
|
|
40
|
-
for (const item of items) {
|
|
41
|
-
const key = keyFn(item);
|
|
42
|
-
if (seen.has(key)) {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
seen.add(key);
|
|
46
|
-
result.push(item);
|
|
47
|
-
}
|
|
48
|
-
return result;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function similarityScore(left, right) {
|
|
52
|
-
const leftTokens = new Set(tokenize(left));
|
|
53
|
-
const rightTokens = new Set(tokenize(right));
|
|
54
|
-
if (leftTokens.size === 0 || rightTokens.size === 0) {
|
|
55
|
-
return 0;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let shared = 0;
|
|
59
|
-
for (const token of leftTokens) {
|
|
60
|
-
if (rightTokens.has(token)) {
|
|
61
|
-
shared += 1;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const union = new Set([...leftTokens, ...rightTokens]);
|
|
66
|
-
return shared / union.size;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function ensureTrailingNewline(text) {
|
|
70
|
-
return text.endsWith('\n') ? text : `${text}\n`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function escapeRegExp(value) {
|
|
74
|
-
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function parseArgv(argv) {
|
|
78
|
-
const flags = {};
|
|
79
|
-
const positionals = [];
|
|
80
|
-
|
|
81
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
82
|
-
const current = argv[i];
|
|
83
|
-
if (!current.startsWith('-')) {
|
|
84
|
-
positionals.push(current);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (current.startsWith('--')) {
|
|
89
|
-
const withoutPrefix = current.slice(2);
|
|
90
|
-
const eqIndex = withoutPrefix.indexOf('=');
|
|
91
|
-
let key;
|
|
92
|
-
let value;
|
|
93
|
-
|
|
94
|
-
if (eqIndex >= 0) {
|
|
95
|
-
key = withoutPrefix.slice(0, eqIndex);
|
|
96
|
-
value = withoutPrefix.slice(eqIndex + 1);
|
|
97
|
-
} else {
|
|
98
|
-
key = withoutPrefix;
|
|
99
|
-
const next = argv[i + 1];
|
|
100
|
-
if (next && !next.startsWith('-')) {
|
|
101
|
-
value = next;
|
|
102
|
-
i += 1;
|
|
103
|
-
} else {
|
|
104
|
-
value = true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (Object.prototype.hasOwnProperty.call(flags, key)) {
|
|
109
|
-
if (Array.isArray(flags[key])) {
|
|
110
|
-
flags[key].push(value);
|
|
111
|
-
} else {
|
|
112
|
-
flags[key] = [flags[key], value];
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
flags[key] = value;
|
|
116
|
-
}
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const short = current.slice(1);
|
|
121
|
-
flags[short] = true;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const command = positionals.length > 0 ? positionals[0] : null;
|
|
125
|
-
return {
|
|
126
|
-
command,
|
|
127
|
-
args: positionals.slice(1),
|
|
128
|
-
flags,
|
|
129
|
-
positionals
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
module.exports = {
|
|
134
|
-
escapeRegExp,
|
|
135
|
-
ensureTrailingNewline,
|
|
136
|
-
parseArgv,
|
|
137
|
-
similarityScore,
|
|
138
|
-
slugify,
|
|
139
|
-
toPosix,
|
|
140
|
-
tokenize,
|
|
141
|
-
uniqueBy
|
|
142
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const STOP_WORDS = new Set([
|
|
6
|
+
'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'for', 'from', 'in', 'into', 'is', 'it', 'of', 'on', 'or', 'that',
|
|
7
|
+
'the', 'to', 'with', 'this', 'these', 'those', 'via', 'per', 'task', 'tasks', 'phase', 'priority'
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
function toPosix(input) {
|
|
11
|
+
return input.split(path.sep).join('/');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function slugify(text) {
|
|
15
|
+
return String(text || '')
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
18
|
+
.replace(/^-+|-+$/g, '')
|
|
19
|
+
.replace(/-{2,}/g, '-') || 'task';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeText(text) {
|
|
23
|
+
return String(text || '')
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.replace(/[`*_~#>\[\](){}.!?,:;"']/g, ' ')
|
|
26
|
+
.replace(/\s+/g, ' ')
|
|
27
|
+
.trim();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function tokenize(text) {
|
|
31
|
+
return normalizeText(text)
|
|
32
|
+
.split(' ')
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.filter((token) => !STOP_WORDS.has(token));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function uniqueBy(items, keyFn) {
|
|
38
|
+
const seen = new Set();
|
|
39
|
+
const result = [];
|
|
40
|
+
for (const item of items) {
|
|
41
|
+
const key = keyFn(item);
|
|
42
|
+
if (seen.has(key)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
seen.add(key);
|
|
46
|
+
result.push(item);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function similarityScore(left, right) {
|
|
52
|
+
const leftTokens = new Set(tokenize(left));
|
|
53
|
+
const rightTokens = new Set(tokenize(right));
|
|
54
|
+
if (leftTokens.size === 0 || rightTokens.size === 0) {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let shared = 0;
|
|
59
|
+
for (const token of leftTokens) {
|
|
60
|
+
if (rightTokens.has(token)) {
|
|
61
|
+
shared += 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const union = new Set([...leftTokens, ...rightTokens]);
|
|
66
|
+
return shared / union.size;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function ensureTrailingNewline(text) {
|
|
70
|
+
return text.endsWith('\n') ? text : `${text}\n`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function escapeRegExp(value) {
|
|
74
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseArgv(argv) {
|
|
78
|
+
const flags = {};
|
|
79
|
+
const positionals = [];
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
82
|
+
const current = argv[i];
|
|
83
|
+
if (!current.startsWith('-')) {
|
|
84
|
+
positionals.push(current);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (current.startsWith('--')) {
|
|
89
|
+
const withoutPrefix = current.slice(2);
|
|
90
|
+
const eqIndex = withoutPrefix.indexOf('=');
|
|
91
|
+
let key;
|
|
92
|
+
let value;
|
|
93
|
+
|
|
94
|
+
if (eqIndex >= 0) {
|
|
95
|
+
key = withoutPrefix.slice(0, eqIndex);
|
|
96
|
+
value = withoutPrefix.slice(eqIndex + 1);
|
|
97
|
+
} else {
|
|
98
|
+
key = withoutPrefix;
|
|
99
|
+
const next = argv[i + 1];
|
|
100
|
+
if (next && !next.startsWith('-')) {
|
|
101
|
+
value = next;
|
|
102
|
+
i += 1;
|
|
103
|
+
} else {
|
|
104
|
+
value = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (Object.prototype.hasOwnProperty.call(flags, key)) {
|
|
109
|
+
if (Array.isArray(flags[key])) {
|
|
110
|
+
flags[key].push(value);
|
|
111
|
+
} else {
|
|
112
|
+
flags[key] = [flags[key], value];
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
flags[key] = value;
|
|
116
|
+
}
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const short = current.slice(1);
|
|
121
|
+
flags[short] = true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const command = positionals.length > 0 ? positionals[0] : null;
|
|
125
|
+
return {
|
|
126
|
+
command,
|
|
127
|
+
args: positionals.slice(1),
|
|
128
|
+
flags,
|
|
129
|
+
positionals
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
escapeRegExp,
|
|
135
|
+
ensureTrailingNewline,
|
|
136
|
+
parseArgv,
|
|
137
|
+
similarityScore,
|
|
138
|
+
slugify,
|
|
139
|
+
toPosix,
|
|
140
|
+
tokenize,
|
|
141
|
+
uniqueBy
|
|
142
|
+
};
|