fix(studio): resolve exact component filenames for Linux previews
Quality / quality (ubuntu-latest) (push) Failing after 10m23s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
Clintchiz committed 2026-10-19 14:06:20 +05:30
1 parent f1cf0e9f2b
commit 59f1d9d28a
5 files changed
+61 -8

No files matched your search

+1 -1
View File
@@ -391,7 +391,7 @@
}, },
"packages/design-studio": { "packages/design-studio": {
"name": "@wrnexus/design-studio", "name": "@wrnexus/design-studio",
"version": "0.1.0", "version": "0.1.1",
"dependencies": { "dependencies": {
"@wrnexus/ai": "workspace:*", "@wrnexus/ai": "workspace:*",
"@wrnexus/compiler": "workspace:*", "@wrnexus/compiler": "workspace:*",
@@ -0,0 +1,16 @@
# Design Studio 0.1.1 — Linux preview lookup
Composed and gallery previews previously inferred a component's filename from
its public name (Input -> Input.wrn). Some shipped components use lowercase
filenames (input.wrn), so the lookup happened to work on Windows and failed on
Linux with ENOENT. Previews now use uiComponentPath from the shipped UI registry,
which resolves declared names to their exact source files, including ComboBox.
A subprocess regression test enforces exact filesystem casing even on Windows
and renders the composed preview and all seven gallery pages (104 components).
All 42 Studio tests and the workspace TypeScript check pass. Existing component
APIs, design schemas, browser drafts and generation contracts are unchanged.
Private package publication and the standalone component site's dependency pin
are updated as part of this fix. The VM must install the updated lockfile,
rebuild and restart before the live preview error can disappear.
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/design-studio", "name": "@wrnexus/design-studio",
"version": "0.1.0", "version": "0.1.1",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
+3 -6
View File
@@ -1,14 +1,14 @@
import { validateAgentPage } from "./agent-pages.ts"; import { validateAgentPage } from "./agent-pages.ts";
import { readFileSync, mkdirSync, writeFileSync } from "node:fs"; import { readFileSync, mkdirSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join, resolve } from "node:path"; import { join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url"; import { fileURLToPath, pathToFileURL } from "node:url";
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import { compileWrnFile } from "@wrnexus/compiler"; import { compileWrnFile } from "@wrnexus/compiler";
import { runWithRequestContext } from "@wrnexus/core"; import { runWithRequestContext } from "@wrnexus/core";
import { REACTIVE_RUNTIME } from "@wrnexus/csr"; import { REACTIVE_RUNTIME } from "@wrnexus/csr";
import { renderDocument } from "@wrnexus/ssr"; import { renderDocument } from "@wrnexus/ssr";
import { uiCss, uiComponentsDir } from "@wrnexus/ui/registry"; import { uiCss, uiComponentPath } from "@wrnexus/ui/registry";
import { resolveThemeConfig, renderThemeCss } from "@wrnexus/styles/theme"; import { resolveThemeConfig, renderThemeCss } from "@wrnexus/styles/theme";
import { import {
COMPONENT_CONTRACTS, COMPONENT_CONTRACTS,
@@ -25,10 +25,7 @@ const escape = (text: string) =>
async function component(name: string, props: Record<string, unknown> = {}) { async function component(name: string, props: Record<string, unknown> = {}) {
if (!Object.hasOwn(COMPONENT_CONTRACTS, name)) throw Error("Unknown component."); if (!Object.hasOwn(COMPONENT_CONTRACTS, name)) throw Error("Unknown component.");
if (!modules.has(name)) { if (!modules.has(name)) {
const sourcePath = resolve( const sourcePath = uiComponentPath(name);
uiComponentsDir(),
name === "ComboBox" ? "Combobox.wrn" : `${name}.wrn`,
);
const source = readFileSync(sourcePath, "utf8"); const source = readFileSync(sourcePath, "utf8");
const directory = join(tmpdir(), "wrnexus-design-studio"); const directory = join(tmpdir(), "wrnexus-design-studio");
mkdirSync(directory, { recursive: true }); mkdirSync(directory, { recursive: true });
@@ -0,0 +1,40 @@
import { expect, test } from "bun:test";
import { pathToFileURL } from "node:url";
import { resolve } from "node:path";
test("composed and catalog previews use exact shipped filename casing on every platform", () => {
const preview = pathToFileURL(resolve(import.meta.dir, "../src/preview.ts")).href;
const probe = `
import { mock } from "bun:test";
import { readFileSync, readdirSync } from "node:fs";
import { dirname, basename } from "node:path";
import { uiComponentsDir } from "@wrnexus/ui/registry";
import { CORE_DESIGN_SYSTEMS } from "@wrnexus/design-system";
const directory = uiComponentsDir();
const names = new Set(readdirSync(directory));
const originalRead = readFileSync;
let checked = 0;
mock.module("node:fs", () => ({ readFileSync(path, ...options) {
if (typeof path === "string" && dirname(path) === directory && path.endsWith(".wrn")) {
if (!names.has(basename(path))) throw Error("Case-sensitive component lookup failed: " + basename(path));
checked++;
}
return originalRead(path, ...options);
}}));
const { previewDocument } = await import(${JSON.stringify(preview)});
await previewDocument(CORE_DESIGN_SYSTEMS.square);
for (let page = 0; page < 7; page++) await previewDocument(CORE_DESIGN_SYSTEMS.soft, { content: "catalog", page });
if (checked < 104) throw Error("Incomplete component coverage: " + checked);
console.log("Exact filename casing verified for all 104 preview components.");
`;
const result = Bun.spawnSync([process.execPath, "--eval", probe], {
cwd: resolve(import.meta.dir, "../../.."),
stdout: "pipe",
stderr: "pipe",
});
expect(new TextDecoder().decode(result.stderr)).not.toContain(
"Case-sensitive component lookup failed",
);
expect(result.exitCode).toBe(0);
expect(new TextDecoder().decode(result.stdout)).toContain("all 104 preview components");
});