UNPKG

unplugin-keep-polite

Version:
120 lines (114 loc) 3.38 kB
import p from 'path'; import { createUnplugin } from 'unplugin'; import MagicString from 'magic-string'; import picocolors from 'picocolors'; import { readFile } from 'fs/promises'; import { createRequire } from 'node:module'; const defaultOptions = { verbose: false, autoReplace: false, replacer: "", customDict: void 0, extraDict: [] }; let dict = /* @__PURE__ */ new Set(); let regexp = null; async function loadDict(options) { if (dict.size) return dict; const { customDict } = options; try { if (!customDict) { const _require = createRequire(import.meta.url); const Words = _require("impolite-word"); dict = new Set(Words); } if (Array.isArray(customDict)) { dict = new Set(customDict); } else if (typeof customDict === "object") { const { path } = customDict; const content = (await readFile(p.resolve(path))).toString(); dict = new Set(JSON.parse(content)); } } catch (error) { console.error(error); } if (options.extraDict) { const extraDict = new Set(options.extraDict); dict = /* @__PURE__ */ new Set([...dict, ...extraDict]); } return dict; } async function parseDict(words) { if (words) return _generateRegExp(words); if (regexp) return regexp; if (dict.size) { regexp = _generateRegExp(dict); return regexp; } console.warn("impolite-word dict is not provided"); return null; } function _generateRegExp(words) { const str = [...words.values()].join("|").replace(/\s/g, "\\s"); const reg = new RegExp(`(\\W|^)(?<word>${str})(\\W|$)`, "g"); return reg; } function logOutput(word, column, filePath) { const { bold, cyan, dim, italic, red } = picocolors; const _word = red(bold(word)); const _filePath = cyan(italic(filePath)); const _date = dim(new Date().toLocaleTimeString()); console.log( `${_date} ${cyan(bold("[unplugin:polite]"))} ${red("impolite words detected:")} ${_word} in ${_filePath} at line ${cyan(column)}` ); } async function transform(code, id, options) { await loadDict(options); const regex = await parseDict(); if (!regex) return { code }; const _code = new MagicString(code); const lines = code.split("\n"); let count = 0; lines.forEach((line, ind) => { for (const matcher of line.matchAll(regex)) { const word = matcher?.groups?.word; if (!word) continue; logOutput(word, ind + 1, id); if (options.autoReplace) { let filterLine = ""; const _preplace = options.replacer; if (typeof _preplace === "string") filterLine = line.replace(new RegExp(word, "g"), _preplace); else if (typeof _preplace === "function") filterLine = _preplace(line); _code.overwrite(count, count + line.length + 1, `${filterLine} `); } } count += line.length + 1; }); return { code: _code.toString(), map: _code.generateMap() }; } const unplugin = createUnplugin((options) => { const _options = { ...defaultOptions, ...options }; return { name: "unplugin-keep-polite", enforce: "pre", transformInclude(id) { if (id.includes("node_modules")) return false; if (p.extname(id)) return true; return false; }, async transform(code, id) { return await transform(code, id, _options); } }; }); export { unplugin as default };