vike 0.4.266 → 0.4.267-commit-b005535
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/client/runtime-client-routing/history.d.ts +19 -19
- package/dist/client/runtime-client-routing/history.js +89 -83
- package/dist/client/runtime-client-routing/renderPageClient.js +1 -6
- package/dist/client/runtime-client-routing/setScrollPosition.js +6 -5
- package/dist/node/prerender/runPrerender.js +11 -0
- package/dist/node/vite/assertEnvVite.js +1 -1
- package/dist/node/vite/plugins/build/handleAssetsManifest.js +2 -1
- package/dist/node/vite/plugins/pluginDev/logSkillHint.js +6 -4
- package/dist/node/vite/plugins/pluginUniversalDeploy.js +3 -2
- package/dist/node/vite/shared/resolveVikeConfigInternal/getPlusFiles.d.ts +2 -2
- package/dist/node/vite/shared/resolveVikeConfigInternal/getPlusFiles.js +3 -3
- package/dist/node/vite/shared/resolveVikeConfigInternal/loadFileAtConfigTime.d.ts +4 -4
- package/dist/node/vite/shared/resolveVikeConfigInternal/loadFileAtConfigTime.js +9 -9
- package/dist/node/vite/shared/resolveVikeConfigInternal/metaBuiltIn.js +4 -0
- package/dist/node/vite/shared/resolveVikeConfigInternal/pointerImports.d.ts +3 -1
- package/dist/node/vite/shared/resolveVikeConfigInternal/pointerImports.js +18 -46
- package/dist/node/vite/shared/resolveVikeConfigInternal/transpileAndExecuteFile.d.ts +3 -3
- package/dist/node/vite/shared/resolveVikeConfigInternal/transpileAndExecuteFile.js +333 -212
- package/dist/node/vite/shared/resolveVikeConfigInternal.js +15 -15
- package/dist/server/runtime/renderPageServer/csp.d.ts +2 -1
- package/dist/server/runtime/renderPageServer/csp.js +2 -1
- package/dist/server/runtime/renderPageServer/headersResponse.js +2 -1
- package/dist/types/Config.d.ts +8 -0
- package/dist/utils/PROJECT_VERSION.d.ts +1 -1
- package/dist/utils/PROJECT_VERSION.js +1 -1
- package/dist/utils/assertNodeVersion.d.ts +2 -0
- package/dist/utils/assertNodeVersion.js +4 -1
- package/dist/utils/debug.d.ts +1 -1
- package/dist/utils/debug.js +1 -1
- package/dist/utils/requireResolve.d.ts +0 -6
- package/dist/utils/requireResolve.js +0 -8
- package/package.json +5 -6
|
@@ -2,13 +2,15 @@ export { transpileAndExecuteFile };
|
|
|
2
2
|
export { getConfigBuildErrorFormatted };
|
|
3
3
|
export { getConfigExecutionErrorIntroMsg };
|
|
4
4
|
export { isTemporaryBuildFile };
|
|
5
|
-
import {
|
|
5
|
+
import { rolldown, VERSION, } from 'rolldown';
|
|
6
|
+
import { parseAst } from 'rolldown/parseAst';
|
|
6
7
|
import fs from 'node:fs';
|
|
7
8
|
import path from 'node:path';
|
|
8
9
|
import crypto from 'node:crypto';
|
|
9
10
|
import pc from '@brillout/picocolors';
|
|
10
11
|
import { import_ } from '@brillout/import';
|
|
11
12
|
import { assert, assertWarning } from '../../../../utils/assert.js';
|
|
13
|
+
import { nodeVersionMin } from '../../../../utils/assertNodeVersion.js';
|
|
12
14
|
import { assertIsNotProductionRuntime } from '../../../../utils/assertSetup.js';
|
|
13
15
|
import { createDebug } from '../../../../utils/debug.js';
|
|
14
16
|
import { genPromise } from '../../../../utils/genPromise.js';
|
|
@@ -19,27 +21,27 @@ import { isPlainJavaScriptFile, isPlainScriptFile } from '../../../../utils/isSc
|
|
|
19
21
|
import { isVitest } from '../../../../utils/isVitest.js';
|
|
20
22
|
import { assertImportIsNpmPackage, isImportNpmPackageOrPathAlias } from '../../../../utils/parseNpmPackage.js';
|
|
21
23
|
import { assertPosixPath, toPosixPath } from '../../../../utils/path.js';
|
|
22
|
-
import {
|
|
23
|
-
import { transformPointerImports } from './pointerImports.js';
|
|
24
|
+
import { pointerImportAttributeSuffix, transformPointerImports } from './pointerImports.js';
|
|
24
25
|
import sourceMapSupport from 'source-map-support';
|
|
25
26
|
import { getFilePathAbsoluteUserRootDir } from '../getFilePath.js';
|
|
27
|
+
import { getMagicString } from '../getMagicString.js';
|
|
26
28
|
import '../../assertEnvVite.js';
|
|
27
29
|
assertIsNotProductionRuntime();
|
|
28
30
|
installSourceMapSupport();
|
|
29
31
|
const debug = createDebug('vike:pointer-imports');
|
|
30
|
-
const
|
|
32
|
+
const debugResolve = createDebug('vike:transpile-resolve');
|
|
31
33
|
const debugConfig = createDebug('vike:config');
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
async function transpileAndExecuteFile(filePath, userRootDir, isExtensionConfig,
|
|
34
|
+
if (debugResolve.isActivated)
|
|
35
|
+
debugResolve('rolldown version', VERSION);
|
|
36
|
+
async function transpileAndExecuteFile(filePath, userRootDir, isExtensionConfig, vikeTranspileCache) {
|
|
35
37
|
const { filePathAbsoluteFilesystem, filePathToShowToUserResolved } = filePath;
|
|
36
38
|
assert(filePathAbsoluteFilesystem);
|
|
37
39
|
const fileExtension = getFileExtension(filePathAbsoluteFilesystem);
|
|
38
|
-
if (
|
|
39
|
-
return await
|
|
40
|
+
if (vikeTranspileCache.transpileCache[filePathAbsoluteFilesystem]) {
|
|
41
|
+
return await vikeTranspileCache.transpileCache[filePathAbsoluteFilesystem];
|
|
40
42
|
}
|
|
41
43
|
const { promise, resolve } = genPromise();
|
|
42
|
-
|
|
44
|
+
vikeTranspileCache.transpileCache[filePathAbsoluteFilesystem] = promise;
|
|
43
45
|
/* We tolerate .tsx so that a file can be both a runtime and config file (`meta.env.config === true && meta.env.server === true`), e.g. https://github.com/brillout/docpress/issues/86
|
|
44
46
|
assertUsage(
|
|
45
47
|
isPlainScriptFile(filePathAbsoluteFilesystem),
|
|
@@ -52,15 +54,15 @@ async function transpileAndExecuteFile(filePath, userRootDir, isExtensionConfig,
|
|
|
52
54
|
}
|
|
53
55
|
let fileExports;
|
|
54
56
|
if (isExtensionConfig && !isHeader && fileExtension.endsWith('js')) {
|
|
55
|
-
// This doesn't track dependencies => we should never use this for user land configs
|
|
56
57
|
if (debugConfig.isActivated) {
|
|
57
|
-
debugConfig(filePathToShowToUserResolved, 'executed directly (no
|
|
58
|
+
debugConfig(filePathToShowToUserResolved, 'executed directly (no transpilation)');
|
|
58
59
|
}
|
|
60
|
+
// This doesn't track dependencies => we should never use this for user land configs
|
|
59
61
|
fileExports = await executeFile(filePathAbsoluteFilesystem, filePath);
|
|
60
62
|
}
|
|
61
63
|
else {
|
|
62
64
|
const transformImports = isHeader ? 'all' : true;
|
|
63
|
-
const code = await
|
|
65
|
+
const code = await transpileFileAndTransformPointerImports(filePath, transformImports, userRootDir, vikeTranspileCache);
|
|
64
66
|
if (debugConfig.isActivated) {
|
|
65
67
|
debugConfig(filePathToShowToUserResolved, code);
|
|
66
68
|
}
|
|
@@ -69,19 +71,19 @@ async function transpileAndExecuteFile(filePath, userRootDir, isExtensionConfig,
|
|
|
69
71
|
resolve({ fileExports });
|
|
70
72
|
return { fileExports };
|
|
71
73
|
}
|
|
72
|
-
async function
|
|
74
|
+
async function transpileFileAndTransformPointerImports(filePath, transformImports, userRootDir, vikeTranspileCache) {
|
|
73
75
|
const { filePathAbsoluteFilesystem, filePathToShowToUserResolved } = filePath;
|
|
74
76
|
assert(filePathAbsoluteFilesystem);
|
|
75
77
|
assertPosixPath(filePathAbsoluteFilesystem);
|
|
76
|
-
|
|
78
|
+
vikeTranspileCache.vikeConfigDependencies.add(filePathAbsoluteFilesystem);
|
|
77
79
|
if (debug.isActivated)
|
|
78
80
|
debug('transpile', filePathToShowToUserResolved);
|
|
79
|
-
let { code, pointerImports } = await
|
|
81
|
+
let { code, pointerImports } = await transpileFile(filePath, userRootDir, transformImports, vikeTranspileCache);
|
|
80
82
|
if (debug.isActivated)
|
|
81
|
-
debug(`code, post
|
|
83
|
+
debug(`code, post transpilation (${filePathToShowToUserResolved})`, code);
|
|
82
84
|
let isImportTransformed = false;
|
|
83
85
|
if (transformImports) {
|
|
84
|
-
const codeMod = transformPointerImports(code,
|
|
86
|
+
const codeMod = transformPointerImports(code, pointerImports);
|
|
85
87
|
if (codeMod) {
|
|
86
88
|
code = codeMod;
|
|
87
89
|
isImportTransformed = true;
|
|
@@ -95,200 +97,316 @@ async function transpileFile(filePath, transformImports, userRootDir, esbuildCac
|
|
|
95
97
|
}
|
|
96
98
|
return code;
|
|
97
99
|
}
|
|
98
|
-
async function
|
|
100
|
+
async function transpileFile(filePath, userRootDir, transformImports, vikeTranspileCache) {
|
|
99
101
|
const entryFilePath = filePath.filePathAbsoluteFilesystem;
|
|
100
102
|
const entryFileDir = path.posix.dirname(entryFilePath);
|
|
101
|
-
const options = {
|
|
102
|
-
platform: 'node',
|
|
103
|
-
entryPoints: [entryFilePath],
|
|
104
|
-
sourcemap: 'inline',
|
|
105
|
-
write: false,
|
|
106
|
-
target: ['node14.18', 'node16'],
|
|
107
|
-
outfile: path.posix.join(
|
|
108
|
-
// Needed for correct inline source map
|
|
109
|
-
entryFileDir,
|
|
110
|
-
// `write: false` => no file is actually emitted
|
|
111
|
-
'NEVER_EMITTED.js'),
|
|
112
|
-
logLevel: 'silent',
|
|
113
|
-
format: 'esm',
|
|
114
|
-
absWorkingDir: userRootDir,
|
|
115
|
-
// Disable tree-shaking to avoid dead-code elimination, so that unused imports aren't removed.
|
|
116
|
-
// Esbuild still sometimes removes unused imports because of TypeScript: https://github.com/evanw/esbuild/issues/3034
|
|
117
|
-
treeShaking: false,
|
|
118
|
-
minify: false,
|
|
119
|
-
metafile: true,
|
|
120
|
-
bundle: true,
|
|
121
|
-
};
|
|
122
103
|
const pointerImports = {};
|
|
123
|
-
|
|
104
|
+
const unresolvedImports = [];
|
|
105
|
+
const plugins = [
|
|
124
106
|
// Determine whether an import should be:
|
|
125
107
|
// - A pointer import
|
|
126
108
|
// - Externalized
|
|
127
109
|
{
|
|
128
|
-
name: 'vike-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
let
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
userRootDir,
|
|
153
|
-
});
|
|
154
|
-
if (debugEsbuildResolve.isActivated)
|
|
155
|
-
debugEsbuildResolve('resolvedWithNode', resolvedWithNode);
|
|
156
|
-
if (resolvedWithNode)
|
|
157
|
-
resolved = { path: resolvedWithNode };
|
|
158
|
-
}
|
|
159
|
-
if (resolved.errors && resolved.errors.length > 0) {
|
|
160
|
-
/* We could do the following to let Node.js throw the error, but we don't because the error shown by esbuild is prettier: the Node.js error refers to the transpiled [build-f7i251e0iwnw]+config.ts.mjs whereas esbuild refers to the source +config.ts file.
|
|
161
|
-
pointerImports[args.path] = false
|
|
162
|
-
return { external: true }
|
|
163
|
-
*/
|
|
164
|
-
// Let esbuild throw the error
|
|
165
|
-
cleanEsbuildErrors(resolved.errors);
|
|
166
|
-
return resolved;
|
|
167
|
-
}
|
|
168
|
-
assert(resolved.path);
|
|
169
|
-
// Built-in modules e.g. node:fs
|
|
170
|
-
if (resolved.path === args.path) {
|
|
171
|
-
const isPointerImport = false;
|
|
172
|
-
pointerImports[args.path] = isPointerImport;
|
|
173
|
-
if (debug.isActivated)
|
|
174
|
-
debug('onResolve() [built-in module]', { args, resolved });
|
|
175
|
-
assert(resolved.external);
|
|
176
|
-
return resolved;
|
|
177
|
-
}
|
|
178
|
-
const importPathResolved = toPosixPath(resolved.path);
|
|
179
|
-
const importPathOriginal = args.path;
|
|
180
|
-
// Esbuild resolves path aliases.
|
|
181
|
-
// - Enabling us to use:
|
|
182
|
-
// - assertImportIsNpmPackage()
|
|
183
|
-
// - isImportNpmPackage(str, { cannotBePathAlias: true })
|
|
184
|
-
assertFilePathAbsoluteFilesystem(importPathResolved);
|
|
185
|
-
// Should we remove this? See comment below.
|
|
186
|
-
const isVikeExtensionImport = (path.startsWith('vike-') && path.endsWith('/config')) || importPathResolved.endsWith('+config.js');
|
|
187
|
-
const isPointerImport = transformImports === 'all' ||
|
|
188
|
-
// .jsx, .vue, .svg, ... => obviously not config code => pointer import
|
|
189
|
-
!isPlainScriptFile(importPathResolved) ||
|
|
190
|
-
// Import of a Vike extension config => make it a pointer import because we want to show nice error messages (that can display whether a config has been set by the user or by a Vike extension).
|
|
191
|
-
// - Should we stop doing this? (And instead let Node.js directly load Vike extensions.)
|
|
192
|
-
// - In principle, we can use the setting 'name' value of Vike extensions.
|
|
193
|
-
// - vike@0.4.162 started soft-requiring Vike extensions to set the name config.
|
|
194
|
-
// - In practice, it seems like it requires some (non-trivial?) refactoring.
|
|
195
|
-
isVikeExtensionImport ||
|
|
196
|
-
args.with?.['type'] === 'vike:pointer';
|
|
197
|
-
assertPosixPath(importPathResolved);
|
|
198
|
-
// False positive if `importPathOriginal` is a path alias that a) looks like an npm package import and b) resolves outside of `userRootDir` => we then we wrongfully assume that `importPathOriginal` is an npm package import.
|
|
199
|
-
// - For example: https://github.com/vikejs/vike/issues/2326
|
|
200
|
-
const isMostLikelyNpmPkgImport = isImportNpmPackageOrPathAlias(importPathOriginal) &&
|
|
201
|
-
(importPathResolved.includes('/node_modules/') ||
|
|
202
|
-
// Linked npm package
|
|
203
|
-
!importPathResolved.startsWith(userRootDir));
|
|
204
|
-
const isExternal = isPointerImport ||
|
|
205
|
-
// Performance: npm package imports can be externalized. (We could as well let esbuild transpile /node_modules/ code but it's useless as /node_modules/ code is already built. It would unnecessarily slow down transpilation.)
|
|
206
|
-
(isMostLikelyNpmPkgImport && isPlainJavaScriptFile(importPathResolved));
|
|
207
|
-
if (!isExternal) {
|
|
208
|
-
// User-land config code (i.e. not runtime code) => let esbuild transpile it
|
|
209
|
-
assert(!isPointerImport);
|
|
210
|
-
if (debug.isActivated)
|
|
211
|
-
debug('onResolve() [non-external]', { args, resolved, isPointerImport, isExternal });
|
|
212
|
-
return resolved;
|
|
213
|
-
}
|
|
214
|
-
let importPathTranspiled;
|
|
215
|
-
assertPosixPath(importPathOriginal);
|
|
216
|
-
if (isImportPathRelative(importPathOriginal)) {
|
|
217
|
-
importPathTranspiled = importPathResolved;
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
// `importPathOriginal` is either:
|
|
221
|
-
// - Npm package import
|
|
222
|
-
// - Path alias
|
|
223
|
-
const filePathAbsoluteUserRootDir = getFilePathAbsoluteUserRootDir({
|
|
224
|
-
filePathAbsoluteFilesystem: importPathResolved,
|
|
225
|
-
userRootDir,
|
|
226
|
-
});
|
|
227
|
-
if (filePathAbsoluteUserRootDir && !isMostLikelyNpmPkgImport) {
|
|
228
|
-
// `importPathOriginal` is most likely a path alias.
|
|
229
|
-
// - We have to use esbuild's path alias resolution, because:
|
|
230
|
-
// - Vike doesn't resolve path aliases at all.
|
|
231
|
-
// - Node.js doesn't support `tsconfig.js#compilerOptions.paths`.
|
|
232
|
-
// - Esbuild path alias resolution seems reliable, e.g. it supports `tsconfig.js#compilerOptions.paths`.
|
|
233
|
-
importPathTranspiled = importPathResolved;
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
// `importPathOriginal` is most likely an npm package import.
|
|
237
|
-
assertImportIsNpmPackage(importPathOriginal);
|
|
238
|
-
// For improved error messages, let the resolution be handled by Vike or Node.js.
|
|
239
|
-
importPathTranspiled = importPathOriginal;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
if (debug.isActivated)
|
|
243
|
-
debug('onResolve() [external]', { args, resolved, importPathTranspiled, isPointerImport, isExternal });
|
|
110
|
+
name: 'vike:pointer-imports',
|
|
111
|
+
async resolveId(importPath, importer, options) {
|
|
112
|
+
if (options.isEntry || options.kind !== 'import-statement')
|
|
113
|
+
return;
|
|
114
|
+
assert(importer);
|
|
115
|
+
// Import with the import attribute `with { type: 'vike:pointer' }`, see transform() below
|
|
116
|
+
const isPointerImportAttribute = importPath.endsWith(pointerImportAttributeSuffix);
|
|
117
|
+
const importPathOriginal = isPointerImportAttribute
|
|
118
|
+
? importPath.slice(0, -pointerImportAttributeSuffix.length)
|
|
119
|
+
: importPath;
|
|
120
|
+
const resolved = await resolveImport(this, importPathOriginal, importer, options);
|
|
121
|
+
if (!resolved) {
|
|
122
|
+
/* We could do the following to let Node.js throw the error, but we don't because the error shown by Rolldown is prettier: the Node.js error refers to the transpiled +config.ts.build-f7i251e0iwnw.mjs whereas Rolldown refers to the source +config.ts file.
|
|
123
|
+
pointerImports[importPathOriginal] = false
|
|
124
|
+
return { id: importPathOriginal, external: true }
|
|
125
|
+
*/
|
|
126
|
+
// Let Rolldown throw the error (see onLog() below)
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
// Built-in modules e.g. node:fs
|
|
130
|
+
// - Rolldown externalizes only built-in modules and HTTP URLs (which Node.js can't import anyway): resolveImport() skips Vike's plugin and we don't use Rolldown's `external` option.
|
|
131
|
+
if (resolved.external) {
|
|
132
|
+
const importPathTranspiled = resolved.id;
|
|
133
|
+
const isPointerImport = false;
|
|
244
134
|
pointerImports[importPathTranspiled] = isPointerImport;
|
|
245
|
-
|
|
246
|
-
|
|
135
|
+
if (debug.isActivated)
|
|
136
|
+
debug('resolveId() [built-in module]', { importPathOriginal, importer, resolved });
|
|
137
|
+
return resolved;
|
|
138
|
+
}
|
|
139
|
+
// Rolldown's internal modules, e.g. helpers injected by Oxc's transformer
|
|
140
|
+
if (isVirtualModule(resolved.id))
|
|
141
|
+
return resolved;
|
|
142
|
+
const importPathResolved = toPosixPath(resolved.id);
|
|
143
|
+
const classified = classifyImport(importPathOriginal, importPathResolved, isPointerImportAttribute, transformImports, userRootDir);
|
|
144
|
+
if (!classified.isExternal) {
|
|
145
|
+
if (debug.isActivated)
|
|
146
|
+
debug('resolveId() [non-external]', { importPathOriginal, importer, resolved });
|
|
147
|
+
return resolved;
|
|
148
|
+
}
|
|
149
|
+
let { isPointerImport, importPathTranspiled } = classified;
|
|
150
|
+
// Keep the mark, so that the ID is different than the ID of the same file loaded and executed at config-time, see transform() below.
|
|
151
|
+
// - The mark is removed by transformPointerImports()
|
|
152
|
+
if (isPointerImportAttribute)
|
|
153
|
+
importPathTranspiled += pointerImportAttributeSuffix;
|
|
154
|
+
if (debug.isActivated)
|
|
155
|
+
debug('resolveId() [external]', {
|
|
156
|
+
importPathOriginal,
|
|
157
|
+
importer,
|
|
158
|
+
resolved,
|
|
159
|
+
importPathTranspiled,
|
|
160
|
+
isPointerImport,
|
|
161
|
+
});
|
|
162
|
+
pointerImports[importPathTranspiled] = isPointerImport;
|
|
163
|
+
return { id: importPathTranspiled, external: true };
|
|
164
|
+
},
|
|
165
|
+
async transform(code, id) {
|
|
166
|
+
// Rolldown's internal modules, e.g. \0rolldown/runtime.js
|
|
167
|
+
if (isVirtualModule(id))
|
|
168
|
+
return;
|
|
169
|
+
// Rolldown doesn't pass import attributes `with { type: 'vike:pointer' }` to resolveId() => we parse the imports ourselves.
|
|
170
|
+
const importsWithPointerAttribute = parseImportsWithPointerAttribute(code, id);
|
|
171
|
+
// Rolldown calls resolveId() only once per import path of a module, and a file cannot be both external and bundled => we mark the import path of imports that have the import attribute, so that they're resolved separately. For example:
|
|
172
|
+
// ```js
|
|
173
|
+
// // Pointer import
|
|
174
|
+
// import { Page } from './Page.ts' with { type: 'vike:pointer' }
|
|
175
|
+
// // Loaded and executed at config-time
|
|
176
|
+
// import { title } from './Page.ts'
|
|
177
|
+
// ```
|
|
178
|
+
const { magicString, getMagicStringResult } = getMagicString(code, id);
|
|
179
|
+
for (const { importPath, importPathSpan } of importsWithPointerAttribute) {
|
|
180
|
+
// We don't mark import paths that cannot be resolved: we let Rolldown throw the resolve error, which then shows the original code
|
|
181
|
+
if (!(await resolveImport(this, importPath, id)))
|
|
182
|
+
continue;
|
|
183
|
+
// Mark the import path, right before its closing quote
|
|
184
|
+
magicString.appendLeft(importPathSpan.end - 1, pointerImportAttributeSuffix);
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
...getMagicStringResult(),
|
|
188
|
+
// Disable tree-shaking of user-land code: we want to execute user-land code as-is. (Rolldown's internal modules, such as its runtime helpers, are tree-shaken.)
|
|
189
|
+
moduleSideEffects: 'no-treeshake',
|
|
190
|
+
};
|
|
247
191
|
},
|
|
248
192
|
},
|
|
249
193
|
// Track dependencies
|
|
194
|
+
// - We collect the dependencies with the load() hook (instead of using Rolldown's output), so that we also collect them if the build fails
|
|
250
195
|
{
|
|
251
196
|
name: 'vike:dependency-tracker',
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return undefined;
|
|
259
|
-
});
|
|
260
|
-
/* To exhaustively collect all dependencies upon build failure, we would also need to use onResolve().
|
|
261
|
-
* - Because onLoad() isn't call if the config dependency can't be resolved.
|
|
197
|
+
load(id) {
|
|
198
|
+
if (isVirtualModule(id))
|
|
199
|
+
return;
|
|
200
|
+
vikeTranspileCache.vikeConfigDependencies.add(toPosixPath(id));
|
|
201
|
+
/* To exhaustively collect all dependencies upon build failure, we would also need to use resolveId().
|
|
202
|
+
* - Because load() isn't call if the config dependency can't be resolved.
|
|
262
203
|
* - For example, the following breaks auto-reload (the config is stuck in its error state and the user needs to touch the importer for the config to reload):
|
|
263
204
|
* ```bash
|
|
264
205
|
* mv ./some-config-dependency.js /tmp/ && mv /tmp/some-config-dependency.js .
|
|
265
206
|
* ```
|
|
266
207
|
* - But implementing a fix is complex and isn't worth it.
|
|
267
|
-
|
|
208
|
+
resolveId(...)
|
|
268
209
|
*/
|
|
269
210
|
},
|
|
270
211
|
},
|
|
271
212
|
];
|
|
272
|
-
let
|
|
213
|
+
let bundle;
|
|
214
|
+
let output;
|
|
273
215
|
try {
|
|
274
|
-
|
|
216
|
+
bundle = await rolldown({
|
|
217
|
+
input: entryFilePath,
|
|
218
|
+
cwd: userRootDir,
|
|
219
|
+
platform: 'node',
|
|
220
|
+
// Resolve path aliases defined in tsconfig.json
|
|
221
|
+
tsconfig: true,
|
|
222
|
+
transform: {
|
|
223
|
+
target: `node${nodeVersionMin}`,
|
|
224
|
+
},
|
|
225
|
+
// Keep the absolute path of pointer imports, so that the import paths of Rolldown's output match `pointerImports`
|
|
226
|
+
makeAbsoluteExternalsRelative: false,
|
|
227
|
+
plugins,
|
|
228
|
+
onLog(level, log, defaultHandler) {
|
|
229
|
+
if (debugResolve.isActivated)
|
|
230
|
+
debugResolve('log', { level, log });
|
|
231
|
+
// Rolldown treats unresolved imports that aren't relative (e.g. npm packages and path aliases) as external, but we want the build to fail instead (as with esbuild):
|
|
232
|
+
// - Rolldown's error shows the import in the source file, whereas the error thrown by Node.js would refer to the temporary build file, see resolveId() above.
|
|
233
|
+
// - Such an external import wouldn't be classified by resolveId() (it wouldn't be in `pointerImports`).
|
|
234
|
+
if (log.code === 'UNRESOLVED_IMPORT') {
|
|
235
|
+
unresolvedImports.push(log);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
defaultHandler(level, log);
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
output = await bundle.generate({
|
|
242
|
+
format: 'esm',
|
|
243
|
+
sourcemap: 'inline',
|
|
244
|
+
// Needed for correct inline source map (bundle.generate() doesn't emit any file)
|
|
245
|
+
dir: entryFileDir,
|
|
246
|
+
// Avoid dead-code elimination (Rolldown's default is `minify: 'dce-only'`)
|
|
247
|
+
minify: false,
|
|
248
|
+
// Single chunk
|
|
249
|
+
codeSplitting: false,
|
|
250
|
+
});
|
|
275
251
|
}
|
|
276
252
|
catch (err) {
|
|
277
|
-
|
|
253
|
+
// Error thrown by one of our plugins, e.g. a failing assert()
|
|
254
|
+
const errVikePlugin = getErrVikePlugin(err);
|
|
255
|
+
if (errVikePlugin)
|
|
256
|
+
throw errVikePlugin;
|
|
257
|
+
formatBuildErr(err, filePath);
|
|
278
258
|
throw err;
|
|
279
259
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const code =
|
|
260
|
+
finally {
|
|
261
|
+
await bundle?.close();
|
|
262
|
+
}
|
|
263
|
+
if (unresolvedImports.length > 0)
|
|
264
|
+
throw getErrUnresolvedImports(unresolvedImports, filePath);
|
|
265
|
+
const chunk = output.output[0];
|
|
266
|
+
assert(output.output.length === 1);
|
|
267
|
+
assert(chunk.type === 'chunk');
|
|
268
|
+
const { code } = chunk;
|
|
289
269
|
assert(typeof code === 'string');
|
|
290
270
|
return { code, pointerImports };
|
|
291
271
|
}
|
|
272
|
+
// Resolve with Rolldown
|
|
273
|
+
async function resolveImport(pluginContext, importPath, importer, options = { kind: 'import-statement' }) {
|
|
274
|
+
const resolved = await pluginContext.resolve(importPath, importer, {
|
|
275
|
+
...options,
|
|
276
|
+
skipSelf: true,
|
|
277
|
+
});
|
|
278
|
+
if (debugResolve.isActivated)
|
|
279
|
+
debugResolve('args', { importPath, importer, options });
|
|
280
|
+
if (debugResolve.isActivated)
|
|
281
|
+
debugResolve('resolved', resolved);
|
|
282
|
+
return resolved;
|
|
283
|
+
}
|
|
284
|
+
// Determine whether an import should be:
|
|
285
|
+
// - A pointer import
|
|
286
|
+
// - Externalized
|
|
287
|
+
function classifyImport(importPathOriginal, importPathResolved, isPointerImportAttribute, transformImports, userRootDir) {
|
|
288
|
+
// Rolldown resolves path aliases.
|
|
289
|
+
// - Enabling us to use:
|
|
290
|
+
// - assertImportIsNpmPackage()
|
|
291
|
+
// - isImportNpmPackage(str, { cannotBePathAlias: true })
|
|
292
|
+
assertFilePathAbsoluteFilesystem(importPathResolved);
|
|
293
|
+
// Should we remove this? See comment below.
|
|
294
|
+
const isVikeExtensionImport = (importPathOriginal.startsWith('vike-') && importPathOriginal.endsWith('/config')) ||
|
|
295
|
+
importPathResolved.endsWith('+config.js');
|
|
296
|
+
const isPointerImport = transformImports === 'all' ||
|
|
297
|
+
// .jsx, .vue, .svg, ... => obviously not config code => pointer import
|
|
298
|
+
!isPlainScriptFile(importPathResolved) ||
|
|
299
|
+
// Import of a Vike extension config => make it a pointer import because we want to show nice error messages (that can display whether a config has been set by the user or by a Vike extension).
|
|
300
|
+
// - Should we stop doing this? (And instead let Node.js directly load Vike extensions.)
|
|
301
|
+
// - In principle, we can use the setting 'name' value of Vike extensions.
|
|
302
|
+
// - vike@0.4.162 started soft-requiring Vike extensions to set the name config.
|
|
303
|
+
// - In practice, it seems like it requires some (non-trivial?) refactoring.
|
|
304
|
+
isVikeExtensionImport ||
|
|
305
|
+
isPointerImportAttribute;
|
|
306
|
+
assertPosixPath(importPathResolved);
|
|
307
|
+
// False positive if `importPathOriginal` is a path alias that a) looks like an npm package import and b) resolves outside of `userRootDir` => we then we wrongfully assume that `importPathOriginal` is an npm package import.
|
|
308
|
+
// - For example: https://github.com/vikejs/vike/issues/2326
|
|
309
|
+
const isMostLikelyNpmPkgImport = isImportNpmPackageOrPathAlias(importPathOriginal) &&
|
|
310
|
+
(importPathResolved.includes('/node_modules/') ||
|
|
311
|
+
// Linked npm package
|
|
312
|
+
!importPathResolved.startsWith(userRootDir));
|
|
313
|
+
const isExternal = isPointerImport ||
|
|
314
|
+
// Performance: npm package imports can be externalized. (We could as well let Rolldown transpile /node_modules/ code but it's useless as /node_modules/ code is already built. It would unnecessarily slow down transpilation.)
|
|
315
|
+
(isMostLikelyNpmPkgImport && isPlainJavaScriptFile(importPathResolved));
|
|
316
|
+
if (!isExternal) {
|
|
317
|
+
// User-land config code (i.e. not runtime code) => let Rolldown transpile it
|
|
318
|
+
assert(!isPointerImport);
|
|
319
|
+
return { isExternal, isPointerImport };
|
|
320
|
+
}
|
|
321
|
+
let importPathTranspiled;
|
|
322
|
+
assertPosixPath(importPathOriginal);
|
|
323
|
+
if (isImportPathRelative(importPathOriginal)) {
|
|
324
|
+
importPathTranspiled = importPathResolved;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
// `importPathOriginal` is either:
|
|
328
|
+
// - Npm package import
|
|
329
|
+
// - Path alias
|
|
330
|
+
const filePathAbsoluteUserRootDir = getFilePathAbsoluteUserRootDir({
|
|
331
|
+
filePathAbsoluteFilesystem: importPathResolved,
|
|
332
|
+
userRootDir,
|
|
333
|
+
});
|
|
334
|
+
if (filePathAbsoluteUserRootDir && !isMostLikelyNpmPkgImport) {
|
|
335
|
+
// `importPathOriginal` is most likely a path alias.
|
|
336
|
+
// - We have to use Rolldown's path alias resolution, because:
|
|
337
|
+
// - Vike doesn't resolve path aliases at all.
|
|
338
|
+
// - Node.js doesn't support `tsconfig.js#compilerOptions.paths`.
|
|
339
|
+
// - Rolldown path alias resolution seems reliable, e.g. it supports `tsconfig.js#compilerOptions.paths`.
|
|
340
|
+
importPathTranspiled = importPathResolved;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
// `importPathOriginal` is most likely an npm package import.
|
|
344
|
+
assertImportIsNpmPackage(importPathOriginal);
|
|
345
|
+
// For improved error messages, let the resolution be handled by Vike or Node.js.
|
|
346
|
+
importPathTranspiled = importPathOriginal;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return { isExternal, isPointerImport, importPathTranspiled };
|
|
350
|
+
}
|
|
351
|
+
// Imports with the import attribute `with { type: 'vike:pointer' }`
|
|
352
|
+
function parseImportsWithPointerAttribute(code, id) {
|
|
353
|
+
// Performance trick
|
|
354
|
+
if (!code.includes('vike:pointer'))
|
|
355
|
+
return [];
|
|
356
|
+
let program;
|
|
357
|
+
try {
|
|
358
|
+
program = parseAst(code, { lang: getLang(id), sourceType: 'module' }, id);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// Let Rolldown throw the parse error (Rolldown shows a prettier error)
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
const imports = [];
|
|
365
|
+
program.body.forEach((node) => {
|
|
366
|
+
if (node.type === 'ImportDeclaration') {
|
|
367
|
+
// Removed by TypeScript transpilation
|
|
368
|
+
if (node.importKind === 'type')
|
|
369
|
+
return;
|
|
370
|
+
if (!hasPointerImportAttribute(node.attributes))
|
|
371
|
+
return;
|
|
372
|
+
imports.push({
|
|
373
|
+
importPath: node.source.value,
|
|
374
|
+
importPathSpan: { start: node.source.start, end: node.source.end },
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
if ((node.type === 'ExportNamedDeclaration' || node.type === 'ExportAllDeclaration') && node.source) {
|
|
378
|
+
// Removed by TypeScript transpilation
|
|
379
|
+
if (node.exportKind === 'type')
|
|
380
|
+
return;
|
|
381
|
+
if (!hasPointerImportAttribute(node.attributes))
|
|
382
|
+
return;
|
|
383
|
+
imports.push({
|
|
384
|
+
importPath: node.source.value,
|
|
385
|
+
importPathSpan: { start: node.source.start, end: node.source.end },
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
return imports;
|
|
390
|
+
}
|
|
391
|
+
function hasPointerImportAttribute(attributes) {
|
|
392
|
+
return attributes.some(({ key, value }) => {
|
|
393
|
+
const keyName = key.type === 'Identifier' ? key.name : key.value;
|
|
394
|
+
return keyName === 'type' && value.value === 'vike:pointer';
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
function getLang(filePath) {
|
|
398
|
+
const fileExtension = getFileExtension(filePath);
|
|
399
|
+
if (['ts', 'mts', 'cts'].includes(fileExtension))
|
|
400
|
+
return 'ts';
|
|
401
|
+
if (fileExtension === 'tsx')
|
|
402
|
+
return 'tsx';
|
|
403
|
+
if (fileExtension === 'jsx')
|
|
404
|
+
return 'jsx';
|
|
405
|
+
return 'js';
|
|
406
|
+
}
|
|
407
|
+
function isVirtualModule(id) {
|
|
408
|
+
return id.startsWith('\0');
|
|
409
|
+
}
|
|
292
410
|
async function executeTranspiledFile(filePath, code) {
|
|
293
411
|
const { filePathAbsoluteFilesystem } = filePath;
|
|
294
412
|
// Alternative to using a temporary file: https://github.com/vitejs/vite/pull/13269
|
|
@@ -345,17 +463,40 @@ function getConfigBuildErrorFormatted(err) {
|
|
|
345
463
|
const errMsgFormatted = err[formatted];
|
|
346
464
|
return errMsgFormatted;
|
|
347
465
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
color: true,
|
|
353
|
-
}))
|
|
354
|
-
.map((m) => m.trim())
|
|
355
|
-
.join('\n');
|
|
466
|
+
function formatBuildErr(err, filePath) {
|
|
467
|
+
if (!isRolldownBuildError(err))
|
|
468
|
+
return;
|
|
469
|
+
const msgRolldown = err.errors.map((e) => e.message.trim()).join('\n');
|
|
356
470
|
const msgIntro = getErrIntroMsg('transpile', filePath);
|
|
357
|
-
const errMsgFormatted = `${msgIntro}\n${
|
|
358
|
-
|
|
471
|
+
const errMsgFormatted = `${msgIntro}\n${msgRolldown}`;
|
|
472
|
+
// Non-enumerable, otherwise `$ vike build` prints it in addition to the error message
|
|
473
|
+
Object.defineProperty(err, formatted, { value: errMsgFormatted, enumerable: false, configurable: true });
|
|
474
|
+
}
|
|
475
|
+
function isRolldownBuildError(err) {
|
|
476
|
+
return (isObject(err) && Array.isArray(err.errors) && err.errors.every((e) => isObject(e) && typeof e.message === 'string'));
|
|
477
|
+
}
|
|
478
|
+
function getErrVikePlugin(err) {
|
|
479
|
+
if (!isRolldownBuildError(err))
|
|
480
|
+
return null;
|
|
481
|
+
const errVikePlugin = err.errors.find((e) => e.code === 'PLUGIN_ERROR' && e.plugin?.startsWith('vike:'));
|
|
482
|
+
return errVikePlugin ?? null;
|
|
483
|
+
}
|
|
484
|
+
function getErrUnresolvedImports(logs, filePath) {
|
|
485
|
+
const errors = logs.map((log) => ({
|
|
486
|
+
...log,
|
|
487
|
+
// We make the build fail, thus:
|
|
488
|
+
// - Color it as an error (instead of as a warning)
|
|
489
|
+
// - Remove `, treating it as an external dependency` from the message `Module not found, treating it as an external dependency`
|
|
490
|
+
message: log.message
|
|
491
|
+
.replace('\x1b[33m[UNRESOLVED_IMPORT]', '\x1b[31m[UNRESOLVED_IMPORT]')
|
|
492
|
+
.replace(', treating it as an external dependency', '.'),
|
|
493
|
+
}));
|
|
494
|
+
const msg = `Build failed with ${errors.length} error${errors.length === 1 ? '' : 's'}:\n\n${errors.map((e) => e.message).join('\n')}`;
|
|
495
|
+
const err = new Error(msg);
|
|
496
|
+
// Getter (like Rolldown's errors), otherwise `$ vike build` prints it in addition to the error message (Node.js's util.inspect() always prints `error.errors` if it's an array)
|
|
497
|
+
Object.defineProperty(err, 'errors', { get: () => errors, enumerable: true, configurable: true });
|
|
498
|
+
formatBuildErr(err, filePath);
|
|
499
|
+
return err;
|
|
359
500
|
}
|
|
360
501
|
const execErrIntroMsg = new WeakMap();
|
|
361
502
|
function getConfigExecutionErrorIntroMsg(err) {
|
|
@@ -418,26 +559,6 @@ function getErrIntroMsg(operation, filePath) {
|
|
|
418
559
|
const msg = `${pc.red(`Failed to ${operation}`)} ${pc.bold(pc.red(filePathToShowToUserResolved))} ${pc.red(`because:`)}`;
|
|
419
560
|
return msg;
|
|
420
561
|
}
|
|
421
|
-
function cleanEsbuildErrors(errors) {
|
|
422
|
-
errors.forEach((e) => (e.notes = e.notes.filter((note) =>
|
|
423
|
-
// Remove note:
|
|
424
|
-
// ```shell
|
|
425
|
-
// You can mark the path "#root/renderer/onRenderHtml_typo" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle.
|
|
426
|
-
// ```
|
|
427
|
-
//
|
|
428
|
-
// From error:
|
|
429
|
-
// ```shell
|
|
430
|
-
// ✘ [ERROR] Could not resolve "#root/renderer/onRenderHtml_typo" [plugin vike-esbuild]
|
|
431
|
-
//
|
|
432
|
-
// renderer/+config.h.js:1:29:
|
|
433
|
-
// 1 │ import { onRenderHtml } from "#root/renderer/onRenderHtml_typo"
|
|
434
|
-
// ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
435
|
-
//
|
|
436
|
-
// You can mark the path "#root/renderer/onRenderHtml_typo" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle.
|
|
437
|
-
//
|
|
438
|
-
// ```
|
|
439
|
-
!note.text.includes('as external to exclude it from the bundle'))));
|
|
440
|
-
}
|
|
441
562
|
function installSourceMapSupport() {
|
|
442
563
|
// Don't break Vitest's source mapping
|
|
443
564
|
if (isVitest())
|