mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-15 10:16:07 +00:00
* fix(ui): make indexed access explicit across the Control UI Burns down all 322 ui-lane noUncheckedIndexedAccess errors: untrusted markdown/diff/patch parsing gets miss-tolerant guards (renderer rules degrade to empty output instead of throwing mid-render), accumulators use canonical sparse initialization, DOM lookups stay null-guarded, and length-checked constructions carry named invariants. * feat(tooling): enforce noUncheckedIndexedAccess in the core and ui lanes Flips the flag on for tsconfig.core.json and tsconfig.ui.json (covering src, all packages including the two deferred ones, and ui) and retires the strict-ratchet lane wholesale: config, script, projects reference, check/CI wiring, changed-lane routing, and sync test. The assertion-ban oxlint override stays as an independent surface.
234 lines
7.2 KiB
TypeScript
234 lines
7.2 KiB
TypeScript
// Oxlint Config tests cover oxlint config script behavior.
|
|
import fs from "node:fs";
|
|
import JSON5 from "json5";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
type OxlintConfig = {
|
|
ignorePatterns?: string[];
|
|
overrides?: Array<{ files?: string[]; rules?: Record<string, unknown> }>;
|
|
rules?: Record<string, unknown>;
|
|
};
|
|
|
|
type OxlintTsconfig = {
|
|
include?: string[];
|
|
exclude?: string[];
|
|
};
|
|
|
|
const ZERO_BASELINE_RULES = [
|
|
"eslint/no-div-regex",
|
|
"eslint/no-constructor-return",
|
|
"eslint/no-extra-label",
|
|
"eslint/no-lone-blocks",
|
|
"eslint/no-multi-str",
|
|
"eslint/no-proto",
|
|
"eslint/no-regex-spaces",
|
|
"eslint/no-sequences",
|
|
"eslint/no-self-compare",
|
|
"eslint/no-var",
|
|
"eslint/no-param-reassign",
|
|
"eslint/no-implicit-coercion",
|
|
"eslint/no-useless-rename",
|
|
"eslint/no-useless-return",
|
|
"eslint/no-new-wrappers",
|
|
"eslint/no-else-return",
|
|
"eslint/no-lonely-if",
|
|
"eslint/no-case-declarations",
|
|
"eslint/object-shorthand",
|
|
"eslint/prefer-exponentiation-operator",
|
|
"eslint/prefer-const",
|
|
"eslint/prefer-numeric-literals",
|
|
"eslint/prefer-object-has-own",
|
|
"eslint/radix",
|
|
"eslint/unicode-bom",
|
|
"eslint/yoda",
|
|
"import/no-absolute-path",
|
|
"import/first",
|
|
"import/no-empty-named-blocks",
|
|
"import/no-duplicates",
|
|
"import/no-self-import",
|
|
"node/no-exports-assign",
|
|
"promise/no-new-statics",
|
|
"typescript/adjacent-overload-signatures",
|
|
"typescript/ban-tslint-comment",
|
|
"typescript/no-import-type-side-effects",
|
|
"typescript/no-inferrable-types",
|
|
"typescript/no-non-null-asserted-nullish-coalescing",
|
|
"typescript/no-unnecessary-qualifier",
|
|
"typescript/prefer-find",
|
|
"typescript/prefer-for-of",
|
|
"typescript/prefer-function-type",
|
|
"typescript/prefer-includes",
|
|
"typescript/prefer-reduce-type-parameter",
|
|
"typescript/prefer-return-this-type",
|
|
"unicorn/consistent-date-clone",
|
|
"unicorn/consistent-empty-array-spread",
|
|
"unicorn/no-console-spaces",
|
|
"unicorn/no-length-as-slice-end",
|
|
"unicorn/no-instanceof-array",
|
|
"unicorn/no-negation-in-equality-check",
|
|
"unicorn/no-new-buffer",
|
|
"unicorn/no-typeof-undefined",
|
|
"unicorn/no-unreadable-array-destructuring",
|
|
"unicorn/no-useless-error-capture-stack-trace",
|
|
"unicorn/no-zero-fractions",
|
|
"unicorn/prefer-array-flat",
|
|
"unicorn/prefer-array-some",
|
|
"unicorn/prefer-dom-node-text-content",
|
|
"unicorn/prefer-keyboard-event-key",
|
|
"unicorn/prefer-math-min-max",
|
|
"unicorn/prefer-negative-index",
|
|
"unicorn/prefer-node-protocol",
|
|
"unicorn/prefer-number-properties",
|
|
"unicorn/prefer-optional-catch-binding",
|
|
"unicorn/prefer-prototype-methods",
|
|
"unicorn/prefer-regexp-test",
|
|
"unicorn/prefer-set-has",
|
|
"unicorn/prefer-structured-clone",
|
|
"unicorn/prefer-string-slice",
|
|
"unicorn/require-array-join-separator",
|
|
"unicorn/require-number-to-fixed-digits-argument",
|
|
"unicorn/throw-new-error",
|
|
"vitest/no-import-node-test",
|
|
"vitest/consistent-vitest-vi",
|
|
"vitest/prefer-called-once",
|
|
"vitest/prefer-called-times",
|
|
"vitest/prefer-expect-type-of",
|
|
];
|
|
|
|
function readJson(path: string): unknown {
|
|
return JSON5.parse(fs.readFileSync(path, "utf8"));
|
|
}
|
|
|
|
describe("oxlint config", () => {
|
|
it("includes bundled extensions in type-aware lint coverage", () => {
|
|
const tsconfig = readJson("config/tsconfig/oxlint.json") as OxlintTsconfig;
|
|
|
|
expect(tsconfig.include).toContain("../../extensions/**/*");
|
|
expect(tsconfig.exclude ?? []).not.toContain("../../extensions");
|
|
});
|
|
|
|
it("includes scripts in root type-aware lint coverage", () => {
|
|
const tsconfig = readJson("config/tsconfig/oxlint.json") as OxlintTsconfig;
|
|
|
|
expect(tsconfig.include).toContain("../../scripts/**/*");
|
|
});
|
|
|
|
it("has a discoverable scripts tsconfig for type-aware linting", () => {
|
|
const tsconfig = readJson("scripts/tsconfig.json") as OxlintTsconfig;
|
|
|
|
expect(tsconfig.include).toContain("**/*.ts");
|
|
expect(tsconfig.exclude ?? []).not.toContain("**/*.ts");
|
|
});
|
|
|
|
it("has a discoverable test tsconfig for type-aware linting", () => {
|
|
const tsconfig = readJson("test/tsconfig.json") as OxlintTsconfig;
|
|
|
|
expect(tsconfig.include).toContain("**/*.ts");
|
|
expect(tsconfig.exclude ?? []).not.toContain("**/*.ts");
|
|
});
|
|
|
|
it("does not ignore the bundled extensions tree", () => {
|
|
const config = readJson(".oxlintrc.json") as OxlintConfig;
|
|
|
|
expect(config.ignorePatterns ?? []).not.toContain("extensions/");
|
|
});
|
|
|
|
it("keeps generated and vendored extension outputs ignored", () => {
|
|
const config = readJson(".oxlintrc.json") as OxlintConfig;
|
|
const ignorePatterns = config.ignorePatterns ?? [];
|
|
|
|
expect(ignorePatterns).toEqual([
|
|
"dist/",
|
|
"dist-runtime/",
|
|
"docs/_layouts/",
|
|
"**/a2ui.bundle.js",
|
|
"extensions/diffs/assets/viewer-runtime.js",
|
|
"extensions/diffs-language-pack/assets/viewer-runtime.js",
|
|
"node_modules/",
|
|
"patches/",
|
|
"pnpm-lock.yaml",
|
|
"skills/**",
|
|
"src/auto-reply/reply/export-html/template.js",
|
|
"vendor/",
|
|
"**/.cache/**",
|
|
"**/.openclaw-runtime-deps-copy-*/**",
|
|
"**/build/**",
|
|
"**/coverage/**",
|
|
"**/dist/**",
|
|
"**/dist-runtime/**",
|
|
"**/node_modules/**",
|
|
]);
|
|
});
|
|
|
|
it("keeps lint overrides limited to the indexed-access and test-file policies", () => {
|
|
const config = readJson(".oxlintrc.json") as OxlintConfig;
|
|
|
|
expect(config.overrides).toEqual([
|
|
{
|
|
files: [
|
|
"packages/markdown-core/**/*.ts",
|
|
"packages/net-policy/**/*.ts",
|
|
"packages/media-understanding-common/**/*.ts",
|
|
"packages/terminal-core/**/*.ts",
|
|
"packages/normalization-core/**/*.ts",
|
|
"packages/model-catalog-core/**/*.ts",
|
|
"packages/web-content-core/**/*.ts",
|
|
"packages/agent-core/**/*.ts",
|
|
"packages/acp-core/**/*.ts",
|
|
"packages/ai/**/*.ts",
|
|
"packages/gateway-client/**/*.ts",
|
|
"packages/gateway-protocol/**/*.ts",
|
|
"packages/llm-core/**/*.ts",
|
|
"packages/media-core/**/*.ts",
|
|
"packages/media-generation-core/**/*.ts",
|
|
"packages/plugin-package-contract/**/*.ts",
|
|
"packages/sdk/**/*.ts",
|
|
],
|
|
rules: {
|
|
"typescript/no-non-null-assertion": "error",
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
"**/*.test.ts",
|
|
"**/*.test.tsx",
|
|
"**/*.e2e.test.ts",
|
|
"**/*.live.test.ts",
|
|
"**/*test-harness.ts",
|
|
"**/*test-helpers.ts",
|
|
"**/*test-support.ts",
|
|
],
|
|
rules: {
|
|
"typescript/no-explicit-any": "off",
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("enables strict empty object type lint with named single-extends interfaces allowed", () => {
|
|
const config = readJson(".oxlintrc.json") as OxlintConfig;
|
|
|
|
expect(config.rules?.["typescript/no-empty-object-type"]).toEqual([
|
|
"error",
|
|
{ allowInterfaces: "with-single-extends" },
|
|
]);
|
|
});
|
|
|
|
it("enables exhaustive switch linting", () => {
|
|
const config = readJson(".oxlintrc.json") as OxlintConfig;
|
|
|
|
expect(config.rules?.["typescript/switch-exhaustiveness-check"]).toEqual([
|
|
"error",
|
|
{ considerDefaultExhaustiveForUnions: true },
|
|
]);
|
|
});
|
|
|
|
it("enables clean zero-baseline lint rules", () => {
|
|
const config = readJson(".oxlintrc.json") as OxlintConfig;
|
|
|
|
for (const rule of ZERO_BASELINE_RULES) {
|
|
expect(config.rules?.[rule]).toBe("error");
|
|
}
|
|
});
|
|
});
|