@lovo/matter-cli 0.4.1 → 0.6.0

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +68 -4
  3. package/dist/add-T7IRU2NK.js +11 -0
  4. package/dist/{chunk-4XUN2PKU.js → chunk-53DI7V2J.js} +22 -21
  5. package/dist/chunk-53DI7V2J.js.map +1 -0
  6. package/dist/chunk-D4HDZEJT.js +37 -0
  7. package/dist/chunk-D4HDZEJT.js.map +1 -0
  8. package/dist/{chunk-OJWKSIZ5.js → chunk-FHRQIHCN.js} +32 -25
  9. package/dist/chunk-FHRQIHCN.js.map +1 -0
  10. package/dist/chunk-Q7CQW6AH.js +27 -0
  11. package/dist/chunk-Q7CQW6AH.js.map +1 -0
  12. package/dist/{chunk-M6S6HYHE.js → chunk-R7OX6KUP.js} +48 -44
  13. package/dist/chunk-R7OX6KUP.js.map +1 -0
  14. package/dist/chunk-RRL35RGP.js +104 -0
  15. package/dist/chunk-RRL35RGP.js.map +1 -0
  16. package/dist/chunk-TBB7CTPP.js +77 -0
  17. package/dist/chunk-TBB7CTPP.js.map +1 -0
  18. package/dist/chunk-YADIAD35.js +16322 -0
  19. package/dist/chunk-YADIAD35.js.map +1 -0
  20. package/dist/chunk-YAWX2IU3.js +46 -0
  21. package/dist/chunk-YAWX2IU3.js.map +1 -0
  22. package/dist/chunk-YSFTOGZX.js +119 -0
  23. package/dist/chunk-YSFTOGZX.js.map +1 -0
  24. package/dist/commands/poster.d.ts +19 -0
  25. package/dist/commands/poster.js +15 -0
  26. package/dist/commands/poster.js.map +1 -0
  27. package/dist/dist-I76TGDBX.js +547 -0
  28. package/dist/dist-I76TGDBX.js.map +1 -0
  29. package/dist/harness/frameReady.ts +35 -0
  30. package/dist/harness/index.html +20 -0
  31. package/dist/harness/index.tsx +41 -0
  32. package/dist/index.js +50 -21
  33. package/dist/index.js.map +1 -1
  34. package/dist/{init-W43YVGBI.js → init-NB5EOU5H.js} +3 -2
  35. package/dist/init-NB5EOU5H.js.map +1 -0
  36. package/dist/{list-S626E7NZ.js → list-L725RQM3.js} +6 -5
  37. package/dist/list-L725RQM3.js.map +1 -0
  38. package/dist/magic-string.es-DKS3S7WW.js +1310 -0
  39. package/dist/magic-string.es-DKS3S7WW.js.map +1 -0
  40. package/dist/poster/bundle.d.ts +12 -0
  41. package/dist/poster/bundle.js +9 -0
  42. package/dist/poster/bundle.js.map +1 -0
  43. package/dist/poster/bundle.test.d.ts +2 -0
  44. package/dist/poster/bundle.test.js +49 -0
  45. package/dist/poster/bundle.test.js.map +1 -0
  46. package/dist/poster/e2e.test.d.ts +2 -0
  47. package/dist/poster/e2e.test.js +103 -0
  48. package/dist/poster/e2e.test.js.map +1 -0
  49. package/dist/poster/playwright.d.ts +19 -0
  50. package/dist/poster/playwright.js +11 -0
  51. package/dist/poster/playwright.js.map +1 -0
  52. package/dist/poster/playwright.test.d.ts +2 -0
  53. package/dist/poster/playwright.test.js +28 -0
  54. package/dist/poster/playwright.test.js.map +1 -0
  55. package/dist/poster/projectRoot.d.ts +3 -0
  56. package/dist/poster/projectRoot.js +9 -0
  57. package/dist/poster/projectRoot.js.map +1 -0
  58. package/dist/poster/projectRoot.test.d.ts +2 -0
  59. package/dist/poster/projectRoot.test.js +47 -0
  60. package/dist/poster/projectRoot.test.js.map +1 -0
  61. package/dist/poster/server.d.ts +18 -0
  62. package/dist/poster/server.js +9 -0
  63. package/dist/poster/server.js.map +1 -0
  64. package/dist/poster/server.test.d.ts +2 -0
  65. package/dist/poster/server.test.js +54 -0
  66. package/dist/poster/server.test.js.map +1 -0
  67. package/dist/{update-3BHHOUCS.js → update-WK7CA42P.js} +26 -17
  68. package/dist/update-WK7CA42P.js.map +1 -0
  69. package/package.json +19 -3
  70. package/dist/add-ABC455QH.js +0 -10
  71. package/dist/chunk-4XUN2PKU.js.map +0 -1
  72. package/dist/chunk-M6S6HYHE.js.map +0 -1
  73. package/dist/chunk-OJWKSIZ5.js.map +0 -1
  74. package/dist/init-W43YVGBI.js.map +0 -1
  75. package/dist/list-S626E7NZ.js.map +0 -1
  76. package/dist/update-3BHHOUCS.js.map +0 -1
  77. /package/dist/{add-ABC455QH.js.map → add-T7IRU2NK.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @lovo/matter-cli
2
2
 
3
+ ## 0.6.0
4
+
5
+ ## 0.5.0
6
+
7
+ ### Minor Changes
8
+
9
+ - 0299ddb: Rename ambiguous CLI flags and the config key to spelled-out names (BREAKING, pre-1.0):
10
+
11
+ - `list`/`add`/`update`: `--ref` → `--reference`
12
+ - `poster`: `--from` → `--source`, `--out` → `--output`, `--type` → `--format`, `--export` → `--export-name`, `--time` → `--capture-delay`
13
+ - `matter.config.json`: removed the `tsx` boolean key (it was validated but never read by any command)
14
+
15
+ Kept: `--registry`, `--quality`, `--width`, `--height`, `--force`, and the config keys `componentsDir`, `registryUrl`, `aliases`.
16
+
17
+ Migration: update any scripts that pass the old flags. You can delete the `tsx` key from your `matter.config.json` if present — it is no longer used (unknown keys are ignored). Re-running `matter-cli init` regenerates a config without it.
18
+
3
19
  ## 0.4.1
4
20
 
5
21
  ## 0.4.0
package/README.md CHANGED
@@ -25,12 +25,11 @@ Writes `matter.config.json` to your project root with sensible defaults:
25
25
  {
26
26
  "componentsDir": "src/components/matter",
27
27
  "registryUrl": "https://raw.githubusercontent.com/lovo-hq/matter/${ref}/registry",
28
- "aliases": { "@/": "src/" },
29
- "tsx": true
28
+ "aliases": { "@/": "src/" }
30
29
  }
31
30
  ```
32
31
 
33
- The `${ref}` placeholder is auto-substituted with the CLI's published version tag (e.g., `v0.1.0`), so you get a stable snapshot. Override with `--ref <tag|branch|sha>` if you want to track `main` or a specific commit.
32
+ The `${ref}` placeholder is auto-substituted with the CLI's published version tag (e.g., `v0.1.0`), so you get a stable snapshot. Override with `--reference <tag|branch|sha>` if you want to track `main` or a specific commit.
34
33
 
35
34
  ### List available components
36
35
 
@@ -58,9 +57,74 @@ npx matter-cli update linear-gradient
58
57
  npx matter-cli update --force
59
58
  ```
60
59
 
60
+ ### Render a static fallback image
61
+
62
+ Render a Matter component tree to an image for use as a `<ShaderScene fallback>` — eliminates the visible blank canvas during WebGPU initialization.
63
+
64
+ ```bash
65
+ npx matter-cli poster --source <file> --output <path> [options]
66
+ ```
67
+
68
+ | Flag | Default | Description |
69
+ | ------------------ | ---------- | ---------------------------------------------------------------------------------------------------- |
70
+ | `--source <file>` | (required) | Path to a `.tsx`/`.ts` file whose chosen export renders the full tree (must include `<ShaderScene>`) |
71
+ | `--output <path>` | (required) | Where to write the image. Extension optional — `--format` decides. Parent directories auto-created. |
72
+ | `--format <format>` | `jpg` | Output format: `png` or `jpg`. Default is `jpg` — best size/quality for most shaders. |
73
+ | `--quality <n>` | `80` | JPEG quality 1–100. Ignored for PNG. |
74
+ | `--export-name <name>` | `default` | Named export to render. |
75
+ | `--capture-delay <seconds>` | `0` | Wait this long after the first non-blank frame before snapshotting. |
76
+ | `--width <px>` | `1280` | Render width. |
77
+ | `--height <px>` | `720` | Render height. |
78
+
79
+ #### Which format should I pick?
80
+
81
+ The default (JPEG q80) handles most shaders well. PNG wins on shaders with large flat-color regions where its lossless palette compression beats JPEG's DCT. As a rule of thumb:
82
+
83
+ | Use PNG (`--format png`) for… | Use the default JPEG for… |
84
+ | --------------------------------- | ------------------------------------------ |
85
+ | `LinearGradient` with hard stops | `Aurora` and similar gradient-heavy scenes |
86
+ | `SimplexNoise` with contour bands | `MeshGradient` (smooth color flow) |
87
+ | Anything with < ~20 unique colors | `FilmGrain` (high-entropy noise) |
88
+
89
+ If unsure, run both — the difference can be 3–7× either direction.
90
+
91
+ **Requires Playwright** as a peer dependency:
92
+
93
+ ```bash
94
+ pnpm add -D playwright
95
+ pnpm exec playwright install chromium
96
+ ```
97
+
98
+ **Examples:**
99
+
100
+ ```bash
101
+ # Default — writes ./public/hero.jpg (JPEG q80)
102
+ npx matter-cli poster --from ./src/components/matter/hero.tsx --out ./public/hero
103
+
104
+ # Posterized shader — PNG compresses smaller
105
+ npx matter-cli poster --from ./gradient.tsx --out ./public/gradient --type png
106
+
107
+ # Higher quality JPEG
108
+ npx matter-cli poster --from ./aurora.tsx --out ./public/aurora --quality 92
109
+ ```
110
+
111
+ Wire it up:
112
+
113
+ ```tsx
114
+ <ShaderScene fallback={<img src="/hero.jpg" alt="" />}>
115
+ <LinearGradient ... />
116
+ </ShaderScene>
117
+ ```
118
+
119
+ **Limitations:**
120
+
121
+ - The component you point at must render the entire tree (including `<ShaderScene>`); the CLI doesn't wrap.
122
+ - Components that depend on app-context hooks (`useTheme`, `useRouter`, etc.) won't render in the headless harness. Extract a presentational child.
123
+ - WebP and AVIF are not supported (would require an extra dependency for marginal savings over JPEG).
124
+
61
125
  ## v1 components
62
126
 
63
- `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `noise-field`, `waves`.
127
+ `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `simplex-noise`, `waves`.
64
128
 
65
129
  Each component depends on `@lovo/matter` and `@lovo/matter-react`, which you install separately:
66
130
 
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runAdd
4
+ } from "./chunk-53DI7V2J.js";
5
+ import "./chunk-FHRQIHCN.js";
6
+ import "./chunk-R7OX6KUP.js";
7
+ import "./chunk-D4HDZEJT.js";
8
+ export {
9
+ runAdd
10
+ };
11
+ //# sourceMappingURL=add-T7IRU2NK.js.map
@@ -3,10 +3,11 @@ import {
3
3
  fetchComponentSource,
4
4
  fetchRegistry,
5
5
  resolveRef
6
- } from "./chunk-OJWKSIZ5.js";
6
+ } from "./chunk-FHRQIHCN.js";
7
7
  import {
8
- readMatterConfig
9
- } from "./chunk-M6S6HYHE.js";
8
+ readMatterConfig,
9
+ resolveRegistryUrl
10
+ } from "./chunk-R7OX6KUP.js";
10
11
 
