chore(tooling): enforce no-floating-promises and drop DOM globals from Node-side typechecking (#104422)

* chore(lint): enforce no-floating-promises repo-wide

* feat(tooling): split DOM globals out of the Node-side typecheck program

tsconfig.core.json now compiles src/ + packages/ with lib ES2023; new
tsconfig.ui.json owns ui/ with DOM. Web-global names used by Node code
alias to canonical undici/stream-web types; highlight.js loads through a
validated narrow API because its d.ts force-includes lib.dom. Boundary,
sparse-guard, changed-lane routing, and profile lanes cover the new ui
graph.

* chore(deps): declare undici-types and teach knip about the highlight.js runtime require

* ci: retrigger dropped pull_request synchronize event

* refactor(types): derive web-global aliases from @types/node fetch globals, drop undici-types dep

* ci: retrigger after dependency guard cleared

* fix(scripts): add ui lane to changed-lanes declaration union
This commit is contained in:
Peter Steinberger
2026-07-11 07:28:20 -07:00
committed by GitHub
parent 13817a6108
commit ae12413ee2
20 changed files with 182 additions and 25 deletions
+1 -1
View File
@@ -56,7 +56,7 @@
- **Package manager**: pnpm (`pnpm install`) - **Package manager**: pnpm (`pnpm install`)
- **Dev**: `pnpm openclaw ...` or `pnpm dev` - **Dev**: `pnpm openclaw ...` or `pnpm dev`
- **Type-check**: `pnpm tsgo` (core production), `pnpm tsgo:prod` (core + extension production), `pnpm check:test-types` (tests) - **Type-check**: `pnpm tsgo` (core production), `pnpm tsgo:prod` (core + UI + extension production), `pnpm check:test-types` (tests)
- **Lint/format**: `pnpm check` - **Lint/format**: `pnpm check`
- **Tests**: `pnpm test` - **Tests**: `pnpm test`
- **Build**: `pnpm build` - **Build**: `pnpm build`
+2 -2
View File
@@ -78,6 +78,7 @@
"typescript/no-empty-object-type": ["error", { "allowInterfaces": "with-single-extends" }], "typescript/no-empty-object-type": ["error", { "allowInterfaces": "with-single-extends" }],
"typescript/no-explicit-any": "error", "typescript/no-explicit-any": "error",
"typescript/no-extraneous-class": "error", "typescript/no-extraneous-class": "error",
"typescript/no-floating-promises": "error",
"typescript/no-import-type-side-effects": "error", "typescript/no-import-type-side-effects": "error",
"typescript/no-meaningless-void-operator": "error", "typescript/no-meaningless-void-operator": "error",
"typescript/no-misused-promises": "error", "typescript/no-misused-promises": "error",
@@ -209,15 +210,14 @@
"dist/", "dist/",
"dist-runtime/", "dist-runtime/",
"docs/_layouts/", "docs/_layouts/",
"**/a2ui.bundle.js",
"extensions/diffs/assets/viewer-runtime.js", "extensions/diffs/assets/viewer-runtime.js",
"extensions/diffs-language-pack/assets/viewer-runtime.js", "extensions/diffs-language-pack/assets/viewer-runtime.js",
"extensions/canvas/src/host/a2ui/a2ui.bundle.js",
"node_modules/", "node_modules/",
"patches/", "patches/",
"pnpm-lock.yaml", "pnpm-lock.yaml",
"skills/**", "skills/**",
"src/auto-reply/reply/export-html/template.js", "src/auto-reply/reply/export-html/template.js",
"src/canvas-host/a2ui/a2ui.bundle.js",
"vendor/", "vendor/",
"**/.cache/**", "**/.cache/**",
"**/.openclaw-runtime-deps-copy-*/**", "**/.openclaw-runtime-deps-copy-*/**",
+3
View File
@@ -145,6 +145,9 @@ const config = {
"@mistralai/mistralai", "@mistralai/mistralai",
"cross-spawn", "cross-spawn",
"file-type", "file-type",
// Loaded via createRequire in src/agents/utils/syntax-highlight.ts because its
// d.ts force-includes lib.dom; knip cannot see the dynamic require.
"highlight.js",
"playwright-core", "playwright-core",
"partial-json", "partial-json",
"sqlite-vec", "sqlite-vec",
+2 -1
View File
@@ -1960,7 +1960,7 @@
"tsgo:extensions": "node scripts/run-tsgo.mjs -p tsconfig.extensions.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions.tsbuildinfo", "tsgo:extensions": "node scripts/run-tsgo.mjs -p tsconfig.extensions.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions.tsbuildinfo",
"tsgo:extensions:all": "node scripts/run-tsgo.mjs -b tsconfig.extensions.projects.json", "tsgo:extensions:all": "node scripts/run-tsgo.mjs -b tsconfig.extensions.projects.json",
"tsgo:extensions:test": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo", "tsgo:extensions:test": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo",
"tsgo:prod": "pnpm tsgo:core && pnpm tsgo:extensions", "tsgo:prod": "pnpm tsgo:core && pnpm tsgo:ui && pnpm tsgo:extensions",
"tsgo:profile": "node scripts/profile-tsgo.mjs", "tsgo:profile": "node scripts/profile-tsgo.mjs",
"tsgo:scripts": "node scripts/run-tsgo.mjs -p tsconfig.scripts.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/scripts.tsbuildinfo", "tsgo:scripts": "node scripts/run-tsgo.mjs -p tsconfig.scripts.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/scripts.tsbuildinfo",
"tsgo:test": "pnpm tsgo:core:test && pnpm tsgo:extensions:test && pnpm tsgo:test:root", "tsgo:test": "pnpm tsgo:core:test && pnpm tsgo:extensions:test && pnpm tsgo:test:root",
@@ -1969,6 +1969,7 @@
"tsgo:test:root": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.root.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-root.tsbuildinfo", "tsgo:test:root": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.root.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-root.tsbuildinfo",
"tsgo:test:src": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.src.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-src.tsbuildinfo", "tsgo:test:src": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.src.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-src.tsbuildinfo",
"tsgo:test:ui": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.ui.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-ui.tsbuildinfo", "tsgo:test:ui": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.ui.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-ui.tsbuildinfo",
"tsgo:ui": "node scripts/run-tsgo.mjs -p tsconfig.ui.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/ui.tsbuildinfo",
"tui": "node scripts/run-node.mjs tui", "tui": "node scripts/run-node.mjs tui",
"tui:dev": "node scripts/run-with-env.mjs OPENCLAW_PROFILE=dev -- node scripts/run-node.mjs --dev tui", "tui:dev": "node scripts/run-with-env.mjs OPENCLAW_PROFILE=dev -- node scripts/run-node.mjs --dev tui",
"tui:pty:test:watch": "node --import tsx scripts/dev/tui-pty-test-watch.ts", "tui:pty:test:watch": "node --import tsx scripts/dev/tui-pty-test-watch.ts",
+1
View File
@@ -1,6 +1,7 @@
export type ChangedLane = export type ChangedLane =
| "core" | "core"
| "coreTests" | "coreTests"
| "ui"
| "extensions" | "extensions"
| "extensionTests" | "extensionTests"
| "scripts" | "scripts"
+16 -2
View File
@@ -12,7 +12,8 @@ const RAW_SYNC_CHANGED_LANES_ENV = "OPENCLAW_CHANGED_LANES_RAW_SYNC";
const DOCS_PATH_RE = /^(?:docs\/|README\.md$|AGENTS\.md$|.*\.mdx?$)/u; const DOCS_PATH_RE = /^(?:docs\/|README\.md$|AGENTS\.md$|.*\.mdx?$)/u;
const APP_PATH_RE = /^(?:apps\/|Swabble\/|appcast\.xml$)/u; const APP_PATH_RE = /^(?:apps\/|Swabble\/|appcast\.xml$)/u;
const EXTENSION_PATH_RE = /^extensions\/[^/]+(?:\/|$)/u; const EXTENSION_PATH_RE = /^extensions\/[^/]+(?:\/|$)/u;
const CORE_PATH_RE = /^(?:src\/|ui\/|packages\/)/u; const CORE_PATH_RE = /^(?:src\/|packages\/)/u;
const UI_PATH_RE = /^(?:ui\/|tsconfig\.ui\.json$)/u;
const SCRIPTS_TYPECHECK_PATH_RE = const SCRIPTS_TYPECHECK_PATH_RE =
/^(?:scripts\/.*\.(?:[cm]?ts|[cm]?tsx)|tsconfig\.scripts\.json)$/u; /^(?:scripts\/.*\.(?:[cm]?ts|[cm]?tsx)|tsconfig\.scripts\.json)$/u;
const TEST_ROOT_TYPECHECK_PATH_RE = const TEST_ROOT_TYPECHECK_PATH_RE =
@@ -58,7 +59,7 @@ export const RELEASE_METADATA_PATHS = new Set([
"package.json", "package.json",
]); ]);
/** @typedef {"core" | "coreTests" | "extensions" | "extensionTests" | "scripts" | "testRoot" | "apps" | "docs" | "tooling" | "liveDockerTooling" | "releaseMetadata" | "all"} ChangedLane */ /** @typedef {"core" | "coreTests" | "ui" | "extensions" | "extensionTests" | "scripts" | "testRoot" | "apps" | "docs" | "tooling" | "liveDockerTooling" | "releaseMetadata" | "all"} ChangedLane */
/** /**
* @typedef {{ * @typedef {{
@@ -87,6 +88,7 @@ export function createEmptyChangedLanes() {
return { return {
core: false, core: false,
coreTests: false, coreTests: false,
ui: false,
extensions: false, extensions: false,
extensionTests: false, extensionTests: false,
scripts: false, scripts: false,
@@ -218,6 +220,18 @@ export function detectChangedLanes(changedPaths, options = {}) {
continue; continue;
} }
if (UI_PATH_RE.test(changedPath)) {
if (isChangedLaneTestPath(changedPath)) {
lanes.coreTests = true;
reasons.push(`${changedPath}: UI test`);
} else {
lanes.ui = true;
lanes.coreTests = true;
reasons.push(`${changedPath}: UI production`);
}
continue;
}
if (APP_PATH_RE.test(changedPath)) { if (APP_PATH_RE.test(changedPath)) {
lanes.apps = true; lanes.apps = true;
reasons.push(`${changedPath}: app surface`); reasons.push(`${changedPath}: app surface`);
+4 -1
View File
@@ -385,6 +385,9 @@ export function createChangedCheckPlan(result, options = {}) {
if (lanes.coreTests) { if (lanes.coreTests) {
addTypecheck("typecheck core tests", ["tsgo:core:test"]); addTypecheck("typecheck core tests", ["tsgo:core:test"]);
} }
if (lanes.ui) {
addTypecheck("typecheck UI", ["tsgo:ui"]);
}
if (lanes.extensions) { if (lanes.extensions) {
addTypecheck("typecheck extensions", ["tsgo:extensions"]); addTypecheck("typecheck extensions", ["tsgo:extensions"]);
} }
@@ -398,7 +401,7 @@ export function createChangedCheckPlan(result, options = {}) {
addTypecheck("typecheck test root", ["tsgo:test:root"]); addTypecheck("typecheck test root", ["tsgo:test:root"]);
} }
if (lanes.core || lanes.coreTests) { if (lanes.core || lanes.coreTests || lanes.ui) {
const coreLintCommand = createTargetedCoreLintCommand(result.paths, baseEnv); const coreLintCommand = createTargetedCoreLintCommand(result.paths, baseEnv);
if (coreLintCommand) { if (coreLintCommand) {
addCommand( addCommand(
+1
View File
@@ -10,6 +10,7 @@ const tsgoPath = path.join(repoRoot, "node_modules", ".bin", "tsgo");
const coreGraphs = [ const coreGraphs = [
{ name: "core", config: "tsconfig.core.json" }, { name: "core", config: "tsconfig.core.json" },
{ name: "ui", config: "tsconfig.ui.json" },
{ name: "core-test", config: "test/tsconfig/tsconfig.core.test.json" }, { name: "core-test", config: "test/tsconfig/tsconfig.core.test.json" },
{ name: "core-test-agents", config: "test/tsconfig/tsconfig.core.test.agents.json" }, { name: "core-test-agents", config: "test/tsconfig/tsconfig.core.test.agents.json" },
{ name: "core-test-non-agents", config: "test/tsconfig/tsconfig.core.test.non-agents.json" }, { name: "core-test-non-agents", config: "test/tsconfig/tsconfig.core.test.non-agents.json" },
+25 -8
View File
@@ -12,14 +12,13 @@ const CORE_TEST_CONFIGS = new Set([
]); ]);
const CORE_PROD_CONFIGS = new Set(["tsconfig.core.json"]); const CORE_PROD_CONFIGS = new Set(["tsconfig.core.json"]);
const UI_PROD_CONFIGS = new Set(["tsconfig.ui.json"]);
const TSGO_SPARSE_SKIP_ENV_KEY = "OPENCLAW_TSGO_SPARSE_SKIP"; const TSGO_SPARSE_SKIP_ENV_KEY = "OPENCLAW_TSGO_SPARSE_SKIP";
const CORE_SPARSE_ROOTS = ["packages", "ui/config", "ui/src"]; const CORE_PROD_SPARSE_ROOTS = ["packages"];
const UI_PROD_SPARSE_ROOTS = ["packages", "src", "ui/config", "ui/src"];
const CORE_TEST_SPARSE_ROOTS = ["packages", "ui/config", "ui/src"];
const CORE_PROD_REQUIRED_PATHS = [ const CORE_PROD_REQUIRED_PATHS = [
{
path: "apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json",
whenPresent: "ui/src/ui/tool-display.ts",
},
{ {
path: "scripts/lib/bundled-runtime-sidecar-paths.json", path: "scripts/lib/bundled-runtime-sidecar-paths.json",
whenPresent: "src/plugins/runtime-sidecar-paths.ts", whenPresent: "src/plugins/runtime-sidecar-paths.ts",
@@ -42,6 +41,13 @@ const CORE_PROD_REQUIRED_PATHS = [
}, },
]; ];
const UI_PROD_REQUIRED_PATHS = [
{
path: "apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json",
whenPresent: "ui/src/lib/chat/tool-display.ts",
},
];
const CORE_TEST_REQUIRED_PATHS = [ const CORE_TEST_REQUIRED_PATHS = [
"packages/plugin-package-contract/src/index.ts", "packages/plugin-package-contract/src/index.ts",
"ui/config/control-ui-chunking.ts", "ui/config/control-ui-chunking.ts",
@@ -85,7 +91,9 @@ export function getSparseTsgoGuardError(
const projectName = projectPath ? path.basename(projectPath) : null; const projectName = projectPath ? path.basename(projectPath) : null;
if ( if (
!projectName || !projectName ||
(!CORE_PROD_CONFIGS.has(projectName) && !CORE_TEST_CONFIGS.has(projectName)) || (!CORE_PROD_CONFIGS.has(projectName) &&
!UI_PROD_CONFIGS.has(projectName) &&
!CORE_TEST_CONFIGS.has(projectName)) ||
isMetadataOnlyCommand(args) isMetadataOnlyCommand(args)
) { ) {
return null; return null;
@@ -118,8 +126,14 @@ export function getSparseTsgoGuardError(
} }
function getRequiredSparseRootsForProject(projectName) { function getRequiredSparseRootsForProject(projectName) {
if (CORE_PROD_CONFIGS.has(projectName) || CORE_TEST_CONFIGS.has(projectName)) { if (CORE_PROD_CONFIGS.has(projectName)) {
return CORE_SPARSE_ROOTS; return CORE_PROD_SPARSE_ROOTS;
}
if (UI_PROD_CONFIGS.has(projectName)) {
return UI_PROD_SPARSE_ROOTS;
}
if (CORE_TEST_CONFIGS.has(projectName)) {
return CORE_TEST_SPARSE_ROOTS;
} }
return []; return [];
} }
@@ -129,6 +143,9 @@ function getRequiredPathsForProject(projectName, cwd, fileExists) {
if (CORE_PROD_CONFIGS.has(projectName)) { if (CORE_PROD_CONFIGS.has(projectName)) {
requiredPaths.push(...conditionalRequiredPaths(CORE_PROD_REQUIRED_PATHS, cwd, fileExists)); requiredPaths.push(...conditionalRequiredPaths(CORE_PROD_REQUIRED_PATHS, cwd, fileExists));
} }
if (UI_PROD_CONFIGS.has(projectName)) {
requiredPaths.push(...conditionalRequiredPaths(UI_PROD_REQUIRED_PATHS, cwd, fileExists));
}
if (CORE_TEST_CONFIGS.has(projectName)) { if (CORE_TEST_CONFIGS.has(projectName)) {
requiredPaths.push(...CORE_TEST_REQUIRED_PATHS); requiredPaths.push(...CORE_TEST_REQUIRED_PATHS);
} }
+4
View File
@@ -21,6 +21,10 @@ const GRAPH_DEFINITIONS = {
config: "tsconfig.core.json", config: "tsconfig.core.json",
description: "core production graph", description: "core production graph",
}, },
ui: {
config: "tsconfig.ui.json",
description: "UI production graph",
},
"core-test": { "core-test": {
config: "test/tsconfig/tsconfig.core.test.json", config: "test/tsconfig/tsconfig.core.test.json",
description: "core colocated test graph", description: "core colocated test graph",
+6 -1
View File
@@ -807,7 +807,12 @@ export class CodeModeHeadlessTimeoutError extends Error {
} }
} }
function createHeadlessAbortScope(signal: AbortSignal | undefined, wallClockMs: number) { // Explicit return type: declaration emit cannot name the inferred AbortSignal
// in the DOM-free core lane (@types/node keeps it in a non-exported module).
function createHeadlessAbortScope(
signal: AbortSignal | undefined,
wallClockMs: number,
): { signal: AbortSignal; cleanup: () => void } {
const controller = new AbortController(); const controller = new AbortController();
const onAbort = () => controller.abort(signal?.reason); const onAbort = () => controller.abort(signal?.reason);
signal?.addEventListener("abort", onAbort, { once: true }); signal?.addEventListener("abort", onAbort, { once: true });
+32 -1
View File
@@ -4,9 +4,40 @@
* Highlight.js emits HTML spans; this module walks that small HTML subset and * Highlight.js emits HTML spans; this module walks that small HTML subset and
* maps active scopes to caller-provided text formatters. * maps active scopes to caller-provided text formatters.
*/ */
import hljs from "highlight.js"; import { createRequire } from "node:module";
import { decodeHtmlEntityAt } from "./html.js"; import { decodeHtmlEntityAt } from "./html.js";
type HighlightJs = {
getLanguage(name: string): unknown;
highlight(
code: string,
options: { language: string; ignoreIllegals?: boolean },
): { value: string };
highlightAuto(code: string, languageSubset?: string[]): { value: string };
};
function isHighlightJs(value: unknown): value is HighlightJs {
return (
typeof value === "object" &&
value !== null &&
"getLanguage" in value &&
typeof value.getLanguage === "function" &&
"highlight" in value &&
typeof value.highlight === "function" &&
"highlightAuto" in value &&
typeof value.highlightAuto === "function"
);
}
// highlight.js ships `/// <reference lib="dom" />` in its d.ts, which would
// silently re-inject DOM globals into the DOM-free core program. Load it
// untyped and validate the narrow API we use instead of importing its types.
const highlightJsModule: unknown = createRequire(import.meta.url)("highlight.js");
if (!isHighlightJs(highlightJsModule)) {
throw new TypeError("highlight.js did not expose the expected Node API");
}
const hljs = highlightJsModule;
/** Formatter applied to highlighted text segments. */ /** Formatter applied to highlighted text segments. */
export type HighlightFormatter = (text: string) => string; export type HighlightFormatter = (text: string) => string;
/** Mapping from highlight.js scope names to text formatters. */ /** Mapping from highlight.js scope names to text formatters. */
+3 -1
View File
@@ -9,7 +9,9 @@ const silentConfigIoLogger = {
/** /**
* Creates config I/O for secrets commands with config-loader logging suppressed. * Creates config I/O for secrets commands with config-loader logging suppressed.
*/ */
export function createSecretsConfigIO(params: { env: NodeJS.ProcessEnv }) { export function createSecretsConfigIO(params: {
env: NodeJS.ProcessEnv;
}): ReturnType<typeof createConfigIO> {
// Secrets command output is owned by the CLI command so --json stays machine-parseable. // Secrets command output is owned by the CLI command so --json stays machine-parseable.
return createConfigIO({ return createConfigIO({
env: params.env, env: params.env,
+20
View File
@@ -0,0 +1,20 @@
// The core lane compiles with lib ES2023 (no DOM), so these web-global names
// no longer resolve. Derive them from the fetch globals @types/node already
// declares (backed by undici) instead of depending on undici-types directly;
// indexed-access derivation tracks @types/node across upgrades.
type BodyInit = Exclude<RequestInit["body"], undefined>;
type FormDataEntryValue = string | import("node:buffer").File;
type HeadersInit = Exclude<RequestInit["headers"], undefined>;
type ReadableStreamReadResult<T> =
| import("node:stream/web").ReadableStreamReadDoneResult<T>
| import("node:stream/web").ReadableStreamReadValueResult<T>;
type RequestCredentials = Exclude<RequestInit["credentials"], undefined>;
type RequestInfo = Parameters<typeof fetch>[0];
// Minimal surface for the one compile() caller; WebAssembly types otherwise
// live in lib.dom, which the core lane intentionally excludes.
declare namespace WebAssembly {
type Module = object;
function compile(bytes: ArrayBuffer | ArrayBufferView): Promise<Module>;
}
+26
View File
@@ -598,6 +598,31 @@ describe("scripts/changed-lanes", () => {
}); });
}); });
it("routes UI production changes to UI prod and core test lanes", () => {
const result = detectChangedLanes(["ui/src/app.ts"]);
const plan = createChangedCheckPlan(result, { env: { PATH: "/usr/bin" } });
expectLanes(result.lanes, {
coreTests: true,
ui: true,
});
expect(plan.commands.map((command) => command.args[0])).toContain("tsgo:ui");
expect(plan.commands.map((command) => command.args[0])).toContain("tsgo:core:test");
expect(plan.commands.map((command) => command.args[0])).not.toContain("tsgo:core");
});
it("routes the UI production config to UI prod and core test lanes", () => {
const result = detectChangedLanes(["tsconfig.ui.json"]);
const plan = createChangedCheckPlan(result, { env: { PATH: "/usr/bin" } });
expectLanes(result.lanes, {
coreTests: true,
ui: true,
});
expect(plan.commands.map((command) => command.args[0])).toContain("tsgo:ui");
expect(plan.commands.map((command) => command.args[0])).toContain("tsgo:core:test");
});
it.each([ it.each([
"scripts/control-ui-i18n.ts", "scripts/control-ui-i18n.ts",
"scripts/lib/example.ts", "scripts/lib/example.ts",
@@ -1851,6 +1876,7 @@ describe("scripts/changed-lanes", () => {
expect(result.lanes).toEqual({ expect(result.lanes).toEqual({
core: false, core: false,
coreTests: false, coreTests: false,
ui: false,
extensions: false, extensions: false,
extensionTests: false, extensionTests: false,
scripts: false, scripts: false,
+1 -2
View File
@@ -140,15 +140,14 @@ describe("oxlint config", () => {
"dist/", "dist/",
"dist-runtime/", "dist-runtime/",
"docs/_layouts/", "docs/_layouts/",
"**/a2ui.bundle.js",
"extensions/diffs/assets/viewer-runtime.js", "extensions/diffs/assets/viewer-runtime.js",
"extensions/diffs-language-pack/assets/viewer-runtime.js", "extensions/diffs-language-pack/assets/viewer-runtime.js",
"extensions/canvas/src/host/a2ui/a2ui.bundle.js",
"node_modules/", "node_modules/",
"patches/", "patches/",
"pnpm-lock.yaml", "pnpm-lock.yaml",
"skills/**", "skills/**",
"src/auto-reply/reply/export-html/template.js", "src/auto-reply/reply/export-html/template.js",
"src/canvas-host/a2ui/a2ui.bundle.js",
"vendor/", "vendor/",
"**/.cache/**", "**/.cache/**",
"**/.openclaw-runtime-deps-copy-*/**", "**/.openclaw-runtime-deps-copy-*/**",
+21 -4
View File
@@ -111,24 +111,41 @@ describe("run-tsgo sparse guard", () => {
`); `);
}); });
it("returns a helpful message for sparse core worktrees missing transitive project files", () => { it("returns a helpful message for sparse UI worktrees missing transitive project files", () => {
const cwd = createTempDir("openclaw-run-tsgo-"); const cwd = createTempDir("openclaw-run-tsgo-");
const uiToolDisplay = path.join(cwd, "ui/src/ui/tool-display.ts"); const uiToolDisplay = path.join(cwd, "ui/src/lib/chat/tool-display.ts");
fs.mkdirSync(path.dirname(uiToolDisplay), { recursive: true }); fs.mkdirSync(path.dirname(uiToolDisplay), { recursive: true });
fs.writeFileSync(uiToolDisplay, "", "utf8"); fs.writeFileSync(uiToolDisplay, "", "utf8");
expect( expect(
getSparseTsgoGuardError(["-p", "tsconfig.core.json"], { getSparseTsgoGuardError(["-p", "tsconfig.ui.json"], {
cwd, cwd,
isSparseCheckoutEnabled: () => true, isSparseCheckoutEnabled: () => true,
}), }),
).toMatchInlineSnapshot(` ).toMatchInlineSnapshot(`
"tsconfig.core.json cannot be typechecked from this sparse checkout because tracked project inputs are missing or only partially included: "tsconfig.ui.json cannot be typechecked from this sparse checkout because tracked project inputs are missing or only partially included:
- apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json - apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json
Expand this worktree's sparse checkout to include those paths, or rerun in a full worktree." Expand this worktree's sparse checkout to include those paths, or rerun in a full worktree."
`); `);
}); });
it("rejects sparse UI worktrees missing the transitive src root", () => {
const cwd = createTempDir("openclaw-run-tsgo-");
expect(
getSparseTsgoGuardError(["-p", "tsconfig.ui.json"], {
cwd,
fileExists: () => true,
isSparseCheckoutEnabled: () => true,
sparseCheckoutPatterns: ["/packages/", "/ui/config/", "/ui/src/"],
}),
).toMatchInlineSnapshot(`
"tsconfig.ui.json cannot be typechecked from this sparse checkout because tracked project inputs are missing or only partially included:
- src
Expand this worktree's sparse checkout to include those paths, or rerun in a full worktree."
`);
});
it("returns a helpful message for sparse core-test worktrees missing ui and packages files", () => { it("returns a helpful message for sparse core-test worktrees missing ui and packages files", () => {
const cwd = createTempDir("openclaw-run-tsgo-"); const cwd = createTempDir("openclaw-run-tsgo-");
+3 -1
View File
@@ -1,10 +1,12 @@
{ {
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
// Node-side production code must not see browser globals; ui/ owns DOM via tsconfig.ui.json.
"lib": ["ES2023"],
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"tsBuildInfoFile": ".artifacts/tsgo-cache/core.tsbuildinfo" "tsBuildInfoFile": ".artifacts/tsgo-cache/core.tsbuildinfo"
}, },
"include": ["src/**/*", "ui/**/*", "packages/**/*"], "include": ["src/**/*", "packages/**/*"],
"exclude": ["node_modules", "dist", "**/dist/**", "**/*.test.ts", "**/*.test.tsx", "test/**"] "exclude": ["node_modules", "dist", "**/dist/**", "**/*.test.ts", "**/*.test.tsx", "test/**"]
} }
+1
View File
@@ -2,6 +2,7 @@
"files": [], "files": [],
"references": [ "references": [
{ "path": "./tsconfig.core.json" }, { "path": "./tsconfig.core.json" },
{ "path": "./tsconfig.ui.json" },
{ "path": "./test/tsconfig/tsconfig.core.test.json" } { "path": "./test/tsconfig/tsconfig.core.test.json" }
] ]
} }
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true,
"tsBuildInfoFile": ".artifacts/tsgo-cache/ui.tsbuildinfo"
},
"include": ["ui/**/*", "src/**/*.d.ts", "packages/**/*.d.ts"],
"exclude": ["node_modules", "dist", "**/dist/**", "**/*.test.ts", "**/*.test.tsx", "test/**"]
}