-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnext.config.mjs
More file actions
142 lines (135 loc) · 4.94 KB
/
Copy pathnext.config.mjs
File metadata and controls
142 lines (135 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// ibl.ai: Node.js 22+ ships a partial localStorage that some bundlers
// expect at module-init time during SSR. Polyfill the missing methods.
if (
typeof window === "undefined" &&
typeof localStorage !== "undefined" &&
typeof localStorage.getItem !== "function"
) {
const _s = {}
globalThis.localStorage = {
getItem: (k) => (_s[k] ?? null),
setItem: (k, v) => { _s[k] = String(v) },
removeItem: (k) => { delete _s[k] },
clear: () => { for (const k in _s) delete _s[k] },
get length() { return Object.keys(_s).length },
key: (i) => Object.keys(_s)[i] ?? null,
}
}
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const require = createRequire(import.meta.url)
/**
* Resolve a package to its root directory so webpack never loads
* duplicate copies (npm/pnpm hoisting with differing peer deps).
* Without this, @reduxjs/toolkit may be duplicated and SDK components
* get a different ReactReduxContext — RTK Query hooks then silently
* return undefined.
*/
function dedup(packageName) {
try {
const entry = require.resolve(packageName)
const marker = `node_modules/${packageName}`
const idx = entry.lastIndexOf(marker)
if (idx !== -1) return entry.slice(0, idx + marker.length)
return undefined
} catch {
return undefined
}
}
const resolveAliases = {}
for (const pkg of [
'@iblai/data-layer',
'@iblai/web-utils',
'@iblai/web-containers',
'@iblai/iblai-js',
'@reduxjs/toolkit',
'react-redux',
'react',
'react-dom',
]) {
const dir = dedup(pkg)
if (dir) resolveAliases[pkg] = dir
}
/**
* Sub-path mount. Reads `NEXT_PUBLIC_BASE_PATH`, normalises the
* leading slash, and feeds Next's `basePath` / `assetPrefix`. Defaults
* to root (`/`) — set `NEXT_PUBLIC_BASE_PATH=/courseai` for a sub-path
* mount. basePath is *build-time* only; to change it, rebuild.
*/
function normaliseBasePath(raw) {
if (raw === undefined || raw === null) return ''
const trimmed = raw.replace(/\/+$/, '') // drop trailing slashes
if (trimmed === '' || trimmed === '/') return '' // explicit root
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`
}
const basePath = normaliseBasePath(process.env.NEXT_PUBLIC_BASE_PATH)
const assetPrefix = basePath ? `${basePath}/` : ''
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
...(basePath ? { basePath } : {}),
...(assetPrefix ? { assetPrefix } : {}),
trailingSlash: !!basePath,
// The SDK ServiceWorkerProvider registers `${basePath}/sw.js` with
// `scope: basePath`. A worker's default max scope is its own
// directory; `Service-Worker-Allowed` lifts the cap so the requested
// scope is permitted. `source` is basePath-prefixed by Next, so
// `/sw.js` matches the served file.
async headers() {
return [
{
source: '/sw.js',
headers: [{ key: 'Service-Worker-Allowed', value: basePath || '/' }],
},
]
},
typescript: {
ignoreBuildErrors: true,
},
// The SDK's `useVoiceChat` has an `isMounted` ref that only ever gets
// reset to `false` (effect cleanup), never back to `true` on setup.
// React StrictMode double-invokes effects in dev
// (mount->cleanup->remount), so the ref stays false and audio-to-text
// resolves but the `isMounted`-guarded `setProcessing(false)` never
// runs -> stuck "Processing...". SDK bug; disabling StrictMode is the
// host workaround per iblai-agent-chat skill known-issues.
reactStrictMode: false,
// `loader: 'custom'` + a basePath-aware loader prepends
// `NEXT_PUBLIC_BASE_PATH` to every `<Image>` src when a sub-path
// mount is configured; no-op (returns src as-is) when it isn't.
// Replaces `unoptimized: true`, which silently dropped basePath
// under a sub-path mount.
images: {
loader: 'custom',
loaderFile: './lib/iblai/image-loader.js',
remotePatterns: [
{
protocol: 'https',
hostname: 'upload.wikimedia.org',
pathname: '/wikipedia/**',
},
],
},
// Keep Turbopack scoped to this app. The previous hardcoded macOS
// path was outside the project, which made Next reject the derived
// distDirRoot ("...should not navigate out of the projectPath").
//
// Do NOT pass absolute `resolveAlias` paths here -- Turbopack treats
// absolute paths as relative-to-project and ends up resolving things
// like `/home/lain/.../node_modules/@iblai/data-layer` into the
// project root (`Module not found: ./home/lain/...`). Native pnpm
// resolution gives Turbopack a single copy per package on its own;
// the webpack alias block below is only used for production builds.
turbopack: {
root: path.resolve(__dirname),
},
webpack: (config) => {
config.resolve = config.resolve || {}
config.resolve.alias = config.resolve.alias || {}
Object.assign(config.resolve.alias, resolveAliases)
return config
},
}
export default nextConfig