11
12
  // src/commands/add.ts
12
13
  import { access, mkdir, writeFile } from "fs/promises";
@@ -32,34 +33,33 @@ async function runAdd(components, opts, io = { cwd: process.cwd(), log: console.
32
33
  if (components.length === 0) {
33
34
  throw new Error("add: at least one component name is required");
34
35
  }
35
- const cfg = await readMatterConfig(io.cwd);
36
- const baseUrl = opts.registry ?? cfg.registryUrl;
36
+ const matterConfig = await readMatterConfig(io.cwd);
37
37
  const ref = resolveRef(opts.ref, opts.cliVersion);
38
- const registryUrl = baseUrl.replace("${ref}", ref);
38
+ const registryUrl = resolveRegistryUrl(matterConfig, { registry: opts.registry, ref });
39
39
  const registry = await fetchRegistry(registryUrl);
40
40
  const resolved = components.map((slug) => resolveComponent(slug, registry, registryUrl));
41
41
  if (opts.force !== true) {
42
- for (const r of resolved) {
43
- const targetPath = join(io.cwd, cfg.componentsDir, r.entry.file);
42
+ for (const resolvedComponent of resolved) {
43
+ const targetPath = join(io.cwd, matterConfig.componentsDir, resolvedComponent.entry.file);
44
44
  if (await fileExists(targetPath)) {
45
45
  throw new Error(`${targetPath} already exists. Pass --force to overwrite.`);
46
46
  }
47
47
  }
48
48
  }
49
49
  const fetched = await Promise.all(
50
- resolved.map(async (r) => {
51
- const source = await fetchComponentSource(registryUrl, r.entry.file);
52
- return { ...r, source };
50
+ resolved.map(async (resolvedComponent) => {
51
+ const source = await fetchComponentSource(registryUrl, resolvedComponent.entry.file);
52
+ return { ...resolvedComponent, source };
53
53
  })
54
54
  );
55
55
  const allDeps = /* @__PURE__ */ new Set();
56
- for (const f of fetched) {
57
- const targetPath = join(io.cwd, cfg.componentsDir, f.entry.file);
58
- const rewritten = rewriteImports(f.source, cfg.aliases);
56
+ for (const fetchedComponent of fetched) {
57
+ const targetPath = join(io.cwd, matterConfig.componentsDir, fetchedComponent.entry.file);
58
+ const rewritten = rewriteImports(fetchedComponent.source, matterConfig.aliases);
59
59
  await mkdir(dirname(targetPath), { recursive: true });
60
60
  await writeFile(targetPath, rewritten, "utf-8");
61
61
  io.log(`Wrote ${targetPath}`);
62
- for (const dep of f.entry.dependencies) allDeps.add(dep);
62
+ for (const dep of fetchedComponent.entry.dependencies) allDeps.add(dep);
63
63
  }
64
64
  const sortedDeps = [...allDeps].sort();
65
65
  io.log("");
@@ -76,17 +76,18 @@ function resolveComponent(slug, registry, registryUrl) {
76
76
  }
77
77
  return { slug, entry };
78
78
  }
79
- async function fileExists(p) {
79
+ async function fileExists(filePath) {
80
80
  try {
81
- await access(p);
81
+ await access(filePath);
82
82
  return true;
83
- } catch (err) {
84
- if (err instanceof Error && "code" in err && err.code === "ENOENT") return false;
85
- throw err;
83
+ } catch (caughtError) {
84
+ if (caughtError instanceof Error && "code" in caughtError && caughtError.code === "ENOENT")
85
+ return false;
86
+ throw caughtError;
86
87
  }
87
88
  }
88
89
 
89
90
  export {
90
91
  runAdd
91
92
  };
92
- //# sourceMappingURL=chunk-4XUN2PKU.js.map
93
+ //# sourceMappingURL=chunk-53DI7V2J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/add.ts","../src/transforms/rewriteImports.ts"],"sourcesContent":["import { access, mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\nimport { readMatterConfig, resolveRegistryUrl } from '../config/matterConfig.js';\nimport {\n fetchComponentSource,\n fetchRegistry,\n type Registry,\n type RegistryEntry,\n} from '../registry/fetchRegistry.js';\nimport { resolveRef } from '../registry/ref.js';\nimport { rewriteImports } from '../transforms/rewriteImports.js';\n\nexport interface AddOptions {\n registry?: string;\n ref?: string;\n force?: boolean;\n cliVersion: string;\n}\n\nexport interface AddIO {\n cwd: string;\n log: (line: string) => void;\n}\n\nexport async function runAdd(\n components: string[],\n opts: AddOptions,\n io: AddIO = { cwd: process.cwd(), log: console.log },\n): Promise<void> {\n if (components.length === 0) {\n throw new Error('add: at least one component name is required');\n }\n\n const matterConfig = await readMatterConfig(io.cwd);\n const ref = resolveRef(opts.ref, opts.cliVersion);\n const registryUrl = resolveRegistryUrl(matterConfig, { registry: opts.registry, ref });\n const registry = await fetchRegistry(registryUrl);\n\n const resolved = components.map((slug) => resolveComponent(slug, registry, registryUrl));\n\n if (opts.force !== true) {\n for (const resolvedComponent of resolved) {\n const targetPath = join(io.cwd, matterConfig.componentsDir, resolvedComponent.entry.file);\n\n if (await fileExists(targetPath)) {\n throw new Error(`${targetPath} already exists. Pass --force to overwrite.`);\n }\n }\n }\n\n const fetched = await Promise.all(\n resolved.map(async (resolvedComponent) => {\n const source = await fetchComponentSource(registryUrl, resolvedComponent.entry.file);\n\n return { ...resolvedComponent, source };\n }),\n );\n\n const allDeps = new Set<string>();\n\n for (const fetchedComponent of fetched) {\n const targetPath = join(io.cwd, matterConfig.componentsDir, fetchedComponent.entry.file);\n const rewritten = rewriteImports(fetchedComponent.source, matterConfig.aliases);\n\n await mkdir(dirname(targetPath), { recursive: true });\n await writeFile(targetPath, rewritten, 'utf-8');\n io.log(`Wrote ${targetPath}`);\n for (const dep of fetchedComponent.entry.dependencies) allDeps.add(dep);\n }\n\n const sortedDeps = [...allDeps].sort();\n\n io.log('');\n io.log(`This component requires: ${sortedDeps.join(', ')}`);\n io.log('Install with your package manager, e.g.:');\n io.log(`npm install ${sortedDeps.join(' ')}`);\n}\n\nfunction resolveComponent(\n slug: string,\n registry: Registry,\n registryUrl: string,\n): { slug: string; entry: RegistryEntry } {\n const entry = registry.components[slug];\n\n if (!entry) {\n throw new Error(\n `Component \"${slug}\" not found in registry at ${registryUrl}. Run \\`matter-cli list\\` to see available components.`,\n );\n }\n\n return { slug, entry };\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n\n return true;\n } catch (caughtError) {\n if (caughtError instanceof Error && 'code' in caughtError && caughtError.code === 'ENOENT')\n return false;\n throw caughtError;\n }\n}\n","export function rewriteImports(source: string, aliases: Record<string, string>): string {\n const sortedAliases = Object.entries(aliases).sort(([a], [b]) => b.length - a.length);\n\n if (sortedAliases.length === 0) return source;\n\n const importRe = /(\\bfrom\\s+|\\bimport\\s*\\(\\s*)(['\"])([^'\"]+)\\2/g;\n\n return source.replace(importRe, (full, lead: string, quote: string, spec: string) => {\n for (const [key, value] of sortedAliases) {\n if (spec.startsWith(key)) {\n return `${lead}${quote}${value}${spec.slice(key.length)}${quote}`;\n }\n }\n\n return full;\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+
32
+ export {
33
+ __commonJS,
34
+ __export,
35
+ __toESM
36
+ };
37
+ //# sourceMappingURL=chunk-D4HDZEJT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -3,30 +3,37 @@
3
3
  // src/registry/readUrl.ts
4
4
  import { readFile } from "fs/promises";
5
5
  import { fileURLToPath } from "url";
6
+ async function readFileUrl(filePath) {
7
+ try {
8
+ return await readFile(filePath, "utf-8");
9
+ } catch (caughtError) {
10
+ if (caughtError instanceof Error && "code" in caughtError && caughtError.code === "ENOENT") {
11
+ throw new Error(`File not found: ${filePath}`);
12
+ }
13
+ throw caughtError;
14
+ }
15
+ }
16
+ async function readHttpUrl(url) {
17
+ let response;
18
+ try {
19
+ response = await fetch(url);
20
+ } catch (caughtError) {
21
+ throw new Error(
22
+ `Failed to fetch ${url}: ${caughtError instanceof Error ? caughtError.message : String(caughtError)}`
23
+ );
24
+ }
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
27
+ }
28
+ return response.text();
29
+ }
6
30
  async function readUrl(url) {
7
31
  const parsed = new URL(url);
8
32
  if (parsed.protocol === "file:") {
9
- const path = fileURLToPath(parsed);
10
- try {
11
- return await readFile(path, "utf-8");
12
- } catch (err) {
13
- if (err instanceof Error && "code" in err && err.code === "ENOENT") {
14
- throw new Error(`File not found: ${path}`);
15
- }
16
- throw err;
17
- }
33
+ return readFileUrl(fileURLToPath(parsed));
18
34
  }
19
35
  if (parsed.protocol === "http:" || parsed.protocol === "https:") {
20
- let res;
21
- try {
22
- res = await fetch(url);
23
- } catch (err) {
24
- throw new Error(`Failed to fetch ${url}: ${err instanceof Error ? err.message : String(err)}`);
25
- }
26
- if (!res.ok) {
27
- throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
28
- }
29
- return await res.text();
36
+ return readHttpUrl(url);
30
37
  }
31
38
  throw new Error(
32
39
  `Unsupported protocol: ${parsed.protocol} (only file://, http://, https:// are supported)`
@@ -44,9 +51,9 @@ async function fetchRegistry(baseUrl) {
44
51
  let parsed;
45
52
  try {
46
53
  parsed = JSON.parse(json);
47
- } catch (err) {
54
+ } catch (caughtError) {
48
55
  throw new Error(
49
- `Registry at ${url} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
56
+ `Registry at ${url} is not valid JSON: ${caughtError instanceof Error ? caughtError.message : String(caughtError)}`
50
57
  );
51
58
  }
52
59
  if (!looksLikeRegistry(parsed)) {
@@ -54,9 +61,9 @@ async function fetchRegistry(baseUrl) {
54
61
  }
55
62
  return parsed;
56
63
  }
57
- function looksLikeRegistry(x) {
58
- if (typeof x !== "object" || x === null || !("components" in x)) return false;
59
- const components = x.components;
64
+ function looksLikeRegistry(value) {
65
+ if (typeof value !== "object" || value === null || !("components" in value)) return false;
66
+ const components = value.components;
60
67
  return typeof components === "object" && components !== null && !Array.isArray(components);
61
68
  }
62
69
  async function fetchComponentSource(baseUrl, file) {
@@ -75,4 +82,4 @@ export {
75
82
  fetchComponentSource,
76
83
  resolveRef
77
84
  };
78
- //# sourceMappingURL=chunk-OJWKSIZ5.js.map
85
+ //# sourceMappingURL=chunk-FHRQIHCN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry/readUrl.ts","../src/registry/fetchRegistry.ts","../src/registry/ref.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { fileURLToPath } from 'node:url';\n\nasync function readFileUrl(filePath: string): Promise<string> {\n try {\n return await readFile(filePath, 'utf-8');\n } catch (caughtError) {\n if (caughtError instanceof Error && 'code' in caughtError && caughtError.code === 'ENOENT') {\n throw new Error(`File not found: ${filePath}`);\n }\n throw caughtError;\n }\n}\n\nasync function readHttpUrl(url: string): Promise<string> {\n let response: Response;\n\n try {\n response = await fetch(url);\n } catch (caughtError) {\n throw new Error(\n `Failed to fetch ${url}: ${caughtError instanceof Error ? caughtError.message : String(caughtError)}`,\n );\n }\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);\n }\n\n return response.text();\n}\n\nexport async function readUrl(url: string): Promise<string> {\n const parsed = new URL(url);\n\n if (parsed.protocol === 'file:') {\n return readFileUrl(fileURLToPath(parsed));\n }\n\n if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {\n return readHttpUrl(url);\n }\n\n throw new Error(\n `Unsupported protocol: ${parsed.protocol} (only file://, http://, https:// are supported)`,\n );\n}\n","import { readUrl } from './readUrl.js';\n\nexport interface RegistryEntry {\n file: string;\n description?: string;\n dependencies: string[];\n uses_primitives?: string[];\n tier: 1 | 2 | 3;\n}\n\nexport interface Registry {\n version: string;\n components: Record<string, RegistryEntry>;\n}\n\nfunction joinUrl(base: string, file: string): string {\n const trimmed = base.endsWith('/') ? base.slice(0, -1) : base;\n\n return `${trimmed}/${file}`;\n}\n\nexport async function fetchRegistry(baseUrl: string): Promise<Registry> {\n const url = joinUrl(baseUrl, 'registry.json');\n const json = await readUrl(url);\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(json);\n } catch (caughtError) {\n throw new Error(\n `Registry at ${url} is not valid JSON: ${caughtError instanceof Error ? caughtError.message : String(caughtError)}`,\n );\n }\n if (!looksLikeRegistry(parsed)) {\n throw new Error(`Registry at ${url} is missing a \"components\" object`);\n }\n\n return parsed;\n}\n\nfunction looksLikeRegistry(value: unknown): value is Registry {\n if (typeof value !== 'object' || value === null || !('components' in value)) return false;\n const components = value.components;\n\n return typeof components === 'object' && components !== null && !Array.isArray(components);\n}\n\n/**\n * Fetch the raw source of a component file referenced by a registry entry.\n */\nexport async function fetchComponentSource(baseUrl: string, file: string): Promise<string> {\n return await readUrl(joinUrl(baseUrl, file));\n}\n","export function resolveRef(ref: string | undefined, cliVersion: string): string {\n if (ref !== undefined && ref !== '') return ref;\n if (cliVersion === '0.0.0') return 'main';\n\n return `v${cliVersion}`;\n}\n"],"mappings":";;;AAAA,SAAS,gBAAgB;AACzB,SAAS,qBAAqB;AAE9B,eAAe,YAAY,UAAmC;AAC5D,MAAI;AACF,WAAO,MAAM,SAAS,UAAU,OAAO;AAAA,EACzC,SAAS,aAAa;AACpB,QAAI,uBAAuB,SAAS,UAAU,eAAe,YAAY,SAAS,UAAU;AAC1F,YAAM,IAAI,MAAM,mBAAmB,QAAQ,EAAE;AAAA,IAC/C;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAe,YAAY,KAA8B;AACvD,MAAI;AAEJ,MAAI;AACF,eAAW,MAAM,MAAM,GAAG;AAAA,EAC5B,SAAS,aAAa;AACpB,UAAM,IAAI;AAAA,MACR,mBAAmB,GAAG,KAAK,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,CAAC;AAAA,IACrG;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,mBAAmB,GAAG,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,EACrF;AAEA,SAAO,SAAS,KAAK;AACvB;AAEA,eAAsB,QAAQ,KAA8B;AAC1D,QAAM,SAAS,IAAI,IAAI,GAAG;AAE1B,MAAI,OAAO,aAAa,SAAS;AAC/B,WAAO,YAAY,cAAc,MAAM,CAAC;AAAA,EAC1C;AAEA,MAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UAAU;AAC/D,WAAO,YAAY,GAAG;AAAA,EACxB;AAEA,QAAM,IAAI;AAAA,IACR,yBAAyB,OAAO,QAAQ;AAAA,EAC1C;AACF;;;AC/BA,SAAS,QAAQ,MAAc,MAAsB;AACnD,QAAM,UAAU,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI;AAEzD,SAAO,GAAG,OAAO,IAAI,IAAI;AAC3B;AAEA,eAAsB,cAAc,SAAoC;AACtE,QAAM,MAAM,QAAQ,SAAS,eAAe;AAC5C,QAAM,OAAO,MAAM,QAAQ,GAAG;AAC9B,MAAI;AAEJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,SAAS,aAAa;AACpB,UAAM,IAAI;AAAA,MACR,eAAe,GAAG,uBAAuB,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,CAAC;AAAA,IACnH;AAAA,EACF;AACA,MAAI,CAAC,kBAAkB,MAAM,GAAG;AAC9B,UAAM,IAAI,MAAM,eAAe,GAAG,mCAAmC;AAAA,EACvE;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,OAAmC;AAC5D,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,gBAAgB,OAAQ,QAAO;AACpF,QAAM,aAAa,MAAM;AAEzB,SAAO,OAAO,eAAe,YAAY,eAAe,QAAQ,CAAC,MAAM,QAAQ,UAAU;AAC3F;AAKA,eAAsB,qBAAqB,SAAiB,MAA+B;AACzF,SAAO,MAAM,QAAQ,QAAQ,SAAS,IAAI,CAAC;AAC7C;;;ACpDO,SAAS,WAAW,KAAyB,YAA4B;AAC9E,MAAI,QAAQ,UAAa,QAAQ,GAAI,QAAO;AAC5C,MAAI,eAAe,QAAS,QAAO;AAEnC,SAAO,IAAI,UAAU;AACvB;","names":[]}
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/poster/projectRoot.ts
4
+ import { access } from "fs/promises";
5
+ import { dirname, resolve } from "path";
6
+ async function findProjectRoot(fromPath) {
7
+ let dir = dirname(resolve(fromPath));
8
+ for (; ; ) {
9
+ try {
10
+ await access(`${dir}/package.json`);
11
+ return dir;
12
+ } catch {
13
+ const parent = dirname(dir);
14
+ if (parent === dir) {
15
+ throw new Error(
16
+ `Could not find a package.json walking up from ${fromPath}. Poster needs a project root to resolve dependencies against.`
17
+ );
18
+ }
19
+ dir = parent;
20
+ }
21
+ }
22
+ }
23
+
24
+ export {
25
+ findProjectRoot
26
+ };
27
+ //# sourceMappingURL=chunk-Q7CQW6AH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/poster/projectRoot.ts"],"sourcesContent":["import { access } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\n\nexport async function findProjectRoot(fromPath: string): Promise<string> {\n let dir = dirname(resolve(fromPath));\n\n // Guard against infinite loops on root (dirname('/') === '/').\n for (;;) {\n try {\n await access(`${dir}/package.json`);\n\n return dir;\n } catch {\n const parent = dirname(dir);\n\n if (parent === dir) {\n throw new Error(\n `Could not find a package.json walking up from ${fromPath}. Poster needs a project root to resolve dependencies against.`,\n );\n }\n dir = parent;\n }\n }\n}\n"],"mappings":";;;AAAA,SAAS,cAAc;AACvB,SAAS,SAAS,eAAe;AAEjC,eAAsB,gBAAgB,UAAmC;AACvE,MAAI,MAAM,QAAQ,QAAQ,QAAQ,CAAC;AAGnC,aAAS;AACP,QAAI;AACF,YAAM,OAAO,GAAG,GAAG,eAAe;AAElC,aAAO;AAAA,IACT,QAAQ;AACN,YAAM,SAAS,QAAQ,GAAG;AAE1B,UAAI,WAAW,KAAK;AAClB,cAAM,IAAI;AAAA,UACR,iDAAiD,QAAQ;AAAA,QAC3D;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;","names":[]}
@@ -3,11 +3,44 @@
3
3
  // src/config/matterConfig.ts
4
4
  import { access, readFile, writeFile } from "fs/promises";
5
5
  import { join } from "path";
6
+
7
+ // src/config/validate.ts
8
+ function isRecord(value) {
9
+ return typeof value === "object" && value !== null;
10
+ }
11
+ function validateMatterConfig(parsed, path) {
12
+ if (!isRecord(parsed)) {
13
+ throw new Error(`${path}: expected an object`);
14
+ }
15
+ const obj = parsed;
16
+ if (typeof obj.componentsDir !== "string" || obj.componentsDir === "") {
17
+ throw new Error(`${path}: missing or empty "componentsDir" string`);
18
+ }
19
+ if (typeof obj.registryUrl !== "string" || obj.registryUrl === "") {
20
+ throw new Error(`${path}: missing or empty "registryUrl" string`);
21
+ }
22
+ if (!isRecord(obj.aliases)) {
23
+ throw new Error(`${path}: missing "aliases" object`);
24
+ }
25
+ const aliases = {};
26
+ for (const [aliasKey, aliasValue] of Object.entries(obj.aliases)) {
27
+ if (typeof aliasValue !== "string") {
28
+ throw new Error(`${path}: aliases.${aliasKey} must be a string`);
29
+ }
30
+ aliases[aliasKey] = aliasValue;
31
+ }
32
+ return {
33
+ componentsDir: obj.componentsDir,
34
+ registryUrl: obj.registryUrl,
35
+ aliases
36
+ };
37
+ }
38
+
39
+ // src/config/matterConfig.ts
6
40
  var DEFAULT_MATTER_CONFIG = {
7
41
  componentsDir: "src/components/matter",
8
42
  registryUrl: "https://raw.githubusercontent.com/lovo-hq/matter/${ref}/registry",
9
- aliases: { "@/": "src/" },
10
- tsx: true
43
+ aliases: { "@/": "src/" }
11
44
  };
12
45
  var CONFIG_FILENAME = "matter.config.json";
13
46
  function configPath(projectRoot) {
@@ -26,70 +59,41 @@ async function readMatterConfig(projectRoot) {
26
59
  let raw;
27
60
  try {
28
61
  raw = await readFile(path, "utf-8");
29
- } catch (err) {
30
- if (err instanceof Error && "code" in err && err.code === "ENOENT") {
62
+ } catch (caughtError) {
63
+ if (caughtError instanceof Error && "code" in caughtError && caughtError.code === "ENOENT") {
31
64
  throw new Error(
32
65
  `matter.config.json not found in ${projectRoot}. Run \`matter-cli init\` first.`
33
66
  );
34
67
  }
35
- throw err;
68
+ throw caughtError;
36
69
  }
37
70
  let parsed;
38
71
  try {
39
72
  parsed = JSON.parse(raw);
40
- } catch (err) {
73
+ } catch (caughtError) {
41
74
  throw new Error(
42
- `${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
75
+ `${path} is not valid JSON: ${caughtError instanceof Error ? caughtError.message : String(caughtError)}`
43
76
  );
44
77
  }
45
78
  return validateMatterConfig(parsed, path);
46
79
  }
47
- async function writeMatterConfig(projectRoot, cfg) {
80
+ function resolveRegistryUrl(matterConfig, opts) {
81
+ const baseUrl = opts.registry ?? matterConfig.registryUrl;
82
+ return baseUrl.replace("${ref}", opts.ref);
83
+ }
84
+ async function writeMatterConfig(projectRoot, matterConfig) {
48
85
  const path = configPath(projectRoot);
49
- const json = `${JSON.stringify(cfg, null, 2)}
86
+ const json = `${JSON.stringify(matterConfig, null, 2)}
50
87
  `;
51
88
  await writeFile(path, json, "utf-8");
52
89
  }
53
- function isRecord(x) {
54
- return typeof x === "object" && x !== null;
55
- }
56
- function validateMatterConfig(parsed, path) {
57
- if (!isRecord(parsed)) {
58
- throw new Error(`${path}: expected an object`);
59
- }
60
- const obj = parsed;
61
- if (typeof obj.componentsDir !== "string" || obj.componentsDir === "") {
62
- throw new Error(`${path}: missing or empty "componentsDir" string`);
63
- }
64
- if (typeof obj.registryUrl !== "string" || obj.registryUrl === "") {
65
- throw new Error(`${path}: missing or empty "registryUrl" string`);
66
- }
67
- if (!isRecord(obj.aliases)) {
68
- throw new Error(`${path}: missing "aliases" object`);
69
- }
70
- if (typeof obj.tsx !== "boolean") {
71
- throw new Error(`${path}: missing "tsx" boolean`);
72
- }
73
- const aliases = {};
74
- for (const [k, v] of Object.entries(obj.aliases)) {
75
- if (typeof v !== "string") {
76
- throw new Error(`${path}: aliases.${k} must be a string`);
77
- }
78
- aliases[k] = v;
79
- }
80
- return {
81
- componentsDir: obj.componentsDir,
82
- registryUrl: obj.registryUrl,
83
- aliases,
84
- tsx: obj.tsx
85
- };
86
- }
87
90
 
88
91
  export {
89
92
  DEFAULT_MATTER_CONFIG,
90
93
  configPath,
91
94
  configExists,
92
95
  readMatterConfig,
96
+ resolveRegistryUrl,
93
97
  writeMatterConfig
94
98
  };
95
- //# sourceMappingURL=chunk-M6S6HYHE.js.map
99
+ //# sourceMappingURL=chunk-R7OX6KUP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/matterConfig.ts","../src/config/validate.ts"],"sourcesContent":["import { access, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { validateMatterConfig } from './validate.js';\n\nexport interface MatterConfig {\n componentsDir: string;\n registryUrl: string;\n aliases: Record<string, string>;\n}\n\nexport const DEFAULT_MATTER_CONFIG: MatterConfig = {\n componentsDir: 'src/components/matter',\n registryUrl: 'https://raw.githubusercontent.com/lovo-hq/matter/${ref}/registry',\n aliases: { '@/': 'src/' },\n};\n\nconst CONFIG_FILENAME = 'matter.config.json';\n\nexport function configPath(projectRoot: string): string {\n return join(projectRoot, CONFIG_FILENAME);\n}\n\nexport async function configExists(projectRoot: string): Promise<boolean> {\n try {\n await access(configPath(projectRoot));\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readMatterConfig(projectRoot: string): Promise<MatterConfig> {\n const path = configPath(projectRoot);\n let raw: string;\n\n try {\n raw = await readFile(path, 'utf-8');\n } catch (caughtError) {\n if (caughtError instanceof Error && 'code' in caughtError && caughtError.code === 'ENOENT') {\n throw new Error(\n `matter.config.json not found in ${projectRoot}. Run \\`matter-cli init\\` first.`,\n );\n }\n throw caughtError;\n }\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(raw);\n } catch (caughtError) {\n throw new Error(\n `${path} is not valid JSON: ${caughtError instanceof Error ? caughtError.message : String(caughtError)}`,\n );\n }\n\n return validateMatterConfig(parsed, path);\n}\n\nexport function resolveRegistryUrl(\n matterConfig: MatterConfig,\n opts: { registry?: string; ref: string },\n): string {\n const baseUrl = opts.registry ?? matterConfig.registryUrl;\n\n return baseUrl.replace('${ref}', opts.ref);\n}\n\nexport async function writeMatterConfig(\n projectRoot: string,\n matterConfig: MatterConfig,\n): Promise<void> {\n const path = configPath(projectRoot);\n const json = `${JSON.stringify(matterConfig, null, 2)}\\n`;\n\n await writeFile(path, json, 'utf-8');\n}\n","import type { MatterConfig } from './matterConfig.js';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nexport function validateMatterConfig(parsed: unknown, path: string): MatterConfig {\n if (!isRecord(parsed)) {\n throw new Error(`${path}: expected an object`);\n }\n const obj = parsed;\n\n if (typeof obj.componentsDir !== 'string' || obj.componentsDir === '') {\n throw new Error(`${path}: missing or empty \"componentsDir\" string`);\n }\n if (typeof obj.registryUrl !== 'string' || obj.registryUrl === '') {\n throw new Error(`${path}: missing or empty \"registryUrl\" string`);\n }\n if (!isRecord(obj.aliases)) {\n throw new Error(`${path}: missing \"aliases\" object`);\n }\n const aliases: Record<string, string> = {};\n\n for (const [aliasKey, aliasValue] of Object.entries(obj.aliases)) {\n if (typeof aliasValue !== 'string') {\n throw new Error(`${path}: aliases.${aliasKey} must be a string`);\n }\n aliases[aliasKey] = aliasValue;\n }\n\n return {\n componentsDir: obj.componentsDir,\n registryUrl: obj.registryUrl,\n aliases,\n };\n}\n"],"mappings":";;;AAAA,SAAS,QAAQ,UAAU,iBAAiB;AAC5C,SAAS,YAAY;;;ACCrB,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEO,SAAS,qBAAqB,QAAiB,MAA4B;AAChF,MAAI,CAAC,SAAS,MAAM,GAAG;AACrB,UAAM,IAAI,MAAM,GAAG,IAAI,sBAAsB;AAAA,EAC/C;AACA,QAAM,MAAM;AAEZ,MAAI,OAAO,IAAI,kBAAkB,YAAY,IAAI,kBAAkB,IAAI;AACrE,UAAM,IAAI,MAAM,GAAG,IAAI,2CAA2C;AAAA,EACpE;AACA,MAAI,OAAO,IAAI,gBAAgB,YAAY,IAAI,gBAAgB,IAAI;AACjE,UAAM,IAAI,MAAM,GAAG,IAAI,yCAAyC;AAAA,EAClE;AACA,MAAI,CAAC,SAAS,IAAI,OAAO,GAAG;AAC1B,UAAM,IAAI,MAAM,GAAG,IAAI,4BAA4B;AAAA,EACrD;AACA,QAAM,UAAkC,CAAC;AAEzC,aAAW,CAAC,UAAU,UAAU,KAAK,OAAO,QAAQ,IAAI,OAAO,GAAG;AAChE,QAAI,OAAO,eAAe,UAAU;AAClC,YAAM,IAAI,MAAM,GAAG,IAAI,aAAa,QAAQ,mBAAmB;AAAA,IACjE;AACA,YAAQ,QAAQ,IAAI;AAAA,EACtB;AAEA,SAAO;AAAA,IACL,eAAe,IAAI;AAAA,IACnB,aAAa,IAAI;AAAA,IACjB;AAAA,EACF;AACF;;;ADxBO,IAAM,wBAAsC;AAAA,EACjD,eAAe;AAAA,EACf,aAAa;AAAA,EACb,SAAS,EAAE,MAAM,OAAO;AAC1B;AAEA,IAAM,kBAAkB;AAEjB,SAAS,WAAW,aAA6B;AACtD,SAAO,KAAK,aAAa,eAAe;AAC1C;AAEA,eAAsB,aAAa,aAAuC;AACxE,MAAI;AACF,UAAM,OAAO,WAAW,WAAW,CAAC;AAEpC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,iBAAiB,aAA4C;AACjF,QAAM,OAAO,WAAW,WAAW;AACnC,MAAI;AAEJ,MAAI;AACF,UAAM,MAAM,SAAS,MAAM,OAAO;AAAA,EACpC,SAAS,aAAa;AACpB,QAAI,uBAAuB,SAAS,UAAU,eAAe,YAAY,SAAS,UAAU;AAC1F,YAAM,IAAI;AAAA,QACR,mCAAmC,WAAW;AAAA,MAChD;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACA,MAAI;AAEJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,SAAS,aAAa;AACpB,UAAM,IAAI;AAAA,MACR,GAAG,IAAI,uBAAuB,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,CAAC;AAAA,IACxG;AAAA,EACF;AAEA,SAAO,qBAAqB,QAAQ,IAAI;AAC1C;AAEO,SAAS,mBACd,cACA,MACQ;AACR,QAAM,UAAU,KAAK,YAAY,aAAa;AAE9C,SAAO,QAAQ,QAAQ,UAAU,KAAK,GAAG;AAC3C;AAEA,eAAsB,kBACpB,aACA,cACe;AACf,QAAM,OAAO,WAAW,WAAW;AACnC,QAAM,OAAO,GAAG,KAAK,UAAU,cAAc,MAAM,CAAC,CAAC;AAAA;AAErD,QAAM,UAAU,MAAM,MAAM,OAAO;AACrC;","names":[]}
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/poster/playwright.ts
4
+ import { access } from "fs/promises";
5
+ import { writeFile } from "fs/promises";
6
+ import { dirname, join } from "path";
7
+ import { pathToFileURL } from "url";
8
+ async function findPlaywrightDir(startDir) {
9
+ let dir = startDir;
10
+ for (; ; ) {
11
+ const candidate = join(dir, "node_modules", "playwright");
12
+ try {
13
+ await access(join(candidate, "package.json"));
14
+ return candidate;
15
+ } catch {
16
+ }
17
+ const parent = dirname(dir);
18
+ if (parent === dir) return null;
19
+ dir = parent;
20
+ }
21
+ }
22
+ function isRecord(value) {
23
+ return typeof value === "object" && value !== null;
24
+ }
25
+ function isPlaywrightNamespace(value) {
26
+ if (!isRecord(value)) return false;
27
+ const chromium = value.chromium;
28
+ if (!isRecord(chromium)) return false;
29
+ return typeof chromium.launch === "function";
30
+ }
31
+ function getDefaultExport(value) {
32
+ if (!isRecord(value)) return void 0;
33
+ return value.default;
34
+ }
35
+ async function resolvePlaywright(projectRoot) {
36
+ const playwrightDir = await findPlaywrightDir(projectRoot);
37
+ if (playwrightDir === null) {
38
+ throw new Error(
39
+ `Install playwright to use this command: pnpm add -D playwright && pnpm exec playwright install chromium`
40
+ );
41
+ }
42
+ for (const entryFilename of ["index.mjs", "index.js"]) {
43
+ const filePath = join(playwrightDir, entryFilename);
44
+ try {
45
+ await access(filePath);
46
+ const rawModule = await import(pathToFileURL(filePath).href);
47
+ const playwrightNamespace = isPlaywrightNamespace(rawModule) ? rawModule : getDefaultExport(rawModule);
48
+ if (!isPlaywrightNamespace(playwrightNamespace)) {
49
+ throw new Error(`Resolved ${filePath} but it does not expose chromium.launch`);
50
+ }
51
+ return playwrightNamespace;
52
+ } catch (caughtError) {
53
+ if (entryFilename === "index.js") throw caughtError;
54
+ }
55
+ }
56
+ throw new Error(`Unable to import playwright from ${playwrightDir}`);
57
+ }
58
+ async function launchAndScreenshot(opts) {
59
+ const playwright = await resolvePlaywright(opts.projectRoot);
60
+ const browser = await playwright.chromium.launch({ headless: true });
61
+ try {
62
+ const browserContext = await browser.newContext({
63
+ viewport: { width: opts.width, height: opts.height },
64
+ deviceScaleFactor: 1
65
+ });
66
+ const page = await browserContext.newPage();
67
+ const consoleErrors = [];
68
+ page.on("pageerror", (pageError) => consoleErrors.push(`pageerror: ${pageError.message}`));
69
+ page.on("console", (msg) => {
70
+ if (msg.type() === "error") consoleErrors.push(`console: ${msg.text()}`);
71
+ });
72
+ await page.goto(opts.url, { waitUntil: "load" });
73
+ try {
74
+ await page.waitForFunction(() => Reflect.get(globalThis, "__matterReady") === true, {
75
+ timeout: opts.readyTimeoutMs
76
+ });
77
+ } catch {
78
+ if (consoleErrors.length > 0) {
79
+ throw new Error(
80
+ `Poster render failed before producing a frame:
81
+ ${consoleErrors.join("\n ")}`
82
+ );
83
+ }
84
+ throw new Error(
85
+ `no canvas content detected within ${opts.readyTimeoutMs / 1e3}s; does your component render a ShaderScene with a visible base layer?`
86
+ );
87
+ }
88
+ if (opts.timeSeconds > 0) {
89
+ await page.waitForTimeout(opts.timeSeconds * 1e3);
90
+ }
91
+ const canvas = page.locator("canvas").first();
92
+ const imageBuffer = opts.format === "jpeg" ? await canvas.screenshot({ type: "jpeg", quality: opts.quality }) : await canvas.screenshot({ type: "png" });
93
+ await writeFile(opts.outPath, imageBuffer);
94
+ return { bytes: imageBuffer.length };
95
+ } finally {
96
+ await browser.close();
97
+ }
98
+ }
99
+
100
+ export {
101
+ resolvePlaywright,
102
+ launchAndScreenshot
103
+ };
104
+ //# sourceMappingURL=chunk-RRL35RGP.js.map