canopycms-next 0.0.67-int.89 → 0.0.67-int.91
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/dist/adapter.d.ts +0 -8
- package/dist/adapter.js +11 -31
- package/dist/client.d.ts +5 -7
- package/dist/client.js +8 -11
- package/dist/config.cjs +262 -4
- package/dist/config.mjs +262 -4
- package/dist/context-wrapper.d.ts +14 -20
- package/dist/context-wrapper.js +22 -37
- package/dist/sharp-tracing.d.ts +64 -0
- package/dist/sharp-tracing.js +320 -0
- package/dist/static.d.ts +30 -56
- package/dist/static.js +2 -7
- package/dist/test-utils.d.ts +0 -4
- package/dist/test-utils.js +0 -4
- package/dist/with-canopy.d.ts +20 -32
- package/dist/with-canopy.js +205 -34
- package/package.json +3 -3
package/dist/adapter.d.ts
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
import { type CanopyHandlerOptions, type CanopyRequest } from 'canopycms/http';
|
|
2
2
|
/**
|
|
3
|
-
* Options for creating a Canopy Next.js handler.
|
|
4
3
|
* Same as core CanopyHandlerOptions - re-exported for convenience.
|
|
5
4
|
*/
|
|
6
5
|
export type CanopyNextOptions = CanopyHandlerOptions;
|
|
7
6
|
/**
|
|
8
|
-
* Wrap a standard Request (or NextRequest) to implement the CanopyRequest interface.
|
|
9
7
|
* Only uses standard Request methods, so any Request subclass works.
|
|
10
8
|
*/
|
|
11
9
|
export declare function wrapNextRequest(req: Request): CanopyRequest;
|
|
12
10
|
/**
|
|
13
11
|
* Catch-all Next.js handler for a single API route (e.g., /api/canopycms/[...canopycms]).
|
|
14
12
|
*
|
|
15
|
-
* This is a thin adapter that:
|
|
16
|
-
* 1. Converts NextRequest to CanopyRequest
|
|
17
|
-
* 2. Extracts path segments from Next.js params
|
|
18
|
-
* 3. Delegates to the core handler
|
|
19
|
-
* 4. Converts CanopyResponse to NextResponse
|
|
20
|
-
*
|
|
21
13
|
* @example
|
|
22
14
|
* ```ts
|
|
23
15
|
* // app/api/canopycms/[...canopycms]/route.ts
|
package/dist/adapter.js
CHANGED
|
@@ -2,7 +2,6 @@ import { NextResponse } from 'next/server';
|
|
|
2
2
|
import { createCanopyRequestHandler, isCanopyBinaryResponse, } from 'canopycms/http';
|
|
3
3
|
import { getErrorMessage, redactCredentials, sanitizeErrorMessage } from 'canopycms/utils/error';
|
|
4
4
|
/**
|
|
5
|
-
* Wrap a standard Request (or NextRequest) to implement the CanopyRequest interface.
|
|
6
5
|
* Only uses standard Request methods, so any Request subclass works.
|
|
7
6
|
*/
|
|
8
7
|
export function wrapNextRequest(req) {
|
|
@@ -30,10 +29,6 @@ export function wrapNextRequest(req) {
|
|
|
30
29
|
},
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Map a CanopyBinaryResponse's framework-agnostic header fields onto the
|
|
35
|
-
* real HTTP header names, omitting any that weren't set.
|
|
36
|
-
*/
|
|
37
32
|
function toBinaryHeaders(headers) {
|
|
38
33
|
const result = {};
|
|
39
34
|
if (headers.contentType)
|
|
@@ -46,17 +41,12 @@ function toBinaryHeaders(headers) {
|
|
|
46
41
|
result['ETag'] = headers.etag;
|
|
47
42
|
return result;
|
|
48
43
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Convert a CanopyResponse (or CanopyBinaryResponse) to a NextResponse.
|
|
51
|
-
*/
|
|
52
44
|
function toNextResponse(response) {
|
|
53
45
|
if (isCanopyBinaryResponse(response)) {
|
|
54
|
-
// A Uint8Array can be backed by an arbitrary ArrayBufferLike (e.g. a
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
// view so this type-checks without an unsafe cast; ReadableStream
|
|
59
|
-
// bodies pass through unchanged.
|
|
46
|
+
// A Uint8Array can be backed by an arbitrary ArrayBufferLike (e.g. a Node Buffer), which
|
|
47
|
+
// doesn't structurally satisfy the DOM lib's BodyInit/ArrayBufferView. Copying through the
|
|
48
|
+
// typed-array constructor yields a plain ArrayBuffer-backed view that type-checks without an
|
|
49
|
+
// unsafe cast; ReadableStream bodies pass through unchanged.
|
|
60
50
|
const body = response.body instanceof Uint8Array ? new Uint8Array(response.body) : response.body;
|
|
61
51
|
return new NextResponse(body, {
|
|
62
52
|
status: response.status,
|
|
@@ -69,7 +59,6 @@ function toNextResponse(response) {
|
|
|
69
59
|
});
|
|
70
60
|
}
|
|
71
61
|
/**
|
|
72
|
-
* Extract path segments from Next.js catch-all route params.
|
|
73
62
|
* Handles both Next.js 14 (direct object) and Next.js 15 (Promise) params.
|
|
74
63
|
*/
|
|
75
64
|
async function extractPathSegments(ctx) {
|
|
@@ -81,12 +70,6 @@ async function extractPathSegments(ctx) {
|
|
|
81
70
|
/**
|
|
82
71
|
* Catch-all Next.js handler for a single API route (e.g., /api/canopycms/[...canopycms]).
|
|
83
72
|
*
|
|
84
|
-
* This is a thin adapter that:
|
|
85
|
-
* 1. Converts NextRequest to CanopyRequest
|
|
86
|
-
* 2. Extracts path segments from Next.js params
|
|
87
|
-
* 3. Delegates to the core handler
|
|
88
|
-
* 4. Converts CanopyResponse to NextResponse
|
|
89
|
-
*
|
|
90
73
|
* @example
|
|
91
74
|
* ```ts
|
|
92
75
|
* // app/api/canopycms/[...canopycms]/route.ts
|
|
@@ -116,17 +99,14 @@ export const createCanopyCatchAllHandler = (options) => {
|
|
|
116
99
|
return toNextResponse(response);
|
|
117
100
|
}
|
|
118
101
|
catch (err) {
|
|
119
|
-
// Defense-in-depth (API-C1): coreHandler already guards itself with a
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
// break the uniform { ok, status, error } envelope the editor expects.
|
|
102
|
+
// Defense-in-depth (API-C1): coreHandler already guards itself with a top-level try/catch,
|
|
103
|
+
// but this adapter also wraps request conversion, so a failure there can never escape as
|
|
104
|
+
// Next's generic unhandled-error 500, which would break the uniform { ok, status, error }
|
|
105
|
+
// envelope the editor expects.
|
|
124
106
|
const message = getErrorMessage(err);
|
|
125
|
-
// Redacted before logging, not just before responding
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// one thing that must not reach a log aggregator. Every failure surface
|
|
129
|
-
// in the worker already redacts; this was the outlier.
|
|
107
|
+
// Redacted before logging, not just before responding: the HTTP body is already sanitized,
|
|
108
|
+
// but this line reaches the server log verbatim, and a git failure message can embed a
|
|
109
|
+
// token-bearing clone URL — the one thing that must not reach a log aggregator.
|
|
130
110
|
console.error('CanopyCMS: Unhandled error in Next.js catch-all handler:', redactCredentials(message));
|
|
131
111
|
return toNextResponse({
|
|
132
112
|
status: 500,
|
package/dist/client.d.ts
CHANGED
|
@@ -3,13 +3,11 @@ import type { CanopyClientConfig, CustomFieldRenderers } from 'canopycms/client'
|
|
|
3
3
|
* Next.js-specific wrapper for CanopyEditorPage that automatically reads
|
|
4
4
|
* URL search params (branch, entry) using Next.js's useSearchParams hook.
|
|
5
5
|
*
|
|
6
|
-
* @param customRenderers Optional per-field-type render overrides, forwarded
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the
|
|
11
|
-
* unreachable from the path every adopter uses, which is an extension point
|
|
12
|
-
* in name only.
|
|
6
|
+
* @param customRenderers Optional per-field-type render overrides, forwarded to
|
|
7
|
+
* `CanopyEditorPage`. Forwarded (not dropped) because Next is the primary target: this wrapper
|
|
8
|
+
* is the entrypoint every adopter imports (the README's Quick Start scaffolds it), so accepting
|
|
9
|
+
* the argument only on core `CanopyEditorPage` would make the extension point unreachable from
|
|
10
|
+
* the path adopters actually use.
|
|
13
11
|
*
|
|
14
12
|
* @example
|
|
15
13
|
* ```tsx
|
package/dist/client.js
CHANGED
|
@@ -7,13 +7,11 @@ import { CanopyEditorPage } from 'canopycms/client';
|
|
|
7
7
|
* Next.js-specific wrapper for CanopyEditorPage that automatically reads
|
|
8
8
|
* URL search params (branch, entry) using Next.js's useSearchParams hook.
|
|
9
9
|
*
|
|
10
|
-
* @param customRenderers Optional per-field-type render overrides, forwarded
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* the
|
|
15
|
-
* unreachable from the path every adopter uses, which is an extension point
|
|
16
|
-
* in name only.
|
|
10
|
+
* @param customRenderers Optional per-field-type render overrides, forwarded to
|
|
11
|
+
* `CanopyEditorPage`. Forwarded (not dropped) because Next is the primary target: this wrapper
|
|
12
|
+
* is the entrypoint every adopter imports (the README's Quick Start scaffolds it), so accepting
|
|
13
|
+
* the argument only on core `CanopyEditorPage` would make the extension point unreachable from
|
|
14
|
+
* the path adopters actually use.
|
|
17
15
|
*
|
|
18
16
|
* @example
|
|
19
17
|
* ```tsx
|
|
@@ -31,10 +29,9 @@ import { CanopyEditorPage } from 'canopycms/client';
|
|
|
31
29
|
*/
|
|
32
30
|
export const NextCanopyEditorPage = (config, customRenderers) => {
|
|
33
31
|
const CorePage = CanopyEditorPage(config, customRenderers);
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// boundary here so adopters don't have to wrap their /edit page themselves.
|
|
32
|
+
// Next.js 15 requires useSearchParams() consumers to sit under a <Suspense> boundary, or static
|
|
33
|
+
// rendering errors with "useSearchParams() should be wrapped in a suspense boundary" — provided
|
|
34
|
+
// here so adopters don't have to wrap their /edit page themselves.
|
|
38
35
|
function EditorWithSearchParams() {
|
|
39
36
|
const urlSearchParams = useSearchParams();
|
|
40
37
|
const searchParams = {
|
package/dist/config.cjs
CHANGED
|
@@ -36,7 +36,187 @@ module.exports = __toCommonJS(config_exports);
|
|
|
36
36
|
|
|
37
37
|
// src/with-canopy.ts
|
|
38
38
|
var import_node_module = require("node:module");
|
|
39
|
+
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
40
|
+
|
|
41
|
+
// src/sharp-tracing.ts
|
|
42
|
+
var import_node_fs = require("node:fs");
|
|
39
43
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
44
|
+
var LOCKFILES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lock", "bun.lockb"];
|
|
45
|
+
var LEGACY_LOCKFILES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"];
|
|
46
|
+
var NEXT_CONFIG_FILES = ["next.config.js", "next.config.mjs", "next.config.ts", "next.config.mts"];
|
|
47
|
+
var GLOB_METACHARACTERS = /[*?[\]{}(),!\\]/;
|
|
48
|
+
var LIBVIPS_PACKAGE = /^@img\/sharp-libvips-/;
|
|
49
|
+
var NATIVE_BINDING_PACKAGE = /^@img\/sharp-(?!libvips-)/;
|
|
50
|
+
function messageOf(err) {
|
|
51
|
+
if (err instanceof Error) return err.message;
|
|
52
|
+
if (typeof err === "string") return err;
|
|
53
|
+
return String(err);
|
|
54
|
+
}
|
|
55
|
+
function isFile(candidate) {
|
|
56
|
+
try {
|
|
57
|
+
return (0, import_node_fs.statSync)(candidate).isFile();
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function isDirectory(candidate) {
|
|
63
|
+
try {
|
|
64
|
+
return (0, import_node_fs.statSync)(candidate).isDirectory();
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function isOutside(root, target) {
|
|
70
|
+
const relative = import_node_path.default.relative(root, target);
|
|
71
|
+
return relative === ".." || relative.startsWith(`..${import_node_path.default.sep}`) || import_node_path.default.isAbsolute(relative);
|
|
72
|
+
}
|
|
73
|
+
function readManifest(packageDir) {
|
|
74
|
+
const parsed = JSON.parse((0, import_node_fs.readFileSync)(import_node_path.default.join(packageDir, "package.json"), "utf8"));
|
|
75
|
+
const manifest = { optionalDependencyNames: [] };
|
|
76
|
+
if (typeof parsed !== "object" || parsed === null) return manifest;
|
|
77
|
+
if ("name" in parsed && typeof parsed.name === "string") manifest.name = parsed.name;
|
|
78
|
+
if ("version" in parsed && typeof parsed.version === "string") manifest.version = parsed.version;
|
|
79
|
+
if ("optionalDependencies" in parsed && typeof parsed.optionalDependencies === "object" && parsed.optionalDependencies !== null) {
|
|
80
|
+
manifest.optionalDependencyNames = Object.keys(parsed.optionalDependencies);
|
|
81
|
+
}
|
|
82
|
+
return manifest;
|
|
83
|
+
}
|
|
84
|
+
function nodeModulesLookupDirs(fromDir) {
|
|
85
|
+
const dirs = [];
|
|
86
|
+
for (let dir = import_node_path.default.resolve(fromDir); ; dir = import_node_path.default.dirname(dir)) {
|
|
87
|
+
if (import_node_path.default.basename(dir) !== "node_modules") dirs.push(import_node_path.default.join(dir, "node_modules"));
|
|
88
|
+
if (import_node_path.default.dirname(dir) === dir) return dirs;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function findInstalledPackage(fromDir, name) {
|
|
92
|
+
for (const lookupDir of nodeModulesLookupDirs(fromDir)) {
|
|
93
|
+
const candidate = import_node_path.default.join(lookupDir, name);
|
|
94
|
+
if (isFile(import_node_path.default.join(candidate, "package.json"))) return (0, import_node_fs.realpathSync)(candidate);
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function installedNextMajor(projectDir) {
|
|
99
|
+
try {
|
|
100
|
+
const nextDir = findInstalledPackage(projectDir, "next");
|
|
101
|
+
if (!nextDir) return null;
|
|
102
|
+
const major = Number.parseInt(readManifest(nextDir).version ?? "", 10);
|
|
103
|
+
return Number.isNaN(major) ? null : major;
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function hasNextConfig(dir) {
|
|
109
|
+
return NEXT_CONFIG_FILES.some((name) => isFile(import_node_path.default.join(dir, name)));
|
|
110
|
+
}
|
|
111
|
+
function findLockFileUpwards(startDir, names) {
|
|
112
|
+
for (let dir = startDir; ; dir = import_node_path.default.dirname(dir)) {
|
|
113
|
+
for (const name of names) {
|
|
114
|
+
const candidate = import_node_path.default.join(dir, name);
|
|
115
|
+
if (isFile(candidate)) return candidate;
|
|
116
|
+
}
|
|
117
|
+
if (import_node_path.default.dirname(dir) === dir) return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function resolveTracingRoot(input) {
|
|
121
|
+
const configured = input.outputFileTracingRoot || input.turbopackRoot;
|
|
122
|
+
if (configured) return import_node_path.default.resolve(input.projectDir, configured);
|
|
123
|
+
if (input.lockfileRoot === "closest") {
|
|
124
|
+
const closest = findLockFileUpwards(input.projectDir, LEGACY_LOCKFILES);
|
|
125
|
+
return closest ? import_node_path.default.dirname(closest) : input.projectDir;
|
|
126
|
+
}
|
|
127
|
+
let lockFile = findLockFileUpwards(input.projectDir, LOCKFILES);
|
|
128
|
+
if (!lockFile) return input.projectDir;
|
|
129
|
+
for (; ; ) {
|
|
130
|
+
const lockDir = import_node_path.default.dirname(lockFile);
|
|
131
|
+
const parentDir = import_node_path.default.dirname(lockDir);
|
|
132
|
+
if (parentDir === lockDir) break;
|
|
133
|
+
const outer = findLockFileUpwards(parentDir, LOCKFILES);
|
|
134
|
+
if (!outer) break;
|
|
135
|
+
lockFile = outer;
|
|
136
|
+
}
|
|
137
|
+
return import_node_path.default.dirname(lockFile);
|
|
138
|
+
}
|
|
139
|
+
function nativeLibraryDirsFor(sharpDir) {
|
|
140
|
+
const found = /* @__PURE__ */ new Set();
|
|
141
|
+
const collectLibvips = (fromDir, dependencyNames) => {
|
|
142
|
+
for (const name of dependencyNames.filter((n) => LIBVIPS_PACKAGE.test(n))) {
|
|
143
|
+
const dir = findInstalledPackage(fromDir, name);
|
|
144
|
+
if (dir) found.add(dir);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const sharpDependencies = readManifest(sharpDir).optionalDependencyNames;
|
|
148
|
+
collectLibvips(sharpDir, sharpDependencies);
|
|
149
|
+
for (const bindingName of sharpDependencies.filter((n) => NATIVE_BINDING_PACKAGE.test(n))) {
|
|
150
|
+
const bindingDir = findInstalledPackage(sharpDir, bindingName);
|
|
151
|
+
if (!bindingDir) continue;
|
|
152
|
+
const bindingDependencies = readManifest(bindingDir).optionalDependencyNames;
|
|
153
|
+
if (bindingDependencies.some((n) => LIBVIPS_PACKAGE.test(n))) {
|
|
154
|
+
collectLibvips(bindingDir, bindingDependencies);
|
|
155
|
+
} else {
|
|
156
|
+
found.add(bindingDir);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return [...found];
|
|
160
|
+
}
|
|
161
|
+
function sharpTracingIncludes(input) {
|
|
162
|
+
try {
|
|
163
|
+
const projectDir = (0, import_node_fs.realpathSync)(input.projectDir);
|
|
164
|
+
const root = (0, import_node_fs.realpathSync)(resolveTracingRoot(input));
|
|
165
|
+
if (isOutside(root, projectDir)) {
|
|
166
|
+
return {
|
|
167
|
+
includes: [],
|
|
168
|
+
problem: `the project directory ${projectDir} is outside the tracing root ${root}`
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const sharpDirs = /* @__PURE__ */ new Set();
|
|
172
|
+
const canopycmsDir = findInstalledPackage(projectDir, "canopycms");
|
|
173
|
+
for (const candidate of [
|
|
174
|
+
canopycmsDir && findInstalledPackage(canopycmsDir, "sharp"),
|
|
175
|
+
findInstalledPackage(projectDir, "sharp")
|
|
176
|
+
]) {
|
|
177
|
+
if (candidate && readManifest(candidate).name === "sharp") sharpDirs.add(candidate);
|
|
178
|
+
}
|
|
179
|
+
if (sharpDirs.size === 0) {
|
|
180
|
+
return {
|
|
181
|
+
includes: [],
|
|
182
|
+
problem: canopycmsDir ? `no sharp is installed for canopycms at ${canopycmsDir}` : `canopycms is not installed where ${projectDir} can find it`
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const includes = /* @__PURE__ */ new Set();
|
|
186
|
+
const notes = [];
|
|
187
|
+
for (const sharpDir of sharpDirs) {
|
|
188
|
+
const libraryDirs = nativeLibraryDirsFor(sharpDir);
|
|
189
|
+
if (libraryDirs.length === 0) {
|
|
190
|
+
notes.push(
|
|
191
|
+
`no libvips package is installed for sharp at ${sharpDir}, and no installed native binding carries its own library`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
for (const libraryDir of libraryDirs) {
|
|
195
|
+
const libDir = import_node_path.default.join(libraryDir, "lib");
|
|
196
|
+
if (!isDirectory(libDir)) {
|
|
197
|
+
notes.push(`${libDir} does not exist`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (isOutside(root, libDir)) {
|
|
201
|
+
notes.push(`${libDir} is outside the tracing root ${root}`);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const relative = import_node_path.default.relative(projectDir, libDir).split(import_node_path.default.sep).join("/");
|
|
205
|
+
if (GLOB_METACHARACTERS.test(relative)) {
|
|
206
|
+
notes.push(`${relative} contains a glob metacharacter`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
includes.add(`${relative}/**/*`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (includes.size === 0) return { includes: [], problem: notes.join("; ") };
|
|
213
|
+
return { includes: [...includes] };
|
|
214
|
+
} catch (err) {
|
|
215
|
+
return { includes: [], problem: messageOf(err) };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/with-canopy.ts
|
|
40
220
|
var REQUIRED_PACKAGES = ["canopycms"];
|
|
41
221
|
var OPTIONAL_PACKAGES = [
|
|
42
222
|
"canopycms-next",
|
|
@@ -70,8 +250,8 @@ function withAssetsRewrite(existingRewrites) {
|
|
|
70
250
|
function resolveReactAliases(resolve) {
|
|
71
251
|
try {
|
|
72
252
|
return {
|
|
73
|
-
react:
|
|
74
|
-
"react-dom":
|
|
253
|
+
react: import_node_path2.default.dirname(resolve("react")),
|
|
254
|
+
"react-dom": import_node_path2.default.dirname(resolve("react-dom"))
|
|
75
255
|
};
|
|
76
256
|
} catch {
|
|
77
257
|
return null;
|
|
@@ -100,8 +280,82 @@ function resolveStaticBuildId() {
|
|
|
100
280
|
}
|
|
101
281
|
return trimmed;
|
|
102
282
|
}
|
|
283
|
+
var ALL_ROUTES = "/**";
|
|
284
|
+
function isTracingIncludes(value) {
|
|
285
|
+
return typeof value === "object" && value !== null && Object.values(value).every(
|
|
286
|
+
(globs) => Array.isArray(globs) && globs.every((g) => typeof g === "string")
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
function readProperty(value, key) {
|
|
290
|
+
return typeof value === "object" && value !== null ? Reflect.get(value, key) : void 0;
|
|
291
|
+
}
|
|
292
|
+
function stringOrUndefined(value) {
|
|
293
|
+
return typeof value === "string" ? value : void 0;
|
|
294
|
+
}
|
|
295
|
+
function isSet(value) {
|
|
296
|
+
return value !== void 0 && value !== null;
|
|
297
|
+
}
|
|
298
|
+
function mergeTracingIncludes(existing, includes) {
|
|
299
|
+
return {
|
|
300
|
+
...existing,
|
|
301
|
+
[ALL_ROUTES]: [.../* @__PURE__ */ new Set([...existing?.[ALL_ROUTES] ?? [], ...includes])]
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function sharpTracingWarning(problem, nextVersionUnknown) {
|
|
305
|
+
return "CanopyCMS: could not add sharp's libvips to this standalone build's file tracing" + (problem ? ` (${problem})` : "") + ". Next.js file tracing can miss that library for sharp 0.35 (https://github.com/vercel/next.js/issues/97973), and the built server then fails to load sharp with ERR_DLOPEN_FAILED. Add it in next.config yourself, with paths relative to the project directory. For pnpm: outputFileTracingIncludes: { '/**': ['node_modules/.pnpm/@img+sharp-libvips-*/node_modules/@img/*/lib/**/*'] }. For npm: outputFileTracingIncludes: { '/**': ['node_modules/@img/sharp-libvips-*/lib/**/*'] }." + (nextVersionUnknown ? " The installed Next.js version could not be read either: on Next 13 or 14, put outputFileTracingIncludes under experimental." : "");
|
|
306
|
+
}
|
|
307
|
+
function unknownNextVersionWarning(projectDir) {
|
|
308
|
+
return `CanopyCMS: could not read the version of the next package installed for ${projectDir}, so sharp's libvips was added to the top-level outputFileTracingIncludes key, which Next 15 and later read. Next 13 and 14 ignore that key: on those versions, move the entry under experimental.outputFileTracingIncludes.`;
|
|
309
|
+
}
|
|
310
|
+
var warnedAboutSharpTracing = false;
|
|
311
|
+
function sharpTracingConfig(nextConfig, options) {
|
|
312
|
+
if (options.staticBuild || nextConfig.output === "export") return {};
|
|
313
|
+
const projectDir = process.cwd();
|
|
314
|
+
const nextMajor = installedNextMajor(projectDir);
|
|
315
|
+
const experimental = nextConfig.experimental ?? {};
|
|
316
|
+
const experimentalOnly = nextMajor !== null && nextMajor < 15;
|
|
317
|
+
const includesUnderExperimental = experimentalOnly || isSet(readProperty(experimental, "outputFileTracingIncludes"));
|
|
318
|
+
const rootUnderExperimental = experimentalOnly || isSet(readProperty(experimental, "outputFileTracingRoot"));
|
|
319
|
+
const writtenIncludes = includesUnderExperimental ? readProperty(experimental, "outputFileTracingIncludes") : nextConfig.outputFileTracingIncludes;
|
|
320
|
+
const existing = isSet(writtenIncludes) ? writtenIncludes : void 0;
|
|
321
|
+
if (existing !== void 0 && !isTracingIncludes(existing)) return {};
|
|
322
|
+
const configuredRoot = stringOrUndefined(
|
|
323
|
+
rootUnderExperimental ? readProperty(experimental, "outputFileTracingRoot") : nextConfig.outputFileTracingRoot
|
|
324
|
+
);
|
|
325
|
+
const turbopack = nextConfig.turbopack;
|
|
326
|
+
const turbopackHasRootKey = typeof turbopack === "object" && turbopack !== null && "root" in turbopack;
|
|
327
|
+
const legacyTurbopackRoot = nextMajor === 15 && !turbopackHasRootKey ? stringOrUndefined(readProperty(readProperty(experimental, "turbo"), "root")) : void 0;
|
|
328
|
+
const turbopackRoot = experimentalOnly ? void 0 : stringOrUndefined(readProperty(turbopack, "root")) ?? legacyTurbopackRoot;
|
|
329
|
+
const { includes, problem } = hasNextConfig(projectDir) ? sharpTracingIncludes({
|
|
330
|
+
projectDir,
|
|
331
|
+
outputFileTracingRoot: configuredRoot,
|
|
332
|
+
turbopackRoot,
|
|
333
|
+
lockfileRoot: experimentalOnly ? "closest" : "outermost"
|
|
334
|
+
}) : {
|
|
335
|
+
includes: [],
|
|
336
|
+
problem: `the working directory ${projectDir} has no next.config file, so withCanopy cannot tell where the project is`
|
|
337
|
+
};
|
|
338
|
+
const nextVersionUnknown = nextMajor === null;
|
|
339
|
+
if (includes.length === 0) {
|
|
340
|
+
if (nextConfig.output === "standalone" && !warnedAboutSharpTracing) {
|
|
341
|
+
warnedAboutSharpTracing = true;
|
|
342
|
+
console.warn(sharpTracingWarning(problem, nextVersionUnknown));
|
|
343
|
+
}
|
|
344
|
+
return {};
|
|
345
|
+
}
|
|
346
|
+
const merged = mergeTracingIncludes(existing, includes);
|
|
347
|
+
if (!includesUnderExperimental) {
|
|
348
|
+
if (nextVersionUnknown && nextConfig.output === "standalone" && !warnedAboutSharpTracing) {
|
|
349
|
+
warnedAboutSharpTracing = true;
|
|
350
|
+
console.warn(unknownNextVersionWarning(projectDir));
|
|
351
|
+
}
|
|
352
|
+
return { outputFileTracingIncludes: merged };
|
|
353
|
+
}
|
|
354
|
+
const legacyExperimental = { ...experimental, outputFileTracingIncludes: merged };
|
|
355
|
+
return { experimental: legacyExperimental };
|
|
356
|
+
}
|
|
103
357
|
function withCanopy(nextConfig = {}, options = {}) {
|
|
104
|
-
const resolve = (0, import_node_module.createRequire)(
|
|
358
|
+
const resolve = (0, import_node_module.createRequire)(import_node_path2.default.join(process.cwd(), "noop.js")).resolve;
|
|
105
359
|
const installedOptional = OPTIONAL_PACKAGES.filter((pkg) => {
|
|
106
360
|
try {
|
|
107
361
|
resolve(pkg);
|
|
@@ -136,6 +390,8 @@ function withCanopy(nextConfig = {}, options = {}) {
|
|
|
136
390
|
}
|
|
137
391
|
return config;
|
|
138
392
|
} : existingWebpack;
|
|
393
|
+
const nextMajor = installedNextMajor(process.cwd());
|
|
394
|
+
const answerTurbopackGuard = webpack !== void 0 && !isSet(existingWebpack) && !isSet(nextConfig.turbopack) && nextMajor !== null && nextMajor >= 16;
|
|
139
395
|
const pageExtensions = [
|
|
140
396
|
.../* @__PURE__ */ new Set([
|
|
141
397
|
...nextConfig.pageExtensions ?? NEXTJS_DEFAULT_PAGE_EXTENSIONS,
|
|
@@ -149,9 +405,11 @@ function withCanopy(nextConfig = {}, options = {}) {
|
|
|
149
405
|
pageExtensions,
|
|
150
406
|
webpack,
|
|
151
407
|
rewrites: withAssetsRewrite(nextConfig.rewrites),
|
|
408
|
+
...sharpTracingConfig(nextConfig, options),
|
|
152
409
|
// Spread conditionally: emitting `generateBuildId: undefined` would be a key Next has to
|
|
153
410
|
// reason about, where absence is unambiguous.
|
|
154
|
-
...generateBuildId ? { generateBuildId } : {}
|
|
411
|
+
...generateBuildId ? { generateBuildId } : {},
|
|
412
|
+
...answerTurbopackGuard ? { turbopack: {} } : {}
|
|
155
413
|
};
|
|
156
414
|
}
|
|
157
415
|
// Annotate the CommonJS export names for ESM import in node:
|