@nifrajs/web 4.0.0 → 5.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 +11 -0
- package/dist/build-vite.d.ts +19 -0
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +31 -10
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +8 -10
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +90 -31
- package/dist/build.js.map +1 -1
- package/dist/cdn.d.ts +8 -0
- package/dist/cdn.d.ts.map +1 -1
- package/dist/cdn.js +14 -15
- package/dist/cdn.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +49 -26
- package/dist/client.js.map +1 -1
- package/dist/deferred.d.ts +2 -2
- package/dist/deferred.d.ts.map +1 -1
- package/dist/deferred.js +28 -7
- package/dist/deferred.js.map +1 -1
- package/dist/dev-feed.d.ts.map +1 -1
- package/dist/dev-feed.js +24 -5
- package/dist/dev-feed.js.map +1 -1
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +4 -14
- package/dist/dev-session.js.map +1 -1
- package/dist/dev.d.ts +7 -17
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +13 -1
- package/dist/dev.js.map +1 -1
- package/dist/draft.d.ts +8 -5
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +30 -13
- package/dist/draft.js.map +1 -1
- package/dist/fonts-google.d.ts.map +1 -1
- package/dist/fonts-google.js +7 -0
- package/dist/fonts-google.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/codegen.d.ts +1 -0
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +56 -29
- package/dist/internal/codegen.js.map +1 -1
- package/dist/internal/disposable-dir.d.ts +8 -0
- package/dist/internal/disposable-dir.d.ts.map +1 -0
- package/dist/internal/disposable-dir.js +25 -0
- package/dist/internal/disposable-dir.js.map +1 -0
- package/dist/internal/entry-chunks.d.ts +9 -0
- package/dist/internal/entry-chunks.d.ts.map +1 -0
- package/dist/internal/entry-chunks.js +15 -0
- package/dist/internal/entry-chunks.js.map +1 -0
- package/dist/internal/evidence-traversal.d.ts +12 -0
- package/dist/internal/evidence-traversal.d.ts.map +1 -0
- package/dist/internal/evidence-traversal.js +48 -0
- package/dist/internal/evidence-traversal.js.map +1 -0
- package/dist/internal/head-merge.d.ts +6 -0
- package/dist/internal/head-merge.d.ts.map +1 -1
- package/dist/internal/head-merge.js +9 -0
- package/dist/internal/head-merge.js.map +1 -1
- package/dist/internal/js-string.d.ts +2 -0
- package/dist/internal/js-string.d.ts.map +1 -1
- package/dist/internal/js-string.js +5 -1
- package/dist/internal/js-string.js.map +1 -1
- package/dist/internal/mount-shadow.js +1 -1
- package/dist/internal/mount-shadow.js.map +1 -1
- package/dist/internal/output-guard.d.ts +7 -0
- package/dist/internal/output-guard.d.ts.map +1 -1
- package/dist/internal/output-guard.js +80 -8
- package/dist/internal/output-guard.js.map +1 -1
- package/dist/internal/page-execution.d.ts +6 -0
- package/dist/internal/page-execution.d.ts.map +1 -1
- package/dist/internal/page-execution.js +377 -108
- package/dist/internal/page-execution.js.map +1 -1
- package/dist/internal/progressive-cookies.d.ts +14 -0
- package/dist/internal/progressive-cookies.d.ts.map +1 -0
- package/dist/internal/progressive-cookies.js +43 -0
- package/dist/internal/progressive-cookies.js.map +1 -0
- package/dist/internal/public-env.d.ts +2 -0
- package/dist/internal/public-env.d.ts.map +1 -0
- package/dist/internal/public-env.js +13 -0
- package/dist/internal/public-env.js.map +1 -0
- package/dist/internal/render-document.d.ts +1 -0
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +28 -5
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/response-controls.d.ts +2 -0
- package/dist/internal/response-controls.d.ts.map +1 -1
- package/dist/internal/response-controls.js +26 -16
- package/dist/internal/response-controls.js.map +1 -1
- package/dist/internal/revive-dates.d.ts +16 -0
- package/dist/internal/revive-dates.d.ts.map +1 -0
- package/dist/internal/revive-dates.js +32 -0
- package/dist/internal/revive-dates.js.map +1 -0
- package/dist/internal/route-path.d.ts +8 -0
- package/dist/internal/route-path.d.ts.map +1 -0
- package/dist/internal/route-path.js +8 -0
- package/dist/internal/route-path.js.map +1 -0
- package/dist/internal/secret-scan.d.ts.map +1 -1
- package/dist/internal/secret-scan.js +105 -19
- package/dist/internal/secret-scan.js.map +1 -1
- package/dist/internal/server-boundary.d.ts.map +1 -1
- package/dist/internal/server-boundary.js +33 -5
- package/dist/internal/server-boundary.js.map +1 -1
- package/dist/internal/web-app.d.ts +21 -16
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +73 -21
- package/dist/internal/web-app.js.map +1 -1
- package/dist/internal/wire-date.d.ts +3 -0
- package/dist/internal/wire-date.d.ts.map +1 -0
- package/dist/internal/wire-date.js +14 -0
- package/dist/internal/wire-date.js.map +1 -0
- package/dist/internal/zone-graph.d.ts +4 -0
- package/dist/internal/zone-graph.d.ts.map +1 -1
- package/dist/internal/zone-graph.js +54 -14
- package/dist/internal/zone-graph.js.map +1 -1
- package/dist/isr.d.ts +58 -2
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +296 -39
- package/dist/isr.js.map +1 -1
- package/dist/llms-txt.d.ts +2 -0
- package/dist/llms-txt.d.ts.map +1 -1
- package/dist/llms-txt.js +29 -3
- package/dist/llms-txt.js.map +1 -1
- package/dist/manifest.d.ts +28 -4
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +14 -3
- package/dist/manifest.js.map +1 -1
- package/dist/open-resolved.d.ts +18 -0
- package/dist/open-resolved.d.ts.map +1 -0
- package/dist/open-resolved.js +62 -0
- package/dist/open-resolved.js.map +1 -0
- package/dist/plugins/kit.d.ts +1 -0
- package/dist/plugins/kit.d.ts.map +1 -1
- package/dist/plugins/kit.js +26 -11
- package/dist/plugins/kit.js.map +1 -1
- package/dist/plugins/vite-leak-guard.d.ts +3 -1
- package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
- package/dist/plugins/vite-leak-guard.js +4 -0
- package/dist/plugins/vite-leak-guard.js.map +1 -1
- package/dist/plugins/zone-guard.d.ts.map +1 -1
- package/dist/plugins/zone-guard.js +10 -2
- package/dist/plugins/zone-guard.js.map +1 -1
- package/dist/public-dir.d.ts +8 -1
- package/dist/public-dir.d.ts.map +1 -1
- package/dist/public-dir.js +17 -6
- package/dist/public-dir.js.map +1 -1
- package/dist/query.d.ts +13 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +89 -26
- package/dist/query.js.map +1 -1
- package/dist/render-seam.d.ts +2 -0
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +1 -0
- package/dist/render-seam.js.map +1 -1
- package/dist/route-types.d.ts.map +1 -1
- package/dist/route-types.js +20 -9
- package/dist/route-types.js.map +1 -1
- package/dist/router.d.ts +9 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +39 -46
- package/dist/router.js.map +1 -1
- package/dist/server-components/build-info.d.ts +3 -0
- package/dist/server-components/build-info.d.ts.map +1 -0
- package/dist/server-components/build-info.js +7 -0
- package/dist/server-components/build-info.js.map +1 -0
- package/dist/server-components/build-target.d.ts +12 -0
- package/dist/server-components/build-target.d.ts.map +1 -0
- package/dist/server-components/build-target.js +153 -0
- package/dist/server-components/build-target.js.map +1 -0
- package/dist/server-components/build.d.ts +24 -0
- package/dist/server-components/build.d.ts.map +1 -0
- package/dist/server-components/build.js +366 -0
- package/dist/server-components/build.js.map +1 -0
- package/dist/server-components/bundle-proof.d.ts +3 -0
- package/dist/server-components/bundle-proof.d.ts.map +1 -0
- package/dist/server-components/bundle-proof.js +23 -0
- package/dist/server-components/bundle-proof.js.map +1 -0
- package/dist/server-components/client.d.ts +46 -0
- package/dist/server-components/client.d.ts.map +1 -0
- package/dist/server-components/client.js +594 -0
- package/dist/server-components/client.js.map +1 -0
- package/dist/server-components/definition.d.ts +11 -0
- package/dist/server-components/definition.d.ts.map +1 -0
- package/dist/server-components/definition.js +94 -0
- package/dist/server-components/definition.js.map +1 -0
- package/dist/server-components/dev-http.d.ts +5 -0
- package/dist/server-components/dev-http.d.ts.map +1 -0
- package/dist/server-components/dev-http.js +71 -0
- package/dist/server-components/dev-http.js.map +1 -0
- package/dist/server-components/error.d.ts +9 -0
- package/dist/server-components/error.d.ts.map +1 -0
- package/dist/server-components/error.js +24 -0
- package/dist/server-components/error.js.map +1 -0
- package/dist/server-components/frames.d.ts +7 -0
- package/dist/server-components/frames.d.ts.map +1 -0
- package/dist/server-components/frames.js +169 -0
- package/dist/server-components/frames.js.map +1 -0
- package/dist/server-components/html-policy.d.ts +6 -0
- package/dist/server-components/html-policy.d.ts.map +1 -0
- package/dist/server-components/html-policy.js +75 -0
- package/dist/server-components/html-policy.js.map +1 -0
- package/dist/server-components/html.d.ts +4 -0
- package/dist/server-components/html.d.ts.map +1 -0
- package/dist/server-components/html.js +67 -0
- package/dist/server-components/html.js.map +1 -0
- package/dist/server-components/islands.d.ts +44 -0
- package/dist/server-components/islands.d.ts.map +1 -0
- package/dist/server-components/islands.js +42 -0
- package/dist/server-components/islands.js.map +1 -0
- package/dist/server-components/native.d.ts +4 -0
- package/dist/server-components/native.d.ts.map +1 -0
- package/dist/server-components/native.js +81 -0
- package/dist/server-components/native.js.map +1 -0
- package/dist/server-components/portal.d.ts +6 -0
- package/dist/server-components/portal.d.ts.map +1 -0
- package/dist/server-components/portal.js +35 -0
- package/dist/server-components/portal.js.map +1 -0
- package/dist/server-components/props.d.ts +3 -0
- package/dist/server-components/props.d.ts.map +1 -0
- package/dist/server-components/props.js +14 -0
- package/dist/server-components/props.js.map +1 -0
- package/dist/server-components/protocol.d.ts +44 -0
- package/dist/server-components/protocol.d.ts.map +1 -0
- package/dist/server-components/protocol.js +139 -0
- package/dist/server-components/protocol.js.map +1 -0
- package/dist/server-components/range.d.ts +24 -0
- package/dist/server-components/range.d.ts.map +1 -0
- package/dist/server-components/range.js +263 -0
- package/dist/server-components/range.js.map +1 -0
- package/dist/server-components/registry.d.ts +6 -0
- package/dist/server-components/registry.d.ts.map +1 -0
- package/dist/server-components/registry.js +30 -0
- package/dist/server-components/registry.js.map +1 -0
- package/dist/server-components/response.d.ts +4 -0
- package/dist/server-components/response.d.ts.map +1 -0
- package/dist/server-components/response.js +32 -0
- package/dist/server-components/response.js.map +1 -0
- package/dist/server-components/router-integration.d.ts +9 -0
- package/dist/server-components/router-integration.d.ts.map +1 -0
- package/dist/server-components/router-integration.js +29 -0
- package/dist/server-components/router-integration.js.map +1 -0
- package/dist/server-components/runtime.d.ts +14 -0
- package/dist/server-components/runtime.d.ts.map +1 -0
- package/dist/server-components/runtime.js +276 -0
- package/dist/server-components/runtime.js.map +1 -0
- package/dist/server-components/scheduler.d.ts +14 -0
- package/dist/server-components/scheduler.d.ts.map +1 -0
- package/dist/server-components/scheduler.js +74 -0
- package/dist/server-components/scheduler.js.map +1 -0
- package/dist/server-components/schema.d.ts +4 -0
- package/dist/server-components/schema.d.ts.map +1 -0
- package/dist/server-components/schema.js +77 -0
- package/dist/server-components/schema.js.map +1 -0
- package/dist/server-components/server-entry.d.ts +12 -0
- package/dist/server-components/server-entry.d.ts.map +1 -0
- package/dist/server-components/server-entry.js +90 -0
- package/dist/server-components/server-entry.js.map +1 -0
- package/dist/server-components/source.d.ts +15 -0
- package/dist/server-components/source.d.ts.map +1 -0
- package/dist/server-components/source.js +269 -0
- package/dist/server-components/source.js.map +1 -0
- package/dist/server-components/state.d.ts +4 -0
- package/dist/server-components/state.d.ts.map +1 -0
- package/dist/server-components/state.js +123 -0
- package/dist/server-components/state.js.map +1 -0
- package/dist/server-components/stream.d.ts +6 -0
- package/dist/server-components/stream.d.ts.map +1 -0
- package/dist/server-components/stream.js +70 -0
- package/dist/server-components/stream.js.map +1 -0
- package/dist/server-components/trusted-html.d.ts +3 -0
- package/dist/server-components/trusted-html.d.ts.map +1 -0
- package/dist/server-components/trusted-html.js +14 -0
- package/dist/server-components/trusted-html.js.map +1 -0
- package/dist/server-components/types.d.ts +107 -0
- package/dist/server-components/types.d.ts.map +1 -0
- package/dist/server-components/types.js +2 -0
- package/dist/server-components/types.js.map +1 -0
- package/dist/server-components/typescript.d.ts +11 -0
- package/dist/server-components/typescript.d.ts.map +1 -0
- package/dist/server-components/typescript.js +28 -0
- package/dist/server-components/typescript.js.map +1 -0
- package/dist/server-components/value.d.ts +5 -0
- package/dist/server-components/value.d.ts.map +1 -0
- package/dist/server-components/value.js +77 -0
- package/dist/server-components/value.js.map +1 -0
- package/dist/server-components/vite.d.ts +12 -0
- package/dist/server-components/vite.d.ts.map +1 -0
- package/dist/server-components/vite.js +197 -0
- package/dist/server-components/vite.js.map +1 -0
- package/dist/server-components.d.ts +5 -0
- package/dist/server-components.d.ts.map +1 -0
- package/dist/server-components.js +4 -0
- package/dist/server-components.js.map +1 -0
- package/dist/service-worker.d.ts +8 -1
- package/dist/service-worker.d.ts.map +1 -1
- package/dist/service-worker.js +20 -6
- package/dist/service-worker.js.map +1 -1
- package/dist/vite.d.ts +12 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +77 -17
- package/dist/vite.js.map +1 -1
- package/dist/zones.d.ts +40 -0
- package/dist/zones.d.ts.map +1 -1
- package/dist/zones.js +170 -6
- package/dist/zones.js.map +1 -1
- package/package.json +97 -4
- package/src/build-vite.ts +31 -9
- package/src/build.ts +109 -31
- package/src/cdn.ts +18 -12
- package/src/client.ts +48 -23
- package/src/deferred.ts +27 -9
- package/src/dev-feed.ts +24 -5
- package/src/dev-session.ts +4 -13
- package/src/dev.ts +29 -1
- package/src/draft.ts +35 -14
- package/src/fonts-google.ts +10 -0
- package/src/index.ts +3 -2
- package/src/internal/codegen.ts +60 -29
- package/src/internal/disposable-dir.ts +24 -0
- package/src/internal/entry-chunks.ts +18 -0
- package/src/internal/evidence-traversal.ts +57 -0
- package/src/internal/head-merge.ts +16 -0
- package/src/internal/js-string.ts +6 -1
- package/src/internal/mount-shadow.ts +1 -1
- package/src/internal/output-guard.ts +88 -7
- package/src/internal/page-execution.ts +506 -108
- package/src/internal/progressive-cookies.ts +60 -0
- package/src/internal/public-env.ts +14 -0
- package/src/internal/render-document.ts +26 -11
- package/src/internal/response-controls.ts +31 -14
- package/src/internal/revive-dates.ts +35 -0
- package/src/internal/route-path.ts +8 -0
- package/src/internal/secret-scan.ts +104 -17
- package/src/internal/server-boundary.ts +29 -5
- package/src/internal/web-app.ts +116 -37
- package/src/internal/wire-date.ts +16 -0
- package/src/internal/zone-graph.ts +59 -19
- package/src/isr.ts +351 -43
- package/src/llms-txt.ts +32 -3
- package/src/manifest.ts +43 -7
- package/src/open-resolved.ts +64 -0
- package/src/plugins/kit.ts +31 -11
- package/src/plugins/vite-leak-guard.ts +5 -1
- package/src/plugins/zone-guard.ts +11 -2
- package/src/public-dir.ts +27 -8
- package/src/query.ts +107 -26
- package/src/render-seam.ts +2 -0
- package/src/route-types.ts +34 -7
- package/src/router.ts +58 -55
- package/src/server-components/build-info.ts +11 -0
- package/src/server-components/build-target.ts +194 -0
- package/src/server-components/build.ts +419 -0
- package/src/server-components/bundle-proof.ts +33 -0
- package/src/server-components/client.ts +663 -0
- package/src/server-components/definition.ts +125 -0
- package/src/server-components/dev-http.ts +75 -0
- package/src/server-components/error.ts +24 -0
- package/src/server-components/frames.ts +181 -0
- package/src/server-components/html-policy.ts +94 -0
- package/src/server-components/html.ts +81 -0
- package/src/server-components/islands.ts +111 -0
- package/src/server-components/native.ts +101 -0
- package/src/server-components/portal.ts +42 -0
- package/src/server-components/props.ts +19 -0
- package/src/server-components/protocol.ts +167 -0
- package/src/server-components/range.ts +313 -0
- package/src/server-components/registry.ts +34 -0
- package/src/server-components/response.ts +44 -0
- package/src/server-components/router-integration.ts +40 -0
- package/src/server-components/runtime.ts +376 -0
- package/src/server-components/scheduler.ts +84 -0
- package/src/server-components/schema.ts +69 -0
- package/src/server-components/server-entry.ts +98 -0
- package/src/server-components/source.ts +324 -0
- package/src/server-components/state.ts +143 -0
- package/src/server-components/stream.ts +71 -0
- package/src/server-components/trusted-html.ts +21 -0
- package/src/server-components/types.ts +138 -0
- package/src/server-components/typescript.ts +39 -0
- package/src/server-components/value.ts +82 -0
- package/src/server-components/vite.ts +242 -0
- package/src/server-components.ts +32 -0
- package/src/service-worker.ts +24 -7
- package/src/vite.ts +68 -17
- package/src/zones.ts +208 -7
package/src/zones.ts
CHANGED
|
@@ -23,6 +23,8 @@ export type Zone =
|
|
|
23
23
|
| "shared"
|
|
24
24
|
/** A server-function module (`*.fn.ts`); the browser only ever receives its generated RPC stub. */
|
|
25
25
|
| "fn"
|
|
26
|
+
/** A compiler-owned server UI module; only opaque reference stubs may reach a browser. */
|
|
27
|
+
| "component"
|
|
26
28
|
/** Framework output (the client entry, the server composition, generated types). */
|
|
27
29
|
| "generated"
|
|
28
30
|
/** A third-party package, or a workspace package declaring `"nifra": { "environment": "library" }`. */
|
|
@@ -38,7 +40,12 @@ export type Classification =
|
|
|
38
40
|
/** A third-party file's path inside `node_modules`: `drizzle-orm/node-postgres/index.js`. */
|
|
39
41
|
readonly modulePath?: string
|
|
40
42
|
}
|
|
41
|
-
| {
|
|
43
|
+
| {
|
|
44
|
+
readonly zone: "error"
|
|
45
|
+
readonly reason: string
|
|
46
|
+
/** The undeclared package this error is about: every file in it shares the one fix. */
|
|
47
|
+
readonly package?: string
|
|
48
|
+
}
|
|
42
49
|
|
|
43
50
|
export type PackageEnvironment = "frontend" | "backend" | "shared" | "library"
|
|
44
51
|
const PACKAGE_ENVIRONMENTS: ReadonlySet<string> = new Set([
|
|
@@ -113,6 +120,7 @@ const SOURCE = "(?:[cm]?[jt]sx?|svelte|vue|mdx)"
|
|
|
113
120
|
export const ROUTE_BACKEND_FILE = /\.backend\.(?:[cm]?[jt]s)$/
|
|
114
121
|
const ZONE_SUFFIX = new RegExp(`\\.(frontend|backend|shared|fn)\\.${SOURCE}$`)
|
|
115
122
|
const LEGACY_SERVER_SUFFIX = new RegExp(`\\.server\\.${SOURCE}$`)
|
|
123
|
+
export const SERVER_COMPONENT_FILE = /\.component\.server\.[cm]?[jt]sx?$/
|
|
116
124
|
|
|
117
125
|
const ZONE_FOLDERS: Readonly<Record<string, Zone>> = {
|
|
118
126
|
frontend: "frontend",
|
|
@@ -128,8 +136,169 @@ export interface ZoneClassifierOptions {
|
|
|
128
136
|
readonly routesDir?: string
|
|
129
137
|
/** Extra absolute files treated as `generated` - a build's own entry modules. */
|
|
130
138
|
readonly generatedFiles?: readonly string[]
|
|
139
|
+
/** The app's zone settings. Default: `"nifra": { "zones": ... }` in the app's package.json. */
|
|
140
|
+
readonly zones?: ZoneConfig
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* App-side zone settings, read from `"nifra": { "zones": ... }` in the app's package.json so every
|
|
145
|
+
* build, dev server, `nifra check` and the editor plugin see the same ones without running app code.
|
|
146
|
+
*/
|
|
147
|
+
export interface ZoneConfig {
|
|
148
|
+
/**
|
|
149
|
+
* The side of a package that does not declare one in its own package.json, by name: exact
|
|
150
|
+
* (`"@shop/db"`) or a trailing-`*` prefix (`"@shop/*"`). The exact name wins over a prefix, the
|
|
151
|
+
* longest prefix over a shorter one, and a package's own declaration over all of them.
|
|
152
|
+
*/
|
|
153
|
+
readonly packages?: Readonly<Record<string, PackageEnvironment>>
|
|
154
|
+
/** App-relative folders holding backend code, beside `backend/` (`"migrations"`). */
|
|
155
|
+
readonly backend?: readonly string[]
|
|
156
|
+
/** App-relative folders holding frontend code, beside `frontend/`. */
|
|
157
|
+
readonly frontend?: readonly string[]
|
|
158
|
+
/** App-relative folders holding code both sides import, beside `shared/`. */
|
|
159
|
+
readonly shared?: readonly string[]
|
|
131
160
|
}
|
|
132
161
|
|
|
162
|
+
/** A `"nifra": { "zones": ... }` setting that cannot be applied. Thrown when a classifier is created. */
|
|
163
|
+
export class ZoneConfigError extends Error {
|
|
164
|
+
override readonly name = "ZoneConfigError"
|
|
165
|
+
/** The file holding the setting, as the caller named it. */
|
|
166
|
+
readonly source: string
|
|
167
|
+
constructor(source: string, problem: string) {
|
|
168
|
+
super(`[nifra] ${source} "nifra.zones": ${problem}`)
|
|
169
|
+
this.source = source
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const FOLDER_SIDES = ["backend", "frontend", "shared"] as const
|
|
174
|
+
const FOLDER_SIDE_NAMES: ReadonlySet<string> = new Set(FOLDER_SIDES)
|
|
175
|
+
const isPackageEnvironment = (value: unknown): value is PackageEnvironment =>
|
|
176
|
+
typeof value === "string" && PACKAGE_ENVIRONMENTS.has(value)
|
|
177
|
+
const RESERVED_FOLDERS: ReadonlySet<string> = new Set([
|
|
178
|
+
"routes",
|
|
179
|
+
"frontend",
|
|
180
|
+
"backend",
|
|
181
|
+
"shared",
|
|
182
|
+
"public",
|
|
183
|
+
"node_modules",
|
|
184
|
+
])
|
|
185
|
+
|
|
186
|
+
const recordOf = (value: unknown): Readonly<Record<string, unknown>> | undefined =>
|
|
187
|
+
typeof value === "object" && value !== null && !Array.isArray(value)
|
|
188
|
+
? Object.fromEntries(Object.entries(value))
|
|
189
|
+
: undefined
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Validate a `zones` setting. `source` names where it came from in the error. Folders are plain
|
|
193
|
+
* app-relative paths (a trailing `/**` is accepted and dropped); any other glob is refused, so a
|
|
194
|
+
* pattern can never match less than its author meant without saying so.
|
|
195
|
+
*/
|
|
196
|
+
export function parseZoneConfig(value: unknown, source: string): ZoneConfig {
|
|
197
|
+
const fail = (message: string): never => {
|
|
198
|
+
throw new ZoneConfigError(source, message)
|
|
199
|
+
}
|
|
200
|
+
const record = recordOf(value) ?? fail("must be an object")
|
|
201
|
+
const config: {
|
|
202
|
+
packages?: Record<string, PackageEnvironment>
|
|
203
|
+
backend?: string[]
|
|
204
|
+
frontend?: string[]
|
|
205
|
+
shared?: string[]
|
|
206
|
+
} = {}
|
|
207
|
+
for (const key of Object.keys(record)) {
|
|
208
|
+
if (key !== "packages" && !FOLDER_SIDE_NAMES.has(key))
|
|
209
|
+
fail(`unknown key "${key}" (expected packages, backend, frontend or shared)`)
|
|
210
|
+
}
|
|
211
|
+
if (record.packages !== undefined) {
|
|
212
|
+
const packages = recordOf(record.packages) ?? fail('"packages" must be an object')
|
|
213
|
+
const parsed: Record<string, PackageEnvironment> = {}
|
|
214
|
+
for (const [pattern, side] of Object.entries(packages)) {
|
|
215
|
+
if (pattern === "" || pattern.indexOf("*") !== pattern.lastIndexOf("*"))
|
|
216
|
+
fail(`package pattern "${pattern}" must be a name, or a name prefix ending in one "*"`)
|
|
217
|
+
if (pattern.includes("*") && !pattern.endsWith("*"))
|
|
218
|
+
fail(`package pattern "${pattern}" may use "*" only at its end`)
|
|
219
|
+
parsed[pattern] = isPackageEnvironment(side)
|
|
220
|
+
? side
|
|
221
|
+
: fail(`packages["${pattern}"] must be one of frontend, backend, shared, library`)
|
|
222
|
+
}
|
|
223
|
+
config.packages = parsed
|
|
224
|
+
}
|
|
225
|
+
const owner = new Map<string, string>()
|
|
226
|
+
for (const side of FOLDER_SIDES) {
|
|
227
|
+
const raw = record[side]
|
|
228
|
+
if (raw === undefined) continue
|
|
229
|
+
const entries: readonly unknown[] = Array.isArray(raw)
|
|
230
|
+
? raw
|
|
231
|
+
: fail(`"${side}" must be an array of app-relative folders`)
|
|
232
|
+
const folders: string[] = []
|
|
233
|
+
for (const entry of entries) {
|
|
234
|
+
const folder = (typeof entry === "string" ? entry : fail(`"${side}" must hold strings`))
|
|
235
|
+
.replaceAll("\\", "/")
|
|
236
|
+
.replace(/^\.\//, "")
|
|
237
|
+
.replace(/\/\*\*$/, "")
|
|
238
|
+
.replace(/\/+$/, "")
|
|
239
|
+
const top = folder.split("/")[0] ?? ""
|
|
240
|
+
if (folder === "" || folder.startsWith("/") || /^[a-z]:/i.test(folder))
|
|
241
|
+
fail(`"${side}" entry "${entry}" must be a folder relative to the app`)
|
|
242
|
+
if (folder.split("/").includes("..")) fail(`"${side}" entry "${entry}" may not contain ".."`)
|
|
243
|
+
if (/[*?[\]{}!]/.test(folder))
|
|
244
|
+
fail(`"${side}" entry "${entry}" must be a plain folder path (a trailing "/**" is allowed)`)
|
|
245
|
+
if (RESERVED_FOLDERS.has(top) || top.startsWith(".nifra"))
|
|
246
|
+
fail(`"${side}" entry "${entry}" is inside ${top}/, which already has its own rules`)
|
|
247
|
+
for (const [other, otherSide] of owner) {
|
|
248
|
+
const nested =
|
|
249
|
+
other === folder || other.startsWith(`${folder}/`) || folder.startsWith(`${other}/`)
|
|
250
|
+
if (nested && otherSide !== side)
|
|
251
|
+
fail(`"${folder}" (${side}) overlaps "${other}" (${otherSide}); a folder has one side`)
|
|
252
|
+
}
|
|
253
|
+
owner.set(folder, side)
|
|
254
|
+
folders.push(folder)
|
|
255
|
+
}
|
|
256
|
+
config[side] = folders
|
|
257
|
+
}
|
|
258
|
+
return config
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** The `nifra.zones` of the nearest package.json at or above `appRoot`, validated. */
|
|
262
|
+
function appZoneConfig(appRoot: string): ZoneConfig {
|
|
263
|
+
let dir = appRoot
|
|
264
|
+
for (;;) {
|
|
265
|
+
const manifest = join(dir, "package.json")
|
|
266
|
+
if (existsSync(manifest)) {
|
|
267
|
+
let parsed: unknown
|
|
268
|
+
try {
|
|
269
|
+
parsed = JSON.parse(readFileSync(manifest, "utf8"))
|
|
270
|
+
} catch {
|
|
271
|
+
return {}
|
|
272
|
+
}
|
|
273
|
+
const zones = recordOf(recordOf(parsed)?.nifra)?.zones
|
|
274
|
+
return zones === undefined ? {} : parseZoneConfig(zones, toPosix(manifest))
|
|
275
|
+
}
|
|
276
|
+
const parent = dirname(dir)
|
|
277
|
+
if (parent === dir) return {}
|
|
278
|
+
dir = parent
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** The side `packages` gives `name`: the exact entry, else the longest matching prefix. */
|
|
283
|
+
function mappedSide(
|
|
284
|
+
packages: Readonly<Record<string, PackageEnvironment>> | undefined,
|
|
285
|
+
name: string | undefined,
|
|
286
|
+
): PackageEnvironment | undefined {
|
|
287
|
+
if (packages === undefined || name === undefined) return undefined
|
|
288
|
+
const exact = packages[name]
|
|
289
|
+
if (exact !== undefined && Object.hasOwn(packages, name)) return exact
|
|
290
|
+
let best: string | undefined
|
|
291
|
+
for (const pattern of Object.keys(packages)) {
|
|
292
|
+
if (!pattern.endsWith("*") || !name.startsWith(pattern.slice(0, -1))) continue
|
|
293
|
+
if (best === undefined || pattern.length > best.length) best = pattern
|
|
294
|
+
}
|
|
295
|
+
return best === undefined ? undefined : packages[best]
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** How the `packages` setting would name `name`: its scope wildcard when it has a scope. */
|
|
299
|
+
const suggestedPattern = (name: string): string =>
|
|
300
|
+
name.startsWith("@") && name.includes("/") ? `${name.slice(0, name.indexOf("/"))}/*` : name
|
|
301
|
+
|
|
133
302
|
export interface ZoneClassifier {
|
|
134
303
|
readonly appRoot: string
|
|
135
304
|
/** Classify a file by its absolute (or appRoot-relative) path. Results are cached per path. */
|
|
@@ -198,6 +367,18 @@ export function createZoneClassifier(options: ZoneClassifierOptions): ZoneClassi
|
|
|
198
367
|
const appRoot = realFile(resolve(options.appRoot))
|
|
199
368
|
const routesDir = realFile(resolve(options.routesDir ?? join(options.appRoot, "routes")))
|
|
200
369
|
const generated = new Set((options.generatedFiles ?? []).map((file) => realFile(resolve(file))))
|
|
370
|
+
const zones =
|
|
371
|
+
options.zones === undefined
|
|
372
|
+
? appZoneConfig(appRoot)
|
|
373
|
+
: parseZoneConfig(options.zones, "ZoneClassifierOptions")
|
|
374
|
+
const configuredFolders: Array<readonly [string, "backend" | "frontend" | "shared"]> = []
|
|
375
|
+
for (const side of FOLDER_SIDES)
|
|
376
|
+
for (const folder of zones[side] ?? []) configuredFolders.push([folder, side])
|
|
377
|
+
const folderSide = (rel: string): "backend" | "frontend" | "shared" | undefined => {
|
|
378
|
+
for (const [folder, side] of configuredFolders)
|
|
379
|
+
if (rel === folder || rel.startsWith(`${folder}/`)) return side
|
|
380
|
+
return undefined
|
|
381
|
+
}
|
|
201
382
|
const cache = new Map<string, Classification>()
|
|
202
383
|
const packages = new Map<string, PackageInfo | null>()
|
|
203
384
|
|
|
@@ -246,7 +427,7 @@ export function createZoneClassifier(options: ZoneClassifierOptions): ZoneClassi
|
|
|
246
427
|
const declared =
|
|
247
428
|
info?.environment !== undefined && PACKAGE_ENVIRONMENTS.has(info.environment)
|
|
248
429
|
? (info.environment as PackageEnvironment)
|
|
249
|
-
:
|
|
430
|
+
: mappedSide(zones.packages, thirdParty)
|
|
250
431
|
return {
|
|
251
432
|
zone: "library",
|
|
252
433
|
packageName: thirdParty,
|
|
@@ -259,6 +440,8 @@ export function createZoneClassifier(options: ZoneClassifierOptions): ZoneClassi
|
|
|
259
440
|
if (SERVER_MANIFEST_FILE.test(base) && isGeneratedServerManifest(file)) {
|
|
260
441
|
return { zone: "generated" }
|
|
261
442
|
}
|
|
443
|
+
if (SERVER_COMPONENT_FILE.test(base)) return { zone: "component" }
|
|
444
|
+
if (/\.island\.[cm]?[jt]sx?$/.test(base)) return { zone: "frontend" }
|
|
262
445
|
if (LEGACY_SERVER_SUFFIX.test(base)) {
|
|
263
446
|
return {
|
|
264
447
|
zone: "error",
|
|
@@ -289,7 +472,7 @@ export function createZoneClassifier(options: ZoneClassifierOptions): ZoneClassi
|
|
|
289
472
|
if (suffix === "shared") return { zone: "shared" }
|
|
290
473
|
return { zone: "route-frontend" }
|
|
291
474
|
}
|
|
292
|
-
const folder = ZONE_FOLDERS[top]
|
|
475
|
+
const folder = ZONE_FOLDERS[top] ?? folderSide(rel)
|
|
293
476
|
if (folder !== undefined) {
|
|
294
477
|
if (suffix === undefined || suffix === folder) return { zone: folder }
|
|
295
478
|
if (suffix === "fn" && folder === "backend") return { zone: "fn" }
|
|
@@ -303,8 +486,11 @@ export function createZoneClassifier(options: ZoneClassifierOptions): ZoneClassi
|
|
|
303
486
|
}
|
|
304
487
|
// First-party code outside the app: a workspace package, or a relative import out of the app.
|
|
305
488
|
const info = packageFor(toPosix(dirname(file)))
|
|
306
|
-
const environment =
|
|
307
|
-
|
|
489
|
+
const environment =
|
|
490
|
+
info?.environment !== undefined && PACKAGE_ENVIRONMENTS.has(info.environment)
|
|
491
|
+
? info.environment
|
|
492
|
+
: mappedSide(zones.packages, info?.name)
|
|
493
|
+
if (environment !== undefined) {
|
|
308
494
|
return environment === "library"
|
|
309
495
|
? {
|
|
310
496
|
zone: "library",
|
|
@@ -313,10 +499,18 @@ export function createZoneClassifier(options: ZoneClassifierOptions): ZoneClassi
|
|
|
313
499
|
}
|
|
314
500
|
: { zone: environment as Zone }
|
|
315
501
|
}
|
|
316
|
-
const
|
|
502
|
+
const declare = `"nifra": { "environment": "frontend" | "backend" | "shared" | "library" }`
|
|
503
|
+
if (info?.name === undefined) {
|
|
504
|
+
return {
|
|
505
|
+
zone: "error",
|
|
506
|
+
reason: `"${file}" is first-party code outside the app and declares no side. Add ${declare} to ${info === undefined ? "a package.json above it" : `${info.dir}/package.json`}`,
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// Named per package, not per file, so a build that reaches 50 files of it reports one fact once.
|
|
317
510
|
return {
|
|
318
511
|
zone: "error",
|
|
319
|
-
|
|
512
|
+
package: info.name,
|
|
513
|
+
reason: `package "${info.name}" is first-party code outside the app and declares no side. Add ${declare} to ${info.dir}/package.json, or give it a side from the app's package.json: "nifra": { "zones": { "packages": { "${suggestedPattern(info.name)}": "backend" } } } (or "library" if the browser may load it)`,
|
|
320
514
|
}
|
|
321
515
|
}
|
|
322
516
|
|
|
@@ -388,8 +582,15 @@ export function browserDenial(
|
|
|
388
582
|
return undefined
|
|
389
583
|
}
|
|
390
584
|
|
|
585
|
+
/** Whether a bare specifier names one of {@link SERVER_PACKAGES} or a database driver entry. */
|
|
586
|
+
export function isServerPackageSpecifier(specifier: string): boolean {
|
|
587
|
+
const name = specifierPackage(specifier)
|
|
588
|
+
return (name !== undefined && SERVER_PACKAGES.includes(name)) || SERVER_SUBPATHS.test(specifier)
|
|
589
|
+
}
|
|
590
|
+
|
|
391
591
|
/** Whether a module in `from` may import (with a value import) a module in `to`. */
|
|
392
592
|
export function importAllowed(from: Zone, to: Zone): boolean {
|
|
593
|
+
if (from === "component") return true
|
|
393
594
|
if (from === "generated" || from === "library") return true
|
|
394
595
|
if (to === "generated" || to === "library") return true
|
|
395
596
|
if (FRONTEND_ZONES.has(from)) return FRONTEND_ZONES.has(to) || to === "shared" || to === "fn"
|