@termdock/twlint 1.0.0
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/LICENSE +201 -0
- package/README.md +185 -0
- package/TRADEMARK.md +39 -0
- package/bin/twlint +6 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +142 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/config-loader.d.ts +3 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +54 -0
- package/dist/core/config-loader.js.map +1 -0
- package/dist/core/config-schema.d.ts +12 -0
- package/dist/core/config-schema.d.ts.map +1 -0
- package/dist/core/config-schema.js +64 -0
- package/dist/core/config-schema.js.map +1 -0
- package/dist/core/dictionary-manager.d.ts +14 -0
- package/dist/core/dictionary-manager.d.ts.map +1 -0
- package/dist/core/dictionary-manager.js +94 -0
- package/dist/core/dictionary-manager.js.map +1 -0
- package/dist/core/linter.d.ts +31 -0
- package/dist/core/linter.d.ts.map +1 -0
- package/dist/core/linter.js +182 -0
- package/dist/core/linter.js.map +1 -0
- package/dist/core/matching/match-strategies.d.ts +26 -0
- package/dist/core/matching/match-strategies.d.ts.map +1 -0
- package/dist/core/matching/match-strategies.js +170 -0
- package/dist/core/matching/match-strategies.js.map +1 -0
- package/dist/core/position-mapper.d.ts +31 -0
- package/dist/core/position-mapper.d.ts.map +1 -0
- package/dist/core/position-mapper.js +101 -0
- package/dist/core/position-mapper.js.map +1 -0
- package/dist/core/rules/context-sensitive.d.ts +17 -0
- package/dist/core/rules/context-sensitive.d.ts.map +1 -0
- package/dist/core/rules/context-sensitive.js +154 -0
- package/dist/core/rules/context-sensitive.js.map +1 -0
- package/dist/core/rules/mainland-terms.d.ts +11 -0
- package/dist/core/rules/mainland-terms.d.ts.map +1 -0
- package/dist/core/rules/mainland-terms.js +45 -0
- package/dist/core/rules/mainland-terms.js.map +1 -0
- package/dist/core/rules/simplified-chars.d.ts +10 -0
- package/dist/core/rules/simplified-chars.d.ts.map +1 -0
- package/dist/core/rules/simplified-chars.js +51 -0
- package/dist/core/rules/simplified-chars.js.map +1 -0
- package/dist/dictionaries/academic.json +141 -0
- package/dist/dictionaries/core.json +1491 -0
- package/dist/dictionaries/extended.json +141 -0
- package/dist/dictionaries/index.json +21 -0
- package/dist/formatters/json.d.ts +5 -0
- package/dist/formatters/json.d.ts.map +1 -0
- package/dist/formatters/json.js +18 -0
- package/dist/formatters/json.js.map +1 -0
- package/dist/formatters/stylish.d.ts +5 -0
- package/dist/formatters/stylish.d.ts.map +1 -0
- package/dist/formatters/stylish.js +45 -0
- package/dist/formatters/stylish.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface TWLintConfigRule {
|
|
2
|
+
files?: string[];
|
|
3
|
+
dictionaries?: string[];
|
|
4
|
+
rules?: Record<string, 'error' | 'warning' | 'info' | 'off'>;
|
|
5
|
+
}
|
|
6
|
+
export type TWLintConfigArray = TWLintConfigRule[];
|
|
7
|
+
export type TWLintConfigSingle = TWLintConfigRule;
|
|
8
|
+
export type TWLintUserConfig = TWLintConfigSingle | TWLintConfigArray;
|
|
9
|
+
export declare const DEFAULT_CONFIG: TWLintConfigSingle;
|
|
10
|
+
export declare function validateConfig(config: unknown): TWLintConfigSingle;
|
|
11
|
+
export declare function createSampleConfig(): string;
|
|
12
|
+
//# sourceMappingURL=config-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["../../src/core/config-schema.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC,CAAA;CAC7D;AAED,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,EAAE,CAAA;AAClD,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,CAAA;AAGjD,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,GAAG,iBAAiB,CAAA;AAErE,eAAO,MAAM,cAAc,EAAE,kBAO5B,CAAA;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,kBAAkB,CAYlE;AA4BD,wBAAgB,kBAAkB,IAAI,MAAM,CAuB3C"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// TWLint 配置檔案結構定義和驗證
|
|
2
|
+
export const DEFAULT_CONFIG = {
|
|
3
|
+
files: ['**/*.md', '**/*.txt'],
|
|
4
|
+
dictionaries: ['core'],
|
|
5
|
+
rules: {
|
|
6
|
+
'simplified-chars': 'error',
|
|
7
|
+
'mainland-terms': 'warning'
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
export function validateConfig(config) {
|
|
11
|
+
if (!config || typeof config !== 'object') {
|
|
12
|
+
return DEFAULT_CONFIG;
|
|
13
|
+
}
|
|
14
|
+
// 如果是陣列,合併所有配置
|
|
15
|
+
if (Array.isArray(config)) {
|
|
16
|
+
return mergeConfigs(config);
|
|
17
|
+
}
|
|
18
|
+
// 單一配置物件
|
|
19
|
+
return mergeConfig(DEFAULT_CONFIG, config);
|
|
20
|
+
}
|
|
21
|
+
function mergeConfigs(configs) {
|
|
22
|
+
let merged = { ...DEFAULT_CONFIG };
|
|
23
|
+
for (const config of configs) {
|
|
24
|
+
if (config && typeof config === 'object') {
|
|
25
|
+
merged = mergeConfig(merged, config);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return merged;
|
|
29
|
+
}
|
|
30
|
+
function mergeConfig(defaultConfig, userConfig) {
|
|
31
|
+
return {
|
|
32
|
+
files: userConfig.files || defaultConfig.files,
|
|
33
|
+
dictionaries: userConfig.dictionaries || defaultConfig.dictionaries,
|
|
34
|
+
rules: {
|
|
35
|
+
...defaultConfig.rules,
|
|
36
|
+
...userConfig.rules
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function createSampleConfig() {
|
|
41
|
+
return `// TWLint 配置檔案
|
|
42
|
+
// 專注於核心功能:簡繁轉換 + 大陸用語檢測
|
|
43
|
+
|
|
44
|
+
export default [
|
|
45
|
+
{
|
|
46
|
+
// 檢查的檔案類型
|
|
47
|
+
files: ["**/*.md", "**/*.txt"],
|
|
48
|
+
|
|
49
|
+
// 使用的詞庫
|
|
50
|
+
dictionaries: ["core"],
|
|
51
|
+
|
|
52
|
+
// 規則配置
|
|
53
|
+
rules: {
|
|
54
|
+
"simplified-chars": "error", // 簡體字檢測(自動修復)
|
|
55
|
+
"mainland-terms": "warning" // 大陸用語檢測(提供建議)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
// 使用方式:
|
|
61
|
+
// twlint check **/*.md // 檢查檔案
|
|
62
|
+
// twlint check **/*.md --fix // 自動修復`;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=config-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../src/core/config-schema.ts"],"names":[],"mappings":"AAAA,qBAAqB;AAcrB,MAAM,CAAC,MAAM,cAAc,GAAuB;IAChD,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAC9B,YAAY,EAAE,CAAC,MAAM,CAAC;IACtB,KAAK,EAAE;QACL,kBAAkB,EAAE,OAAO;QAC3B,gBAAgB,EAAE,SAAS;KAC5B;CACF,CAAA;AAED,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1C,OAAO,cAAc,CAAA;IACvB,CAAC;IAED,eAAe;IACf,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO,YAAY,CAAC,MAAM,CAAC,CAAA;IAC7B,CAAC;IAED,SAAS;IACT,OAAO,WAAW,CAAC,cAAc,EAAE,MAAqC,CAAC,CAAA;AAC3E,CAAC;AAED,SAAS,YAAY,CAAC,OAAkB;IACtC,IAAI,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAA;IAElC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,MAAqC,CAAC,CAAA;QACrE,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,WAAW,CAClB,aAAiC,EACjC,UAAuC;IAEvC,OAAO;QACL,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,aAAa,CAAC,KAAK;QAC9C,YAAY,EAAE,UAAU,CAAC,YAAY,IAAI,aAAa,CAAC,YAAY;QACnE,KAAK,EAAE;YACL,GAAG,aAAa,CAAC,KAAK;YACtB,GAAG,UAAU,CAAC,KAAK;SACpB;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO;;;;;;;;;;;;;;;;;;;;;+CAqBsC,CAAA;AAC/C,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CompiledDict, MatchResult } from '../types.js';
|
|
2
|
+
export declare class DictionaryManager {
|
|
3
|
+
private cache;
|
|
4
|
+
private readonly dictionariesPath;
|
|
5
|
+
private readonly strategyRegistry;
|
|
6
|
+
constructor(dictionariesPath?: string);
|
|
7
|
+
loadDictionary(name: string): Promise<CompiledDict>;
|
|
8
|
+
findMatches(text: string): MatchResult[];
|
|
9
|
+
scanAvailableDictionaries(): Promise<string[]>;
|
|
10
|
+
getLoadedDictionaries(): string[];
|
|
11
|
+
clearCache(): void;
|
|
12
|
+
getAvailableDictionaries(): string[];
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=dictionary-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictionary-manager.d.ts","sourceRoot":"","sources":["../../src/core/dictionary-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAa,WAAW,EAAE,MAAM,aAAa,CAAA;AAGvE,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuB;gBAE5C,gBAAgB,CAAC,EAAE,MAAM;IAW/B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAkBzD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE;IAsClC,yBAAyB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAepD,qBAAqB,IAAI,MAAM,EAAE;IAIjC,UAAU,IAAI,IAAI;IAKlB,wBAAwB,IAAI,MAAM,EAAE;CAIrC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { MatchStrategyRegistry } from './matching/match-strategies.js';
|
|
5
|
+
export class DictionaryManager {
|
|
6
|
+
cache = new Map();
|
|
7
|
+
dictionariesPath;
|
|
8
|
+
strategyRegistry;
|
|
9
|
+
constructor(dictionariesPath) {
|
|
10
|
+
if (dictionariesPath) {
|
|
11
|
+
this.dictionariesPath = dictionariesPath;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
// 動態解析詞庫路徑:相對於編譯後的 JS 檔案位置
|
|
15
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
this.dictionariesPath = join(currentDir, '..', 'dictionaries');
|
|
17
|
+
}
|
|
18
|
+
this.strategyRegistry = new MatchStrategyRegistry();
|
|
19
|
+
}
|
|
20
|
+
async loadDictionary(name) {
|
|
21
|
+
if (this.cache.has(name)) {
|
|
22
|
+
return this.cache.get(name);
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const dictPath = join(this.dictionariesPath, `${name}.json`);
|
|
26
|
+
const content = await readFile(dictPath, 'utf-8');
|
|
27
|
+
const dict = JSON.parse(content);
|
|
28
|
+
this.cache.set(name, dict);
|
|
29
|
+
return dict;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33
|
+
throw new Error(`Failed to load dictionary "${name}": ${message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
findMatches(text) {
|
|
37
|
+
const allMatches = [];
|
|
38
|
+
const processedRanges = new Set();
|
|
39
|
+
for (const dict of this.cache.values()) {
|
|
40
|
+
for (const [term, entry] of Object.entries(dict.lookup)) {
|
|
41
|
+
const strategy = this.strategyRegistry.getStrategy(entry.match_type || 'exact');
|
|
42
|
+
if (!strategy)
|
|
43
|
+
continue;
|
|
44
|
+
const matches = strategy.match(text, term, entry.context);
|
|
45
|
+
for (const match of matches) {
|
|
46
|
+
const rangeKey = `${match.start}-${match.end}`;
|
|
47
|
+
// 避免重複匹配同一個位置
|
|
48
|
+
if (!processedRanges.has(rangeKey)) {
|
|
49
|
+
processedRanges.add(rangeKey);
|
|
50
|
+
allMatches.push({
|
|
51
|
+
...match,
|
|
52
|
+
replacement: entry.taiwan,
|
|
53
|
+
confidence: entry.confidence,
|
|
54
|
+
rule: `${dict.metadata.name}-${entry.match_type}`
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// 按信心度和位置排序:高信心度優先,相同信心度則按位置順序
|
|
61
|
+
return allMatches.sort((a, b) => {
|
|
62
|
+
if (Math.abs(a.confidence - b.confidence) > 0.01) {
|
|
63
|
+
return b.confidence - a.confidence;
|
|
64
|
+
}
|
|
65
|
+
return a.start - b.start;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async scanAvailableDictionaries() {
|
|
69
|
+
try {
|
|
70
|
+
const { readdir } = await import('fs/promises');
|
|
71
|
+
const files = await readdir(this.dictionariesPath);
|
|
72
|
+
return files
|
|
73
|
+
.filter(file => file.endsWith('.json'))
|
|
74
|
+
.map(file => file.replace('.json', ''))
|
|
75
|
+
.filter(name => name !== 'index'); // 排除索引文件
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
console.warn(`Failed to scan dictionary directory: ${this.dictionariesPath}`);
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
getLoadedDictionaries() {
|
|
83
|
+
return Array.from(this.cache.keys());
|
|
84
|
+
}
|
|
85
|
+
clearCache() {
|
|
86
|
+
this.cache.clear();
|
|
87
|
+
}
|
|
88
|
+
// Legacy method for backward compatibility - delegates to scanAvailableDictionaries
|
|
89
|
+
getAvailableDictionaries() {
|
|
90
|
+
console.warn('getAvailableDictionaries() is deprecated. Use scanAvailableDictionaries() instead.');
|
|
91
|
+
return ['core', 'academic', 'extended']; // Fallback hardcoded list
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=dictionary-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictionary-manager.js","sourceRoot":"","sources":["../../src/core/dictionary-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { LintResult, Issue, TWLintConfig } from '../types.js';
|
|
2
|
+
export declare class TWLinter {
|
|
3
|
+
private dictManager;
|
|
4
|
+
private rules;
|
|
5
|
+
private config;
|
|
6
|
+
private deepMode;
|
|
7
|
+
private converter;
|
|
8
|
+
constructor(config: TWLintConfig, options?: {
|
|
9
|
+
deep?: boolean;
|
|
10
|
+
});
|
|
11
|
+
private initializeRules;
|
|
12
|
+
lintFiles(patterns: string[]): Promise<LintResult[]>;
|
|
13
|
+
lintText(text: string, filePath?: string): Promise<Issue[]>;
|
|
14
|
+
fixText(text: string): Promise<string>;
|
|
15
|
+
private expandFilePatterns;
|
|
16
|
+
private loadDictionaries;
|
|
17
|
+
private getActiveRules;
|
|
18
|
+
/**
|
|
19
|
+
* 使用 opencc-js 將簡體中文轉換為繁體中文
|
|
20
|
+
*/
|
|
21
|
+
private convertToTraditional;
|
|
22
|
+
/**
|
|
23
|
+
* 檢查規則是否啟用
|
|
24
|
+
*/
|
|
25
|
+
private isRuleActive;
|
|
26
|
+
/**
|
|
27
|
+
* 使用 PositionMapper 調整位置映射
|
|
28
|
+
*/
|
|
29
|
+
private adjustPositionsWithMapper;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=linter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../../src/core/linter.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAQ,MAAM,aAAa,CAAA;AAExE,qBAAa,QAAQ;IACnB,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,SAAS,CAA8B;gBAEnC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE;IAQ9D,OAAO,CAAC,eAAe;IAQjB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IA+BpD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAoC3D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAgC9B,kBAAkB;YAWlB,gBAAgB;IAoB9B,OAAO,CAAC,cAAc;IAmBtB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,YAAY;IAKpB;;OAEG;IACH,OAAO,CAAC,yBAAyB;CAUlC"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { glob } from 'glob';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { DictionaryManager } from './dictionary-manager.js';
|
|
4
|
+
import { SimplifiedCharsRule } from './rules/simplified-chars.js';
|
|
5
|
+
import { MainlandTermsRule } from './rules/mainland-terms.js';
|
|
6
|
+
import { PositionMapper } from './position-mapper.js';
|
|
7
|
+
import { Converter } from 'opencc-js';
|
|
8
|
+
export class TWLinter {
|
|
9
|
+
dictManager;
|
|
10
|
+
rules = new Map();
|
|
11
|
+
config;
|
|
12
|
+
deepMode = false;
|
|
13
|
+
converter;
|
|
14
|
+
constructor(config, options) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.deepMode = options?.deep || false;
|
|
17
|
+
this.dictManager = new DictionaryManager();
|
|
18
|
+
this.converter = Converter({ from: 'cn', to: 'tw' });
|
|
19
|
+
this.initializeRules();
|
|
20
|
+
}
|
|
21
|
+
initializeRules() {
|
|
22
|
+
// 註冊簡體字檢測規則
|
|
23
|
+
this.rules.set('simplified-chars', new SimplifiedCharsRule());
|
|
24
|
+
// 註冊大陸用語檢測規則 (需要詞庫管理器)
|
|
25
|
+
this.rules.set('mainland-terms', new MainlandTermsRule(this.dictManager));
|
|
26
|
+
}
|
|
27
|
+
async lintFiles(patterns) {
|
|
28
|
+
const files = await this.expandFilePatterns(patterns);
|
|
29
|
+
const results = [];
|
|
30
|
+
for (const filePath of files) {
|
|
31
|
+
try {
|
|
32
|
+
const content = await readFile(filePath, 'utf-8');
|
|
33
|
+
const issues = await this.lintText(content, filePath);
|
|
34
|
+
results.push({
|
|
35
|
+
filePath,
|
|
36
|
+
messages: issues
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
+
results.push({
|
|
42
|
+
filePath,
|
|
43
|
+
messages: [{
|
|
44
|
+
line: 1,
|
|
45
|
+
column: 1,
|
|
46
|
+
message: `Failed to read file: ${message}`,
|
|
47
|
+
severity: 'error',
|
|
48
|
+
rule: 'file-read-error',
|
|
49
|
+
fixable: false
|
|
50
|
+
}]
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
55
|
+
}
|
|
56
|
+
async lintText(text, filePath) {
|
|
57
|
+
await this.loadDictionaries(this.deepMode);
|
|
58
|
+
const issues = [];
|
|
59
|
+
const activeRules = this.getActiveRules();
|
|
60
|
+
// Process all active rules
|
|
61
|
+
for (const rule of activeRules) {
|
|
62
|
+
try {
|
|
63
|
+
let textToCheck = text;
|
|
64
|
+
let positionMapper;
|
|
65
|
+
// Apply text conversion if needed (for mainland-terms rule)
|
|
66
|
+
if (rule.name === 'mainland-terms') {
|
|
67
|
+
const convertedText = this.convertToTraditional(text);
|
|
68
|
+
textToCheck = convertedText;
|
|
69
|
+
positionMapper = new PositionMapper(text, convertedText);
|
|
70
|
+
}
|
|
71
|
+
const ruleIssues = await rule.check(textToCheck);
|
|
72
|
+
// Adjust positions if we used converted text
|
|
73
|
+
const adjustedIssues = positionMapper
|
|
74
|
+
? this.adjustPositionsWithMapper(ruleIssues, positionMapper)
|
|
75
|
+
: ruleIssues;
|
|
76
|
+
issues.push(...adjustedIssues);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80
|
+
console.warn(`Warning: Rule "${rule.name}" failed: ${message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return issues;
|
|
84
|
+
}
|
|
85
|
+
async fixText(text) {
|
|
86
|
+
// 確保詞庫已載入
|
|
87
|
+
await this.loadDictionaries(this.deepMode);
|
|
88
|
+
let fixedText = text;
|
|
89
|
+
// 階段1:簡繁轉換修復
|
|
90
|
+
const simplifiedRule = this.rules.get('simplified-chars');
|
|
91
|
+
if (simplifiedRule && this.isRuleActive('simplified-chars') && simplifiedRule.fix) {
|
|
92
|
+
try {
|
|
93
|
+
fixedText = await simplifiedRule.fix(fixedText);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
console.warn(`Warning: Failed to apply simplified-chars fix: ${message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// 階段2:大陸用語修復(在轉換後的文本上進行)
|
|
101
|
+
const mainlandRule = this.rules.get('mainland-terms');
|
|
102
|
+
if (mainlandRule && this.isRuleActive('mainland-terms') && mainlandRule.fix) {
|
|
103
|
+
try {
|
|
104
|
+
fixedText = await mainlandRule.fix(fixedText);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
108
|
+
console.warn(`Warning: Failed to apply mainland-terms fix: ${message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return fixedText;
|
|
112
|
+
}
|
|
113
|
+
async expandFilePatterns(patterns) {
|
|
114
|
+
const files = [];
|
|
115
|
+
for (const pattern of patterns) {
|
|
116
|
+
const matches = await glob(pattern, { ignore: 'node_modules/**' });
|
|
117
|
+
files.push(...matches);
|
|
118
|
+
}
|
|
119
|
+
return [...new Set(files)]; // Remove duplicates
|
|
120
|
+
}
|
|
121
|
+
async loadDictionaries(deep) {
|
|
122
|
+
let dictNames = this.config.dictionaries || ['core'];
|
|
123
|
+
// 深度模式:載入所有可用詞庫
|
|
124
|
+
if (deep) {
|
|
125
|
+
const availableDicts = this.dictManager.getAvailableDictionaries();
|
|
126
|
+
dictNames = [...new Set([...dictNames, ...availableDicts])];
|
|
127
|
+
}
|
|
128
|
+
for (const name of dictNames) {
|
|
129
|
+
try {
|
|
130
|
+
await this.dictManager.loadDictionary(name);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
// 忽略無法載入的詞庫,但記錄警告
|
|
134
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
135
|
+
console.warn(`Warning: Failed to load dictionary "${name}": ${message}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
getActiveRules() {
|
|
140
|
+
const activeRules = [];
|
|
141
|
+
const rules = this.config.rules || {
|
|
142
|
+
'simplified-chars': 'error',
|
|
143
|
+
'mainland-terms': 'warning'
|
|
144
|
+
};
|
|
145
|
+
for (const [ruleName, severity] of Object.entries(rules)) {
|
|
146
|
+
if (severity !== 'off') {
|
|
147
|
+
const rule = this.rules.get(ruleName);
|
|
148
|
+
if (rule) {
|
|
149
|
+
activeRules.push(rule);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return activeRules;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 使用 opencc-js 將簡體中文轉換為繁體中文
|
|
157
|
+
*/
|
|
158
|
+
convertToTraditional(text) {
|
|
159
|
+
return this.converter(text);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 檢查規則是否啟用
|
|
163
|
+
*/
|
|
164
|
+
isRuleActive(ruleName) {
|
|
165
|
+
const rules = this.config.rules || {};
|
|
166
|
+
return rules[ruleName] !== 'off';
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* 使用 PositionMapper 調整位置映射
|
|
170
|
+
*/
|
|
171
|
+
adjustPositionsWithMapper(issues, mapper) {
|
|
172
|
+
return issues.map(issue => {
|
|
173
|
+
const originalPos = mapper.mapToOriginal(issue.line, issue.column);
|
|
174
|
+
return {
|
|
175
|
+
...issue,
|
|
176
|
+
line: originalPos.line,
|
|
177
|
+
column: originalPos.column
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=linter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linter.js","sourceRoot":"","sources":["../../src/core/linter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { MatchStrategy, MatchResult, ContextRule } from '../../types.js';
|
|
2
|
+
export declare class ExactMatchStrategy implements MatchStrategy {
|
|
3
|
+
name: "exact";
|
|
4
|
+
match(text: string, term: string, context?: ContextRule): MatchResult[];
|
|
5
|
+
private isValidMatch;
|
|
6
|
+
}
|
|
7
|
+
export declare class WordBoundaryStrategy implements MatchStrategy {
|
|
8
|
+
name: "word_boundary";
|
|
9
|
+
match(text: string, term: string, context?: ContextRule): MatchResult[];
|
|
10
|
+
private isValidMatch;
|
|
11
|
+
private escapeRegExp;
|
|
12
|
+
}
|
|
13
|
+
export declare class ContextSensitiveStrategy implements MatchStrategy {
|
|
14
|
+
name: "context_sensitive";
|
|
15
|
+
match(text: string, term: string, context?: ContextRule): MatchResult[];
|
|
16
|
+
private isValidContext;
|
|
17
|
+
}
|
|
18
|
+
export declare class MatchStrategyRegistry {
|
|
19
|
+
private strategies;
|
|
20
|
+
constructor();
|
|
21
|
+
private registerDefaults;
|
|
22
|
+
register(strategy: MatchStrategy): void;
|
|
23
|
+
getStrategy(name: string): MatchStrategy | undefined;
|
|
24
|
+
getAllStrategies(): MatchStrategy[];
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=match-strategies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match-strategies.d.ts","sourceRoot":"","sources":["../../../src/core/matching/match-strategies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE7E,qBAAa,kBAAmB,YAAW,aAAa;IACtD,IAAI,EAAG,OAAO,CAAS;IAEvB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE;IAyBvE,OAAO,CAAC,YAAY;CA8BrB;AAED,qBAAa,oBAAqB,YAAW,aAAa;IACxD,IAAI,EAAG,eAAe,CAAS;IAE/B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE;IA0BvE,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,YAAY;CAGrB;AAED,qBAAa,wBAAyB,YAAW,aAAa;IAC5D,IAAI,EAAG,mBAAmB,CAAS;IAEnC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE;IA8BvE,OAAO,CAAC,cAAc;CA4BvB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,UAAU,CAAmC;;IAMrD,OAAO,CAAC,gBAAgB;IAMxB,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAIvC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIpD,gBAAgB,IAAI,aAAa,EAAE;CAGpC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
export class ExactMatchStrategy {
|
|
2
|
+
name = 'exact';
|
|
3
|
+
match(text, term, context) {
|
|
4
|
+
const results = [];
|
|
5
|
+
let startIndex = 0;
|
|
6
|
+
while (true) {
|
|
7
|
+
const index = text.indexOf(term, startIndex);
|
|
8
|
+
if (index === -1)
|
|
9
|
+
break;
|
|
10
|
+
if (this.isValidMatch(text, term, index, context)) {
|
|
11
|
+
results.push({
|
|
12
|
+
term,
|
|
13
|
+
replacement: '', // Will be filled by caller
|
|
14
|
+
start: index,
|
|
15
|
+
end: index + term.length,
|
|
16
|
+
confidence: 1.0,
|
|
17
|
+
rule: 'exact-match'
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
startIndex = index + 1;
|
|
21
|
+
}
|
|
22
|
+
return results;
|
|
23
|
+
}
|
|
24
|
+
isValidMatch(text, term, index, context) {
|
|
25
|
+
if (!context)
|
|
26
|
+
return true;
|
|
27
|
+
const beforeText = text.substring(Math.max(0, index - 20), index);
|
|
28
|
+
const afterText = text.substring(index + term.length, Math.min(text.length, index + term.length + 20));
|
|
29
|
+
// Check exclude patterns
|
|
30
|
+
if (context.exclude) {
|
|
31
|
+
const surroundingText = beforeText + term + afterText;
|
|
32
|
+
for (const excludePattern of context.exclude) {
|
|
33
|
+
if (surroundingText.includes(excludePattern)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Check before context
|
|
39
|
+
if (context.before) {
|
|
40
|
+
const hasValidBefore = context.before.some(pattern => beforeText.includes(pattern));
|
|
41
|
+
if (!hasValidBefore)
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// Check after context
|
|
45
|
+
if (context.after) {
|
|
46
|
+
const hasValidAfter = context.after.some(pattern => afterText.includes(pattern));
|
|
47
|
+
if (!hasValidAfter)
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export class WordBoundaryStrategy {
|
|
54
|
+
name = 'word_boundary';
|
|
55
|
+
match(text, term, context) {
|
|
56
|
+
const results = [];
|
|
57
|
+
// For Chinese text, word boundary is different from English \b
|
|
58
|
+
// We need to check if the term is surrounded by non-Chinese characters or string boundaries
|
|
59
|
+
const chineseWordPattern = new RegExp(`(?<![\\u4e00-\\u9fff])${this.escapeRegExp(term)}(?![\\u4e00-\\u9fff])`, 'g');
|
|
60
|
+
let match;
|
|
61
|
+
while ((match = chineseWordPattern.exec(text)) !== null) {
|
|
62
|
+
const index = match.index;
|
|
63
|
+
if (this.isValidMatch(text, term, index, context)) {
|
|
64
|
+
results.push({
|
|
65
|
+
term,
|
|
66
|
+
replacement: '', // Will be filled by caller
|
|
67
|
+
start: index,
|
|
68
|
+
end: index + term.length,
|
|
69
|
+
confidence: 0.9,
|
|
70
|
+
rule: 'word-boundary'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
isValidMatch(text, term, index, context) {
|
|
77
|
+
if (!context)
|
|
78
|
+
return true;
|
|
79
|
+
const beforeText = text.substring(Math.max(0, index - 20), index);
|
|
80
|
+
const afterText = text.substring(index + term.length, Math.min(text.length, index + term.length + 20));
|
|
81
|
+
// Check exclude patterns
|
|
82
|
+
if (context.exclude) {
|
|
83
|
+
const surroundingText = beforeText + term + afterText;
|
|
84
|
+
for (const excludePattern of context.exclude) {
|
|
85
|
+
if (surroundingText.includes(excludePattern)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
escapeRegExp(string) {
|
|
93
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export class ContextSensitiveStrategy {
|
|
97
|
+
name = 'context_sensitive';
|
|
98
|
+
match(text, term, context) {
|
|
99
|
+
if (!context || (!context.before && !context.after)) {
|
|
100
|
+
// Fallback to word boundary if no context specified
|
|
101
|
+
return new WordBoundaryStrategy().match(text, term, context);
|
|
102
|
+
}
|
|
103
|
+
const results = [];
|
|
104
|
+
let startIndex = 0;
|
|
105
|
+
while (true) {
|
|
106
|
+
const index = text.indexOf(term, startIndex);
|
|
107
|
+
if (index === -1)
|
|
108
|
+
break;
|
|
109
|
+
if (this.isValidContext(text, term, index, context)) {
|
|
110
|
+
results.push({
|
|
111
|
+
term,
|
|
112
|
+
replacement: '', // Will be filled by caller
|
|
113
|
+
start: index,
|
|
114
|
+
end: index + term.length,
|
|
115
|
+
confidence: 0.8,
|
|
116
|
+
rule: 'context-sensitive'
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
startIndex = index + 1;
|
|
120
|
+
}
|
|
121
|
+
return results;
|
|
122
|
+
}
|
|
123
|
+
isValidContext(text, term, index, context) {
|
|
124
|
+
const beforeText = text.substring(Math.max(0, index - 50), index);
|
|
125
|
+
const afterText = text.substring(index + term.length, Math.min(text.length, index + term.length + 50));
|
|
126
|
+
// Check exclude patterns first
|
|
127
|
+
if (context.exclude) {
|
|
128
|
+
const surroundingText = beforeText + term + afterText;
|
|
129
|
+
for (const excludePattern of context.exclude) {
|
|
130
|
+
if (surroundingText.includes(excludePattern)) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Must match before context if specified
|
|
136
|
+
if (context.before) {
|
|
137
|
+
const hasValidBefore = context.before.some(pattern => beforeText.includes(pattern));
|
|
138
|
+
if (!hasValidBefore)
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
// Must match after context if specified
|
|
142
|
+
if (context.after) {
|
|
143
|
+
const hasValidAfter = context.after.some(pattern => afterText.includes(pattern));
|
|
144
|
+
if (!hasValidAfter)
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export class MatchStrategyRegistry {
|
|
151
|
+
strategies = new Map();
|
|
152
|
+
constructor() {
|
|
153
|
+
this.registerDefaults();
|
|
154
|
+
}
|
|
155
|
+
registerDefaults() {
|
|
156
|
+
this.register(new ExactMatchStrategy());
|
|
157
|
+
this.register(new WordBoundaryStrategy());
|
|
158
|
+
this.register(new ContextSensitiveStrategy());
|
|
159
|
+
}
|
|
160
|
+
register(strategy) {
|
|
161
|
+
this.strategies.set(strategy.name, strategy);
|
|
162
|
+
}
|
|
163
|
+
getStrategy(name) {
|
|
164
|
+
return this.strategies.get(name);
|
|
165
|
+
}
|
|
166
|
+
getAllStrategies() {
|
|
167
|
+
return Array.from(this.strategies.values());
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=match-strategies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match-strategies.js","sourceRoot":"","sources":["../../../src/core/matching/match-strategies.ts"],"names":[],"mappings":"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"}
|