@unlaxer/dde-toolkit 0.1.6 → 0.1.8
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/lib/dictionary.js +27 -1
- package/lib/markdown.js +47 -2
- package/package.json +1 -1
- package/version.txt +1 -1
package/lib/dictionary.js
CHANGED
|
@@ -110,7 +110,33 @@ export function buildDictionary(glossaryDir, dictionaryPath, lang = 'en') {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
// 3.
|
|
113
|
+
// 3. .ja.md の H1 から日本語用語を自動補完
|
|
114
|
+
// dictionary.yaml に ja: エントリがない .ja.md が対象(lang=ja のみ)
|
|
115
|
+
if (lang === 'ja' && existsSync(glossaryDir)) {
|
|
116
|
+
const jaFiles = readdirSync(glossaryDir).filter(f => f.endsWith('.ja.md') && f !== 'README.ja.md');
|
|
117
|
+
for (const jaFile of jaFiles) {
|
|
118
|
+
const jaFilePath = join(glossaryDir, jaFile);
|
|
119
|
+
// 既に ja: エントリがあればスキップ
|
|
120
|
+
if (entries.some(e => e.file === jaFilePath && e.lang === 'ja')) continue;
|
|
121
|
+
// H1 から日本語用語を抽出
|
|
122
|
+
try {
|
|
123
|
+
const content = readFileSync(jaFilePath, 'utf8');
|
|
124
|
+
const h1Match = content.match(/^#\s+(.+)$/m);
|
|
125
|
+
if (!h1Match) continue;
|
|
126
|
+
const jaTerm = h1Match[1]
|
|
127
|
+
.replace(/([^)]*)/g, '') // 全角括弧内を削除
|
|
128
|
+
.replace(/\([^)]*\)/g, '') // 半角括弧内を削除
|
|
129
|
+
.trim();
|
|
130
|
+
if (jaTerm) {
|
|
131
|
+
entries.push({ term: jaTerm, file: jaFilePath, lang: 'ja' });
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// 読み取り失敗は無視
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 4. 文字数降順ソート(最長一致のため)
|
|
114
140
|
entries.sort((a, b) => b.term.length - a.term.length);
|
|
115
141
|
|
|
116
142
|
return entries;
|
package/lib/markdown.js
CHANGED
|
@@ -85,6 +85,7 @@ export function processMarkdown(content, dictionary, lang = 'en', sourceFile = n
|
|
|
85
85
|
* テキスト内で用語の置換箇所を検出する
|
|
86
86
|
* - 段落ごとに 1 用語 1 回まで
|
|
87
87
|
* - 最長一致(辞書はすでに降順ソート済み)
|
|
88
|
+
* - ASCII 用語は単語境界チェックを適用(複合語内マッチを防ぐ)
|
|
88
89
|
*/
|
|
89
90
|
function findReplacements(text, dictionary, alreadyMatched, lang) {
|
|
90
91
|
// 使用済み範囲を追跡(重複マッチ防止)
|
|
@@ -95,7 +96,20 @@ function findReplacements(text, dictionary, alreadyMatched, lang) {
|
|
|
95
96
|
if (entry.lang !== lang && entry.lang !== 'en') continue;
|
|
96
97
|
if (alreadyMatched.has(entry.term)) continue;
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
// 単語境界チェックが必要な ASCII 用語は、条件を満たす最初の出現を探す
|
|
100
|
+
let idx = -1;
|
|
101
|
+
const needsBoundary = isAsciiTerm(entry.term);
|
|
102
|
+
let searchFrom = 0;
|
|
103
|
+
while (true) {
|
|
104
|
+
const found = text.indexOf(entry.term, searchFrom);
|
|
105
|
+
if (found === -1) break;
|
|
106
|
+
if (needsBoundary && !hasWordBoundary(text, found, found + entry.term.length)) {
|
|
107
|
+
searchFrom = found + 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
idx = found;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
99
113
|
if (idx === -1) continue;
|
|
100
114
|
|
|
101
115
|
// 重複範囲チェック
|
|
@@ -113,6 +127,23 @@ function findReplacements(text, dictionary, alreadyMatched, lang) {
|
|
|
113
127
|
return replacements;
|
|
114
128
|
}
|
|
115
129
|
|
|
130
|
+
/**
|
|
131
|
+
* 用語が ASCII 文字のみで構成されているか(日本語等は除外)
|
|
132
|
+
*/
|
|
133
|
+
function isAsciiTerm(term) {
|
|
134
|
+
return /^[\x00-\x7F]+$/.test(term);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* text[start..end] の前後が単語文字(\w)でないか確認する
|
|
139
|
+
*/
|
|
140
|
+
function hasWordBoundary(text, start, end) {
|
|
141
|
+
const wordChar = /\w/;
|
|
142
|
+
if (start > 0 && wordChar.test(text[start - 1])) return false;
|
|
143
|
+
if (end < text.length && wordChar.test(text[end])) return false;
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
116
147
|
/**
|
|
117
148
|
* テキストと置換リストから AST ノード配列を生成
|
|
118
149
|
* @param {string} sourceFile - リンクを埋め込むファイルのパス(相対パス計算用)
|
|
@@ -185,7 +216,21 @@ export function findUnlinked(content, dictionary, lang = 'en') {
|
|
|
185
216
|
|
|
186
217
|
for (const entry of dictionary) {
|
|
187
218
|
if (entry.lang !== lang && entry.lang !== 'en') continue;
|
|
188
|
-
|
|
219
|
+
const text = node.value;
|
|
220
|
+
const needsBoundary = isAsciiTerm(entry.term);
|
|
221
|
+
let found = false;
|
|
222
|
+
let searchFrom = 0;
|
|
223
|
+
while (true) {
|
|
224
|
+
const idx = text.indexOf(entry.term, searchFrom);
|
|
225
|
+
if (idx === -1) break;
|
|
226
|
+
if (needsBoundary && !hasWordBoundary(text, idx, idx + entry.term.length)) {
|
|
227
|
+
searchFrom = idx + 1;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
found = true;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
if (found) {
|
|
189
234
|
const key = entry.term;
|
|
190
235
|
if (!unlinked.has(key)) {
|
|
191
236
|
unlinked.set(key, { term: entry.term, file: entry.file, count: 0 });
|
package/package.json
CHANGED
package/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.8
|