creght-cli 0.7.6 → 0.9.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 +93 -100
- package/package.json +1 -20
- package/vendor/darwin-arm64/creght +0 -0
- package/vendor/darwin-x64/creght +0 -0
- package/vendor/linux-arm64/creght +0 -0
- package/vendor/linux-x64/creght +0 -0
- package/vendor/win32-arm64/creght.exe +0 -0
- package/vendor/win32-x64/creght.exe +0 -0
- package/vite/import-map.js +0 -55
- package/vite/index.d.ts +0 -28
- package/vite/index.js +0 -546
- package/vite/index.test.mjs +0 -47
- package/vite/package.json +0 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Creght CLI is a thin local bridge for syncing site code between a local directory and Creght.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Creght remains responsible for cloud rendering, CMS, assets, and the preview environment. The CLI does not render sites locally; use `creght preview` to open the canonical remote preview.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -180,54 +180,6 @@ generated/*
|
|
|
180
180
|
/scratch/
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
-
## Local Vite Preview
|
|
184
|
-
|
|
185
|
-
Creght projects pulled by the CLI usually do not have their own `package.json`
|
|
186
|
-
or `node_modules`. The local preview plugin therefore uses Vite only for local
|
|
187
|
-
file serving and TSX transpilation; third-party packages continue to resolve
|
|
188
|
-
through the Creght import map, matching the Web editor preview model. In
|
|
189
|
-
`creght dev`, the CLI serves the workspace directory, loads the platform import
|
|
190
|
-
map from server system info, and passes it to the Vite plugin; the plugin's
|
|
191
|
-
local map is only a fallback.
|
|
192
|
-
|
|
193
|
-
Install Vite in the local project folder:
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
cd ./mysite
|
|
197
|
-
npm init -y
|
|
198
|
-
npm install -D vite esbuild creght-cli
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Create `vite.config.mjs`:
|
|
202
|
-
|
|
203
|
-
```js
|
|
204
|
-
import { defineConfig } from 'vite'
|
|
205
|
-
import creght from 'creght-cli/vite'
|
|
206
|
-
|
|
207
|
-
export default defineConfig({
|
|
208
|
-
plugins: [
|
|
209
|
-
creght({
|
|
210
|
-
apiHost: 'https://creght.cn',
|
|
211
|
-
projectId: '<project_id>',
|
|
212
|
-
// token: process.env.CREGHT_TOKEN,
|
|
213
|
-
}),
|
|
214
|
-
],
|
|
215
|
-
})
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
Run it:
|
|
219
|
-
|
|
220
|
-
```bash
|
|
221
|
-
npx vite --host 0.0.0.0
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
The plugin maps `/page/Index.tsx` to `/`, `/page/About.tsx` to `/about`, starts
|
|
225
|
-
from the platform import map, merges `creght.config.ts` import-map entries,
|
|
226
|
-
loads `/index.css` through the Tailwind browser runtime, proxies local `/api/*`
|
|
227
|
-
requests to `apiHost`, calls page `getServerSideProps()` in the browser for a
|
|
228
|
-
preview-only first render, and uses Vite HMR to re-import the current page
|
|
229
|
-
module after local file changes without a full page reload.
|
|
230
|
-
|
|
231
183
|
## Push Local Changes
|
|
232
184
|
|
|
233
185
|
Push the current local directory snapshot to Creght and exit:
|
|
@@ -288,91 +240,130 @@ content is backed up under `.creght/backup/<timestamp>-*/`.
|
|
|
288
240
|
`base_to_local_diff` / `base_to_remote_diff`, so agents can decide how to
|
|
289
241
|
resolve without extra round-trips.
|
|
290
242
|
|
|
291
|
-
##
|
|
243
|
+
## Open Preview
|
|
292
244
|
|
|
293
|
-
|
|
294
|
-
files:
|
|
245
|
+
Open the remote preview URL for a site in the browser:
|
|
295
246
|
|
|
296
247
|
```bash
|
|
297
|
-
creght
|
|
248
|
+
creght preview --site_id=<project_id>/<site_id>
|
|
298
249
|
```
|
|
299
250
|
|
|
300
|
-
For local
|
|
251
|
+
For local development:
|
|
301
252
|
|
|
302
253
|
```bash
|
|
303
|
-
CREGHT_API_HOST=http://localhost:8433 creght
|
|
254
|
+
CREGHT_API_HOST=http://localhost:8433 creght preview --site_id=<project_id>/<site_id>
|
|
304
255
|
```
|
|
305
256
|
|
|
306
|
-
|
|
307
|
-
Creght, and listens to the existing WebSocket collaboration channel so editor
|
|
308
|
-
changes are written back to the local directory. MVP conflict handling is last
|
|
309
|
-
write wins.
|
|
257
|
+
## Publish Site
|
|
310
258
|
|
|
311
|
-
|
|
259
|
+
Publish a site:
|
|
312
260
|
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
➜ Local: http://localhost:5173/
|
|
316
|
-
Local Vite: started (preferred http://localhost:5173; use the Vite Local URL above)
|
|
261
|
+
```bash
|
|
262
|
+
creght publish --site_id=<project_id>/<site_id>
|
|
317
263
|
```
|
|
318
264
|
|
|
319
|
-
|
|
320
|
-
address. If that port is occupied, Vite uses its normal auto-port behavior and
|
|
321
|
-
prints the actual URL in the terminal:
|
|
265
|
+
With a publish note:
|
|
322
266
|
|
|
323
267
|
```bash
|
|
324
|
-
creght
|
|
268
|
+
creght publish --site_id=<project_id>/<site_id> --note="Update homepage copy"
|
|
325
269
|
```
|
|
326
270
|
|
|
327
|
-
|
|
271
|
+
For local development:
|
|
328
272
|
|
|
329
273
|
```bash
|
|
330
|
-
creght
|
|
274
|
+
CREGHT_API_HOST=http://localhost:8433 creght publish --site_id=<project_id>/<site_id>
|
|
331
275
|
```
|
|
332
276
|
|
|
333
|
-
|
|
334
|
-
has `node_modules/.bin/vite`, that local Vite is used; otherwise the CLI starts
|
|
335
|
-
a hidden temporary Vite runtime under `.creght/` and installs `vite` plus
|
|
336
|
-
`esbuild` there.
|
|
277
|
+
`publish` reports the version it created, so you can publish it again later:
|
|
337
278
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
279
|
+
```text
|
|
280
|
+
Published <project_id>/<site_id>
|
|
281
|
+
version 14 (id 458) is live on demo.creght.cn
|
|
282
|
+
```
|
|
341
283
|
|
|
342
|
-
##
|
|
284
|
+
## Site Versions
|
|
343
285
|
|
|
344
|
-
|
|
286
|
+
A site version is an immutable snapshot of the site's source files — the
|
|
287
|
+
platform equivalent of a git commit. Create one whenever a piece of work is
|
|
288
|
+
done, then publish whichever version you want the live site to serve.
|
|
289
|
+
|
|
290
|
+
Snapshots cover source files only. CMS content and the platform state under
|
|
291
|
+
`/platform/**` (CMS/form/table/auth definitions) are live, so publishing an
|
|
292
|
+
older version does not roll those back.
|
|
293
|
+
|
|
294
|
+
Inside a pulled workspace `--site_id` is optional; the version commands
|
|
295
|
+
discover `.creght/state.json` from the current directory or its parents, like
|
|
296
|
+
`pull`/`push` do.
|
|
297
|
+
|
|
298
|
+
Snapshot the current site source without publishing it:
|
|
345
299
|
|
|
346
300
|
```bash
|
|
347
|
-
creght
|
|
301
|
+
creght version create --note="Add pricing page"
|
|
348
302
|
```
|
|
349
303
|
|
|
350
|
-
|
|
304
|
+
A version records the *remote* files, so local edits that were never pushed
|
|
305
|
+
would be missing from it. `create` therefore compares the workspace against the
|
|
306
|
+
site first and refuses to run while anything is unpushed:
|
|
351
307
|
|
|
352
|
-
```
|
|
353
|
-
|
|
308
|
+
```text
|
|
309
|
+
update /page/Index.tsx
|
|
310
|
+
1 change is not pushed yet and would be missing from this version; run creght
|
|
311
|
+
push first, or pass --allow-dirty to snapshot the remote site as it is
|
|
354
312
|
```
|
|
355
313
|
|
|
356
|
-
|
|
314
|
+
Run `creght push` first, or pass `--allow-dirty` to snapshot the remote site as
|
|
315
|
+
it is. The platform rejects a snapshot identical to the newest version, so
|
|
316
|
+
repeated `create` calls never pile up duplicates.
|
|
357
317
|
|
|
358
|
-
|
|
318
|
+
List versions, newest first:
|
|
359
319
|
|
|
360
320
|
```bash
|
|
361
|
-
creght
|
|
321
|
+
creght version list
|
|
362
322
|
```
|
|
363
323
|
|
|
364
|
-
|
|
324
|
+
```text
|
|
325
|
+
VERSION ID CREATED FROM NOTE
|
|
326
|
+
12 456 2026-08-05 14:31 api_generate_version Add pricing page
|
|
327
|
+
* 11 455 2026-08-05 11:02 publish Fix nav
|
|
328
|
+
10 442 2026-08-04 18:40 agent -
|
|
329
|
+
* live: version 11 (id 455), served by demo.creght.cn
|
|
330
|
+
pinned: www.example.com -> version 12 (id 456)
|
|
331
|
+
1 change is pending on the site since the newest version; run creght version create to snapshot them
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`*` marks the version the live site serves. `VERSION` is the per-site number
|
|
335
|
+
that `version publish` takes; `ID` is the platform-wide version id. Use
|
|
336
|
+
`--limit=<n>` to shorten the list and `--json` to get the raw publish state,
|
|
337
|
+
including every version, the pinned domains, and the exact files that changed
|
|
338
|
+
since the newest version.
|
|
339
|
+
|
|
340
|
+
Make an existing version live, forward to a newer one or back to an older one:
|
|
365
341
|
|
|
366
342
|
```bash
|
|
367
|
-
creght publish
|
|
343
|
+
creght version publish 12
|
|
344
|
+
creght version publish 12 --note="Roll back nav change"
|
|
368
345
|
```
|
|
369
346
|
|
|
370
|
-
|
|
347
|
+
Every domain follows it except domains pinned to a specific version.
|
|
348
|
+
Publishing the version already being served is a no-op that just refreshes its
|
|
349
|
+
caches.
|
|
350
|
+
|
|
351
|
+
Publishing a version changes only which snapshot the live site serves — it
|
|
352
|
+
restores nothing. The editable site workspace and your local files are
|
|
353
|
+
untouched, and `creght pull` still fetches the current workspace rather than the
|
|
354
|
+
published version's files. So after rolling production back to an older version,
|
|
355
|
+
the workspace still holds the newer source and the next `publish` would ship it
|
|
356
|
+
again; to revert the code itself, edit locally and push.
|
|
357
|
+
|
|
358
|
+
`12` is the per-site number from the `VERSION` column. To reach a version older
|
|
359
|
+
than the window `version list` returns, select it by id instead:
|
|
371
360
|
|
|
372
361
|
```bash
|
|
373
|
-
|
|
362
|
+
creght version publish id:456
|
|
374
363
|
```
|
|
375
364
|
|
|
365
|
+
Bare `creght version` still prints the installed CLI version.
|
|
366
|
+
|
|
376
367
|
## Manage CMS Collections
|
|
377
368
|
|
|
378
369
|
List CMS collections:
|
|
@@ -508,8 +499,7 @@ The workflow is the same as for any site file:
|
|
|
508
499
|
|
|
509
500
|
1. Run `creght pull --site_id=<project_id>/<site_id> --dir=./mysite`.
|
|
510
501
|
2. Edit or create files under `./mysite/backend/func/`.
|
|
511
|
-
3. Run `creght push --site_id=<project_id>/<site_id> --dir=./mysite
|
|
512
|
-
`creght dev` running.
|
|
502
|
+
3. Run `creght push --site_id=<project_id>/<site_id> --dir=./mysite`.
|
|
513
503
|
|
|
514
504
|
Examples:
|
|
515
505
|
|
|
@@ -552,7 +542,7 @@ The output matches the Func HTTP response protocol: successful runs print
|
|
|
552
542
|
top-level `ok` execution wrapper.
|
|
553
543
|
|
|
554
544
|
There are no `creght func list/get/create/update/delete` commands. Manage Func
|
|
555
|
-
code by editing `backend/func` files and syncing them with pull/push
|
|
545
|
+
code by editing `backend/func` files and syncing them with pull/push
|
|
556
546
|
like any other site file; `func run` only runs sample input.
|
|
557
547
|
|
|
558
548
|
## Upload Assets
|
|
@@ -603,9 +593,11 @@ Creght, list projects and sites, pull remote site files into a local directory
|
|
|
603
593
|
with three-way merge, push local files back to Creght, resolve conflicts, open
|
|
604
594
|
the remote preview, and publish a site.
|
|
605
595
|
|
|
596
|
+
It can also manage site versions: immutable snapshots of a site's source files,
|
|
597
|
+
created and published like git commits.
|
|
598
|
+
|
|
606
599
|
The CLI commands still use the Creght backend and web app for the canonical
|
|
607
|
-
preview. The
|
|
608
|
-
not implement full production SSR.
|
|
600
|
+
preview. The CLI does not render sites locally.
|
|
609
601
|
|
|
610
602
|
```bash
|
|
611
603
|
creght login [--web=https://creght.cn]
|
|
@@ -614,7 +606,6 @@ creght project list
|
|
|
614
606
|
creght pull --site_id=<project_id>/<site_id> --dir=./mysite
|
|
615
607
|
creght push --site_id=<project_id>/<site_id> --dir=./mysite
|
|
616
608
|
creght resolve --list
|
|
617
|
-
creght dev --site_id=<project_id>/<site_id> --dir=./mysite [--web=https://creght.cn]
|
|
618
609
|
creght preview --site_id=<project_id>/<site_id>
|
|
619
610
|
creght publish --site_id=<project_id>/<site_id> [--note=<note>]
|
|
620
611
|
creght cms collections --site_id=<project_id>/<site_id>
|
|
@@ -628,6 +619,9 @@ creght table record create --site_id=<project_id>/<site_id> --table=<key> --data
|
|
|
628
619
|
creght func run --site_id=<project_id>/<site_id> --key=<key.method> --input=./input.json
|
|
629
620
|
creght upload --site_id=<project_id>/<site_id> --file=./image.png
|
|
630
621
|
creght version
|
|
622
|
+
creght version create [--note=<note>] [--allow-dirty]
|
|
623
|
+
creght version list [--limit=<n>] [--json]
|
|
624
|
+
creght version publish <version_no> [--note=<note>]
|
|
631
625
|
```
|
|
632
626
|
|
|
633
627
|
Command meanings:
|
|
@@ -638,16 +632,15 @@ Command meanings:
|
|
|
638
632
|
- `pull`: Download site files (including Func code under `backend/func/`) into a local workspace, three-way merging remote and local edits.
|
|
639
633
|
- `push`: Push local workspace changes to the remote site/project after a three-way conflict check.
|
|
640
634
|
- `resolve`: List files with conflict markers, or resolve one by keeping the local (`--ours`) or remote (`--theirs`) side.
|
|
641
|
-
- `dev`: Bidirectionally sync local files with cloud realtime files and the online Web editor.
|
|
642
635
|
- `preview`: Open the remote preview URL for a site in the browser.
|
|
643
|
-
- `publish`:
|
|
636
|
+
- `publish`: Snapshot the current remote site source into a new version and make it live in one step.
|
|
644
637
|
- `cms`: Manage CMS collections.
|
|
645
638
|
- `content`: Manage CMS content entries.
|
|
646
639
|
- `form`: Manage forms and form submissions.
|
|
647
640
|
- `table`: Manage project JSON tables and records used by Func.
|
|
648
|
-
- `func`: Run project Func backend code with sample input. Func code itself is edited as `backend/func` site files and synced with pull/push
|
|
641
|
+
- `func`: Run project Func backend code with sample input. Func code itself is edited as `backend/func` site files and synced with pull/push.
|
|
649
642
|
- `upload`: Upload a local file as a Creght site asset and print its URL.
|
|
650
|
-
- `version`: Print the installed CLI version.
|
|
643
|
+
- `version`: Print the installed CLI version. Subcommands manage site versions — immutable source snapshots: `version create` records one, `version list` shows them and which is live, `version publish` makes one live.
|
|
651
644
|
|
|
652
645
|
## Release
|
|
653
646
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "creght-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Creght CLI for syncing local site code with Creght.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/creght-dev/creght-cli#readme",
|
|
@@ -14,12 +14,6 @@
|
|
|
14
14
|
"bin": {
|
|
15
15
|
"creght": "bin/creght.js"
|
|
16
16
|
},
|
|
17
|
-
"exports": {
|
|
18
|
-
"./vite": {
|
|
19
|
-
"types": "./vite/index.d.ts",
|
|
20
|
-
"import": "./vite/index.js"
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
17
|
"scripts": {
|
|
24
18
|
"prepare:npm-binaries": "node scripts/prepare-npm-binaries.js",
|
|
25
19
|
"test:npm": "node scripts/install.js --check"
|
|
@@ -27,25 +21,12 @@
|
|
|
27
21
|
"files": [
|
|
28
22
|
"bin/",
|
|
29
23
|
"scripts/",
|
|
30
|
-
"vite/",
|
|
31
24
|
"vendor/",
|
|
32
25
|
"README.md"
|
|
33
26
|
],
|
|
34
27
|
"engines": {
|
|
35
28
|
"node": ">=18"
|
|
36
29
|
},
|
|
37
|
-
"peerDependencies": {
|
|
38
|
-
"esbuild": ">=0.20",
|
|
39
|
-
"vite": ">=5"
|
|
40
|
-
},
|
|
41
|
-
"peerDependenciesMeta": {
|
|
42
|
-
"esbuild": {
|
|
43
|
-
"optional": true
|
|
44
|
-
},
|
|
45
|
-
"vite": {
|
|
46
|
-
"optional": true
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
30
|
"os": [
|
|
50
31
|
"darwin",
|
|
51
32
|
"linux",
|
|
Binary file
|
package/vendor/darwin-x64/creght
CHANGED
|
Binary file
|
|
Binary file
|
package/vendor/linux-x64/creght
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/vite/import-map.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
function mergeExternalDeps(externalValue, requiredDeps) {
|
|
2
|
-
try {
|
|
3
|
-
externalValue = decodeURIComponent(externalValue)
|
|
4
|
-
} catch {
|
|
5
|
-
// Keep the original value and continue merging invalid encoded external lists.
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const seen = new Set()
|
|
9
|
-
const deps = externalValue
|
|
10
|
-
.split(',')
|
|
11
|
-
.map((dep) => dep.trim())
|
|
12
|
-
.filter(Boolean)
|
|
13
|
-
.filter((dep) => {
|
|
14
|
-
if (seen.has(dep)) return false
|
|
15
|
-
seen.add(dep)
|
|
16
|
-
return true
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
for (const dep of requiredDeps) {
|
|
20
|
-
if (seen.has(dep)) continue
|
|
21
|
-
seen.add(dep)
|
|
22
|
-
deps.push(dep)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return deps.join(',')
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function normalizeImportMapExternal(specifier, url) {
|
|
29
|
-
if (!/^https?:\/\//i.test(url)) return url
|
|
30
|
-
if (
|
|
31
|
-
specifier === 'react' ||
|
|
32
|
-
specifier.startsWith('react/') ||
|
|
33
|
-
specifier === 'react-dom' ||
|
|
34
|
-
specifier.startsWith('react-dom/')
|
|
35
|
-
) {
|
|
36
|
-
return url
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const requiredDeps = ['react', 'react-dom']
|
|
40
|
-
const hashIndex = url.indexOf('#')
|
|
41
|
-
const beforeHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url
|
|
42
|
-
const hash = hashIndex >= 0 ? url.slice(hashIndex) : ''
|
|
43
|
-
const hasPrefixSlash = specifier.endsWith('/') && beforeHash.endsWith('/')
|
|
44
|
-
const externalTarget = hasPrefixSlash ? beforeHash.slice(0, -1) : beforeHash
|
|
45
|
-
const externalSuffix = hasPrefixSlash ? '/' : ''
|
|
46
|
-
const externalMatch = externalTarget.match(/([?&])external=([^&#]*)/)
|
|
47
|
-
|
|
48
|
-
if (externalMatch && externalMatch.index !== undefined) {
|
|
49
|
-
const start = externalMatch.index + externalMatch[1].length + 'external='.length
|
|
50
|
-
const end = start + externalMatch[2].length
|
|
51
|
-
return `${externalTarget.slice(0, start)}${mergeExternalDeps(externalMatch[2], requiredDeps)}${externalTarget.slice(end)}${externalSuffix}${hash}`
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return `${externalTarget}${hasPrefixSlash || externalTarget.includes('?') ? '&' : '?'}external=${requiredDeps.join(',')}${externalSuffix}${hash}`
|
|
55
|
-
}
|
package/vite/index.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { Plugin } from 'vite'
|
|
2
|
-
|
|
3
|
-
export type CreghtVitePluginOptions = {
|
|
4
|
-
/**
|
|
5
|
-
* Local Creght project root. Defaults to Vite's root.
|
|
6
|
-
*/
|
|
7
|
-
root?: string
|
|
8
|
-
/**
|
|
9
|
-
* Creght API host used by the local /api proxy at runtime.
|
|
10
|
-
*/
|
|
11
|
-
apiHost?: string
|
|
12
|
-
/**
|
|
13
|
-
* Project id for runtime CMS/form requests.
|
|
14
|
-
*/
|
|
15
|
-
projectId?: string
|
|
16
|
-
/**
|
|
17
|
-
* Optional CLI auth token. When set, local /api proxy sends it as Bearer.
|
|
18
|
-
*/
|
|
19
|
-
token?: string
|
|
20
|
-
/**
|
|
21
|
-
* Platform import map entries. `creght dev` fills this from server system info.
|
|
22
|
-
* Manually configured entries override the built-in fallback when no server map is provided.
|
|
23
|
-
*/
|
|
24
|
-
importMap?: Record<string, string>
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export declare function creght(options?: CreghtVitePluginOptions): Plugin
|
|
28
|
-
export default creght
|
package/vite/index.js
DELETED
|
@@ -1,546 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { transform as esbuildTransform } from 'esbuild'
|
|
4
|
-
import { normalizeImportMapExternal } from './import-map.js'
|
|
5
|
-
|
|
6
|
-
const modulePrefix = '/@creght/module'
|
|
7
|
-
const assetPrefix = '/@creght/asset'
|
|
8
|
-
const runtimePrefix = '/@creght/runtime'
|
|
9
|
-
const pageExts = ['.tsx', '.ts', '.jsx', '.js']
|
|
10
|
-
const sourceExts = new Set([...pageExts, '.css'])
|
|
11
|
-
|
|
12
|
-
const fallbackImportMap = {
|
|
13
|
-
react: 'https://esm.talizen.com/react@19.2.4?dev',
|
|
14
|
-
'react/': 'https://esm.talizen.com/react@19.2.4&dev/',
|
|
15
|
-
'react-dom': 'https://esm.talizen.com/react-dom@19.2.4?dev',
|
|
16
|
-
'react-dom/': 'https://esm.talizen.com/react-dom@19.2.4&dev/',
|
|
17
|
-
'react-dom/client': 'https://esm.talizen.com/react-dom@19.2.4/client?dev',
|
|
18
|
-
'class-variance-authority': 'https://esm.talizen.com/class-variance-authority@0.7.1',
|
|
19
|
-
clsx: 'https://esm.talizen.com/clsx@2.1.1',
|
|
20
|
-
'tailwind-merge': 'https://esm.talizen.com/tailwind-merge@3.5.0',
|
|
21
|
-
'@radix-ui/react-slot': 'https://esm.talizen.com/@radix-ui/react-slot@1.2.4',
|
|
22
|
-
'lucide-react': 'https://esm.talizen.com/lucide-react@0.577.0?dev&external=react,react-dom',
|
|
23
|
-
motion: 'https://esm.talizen.com/motion@12.38.0?dev',
|
|
24
|
-
'motion/react': 'https://esm.talizen.com/motion@12.38.0/react?dev&external=react,react-dom',
|
|
25
|
-
'framer-motion': 'https://esm.talizen.com/framer-motion@12.38.0?dev&external=react,react-dom',
|
|
26
|
-
three: 'https://esm.talizen.com/three@0.167.1',
|
|
27
|
-
'three/': 'https://esm.talizen.com/three@0.167.1/',
|
|
28
|
-
'@react-three/fiber': 'https://esm.talizen.com/@react-three/fiber@9.3.0?external=react,react-dom,three',
|
|
29
|
-
'@react-three/drei': 'https://esm.talizen.com/@react-three/drei@10.7.4?external=react,react-dom,three,@react-three',
|
|
30
|
-
talizen: 'https://esm.talizen.com/talizen@0.1.4',
|
|
31
|
-
'talizen/': 'https://esm.talizen.com/talizen@0.1.4/',
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const escapeHtml = (s) => String(s)
|
|
35
|
-
.replaceAll('&', '&')
|
|
36
|
-
.replaceAll('<', '<')
|
|
37
|
-
.replaceAll('>', '>')
|
|
38
|
-
.replaceAll('"', '"')
|
|
39
|
-
|
|
40
|
-
const jsonScript = (value) => JSON.stringify(value).replaceAll('<', '\\u003c')
|
|
41
|
-
|
|
42
|
-
const transformWithEsbuild = (source, filename, options) =>
|
|
43
|
-
esbuildTransform(source, { ...options, sourcefile: filename })
|
|
44
|
-
|
|
45
|
-
const isRelativeSpecifier = (specifier) =>
|
|
46
|
-
specifier.startsWith('./') || specifier.startsWith('../') || specifier.startsWith('/')
|
|
47
|
-
|
|
48
|
-
const contentTypeForPath = (file) => {
|
|
49
|
-
switch (path.extname(file).toLowerCase()) {
|
|
50
|
-
case '.css':
|
|
51
|
-
return 'text/css; charset=utf-8'
|
|
52
|
-
case '.js':
|
|
53
|
-
case '.mjs':
|
|
54
|
-
return 'text/javascript; charset=utf-8'
|
|
55
|
-
case '.json':
|
|
56
|
-
return 'application/json; charset=utf-8'
|
|
57
|
-
case '.svg':
|
|
58
|
-
return 'image/svg+xml'
|
|
59
|
-
case '.png':
|
|
60
|
-
return 'image/png'
|
|
61
|
-
case '.jpg':
|
|
62
|
-
case '.jpeg':
|
|
63
|
-
return 'image/jpeg'
|
|
64
|
-
case '.gif':
|
|
65
|
-
return 'image/gif'
|
|
66
|
-
case '.webp':
|
|
67
|
-
return 'image/webp'
|
|
68
|
-
case '.woff':
|
|
69
|
-
return 'font/woff'
|
|
70
|
-
case '.woff2':
|
|
71
|
-
return 'font/woff2'
|
|
72
|
-
default:
|
|
73
|
-
return 'application/octet-stream'
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const normalizeProjectPath = (value) => {
|
|
78
|
-
const out = path.posix.normalize('/' + value.replaceAll(path.sep, '/').replace(/^\/+/, ''))
|
|
79
|
-
if (out.startsWith('/../')) {
|
|
80
|
-
throw new Error(`unsafe Creght path: ${value}`)
|
|
81
|
-
}
|
|
82
|
-
return out
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function exists(file) {
|
|
86
|
-
try {
|
|
87
|
-
await fs.access(file)
|
|
88
|
-
return true
|
|
89
|
-
} catch {
|
|
90
|
-
return false
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function resolveFile(projectRoot, projectPath) {
|
|
95
|
-
const clean = normalizeProjectPath(projectPath)
|
|
96
|
-
const abs = path.join(projectRoot, clean.slice(1))
|
|
97
|
-
if (await exists(abs)) return { projectPath: clean, abs }
|
|
98
|
-
|
|
99
|
-
for (const ext of pageExts) {
|
|
100
|
-
if (await exists(abs + ext)) {
|
|
101
|
-
return { projectPath: clean + ext, abs: abs + ext }
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
for (const ext of pageExts) {
|
|
106
|
-
const indexPath = path.join(abs, 'Index' + ext)
|
|
107
|
-
if (await exists(indexPath)) {
|
|
108
|
-
return { projectPath: normalizeProjectPath(path.posix.join(clean, 'Index' + ext)), abs: indexPath }
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
throw new Error(`Creght module not found: ${projectPath}`)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async function readSiteConfig(projectRoot) {
|
|
116
|
-
for (const name of ['creght.config.ts', 'creght.config.js', 'talizen.config.ts', 'talizen.config.js', 'folia.config.ts', 'folia.config.js']) {
|
|
117
|
-
const abs = path.join(projectRoot, name)
|
|
118
|
-
if (!(await exists(abs))) continue
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
const source = await fs.readFile(abs, 'utf8')
|
|
122
|
-
const transformed = await transformWithEsbuild(source, abs, {
|
|
123
|
-
loader: name.endsWith('.ts') ? 'ts' : 'js',
|
|
124
|
-
format: 'cjs',
|
|
125
|
-
target: 'es2020',
|
|
126
|
-
})
|
|
127
|
-
const module = { exports: {} }
|
|
128
|
-
const require = (specifier) => {
|
|
129
|
-
if (specifier === 'creght' || specifier === 'creght/config' || specifier === 'talizen' || specifier === 'talizen/config') {
|
|
130
|
-
return { defineConfig: (config) => config }
|
|
131
|
-
}
|
|
132
|
-
if (specifier.startsWith('creght/')) return {}
|
|
133
|
-
if (specifier.startsWith('talizen/')) return {}
|
|
134
|
-
throw new Error(`[creght.config] unsupported import: ${specifier}`)
|
|
135
|
-
}
|
|
136
|
-
const fn = new Function('module', 'exports', 'require', 'defineConfig', transformed.code)
|
|
137
|
-
fn(module, module.exports, require, (config) => config)
|
|
138
|
-
const value = module.exports?.default ?? module.exports
|
|
139
|
-
return value && typeof value === 'object' ? value : {}
|
|
140
|
-
} catch (err) {
|
|
141
|
-
console.warn(`[creght vite] failed to evaluate ${name}:`, err)
|
|
142
|
-
return {}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return {}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async function readIndexCss(projectRoot, config) {
|
|
150
|
-
if (typeof config.tailwindCss === 'string') return config.tailwindCss
|
|
151
|
-
|
|
152
|
-
const cssPath = path.join(projectRoot, 'index.css')
|
|
153
|
-
if (!(await exists(cssPath))) return ''
|
|
154
|
-
return fs.readFile(cssPath, 'utf8')
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async function buildImportMap(projectRoot, options) {
|
|
158
|
-
const config = await readSiteConfig(projectRoot)
|
|
159
|
-
const userImports = config.importMap?.imports || {}
|
|
160
|
-
const imports = Object.keys(options.importMap || {}).length > 0
|
|
161
|
-
? { ...options.importMap }
|
|
162
|
-
: { ...fallbackImportMap }
|
|
163
|
-
|
|
164
|
-
for (const [specifier, url] of Object.entries(userImports)) {
|
|
165
|
-
imports[specifier] = normalizeImportMapExternal(specifier, String(url))
|
|
166
|
-
}
|
|
167
|
-
for (const [specifier, url] of Object.entries(options.importMap || {})) {
|
|
168
|
-
imports[specifier] = normalizeImportMapExternal(specifier, String(url))
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return { config, imports }
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function listPages(projectRoot) {
|
|
175
|
-
const pageRoot = path.join(projectRoot, 'page')
|
|
176
|
-
if (!(await exists(pageRoot))) return []
|
|
177
|
-
|
|
178
|
-
const out = []
|
|
179
|
-
async function walk(dir) {
|
|
180
|
-
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
181
|
-
for (const entry of entries) {
|
|
182
|
-
const abs = path.join(dir, entry.name)
|
|
183
|
-
if (entry.isDirectory()) {
|
|
184
|
-
await walk(abs)
|
|
185
|
-
continue
|
|
186
|
-
}
|
|
187
|
-
const ext = path.extname(entry.name)
|
|
188
|
-
if (!pageExts.includes(ext)) continue
|
|
189
|
-
if (entry.name.includes('.canvas.')) continue
|
|
190
|
-
const rel = normalizeProjectPath(path.relative(projectRoot, abs))
|
|
191
|
-
out.push({ file: rel, route: routeFromPagePath(rel) })
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
await walk(pageRoot)
|
|
195
|
-
return out.sort((a, b) => a.route.localeCompare(b.route))
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function routeFromPagePath(file) {
|
|
199
|
-
const withoutExt = file.replace(/^\/page\//, '').replace(/\.[^.]+$/, '')
|
|
200
|
-
if (withoutExt === 'Index') return '/'
|
|
201
|
-
return '/' + withoutExt
|
|
202
|
-
.replace(/\/Index$/, '')
|
|
203
|
-
.split('/')
|
|
204
|
-
.map((segment) => segment.startsWith('[') && segment.endsWith(']') ? `:${segment.slice(1, -1)}` : segment.toLowerCase())
|
|
205
|
-
.join('/')
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function matchRoute(routes, pathname) {
|
|
209
|
-
const normalized = pathname !== '/' ? pathname.replace(/\/+$/, '') : '/'
|
|
210
|
-
const exact = routes.find((r) => r.route === normalized)
|
|
211
|
-
if (exact) return { ...exact, params: {} }
|
|
212
|
-
|
|
213
|
-
for (const route of routes) {
|
|
214
|
-
const routeParts = route.route.split('/').filter(Boolean)
|
|
215
|
-
const pathParts = normalized.split('/').filter(Boolean)
|
|
216
|
-
if (routeParts.length !== pathParts.length) continue
|
|
217
|
-
const params = {}
|
|
218
|
-
let ok = true
|
|
219
|
-
for (let i = 0; i < routeParts.length; i++) {
|
|
220
|
-
const part = routeParts[i]
|
|
221
|
-
if (part.startsWith(':')) {
|
|
222
|
-
params[part.slice(1)] = decodeURIComponent(pathParts[i])
|
|
223
|
-
continue
|
|
224
|
-
}
|
|
225
|
-
if (part !== pathParts[i].toLowerCase()) {
|
|
226
|
-
ok = false
|
|
227
|
-
break
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (ok) return { ...route, params }
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return { ...(routes.find((r) => r.route === '/') || routes[0]), params: {} }
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function rewriteModuleSpecifiers(code, importerPath) {
|
|
237
|
-
const rewrite = (specifier) => {
|
|
238
|
-
if (!isRelativeSpecifier(specifier)) return specifier
|
|
239
|
-
const [rawPath, rawQuery = ''] = specifier.split('?')
|
|
240
|
-
const importerDir = path.posix.dirname(importerPath)
|
|
241
|
-
const resolved = normalizeProjectPath(path.posix.join(importerDir, rawPath))
|
|
242
|
-
const target = rawQuery ? `${resolved}?${rawQuery}` : resolved
|
|
243
|
-
return `${modulePrefix}?path=${encodeURIComponent(target)}`
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return code
|
|
247
|
-
.replace(/(from\s*["'])([^"']+)(["'])/g, (_, before, specifier, after) => `${before}${rewrite(specifier)}${after}`)
|
|
248
|
-
.replace(/(import\s*["'])([^"']+)(["'])/g, (_, before, specifier, after) => `${before}${rewrite(specifier)}${after}`)
|
|
249
|
-
.replace(/(import\s*\(\s*["'])([^"']+)(["']\s*\))/g, (_, before, specifier, after) => `${before}${rewrite(specifier)}${after}`)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
async function transformProjectModule(projectRoot, projectPath) {
|
|
253
|
-
const [pathWithoutQuery, query = ''] = projectPath.split('?')
|
|
254
|
-
const { projectPath: resolvedProjectPath, abs } = await resolveFile(projectRoot, pathWithoutQuery)
|
|
255
|
-
const ext = path.extname(abs).toLowerCase()
|
|
256
|
-
|
|
257
|
-
if (query === 'raw') {
|
|
258
|
-
const source = await fs.readFile(abs, 'utf8')
|
|
259
|
-
return `export default ${JSON.stringify(source)};\n`
|
|
260
|
-
}
|
|
261
|
-
if (query === 'url') {
|
|
262
|
-
return `export default ${JSON.stringify(`${assetPrefix}?path=${encodeURIComponent(resolvedProjectPath)}`)};\n`
|
|
263
|
-
}
|
|
264
|
-
if (!sourceExts.has(ext)) {
|
|
265
|
-
return `export default ${JSON.stringify(`${assetPrefix}?path=${encodeURIComponent(resolvedProjectPath)}`)};\n`
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const source = await fs.readFile(abs, 'utf8')
|
|
269
|
-
if (abs.endsWith('.css')) {
|
|
270
|
-
return `
|
|
271
|
-
const css = ${JSON.stringify(source)};
|
|
272
|
-
let style = document.querySelector('style[data-talizen-css-module="${resolvedProjectPath}"]');
|
|
273
|
-
if (!style) {
|
|
274
|
-
style = document.createElement('style');
|
|
275
|
-
style.dataset.talizenCssModule = ${JSON.stringify(resolvedProjectPath)};
|
|
276
|
-
document.head.appendChild(style);
|
|
277
|
-
}
|
|
278
|
-
style.textContent = css;
|
|
279
|
-
export default css;
|
|
280
|
-
`
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const loader = abs.endsWith('.tsx') ? 'tsx' : abs.endsWith('.ts') ? 'ts' : abs.endsWith('.jsx') ? 'jsx' : 'js'
|
|
284
|
-
const result = await transformWithEsbuild(source, abs, {
|
|
285
|
-
loader,
|
|
286
|
-
jsx: 'automatic',
|
|
287
|
-
jsxDev: true,
|
|
288
|
-
sourcemap: 'inline',
|
|
289
|
-
target: 'es2020',
|
|
290
|
-
define: {
|
|
291
|
-
'process.env.NODE_ENV': '"development"',
|
|
292
|
-
'process.env.RENDER_ENV': '"design"',
|
|
293
|
-
'process.env.RENDER_MODE': '"design"',
|
|
294
|
-
},
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
return rewriteModuleSpecifiers(result.code, resolvedProjectPath)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function renderRuntimeScript(entryFile, params, options) {
|
|
301
|
-
const projectId = options.projectId || ''
|
|
302
|
-
const authHeaders = options.token ? { Authorization: `Bearer ${options.token}` } : {}
|
|
303
|
-
|
|
304
|
-
return `
|
|
305
|
-
import React from 'react';
|
|
306
|
-
import { createRoot } from 'react-dom/client';
|
|
307
|
-
import { createHotContext } from '/@vite/client';
|
|
308
|
-
|
|
309
|
-
const creghtRuntimeHeaders = ${jsonScript(authHeaders)};
|
|
310
|
-
const creghtRuntimeFetch = window.fetch.bind(window);
|
|
311
|
-
|
|
312
|
-
window.TalizenConfig = {
|
|
313
|
-
baseUrl: window.location.origin + '/api/u/v2/project/' + ${JSON.stringify(projectId)},
|
|
314
|
-
headers: creghtRuntimeHeaders,
|
|
315
|
-
fetch(input, init = {}) {
|
|
316
|
-
const headers = new Headers(init.headers || {});
|
|
317
|
-
for (const [key, value] of Object.entries(creghtRuntimeHeaders)) {
|
|
318
|
-
if (!headers.has(key)) headers.set(key, value);
|
|
319
|
-
}
|
|
320
|
-
return creghtRuntimeFetch(input, { ...init, headers });
|
|
321
|
-
},
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
const rootEl = document.getElementById('root');
|
|
325
|
-
const root = createRoot(rootEl);
|
|
326
|
-
let renderVersion = 0;
|
|
327
|
-
|
|
328
|
-
async function loadPageModule() {
|
|
329
|
-
return import(${JSON.stringify(`${modulePrefix}?path=${encodeURIComponent(entryFile)}`)} + '&t=' + Date.now());
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
async function renderPage() {
|
|
333
|
-
const version = ++renderVersion;
|
|
334
|
-
const pageModule = await loadPageModule();
|
|
335
|
-
if (version !== renderVersion) return;
|
|
336
|
-
|
|
337
|
-
const Page = pageModule.default || pageModule.App;
|
|
338
|
-
let props = {};
|
|
339
|
-
|
|
340
|
-
if (!Page) {
|
|
341
|
-
throw new Error('Creght page has no default export: ${entryFile}');
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (typeof pageModule.getServerSideProps === 'function') {
|
|
345
|
-
const result = await pageModule.getServerSideProps({
|
|
346
|
-
query: Object.fromEntries(new URLSearchParams(window.location.search)),
|
|
347
|
-
params: ${jsonScript(params)},
|
|
348
|
-
});
|
|
349
|
-
props = result && result.props ? result.props : {};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
root.render(React.createElement(Page, props));
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const hot = createHotContext(${JSON.stringify(`${runtimePrefix}?entry=${encodeURIComponent(entryFile)}`)});
|
|
356
|
-
|
|
357
|
-
await renderPage();
|
|
358
|
-
|
|
359
|
-
hot.on('creght:update', () => {
|
|
360
|
-
renderPage().catch((err) => {
|
|
361
|
-
console.error('[creght vite] hot update failed', err);
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
`
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
async function renderHtml(projectRoot, pathname, options) {
|
|
368
|
-
const routes = await listPages(projectRoot)
|
|
369
|
-
if (routes.length === 0) {
|
|
370
|
-
return `<!doctype html><div style="font:14px system-ui;padding:24px">No Creght pages found in <code>/page</code>.</div>`
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const matched = matchRoute(routes, pathname)
|
|
374
|
-
const { config, imports } = await buildImportMap(projectRoot, options)
|
|
375
|
-
const indexCss = await readIndexCss(projectRoot, config)
|
|
376
|
-
const customCode = config.customCode || {}
|
|
377
|
-
|
|
378
|
-
return `<!doctype html>
|
|
379
|
-
<html lang="en">
|
|
380
|
-
<head>
|
|
381
|
-
<meta charset="UTF-8">
|
|
382
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
383
|
-
<title>${escapeHtml(matched.route === '/' ? 'Creght' : matched.route)}</title>
|
|
384
|
-
<script type="importmap">${jsonScript({ imports })}</script>
|
|
385
|
-
<script type="module" src="/@vite/client"></script>
|
|
386
|
-
<style type="text/tailwindcss">${indexCss}</style>
|
|
387
|
-
<script type="module" src="https://esm.talizen.com/@tailwindcss/browser@4"></script>
|
|
388
|
-
${customCode.head || ''}
|
|
389
|
-
</head>
|
|
390
|
-
<body>
|
|
391
|
-
${customCode.bodyStart || ''}
|
|
392
|
-
<div id="root"></div>
|
|
393
|
-
<script type="module" src="${runtimePrefix}?entry=${encodeURIComponent(matched.file)}¶ms=${encodeURIComponent(JSON.stringify(matched.params))}&t=${Date.now()}"></script>
|
|
394
|
-
${customCode.bodyEnd || customCode.body || ''}
|
|
395
|
-
</body>
|
|
396
|
-
</html>`
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
export function creght(options = {}) {
|
|
400
|
-
let projectRoot = ''
|
|
401
|
-
const apiHost = (options.apiHost || 'https://creght.cn').replace(/\/+$/, '')
|
|
402
|
-
|
|
403
|
-
return {
|
|
404
|
-
name: 'creght-local-preview',
|
|
405
|
-
enforce: 'pre',
|
|
406
|
-
configResolved(config) {
|
|
407
|
-
projectRoot = path.resolve(options.root || config.root)
|
|
408
|
-
},
|
|
409
|
-
configureServer(server) {
|
|
410
|
-
const hmrTimers = new Map()
|
|
411
|
-
const sendCreghtUpdate = (file) => {
|
|
412
|
-
if (!file.startsWith(projectRoot)) return
|
|
413
|
-
|
|
414
|
-
const projectPath = normalizeProjectPath(path.relative(projectRoot, file))
|
|
415
|
-
const existing = hmrTimers.get(projectPath)
|
|
416
|
-
if (existing) clearTimeout(existing)
|
|
417
|
-
|
|
418
|
-
hmrTimers.set(projectPath, setTimeout(() => {
|
|
419
|
-
hmrTimers.delete(projectPath)
|
|
420
|
-
server.ws.send({
|
|
421
|
-
type: 'custom',
|
|
422
|
-
event: 'creght:update',
|
|
423
|
-
data: {
|
|
424
|
-
file: projectPath,
|
|
425
|
-
time: Date.now(),
|
|
426
|
-
},
|
|
427
|
-
})
|
|
428
|
-
}, 80))
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
server.watcher.add(path.join(projectRoot, '**/*'))
|
|
432
|
-
server.watcher.on('change', sendCreghtUpdate)
|
|
433
|
-
server.watcher.on('add', sendCreghtUpdate)
|
|
434
|
-
server.watcher.on('unlink', sendCreghtUpdate)
|
|
435
|
-
|
|
436
|
-
server.middlewares.use(async (req, res, next) => {
|
|
437
|
-
try {
|
|
438
|
-
const url = new URL(req.url || '/', 'http://localhost')
|
|
439
|
-
|
|
440
|
-
if (url.pathname.startsWith('/api/')) {
|
|
441
|
-
const headers = new Headers()
|
|
442
|
-
for (const [key, value] of Object.entries(req.headers)) {
|
|
443
|
-
if (Array.isArray(value)) {
|
|
444
|
-
headers.set(key, value.join(', '))
|
|
445
|
-
} else if (value != null) {
|
|
446
|
-
headers.set(key, value)
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
headers.set('host', new URL(apiHost).host)
|
|
450
|
-
if (options.token) {
|
|
451
|
-
headers.delete('cookie')
|
|
452
|
-
headers.set('authorization', `Bearer ${options.token}`)
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
const method = req.method || 'GET'
|
|
456
|
-
const body = method === 'GET' || method === 'HEAD' ? undefined : req
|
|
457
|
-
const upstream = await fetch(apiHost + url.pathname + url.search, {
|
|
458
|
-
method,
|
|
459
|
-
headers,
|
|
460
|
-
body,
|
|
461
|
-
// Required by Node fetch when the request body is a stream.
|
|
462
|
-
duplex: body ? 'half' : undefined,
|
|
463
|
-
})
|
|
464
|
-
|
|
465
|
-
res.statusCode = upstream.status
|
|
466
|
-
upstream.headers.forEach((value, key) => {
|
|
467
|
-
const lowerKey = key.toLowerCase()
|
|
468
|
-
if (lowerKey === 'content-encoding') return
|
|
469
|
-
if (lowerKey === 'content-length') return
|
|
470
|
-
if (lowerKey === 'transfer-encoding') return
|
|
471
|
-
if (lowerKey === 'connection') return
|
|
472
|
-
res.setHeader(key, value)
|
|
473
|
-
})
|
|
474
|
-
const data = Buffer.from(await upstream.arrayBuffer())
|
|
475
|
-
res.end(data)
|
|
476
|
-
return
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (url.pathname === modulePrefix) {
|
|
480
|
-
const projectPath = url.searchParams.get('path')
|
|
481
|
-
if (!projectPath) {
|
|
482
|
-
res.statusCode = 400
|
|
483
|
-
res.end('missing path')
|
|
484
|
-
return
|
|
485
|
-
}
|
|
486
|
-
const code = await transformProjectModule(projectRoot, projectPath)
|
|
487
|
-
res.setHeader('Content-Type', 'text/javascript; charset=utf-8')
|
|
488
|
-
res.end(code)
|
|
489
|
-
return
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
if (url.pathname === runtimePrefix) {
|
|
493
|
-
const entry = url.searchParams.get('entry')
|
|
494
|
-
const rawParams = url.searchParams.get('params') || '{}'
|
|
495
|
-
if (!entry) {
|
|
496
|
-
res.statusCode = 400
|
|
497
|
-
res.end('missing entry')
|
|
498
|
-
return
|
|
499
|
-
}
|
|
500
|
-
let params = {}
|
|
501
|
-
try {
|
|
502
|
-
params = JSON.parse(rawParams)
|
|
503
|
-
} catch {
|
|
504
|
-
params = {}
|
|
505
|
-
}
|
|
506
|
-
res.setHeader('Content-Type', 'text/javascript; charset=utf-8')
|
|
507
|
-
res.end(renderRuntimeScript(entry, params, options))
|
|
508
|
-
return
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
if (url.pathname === assetPrefix) {
|
|
512
|
-
const projectPath = url.searchParams.get('path')
|
|
513
|
-
if (!projectPath) {
|
|
514
|
-
res.statusCode = 400
|
|
515
|
-
res.end('missing path')
|
|
516
|
-
return
|
|
517
|
-
}
|
|
518
|
-
const { abs } = await resolveFile(projectRoot, projectPath)
|
|
519
|
-
res.setHeader('Content-Type', contentTypeForPath(abs))
|
|
520
|
-
res.end(await fs.readFile(abs))
|
|
521
|
-
return
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (req.method !== 'GET') {
|
|
525
|
-
next()
|
|
526
|
-
return
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
const accept = req.headers.accept || ''
|
|
530
|
-
if (!accept.includes('text/html')) {
|
|
531
|
-
next()
|
|
532
|
-
return
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const html = await renderHtml(projectRoot, url.pathname, options)
|
|
536
|
-
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
|
537
|
-
res.end(html)
|
|
538
|
-
} catch (err) {
|
|
539
|
-
next(err)
|
|
540
|
-
}
|
|
541
|
-
})
|
|
542
|
-
},
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
export default creght
|
package/vite/index.test.mjs
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import { describe, it } from 'node:test'
|
|
3
|
-
|
|
4
|
-
import { normalizeImportMapExternal } from './import-map.js'
|
|
5
|
-
|
|
6
|
-
describe('normalizeImportMapExternal', () => {
|
|
7
|
-
it('adds react and react-dom externals to regular import map entries', () => {
|
|
8
|
-
assert.equal(
|
|
9
|
-
normalizeImportMapExternal('framer-motion', 'https://esm.talizen.com/framer-motion'),
|
|
10
|
-
'https://esm.talizen.com/framer-motion?external=react,react-dom',
|
|
11
|
-
)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('merges missing externals without dropping existing entries', () => {
|
|
15
|
-
assert.equal(
|
|
16
|
-
normalizeImportMapExternal(
|
|
17
|
-
'@react-three/drei',
|
|
18
|
-
'https://esm.talizen.com/@react-three/drei?external=three,react,@react-three/fiber',
|
|
19
|
-
),
|
|
20
|
-
'https://esm.talizen.com/@react-three/drei?external=three,react,@react-three/fiber,react-dom',
|
|
21
|
-
)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('inserts externals before the trailing slash for prefix import map entries', () => {
|
|
25
|
-
assert.equal(
|
|
26
|
-
normalizeImportMapExternal('talizen/', 'https://esm.talizen.com/talizen@0.1.4/'),
|
|
27
|
-
'https://esm.talizen.com/talizen@0.1.4&external=react,react-dom/',
|
|
28
|
-
)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('merges externals before the trailing slash for prefix entries', () => {
|
|
32
|
-
assert.equal(
|
|
33
|
-
normalizeImportMapExternal(
|
|
34
|
-
'@kobalte/core/',
|
|
35
|
-
'https://esm.talizen.com/@kobalte/core&external=react/',
|
|
36
|
-
),
|
|
37
|
-
'https://esm.talizen.com/@kobalte/core&external=react,react-dom/',
|
|
38
|
-
)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('does not modify host React runtime specifiers', () => {
|
|
42
|
-
assert.equal(
|
|
43
|
-
normalizeImportMapExternal('react-dom/client', 'https://esm.talizen.com/react-dom@19/client?dev'),
|
|
44
|
-
'https://esm.talizen.com/react-dom@19/client?dev',
|
|
45
|
-
)
|
|
46
|
-
})
|
|
47
|
-
})
|
package/vite/package.json
DELETED