netjes 0.0.0-stage → 0.0.2
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/LICENSE +21 -0
- package/README.md +54 -2
- package/bin/netjes.mjs +13 -0
- package/cli/agents.mjs +66 -0
- package/cli/commands.mjs +467 -0
- package/cli/help.mjs +89 -0
- package/cli/info.mjs +454 -0
- package/cli/main.mjs +167 -0
- package/cli/packages.mjs +19 -0
- package/cli/process-io.mjs +163 -0
- package/cli/routes.mjs +31 -0
- package/cli/scaffold.mjs +193 -0
- package/cli/suggest.mjs +50 -0
- package/cli/terminal.mjs +40 -0
- package/dist/actions.d.ts +98 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +176 -0
- package/dist/actions.js.map +1 -0
- package/dist/client-entry.d.ts +2 -0
- package/dist/client-entry.d.ts.map +1 -0
- package/dist/client-entry.js +126 -0
- package/dist/client-entry.js.map +1 -0
- package/dist/deferred.d.ts +39 -0
- package/dist/deferred.d.ts.map +1 -0
- package/dist/deferred.js +123 -0
- package/dist/deferred.js.map +1 -0
- package/dist/define-page.d.ts +15 -0
- package/dist/define-page.d.ts.map +1 -0
- package/dist/define-page.js +27 -0
- package/dist/define-page.js.map +1 -0
- package/dist/document.d.ts +19 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +17 -0
- package/dist/document.js.map +1 -0
- package/dist/draft-mode.d.ts +5 -0
- package/dist/draft-mode.d.ts.map +1 -0
- package/dist/draft-mode.js +6 -0
- package/dist/draft-mode.js.map +1 -0
- package/dist/dynamic.d.ts +20 -0
- package/dist/dynamic.d.ts.map +1 -0
- package/dist/dynamic.js +37 -0
- package/dist/dynamic.js.map +1 -0
- package/dist/form-data.d.ts +10 -0
- package/dist/form-data.d.ts.map +1 -0
- package/dist/form-data.js +10 -0
- package/dist/form-data.js.map +1 -0
- package/dist/form.d.ts +35 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +80 -0
- package/dist/form.js.map +1 -0
- package/dist/href.d.ts +25 -0
- package/dist/href.d.ts.map +1 -0
- package/dist/href.js +71 -0
- package/dist/href.js.map +1 -0
- package/dist/image.d.ts +36 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +60 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +17 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +46 -0
- package/dist/link.js.map +1 -0
- package/dist/locale.d.ts +3 -0
- package/dist/locale.d.ts.map +1 -0
- package/dist/locale.js +6 -0
- package/dist/locale.js.map +1 -0
- package/dist/metadata.d.ts +83 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +100 -0
- package/dist/metadata.js.map +1 -0
- package/dist/modules.d.ts +74 -0
- package/dist/modules.d.ts.map +1 -0
- package/dist/modules.js +85 -0
- package/dist/modules.js.map +1 -0
- package/dist/nonce.d.ts +8 -0
- package/dist/nonce.d.ts.map +1 -0
- package/dist/nonce.js +12 -0
- package/dist/nonce.js.map +1 -0
- package/dist/payload.d.ts +171 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +273 -0
- package/dist/payload.js.map +1 -0
- package/dist/public-config.d.ts +11 -0
- package/dist/public-config.d.ts.map +1 -0
- package/dist/public-config.js +10 -0
- package/dist/public-config.js.map +1 -0
- package/dist/query.d.ts +22 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +68 -0
- package/dist/query.js.map +1 -0
- package/dist/render-cleanup.d.ts +6 -0
- package/dist/render-cleanup.d.ts.map +1 -0
- package/dist/render-cleanup.js +7 -0
- package/dist/render-cleanup.js.map +1 -0
- package/dist/root.d.ts +12 -0
- package/dist/root.d.ts.map +1 -0
- package/dist/root.js +26 -0
- package/dist/root.js.map +1 -0
- package/dist/route-tree.d.ts +56 -0
- package/dist/route-tree.d.ts.map +1 -0
- package/dist/route-tree.js +86 -0
- package/dist/route-tree.js.map +1 -0
- package/dist/router.d.ts +118 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +938 -0
- package/dist/router.js.map +1 -0
- package/dist/schema.d.ts +35 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +74 -0
- package/dist/schema.js.map +1 -0
- package/dist/script.d.ts +23 -0
- package/dist/script.d.ts.map +1 -0
- package/dist/script.js +89 -0
- package/dist/script.js.map +1 -0
- package/dist/server-entry.d.ts +2 -0
- package/dist/server-entry.d.ts.map +1 -0
- package/dist/server-entry.js +4 -0
- package/dist/server-entry.js.map +1 -0
- package/dist/server-render.d.ts +19 -0
- package/dist/server-render.d.ts.map +1 -0
- package/dist/server-render.js +193 -0
- package/dist/server-render.js.map +1 -0
- package/dist/testing-setup.d.ts +2 -0
- package/dist/testing-setup.d.ts.map +1 -0
- package/dist/testing-setup.js +8 -0
- package/dist/testing-setup.js.map +1 -0
- package/dist/testing.d.ts +76 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +106 -0
- package/dist/testing.js.map +1 -0
- package/dist/vitals-config.d.ts +4 -0
- package/dist/vitals-config.d.ts.map +1 -0
- package/dist/vitals-config.js +5 -0
- package/dist/vitals-config.js.map +1 -0
- package/dist/vitals.d.ts +13 -0
- package/dist/vitals.d.ts.map +1 -0
- package/dist/vitals.js +60 -0
- package/dist/vitals.js.map +1 -0
- package/dist/zod.d.ts +19 -0
- package/dist/zod.d.ts.map +1 -0
- package/dist/zod.js +65 -0
- package/dist/zod.js.map +1 -0
- package/package.json +147 -4
- package/sidecar/server.mjs +325 -0
- package/skills/netjes/SKILL.md +93 -0
- package/skills/netjes/references/caching-and-streaming.md +68 -0
- package/skills/netjes/references/data-and-actions.md +98 -0
- package/skills/netjes/references/deployment.md +57 -0
- package/skills/netjes/references/routing.md +65 -0
- package/skills/netjes/references/testing.md +57 -0
- package/src/actions.tsx +241 -0
- package/src/client-entry.tsx +126 -0
- package/src/deferred.ts +134 -0
- package/src/define-page.tsx +41 -0
- package/src/document.tsx +49 -0
- package/src/draft-mode.ts +6 -0
- package/src/dynamic.tsx +64 -0
- package/src/form-data.ts +10 -0
- package/src/form.tsx +122 -0
- package/src/href.ts +79 -0
- package/src/image.tsx +91 -0
- package/src/index.ts +40 -0
- package/src/link.tsx +73 -0
- package/src/locale.ts +6 -0
- package/src/metadata.tsx +181 -0
- package/src/modules.ts +133 -0
- package/src/nonce.tsx +14 -0
- package/src/payload.ts +405 -0
- package/src/public-config.tsx +19 -0
- package/src/query.tsx +82 -0
- package/src/render-cleanup.ts +7 -0
- package/src/root.tsx +39 -0
- package/src/route-tree.tsx +139 -0
- package/src/router.tsx +1188 -0
- package/src/schema.ts +98 -0
- package/src/script.tsx +103 -0
- package/src/server-entry.tsx +4 -0
- package/src/server-render.tsx +223 -0
- package/src/testing-setup.ts +8 -0
- package/src/testing.tsx +177 -0
- package/src/virtual.d.ts +12 -0
- package/src/vitals-config.ts +6 -0
- package/src/vitals.tsx +68 -0
- package/src/zod.ts +72 -0
- package/template/.dockerignore +11 -0
- package/template/Dockerfile +58 -0
- package/template/Features/Home/HomePage.cs +32 -0
- package/template/Program.cs +28 -0
- package/template/Properties/launchSettings.json +13 -0
- package/template/README.md +24 -0
- package/template/SiteProps.cs +4 -0
- package/template/_AGENTS.md +64 -0
- package/template/_CLAUDE.md +1 -0
- package/template/__NAME__.csproj +11 -0
- package/template/_gitattributes +2 -0
- package/template/_gitignore +11 -0
- package/template/app/about/page.tsx +17 -0
- package/template/app/error.tsx +15 -0
- package/template/app/layout.tsx +33 -0
- package/template/app/not-found.tsx +14 -0
- package/template/app/page.tsx +14 -0
- package/template/appsettings.Development.json +3 -0
- package/template/appsettings.json +9 -0
- package/template/components/Greeting.test.tsx +25 -0
- package/template/components/Greeting.tsx +26 -0
- package/template/components/HelloForm.test.tsx +27 -0
- package/template/components/HelloForm.tsx +34 -0
- package/template/docker-compose.override.yml +15 -0
- package/template/docker-compose.prod.yml +26 -0
- package/template/docker-compose.yml +16 -0
- package/template/generated/netjes.ts +73 -0
- package/template/package.json +36 -0
- package/template/public/favicon.svg +6 -0
- package/template/styles/global.css +7 -0
- package/template/tsconfig.json +19 -0
- package/template/vite.config.ts +7 -0
- package/vite/plugin.d.ts +29 -0
- package/vite/plugin.mjs +291 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { parseWith } from './schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* Declares a page whose props (sent by the C# loader) are validated with a Standard Schema before rendering,
|
|
5
|
+
* so the component receives parsed, typed props instead of trusting the server's JSON.
|
|
6
|
+
*/
|
|
7
|
+
export function definePage(schema, Page) {
|
|
8
|
+
function ValidatedPage(props) {
|
|
9
|
+
const parsed = parseWith(schema, props, 'props');
|
|
10
|
+
return _jsx(Page, { ...parsed });
|
|
11
|
+
}
|
|
12
|
+
ValidatedPage.displayName = `definePage(${Page.displayName ?? Page.name ?? 'Page'})`;
|
|
13
|
+
return ValidatedPage;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Declares a layout whose loader props (from `MapLayoutLoader` in C#) are validated with a Standard Schema. The
|
|
17
|
+
* component gets the parsed props next to `children` and `params`.
|
|
18
|
+
*/
|
|
19
|
+
export function defineLayout(schema, Layout) {
|
|
20
|
+
function ValidatedLayout({ children, params, ...props }) {
|
|
21
|
+
const parsed = parseWith(schema, props, 'layout props');
|
|
22
|
+
return (_jsx(Layout, { ...parsed, params: params, children: children }));
|
|
23
|
+
}
|
|
24
|
+
ValidatedLayout.displayName = `defineLayout(${Layout.displayName ?? Layout.name ?? 'Layout'})`;
|
|
25
|
+
return ValidatedLayout;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=define-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-page.js","sourceRoot":"","sources":["../src/define-page.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAyB,SAAS,EAAE,MAAM,aAAa,CAAC;AAI/D;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,MAAc,EACd,IAAyD;IAEzD,SAAS,aAAa,CAAC,KAA8B;QACnD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAkD,CAAC;QAClG,OAAO,KAAC,IAAI,OAAK,MAAM,GAAI,CAAC;IAC9B,CAAC;IACD,aAAa,CAAC,WAAW,GAAG,cAAc,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,GAAG,CAAC;IACrF,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,MAAyE;IAEzE,SAAS,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,EAAyC;QAC5F,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,cAAc,CAAkD,CAAC;QACzG,OAAO,CACL,KAAC,MAAM,OAAK,MAAM,EAAE,MAAM,EAAE,MAAM,YAC/B,QAAQ,GACF,CACV,CAAC;IACJ,CAAC;IACD,eAAe,CAAC,WAAW,GAAG,gBAAgB,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,IAAI,IAAI,QAAQ,GAAG,CAAC;IAC/F,OAAO,eAAe,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { type DocumentInfo } from './payload.ts';
|
|
3
|
+
type DocumentProps = {
|
|
4
|
+
info: DocumentInfo;
|
|
5
|
+
dataJson: string;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The `<html>` shell around every page. React renders and hydrates the whole document, so `<title>`, `<meta>`
|
|
10
|
+
* and `<link>` rendered by pages and layouts end up in the head.
|
|
11
|
+
*
|
|
12
|
+
* Documents stream, so the client entry is `async`: it hydrates the shell as soon as the shell is in, not once the
|
|
13
|
+
* last Suspense boundary has arrived. Everything it reads comes before the content: the deferred-values bootstrap in
|
|
14
|
+
* the head, the payload first in the body. The empty element at the end of the body is the last thing in the shell,
|
|
15
|
+
* which the client entry waits for before it hydrates: a cached entry can run long before the parser gets there.
|
|
16
|
+
*/
|
|
17
|
+
export declare function Document({ info, dataJson, children }: DocumentProps): import("react").JSX.Element;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAmB,KAAK,YAAY,EAAgB,MAAM,cAAc,CAAC;AAEhF,KAAK,aAAa,GAAG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAC;AAEnF;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,aAAa,+BAiCnE"}
|
package/dist/document.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DEFERRED_BOOTSTRAP } from './deferred.js';
|
|
3
|
+
import { DATA_ELEMENT_ID, SHELL_END_ID } from './payload.js';
|
|
4
|
+
/**
|
|
5
|
+
* The `<html>` shell around every page. React renders and hydrates the whole document, so `<title>`, `<meta>`
|
|
6
|
+
* and `<link>` rendered by pages and layouts end up in the head.
|
|
7
|
+
*
|
|
8
|
+
* Documents stream, so the client entry is `async`: it hydrates the shell as soon as the shell is in, not once the
|
|
9
|
+
* last Suspense boundary has arrived. Everything it reads comes before the content: the deferred-values bootstrap in
|
|
10
|
+
* the head, the payload first in the body. The empty element at the end of the body is the last thing in the shell,
|
|
11
|
+
* which the client entry waits for before it hydrates: a cached entry can run long before the parser gets there.
|
|
12
|
+
*/
|
|
13
|
+
export function Document({ info, dataJson, children }) {
|
|
14
|
+
const { nonce } = info;
|
|
15
|
+
return (_jsxs("html", { lang: info.lang, children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), nonce && _jsx("meta", { property: "csp-nonce", nonce: nonce }), !info.clientRender && _jsx("script", { nonce: nonce, dangerouslySetInnerHTML: { __html: DEFERRED_BOOTSTRAP } }), info.assets.stylesheets.map((href) => (_jsx("link", { rel: "stylesheet", href: href, nonce: nonce }, href))), info.devStyles?.map((style) => (_jsx("style", { "data-vite-dev-id": style.id, nonce: nonce, dangerouslySetInnerHTML: { __html: style.css } }, style.id))), info.assets.modulePreloads.map((href) => (_jsx("link", { rel: "modulepreload", href: href, nonce: nonce }, href))), info.assets.scripts.map((src) => (_jsx("script", { type: "module", src: src, async: true, nonce: nonce }, src)))] }), _jsxs("body", { children: [_jsx("script", { id: DATA_ELEMENT_ID, type: "application/json", nonce: nonce, dangerouslySetInnerHTML: { __html: dataJson } }), children, _jsx("template", { id: SHELL_END_ID })] })] }));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,eAAe,EAAqB,YAAY,EAAE,MAAM,cAAc,CAAC;AAIhF;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAiB;IAClE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvB,OAAO,CACL,gBAAM,IAAI,EAAE,IAAI,CAAC,IAAI,aACnB,2BACE,eAAM,OAAO,EAAC,OAAO,GAAG,EACxB,eAAM,IAAI,EAAC,UAAU,EAAC,OAAO,EAAC,qCAAqC,GAAG,EAErE,KAAK,IAAI,eAAM,QAAQ,EAAC,WAAW,EAAC,KAAK,EAAE,KAAK,GAAI,EAGpD,CAAC,IAAI,CAAC,YAAY,IAAI,iBAAQ,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,GAAI,EACvG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACrC,eAAiB,GAAG,EAAC,YAAY,EAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,IAA/C,IAAI,CAA+C,CAC/D,CAAC,EACD,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAC9B,oCAAwC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,EAAE,IAAlG,KAAK,CAAC,EAAE,CAA8F,CACnH,CAAC,EACD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACxC,eAAiB,GAAG,EAAC,eAAe,EAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,IAAlD,IAAI,CAAkD,CAClE,CAAC,EACD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAChC,iBAAkB,IAAI,EAAC,QAAQ,EAAC,GAAG,EAAE,GAAG,EAAE,KAAK,QAAC,KAAK,EAAE,KAAK,IAA/C,GAAG,CAAgD,CACjE,CAAC,IACG,EACP,2BAEE,iBAAQ,EAAE,EAAE,eAAe,EAAE,IAAI,EAAC,kBAAkB,EAAC,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAI,EACnH,QAAQ,EACT,mBAAU,EAAE,EAAE,YAAY,GAAI,IACzB,IACF,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-mode.d.ts","sourceRoot":"","sources":["../src/draft-mode.ts"],"names":[],"mappings":"AAEA,wHAAwH;AACxH,wBAAgB,YAAY,IAAI;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,CAErD"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { usePagePayload } from './router.js';
|
|
2
|
+
/** Whether this page was rendered in draft mode (`IDraftMode.Enable()` on the server), like Next.js's `draftMode()`. */
|
|
3
|
+
export function useDraftMode() {
|
|
4
|
+
return { isEnabled: usePagePayload().draftMode === true };
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=draft-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-mode.js","sourceRoot":"","sources":["../src/draft-mode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAc,CAAC;AAE9C,wHAAwH;AACxH,MAAM,UAAU,YAAY;IAC1B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ComponentType, type ReactNode } from 'react';
|
|
2
|
+
/** Options for `dynamic`. */
|
|
3
|
+
export type DynamicOptions = {
|
|
4
|
+
/** Server-render the component (default), or only render it in the browser (`false`), e.g. for code that needs `window`. */
|
|
5
|
+
ssr?: boolean;
|
|
6
|
+
/** Shown while the module loads, and on the server when `ssr` is false. */
|
|
7
|
+
loading?: ComponentType;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Like `next/dynamic`: a component whose module loads on demand, in its own chunk.
|
|
11
|
+
* `dynamic(() => import('./Chart.tsx'), { loading: Spinner })`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function dynamic<P extends object>(load: () => Promise<{
|
|
14
|
+
default: ComponentType<P>;
|
|
15
|
+
} | ComponentType<P>>, options?: DynamicOptions): ComponentType<P>;
|
|
16
|
+
/** A component that renders only in the browser, after hydration; `fallback` (default nothing) until then and on the server. */
|
|
17
|
+
export declare function clientOnly<P extends object>(Component: ComponentType<P>, options?: {
|
|
18
|
+
fallback?: ReactNode;
|
|
19
|
+
}): ComponentType<P>;
|
|
20
|
+
//# sourceMappingURL=dynamic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic.d.ts","sourceRoot":"","sources":["../src/dynamic.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAwC,MAAM,OAAO,CAAC;AAiBjG,6BAA6B;AAC7B,MAAM,MAAM,cAAc,GAAG;IAC3B,4HAA4H;IAC5H,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,2EAA2E;IAC3E,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EACtC,IAAI,EAAE,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;CAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,EACrE,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAAC,CAAC,CAAC,CAkBlB;AAED,gIAAgI;AAChI,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAO,GAAG,aAAa,CAAC,CAAC,CAAC,CAUlI"}
|
package/dist/dynamic.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense, lazy, useSyncExternalStore } from 'react';
|
|
3
|
+
const subscribe = () => () => { };
|
|
4
|
+
/** False on the server and while hydrating, true afterwards (and for client-only renders): hydration-safe. */
|
|
5
|
+
function useHydrated() {
|
|
6
|
+
return useSyncExternalStore(subscribe, () => true, () => false);
|
|
7
|
+
}
|
|
8
|
+
function ClientOnly({ fallback, children }) {
|
|
9
|
+
return useHydrated() ? children : fallback;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Like `next/dynamic`: a component whose module loads on demand, in its own chunk.
|
|
13
|
+
* `dynamic(() => import('./Chart.tsx'), { loading: Spinner })`.
|
|
14
|
+
*/
|
|
15
|
+
export function dynamic(load, options = {}) {
|
|
16
|
+
const Lazy = lazy(async () => {
|
|
17
|
+
const module = await load();
|
|
18
|
+
return { default: typeof module === 'function' ? module : module.default };
|
|
19
|
+
});
|
|
20
|
+
const Loading = options.loading;
|
|
21
|
+
function Dynamic(props) {
|
|
22
|
+
const fallback = Loading ? _jsx(Loading, {}) : null;
|
|
23
|
+
const content = (_jsx(Suspense, { fallback: fallback, children: _jsx(Lazy, { ...props }) }));
|
|
24
|
+
return options.ssr === false ? _jsx(ClientOnly, { fallback: fallback, children: content }) : content;
|
|
25
|
+
}
|
|
26
|
+
Dynamic.displayName = 'Dynamic';
|
|
27
|
+
return Dynamic;
|
|
28
|
+
}
|
|
29
|
+
/** A component that renders only in the browser, after hydration; `fallback` (default nothing) until then and on the server. */
|
|
30
|
+
export function clientOnly(Component, options = {}) {
|
|
31
|
+
function ClientOnlyComponent(props) {
|
|
32
|
+
return (_jsx(ClientOnly, { fallback: options.fallback ?? null, children: _jsx(Component, { ...props }) }));
|
|
33
|
+
}
|
|
34
|
+
ClientOnlyComponent.displayName = `clientOnly(${Component.displayName ?? Component.name ?? 'Component'})`;
|
|
35
|
+
return ClientOnlyComponent;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=dynamic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamic.js","sourceRoot":"","sources":["../src/dynamic.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAsC,QAAQ,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAEjG,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;AAEjC,8GAA8G;AAC9G,SAAS,WAAW;IAClB,OAAO,oBAAoB,CACzB,SAAS,EACT,GAAG,EAAE,CAAC,IAAI,EACV,GAAG,EAAE,CAAC,KAAK,CACZ,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAgD;IACtF,OAAO,WAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC7C,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,OAAO,CACrB,IAAqE,EACrE,OAAO,GAAmB,EAAE;IAE5B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC7E,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,SAAS,OAAO,CAAC,KAAQ;QACvB,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,KAAC,OAAO,KAAG,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9C,MAAM,OAAO,GAAG,CACd,KAAC,QAAQ,IAAC,QAAQ,EAAE,QAAQ,YAC1B,KAAC,IAAI,OAAK,KAAK,GAAI,GACV,CACZ,CAAC;QACF,OAAO,OAAO,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,QAAQ,EAAE,QAAQ,YAAG,OAAO,GAAc,CAAC,CAAC,CAAC,OAAO,CAAC;IAClG,CAAC;IACD,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAChC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gIAAgI;AAChI,MAAM,UAAU,UAAU,CAAmB,SAA2B,EAAE,OAAO,GAA6B,EAAE;IAC9G,SAAS,mBAAmB,CAAC,KAAQ;QACnC,OAAO,CACL,KAAC,UAAU,IAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI,YAC5C,KAAC,SAAS,OAAK,KAAK,GAAI,GACb,CACd,CAAC;IACJ,CAAC;IACD,mBAAmB,CAAC,WAAW,GAAG,cAAc,SAAS,CAAC,WAAW,IAAI,SAAS,CAAC,IAAI,IAAI,WAAW,GAAG,CAAC;IAC1G,OAAO,mBAAmB,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A form's fields as a request body: URL-encoded, or multipart when there are files or the form asks for it. */
|
|
2
|
+
export type EncodedForm = {
|
|
3
|
+
body: string;
|
|
4
|
+
contentType: string;
|
|
5
|
+
} | {
|
|
6
|
+
body: FormData;
|
|
7
|
+
contentType: null;
|
|
8
|
+
};
|
|
9
|
+
export declare function encodeForm(data: FormData, multipart?: boolean): EncodedForm;
|
|
10
|
+
//# sourceMappingURL=form-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-data.d.ts","sourceRoot":"","sources":["../src/form-data.ts"],"names":[],"mappings":"AAAA,iHAAiH;AACjH,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,IAAI,CAAA;CAAE,CAAC;AAExG,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,UAAQ,GAAG,WAAW,CAMzE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function encodeForm(data, multipart = false) {
|
|
2
|
+
const entries = [...data.entries()];
|
|
3
|
+
if (multipart || entries.some(([, value]) => typeof value !== 'string'))
|
|
4
|
+
return { body: data, contentType: null };
|
|
5
|
+
const params = new URLSearchParams();
|
|
6
|
+
for (const [name, value] of entries)
|
|
7
|
+
params.append(name, String(value));
|
|
8
|
+
return { body: params.toString(), contentType: 'application/x-www-form-urlencoded;charset=UTF-8' };
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=form-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-data.js","sourceRoot":"","sources":["../src/form-data.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,UAAU,CAAC,IAAc,EAAE,SAAS,GAAG,KAAK;IAC1D,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACpC,IAAI,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAClH,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,OAAO;QAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;AACrG,CAAC"}
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, FormHTMLAttributes } from 'react';
|
|
2
|
+
/** A React form action, e.g. the `formAction` from `useAction(serverAction('name'))`. */
|
|
3
|
+
export type FormAction = (formData: FormData) => void | Promise<void>;
|
|
4
|
+
/** The props of `<Form>`: a form's own, with client navigation and server action support. */
|
|
5
|
+
export type FormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'method' | 'action'> & {
|
|
6
|
+
method?: 'get' | 'post';
|
|
7
|
+
/**
|
|
8
|
+
* Where to submit. Default: the current URL, which is where `MapPageAction` handlers for this page listen. Or a
|
|
9
|
+
* form action function: a server action from `useAction` (a real form post without JavaScript), or any function.
|
|
10
|
+
*/
|
|
11
|
+
action?: string | FormAction;
|
|
12
|
+
replace?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Let the browser submit it the classic way, with a full page load: for responses that are not pages, such as
|
|
15
|
+
* large file downloads, which the browser then streams to disk instead of Netjes buffering them.
|
|
16
|
+
*/
|
|
17
|
+
reloadDocument?: boolean;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A `<form>` that submits without a full page load and handles the page action's result like a navigation:
|
|
21
|
+
* a redirect is followed, re-rendered props replace the page. Without JavaScript it is a normal form post.
|
|
22
|
+
* POST forms include the antiforgery token when the server has antiforgery enabled.
|
|
23
|
+
*
|
|
24
|
+
* With a function `action` React runs it (so `useFormStatus` works). For server actions the server renders a real
|
|
25
|
+
* `action`/`method`, the token and the return URL, so the same form also posts without JavaScript.
|
|
26
|
+
*/
|
|
27
|
+
export declare function Form({ method, action, replace, reloadDocument, onSubmit, children, ...rest }: FormProps): import("react").JSX.Element;
|
|
28
|
+
/** The props of `<SubmitButton>`: a button's own, except `type`. */
|
|
29
|
+
export type SubmitButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'>;
|
|
30
|
+
/**
|
|
31
|
+
* A submit button whose `formAction` is rendered under the base path, so it posts to the right place with and
|
|
32
|
+
* without JavaScript (a plain `<button formAction="/x">` would miss the base without JavaScript).
|
|
33
|
+
*/
|
|
34
|
+
export declare function SubmitButton({ formAction, ...rest }: SubmitButtonProps): import("react").JSX.Element;
|
|
35
|
+
//# sourceMappingURL=form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAe,MAAM,OAAO,CAAC;AAMnF,yFAAyF;AACzF,MAAM,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtE,6FAA6F;AAC7F,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,eAAe,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC,GAAG;IACvF,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAYF;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,EAAE,MAAc,EAAE,MAAM,EAAE,OAAe,EAAE,cAAsB,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,SAAS,+BAiE/H;AAED,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC;AAEtF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,EAAE,iBAAiB,+BAItE"}
|
package/dist/form.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { RETURN_FIELD, actionMetaOf } from './actions.js';
|
|
3
|
+
import { encodeForm } from './form-data.js';
|
|
4
|
+
import { useBasePath, useInternalRouter, usePagePayload, withBasePath } from './router.js';
|
|
5
|
+
function formDataOf(form, submitter) {
|
|
6
|
+
try {
|
|
7
|
+
return new FormData(form, submitter);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
const data = new FormData(form);
|
|
11
|
+
if (submitter instanceof HTMLButtonElement && submitter.name)
|
|
12
|
+
data.append(submitter.name, submitter.value);
|
|
13
|
+
return data;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A `<form>` that submits without a full page load and handles the page action's result like a navigation:
|
|
18
|
+
* a redirect is followed, re-rendered props replace the page. Without JavaScript it is a normal form post.
|
|
19
|
+
* POST forms include the antiforgery token when the server has antiforgery enabled.
|
|
20
|
+
*
|
|
21
|
+
* With a function `action` React runs it (so `useFormStatus` works). For server actions the server renders a real
|
|
22
|
+
* `action`/`method`, the token and the return URL, so the same form also posts without JavaScript.
|
|
23
|
+
*/
|
|
24
|
+
export function Form({ method = 'get', action, replace = false, reloadDocument = false, onSubmit, children, ...rest }) {
|
|
25
|
+
const router = useInternalRouter();
|
|
26
|
+
const payload = usePagePayload();
|
|
27
|
+
const basePath = useBasePath();
|
|
28
|
+
const isFunction = typeof action === 'function';
|
|
29
|
+
const serverAction = actionMetaOf(action);
|
|
30
|
+
const actionUrl = typeof action === 'string' ? withBasePath(action, basePath) : undefined;
|
|
31
|
+
const token = method === 'post' || serverAction ? payload.antiforgery : undefined;
|
|
32
|
+
const hidden = (_jsxs(_Fragment, { children: [token?.requestToken && _jsx("input", { type: "hidden", name: token.formFieldName, value: token.requestToken }), serverAction && _jsx("input", { type: "hidden", name: RETURN_FIELD, value: payload.url })] }));
|
|
33
|
+
if (isFunction) {
|
|
34
|
+
// React renders function actions as an inert placeholder on the server; a server action gets its real URL
|
|
35
|
+
// there instead. React keeps the server's attributes when hydrating a function action.
|
|
36
|
+
const onServer = typeof document === 'undefined';
|
|
37
|
+
return (_jsxs("form", { ...rest, action: onServer && serverAction ? withBasePath(serverAction.url, basePath) : action, method: onServer && serverAction ? 'post' : undefined, onSubmit: onSubmit, children: [hidden, children] }));
|
|
38
|
+
}
|
|
39
|
+
function handleSubmit(event) {
|
|
40
|
+
onSubmit?.(event);
|
|
41
|
+
if (event.defaultPrevented || reloadDocument)
|
|
42
|
+
return;
|
|
43
|
+
const form = event.currentTarget;
|
|
44
|
+
const submitter = event.submitter;
|
|
45
|
+
const target = submitter?.getAttribute('formtarget') ?? form.getAttribute('target');
|
|
46
|
+
if (target && target !== '_self')
|
|
47
|
+
return;
|
|
48
|
+
const formAction = submitter?.getAttribute('formaction');
|
|
49
|
+
const url = new URL(formAction ? withBasePath(formAction, basePath) : (actionUrl ?? window.location.href), window.location.href);
|
|
50
|
+
if (url.origin !== window.location.origin)
|
|
51
|
+
return;
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
const data = formDataOf(form, submitter);
|
|
54
|
+
const submitMethod = (submitter?.getAttribute('formmethod') ?? method).toLowerCase();
|
|
55
|
+
if (submitMethod === 'post') {
|
|
56
|
+
void router.submit(url, encodeForm(data, form.enctype === 'multipart/form-data'));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const query = new URLSearchParams();
|
|
60
|
+
for (const [name, value] of data.entries())
|
|
61
|
+
if (typeof value === 'string')
|
|
62
|
+
query.append(name, value);
|
|
63
|
+
url.search = query.toString();
|
|
64
|
+
url.hash = '';
|
|
65
|
+
// Built from the form's own action, which is not one of the app's typed routes.
|
|
66
|
+
const href = url.href;
|
|
67
|
+
void (replace ? router.replace(href) : router.push(href));
|
|
68
|
+
}
|
|
69
|
+
return (_jsxs("form", { method: method, action: actionUrl, ...rest, onSubmit: handleSubmit, children: [hidden, children] }));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A submit button whose `formAction` is rendered under the base path, so it posts to the right place with and
|
|
73
|
+
* without JavaScript (a plain `<button formAction="/x">` would miss the base without JavaScript).
|
|
74
|
+
*/
|
|
75
|
+
export function SubmitButton({ formAction, ...rest }) {
|
|
76
|
+
const basePath = useBasePath();
|
|
77
|
+
const action = typeof formAction === 'string' ? withBasePath(formAction, basePath) : formAction;
|
|
78
|
+
return _jsx("button", { type: "submit", formAction: action, ...rest });
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=form.js.map
|
package/dist/form.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.js","sourceRoot":"","sources":["../src/form.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAe,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAc,CAAC;AAqB5F,SAAS,UAAU,CAAC,IAAqB,EAAE,SAA6B;IACtE,IAAI,CAAC;QACH,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,SAAS,YAAY,iBAAiB,IAAI,SAAS,CAAC,IAAI;YAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;QAC3G,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,EAAE,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,cAAc,GAAG,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAa;IAC9H,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC;IAChD,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1F,MAAM,KAAK,GAAG,MAAM,KAAK,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;IAClF,MAAM,MAAM,GAAG,CACb,8BACG,KAAK,EAAE,YAAY,IAAI,gBAAO,IAAI,EAAC,QAAQ,EAAC,IAAI,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,YAAY,GAAI,EACpG,YAAY,IAAI,gBAAO,IAAI,EAAC,QAAQ,EAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,GAAI,IAC/E,CACJ,CAAC;IAEF,IAAI,UAAU,EAAE,CAAC;QACf,0GAA0G;QAC1G,uFAAuF;QACvF,MAAM,QAAQ,GAAG,OAAO,QAAQ,KAAK,WAAW,CAAC;QACjD,OAAO,CACL,mBACM,IAAI,EACR,MAAM,EAAE,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,EACpF,MAAM,EAAE,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EACrD,QAAQ,EAAE,QAAQ,aAEjB,MAAM,EACN,QAAQ,IACJ,CACR,CAAC;IACJ,CAAC;IAED,SAAS,YAAY,CAAC,KAAmC;QACvD,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;QAClB,IAAI,KAAK,CAAC,gBAAgB,IAAI,cAAc;YAAE,OAAO;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAClC,MAAM,MAAM,GAAG,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACpF,IAAI,MAAM,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO;QACzC,MAAM,UAAU,GAAG,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjI,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,QAAQ,CAAC,MAAM;YAAE,OAAO;QAClD,KAAK,CAAC,cAAc,EAAE,CAAC;QAEvB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QACrF,IAAI,YAAY,KAAK,MAAM,EAAE,CAAC;YAC5B,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,KAAK,qBAAqB,CAAC,CAAC,CAAC;YAClF,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;YAAE,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACrG,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;QACd,gFAAgF;QAChF,MAAM,IAAI,GAAG,GAAG,CAAC,IAAY,CAAC;QAC9B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,CACL,gBAAM,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAM,IAAI,EAAE,QAAQ,EAAE,YAAY,aACtE,MAAM,EACN,QAAQ,IACJ,CACR,CAAC;AACJ,CAAC;AAKD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,EAAqB;IACrE,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IAChG,OAAO,iBAAQ,IAAI,EAAC,QAAQ,EAAC,UAAU,EAAE,MAAM,KAAM,IAAI,GAAI,CAAC;AAChE,CAAC"}
|
package/dist/href.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apps register their generated route type here (the file `netjes generate` writes does it), which types the
|
|
3
|
+
* `href` of `<Link>` and `router.push`: `declare module 'netjes' { interface Register { href: Href } }`.
|
|
4
|
+
*/
|
|
5
|
+
export interface Register {
|
|
6
|
+
}
|
|
7
|
+
/** A URL `<Link>` and the router accept: the app's routes once registered, any string before that. */
|
|
8
|
+
export type Href = Register extends {
|
|
9
|
+
href: infer H extends string;
|
|
10
|
+
} ? H : string;
|
|
11
|
+
/** One value of a query string: `null` and `undefined` leave the key out. */
|
|
12
|
+
export type SearchValue = string | number | boolean | null | undefined;
|
|
13
|
+
/** A query string as an object: a value per key, or a list of values for a key that repeats. */
|
|
14
|
+
export type SearchParamsInput = Record<string, SearchValue | readonly SearchValue[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Whether a URL is one of the app's pages (by route pattern), a link within the page (`?…`, `#…`), or another
|
|
17
|
+
* site. The generated `isHref()` and `Href` schema call this to check URLs that come from data.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isRouteHref(value: string, patterns: readonly string[]): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Builds the URL for a route pattern written in file-name syntax (`/blog/[slug]`, `/docs/[...path]`,
|
|
22
|
+
* `/shop/[[...filters]]`), encoding every segment value. The generated `route()` helper calls this with typed params.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildHref(pattern: string, params: Record<string, unknown>, search?: SearchParamsInput): string;
|
|
25
|
+
//# sourceMappingURL=href.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"href.d.ts","sourceRoot":"","sources":["../src/href.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,QAAQ;CAAG;AAE5B,sGAAsG;AACtG,MAAM,MAAM,IAAI,GAAG,QAAQ,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GAAG,CAAC,GAAG,MAAM,CAAC;AAElF,6EAA6E;AAC7E,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AACvE,gGAAgG;AAChG,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAC,CAAC;AAarF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM/E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAoC9G"}
|
package/dist/href.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
function matches(pattern, path) {
|
|
2
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
3
|
+
const segment = pattern[i];
|
|
4
|
+
if (segment.startsWith('[[...'))
|
|
5
|
+
return true;
|
|
6
|
+
if (segment.startsWith('[...'))
|
|
7
|
+
return path.length > i;
|
|
8
|
+
if (i >= path.length)
|
|
9
|
+
return false;
|
|
10
|
+
if (!segment.startsWith('[') && segment !== path[i])
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return pattern.length === path.length;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether a URL is one of the app's pages (by route pattern), a link within the page (`?…`, `#…`), or another
|
|
17
|
+
* site. The generated `isHref()` and `Href` schema call this to check URLs that come from data.
|
|
18
|
+
*/
|
|
19
|
+
export function isRouteHref(value, patterns) {
|
|
20
|
+
if (/^(https?:|mailto:|tel:)/i.test(value) || value.startsWith('?') || value.startsWith('#'))
|
|
21
|
+
return true;
|
|
22
|
+
// "//host" and "/\host" are other hosts as far as browsers are concerned.
|
|
23
|
+
if (!value.startsWith('/') || value.startsWith('//') || value.startsWith('/\\'))
|
|
24
|
+
return false;
|
|
25
|
+
const path = (value.split(/[?#]/, 1)[0] ?? '').split('/').filter(Boolean);
|
|
26
|
+
return patterns.some((pattern) => matches(pattern.split('/').filter(Boolean), path));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Builds the URL for a route pattern written in file-name syntax (`/blog/[slug]`, `/docs/[...path]`,
|
|
30
|
+
* `/shop/[[...filters]]`), encoding every segment value. The generated `route()` helper calls this with typed params.
|
|
31
|
+
*/
|
|
32
|
+
export function buildHref(pattern, params, search) {
|
|
33
|
+
const missing = (name) => {
|
|
34
|
+
throw new Error(`netjes: route '${pattern}' needs the parameter '${name}'`);
|
|
35
|
+
};
|
|
36
|
+
const segments = pattern
|
|
37
|
+
.split('/')
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.flatMap((segment) => {
|
|
40
|
+
const optional = /^\[\[\.\.\.(\w+)\]\]$/.exec(segment)?.[1];
|
|
41
|
+
if (optional) {
|
|
42
|
+
const values = params[optional];
|
|
43
|
+
return Array.isArray(values) ? values.map((v) => encodeURIComponent(String(v))) : [];
|
|
44
|
+
}
|
|
45
|
+
const catchAll = /^\[\.\.\.(\w+)\]$/.exec(segment)?.[1];
|
|
46
|
+
if (catchAll) {
|
|
47
|
+
const values = params[catchAll];
|
|
48
|
+
if (!Array.isArray(values) || values.length === 0)
|
|
49
|
+
return missing(catchAll);
|
|
50
|
+
return values.map((v) => encodeURIComponent(String(v)));
|
|
51
|
+
}
|
|
52
|
+
const single = /^\[(\w+)\]$/.exec(segment)?.[1];
|
|
53
|
+
if (single) {
|
|
54
|
+
const value = params[single];
|
|
55
|
+
if (value === undefined || value === null || value === '')
|
|
56
|
+
return missing(single);
|
|
57
|
+
return [encodeURIComponent(String(value))];
|
|
58
|
+
}
|
|
59
|
+
return [segment];
|
|
60
|
+
});
|
|
61
|
+
const query = new URLSearchParams();
|
|
62
|
+
for (const [key, value] of Object.entries(search ?? {})) {
|
|
63
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
64
|
+
if (item !== undefined && item !== null)
|
|
65
|
+
query.append(key, String(item));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const queryString = query.toString();
|
|
69
|
+
return `/${segments.join('/')}${queryString ? `?${queryString}` : ''}`;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=href.js.map
|
package/dist/href.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"href.js","sourceRoot":"","sources":["../src/href.ts"],"names":[],"mappings":"AAeA,SAAS,OAAO,CAAC,OAA0B,EAAE,IAAuB;IAClE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7C,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QACnC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IACpE,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,QAA2B;IACpE,IAAI,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1G,0EAA0E;IAC1E,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9F,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1E,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACvF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,MAA+B,EAAE,MAA0B;IACpG,MAAM,OAAO,GAAG,CAAC,IAAY,EAAS,EAAE;QACtC,MAAM,IAAI,KAAK,CAAC,kBAAkB,OAAO,0BAA0B,IAAI,GAAG,CAAC,CAAC;IAC9E,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO;SACrB,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,OAAO,CAAC;SACf,OAAO,CAAC,CAAC,OAAO,EAAY,EAAE;QAC7B,MAAM,QAAQ,GAAG,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5D,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YAChC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,CAAC;QACD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACxD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC5E,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAChD,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;YAClF,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;IAEL,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;gBAAE,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IACD,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;IACrC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACzE,CAAC"}
|
package/dist/image.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ImgHTMLAttributes } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Where the optimizer lives, and the widths and qualities it serves: Netjes:Images on the server, sent with the first
|
|
4
|
+
* document. Without `qualities`, any quality is requested as given.
|
|
5
|
+
*/
|
|
6
|
+
export type ImageConfig = {
|
|
7
|
+
path: string;
|
|
8
|
+
widths: readonly number[];
|
|
9
|
+
qualities?: readonly number[];
|
|
10
|
+
basePath?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const ImageConfigProvider: import("react").Provider<ImageConfig>;
|
|
13
|
+
/** The props of `<Image>`: an `<img>`'s own, with a required size and `alt`, and the optimizer's options. */
|
|
14
|
+
export type ImageProps = Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'srcSet' | 'width' | 'height' | 'loading' | 'alt'> & {
|
|
15
|
+
/** A path in `public/` (`/img/hero.png`) or a URL on a host in Netjes:Images:RemoteHosts. */
|
|
16
|
+
src: string;
|
|
17
|
+
/** Required, as for any image; `""` for decorative ones. */
|
|
18
|
+
alt: string;
|
|
19
|
+
/** The intrinsic size in CSS pixels; reserves space so the page does not shift as it loads. */
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
/** For responsive layouts, e.g. `(max-width: 768px) 100vw, 50vw`: the browser then picks from every width. */
|
|
23
|
+
sizes?: string;
|
|
24
|
+
/** 1–100. Default 75. Rounded to the nearest of Netjes:Images:Qualities. */
|
|
25
|
+
quality?: number;
|
|
26
|
+
/** Above the fold: load eagerly with high priority, and preload it from the document head. */
|
|
27
|
+
priority?: boolean;
|
|
28
|
+
/** Serve `src` as is. SVGs and data URLs always are. */
|
|
29
|
+
unoptimized?: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Like `next/image`: an `<img>` with width and height (no layout shift), lazy loading, and a `srcset` of resized
|
|
33
|
+
* copies from the `/_netjes/image` endpoint, so each screen downloads only what it needs.
|
|
34
|
+
*/
|
|
35
|
+
export declare function Image({ src, alt, width, height, sizes, quality, priority, unoptimized, ...rest }: ImageProps): import("react").JSX.Element;
|
|
36
|
+
//# sourceMappingURL=image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAA6B,MAAM,OAAO,CAAC;AAG1E;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAUxH,eAAO,MAAM,mBAAmB,uCAA8B,CAAC;AAE/D,6GAA6G;AAC7G,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC,GAAG;IAC9H,6FAA6F;IAC7F,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,+FAA+F;IAC/F,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,8GAA8G;IAC9G,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAyBF;;;GAGG;AACH,wBAAgB,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAY,EAAE,QAAgB,EAAE,WAAmB,EAAE,GAAG,IAAI,EAAE,EAAE,UAAU,+BAyBjI"}
|
package/dist/image.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
import { preload } from 'react-dom';
|
|
4
|
+
/** Next.js's device and image sizes; the server's Netjes:Images:Widths default to the same. */
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
path: '/_netjes/image',
|
|
7
|
+
widths: [16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
8
|
+
};
|
|
9
|
+
const ImageConfigContext = createContext(DEFAULT_CONFIG);
|
|
10
|
+
export const ImageConfigProvider = ImageConfigContext.Provider;
|
|
11
|
+
function optimizerUrl(config, src, width, quality) {
|
|
12
|
+
return `${config.path}?url=${encodeURIComponent(src)}&w=${width}&q=${quality}`;
|
|
13
|
+
}
|
|
14
|
+
/** The accepted quality nearest to the one asked for (the higher one on a tie). */
|
|
15
|
+
function qualityFor(qualities, wanted) {
|
|
16
|
+
if (!qualities?.length)
|
|
17
|
+
return wanted;
|
|
18
|
+
return qualities.reduce((best, q) => (Math.abs(q - wanted) < Math.abs(best - wanted) || (Math.abs(q - wanted) === Math.abs(best - wanted) && q > best) ? q : best));
|
|
19
|
+
}
|
|
20
|
+
/** The smallest configured width that covers `target`, or the largest there is. */
|
|
21
|
+
function widthFor(widths, target) {
|
|
22
|
+
return widths.find((w) => w >= target) ?? widths[widths.length - 1] ?? target;
|
|
23
|
+
}
|
|
24
|
+
function underBasePath(src, basePath) {
|
|
25
|
+
return basePath && src.startsWith('/') && !src.startsWith('//') && !src.startsWith(`${basePath}/`) ? basePath + src : src;
|
|
26
|
+
}
|
|
27
|
+
function isUnoptimizable(src) {
|
|
28
|
+
return src.startsWith('data:') || src.startsWith('blob:') || /\.svg($|[?#])/i.test(src);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Like `next/image`: an `<img>` with width and height (no layout shift), lazy loading, and a `srcset` of resized
|
|
32
|
+
* copies from the `/_netjes/image` endpoint, so each screen downloads only what it needs.
|
|
33
|
+
*/
|
|
34
|
+
export function Image({ src, alt, width, height, sizes, quality = 75, priority = false, unoptimized = false, ...rest }) {
|
|
35
|
+
const config = useContext(ImageConfigContext);
|
|
36
|
+
const common = {
|
|
37
|
+
...rest,
|
|
38
|
+
alt,
|
|
39
|
+
width,
|
|
40
|
+
height,
|
|
41
|
+
decoding: 'async',
|
|
42
|
+
loading: priority ? 'eager' : 'lazy',
|
|
43
|
+
...(priority ? { fetchPriority: 'high' } : {}),
|
|
44
|
+
};
|
|
45
|
+
// Served as is, from public/: under the app's base path. (The optimizer's `url` stays a public/ path.)
|
|
46
|
+
if (unoptimized || isUnoptimizable(src))
|
|
47
|
+
return _jsx("img", { ...common, src: underBasePath(src, config.basePath) });
|
|
48
|
+
const widths = [...config.widths].sort((a, b) => a - b);
|
|
49
|
+
const q = qualityFor(config.qualities, quality);
|
|
50
|
+
// Responsive: every width, the browser picks by `sizes`. Fixed: 1x and 2x, each once.
|
|
51
|
+
const chosen = sizes ? widths : [...new Set([widthFor(widths, width), widthFor(widths, width * 2)])];
|
|
52
|
+
const srcSet = chosen
|
|
53
|
+
.map((w, i) => `${optimizerUrl(config, src, w, q)} ${sizes ? `${w}w` : `${i + 1}x`}`)
|
|
54
|
+
.join(', ');
|
|
55
|
+
const fallback = optimizerUrl(config, src, chosen[chosen.length - 1] ?? width, q);
|
|
56
|
+
if (priority)
|
|
57
|
+
preload(fallback, { as: 'image', imageSrcSet: srcSet, ...(sizes ? { imageSizes: sizes } : {}), fetchPriority: 'high' });
|
|
58
|
+
return _jsx("img", { ...common, sizes: sizes, srcSet: srcSet, src: fallback });
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { type ActionState, type PageActionFailure, type ServerAction, ServerActionError, type UseActionOptions, serverAction, useAction, usePageActionState, } from './actions.tsx';
|
|
2
|
+
export { defineLayout, definePage } from './define-page.tsx';
|
|
3
|
+
export { useDraftMode } from './draft-mode.ts';
|
|
4
|
+
export { type DynamicOptions, clientOnly, dynamic } from './dynamic.tsx';
|
|
5
|
+
export { Form, type FormAction, type FormProps, SubmitButton, type SubmitButtonProps } from './form.tsx';
|
|
6
|
+
export { type Href, type Register, type SearchParamsInput, type SearchValue, buildHref, isRouteHref } from './href.ts';
|
|
7
|
+
export { Image, type ImageProps } from './image.tsx';
|
|
8
|
+
export { Link, type LinkProps } from './link.tsx';
|
|
9
|
+
export { useLocale } from './locale.ts';
|
|
10
|
+
export type { ErrorPageProps, LayoutProps } from './modules.ts';
|
|
11
|
+
export { useNonce } from './nonce.tsx';
|
|
12
|
+
export type { PageKind, Params, SeededQuery, SegmentRef } from './payload.ts';
|
|
13
|
+
export { usePublicConfig } from './public-config.tsx';
|
|
14
|
+
export { Script, type ScriptProps, type ScriptStrategy } from './script.tsx';
|
|
15
|
+
export { type NavigationState, type SubmitError, type Router, useBasePath, useHref, useNavigation, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useSharedProps, } from './router.tsx';
|
|
16
|
+
export type { StandardSchemaV1 } from './schema.ts';
|
|
17
|
+
export type { Metadata, MetadataImage, MetadataTitle } from './metadata.tsx';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,YAAY,EACZ,SAAS,EACT,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,KAAK,cAAc,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzG,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACvH,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,WAAW,EACX,OAAO,EACP,aAAa,EACb,SAAS,EACT,WAAW,EACX,SAAS,EACT,eAAe,EACf,wBAAwB,EACxB,yBAAyB,EACzB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { ServerActionError, serverAction, useAction, usePageActionState, } from './actions.js';
|
|
2
|
+
export { defineLayout, definePage } from './define-page.js';
|
|
3
|
+
export { useDraftMode } from './draft-mode.js';
|
|
4
|
+
export { clientOnly, dynamic } from './dynamic.js';
|
|
5
|
+
export { Form, SubmitButton } from './form.js';
|
|
6
|
+
export { buildHref, isRouteHref } from './href.js';
|
|
7
|
+
export { Image } from './image.js';
|
|
8
|
+
export { Link } from './link.js';
|
|
9
|
+
export { useLocale } from './locale.js';
|
|
10
|
+
export { useNonce } from './nonce.js';
|
|
11
|
+
export { usePublicConfig } from './public-config.js';
|
|
12
|
+
export { Script } from './script.js';
|
|
13
|
+
export { useBasePath, useHref, useNavigation, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useSharedProps, } from './router.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,iBAAiB,EAEjB,YAAY,EACZ,SAAS,EACT,kBAAkB,GACnB,MAAM,cAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAuB,UAAU,EAAE,OAAO,EAAE,MAAM,cAAe,CAAC;AACzE,OAAO,EAAE,IAAI,EAAmC,YAAY,EAA0B,MAAM,WAAY,CAAC;AACzG,OAAO,EAAsE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACvH,OAAO,EAAE,KAAK,EAAmB,MAAM,YAAa,CAAC;AACrD,OAAO,EAAE,IAAI,EAAkB,MAAM,WAAY,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAa,CAAC;AAEvC,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAqB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAyC,MAAM,aAAc,CAAC;AAC7E,OAAO,EAIL,WAAW,EACX,OAAO,EACP,aAAa,EACb,SAAS,EACT,WAAW,EACX,SAAS,EACT,eAAe,EACf,wBAAwB,EACxB,yBAAyB,EACzB,cAAc,GACf,MAAM,aAAc,CAAC"}
|