nextjs-studio 0.3.0 → 1.0.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/README.md +3 -1
- package/dist/bin/nextjs-studio.js +315 -54
- package/dist/bin/nextjs-studio.js.map +1 -1
- package/dist/cli/ui/.next/BUILD_ID +1 -0
- package/dist/cli/ui/.next/app-path-routes-manifest.json +12 -0
- package/dist/cli/ui/.next/build-manifest.json +20 -0
- package/dist/cli/ui/.next/export-marker.json +6 -0
- package/dist/cli/ui/.next/images-manifest.json +67 -0
- package/dist/cli/ui/.next/next-minimal-server.js.nft.json +1 -0
- package/dist/cli/ui/.next/next-server.js.nft.json +1 -0
- package/dist/cli/ui/.next/package.json +1 -0
- package/dist/cli/ui/.next/prerender-manifest.json +61 -0
- package/dist/cli/ui/.next/react-loadable-manifest.json +286 -0
- package/dist/cli/ui/.next/required-server-files.js +338 -0
- package/dist/cli/ui/.next/required-server-files.json +338 -0
- package/dist/cli/ui/.next/routes-manifest.json +125 -0
- package/dist/cli/ui/.next/server/app/_global-error/page.js +3 -0
- package/dist/cli/ui/.next/server/app/_global-error/page.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/_global-error/page_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/_global-error.html +2 -0
- package/dist/cli/ui/.next/server/app/_global-error.meta +16 -0
- package/dist/cli/ui/.next/server/app/_global-error.rsc +12 -0
- package/dist/cli/ui/.next/server/app/_global-error.segments/_full.segment.rsc +12 -0
- package/dist/cli/ui/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +5 -0
- package/dist/cli/ui/.next/server/app/_global-error.segments/_global-error.segment.rsc +4 -0
- package/dist/cli/ui/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
- package/dist/cli/ui/.next/server/app/_global-error.segments/_index.segment.rsc +4 -0
- package/dist/cli/ui/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
- package/dist/cli/ui/.next/server/app/_not-found/page.js +2 -0
- package/dist/cli/ui/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/_not-found.html +1 -0
- package/dist/cli/ui/.next/server/app/_not-found.meta +16 -0
- package/dist/cli/ui/.next/server/app/_not-found.rsc +15 -0
- package/dist/cli/ui/.next/server/app/_not-found.segments/_full.segment.rsc +15 -0
- package/dist/cli/ui/.next/server/app/_not-found.segments/_head.segment.rsc +5 -0
- package/dist/cli/ui/.next/server/app/_not-found.segments/_index.segment.rsc +7 -0
- package/dist/cli/ui/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -0
- package/dist/cli/ui/.next/server/app/_not-found.segments/_not-found.segment.rsc +4 -0
- package/dist/cli/ui/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -0
- package/dist/cli/ui/.next/server/app/api/media/[collection]/[filename]/route.js +1 -0
- package/dist/cli/ui/.next/server/app/api/media/[collection]/[filename]/route.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/api/media/[collection]/[filename]/route_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/api/media/[collection]/route.js +1 -0
- package/dist/cli/ui/.next/server/app/api/media/[collection]/route.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/api/media/[collection]/route_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/api/public/[...path]/route.js +1 -0
- package/dist/cli/ui/.next/server/app/api/public/[...path]/route.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/api/public/[...path]/route_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/api/sync/[collection]/route.js +1 -0
- package/dist/cli/ui/.next/server/app/api/sync/[collection]/route.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/api/sync/[collection]/route_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/api/watch/route.js +3 -0
- package/dist/cli/ui/.next/server/app/api/watch/route.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/api/watch/route_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/collection/[name]/[slug]/page.js +99 -0
- package/dist/cli/ui/.next/server/app/collection/[name]/[slug]/page.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/collection/[name]/[slug]/page_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/collection/[name]/page.js +5 -0
- package/dist/cli/ui/.next/server/app/collection/[name]/page.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/collection/[name]/page_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app/page.js +2 -0
- package/dist/cli/ui/.next/server/app/page.js.nft.json +1 -0
- package/dist/cli/ui/.next/server/app/page_client-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/app-paths-manifest.json +12 -0
- package/dist/cli/ui/.next/server/chunks/1875.js +1 -0
- package/dist/cli/ui/.next/server/chunks/2198.js +1 -0
- package/dist/cli/ui/.next/server/chunks/2446.js +36 -0
- package/dist/cli/ui/.next/server/chunks/2474.js +148 -0
- package/dist/cli/ui/.next/server/chunks/2490.js +55 -0
- package/dist/cli/ui/.next/server/chunks/2717.js +56 -0
- package/dist/cli/ui/.next/server/chunks/2729.js +166 -0
- package/dist/cli/ui/.next/server/chunks/2774.js +1 -0
- package/dist/cli/ui/.next/server/chunks/2796.js +1 -0
- package/dist/cli/ui/.next/server/chunks/3145.js +1 -0
- package/dist/cli/ui/.next/server/chunks/3186.js +1 -0
- package/dist/cli/ui/.next/server/chunks/3494.js +24 -0
- package/dist/cli/ui/.next/server/chunks/3610.js +1 -0
- package/dist/cli/ui/.next/server/chunks/3659.js +93 -0
- package/dist/cli/ui/.next/server/chunks/3744.js +1 -0
- package/dist/cli/ui/.next/server/chunks/3806.js +136 -0
- package/dist/cli/ui/.next/server/chunks/408.js +1 -0
- package/dist/cli/ui/.next/server/chunks/4189.js +62 -0
- package/dist/cli/ui/.next/server/chunks/4293.js +1 -0
- package/dist/cli/ui/.next/server/chunks/4469.js +4 -0
- package/dist/cli/ui/.next/server/chunks/4484.js +1 -0
- package/dist/cli/ui/.next/server/chunks/449.js +82 -0
- package/dist/cli/ui/.next/server/chunks/4607.js +22 -0
- package/dist/cli/ui/.next/server/chunks/479.js +5 -0
- package/dist/cli/ui/.next/server/chunks/4931.js +63 -0
- package/dist/cli/ui/.next/server/chunks/528.js +1 -0
- package/dist/cli/ui/.next/server/chunks/5341.js +1 -0
- package/dist/cli/ui/.next/server/chunks/5585.js +29 -0
- package/dist/cli/ui/.next/server/chunks/5677.js +1 -0
- package/dist/cli/ui/.next/server/chunks/5724.js +1 -0
- package/dist/cli/ui/.next/server/chunks/5760.js +174 -0
- package/dist/cli/ui/.next/server/chunks/5784.js +24 -0
- package/dist/cli/ui/.next/server/chunks/6.js +262 -0
- package/dist/cli/ui/.next/server/chunks/6040.js +1 -0
- package/dist/cli/ui/.next/server/chunks/6067.js +13 -0
- package/dist/cli/ui/.next/server/chunks/6553.js +1 -0
- package/dist/cli/ui/.next/server/chunks/6656.js +201 -0
- package/dist/cli/ui/.next/server/chunks/6672.js +1 -0
- package/dist/cli/ui/.next/server/chunks/6872.js +1 -0
- package/dist/cli/ui/.next/server/chunks/7146.js +59 -0
- package/dist/cli/ui/.next/server/chunks/7354.js +1 -0
- package/dist/cli/ui/.next/server/chunks/7677.js +1 -0
- package/dist/cli/ui/.next/server/chunks/7781.js +1 -0
- package/dist/cli/ui/.next/server/chunks/8194.js +215 -0
- package/dist/cli/ui/.next/server/chunks/8231.js +1 -0
- package/dist/cli/ui/.next/server/chunks/8319.js +2 -0
- package/dist/cli/ui/.next/server/chunks/8339.js +1 -0
- package/dist/cli/ui/.next/server/chunks/8775.js +1 -0
- package/dist/cli/ui/.next/server/chunks/9503.js +43 -0
- package/dist/cli/ui/.next/server/chunks/958.js +131 -0
- package/dist/cli/ui/.next/server/chunks/9739.js +1 -0
- package/dist/cli/ui/.next/server/functions-config-manifest.json +4 -0
- package/dist/cli/ui/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/dist/cli/ui/.next/server/middleware-build-manifest.js +1 -0
- package/dist/cli/ui/.next/server/middleware-manifest.json +6 -0
- package/dist/cli/ui/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/dist/cli/ui/.next/server/next-font-manifest.js +1 -0
- package/dist/cli/ui/.next/server/next-font-manifest.json +1 -0
- package/dist/cli/ui/.next/server/pages/404.html +1 -0
- package/dist/cli/ui/.next/server/pages/500.html +2 -0
- package/dist/cli/ui/.next/server/pages-manifest.json +4 -0
- package/dist/cli/ui/.next/server/server-reference-manifest.js +1 -0
- package/dist/cli/ui/.next/server/server-reference-manifest.json +1 -0
- package/dist/cli/ui/.next/server/webpack-runtime.js +1 -0
- package/dist/cli/ui/.next/static/chunks/0937d497-2b220e2d0368e918.js +1 -0
- package/dist/cli/ui/.next/static/chunks/1013.5541c1c75688ef6e.js +1 -0
- package/dist/cli/ui/.next/static/chunks/1039.d26ef6a818f26d70.js +4 -0
- package/dist/cli/ui/.next/static/chunks/1325.cb67348b484ce7a0.js +24 -0
- package/dist/cli/ui/.next/static/chunks/1858-d10f8bead2218ad2.js +2 -0
- package/dist/cli/ui/.next/static/chunks/1882.05c68ab6bdcd8c31.js +1 -0
- package/dist/cli/ui/.next/static/chunks/2011.7e73842f1e8e2cde.js +1 -0
- package/dist/cli/ui/.next/static/chunks/2046.73486ea936c9afd2.js +1 -0
- package/dist/cli/ui/.next/static/chunks/2389.b73d3a1286a228d0.js +1 -0
- package/dist/cli/ui/.next/static/chunks/2430.08df5899978ebf26.js +43 -0
- package/dist/cli/ui/.next/static/chunks/2462-4255d8f51dc56f12.js +1 -0
- package/dist/cli/ui/.next/static/chunks/3098-4269da0468edc350.js +1 -0
- package/dist/cli/ui/.next/static/chunks/317.b4fcc40f219152b1.js +1 -0
- package/dist/cli/ui/.next/static/chunks/3197.d2f3398e552228e4.js +1 -0
- package/dist/cli/ui/.next/static/chunks/3212-1a9c9d110d15abab.js +4 -0
- package/dist/cli/ui/.next/static/chunks/3809.8df186de2ff997f8.js +1 -0
- package/dist/cli/ui/.next/static/chunks/3918.caaf4eb4f6450524.js +1 -0
- package/dist/cli/ui/.next/static/chunks/3987-6d3217e36ae5b4dc.js +1 -0
- package/dist/cli/ui/.next/static/chunks/3c774391-ec08594272867fd5.js +79 -0
- package/dist/cli/ui/.next/static/chunks/4075052f.9006147a6182ed95.js +1 -0
- package/dist/cli/ui/.next/static/chunks/4389.e7a3c58aa62118e3.js +82 -0
- package/dist/cli/ui/.next/static/chunks/46bdbe0e-f5103600b23143b8.js +1 -0
- package/dist/cli/ui/.next/static/chunks/4860.2ba51d1d81857c81.js +1 -0
- package/dist/cli/ui/.next/static/chunks/4882.794e2a9231f129e5.js +55 -0
- package/dist/cli/ui/.next/static/chunks/5149.8de9918b9ace58f5.js +166 -0
- package/dist/cli/ui/.next/static/chunks/5230.b7c87400f0d8516f.js +215 -0
- package/dist/cli/ui/.next/static/chunks/5263.7adc3ade53cd4ed3.js +24 -0
- package/dist/cli/ui/.next/static/chunks/5313.1dfcc2126f85a8f8.js +1 -0
- package/dist/cli/ui/.next/static/chunks/5350-17c792e27bf38c77.js +1 -0
- package/dist/cli/ui/.next/static/chunks/5638.e3a2119354708654.js +1 -0
- package/dist/cli/ui/.next/static/chunks/5758.33b45c6c407ecd94.js +1 -0
- package/dist/cli/ui/.next/static/chunks/589.019158de5d02c262.js +1 -0
- package/dist/cli/ui/.next/static/chunks/6013.e33e73569339afee.js +62 -0
- package/dist/cli/ui/.next/static/chunks/6084.7511ad4ebb4f9df5.js +1 -0
- package/dist/cli/ui/.next/static/chunks/612.d3aeebcaaaf06fb2.js +1 -0
- package/dist/cli/ui/.next/static/chunks/6239.6c0cd99ea36222b6.js +5 -0
- package/dist/cli/ui/.next/static/chunks/6299-79a74877b87acf9f.js +1 -0
- package/dist/cli/ui/.next/static/chunks/6484.d38aa531768c3452.js +148 -0
- package/dist/cli/ui/.next/static/chunks/6505.731f74c28b8980b3.js +1 -0
- package/dist/cli/ui/.next/static/chunks/6584.be94a55391bcdcce.js +262 -0
- package/dist/cli/ui/.next/static/chunks/6800.1066ad1f5add7c75.js +1 -0
- package/dist/cli/ui/.next/static/chunks/7052.89e2de773d10ac20.js +131 -0
- package/dist/cli/ui/.next/static/chunks/7127.360abfa43dc4427e.js +36 -0
- package/dist/cli/ui/.next/static/chunks/7430.2b247d9ac6ef694c.js +149 -0
- package/dist/cli/ui/.next/static/chunks/7444.66c4d6bbc19309b3.js +59 -0
- package/dist/cli/ui/.next/static/chunks/7546.4cb4db4685212384.js +63 -0
- package/dist/cli/ui/.next/static/chunks/7564.7b9271fcf06df80c.js +29 -0
- package/dist/cli/ui/.next/static/chunks/7664.8268c156d8988844.js +174 -0
- package/dist/cli/ui/.next/static/chunks/7722-04e64cc1b26cfa65.js +1 -0
- package/dist/cli/ui/.next/static/chunks/8701.3995b58a75e43147.js +56 -0
- package/dist/cli/ui/.next/static/chunks/8800.548c3da26cfebf06.js +1 -0
- package/dist/cli/ui/.next/static/chunks/8814-7b64110c79ce7209.js +15 -0
- package/dist/cli/ui/.next/static/chunks/9058.a195d15265251194.js +1 -0
- package/dist/cli/ui/.next/static/chunks/9211.91144a0d23e4cf6d.js +1 -0
- package/dist/cli/ui/.next/static/chunks/9690.c1f08c02a675b9ce.js +1 -0
- package/dist/cli/ui/.next/static/chunks/9919.1ccc9ca4a749d9b7.js +93 -0
- package/dist/cli/ui/.next/static/chunks/app/_global-error/page-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/_not-found/page-202c1e16a4b86ac8.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/api/media/[collection]/[filename]/route-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/api/media/[collection]/route-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/api/public/[...path]/route-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/api/sync/[collection]/route-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/api/watch/route-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/collection/[name]/[slug]/page-7ea9eff252374845.js +6 -0
- package/dist/cli/ui/.next/static/chunks/app/collection/[name]/loading-03fc0b653e7060ae.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/collection/[name]/page-ad5976bebc9820cf.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/layout-74cd2ebe3fac00e7.js +1 -0
- package/dist/cli/ui/.next/static/chunks/app/page-ed5acadf20146966.js +1 -0
- package/dist/cli/ui/.next/static/chunks/b1767599.6a816b06e55b02a1.js +1 -0
- package/dist/cli/ui/.next/static/chunks/c6164070.3b9741bce302db0f.js +136 -0
- package/dist/cli/ui/.next/static/chunks/fb2ceba8.7fc23e46ca00cfd1.js +53 -0
- package/dist/cli/ui/.next/static/chunks/framework-ada624c9bf38edc6.js +1 -0
- package/dist/cli/ui/.next/static/chunks/main-0513ff42a7f2e1a0.js +5 -0
- package/dist/cli/ui/.next/static/chunks/main-app-b8d23dfb755744e8.js +1 -0
- package/dist/cli/ui/.next/static/chunks/next/dist/client/components/builtin/app-error-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/next/dist/client/components/builtin/forbidden-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/next/dist/client/components/builtin/global-error-558ee1074f45044b.js +1 -0
- package/dist/cli/ui/.next/static/chunks/next/dist/client/components/builtin/not-found-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/next/dist/client/components/builtin/unauthorized-ad53913b77389472.js +1 -0
- package/dist/cli/ui/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/cli/ui/.next/static/chunks/webpack-acb903cd88aafa6f.js +1 -0
- package/dist/cli/ui/.next/static/css/e143949aa3f17880.css +3 -0
- package/dist/cli/ui/.next/static/media/034d78ad42e9620c-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/0484562807a97172-s.p.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/29a4aea02fdee119-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/29e7bbdce9332268-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/4c285fdca692ea22-s.p.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/6c177e25b87fd9cd-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/6c9a125e97d835e1-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/8888a3826f4a3af4-s.p.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/a1386beebedccca4-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/b957ea75a84b6ea7-s.p.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/c3bc380753a8436c-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/db911767852bc875-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/eafabf029ad39a43-s.p.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/f10b8e9d91f3edcb-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/media/fe0777f1195381cb-s.woff2 +0 -0
- package/dist/cli/ui/.next/static/zkOuvkSWApTVHCrj7neNk/_buildManifest.js +1 -0
- package/dist/cli/ui/.next/static/zkOuvkSWApTVHCrj7neNk/_ssgManifest.js +1 -0
- package/dist/cli/ui/next.config.js +1 -0
- package/dist/cli/ui/package.json +4 -0
- package/dist/core/index.d.ts +34 -356
- package/dist/core/index.js +112 -332
- package/dist/core/index.js.map +1 -1
- package/dist/core/server.d.ts +135 -0
- package/dist/core/server.js +707 -0
- package/dist/core/server.js.map +1 -0
- package/dist/query-builder-KXz9cPzF.d.ts +330 -0
- package/package.json +32 -11
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ A Git-based, local-first CMS for Next.js projects.
|
|
|
4
4
|
|
|
5
5
|
Content lives in your repository as plain files. No database, no backend, no sync service. Editing happens through a standalone local server. Everything resolves at build time.
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
11
|
- **Content Collections** — folders inside `/contents` become collections automatically
|
|
@@ -12,7 +14,7 @@ Content lives in your repository as plain files. No database, no backend, no syn
|
|
|
12
14
|
- **JSON Form Editor** — auto-generated forms for JSON objects with nested field support
|
|
13
15
|
- **Schema Validation** — Zod-based validation with field-level type definitions
|
|
14
16
|
- **Media Library** — per-collection media folders, upload via drag & drop, paste, toolbar, or slash commands
|
|
15
|
-
- **
|
|
17
|
+
- **Sync Scripts** — run data import and sync scripts directly from the CMS UI
|
|
16
18
|
- **File Watching** — live updates via chokidar and WebSocket
|
|
17
19
|
|
|
18
20
|
## Quick start
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/bin/nextjs-studio.ts
|
|
4
|
-
import { existsSync } from "fs";
|
|
4
|
+
import { existsSync as existsSync2 } from "fs";
|
|
5
5
|
import fs2 from "fs/promises";
|
|
6
|
-
import
|
|
6
|
+
import path3 from "path";
|
|
7
|
+
import { createRequire } from "module";
|
|
7
8
|
import { spawn } from "child_process";
|
|
8
9
|
import { Command } from "commander";
|
|
9
10
|
|
|
10
11
|
// src/shared/constants.ts
|
|
11
12
|
var CONTENTS_DIR = "contents";
|
|
12
13
|
var CLI_PORT = 3030;
|
|
14
|
+
var CONFIG_FILENAMES = ["studio.config.ts", "studio.config.js", "studio.config.mjs"];
|
|
13
15
|
var SUPPORTED_EXTENSIONS = [".mdx", ".json"];
|
|
14
16
|
var COLLECTION_ORDER_FILE = "collection.json";
|
|
15
17
|
var IMAGE_MIME_TYPES = [
|
|
@@ -31,8 +33,9 @@ var AUDIO_MIME_TYPES = [
|
|
|
31
33
|
];
|
|
32
34
|
var MEDIA_MIME_TYPES = [...IMAGE_MIME_TYPES, ...VIDEO_MIME_TYPES, ...AUDIO_MIME_TYPES];
|
|
33
35
|
|
|
34
|
-
// src/
|
|
36
|
+
// src/core/fs-adapter.ts
|
|
35
37
|
import fs from "fs/promises";
|
|
38
|
+
import fsSync from "fs";
|
|
36
39
|
import path from "path";
|
|
37
40
|
var FsAdapter = class {
|
|
38
41
|
basePath;
|
|
@@ -127,6 +130,33 @@ var FsAdapter = class {
|
|
|
127
130
|
normalizeSlug(relativePath, ext) {
|
|
128
131
|
return relativePath.replace(ext, "").split(path.sep).join("/");
|
|
129
132
|
}
|
|
133
|
+
readFileSync(filePath) {
|
|
134
|
+
return fsSync.readFileSync(this.resolve(filePath), "utf-8");
|
|
135
|
+
}
|
|
136
|
+
existsSync(filePath) {
|
|
137
|
+
return fsSync.existsSync(this.resolve(filePath));
|
|
138
|
+
}
|
|
139
|
+
listFilesSync(dirPath, extensions) {
|
|
140
|
+
const fullPath = this.resolve(dirPath);
|
|
141
|
+
const filterExts = extensions ?? SUPPORTED_EXTENSIONS;
|
|
142
|
+
let entries;
|
|
143
|
+
try {
|
|
144
|
+
entries = fsSync.readdirSync(fullPath, { withFileTypes: true });
|
|
145
|
+
} catch {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
return entries.filter((entry) => entry.isFile() && filterExts.some((ext) => entry.name.endsWith(ext))).map((entry) => this.join(dirPath, entry.name));
|
|
149
|
+
}
|
|
150
|
+
listDirectoriesSync(dirPath) {
|
|
151
|
+
const fullPath = this.resolve(dirPath);
|
|
152
|
+
let entries;
|
|
153
|
+
try {
|
|
154
|
+
entries = fsSync.readdirSync(fullPath, { withFileTypes: true });
|
|
155
|
+
} catch {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => this.join(dirPath, entry.name));
|
|
159
|
+
}
|
|
130
160
|
};
|
|
131
161
|
|
|
132
162
|
// src/core/indexer.ts
|
|
@@ -134,9 +164,49 @@ import slugify from "@sindresorhus/slugify";
|
|
|
134
164
|
|
|
135
165
|
// src/core/parsers/parser-mdx.ts
|
|
136
166
|
import matter from "gray-matter";
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
167
|
+
|
|
168
|
+
// src/core/frontmatter-binder.ts
|
|
169
|
+
var TOKEN_REGEX = /\{frontmatter\.([a-zA-Z0-9_.]+)\}/g;
|
|
170
|
+
function bindFrontmatter(body, data) {
|
|
171
|
+
return body.replace(TOKEN_REGEX, (_match, path4) => {
|
|
172
|
+
const value = resolvePath(data, path4);
|
|
173
|
+
if (value === void 0 || value === null) return _match;
|
|
174
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
175
|
+
return String(value);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function resolvePath(obj, path4) {
|
|
179
|
+
const keys = path4.split(".");
|
|
180
|
+
let current = obj;
|
|
181
|
+
for (const key of keys) {
|
|
182
|
+
if (typeof current !== "object" || current === null) return void 0;
|
|
183
|
+
current = current[key];
|
|
184
|
+
}
|
|
185
|
+
return current;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/core/parsers/parser-mdx.ts
|
|
189
|
+
function normalizeDates(data) {
|
|
190
|
+
const result = {};
|
|
191
|
+
for (const [key, value] of Object.entries(data)) {
|
|
192
|
+
if (value instanceof Date) {
|
|
193
|
+
result[key] = value.toISOString().split("T")[0];
|
|
194
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
195
|
+
result[key] = normalizeDates(value);
|
|
196
|
+
} else {
|
|
197
|
+
result[key] = value;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
function parseMdx(content, options) {
|
|
203
|
+
const { data: rawData, content: body } = matter(content);
|
|
204
|
+
const data = normalizeDates(rawData);
|
|
205
|
+
const trimmed = body.trim();
|
|
206
|
+
return {
|
|
207
|
+
data,
|
|
208
|
+
body: options?.bindTokens ? bindFrontmatter(trimmed, data) : trimmed
|
|
209
|
+
};
|
|
140
210
|
}
|
|
141
211
|
|
|
142
212
|
// src/core/parsers/parser-json.ts
|
|
@@ -163,6 +233,7 @@ var RE_ISO_DATETIME = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]
|
|
|
163
233
|
var RE_EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
164
234
|
var RE_URL = /^https?:\/\/.+/;
|
|
165
235
|
var LONG_TEXT_THRESHOLD = 200;
|
|
236
|
+
var RICH_TEXT_FIELD_NAMES = ["description", "descriptions", "text", "content"];
|
|
166
237
|
function isISODate(value) {
|
|
167
238
|
return RE_ISO_DATE.test(value);
|
|
168
239
|
}
|
|
@@ -180,6 +251,7 @@ function inferStringField(name, strings) {
|
|
|
180
251
|
if (strings.every(isUrl)) return { name, type: "url" };
|
|
181
252
|
if (strings.every(isISODateTime)) return { name, type: "date", includeTime: true };
|
|
182
253
|
if (strings.every(isISODate)) return { name, type: "date" };
|
|
254
|
+
if (RICH_TEXT_FIELD_NAMES.includes(name.toLowerCase())) return { name, type: "long-text" };
|
|
183
255
|
const isLong = strings.some((s) => s.length > LONG_TEXT_THRESHOLD || s.includes("\n"));
|
|
184
256
|
return { name, type: isLong ? "long-text" : "text" };
|
|
185
257
|
}
|
|
@@ -223,6 +295,21 @@ function inferSchema(entries, collectionName) {
|
|
|
223
295
|
return { collection: collectionName, fields: inferFields(rows) };
|
|
224
296
|
}
|
|
225
297
|
|
|
298
|
+
// src/core/locale-parser.ts
|
|
299
|
+
var LOCALE_REGEX = /\.([a-z]{2}(?:-[A-Z]{2})?)\.mdx$/;
|
|
300
|
+
function parseLocaleFromFilename(filename) {
|
|
301
|
+
const match = LOCALE_REGEX.exec(filename);
|
|
302
|
+
return match?.[1];
|
|
303
|
+
}
|
|
304
|
+
function stripLocaleFromSlug(slug, locale) {
|
|
305
|
+
if (!locale) return slug;
|
|
306
|
+
const dotSuffix = `.${locale}`;
|
|
307
|
+
if (slug.endsWith(dotSuffix)) return slug.slice(0, -dotSuffix.length);
|
|
308
|
+
const dashSuffix = `-${locale}`;
|
|
309
|
+
if (slug.endsWith(dashSuffix)) return slug.slice(0, -dashSuffix.length);
|
|
310
|
+
return slug;
|
|
311
|
+
}
|
|
312
|
+
|
|
226
313
|
// src/core/indexer.ts
|
|
227
314
|
var ContentIndex = class {
|
|
228
315
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -241,6 +328,16 @@ var ContentIndex = class {
|
|
|
241
328
|
await this.indexCollection(dirName, collectionName, collectionConfig?.schema);
|
|
242
329
|
}
|
|
243
330
|
}
|
|
331
|
+
buildSync(config) {
|
|
332
|
+
this.clear();
|
|
333
|
+
const dirs = this.fs.listDirectoriesSync(".");
|
|
334
|
+
for (const dir of dirs) {
|
|
335
|
+
const dirName = this.fs.basename(dir);
|
|
336
|
+
const collectionName = slugify(dirName);
|
|
337
|
+
const collectionConfig = config?.collections?.[collectionName];
|
|
338
|
+
this.indexCollectionSync(dirName, collectionName, collectionConfig?.schema);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
244
341
|
getCollection(name) {
|
|
245
342
|
return this.entries.get(name) ?? [];
|
|
246
343
|
}
|
|
@@ -251,6 +348,35 @@ var ContentIndex = class {
|
|
|
251
348
|
this.entries.clear();
|
|
252
349
|
this.collections.clear();
|
|
253
350
|
}
|
|
351
|
+
updateEntry(collectionName, entry) {
|
|
352
|
+
const entries = this.entries.get(collectionName) ?? [];
|
|
353
|
+
const idx = entries.findIndex((e) => e.slug === entry.slug);
|
|
354
|
+
if (idx >= 0) {
|
|
355
|
+
entries[idx] = entry;
|
|
356
|
+
} else {
|
|
357
|
+
entries.push(entry);
|
|
358
|
+
}
|
|
359
|
+
this.entries.set(collectionName, entries);
|
|
360
|
+
this.updateCollectionMeta(collectionName);
|
|
361
|
+
}
|
|
362
|
+
removeEntry(collectionName, slug) {
|
|
363
|
+
const entries = this.entries.get(collectionName);
|
|
364
|
+
if (!entries) return;
|
|
365
|
+
const filtered = entries.filter((e) => e.slug !== slug);
|
|
366
|
+
this.entries.set(collectionName, filtered);
|
|
367
|
+
this.updateCollectionMeta(collectionName);
|
|
368
|
+
}
|
|
369
|
+
updateCollectionMeta(collectionName) {
|
|
370
|
+
const col = this.collections.get(collectionName);
|
|
371
|
+
const entries = this.entries.get(collectionName) ?? [];
|
|
372
|
+
if (col) {
|
|
373
|
+
this.collections.set(collectionName, {
|
|
374
|
+
...col,
|
|
375
|
+
count: entries.length,
|
|
376
|
+
type: this.detectCollectionType(entries)
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
254
380
|
async indexCollection(dirName, collectionName, manualSchema) {
|
|
255
381
|
const entries = [];
|
|
256
382
|
await this.scanDir(dirName, collectionName, dirName, entries);
|
|
@@ -269,6 +395,24 @@ var ContentIndex = class {
|
|
|
269
395
|
schema
|
|
270
396
|
});
|
|
271
397
|
}
|
|
398
|
+
indexCollectionSync(dirName, collectionName, manualSchema) {
|
|
399
|
+
const entries = [];
|
|
400
|
+
this.scanDirSync(dirName, collectionName, dirName, entries);
|
|
401
|
+
const orderPath = this.fs.join(dirName, COLLECTION_ORDER_FILE);
|
|
402
|
+
const ordering = this.readOrderingSync(orderPath);
|
|
403
|
+
if (ordering) {
|
|
404
|
+
this.applyOrdering(entries, ordering);
|
|
405
|
+
}
|
|
406
|
+
const schema = manualSchema ?? inferSchema(entries, collectionName);
|
|
407
|
+
this.entries.set(collectionName, entries);
|
|
408
|
+
this.collections.set(collectionName, {
|
|
409
|
+
name: collectionName,
|
|
410
|
+
type: this.detectCollectionType(entries),
|
|
411
|
+
count: entries.length,
|
|
412
|
+
basePath: dirName,
|
|
413
|
+
schema
|
|
414
|
+
});
|
|
415
|
+
}
|
|
272
416
|
async scanDir(dirName, collectionName, dirPath, entries) {
|
|
273
417
|
const subDirs = await this.fs.listDirectories(dirPath);
|
|
274
418
|
for (const subDir of subDirs) {
|
|
@@ -283,20 +427,43 @@ var ContentIndex = class {
|
|
|
283
427
|
const relativePath = this.fs.relative(dirName, filePath);
|
|
284
428
|
const slug = this.fs.normalizeSlug(relativePath, ext).split("/").map((segment) => slugify(segment)).join("/");
|
|
285
429
|
if (ext === ".mdx") {
|
|
286
|
-
entries.push(this.buildMdxEntry(collectionName, slug, content));
|
|
430
|
+
entries.push(this.buildMdxEntry(collectionName, slug, fileName, content));
|
|
287
431
|
} else if (ext === ".json") {
|
|
288
432
|
entries.push(...this.buildJsonEntries(collectionName, slug, content));
|
|
289
433
|
}
|
|
290
434
|
}
|
|
291
435
|
}
|
|
292
|
-
|
|
436
|
+
scanDirSync(dirName, collectionName, dirPath, entries) {
|
|
437
|
+
const subDirs = this.fs.listDirectoriesSync(dirPath);
|
|
438
|
+
for (const subDir of subDirs) {
|
|
439
|
+
this.scanDirSync(dirName, collectionName, subDir, entries);
|
|
440
|
+
}
|
|
441
|
+
const files = this.fs.listFilesSync(dirPath);
|
|
442
|
+
for (const filePath of files) {
|
|
443
|
+
const fileName = this.fs.basename(filePath);
|
|
444
|
+
if (fileName === COLLECTION_ORDER_FILE) continue;
|
|
445
|
+
const ext = this.fs.extname(fileName);
|
|
446
|
+
const content = this.fs.readFileSync(filePath);
|
|
447
|
+
const relativePath = this.fs.relative(dirName, filePath);
|
|
448
|
+
const slug = this.fs.normalizeSlug(relativePath, ext).split("/").map((segment) => slugify(segment)).join("/");
|
|
449
|
+
if (ext === ".mdx") {
|
|
450
|
+
entries.push(this.buildMdxEntry(collectionName, slug, fileName, content));
|
|
451
|
+
} else if (ext === ".json") {
|
|
452
|
+
entries.push(...this.buildJsonEntries(collectionName, slug, content));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
buildMdxEntry(collectionName, slug, fileName, content) {
|
|
293
457
|
const parsed = parseMdx(content);
|
|
458
|
+
const locale = parseLocaleFromFilename(fileName);
|
|
459
|
+
const normalizedSlug = stripLocaleFromSlug(slug, locale);
|
|
294
460
|
return {
|
|
295
461
|
collection: collectionName,
|
|
296
|
-
slug,
|
|
297
|
-
path: `/${collectionName}/${
|
|
462
|
+
slug: normalizedSlug,
|
|
463
|
+
path: `/${collectionName}/${normalizedSlug}`,
|
|
298
464
|
body: parsed.body,
|
|
299
|
-
data: parsed.data
|
|
465
|
+
data: parsed.data,
|
|
466
|
+
...locale ? { locale } : {}
|
|
300
467
|
};
|
|
301
468
|
}
|
|
302
469
|
buildJsonEntries(collectionName, slug, content) {
|
|
@@ -325,6 +492,17 @@ var ContentIndex = class {
|
|
|
325
492
|
}
|
|
326
493
|
return null;
|
|
327
494
|
}
|
|
495
|
+
readOrderingSync(orderPath) {
|
|
496
|
+
if (!this.fs.existsSync(orderPath)) return null;
|
|
497
|
+
try {
|
|
498
|
+
const content = this.fs.readFileSync(orderPath);
|
|
499
|
+
const parsed = JSON.parse(content);
|
|
500
|
+
if (Array.isArray(parsed)) return parsed;
|
|
501
|
+
} catch (error) {
|
|
502
|
+
console.warn(`[Nextjs Studio] Failed to parse ordering file: ${orderPath}`, error);
|
|
503
|
+
}
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
328
506
|
applyOrdering(entries, ordering) {
|
|
329
507
|
const orderMap = new Map(ordering.map((slug, index) => [slug, index]));
|
|
330
508
|
entries.sort((a, b) => {
|
|
@@ -443,20 +621,59 @@ function generateCollectionTypes(schemas) {
|
|
|
443
621
|
"export type Slug = Brand<string, 'Slug'>;"
|
|
444
622
|
].join("\n");
|
|
445
623
|
const interfaces = schemas.map(generateInterfaceForSchema).join("\n\n");
|
|
446
|
-
const collectionMap = schemas.map((schema) => ` ${JSON.stringify(schema.collection)}: ${toPascalCase(schema.collection)}Entry;`).join("\n");
|
|
447
624
|
const collectionRegistry = [
|
|
448
|
-
"
|
|
449
|
-
"
|
|
450
|
-
|
|
625
|
+
"// Augment both entry points so queryCollection() is fully typed regardless of import path.",
|
|
626
|
+
"declare module 'nextjs-studio' {",
|
|
627
|
+
" interface CollectionTypeMap {",
|
|
628
|
+
schemas.map((schema) => ` ${JSON.stringify(schema.collection)}: ${toPascalCase(schema.collection)}Entry;`).join("\n"),
|
|
629
|
+
" }",
|
|
630
|
+
"}",
|
|
631
|
+
"declare module 'nextjs-studio/server' {",
|
|
632
|
+
" interface CollectionTypeMap {",
|
|
633
|
+
schemas.map((schema) => ` ${JSON.stringify(schema.collection)}: ${toPascalCase(schema.collection)}Entry;`).join("\n"),
|
|
634
|
+
" }",
|
|
451
635
|
"}"
|
|
452
636
|
].join("\n");
|
|
453
637
|
return [banner, interfaces, collectionRegistry].join("\n\n") + "\n";
|
|
454
638
|
}
|
|
455
639
|
|
|
640
|
+
// src/core/config-loader.ts
|
|
641
|
+
import { existsSync } from "fs";
|
|
642
|
+
import path2 from "path";
|
|
643
|
+
import { pathToFileURL } from "url";
|
|
644
|
+
function resolveConfigPath(projectRoot) {
|
|
645
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
646
|
+
const fullPath = path2.resolve(projectRoot, filename);
|
|
647
|
+
if (existsSync(fullPath)) return fullPath;
|
|
648
|
+
}
|
|
649
|
+
return void 0;
|
|
650
|
+
}
|
|
651
|
+
async function loadStudioConfig(projectRoot) {
|
|
652
|
+
const configPath2 = resolveConfigPath(projectRoot);
|
|
653
|
+
if (!configPath2) return {};
|
|
654
|
+
return loadConfigFromPath(configPath2);
|
|
655
|
+
}
|
|
656
|
+
async function loadConfigFromPath(configPath2) {
|
|
657
|
+
try {
|
|
658
|
+
const fileUrl = pathToFileURL(configPath2).href;
|
|
659
|
+
const mod = await import(
|
|
660
|
+
/* webpackIgnore: true */
|
|
661
|
+
fileUrl
|
|
662
|
+
);
|
|
663
|
+
const config = mod.default ?? mod.config ?? mod;
|
|
664
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
665
|
+
return {};
|
|
666
|
+
}
|
|
667
|
+
return config;
|
|
668
|
+
} catch {
|
|
669
|
+
return {};
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
456
673
|
// package.json
|
|
457
674
|
var package_default = {
|
|
458
675
|
name: "nextjs-studio",
|
|
459
|
-
version: "0.
|
|
676
|
+
version: "1.0.0",
|
|
460
677
|
description: "A Git-based, local-first CMS for Next.js projects",
|
|
461
678
|
keywords: [
|
|
462
679
|
"nextjs",
|
|
@@ -470,7 +687,7 @@ var package_default = {
|
|
|
470
687
|
homepage: "https://github.com/TiagoDanin/Nextjs-Studio",
|
|
471
688
|
repository: {
|
|
472
689
|
type: "git",
|
|
473
|
-
url: "https://github.com/TiagoDanin/Nextjs-Studio.git"
|
|
690
|
+
url: "git+https://github.com/TiagoDanin/Nextjs-Studio.git"
|
|
474
691
|
},
|
|
475
692
|
license: "MIT",
|
|
476
693
|
author: "Tiago Danin",
|
|
@@ -479,20 +696,43 @@ var package_default = {
|
|
|
479
696
|
".": {
|
|
480
697
|
types: "./dist/core/index.d.ts",
|
|
481
698
|
import: "./dist/core/index.js"
|
|
699
|
+
},
|
|
700
|
+
"./server": {
|
|
701
|
+
types: "./dist/core/server.d.ts",
|
|
702
|
+
import: "./dist/core/server.js"
|
|
482
703
|
}
|
|
483
704
|
},
|
|
484
705
|
main: "./dist/core/index.js",
|
|
485
706
|
types: "./dist/core/index.d.ts",
|
|
486
|
-
bin: "
|
|
707
|
+
bin: "dist/bin/nextjs-studio.js",
|
|
487
708
|
files: [
|
|
488
|
-
"dist",
|
|
709
|
+
"dist/bin",
|
|
710
|
+
"dist/core",
|
|
711
|
+
"dist/*.d.ts",
|
|
712
|
+
"dist/cli/ui/package.json",
|
|
713
|
+
"dist/cli/ui/next.config.js",
|
|
714
|
+
"dist/cli/ui/.next/static",
|
|
715
|
+
"dist/cli/ui/.next/server",
|
|
716
|
+
"dist/cli/ui/.next/BUILD_ID",
|
|
717
|
+
"dist/cli/ui/.next/app-path-routes-manifest.json",
|
|
718
|
+
"dist/cli/ui/.next/build-manifest.json",
|
|
719
|
+
"dist/cli/ui/.next/export-marker.json",
|
|
720
|
+
"dist/cli/ui/.next/images-manifest.json",
|
|
721
|
+
"dist/cli/ui/.next/next-minimal-server.js.nft.json",
|
|
722
|
+
"dist/cli/ui/.next/next-server.js.nft.json",
|
|
723
|
+
"dist/cli/ui/.next/package.json",
|
|
724
|
+
"dist/cli/ui/.next/prerender-manifest.json",
|
|
725
|
+
"dist/cli/ui/.next/react-loadable-manifest.json",
|
|
726
|
+
"dist/cli/ui/.next/required-server-files.js",
|
|
727
|
+
"dist/cli/ui/.next/required-server-files.json",
|
|
728
|
+
"dist/cli/ui/.next/routes-manifest.json",
|
|
489
729
|
"README.md",
|
|
490
730
|
"LICENSE"
|
|
491
731
|
],
|
|
492
732
|
scripts: {
|
|
493
733
|
dev: "tsx src/bin/nextjs-studio.ts --dir example/contents",
|
|
494
734
|
"studio:dev": "cross-env STUDIO_CONTENTS_DIR=example/contents next dev --port 3030 --webpack src/cli/ui",
|
|
495
|
-
"studio:build":
|
|
735
|
+
"studio:build": `next build --webpack src/cli/ui && node -e "const fs=require('fs');fs.copyFileSync('src/cli/ui/package.json','dist/cli/ui/package.json');fs.writeFileSync('dist/cli/ui/next.config.js','module.exports={distDir:\\'.next\\'};')"`,
|
|
496
736
|
build: "tsup && yarn studio:build",
|
|
497
737
|
lint: "eslint src/",
|
|
498
738
|
"type-check": "tsc --noEmit",
|
|
@@ -529,10 +769,7 @@ var package_default = {
|
|
|
529
769
|
lowlight: "^3.3.0",
|
|
530
770
|
"lucide-react": "^0.574.0",
|
|
531
771
|
mermaid: "^11.6.0",
|
|
532
|
-
next: "^16.1.6",
|
|
533
772
|
"next-themes": "^0.4.6",
|
|
534
|
-
react: "^19.2.4",
|
|
535
|
-
"react-dom": "^19.2.4",
|
|
536
773
|
"tailwind-merge": "^3.4.1",
|
|
537
774
|
tailwindcss: "^4.1.18",
|
|
538
775
|
"tippy.js": "^6.3.7",
|
|
@@ -549,7 +786,10 @@ var package_default = {
|
|
|
549
786
|
chokidar: "^5.0.0",
|
|
550
787
|
commander: "^14.0.3",
|
|
551
788
|
"gray-matter": "^4.0.3",
|
|
552
|
-
"lodash-es": "^4.17.23"
|
|
789
|
+
"lodash-es": "^4.17.23",
|
|
790
|
+
next: "^16.1.6",
|
|
791
|
+
react: "^19.2.4",
|
|
792
|
+
"react-dom": "^19.2.4"
|
|
553
793
|
}
|
|
554
794
|
};
|
|
555
795
|
|
|
@@ -557,54 +797,75 @@ var package_default = {
|
|
|
557
797
|
var { version } = package_default;
|
|
558
798
|
var program = new Command().name("Nextjs Studio").description("Local-first CMS for Next.js projects").version(version).option("-d, --dir <path>", "Path to contents directory", CONTENTS_DIR).option("-p, --port <number>", "Port to run the studio on", String(CLI_PORT)).option("--generate-types", "Generate TypeScript types for content collections").parse();
|
|
559
799
|
var opts = program.opts();
|
|
560
|
-
var contentsDir =
|
|
800
|
+
var contentsDir = path3.resolve(opts.dir);
|
|
561
801
|
var port = Number(opts.port);
|
|
562
|
-
|
|
563
|
-
const outDir =
|
|
564
|
-
const outFile =
|
|
565
|
-
console.log(`Generating types from ${
|
|
566
|
-
const
|
|
567
|
-
const
|
|
568
|
-
const
|
|
569
|
-
const schemas =
|
|
802
|
+
async function runGenerateTypes(sourceDir) {
|
|
803
|
+
const outDir = path3.resolve(".studio");
|
|
804
|
+
const outFile = path3.join(outDir, "studio.d.ts");
|
|
805
|
+
console.log(`Generating types from ${sourceDir}...`);
|
|
806
|
+
const config = await loadStudioConfig(process.cwd());
|
|
807
|
+
const fsAdapter = new FsAdapter(sourceDir);
|
|
808
|
+
const index = await loadContent(fsAdapter, config);
|
|
809
|
+
const schemas = index.getCollections().flatMap((c) => c.schema ? [c.schema] : []);
|
|
570
810
|
const code = generateCollectionTypes(schemas);
|
|
571
811
|
await fs2.mkdir(outDir, { recursive: true });
|
|
572
812
|
await fs2.writeFile(outFile, code, "utf-8");
|
|
573
813
|
console.log(`Types written to ${outFile}`);
|
|
814
|
+
}
|
|
815
|
+
function resolveServerProcess(uiDir2, serverPort, env) {
|
|
816
|
+
const buildId = path3.resolve(uiDir2, ".next/BUILD_ID");
|
|
817
|
+
if (existsSync2(buildId)) {
|
|
818
|
+
const require2 = createRequire(import.meta.url);
|
|
819
|
+
const nextBin = require2.resolve("next/dist/bin/next");
|
|
820
|
+
return spawn("node", [nextBin, "start", "--port", String(serverPort)], {
|
|
821
|
+
cwd: uiDir2,
|
|
822
|
+
stdio: "inherit",
|
|
823
|
+
env
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
const uiPackageJson = path3.resolve(uiDir2, "package.json");
|
|
827
|
+
if (existsSync2(uiPackageJson)) {
|
|
828
|
+
const nextBin = path3.resolve(uiDir2, "../../../node_modules/next/dist/bin/next");
|
|
829
|
+
return spawn("node", [nextBin, "dev", "--port", String(serverPort), "--webpack"], {
|
|
830
|
+
cwd: uiDir2,
|
|
831
|
+
stdio: "inherit",
|
|
832
|
+
env
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
function forwardSignals(child) {
|
|
838
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
839
|
+
process.on(signal, () => child.kill(signal));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (opts.generateTypes) {
|
|
843
|
+
await runGenerateTypes(contentsDir);
|
|
574
844
|
process.exit(0);
|
|
575
845
|
}
|
|
576
|
-
var uiDir =
|
|
577
|
-
var
|
|
846
|
+
var uiDir = path3.resolve(import.meta.dirname, "../cli/ui");
|
|
847
|
+
var configPath = resolveConfigPath(process.cwd());
|
|
578
848
|
var serverEnv = {
|
|
579
849
|
...process.env,
|
|
580
850
|
STUDIO_CONTENTS_DIR: contentsDir,
|
|
851
|
+
STUDIO_PROJECT_DIR: process.cwd(),
|
|
581
852
|
PORT: String(port),
|
|
582
|
-
HOSTNAME: "0.0.0.0"
|
|
853
|
+
HOSTNAME: "0.0.0.0",
|
|
854
|
+
...configPath ? { STUDIO_CONFIG_PATH: configPath } : {}
|
|
583
855
|
};
|
|
856
|
+
var serverProcess = resolveServerProcess(uiDir, port, serverEnv);
|
|
857
|
+
if (!serverProcess) {
|
|
858
|
+
console.error("Error: Studio UI server not found.");
|
|
859
|
+
console.error("The pre-built UI is not included in this installation.");
|
|
860
|
+
process.exit(1);
|
|
861
|
+
}
|
|
584
862
|
console.log(`Nextjs Studio v${version}`);
|
|
585
863
|
console.log(`Contents: ${contentsDir}`);
|
|
586
864
|
console.log(`Starting on http://localhost:${port}`);
|
|
587
|
-
function createServerProcess() {
|
|
588
|
-
if (existsSync(standaloneServer)) {
|
|
589
|
-
return spawn("node", [standaloneServer], { stdio: "inherit", env: serverEnv });
|
|
590
|
-
}
|
|
591
|
-
const npxBin = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
592
|
-
return spawn(npxBin, ["next", "dev", "--port", String(port), "--webpack"], {
|
|
593
|
-
cwd: uiDir,
|
|
594
|
-
stdio: "inherit",
|
|
595
|
-
env: serverEnv
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
function registerSignalForwarding(child) {
|
|
599
|
-
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
600
|
-
process.on(signal, () => child.kill(signal));
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
var serverProcess = createServerProcess();
|
|
604
865
|
serverProcess.on("error", (error) => {
|
|
605
866
|
console.error("Failed to start server:", error.message);
|
|
606
867
|
process.exit(1);
|
|
607
868
|
});
|
|
608
869
|
serverProcess.on("close", (code) => process.exit(code ?? 0));
|
|
609
|
-
|
|
870
|
+
forwardSignals(serverProcess);
|
|
610
871
|
//# sourceMappingURL=nextjs-studio.js.map
|