@fragments-sdk/cli 0.3.0 → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fragments-sdk/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "CLI, MCP server, and dev tools for Fragments design system",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,166 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { resolve, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { existsSync } from "node:fs";
5
+ import { readFile } from "node:fs/promises";
6
+
7
+ /**
8
+ * Integration tests for the consolidated viewer.
9
+ *
10
+ * After packages were merged into @fragments-sdk/cli, the viewer's
11
+ * path resolution changed. These tests verify that:
12
+ * - Viewer assets (HTML, TSX entry points) are found at the correct paths
13
+ * - The @fragments/core alias resolves to the consolidated core source
14
+ * - The virtual module generates valid import statements
15
+ * - The Vite config references correct file system locations
16
+ */
17
+
18
+ // Simulate the same path resolution used in server.ts and vite-plugin.ts
19
+ // At runtime, __dirname is dist/. In tests (vitest), it's the source dir.
20
+ const testDir = dirname(fileURLToPath(import.meta.url));
21
+ const viewerDir = resolve(testDir, "..");
22
+ const cliPackageRoot = resolve(viewerDir, "../..");
23
+
24
+ describe("viewer path resolution", () => {
25
+ it("viewerRoot resolves to a directory containing index.html", () => {
26
+ // server.ts: viewerRoot = resolve(cliPackageRoot, "src/viewer")
27
+ const viewerRoot = resolve(cliPackageRoot, "src/viewer");
28
+ expect(existsSync(resolve(viewerRoot, "index.html"))).toBe(true);
29
+ });
30
+
31
+ it("viewerRoot contains entry.tsx", () => {
32
+ const viewerRoot = resolve(cliPackageRoot, "src/viewer");
33
+ expect(existsSync(resolve(viewerRoot, "entry.tsx"))).toBe(true);
34
+ });
35
+
36
+ it("viewerRoot contains preview-frame.html", () => {
37
+ const viewerRoot = resolve(cliPackageRoot, "src/viewer");
38
+ expect(existsSync(resolve(viewerRoot, "preview-frame.html"))).toBe(true);
39
+ });
40
+
41
+ it("viewerRoot contains preview-frame-entry.tsx", () => {
42
+ const viewerRoot = resolve(cliPackageRoot, "src/viewer");
43
+ expect(existsSync(resolve(viewerRoot, "preview-frame-entry.tsx"))).toBe(true);
44
+ });
45
+
46
+ it("viewerRoot contains render-template.html", () => {
47
+ const viewerRoot = resolve(cliPackageRoot, "src/viewer");
48
+ expect(existsSync(resolve(viewerRoot, "render-template.html"))).toBe(true);
49
+ });
50
+
51
+ it("viewerRoot contains tailwind.config.js", () => {
52
+ const viewerRoot = resolve(cliPackageRoot, "src/viewer");
53
+ expect(existsSync(resolve(viewerRoot, "tailwind.config.js"))).toBe(true);
54
+ });
55
+
56
+ it("viewerRoot contains postcss.config.js", () => {
57
+ const viewerRoot = resolve(cliPackageRoot, "src/viewer");
58
+ expect(existsSync(resolve(viewerRoot, "postcss.config.js"))).toBe(true);
59
+ });
60
+
61
+ it("viewerRoot contains styles/globals.css", () => {
62
+ const viewerRoot = resolve(cliPackageRoot, "src/viewer");
63
+ expect(existsSync(resolve(viewerRoot, "styles/globals.css"))).toBe(true);
64
+ });
65
+ });
66
+
67
+ describe("@fragments/core alias resolution", () => {
68
+ it("core/index.ts exists at the expected path", () => {
69
+ // server.ts: "@fragments/core": resolve(cliPackageRoot, "src/core/index.ts")
70
+ const corePath = resolve(cliPackageRoot, "src/core/index.ts");
71
+ expect(existsSync(corePath)).toBe(true);
72
+ });
73
+
74
+ it("core/index.ts exports storyModuleToSegment", async () => {
75
+ const corePath = resolve(cliPackageRoot, "src/core/index.ts");
76
+ const content = await readFile(corePath, "utf-8");
77
+ // The virtual module imports { storyModuleToSegment, setPreviewConfig }
78
+ expect(content).toContain("storyModuleToSegment");
79
+ });
80
+
81
+ it("core/index.ts exports setPreviewConfig", async () => {
82
+ const corePath = resolve(cliPackageRoot, "src/core/index.ts");
83
+ const content = await readFile(corePath, "utf-8");
84
+ expect(content).toContain("setPreviewConfig");
85
+ });
86
+ });
87
+
88
+ describe("virtual module @fragments/core import", () => {
89
+ it("vite-plugin generates import from @fragments/core (resolved via alias)", async () => {
90
+ // The virtual module template in vite-plugin.ts must import from @fragments/core
91
+ // which is resolved by the Vite alias to the consolidated core source
92
+ const pluginPath = resolve(viewerDir, "vite-plugin.ts");
93
+ const content = await readFile(pluginPath, "utf-8");
94
+
95
+ // The generated virtual module string should reference @fragments/core
96
+ expect(content).toContain(
97
+ 'import { storyModuleToSegment, setPreviewConfig } from "@fragments/core"'
98
+ );
99
+ });
100
+
101
+ it("server.ts sets up @fragments/core alias to src/core/index.ts", async () => {
102
+ const serverPath = resolve(viewerDir, "server.ts");
103
+ const content = await readFile(serverPath, "utf-8");
104
+
105
+ // The alias must resolve @fragments/core to the consolidated core
106
+ expect(content).toContain('"@fragments/core": resolve(cliPackageRoot, "src/core/index.ts")');
107
+ });
108
+
109
+ it("server.ts sets up @fragments/viewer alias", async () => {
110
+ const serverPath = resolve(viewerDir, "server.ts");
111
+ const content = await readFile(serverPath, "utf-8");
112
+
113
+ expect(content).toContain('"@fragments/viewer": viewerRoot');
114
+ });
115
+ });
116
+
117
+ describe("viewer HTML templates", () => {
118
+ it("index.html contains entry.tsx script reference", async () => {
119
+ const htmlPath = resolve(viewerDir, "index.html");
120
+ const content = await readFile(htmlPath, "utf-8");
121
+
122
+ // The HTML references /src/entry.tsx which gets rewritten to an absolute path
123
+ expect(content).toContain('src="/src/entry.tsx"');
124
+ });
125
+
126
+ it("preview-frame.html contains preview-frame-entry.tsx reference", async () => {
127
+ const htmlPath = resolve(viewerDir, "preview-frame.html");
128
+ const content = await readFile(htmlPath, "utf-8");
129
+
130
+ expect(content).toContain('src="/src/preview-frame-entry.tsx"');
131
+ });
132
+
133
+ it("vite-plugin rewrites entry paths using viewerAssetsRoot", async () => {
134
+ const pluginPath = resolve(viewerDir, "vite-plugin.ts");
135
+ const content = await readFile(pluginPath, "utf-8");
136
+
137
+ // The plugin must use viewerAssetsRoot (not __dirname or resolve(__dirname, ".."))
138
+ // to find viewer HTML and entry files
139
+ expect(content).toContain("const viewerAssetsRoot = resolve(__dirname,");
140
+ expect(content).toContain('const viewerRoot = viewerAssetsRoot');
141
+ });
142
+ });
143
+
144
+ describe("no stale @fragments/* package references", () => {
145
+ it("server.ts does not reference old resolveFragmentsPackage for core", async () => {
146
+ const serverPath = resolve(viewerDir, "server.ts");
147
+ const content = await readFile(serverPath, "utf-8");
148
+
149
+ // Should NOT try to resolve @fragments/core from node_modules
150
+ expect(content).not.toContain('resolveFragmentsPackage("core"');
151
+ });
152
+
153
+ it("no source files import from @fragments/core as a runtime dependency", async () => {
154
+ // All source-level imports should use relative paths (../core/).
155
+ // Only the generated virtual module string uses @fragments/core (resolved via alias).
156
+ const serverPath = resolve(viewerDir, "server.ts");
157
+ const content = await readFile(serverPath, "utf-8");
158
+
159
+ // server.ts should import from relative paths, not @fragments/core
160
+ const lines = content.split("\n");
161
+ const importLines = lines.filter(
162
+ (l) => l.startsWith("import") && l.includes("@fragments/core")
163
+ );
164
+ expect(importLines).toHaveLength(0);
165
+ });
166
+ });
@@ -150,14 +150,14 @@ export async function createDevServer(
150
150
  alias: {
151
151
  // Allow importing from viewer package
152
152
  "@fragments/viewer": viewerRoot,
153
+ // Resolve @fragments/core to the consolidated core source
154
+ "@fragments/core": resolve(cliPackageRoot, "src/core/index.ts"),
153
155
  // Ensure ALL react imports resolve to project's node_modules
154
156
  // This is critical for viewer files loaded from outside project root
155
157
  "react": safeRealpath(join(nodeModulesPath, "react")),
156
158
  "react-dom": safeRealpath(join(nodeModulesPath, "react-dom")),
157
159
  "react/jsx-runtime": safeRealpath(join(nodeModulesPath, "react/jsx-runtime")),
158
160
  "react/jsx-dev-runtime": safeRealpath(join(nodeModulesPath, "react/jsx-dev-runtime")),
159
- // Resolve @fragments packages - try project's node_modules first, fall back to monorepo
160
- "@fragments/core": resolveFragmentsPackage("core", nodeModulesPath),
161
161
  },
162
162
  },
163
163
  };
@@ -7,7 +7,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
7
7
  export default {
8
8
  content: [
9
9
  resolve(__dirname, 'index.html'),
10
- resolve(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
10
+ resolve(__dirname, '**/*.{js,ts,jsx,tsx}'),
11
11
  ],
12
12
  darkMode: 'class',
13
13
  theme: {