acrylblends 0.6.3 → 0.7.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 +4 -3
- package/docs/framework/README.md +2 -2
- package/docs/framework/UPSTREAM.md +1 -1
- package/docs/framework/guides/jsx.md +51 -1
- package/docs/framework/helpers/ssg.md +3 -6
- package/docs/framework/middleware/builtin/jsx-renderer.md +0 -3
- package/docs/framework/upstream.json +2 -2
- package/lib/accepts.d.ts +1 -1
- package/lib/acryl-base.d.ts +1 -1
- package/lib/adapter.d.ts +1 -1
- package/lib/{app-wFwpg-bt.d.ts → app-D8riatvv.d.ts} +1 -1
- package/lib/{base-ddiVmiE7.js → base-NSN_SNv-.js} +1 -0
- package/lib/basic-auth.d.ts +1 -1
- package/lib/bearer-auth.d.ts +1 -1
- package/lib/body-limit.d.ts +1 -1
- package/lib/cache.d.ts +1 -1
- package/lib/cache.js +4 -4
- package/lib/{client-BHTr318g.js → client-DDLXwhBo.js} +2 -125
- package/lib/client.d.ts +2 -2
- package/lib/client.js +2 -1
- package/lib/combine.d.ts +1 -1
- package/lib/{components-DYZdxJIJ.js → components-JXVm45kK.js} +1 -1
- package/lib/{components-Ce5FRyHU.js → components-cEW3ovK0.js} +3 -3
- package/lib/compress.d.ts +1 -1
- package/lib/conninfo.d.ts +2 -2
- package/lib/{context-DVQBjuns.d.ts → context-DS3fYyG-.d.ts} +1 -1
- package/lib/context-storage.d.ts +1 -1
- package/lib/cookie.d.ts +1 -1
- package/lib/cors.d.ts +1 -1
- package/lib/csrf.d.ts +1 -1
- package/lib/dev.d.ts +2 -2
- package/lib/etag.d.ts +1 -1
- package/lib/factory.d.ts +2 -2
- package/lib/{hooks-Bq5q9IMl.js → hooks-w72iLeFR.js} +6 -6
- package/lib/{index-CUTfU69R.d.ts → index-Bb7YJKCq.d.ts} +2 -2
- package/lib/{index-CsvB-F9j.d.ts → index-BvFh1Gfx.d.ts} +1 -1
- package/lib/{index-Cqcdluyn.d.ts → index-CTy4N7ng.d.ts} +2 -2
- package/lib/index.d.ts +4 -4
- package/lib/ip-restriction.d.ts +2 -2
- package/lib/jsx/dom/client.js +1 -1
- package/lib/jsx/dom/jsx-dev-runtime.js +1 -1
- package/lib/jsx/dom/jsx-runtime.js +1 -1
- package/lib/jsx/dom/server.js +2 -2
- package/lib/jsx/dom.js +4 -4
- package/lib/jsx/jsx-dev-runtime.js +1 -1
- package/lib/jsx/jsx-runtime.js +1 -1
- package/lib/jsx/streaming.js +1 -1
- package/lib/{jsx-dev-runtime-kAhR_4C7.js → jsx-dev-runtime-CWLRA2F3.js} +2 -2
- package/lib/jsx-renderer.d.ts +75 -0
- package/lib/jsx-renderer.js +99 -0
- package/lib/jsx.js +4 -4
- package/lib/jwk.d.ts +1 -1
- package/lib/jwt.d.ts +1 -1
- package/lib/language.d.ts +1 -1
- package/lib/logger.d.ts +1 -1
- package/lib/loopback-only.d.ts +1 -1
- package/lib/method-not-allowed.d.ts +2 -2
- package/lib/method-override.d.ts +2 -2
- package/lib/mount.d.ts +1 -1
- package/lib/node-server/conninfo.d.ts +1 -1
- package/lib/node-server/register.d.ts +1 -0
- package/lib/node-server/register.js +73 -0
- package/lib/node-server/serve-static.d.ts +1 -1
- package/lib/node-server/serve-static.js +1 -1
- package/lib/node-server.d.ts +1 -1
- package/lib/node-server.js +1 -1
- package/lib/powered-by.d.ts +1 -1
- package/lib/pretty-json.d.ts +1 -1
- package/lib/quick.d.ts +1 -1
- package/lib/request-id.d.ts +1 -1
- package/lib/request.d.ts +1 -1
- package/lib/route.d.ts +1 -1
- package/lib/schema-validator.d.ts +1 -1
- package/lib/secure-headers.d.ts +1 -1
- package/lib/{serve-static-Cw23shXv.js → serve-static-B14MF1Va.js} +1 -0
- package/lib/serve-static.d.ts +1 -1
- package/lib/ssg-BwjcA-Ge.js +389 -0
- package/lib/ssg.d.ts +156 -0
- package/lib/ssg.js +2 -0
- package/lib/streaming.d.ts +1 -1
- package/lib/testing.d.ts +3 -3
- package/lib/testing.js +1 -1
- package/lib/timeout.d.ts +1 -1
- package/lib/timing.d.ts +1 -1
- package/lib/tiny.d.ts +1 -1
- package/lib/trailing-slash.d.ts +1 -1
- package/lib/{types-nEL8VPDC.d.ts → types-Dv_nV2cv.d.ts} +1 -1
- package/lib/{types-CQiGmsWW.d.ts → types-zNxyEpG2.d.ts} +2 -2
- package/lib/types.d.ts +1 -1
- package/lib/utils/body.d.ts +1 -1
- package/lib/utils/ipaddr.d.ts +1 -1
- package/lib/utils-Cx61GFMC.js +125 -0
- package/lib/validator.d.ts +1 -1
- package/lib/ws.d.ts +1 -1
- package/package.json +13 -1
- package/plugins/acryl-inspect/upstream.json +1 -1
- package/plugins/acryl-node-server/src/register.ts +86 -0
- package/plugins/acryl-node-server/upstream.json +5 -3
- package/plugins/acryl-router/src/core/helper/ssg/index.ts +14 -0
- package/plugins/acryl-router/src/core/helper/ssg/middleware.ts +83 -0
- package/plugins/acryl-router/src/core/helper/ssg/plugins.ts +72 -0
- package/plugins/acryl-router/src/core/helper/ssg/ssg.ts +471 -0
- package/plugins/acryl-router/src/core/helper/ssg/utils.ts +118 -0
- package/plugins/acryl-router/src/core/jsx/dom/render.ts +6 -6
- package/plugins/acryl-router/src/core/middleware/cache/index.ts +4 -4
- package/plugins/acryl-router/src/core/middleware/jsx-renderer/index.ts +162 -0
- package/plugins/acryl-router/src/helpers/index.ts +1 -0
- package/plugins/acryl-router/src/middleware/index.ts +1 -0
- package/plugins/acryl-router/upstream.json +2 -2
- package/plugins/acryl-router-http/upstream.json +1 -1
- package/plugins/acryl-state/upstream.json +1 -1
- package/plugins/desktop/main.ts +4 -1
- package/plugins/desktop/upstream.json +1 -1
- package/plugins/desktop-web/main.ts +3 -1
- package/plugins/desktop-web/upstream.json +1 -1
- package/plugins/extensions/resolve.ts +4 -0
- package/shared/AGENTS.core.md +3 -0
- package/templates/agent/package.json +7 -3
- package/templates/agent/tsconfig.json +10 -2
- package/templates/blank/package.json +7 -3
- package/templates/blank/src/about.tsx +14 -0
- package/templates/blank/src/routes.ts +2 -0
- package/templates/blank/test/app.test.ts +15 -0
- package/templates/blank/tsconfig.json +3 -1
- package/templates/todo/package.json +7 -3
- package/templates/todo/tsconfig.json +3 -1
- package/templates/todo/upstream.json +1 -1
- package/plugins/acryl-inspect/tests/report.spec.ts +0 -69
- package/plugins/acryl-inspect/tests/service.spec.ts +0 -121
- package/plugins/acryl-node-server/tests/adapter/app.ts +0 -76
- package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +0 -5
- package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +0 -1
- package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +0 -34
- package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +0 -248
- package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +0 -457
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +0 -23
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +0 -27
- package/plugins/acryl-node-server/tests/adapter/headers.test.ts +0 -252
- package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +0 -190
- package/plugins/acryl-node-server/tests/adapter/listener.test.ts +0 -727
- package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +0 -189
- package/plugins/acryl-node-server/tests/adapter/request.test.ts +0 -1230
- package/plugins/acryl-node-server/tests/adapter/response.test.ts +0 -285
- package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +0 -849
- package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +0 -790
- package/plugins/acryl-node-server/tests/adapter/server.test.ts +0 -1312
- package/plugins/acryl-node-server/tests/adapter/setup.ts +0 -16
- package/plugins/acryl-node-server/tests/adapter/url.test.ts +0 -76
- package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +0 -23
- package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +0 -75
- package/plugins/acryl-node-server/tests/adapter/utils.test.ts +0 -235
- package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +0 -260
- package/plugins/acryl-node-server/tests/server.spec.ts +0 -141
- package/plugins/acryl-router/src/core/app.test.ts +0 -3833
- package/plugins/acryl-router/src/core/client/client.test.ts +0 -2313
- package/plugins/acryl-router/src/core/client/types.test.ts +0 -149
- package/plugins/acryl-router/src/core/client/utils.test.ts +0 -405
- package/plugins/acryl-router/src/core/compose.test.ts +0 -751
- package/plugins/acryl-router/src/core/context.test.ts +0 -629
- package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +0 -344
- package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +0 -55
- package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +0 -510
- package/plugins/acryl-router/src/core/helper/css/common.case.test.tsx +0 -506
- package/plugins/acryl-router/src/core/helper/css/index.test.tsx +0 -678
- package/plugins/acryl-router/src/core/helper/dev/index.test.ts +0 -181
- package/plugins/acryl-router/src/core/helper/factory/index.test.ts +0 -434
- package/plugins/acryl-router/src/core/helper/html/index.test.ts +0 -78
- package/plugins/acryl-router/src/core/helper/proxy/index.test.ts +0 -356
- package/plugins/acryl-router/src/core/helper/route/index.test.ts +0 -259
- package/plugins/acryl-router/src/core/helper/streaming/sse.test.tsx +0 -557
- package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +0 -128
- package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +0 -34
- package/plugins/acryl-router/src/core/helper/testing/index.test.ts +0 -42
- package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +0 -124
- package/plugins/acryl-router/src/core/http-exception.test.ts +0 -45
- package/plugins/acryl-router/src/core/jsx/base.test.tsx +0 -97
- package/plugins/acryl-router/src/core/jsx/children.test.ts +0 -52
- package/plugins/acryl-router/src/core/jsx/components.test.tsx +0 -936
- package/plugins/acryl-router/src/core/jsx/context-isolation.test.ts +0 -655
- package/plugins/acryl-router/src/core/jsx/dom/client.test.tsx +0 -128
- package/plugins/acryl-router/src/core/jsx/dom/components.test.tsx +0 -330
- package/plugins/acryl-router/src/core/jsx/dom/context.test.tsx +0 -184
- package/plugins/acryl-router/src/core/jsx/dom/css.test.tsx +0 -149
- package/plugins/acryl-router/src/core/jsx/dom/hooks/index.test.tsx +0 -172
- package/plugins/acryl-router/src/core/jsx/dom/index.test.tsx +0 -3681
- package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.test.tsx +0 -1344
- package/plugins/acryl-router/src/core/jsx/dom/server.test.tsx +0 -199
- package/plugins/acryl-router/src/core/jsx/hooks/dom.test.tsx +0 -859
- package/plugins/acryl-router/src/core/jsx/hooks/string.test.tsx +0 -40
- package/plugins/acryl-router/src/core/jsx/index.test.tsx +0 -1744
- package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.test.tsx +0 -666
- package/plugins/acryl-router/src/core/jsx/jsx-runtime.test.tsx +0 -85
- package/plugins/acryl-router/src/core/jsx/streaming.test.tsx +0 -1281
- package/plugins/acryl-router/src/core/jsx/utils.test.ts +0 -366
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +0 -434
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +0 -1018
- package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +0 -273
- package/plugins/acryl-router/src/core/middleware/cache/index.test.ts +0 -1211
- package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +0 -355
- package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +0 -535
- package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +0 -43
- package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +0 -532
- package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +0 -534
- package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +0 -21
- package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +0 -503
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +0 -217
- package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +0 -1135
- package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +0 -48
- package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +0 -802
- package/plugins/acryl-router/src/core/middleware/language/index.test.ts +0 -552
- package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +0 -204
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +0 -292
- package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +0 -221
- package/plugins/acryl-router/src/core/middleware/mount/index.test.ts +0 -334
- package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +0 -37
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +0 -218
- package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +0 -168
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +0 -671
- package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +0 -450
- package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +0 -50
- package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +0 -70
- package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +0 -310
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +0 -381
- package/plugins/acryl-router/src/core/preset/quick.test.ts +0 -42
- package/plugins/acryl-router/src/core/preset/tiny.test.ts +0 -42
- package/plugins/acryl-router/src/core/request.test.ts +0 -751
- package/plugins/acryl-router/src/core/router/common.case.test.ts +0 -1132
- package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +0 -80
- package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +0 -46
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +0 -154
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +0 -265
- package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +0 -13
- package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +0 -896
- package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +0 -8
- package/plugins/acryl-router/src/core/types.test.ts +0 -4134
- package/plugins/acryl-router/src/core/utils/accept.test.ts +0 -299
- package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +0 -56
- package/plugins/acryl-router/src/core/utils/body.test.ts +0 -496
- package/plugins/acryl-router/src/core/utils/buffer.test.ts +0 -147
- package/plugins/acryl-router/src/core/utils/color.test.ts +0 -23
- package/plugins/acryl-router/src/core/utils/concurrent.test.ts +0 -69
- package/plugins/acryl-router/src/core/utils/cookie.test.ts +0 -460
- package/plugins/acryl-router/src/core/utils/crypto.test.ts +0 -43
- package/plugins/acryl-router/src/core/utils/encode.test.ts +0 -61
- package/plugins/acryl-router/src/core/utils/filepath.test.ts +0 -78
- package/plugins/acryl-router/src/core/utils/html.test.ts +0 -16
- package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +0 -149
- package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +0 -86
- package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +0 -1788
- package/plugins/acryl-router/src/core/utils/mime.test.ts +0 -64
- package/plugins/acryl-router/src/core/utils/stream.test.ts +0 -212
- package/plugins/acryl-router/src/core/utils/types.test.ts +0 -295
- package/plugins/acryl-router/src/core/utils/url.test.ts +0 -384
- package/plugins/acryl-router/src/core/validator/utils.test.ts +0 -82
- package/plugins/acryl-router/src/core/validator/validator.test.ts +0 -1488
- package/plugins/acryl-router/tests/docs-examples.spec.ts +0 -152
- package/plugins/acryl-router/tests/jsx.spec.tsx +0 -63
- package/plugins/acryl-router/tests/live-install.spec.ts +0 -64
- package/plugins/acryl-router/tests/loader.spec.ts +0 -72
- package/plugins/acryl-router/tests/loopback-only.spec.ts +0 -80
- package/plugins/acryl-router/tests/no-dom/consumer.ts +0 -11
- package/plugins/acryl-router/tests/no-dom/tsconfig.json +0 -13
- package/plugins/acryl-router/tests/service.spec.ts +0 -426
- package/plugins/acryl-router/tests/shadow-state.spec.ts +0 -79
- package/plugins/acryl-router-http/tests/adapter.spec.ts +0 -174
- package/plugins/acryl-router-http/tests/stream.spec.ts +0 -133
- package/plugins/acryl-router-http/tests/surfaces.spec.ts +0 -101
- package/plugins/acryl-state/src/react.ts +0 -11
- package/plugins/acryl-state/tests/cell.spec.ts +0 -71
- package/plugins/acryl-state/tests/react.spec.ts +0 -39
- package/plugins/acryl-state/tests/service.spec.ts +0 -132
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
import { replaceUrlParam } from '../../client/utils.ts'
|
|
2
|
+
import type { Acryl } from '../../app.ts'
|
|
3
|
+
import type { Env, Schema } from '../../types.ts'
|
|
4
|
+
import { createPool } from '../../utils/concurrent.ts'
|
|
5
|
+
import { getExtension } from '../../utils/mime.ts'
|
|
6
|
+
import type { AddedSSGDataRequest, SSGParams } from './middleware.ts'
|
|
7
|
+
import { SSG_CONTEXT, X_ACRYL_DISABLE_SSG_HEADER_KEY } from './middleware.ts'
|
|
8
|
+
import { defaultPlugin } from './plugins.ts'
|
|
9
|
+
import {
|
|
10
|
+
dirname,
|
|
11
|
+
ensureWithinOutDir,
|
|
12
|
+
filterStaticGenerateRoutes,
|
|
13
|
+
isDynamicRoute,
|
|
14
|
+
joinPaths,
|
|
15
|
+
} from './utils.ts'
|
|
16
|
+
|
|
17
|
+
const DEFAULT_CONCURRENCY = 2 // default concurrency for ssg
|
|
18
|
+
|
|
19
|
+
// 'default_content_type' is designed according to Bun's performance optimization,
|
|
20
|
+
// which omits Content-Type by default for text responses.
|
|
21
|
+
// This is based on benchmarks showing performance gains without Content-Type.
|
|
22
|
+
// In Acryl, using `c.text()` without a Content-Type implicitly assumes 'text/plain; charset=UTF-8'.
|
|
23
|
+
// This approach maintains performance consistency across different environments.
|
|
24
|
+
// For details, see GitHub issues: oven-sh/bun#8530
|
|
25
|
+
const DEFAULT_CONTENT_TYPE = 'text/plain'
|
|
26
|
+
|
|
27
|
+
export const DEFAULT_OUTPUT_DIR = './static'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @experimental
|
|
31
|
+
* `FileSystemModule` is an experimental feature.
|
|
32
|
+
* The API might be changed.
|
|
33
|
+
*/
|
|
34
|
+
export interface FileSystemModule {
|
|
35
|
+
writeFile(path: string, data: string | Uint8Array): Promise<void>
|
|
36
|
+
mkdir(path: string, options: { recursive: boolean }): Promise<void | string>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @experimental
|
|
41
|
+
* `ToSSGResult` is an experimental feature.
|
|
42
|
+
* The API might be changed.
|
|
43
|
+
*/
|
|
44
|
+
export interface ToSSGResult {
|
|
45
|
+
success: boolean
|
|
46
|
+
files: string[]
|
|
47
|
+
error?: Error
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const generateFilePath = (
|
|
51
|
+
routePath: string,
|
|
52
|
+
outDir: string,
|
|
53
|
+
mimeType: string,
|
|
54
|
+
extensionMap?: Record<string, string>
|
|
55
|
+
): string => {
|
|
56
|
+
const extension = determineExtension(mimeType, extensionMap)
|
|
57
|
+
|
|
58
|
+
let filePath: string
|
|
59
|
+
if (routePath.endsWith(`.${extension}`)) {
|
|
60
|
+
filePath = joinPaths(outDir, routePath)
|
|
61
|
+
} else if (routePath === '/') {
|
|
62
|
+
filePath = joinPaths(outDir, `index.${extension}`)
|
|
63
|
+
} else if (routePath.endsWith('/')) {
|
|
64
|
+
filePath = joinPaths(outDir, routePath, `index.${extension}`)
|
|
65
|
+
} else {
|
|
66
|
+
filePath = joinPaths(outDir, `${routePath}.${extension}`)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ensureWithinOutDir(outDir, filePath)
|
|
70
|
+
|
|
71
|
+
return filePath
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const parseResponseContent = async (response: Response): Promise<string | ArrayBuffer> => {
|
|
75
|
+
const contentType = response.headers.get('Content-Type')
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
if (contentType?.includes('text') || contentType?.includes('json')) {
|
|
79
|
+
return await response.text()
|
|
80
|
+
} else {
|
|
81
|
+
return await response.arrayBuffer()
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Error processing response: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const defaultExtensionMap: Record<string, string> = {
|
|
91
|
+
'text/html': 'html',
|
|
92
|
+
'text/xml': 'xml',
|
|
93
|
+
'application/xml': 'xml',
|
|
94
|
+
'application/atom+xml': 'xml',
|
|
95
|
+
'application/rss+xml': 'xml',
|
|
96
|
+
'application/yaml': 'yaml',
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const determineExtension = (
|
|
100
|
+
mimeType: string,
|
|
101
|
+
userExtensionMap?: Record<string, string>
|
|
102
|
+
): string => {
|
|
103
|
+
const extensionMap = userExtensionMap || defaultExtensionMap
|
|
104
|
+
if (mimeType in extensionMap) {
|
|
105
|
+
return extensionMap[mimeType]
|
|
106
|
+
}
|
|
107
|
+
return getExtension(mimeType) || 'html'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type BeforeRequestHook = (req: Request) => Request | false | Promise<Request | false>
|
|
111
|
+
export type AfterResponseHook = (res: Response) => Response | false | Promise<Response | false>
|
|
112
|
+
export type AfterGenerateHook = (
|
|
113
|
+
result: ToSSGResult,
|
|
114
|
+
fsModule: FileSystemModule,
|
|
115
|
+
options?: ToSSGOptions
|
|
116
|
+
) => void | Promise<void>
|
|
117
|
+
|
|
118
|
+
export const combineBeforeRequestHooks = (
|
|
119
|
+
hooks: BeforeRequestHook | BeforeRequestHook[]
|
|
120
|
+
): BeforeRequestHook => {
|
|
121
|
+
if (!Array.isArray(hooks)) {
|
|
122
|
+
return hooks
|
|
123
|
+
}
|
|
124
|
+
return async (req: Request): Promise<Request | false> => {
|
|
125
|
+
let currentReq = req
|
|
126
|
+
for (const hook of hooks) {
|
|
127
|
+
const result = await hook(currentReq)
|
|
128
|
+
if (result === false) {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
if (result instanceof Request) {
|
|
132
|
+
currentReq = result
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return currentReq
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const combineAfterResponseHooks = (
|
|
140
|
+
hooks: AfterResponseHook | AfterResponseHook[]
|
|
141
|
+
): AfterResponseHook => {
|
|
142
|
+
if (!Array.isArray(hooks)) {
|
|
143
|
+
return hooks
|
|
144
|
+
}
|
|
145
|
+
return async (res: Response): Promise<Response | false> => {
|
|
146
|
+
let currentRes = res
|
|
147
|
+
for (const hook of hooks) {
|
|
148
|
+
const result = await hook(currentRes)
|
|
149
|
+
if (result === false) {
|
|
150
|
+
return false
|
|
151
|
+
}
|
|
152
|
+
if (result instanceof Response) {
|
|
153
|
+
currentRes = result
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return currentRes
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const combineAfterGenerateHooks = (
|
|
161
|
+
hooks: AfterGenerateHook | AfterGenerateHook[],
|
|
162
|
+
fsModule: FileSystemModule,
|
|
163
|
+
options?: ToSSGOptions
|
|
164
|
+
): AfterGenerateHook => {
|
|
165
|
+
if (!Array.isArray(hooks)) {
|
|
166
|
+
return hooks
|
|
167
|
+
}
|
|
168
|
+
return async (result: ToSSGResult): Promise<void> => {
|
|
169
|
+
for (const hook of hooks) {
|
|
170
|
+
await hook(result, fsModule, options)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface SSGPlugin {
|
|
176
|
+
beforeRequestHook?: BeforeRequestHook | BeforeRequestHook[]
|
|
177
|
+
afterResponseHook?: AfterResponseHook | AfterResponseHook[]
|
|
178
|
+
afterGenerateHook?: AfterGenerateHook | AfterGenerateHook[]
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface ToSSGOptions {
|
|
182
|
+
dir?: string
|
|
183
|
+
/**
|
|
184
|
+
* @deprecated Use plugins[].beforeRequestHook instead.
|
|
185
|
+
*/
|
|
186
|
+
beforeRequestHook?: BeforeRequestHook | BeforeRequestHook[]
|
|
187
|
+
/**
|
|
188
|
+
* @deprecated Use plugins[].afterResponseHook instead.
|
|
189
|
+
*/
|
|
190
|
+
afterResponseHook?: AfterResponseHook | AfterResponseHook[]
|
|
191
|
+
/**
|
|
192
|
+
* @deprecated Use plugins[].afterGenerateHook instead.
|
|
193
|
+
*/
|
|
194
|
+
afterGenerateHook?: AfterGenerateHook | AfterGenerateHook[]
|
|
195
|
+
concurrency?: number
|
|
196
|
+
extensionMap?: Record<string, string>
|
|
197
|
+
plugins?: SSGPlugin[]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @experimental
|
|
202
|
+
* `fetchRoutesContent` is an experimental feature.
|
|
203
|
+
* The API might be changed.
|
|
204
|
+
*/
|
|
205
|
+
export const fetchRoutesContent = function* <
|
|
206
|
+
E extends Env = Env,
|
|
207
|
+
S extends Schema = {},
|
|
208
|
+
BasePath extends string = '/',
|
|
209
|
+
>(
|
|
210
|
+
app: Acryl<E, S, BasePath>,
|
|
211
|
+
beforeRequestHook?: BeforeRequestHook,
|
|
212
|
+
afterResponseHook?: AfterResponseHook,
|
|
213
|
+
concurrency?: number
|
|
214
|
+
): Generator<
|
|
215
|
+
Promise<
|
|
216
|
+
| Generator<
|
|
217
|
+
Promise<{ routePath: string; mimeType: string; content: string | ArrayBuffer } | undefined>
|
|
218
|
+
>
|
|
219
|
+
| undefined
|
|
220
|
+
>
|
|
221
|
+
> {
|
|
222
|
+
const baseURL = 'http://localhost'
|
|
223
|
+
const pool = createPool({ concurrency })
|
|
224
|
+
|
|
225
|
+
for (const route of filterStaticGenerateRoutes(app)) {
|
|
226
|
+
// GET Route Info
|
|
227
|
+
const thisRouteBaseURL = new URL(route.path, baseURL).toString()
|
|
228
|
+
|
|
229
|
+
let forGetInfoURLRequest = new Request(thisRouteBaseURL) as AddedSSGDataRequest
|
|
230
|
+
|
|
231
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
232
|
+
yield new Promise(async (resolveGetInfo, rejectGetInfo) => {
|
|
233
|
+
try {
|
|
234
|
+
if (beforeRequestHook) {
|
|
235
|
+
const maybeRequest = await beforeRequestHook(forGetInfoURLRequest)
|
|
236
|
+
if (!maybeRequest) {
|
|
237
|
+
resolveGetInfo(undefined)
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
forGetInfoURLRequest = maybeRequest as unknown as AddedSSGDataRequest
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
await pool.run(() => app.fetch(forGetInfoURLRequest, { [SSG_CONTEXT]: true }))
|
|
244
|
+
|
|
245
|
+
if (!forGetInfoURLRequest.ssgParams) {
|
|
246
|
+
if (isDynamicRoute(route.path)) {
|
|
247
|
+
resolveGetInfo(undefined)
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
forGetInfoURLRequest.ssgParams = [{}]
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const requestInit = {
|
|
254
|
+
method: forGetInfoURLRequest.method,
|
|
255
|
+
headers: forGetInfoURLRequest.headers,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
resolveGetInfo(
|
|
259
|
+
(function* () {
|
|
260
|
+
for (const param of forGetInfoURLRequest.ssgParams as SSGParams) {
|
|
261
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
262
|
+
yield new Promise(async (resolveReq, rejectReq) => {
|
|
263
|
+
try {
|
|
264
|
+
const replacedUrlParam = replaceUrlParam(route.path, param)
|
|
265
|
+
let response = await pool.run(() =>
|
|
266
|
+
app.request(replacedUrlParam, requestInit, {
|
|
267
|
+
[SSG_CONTEXT]: true,
|
|
268
|
+
})
|
|
269
|
+
)
|
|
270
|
+
if (response.headers.get(X_ACRYL_DISABLE_SSG_HEADER_KEY)) {
|
|
271
|
+
resolveReq(undefined)
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
if (afterResponseHook) {
|
|
275
|
+
const maybeResponse = await afterResponseHook(response)
|
|
276
|
+
if (!maybeResponse) {
|
|
277
|
+
resolveReq(undefined)
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
response = maybeResponse
|
|
281
|
+
}
|
|
282
|
+
const mimeType =
|
|
283
|
+
response.headers.get('Content-Type')?.split(';')[0] || DEFAULT_CONTENT_TYPE
|
|
284
|
+
const content = await parseResponseContent(response)
|
|
285
|
+
resolveReq({
|
|
286
|
+
routePath: replacedUrlParam,
|
|
287
|
+
mimeType,
|
|
288
|
+
content,
|
|
289
|
+
})
|
|
290
|
+
} catch (error) {
|
|
291
|
+
rejectReq(error)
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
})()
|
|
296
|
+
)
|
|
297
|
+
} catch (error) {
|
|
298
|
+
rejectGetInfo(error)
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* @experimental
|
|
306
|
+
* `saveContentToFile` is an experimental feature.
|
|
307
|
+
* The API might be changed.
|
|
308
|
+
*/
|
|
309
|
+
const createdDirs: Set<string> = new Set()
|
|
310
|
+
export const saveContentToFile = async (
|
|
311
|
+
data: Promise<{ routePath: string; content: string | ArrayBuffer; mimeType: string } | undefined>,
|
|
312
|
+
fsModule: FileSystemModule,
|
|
313
|
+
outDir: string,
|
|
314
|
+
extensionMap?: Record<string, string>
|
|
315
|
+
): Promise<string | undefined> => {
|
|
316
|
+
const awaitedData = await data
|
|
317
|
+
if (!awaitedData) {
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
const { routePath, content, mimeType } = awaitedData
|
|
321
|
+
const filePath = generateFilePath(routePath, outDir, mimeType, extensionMap)
|
|
322
|
+
const dirPath = dirname(filePath)
|
|
323
|
+
|
|
324
|
+
// `dirPath` is empty when outDir itself is the current directory
|
|
325
|
+
if (dirPath !== '' && !createdDirs.has(dirPath)) {
|
|
326
|
+
await fsModule.mkdir(dirPath, { recursive: true })
|
|
327
|
+
createdDirs.add(dirPath)
|
|
328
|
+
}
|
|
329
|
+
if (typeof content === 'string') {
|
|
330
|
+
await fsModule.writeFile(filePath, content)
|
|
331
|
+
} else if (content instanceof ArrayBuffer) {
|
|
332
|
+
await fsModule.writeFile(filePath, new Uint8Array(content))
|
|
333
|
+
}
|
|
334
|
+
return filePath
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* @experimental
|
|
339
|
+
* `ToSSGInterface` is an experimental feature.
|
|
340
|
+
* The API might be changed.
|
|
341
|
+
*/
|
|
342
|
+
export interface ToSSGInterface {
|
|
343
|
+
(
|
|
344
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
345
|
+
app: Acryl<any, any, any>,
|
|
346
|
+
fsModule: FileSystemModule,
|
|
347
|
+
options?: ToSSGOptions
|
|
348
|
+
): Promise<ToSSGResult>
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @experimental
|
|
353
|
+
* `ToSSGAdaptorInterface` is an experimental feature.
|
|
354
|
+
* The API might be changed.
|
|
355
|
+
*/
|
|
356
|
+
export interface ToSSGAdaptorInterface<
|
|
357
|
+
E extends Env = Env,
|
|
358
|
+
S extends Schema = {},
|
|
359
|
+
BasePath extends string = '/',
|
|
360
|
+
> {
|
|
361
|
+
(app: Acryl<E, S, BasePath>, options?: ToSSGOptions): Promise<ToSSGResult>
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* @experimental
|
|
366
|
+
* `toSSG` is an experimental feature.
|
|
367
|
+
* The API might be changed.
|
|
368
|
+
*/
|
|
369
|
+
export const toSSG: ToSSGInterface = async (app, fs, options) => {
|
|
370
|
+
let result: ToSSGResult | undefined
|
|
371
|
+
const getInfoPromises: Promise<unknown>[] = []
|
|
372
|
+
const savePromises: Promise<string | undefined>[] = []
|
|
373
|
+
const plugins = options?.plugins || [defaultPlugin()]
|
|
374
|
+
const beforeRequestHooks: BeforeRequestHook[] = []
|
|
375
|
+
const afterResponseHooks: AfterResponseHook[] = []
|
|
376
|
+
const afterGenerateHooks: AfterGenerateHook[] = []
|
|
377
|
+
if (options?.beforeRequestHook) {
|
|
378
|
+
beforeRequestHooks.push(
|
|
379
|
+
...(Array.isArray(options.beforeRequestHook)
|
|
380
|
+
? options.beforeRequestHook
|
|
381
|
+
: [options.beforeRequestHook])
|
|
382
|
+
)
|
|
383
|
+
}
|
|
384
|
+
if (options?.afterResponseHook) {
|
|
385
|
+
afterResponseHooks.push(
|
|
386
|
+
...(Array.isArray(options.afterResponseHook)
|
|
387
|
+
? options.afterResponseHook
|
|
388
|
+
: [options.afterResponseHook])
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
if (options?.afterGenerateHook) {
|
|
392
|
+
afterGenerateHooks.push(
|
|
393
|
+
...(Array.isArray(options.afterGenerateHook)
|
|
394
|
+
? options.afterGenerateHook
|
|
395
|
+
: [options.afterGenerateHook])
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
for (const plugin of plugins) {
|
|
399
|
+
if (plugin.beforeRequestHook) {
|
|
400
|
+
beforeRequestHooks.push(
|
|
401
|
+
...(Array.isArray(plugin.beforeRequestHook)
|
|
402
|
+
? plugin.beforeRequestHook
|
|
403
|
+
: [plugin.beforeRequestHook])
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
if (plugin.afterResponseHook) {
|
|
407
|
+
afterResponseHooks.push(
|
|
408
|
+
...(Array.isArray(plugin.afterResponseHook)
|
|
409
|
+
? plugin.afterResponseHook
|
|
410
|
+
: [plugin.afterResponseHook])
|
|
411
|
+
)
|
|
412
|
+
}
|
|
413
|
+
if (plugin.afterGenerateHook) {
|
|
414
|
+
afterGenerateHooks.push(
|
|
415
|
+
...(Array.isArray(plugin.afterGenerateHook)
|
|
416
|
+
? plugin.afterGenerateHook
|
|
417
|
+
: [plugin.afterGenerateHook])
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const outputDir = options?.dir ?? DEFAULT_OUTPUT_DIR
|
|
423
|
+
const concurrency = options?.concurrency ?? DEFAULT_CONCURRENCY
|
|
424
|
+
|
|
425
|
+
const combinedBeforeRequestHook = combineBeforeRequestHooks(
|
|
426
|
+
beforeRequestHooks.length > 0 ? beforeRequestHooks : [(req) => req]
|
|
427
|
+
)
|
|
428
|
+
const combinedAfterResponseHook = combineAfterResponseHooks(
|
|
429
|
+
afterResponseHooks.length > 0 ? afterResponseHooks : [(req) => req]
|
|
430
|
+
)
|
|
431
|
+
const getInfoGen = fetchRoutesContent(
|
|
432
|
+
app,
|
|
433
|
+
combinedBeforeRequestHook,
|
|
434
|
+
combinedAfterResponseHook,
|
|
435
|
+
concurrency
|
|
436
|
+
)
|
|
437
|
+
for (const getInfo of getInfoGen) {
|
|
438
|
+
getInfoPromises.push(
|
|
439
|
+
getInfo.then((getContentGen) => {
|
|
440
|
+
if (!getContentGen) {
|
|
441
|
+
return
|
|
442
|
+
}
|
|
443
|
+
for (const content of getContentGen) {
|
|
444
|
+
savePromises.push(
|
|
445
|
+
saveContentToFile(content, fs, outputDir, options?.extensionMap).catch((e) => e)
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
})
|
|
449
|
+
)
|
|
450
|
+
}
|
|
451
|
+
await Promise.all(getInfoPromises)
|
|
452
|
+
const files: string[] = []
|
|
453
|
+
for (const savePromise of savePromises) {
|
|
454
|
+
const fileOrError = await savePromise
|
|
455
|
+
if (typeof fileOrError === 'string') {
|
|
456
|
+
files.push(fileOrError)
|
|
457
|
+
} else if (fileOrError) {
|
|
458
|
+
throw fileOrError
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
result = { success: true, files }
|
|
462
|
+
} catch (error) {
|
|
463
|
+
const errorObj = error instanceof Error ? error : new Error(String(error))
|
|
464
|
+
result = { success: false, files: [], error: errorObj }
|
|
465
|
+
}
|
|
466
|
+
if (afterGenerateHooks.length > 0) {
|
|
467
|
+
const combinedAfterGenerateHooks = combineAfterGenerateHooks(afterGenerateHooks, fs, options)
|
|
468
|
+
await combinedAfterGenerateHooks(result, fs, options)
|
|
469
|
+
}
|
|
470
|
+
return result
|
|
471
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { Acryl } from '../../app.ts'
|
|
2
|
+
import { METHOD_NAME_ALL } from '../../router.ts'
|
|
3
|
+
import type { Env, RouterRoute } from '../../types.ts'
|
|
4
|
+
import { findTargetHandler, isMiddleware } from '../../utils/handler.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get dirname
|
|
8
|
+
* @param path File Path
|
|
9
|
+
* @returns Parent dir path
|
|
10
|
+
*/
|
|
11
|
+
export const dirname = (path: string): string => {
|
|
12
|
+
const separatedPath = path.split(/[\/\\]/)
|
|
13
|
+
return separatedPath.slice(0, -1).join('/') // Windows supports slash path
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const normalizePath = (path: string): string => {
|
|
17
|
+
return path.replace(/(\\)/g, '/').replace(/\/$/g, '')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getUncRoot = (path: string): string | undefined => {
|
|
21
|
+
const uncRoot = path.replace(/\\/g, '/').match(/^\/\/([^/]+)\/([^/]+)/)
|
|
22
|
+
if (uncRoot) {
|
|
23
|
+
return `${uncRoot[1].toLowerCase()}/${uncRoot[2].toLowerCase()}`
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const handleParent = (resultPaths: string[]): void => {
|
|
28
|
+
if (resultPaths.length === 0 || resultPaths[resultPaths.length - 1] === '..') {
|
|
29
|
+
resultPaths.push('..')
|
|
30
|
+
} else {
|
|
31
|
+
resultPaths.pop()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const handleNonDot = (path: string, resultPaths: string[]): void => {
|
|
36
|
+
path = path.replace(/^\.(?!.)/, '')
|
|
37
|
+
if (path !== '') {
|
|
38
|
+
resultPaths.push(path)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const handleSegments = (paths: string[], resultPaths: string[]): void => {
|
|
43
|
+
for (const path of paths) {
|
|
44
|
+
// Handle `..`
|
|
45
|
+
if (path === '..') {
|
|
46
|
+
handleParent(resultPaths)
|
|
47
|
+
} else {
|
|
48
|
+
// Handle `.` or `abc`
|
|
49
|
+
handleNonDot(path, resultPaths)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const joinPaths = (...paths: string[]): string => {
|
|
55
|
+
const hasUncPrefix = getUncRoot(paths[0]) !== undefined
|
|
56
|
+
paths = paths.map(normalizePath)
|
|
57
|
+
const resultPaths: string[] = []
|
|
58
|
+
handleSegments(paths.join('/').split('/'), resultPaths)
|
|
59
|
+
return (hasUncPrefix ? '//' : paths[0][0] === '/' ? '/' : '') + resultPaths.join('/')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface FilterStaticGenerateRouteData {
|
|
63
|
+
path: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const filterStaticGenerateRoutes = <E extends Env>(
|
|
67
|
+
acryl: Acryl<E>
|
|
68
|
+
): FilterStaticGenerateRouteData[] => {
|
|
69
|
+
return acryl.routes.reduce((acc, { method, handler, path }: RouterRoute) => {
|
|
70
|
+
const targetHandler = findTargetHandler(handler)
|
|
71
|
+
if (['GET', METHOD_NAME_ALL].includes(method) && !isMiddleware(targetHandler)) {
|
|
72
|
+
acc.push({ path })
|
|
73
|
+
}
|
|
74
|
+
return acc
|
|
75
|
+
}, [] as FilterStaticGenerateRouteData[])
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const isDynamicRoute = (path: string): boolean => {
|
|
79
|
+
return path.split('/').some((segment) => segment.startsWith(':') || segment.includes('*'))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const toSegments = (path: string): string[] => (path === '' ? [] : path.split('/'))
|
|
83
|
+
|
|
84
|
+
const getPathRoot = (path: string): string => {
|
|
85
|
+
const normalizedPath = path.replace(/\\/g, '/')
|
|
86
|
+
const uncRoot = getUncRoot(normalizedPath)
|
|
87
|
+
if (uncRoot) {
|
|
88
|
+
return `unc:${uncRoot}`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const driveRoot = normalizedPath.match(/^([A-Za-z]):/)
|
|
92
|
+
if (driveRoot) {
|
|
93
|
+
const kind = normalizedPath[2] === '/' ? 'drive-absolute' : 'drive-relative'
|
|
94
|
+
return `${kind}:${driveRoot[1].toLowerCase()}`
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return normalizedPath.startsWith('/') ? 'absolute' : 'relative'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const ensureWithinOutDir = (outDir: string, filePath: string): void => {
|
|
101
|
+
const outDirSegments = toSegments(joinPaths(outDir))
|
|
102
|
+
const filePathSegments = toSegments(joinPaths(filePath))
|
|
103
|
+
|
|
104
|
+
const hasMismatchedPathRoot = getPathRoot(outDir) !== getPathRoot(filePath)
|
|
105
|
+
|
|
106
|
+
// `joinPaths` collects every remaining `..` at the head, so a `..` right after
|
|
107
|
+
// the outDir segments means the file path climbs above outDir
|
|
108
|
+
const climbsAboveOutDir = filePathSegments[outDirSegments.length] === '..'
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
hasMismatchedPathRoot ||
|
|
112
|
+
filePathSegments.length <= outDirSegments.length ||
|
|
113
|
+
!outDirSegments.every((segment, i) => segment === filePathSegments[i]) ||
|
|
114
|
+
climbsAboveOutDir
|
|
115
|
+
) {
|
|
116
|
+
throw new Error(`Path traversal detected: "${filePath}" is outside the output directory`)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -14,7 +14,7 @@ import { STASH_EFFECT } from '../hooks/index.ts'
|
|
|
14
14
|
import { normalizeIntrinsicElementKey, styleObjectForEach } from '../utils.ts'
|
|
15
15
|
import { createContext } from './context.ts' // import dom-specific versions
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const ACRYL_PORTAL_ELEMENT = '_hp'
|
|
18
18
|
|
|
19
19
|
const eventAliasMap: Record<string, string> = {
|
|
20
20
|
Change: 'Input',
|
|
@@ -334,8 +334,8 @@ const getNextChildren = (
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
const findInsertBefore = (node: Node | undefined): SupportedElement | Text | undefined => {
|
|
337
|
-
while (node && (node.tag ===
|
|
338
|
-
node = node.tag ===
|
|
337
|
+
while (node && (node.tag === ACRYL_PORTAL_ELEMENT || !node.e)) {
|
|
338
|
+
node = node.tag === ACRYL_PORTAL_ELEMENT || !node.vC?.[0] ? node.nN : node.vC[0]
|
|
339
339
|
}
|
|
340
340
|
return node?.e
|
|
341
341
|
}
|
|
@@ -407,7 +407,7 @@ const applyNodeObject = (node: NodeObject, container: Container, isNew: boolean)
|
|
|
407
407
|
offset = offsetByNextNode
|
|
408
408
|
} else {
|
|
409
409
|
offset =
|
|
410
|
-
findChildNodeIndex(childNodes, next.find((n) => n.tag !==
|
|
410
|
+
findChildNodeIndex(childNodes, next.find((n) => n.tag !== ACRYL_PORTAL_ELEMENT && n.e)?.e) ??
|
|
411
411
|
-1
|
|
412
412
|
}
|
|
413
413
|
|
|
@@ -443,7 +443,7 @@ const applyNodeObject = (node: NodeObject, container: Container, isNew: boolean)
|
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
445
|
}
|
|
446
|
-
if (child.tag ===
|
|
446
|
+
if (child.tag === ACRYL_PORTAL_ELEMENT) {
|
|
447
447
|
offset--
|
|
448
448
|
} else if (isNew) {
|
|
449
449
|
if (!el.parentNode) {
|
|
@@ -868,7 +868,7 @@ export const flushSync = (callback: () => void): void => {
|
|
|
868
868
|
|
|
869
869
|
export const createPortal = (children: Child, container: HTMLElement, key?: string): Child =>
|
|
870
870
|
({
|
|
871
|
-
tag:
|
|
871
|
+
tag: ACRYL_PORTAL_ELEMENT,
|
|
872
872
|
props: {
|
|
873
873
|
children,
|
|
874
874
|
},
|
|
@@ -17,10 +17,10 @@ const defaultCacheableStatusCodes: ReadonlyArray<StatusCode> = [200]
|
|
|
17
17
|
const defaultMaxQueryBodySize = 64 * 1024
|
|
18
18
|
|
|
19
19
|
const cacheKeyPath = '/.acryl-router/cache'
|
|
20
|
-
const cacheKeyParameter = '
|
|
21
|
-
const cacheMethodKeyParameter = '
|
|
22
|
-
const queryDigestKeyParameter = '
|
|
23
|
-
const cacheVaryKeyParameter = '
|
|
20
|
+
const cacheKeyParameter = '__acryl_cache_key'
|
|
21
|
+
const cacheMethodKeyParameter = '__acryl_cache_method'
|
|
22
|
+
const queryDigestKeyParameter = '__acryl_query_digest'
|
|
23
|
+
const cacheVaryKeyParameter = '__acryl_cache_vary'
|
|
24
24
|
|
|
25
25
|
type CacheKeyRequest =
|
|
26
26
|
| { method: 'GET' }
|