@thomas.f/prnext 0.1.0-alpha.1
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 +54 -0
- package/build/action-exports.mjs +107 -0
- package/build/actions.mjs +214 -0
- package/build/app-config.mjs +138 -0
- package/build/app-routing.mjs +119 -0
- package/build/app-static.mjs +158 -0
- package/build/app.mjs +536 -0
- package/build/cache-components.mjs +91 -0
- package/build/cache-handlers.mjs +54 -0
- package/build/check.mjs +40 -0
- package/build/compiler.mjs +222 -0
- package/build/config.mjs +193 -0
- package/build/css-modules.mjs +114 -0
- package/build/custom-routes.mjs +363 -0
- package/build/dev-worker.mjs +18 -0
- package/build/dev.mjs +76 -0
- package/build/directory-cache.mjs +29 -0
- package/build/dynamic.mjs +125 -0
- package/build/edge.mjs +141 -0
- package/build/font-data.LICENSE.txt +25 -0
- package/build/font-data.json +1 -0
- package/build/font-files.mjs +221 -0
- package/build/fonts.mjs +131 -0
- package/build/framework-imports.mjs +17 -0
- package/build/i18n.mjs +39 -0
- package/build/images-config.mjs +62 -0
- package/build/images.mjs +86 -0
- package/build/incremental.mjs +76 -0
- package/build/index.mjs +356 -0
- package/build/loader-cache.mjs +69 -0
- package/build/metadata-images.mjs +72 -0
- package/build/metadata-routes.mjs +77 -0
- package/build/middleware.mjs +231 -0
- package/build/migrate.mjs +289 -0
- package/build/module-resolution.mjs +78 -0
- package/build/native-discovery.mjs +134 -0
- package/build/native-packages.mjs +105 -0
- package/build/npm-packages.mjs +110 -0
- package/build/og.mjs +14 -0
- package/build/page-extensions.mjs +12 -0
- package/build/passthrough-loader.cjs +4 -0
- package/build/precompress.mjs +122 -0
- package/build/scan.mjs +178 -0
- package/build/script-workers.mjs +25 -0
- package/build/server-actions-config.mjs +27 -0
- package/build/source-maps.mjs +34 -0
- package/build/standalone-config.mjs +19 -0
- package/build/standalone-runner.mjs +21 -0
- package/build/standalone-worker.mjs +5 -0
- package/build/standalone.mjs +240 -0
- package/build/static-export.mjs +93 -0
- package/build/styles.mjs +174 -0
- package/build/transform-cache.mjs +50 -0
- package/build/transform.mjs +155 -0
- package/build/turbopack-conditions.mjs +59 -0
- package/build/webpack-bridge.cjs +3 -0
- package/build/webpack-graph.mjs +256 -0
- package/build/webpack-loader.cjs +9 -0
- package/cli.mjs +210 -0
- package/compat/app-client-page.cjs +32 -0
- package/compat/app-context.cjs +40 -0
- package/compat/app-context.d.cts +20 -0
- package/compat/app-dynamic.cjs +34 -0
- package/compat/app-error-boundary.cjs +53 -0
- package/compat/app-global-error-boundary.cjs +34 -0
- package/compat/app-layout-context.cjs +27 -0
- package/compat/app-navigation-boundary.cjs +47 -0
- package/compat/app.cjs +33 -0
- package/compat/app.d.cts +9 -0
- package/compat/cache-handlers.cjs +223 -0
- package/compat/cache-life.cjs +34 -0
- package/compat/cache.cjs +99 -0
- package/compat/cache.d.cts +36 -0
- package/compat/compat/router.d.cts +2 -0
- package/compat/compat-router.cjs +7 -0
- package/compat/constants.cjs +8 -0
- package/compat/constants.d.cts +7 -0
- package/compat/cookies.cjs +129 -0
- package/compat/data-cache.cjs +389 -0
- package/compat/dist/shared/lib/app-router-context.shared-runtime.d.cts +3 -0
- package/compat/dist/shared/lib/router-context.d.cts +1 -0
- package/compat/dist/shared/lib/router-context.shared-runtime.d.cts +3 -0
- package/compat/document.cjs +73 -0
- package/compat/document.d.cts +41 -0
- package/compat/draft.cjs +28 -0
- package/compat/dynamic-bailout.cjs +13 -0
- package/compat/dynamic.cjs +119 -0
- package/compat/dynamic.d.cts +32 -0
- package/compat/error.cjs +36 -0
- package/compat/error.d.cts +15 -0
- package/compat/fetch.d.cts +10 -0
- package/compat/font/google.d.cts +1 -0
- package/compat/font/local.d.cts +2 -0
- package/compat/font-google.cjs +2 -0
- package/compat/font-google.d.cts +1944 -0
- package/compat/font-local.cjs +5 -0
- package/compat/font-local.d.cts +3 -0
- package/compat/font-types.d.cts +25 -0
- package/compat/head.cjs +180 -0
- package/compat/head.d.cts +3 -0
- package/compat/headers.cjs +89 -0
- package/compat/headers.d.cts +26 -0
- package/compat/image-config.cjs +3 -0
- package/compat/image-imports.d.ts +12 -0
- package/compat/image-shared.cjs +62 -0
- package/compat/image.cjs +64 -0
- package/compat/image.d.cts +35 -0
- package/compat/incremental-cache.cjs +186 -0
- package/compat/index.d.ts +159 -0
- package/compat/instant-samples.cjs +39 -0
- package/compat/instant.cjs +13 -0
- package/compat/link.cjs +68 -0
- package/compat/link.d.cts +17 -0
- package/compat/locale.cjs +30 -0
- package/compat/metadata.d.cts +77 -0
- package/compat/navigation-controls.cjs +27 -0
- package/compat/navigation-server.cjs +25 -0
- package/compat/navigation.cjs +52 -0
- package/compat/navigation.d.cts +28 -0
- package/compat/next-app-router-context.cjs +6 -0
- package/compat/next-router-context.cjs +3 -0
- package/compat/og.cjs +32 -0
- package/compat/og.d.cts +1 -0
- package/compat/paths.cjs +33 -0
- package/compat/preview.cjs +41 -0
- package/compat/rewrite.cjs +41 -0
- package/compat/route-request.cjs +64 -0
- package/compat/router.cjs +137 -0
- package/compat/router.d.cts +58 -0
- package/compat/script-context.cjs +5 -0
- package/compat/script-loader.cjs +135 -0
- package/compat/script.cjs +69 -0
- package/compat/script.d.cts +17 -0
- package/compat/server-primitives.cjs +109 -0
- package/compat/server.cjs +13 -0
- package/compat/server.d.cts +37 -0
- package/compat/static-generation.cjs +108 -0
- package/compat/use-cache.cjs +129 -0
- package/index.mjs +1 -0
- package/native/platforms.json +6 -0
- package/native/resolve.mjs +81 -0
- package/package.json +127 -0
- package/release/verify.mjs +59 -0
- package/runtime/action-client.mjs +19 -0
- package/runtime/action-crypto.mjs +138 -0
- package/runtime/action-reference.mjs +2 -0
- package/runtime/action-server.mjs +161 -0
- package/runtime/action-ssr.mjs +5 -0
- package/runtime/api.mjs +326 -0
- package/runtime/app-client.mjs +539 -0
- package/runtime/app-content.mjs +8 -0
- package/runtime/app-errors.mjs +12 -0
- package/runtime/app-instant.mjs +30 -0
- package/runtime/app-metadata.mjs +410 -0
- package/runtime/app-partial-model.mjs +228 -0
- package/runtime/app-partial.mjs +130 -0
- package/runtime/app-prefetch.mjs +34 -0
- package/runtime/app-recovery.mjs +87 -0
- package/runtime/app-render.mjs +544 -0
- package/runtime/app-routing-access.mjs +143 -0
- package/runtime/app-routing-context.mjs +35 -0
- package/runtime/app-routing-state.mjs +61 -0
- package/runtime/app-routing.mjs +323 -0
- package/runtime/app-segments.mjs +22 -0
- package/runtime/app-static-params.mjs +120 -0
- package/runtime/app-static.mjs +86 -0
- package/runtime/app-stream-channel.mjs +92 -0
- package/runtime/app-stream-html.mjs +279 -0
- package/runtime/build-directory.mjs +48 -0
- package/runtime/cache-arguments.mjs +33 -0
- package/runtime/cache-components-rsc.mjs +64 -0
- package/runtime/cache-request.mjs +23 -0
- package/runtime/client-navigation.mjs +40 -0
- package/runtime/dev-client.mjs +129 -0
- package/runtime/dev-file-changes.mjs +110 -0
- package/runtime/document.mjs +96 -0
- package/runtime/edge.mjs +105 -0
- package/runtime/env.mjs +68 -0
- package/runtime/fetch-cache.mjs +379 -0
- package/runtime/font-preload.mjs +4 -0
- package/runtime/http.mjs +190 -0
- package/runtime/incremental-cache.mjs +83 -0
- package/runtime/metadata-route.mjs +83 -0
- package/runtime/middleware.mjs +186 -0
- package/runtime/module-loader.mjs +21 -0
- package/runtime/navigation.mjs +16 -0
- package/runtime/og-loader.mjs +17 -0
- package/runtime/pages-client.mjs +197 -0
- package/runtime/pages-error-props.mjs +6 -0
- package/runtime/pages-errors.mjs +85 -0
- package/runtime/pages-initial-props.mjs +43 -0
- package/runtime/pages-navigation.mjs +565 -0
- package/runtime/pages-paths.mjs +80 -0
- package/runtime/pages-revalidate.mjs +33 -0
- package/runtime/partial-artifact-cache.mjs +37 -0
- package/runtime/partial-template.mjs +174 -0
- package/runtime/profile.mjs +9 -0
- package/runtime/profiles.json +47 -0
- package/runtime/render.mjs +304 -0
- package/runtime/request-transport.mjs +41 -0
- package/runtime/request-work.mjs +7 -0
- package/runtime/route-static.mjs +117 -0
- package/runtime/rsc-worker.mjs +426 -0
- package/runtime/script-html.mjs +61 -0
- package/runtime/stream-select.mjs +25 -0
- package/runtime/stream-utils.mjs +127 -0
- package/runtime/transport.mjs +108 -0
- package/runtime/watch-project.mjs +130 -0
- package/runtime/worker-artifacts.mjs +22 -0
- package/runtime/worker-metadata.mjs +31 -0
- package/runtime/worker.mjs +197 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface NextFont {
|
|
2
|
+
className: string;
|
|
3
|
+
style: { fontFamily: string; fontWeight?: number; fontStyle?: string };
|
|
4
|
+
}
|
|
5
|
+
export interface NextFontWithVariable extends NextFont { variable: string }
|
|
6
|
+
export interface FontOptions<T extends string | undefined = undefined> {
|
|
7
|
+
display?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
|
|
8
|
+
preload?: boolean;
|
|
9
|
+
fallback?: string[];
|
|
10
|
+
variable?: T;
|
|
11
|
+
}
|
|
12
|
+
export interface LocalFontOptions<T extends string | undefined = undefined> extends FontOptions<T> {
|
|
13
|
+
src: string | Array<{ path: string; weight?: string; style?: string }>;
|
|
14
|
+
weight?: string;
|
|
15
|
+
style?: string;
|
|
16
|
+
adjustFontFallback?: 'Arial' | 'Times New Roman' | false;
|
|
17
|
+
declarations?: Array<{ prop: string; value: string }>;
|
|
18
|
+
}
|
|
19
|
+
export interface GoogleFontOptions<T extends string | undefined = undefined> extends FontOptions<T> {
|
|
20
|
+
weight?: string | string[];
|
|
21
|
+
style?: string | string[];
|
|
22
|
+
subsets?: string[];
|
|
23
|
+
axes?: string[];
|
|
24
|
+
adjustFontFallback?: boolean;
|
|
25
|
+
}
|
package/compat/head.cjs
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const React = require('react');
|
|
3
|
+
const HeadContext = React.createContext(null);
|
|
4
|
+
|
|
5
|
+
function flatten(children, result = []) {
|
|
6
|
+
React.Children.forEach(children, child => {
|
|
7
|
+
if (!React.isValidElement(child)) return;
|
|
8
|
+
if (child.type === React.Fragment) flatten(child.props.children, result);
|
|
9
|
+
else if (typeof child.type === 'string') result.push(child);
|
|
10
|
+
});
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function headKey(element) {
|
|
15
|
+
if (element.type === 'title' || element.type === 'base') return element.type;
|
|
16
|
+
if (element.key && !String(element.key).startsWith('.')) return `key:${element.key}`;
|
|
17
|
+
if (element.type === 'meta') {
|
|
18
|
+
for (const key of ['charSet', 'name', 'httpEquiv', 'property', 'itemProp']) {
|
|
19
|
+
if (element.props[key] !== undefined) return key === 'charSet' ? 'meta:charSet' : `meta:${key}:${element.props[key]}`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function dedupeHead(elements) {
|
|
26
|
+
const keys = new Set();
|
|
27
|
+
const tags = new Set();
|
|
28
|
+
const categories = new Map();
|
|
29
|
+
return elements.slice().reverse().filter(element => {
|
|
30
|
+
if (!React.isValidElement(element)) return false;
|
|
31
|
+
let key = element.key;
|
|
32
|
+
if (typeof key === 'string' && key.startsWith('.')) key = key.includes('$') ? key.slice(key.indexOf('$') + 1) : null;
|
|
33
|
+
const hasKey = key != null;
|
|
34
|
+
let unique = true;
|
|
35
|
+
if (hasKey) {
|
|
36
|
+
if (keys.has(key)) unique = false;
|
|
37
|
+
else keys.add(key);
|
|
38
|
+
}
|
|
39
|
+
if (element.type === 'title' || element.type === 'base') {
|
|
40
|
+
if (tags.has(element.type)) unique = false;
|
|
41
|
+
else tags.add(element.type);
|
|
42
|
+
} else if (element.type === 'meta') {
|
|
43
|
+
// Next treats explicit keys and metadata categories independently. In
|
|
44
|
+
// particular, a keyed viewport replaces the unkeyed default, while
|
|
45
|
+
// distinct keyed name tags and repeated Open Graph properties survive.
|
|
46
|
+
for (const name of ['name', 'httpEquiv', 'charSet', 'itemProp']) {
|
|
47
|
+
if (!Object.hasOwn(element.props, name)) continue;
|
|
48
|
+
const value = name === 'charSet' ? '' : element.props[name];
|
|
49
|
+
let seen = categories.get(name);
|
|
50
|
+
if (!seen) categories.set(name, seen = new Set());
|
|
51
|
+
if ((name !== 'name' || !hasKey) && seen.has(value)) unique = false;
|
|
52
|
+
else seen.add(value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return unique;
|
|
56
|
+
}).reverse();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function defaultHead() {
|
|
60
|
+
return [React.createElement('meta', { charSet: 'utf-8' }),
|
|
61
|
+
React.createElement('meta', { name: 'viewport', content: 'width=device-width' })];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function executableScript(node) {
|
|
65
|
+
return node.tagName === 'SCRIPT' && node.getAttribute('type')?.toLowerCase() !== 'application/ld+json';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function headNode(element) {
|
|
69
|
+
const jsonLd = element.type === 'script' && String(element.props.type).toLowerCase() === 'application/ld+json';
|
|
70
|
+
if (element.type === 'script' && !jsonLd) {
|
|
71
|
+
console.warn('PRNext Head does not apply executable script updates in the browser. Server-rendered scripts are preserved.');
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (!jsonLd && !['title', 'meta', 'link', 'base', 'style'].includes(element.type)) return null;
|
|
75
|
+
const node = document.createElement(element.type);
|
|
76
|
+
for (const [key, value] of Object.entries(element.props)) {
|
|
77
|
+
if (['children', 'dangerouslySetInnerHTML'].includes(key) || key.startsWith('on') || value == null || value === false) continue;
|
|
78
|
+
const attribute = ({ charSet: 'charset', httpEquiv: 'http-equiv', className: 'class', crossOrigin: 'crossorigin' })[key] || key;
|
|
79
|
+
node.setAttribute(attribute, value === true ? '' : String(value));
|
|
80
|
+
}
|
|
81
|
+
if (typeof element.props.children === 'string' || typeof element.props.children === 'number') node.textContent = String(element.props.children);
|
|
82
|
+
else if ((element.type === 'style' || jsonLd) && element.props.dangerouslySetInnerHTML?.__html !== undefined) node.textContent = element.props.dangerouslySetInnerHTML.__html;
|
|
83
|
+
node.setAttribute('data-prnext-head', '');
|
|
84
|
+
return node;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function pageHeadManager() {
|
|
88
|
+
const mounted = new Map();
|
|
89
|
+
let anchor;
|
|
90
|
+
let active = true;
|
|
91
|
+
function reconcile() {
|
|
92
|
+
if (!active) return;
|
|
93
|
+
const existing = [...document.head.querySelectorAll('[data-prnext-head]')].filter(node => !executableScript(node));
|
|
94
|
+
if (!anchor) {
|
|
95
|
+
anchor = document.createComment('prnext-head');
|
|
96
|
+
document.head.insertBefore(anchor, existing[0] || document.head.firstChild);
|
|
97
|
+
}
|
|
98
|
+
const entries = dedupeHead([...defaultHead(), ...[...mounted.values()].flat()]);
|
|
99
|
+
const wanted = entries.map(headNode).filter(Boolean).map(node => {
|
|
100
|
+
const index = existing.findIndex(old => old.isEqualNode(node));
|
|
101
|
+
return index < 0 ? node : existing.splice(index, 1)[0];
|
|
102
|
+
});
|
|
103
|
+
for (const node of existing) node.remove();
|
|
104
|
+
// Keep managed page tags before the Document's unmanaged children. Moving
|
|
105
|
+
// them to the end changes which of two title tags the browser observes.
|
|
106
|
+
let cursor = anchor;
|
|
107
|
+
for (let index = wanted.length - 1; index >= 0; index--) {
|
|
108
|
+
const node = wanted[index];
|
|
109
|
+
if (node.nextSibling !== cursor) document.head.insertBefore(node, cursor);
|
|
110
|
+
cursor = node;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
set(owner, entries) { mounted.set(owner, entries); reconcile(); },
|
|
115
|
+
delete(owner) { mounted.delete(owner); reconcile(); },
|
|
116
|
+
activate() { active = true; reconcile(); },
|
|
117
|
+
dispose() { active = false; mounted.clear(); anchor?.remove(); anchor = undefined; },
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function HeadProvider({ collector, restoreDefaults = false, children }) {
|
|
122
|
+
const owned = React.useRef([]);
|
|
123
|
+
const manager = React.useRef(null);
|
|
124
|
+
if (restoreDefaults && !manager.current) manager.current = pageHeadManager();
|
|
125
|
+
const value = React.useMemo(() => ({ collector: collector || owned.current, manager: restoreDefaults ? manager.current : null }), [collector, restoreDefaults]);
|
|
126
|
+
React.useEffect(() => {
|
|
127
|
+
if (!restoreDefaults) return;
|
|
128
|
+
manager.current.activate();
|
|
129
|
+
return () => manager.current.dispose();
|
|
130
|
+
}, [restoreDefaults]);
|
|
131
|
+
return React.createElement(HeadContext.Provider, { value }, children);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function Head({ children }) {
|
|
135
|
+
const value = React.useContext(HeadContext);
|
|
136
|
+
const collector = Array.isArray(value) ? value : value?.collector;
|
|
137
|
+
const manager = value?.manager;
|
|
138
|
+
const owner = React.useId();
|
|
139
|
+
const entries = flatten(children).map(element => React.cloneElement(element, { 'data-prnext-head-owner': owner }));
|
|
140
|
+
if (typeof document === 'undefined') {
|
|
141
|
+
if (!collector) throw new Error('Head must be used inside the PRNext head provider');
|
|
142
|
+
collector.push(...entries);
|
|
143
|
+
}
|
|
144
|
+
React.useEffect(() => {
|
|
145
|
+
if (manager) {
|
|
146
|
+
manager.set(owner, entries);
|
|
147
|
+
return () => manager.delete(owner);
|
|
148
|
+
}
|
|
149
|
+
// Hydration starts with the server head. Apply component updates without executing scripts.
|
|
150
|
+
const installed = [];
|
|
151
|
+
for (const existing of document.head.querySelectorAll('[data-prnext-head-owner]')) {
|
|
152
|
+
if (existing.getAttribute('data-prnext-head-owner') !== owner) continue;
|
|
153
|
+
// Executable scripts emitted by SSR already ran. Leave them in place;
|
|
154
|
+
// reinserting them during hydration would run application code twice.
|
|
155
|
+
if (executableScript(existing)) continue;
|
|
156
|
+
existing.remove();
|
|
157
|
+
}
|
|
158
|
+
for (const element of dedupeHead(entries)) {
|
|
159
|
+
const node = headNode(element);
|
|
160
|
+
if (!node) continue;
|
|
161
|
+
const identity = headKey(element);
|
|
162
|
+
if (identity) {
|
|
163
|
+
for (const existing of document.head.querySelectorAll('[data-prnext-head]')) {
|
|
164
|
+
if (element.type === 'title' && existing.tagName === 'TITLE') existing.remove();
|
|
165
|
+
else if (element.type === 'meta' && existing.tagName === 'META' && ['name', 'property', 'http-equiv', 'charset', 'itemprop'].some(attribute => node.hasAttribute(attribute) && existing.getAttribute(attribute) === node.getAttribute(attribute))) existing.remove();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
document.head.appendChild(node);
|
|
169
|
+
installed.push(node);
|
|
170
|
+
}
|
|
171
|
+
return () => { for (const node of installed) node.remove(); };
|
|
172
|
+
}, [children, owner, manager]);
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = Head;
|
|
177
|
+
module.exports.default = Head;
|
|
178
|
+
module.exports.HeadProvider = HeadProvider;
|
|
179
|
+
module.exports.HeadContext = HeadContext;
|
|
180
|
+
module.exports.dedupeHead = dedupeHead;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const {sampleMethods} = require('./instant-samples.cjs');
|
|
3
|
+
const { AsyncLocalStorage } = require('node:async_hooks');
|
|
4
|
+
const { CookieStore, parseCookieHeader, serializeCookie } = require('./cookies.cjs');
|
|
5
|
+
const { createCacheState, inCacheScope, currentCacheScope } = require('./data-cache.cjs');
|
|
6
|
+
const { createStaticState, dynamicUsage, forceStaticRender } = require('./static-generation.cjs');
|
|
7
|
+
const { isDraftRequest, draftCookie } = require('./draft.cjs');
|
|
8
|
+
const { readPreviewData } = require('./preview.cjs');
|
|
9
|
+
const { staticBailout } = require('./static-generation.cjs');
|
|
10
|
+
|
|
11
|
+
const requestStorage = new AsyncLocalStorage();
|
|
12
|
+
function currentRequest() {
|
|
13
|
+
const context = requestStorage.getStore();
|
|
14
|
+
if (!context) throw new Error('Request APIs can only be used while handling a PRNext request');
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
function runWithoutRequestContext(callback) { return requestStorage.exit(callback); }
|
|
18
|
+
|
|
19
|
+
function readonlyHeaders(values) {
|
|
20
|
+
const headers = new Headers(values);
|
|
21
|
+
const proxy = new Proxy(headers, { get(target, property) {
|
|
22
|
+
if (['set', 'append', 'delete'].includes(property)) return () => { throw new Error('headers() is read-only'); };
|
|
23
|
+
if (property === 'forEach') return (callback, thisArg) => target.forEach((value, name) => callback.call(thisArg, value, name, proxy));
|
|
24
|
+
const value = Reflect.get(target, property, target);
|
|
25
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
26
|
+
} });
|
|
27
|
+
return proxy;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function runRequestContext(input, callback) {
|
|
31
|
+
const lazy = input.phase === 'pages' && (!input.headers || [Object.prototype, null].includes(Object.getPrototypeOf(input.headers)));
|
|
32
|
+
// Snapshot values now, preserving readonly request semantics even when the
|
|
33
|
+
// caller subsequently mutates its input. Pages APIs normally never need the
|
|
34
|
+
// Web Headers/CookieStore objects, so construct those only on actual access.
|
|
35
|
+
const raw = lazy ? Object.create(null) : undefined;
|
|
36
|
+
if (lazy && !forceStaticRender(input)) for (const name of Object.keys(input.headers || {})) raw[name] = String(input.headers[name]);
|
|
37
|
+
let headers = lazy ? undefined : readonlyHeaders(forceStaticRender(input) ? {} : input.headers || {});
|
|
38
|
+
const getHeaders = () => headers ??= readonlyHeaders(raw);
|
|
39
|
+
const outgoingCookies = new Map();
|
|
40
|
+
const mutableCookies = Boolean(input.mutableCookies) && !forceStaticRender(input);
|
|
41
|
+
const makeCookies = () => new CookieStore(getHeaders().get('cookie') || '', {
|
|
42
|
+
mutable: mutableCookies,
|
|
43
|
+
onChange(cookie) { outgoingCookies.set(cookie.name, serializeCookie(cookie)); },
|
|
44
|
+
});
|
|
45
|
+
let cookies = lazy ? undefined : makeCookies();
|
|
46
|
+
const getCookies = () => cookies ??= makeCookies();
|
|
47
|
+
// Next merges this trusted middleware response marker only into App render
|
|
48
|
+
// and action cookies. Request headers, Route Handlers and Pages APIs retain
|
|
49
|
+
// their original Cookie header. Native ingress strips spoofed markers.
|
|
50
|
+
if (['render', 'action'].includes(input.phase) && !forceStaticRender(input)) {
|
|
51
|
+
const marker = headers.get('x-middleware-set-cookie');
|
|
52
|
+
for (const line of marker?.split(/,(?=\s*[^;,=\s]+\s*=)/) || []) {
|
|
53
|
+
for (const [name, cookie] of parseCookieHeader(line.split(';', 1)[0])) cookies._cookies.set(name, cookie);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const context = { ...input, previewData: readPreviewData(input), draftMode: isDraftRequest(input), basePath: input.basePath ?? input.manifest?.config?.basePath ?? '', cacheState: input.cacheState || createCacheState(),
|
|
57
|
+
staticState: input.staticState || (input.staticGeneration ? createStaticState(input.staticGeneration, input.cacheConfig) : undefined),
|
|
58
|
+
headers, cookies, outgoingCookies };
|
|
59
|
+
if (lazy) Object.defineProperties(context, {
|
|
60
|
+
headers: { enumerable: true, configurable: true, get: getHeaders, set(value) { headers = value; } },
|
|
61
|
+
cookies: { enumerable: true, configurable: true, get: getCookies, set(value) { cookies = value; } },
|
|
62
|
+
});
|
|
63
|
+
return requestStorage.run(context, callback);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function assertUncached(name) {
|
|
67
|
+
if (inCacheScope()) throw new Error(`${name}() cannot be accessed inside unstable_cache; read it outside and pass the required values as arguments`);
|
|
68
|
+
}
|
|
69
|
+
async function headers() { if (currentRequest().instantValidation && currentRequest().instantSample) { assertUncached('headers'); return sampleMethods(currentRequest(), 'headers', currentRequest().headers); } if (currentCacheScope()?.kind !== 'private') assertUncached('headers'); dynamicUsage('headers()'); return currentRequest().headers; }
|
|
70
|
+
async function cookies() { if (currentRequest().instantValidation && currentRequest().instantSample) { assertUncached('cookies'); return sampleMethods(currentRequest(), 'cookies', currentRequest().cookies); } if (currentCacheScope()?.kind !== 'private') assertUncached('cookies'); dynamicUsage('cookies()'); return currentRequest().cookies; }
|
|
71
|
+
async function draftMode() {
|
|
72
|
+
const context = currentRequest();
|
|
73
|
+
if (!context.draftProvider) context.draftProvider = {
|
|
74
|
+
get isEnabled() { return context.draftMode; },
|
|
75
|
+
enable() { change(true); },
|
|
76
|
+
disable() { change(false); },
|
|
77
|
+
};
|
|
78
|
+
function change(enabled) {
|
|
79
|
+
assertUncached('draftMode().enable()/disable()');
|
|
80
|
+
if (context.staticState) staticBailout('draftMode().enable()/disable()', context);
|
|
81
|
+
// CookieStore rejects render-phase writes and writes after headers commit.
|
|
82
|
+
context.cookies.set(draftCookie(context, enabled));
|
|
83
|
+
context.draftMode = enabled;
|
|
84
|
+
context.draftChanged = true;
|
|
85
|
+
}
|
|
86
|
+
return context.draftProvider;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = { headers, cookies, draftMode, runRequestContext, currentRequest, runWithoutRequestContext };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface CookieOptions {
|
|
2
|
+
path?: string;
|
|
3
|
+
domain?: string;
|
|
4
|
+
expires?: Date | number;
|
|
5
|
+
maxAge?: number;
|
|
6
|
+
httpOnly?: boolean;
|
|
7
|
+
secure?: boolean;
|
|
8
|
+
sameSite?: boolean | 'lax' | 'strict' | 'none';
|
|
9
|
+
priority?: 'low' | 'medium' | 'high';
|
|
10
|
+
partitioned?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface Cookie extends CookieOptions { name: string; value: string }
|
|
13
|
+
export interface CookieStore extends Iterable<[string, Cookie]> {
|
|
14
|
+
readonly size: number;
|
|
15
|
+
get(name: string | { name: string }): Cookie | undefined;
|
|
16
|
+
getAll(name?: string | { name: string }): Cookie[];
|
|
17
|
+
has(name: string): boolean;
|
|
18
|
+
set(name: string, value: string, options?: CookieOptions): CookieStore;
|
|
19
|
+
set(cookie: Cookie): CookieStore;
|
|
20
|
+
delete(name: string | { name: string; path?: string; domain?: string }): CookieStore;
|
|
21
|
+
toString(): string;
|
|
22
|
+
}
|
|
23
|
+
export function headers(): Promise<Omit<Headers, 'set' | 'append' | 'delete'>>;
|
|
24
|
+
export function cookies(): Promise<CookieStore>;
|
|
25
|
+
export interface DraftMode { readonly isEnabled: boolean; enable(): void; disable(): void }
|
|
26
|
+
export function draftMode(): Promise<DraftMode>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// The compiler replaces this module per build; importing PRNext itself remains usable.
|
|
3
|
+
module.exports = { deviceSizes:[640,750,828,1080,1200,1920,2048,3840],imageSizes:[32,48,64,96,128,256,384],path:'/_prnext/image',qualities:[75],remotePatterns:[],domains:[],unoptimized:false,loader:'default' };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Static imports emitted by the PRNext image compiler.
|
|
2
|
+
declare module '*.png' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
3
|
+
declare module '*.jpg' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
4
|
+
declare module '*.jpeg' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
5
|
+
declare module '*.gif' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
6
|
+
declare module '*.webp' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
7
|
+
declare module '*.avif' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
8
|
+
declare module '*.ico' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
9
|
+
declare module '*.bmp' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
10
|
+
declare module '*.tiff' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
11
|
+
declare module '*.tif' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
12
|
+
declare module '*.svg' { const image: import('./image.cjs').StaticImageData; export default image; }
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const config = require('./image-config.cjs');
|
|
3
|
+
const allSizes = [...new Set([...config.deviceSizes, ...config.imageSizes])].sort((a,b) => a-b);
|
|
4
|
+
function widths(width, sizes) {
|
|
5
|
+
if (sizes) {
|
|
6
|
+
const percent = [...sizes.matchAll(/(^|\s)(1?\d?\d)vw/g)].map(match => Number(match[2]));
|
|
7
|
+
return { values: percent.length ? allSizes.filter(size => size >= config.deviceSizes[0] * Math.min(...percent) / 100) : allSizes, kind: 'w' };
|
|
8
|
+
}
|
|
9
|
+
if (!width) return { values: config.deviceSizes, kind: 'w' };
|
|
10
|
+
return { values: [...new Set([width, width * 2].map(size => allSizes.find(value => value >= size) || allSizes.at(-1)))], kind: 'x' };
|
|
11
|
+
}
|
|
12
|
+
function defaultLoader({ src, width, quality }) {
|
|
13
|
+
if (config.loader === 'custom') throw new Error(`Image with src "${src}" requires a loader prop or images.loaderFile`);
|
|
14
|
+
const qualities = config.qualities || [75];
|
|
15
|
+
quality = qualities.reduce((best, value) => Math.abs(value - (quality || 75)) < Math.abs(best - (quality || 75)) ? value : best);
|
|
16
|
+
return `${config.path}?url=${encodeURIComponent(src)}&w=${width}&q=${quality}`;
|
|
17
|
+
}
|
|
18
|
+
function integer(value) { return typeof value === 'number' ? value : typeof value === 'string' && /^\d+$/.test(value) ? Number(value) : undefined; }
|
|
19
|
+
function imageProps(input) {
|
|
20
|
+
const { src, alt, width, height, fill = false, priority, preload, quality, loader: inputLoader, unoptimized: rawUnoptimized, placeholder = 'empty', blurDataURL: inputBlur,
|
|
21
|
+
blurWidth: _blurWidth, blurHeight: _blurHeight, onLoadingComplete, onLoad, onError, style, sizes: inputSizes, overrideSrc, layout, objectFit, objectPosition, lazyBoundary: _lazyBoundary, lazyRoot: _lazyRoot, ...rest } = input;
|
|
22
|
+
const source = typeof src === 'object' && src !== null ? src.default || src : { src };
|
|
23
|
+
if (!source || typeof source.src !== 'string') throw new Error('Image src must be a URL string or a static image import');
|
|
24
|
+
let w = integer(width), h = integer(height);
|
|
25
|
+
const isFill = fill || layout === 'fill';
|
|
26
|
+
if (!isFill && source.width && source.height) {
|
|
27
|
+
if (!w && !h) { w = source.width; h = source.height; }
|
|
28
|
+
else if (w && !h) h = Math.round(source.height * w / source.width);
|
|
29
|
+
else if (h && !w) w = Math.round(source.width * h / source.height);
|
|
30
|
+
}
|
|
31
|
+
if (!isFill && (!w || !h || w < 0 || h < 0)) throw new Error(`Image with src "${source.src}" requires positive width and height, or fill`);
|
|
32
|
+
const unoptimized = rawUnoptimized || config.unoptimized || /^(?:data:|blob:)/.test(source.src) || (!config.dangerouslyAllowSVG && source.src.split('?')[0].endsWith('.svg'));
|
|
33
|
+
const sizes = inputSizes || (isFill || layout === 'responsive' ? '100vw' : undefined);
|
|
34
|
+
const loader = inputLoader || config.customLoader || defaultLoader;
|
|
35
|
+
const qualityNumber = integer(quality);
|
|
36
|
+
const candidates = widths(w, sizes);
|
|
37
|
+
const attributes = unoptimized ? { src: source.src } : {
|
|
38
|
+
sizes: sizes || (candidates.kind === 'w' ? '100vw' : undefined),
|
|
39
|
+
srcSet: candidates.values.map((value, index) => `${loader({ src: source.src, width: value, quality: qualityNumber })} ${candidates.kind === 'w' ? value : index + 1}${candidates.kind}`).join(', '),
|
|
40
|
+
src: loader({ src: source.src, width: candidates.values.at(-1), quality: qualityNumber }),
|
|
41
|
+
};
|
|
42
|
+
const blurDataURL = inputBlur || source.blurDataURL;
|
|
43
|
+
if (placeholder === 'blur' && !blurDataURL) throw new Error(`Image with src "${source.src}" uses placeholder="blur" without blurDataURL`);
|
|
44
|
+
const baseStyle = isFill ? { position: 'absolute', height: '100%', width: '100%', left: 0, top: 0, right: 0, bottom: 0, objectFit, objectPosition } : { objectFit, objectPosition };
|
|
45
|
+
if (layout === 'responsive') Object.assign(baseStyle, { width: '100%', height: 'auto' });
|
|
46
|
+
if (layout === 'intrinsic') Object.assign(baseStyle, { maxWidth: '100%', height: 'auto' });
|
|
47
|
+
return { props: { ...rest, alt, width: isFill ? undefined : w, height: isFill ? undefined : h,
|
|
48
|
+
loading: rest.loading || (priority || preload || /^(?:data:|blob:)/.test(source.src) ? undefined : 'lazy'), decoding: rest.decoding || 'async',
|
|
49
|
+
'data-nimg': isFill ? 'fill' : '1', style: { color: 'transparent', ...baseStyle, ...style }, ...attributes, src: overrideSrc || attributes.src },
|
|
50
|
+
placeholder, blurDataURL, priority: priority || preload, onLoad, onError, onLoadingComplete };
|
|
51
|
+
}
|
|
52
|
+
function getImageProps(input) {
|
|
53
|
+
const result = imageProps(input);
|
|
54
|
+
if (result.placeholder !== 'empty') Object.assign(result.props.style, placeholderStyle(result));
|
|
55
|
+
return { props: Object.fromEntries(Object.entries(result.props).filter(([, value]) => value !== undefined)) };
|
|
56
|
+
}
|
|
57
|
+
function placeholderStyle(result) {
|
|
58
|
+
if (result.placeholder === "empty") return {};
|
|
59
|
+
const url = result.placeholder === 'blur' ? result.blurDataURL : result.placeholder;
|
|
60
|
+
return url ? { backgroundSize: result.props.style.objectFit === 'contain' ? 'contain' : 'cover', backgroundPosition: result.props.style.objectPosition || '50% 50%', backgroundRepeat: 'no-repeat', backgroundImage: `url("${url.replaceAll('"', '%22')}")` } : {};
|
|
61
|
+
}
|
|
62
|
+
module.exports={imageProps,getImageProps,placeholderStyle};
|
package/compat/image.cjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
const React = require('react');
|
|
4
|
+
const ReactDOM = require('react-dom');
|
|
5
|
+
const {imageProps,getImageProps,placeholderStyle}=require('./image-shared.cjs');
|
|
6
|
+
const useBrowserLayoutEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
|
|
7
|
+
const Image = React.forwardRef(function Image(input, ref) {
|
|
8
|
+
const result = imageProps(input);
|
|
9
|
+
const [complete, setComplete] = React.useState(false);
|
|
10
|
+
const [failed, setFailed] = React.useState(false);
|
|
11
|
+
const internalRef = React.useRef(null);
|
|
12
|
+
const retriedRef = React.useRef(null);
|
|
13
|
+
const latest = React.useRef(result);
|
|
14
|
+
latest.current = result;
|
|
15
|
+
const loaded = React.useCallback(image => {
|
|
16
|
+
if (!image || image.naturalWidth === 0 || image.dataset.loadedSrc === image.src) return;
|
|
17
|
+
const source = image.src;
|
|
18
|
+
image.dataset.loadedSrc = image.src;
|
|
19
|
+
Promise.resolve(image.decode?.()).catch(() => {}).then(() => {
|
|
20
|
+
if (!image.isConnected || image.naturalWidth === 0 || image.src !== source) return;
|
|
21
|
+
setComplete(true);
|
|
22
|
+
const { onLoad, onLoadingComplete } = latest.current;
|
|
23
|
+
if (onLoad) {
|
|
24
|
+
const event = new Event('load');
|
|
25
|
+
Object.defineProperty(event, 'target', { value: image });
|
|
26
|
+
onLoad({ ...event, type: 'load', nativeEvent: event, currentTarget: image, target: image, persist() {}, isDefaultPrevented: () => event.defaultPrevented, isPropagationStopped: () => false, preventDefault: () => event.preventDefault(), stopPropagation: () => event.stopPropagation() });
|
|
27
|
+
}
|
|
28
|
+
onLoadingComplete?.(image);
|
|
29
|
+
});
|
|
30
|
+
}, []);
|
|
31
|
+
const attach = React.useCallback(image => {
|
|
32
|
+
internalRef.current = image;
|
|
33
|
+
const cleanup = typeof ref === 'function' ? ref(image) : undefined;
|
|
34
|
+
if (ref && typeof ref !== 'function') ref.current = image;
|
|
35
|
+
if (image?.complete) loaded(image);
|
|
36
|
+
if (image) return () => {
|
|
37
|
+
internalRef.current = null;
|
|
38
|
+
if (typeof cleanup === 'function') cleanup();
|
|
39
|
+
else if (typeof ref === 'function') ref(null);
|
|
40
|
+
else if (ref) ref.current = null;
|
|
41
|
+
};
|
|
42
|
+
}, [ref, loaded]);
|
|
43
|
+
useBrowserLayoutEffect(() => {
|
|
44
|
+
const image = internalRef.current;
|
|
45
|
+
if (!image?.complete || image.naturalWidth !== 0 || !image.getAttribute('src')) return;
|
|
46
|
+
// Native error events that precede hydration are not replayed by React.
|
|
47
|
+
// Retry after handlers are attached so onError receives a real image event.
|
|
48
|
+
if (latest.current.onError && retriedRef.current !== image) {
|
|
49
|
+
retriedRef.current = image;
|
|
50
|
+
image.src = image.src;
|
|
51
|
+
} else {
|
|
52
|
+
setComplete(true);
|
|
53
|
+
setFailed(true);
|
|
54
|
+
}
|
|
55
|
+
}, []);
|
|
56
|
+
if (result.priority && ReactDOM.preload) ReactDOM.preload(result.props.src, { as: 'image', imageSrcSet: result.props.srcSet, imageSizes: result.props.sizes, crossOrigin: result.props.crossOrigin, referrerPolicy: result.props.referrerPolicy, fetchPriority: result.props.fetchPriority });
|
|
57
|
+
return React.createElement('img', { ...result.props,
|
|
58
|
+
style: { ...result.props.style, ...(complete ? {} : placeholderStyle(result)), ...(failed ? { color: undefined } : {}) }, ref: attach,
|
|
59
|
+
onLoad: event => loaded(event.currentTarget), onError: event => { setComplete(true); setFailed(true); result.onError?.(event); },
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
module.exports = Image;
|
|
63
|
+
module.exports.default = Image;
|
|
64
|
+
module.exports.getImageProps = getImageProps;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/// <reference path="./image-imports.d.ts" />
|
|
2
|
+
import type { ImgHTMLAttributes, ForwardRefExoticComponent, RefAttributes } from 'react';
|
|
3
|
+
export interface ImageLoaderProps { src: string; width: number; quality?: number }
|
|
4
|
+
export type ImageLoader = (props: ImageLoaderProps) => string;
|
|
5
|
+
export interface RemotePattern { protocol?: 'http' | 'https'; hostname: string; port?: string; pathname?: string; search?: string }
|
|
6
|
+
export interface LocalPattern { pathname?: string; search?: string }
|
|
7
|
+
export interface ImageConfig {
|
|
8
|
+
deviceSizes?: number[]; imageSizes?: number[]; qualities?: number[];
|
|
9
|
+
loader?: 'default' | 'custom'; loaderFile?: string; path?: string;
|
|
10
|
+
domains?: string[]; remotePatterns?: Array<RemotePattern | URL>; localPatterns?: LocalPattern[];
|
|
11
|
+
formats?: Array<'image/avif' | 'image/webp'>; unoptimized?: boolean; disableStaticImages?: boolean;
|
|
12
|
+
minimumCacheTTL?: number; maximumDiskCacheSize?: number; maximumRedirects?: number; maximumResponseBody?: number;
|
|
13
|
+
dangerouslyAllowLocalIP?: boolean; dangerouslyAllowSVG?: boolean;
|
|
14
|
+
contentSecurityPolicy?: string; contentDispositionType?: 'attachment' | 'inline';
|
|
15
|
+
}
|
|
16
|
+
export interface StaticImageData { src: string; width: number; height: number; blurDataURL?: string; blurWidth?: number; blurHeight?: number }
|
|
17
|
+
export interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'width' | 'height'> {
|
|
18
|
+
src: string | StaticImageData | { default: StaticImageData };
|
|
19
|
+
alt: string;
|
|
20
|
+
width?: number | `${number}`;
|
|
21
|
+
height?: number | `${number}`;
|
|
22
|
+
fill?: boolean;
|
|
23
|
+
priority?: boolean;
|
|
24
|
+
preload?: boolean;
|
|
25
|
+
quality?: number | `${number}`;
|
|
26
|
+
overrideSrc?: string;
|
|
27
|
+
loader?: ImageLoader;
|
|
28
|
+
unoptimized?: boolean;
|
|
29
|
+
placeholder?: 'empty' | 'blur' | string;
|
|
30
|
+
blurDataURL?: string;
|
|
31
|
+
onLoadingComplete?: (image: HTMLImageElement) => void;
|
|
32
|
+
}
|
|
33
|
+
declare const Image: ForwardRefExoticComponent<ImageProps & RefAttributes<HTMLImageElement>>;
|
|
34
|
+
export function getImageProps(props: ImageProps): { props: ImgHTMLAttributes<HTMLImageElement> };
|
|
35
|
+
export default Image;
|