import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { dirname, join, resolve } from "node:path"; import console from "node:console"; import process from "node:process"; import { fileURLToPath } from "node:url"; const scriptDir = dirname(fileURLToPath(import.meta.url)); const root = resolve(process.argv[2] || process.cwd()); const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); const backupRoot = join(root, ".wrnexus", `overlay-reactivity-backup-${timestamp}`); mkdirSync(backupRoot, { recursive: true }); function backup(path) { if (!existsSync(path)) return; const relative = path.slice(root.length).replace(/^[\\/]+/, ""); const target = join(backupRoot, relative); mkdirSync(dirname(target), { recursive: true }); copyFileSync(path, target); } function writeFromPack(sourceName, targetName = sourceName) { const source = join(scriptDir, "packages", "ui", "components", sourceName); const target = join(root, "packages", "ui", "components", targetName); if (!existsSync(source)) throw new Error(`Missing packed component: ${source}`); backup(target); mkdirSync(dirname(target), { recursive: true }); copyFileSync(source, target); console.log(`Updated ${target}`); } for (const name of ["ContextMenu.wrn", "Drawer.wrn", "Dropdown.wrn", "Modal.wrn", "Popover.wrn"]) { writeFromPack(name); } const tooltipUpper = join(root, "packages", "ui", "components", "Tooltip.wrn"); const tooltipLower = join(root, "packages", "ui", "components", "tooltip.wrn"); const tooltipTargetName = existsSync(tooltipUpper) ? "Tooltip.wrn" : existsSync(tooltipLower) ? "tooltip.wrn" : "Tooltip.wrn"; writeFromPack("Tooltip.wrn", tooltipTargetName); const generatorPath = join( root, "examples", "component-showcase", "scripts", "generate-showcase.mjs", ); if (!existsSync(generatorPath)) throw new Error(`Missing showcase generator: ${generatorPath}`); backup(generatorPath); let generator = readFileSync(generatorPath, "utf8"); generator = generator.replaceAll( "@${eventName}='console.log(event.detail)'", "@${eventName}='console.log(event.type, event.detail || {})'", ); // Keep interactive overlays uncontrolled in generated previews. if (!generator.includes("overlayVisibilityProps")) { const booleanMarker = ' if (prop.type === "boolean") {'; const booleanIndex = generator.indexOf(booleanMarker); if (booleanIndex >= 0) { generator = generator.replace( booleanMarker, `${booleanMarker}\n if (["ContextMenu", "Drawer", "Dropdown", "Modal", "Popover", "Tooltip"].includes(component.name) && ["open", "visible", "defaultopen"].includes(name)) return false;`, ); } } writeFileSync(generatorPath, generator, "utf8"); const testPath = join(root, "examples", "component-showcase", "test", "showcase.test.ts"); if (existsSync(testPath)) { backup(testPath); let tests = readFileSync(testPath, "utf8"); tests = tests.replaceAll( "@${event}='console.log(event.detail)'", "@${event}='console.log(event.type, event.detail || {})'", ); writeFileSync(testPath, tests, "utf8"); } const uiTestPath = join(root, "packages", "ui", "test", "overlay-reactivity.test.ts"); backup(uiTestPath); writeFileSync( uiTestPath, `import { expect, test } from "bun:test";\nimport { readFileSync } from "node:fs";\nimport { join } from "node:path";\n\nconst root = join(import.meta.dir, "..");\nconst files = [\n "ContextMenu.wrn",\n "Drawer.wrn",\n "Dropdown.wrn",\n "Modal.wrn",\n "Popover.wrn",\n "Tooltip.wrn",\n];\n\nfor (const file of files) {\n test(\`\${file} binds overlay visibility directly to open or visible\`, () => {\n const source = readFileSync(join(root, "components", file), "utf8");\n expect(source).toContain("open || visible");\n expect(source).not.toContain("data-show='{isOpen()}'");\n expect(source).not.toContain("data-open='{isOpen()");\n });\n}\n`, "utf8", ); for (const cachePath of [ join(root, "examples", "component-showcase", ".wrnexus"), join(root, "examples", "component-showcase", "dist"), ]) { rmSync(cachePath, { recursive: true, force: true }); } console.log(`Backup created at ${backupRoot}`); console.log("Overlay component visibility bindings and showcase event logging are updated.");