@decocms/start 0.31.1 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -1
- package/scripts/migrate/colors.ts +46 -0
- package/scripts/migrate/phase-analyze.ts +402 -0
- package/scripts/migrate/phase-cleanup.ts +212 -0
- package/scripts/migrate/phase-report.ts +171 -0
- package/scripts/migrate/phase-scaffold.ts +133 -0
- package/scripts/migrate/phase-transform.ts +102 -0
- package/scripts/migrate/phase-verify.ts +308 -0
- package/scripts/migrate/templates/knip-config.ts +27 -0
- package/scripts/migrate/templates/package-json.ts +98 -0
- package/scripts/migrate/templates/routes.ts +280 -0
- package/scripts/migrate/templates/server-entry.ts +163 -0
- package/scripts/migrate/templates/setup.ts +30 -0
- package/scripts/migrate/templates/tsconfig.ts +21 -0
- package/scripts/migrate/templates/vite-config.ts +108 -0
- package/scripts/migrate/templates/wrangler.ts +25 -0
- package/scripts/migrate/transforms/deno-isms.ts +59 -0
- package/scripts/migrate/transforms/fresh-apis.ts +218 -0
- package/scripts/migrate/transforms/imports.ts +217 -0
- package/scripts/migrate/transforms/jsx.ts +184 -0
- package/scripts/migrate/transforms/tailwind.ts +409 -0
- package/scripts/migrate/types.ts +141 -0
- package/scripts/migrate.ts +135 -0
- package/scripts/tailwind-lint.ts +518 -0
- package/src/types/widgets.ts +1 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { MigrationContext } from "./types.ts";
|
|
4
|
+
import { logPhase } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
interface Check {
|
|
7
|
+
name: string;
|
|
8
|
+
fn: (ctx: MigrationContext) => boolean;
|
|
9
|
+
severity: "error" | "warning";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const REQUIRED_FILES = [
|
|
13
|
+
"package.json",
|
|
14
|
+
"tsconfig.json",
|
|
15
|
+
"vite.config.ts",
|
|
16
|
+
"wrangler.jsonc",
|
|
17
|
+
"knip.config.ts",
|
|
18
|
+
".prettierrc",
|
|
19
|
+
"src/server.ts",
|
|
20
|
+
"src/worker-entry.ts",
|
|
21
|
+
"src/router.tsx",
|
|
22
|
+
"src/runtime.ts",
|
|
23
|
+
"src/context.ts",
|
|
24
|
+
"src/setup.ts",
|
|
25
|
+
"src/styles/app.css",
|
|
26
|
+
"src/apps/site.ts",
|
|
27
|
+
"src/routes/__root.tsx",
|
|
28
|
+
"src/routes/index.tsx",
|
|
29
|
+
"src/routes/$.tsx",
|
|
30
|
+
"src/routes/deco/meta.ts",
|
|
31
|
+
"src/routes/deco/invoke.$.ts",
|
|
32
|
+
"src/routes/deco/render.ts",
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const MUST_NOT_EXIST = [
|
|
36
|
+
"deno.json",
|
|
37
|
+
"fresh.gen.ts",
|
|
38
|
+
"manifest.gen.ts",
|
|
39
|
+
"dev.ts",
|
|
40
|
+
"main.ts",
|
|
41
|
+
"islands/BlogFeed.tsx",
|
|
42
|
+
"routes/_app.tsx",
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const checks: Check[] = [
|
|
46
|
+
{
|
|
47
|
+
name: "All scaffolded files exist",
|
|
48
|
+
severity: "error",
|
|
49
|
+
fn: (ctx) => {
|
|
50
|
+
const missing = REQUIRED_FILES.filter(
|
|
51
|
+
(f) => !fs.existsSync(path.join(ctx.sourceDir, f)),
|
|
52
|
+
);
|
|
53
|
+
if (missing.length > 0) {
|
|
54
|
+
console.log(` Missing: ${missing.join(", ")}`);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "Old artifacts removed",
|
|
62
|
+
severity: "error",
|
|
63
|
+
fn: (ctx) => {
|
|
64
|
+
const remaining = MUST_NOT_EXIST.filter(
|
|
65
|
+
(f) => fs.existsSync(path.join(ctx.sourceDir, f)),
|
|
66
|
+
);
|
|
67
|
+
if (remaining.length > 0) {
|
|
68
|
+
console.log(` Still exists: ${remaining.join(", ")}`);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "No preact imports in src/",
|
|
76
|
+
severity: "error",
|
|
77
|
+
fn: (ctx) => {
|
|
78
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
79
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
80
|
+
const bad = findFilesWithPattern(srcDir, /from\s+["']preact/);
|
|
81
|
+
if (bad.length > 0) {
|
|
82
|
+
console.log(` Still has preact imports: ${bad.join(", ")}`);
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "No $fresh imports in src/",
|
|
90
|
+
severity: "error",
|
|
91
|
+
fn: (ctx) => {
|
|
92
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
93
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
94
|
+
const bad = findFilesWithPattern(srcDir, /from\s+["']\$fresh/);
|
|
95
|
+
if (bad.length > 0) {
|
|
96
|
+
console.log(` Still has $fresh imports: ${bad.join(", ")}`);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "No deno-lint-ignore in src/",
|
|
104
|
+
severity: "warning",
|
|
105
|
+
fn: (ctx) => {
|
|
106
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
107
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
108
|
+
const bad = findFilesWithPattern(srcDir, /deno-lint-ignore/);
|
|
109
|
+
if (bad.length > 0) {
|
|
110
|
+
console.log(` Still has deno-lint-ignore: ${bad.join(", ")}`);
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'No class= in JSX (should be className=)',
|
|
118
|
+
severity: "warning",
|
|
119
|
+
fn: (ctx) => {
|
|
120
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
121
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
122
|
+
const bad = findFilesWithPattern(srcDir, /<[a-zA-Z][^>]*\sclass\s*=/);
|
|
123
|
+
if (bad.length > 0) {
|
|
124
|
+
console.log(` Still has class= in JSX: ${bad.slice(0, 5).join(", ")}${bad.length > 5 ? ` (+${bad.length - 5} more)` : ""}`);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "public/ has static assets",
|
|
132
|
+
severity: "warning",
|
|
133
|
+
fn: (ctx) => {
|
|
134
|
+
const publicDir = path.join(ctx.sourceDir, "public");
|
|
135
|
+
if (!fs.existsSync(publicDir)) {
|
|
136
|
+
console.log(" public/ directory missing");
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const hasSprites = fs.existsSync(
|
|
140
|
+
path.join(publicDir, "sprites.svg"),
|
|
141
|
+
);
|
|
142
|
+
const hasFavicon = fs.existsSync(
|
|
143
|
+
path.join(publicDir, "favicon.ico"),
|
|
144
|
+
);
|
|
145
|
+
if (!hasSprites) console.log(" Missing: public/sprites.svg");
|
|
146
|
+
if (!hasFavicon) console.log(" Missing: public/favicon.ico");
|
|
147
|
+
return hasSprites && hasFavicon;
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: "package.json has correct dependencies",
|
|
152
|
+
severity: "error",
|
|
153
|
+
fn: (ctx) => {
|
|
154
|
+
const pkgPath = path.join(ctx.sourceDir, "package.json");
|
|
155
|
+
if (!fs.existsSync(pkgPath)) return false;
|
|
156
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
157
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
158
|
+
const required = [
|
|
159
|
+
"@decocms/start",
|
|
160
|
+
"@decocms/apps",
|
|
161
|
+
"react",
|
|
162
|
+
"react-dom",
|
|
163
|
+
"@tanstack/react-start",
|
|
164
|
+
"vite",
|
|
165
|
+
"knip",
|
|
166
|
+
];
|
|
167
|
+
const missing = required.filter((d) => !deps[d]);
|
|
168
|
+
if (missing.length > 0) {
|
|
169
|
+
console.log(` Missing deps: ${missing.join(", ")}`);
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: "No site/ imports (should be ~/)",
|
|
177
|
+
severity: "warning",
|
|
178
|
+
fn: (ctx) => {
|
|
179
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
180
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
181
|
+
const bad = findFilesWithPattern(srcDir, /from\s+["']site\//);
|
|
182
|
+
if (bad.length > 0) {
|
|
183
|
+
console.log(` Still has site/ imports: ${bad.join(", ")}`);
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "No .ts/.tsx extensions in relative import paths",
|
|
191
|
+
severity: "warning",
|
|
192
|
+
fn: (ctx) => {
|
|
193
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
194
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
195
|
+
// Match relative imports with .ts/.tsx extensions
|
|
196
|
+
const bad = findFilesWithPattern(srcDir, /from\s+["'](?:\.\.?\/|~\/)[^"']*\.tsx?["']/);
|
|
197
|
+
if (bad.length > 0) {
|
|
198
|
+
console.log(` Still has .ts/.tsx extensions in imports: ${bad.join(", ")}`);
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "No for= in JSX (should be htmlFor=)",
|
|
206
|
+
severity: "warning",
|
|
207
|
+
fn: (ctx) => {
|
|
208
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
209
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
210
|
+
const bad = findFilesWithPattern(srcDir, /<label[^>]*\sfor\s*=/);
|
|
211
|
+
if (bad.length > 0) {
|
|
212
|
+
console.log(` Still has for= in JSX: ${bad.join(", ")}`);
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "No relative imports to deleted SDK files",
|
|
220
|
+
severity: "error",
|
|
221
|
+
fn: (ctx) => {
|
|
222
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
223
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
224
|
+
// Only match relative imports (../ or ./) to deleted SDK files, not @decocms/* package imports
|
|
225
|
+
const bad = findFilesWithPattern(srcDir, /from\s+["'](?:\.\.?\/)[^"']*\/sdk\/(?:clx|useId|useOffer|useVariantPossiblities|usePlatform)["']/);
|
|
226
|
+
if (bad.length > 0) {
|
|
227
|
+
console.log(` Still has relative imports to deleted SDK files: ${bad.join(", ")}`);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
return true;
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "No imports to deleted static files",
|
|
235
|
+
severity: "error",
|
|
236
|
+
fn: (ctx) => {
|
|
237
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
238
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
239
|
+
const bad = findFilesWithPattern(srcDir, /from\s+["'][^"']*static\/adminIcons/);
|
|
240
|
+
if (bad.length > 0) {
|
|
241
|
+
console.log(` Still has imports to static/adminIcons: ${bad.join(", ")}`);
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
return true;
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
function findFilesWithPattern(
|
|
250
|
+
dir: string,
|
|
251
|
+
pattern: RegExp,
|
|
252
|
+
results: string[] = [],
|
|
253
|
+
baseDir?: string,
|
|
254
|
+
): string[] {
|
|
255
|
+
const root = baseDir ?? dir;
|
|
256
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
257
|
+
for (const entry of entries) {
|
|
258
|
+
const fullPath = path.join(dir, entry.name);
|
|
259
|
+
if (entry.isDirectory()) {
|
|
260
|
+
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "server") continue;
|
|
261
|
+
findFilesWithPattern(fullPath, pattern, results, root);
|
|
262
|
+
} else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
|
|
263
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
264
|
+
if (pattern.test(content)) {
|
|
265
|
+
results.push(path.basename(fullPath));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return results;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function verify(ctx: MigrationContext): boolean {
|
|
273
|
+
logPhase("Verify (Smoke Test)");
|
|
274
|
+
|
|
275
|
+
if (ctx.dryRun) {
|
|
276
|
+
console.log(" Skipping verify in dry-run mode\n");
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
let errors = 0;
|
|
281
|
+
let warnings = 0;
|
|
282
|
+
|
|
283
|
+
for (const check of checks) {
|
|
284
|
+
const pass = check.fn(ctx);
|
|
285
|
+
const icon = pass ? "\x1b[32m✓\x1b[0m" : check.severity === "error" ? "\x1b[31m✗\x1b[0m" : "\x1b[33m⚠\x1b[0m";
|
|
286
|
+
console.log(` ${icon} ${check.name}`);
|
|
287
|
+
if (!pass) {
|
|
288
|
+
if (check.severity === "error") errors++;
|
|
289
|
+
else warnings++;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
console.log(
|
|
294
|
+
`\n Result: ${checks.length - errors - warnings} passed, ${errors} errors, ${warnings} warnings`,
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
if (errors > 0) {
|
|
298
|
+
console.log(" \x1b[31mVerification FAILED — migration has issues that must be fixed\x1b[0m");
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
if (warnings > 0) {
|
|
302
|
+
console.log(" \x1b[33mVerification passed with warnings\x1b[0m");
|
|
303
|
+
} else {
|
|
304
|
+
console.log(" \x1b[32mVerification PASSED\x1b[0m");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function generateKnipConfig(): string {
|
|
2
|
+
return `import type { KnipConfig } from "knip";
|
|
3
|
+
|
|
4
|
+
const config: KnipConfig = {
|
|
5
|
+
entry: [
|
|
6
|
+
"src/routes/**/*.{ts,tsx}",
|
|
7
|
+
"src/setup.ts",
|
|
8
|
+
"src/runtime.ts",
|
|
9
|
+
"src/sections/**/*.{ts,tsx}",
|
|
10
|
+
"vite.config.ts",
|
|
11
|
+
],
|
|
12
|
+
project: ["src/**/*.{ts,tsx}"],
|
|
13
|
+
ignore: [
|
|
14
|
+
"src/server/invoke.gen.ts",
|
|
15
|
+
"src/server/cms/blocks.gen.ts",
|
|
16
|
+
"src/routeTree.gen.ts",
|
|
17
|
+
],
|
|
18
|
+
ignoreDependencies: [
|
|
19
|
+
"babel-plugin-react-compiler",
|
|
20
|
+
"@vitejs/plugin-react",
|
|
21
|
+
"wrangler",
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default config;
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { MigrationContext } from "../types.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract npm dependencies from deno.json import map.
|
|
5
|
+
* Entries like `"fuse.js": "npm:fuse.js@7.0.0"` become `"fuse.js": "^7.0.0"`.
|
|
6
|
+
*/
|
|
7
|
+
function extractNpmDeps(importMap: Record<string, string>): Record<string, string> {
|
|
8
|
+
const deps: Record<string, string> = {};
|
|
9
|
+
for (const [key, value] of Object.entries(importMap)) {
|
|
10
|
+
if (!value.startsWith("npm:")) continue;
|
|
11
|
+
// Skip preact, deco, and other framework deps we handle ourselves
|
|
12
|
+
if (key.startsWith("preact") || key.startsWith("@preact/")) continue;
|
|
13
|
+
if (key.startsWith("@deco/")) continue;
|
|
14
|
+
if (key === "daisyui") continue; // we pin our own version
|
|
15
|
+
|
|
16
|
+
const raw = value.slice(4); // remove "npm:"
|
|
17
|
+
const atIdx = raw.lastIndexOf("@");
|
|
18
|
+
if (atIdx <= 0) {
|
|
19
|
+
deps[raw] = "*";
|
|
20
|
+
} else {
|
|
21
|
+
const name = raw.slice(0, atIdx);
|
|
22
|
+
const version = raw.slice(atIdx + 1);
|
|
23
|
+
deps[name] = `^${version}`;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return deps;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function generatePackageJson(ctx: MigrationContext): string {
|
|
30
|
+
const siteDeps = {
|
|
31
|
+
...extractNpmDeps(ctx.importMap),
|
|
32
|
+
...ctx.discoveredNpmDeps,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const pkg = {
|
|
36
|
+
name: ctx.siteName,
|
|
37
|
+
version: "0.1.0",
|
|
38
|
+
type: "module",
|
|
39
|
+
description: `${ctx.siteName} storefront powered by TanStack Start`,
|
|
40
|
+
scripts: {
|
|
41
|
+
dev: "vite dev",
|
|
42
|
+
"generate:blocks":
|
|
43
|
+
"tsx node_modules/@decocms/start/scripts/generate-blocks.ts",
|
|
44
|
+
"generate:routes": "tsr generate",
|
|
45
|
+
"generate:schema": `tsx node_modules/@decocms/start/scripts/generate-schema.ts --site ${ctx.siteName}`,
|
|
46
|
+
build:
|
|
47
|
+
"npm run generate:blocks && npm run generate:schema && tsr generate && vite build",
|
|
48
|
+
preview: "vite preview",
|
|
49
|
+
deploy: "npm run build && wrangler deploy",
|
|
50
|
+
types: "wrangler types",
|
|
51
|
+
typecheck: "tsc --noEmit",
|
|
52
|
+
format: 'prettier --write "src/**/*.{ts,tsx}"',
|
|
53
|
+
"format:check": 'prettier --check "src/**/*.{ts,tsx}"',
|
|
54
|
+
knip: "knip",
|
|
55
|
+
clean:
|
|
56
|
+
"rm -rf node_modules .cache dist .wrangler/state node_modules/.vite && npm install",
|
|
57
|
+
"tailwind:lint":
|
|
58
|
+
"tsx node_modules/@decocms/start/scripts/tailwind-lint.ts",
|
|
59
|
+
"tailwind:fix":
|
|
60
|
+
"tsx node_modules/@decocms/start/scripts/tailwind-lint.ts --fix",
|
|
61
|
+
},
|
|
62
|
+
author: "deco.cx",
|
|
63
|
+
license: "MIT",
|
|
64
|
+
dependencies: {
|
|
65
|
+
"@decocms/apps": "^0.25.2",
|
|
66
|
+
"@decocms/start": "^0.32.0",
|
|
67
|
+
"@tanstack/react-query": "5.90.21",
|
|
68
|
+
"@tanstack/react-router": "1.166.7",
|
|
69
|
+
"@tanstack/react-start": "1.166.8",
|
|
70
|
+
"@tanstack/react-store": "0.9.2",
|
|
71
|
+
"@tanstack/store": "0.9.2",
|
|
72
|
+
"colorjs.io": "^0.6.1",
|
|
73
|
+
react: "^19.2.4",
|
|
74
|
+
"react-dom": "^19.2.4",
|
|
75
|
+
...siteDeps,
|
|
76
|
+
},
|
|
77
|
+
devDependencies: {
|
|
78
|
+
"@cloudflare/vite-plugin": "^1.27.0",
|
|
79
|
+
"@tailwindcss/vite": "^4.2.1",
|
|
80
|
+
"@tanstack/router-cli": "1.166.7",
|
|
81
|
+
"@types/react": "^19.2.14",
|
|
82
|
+
"@types/react-dom": "^19.2.3",
|
|
83
|
+
"@vitejs/plugin-react": "^5.1.4",
|
|
84
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
85
|
+
"daisyui": "^5.5.19",
|
|
86
|
+
knip: "^5.61.2",
|
|
87
|
+
prettier: "^3.5.3",
|
|
88
|
+
tailwindcss: "^4.2.1",
|
|
89
|
+
"ts-morph": "^27.0.2",
|
|
90
|
+
tsx: "^4.19.4",
|
|
91
|
+
typescript: "^5.9.3",
|
|
92
|
+
vite: "^7.3.1",
|
|
93
|
+
wrangler: "^4.72.0",
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return JSON.stringify(pkg, null, 2) + "\n";
|
|
98
|
+
}
|