@object-ui/cli 17.3.0 → 17.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.
- package/CHANGELOG.md +537 -0
- package/README.md +6 -8
- package/dist/{chunk-EV2DCI7W.js → chunk-AP2INOE7.js} +717 -599
- package/dist/chunk-AP2INOE7.js.map +1 -0
- package/dist/cli.js +1018 -299
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/package.json +11 -9
- package/dist/chunk-EV2DCI7W.js.map +0 -1
|
@@ -1,16 +1,60 @@
|
|
|
1
1
|
// src/commands/serve.ts
|
|
2
|
-
import { createServer } from "vite";
|
|
2
|
+
import { createServer, createLogger } from "vite";
|
|
3
3
|
import react from "@vitejs/plugin-react";
|
|
4
|
-
import {
|
|
5
|
-
import { join as
|
|
6
|
-
import
|
|
4
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
5
|
+
import { join as join5 } from "path";
|
|
6
|
+
import chalk3 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 =
|
|
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 =
|
|
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.31.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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,257 +769,313 @@ ${routeComponents.join("\n")}
|
|
|
700
769
|
}
|
|
701
770
|
|
|
702
771
|
export default App;`;
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
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
|
-
}
|
|
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;
|
|
779
|
+
}
|
|
736
780
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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%;
|
|
781
|
+
// src/utils/project-source.ts
|
|
782
|
+
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
783
|
+
import { basename, dirname as dirname3, join as join3, relative, resolve } from "path";
|
|
784
|
+
import chalk2 from "chalk";
|
|
785
|
+
function readAppConfig(path, warnings) {
|
|
786
|
+
try {
|
|
787
|
+
return parseSchemaFile(path);
|
|
788
|
+
} catch (error) {
|
|
789
|
+
warnings.push(
|
|
790
|
+
`Failed to parse app config ${path}: ${error instanceof Error ? error.message : error}`
|
|
791
|
+
);
|
|
792
|
+
return null;
|
|
762
793
|
}
|
|
763
794
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
795
|
+
function resolveProjectSource(cwd, schemaArg) {
|
|
796
|
+
const warnings = [];
|
|
797
|
+
const absoluteSchemaPath = resolve(cwd, schemaArg);
|
|
798
|
+
if (existsSync3(absoluteSchemaPath)) {
|
|
799
|
+
const argumentStats = statSync2(absoluteSchemaPath);
|
|
800
|
+
if (argumentStats.isFile()) {
|
|
801
|
+
const projectRoot = dirname3(absoluteSchemaPath);
|
|
802
|
+
const pagesDir = join3(projectRoot, "pages");
|
|
803
|
+
if (existsSync3(pagesDir)) {
|
|
804
|
+
return {
|
|
805
|
+
mode: "routes",
|
|
806
|
+
projectRoot,
|
|
807
|
+
pagesDir,
|
|
808
|
+
pagesDirOrigin: "schema-dir",
|
|
809
|
+
routes: scanRoutes(pagesDir),
|
|
810
|
+
appConfig: readAppConfig(absoluteSchemaPath, warnings),
|
|
811
|
+
appConfigPath: absoluteSchemaPath,
|
|
812
|
+
warnings
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
} else if (argumentStats.isDirectory()) {
|
|
816
|
+
const fromDirectory = resolveDirectoryArgument(absoluteSchemaPath, warnings);
|
|
817
|
+
if (fromDirectory) return fromDirectory;
|
|
818
|
+
}
|
|
768
819
|
}
|
|
769
|
-
|
|
770
|
-
|
|
820
|
+
const localPagesDir = join3(cwd, "pages");
|
|
821
|
+
if (existsSync3(localPagesDir)) {
|
|
822
|
+
const localAppConfigPath = join3(cwd, "app.json");
|
|
823
|
+
const hasLocalAppConfig = existsSync3(localAppConfigPath);
|
|
824
|
+
return {
|
|
825
|
+
mode: "routes",
|
|
826
|
+
projectRoot: cwd,
|
|
827
|
+
pagesDir: localPagesDir,
|
|
828
|
+
pagesDirOrigin: "cwd",
|
|
829
|
+
routes: scanRoutes(localPagesDir),
|
|
830
|
+
appConfig: hasLocalAppConfig ? readAppConfig(localAppConfigPath, warnings) : null,
|
|
831
|
+
appConfigPath: hasLocalAppConfig ? localAppConfigPath : null,
|
|
832
|
+
warnings
|
|
833
|
+
};
|
|
771
834
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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}'`);
|
|
835
|
+
if (!existsSync3(absoluteSchemaPath)) {
|
|
836
|
+
throw new Error(
|
|
837
|
+
`Schema file not found: ${schemaArg}
|
|
838
|
+
Run 'objectui init' to create a sample schema.`
|
|
839
|
+
);
|
|
782
840
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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
|
-
}
|
|
841
|
+
if (statSync2(absoluteSchemaPath).isDirectory()) {
|
|
842
|
+
throw new Error(
|
|
843
|
+
`Not a project directory: ${schemaArg}
|
|
844
|
+
A directory argument must be a 'pages' directory, or contain one, for file-system routing \u2014 ${absoluteSchemaPath} is neither.
|
|
845
|
+
Pass a JSON/YAML schema file instead, or run 'objectui init' to create a project.`
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
let schema;
|
|
849
|
+
try {
|
|
850
|
+
schema = parseSchemaFile(absoluteSchemaPath);
|
|
851
|
+
} catch (error) {
|
|
852
|
+
throw new Error(`Invalid schema file: ${error instanceof Error ? error.message : error}`);
|
|
853
|
+
}
|
|
854
|
+
return {
|
|
855
|
+
mode: "schema",
|
|
856
|
+
projectRoot: dirname3(absoluteSchemaPath),
|
|
857
|
+
schemaFile: absoluteSchemaPath,
|
|
858
|
+
schema,
|
|
859
|
+
warnings
|
|
869
860
|
};
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
861
|
+
}
|
|
862
|
+
function resolveDirectoryArgument(argumentDir, warnings) {
|
|
863
|
+
const isPagesDir = basename(argumentDir) === "pages";
|
|
864
|
+
const pagesDir = isPagesDir ? argumentDir : join3(argumentDir, "pages");
|
|
865
|
+
if (!isPagesDir && !(existsSync3(pagesDir) && statSync2(pagesDir).isDirectory())) {
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
const projectRoot = isPagesDir ? dirname3(argumentDir) : argumentDir;
|
|
869
|
+
const appConfigPath = join3(projectRoot, "app.json");
|
|
870
|
+
const hasAppConfig = existsSync3(appConfigPath) && statSync2(appConfigPath).isFile();
|
|
871
|
+
return {
|
|
872
|
+
mode: "routes",
|
|
873
|
+
projectRoot,
|
|
874
|
+
pagesDir,
|
|
875
|
+
pagesDirOrigin: "argument-dir",
|
|
876
|
+
routes: scanRoutes(pagesDir),
|
|
877
|
+
appConfig: hasAppConfig ? readAppConfig(appConfigPath, warnings) : null,
|
|
878
|
+
appConfigPath: hasAppConfig ? appConfigPath : null,
|
|
879
|
+
warnings
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
function scanRoutes(pagesDir) {
|
|
883
|
+
const routes = scanPagesDirectory(pagesDir);
|
|
884
|
+
if (routes.length === 0) {
|
|
885
|
+
throw new Error(`No schema files found in ${pagesDir}`);
|
|
886
|
+
}
|
|
887
|
+
return routes;
|
|
888
|
+
}
|
|
889
|
+
function reportProjectSource(source, cwd) {
|
|
890
|
+
for (const warning of source.warnings) {
|
|
891
|
+
console.warn(chalk2.yellow(`\u26A0 ${warning}`));
|
|
892
|
+
}
|
|
893
|
+
if (source.mode === "schema") {
|
|
894
|
+
console.log(chalk2.blue("\u{1F4CB} Loading schema:"), chalk2.cyan(relative(cwd, source.schemaFile) || source.schemaFile));
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
console.log(chalk2.blue(`\u{1F4C2} Detected project structure at ${source.projectRoot}`));
|
|
898
|
+
if (source.appConfigPath && source.appConfig) {
|
|
899
|
+
console.log(chalk2.blue(`\u2699\uFE0F Loaded App Config from ${relative(cwd, source.appConfigPath) || source.appConfigPath}`));
|
|
900
|
+
}
|
|
901
|
+
console.log(chalk2.blue(`\u{1F4C1} Using file-system routing from ${source.pagesDir}`));
|
|
902
|
+
console.log(chalk2.green(`\u2713 Found ${source.routes.length} route(s)`));
|
|
903
|
+
for (const route of source.routes) {
|
|
904
|
+
console.log(chalk2.dim(` ${route.path} \u2192 ${relative(cwd, route.filePath)}`));
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// src/utils/workspace-vite.ts
|
|
909
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, statSync as statSync3, unlinkSync } from "fs";
|
|
910
|
+
import { createRequire } from "module";
|
|
911
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
912
|
+
import { globSync } from "glob";
|
|
913
|
+
import { load as loadYaml } from "js-yaml";
|
|
914
|
+
var PLATFORM_SCOPE = "@object-ui/";
|
|
915
|
+
var BARREL_EXTENSIONS = [".mjs", ".js", ".mts", ".ts", ".jsx", ".tsx"];
|
|
916
|
+
function isWorkspaceRoot(cwd) {
|
|
917
|
+
return existsSync4(join4(cwd, "pnpm-workspace.yaml"));
|
|
918
|
+
}
|
|
919
|
+
function workspacePatterns(cwd) {
|
|
920
|
+
const manifestPath = join4(cwd, "pnpm-workspace.yaml");
|
|
921
|
+
if (!existsSync4(manifestPath)) return [];
|
|
922
|
+
const parsed = loadYaml(readFileSync3(manifestPath, "utf-8"));
|
|
923
|
+
const patterns = parsed?.packages;
|
|
924
|
+
if (!Array.isArray(patterns)) return [];
|
|
925
|
+
return patterns.filter((p) => typeof p === "string");
|
|
926
|
+
}
|
|
927
|
+
function workspacePackageDirs(cwd) {
|
|
928
|
+
const patterns = workspacePatterns(cwd);
|
|
929
|
+
const include = patterns.filter((p) => !p.startsWith("!"));
|
|
930
|
+
const exclude = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
|
|
931
|
+
if (include.length === 0) return [];
|
|
932
|
+
const matches = globSync(include, {
|
|
933
|
+
cwd,
|
|
934
|
+
absolute: true,
|
|
935
|
+
ignore: ["**/node_modules/**", ...exclude]
|
|
936
|
+
});
|
|
937
|
+
return matches.filter((dir) => existsSync4(join4(dir, "package.json")) && statSync3(dir).isDirectory()).sort();
|
|
938
|
+
}
|
|
939
|
+
function findSourceBarrel(srcDir) {
|
|
940
|
+
for (const ext of BARREL_EXTENSIONS) {
|
|
941
|
+
const candidate = join4(srcDir, `index${ext}`);
|
|
942
|
+
if (existsSync4(candidate)) return candidate;
|
|
943
|
+
}
|
|
944
|
+
return void 0;
|
|
945
|
+
}
|
|
946
|
+
function workspaceSourceAliases(cwd) {
|
|
947
|
+
const aliases = {};
|
|
948
|
+
for (const dir of workspacePackageDirs(cwd)) {
|
|
949
|
+
let name;
|
|
950
|
+
try {
|
|
951
|
+
name = JSON.parse(readFileSync3(join4(dir, "package.json"), "utf-8")).name;
|
|
952
|
+
} catch {
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
if (typeof name !== "string" || !name.startsWith(PLATFORM_SCOPE)) continue;
|
|
956
|
+
const srcDir = join4(dir, "src");
|
|
957
|
+
if (!existsSync4(srcDir) || !findSourceBarrel(srcDir)) continue;
|
|
958
|
+
aliases[name] = srcDir;
|
|
959
|
+
}
|
|
960
|
+
return aliases;
|
|
961
|
+
}
|
|
962
|
+
function resolveLucideAlias(cwd) {
|
|
963
|
+
const require2 = createRequire(join4(cwd, "package.json"));
|
|
964
|
+
try {
|
|
965
|
+
return dirname4(require2.resolve("lucide-react/package.json", { paths: [join4(cwd, "packages/components")] }));
|
|
966
|
+
} catch {
|
|
967
|
+
return void 0;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
async function loadTempAppPostcssPlugins() {
|
|
971
|
+
try {
|
|
972
|
+
const [tailwindPostcss, autoprefixer] = await Promise.all([
|
|
973
|
+
import("@tailwindcss/postcss"),
|
|
974
|
+
import("autoprefixer")
|
|
975
|
+
]);
|
|
976
|
+
return [tailwindPostcss.default(), autoprefixer.default()];
|
|
977
|
+
} catch (error) {
|
|
978
|
+
throw new Error(
|
|
979
|
+
`Failed to load the Tailwind CSS PostCSS pipeline: ${error instanceof Error ? error.message : error}
|
|
980
|
+
Both '@tailwindcss/postcss' and 'autoprefixer' are dependencies of @object-ui/cli \u2014 a
|
|
981
|
+
broken install of the CLI is the likeliest cause; reinstall it and try again.
|
|
982
|
+
(Refusing to start unstyled: that failure is silent in the browser.)`
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
async function prepareWorkspaceTempApp(cwd, tmpDir) {
|
|
987
|
+
const postcssPath = join4(tmpDir, "postcss.config.js");
|
|
988
|
+
if (existsSync4(postcssPath)) {
|
|
989
|
+
unlinkSync(postcssPath);
|
|
990
|
+
}
|
|
991
|
+
const alias = { ...workspaceSourceAliases(cwd) };
|
|
992
|
+
const lucide = resolveLucideAlias(cwd);
|
|
993
|
+
if (lucide) {
|
|
994
|
+
alias["lucide-react"] = lucide;
|
|
995
|
+
}
|
|
996
|
+
return {
|
|
997
|
+
resolve: { alias },
|
|
998
|
+
css: { postcss: { plugins: await loadTempAppPostcssPlugins() } }
|
|
890
999
|
};
|
|
891
|
-
writeFileSync(join(tmpDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
892
1000
|
}
|
|
893
1001
|
|
|
894
1002
|
// src/commands/serve.ts
|
|
1003
|
+
function resolveServeOpenOption(open) {
|
|
1004
|
+
return open !== false;
|
|
1005
|
+
}
|
|
1006
|
+
var BROWSER_OPEN_SPAWN_ENOENT = /^Error: spawn (\S+) ENOENT/;
|
|
1007
|
+
function createServeLogger(base) {
|
|
1008
|
+
return {
|
|
1009
|
+
info: (msg, opts) => base.info(msg, opts),
|
|
1010
|
+
warn: (msg, opts) => base.warn(msg, opts),
|
|
1011
|
+
warnOnce: (msg, opts) => base.warnOnce(msg, opts),
|
|
1012
|
+
error(msg, opts) {
|
|
1013
|
+
const match = BROWSER_OPEN_SPAWN_ENOENT.exec(msg);
|
|
1014
|
+
if (match) {
|
|
1015
|
+
const [, command] = match;
|
|
1016
|
+
console.log(
|
|
1017
|
+
chalk3.dim(
|
|
1018
|
+
` (could not open the browser automatically \u2014 '${command}' is not available in this environment; open the URL above manually)`
|
|
1019
|
+
)
|
|
1020
|
+
);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
base.error(msg, opts);
|
|
1024
|
+
},
|
|
1025
|
+
clearScreen: (type) => base.clearScreen(type),
|
|
1026
|
+
hasErrorLogged: (error) => base.hasErrorLogged(error),
|
|
1027
|
+
get hasWarned() {
|
|
1028
|
+
return base.hasWarned;
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
895
1032
|
async function serve(schemaPath, options) {
|
|
896
1033
|
const cwd = process.cwd();
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
console.log(chalk2.blue("\u{1F4C1} Detected pages/ directory - using file-system routing"));
|
|
904
|
-
routes = scanPagesDirectory(pagesDir);
|
|
905
|
-
useFileSystemRouting = true;
|
|
906
|
-
if (routes.length === 0) {
|
|
907
|
-
throw new Error("No schema files found in pages/ directory");
|
|
908
|
-
}
|
|
909
|
-
console.log(chalk2.green(`\u2713 Found ${routes.length} route(s)`));
|
|
910
|
-
routes.forEach((route) => {
|
|
911
|
-
console.log(chalk2.dim(` ${route.path} \u2192 ${relative(cwd, route.filePath)}`));
|
|
912
|
-
});
|
|
1034
|
+
const source = resolveProjectSource(cwd, schemaPath);
|
|
1035
|
+
reportProjectSource(source, cwd);
|
|
1036
|
+
const tmpDir = join5(cwd, ".objectui-tmp");
|
|
1037
|
+
mkdirSync2(tmpDir, { recursive: true });
|
|
1038
|
+
if (source.mode === "routes") {
|
|
1039
|
+
createTempAppWithRouting(tmpDir, source.routes, source.appConfig);
|
|
913
1040
|
} else {
|
|
914
|
-
|
|
915
|
-
if (!existsSync2(fullSchemaPath)) {
|
|
916
|
-
throw new Error(`Schema file not found: ${schemaPath}
|
|
917
|
-
Run 'objectui init' to create a sample schema.`);
|
|
918
|
-
}
|
|
919
|
-
console.log(chalk2.blue("\u{1F4CB} Loading schema:"), chalk2.cyan(schemaPath));
|
|
920
|
-
try {
|
|
921
|
-
schema = parseSchemaFile(fullSchemaPath);
|
|
922
|
-
} catch (error) {
|
|
923
|
-
throw new Error(`Invalid schema file: ${error instanceof Error ? error.message : error}`);
|
|
924
|
-
}
|
|
1041
|
+
createTempApp(tmpDir, source.schema);
|
|
925
1042
|
}
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
createTempAppWithRouting(tmpDir, routes);
|
|
1043
|
+
const isMonorepo = isWorkspaceRoot(cwd);
|
|
1044
|
+
if (isMonorepo) {
|
|
1045
|
+
console.log(chalk3.blue("\u{1F4E6} Detected monorepo - using root node_modules"));
|
|
930
1046
|
} else {
|
|
931
|
-
|
|
1047
|
+
console.log(chalk3.blue("\u{1F4E6} Installing dependencies..."));
|
|
1048
|
+
console.log(chalk3.dim(" This may take a moment on first run..."));
|
|
1049
|
+
try {
|
|
1050
|
+
execSync("npm install --silent --prefer-offline", {
|
|
1051
|
+
cwd: tmpDir,
|
|
1052
|
+
stdio: "inherit"
|
|
1053
|
+
});
|
|
1054
|
+
console.log(chalk3.green("\u2713 Dependencies installed"));
|
|
1055
|
+
} catch {
|
|
1056
|
+
throw new Error("Failed to install dependencies. Please check your internet connection and try again.");
|
|
1057
|
+
}
|
|
932
1058
|
}
|
|
933
|
-
console.log(
|
|
934
|
-
console.log(
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
cwd: tmpDir,
|
|
938
|
-
stdio: "inherit"
|
|
939
|
-
});
|
|
940
|
-
console.log(chalk2.green("\u2713 Dependencies installed"));
|
|
941
|
-
} catch {
|
|
942
|
-
throw new Error("Failed to install dependencies. Please check your internet connection and try again.");
|
|
1059
|
+
console.log(chalk3.green("\u2713 Schema loaded successfully"));
|
|
1060
|
+
console.log(chalk3.blue("\u{1F680} Starting development server...\n"));
|
|
1061
|
+
if (isMonorepo) {
|
|
1062
|
+
console.log(chalk3.blue("\u{1F4E6} Detected monorepo - configuring workspace aliases"));
|
|
943
1063
|
}
|
|
944
|
-
|
|
945
|
-
console.log(chalk2.blue("\u{1F680} Starting development server...\n"));
|
|
1064
|
+
const workspaceConfig = isMonorepo ? await prepareWorkspaceTempApp(cwd, tmpDir) : {};
|
|
946
1065
|
const viteConfig = {
|
|
947
1066
|
root: tmpDir,
|
|
948
1067
|
server: {
|
|
949
1068
|
port: parseInt(options.port),
|
|
950
1069
|
host: options.host,
|
|
951
|
-
open:
|
|
1070
|
+
open: resolveServeOpenOption(options.open),
|
|
1071
|
+
fs: {
|
|
1072
|
+
// Allow serving the workspace sources the aliases point at
|
|
1073
|
+
allow: [cwd]
|
|
1074
|
+
}
|
|
952
1075
|
},
|
|
953
|
-
plugins: [react()]
|
|
1076
|
+
plugins: [react()],
|
|
1077
|
+
customLogger: createServeLogger(createLogger()),
|
|
1078
|
+
...workspaceConfig
|
|
954
1079
|
};
|
|
955
1080
|
const server = await createServer(viteConfig);
|
|
956
1081
|
await server.listen();
|
|
@@ -958,18 +1083,18 @@ Run 'objectui init' to create a sample schema.`);
|
|
|
958
1083
|
const protocol = server.config.server.https ? "https" : "http";
|
|
959
1084
|
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
960
1085
|
console.log();
|
|
961
|
-
console.log(
|
|
1086
|
+
console.log(chalk3.green("\u2713 Server started successfully!"));
|
|
962
1087
|
console.log();
|
|
963
|
-
console.log(
|
|
1088
|
+
console.log(chalk3.bold(" Local: ") + chalk3.cyan(`${protocol}://${displayHost}:${port}`));
|
|
964
1089
|
console.log();
|
|
965
|
-
console.log(
|
|
1090
|
+
console.log(chalk3.dim(" Press Ctrl+C to stop the server"));
|
|
966
1091
|
console.log();
|
|
967
1092
|
}
|
|
968
1093
|
|
|
969
1094
|
// src/commands/init.ts
|
|
970
|
-
import { existsSync as
|
|
971
|
-
import { join as
|
|
972
|
-
import
|
|
1095
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
1096
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
1097
|
+
import chalk4 from "chalk";
|
|
973
1098
|
var templates = {
|
|
974
1099
|
simple: {
|
|
975
1100
|
type: "div",
|
|
@@ -1312,29 +1437,35 @@ var templates = {
|
|
|
1312
1437
|
]
|
|
1313
1438
|
}
|
|
1314
1439
|
};
|
|
1315
|
-
|
|
1316
|
-
const
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1440
|
+
function buildInitPackageJson(name) {
|
|
1441
|
+
const platformRange = platformPackageRange();
|
|
1442
|
+
return {
|
|
1443
|
+
name,
|
|
1444
|
+
version: "0.1.0",
|
|
1445
|
+
private: true,
|
|
1446
|
+
type: "module",
|
|
1447
|
+
scripts: {
|
|
1448
|
+
dev: "vite",
|
|
1449
|
+
build: "tsc && vite build",
|
|
1450
|
+
preview: "vite preview"
|
|
1451
|
+
},
|
|
1452
|
+
dependencies: {
|
|
1453
|
+
"@object-ui/components": platformRange,
|
|
1454
|
+
"@object-ui/react": platformRange,
|
|
1455
|
+
react: REACT_RANGE,
|
|
1456
|
+
"react-dom": REACT_RANGE
|
|
1457
|
+
},
|
|
1458
|
+
devDependencies: { ...SCAFFOLD_DEV_DEPENDENCIES }
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
function buildInitFiles(name, templateName) {
|
|
1462
|
+
const template = templates[templateName];
|
|
1329
1463
|
if (!template) {
|
|
1330
1464
|
throw new Error(
|
|
1331
|
-
`Unknown template: ${
|
|
1465
|
+
`Unknown template: ${templateName}
|
|
1332
1466
|
Available templates: ${Object.keys(templates).join(", ")}`
|
|
1333
1467
|
);
|
|
1334
1468
|
}
|
|
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
1469
|
const readme = `# ${name}
|
|
1339
1470
|
|
|
1340
1471
|
An Object UI application built from JSON schemas.
|
|
@@ -1377,46 +1508,12 @@ Edit \`app.json\` to customize your application. The dev server will automatical
|
|
|
1377
1508
|
|
|
1378
1509
|
Built with \u2764\uFE0F using [Object UI](https://www.objectui.org)
|
|
1379
1510
|
`;
|
|
1380
|
-
writeFileSync2(join3(targetDir, "README.md"), readme);
|
|
1381
|
-
console.log(chalk3.green("\u2713 Created README.md"));
|
|
1382
1511
|
const gitignore = `.objectui-tmp
|
|
1383
1512
|
node_modules
|
|
1384
1513
|
dist
|
|
1385
1514
|
.DS_Store
|
|
1386
1515
|
*.log
|
|
1387
1516
|
`;
|
|
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
1517
|
const viteConfig = `import { defineConfig } from 'vite';
|
|
1421
1518
|
import react from '@vitejs/plugin-react';
|
|
1422
1519
|
|
|
@@ -1427,15 +1524,6 @@ export default defineConfig({
|
|
|
1427
1524
|
},
|
|
1428
1525
|
});
|
|
1429
1526
|
`;
|
|
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
1527
|
const postcssConfig = `export default {
|
|
1440
1528
|
plugins: {
|
|
1441
1529
|
'@tailwindcss/postcss': {},
|
|
@@ -1443,8 +1531,6 @@ export default {
|
|
|
1443
1531
|
},
|
|
1444
1532
|
};
|
|
1445
1533
|
`;
|
|
1446
|
-
writeFileSync2(join3(targetDir, "postcss.config.js"), postcssConfig);
|
|
1447
|
-
console.log(chalk3.green("\u2713 Created postcss.config.js"));
|
|
1448
1534
|
const indexHtml = `<!doctype html>
|
|
1449
1535
|
<html lang="en">
|
|
1450
1536
|
<head>
|
|
@@ -1458,14 +1544,9 @@ export default {
|
|
|
1458
1544
|
</body>
|
|
1459
1545
|
</html>
|
|
1460
1546
|
`;
|
|
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
1547
|
const indexCss = `@import 'tailwindcss';
|
|
1548
|
+
@import '@object-ui/components/style.css';
|
|
1466
1549
|
`;
|
|
1467
|
-
writeFileSync2(join3(srcDir, "index.css"), indexCss);
|
|
1468
|
-
console.log(chalk3.green("\u2713 Created src/index.css"));
|
|
1469
1550
|
const mainTsx = `import { StrictMode } from 'react';
|
|
1470
1551
|
import { createRoot } from 'react-dom/client';
|
|
1471
1552
|
import App from './App';
|
|
@@ -1477,17 +1558,18 @@ createRoot(document.getElementById('root')!).render(
|
|
|
1477
1558
|
</StrictMode>,
|
|
1478
1559
|
);
|
|
1479
1560
|
`;
|
|
1480
|
-
writeFileSync2(join3(srcDir, "main.tsx"), mainTsx);
|
|
1481
|
-
console.log(chalk3.green("\u2713 Created src/main.tsx"));
|
|
1482
1561
|
const appTsx = `import { SchemaRenderer } from '@object-ui/react';
|
|
1562
|
+
// Registers the component renderers SchemaRenderer looks up. Side-effect
|
|
1563
|
+
// import \u2014 removing it makes every node render "Unknown component type".
|
|
1564
|
+
import '@object-ui/components';
|
|
1483
1565
|
import schema from '../app.json';
|
|
1484
1566
|
|
|
1485
1567
|
export default function App() {
|
|
1486
1568
|
return <SchemaRenderer schema={schema} />;
|
|
1487
1569
|
}
|
|
1488
1570
|
`;
|
|
1489
|
-
|
|
1490
|
-
|
|
1571
|
+
const viteEnvDts = `/// <reference types="vite/client" />
|
|
1572
|
+
`;
|
|
1491
1573
|
const tsconfig = {
|
|
1492
1574
|
compilerOptions: {
|
|
1493
1575
|
target: "ES2020",
|
|
@@ -1509,29 +1591,65 @@ export default function App() {
|
|
|
1509
1591
|
},
|
|
1510
1592
|
include: ["src"]
|
|
1511
1593
|
};
|
|
1512
|
-
|
|
1513
|
-
|
|
1594
|
+
return {
|
|
1595
|
+
"app.json": JSON.stringify(template, null, 2),
|
|
1596
|
+
"README.md": readme,
|
|
1597
|
+
".gitignore": gitignore,
|
|
1598
|
+
"package.json": JSON.stringify(buildInitPackageJson(name), null, 2),
|
|
1599
|
+
"vite.config.ts": viteConfig,
|
|
1600
|
+
"postcss.config.js": postcssConfig,
|
|
1601
|
+
"index.html": indexHtml,
|
|
1602
|
+
"src/index.css": indexCss,
|
|
1603
|
+
"src/main.tsx": mainTsx,
|
|
1604
|
+
"src/App.tsx": appTsx,
|
|
1605
|
+
"src/vite-env.d.ts": viteEnvDts,
|
|
1606
|
+
"tsconfig.json": JSON.stringify(tsconfig, null, 2)
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
async function init(name, options) {
|
|
1610
|
+
const cwd = process.cwd();
|
|
1611
|
+
const projectDir = join6(cwd, name);
|
|
1612
|
+
if (existsSync5(projectDir) && name !== ".") {
|
|
1613
|
+
throw new Error(`Directory "${name}" already exists. Please choose a different name.`);
|
|
1614
|
+
}
|
|
1615
|
+
const targetDir = name === "." ? cwd : projectDir;
|
|
1616
|
+
if (name !== ".") {
|
|
1617
|
+
mkdirSync3(projectDir, { recursive: true });
|
|
1618
|
+
}
|
|
1619
|
+
console.log(chalk4.blue("\u{1F3A8} Creating Object UI application..."));
|
|
1620
|
+
console.log(chalk4.dim(` Template: ${options.template}`));
|
|
1621
|
+
console.log();
|
|
1622
|
+
for (const [relativePath, contents] of Object.entries(
|
|
1623
|
+
buildInitFiles(name, options.template)
|
|
1624
|
+
)) {
|
|
1625
|
+
const target = join6(targetDir, relativePath);
|
|
1626
|
+
mkdirSync3(dirname5(target), { recursive: true });
|
|
1627
|
+
writeFileSync2(target, contents);
|
|
1628
|
+
console.log(chalk4.green(`\u2713 Created ${relativePath}`));
|
|
1629
|
+
}
|
|
1514
1630
|
console.log();
|
|
1515
|
-
console.log(
|
|
1631
|
+
console.log(chalk4.green("\u2728 Application created successfully!"));
|
|
1516
1632
|
console.log();
|
|
1517
|
-
console.log(
|
|
1633
|
+
console.log(chalk4.bold("Next steps:"));
|
|
1518
1634
|
console.log();
|
|
1519
1635
|
if (name !== ".") {
|
|
1520
|
-
console.log(
|
|
1636
|
+
console.log(chalk4.cyan(` cd ${name}`));
|
|
1521
1637
|
}
|
|
1522
|
-
console.log(
|
|
1523
|
-
console.log(
|
|
1638
|
+
console.log(chalk4.cyan(" npm install"));
|
|
1639
|
+
console.log(chalk4.cyan(" npm run dev"));
|
|
1524
1640
|
console.log();
|
|
1525
|
-
console.log(
|
|
1641
|
+
console.log(chalk4.dim(" The development server will start on http://localhost:3000"));
|
|
1526
1642
|
console.log();
|
|
1527
1643
|
}
|
|
1528
1644
|
|
|
1529
1645
|
export {
|
|
1530
|
-
parseSchemaFile,
|
|
1531
|
-
scanPagesDirectory,
|
|
1532
1646
|
createTempApp,
|
|
1533
1647
|
createTempAppWithRouting,
|
|
1648
|
+
resolveProjectSource,
|
|
1649
|
+
reportProjectSource,
|
|
1650
|
+
isWorkspaceRoot,
|
|
1651
|
+
prepareWorkspaceTempApp,
|
|
1534
1652
|
serve,
|
|
1535
1653
|
init
|
|
1536
1654
|
};
|
|
1537
|
-
//# sourceMappingURL=chunk-
|
|
1655
|
+
//# sourceMappingURL=chunk-AP2INOE7.js.map
|