1import { assert, describe } from "@effect/vitest";
2
3import { createOxlintRuleHarness } from "../test/utils.ts";
4
5const rule = createOxlintRuleHarness("t3code/no-hermes-unsupported-apis", {
6 filename: "fixture.ts",
7});
8
9describe("t3code/no-hermes-unsupported-apis", () => {
10 rule.valid("allows in-place sort on a copy", `const sorted = [...items].sort(compare);`);
11
12 rule.valid("allows in-place reverse on a copy", `const reversed = [...items].reverse();`);
13
14 rule.valid(
15 "allows other ES2023 methods Hermes ships",
16 `const last = items.findLast(Boolean); const tail = items.at(-1);`,
17 );
18
19 rule.valid(
20 "ignores property reads that are not calls",
21 `const hasToSorted = typeof Array.prototype.toSorted === "function";`,
22 );
23
24 rule.invalid("reports toSorted", `const sorted = items.toSorted(compare);`, (output) => {
25 assert.match(output, /Array#toSorted/);
26 });
27
28 rule.invalid(
29 "reports toReversed in a chain",
30 `const open = chains.map((chain) => chain.layers.toReversed().filter(isOpen));`,
31 (output) => {
32 assert.match(output, /Array#toReversed/);
33 },
34 );
35
36 rule.invalid(
37 "reports toSpliced via computed access with a copy-then-splice remediation",
38 `const next = items["toSpliced"](0, 1);`,
39 (output) => {
40 assert.match(output, /Array#toSpliced/);
41 assert.match(output, /const copy = \[\.\.\.array\]; copy\.splice\(\.\.\.\); use copy/);
42 },
43 );
44
45 rule.invalid(
46 "reports a static template-literal property name",
47 "const reversed = items[`toReversed`]();",
48 (output) => {
49 assert.match(output, /Array#toReversed/);
50 },
51 );
52
53 rule.valid(
54 "ignores a template-literal property with substitutions",
55 "const value = items[`to${suffix}`]();",
56 );
57
58 rule.valid("allows supported Intl constructors", `new Intl.NumberFormat();`);
59 rule.valid("allows feature detection", `const supported = typeof Intl.Segmenter === "function";`);
60 rule.valid("allows unrelated Segmenter constructors", `new custom.Segmenter();`);
61 rule.valid("ignores dynamic computed names", `new Intl[Segmenter](); items[toSorted]();`);
62
63 for (const expression of [
64 "new Intl.Segmenter(undefined, { granularity: 'grapheme' })",
65 "new Intl['Segmenter']()",
66 "new Intl[`Segmenter`]()",
67 "new globalThis.Intl.Segmenter()",
68 "new globalThis['Intl']['Segmenter']()",
69 "new global.Intl.Segmenter()",
70 "new window.Intl.Segmenter()",
71 "Intl.Segmenter()",
72 "Intl.Segmenter?.()",
73 ]) {
74 rule.invalid(`reports ${expression}`, `${expression};`, (output) => {
75 assert.match(output, /Hermes does not implement Intl\.Segmenter/);
76 assert.match(output, /portable implementation/);
77 });
78 }
79});