Add oxlint for type-aware linting that Biome cannot cover

Install oxlint with tsgolint for TypeScript type information. Enable
rules for unnecessary type assertions, deprecated API usage, preferring
replaceAll over replace with global regex, and String.raw for escaped
backslashes. Fix all violations: remove redundant as-casts, replace
deprecated FormEvent with SubmitEvent, convert replace(/g) to
replaceAll, and use String.raw in escapeRegExp. Add oxlint to the
pnpm check gate alongside Biome.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-14 14:41:30 +01:00
parent 36768d3aa1
commit c94c30e459
12 changed files with 345 additions and 47 deletions

View File

@@ -30,11 +30,11 @@ describe("stripTags", () => {
expect(stripTags("{@hit 5}")).toBe("+5");
});
it("strips {@h} to Hit: ", () => {
it("strips {@h} to Hit:", () => {
expect(stripTags("{@h}")).toBe("Hit: ");
});
it("strips {@hom} to Hit or Miss: ", () => {
it("strips {@hom} to Hit or Miss:", () => {
expect(stripTags("{@hom}")).toBe("Hit or Miss: ");
});

View File

@@ -373,8 +373,8 @@ function extractCr(cr: string | { cr: string } | undefined): string {
function makeCreatureId(source: string, name: string): CreatureId {
const slug = name
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)/g, "");
.replaceAll(/[^a-z0-9]+/g, "-")
.replaceAll(/(^-|-$)/g, "");
return creatureId(`${source.toLowerCase()}:${slug}`);
}

View File

@@ -25,55 +25,58 @@ export function stripTags(text: string): string {
let result = text;
// {@h} → "Hit: "
result = result.replace(/\{@h\}/g, "Hit: ");
result = result.replaceAll("{@h}", "Hit: ");
// {@hom} → "Hit or Miss: "
result = result.replace(/\{@hom\}/g, "Hit or Miss: ");
result = result.replaceAll("{@hom}", "Hit or Miss: ");
// {@actTrigger} → "Trigger:"
result = result.replace(/\{@actTrigger\}/g, "Trigger:");
result = result.replaceAll("{@actTrigger}", "Trigger:");
// {@actResponse} → "Response:"
result = result.replace(/\{@actResponse\}/g, "Response:");
result = result.replaceAll("{@actResponse}", "Response:");
// {@actSaveSuccess} → "Success:"
result = result.replace(/\{@actSaveSuccess\}/g, "Success:");
result = result.replaceAll("{@actSaveSuccess}", "Success:");
// {@actSaveSuccessOrFail} → handled below as parameterized
// {@recharge 5} → "(Recharge 5-6)", {@recharge} → "(Recharge 6)"
result = result.replace(/\{@recharge\s+(\d)\}/g, "(Recharge $1-6)");
result = result.replace(/\{@recharge\}/g, "(Recharge 6)");
result = result.replaceAll(/\{@recharge\s+(\d)\}/g, "(Recharge $1-6)");
result = result.replaceAll("{@recharge}", "(Recharge 6)");
// {@dc N} → "DC N"
result = result.replace(/\{@dc\s+(\d+)\}/g, "DC $1");
result = result.replaceAll(/\{@dc\s+(\d+)\}/g, "DC $1");
// {@hit N} → "+N"
result = result.replace(/\{@hit\s+(\d+)\}/g, "+$1");
result = result.replaceAll(/\{@hit\s+(\d+)\}/g, "+$1");
// {@atkr type} → mapped attack roll text
result = result.replace(/\{@atkr\s+([^}]+)\}/g, (_, type: string) => {
result = result.replaceAll(/\{@atkr\s+([^}]+)\}/g, (_, type: string) => {
return ATKR_MAP[type.trim()] ?? "Attack Roll:";
});
// {@actSave ability} → "Ability saving throw"
result = result.replace(/\{@actSave\s+([^}]+)\}/g, (_, ability: string) => {
const name = ABILITY_MAP[ability.trim().toLowerCase()];
return name ? `${name} saving throw` : `${ability} saving throw`;
});
result = result.replaceAll(
/\{@actSave\s+([^}]+)\}/g,
(_, ability: string) => {
const name = ABILITY_MAP[ability.trim().toLowerCase()];
return name ? `${name} saving throw` : `${ability} saving throw`;
},
);
// {@actSaveFail} → "Failure:" or {@actSaveFail N} → "Failure by N or More:"
result = result.replace(
result = result.replaceAll(
/\{@actSaveFail\s+(\d+)\}/g,
"Failure by $1 or More:",
);
result = result.replace(/\{@actSaveFail\}/g, "Failure:");
result = result.replaceAll("{@actSaveFail}", "Failure:");
// {@actSaveSuccessOrFail} → keep as-is label
result = result.replace(/\{@actSaveSuccessOrFail\}/g, "Success or Failure:");
result = result.replaceAll("{@actSaveSuccessOrFail}", "Success or Failure:");
// {@actSaveFailBy N} → "Failure by N or More:"
result = result.replace(
result = result.replaceAll(
/\{@actSaveFailBy\s+(\d+)\}/g,
"Failure by $1 or More:",
);
@@ -81,7 +84,7 @@ export function stripTags(text: string): string {
// Generic tags: {@tag Display|Source|...} → Display (first segment before |)
// Covers: spell, condition, damage, dice, variantrule, action, skill,
// creature, hazard, status, plus any unknown tags
result = result.replace(
result = result.replaceAll(
/\{@(\w+)\s+([^}]+)\}/g,
(_, tag: string, content: string) => {
// For tags with Display|Source format, extract first segment