@object-ui/cli 17.2.0 → 17.5.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.
@@ -1,16 +1,60 @@
1
1
  // src/commands/serve.ts
2
2
  import { createServer } from "vite";
3
3
  import react from "@vitejs/plugin-react";
4
- import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
5
- import { join as join2, resolve, relative } from "path";
4
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
5
+ import { join as join3, resolve, relative } from "path";
6
6
  import chalk2 from "chalk";
7
7
  import { execSync } from "child_process";
8
8
 
9
9
  // src/utils/app-generator.ts
10
- import { readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, existsSync } from "fs";
11
- import { join } from "path";
10
+ import { readFileSync as readFileSync2, writeFileSync, mkdirSync, readdirSync, statSync, existsSync as existsSync2 } from "fs";
11
+ import { dirname as dirname2, join as join2 } from "path";
12
12
  import chalk from "chalk";
13
13
  import * as yaml from "js-yaml";
14
+
15
+ // src/utils/scaffold-dependencies.ts
16
+ import { existsSync, readFileSync } from "fs";
17
+ import { dirname, join } from "path";
18
+ import { fileURLToPath } from "url";
19
+ var CLI_PACKAGE_NAME = "@object-ui/cli";
20
+ var cachedCliVersion;
21
+ function cliVersion() {
22
+ if (cachedCliVersion !== void 0) return cachedCliVersion;
23
+ let dir = dirname(fileURLToPath(import.meta.url));
24
+ for (; ; ) {
25
+ const candidate = join(dir, "package.json");
26
+ if (existsSync(candidate)) {
27
+ const manifest = JSON.parse(readFileSync(candidate, "utf-8"));
28
+ if (manifest.name === CLI_PACKAGE_NAME && typeof manifest.version === "string") {
29
+ cachedCliVersion = manifest.version;
30
+ return cachedCliVersion;
31
+ }
32
+ }
33
+ const parent = dirname(dir);
34
+ if (parent === dir) break;
35
+ dir = parent;
36
+ }
37
+ throw new Error(
38
+ `Could not locate the ${CLI_PACKAGE_NAME} manifest to version the generated app's platform dependencies against. Refusing to write a package.json with a guessed range.`
39
+ );
40
+ }
41
+ function platformPackageRange() {
42
+ return `^${cliVersion()}`;
43
+ }
44
+ var REACT_RANGE = "19.2.8";
45
+ var SCAFFOLD_DEV_DEPENDENCIES = {
46
+ "@tailwindcss/postcss": "^4.3.3",
47
+ "@types/react": "19.2.18",
48
+ "@types/react-dom": "19.2.4",
49
+ "@vitejs/plugin-react": "^6.0.5",
50
+ autoprefixer: "^10.5.4",
51
+ postcss: "^8.5.26",
52
+ tailwindcss: "^4.3.3",
53
+ typescript: "^6.0.3",
54
+ vite: "^8.2.1"
55
+ };
56
+
57
+ // src/utils/app-generator.ts
14
58
  function isSupportedSchemaFile(filename) {
15
59
  return filename.endsWith(".json") || filename.endsWith(".yml") || filename.endsWith(".yaml");
16
60
  }
@@ -18,7 +62,7 @@ function getBaseFileName(filename) {
18
62
  return filename.replace(/\.(json|yml|yaml)$/, "");
19
63
  }
