unplugin-keep-polite
Version:
keep polite by plugin
120 lines (114 loc) • 3.38 kB
JavaScript
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 };