75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import https from 'https'
|
|
import querystring from 'querystring'
|
|
|
|
export interface GrammarMatch {
|
|
offset: number
|
|
length: number
|
|
message: string
|
|
shortMessage: string
|
|
replacement: string | null
|
|
ruleId: string
|
|
categoryId: string
|
|
}
|
|
|
|
const IGNORED_RULE_IDS = new Set([
|
|
'WHITESPACE_RULE',
|
|
'UNPAIRED_BRACKETS',
|
|
'EN_QUOTES',
|
|
'DASH_RULE',
|
|
'WORD_CONTAINS_UNDERSCORE',
|
|
])
|
|
|
|
export async function checkGrammar(text: string, language = 'en-US'): Promise<GrammarMatch[]> {
|
|
return new Promise((resolve, reject) => {
|
|
const body = querystring.stringify({ text, language, enabledOnly: 'false' })
|
|
const req = https.request(
|
|
{
|
|
hostname: 'api.languagetool.org',
|
|
path: '/v2/check',
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'Content-Length': Buffer.byteLength(body),
|
|
'Accept': 'application/json',
|
|
},
|
|
},
|
|
(res) => {
|
|
let data = ''
|
|
res.on('data', (chunk) => { data += chunk })
|
|
res.on('end', () => {
|
|
try {
|
|
const parsed = JSON.parse(data) as {
|
|
matches: Array<{
|
|
offset: number
|
|
length: number
|
|
message: string
|
|
shortMessage: string
|
|
replacements: Array<{ value: string }>
|
|
rule: { id: string; category: { id: string } }
|
|
}>
|
|
}
|
|
const matches: GrammarMatch[] = parsed.matches
|
|
.filter((m) => !IGNORED_RULE_IDS.has(m.rule.id))
|
|
.map((m) => ({
|
|
offset: m.offset,
|
|
length: m.length,
|
|
message: m.message,
|
|
shortMessage: m.shortMessage || m.message,
|
|
replacement: m.replacements[0]?.value ?? null,
|
|
ruleId: m.rule.id,
|
|
categoryId: m.rule.category.id,
|
|
}))
|
|
resolve(matches)
|
|
} catch (e) {
|
|
reject(new Error(`LanguageTool parse error: ${String(e)}`))
|
|
}
|
|
})
|
|
}
|
|
)
|
|
req.on('error', reject)
|
|
req.setTimeout(15000, () => { req.destroy(); reject(new Error('Grammar check timed out')) })
|
|
req.write(body)
|
|
req.end()
|
|
})
|
|
}
|