20
64
  function parseSchemaFile(filePath) {
21
- const content = readFileSync(filePath, "utf-8");
65
+ const content = readFileSync2(filePath, "utf-8");
22
66
  if (filePath.endsWith(".json")) {
23
67
  return JSON.parse(content);
24
68
  } else if (filePath.endsWith(".yml") || filePath.endsWith(".yaml")) {
@@ -31,7 +75,7 @@ function scanPagesDirectory(pagesDir) {
31
75
  const scanDir = (dir, routePrefix = "") => {
32
76
  const entries = readdirSync(dir);
33
77
  for (const entry of entries) {
34
- const fullPath = join(dir, entry);
78
+ const fullPath = join2(dir, entry);
35
79
  const stat = statSync(fullPath);
36
80
  if (stat.isDirectory()) {
37
81
  const newPrefix = routePrefix + "/" + entry;
@@ -73,7 +117,255 @@ function scanPagesDirectory(pagesDir) {
73
117
  });
74
118
  return routes;
75
119
  }
120
+ var PLATFORM_RUNTIME_PACKAGES = [
121
+ "@object-ui/react",
122
+ "@object-ui/components",
123
+ "@object-ui/plugin-charts",
124
+ "@object-ui/plugin-editor",
125
+ "@object-ui/plugin-kanban",
126
+ "@object-ui/plugin-markdown",
127
+ "@object-ui/plugin-form",
128
+ "@object-ui/plugin-grid",
129
+ "@object-ui/plugin-view"
130
+ ];
131
+ function buildAppDependencies() {
132
+ const range = platformPackageRange();
133
+ return {
134
+ react: REACT_RANGE,
135
+ "react-dom": REACT_RANGE,
136
+ ...Object.fromEntries(PLATFORM_RUNTIME_PACKAGES.map((name) => [name, range]))
137
+ };
138
+ }
139
+ function buildRoutedAppDependencies() {
140
+ const range = platformPackageRange();
141
+ return {
142
+ react: REACT_RANGE,
143
+ "react-dom": REACT_RANGE,
144
+ "react-router-dom": "^7.18.2",
145
+ "lucide-react": "^1.29.0",
146
+ ...Object.fromEntries(PLATFORM_RUNTIME_PACKAGES.map((name) => [name, range]))
147
+ };
148
+ }
149
+ var APP_VITE_ENV_D_TS = `/// <reference types="vite/client" />
150
+ `;
151
+ var APP_TSCONFIG = {
152
+ compilerOptions: {
153
+ target: "ES2020",
154
+ useDefineForClassFields: true,
155
+ lib: ["ES2020", "DOM", "DOM.Iterable"],
156
+ module: "ESNext",
157
+ skipLibCheck: true,
158
+ moduleResolution: "bundler",
159
+ allowImportingTsExtensions: true,
160
+ resolveJsonModule: true,
161
+ isolatedModules: true,
162
+ noEmit: true,
163
+ jsx: "react-jsx",
164
+ strict: true,
165
+ noUnusedLocals: true,
166
+ noUnusedParameters: true,
167
+ noFallthroughCasesInSwitch: true
168
+ },
169
+ include: ["src"]
170
+ };
171
+ var APP_POSTCSS_CONFIG = `export default {
172
+ plugins: {
173
+ '@tailwindcss/postcss': {},
174
+ autoprefixer: {},
175
+ },
176
+ };`;
177
+ function currentContext() {
178
+ const cwd = process.cwd();
179
+ return { cwd, isMonorepo: existsSync2(join2(cwd, "pnpm-workspace.yaml")) };
180
+ }
181
+ function buildAppPackageJson(context) {
182
+ return {
183
+ name: "objectui-temp-app",
184
+ private: true,
185
+ type: "module",
186
+ // In monorepo, we use root node_modules, so we don't need dependencies here
187
+ dependencies: context.isMonorepo ? {} : buildAppDependencies(),
188
+ devDependencies: context.isMonorepo ? {} : { ...SCAFFOLD_DEV_DEPENDENCIES }
189
+ };
190
+ }
191
+ function buildRoutedAppPackageJson() {
192
+ return {
193
+ name: "objectui-temp-app",
194
+ private: true,
195
+ type: "module",
196
+ dependencies: buildRoutedAppDependencies(),
197
+ devDependencies: { ...SCAFFOLD_DEV_DEPENDENCIES }
198
+ };
199
+ }
200
+ function buildAppSourceDirectives(context) {
201
+ const sources = [`@source '../index.html';`, `@source '../src/**/*.{js,ts,jsx,tsx,json}';`];
202
+ if (context.isMonorepo) {
203
+ sources.push(`@source '${join2(context.cwd, "packages/components/src/**/*.{ts,tsx}")}';`);
204
+ sources.push(`@source '${join2(context.cwd, "packages/plugin-*/src/**/*.{ts,tsx}")}';`);
205
+ } else {
206
+ sources.push(`@source '../node_modules/@object-ui/*/dist/**/*.js';`);
207
+ }
208
+ return sources;
209
+ }
210
+ var APP_THEME_TOKENS = `@theme {
211
+ /* Border radius tokens */
212
+ --radius-lg: var(--radius);
213
+ --radius-md: calc(var(--radius) - 2px);
214
+ --radius-sm: calc(var(--radius) - 4px);
215
+
216
+ /* Color tokens mapped to the CSS variables declared below */
217
+ --color-border: hsl(var(--border));
218
+ --color-input: hsl(var(--input));
219
+ --color-ring: hsl(var(--ring));
220
+ --color-background: hsl(var(--background));
221
+ --color-foreground: hsl(var(--foreground));
222
+ --color-primary: hsl(var(--primary));
223
+ --color-primary-foreground: hsl(var(--primary-foreground));
224
+ --color-secondary: hsl(var(--secondary));
225
+ --color-secondary-foreground: hsl(var(--secondary-foreground));
226
+ --color-destructive: hsl(var(--destructive));
227
+ --color-destructive-foreground: hsl(var(--destructive-foreground));
228
+ --color-muted: hsl(var(--muted));
229
+ --color-muted-foreground: hsl(var(--muted-foreground));
230
+ --color-accent: hsl(var(--accent));
231
+ --color-accent-foreground: hsl(var(--accent-foreground));
232
+ --color-popover: hsl(var(--popover));
233
+ --color-popover-foreground: hsl(var(--popover-foreground));
234
+ --color-card: hsl(var(--card));
235
+ --color-card-foreground: hsl(var(--card-foreground));
236
+ --color-sidebar: hsl(var(--sidebar));
237
+ --color-sidebar-foreground: hsl(var(--sidebar-foreground));
238
+ --color-sidebar-primary: hsl(var(--sidebar-primary));
239
+ --color-sidebar-primary-foreground: hsl(var(--sidebar-primary-foreground));
240
+ --color-sidebar-accent: hsl(var(--sidebar-accent));
241
+ --color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground));
242
+ --color-sidebar-border: hsl(var(--sidebar-border));
243
+ --color-sidebar-ring: hsl(var(--sidebar-ring));
244
+
245
+ /* Chart colors */
246
+ --color-chart-1: hsl(var(--chart-1));
247
+ --color-chart-2: hsl(var(--chart-2));
248
+ --color-chart-3: hsl(var(--chart-3));
249
+ --color-chart-4: hsl(var(--chart-4));
250
+ --color-chart-5: hsl(var(--chart-5));
251
+ }`;
252
+ var APP_THEME_VARIABLES = `:root {
253
+ color-scheme: light;
254
+
255
+ --background: 0 0% 100%;
256
+ --foreground: 222.2 84% 4.9%;
257
+ --card: 0 0% 100%;
258
+ --card-foreground: 222.2 84% 4.9%;
259
+ --popover: 0 0% 100%;
260
+ --popover-foreground: 222.2 84% 4.9%;
261
+ --primary: 222.2 47.4% 11.2%;
262
+ --primary-foreground: 210 40% 98%;
263
+ --secondary: 210 40% 96.1%;
264
+ --secondary-foreground: 222.2 47.4% 11.2%;
265
+ --muted: 210 40% 96.1%;
266
+ --muted-foreground: 215.4 16.3% 46.9%;
267
+ --accent: 210 40% 96.1%;
268
+ --accent-foreground: 222.2 47.4% 11.2%;
269
+ --destructive: 0 84.2% 60.2%;
270
+ --destructive-foreground: 210 40% 98%;
271
+ --border: 214.3 31.8% 91.4%;
272
+ --input: 214.3 31.8% 91.4%;
273
+ --ring: 222.2 84% 4.9%;
274
+ --radius: 0.5rem;
275
+ --chart-1: 12 76% 61%;
276
+ --chart-2: 173 58% 39%;
277
+ --chart-3: 197 37% 24%;
278
+ --chart-4: 43 74% 66%;
279
+ --chart-5: 27 87% 67%;
280
+
281
+ /* Sidebar colors */
282
+ --sidebar: 0 0% 98%;
283
+ --sidebar-foreground: 240 5.3% 26.1%;
284
+ --sidebar-primary: 240 5.9% 10%;
285
+ --sidebar-primary-foreground: 0 0% 98%;
286
+ --sidebar-accent: 240 4.8% 95.9%;
287
+ --sidebar-accent-foreground: 240 5.9% 10%;
288
+ --sidebar-border: 220 13% 91%;
289
+ --sidebar-ring: 217.2 91.2% 59.8%;
290
+ }
291
+
292
+ .dark {
293
+ color-scheme: dark;
294
+
295
+ --background: 222.2 84% 4.9%;
296
+ --foreground: 210 40% 98%;
297
+ --card: 222.2 84% 4.9%;
298
+ --card-foreground: 210 40% 98%;
299
+ --popover: 222.2 84% 4.9%;
300
+ --popover-foreground: 210 40% 98%;
301
+ --primary: 210 40% 98%;
302
+ --primary-foreground: 222.2 47.4% 11.2%;
303
+ --secondary: 217.2 32.6% 17.5%;
304
+ --secondary-foreground: 210 40% 98%;
305
+ --muted: 217.2 32.6% 17.5%;
306
+ --muted-foreground: 215 20.2% 65.1%;
307
+ --accent: 217.2 32.6% 17.5%;
308
+ --accent-foreground: 210 40% 98%;
309
+ --destructive: 0 62.8% 30.6%;
310
+ --destructive-foreground: 210 40% 98%;
311
+ --border: 217.2 32.6% 17.5%;
312
+ --input: 217.2 32.6% 17.5%;
313
+ --ring: 212.7 26.8% 83.9%;
314
+ --chart-1: 220 70% 50%;
315
+ --chart-2: 160 60% 45%;
316
+ --chart-3: 30 80% 55%;
317
+ --chart-4: 280 65% 60%;
318
+ --chart-5: 340 75% 55%;
319
+
320
+ /* Sidebar colors for dark mode */
321
+ --sidebar: 240 5.9% 10%;
322
+ --sidebar-foreground: 240 4.8% 95.9%;
323
+ --sidebar-primary: 224.3 76.3% 48%;
324
+ --sidebar-primary-foreground: 0 0% 100%;
325
+ --sidebar-accent: 240 3.7% 15.9%;
326
+ --sidebar-accent-foreground: 240 4.8% 95.9%;
327
+ --sidebar-border: 240 3.7% 15.9%;
328
+ --sidebar-ring: 217.2 91.2% 59.8%;
329
+ }`;
330
+ function buildAppIndexCss(context) {
331
+ return `@import 'tailwindcss';
332
+
333
+ /* Class-based dark variant: follow \`.dark\` on <html> \u2014 the routed app's
334
+ \`src/theme-provider.tsx\` toggles exactly that class \u2014 instead of the OS
335
+ \`prefers-color-scheme\`. Same spelling as packages/components/src/index.css. */
336
+ @custom-variant dark (&:where(.dark, .dark *));
337
+
338
+ /* Sources to scan for utility classes (v4's replacement for v3 \`content\`) */
339
+ ${buildAppSourceDirectives(context).join("\n")}
340
+
341
+ ${APP_THEME_TOKENS}
342
+
343
+ ${APP_THEME_VARIABLES}
344
+
345
+ * {
346
+ border-color: hsl(var(--border));
347
+ }
348
+
349
+ body {
350
+ background-color: hsl(var(--background));
351
+ color: hsl(var(--foreground));
352
+ font-family: var(--font-sans);
353
+ -webkit-font-smoothing: antialiased;
354
+ -moz-osx-font-smoothing: grayscale;
355
+ min-height: 100vh;
356
+ }`;
357
+ }
358
+ function writeGeneratedFiles(tmpDir, files) {
359
+ for (const [relativePath, contents] of Object.entries(files)) {
360
+ const target = join2(tmpDir, relativePath);
361
+ mkdirSync(dirname2(target), { recursive: true });
362
+ writeFileSync(target, contents);
363
+ }
364
+ }
76
365
  function createTempApp(tmpDir, schema) {
366
+ writeGeneratedFiles(tmpDir, buildAppFiles(schema, currentContext()));
367
+ }
368
+ function buildAppFiles(schema, context) {
77
369
  const html = `<!DOCTYPE html>
78
370
  <html lang="en">
79
371
  <head>
@@ -86,9 +378,6 @@ function createTempApp(tmpDir, schema) {
86
378
  <script type="module" src="/src/main.tsx"></script>
87
379
  </body>
88
380
  </html>`;
89
- writeFileSync(join(tmpDir, "index.html"), html);
90
- const srcDir = join(tmpDir, "src");
91
- mkdirSync(srcDir, { recursive: true });
92
381
  const mainTsx = `import React from 'react';
93
382
  import ReactDOM from 'react-dom/client';
94
383
  import App from './App';
@@ -99,7 +388,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
99
388
  <App />
100
389
  </React.StrictMode>
101
390
  );`;
102
- writeFileSync(join(srcDir, "main.tsx"), mainTsx);
103
391
  const appTsx = `import { SchemaRenderer } from '@object-ui/react';
104
392
  import '@object-ui/components';
105
393
  import '@object-ui/plugin-charts';
@@ -117,256 +405,22 @@ function App() {
117
405
  }
118
406
 
119
407
  export default App;`;
120
- writeFileSync(join(srcDir, "App.tsx"), appTsx);
121
- const indexCss = `@tailwind base;
122
- @tailwind components;
123
- @tailwind utilities;
124
-
125
- @layer base {
126
- :root {
127
- --background: 0 0% 100%;
128
- --foreground: 222.2 84% 4.9%;
129
- --card: 0 0% 100%;
130
- --card-foreground: 222.2 84% 4.9%;
131
- --popover: 0 0% 100%;
132
- --popover-foreground: 222.2 84% 4.9%;
133
- --primary: 222.2 47.4% 11.2%;
134
- --primary-foreground: 210 40% 98%;
135
- --secondary: 210 40% 96.1%;
136
- --secondary-foreground: 222.2 47.4% 11.2%;
137
- --muted: 210 40% 96.1%;
138
- --muted-foreground: 215.4 16.3% 46.9%;
139
- --accent: 210 40% 96.1%;
140
- --accent-foreground: 222.2 47.4% 11.2%;
141
- --destructive: 0 84.2% 60.2%;
142
- --destructive-foreground: 210 40% 98%;
143
- --border: 214.3 31.8% 91.4%;
144
- --input: 214.3 31.8% 91.4%;
145
- --ring: 222.2 84% 4.9%;
146
- --radius: 0.5rem;
147
- --chart-1: 12 76% 61%;
148
- --chart-2: 173 58% 39%;
149
- --chart-3: 197 37% 24%;
150
- --chart-4: 43 74% 66%;
151
- --chart-5: 27 87% 67%;
152
- }
153
-
154
- .dark {
155
- --background: 222.2 84% 4.9%;
156
- --foreground: 210 40% 98%;
157
- --card: 222.2 84% 4.9%;
158
- --card-foreground: 210 40% 98%;
159
- --popover: 222.2 84% 4.9%;
160
- --popover-foreground: 210 40% 98%;
161
- --primary: 210 40% 98%;
162
- --primary-foreground: 222.2 47.4% 11.2%;
163
- --secondary: 217.2 32.6% 17.5%;
164
- --secondary-foreground: 210 40% 98%;
165
- --muted: 217.2 32.6% 17.5%;
166
- --muted-foreground: 215 20.2% 65.1%;
167
- --accent: 217.2 32.6% 17.5%;
168
- --accent-foreground: 210 40% 98%;
169
- --destructive: 0 62.8% 30.6%;
170
- --destructive-foreground: 210 40% 98%;
171
- --border: 217.2 32.6% 17.5%;
172
- --input: 217.2 32.6% 17.5%;
173
- --ring: 212.7 26.8% 83.9%;
174
- --chart-1: 220 70% 50%;
175
- --chart-2: 160 60% 45%;
176
- --chart-3: 30 80% 55%;
177
- --chart-4: 280 65% 60%;
178
- --chart-5: 340 75% 55%;
179
- }
180
- }
181
-
182
- @layer base {
183
- * {
184
- @apply border-border;
185
- }
186
- body {
187
- @apply bg-background text-foreground;
188
- }
189
- }`;
190
- writeFileSync(join(srcDir, "index.css"), indexCss);
191
- const tailwindConfig = `/** @type {import('tailwindcss').Config} */
192
- export default {
193
- darkMode: ['class'],
194
- content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
195
- theme: {
196
- extend: {
197
- borderRadius: {
198
- lg: 'var(--radius)',
199
- md: 'calc(var(--radius) - 2px)',
200
- sm: 'calc(var(--radius) - 4px)',
201
- },
202
- colors: {
203
- background: 'hsl(var(--background))',
204
- foreground: 'hsl(var(--foreground))',
205
- card: {
206
- DEFAULT: 'hsl(var(--card))',
207
- foreground: 'hsl(var(--card-foreground))',
208
- },
209
- popover: {
210
- DEFAULT: 'hsl(var(--popover))',
211
- foreground: 'hsl(var(--popover-foreground))',
212
- },
213
- primary: {
214
- DEFAULT: 'hsl(var(--primary))',
215
- foreground: 'hsl(var(--primary-foreground))',
216
- },
217
- secondary: {
218
- DEFAULT: 'hsl(var(--secondary))',
219
- foreground: 'hsl(var(--secondary-foreground))',
220
- },
221
- muted: {
222
- DEFAULT: 'hsl(var(--muted))',
223
- foreground: 'hsl(var(--muted-foreground))',
224
- },
225
- accent: {
226
- DEFAULT: 'hsl(var(--accent))',
227
- foreground: 'hsl(var(--accent-foreground))',
228
- },
229
- destructive: {
230
- DEFAULT: 'hsl(var(--destructive))',
231
- foreground: 'hsl(var(--destructive-foreground))',
232
- },
233
- border: 'hsl(var(--border))',
234
- input: 'hsl(var(--input))',
235
- ring: 'hsl(var(--ring))',
236
- chart: {
237
- 1: 'hsl(var(--chart-1))',
238
- 2: 'hsl(var(--chart-2))',
239
- 3: 'hsl(var(--chart-3))',
240
- 4: 'hsl(var(--chart-4))',
241
- 5: 'hsl(var(--chart-5))',
242
- },
243
- },
244
- },
245
- },
246
- plugins: [],
247
- };`;
248
- const cwd = process.cwd();
249
- const isMonorepo = existsSync(join(cwd, "pnpm-workspace.yaml"));
250
- const contentPaths = ["'./index.html'", "'./src/**/*.{js,ts,jsx,tsx,json}'"];
251
- if (isMonorepo) {
252
- const componentsPath = join(cwd, "packages/components/src/**/*.{ts,tsx}");
253
- const pluginsPath = join(cwd, "packages/plugin-*/src/**/*.{ts,tsx}");
254
- contentPaths.push(`'${componentsPath}'`);
255
- contentPaths.push(`'${pluginsPath}'`);
256
- }
257
- const finalTailwindConfig = `/** @type {import('tailwindcss').Config} */
258
- export default {
259
- darkMode: ['class'],
260
- content: [${contentPaths.join(", ")}],
261
- theme: {
262
- extend: {
263
- borderRadius: {
264
- lg: 'var(--radius)',
265
- md: 'calc(var(--radius) - 2px)',
266
- sm: 'calc(var(--radius) - 4px)',
267
- },
268
- colors: {
269
- background: 'hsl(var(--background))',
270
- foreground: 'hsl(var(--foreground))',
271
- card: {
272
- DEFAULT: 'hsl(var(--card))',
273
- foreground: 'hsl(var(--card-foreground))',
274
- },
275
- popover: {
276
- DEFAULT: 'hsl(var(--popover))',
277
- foreground: 'hsl(var(--popover-foreground))',
278
- },
279
- primary: {
280
- DEFAULT: 'hsl(var(--primary))',
281
- foreground: 'hsl(var(--primary-foreground))',
282
- },
283
- secondary: {
284
- DEFAULT: 'hsl(var(--secondary))',
285
- foreground: 'hsl(var(--secondary-foreground))',
286
- },
287
- muted: {
288
- DEFAULT: 'hsl(var(--muted))',
289
- foreground: 'hsl(var(--muted-foreground))',
290
- },
291
- accent: {
292
- DEFAULT: 'hsl(var(--accent))',
293
- foreground: 'hsl(var(--accent-foreground))',
294
- },
295
- destructive: {
296
- DEFAULT: 'hsl(var(--destructive))',
297
- foreground: 'hsl(var(--destructive-foreground))',
298
- },
299
- border: 'hsl(var(--border))',
300
- input: 'hsl(var(--input))',
301
- ring: 'hsl(var(--ring))',
302
- chart: {
303
- 1: 'hsl(var(--chart-1))',
304
- 2: 'hsl(var(--chart-2))',
305
- 3: 'hsl(var(--chart-3))',
306
- 4: 'hsl(var(--chart-4))',
307
- 5: 'hsl(var(--chart-5))',
308
- },
309
- },
310
- },
311
- },
312
- plugins: [],
313
- };`;
314
- writeFileSync(join(tmpDir, "tailwind.config.js"), finalTailwindConfig);
315
- const finalPostcssConfig = `export default {
316
- plugins: {
317
- tailwindcss: {},
318
- autoprefixer: {},
319
- },
320
- };`;
321
- writeFileSync(join(tmpDir, "postcss.config.js"), finalPostcssConfig);
322
- const baseDependencies = {
323
- react: "^18.3.1",
324
- "react-dom": "^18.3.1",
325
- "@object-ui/react": "^0.1.0",
326
- "@object-ui/components": "^0.1.0"
327
- };
328
- const baseDevDependencies = {
329
- "@types/react": "^18.3.12",
330
- "@types/react-dom": "^18.3.1",
331
- "@vitejs/plugin-react": "^4.2.1",
332
- autoprefixer: "^10.4.23",
333
- postcss: "^8.5.6",
334
- tailwindcss: "^3.4.19",
335
- typescript: "~5.7.3",
336
- vite: "^5.0.0"
337
- };
338
- const packageJson = {
339
- name: "objectui-temp-app",
340
- private: true,
341
- type: "module",
342
- // In monorepo, we use root node_modules, so we don't need dependencies here
343
- dependencies: isMonorepo ? {} : baseDependencies,
344
- devDependencies: isMonorepo ? {} : baseDevDependencies
345
- };
346
- writeFileSync(join(tmpDir, "package.json"), JSON.stringify(packageJson, null, 2));
347
- const tsconfig = {
348
- compilerOptions: {
349
- target: "ES2020",
350
- useDefineForClassFields: true,
351
- lib: ["ES2020", "DOM", "DOM.Iterable"],
352
- module: "ESNext",
353
- skipLibCheck: true,
354
- moduleResolution: "bundler",
355
- allowImportingTsExtensions: true,
356
- resolveJsonModule: true,
357
- isolatedModules: true,
358
- noEmit: true,
359
- jsx: "react-jsx",
360
- strict: true,
361
- noUnusedLocals: true,
362
- noUnusedParameters: true,
363
- noFallthroughCasesInSwitch: true
364
- },
365
- include: ["src"]
408
+ return {
409
+ "index.html": html,
410
+ "src/main.tsx": mainTsx,
411
+ "src/App.tsx": appTsx,
412
+ "src/vite-env.d.ts": APP_VITE_ENV_D_TS,
413
+ "src/index.css": buildAppIndexCss(context),
414
+ "postcss.config.js": APP_POSTCSS_CONFIG,
415
+ "package.json": JSON.stringify(buildAppPackageJson(context), null, 2),
416
+ "tsconfig.json": JSON.stringify(APP_TSCONFIG, null, 2)
366
417
  };
367
- writeFileSync(join(tmpDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
368
418
  }
369
419
  function createTempAppWithRouting(tmpDir, routes, appConfig) {
420
+ writeGeneratedFiles(tmpDir, buildRoutedAppFiles(routes, appConfig, currentContext()));
421
+ }
422
+ function buildRoutedAppFiles(routes, appConfig, context) {
423
+ const files = {};
370
424
  const html = `<!DOCTYPE html>
371
425
  <html lang="en">
372
426
  <head>
@@ -379,20 +433,13 @@ function createTempAppWithRouting(tmpDir, routes, appConfig) {
379
433
  <script type="module" src="/src/main.tsx"></script>
380
434
  </body>
381
435
  </html>`;
382
- writeFileSync(join(tmpDir, "index.html"), html);
383
- const srcDir = join(tmpDir, "src");
384
- mkdirSync(srcDir, { recursive: true });
385
- const schemasDir = join(srcDir, "schemas");
386
- mkdirSync(schemasDir, { recursive: true });
436
+ files["index.html"] = html;
387
437
  const schemaImports = [];
388
438
  const routeComponents = [];
389
439
  routes.forEach((route, index) => {
390
440
  const schemaVarName = `schema${index}`;
391
441
  const schemaFileName = `page${index}.json`;
392
- writeFileSync(
393
- join(schemasDir, schemaFileName),
394
- JSON.stringify(route.schema, null, 2)
395
- );
442
+ files[`src/schemas/${schemaFileName}`] = JSON.stringify(route.schema, null, 2);
396
443
  schemaImports.push(`import ${schemaVarName} from './schemas/${schemaFileName}';`);
397
444
  routeComponents.push(` <Route path="${route.path}" element={<SchemaRenderer schema={${schemaVarName}} />} />`);
398
445
  });
@@ -469,7 +516,7 @@ export const useTheme = () => {
469
516
 
470
517
  return context
471
518
  }`;
472
- writeFileSync(join(srcDir, "theme-provider.tsx"), themeProviderTsx);
519
+ files["src/theme-provider.tsx"] = themeProviderTsx;
473
520
  const mainTsx = `import React from 'react';
474
521
  import ReactDOM from 'react-dom/client';
475
522
  import App from './App';
@@ -483,19 +530,18 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
483
530
  </ThemeProvider>
484
531
  </React.StrictMode>
485
532
  );`;
486
- writeFileSync(join(srcDir, "main.tsx"), mainTsx);
533
+ files["src/main.tsx"] = mainTsx;
487
534
  let layoutImport = "";
488
535
  let layoutWrapperStart = "";
489
536
  let layoutWrapperEnd = "";
490
537
  if (appConfig) {
491
538
  const layoutCode = `
539
+ import type { ComponentType, ReactNode } from 'react';
492
540
  import { Link, useLocation } from 'react-router-dom';
493
541
  import * as LucideIcons from 'lucide-react';
494
542
  import { Moon, Sun } from "lucide-react"
495
543
  import { useTheme } from "./theme-provider"
496
- import {
497
- cn,
498
- Button,
544
+ import {
499
545
  DropdownMenu,
500
546
  DropdownMenuContent,
501
547
  DropdownMenuItem,
@@ -507,8 +553,6 @@ import {
507
553
  SidebarHeader,
508
554
  SidebarRail,
509
555
  SidebarGroup,
510
- SidebarGroupContent,
511
- SidebarGroupLabel,
512
556
  SidebarMenu,
513
557
  SidebarMenuItem,
514
558
  SidebarMenuButton,
@@ -521,11 +565,36 @@ import {
521
565
  Collapsible,
522
566
  CollapsibleTrigger,
523
567
  CollapsibleContent
524
- } from '@object-ui/components';
568
+ } from '@object-ui/components';
569
+
570
+ /** One entry of \`app.json\`'s \`menu\`, as this layout reads it. */
571
+ type MenuItem = {
572
+ label?: string;
573
+ path?: string;
574
+ icon?: string;
575
+ children?: MenuItem[];
576
+ };
577
+
578
+ /** The \`app.json\` fields this layout renders. */
579
+ type AppConfig = {
580
+ title?: string;
581
+ logo?: string;
582
+ menu?: MenuItem[];
583
+ };
584
+
585
+ // Lucide publishes every icon as a named export, and a menu entry names one as
586
+ // a string \u2014 so the lookup is a runtime index into the namespace object, which
587
+ // has no index signature. Narrowing it to the component-by-name shape this file
588
+ // actually uses keeps that honest without an \`any\` or a \`@ts-expect-error\`; the
589
+ // \`undefined\` arm is real, since the namespace also exports helpers that are not
590
+ // components and a schema may name an icon that does not exist.
591
+ const lucideIcons = LucideIcons as unknown as Record<
592
+ string,
593
+ ComponentType<{ className?: string }> | undefined
594
+ >;
525
595
 
526
- const DynamicIcon = ({ name, className }) => {
527
- // @ts-expect-error - Dynamic icon lookup from Lucide icons
528
- const Icon = LucideIcons[name];
596
+ const DynamicIcon = ({ name, className }: { name: string; className?: string }) => {
597
+ const Icon = lucideIcons[name];
529
598
  if (!Icon) return null;
530
599
  return <Icon className={className} />;
531
600
  };
@@ -566,7 +635,7 @@ export function ModeToggle() {
566
635
  )
567
636
  }
568
637
 
569
- const AppLayout = ({ app, children }) => {
638
+ const AppLayout = ({ app, children }: { app: AppConfig; children: ReactNode }) => {
570
639
  const location = useLocation();
571
640
  const menu = app.menu || [];
572
641
 
@@ -668,13 +737,13 @@ const AppLayout = ({ app, children }) => {
668
737
 
669
738
  export default AppLayout;
670
739
  `;
671
- writeFileSync(join(srcDir, "Layout.tsx"), layoutCode);
740
+ files["src/Layout.tsx"] = layoutCode;
672
741
  layoutImport = `import AppLayout from './Layout';
673
742
  const appConfig = ${JSON.stringify(appConfig)};`;
674
743
  layoutWrapperStart = `<AppLayout app={appConfig}>`;
675
744
  layoutWrapperEnd = `</AppLayout>`;
676
745
  }
677
- const appTsx = `import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
746
+ const appTsx = `import { BrowserRouter, Routes, Route } from 'react-router-dom';
678
747
  import { SchemaRenderer } from '@object-ui/react';
679
748
  import '@object-ui/components';
680
749
  import '@object-ui/plugin-charts';
@@ -700,202 +769,20 @@ ${routeComponents.join("\n")}
700
769
  }
701
770
 
702
771
  export default App;`;
703
- writeFileSync(join(srcDir, "App.tsx"), appTsx);
704
- const indexCss = `@tailwind base;
705
- @tailwind components;
706
- @tailwind utilities;
707
-
708
- @layer base {
709
- :root {
710
- --background: 0 0% 100%;
711
- --foreground: 222.2 84% 4.9%;
712
- --card: 0 0% 100%;
713
- --card-foreground: 222.2 84% 4.9%;
714
- --popover: 0 0% 100%;
715
- --popover-foreground: 222.2 84% 4.9%;
716
- --primary: 222.2 47.4% 11.2%;
717
- --primary-foreground: 210 40% 98%;
718
- --secondary: 210 40% 96.1%;
719
- --secondary-foreground: 222.2 47.4% 11.2%;
720
- --muted: 210 40% 96.1%;
721
- --muted-foreground: 215.4 16.3% 46.9%;
722
- --accent: 210 40% 96.1%;
723
- --accent-foreground: 222.2 47.4% 11.2%;
724
- --destructive: 0 84.2% 60.2%;
725
- --destructive-foreground: 210 40% 98%;
726
- --border: 214.3 31.8% 91.4%;
727
- --input: 214.3 31.8% 91.4%;
728
- --ring: 222.2 84% 4.9%;
729
- --radius: 0.5rem;
730
- --chart-1: 12 76% 61%;
731
- --chart-2: 173 58% 39%;
732
- --chart-3: 197 37% 24%;
733
- --chart-4: 43 74% 66%;
734
- --chart-5: 27 87% 67%;
735
- }
736
-
737
- .dark {
738
- --background: 222.2 84% 4.9%;
739
- --foreground: 210 40% 98%;
740
- --card: 222.2 84% 4.9%;
741
- --card-foreground: 210 40% 98%;
742
- --popover: 222.2 84% 4.9%;
743
- --popover-foreground: 210 40% 98%;
744
- --primary: 210 40% 98%;
745
- --primary-foreground: 222.2 47.4% 11.2%;
746
- --secondary: 217.2 32.6% 17.5%;
747
- --secondary-foreground: 210 40% 98%;
748
- --muted: 217.2 32.6% 17.5%;
749
- --muted-foreground: 215 20.2% 65.1%;
750
- --accent: 217.2 32.6% 17.5%;
751
- --accent-foreground: 210 40% 98%;
752
- --destructive: 0 62.8% 30.6%;
753
- --destructive-foreground: 210 40% 98%;
754
- --border: 217.2 32.6% 17.5%;
755
- --input: 217.2 32.6% 17.5%;
756
- --ring: 212.7 26.8% 83.9%;
757
- --chart-1: 220 70% 50%;
758
- --chart-2: 160 60% 45%;
759
- --chart-3: 30 80% 55%;
760
- --chart-4: 280 65% 60%;
761
- --chart-5: 340 75% 55%;
762
- }
763
- }
764
-
765
- @layer base {
766
- * {
767
- @apply border-border;
768
- }
769
- body {
770
- @apply bg-background text-foreground font-sans antialiased min-h-screen;
771
- }
772
- }`;
773
- writeFileSync(join(srcDir, "index.css"), indexCss);
774
- const cwd = process.cwd();
775
- const isMonorepo = existsSync(join(cwd, "pnpm-workspace.yaml"));
776
- const contentPaths = ["'./index.html'", "'./src/**/*.{js,ts,jsx,tsx,json}'"];
777
- if (isMonorepo) {
778
- const componentsPath = join(cwd, "packages/components/src/**/*.{ts,tsx}");
779
- const pluginsPath = join(cwd, "packages/plugin-*/src/**/*.{ts,tsx}");
780
- contentPaths.push(`'${componentsPath}'`);
781
- contentPaths.push(`'${pluginsPath}'`);
782
- }
783
- const tailwindConfig = `/** @type {import('tailwindcss').Config} */
784
- export default {
785
- darkMode: ['class'],
786
- content: [${contentPaths.join(", ")}],
787
- theme: {
788
- extend: {
789
- borderRadius: {
790
- lg: 'var(--radius)',
791
- md: 'calc(var(--radius) - 2px)',
792
- sm: 'calc(var(--radius) - 4px)',
793
- },
794
- colors: {
795
- background: 'hsl(var(--background))',
796
- foreground: 'hsl(var(--foreground))',
797
- card: {
798
- DEFAULT: 'hsl(var(--card))',
799
- foreground: 'hsl(var(--card-foreground))',
800
- },
801
- popover: {
802
- DEFAULT: 'hsl(var(--popover))',
803
- foreground: 'hsl(var(--popover-foreground))',
804
- },
805
- primary: {
806
- DEFAULT: 'hsl(var(--primary))',
807
- foreground: 'hsl(var(--primary-foreground))',
808
- },
809
- secondary: {
810
- DEFAULT: 'hsl(var(--secondary))',
811
- foreground: 'hsl(var(--secondary-foreground))',
812
- },
813
- muted: {
814
- DEFAULT: 'hsl(var(--muted))',
815
- foreground: 'hsl(var(--muted-foreground))',
816
- },
817
- accent: {
818
- DEFAULT: 'hsl(var(--accent))',
819
- foreground: 'hsl(var(--accent-foreground))',
820
- },
821
- destructive: {
822
- DEFAULT: 'hsl(var(--destructive))',
823
- foreground: 'hsl(var(--destructive-foreground))',
824
- },
825
- border: 'hsl(var(--border))',
826
- input: 'hsl(var(--input))',
827
- ring: 'hsl(var(--ring))',
828
- chart: {
829
- 1: 'hsl(var(--chart-1))',
830
- 2: 'hsl(var(--chart-2))',
831
- 3: 'hsl(var(--chart-3))',
832
- 4: 'hsl(var(--chart-4))',
833
- 5: 'hsl(var(--chart-5))',
834
- },
835
- },
836
- },
837
- },
838
- plugins: [],
839
- };`;
840
- writeFileSync(join(tmpDir, "tailwind.config.js"), tailwindConfig);
841
- const postcssConfig = `export default {
842
- plugins: {
843
- tailwindcss: {},
844
- autoprefixer: {},
845
- },
846
- };`;
847
- writeFileSync(join(tmpDir, "postcss.config.js"), postcssConfig);
848
- const packageJson = {
849
- name: "objectui-temp-app",
850
- private: true,
851
- type: "module",
852
- dependencies: {
853
- react: "^18.3.1",
854
- "react-dom": "^18.3.1",
855
- "react-router-dom": "^7.12.0",
856
- "@object-ui/react": "^0.1.0",
857
- "@object-ui/components": "^0.1.0"
858
- },
859
- devDependencies: {
860
- "@types/react": "^18.3.12",
861
- "@types/react-dom": "^18.3.1",
862
- "@vitejs/plugin-react": "^4.2.1",
863
- autoprefixer: "^10.4.23",
864
- postcss: "^8.5.6",
865
- tailwindcss: "^3.4.19",
866
- typescript: "~5.7.3",
867
- vite: "^5.0.0"
868
- }
869
- };
870
- writeFileSync(join(tmpDir, "package.json"), JSON.stringify(packageJson, null, 2));
871
- const tsconfig = {
872
- compilerOptions: {
873
- target: "ES2020",
874
- useDefineForClassFields: true,
875
- lib: ["ES2020", "DOM", "DOM.Iterable"],
876
- module: "ESNext",
877
- skipLibCheck: true,
878
- moduleResolution: "bundler",
879
- allowImportingTsExtensions: true,
880
- resolveJsonModule: true,
881
- isolatedModules: true,
882
- noEmit: true,
883
- jsx: "react-jsx",
884
- strict: true,
885
- noUnusedLocals: true,
886
- noUnusedParameters: true,
887
- noFallthroughCasesInSwitch: true
888
- },
889
- include: ["src"]
890
- };
891
- writeFileSync(join(tmpDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
772
+ files["src/App.tsx"] = appTsx;
773
+ files["src/vite-env.d.ts"] = APP_VITE_ENV_D_TS;
774
+ files["src/index.css"] = buildAppIndexCss(context);
775
+ files["postcss.config.js"] = APP_POSTCSS_CONFIG;
776
+ files["package.json"] = JSON.stringify(buildRoutedAppPackageJson(), null, 2);
777
+ files["tsconfig.json"] = JSON.stringify(APP_TSCONFIG, null, 2);
778
+ return files;
892
779
  }
893
780
 
894
781
  // src/commands/serve.ts
895
782
  async function serve(schemaPath, options) {
896
783
  const cwd = process.cwd();
897
- const pagesDir = join2(cwd, "pages");
898
- const hasPagesDir = existsSync2(pagesDir);
784
+ const pagesDir = join3(cwd, "pages");
785
+ const hasPagesDir = existsSync3(pagesDir);
899
786
  let routes = [];
900
787
  let schema = null;
901
788
  let useFileSystemRouting = false;
@@ -912,7 +799,7 @@ async function serve(schemaPath, options) {
912
799
  });
913
800
  } else {
914
801
  const fullSchemaPath = resolve(cwd, schemaPath);
915
- if (!existsSync2(fullSchemaPath)) {
802
+ if (!existsSync3(fullSchemaPath)) {
916
803
  throw new Error(`Schema file not found: ${schemaPath}
917
804
  Run 'objectui init' to create a sample schema.`);
918
805
  }
@@ -923,7 +810,7 @@ Run 'objectui init' to create a sample schema.`);
923
810
  throw new Error(`Invalid schema file: ${error instanceof Error ? error.message : error}`);
924
811
  }
925
812
  }
926
- const tmpDir = join2(cwd, ".objectui-tmp");
813
+ const tmpDir = join3(cwd, ".objectui-tmp");
927
814
  mkdirSync2(tmpDir, { recursive: true });
928
815
  if (useFileSystemRouting) {
929
816
  createTempAppWithRouting(tmpDir, routes);
@@ -967,8 +854,8 @@ Run 'objectui init' to create a sample schema.`);
967
854
  }
968
855
 
969
856
  // src/commands/init.ts
970
- import { existsSync as existsSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
971
- import { join as join3 } from "path";
857
+ import { existsSync as existsSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
858
+ import { dirname as dirname3, join as join4 } from "path";
972
859
  import chalk3 from "chalk";
973
860
  var templates = {
974
861
  simple: {
@@ -1312,29 +1199,35 @@ var templates = {
1312
1199
  ]
1313
1200
  }
1314
1201
  };
1315
- async function init(name, options) {
1316
- const cwd = process.cwd();
1317
- const projectDir = join3(cwd, name);
1318
- if (existsSync3(projectDir) && name !== ".") {
1319
- throw new Error(`Directory "${name}" already exists. Please choose a different name.`);
1320
- }
1321
- const targetDir = name === "." ? cwd : projectDir;
1322
- if (name !== ".") {
1323
- mkdirSync3(projectDir, { recursive: true });
1324
- }
1325
- console.log(chalk3.blue("\u{1F3A8} Creating Object UI application..."));
1326
- console.log(chalk3.dim(` Template: ${options.template}`));
1327
- console.log();
1328
- const template = templates[options.template];
1202
+ function buildInitPackageJson(name) {
1203
+ const platformRange = platformPackageRange();
1204
+ return {
1205
+ name,
1206
+ version: "0.1.0",
1207
+ private: true,
1208
+ type: "module",
1209
+ scripts: {
1210
+ dev: "vite",
1211
+ build: "tsc && vite build",
1212
+ preview: "vite preview"
1213
+ },
1214
+ dependencies: {
1215
+ "@object-ui/components": platformRange,
1216
+ "@object-ui/react": platformRange,
1217
+ react: REACT_RANGE,
1218
+ "react-dom": REACT_RANGE
1219
+ },
1220
+ devDependencies: { ...SCAFFOLD_DEV_DEPENDENCIES }
1221
+ };
1222
+ }
1223
+ function buildInitFiles(name, templateName) {
1224
+ const template = templates[templateName];
1329
1225
  if (!template) {
1330
1226
  throw new Error(
1331
- `Unknown template: ${options.template}
1227
+ `Unknown template: ${templateName}
1332
1228
  Available templates: ${Object.keys(templates).join(", ")}`
1333
1229
  );
1334
1230
  }
1335
- const schemaPath = join3(targetDir, "app.json");
1336
- writeFileSync2(schemaPath, JSON.stringify(template, null, 2));
1337
- console.log(chalk3.green("\u2713 Created app.json"));
1338
1231
  const readme = `# ${name}
1339
1232
 
1340
1233
  An Object UI application built from JSON schemas.
@@ -1377,46 +1270,12 @@ Edit \`app.json\` to customize your application. The dev server will automatical
1377
1270
 
1378
1271
  Built with \u2764\uFE0F using [Object UI](https://www.objectui.org)
1379
1272
  `;
1380
- writeFileSync2(join3(targetDir, "README.md"), readme);
1381
- console.log(chalk3.green("\u2713 Created README.md"));
1382
1273
  const gitignore = `.objectui-tmp
1383
1274
  node_modules
1384
1275
  dist
1385
1276
  .DS_Store
1386
1277
  *.log
1387
1278
  `;
1388
- writeFileSync2(join3(targetDir, ".gitignore"), gitignore);
1389
- console.log(chalk3.green("\u2713 Created .gitignore"));
1390
- const packageJson = {
1391
- name,
1392
- version: "0.1.0",
1393
- private: true,
1394
- type: "module",
1395
- scripts: {
1396
- dev: "vite",
1397
- build: "tsc && vite build",
1398
- preview: "vite preview"
1399
- },
1400
- dependencies: {
1401
- "@object-ui/components": "^2.0.0",
1402
- "@object-ui/react": "^2.0.0",
1403
- react: "^19.2.0",
1404
- "react-dom": "^19.2.0"
1405
- },
1406
- devDependencies: {
1407
- "@tailwindcss/postcss": "^4.1.18",
1408
- "@types/react": "^19.2.13",
1409
- "@types/react-dom": "^19.2.6",
1410
- "@vitejs/plugin-react": "^5.1.3",
1411
- autoprefixer: "^10.4.23",
1412
- postcss: "^8.5.6",
1413
- tailwindcss: "^4.1.18",
1414
- typescript: "^5.9.3",
1415
- vite: "^7.3.1"
1416
- }
1417
- };
1418
- writeFileSync2(join3(targetDir, "package.json"), JSON.stringify(packageJson, null, 2));
1419
- console.log(chalk3.green("\u2713 Created package.json"));
1420
1279
  const viteConfig = `import { defineConfig } from 'vite';
1421
1280
  import react from '@vitejs/plugin-react';
1422
1281
 
@@ -1427,15 +1286,6 @@ export default defineConfig({
1427
1286
  },
1428
1287
  });
1429
1288
  `;
1430
- writeFileSync2(join3(targetDir, "vite.config.ts"), viteConfig);
1431
- console.log(chalk3.green("\u2713 Created vite.config.ts"));
1432
- const tailwindConfig = `/** @type {import('tailwindcss').Config} */
1433
- export default {
1434
- content: ['./index.html', './src/**/*.{ts,tsx}'],
1435
- };
1436
- `;
1437
- writeFileSync2(join3(targetDir, "tailwind.config.js"), tailwindConfig);
1438
- console.log(chalk3.green("\u2713 Created tailwind.config.js"));
1439
1289
  const postcssConfig = `export default {
1440
1290
  plugins: {
1441
1291
  '@tailwindcss/postcss': {},
@@ -1443,8 +1293,6 @@ export default {
1443
1293
  },
1444
1294
  };
1445
1295
  `;
1446
- writeFileSync2(join3(targetDir, "postcss.config.js"), postcssConfig);
1447
- console.log(chalk3.green("\u2713 Created postcss.config.js"));
1448
1296
  const indexHtml = `<!doctype html>
1449
1297
  <html lang="en">
1450
1298
  <head>
@@ -1458,14 +1306,9 @@ export default {
1458
1306
  </body>
1459
1307
  </html>
1460
1308
  `;
1461
- writeFileSync2(join3(targetDir, "index.html"), indexHtml);
1462
- console.log(chalk3.green("\u2713 Created index.html"));
1463
- const srcDir = join3(targetDir, "src");
1464
- mkdirSync3(srcDir, { recursive: true });
1465
1309
  const indexCss = `@import 'tailwindcss';
1310
+ @import '@object-ui/components/style.css';
1466
1311
  `;
1467
- writeFileSync2(join3(srcDir, "index.css"), indexCss);
1468
- console.log(chalk3.green("\u2713 Created src/index.css"));
1469
1312
  const mainTsx = `import { StrictMode } from 'react';
1470
1313
  import { createRoot } from 'react-dom/client';
1471
1314
  import App from './App';
@@ -1477,17 +1320,18 @@ createRoot(document.getElementById('root')!).render(
1477
1320
  </StrictMode>,
1478
1321
  );
1479
1322
  `;
1480
- writeFileSync2(join3(srcDir, "main.tsx"), mainTsx);
1481
- console.log(chalk3.green("\u2713 Created src/main.tsx"));
1482
1323
  const appTsx = `import { SchemaRenderer } from '@object-ui/react';
1324
+ // Registers the component renderers SchemaRenderer looks up. Side-effect
1325
+ // import \u2014 removing it makes every node render "Unknown component type".
1326
+ import '@object-ui/components';
1483
1327
  import schema from '../app.json';
1484
1328
 
1485
1329
  export default function App() {
1486
1330
  return <SchemaRenderer schema={schema} />;
1487
1331
  }
1488
1332
  `;
1489
- writeFileSync2(join3(srcDir, "App.tsx"), appTsx);
1490
- console.log(chalk3.green("\u2713 Created src/App.tsx"));
1333
+ const viteEnvDts = `/// <reference types="vite/client" />
1334
+ `;
1491
1335
  const tsconfig = {
1492
1336
  compilerOptions: {
1493
1337
  target: "ES2020",
@@ -1509,8 +1353,42 @@ export default function App() {
1509
1353
  },
1510
1354
  include: ["src"]
1511
1355
  };
1512
- writeFileSync2(join3(targetDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
1513
- console.log(chalk3.green("\u2713 Created tsconfig.json"));
1356
+ return {
1357
+ "app.json": JSON.stringify(template, null, 2),
1358
+ "README.md": readme,
1359
+ ".gitignore": gitignore,
1360
+ "package.json": JSON.stringify(buildInitPackageJson(name), null, 2),
1361
+ "vite.config.ts": viteConfig,
1362
+ "postcss.config.js": postcssConfig,
1363
+ "index.html": indexHtml,
1364
+ "src/index.css": indexCss,
1365
+ "src/main.tsx": mainTsx,
1366
+ "src/App.tsx": appTsx,
1367
+ "src/vite-env.d.ts": viteEnvDts,
1368
+ "tsconfig.json": JSON.stringify(tsconfig, null, 2)
1369
+ };
1370
+ }
1371
+ async function init(name, options) {
1372
+ const cwd = process.cwd();
1373
+ const projectDir = join4(cwd, name);
1374
+ if (existsSync4(projectDir) && name !== ".") {
1375
+ throw new Error(`Directory "${name}" already exists. Please choose a different name.`);
1376
+ }
1377
+ const targetDir = name === "." ? cwd : projectDir;
1378
+ if (name !== ".") {
1379
+ mkdirSync3(projectDir, { recursive: true });
1380
+ }
1381
+ console.log(chalk3.blue("\u{1F3A8} Creating Object UI application..."));
1382
+ console.log(chalk3.dim(` Template: ${options.template}`));
1383
+ console.log();
1384
+ for (const [relativePath, contents] of Object.entries(
1385
+ buildInitFiles(name, options.template)
1386
+ )) {
1387
+ const target = join4(targetDir, relativePath);
1388
+ mkdirSync3(dirname3(target), { recursive: true });
1389
+ writeFileSync2(target, contents);
1390
+ console.log(chalk3.green(`\u2713 Created ${relativePath}`));
1391
+ }
1514
1392
  console.log();
1515
1393
  console.log(chalk3.green("\u2728 Application created successfully!"));
1516
1394
  console.log();
@@ -1534,4 +1412,4 @@ export {
1534
1412
  serve,
1535
1413
  init
1536
1414
  };
1537
- //# sourceMappingURL=chunk-EV2DCI7W.js.map
1415
+ //# sourceMappingURL=chunk-2MZA766D.js.map