@recursica/adapter-tester 5.1.0 → 5.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/adapter-tester.schema.json.d.ts +2 -2
- package/dist/cli.cjs +3 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/fileConfig.d.ts +1 -1
- package/dist/fileConfig.d.ts.map +1 -1
- package/dist/golden/resolveSourceOfTruthGolden.d.ts.map +1 -1
- package/dist/harness/mantineSourceOfTruth.d.ts +2 -2
- package/dist/harness/mantineSourceOfTruth.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{mantineSourceOfTruth-DE7XOgea.js → mantineSourceOfTruth-BDJ5mfpd.js} +7 -7
- package/dist/mantineSourceOfTruth-BDJ5mfpd.js.map +1 -0
- package/dist/{mantineSourceOfTruth-Dpe4mlmF.cjs → mantineSourceOfTruth-CdRlVrRC.cjs} +5 -5
- package/dist/mantineSourceOfTruth-CdRlVrRC.cjs.map +1 -0
- package/dist/testing/runVisualRegression.d.ts.map +1 -1
- package/dist/testing.cjs +4 -4
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +96 -83
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter-tester.schema.json +2 -2
- package/dist/mantineSourceOfTruth-DE7XOgea.js.map +0 -1
- package/dist/mantineSourceOfTruth-Dpe4mlmF.cjs.map +0 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/portDiscovery.ts","../src/devServer.ts","../src/validateFileConfig.ts","../src/fileConfig.ts","../src/cli.ts"],"sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport net from \"node:net\";\nimport type { HarnessWebServerConfig } from \"./harness/mantineSourceOfTruth.js\";\n\n/**\n * Boots a target's Storybook and discovers the real port it ends up on,\n * instead of pinning one via `-p`/`--port`. Storybook silently falls back to\n * an OS-assigned port whenever its default/configured one is taken (this is\n * what caused the flaky `webServer` timeouts noted in mui-adapter), so the\n * only reliable source of truth is the URL it prints in its own startup\n * banner. Used by both the automated/headless run (cli.ts) and Dev Mode\n * (devServer.ts) — neither pins a port anymore.\n */\n\nexport interface LaunchTarget {\n /** Human-readable name used in log lines. */\n name: string;\n command: string;\n cwd: string;\n /** Reuse an already-running instance (detected via the last-known-port\n * cache) instead of spawning a new one. Mirrors the old `reuseExistingServer`\n * behavior, which used to just probe the one fixed configured port. */\n reuseExistingServer: boolean;\n /** File the discovered port is cached in between runs, so a later\n * `reuseExistingServer` run knows where to look. One per target. */\n cacheFile: string;\n timeoutMs?: number;\n}\n\nexport interface DiscoveredServer {\n url: string;\n port: number;\n /** The process we spawned, or `null` if an already-running instance was\n * reused — callers should only kill what they started. */\n process: ChildProcess | null;\n}\n\n// Storybook prints its bound address in its startup banner wrapped in ANSI\n// color codes and box-drawing chars, e.g.:\n// │ │ - Local: http://localhost:57496/ │ │\n// Strip ANSI first, then match the URL itself.\nconst ANSI_PATTERN = /\\x1b\\[[0-9;]*m/g;\nconst LOCAL_URL_PATTERN = /Local:\\s*(https?:\\/\\/localhost:\\d+)/;\n\nfunction isPortActive(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const socket = new net.Socket();\n socket.setTimeout(200);\n socket.once(\"connect\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.once(\"timeout\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.once(\"error\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.connect(port, \"127.0.0.1\");\n });\n}\n\nfunction readCachedPort(cacheFile: string): number | undefined {\n try {\n const port = JSON.parse(readFileSync(cacheFile, \"utf8\")).port;\n return typeof port === \"number\" ? port : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction writeCachedPort(cacheFile: string, port: number): void {\n mkdirSync(dirname(cacheFile), { recursive: true });\n writeFileSync(cacheFile, JSON.stringify({ port }));\n}\n\n/** Spawns `target.command`, streaming its output to the console like before,\n * and resolves once it reports the URL it actually bound to. */\nfunction spawnAndDetect(\n target: LaunchTarget,\n timeoutMs: number,\n): Promise<DiscoveredServer> {\n return new Promise((resolve, reject) => {\n const child = spawn(target.command, {\n cwd: target.cwd,\n stdio: \"pipe\",\n shell: true,\n });\n\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n child.kill();\n reject(\n new Error(\n `Timed out after ${timeoutMs}ms waiting for ${target.name}'s Storybook to report its URL (no \"Local: http://localhost:<port>\" line seen in its output).`,\n ),\n );\n }, timeoutMs);\n\n const handleOutput = (data: Buffer, isError: boolean) => {\n const text = data.toString();\n for (const line of text.split(\"\\n\")) {\n if (line.trim()) {\n const label = isError\n ? `${target.name} SB ERROR`\n : `${target.name} SB`;\n (isError ? console.error : console.log)(`[${label}] ${line.trim()}`);\n }\n }\n if (settled) return;\n const match = text.replace(ANSI_PATTERN, \"\").match(LOCAL_URL_PATTERN);\n if (match) {\n settled = true;\n clearTimeout(timer);\n // Non-null: LOCAL_URL_PATTERN has exactly one capture group, and a\n // match only happens when it participated.\n const url = match[1]!;\n const port = Number(new URL(url).port);\n writeCachedPort(target.cacheFile, port);\n resolve({ url, port, process: child });\n }\n };\n child.stdout?.on(\"data\", (data: Buffer) => handleOutput(data, false));\n child.stderr?.on(\"data\", (data: Buffer) => handleOutput(data, true));\n child.on(\"error\", (err) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n reject(err);\n });\n child.on(\"exit\", (code) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n reject(\n new Error(\n `${target.name}'s Storybook exited (code ${code}) before reporting its URL.`,\n ),\n );\n });\n });\n}\n\n/** Adapts a resolved `HarnessWebServerConfig` into a `LaunchTarget`. */\nexport function toLaunchTarget(\n name: string,\n server: HarnessWebServerConfig,\n): LaunchTarget {\n return {\n name,\n command: server.command,\n cwd: server.cwd,\n reuseExistingServer: server.reuseExistingServer,\n cacheFile: server.cacheFile,\n timeoutMs: server.timeout,\n };\n}\n\n/** Reuses an already-running Storybook if `reuseExistingServer` is set and\n * the last-known-port cache points at something still listening; otherwise\n * spawns `target.command` fresh and detects the real port from its output. */\nexport async function launchAndDetectStorybook(\n target: LaunchTarget,\n): Promise<DiscoveredServer> {\n if (target.reuseExistingServer) {\n const cachedPort = readCachedPort(target.cacheFile);\n if (cachedPort !== undefined && (await isPortActive(cachedPort))) {\n console.log(\n `[Dev Launcher] ${target.name} is already running on port ${cachedPort} (reused).`,\n );\n return {\n url: `http://localhost:${cachedPort}`,\n port: cachedPort,\n process: null,\n };\n }\n }\n console.log(`[Dev Launcher] Launching Storybook for ${target.name}...`);\n return spawnAndDetect(target, target.timeoutMs ?? 120 * 1000);\n}\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport express from \"express\";\nimport { createProxyMiddleware } from \"http-proxy-middleware\";\nimport type { AdapterTesterConfig } from \"./config.js\";\nimport type { HarnessWebServerConfig } from \"./harness/mantineSourceOfTruth.js\";\nimport { launchAndDetectStorybook, toLaunchTarget } from \"./portDiscovery.js\";\n\n/**\n * Interactive Dev Mode: a synced, side-by-side browser view of this\n * project's own Storybook and the source of truth, with per-story note\n * taking and an AI-report export. Boots both Storybooks (reusing them if\n * already running) behind a proxy so the own/target pane — which drives\n * the sync and carries Storybook's nav sidebar — loads same-origin on the\n * left, with the source of truth following along as a bare preview on the\n * right.\n *\n * Config-driven — takes the same `{ engineConfig, webServers }` shape\n * `resolveConfig()` produces, in the same [sourceOfTruth, target] order.\n */\n\nconst distDir = dirname(fileURLToPath(import.meta.url));\n\nfunction openBrowser(url: string): void {\n const startCmd =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"start\"\n : \"xdg-open\";\n console.log(`[Dev Launcher] Auto-launching browser: ${url}`);\n spawn(startCmd, [url], { shell: process.platform === \"win32\" }).on(\n \"error\",\n (err) => {\n console.error(`[Dev Launcher] Failed to auto-launch browser:`, err);\n },\n );\n}\n\nexport interface DevServerOptions {\n /** Port the dev-mode proxy UI itself listens on. Defaults to 6010. */\n port?: number;\n}\n\nexport async function startDevServer(\n engineConfig: AdapterTesterConfig,\n webServers: HarnessWebServerConfig[],\n options: DevServerOptions = {},\n): Promise<void> {\n const [sourceOfTruth, target] = engineConfig.targets;\n const [sourceOfTruthServer, targetServer] = webServers;\n if (\n !sourceOfTruth?.sourceOfTruth ||\n !target ||\n target.sourceOfTruth ||\n !sourceOfTruthServer ||\n !targetServer\n ) {\n throw new Error(\n \"adapter-tester dev mode requires exactly two targets: [sourceOfTruth, target] — check adapter-tester.config.json.\",\n );\n }\n\n const devPort = options.port ?? 6010;\n\n // Neither Storybook is pinned to a specific port — each one silently\n // falls back to an OS-assigned port whenever its default/configured one is\n // taken, so the real port is only known once it reports it (see\n // portDiscovery.ts). Booted concurrently since neither depends on the other.\n console.log(\n `[Dev Launcher] Resolving ${sourceOfTruth.name} and ${target.name} Storybooks...`,\n );\n const [sourceOfTruthRunning, targetRunning] = await Promise.all([\n launchAndDetectStorybook(\n toLaunchTarget(sourceOfTruth.name, sourceOfTruthServer),\n ),\n launchAndDetectStorybook(toLaunchTarget(target.name, targetServer)),\n ]);\n console.log(`[Dev Launcher] Both Storybooks are active and responsive!`);\n\n const spawned = [sourceOfTruthRunning.process, targetRunning.process].filter(\n (child): child is ChildProcess => child !== null,\n );\n\n const app = express();\n const publicDir = join(distDir, \"../public\");\n const headerPath = join(distDir, \"../report-header.txt\");\n\n // Serve the Dev Mode UI at the root path ONLY if there is no query string.\n // This allows the iframe (which loads with ?path=/story/...) to pass\n // through to the proxy, avoiding an infinite loop of nested wrappers.\n app.get(\"/\", (req, res, next) => {\n if (req.query.path) {\n next();\n return;\n }\n const html = readFileSync(join(publicDir, \"index.html\"), \"utf8\").replace(\n \"<head>\",\n `<head>\\n <script>window.__ADAPTER_TESTER__ = ${JSON.stringify({\n ownName: target.name,\n sourceOfTruthName: sourceOfTruth.name,\n sourceOfTruthPort: sourceOfTruthRunning.port,\n })};</script>`,\n );\n res.send(html);\n });\n\n // Serve the AI prompt header dynamically so it can be edited externally.\n // `reportHeader` in adapter-tester.config.json overrides the file entirely.\n app.get(\"/report-header.txt\", (req, res) => {\n if (engineConfig.reportHeader !== undefined) {\n res.type(\"text/plain\").send(engineConfig.reportHeader);\n return;\n }\n try {\n res.type(\"text/plain\").send(readFileSync(headerPath, \"utf8\"));\n } catch {\n res.status(500).send(\"Error loading report-header.txt\");\n }\n });\n\n // Proxy everything else to this project's own Storybook, preserving\n // absolute paths (e.g. /@vite/client) so HMR keeps working same-origin.\n // This is the pane that drives the sync and shows Storybook's nav sidebar.\n app.use(\n \"/\",\n createProxyMiddleware({\n target: targetRunning.url,\n changeOrigin: true,\n ws: true,\n }),\n );\n\n let cleaningUp = false;\n const cleanup = () => {\n if (cleaningUp) return;\n cleaningUp = true;\n console.log(\n \"\\n[Dev Launcher] Shutting down Dev Mode server and spawned Storybooks...\",\n );\n for (const child of spawned) child.kill(\"SIGINT\");\n process.exit(0);\n };\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n app.listen(devPort, () => {\n console.log(`\n====================================================\n🚀 Adapter Dev Mode proxy running at:\n http://localhost:${devPort}\n====================================================\n`);\n openBrowser(`http://localhost:${devPort}`);\n });\n}\n","import { Ajv } from \"ajv\";\nimport * as ajvFormatsModule from \"ajv-formats\";\nimport type { FormatsPlugin } from \"ajv-formats\";\nimport schema from \"./adapter-tester.schema.json\" with { type: \"json\" };\n\n// ajv-formats ships an ESM-style `export default` on a CJS build with no\n// `\"type\"` field, which under `moduleResolution: NodeNext` TS resolves to\n// the raw module namespace instead of unwrapping the default — a known\n// ajv-formats/TS interop gap, not a version mismatch.\nconst addFormats = (ajvFormatsModule as unknown as { default: FormatsPlugin })\n .default;\n\nconst ajv = new Ajv({ allErrors: true, strict: true });\naddFormats(ajv);\nconst validate = ajv.compile(schema);\n\n/**\n * Validates a parsed `adapter-tester.config.json` against\n * `adapter-tester.schema.json`. Throws with every violation listed — callers\n * must not silently coerce or drop invalid fields.\n */\nexport function validateFileConfig(data: unknown, path: string): void {\n if (validate(data)) return;\n\n const errors = (validate.errors ?? [])\n .map((error) => {\n const extra = error.params?.additionalProperty\n ? ` '${error.params.additionalProperty}'`\n : \"\";\n return ` - ${error.instancePath || \"root\"} ${error.message}${extra}`;\n })\n .join(\"\\n\");\n throw new Error(`Invalid ${path}:\\n${errors}`);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type {\n AdapterTesterConfig,\n SourceOfTruthGoldenLocation,\n StoryOverride,\n} from \"./config.js\";\nimport type { HarnessWebServerConfig } from \"./harness/mantineSourceOfTruth.js\";\nimport { mantineSourceOfTruthWebServer } from \"./harness/mantineSourceOfTruth.js\";\nimport { validateFileConfig } from \"./validateFileConfig.js\";\n\nconst MANTINE_ADAPTER_PACKAGE_NAME = \"@recursica/mantine-adapter\";\n\nexport const CONFIG_FILE_NAME = \"adapter-tester.config.json\";\nconst DEFAULT_STORYBOOK_COMMAND = \"npm run storybook\";\nconst DEFAULT_STORYBOOK_PORT = 6006;\nconst DEFAULT_SOURCE_OF_TRUTH_PORT = 6011;\nconst DEFAULT_SOURCE_OF_TRUTH_NAME = \"Mantine\";\nconst DEFAULT_GOLDEN_THRESHOLD_PIXELS = 10;\nconst DEFAULT_SOURCE_OF_TRUTH_THRESHOLD_PIXELS = 3500;\n\ninterface StorybookTargetFileConfig {\n /** First-guess port only, not authoritative — the real port is\n * auto-detected from this Storybook's own startup output, since Storybook\n * silently falls back to an OS-assigned port whenever this one is taken.\n * Auto-detected from this project's own `scripts.storybook` (a `-p\n * <port>`/`--port <port>` flag) when omitted, falling back to 6006. */\n port?: number;\n /** Command that boots the target's Storybook. Defaults to `npm run storybook`. */\n command?: string;\n /** Directory the command runs in, relative to the config file. Defaults to \".\" */\n cwd?: string;\n}\n\ninterface MantineHarnessSourceOfTruthFileConfig {\n /** Default mode: boots a throwaway harness that installs the published\n * `@recursica/mantine-adapter` from npm — no monorepo checkout required. */\n type?: \"mantine-harness\";\n /** First-guess port only, not authoritative — see `HarnessWebServerConfig.port`. */\n port?: number;\n mantineAdapterVersion?: string;\n storybookTemplateVersion?: string;\n}\n\ninterface UrlSourceOfTruthFileConfig {\n /** Non-standard mode: points at an already-addressable Storybook — e.g. a\n * sibling workspace package's own Storybook inside this monorepo. */\n type: \"url\";\n name?: string;\n /** First-guess port only, not authoritative — see `HarnessWebServerConfig.port`.\n * Defaults to 6011. */\n port?: number;\n command?: string;\n cwd?: string;\n}\n\ntype SourceOfTruthFileConfig =\n | MantineHarnessSourceOfTruthFileConfig\n | UrlSourceOfTruthFileConfig;\n\nexport interface AdapterTesterFileConfig {\n /** Label for this project's own target. Defaults to the unscoped name in\n * this project's package.json (e.g. \"@recursica/mui-adapter\" -> \"mui-adapter\"). */\n name?: string;\n storybook?: StorybookTargetFileConfig;\n sourceOfTruth?: SourceOfTruthFileConfig;\n goldenThresholdPixels?: number;\n sourceOfTruthThresholdPixels?: number;\n stories?: Record<string, StoryOverride>;\n excludeTitlePrefixes?: string[];\n /** Overrides the AI report header text shown in Dev Mode's \"Full Report\"\n * export. Defaults to the contents of `report-header.txt`. */\n reportHeader?: string;\n /**\n * True only for the source-of-truth adapter's own config (mantine-adapter).\n * Skips `sourceOfTruth` entirely — there's nothing above it to diverge\n * from — and runs the own-drift golden check standalone, against just this\n * project's own Storybook. Defaults to false.\n */\n isSourceOfTruthAdapter?: boolean;\n}\n\nexport interface ResolvedAdapterTesterConfig {\n engineConfig: AdapterTesterConfig;\n webServers: HarnessWebServerConfig[];\n}\n\nexport interface ResolveConfigOverrides {\n /** From `--source-of-truth-version`. Overrides `sourceOfTruth.mantineAdapterVersion`. */\n mantineAdapterVersion?: string;\n}\n\nfunction readOwnPackageJson(cwd: string): any {\n const path = join(cwd, \"package.json\");\n if (!existsSync(path)) {\n throw new Error(\n `No package.json found in ${cwd} — run adapter-tester from the root of the project being tested.`,\n );\n }\n return JSON.parse(readFileSync(path, \"utf8\"));\n}\n\nfunction detectOwnPort(cwd: string): number {\n const pkg = readOwnPackageJson(cwd);\n const storybookScript = pkg.scripts?.storybook as string | undefined;\n const match = storybookScript?.match(/(?:-p|--port)[ =](\\d+)/);\n return match ? Number(match[1]) : DEFAULT_STORYBOOK_PORT;\n}\n\nfunction detectOwnName(cwd: string): string {\n const pkg = readOwnPackageJson(cwd);\n const name = pkg.name as string | undefined;\n return name ? name.split(\"/\").pop()! : \"Adapter\";\n}\n\nfunction loadFileConfig(cwd: string): AdapterTesterFileConfig {\n const path = join(cwd, CONFIG_FILE_NAME);\n if (!existsSync(path)) {\n return {};\n }\n const data = JSON.parse(readFileSync(path, \"utf8\"));\n validateFileConfig(data, path);\n return data;\n}\n\n/**\n * Loads `adapter-tester.config.json` from `cwd` (or falls back to defaults\n * when the file doesn't exist) and resolves it into the engine config\n * `resolveVisualRegressionPlan` consumes plus the Playwright `webServer` entries\n * needed to boot both sides of the comparison.\n *\n * Default mode (no `sourceOfTruth`/`storybook` set): compares this project's\n * own Storybook against a throwaway Mantine harness — no monorepo checkout\n * required. Set `sourceOfTruth.type: \"url\"` for the non-standard mode used\n * to compare sibling workspace packages inside this monorepo.\n *\n * `overrides.mantineAdapterVersion` (from `--source-of-truth-version`) pins\n * the mantine-harness install/golden-fetch version for this run, overriding\n * `sourceOfTruth.mantineAdapterVersion`. Throws if passed together with\n * `isSourceOfTruthAdapter` or `sourceOfTruth.type: \"url\"` — neither has a\n * version to pin.\n */\nexport function resolveConfig(\n cwd: string,\n overrides: ResolveConfigOverrides = {},\n): ResolvedAdapterTesterConfig {\n const file = loadFileConfig(cwd);\n const isSourceOfTruthAdapter = file.isSourceOfTruthAdapter ?? false;\n\n if (overrides.mantineAdapterVersion && isSourceOfTruthAdapter) {\n throw new Error(\n \"--source-of-truth-version has no effect on the source-of-truth adapter's own config (isSourceOfTruthAdapter: true) — there's nothing to pin a version for.\",\n );\n }\n\n const ownName = file.name ?? detectOwnName(cwd);\n const ownPort = file.storybook?.port ?? detectOwnPort(cwd);\n const ownCommand = file.storybook?.command ?? DEFAULT_STORYBOOK_COMMAND;\n const ownCwd = resolve(cwd, file.storybook?.cwd ?? \".\");\n const ownWebServer: HarnessWebServerConfig = {\n command: ownCommand,\n port: ownPort,\n cwd: ownCwd,\n reuseExistingServer: !process.env.CI,\n cacheFile: join(cwd, \".adapter-tester\", \"last-port-own.json\"),\n timeout: 120 * 1000,\n };\n\n const sharedEngineConfig = {\n goldenThresholdPixels:\n file.goldenThresholdPixels ?? DEFAULT_GOLDEN_THRESHOLD_PIXELS,\n sourceOfTruthThresholdPixels:\n file.sourceOfTruthThresholdPixels ??\n DEFAULT_SOURCE_OF_TRUTH_THRESHOLD_PIXELS,\n stories: file.stories,\n excludeTitlePrefixes: file.excludeTitlePrefixes,\n reportHeader: file.reportHeader,\n // Keyed off `ownCwd`, not `cwd` — the project actually being tested, not\n // wherever the config file happens to live. Matters whenever\n // `storybook.cwd` points at a sibling package: its goldens must resolve\n // to that package's own `test/golden/` (the same directory used when\n // that package runs adapter-tester directly), not a directory under\n // wherever this config file lives.\n goldenDir: join(ownCwd, \"test\", \"golden\"),\n isSourceOfTruthAdapter,\n // Overwritten by the CLI from --update-golden/--approve-divergence.\n goldenMode: \"check\" as const,\n // Overwritten by the CLI from --divergence-only.\n checkMode: \"own\" as const,\n };\n\n // The source-of-truth adapter's own config has nothing above it to\n // diverge from — no second target, no harness/webServer for it at all.\n if (isSourceOfTruthAdapter) {\n return {\n engineConfig: {\n ...sharedEngineConfig,\n targets: [{ name: ownName, url: `http://localhost:${ownPort}` }],\n },\n webServers: [ownWebServer],\n };\n }\n\n const sourceOfTruth = file.sourceOfTruth ?? {\n type: \"mantine-harness\" as const,\n };\n\n let sourceOfTruthName: string;\n let sourceOfTruthPort: number;\n let sourceOfTruthWebServer: HarnessWebServerConfig;\n let sourceOfTruthGolden: SourceOfTruthGoldenLocation;\n\n if (sourceOfTruth.type === \"url\") {\n if (overrides.mantineAdapterVersion) {\n throw new Error(\n '--source-of-truth-version has no effect with sourceOfTruth.type: \"url\" — that mode reads a sibling package\\'s local checkout directly, not a published version.',\n );\n }\n sourceOfTruthName = sourceOfTruth.name ?? DEFAULT_SOURCE_OF_TRUTH_NAME;\n sourceOfTruthPort = sourceOfTruth.port ?? DEFAULT_SOURCE_OF_TRUTH_PORT;\n const sourceOfTruthCwd = resolve(cwd, sourceOfTruth.cwd ?? \".\");\n sourceOfTruthWebServer = {\n command: sourceOfTruth.command ?? DEFAULT_STORYBOOK_COMMAND,\n port: sourceOfTruthPort,\n cwd: sourceOfTruthCwd,\n reuseExistingServer: !process.env.CI,\n cacheFile: join(cwd, \".adapter-tester\", \"last-port-source-of-truth.json\"),\n timeout: 120 * 1000,\n };\n // Sibling package already checked out locally — read its golden files\n // directly, including any uncommitted local changes. No install, no\n // network call.\n sourceOfTruthGolden = {\n type: \"local\",\n dir: join(sourceOfTruthCwd, \"test\", \"golden\"),\n };\n } else {\n sourceOfTruthName = DEFAULT_SOURCE_OF_TRUTH_NAME;\n sourceOfTruthPort = sourceOfTruth.port ?? DEFAULT_SOURCE_OF_TRUTH_PORT;\n const mantineAdapterVersion =\n overrides.mantineAdapterVersion ?? sourceOfTruth.mantineAdapterVersion;\n sourceOfTruthWebServer = mantineSourceOfTruthWebServer({\n dir: join(cwd, \".adapter-tester/mantine-harness\"),\n port: sourceOfTruthPort,\n mantineAdapterVersion,\n storybookTemplateVersion: sourceOfTruth.storybookTemplateVersion,\n });\n // No local checkout — resolve the installed version against the npm\n // registry and fetch that version's golden files from the public repo.\n // Never needs `sourceOfTruthWebServer` above; the golden check on its\n // own doesn't boot a second Storybook at all (see cli.ts).\n sourceOfTruthGolden = {\n type: \"npm\",\n packageName: MANTINE_ADAPTER_PACKAGE_NAME,\n versionSpec: mantineAdapterVersion ?? \"latest\",\n cacheDir: join(cwd, \".adapter-tester/mantine-golden-cache\"),\n };\n }\n\n return {\n engineConfig: {\n ...sharedEngineConfig,\n targets: [\n {\n name: sourceOfTruthName,\n url: `http://localhost:${sourceOfTruthPort}`,\n sourceOfTruth: true,\n },\n { name: ownName, url: `http://localhost:${ownPort}` },\n ],\n sourceOfTruthGolden,\n },\n // [sourceOfTruth, own] — Dev Mode needs both; the automated golden run\n // (cli.ts) only ever boots the last entry, since its divergence check\n // reads stored golden files, not a live source-of-truth page.\n webServers: [sourceOfTruthWebServer, ownWebServer],\n };\n}\n","import { spawnSync } from \"node:child_process\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport type { AdapterTesterConfig } from \"./config.js\";\nimport { startDevServer } from \"./devServer.js\";\nimport { resolveConfig } from \"./fileConfig.js\";\nimport type { HarnessWebServerConfig } from \"./harness/mantineSourceOfTruth.js\";\nimport { launchAndDetectStorybook, toLaunchTarget } from \"./portDiscovery.js\";\n\n// `dist/testing.js` is always a sibling of this file — both inside a\n// consumer's node_modules/@recursica/adapter-tester/dist and inside this\n// monorepo's own packages/adapter-tester/dist when self-hosting — so the\n// generated spec below can reach it without depending on how the package\n// itself resolves at import time.\nconst distDir = dirname(fileURLToPath(import.meta.url));\nconst testingEntry = pathToFileURL(join(distDir, \"testing.js\")).href;\n\n// `--source-of-truth-version <value>` takes a value, unlike every other own\n// flag below — pulled out of argv first so the boolean flags/passthrough\n// logic never has to know it exists.\nconst args = process.argv.slice(2);\nlet sourceOfTruthVersion: string | undefined;\nconst sourceOfTruthVersionFlagIndex = args.indexOf(\"--source-of-truth-version\");\nif (sourceOfTruthVersionFlagIndex !== -1) {\n const value = args[sourceOfTruthVersionFlagIndex + 1];\n if (value === undefined || value.startsWith(\"--\")) {\n throw new Error(\n \"--source-of-truth-version requires a value, e.g. --source-of-truth-version 0.53.0\",\n );\n }\n sourceOfTruthVersion = value;\n args.splice(sourceOfTruthVersionFlagIndex, 2);\n}\n\n// `--story <story-id>` scopes a run to exactly one story — pulled out the\n// same way, ahead of the passthrough logic, since it's consumed here (to\n// filter the generated spec) rather than forwarded to Playwright. Equivalent\n// to `--grep \"<story-id>\"`, but exact-match and easier to reach for when\n// iterating on a single component.\nlet storyId: string | undefined;\nconst storyFlagIndex = args.indexOf(\"--story\");\nif (storyFlagIndex !== -1) {\n const value = args[storyFlagIndex + 1];\n if (value === undefined || value.startsWith(\"--\")) {\n throw new Error(\n \"--story requires a story id, e.g. --story ui-kit-button--loading\",\n );\n }\n storyId = value;\n args.splice(storyFlagIndex, 2);\n}\n\nconst cwd = process.cwd();\nconst { engineConfig, webServers } = resolveConfig(cwd, {\n mantineAdapterVersion: sourceOfTruthVersion,\n});\n\nif (args.includes(\"--update-golden\")) {\n engineConfig.goldenMode = \"update-golden\";\n} else if (args.includes(\"--approve-divergence\")) {\n engineConfig.goldenMode = \"approve-divergence\";\n}\n\nif (args.includes(\"--divergence-only\")) {\n if (engineConfig.isSourceOfTruthAdapter) {\n throw new Error(\n \"--divergence-only has nothing to diverge from — isSourceOfTruthAdapter is true in this project's adapter-tester.config.json.\",\n );\n }\n engineConfig.checkMode = \"divergence\";\n} else if (engineConfig.goldenMode === \"approve-divergence\") {\n throw new Error(\n \"--approve-divergence only makes sense with --divergence-only — there's nothing to approve without the divergence check running.\",\n );\n}\n\nif (args.includes(\"--dry-run\")) {\n console.log(JSON.stringify({ engineConfig, webServers }, null, 2));\n process.exit(0);\n}\n\n// Any arg besides our own flags is passed straight through to `playwright\n// test` — e.g. `npm run adapter-tester:automated -- --grep \"Toast\"` to scope\n// a run to matching stories, instead of every invocation running the full\n// suite. Our own flags are consumed above, not forwarded — Playwright itself\n// doesn't know about them.\nconst OWN_FLAGS = new Set([\n \"--dry-run\",\n \"--serve\",\n \"--update-golden\",\n \"--approve-divergence\",\n \"--divergence-only\",\n]);\nconst passthroughArgs = args.filter((arg) => !OWN_FLAGS.has(arg));\n\n// The bundled build target doesn't support top-level await, so the async\n// work (launching Storybook and detecting its real port) lives in main().\nmain().catch((err) => {\n console.error(err);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n if (args.includes(\"--serve\")) {\n if (engineConfig.isSourceOfTruthAdapter) {\n throw new Error(\n \"Dev Mode (--serve) has nothing to sync this project's Storybook against — isSourceOfTruthAdapter is true in this project's adapter-tester.config.json.\",\n );\n }\n // Dual-Storybook interactive Dev Mode — no Playwright, no screenshots.\n // Used by the `adapter-tester` npm script.\n await startDevServer(engineConfig, webServers);\n } else {\n // The golden-image checks never need the source-of-truth adapter's own\n // Storybook running — the divergence check reads its stored golden files,\n // not a live page — so only the last (own) webServer is booted here. Dev\n // Mode above is the one thing that still needs both.\n const ownWebServer = webServers.at(-1);\n if (!ownWebServer) {\n throw new Error(\"resolveConfig() returned no webServers to boot.\");\n }\n await runAutomated(ownWebServer, engineConfig);\n }\n}\n\n/**\n * Boots this project's own Storybook (auto-detecting the port it actually\n * lands on — see portDiscovery.ts), generates a throwaway Playwright config\n * + spec under `.adapter-tester/run/`, and runs the automated pixel-diff\n * suite against it. Used by the `adapter-tester:automated` npm script.\n *\n * Playwright's own `webServer` option can't be used here — it only knows how\n * to poll a port/URL decided *before* the command it spawns runs, but\n * Storybook silently falls back to a different, OS-assigned port whenever\n * its configured one is taken. So this boots and waits for Storybook itself,\n * then points the generated config straight at whatever URL it actually\n * reports.\n */\nasync function runAutomated(\n server: HarnessWebServerConfig,\n config: AdapterTesterConfig,\n): Promise<void> {\n const runDir = join(cwd, \".adapter-tester/run\");\n mkdirSync(runDir, { recursive: true });\n\n const ownTarget = config.targets.at(-1);\n if (!ownTarget) {\n throw new Error(\"resolveConfig() returned no targets to check.\");\n }\n const { url, process: spawned } = await launchAndDetectStorybook(\n toLaunchTarget(ownTarget.name, server),\n );\n ownTarget.url = url;\n\n writeFileSync(\n join(runDir, \"playwright.config.js\"),\n `// Generated by \\`adapter-tester\\` — do not edit, regenerated on every run.\nimport { defineConfig, devices } from \"@playwright/test\";\n\nexport default defineConfig({\n testDir: ${JSON.stringify(runDir)},\n // Each golden check only reads/writes its own story's manifest.json entry,\n // under a lock, so concurrent workers never race each other (see\n // \\`updateManifestEntry\\`). Defaults to 1 worker (1 Chromium instance) to\n // avoid exhausting memory running the full suite; override with\n // \\`--workers <n>\\` (forwarded straight through to Playwright) to parallelize.\n workers: 1,\n fullyParallel: true,\n forbidOnly: !!process.env.CI,\n retries: process.env.CI ? 2 : 0,\n reporter: [[\"html\", { open: \"never\" }], [\"list\"]],\n use: { trace: \"on-first-retry\" },\n projects: [{ name: \"chromium\", use: { ...devices[\"Desktop Chrome\"] } }],\n // No webServer entry — this project's own Storybook is already running by\n // the time this config is used (see runAutomated() in cli.ts).\n});\n`,\n );\n\n writeFileSync(\n join(runDir, \"visual-regression.spec.js\"),\n `// Generated by \\`adapter-tester\\` — do not edit, regenerated on every run.\nimport { test, expect } from \"@playwright/test\";\nimport { resolveVisualRegressionPlan } from ${JSON.stringify(testingEntry)};\n\n// \\`test.describe\\`/\\`test\\` are called here, not inside resolveVisualRegressionPlan,\n// so Playwright's HTML report groups the suite under this file instead of a\n// sourcemapped path into adapter-tester's own library code.\nconst { ownTargetName, suiteLabel, stories, missingFromSourceOfTruth, checkStory } =\n await resolveVisualRegressionPlan(${JSON.stringify(config, null, 2)});\n\n// Set from \\`--story <story-id>\\` — scopes this run to exactly one story and\n// skips the (suite-wide) parity check, which has nothing to do with it.\nconst storyId = ${JSON.stringify(storyId ?? null)};\nconst scopedStories = storyId\n ? stories.filter((story) => story.id === storyId)\n : stories;\nif (storyId && scopedStories.length === 0) {\n throw new Error(\\`--story \"\\${storyId}\" matched no story in this Storybook.\\`);\n}\n\ntest.describe(\\`\\${ownTargetName} — \\${suiteLabel}\\`, () => {\n if (!storyId && missingFromSourceOfTruth.length > 0) {\n test(\"story parity with source of truth\", () => {\n expect(\n missingFromSourceOfTruth,\n \\`\\${missingFromSourceOfTruth.length} stor(y/ies) exist in the source of truth but are missing here. Add the missing story, or mark it \\\\\\`exclude: true\\\\\\` under \\\\\\`stories\\\\\\` in adapter-tester.config.json if intentional.\\`,\n ).toEqual([]);\n });\n }\n for (const story of scopedStories) {\n test(story.id, async ({ browser }, testInfo) => {\n await checkStory(story, browser, testInfo);\n });\n }\n});\n`,\n );\n\n try {\n const result = spawnSync(\n \"npx\",\n [\n \"playwright\",\n \"test\",\n \"--config\",\n join(runDir, \"playwright.config.js\"),\n ...passthroughArgs,\n ],\n { stdio: \"inherit\", cwd, shell: process.platform === \"win32\" },\n );\n process.exitCode = result.status ?? 1;\n } finally {\n // Only tear down the Storybook we spawned — an instance we reused via\n // the last-known-port cache was already running before we got here, and\n // should stay running after.\n spawned?.kill(\"SIGTERM\");\n }\n}\n"],"names":["ANSI_PATTERN","LOCAL_URL_PATTERN","isPortActive","port","resolve","socket","net","readCachedPort","cacheFile","readFileSync","writeCachedPort","mkdirSync","dirname","writeFileSync","spawnAndDetect","target","timeoutMs","reject","child","spawn","settled","timer","handleOutput","data","isError","text","line","label","match","url","_a","_b","err","code","toLaunchTarget","name","server","launchAndDetectStorybook","cachedPort","distDir","fileURLToPath","openBrowser","startCmd","startDevServer","engineConfig","webServers","options","sourceOfTruth","sourceOfTruthServer","targetServer","devPort","sourceOfTruthRunning","targetRunning","spawned","app","express","publicDir","join","headerPath","req","res","next","html","createProxyMiddleware","cleaningUp","cleanup","addFormats","ajvFormatsModule.default","ajv","Ajv","validate","schema","validateFileConfig","path","errors","error","extra","MANTINE_ADAPTER_PACKAGE_NAME","CONFIG_FILE_NAME","DEFAULT_STORYBOOK_COMMAND","DEFAULT_STORYBOOK_PORT","DEFAULT_SOURCE_OF_TRUTH_PORT","DEFAULT_SOURCE_OF_TRUTH_NAME","DEFAULT_GOLDEN_THRESHOLD_PIXELS","DEFAULT_SOURCE_OF_TRUTH_THRESHOLD_PIXELS","readOwnPackageJson","cwd","existsSync","detectOwnPort","storybookScript","detectOwnName","loadFileConfig","resolveConfig","overrides","file","isSourceOfTruthAdapter","ownName","ownPort","ownCommand","ownCwd","_c","ownWebServer","sharedEngineConfig","sourceOfTruthName","sourceOfTruthPort","sourceOfTruthWebServer","sourceOfTruthGolden","sourceOfTruthCwd","mantineAdapterVersion","mantineSourceOfTruthWebServer","testingEntry","pathToFileURL","args","sourceOfTruthVersion","sourceOfTruthVersionFlagIndex","value","storyId","storyFlagIndex","OWN_FLAGS","passthroughArgs","arg","main","runAutomated","config","runDir","ownTarget","result","spawnSync"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/portDiscovery.ts","../src/devServer.ts","../src/validateFileConfig.ts","../src/fileConfig.ts","../src/cli.ts"],"sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport net from \"node:net\";\nimport type { HarnessWebServerConfig } from \"./harness/mantineSourceOfTruth.js\";\n\n/**\n * Boots a target's Storybook and discovers the real port it ends up on,\n * instead of pinning one via `-p`/`--port`. Storybook silently falls back to\n * an OS-assigned port whenever its default/configured one is taken (this is\n * what caused the flaky `webServer` timeouts noted in mui-adapter), so the\n * only reliable source of truth is the URL it prints in its own startup\n * banner. Used by both the automated/headless run (cli.ts) and Dev Mode\n * (devServer.ts) — neither pins a port anymore.\n */\n\nexport interface LaunchTarget {\n /** Human-readable name used in log lines. */\n name: string;\n command: string;\n cwd: string;\n /** Reuse an already-running instance (detected via the last-known-port\n * cache) instead of spawning a new one. Mirrors the old `reuseExistingServer`\n * behavior, which used to just probe the one fixed configured port. */\n reuseExistingServer: boolean;\n /** File the discovered port is cached in between runs, so a later\n * `reuseExistingServer` run knows where to look. One per target. */\n cacheFile: string;\n timeoutMs?: number;\n}\n\nexport interface DiscoveredServer {\n url: string;\n port: number;\n /** The process we spawned, or `null` if an already-running instance was\n * reused — callers should only kill what they started. */\n process: ChildProcess | null;\n}\n\n// Storybook prints its bound address in its startup banner wrapped in ANSI\n// color codes and box-drawing chars, e.g.:\n// │ │ - Local: http://localhost:57496/ │ │\n// Strip ANSI first, then match the URL itself.\nconst ANSI_PATTERN = /\\x1b\\[[0-9;]*m/g;\nconst LOCAL_URL_PATTERN = /Local:\\s*(https?:\\/\\/localhost:\\d+)/;\n\nfunction isPortActive(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const socket = new net.Socket();\n socket.setTimeout(200);\n socket.once(\"connect\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.once(\"timeout\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.once(\"error\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.connect(port, \"127.0.0.1\");\n });\n}\n\nfunction readCachedPort(cacheFile: string): number | undefined {\n try {\n const port = JSON.parse(readFileSync(cacheFile, \"utf8\")).port;\n return typeof port === \"number\" ? port : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction writeCachedPort(cacheFile: string, port: number): void {\n mkdirSync(dirname(cacheFile), { recursive: true });\n writeFileSync(cacheFile, JSON.stringify({ port }));\n}\n\n/** Spawns `target.command`, streaming its output to the console like before,\n * and resolves once it reports the URL it actually bound to. */\nfunction spawnAndDetect(\n target: LaunchTarget,\n timeoutMs: number,\n): Promise<DiscoveredServer> {\n return new Promise((resolve, reject) => {\n const child = spawn(target.command, {\n cwd: target.cwd,\n stdio: \"pipe\",\n shell: true,\n });\n\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n child.kill();\n reject(\n new Error(\n `Timed out after ${timeoutMs}ms waiting for ${target.name}'s Storybook to report its URL (no \"Local: http://localhost:<port>\" line seen in its output).`,\n ),\n );\n }, timeoutMs);\n\n const handleOutput = (data: Buffer, isError: boolean) => {\n const text = data.toString();\n for (const line of text.split(\"\\n\")) {\n if (line.trim()) {\n const label = isError\n ? `${target.name} SB ERROR`\n : `${target.name} SB`;\n (isError ? console.error : console.log)(`[${label}] ${line.trim()}`);\n }\n }\n if (settled) return;\n const match = text.replace(ANSI_PATTERN, \"\").match(LOCAL_URL_PATTERN);\n if (match) {\n settled = true;\n clearTimeout(timer);\n // Non-null: LOCAL_URL_PATTERN has exactly one capture group, and a\n // match only happens when it participated.\n const url = match[1]!;\n const port = Number(new URL(url).port);\n writeCachedPort(target.cacheFile, port);\n resolve({ url, port, process: child });\n }\n };\n child.stdout?.on(\"data\", (data: Buffer) => handleOutput(data, false));\n child.stderr?.on(\"data\", (data: Buffer) => handleOutput(data, true));\n child.on(\"error\", (err) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n reject(err);\n });\n child.on(\"exit\", (code) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n reject(\n new Error(\n `${target.name}'s Storybook exited (code ${code}) before reporting its URL.`,\n ),\n );\n });\n });\n}\n\n/** Adapts a resolved `HarnessWebServerConfig` into a `LaunchTarget`. */\nexport function toLaunchTarget(\n name: string,\n server: HarnessWebServerConfig,\n): LaunchTarget {\n return {\n name,\n command: server.command,\n cwd: server.cwd,\n reuseExistingServer: server.reuseExistingServer,\n cacheFile: server.cacheFile,\n timeoutMs: server.timeout,\n };\n}\n\n/** Reuses an already-running Storybook if `reuseExistingServer` is set and\n * the last-known-port cache points at something still listening; otherwise\n * spawns `target.command` fresh and detects the real port from its output. */\nexport async function launchAndDetectStorybook(\n target: LaunchTarget,\n): Promise<DiscoveredServer> {\n if (target.reuseExistingServer) {\n const cachedPort = readCachedPort(target.cacheFile);\n if (cachedPort !== undefined && (await isPortActive(cachedPort))) {\n console.log(\n `[Dev Launcher] ${target.name} is already running on port ${cachedPort} (reused).`,\n );\n return {\n url: `http://localhost:${cachedPort}`,\n port: cachedPort,\n process: null,\n };\n }\n }\n console.log(`[Dev Launcher] Launching Storybook for ${target.name}...`);\n return spawnAndDetect(target, target.timeoutMs ?? 120 * 1000);\n}\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport express from \"express\";\nimport { createProxyMiddleware } from \"http-proxy-middleware\";\nimport type { AdapterTesterConfig } from \"./config.js\";\nimport type { HarnessWebServerConfig } from \"./harness/mantineSourceOfTruth.js\";\nimport { launchAndDetectStorybook, toLaunchTarget } from \"./portDiscovery.js\";\n\n/**\n * Interactive Dev Mode: a synced, side-by-side browser view of this\n * project's own Storybook and the source of truth, with per-story note\n * taking and an AI-report export. Boots both Storybooks (reusing them if\n * already running) behind a proxy so the own/target pane — which drives\n * the sync and carries Storybook's nav sidebar — loads same-origin on the\n * left, with the source of truth following along as a bare preview on the\n * right.\n *\n * Config-driven — takes the same `{ engineConfig, webServers }` shape\n * `resolveConfig()` produces, in the same [sourceOfTruth, target] order.\n */\n\nconst distDir = dirname(fileURLToPath(import.meta.url));\n\nfunction openBrowser(url: string): void {\n const startCmd =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"start\"\n : \"xdg-open\";\n console.log(`[Dev Launcher] Auto-launching browser: ${url}`);\n spawn(startCmd, [url], { shell: process.platform === \"win32\" }).on(\n \"error\",\n (err) => {\n console.error(`[Dev Launcher] Failed to auto-launch browser:`, err);\n },\n );\n}\n\nexport interface DevServerOptions {\n /** Port the dev-mode proxy UI itself listens on. Defaults to 6010. */\n port?: number;\n}\n\nexport async function startDevServer(\n engineConfig: AdapterTesterConfig,\n webServers: HarnessWebServerConfig[],\n options: DevServerOptions = {},\n): Promise<void> {\n const [sourceOfTruth, target] = engineConfig.targets;\n const [sourceOfTruthServer, targetServer] = webServers;\n if (\n !sourceOfTruth?.sourceOfTruth ||\n !target ||\n target.sourceOfTruth ||\n !sourceOfTruthServer ||\n !targetServer\n ) {\n throw new Error(\n \"adapter-tester dev mode requires exactly two targets: [sourceOfTruth, target] — check adapter-tester.config.json.\",\n );\n }\n\n const devPort = options.port ?? 6010;\n\n // Neither Storybook is pinned to a specific port — each one silently\n // falls back to an OS-assigned port whenever its default/configured one is\n // taken, so the real port is only known once it reports it (see\n // portDiscovery.ts). Booted concurrently since neither depends on the other.\n console.log(\n `[Dev Launcher] Resolving ${sourceOfTruth.name} and ${target.name} Storybooks...`,\n );\n const [sourceOfTruthRunning, targetRunning] = await Promise.all([\n launchAndDetectStorybook(\n toLaunchTarget(sourceOfTruth.name, sourceOfTruthServer),\n ),\n launchAndDetectStorybook(toLaunchTarget(target.name, targetServer)),\n ]);\n console.log(`[Dev Launcher] Both Storybooks are active and responsive!`);\n\n const spawned = [sourceOfTruthRunning.process, targetRunning.process].filter(\n (child): child is ChildProcess => child !== null,\n );\n\n const app = express();\n const publicDir = join(distDir, \"../public\");\n const headerPath = join(distDir, \"../report-header.txt\");\n\n // Serve the Dev Mode UI at the root path ONLY if there is no query string.\n // This allows the iframe (which loads with ?path=/story/...) to pass\n // through to the proxy, avoiding an infinite loop of nested wrappers.\n app.get(\"/\", (req, res, next) => {\n if (req.query.path) {\n next();\n return;\n }\n const html = readFileSync(join(publicDir, \"index.html\"), \"utf8\").replace(\n \"<head>\",\n `<head>\\n <script>window.__ADAPTER_TESTER__ = ${JSON.stringify({\n ownName: target.name,\n sourceOfTruthName: sourceOfTruth.name,\n sourceOfTruthPort: sourceOfTruthRunning.port,\n })};</script>`,\n );\n res.send(html);\n });\n\n // Serve the AI prompt header dynamically so it can be edited externally.\n // `reportHeader` in adapter-tester.config.json overrides the file entirely.\n app.get(\"/report-header.txt\", (req, res) => {\n if (engineConfig.reportHeader !== undefined) {\n res.type(\"text/plain\").send(engineConfig.reportHeader);\n return;\n }\n try {\n res.type(\"text/plain\").send(readFileSync(headerPath, \"utf8\"));\n } catch {\n res.status(500).send(\"Error loading report-header.txt\");\n }\n });\n\n // Proxy everything else to this project's own Storybook, preserving\n // absolute paths (e.g. /@vite/client) so HMR keeps working same-origin.\n // This is the pane that drives the sync and shows Storybook's nav sidebar.\n app.use(\n \"/\",\n createProxyMiddleware({\n target: targetRunning.url,\n changeOrigin: true,\n ws: true,\n }),\n );\n\n let cleaningUp = false;\n const cleanup = () => {\n if (cleaningUp) return;\n cleaningUp = true;\n console.log(\n \"\\n[Dev Launcher] Shutting down Dev Mode server and spawned Storybooks...\",\n );\n for (const child of spawned) child.kill(\"SIGINT\");\n process.exit(0);\n };\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n app.listen(devPort, () => {\n console.log(`\n====================================================\n🚀 Adapter Dev Mode proxy running at:\n http://localhost:${devPort}\n====================================================\n`);\n openBrowser(`http://localhost:${devPort}`);\n });\n}\n","import { Ajv } from \"ajv\";\nimport * as ajvFormatsModule from \"ajv-formats\";\nimport type { FormatsPlugin } from \"ajv-formats\";\nimport schema from \"./adapter-tester.schema.json\" with { type: \"json\" };\n\n// ajv-formats ships an ESM-style `export default` on a CJS build with no\n// `\"type\"` field, which under `moduleResolution: NodeNext` TS resolves to\n// the raw module namespace instead of unwrapping the default — a known\n// ajv-formats/TS interop gap, not a version mismatch.\nconst addFormats = (ajvFormatsModule as unknown as { default: FormatsPlugin })\n .default;\n\nconst ajv = new Ajv({ allErrors: true, strict: true });\naddFormats(ajv);\nconst validate = ajv.compile(schema);\n\n/**\n * Validates a parsed `adapter-tester.config.json` against\n * `adapter-tester.schema.json`. Throws with every violation listed — callers\n * must not silently coerce or drop invalid fields.\n */\nexport function validateFileConfig(data: unknown, path: string): void {\n if (validate(data)) return;\n\n const errors = (validate.errors ?? [])\n .map((error) => {\n const extra = error.params?.additionalProperty\n ? ` '${error.params.additionalProperty}'`\n : \"\";\n return ` - ${error.instancePath || \"root\"} ${error.message}${extra}`;\n })\n .join(\"\\n\");\n throw new Error(`Invalid ${path}:\\n${errors}`);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type {\n AdapterTesterConfig,\n SourceOfTruthGoldenLocation,\n StoryOverride,\n} from \"./config.js\";\nimport type { HarnessWebServerConfig } from \"./harness/mantineSourceOfTruth.js\";\nimport { mantineSourceOfTruthWebServer } from \"./harness/mantineSourceOfTruth.js\";\nimport { validateFileConfig } from \"./validateFileConfig.js\";\n\nconst MANTINE_ADAPTER_PACKAGE_NAME = \"@recursica/adapter-mantine-v8\";\n\nexport const CONFIG_FILE_NAME = \"adapter-tester.config.json\";\nconst DEFAULT_STORYBOOK_COMMAND = \"npm run storybook\";\nconst DEFAULT_STORYBOOK_PORT = 6006;\nconst DEFAULT_SOURCE_OF_TRUTH_PORT = 6011;\nconst DEFAULT_SOURCE_OF_TRUTH_NAME = \"Mantine\";\nconst DEFAULT_GOLDEN_THRESHOLD_PIXELS = 10;\nconst DEFAULT_SOURCE_OF_TRUTH_THRESHOLD_PIXELS = 3500;\n\ninterface StorybookTargetFileConfig {\n /** First-guess port only, not authoritative — the real port is\n * auto-detected from this Storybook's own startup output, since Storybook\n * silently falls back to an OS-assigned port whenever this one is taken.\n * Auto-detected from this project's own `scripts.storybook` (a `-p\n * <port>`/`--port <port>` flag) when omitted, falling back to 6006. */\n port?: number;\n /** Command that boots the target's Storybook. Defaults to `npm run storybook`. */\n command?: string;\n /** Directory the command runs in, relative to the config file. Defaults to \".\" */\n cwd?: string;\n}\n\ninterface MantineHarnessSourceOfTruthFileConfig {\n /** Default mode: boots a throwaway harness that installs the published\n * `@recursica/adapter-mantine-v8` from npm — no monorepo checkout required. */\n type?: \"mantine-harness\";\n /** First-guess port only, not authoritative — see `HarnessWebServerConfig.port`. */\n port?: number;\n mantineAdapterVersion?: string;\n storybookTemplateVersion?: string;\n}\n\ninterface UrlSourceOfTruthFileConfig {\n /** Non-standard mode: points at an already-addressable Storybook — e.g. a\n * sibling workspace package's own Storybook inside this monorepo. */\n type: \"url\";\n name?: string;\n /** First-guess port only, not authoritative — see `HarnessWebServerConfig.port`.\n * Defaults to 6011. */\n port?: number;\n command?: string;\n cwd?: string;\n}\n\ntype SourceOfTruthFileConfig =\n | MantineHarnessSourceOfTruthFileConfig\n | UrlSourceOfTruthFileConfig;\n\nexport interface AdapterTesterFileConfig {\n /** Label for this project's own target. Defaults to the unscoped name in\n * this project's package.json (e.g. \"@recursica/mui-adapter\" -> \"mui-adapter\"). */\n name?: string;\n storybook?: StorybookTargetFileConfig;\n sourceOfTruth?: SourceOfTruthFileConfig;\n goldenThresholdPixels?: number;\n sourceOfTruthThresholdPixels?: number;\n stories?: Record<string, StoryOverride>;\n excludeTitlePrefixes?: string[];\n /** Overrides the AI report header text shown in Dev Mode's \"Full Report\"\n * export. Defaults to the contents of `report-header.txt`. */\n reportHeader?: string;\n /**\n * True only for the source-of-truth adapter's own config (mantine-adapter).\n * Skips `sourceOfTruth` entirely — there's nothing above it to diverge\n * from — and runs the own-drift golden check standalone, against just this\n * project's own Storybook. Defaults to false.\n */\n isSourceOfTruthAdapter?: boolean;\n}\n\nexport interface ResolvedAdapterTesterConfig {\n engineConfig: AdapterTesterConfig;\n webServers: HarnessWebServerConfig[];\n}\n\nexport interface ResolveConfigOverrides {\n /** From `--source-of-truth-version`. Overrides `sourceOfTruth.mantineAdapterVersion`. */\n mantineAdapterVersion?: string;\n}\n\nfunction readOwnPackageJson(cwd: string): any {\n const path = join(cwd, \"package.json\");\n if (!existsSync(path)) {\n throw new Error(\n `No package.json found in ${cwd} — run adapter-tester from the root of the project being tested.`,\n );\n }\n return JSON.parse(readFileSync(path, \"utf8\"));\n}\n\nfunction detectOwnPort(cwd: string): number {\n const pkg = readOwnPackageJson(cwd);\n const storybookScript = pkg.scripts?.storybook as string | undefined;\n const match = storybookScript?.match(/(?:-p|--port)[ =](\\d+)/);\n return match ? Number(match[1]) : DEFAULT_STORYBOOK_PORT;\n}\n\nfunction detectOwnName(cwd: string): string {\n const pkg = readOwnPackageJson(cwd);\n const name = pkg.name as string | undefined;\n return name ? name.split(\"/\").pop()! : \"Adapter\";\n}\n\nfunction loadFileConfig(cwd: string): AdapterTesterFileConfig {\n const path = join(cwd, CONFIG_FILE_NAME);\n if (!existsSync(path)) {\n return {};\n }\n const data = JSON.parse(readFileSync(path, \"utf8\"));\n validateFileConfig(data, path);\n return data;\n}\n\n/**\n * Loads `adapter-tester.config.json` from `cwd` (or falls back to defaults\n * when the file doesn't exist) and resolves it into the engine config\n * `resolveVisualRegressionPlan` consumes plus the Playwright `webServer` entries\n * needed to boot both sides of the comparison.\n *\n * Default mode (no `sourceOfTruth`/`storybook` set): compares this project's\n * own Storybook against a throwaway Mantine harness — no monorepo checkout\n * required. Set `sourceOfTruth.type: \"url\"` for the non-standard mode used\n * to compare sibling workspace packages inside this monorepo.\n *\n * `overrides.mantineAdapterVersion` (from `--source-of-truth-version`) pins\n * the mantine-harness install/golden-fetch version for this run, overriding\n * `sourceOfTruth.mantineAdapterVersion`. Throws if passed together with\n * `isSourceOfTruthAdapter` or `sourceOfTruth.type: \"url\"` — neither has a\n * version to pin.\n */\nexport function resolveConfig(\n cwd: string,\n overrides: ResolveConfigOverrides = {},\n): ResolvedAdapterTesterConfig {\n const file = loadFileConfig(cwd);\n const isSourceOfTruthAdapter = file.isSourceOfTruthAdapter ?? false;\n\n if (overrides.mantineAdapterVersion && isSourceOfTruthAdapter) {\n throw new Error(\n \"--source-of-truth-version has no effect on the source-of-truth adapter's own config (isSourceOfTruthAdapter: true) — there's nothing to pin a version for.\",\n );\n }\n\n const ownName = file.name ?? detectOwnName(cwd);\n const ownPort = file.storybook?.port ?? detectOwnPort(cwd);\n const ownCommand = file.storybook?.command ?? DEFAULT_STORYBOOK_COMMAND;\n const ownCwd = resolve(cwd, file.storybook?.cwd ?? \".\");\n const ownWebServer: HarnessWebServerConfig = {\n command: ownCommand,\n port: ownPort,\n cwd: ownCwd,\n reuseExistingServer: !process.env.CI,\n cacheFile: join(cwd, \".adapter-tester\", \"last-port-own.json\"),\n timeout: 120 * 1000,\n };\n\n const sharedEngineConfig = {\n goldenThresholdPixels:\n file.goldenThresholdPixels ?? DEFAULT_GOLDEN_THRESHOLD_PIXELS,\n sourceOfTruthThresholdPixels:\n file.sourceOfTruthThresholdPixels ??\n DEFAULT_SOURCE_OF_TRUTH_THRESHOLD_PIXELS,\n stories: file.stories,\n excludeTitlePrefixes: file.excludeTitlePrefixes,\n reportHeader: file.reportHeader,\n // Keyed off `ownCwd`, not `cwd` — the project actually being tested, not\n // wherever the config file happens to live. Matters whenever\n // `storybook.cwd` points at a sibling package: its goldens must resolve\n // to that package's own `test/golden/` (the same directory used when\n // that package runs adapter-tester directly), not a directory under\n // wherever this config file lives.\n goldenDir: join(ownCwd, \"test\", \"golden\"),\n isSourceOfTruthAdapter,\n // Overwritten by the CLI from --update-golden/--approve-divergence.\n goldenMode: \"check\" as const,\n // Overwritten by the CLI from --divergence-only.\n checkMode: \"own\" as const,\n };\n\n // The source-of-truth adapter's own config has nothing above it to\n // diverge from — no second target, no harness/webServer for it at all.\n if (isSourceOfTruthAdapter) {\n return {\n engineConfig: {\n ...sharedEngineConfig,\n targets: [{ name: ownName, url: `http://localhost:${ownPort}` }],\n },\n webServers: [ownWebServer],\n };\n }\n\n const sourceOfTruth = file.sourceOfTruth ?? {\n type: \"mantine-harness\" as const,\n };\n\n let sourceOfTruthName: string;\n let sourceOfTruthPort: number;\n let sourceOfTruthWebServer: HarnessWebServerConfig;\n let sourceOfTruthGolden: SourceOfTruthGoldenLocation;\n\n if (sourceOfTruth.type === \"url\") {\n if (overrides.mantineAdapterVersion) {\n throw new Error(\n '--source-of-truth-version has no effect with sourceOfTruth.type: \"url\" — that mode reads a sibling package\\'s local checkout directly, not a published version.',\n );\n }\n sourceOfTruthName = sourceOfTruth.name ?? DEFAULT_SOURCE_OF_TRUTH_NAME;\n sourceOfTruthPort = sourceOfTruth.port ?? DEFAULT_SOURCE_OF_TRUTH_PORT;\n const sourceOfTruthCwd = resolve(cwd, sourceOfTruth.cwd ?? \".\");\n sourceOfTruthWebServer = {\n command: sourceOfTruth.command ?? DEFAULT_STORYBOOK_COMMAND,\n port: sourceOfTruthPort,\n cwd: sourceOfTruthCwd,\n reuseExistingServer: !process.env.CI,\n cacheFile: join(cwd, \".adapter-tester\", \"last-port-source-of-truth.json\"),\n timeout: 120 * 1000,\n };\n // Sibling package already checked out locally — read its golden files\n // directly, including any uncommitted local changes. No install, no\n // network call.\n sourceOfTruthGolden = {\n type: \"local\",\n dir: join(sourceOfTruthCwd, \"test\", \"golden\"),\n };\n } else {\n sourceOfTruthName = DEFAULT_SOURCE_OF_TRUTH_NAME;\n sourceOfTruthPort = sourceOfTruth.port ?? DEFAULT_SOURCE_OF_TRUTH_PORT;\n const mantineAdapterVersion =\n overrides.mantineAdapterVersion ?? sourceOfTruth.mantineAdapterVersion;\n sourceOfTruthWebServer = mantineSourceOfTruthWebServer({\n dir: join(cwd, \".adapter-tester/mantine-harness\"),\n port: sourceOfTruthPort,\n mantineAdapterVersion,\n storybookTemplateVersion: sourceOfTruth.storybookTemplateVersion,\n });\n // No local checkout — resolve the installed version against the npm\n // registry and fetch that version's golden files from the public repo.\n // Never needs `sourceOfTruthWebServer` above; the golden check on its\n // own doesn't boot a second Storybook at all (see cli.ts).\n sourceOfTruthGolden = {\n type: \"npm\",\n packageName: MANTINE_ADAPTER_PACKAGE_NAME,\n versionSpec: mantineAdapterVersion ?? \"latest\",\n cacheDir: join(cwd, \".adapter-tester/mantine-golden-cache\"),\n };\n }\n\n return {\n engineConfig: {\n ...sharedEngineConfig,\n targets: [\n {\n name: sourceOfTruthName,\n url: `http://localhost:${sourceOfTruthPort}`,\n sourceOfTruth: true,\n },\n { name: ownName, url: `http://localhost:${ownPort}` },\n ],\n sourceOfTruthGolden,\n },\n // [sourceOfTruth, own] — Dev Mode needs both; the automated golden run\n // (cli.ts) only ever boots the last entry, since its divergence check\n // reads stored golden files, not a live source-of-truth page.\n webServers: [sourceOfTruthWebServer, ownWebServer],\n };\n}\n","import { spawnSync } from \"node:child_process\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport type { AdapterTesterConfig } from \"./config.js\";\nimport { startDevServer } from \"./devServer.js\";\nimport { resolveConfig } from \"./fileConfig.js\";\nimport type { HarnessWebServerConfig } from \"./harness/mantineSourceOfTruth.js\";\nimport { launchAndDetectStorybook, toLaunchTarget } from \"./portDiscovery.js\";\n\n// `dist/testing.js` is always a sibling of this file — both inside a\n// consumer's node_modules/@recursica/adapter-tester/dist and inside this\n// monorepo's own packages/adapter-tester/dist when self-hosting — so the\n// generated spec below can reach it without depending on how the package\n// itself resolves at import time.\nconst distDir = dirname(fileURLToPath(import.meta.url));\nconst testingEntry = pathToFileURL(join(distDir, \"testing.js\")).href;\n\n// `--source-of-truth-version <value>` takes a value, unlike every other own\n// flag below — pulled out of argv first so the boolean flags/passthrough\n// logic never has to know it exists.\nconst args = process.argv.slice(2);\nlet sourceOfTruthVersion: string | undefined;\nconst sourceOfTruthVersionFlagIndex = args.indexOf(\"--source-of-truth-version\");\nif (sourceOfTruthVersionFlagIndex !== -1) {\n const value = args[sourceOfTruthVersionFlagIndex + 1];\n if (value === undefined || value.startsWith(\"--\")) {\n throw new Error(\n \"--source-of-truth-version requires a value, e.g. --source-of-truth-version 0.53.0\",\n );\n }\n sourceOfTruthVersion = value;\n args.splice(sourceOfTruthVersionFlagIndex, 2);\n}\n\n// `--story <story-id>` scopes a run to exactly one story — pulled out the\n// same way, ahead of the passthrough logic, since it's consumed here (to\n// filter the generated spec) rather than forwarded to Playwright. Equivalent\n// to `--grep \"<story-id>\"`, but exact-match and easier to reach for when\n// iterating on a single component.\nlet storyId: string | undefined;\nconst storyFlagIndex = args.indexOf(\"--story\");\nif (storyFlagIndex !== -1) {\n const value = args[storyFlagIndex + 1];\n if (value === undefined || value.startsWith(\"--\")) {\n throw new Error(\n \"--story requires a story id, e.g. --story ui-kit-button--loading\",\n );\n }\n storyId = value;\n args.splice(storyFlagIndex, 2);\n}\n\nconst cwd = process.cwd();\nconst { engineConfig, webServers } = resolveConfig(cwd, {\n mantineAdapterVersion: sourceOfTruthVersion,\n});\n\nif (args.includes(\"--update-golden\")) {\n engineConfig.goldenMode = \"update-golden\";\n} else if (args.includes(\"--approve-divergence\")) {\n engineConfig.goldenMode = \"approve-divergence\";\n}\n\nif (args.includes(\"--divergence-only\")) {\n if (engineConfig.isSourceOfTruthAdapter) {\n throw new Error(\n \"--divergence-only has nothing to diverge from — isSourceOfTruthAdapter is true in this project's adapter-tester.config.json.\",\n );\n }\n engineConfig.checkMode = \"divergence\";\n} else if (engineConfig.goldenMode === \"approve-divergence\") {\n throw new Error(\n \"--approve-divergence only makes sense with --divergence-only — there's nothing to approve without the divergence check running.\",\n );\n}\n\nif (args.includes(\"--dry-run\")) {\n console.log(JSON.stringify({ engineConfig, webServers }, null, 2));\n process.exit(0);\n}\n\n// Any arg besides our own flags is passed straight through to `playwright\n// test` — e.g. `npm run adapter-tester:automated -- --grep \"Toast\"` to scope\n// a run to matching stories, instead of every invocation running the full\n// suite. Our own flags are consumed above, not forwarded — Playwright itself\n// doesn't know about them.\nconst OWN_FLAGS = new Set([\n \"--dry-run\",\n \"--serve\",\n \"--update-golden\",\n \"--approve-divergence\",\n \"--divergence-only\",\n]);\nconst passthroughArgs = args.filter((arg) => !OWN_FLAGS.has(arg));\n\n// The bundled build target doesn't support top-level await, so the async\n// work (launching Storybook and detecting its real port) lives in main().\nmain().catch((err) => {\n console.error(err);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n if (args.includes(\"--serve\")) {\n if (engineConfig.isSourceOfTruthAdapter) {\n throw new Error(\n \"Dev Mode (--serve) has nothing to sync this project's Storybook against — isSourceOfTruthAdapter is true in this project's adapter-tester.config.json.\",\n );\n }\n // Dual-Storybook interactive Dev Mode — no Playwright, no screenshots.\n // Used by the `adapter-tester` npm script.\n await startDevServer(engineConfig, webServers);\n } else {\n // The golden-image checks never need the source-of-truth adapter's own\n // Storybook running — the divergence check reads its stored golden files,\n // not a live page — so only the last (own) webServer is booted here. Dev\n // Mode above is the one thing that still needs both.\n const ownWebServer = webServers.at(-1);\n if (!ownWebServer) {\n throw new Error(\"resolveConfig() returned no webServers to boot.\");\n }\n await runAutomated(ownWebServer, engineConfig);\n }\n}\n\n/**\n * Boots this project's own Storybook (auto-detecting the port it actually\n * lands on — see portDiscovery.ts), generates a throwaway Playwright config\n * + spec under `.adapter-tester/run/`, and runs the automated pixel-diff\n * suite against it. Used by the `adapter-tester:automated` npm script.\n *\n * Playwright's own `webServer` option can't be used here — it only knows how\n * to poll a port/URL decided *before* the command it spawns runs, but\n * Storybook silently falls back to a different, OS-assigned port whenever\n * its configured one is taken. So this boots and waits for Storybook itself,\n * then points the generated config straight at whatever URL it actually\n * reports.\n */\nasync function runAutomated(\n server: HarnessWebServerConfig,\n config: AdapterTesterConfig,\n): Promise<void> {\n const runDir = join(cwd, \".adapter-tester/run\");\n mkdirSync(runDir, { recursive: true });\n\n const ownTarget = config.targets.at(-1);\n if (!ownTarget) {\n throw new Error(\"resolveConfig() returned no targets to check.\");\n }\n const { url, process: spawned } = await launchAndDetectStorybook(\n toLaunchTarget(ownTarget.name, server),\n );\n ownTarget.url = url;\n\n writeFileSync(\n join(runDir, \"playwright.config.js\"),\n `// Generated by \\`adapter-tester\\` — do not edit, regenerated on every run.\nimport { defineConfig, devices } from \"@playwright/test\";\n\nexport default defineConfig({\n testDir: ${JSON.stringify(runDir)},\n // Each golden check only reads/writes its own story's manifest.json entry,\n // under a lock, so concurrent workers never race each other (see\n // \\`updateManifestEntry\\`). Defaults to 1 worker (1 Chromium instance) to\n // avoid exhausting memory running the full suite; override with\n // \\`--workers <n>\\` (forwarded straight through to Playwright) to parallelize.\n workers: 1,\n fullyParallel: true,\n forbidOnly: !!process.env.CI,\n retries: process.env.CI ? 2 : 0,\n reporter: [[\"html\", { open: \"never\" }], [\"list\"]],\n use: { trace: \"on-first-retry\" },\n projects: [{ name: \"chromium\", use: { ...devices[\"Desktop Chrome\"] } }],\n // No webServer entry — this project's own Storybook is already running by\n // the time this config is used (see runAutomated() in cli.ts).\n});\n`,\n );\n\n writeFileSync(\n join(runDir, \"visual-regression.spec.js\"),\n `// Generated by \\`adapter-tester\\` — do not edit, regenerated on every run.\nimport { test, expect } from \"@playwright/test\";\nimport { resolveVisualRegressionPlan } from ${JSON.stringify(testingEntry)};\n\n// \\`test.describe\\`/\\`test\\` are called here, not inside resolveVisualRegressionPlan,\n// so Playwright's HTML report groups the suite under this file instead of a\n// sourcemapped path into adapter-tester's own library code.\nconst { ownTargetName, suiteLabel, stories, missingFromSourceOfTruth, checkStory } =\n await resolveVisualRegressionPlan(${JSON.stringify(config, null, 2)});\n\n// Set from \\`--story <story-id>\\` — scopes this run to exactly one story and\n// skips the (suite-wide) parity check, which has nothing to do with it.\nconst storyId = ${JSON.stringify(storyId ?? null)};\nconst scopedStories = storyId\n ? stories.filter((story) => story.id === storyId)\n : stories;\nif (storyId && scopedStories.length === 0) {\n throw new Error(\\`--story \"\\${storyId}\" matched no story in this Storybook.\\`);\n}\n\ntest.describe(\\`\\${ownTargetName} — \\${suiteLabel}\\`, () => {\n if (!storyId && missingFromSourceOfTruth.length > 0) {\n test(\"story parity with source of truth\", () => {\n expect(\n missingFromSourceOfTruth,\n \\`\\${missingFromSourceOfTruth.length} stor(y/ies) exist in the source of truth but are missing here. Add the missing story, or mark it \\\\\\`exclude: true\\\\\\` under \\\\\\`stories\\\\\\` in adapter-tester.config.json if intentional.\\`,\n ).toEqual([]);\n });\n }\n for (const story of scopedStories) {\n test(story.id, async ({ browser }, testInfo) => {\n await checkStory(story, browser, testInfo);\n });\n }\n});\n`,\n );\n\n try {\n const result = spawnSync(\n \"npx\",\n [\n \"playwright\",\n \"test\",\n \"--config\",\n join(runDir, \"playwright.config.js\"),\n ...passthroughArgs,\n ],\n { stdio: \"inherit\", cwd, shell: process.platform === \"win32\" },\n );\n process.exitCode = result.status ?? 1;\n } finally {\n // Only tear down the Storybook we spawned — an instance we reused via\n // the last-known-port cache was already running before we got here, and\n // should stay running after.\n spawned?.kill(\"SIGTERM\");\n }\n}\n"],"names":["ANSI_PATTERN","LOCAL_URL_PATTERN","isPortActive","port","resolve","socket","net","readCachedPort","cacheFile","readFileSync","writeCachedPort","mkdirSync","dirname","writeFileSync","spawnAndDetect","target","timeoutMs","reject","child","spawn","settled","timer","handleOutput","data","isError","text","line","label","match","url","_a","_b","err","code","toLaunchTarget","name","server","launchAndDetectStorybook","cachedPort","distDir","fileURLToPath","openBrowser","startCmd","startDevServer","engineConfig","webServers","options","sourceOfTruth","sourceOfTruthServer","targetServer","devPort","sourceOfTruthRunning","targetRunning","spawned","app","express","publicDir","join","headerPath","req","res","next","html","createProxyMiddleware","cleaningUp","cleanup","addFormats","ajvFormatsModule.default","ajv","Ajv","validate","schema","validateFileConfig","path","errors","error","extra","MANTINE_ADAPTER_PACKAGE_NAME","CONFIG_FILE_NAME","DEFAULT_STORYBOOK_COMMAND","DEFAULT_STORYBOOK_PORT","DEFAULT_SOURCE_OF_TRUTH_PORT","DEFAULT_SOURCE_OF_TRUTH_NAME","DEFAULT_GOLDEN_THRESHOLD_PIXELS","DEFAULT_SOURCE_OF_TRUTH_THRESHOLD_PIXELS","readOwnPackageJson","cwd","existsSync","detectOwnPort","storybookScript","detectOwnName","loadFileConfig","resolveConfig","overrides","file","isSourceOfTruthAdapter","ownName","ownPort","ownCommand","ownCwd","_c","ownWebServer","sharedEngineConfig","sourceOfTruthName","sourceOfTruthPort","sourceOfTruthWebServer","sourceOfTruthGolden","sourceOfTruthCwd","mantineAdapterVersion","mantineSourceOfTruthWebServer","testingEntry","pathToFileURL","args","sourceOfTruthVersion","sourceOfTruthVersionFlagIndex","value","storyId","storyFlagIndex","OWN_FLAGS","passthroughArgs","arg","main","runAutomated","config","runDir","ownTarget","result","spawnSync"],"mappings":"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"}
|
package/dist/fileConfig.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ interface StorybookTargetFileConfig {
|
|
|
15
15
|
}
|
|
16
16
|
interface MantineHarnessSourceOfTruthFileConfig {
|
|
17
17
|
/** Default mode: boots a throwaway harness that installs the published
|
|
18
|
-
* `@recursica/mantine-
|
|
18
|
+
* `@recursica/adapter-mantine-v8` from npm — no monorepo checkout required. */
|
|
19
19
|
type?: "mantine-harness";
|
|
20
20
|
/** First-guess port only, not authoritative — see `HarnessWebServerConfig.port`. */
|
|
21
21
|
port?: number;
|
package/dist/fileConfig.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileConfig.d.ts","sourceRoot":"","sources":["../src/fileConfig.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EAEnB,aAAa,EACd,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAMhF,eAAO,MAAM,gBAAgB,+BAA+B,CAAC;AAQ7D,UAAU,yBAAyB;IACjC;;;;2EAIuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,qCAAqC;IAC7C;
|
|
1
|
+
{"version":3,"file":"fileConfig.d.ts","sourceRoot":"","sources":["../src/fileConfig.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EAEnB,aAAa,EACd,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAMhF,eAAO,MAAM,gBAAgB,+BAA+B,CAAC;AAQ7D,UAAU,yBAAyB;IACjC;;;;2EAIuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,qCAAqC;IAC7C;mFAC+E;IAC/E,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,UAAU,0BAA0B;IAClC;yEACqE;IACrE,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;2BACuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,KAAK,uBAAuB,GACxB,qCAAqC,GACrC,0BAA0B,CAAC;AAE/B,MAAM,WAAW,uBAAuB;IACtC;uFACmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACxC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC;kEAC8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,mBAAmB,CAAC;IAClC,UAAU,EAAE,sBAAsB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAmCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,sBAA2B,GACrC,2BAA2B,CAoI7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveSourceOfTruthGolden.d.ts","sourceRoot":"","sources":["../../src/golden/resolveSourceOfTruthGolden.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"resolveSourceOfTruthGolden.d.ts","sourceRoot":"","sources":["../../src/golden/resolveSourceOfTruthGolden.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAO5B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,cAAc,CAAC;IACzB;kDAC8C;IAC9C,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACpD;AA2BD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,2BAA2B,GACpC,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAiFrC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generates a small, throwaway Storybook project that installs
|
|
3
|
-
* `@recursica/mantine-
|
|
3
|
+
* `@recursica/adapter-mantine-v8` as a real npm dependency (not a workspace
|
|
4
4
|
* link) and boots a real Storybook from its published `src/**\/*.stories.tsx`
|
|
5
5
|
* files, using `@recursica/storybook-template`'s exported factories.
|
|
6
6
|
*
|
|
@@ -21,7 +21,7 @@ export interface MantineSourceOfTruthHarnessOptions {
|
|
|
21
21
|
/** First-guess port, shown for `--dry-run` visibility only. The harness's
|
|
22
22
|
* Storybook is never pinned to this — see `HarnessWebServerConfig.port`. */
|
|
23
23
|
port: number;
|
|
24
|
-
/** npm version/range for @recursica/mantine-
|
|
24
|
+
/** npm version/range for @recursica/adapter-mantine-v8. Defaults to "latest". */
|
|
25
25
|
mantineAdapterVersion?: string;
|
|
26
26
|
/** npm version/range for @recursica/storybook-template. Defaults to "latest". */
|
|
27
27
|
storybookTemplateVersion?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mantineSourceOfTruth.d.ts","sourceRoot":"","sources":["../../src/harness/mantineSourceOfTruth.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,kCAAkC;IACjD;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;gFAC4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,
|
|
1
|
+
{"version":3,"file":"mantineSourceOfTruth.d.ts","sourceRoot":"","sources":["../../src/harness/mantineSourceOfTruth.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,kCAAkC;IACjD;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;gFAC4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iFAAiF;IACjF,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB;;;;mBAIe;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB,EAAE,OAAO,CAAC;IAC7B;iEAC6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAuID,8EAA8E;AAC9E,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,kCAAkC,GAC1C,MAAM,CAwBR;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,kCAAkC,GAC1C,sBAAsB,CAyBxB"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./mantineSourceOfTruth-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./mantineSourceOfTruth-CdRlVrRC.cjs");function n(r){const e=r.targets.filter(t=>t.sourceOfTruth).length;if(e!==1)throw new Error(`adapter-tester config must mark exactly one target as sourceOfTruth (found ${e})`);return r}function o(r){const e=r.targets.find(t=>t.sourceOfTruth);if(!e)throw new Error("adapter-tester config has no target marked sourceOfTruth");return e}exports.mantineSourceOfTruthWebServer=u.mantineSourceOfTruthWebServer;exports.scaffoldMantineSourceOfTruthHarness=u.scaffoldMantineSourceOfTruthHarness;exports.defineAdapterTesterConfig=n;exports.getSourceOfTruth=o;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ function m(r) {
|
|
|
19
19
|
storybook: "storybook dev"
|
|
20
20
|
},
|
|
21
21
|
dependencies: {
|
|
22
|
-
"@recursica/mantine-
|
|
22
|
+
"@recursica/adapter-mantine-v8": r.mantineAdapterVersion,
|
|
23
23
|
"@recursica/storybook-template": r.storybookTemplateVersion,
|
|
24
24
|
"@recursica/official-release": "latest",
|
|
25
25
|
"@recursica/adapter-common": "latest",
|
|
@@ -38,7 +38,7 @@ const u = `import { createMainConfig } from "@recursica/storybook-template/main"
|
|
|
38
38
|
|
|
39
39
|
const config = createMainConfig({
|
|
40
40
|
stories: [
|
|
41
|
-
"../node_modules/@recursica/mantine-
|
|
41
|
+
"../node_modules/@recursica/adapter-mantine-v8/src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
|
42
42
|
],
|
|
43
43
|
enableCORS: true,
|
|
44
44
|
});
|
|
@@ -72,7 +72,7 @@ const basePreview = createPreviewConfig({
|
|
|
72
72
|
recursicaUIKitJsonPath: recursicaUIKit,
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
// Mirrors mantine-
|
|
75
|
+
// Mirrors adapter-mantine-v8's own .storybook/preview.tsx decorator (every story defaults to
|
|
76
76
|
// withLayer: true, layer: 0, wrapped with 48px padding) — every real adapter's own preview.tsx
|
|
77
77
|
// applies this same wrapping, so a target adapter's story renders inside the same Layer
|
|
78
78
|
// chrome/padding the source-of-truth side does. Without this, target screenshots come out
|
|
@@ -126,13 +126,13 @@ function y(r) {
|
|
|
126
126
|
), o(t(e, ".storybook/main.ts"), u), o(t(e, ".storybook/preview.tsx"), f), o(t(e, ".gitignore"), `node_modules
|
|
127
127
|
`), e;
|
|
128
128
|
}
|
|
129
|
-
function
|
|
129
|
+
function k(r) {
|
|
130
130
|
const e = y(r), {
|
|
131
131
|
mantineAdapterVersion: s = "latest",
|
|
132
132
|
storybookTemplateVersion: i = "latest"
|
|
133
133
|
} = r;
|
|
134
134
|
return {
|
|
135
|
-
command: `npm install @recursica/mantine-
|
|
135
|
+
command: `npm install @recursica/adapter-mantine-v8@${s} @recursica/storybook-template@${i} --no-audit --no-fund && npm run storybook`,
|
|
136
136
|
port: r.port,
|
|
137
137
|
cwd: e,
|
|
138
138
|
reuseExistingServer: !process.env.CI,
|
|
@@ -141,7 +141,7 @@ function w(r) {
|
|
|
141
141
|
};
|
|
142
142
|
}
|
|
143
143
|
export {
|
|
144
|
-
|
|
144
|
+
k as m,
|
|
145
145
|
y as s
|
|
146
146
|
};
|
|
147
|
-
//# sourceMappingURL=mantineSourceOfTruth-
|
|
147
|
+
//# sourceMappingURL=mantineSourceOfTruth-BDJ5mfpd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mantineSourceOfTruth-BDJ5mfpd.js","sources":["../src/harness/mantineSourceOfTruth.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Generates a small, throwaway Storybook project that installs\n * `@recursica/adapter-mantine-v8` as a real npm dependency (not a workspace\n * link) and boots a real Storybook from its published `src/**\\/*.stories.tsx`\n * files, using `@recursica/storybook-template`'s exported factories.\n *\n * This lets any repo — including ones that never checked out the Recursica\n * monorepo — run adapter-tester's visual regression suite against Mantine\n * (Recursica's source-of-truth adapter) as one side of the comparison, while\n * the other side is that repo's own already-running local Storybook.\n *\n * See PROPOSAL-installed-package-harness.md for the verified prototype this\n * is built from, and the three upstream gaps it works around.\n */\n\nexport interface MantineSourceOfTruthHarnessOptions {\n /**\n * Directory the harness project is scaffolded into. Regenerated on every\n * call — add it to your .gitignore rather than committing it.\n */\n dir: string;\n /** First-guess port, shown for `--dry-run` visibility only. The harness's\n * Storybook is never pinned to this — see `HarnessWebServerConfig.port`. */\n port: number;\n /** npm version/range for @recursica/adapter-mantine-v8. Defaults to \"latest\". */\n mantineAdapterVersion?: string;\n /** npm version/range for @recursica/storybook-template. Defaults to \"latest\". */\n storybookTemplateVersion?: string;\n}\n\nexport interface HarnessWebServerConfig {\n command: string;\n /** First-guess port, shown for `--dry-run` visibility only — not\n * authoritative. The real port is whatever this Storybook's own startup\n * banner reports once it's actually running (see portDiscovery.ts), since\n * Storybook silently falls back to an OS-assigned port whenever this one\n * is taken. */\n port: number;\n cwd: string;\n reuseExistingServer: boolean;\n /** File the real, detected port is cached in between runs, so a later\n * `reuseExistingServer` run can find this instance again. */\n cacheFile: string;\n timeout: number;\n}\n\n// Peer/dev ranges pinned to what @recursica/mantine-adapter and\n// @recursica/storybook-template themselves require, so the harness can't\n// drift onto an incompatible Mantine or Storybook major version.\nconst MANTINE_CORE_RANGE = \"^8.0.0\";\nconst STORYBOOK_RANGE = \"^10.3.3\";\nconst REACT_RANGE = \"^19.0.0\";\n\n// storybook-template's createMainConfig() defaults its addons list to these\n// three but doesn't declare them as peerDependencies (proposal gap 2) — a\n// harness that skips installing any of them gets a silent \"could not\n// resolve addon\" warning at boot, then a hard runtime crash later when Vite\n// pre-bundles preview.tsx's dependency graph. Installed explicitly here.\nconst DEFAULT_ADDON_DEPENDENCIES = {\n \"@storybook/addon-docs\": STORYBOOK_RANGE,\n \"@storybook/addon-a11y\": STORYBOOK_RANGE,\n \"storybook-dark-mode\": \"^5.0.0\",\n};\n\n// adapter-mantine-v8's Introduction.stories.tsx (Version.tsx/OverStyling.tsx)\n// needs react-markdown, but it's a devDependency there — Storybook-only,\n// never bundled into dist — so an external `npm install` of the published\n// package won't pull it in. The harness boots a real Storybook against\n// src/, so it must provide this itself. Installed explicitly here.\nconst WORKAROUND_DEPENDENCIES = {\n \"react-markdown\": \"^10.1.0\",\n};\n\nfunction harnessPackageJson(options: {\n mantineAdapterVersion: string;\n storybookTemplateVersion: string;\n}) {\n return {\n name: \"adapter-tester-mantine-source-of-truth-harness\",\n private: true,\n type: \"module\",\n scripts: {\n // No `-p` pin — Storybook silently falls back to an OS-assigned port\n // whenever its default is taken, so the caller detects the real port\n // from this process's own output rather than trusting a fixed one.\n storybook: `storybook dev`,\n },\n dependencies: {\n \"@recursica/adapter-mantine-v8\": options.mantineAdapterVersion,\n \"@recursica/storybook-template\": options.storybookTemplateVersion,\n \"@recursica/official-release\": \"latest\",\n \"@recursica/adapter-common\": \"latest\",\n \"@mantine/core\": MANTINE_CORE_RANGE,\n \"@mantine/dates\": MANTINE_CORE_RANGE,\n react: REACT_RANGE,\n \"react-dom\": REACT_RANGE,\n storybook: STORYBOOK_RANGE,\n \"@storybook/react-vite\": STORYBOOK_RANGE,\n ...DEFAULT_ADDON_DEPENDENCIES,\n ...WORKAROUND_DEPENDENCIES,\n },\n };\n}\n\nconst MAIN_TS = `import { createMainConfig } from \"@recursica/storybook-template/main\";\n\nconst config = createMainConfig({\n stories: [\n \"../node_modules/@recursica/adapter-mantine-v8/src/**/*.stories.@(js|jsx|mjs|ts|tsx)\",\n ],\n enableCORS: true,\n});\n\n// react-docgen-typescript can't resolve a TS project for a config file living\n// in .storybook/ when the component source it's docgen'ing lives three\n// directories down inside node_modules — it throws \"Cannot read properties\n// of undefined (reading 'fileExists')\", which surfaces as a plain 404 on\n// preview.tsx. Docgen only powers Storybook's Controls/Docs tables, which\n// this harness never renders, so disabling it is a safe workaround (see\n// PROPOSAL-installed-package-harness.md, gap 3).\nconfig.typescript = { ...config.typescript, reactDocgen: false };\n\nexport default config;\n`;\n\nconst PREVIEW_TSX = `import type { Preview } from \"@storybook/react-vite\";\nimport { createPreviewConfig } from \"@recursica/storybook-template/preview\";\nimport { MantineProvider } from \"@mantine/core\";\nimport { Layer } from \"@recursica/adapter-common\";\nimport \"@mantine/core/styles.css\";\nimport \"@mantine/dates/styles.css\";\nimport \"@recursica/adapter-common/style.css\";\nimport \"@recursica/official-release/recursica_variables_scoped.css\";\nimport recursicaTokens from \"@recursica/official-release/recursica_tokens.json\";\nimport recursicaBrand from \"@recursica/official-release/recursica_brand.json\";\nimport recursicaUIKit from \"@recursica/official-release/recursica_ui-kit.json\";\n\nconst basePreview = createPreviewConfig({\n defaultTheme: \"light\",\n recursicaTokensJsonPath: recursicaTokens,\n recursicaBrandJsonPath: recursicaBrand,\n recursicaUIKitJsonPath: recursicaUIKit,\n});\n\n// Mirrors adapter-mantine-v8's own .storybook/preview.tsx decorator (every story defaults to\n// withLayer: true, layer: 0, wrapped with 48px padding) — every real adapter's own preview.tsx\n// applies this same wrapping, so a target adapter's story renders inside the same Layer\n// chrome/padding the source-of-truth side does. Without this, target screenshots come out\n// dramatically smaller/differently-positioned than the source of truth's (no Layer padding,\n// background, or border-radius at all), which alone can blow past the pixel-diff threshold\n// regardless of whether the actual Recursica tokens match — a false positive, not a real\n// component bug. ColorSchemeWrapper (mantine-adapter's dark-mode-toggle sync helper) is\n// intentionally not replicated — it only matters for the interactive dev-mode UI, not automated\n// screenshot diffing, which always runs in a single theme.\nconst preview: Preview = {\n ...basePreview,\n decorators: [\n (Story, context) => {\n const { withLayer = true, layer = 0 } = context.args;\n const content = <Story />;\n return (\n <MantineProvider>\n {withLayer ? (\n <Layer layer={layer as 0 | 1 | 2 | 3} style={{ padding: \"48px\" }}>\n {content}\n </Layer>\n ) : (\n content\n )}\n </MantineProvider>\n );\n },\n ...(basePreview.decorators || []),\n ],\n};\n\nexport default preview;\n`;\n\n/** Writes the harness project's files to `options.dir` without booting it. */\nexport function scaffoldMantineSourceOfTruthHarness(\n options: MantineSourceOfTruthHarnessOptions,\n): string {\n const {\n dir,\n mantineAdapterVersion = \"latest\",\n storybookTemplateVersion = \"latest\",\n } = options;\n\n mkdirSync(join(dir, \".storybook\"), { recursive: true });\n writeFileSync(\n join(dir, \"package.json\"),\n JSON.stringify(\n harnessPackageJson({\n mantineAdapterVersion,\n storybookTemplateVersion,\n }),\n null,\n 2,\n ) + \"\\n\",\n );\n writeFileSync(join(dir, \".storybook/main.ts\"), MAIN_TS);\n writeFileSync(join(dir, \".storybook/preview.tsx\"), PREVIEW_TSX);\n writeFileSync(join(dir, \".gitignore\"), \"node_modules\\n\");\n\n return dir;\n}\n\n/**\n * Scaffolds the harness and returns a Playwright `webServer` entry for it.\n * Spread the result directly into `playwright.config.ts`'s `webServer` array.\n */\nexport function mantineSourceOfTruthWebServer(\n options: MantineSourceOfTruthHarnessOptions,\n): HarnessWebServerConfig {\n const dir = scaffoldMantineSourceOfTruthHarness(options);\n const {\n mantineAdapterVersion = \"latest\",\n storybookTemplateVersion = \"latest\",\n } = options;\n\n // A bare `npm install` is satisfied by a package-lock.json already sitting\n // in `dir` from a prior run and skips re-resolving against the registry\n // entirely — no network call — so a run can silently keep testing against\n // a stale @recursica/adapter-mantine-v8/storybook-template even after a newer\n // version is published. Naming the two version-pinned packages as explicit\n // `pkg@specifier` CLI args instead forces npm to re-check just those two\n // against the registry every run, while the rest of node_modules stays\n // cached.\n const command = `npm install @recursica/adapter-mantine-v8@${mantineAdapterVersion} @recursica/storybook-template@${storybookTemplateVersion} --no-audit --no-fund && npm run storybook`;\n\n return {\n command,\n port: options.port,\n cwd: dir,\n reuseExistingServer: !process.env.CI,\n cacheFile: join(dir, \"last-port.json\"),\n timeout: 180 * 1000,\n };\n}\n"],"names":["MANTINE_CORE_RANGE","STORYBOOK_RANGE","REACT_RANGE","DEFAULT_ADDON_DEPENDENCIES","WORKAROUND_DEPENDENCIES","harnessPackageJson","options","MAIN_TS","PREVIEW_TSX","scaffoldMantineSourceOfTruthHarness","dir","mantineAdapterVersion","storybookTemplateVersion","mkdirSync","join","writeFileSync","mantineSourceOfTruthWebServer"],"mappings":";;AAoDA,MAAMA,IAAqB,UACrBC,IAAkB,WAClBC,IAAc,WAOdC,IAA6B;AAAA,EACjC,yBAAyBF;AAAA,EACzB,yBAAyBA;AAAA,EACzB,uBAAuB;AACzB,GAOMG,IAA0B;AAAA,EAC9B,kBAAkB;AACpB;AAEA,SAASC,EAAmBC,GAGzB;AACD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA;AAAA;AAAA;AAAA,MAIP,WAAW;AAAA,IAAA;AAAA,IAEb,cAAc;AAAA,MACZ,iCAAiCA,EAAQ;AAAA,MACzC,iCAAiCA,EAAQ;AAAA,MACzC,+BAA+B;AAAA,MAC/B,6BAA6B;AAAA,MAC7B,iBAAiBN;AAAA,MACjB,kBAAkBA;AAAA,MAClB,OAAOE;AAAA,MACP,aAAaA;AAAA,MACb,WAAWD;AAAA,MACX,yBAAyBA;AAAA,MACzB,GAAGE;AAAA,MACH,GAAGC;AAAA,IAAA;AAAA,EACL;AAEJ;AAEA,MAAMG,IAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAqBVC,IAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuDb,SAASC,EACdH,GACQ;AACR,QAAM;AAAA,IACJ,KAAAI;AAAA,IACA,uBAAAC,IAAwB;AAAA,IACxB,0BAAAC,IAA2B;AAAA,EAAA,IACzBN;AAEJ,SAAAO,EAAUC,EAAKJ,GAAK,YAAY,GAAG,EAAE,WAAW,IAAM,GACtDK;AAAA,IACED,EAAKJ,GAAK,cAAc;AAAA,IACxB,KAAK;AAAA,MACHL,EAAmB;AAAA,QACjB,uBAAAM;AAAA,QACA,0BAAAC;AAAA,MAAA,CACD;AAAA,MACD;AAAA,MACA;AAAA,IAAA,IACE;AAAA;AAAA,EAAA,GAENG,EAAcD,EAAKJ,GAAK,oBAAoB,GAAGH,CAAO,GACtDQ,EAAcD,EAAKJ,GAAK,wBAAwB,GAAGF,CAAW,GAC9DO,EAAcD,EAAKJ,GAAK,YAAY,GAAG;AAAA,CAAgB,GAEhDA;AACT;AAMO,SAASM,EACdV,GACwB;AACxB,QAAMI,IAAMD,EAAoCH,CAAO,GACjD;AAAA,IACJ,uBAAAK,IAAwB;AAAA,IACxB,0BAAAC,IAA2B;AAAA,EAAA,IACzBN;AAYJ,SAAO;AAAA,IACL,SAHc,6CAA6CK,CAAqB,kCAAkCC,CAAwB;AAAA,IAI1I,MAAMN,EAAQ;AAAA,IACd,KAAKI;AAAA,IACL,qBAAqB,CAAC,QAAQ,IAAI;AAAA,IAClC,WAAWI,EAAKJ,GAAK,gBAAgB;AAAA,IACrC,SAAS,MAAM;AAAA,EAAA;AAEnB;"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"use strict";const o=require("node:fs"),t=require("node:path"),n="^8.0.0",s="^10.3.3",c="^19.0.0",l={"@storybook/addon-docs":s,"@storybook/addon-a11y":s,"storybook-dark-mode":"^5.0.0"},p={"react-markdown":"^10.1.0"};function u(r){return{name:"adapter-tester-mantine-source-of-truth-harness",private:!0,type:"module",scripts:{storybook:"storybook dev"},dependencies:{"@recursica/mantine-
|
|
1
|
+
"use strict";const o=require("node:fs"),t=require("node:path"),n="^8.0.0",s="^10.3.3",c="^19.0.0",l={"@storybook/addon-docs":s,"@storybook/addon-a11y":s,"storybook-dark-mode":"^5.0.0"},p={"react-markdown":"^10.1.0"};function u(r){return{name:"adapter-tester-mantine-source-of-truth-harness",private:!0,type:"module",scripts:{storybook:"storybook dev"},dependencies:{"@recursica/adapter-mantine-v8":r.mantineAdapterVersion,"@recursica/storybook-template":r.storybookTemplateVersion,"@recursica/official-release":"latest","@recursica/adapter-common":"latest","@mantine/core":n,"@mantine/dates":n,react:c,"react-dom":c,storybook:s,"@storybook/react-vite":s,...l,...p}}}const m=`import { createMainConfig } from "@recursica/storybook-template/main";
|
|
2
2
|
|
|
3
3
|
const config = createMainConfig({
|
|
4
4
|
stories: [
|
|
5
|
-
"../node_modules/@recursica/mantine-
|
|
5
|
+
"../node_modules/@recursica/adapter-mantine-v8/src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
|
6
6
|
],
|
|
7
7
|
enableCORS: true,
|
|
8
8
|
});
|
|
@@ -36,7 +36,7 @@ const basePreview = createPreviewConfig({
|
|
|
36
36
|
recursicaUIKitJsonPath: recursicaUIKit,
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
// Mirrors mantine-
|
|
39
|
+
// Mirrors adapter-mantine-v8's own .storybook/preview.tsx decorator (every story defaults to
|
|
40
40
|
// withLayer: true, layer: 0, wrapped with 48px padding) — every real adapter's own preview.tsx
|
|
41
41
|
// applies this same wrapping, so a target adapter's story renders inside the same Layer
|
|
42
42
|
// chrome/padding the source-of-truth side does. Without this, target screenshots come out
|
|
@@ -71,5 +71,5 @@ const preview: Preview = {
|
|
|
71
71
|
export default preview;
|
|
72
72
|
`;function d(r){const{dir:e,mantineAdapterVersion:a="latest",storybookTemplateVersion:i="latest"}=r;return o.mkdirSync(t.join(e,".storybook"),{recursive:!0}),o.writeFileSync(t.join(e,"package.json"),JSON.stringify(u({mantineAdapterVersion:a,storybookTemplateVersion:i}),null,2)+`
|
|
73
73
|
`),o.writeFileSync(t.join(e,".storybook/main.ts"),m),o.writeFileSync(t.join(e,".storybook/preview.tsx"),f),o.writeFileSync(t.join(e,".gitignore"),`node_modules
|
|
74
|
-
`),e}function y(r){const e=d(r),{mantineAdapterVersion:a="latest",storybookTemplateVersion:i="latest"}=r;return{command:`npm install @recursica/mantine-
|
|
75
|
-
//# sourceMappingURL=mantineSourceOfTruth-
|
|
74
|
+
`),e}function y(r){const e=d(r),{mantineAdapterVersion:a="latest",storybookTemplateVersion:i="latest"}=r;return{command:`npm install @recursica/adapter-mantine-v8@${a} @recursica/storybook-template@${i} --no-audit --no-fund && npm run storybook`,port:r.port,cwd:e,reuseExistingServer:!process.env.CI,cacheFile:t.join(e,"last-port.json"),timeout:180*1e3}}exports.mantineSourceOfTruthWebServer=y;exports.scaffoldMantineSourceOfTruthHarness=d;
|
|
75
|
+
//# sourceMappingURL=mantineSourceOfTruth-CdRlVrRC.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mantineSourceOfTruth-CdRlVrRC.cjs","sources":["../src/harness/mantineSourceOfTruth.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Generates a small, throwaway Storybook project that installs\n * `@recursica/adapter-mantine-v8` as a real npm dependency (not a workspace\n * link) and boots a real Storybook from its published `src/**\\/*.stories.tsx`\n * files, using `@recursica/storybook-template`'s exported factories.\n *\n * This lets any repo — including ones that never checked out the Recursica\n * monorepo — run adapter-tester's visual regression suite against Mantine\n * (Recursica's source-of-truth adapter) as one side of the comparison, while\n * the other side is that repo's own already-running local Storybook.\n *\n * See PROPOSAL-installed-package-harness.md for the verified prototype this\n * is built from, and the three upstream gaps it works around.\n */\n\nexport interface MantineSourceOfTruthHarnessOptions {\n /**\n * Directory the harness project is scaffolded into. Regenerated on every\n * call — add it to your .gitignore rather than committing it.\n */\n dir: string;\n /** First-guess port, shown for `--dry-run` visibility only. The harness's\n * Storybook is never pinned to this — see `HarnessWebServerConfig.port`. */\n port: number;\n /** npm version/range for @recursica/adapter-mantine-v8. Defaults to \"latest\". */\n mantineAdapterVersion?: string;\n /** npm version/range for @recursica/storybook-template. Defaults to \"latest\". */\n storybookTemplateVersion?: string;\n}\n\nexport interface HarnessWebServerConfig {\n command: string;\n /** First-guess port, shown for `--dry-run` visibility only — not\n * authoritative. The real port is whatever this Storybook's own startup\n * banner reports once it's actually running (see portDiscovery.ts), since\n * Storybook silently falls back to an OS-assigned port whenever this one\n * is taken. */\n port: number;\n cwd: string;\n reuseExistingServer: boolean;\n /** File the real, detected port is cached in between runs, so a later\n * `reuseExistingServer` run can find this instance again. */\n cacheFile: string;\n timeout: number;\n}\n\n// Peer/dev ranges pinned to what @recursica/mantine-adapter and\n// @recursica/storybook-template themselves require, so the harness can't\n// drift onto an incompatible Mantine or Storybook major version.\nconst MANTINE_CORE_RANGE = \"^8.0.0\";\nconst STORYBOOK_RANGE = \"^10.3.3\";\nconst REACT_RANGE = \"^19.0.0\";\n\n// storybook-template's createMainConfig() defaults its addons list to these\n// three but doesn't declare them as peerDependencies (proposal gap 2) — a\n// harness that skips installing any of them gets a silent \"could not\n// resolve addon\" warning at boot, then a hard runtime crash later when Vite\n// pre-bundles preview.tsx's dependency graph. Installed explicitly here.\nconst DEFAULT_ADDON_DEPENDENCIES = {\n \"@storybook/addon-docs\": STORYBOOK_RANGE,\n \"@storybook/addon-a11y\": STORYBOOK_RANGE,\n \"storybook-dark-mode\": \"^5.0.0\",\n};\n\n// adapter-mantine-v8's Introduction.stories.tsx (Version.tsx/OverStyling.tsx)\n// needs react-markdown, but it's a devDependency there — Storybook-only,\n// never bundled into dist — so an external `npm install` of the published\n// package won't pull it in. The harness boots a real Storybook against\n// src/, so it must provide this itself. Installed explicitly here.\nconst WORKAROUND_DEPENDENCIES = {\n \"react-markdown\": \"^10.1.0\",\n};\n\nfunction harnessPackageJson(options: {\n mantineAdapterVersion: string;\n storybookTemplateVersion: string;\n}) {\n return {\n name: \"adapter-tester-mantine-source-of-truth-harness\",\n private: true,\n type: \"module\",\n scripts: {\n // No `-p` pin — Storybook silently falls back to an OS-assigned port\n // whenever its default is taken, so the caller detects the real port\n // from this process's own output rather than trusting a fixed one.\n storybook: `storybook dev`,\n },\n dependencies: {\n \"@recursica/adapter-mantine-v8\": options.mantineAdapterVersion,\n \"@recursica/storybook-template\": options.storybookTemplateVersion,\n \"@recursica/official-release\": \"latest\",\n \"@recursica/adapter-common\": \"latest\",\n \"@mantine/core\": MANTINE_CORE_RANGE,\n \"@mantine/dates\": MANTINE_CORE_RANGE,\n react: REACT_RANGE,\n \"react-dom\": REACT_RANGE,\n storybook: STORYBOOK_RANGE,\n \"@storybook/react-vite\": STORYBOOK_RANGE,\n ...DEFAULT_ADDON_DEPENDENCIES,\n ...WORKAROUND_DEPENDENCIES,\n },\n };\n}\n\nconst MAIN_TS = `import { createMainConfig } from \"@recursica/storybook-template/main\";\n\nconst config = createMainConfig({\n stories: [\n \"../node_modules/@recursica/adapter-mantine-v8/src/**/*.stories.@(js|jsx|mjs|ts|tsx)\",\n ],\n enableCORS: true,\n});\n\n// react-docgen-typescript can't resolve a TS project for a config file living\n// in .storybook/ when the component source it's docgen'ing lives three\n// directories down inside node_modules — it throws \"Cannot read properties\n// of undefined (reading 'fileExists')\", which surfaces as a plain 404 on\n// preview.tsx. Docgen only powers Storybook's Controls/Docs tables, which\n// this harness never renders, so disabling it is a safe workaround (see\n// PROPOSAL-installed-package-harness.md, gap 3).\nconfig.typescript = { ...config.typescript, reactDocgen: false };\n\nexport default config;\n`;\n\nconst PREVIEW_TSX = `import type { Preview } from \"@storybook/react-vite\";\nimport { createPreviewConfig } from \"@recursica/storybook-template/preview\";\nimport { MantineProvider } from \"@mantine/core\";\nimport { Layer } from \"@recursica/adapter-common\";\nimport \"@mantine/core/styles.css\";\nimport \"@mantine/dates/styles.css\";\nimport \"@recursica/adapter-common/style.css\";\nimport \"@recursica/official-release/recursica_variables_scoped.css\";\nimport recursicaTokens from \"@recursica/official-release/recursica_tokens.json\";\nimport recursicaBrand from \"@recursica/official-release/recursica_brand.json\";\nimport recursicaUIKit from \"@recursica/official-release/recursica_ui-kit.json\";\n\nconst basePreview = createPreviewConfig({\n defaultTheme: \"light\",\n recursicaTokensJsonPath: recursicaTokens,\n recursicaBrandJsonPath: recursicaBrand,\n recursicaUIKitJsonPath: recursicaUIKit,\n});\n\n// Mirrors adapter-mantine-v8's own .storybook/preview.tsx decorator (every story defaults to\n// withLayer: true, layer: 0, wrapped with 48px padding) — every real adapter's own preview.tsx\n// applies this same wrapping, so a target adapter's story renders inside the same Layer\n// chrome/padding the source-of-truth side does. Without this, target screenshots come out\n// dramatically smaller/differently-positioned than the source of truth's (no Layer padding,\n// background, or border-radius at all), which alone can blow past the pixel-diff threshold\n// regardless of whether the actual Recursica tokens match — a false positive, not a real\n// component bug. ColorSchemeWrapper (mantine-adapter's dark-mode-toggle sync helper) is\n// intentionally not replicated — it only matters for the interactive dev-mode UI, not automated\n// screenshot diffing, which always runs in a single theme.\nconst preview: Preview = {\n ...basePreview,\n decorators: [\n (Story, context) => {\n const { withLayer = true, layer = 0 } = context.args;\n const content = <Story />;\n return (\n <MantineProvider>\n {withLayer ? (\n <Layer layer={layer as 0 | 1 | 2 | 3} style={{ padding: \"48px\" }}>\n {content}\n </Layer>\n ) : (\n content\n )}\n </MantineProvider>\n );\n },\n ...(basePreview.decorators || []),\n ],\n};\n\nexport default preview;\n`;\n\n/** Writes the harness project's files to `options.dir` without booting it. */\nexport function scaffoldMantineSourceOfTruthHarness(\n options: MantineSourceOfTruthHarnessOptions,\n): string {\n const {\n dir,\n mantineAdapterVersion = \"latest\",\n storybookTemplateVersion = \"latest\",\n } = options;\n\n mkdirSync(join(dir, \".storybook\"), { recursive: true });\n writeFileSync(\n join(dir, \"package.json\"),\n JSON.stringify(\n harnessPackageJson({\n mantineAdapterVersion,\n storybookTemplateVersion,\n }),\n null,\n 2,\n ) + \"\\n\",\n );\n writeFileSync(join(dir, \".storybook/main.ts\"), MAIN_TS);\n writeFileSync(join(dir, \".storybook/preview.tsx\"), PREVIEW_TSX);\n writeFileSync(join(dir, \".gitignore\"), \"node_modules\\n\");\n\n return dir;\n}\n\n/**\n * Scaffolds the harness and returns a Playwright `webServer` entry for it.\n * Spread the result directly into `playwright.config.ts`'s `webServer` array.\n */\nexport function mantineSourceOfTruthWebServer(\n options: MantineSourceOfTruthHarnessOptions,\n): HarnessWebServerConfig {\n const dir = scaffoldMantineSourceOfTruthHarness(options);\n const {\n mantineAdapterVersion = \"latest\",\n storybookTemplateVersion = \"latest\",\n } = options;\n\n // A bare `npm install` is satisfied by a package-lock.json already sitting\n // in `dir` from a prior run and skips re-resolving against the registry\n // entirely — no network call — so a run can silently keep testing against\n // a stale @recursica/adapter-mantine-v8/storybook-template even after a newer\n // version is published. Naming the two version-pinned packages as explicit\n // `pkg@specifier` CLI args instead forces npm to re-check just those two\n // against the registry every run, while the rest of node_modules stays\n // cached.\n const command = `npm install @recursica/adapter-mantine-v8@${mantineAdapterVersion} @recursica/storybook-template@${storybookTemplateVersion} --no-audit --no-fund && npm run storybook`;\n\n return {\n command,\n port: options.port,\n cwd: dir,\n reuseExistingServer: !process.env.CI,\n cacheFile: join(dir, \"last-port.json\"),\n timeout: 180 * 1000,\n };\n}\n"],"names":["MANTINE_CORE_RANGE","STORYBOOK_RANGE","REACT_RANGE","DEFAULT_ADDON_DEPENDENCIES","WORKAROUND_DEPENDENCIES","harnessPackageJson","options","MAIN_TS","PREVIEW_TSX","scaffoldMantineSourceOfTruthHarness","dir","mantineAdapterVersion","storybookTemplateVersion","mkdirSync","join","writeFileSync","mantineSourceOfTruthWebServer"],"mappings":"+DAoDMA,EAAqB,SACrBC,EAAkB,UAClBC,EAAc,UAOdC,EAA6B,CACjC,wBAAyBF,EACzB,wBAAyBA,EACzB,sBAAuB,QACzB,EAOMG,EAA0B,CAC9B,iBAAkB,SACpB,EAEA,SAASC,EAAmBC,EAGzB,CACD,MAAO,CACL,KAAM,iDACN,QAAS,GACT,KAAM,SACN,QAAS,CAIP,UAAW,eAAA,EAEb,aAAc,CACZ,gCAAiCA,EAAQ,sBACzC,gCAAiCA,EAAQ,yBACzC,8BAA+B,SAC/B,4BAA6B,SAC7B,gBAAiBN,EACjB,iBAAkBA,EAClB,MAAOE,EACP,YAAaA,EACb,UAAWD,EACX,wBAAyBA,EACzB,GAAGE,EACH,GAAGC,CAAA,CACL,CAEJ,CAEA,MAAMG,EAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBVC,EAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuDb,SAASC,EACdH,EACQ,CACR,KAAM,CACJ,IAAAI,EACA,sBAAAC,EAAwB,SACxB,yBAAAC,EAA2B,QAAA,EACzBN,EAEJO,OAAAA,EAAAA,UAAUC,EAAAA,KAAKJ,EAAK,YAAY,EAAG,CAAE,UAAW,GAAM,EACtDK,EAAAA,cACED,EAAAA,KAAKJ,EAAK,cAAc,EACxB,KAAK,UACHL,EAAmB,CACjB,sBAAAM,EACA,yBAAAC,CAAA,CACD,EACD,KACA,CAAA,EACE;AAAA,CAAA,EAENG,EAAAA,cAAcD,EAAAA,KAAKJ,EAAK,oBAAoB,EAAGH,CAAO,EACtDQ,EAAAA,cAAcD,EAAAA,KAAKJ,EAAK,wBAAwB,EAAGF,CAAW,EAC9DO,EAAAA,cAAcD,EAAAA,KAAKJ,EAAK,YAAY,EAAG;AAAA,CAAgB,EAEhDA,CACT,CAMO,SAASM,EACdV,EACwB,CACxB,MAAMI,EAAMD,EAAoCH,CAAO,EACjD,CACJ,sBAAAK,EAAwB,SACxB,yBAAAC,EAA2B,QAAA,EACzBN,EAYJ,MAAO,CACL,QAHc,6CAA6CK,CAAqB,kCAAkCC,CAAwB,6CAI1I,KAAMN,EAAQ,KACd,IAAKI,EACL,oBAAqB,CAAC,QAAQ,IAAI,GAClC,UAAWI,EAAAA,KAAKJ,EAAK,gBAAgB,EACrC,QAAS,IAAM,GAAA,CAEnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runVisualRegression.d.ts","sourceRoot":"","sources":["../../src/testing/runVisualRegression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAcxD,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAmED;;;;wEAIwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B;;;+EAG2E;IAC3E,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC;;;;uDAImD;IACnD,UAAU,EAAE,CACV,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,KACf,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED;;;;wEAIwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B;;;;uDAImD;IACnD,UAAU,EAAE,CACV,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,KACf,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"runVisualRegression.d.ts","sourceRoot":"","sources":["../../src/testing/runVisualRegression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAcxD,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAmED;;;;wEAIwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B;;;+EAG2E;IAC3E,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC;;;;uDAImD;IACnD,UAAU,EAAE,CACV,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,KACf,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED;;;;wEAIwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B;;;;uDAImD;IACnD,UAAU,EAAE,CACV,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,KACf,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC,CA6R/B"}
|
package/dist/testing.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const L=require("@playwright/test"),i=require("node:fs"),X=require("pixelmatch"),P=require("pngjs"),E=require("node:path"),R=require("./index-C6uYPRmx.cjs");function B(e,t){const n=P.PNG.sync.read(e),r=P.PNG.sync.read(t);if(n.width!==r.width||n.height!==r.height)return{diffPixels:1/0,diffImage:null};const s=new P.PNG({width:n.width,height:n.height});return{diffPixels:X(n.data,r.data,s.data,n.width,n.height,{threshold:.1}),diffImage:P.PNG.sync.write(s)}}const z="http://json-schema.org/draft-07/schema#",H="https://github.com/borderux/recursica/tree/main/packages/adapter-tester/src/golden/manifest.schema.json",Y="test/golden/manifest.json",Q="Tracks golden (baseline) image metadata for @recursica/adapter-tester's golden-image visual regression checks. One manifest lives alongside its adapter's test/golden/<story-id>.png files, keyed by story id.",Z="object",ee={type:"object",additionalProperties:!1,required:["createdAt"],properties:{createdAt:{type:"string",format:"date-time",description:"When this story's own golden PNG was last captured, via --update-golden, --approve-divergence, or first-run auto-create."},sourceOfTruthCreatedAt:{type:"string",format:"date-time",description:"The source-of-truth adapter's (mantine) manifest `createdAt` for this story at the time this adapter's divergence from it was last reviewed via --approve-divergence. Omitted on the source-of-truth adapter's own manifest, and omitted here until the first approval. If mantine's current `createdAt` for this story is newer than this value, the divergence is flagged again for re-review."}}},te={$schema:z,$id:H,title:Y,description:Q,type:Z,additionalProperties:ee},re=R.index,D=new R.ajvExports.Ajv({allErrors:!0,strict:!0});re(D);const _=D.compile(te);function I(e,t){if(_(e))return;const n=(_.errors??[]).map(r=>{var a;const s=(a=r.params)!=null&&a.additionalProperty?` '${r.params.additionalProperty}'`:"";return` - ${r.instancePath||"root"} ${r.message}${s}`}).join(`
|
|
2
2
|
`);throw new Error(`Invalid ${t}:
|
|
3
|
-
${n}`)}function
|
|
4
|
-
`),i.renameSync(s,n)}const oe=25,se=15e3;function ie(e){return new Promise(t=>setTimeout(t,e))}async function ae(e){i.mkdirSync(e,{recursive:!0});const t=J(e),n=Date.now()+se;for(;;)try{i.closeSync(i.openSync(t,"wx"));return}catch(r){if(r.code!=="EEXIST")throw r;if(Date.now()>=n)throw new Error(`Timed out waiting for the manifest lock at "${t}" — delete it if a previous run crashed while holding it.`);await ie(oe)}}function ce(e){i.rmSync(J(e),{force:!0})}async function U(e,t,n){await ae(e);try{const r=b(e),s=n(r[t]);return s===void 0?delete r[t]:r[t]=s,ne(e,r),s}finally{ce(e)}}function de(e,t,n){i.mkdirSync(e,{recursive:!0}),i.writeFileSync(x(e,t),n)}async function ue(e,t){const n=b(e),r=Object.keys(n).filter(s=>!t.has(s));for(const s of r){const a=x(e,s);i.existsSync(a)&&i.unlinkSync(a),await U(e,s,()=>{})}return r}const he="borderux/recursica";async function le(e,t){var a,u;const n=await fetch(`https://registry.npmjs.org/${e}`);if(!n.ok)throw new Error(`Could not reach npm registry for ${e}: ${n.statusText}`);const r=await n.json(),s=((a=r["dist-tags"])==null?void 0:a[t])??((u=r.versions)!=null&&u[t]?t:void 0);if(!s)throw new Error(`${e} has no version or dist-tag "${t}" on the npm registry.`);return s}
|
|
5
|
-
`));const V=d==="divergence"?"Source-of-Truth Divergence Check":"Own-Drift Golden Image Check";return{ownTargetName:t.name,suiteLabel:V,stories:G,missingFromSourceOfTruth:
|
|
3
|
+
${n}`)}function j(e){return E.join(e,"manifest.json")}function J(e){return E.join(e,"manifest.json.lock")}function x(e,t){return E.join(e,`${t}.png`)}function b(e){const t=j(e);if(!i.existsSync(t))return{};const n=JSON.parse(i.readFileSync(t,"utf8"));return I(n,t),n}function ne(e,t){const n=j(e);I(t,n);const r={};for(const a of Object.keys(t).sort())r[a]=t[a];i.mkdirSync(e,{recursive:!0});const s=`${n}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;i.writeFileSync(s,JSON.stringify(r,null,2)+`
|
|
4
|
+
`),i.renameSync(s,n)}const oe=25,se=15e3;function ie(e){return new Promise(t=>setTimeout(t,e))}async function ae(e){i.mkdirSync(e,{recursive:!0});const t=J(e),n=Date.now()+se;for(;;)try{i.closeSync(i.openSync(t,"wx"));return}catch(r){if(r.code!=="EEXIST")throw r;if(Date.now()>=n)throw new Error(`Timed out waiting for the manifest lock at "${t}" — delete it if a previous run crashed while holding it.`);await ie(oe)}}function ce(e){i.rmSync(J(e),{force:!0})}async function U(e,t,n){await ae(e);try{const r=b(e),s=n(r[t]);return s===void 0?delete r[t]:r[t]=s,ne(e,r),s}finally{ce(e)}}function de(e,t,n){i.mkdirSync(e,{recursive:!0}),i.writeFileSync(x(e,t),n)}async function ue(e,t){const n=b(e),r=Object.keys(n).filter(s=>!t.has(s));for(const s of r){const a=x(e,s);i.existsSync(a)&&i.unlinkSync(a),await U(e,s,()=>{})}return r}const he="borderux/recursica-adapter-mantine-v8";async function le(e,t){var a,u;const n=await fetch(`https://registry.npmjs.org/${e}`);if(!n.ok)throw new Error(`Could not reach npm registry for ${e}: ${n.statusText}`);const r=await n.json(),s=((a=r["dist-tags"])==null?void 0:a[t])??((u=r.versions)!=null&&u[t]?t:void 0);if(!s)throw new Error(`${e} has no version or dist-tag "${t}" on the npm registry.`);return s}async function fe(e){if(e.type==="local")return i.existsSync(j(e.dir))?{manifest:b(e.dir),async readImage(h){const d=x(e.dir,h);return i.existsSync(d)?i.readFileSync(d):null}}:(console.warn(`No golden baseline found yet at ${e.dir} — source-of-truth divergence check skipped for this run.`),null);let t;try{t=await le(e.packageName,e.versionSpec)}catch(c){return console.warn(`Could not resolve ${e.packageName}@${e.versionSpec} — source-of-truth divergence check skipped for this run.`,c),null}const n=E.join(e.cacheDir,t),r=`${e.packageName}@${t}`,s=`https://raw.githubusercontent.com/${he}/${r}/test/golden`;let a;const u=j(n);if(i.existsSync(u))a=b(n);else{let c;try{c=await fetch(`${s}/manifest.json`)}catch(m){return console.warn(`Could not reach GitHub to fetch ${r}'s golden baseline — source-of-truth divergence check skipped for this run.`,m),null}if(!c.ok)return console.warn(`No golden baseline published for ${r} — source-of-truth divergence check skipped for this run.`),null;const h=await c.text(),d=JSON.parse(h);I(d,`${s}/manifest.json`),i.mkdirSync(n,{recursive:!0}),i.writeFileSync(u,h),a=d}return{manifest:a,async readImage(c){const h=x(n,c);if(i.existsSync(h))return i.readFileSync(h);const d=await fetch(`${s}/${c}.png`);if(!d.ok)return null;const m=Buffer.from(await d.arrayBuffer());return i.mkdirSync(n,{recursive:!0}),i.writeFileSync(h,m),m}}}const pe=["Theme","Tokens","Introduction"];function M(e,t){return e===t||e.startsWith(t)}async function ge(e,t){let n;try{const r=await fetch(`${e.url}/index.json`);if(!r.ok)throw new Error(`Failed to fetch Storybook index: ${r.statusText}`);const a=(await r.json()).entries||{};n=Object.values(a).filter(u=>u.type==="story"&&!t.some(c=>u.title===c||u.title.startsWith(`${c}/`))),n.sort((u,c)=>u.id.localeCompare(c.id))}catch(r){throw console.error("Failed to load Storybook index from",`${e.url}/index.json`,r),new Error(`Storybook target "${e.name}" is not responsive or index.json is missing. Please ensure its Storybook is running.`)}return n}function q(e,t,n){let r;for(const s of Object.keys(t))M(e,s)&&(!r||s.length>r.length)&&(r=s);return r!==void 0?t[r]:n}async function me(e){const t=e.isSourceOfTruthAdapter?e.targets[0]:e.targets.find(o=>!o.sourceOfTruth);if(!t)throw new Error("adapter-tester config has no non-sourceOfTruth target to run the golden check against.");const n=e.excludeTitlePrefixes??pe,r=e.stories??{},s=Object.keys(r).filter(o=>r[o].exclude),a=Object.fromEntries(Object.entries(r).filter(([,o])=>o.goldenThreshold!==void 0).map(([o,l])=>[o,l.goldenThreshold])),u=Object.fromEntries(Object.entries(r).filter(([,o])=>o.sourceOfTruthThreshold!==void 0).map(([o,l])=>[o,l.sourceOfTruthThreshold])),c=e.goldenDir,h=e.goldenMode,d=e.checkMode,m=await ge(t,n),G=m.filter(o=>!s.some(l=>M(o.id,l)));if(h==="update-golden"){const o=await ue(c,new Set(G.map(l=>l.id)));o.length>0&&console.warn(`Pruned ${o.length} orphaned golden(s) no longer in Storybook: ${o.join(", ")}`)}const w=d!=="divergence"||e.isSourceOfTruthAdapter||!e.sourceOfTruthGolden?null:await fe(e.sourceOfTruthGolden),K=new Set(m.map(o=>o.id)),T=w?Object.keys(w.manifest).filter(o=>!K.has(o)&&!s.some(l=>M(o,l))).sort():[];T.length>0&&console.error(`[adapter-tester] ${T.length} stor(y/ies) exist in the source of truth but are missing here: ${T.join(", ")}. Add the missing story, or mark it \`exclude: true\` under \`stories\` in adapter-tester.config.json if intentional.`),console.log([`[adapter-tester] target: "${t.name}" (${t.url})`,`[adapter-tester] checkMode: "${d}" (${d==="divergence"?"live render vs source-of-truth's golden":"live render vs this project's own golden"})`,`[adapter-tester] goldenMode: "${h}"`,`[adapter-tester] goldenThresholdPixels: ${e.goldenThresholdPixels}`,`[adapter-tester] sourceOfTruthThresholdPixels: ${e.sourceOfTruthThresholdPixels}`,d==="divergence"?e.isSourceOfTruthAdapter?"[adapter-tester] sourceOfTruthGolden: skipped — this is the source-of-truth adapter, nothing to diverge from":e.sourceOfTruthGolden?w?`[adapter-tester] sourceOfTruthGolden: resolved, ${Object.keys(w.manifest).length} golden(s) available (config: ${JSON.stringify(e.sourceOfTruthGolden)})`:`[adapter-tester] sourceOfTruthGolden: unavailable — no baseline found or unreachable (config: ${JSON.stringify(e.sourceOfTruthGolden)}); divergence check will skip every story`:"[adapter-tester] sourceOfTruthGolden: skipped — no sourceOfTruthGolden configured":'[adapter-tester] sourceOfTruthGolden: not used in "own" checkMode',`[adapter-tester] stories: ${G.length} to check (excluded: ${s.length}, title prefixes excluded: ${n.join(", ")||"none"})`,`[adapter-tester] story parity with source of truth: ${T.length===0?"OK":`${T.length} missing (see error above)`}`].join(`
|
|
5
|
+
`));const V=d==="divergence"?"Source-of-Truth Divergence Check":"Own-Drift Golden Image Check";return{ownTargetName:t.name,suiteLabel:V,stories:G,missingFromSourceOfTruth:T,checkStory:async(o,l,g)=>{const y=await l.newPage();await y.setViewportSize({width:800,height:600}),await y.goto(`${t.url}/iframe.html?id=${o.id}&viewMode=story`,{waitUntil:"networkidle"}),await y.waitForSelector("#storybook-root"),await y.addStyleTag({content:"* { -webkit-font-smoothing: antialiased !important; -moz-osx-font-smoothing: grayscale !important; }"}),await y.evaluate(()=>Promise.all(Array.from(document.querySelectorAll('link[rel="stylesheet"]')).map(f=>f.sheet?Promise.resolve():new Promise(p=>{f.addEventListener("load",()=>p(),{once:!0}),f.addEventListener("error",()=>p(),{once:!0})}))).then(()=>document.fonts.ready)),await y.waitForTimeout(300);const k=await y.screenshot(),N=x(c,o.id),v=b(c)[o.id],W=h!=="check"||!v||!i.existsSync(N);let $;if(W)de(c,o.id,k),$=v!=null&&v.sourceOfTruthCreatedAt?{createdAt:new Date().toISOString(),sourceOfTruthCreatedAt:v.sourceOfTruthCreatedAt}:{createdAt:new Date().toISOString()},v||g.annotations.push({type:"golden-created",description:`No golden existed yet for "${o.id}" — captured one from this run.`});else if($=v,d==="own"){const f=i.readFileSync(N),{diffPixels:p,diffImage:S}=B(k,f),O=q(o.id,a,e.goldenThresholdPixels);p>=O&&(await g.attach("expected",{body:f,contentType:"image/png"}),await g.attach("actual",{body:k,contentType:"image/png"}),S&&await g.attach("diff",{body:S,contentType:"image/png"})),L.expect.soft(p,`"${o.id}" has drifted from its own golden image (${p} mismatched pixels, threshold ${O})`).toBeLessThan(O)}if(w){const f=w.manifest[o.id],p=f?await w.readImage(o.id):null;if(f&&p)if(h==="approve-divergence")$={...$,sourceOfTruthCreatedAt:f.createdAt};else{const{diffPixels:S,diffImage:O}=B(k,p),A=q(o.id,u,e.sourceOfTruthThresholdPixels),F=$.sourceOfTruthCreatedAt,C=S<A||F!==void 0&&F>=f.createdAt;C||(await g.attach("expected",{body:p,contentType:"image/png"}),await g.attach("actual",{body:k,contentType:"image/png"}),O&&await g.attach("diff",{body:O,contentType:"image/png"}),g.annotations.push({type:"source-of-truth-divergence",description:`"${o.id}" differs from the source of truth's golden by ${S} mismatched pixels (threshold ${A}) and hasn't been reviewed — run with --approve-divergence if this is intentional, or fix the adapter styling.`})),L.expect.soft(C,`"${o.id}" differs from the source of truth's golden by ${S} mismatched pixels (threshold ${A}) and hasn't been reviewed — run with --approve-divergence if this is intentional, or fix the adapter styling.`).toBe(!0)}}await U(c,o.id,()=>$)}}}exports.resolveVisualRegressionPlan=me;
|
|
6
6
|
//# sourceMappingURL=testing.cjs.map
|