vike 0.4.149 → 0.4.150-commit-d9acc70

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.
Files changed (41) hide show
  1. package/dist/cjs/node/plugin/plugins/commonConfig/assertResolveAlias.js +3 -0
  2. package/dist/cjs/node/plugin/plugins/importBuild/index.js +13 -7
  3. package/dist/cjs/node/plugin/plugins/importUserCode/index.js +3 -1
  4. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +1 -1
  5. package/dist/cjs/node/runtime/html/stream.js +57 -17
  6. package/dist/cjs/node/runtime/index-deprecated.js +3 -2
  7. package/dist/cjs/node/runtime/index.js +3 -2
  8. package/dist/cjs/node/runtime/renderPage/getHttpResponseBody.js +2 -2
  9. package/dist/cjs/node/runtime/renderPage/getPageAssets/retrieveAssetsDev.js +13 -5
  10. package/dist/cjs/node/runtime/renderPage/getPageAssets/sortPageAssetsForEarlyHintsHeader.js +6 -0
  11. package/dist/cjs/node/runtime/renderPage/inferMediaType.js +38 -0
  12. package/dist/cjs/shared/page-configs/assertExports.js +11 -4
  13. package/dist/cjs/shared/route/resolveRedirects.js +2 -10
  14. package/dist/cjs/shared/route/resolveUrlPathname.js +48 -0
  15. package/dist/cjs/utils/debug.js +29 -15
  16. package/dist/cjs/utils/getOutDirs.js +11 -1
  17. package/dist/cjs/utils/projectInfo.js +1 -1
  18. package/dist/esm/node/plugin/plugins/commonConfig/assertResolveAlias.js +3 -0
  19. package/dist/esm/node/plugin/plugins/importBuild/index.js +13 -7
  20. package/dist/esm/node/plugin/plugins/importUserCode/index.js +3 -1
  21. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/debug.d.ts +1 -1
  22. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +1 -1
  23. package/dist/esm/node/runtime/html/stream.js +57 -17
  24. package/dist/esm/node/runtime/index-deprecated.js +3 -2
  25. package/dist/esm/node/runtime/index.js +3 -2
  26. package/dist/esm/node/runtime/renderPage/getHttpResponseBody.js +2 -2
  27. package/dist/esm/node/runtime/renderPage/getPageAssets/retrieveAssetsDev.js +13 -5
  28. package/dist/esm/node/runtime/renderPage/getPageAssets/sortPageAssetsForEarlyHintsHeader.js +6 -0
  29. package/dist/esm/node/runtime/renderPage/inferMediaType.d.ts +2 -2
  30. package/dist/esm/node/runtime/renderPage/inferMediaType.js +38 -0
  31. package/dist/esm/shared/page-configs/assertExports.js +11 -4
  32. package/dist/esm/shared/route/resolveRedirects.js +2 -10
  33. package/dist/esm/shared/route/resolveUrlPathname.d.ts +12 -0
  34. package/dist/esm/shared/route/resolveUrlPathname.js +45 -0
  35. package/dist/esm/utils/debug.d.ts +2 -2
  36. package/dist/esm/utils/debug.js +29 -15
  37. package/dist/esm/utils/debugGlob.d.ts +1 -1
  38. package/dist/esm/utils/getOutDirs.js +11 -1
  39. package/dist/esm/utils/projectInfo.d.ts +2 -2
  40. package/dist/esm/utils/projectInfo.js +1 -1
  41. package/package.json +2 -2
@@ -40,6 +40,9 @@ function assertResolveAlias(config) {
40
40
  // Allow un-distinguishable aliases set by @preact/preset-vite
41
41
  if (find.startsWith('react'))
42
42
  return;
43
+ // Allow un-distinguishable aliases set by @vitejs/plugin-vue2 https://github.com/vikejs/vike/issues/1329
44
+ if (find === 'vue')
45
+ return;
43
46
  {
44
47
  const msg = `${errPrefix} defines an invalid ${picocolors_1.default.cyan('resolve.alias')}: a path alias cannot be the empty string ${picocolors_1.default.cyan("''")}`;
45
48
  (0, utils_js_1.assertUsage)(find !== '', msg);
@@ -32,19 +32,17 @@ function importBuild() {
32
32
  }
33
33
  exports.importBuild = importBuild;
34
34
  function getImporterCode(config, pageFilesEntry) {
35
- const filePathAbsolute = (0, utils_js_1.toPosixPath)(
36
- // [RELATIVE_PATH_FROM_DIST] Current file: node_modules/vike/dist/esm/node/plugin/plugins/importBuild/index.js
37
- require_.resolve(`../../../../../../dist/esm/node/runtime/globalContext/loadImportBuild.js`));
38
- const { outDirServer } = (0, utils_js_1.getOutDirs)(config);
39
- const filePathRelative = path_1.default.posix.relative(outDirServer, filePathAbsolute);
35
+ const importPath = getImportPath(config);
40
36
  // The only reason we went for using CJS require() instead of ESM import() is because import() doesn't support .json files
41
37
  const importerCode = [
42
38
  '(async () => {',
43
- ` const { setImportBuildGetters } = await import('${filePathRelative}');`,
39
+ ` const { setImportBuildGetters } = await import('${importPath}');`,
44
40
  ' setImportBuildGetters({',
45
41
  ` pageFiles: () => import('./${pageFilesEntry}'),`,
46
- " clientManifest: () => require('../assets.json'),",
42
+ // TODO: rename clientManifest -> assetManifest
43
+ // TODO: rename PluginManifest -> vikeManifest
47
44
  // TODO: use virtual file instead of generating vike.json
45
+ " clientManifest: () => require('../assets.json'),",
48
46
  " pluginManifest: () => require('../client/vike.json'),",
49
47
  ' });',
50
48
  '})()',
@@ -52,3 +50,11 @@ function getImporterCode(config, pageFilesEntry) {
52
50
  ].join('\n');
53
51
  return importerCode;
54
52
  }
53
+ function getImportPath(config) {
54
+ const filePathAbsolute = (0, utils_js_1.toPosixPath)(
55
+ // [RELATIVE_PATH_FROM_DIST] Current file: node_modules/vike/dist/esm/node/plugin/plugins/importBuild/index.js
56
+ require_.resolve(`../../../../../../dist/esm/node/runtime/globalContext/loadImportBuild.js`));
57
+ const { outDirServer } = (0, utils_js_1.getOutDirs)(config);
58
+ const filePathRelative = path_1.default.posix.relative(outDirServer, filePathAbsolute);
59
+ return filePathRelative;
60
+ }
@@ -111,7 +111,9 @@ function handleHotUpdate(ctx, config, configVike) {
111
111
  return;
112
112
  }
113
113
  if (isVikeConfig) {
114
- (0, utils_js_1.assert)(!isViteModule);
114
+ /* Tailwind breaks this assertion, see https://github.com/vikejs/vike/discussions/1330#discussioncomment-7787238
115
+ assert(!isViteModule)
116
+ */
115
117
  reloadConfig(file, config, configVike, 'modified');
116
118
  const virtualModules = getVirtualModules(server);
117
119
  return virtualModules;
@@ -126,5 +126,5 @@ async function isUsingGit(userRootDir) {
126
126
  async function runCmd(cmd, cwd) {
127
127
  const res = await execA(cmd, { cwd });
128
128
  (0, utils_js_1.assert)(res.stderr === '');
129
- return res.stdout.toString().trim().split('\n');
129
+ return res.stdout.toString().split('\n').filter(Boolean);
130
130
  }
@@ -200,7 +200,9 @@ exports.pipeToStreamWritableNode = pipeToStreamWritableNode;
200
200
  async function processStream(streamOriginal, { injectStringAtBegin, injectStringAtEnd, onErrorWhileStreaming, enableEagerStreaming }) {
201
201
  const buffer = [];
202
202
  let streamOriginalHasStartedEmitting = false;
203
- let streamEnded = false;
203
+ let streamOriginalEnded = false;
204
+ let streamClosed = false;
205
+ let onEndWasCalled = false;
204
206
  let isReadyToWrite = false;
205
207
  let wrapperCreated = false;
206
208
  let shouldFlushStream = false;
@@ -224,6 +226,14 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
224
226
  writeStream(injectionBegin); // Adds injectionBegin to buffer
225
227
  flushStream(); // Sets shouldFlushStream to true
226
228
  }
229
+ // We call onStreamEvent() also when the stream ends in order to properly handle the situation when the stream didn't emit any data
230
+ const onStreamDataOrEnd = (cb) => {
231
+ (0, utils_js_1.assert)(streamOriginalEnded === false);
232
+ streamOriginalHasStartedEmitting = true;
233
+ cb();
234
+ if (wrapperCreated)
235
+ resolvePromise();
236
+ };
227
237
  const { streamWrapper, streamWrapperOperations } = await createStreamWrapper({
228
238
  streamOriginal,
229
239
  onReadyToWrite() {
@@ -241,19 +251,21 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
241
251
  }
242
252
  },
243
253
  onData(chunk) {
244
- (0, utils_js_1.assert)(streamEnded === false);
245
- streamOriginalHasStartedEmitting = true;
246
- writeStream(chunk);
247
- if (wrapperCreated)
248
- resolvePromise();
254
+ onStreamDataOrEnd(() => {
255
+ writeStream(chunk);
256
+ });
249
257
  },
250
- async onEnd() {
258
+ async onEnd(
259
+ // Should we use this `isCancel`? Maybe we can skip `injectStringAtEnd()`?
260
+ isCancel) {
251
261
  try {
262
+ (0, utils_js_1.assert)(!onEndWasCalled);
263
+ onEndWasCalled = true;
252
264
  debug('stream end');
253
- streamEnded = true;
254
- streamOriginalHasStartedEmitting = true; // In case original stream (stream provided by user) emits no data
255
- if (wrapperCreated)
256
- resolvePromise(); // In case original stream (stream provided by user) emits no data
265
+ // We call onStreamEvent() also here in case the stream didn't emit any data
266
+ onStreamDataOrEnd(() => {
267
+ streamOriginalEnded = true;
268
+ });
257
269
  if (injectStringAtEnd) {
258
270
  const injectEnd = await injectStringAtEnd();
259
271
  writeStream(injectEnd);
@@ -261,10 +273,12 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
261
273
  await promiseReadyToWrite; // E.g. if the user calls the pipe wrapper after the original writable has ended
262
274
  (0, utils_js_1.assert)(isReady());
263
275
  flushBuffer();
276
+ streamClosed = true;
264
277
  debug('stream ended');
265
278
  }
266
279
  catch (err) {
267
- // We should catch and gracefully handle user land errors, as any error thrown here kills the server
280
+ // Ideally, we should catch and gracefully handle user land errors, as any error thrown here kills the server. (I assume that the fact it kills the server is a Node.js bug?)
281
+ // Show "[vike][Bug] You stumbled upon a bug in Vike's source code" to user while printing original error
268
282
  if (!(0, utils_js_1.isBug)(err)) {
269
283
  console.error(err);
270
284
  (0, utils_js_1.assert)(false);
@@ -288,6 +302,7 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
288
302
  function flushBuffer() {
289
303
  if (!isReady())
290
304
  return;
305
+ (0, utils_js_1.assert)(!streamClosed);
291
306
  buffer.forEach((chunk) => {
292
307
  streamWrapperOperations.writeChunk(chunk);
293
308
  });
@@ -485,6 +500,17 @@ async function createStreamWrapper({ streamOriginal, onError, onData, onEnd, onF
485
500
  if (isStreamReadableWeb(streamOriginal)) {
486
501
  debug('onRenderHtml() hook returned Web Readable');
487
502
  const readableOriginal = streamOriginal;
503
+ let controllerProxyIsClosed = false;
504
+ let isClosed = false;
505
+ let isCancel = false;
506
+ const closeStream = async () => {
507
+ if (isClosed)
508
+ return;
509
+ isClosed = true;
510
+ await onEnd(isCancel);
511
+ controllerProxy.close();
512
+ controllerProxyIsClosed = true;
513
+ };
488
514
  let controllerProxy;
489
515
  assertReadableStreamConstructor();
490
516
  const readableProxy = new ReadableStream({
@@ -498,16 +524,30 @@ async function createStreamWrapper({ streamOriginal, onError, onData, onEnd, onF
498
524
  controllerProxy.close();
499
525
  },
500
526
  async onEnd() {
501
- await onEnd();
502
- controllerProxy.close();
527
+ await closeStream();
503
528
  }
504
529
  });
530
+ },
531
+ async cancel(...args) {
532
+ isCancel = true;
533
+ await readableOriginal.cancel(...args);
534
+ // If readableOriginal has implemented readableOriginal.cancel() then the onEnd() callback and therfore closeStream() may already have been called at this point
535
+ await closeStream();
505
536
  }
506
537
  });
507
538
  const writeChunk = (chunk) => {
508
- controllerProxy.enqueue(encodeForWebStream(chunk));
509
- if (debug.isEnabled) {
510
- debug('data written (Web Readable)', String(chunk));
539
+ if (
540
+ // If readableOriginal doesn't implement readableOriginal.cancel() then it may still emit data after we close the stream. We therefore need to check whether we closed `controllerProxy`.
541
+ !controllerProxyIsClosed) {
542
+ controllerProxy.enqueue(encodeForWebStream(chunk));
543
+ if (debug.isEnabled) {
544
+ debug('data written (Web Readable)', String(chunk));
545
+ }
546
+ }
547
+ else {
548
+ if (debug.isEnabled) {
549
+ debug('data emitted but not written (Web Readable)', String(chunk));
550
+ }
511
551
  }
512
552
  };
513
553
  // Readables don't have the notion of flushing
@@ -20,6 +20,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.RenderErrorPage = void 0;
23
+ const isBrowser_js_1 = require("../../utils/isBrowser.js");
24
+ const assert_js_1 = require("../../utils/assert.js");
25
+ (0, assert_js_1.assertUsage)(!(0, isBrowser_js_1.isBrowser)(), "It's forbidden to `import { something } from 'vike'` on the client-side: the module 'vike' is a server-only module.", { showStackTrace: true });
23
26
  __exportStar(require("./index-common.js"), exports);
24
27
  __exportStar(require("../../types/index-dreprecated.js"), exports);
25
28
  const utils_js_1 = require("./utils.js");
@@ -57,5 +60,3 @@ exports.RenderErrorPage = RenderErrorPage;
57
60
  picocolors_1.default.green(" import type { something } from 'vike/types'"),
58
61
  "Make sure to import renderPage(), escapeInject, html, dangerouslySkipEscape(), pipeWebStream(), pipeNodeStream(), pipeStream(), stampPipe() from 'vike/server'. (Or inspect the error stack below to find the import causing this warning.)"
59
62
  ].join('\n'), { showStackTrace: true, onlyOnce: true });
60
- const utils_js_2 = require("./utils.js");
61
- (0, utils_js_2.assertUsage)(!(0, utils_js_2.isBrowser)(), "It's forbidden to `import { something } from 'vike'` in code loaded in the browser: the module 'vike' is a server-only module.", { showStackTrace: true });
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ const isBrowser_js_1 = require("../../utils/isBrowser.js");
18
+ const assert_js_1 = require("../../utils/assert.js");
19
+ (0, assert_js_1.assertUsage)(!(0, isBrowser_js_1.isBrowser)(), "It's forbidden to `import { something } from 'vike/server'` on the client-side: the module 'vike/server' is a server-only module.", { showStackTrace: true });
17
20
  __exportStar(require("./index-common.js"), exports);
18
- const utils_js_1 = require("./utils.js");
19
- (0, utils_js_1.assertUsage)(!(0, utils_js_1.isBrowser)(), "It's forbidden to `import { something } from 'vike/server'` in code loaded on the client-side: the module 'vike/server' is a server-only module.", { showStackTrace: true });
@@ -76,7 +76,7 @@ function getHttpResponseBodyStreamHandlers(htmlRender, renderHook) {
76
76
  return;
77
77
  }
78
78
  else {
79
- (0, utils_js_1.assert)((0, stream_js_1.isStreamReadableWeb)(htmlRender) || (0, stream_js_1.isStreamPipeWeb)(htmlRender));
79
+ (0, utils_js_1.assert)((0, stream_js_1.isStreamReadableNode)(htmlRender) || (0, stream_js_1.isStreamPipeNode)(htmlRender));
80
80
  (0, utils_js_1.assertUsage)(false, getErrMsgMixingStreamTypes('Web Writable'));
81
81
  }
82
82
  }
@@ -86,7 +86,7 @@ function getHttpResponseBodyStreamHandlers(htmlRender, renderHook) {
86
86
  return;
87
87
  }
88
88
  else {
89
- (0, utils_js_1.assert)((0, stream_js_1.isStreamReadableNode)(htmlRender) || (0, stream_js_1.isStreamPipeNode)(htmlRender));
89
+ (0, utils_js_1.assert)((0, stream_js_1.isStreamReadableWeb)(htmlRender) || (0, stream_js_1.isStreamPipeWeb)(htmlRender));
90
90
  (0, utils_js_1.assertUsage)(false, getErrMsgMixingStreamTypes('Node.js Writable'));
91
91
  }
92
92
  }
@@ -40,13 +40,21 @@ function collectCss(mod, styleUrls, visitedModules, importer) {
40
40
  if (mod.url.startsWith('/')) {
41
41
  styleUrls.add(mod.url);
42
42
  }
43
+ else if (mod.url.startsWith('\0')) {
44
+ // Virtual modules
45
+ // - https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention
46
+ // - I believe some Vite plugins don't respect the \0 virtual module convention. What should we do then?
47
+ // - https://github.com/vikejs/vike/issues/1327
48
+ // - https://github.com/vikejs/vike/commit/3f7b9916dddc84e29e2c20d2b0df7211b6f1acbd
49
+ styleUrls.add(`/@id/${mod.url.substring(1)}`);
50
+ }
43
51
  else {
44
- // Vuetify uses virtual SCSS modules which we skip
45
- // - We skip because `<link rel="stylesheet" type="text/css" href="virtual-module.css">` doesn't work
46
- // - Reproduction: https://github.com/vikejs/vike/issues/479
47
- // - Possible workaround: `<script>import 'virtual-module.css'</script>`
48
- // logModule(mod)
52
+ // Is this useful? Maybe for virtual modules that don't respect the \0 virtual module convention?
53
+ styleUrls.add(`/@id/${mod.url}`);
49
54
  }
55
+ /* Debug:
56
+ logModule(mod)
57
+ //*/
50
58
  }
51
59
  mod.importedModules.forEach((dep) => {
52
60
  collectCss(dep, styleUrls, visitedModules, mod);
@@ -22,6 +22,12 @@ function sortPageAssetsForEarlyHintsHeader(pageAssets) {
22
22
  if (assetType === 'image')
23
23
  return priority;
24
24
  priority--;
25
+ if (assetType === 'video')
26
+ return priority;
27
+ priority--;
28
+ if (assetType === 'audio')
29
+ return priority;
30
+ priority--;
25
31
  // Others
26
32
  if (assetType !== 'script')
27
33
  return priority;
@@ -36,6 +36,44 @@ function inferMediaType(href) {
36
36
  if (href.endsWith('.woff2')) {
37
37
  return { assetType: 'font', mediaType: 'font/woff2' };
38
38
  }
39
+ // Videos
40
+ if (href.endsWith('.mp4')) {
41
+ return { assetType: 'video', mediaType: 'video/mp4' };
42
+ }
43
+ if (href.endsWith('.webm')) {
44
+ return { assetType: 'video', mediaType: 'video/webm' };
45
+ }
46
+ if (href.endsWith('.ogv')) {
47
+ return { assetType: 'video', mediaType: 'video/ogg' };
48
+ }
49
+ if (href.endsWith('.mpeg') || href.endsWith('.mpg')) {
50
+ return { assetType: 'video', mediaType: 'video/mpeg' };
51
+ }
52
+ if (href.endsWith('.avi')) {
53
+ return { assetType: 'video', mediaType: 'video/x-msvideo' };
54
+ }
55
+ if (href.endsWith('.mov') || href.endsWith('.qt')) {
56
+ return { assetType: 'video', mediaType: 'video/quicktime' };
57
+ }
58
+ // Audios
59
+ if (href.endsWith('.mp3')) {
60
+ return { assetType: 'audio', mediaType: 'audio/mpeg' };
61
+ }
62
+ if (href.endsWith('.wav')) {
63
+ return { assetType: 'audio', mediaType: 'audio/wav' };
64
+ }
65
+ if (href.endsWith('.ogg')) {
66
+ return { assetType: 'audio', mediaType: 'audio/ogg' };
67
+ }
68
+ if (href.endsWith('.m4a')) {
69
+ return { assetType: 'audio', mediaType: 'audio/aac' };
70
+ }
71
+ if (href.endsWith('midi') || href.endsWith('.mid')) {
72
+ return { assetType: 'audio', mediaType: 'audio/midi' };
73
+ }
74
+ if (href.endsWith('.flac')) {
75
+ return { assetType: 'audio', mediaType: 'audio/flac' };
76
+ }
39
77
  return null;
40
78
  }
41
79
  exports.inferMediaType = inferMediaType;
@@ -34,13 +34,20 @@ function assertExports(fileExports, filePathToShowToUser, configName) {
34
34
  if (exportsRelevant.length === 1) {
35
35
  return;
36
36
  }
37
- else {
38
- (0, utils_js_1.assert)(exportsRelevant.length === 0);
39
- let errMsg = `${filePathToShowToUser} doesn't export any value, but it should have a ${picocolors_1.default.cyan('export default')}`;
37
+ const exportDefault = picocolors_1.default.cyan('export default');
38
+ const exportConfigName = picocolors_1.default.cyan(`export { ${configName} }`);
39
+ if (exportsRelevant.length === 0) {
40
+ let errMsg = `${filePathToShowToUser} doesn't export any value, but it should have a ${exportDefault}`;
40
41
  if (configName)
41
- errMsg += ` or ${picocolors_1.default.cyan(`export { ${configName} }`)}`;
42
+ errMsg += ` or ${exportConfigName}`;
42
43
  (0, utils_js_1.assertUsage)(false, errMsg);
43
44
  }
45
+ else {
46
+ (0, utils_js_1.assert)(exportsRelevant.length === 2);
47
+ (0, utils_js_1.assertWarning)(false, `${filePathToShowToUser} remove ${exportConfigName} or ${exportDefault}`, {
48
+ onlyOnce: true
49
+ });
50
+ }
44
51
  }
45
52
  else {
46
53
  // !configName => isConfigFile
@@ -7,6 +7,7 @@ exports.resolveRouteStringRedirect = exports.resolveRedirects = void 0;
7
7
  const assertIsNotBrowser_js_1 = require("../../utils/assertIsNotBrowser.js");
8
8
  const parseUrl_extras_js_1 = require("../../utils/parseUrl-extras.js");
9
9
  const utils_js_1 = require("../utils.js");
10
+ const resolveUrlPathname_js_1 = require("./resolveUrlPathname.js");
10
11
  const resolveRouteString_js_1 = require("./resolveRouteString.js");
11
12
  const picocolors_1 = __importDefault(require("@brillout/picocolors"));
12
13
  (0, assertIsNotBrowser_js_1.assertIsNotBrowser)(); // Don't bloat the client
@@ -31,16 +32,7 @@ function resolveRouteStringRedirect(urlSource, urlTarget, urlPathname) {
31
32
  const match = (0, resolveRouteString_js_1.resolveRouteString)(urlSource, urlPathname);
32
33
  if (!match)
33
34
  return null;
34
- let urlResolved = urlTarget;
35
- Object.entries(match.routeParams).forEach(([key, val]) => {
36
- if (key !== '*') {
37
- key = `@${key}`;
38
- }
39
- urlResolved = urlResolved.replaceAll(key, val);
40
- });
41
- if (!urlResolved.startsWith('mailto:')) {
42
- (0, utils_js_1.assertUsage)(!urlResolved.includes('@'), 'URL should not contain "@" unless it is a mailto link.');
43
- }
35
+ const urlResolved = (0, resolveUrlPathname_js_1.resolveUrlPathname)(urlTarget, match.routeParams);
44
36
  if (urlResolved === urlPathname)
45
37
  return null;
46
38
  (0, utils_js_1.assert)(urlResolved.startsWith('/') || (0, parseUrl_extras_js_1.isUriWithProtocol)(urlResolved));
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveUrlPathname = void 0;
4
+ const assertIsNotBrowser_js_1 = require("../../utils/assertIsNotBrowser.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ (0, assertIsNotBrowser_js_1.assertIsNotBrowser)(); // Don't bloat the client
7
+ /** Given a `routeString` and `routeParams`, resolve `urlPathname`.
8
+ *
9
+ * Basically, the correct implementation of following:
10
+ * ```js
11
+ * let urlPathname = routeString
12
+ * Object.entries(routeParams).forEach(([key, val]) => {
13
+ * urlPathname = urlPathname.replaceAll(key, val)
14
+ * })
15
+ * ```
16
+ */
17
+ function resolveUrlPathname(routeString, routeParams) {
18
+ let parts = [{ val: routeString, type: 'ROUTE_STRING' }];
19
+ Object.entries(routeParams).forEach(([key, val]) => {
20
+ if (key.startsWith('*')) {
21
+ (0, utils_js_1.assert)(key === '*' || /\d+/.test(key.slice(1)));
22
+ (0, utils_js_1.assertUsage)(key === '*', "Resolving URL with multiple globs isn't implemented yet");
23
+ }
24
+ else {
25
+ key = `@${key}`;
26
+ }
27
+ parts = parts
28
+ .map((part) => {
29
+ if (part.type === 'URL') {
30
+ return part;
31
+ }
32
+ else {
33
+ return part.val
34
+ .split(key)
35
+ .map((rest, i) => {
36
+ const partURL = { val, type: 'URL' };
37
+ const partRouteString = { val: rest, type: 'ROUTE_STRING' };
38
+ return i === 0 ? [partRouteString] : [partURL, partRouteString];
39
+ })
40
+ .flat();
41
+ }
42
+ })
43
+ .flat();
44
+ });
45
+ const urlPathname = parts.map((p) => p.val).join('');
46
+ return urlPathname;
47
+ }
48
+ exports.resolveUrlPathname = resolveUrlPathname;
@@ -12,22 +12,10 @@ const getTerminWidth_js_1 = require("./getTerminWidth.js");
12
12
  globalThis.__brillout_debug_createDebugger = createDebugger;
13
13
  function createDebugger(namespace, optionsGlobal) {
14
14
  (0, checkType_js_1.checkType)(namespace);
15
- const debugWithOptions = (options) => {
15
+ const debugWithOptions = (optionsLocal) => {
16
16
  return (...msgs) => {
17
- if (!isDebugEnabled(namespace))
18
- return;
19
- let [msgFirst, ...msgsRest] = msgs;
20
- const padding = ' '.repeat(namespace.length + 1);
21
- const optionsResolved = { ...optionsGlobal, ...options };
22
- msgFirst = formatMsg(msgFirst, optionsResolved, padding, 'FIRST');
23
- msgsRest = msgsRest.map((msg, i) => {
24
- const position = i === msgsRest.length - 1 ? 'LAST' : 'MIDDLE';
25
- return formatMsg(msg, optionsResolved, padding, position);
26
- });
27
- console.log('\x1b[1m%s\x1b[0m', namespace, msgFirst);
28
- msgsRest.forEach((msg) => {
29
- console.log(msg);
30
- });
17
+ const options = { ...optionsGlobal, ...optionsLocal };
18
+ debug_(namespace, options, ...msgs);
31
19
  };
32
20
  };
33
21
  const debug = (...msgs) => debugWithOptions({})(...msgs);
@@ -35,6 +23,32 @@ function createDebugger(namespace, optionsGlobal) {
35
23
  return debug;
36
24
  }
37
25
  exports.createDebugger = createDebugger;
26
+ function debug_(namespace, options, ...msgs) {
27
+ if (!isDebugEnabled(namespace))
28
+ return;
29
+ let [msgFirst, ...msgsRest] = msgs;
30
+ const padding = ' '.repeat(namespace.length + 1);
31
+ msgFirst = formatMsg(msgFirst, options, padding, 'FIRST');
32
+ msgsRest = msgsRest.map((msg, i) => {
33
+ const position = i === msgsRest.length - 1 ? 'LAST' : 'MIDDLE';
34
+ return formatMsg(msg, options, padding, position);
35
+ });
36
+ let logFirst;
37
+ let logsRest;
38
+ const noNewLine = msgsRest.length <= 1 && [msgFirst, ...msgsRest].every((m) => typeof m === 'string' && !m.includes('\n'));
39
+ if (noNewLine) {
40
+ logFirst = [msgFirst, ...msgsRest].map((m) => String(m).trim());
41
+ logsRest = [];
42
+ }
43
+ else {
44
+ logFirst = [msgFirst];
45
+ logsRest = msgsRest;
46
+ }
47
+ console.log('\x1b[1m%s\x1b[0m', namespace, ...logFirst);
48
+ logsRest.forEach((msg) => {
49
+ console.log(msg);
50
+ });
51
+ }
38
52
  function isDebugEnabled(namespace) {
39
53
  (0, checkType_js_1.checkType)(namespace);
40
54
  let DEBUG;
@@ -9,7 +9,10 @@ const assert_js_1 = require("./assert.js");
9
9
  const path_shim_js_1 = require("./path-shim.js");
10
10
  const filesystemPathHandling_js_1 = require("./filesystemPathHandling.js");
11
11
  const picocolors_1 = __importDefault(require("@brillout/picocolors"));
12
+ const debug_js_1 = require("./debug.js");
13
+ const debug = (0, debug_js_1.createDebugger)('vike:outDir');
12
14
  function getOutDirs(config) {
15
+ debug('getOutDirs()', new Error().stack);
13
16
  let outDirRoot;
14
17
  {
15
18
  const outDir = getOutDirFromViteResolvedConfig(config);
@@ -23,12 +26,17 @@ function getOutDirs(config) {
23
26
  outDirRoot = outDir.slice(0, -1 * '/client'.length);
24
27
  }
25
28
  }
26
- return getOutDirsAll(outDirRoot, config.root);
29
+ const outDirs = getOutDirsAll(outDirRoot, config.root);
30
+ debug('outDirRoot', outDirRoot);
31
+ debug('outDirs', outDirs);
32
+ return outDirs;
27
33
  }
28
34
  exports.getOutDirs = getOutDirs;
29
35
  /** Appends `client/` or `server/` to `config.build.outDir` */
30
36
  function resolveOutDir(config) {
37
+ debug('resolveOutDir()', new Error().stack);
31
38
  const outDir = getOutDirFromViteUserConfig(config) || 'dist';
39
+ debug('outDir', 'outDir');
32
40
  // outDir may already be resolved when using Telefunc + vike (because both Telefunc and vike use this logic)
33
41
  if (!isOutDirRoot(outDir)) {
34
42
  assertOutDirResolved(outDir, config);
@@ -37,9 +45,11 @@ function resolveOutDir(config) {
37
45
  else {
38
46
  const { outDirClient, outDirServer } = determineOutDirs(outDir);
39
47
  if ((0, viteIsSSR_js_1.viteIsSSR)(config)) {
48
+ debug('outDirServer', 'outDirServer');
40
49
  return outDirServer;
41
50
  }
42
51
  else {
52
+ debug('outDirClient', 'outDirClient');
43
53
  return outDirClient;
44
54
  }
45
55
  }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PROJECT_VERSION = exports.projectInfo = void 0;
4
4
  const assertSingleInstance_js_1 = require("./assertSingleInstance.js");
5
- const PROJECT_VERSION = '0.4.149';
5
+ const PROJECT_VERSION = '0.4.150-commit-d9acc70';
6
6
  exports.PROJECT_VERSION = PROJECT_VERSION;
7
7
  const projectInfo = {
8
8
  projectName: 'Vike',
@@ -35,6 +35,9 @@ function assertResolveAlias(config) {
35
35
  // Allow un-distinguishable aliases set by @preact/preset-vite
36
36
  if (find.startsWith('react'))
37
37
  return;
38
+ // Allow un-distinguishable aliases set by @vitejs/plugin-vue2 https://github.com/vikejs/vike/issues/1329
39
+ if (find === 'vue')
40
+ return;
38
41
  {
39
42
  const msg = `${errPrefix} defines an invalid ${pc.cyan('resolve.alias')}: a path alias cannot be the empty string ${pc.cyan("''")}`;
40
43
  assertUsage(find !== '', msg);
@@ -26,19 +26,17 @@ function importBuild() {
26
26
  ];
27
27
  }
28
28
  function getImporterCode(config, pageFilesEntry) {
29
- const filePathAbsolute = toPosixPath(
30
- // [RELATIVE_PATH_FROM_DIST] Current file: node_modules/vike/dist/esm/node/plugin/plugins/importBuild/index.js
31
- require_.resolve(`../../../../../../dist/esm/node/runtime/globalContext/loadImportBuild.js`));
32
- const { outDirServer } = getOutDirs(config);
33
- const filePathRelative = path.posix.relative(outDirServer, filePathAbsolute);
29
+ const importPath = getImportPath(config);
34
30
  // The only reason we went for using CJS require() instead of ESM import() is because import() doesn't support .json files
35
31
  const importerCode = [
36
32
  '(async () => {',
37
- ` const { setImportBuildGetters } = await import('${filePathRelative}');`,
33
+ ` const { setImportBuildGetters } = await import('${importPath}');`,
38
34
  ' setImportBuildGetters({',
39
35
  ` pageFiles: () => import('./${pageFilesEntry}'),`,
40
- " clientManifest: () => require('../assets.json'),",
36
+ // TODO: rename clientManifest -> assetManifest
37
+ // TODO: rename PluginManifest -> vikeManifest
41
38
  // TODO: use virtual file instead of generating vike.json
39
+ " clientManifest: () => require('../assets.json'),",
42
40
  " pluginManifest: () => require('../client/vike.json'),",
43
41
  ' });',
44
42
  '})()',
@@ -46,3 +44,11 @@ function getImporterCode(config, pageFilesEntry) {
46
44
  ].join('\n');
47
45
  return importerCode;
48
46
  }
47
+ function getImportPath(config) {
48
+ const filePathAbsolute = toPosixPath(
49
+ // [RELATIVE_PATH_FROM_DIST] Current file: node_modules/vike/dist/esm/node/plugin/plugins/importBuild/index.js
50
+ require_.resolve(`../../../../../../dist/esm/node/runtime/globalContext/loadImportBuild.js`));
51
+ const { outDirServer } = getOutDirs(config);
52
+ const filePathRelative = path.posix.relative(outDirServer, filePathAbsolute);
53
+ return filePathRelative;
54
+ }
@@ -105,7 +105,9 @@ function handleHotUpdate(ctx, config, configVike) {
105
105
  return;
106
106
  }
107
107
  if (isVikeConfig) {
108
- assert(!isViteModule);
108
+ /* Tailwind breaks this assertion, see https://github.com/vikejs/vike/discussions/1330#discussioncomment-7787238
109
+ assert(!isViteModule)
110
+ */
109
111
  reloadConfig(file, config, configVike, 'modified');
110
112
  const virtualModules = getVirtualModules(server);
111
113
  return virtualModules;
@@ -1,5 +1,5 @@
1
1
  export declare const debug: ((...msgs: unknown[]) => void) & {
2
- options: (options: {
2
+ options: (optionsLocal: {
3
3
  serialization?: {
4
4
  emptyArray?: string | undefined;
5
5
  } | undefined;
@@ -120,5 +120,5 @@ async function isUsingGit(userRootDir) {
120
120
  async function runCmd(cmd, cwd) {
121
121
  const res = await execA(cmd, { cwd });
122
122
  assert(res.stderr === '');
123
- return res.stdout.toString().trim().split('\n');
123
+ return res.stdout.toString().split('\n').filter(Boolean);
124
124
  }
@@ -205,7 +205,9 @@ function pipeToStreamWritableNode(htmlRender, writable) {
205
205
  async function processStream(streamOriginal, { injectStringAtBegin, injectStringAtEnd, onErrorWhileStreaming, enableEagerStreaming }) {
206
206
  const buffer = [];
207
207
  let streamOriginalHasStartedEmitting = false;
208
- let streamEnded = false;
208
+ let streamOriginalEnded = false;
209
+ let streamClosed = false;
210
+ let onEndWasCalled = false;
209
211
  let isReadyToWrite = false;
210
212
  let wrapperCreated = false;
211
213
  let shouldFlushStream = false;
@@ -229,6 +231,14 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
229
231
  writeStream(injectionBegin); // Adds injectionBegin to buffer
230
232
  flushStream(); // Sets shouldFlushStream to true
231
233
  }
234
+ // We call onStreamEvent() also when the stream ends in order to properly handle the situation when the stream didn't emit any data
235
+ const onStreamDataOrEnd = (cb) => {
236
+ assert(streamOriginalEnded === false);
237
+ streamOriginalHasStartedEmitting = true;
238
+ cb();
239
+ if (wrapperCreated)
240
+ resolvePromise();
241
+ };
232
242
  const { streamWrapper, streamWrapperOperations } = await createStreamWrapper({
233
243
  streamOriginal,
234
244
  onReadyToWrite() {
@@ -246,19 +256,21 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
246
256
  }
247
257
  },
248
258
  onData(chunk) {
249
- assert(streamEnded === false);
250
- streamOriginalHasStartedEmitting = true;
251
- writeStream(chunk);
252
- if (wrapperCreated)
253
- resolvePromise();
259
+ onStreamDataOrEnd(() => {
260
+ writeStream(chunk);
261
+ });
254
262
  },
255
- async onEnd() {
263
+ async onEnd(
264
+ // Should we use this `isCancel`? Maybe we can skip `injectStringAtEnd()`?
265
+ isCancel) {
256
266
  try {
267
+ assert(!onEndWasCalled);
268
+ onEndWasCalled = true;
257
269
  debug('stream end');
258
- streamEnded = true;
259
- streamOriginalHasStartedEmitting = true; // In case original stream (stream provided by user) emits no data
260
- if (wrapperCreated)
261
- resolvePromise(); // In case original stream (stream provided by user) emits no data
270
+ // We call onStreamEvent() also here in case the stream didn't emit any data
271
+ onStreamDataOrEnd(() => {
272
+ streamOriginalEnded = true;
273
+ });
262
274
  if (injectStringAtEnd) {
263
275
  const injectEnd = await injectStringAtEnd();
264
276
  writeStream(injectEnd);
@@ -266,10 +278,12 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
266
278
  await promiseReadyToWrite; // E.g. if the user calls the pipe wrapper after the original writable has ended
267
279
  assert(isReady());
268
280
  flushBuffer();
281
+ streamClosed = true;
269
282
  debug('stream ended');
270
283
  }
271
284
  catch (err) {
272
- // We should catch and gracefully handle user land errors, as any error thrown here kills the server
285
+ // Ideally, we should catch and gracefully handle user land errors, as any error thrown here kills the server. (I assume that the fact it kills the server is a Node.js bug?)
286
+ // Show "[vike][Bug] You stumbled upon a bug in Vike's source code" to user while printing original error
273
287
  if (!isBug(err)) {
274
288
  console.error(err);
275
289
  assert(false);
@@ -293,6 +307,7 @@ async function processStream(streamOriginal, { injectStringAtBegin, injectString
293
307
  function flushBuffer() {
294
308
  if (!isReady())
295
309
  return;
310
+ assert(!streamClosed);
296
311
  buffer.forEach((chunk) => {
297
312
  streamWrapperOperations.writeChunk(chunk);
298
313
  });
@@ -489,6 +504,17 @@ async function createStreamWrapper({ streamOriginal, onError, onData, onEnd, onF
489
504
  if (isStreamReadableWeb(streamOriginal)) {
490
505
  debug('onRenderHtml() hook returned Web Readable');
491
506
  const readableOriginal = streamOriginal;
507
+ let controllerProxyIsClosed = false;
508
+ let isClosed = false;
509
+ let isCancel = false;
510
+ const closeStream = async () => {
511
+ if (isClosed)
512
+ return;
513
+ isClosed = true;
514
+ await onEnd(isCancel);
515
+ controllerProxy.close();
516
+ controllerProxyIsClosed = true;
517
+ };
492
518
  let controllerProxy;
493
519
  assertReadableStreamConstructor();
494
520
  const readableProxy = new ReadableStream({
@@ -502,16 +528,30 @@ async function createStreamWrapper({ streamOriginal, onError, onData, onEnd, onF
502
528
  controllerProxy.close();
503
529
  },
504
530
  async onEnd() {
505
- await onEnd();
506
- controllerProxy.close();
531
+ await closeStream();
507
532
  }
508
533
  });
534
+ },
535
+ async cancel(...args) {
536
+ isCancel = true;
537
+ await readableOriginal.cancel(...args);
538
+ // If readableOriginal has implemented readableOriginal.cancel() then the onEnd() callback and therfore closeStream() may already have been called at this point
539
+ await closeStream();
509
540
  }
510
541
  });
511
542
  const writeChunk = (chunk) => {
512
- controllerProxy.enqueue(encodeForWebStream(chunk));
513
- if (debug.isEnabled) {
514
- debug('data written (Web Readable)', String(chunk));
543
+ if (
544
+ // If readableOriginal doesn't implement readableOriginal.cancel() then it may still emit data after we close the stream. We therefore need to check whether we closed `controllerProxy`.
545
+ !controllerProxyIsClosed) {
546
+ controllerProxy.enqueue(encodeForWebStream(chunk));
547
+ if (debug.isEnabled) {
548
+ debug('data written (Web Readable)', String(chunk));
549
+ }
550
+ }
551
+ else {
552
+ if (debug.isEnabled) {
553
+ debug('data emitted but not written (Web Readable)', String(chunk));
554
+ }
515
555
  }
516
556
  };
517
557
  // Readables don't have the notion of flushing
@@ -1,5 +1,8 @@
1
1
  // TODO/v1-release: replace this with:
2
2
  // assertUsage(false, "`import { something } from 'vike'` doesn't exist: instead import from 'vike/server', 'vike/client', 'vike/plugin', ...")
3
+ import { isBrowser } from '../../utils/isBrowser.js';
4
+ import { assertUsage } from '../../utils/assert.js';
5
+ assertUsage(!isBrowser(), "It's forbidden to `import { something } from 'vike'` on the client-side: the module 'vike' is a server-only module.", { showStackTrace: true });
3
6
  export * from './index-common.js';
4
7
  export * from '../../types/index-dreprecated.js';
5
8
  import { assertWarning } from './utils.js';
@@ -36,5 +39,3 @@ assertWarning(false, [
36
39
  pc.green(" import type { something } from 'vike/types'"),
37
40
  "Make sure to import renderPage(), escapeInject, html, dangerouslySkipEscape(), pipeWebStream(), pipeNodeStream(), pipeStream(), stampPipe() from 'vike/server'. (Or inspect the error stack below to find the import causing this warning.)"
38
41
  ].join('\n'), { showStackTrace: true, onlyOnce: true });
39
- import { isBrowser, assertUsage } from './utils.js';
40
- assertUsage(!isBrowser(), "It's forbidden to `import { something } from 'vike'` in code loaded in the browser: the module 'vike' is a server-only module.", { showStackTrace: true });
@@ -1,3 +1,4 @@
1
+ import { isBrowser } from '../../utils/isBrowser.js';
2
+ import { assertUsage } from '../../utils/assert.js';
3
+ assertUsage(!isBrowser(), "It's forbidden to `import { something } from 'vike/server'` on the client-side: the module 'vike/server' is a server-only module.", { showStackTrace: true });
1
4
  export * from './index-common.js';
2
- import { isBrowser, assertUsage } from './utils.js';
3
- assertUsage(!isBrowser(), "It's forbidden to `import { something } from 'vike/server'` in code loaded on the client-side: the module 'vike/server' is a server-only module.", { showStackTrace: true });
@@ -71,7 +71,7 @@ function getHttpResponseBodyStreamHandlers(htmlRender, renderHook) {
71
71
  return;
72
72
  }
73
73
  else {
74
- assert(isStreamReadableWeb(htmlRender) || isStreamPipeWeb(htmlRender));
74
+ assert(isStreamReadableNode(htmlRender) || isStreamPipeNode(htmlRender));
75
75
  assertUsage(false, getErrMsgMixingStreamTypes('Web Writable'));
76
76
  }
77
77
  }
@@ -81,7 +81,7 @@ function getHttpResponseBodyStreamHandlers(htmlRender, renderHook) {
81
81
  return;
82
82
  }
83
83
  else {
84
- assert(isStreamReadableNode(htmlRender) || isStreamPipeNode(htmlRender));
84
+ assert(isStreamReadableWeb(htmlRender) || isStreamPipeWeb(htmlRender));
85
85
  assertUsage(false, getErrMsgMixingStreamTypes('Node.js Writable'));
86
86
  }
87
87
  }
@@ -37,13 +37,21 @@ function collectCss(mod, styleUrls, visitedModules, importer) {
37
37
  if (mod.url.startsWith('/')) {
38
38
  styleUrls.add(mod.url);
39
39
  }
40
+ else if (mod.url.startsWith('\0')) {
41
+ // Virtual modules
42
+ // - https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention
43
+ // - I believe some Vite plugins don't respect the \0 virtual module convention. What should we do then?
44
+ // - https://github.com/vikejs/vike/issues/1327
45
+ // - https://github.com/vikejs/vike/commit/3f7b9916dddc84e29e2c20d2b0df7211b6f1acbd
46
+ styleUrls.add(`/@id/${mod.url.substring(1)}`);
47
+ }
40
48
  else {
41
- // Vuetify uses virtual SCSS modules which we skip
42
- // - We skip because `<link rel="stylesheet" type="text/css" href="virtual-module.css">` doesn't work
43
- // - Reproduction: https://github.com/vikejs/vike/issues/479
44
- // - Possible workaround: `<script>import 'virtual-module.css'</script>`
45
- // logModule(mod)
49
+ // Is this useful? Maybe for virtual modules that don't respect the \0 virtual module convention?
50
+ styleUrls.add(`/@id/${mod.url}`);
46
51
  }
52
+ /* Debug:
53
+ logModule(mod)
54
+ //*/
47
55
  }
48
56
  mod.importedModules.forEach((dep) => {
49
57
  collectCss(dep, styleUrls, visitedModules, mod);
@@ -20,6 +20,12 @@ function sortPageAssetsForEarlyHintsHeader(pageAssets) {
20
20
  if (assetType === 'image')
21
21
  return priority;
22
22
  priority--;
23
+ if (assetType === 'video')
24
+ return priority;
25
+ priority--;
26
+ if (assetType === 'audio')
27
+ return priority;
28
+ priority--;
23
29
  // Others
24
30
  if (assetType !== 'script')
25
31
  return priority;
@@ -1,7 +1,7 @@
1
1
  export { inferMediaType };
2
2
  export type { MediaType };
3
3
  type MediaType = null | {
4
- assetType: 'image' | 'script' | 'font' | 'style';
5
- mediaType: 'text/javascript' | 'text/css' | 'image/jpeg' | 'image/png' | 'image/webp' | 'image/gif' | 'image/svg+xml' | 'font/ttf' | 'font/woff' | 'font/woff2';
4
+ assetType: 'image' | 'script' | 'font' | 'style' | 'audio' | 'video' | 'document' | 'fetch' | 'track' | 'worker' | 'embed' | 'object';
5
+ mediaType: 'text/javascript' | 'text/css' | 'image/jpeg' | 'image/png' | 'image/webp' | 'image/gif' | 'image/svg+xml' | 'font/ttf' | 'font/woff' | 'font/woff2' | 'video/mp4' | 'video/webm' | 'video/ogg' | 'video/mpeg' | 'video/x-msvideo' | 'video/quicktime' | 'audio/mpeg' | 'audio/wav' | 'audio/ogg' | 'audio/aac' | 'audio/midi' | 'audio/flac';
6
6
  };
7
7
  declare function inferMediaType(href: string): MediaType;
@@ -34,5 +34,43 @@ function inferMediaType(href) {
34
34
  if (href.endsWith('.woff2')) {
35
35
  return { assetType: 'font', mediaType: 'font/woff2' };
36
36
  }
37
+ // Videos
38
+ if (href.endsWith('.mp4')) {
39
+ return { assetType: 'video', mediaType: 'video/mp4' };
40
+ }
41
+ if (href.endsWith('.webm')) {
42
+ return { assetType: 'video', mediaType: 'video/webm' };
43
+ }
44
+ if (href.endsWith('.ogv')) {
45
+ return { assetType: 'video', mediaType: 'video/ogg' };
46
+ }
47
+ if (href.endsWith('.mpeg') || href.endsWith('.mpg')) {
48
+ return { assetType: 'video', mediaType: 'video/mpeg' };
49
+ }
50
+ if (href.endsWith('.avi')) {
51
+ return { assetType: 'video', mediaType: 'video/x-msvideo' };
52
+ }
53
+ if (href.endsWith('.mov') || href.endsWith('.qt')) {
54
+ return { assetType: 'video', mediaType: 'video/quicktime' };
55
+ }
56
+ // Audios
57
+ if (href.endsWith('.mp3')) {
58
+ return { assetType: 'audio', mediaType: 'audio/mpeg' };
59
+ }
60
+ if (href.endsWith('.wav')) {
61
+ return { assetType: 'audio', mediaType: 'audio/wav' };
62
+ }
63
+ if (href.endsWith('.ogg')) {
64
+ return { assetType: 'audio', mediaType: 'audio/ogg' };
65
+ }
66
+ if (href.endsWith('.m4a')) {
67
+ return { assetType: 'audio', mediaType: 'audio/aac' };
68
+ }
69
+ if (href.endsWith('midi') || href.endsWith('.mid')) {
70
+ return { assetType: 'audio', mediaType: 'audio/midi' };
71
+ }
72
+ if (href.endsWith('.flac')) {
73
+ return { assetType: 'audio', mediaType: 'audio/flac' };
74
+ }
37
75
  return null;
38
76
  }
@@ -28,13 +28,20 @@ function assertExports(fileExports, filePathToShowToUser, configName) {
28
28
  if (exportsRelevant.length === 1) {
29
29
  return;
30
30
  }
31
- else {
32
- assert(exportsRelevant.length === 0);
33
- let errMsg = `${filePathToShowToUser} doesn't export any value, but it should have a ${pc.cyan('export default')}`;
31
+ const exportDefault = pc.cyan('export default');
32
+ const exportConfigName = pc.cyan(`export { ${configName} }`);
33
+ if (exportsRelevant.length === 0) {
34
+ let errMsg = `${filePathToShowToUser} doesn't export any value, but it should have a ${exportDefault}`;
34
35
  if (configName)
35
- errMsg += ` or ${pc.cyan(`export { ${configName} }`)}`;
36
+ errMsg += ` or ${exportConfigName}`;
36
37
  assertUsage(false, errMsg);
37
38
  }
39
+ else {
40
+ assert(exportsRelevant.length === 2);
41
+ assertWarning(false, `${filePathToShowToUser} remove ${exportConfigName} or ${exportDefault}`, {
42
+ onlyOnce: true
43
+ });
44
+ }
38
45
  }
39
46
  else {
40
47
  // !configName => isConfigFile
@@ -4,6 +4,7 @@ export { resolveRouteStringRedirect };
4
4
  import { assertIsNotBrowser } from '../../utils/assertIsNotBrowser.js';
5
5
  import { isUriWithProtocol } from '../../utils/parseUrl-extras.js';
6
6
  import { assert, assertUsage } from '../utils.js';
7
+ import { resolveUrlPathname } from './resolveUrlPathname.js';
7
8
  import { assertRouteString, resolveRouteString } from './resolveRouteString.js';
8
9
  import pc from '@brillout/picocolors';
9
10
  assertIsNotBrowser(); // Don't bloat the client
@@ -27,16 +28,7 @@ function resolveRouteStringRedirect(urlSource, urlTarget, urlPathname) {
27
28
  const match = resolveRouteString(urlSource, urlPathname);
28
29
  if (!match)
29
30
  return null;
30
- let urlResolved = urlTarget;
31
- Object.entries(match.routeParams).forEach(([key, val]) => {
32
- if (key !== '*') {
33
- key = `@${key}`;
34
- }
35
- urlResolved = urlResolved.replaceAll(key, val);
36
- });
37
- if (!urlResolved.startsWith('mailto:')) {
38
- assertUsage(!urlResolved.includes('@'), 'URL should not contain "@" unless it is a mailto link.');
39
- }
31
+ const urlResolved = resolveUrlPathname(urlTarget, match.routeParams);
40
32
  if (urlResolved === urlPathname)
41
33
  return null;
42
34
  assert(urlResolved.startsWith('/') || isUriWithProtocol(urlResolved));
@@ -0,0 +1,12 @@
1
+ export { resolveUrlPathname };
2
+ /** Given a `routeString` and `routeParams`, resolve `urlPathname`.
3
+ *
4
+ * Basically, the correct implementation of following:
5
+ * ```js
6
+ * let urlPathname = routeString
7
+ * Object.entries(routeParams).forEach(([key, val]) => {
8
+ * urlPathname = urlPathname.replaceAll(key, val)
9
+ * })
10
+ * ```
11
+ */
12
+ declare function resolveUrlPathname(routeString: string, routeParams: Record<string, string>): string;
@@ -0,0 +1,45 @@
1
+ export { resolveUrlPathname };
2
+ import { assertIsNotBrowser } from '../../utils/assertIsNotBrowser.js';
3
+ import { assert, assertUsage } from '../utils.js';
4
+ assertIsNotBrowser(); // Don't bloat the client
5
+ /** Given a `routeString` and `routeParams`, resolve `urlPathname`.
6
+ *
7
+ * Basically, the correct implementation of following:
8
+ * ```js
9
+ * let urlPathname = routeString
10
+ * Object.entries(routeParams).forEach(([key, val]) => {
11
+ * urlPathname = urlPathname.replaceAll(key, val)
12
+ * })
13
+ * ```
14
+ */
15
+ function resolveUrlPathname(routeString, routeParams) {
16
+ let parts = [{ val: routeString, type: 'ROUTE_STRING' }];
17
+ Object.entries(routeParams).forEach(([key, val]) => {
18
+ if (key.startsWith('*')) {
19
+ assert(key === '*' || /\d+/.test(key.slice(1)));
20
+ assertUsage(key === '*', "Resolving URL with multiple globs isn't implemented yet");
21
+ }
22
+ else {
23
+ key = `@${key}`;
24
+ }
25
+ parts = parts
26
+ .map((part) => {
27
+ if (part.type === 'URL') {
28
+ return part;
29
+ }
30
+ else {
31
+ return part.val
32
+ .split(key)
33
+ .map((rest, i) => {
34
+ const partURL = { val, type: 'URL' };
35
+ const partRouteString = { val: rest, type: 'ROUTE_STRING' };
36
+ return i === 0 ? [partRouteString] : [partURL, partRouteString];
37
+ })
38
+ .flat();
39
+ }
40
+ })
41
+ .flat();
42
+ });
43
+ const urlPathname = parts.map((p) => p.val).join('');
44
+ return urlPathname;
45
+ }
@@ -1,7 +1,7 @@
1
1
  export { createDebugger };
2
2
  export { isDebugEnabled };
3
3
  export type { Debug };
4
- type Namespace = 'vike:error' | 'vike:extractAssets' | 'vike:extractExportNames' | 'vike:glob' | 'vike:pageFiles' | 'vike:log' | 'vike:routing' | 'vike:virtual-files' | 'vike:stem' | 'vike:stream';
4
+ type Namespace = 'vike:error' | 'vike:extractAssets' | 'vike:extractExportNames' | 'vike:glob' | 'vike:pageFiles' | 'vike:log' | 'vike:routing' | 'vike:virtual-files' | 'vike:stem' | 'vike:stream' | 'vike:outDir';
5
5
  type Debug = ReturnType<typeof createDebugger>;
6
6
  type Options = {
7
7
  serialization?: {
@@ -9,7 +9,7 @@ type Options = {
9
9
  };
10
10
  };
11
11
  declare function createDebugger(namespace: Namespace, optionsGlobal?: Options): ((...msgs: unknown[]) => void) & {
12
- options: (options: Options) => (...msgs: unknown[]) => void;
12
+ options: (optionsLocal: Options) => (...msgs: unknown[]) => void;
13
13
  isEnabled: boolean;
14
14
  };
15
15
  declare function isDebugEnabled(namespace: Namespace): boolean;
@@ -11,28 +11,42 @@ assert(!isBrowser());
11
11
  globalThis.__brillout_debug_createDebugger = createDebugger;
12
12
  function createDebugger(namespace, optionsGlobal) {
13
13
  checkType(namespace);
14
- const debugWithOptions = (options) => {
14
+ const debugWithOptions = (optionsLocal) => {
15
15
  return (...msgs) => {
16
- if (!isDebugEnabled(namespace))
17
- return;
18
- let [msgFirst, ...msgsRest] = msgs;
19
- const padding = ' '.repeat(namespace.length + 1);
20
- const optionsResolved = { ...optionsGlobal, ...options };
21
- msgFirst = formatMsg(msgFirst, optionsResolved, padding, 'FIRST');
22
- msgsRest = msgsRest.map((msg, i) => {
23
- const position = i === msgsRest.length - 1 ? 'LAST' : 'MIDDLE';
24
- return formatMsg(msg, optionsResolved, padding, position);
25
- });
26
- console.log('\x1b[1m%s\x1b[0m', namespace, msgFirst);
27
- msgsRest.forEach((msg) => {
28
- console.log(msg);
29
- });
16
+ const options = { ...optionsGlobal, ...optionsLocal };
17
+ debug_(namespace, options, ...msgs);
30
18
  };
31
19
  };
32
20
  const debug = (...msgs) => debugWithOptions({})(...msgs);
33
21
  objectAssign(debug, { options: debugWithOptions, isEnabled: isDebugEnabled(namespace) });
34
22
  return debug;
35
23
  }
24
+ function debug_(namespace, options, ...msgs) {
25
+ if (!isDebugEnabled(namespace))
26
+ return;
27
+ let [msgFirst, ...msgsRest] = msgs;
28
+ const padding = ' '.repeat(namespace.length + 1);
29
+ msgFirst = formatMsg(msgFirst, options, padding, 'FIRST');
30
+ msgsRest = msgsRest.map((msg, i) => {
31
+ const position = i === msgsRest.length - 1 ? 'LAST' : 'MIDDLE';
32
+ return formatMsg(msg, options, padding, position);
33
+ });
34
+ let logFirst;
35
+ let logsRest;
36
+ const noNewLine = msgsRest.length <= 1 && [msgFirst, ...msgsRest].every((m) => typeof m === 'string' && !m.includes('\n'));
37
+ if (noNewLine) {
38
+ logFirst = [msgFirst, ...msgsRest].map((m) => String(m).trim());
39
+ logsRest = [];
40
+ }
41
+ else {
42
+ logFirst = [msgFirst];
43
+ logsRest = msgsRest;
44
+ }
45
+ console.log('\x1b[1m%s\x1b[0m', namespace, ...logFirst);
46
+ logsRest.forEach((msg) => {
47
+ console.log(msg);
48
+ });
49
+ }
36
50
  function isDebugEnabled(namespace) {
37
51
  checkType(namespace);
38
52
  let DEBUG;
@@ -1,5 +1,5 @@
1
1
  export declare const debugGlob: ((...msgs: unknown[]) => void) & {
2
- options: (options: {
2
+ options: (optionsLocal: {
3
3
  serialization?: {
4
4
  emptyArray?: string | undefined;
5
5
  } | undefined;
@@ -5,7 +5,10 @@ import { assert, assertUsage } from './assert.js';
5
5
  import { pathJoin } from './path-shim.js';
6
6
  import { assertPosixPath, toPosixPath } from './filesystemPathHandling.js';
7
7
  import pc from '@brillout/picocolors';
8
+ import { createDebugger } from './debug.js';
9
+ const debug = createDebugger('vike:outDir');
8
10
  function getOutDirs(config) {
11
+ debug('getOutDirs()', new Error().stack);
9
12
  let outDirRoot;
10
13
  {
11
14
  const outDir = getOutDirFromViteResolvedConfig(config);
@@ -19,11 +22,16 @@ function getOutDirs(config) {
19
22
  outDirRoot = outDir.slice(0, -1 * '/client'.length);
20
23
  }
21
24
  }
22
- return getOutDirsAll(outDirRoot, config.root);
25
+ const outDirs = getOutDirsAll(outDirRoot, config.root);
26
+ debug('outDirRoot', outDirRoot);
27
+ debug('outDirs', outDirs);
28
+ return outDirs;
23
29
  }
24
30
  /** Appends `client/` or `server/` to `config.build.outDir` */
25
31
  function resolveOutDir(config) {
32
+ debug('resolveOutDir()', new Error().stack);
26
33
  const outDir = getOutDirFromViteUserConfig(config) || 'dist';
34
+ debug('outDir', 'outDir');
27
35
  // outDir may already be resolved when using Telefunc + vike (because both Telefunc and vike use this logic)
28
36
  if (!isOutDirRoot(outDir)) {
29
37
  assertOutDirResolved(outDir, config);
@@ -32,9 +40,11 @@ function resolveOutDir(config) {
32
40
  else {
33
41
  const { outDirClient, outDirServer } = determineOutDirs(outDir);
34
42
  if (viteIsSSR(config)) {
43
+ debug('outDirServer', 'outDirServer');
35
44
  return outDirServer;
36
45
  }
37
46
  else {
47
+ debug('outDirClient', 'outDirClient');
38
48
  return outDirClient;
39
49
  }
40
50
  }
@@ -1,13 +1,13 @@
1
1
  export { projectInfo };
2
2
  export type { ProjectTag };
3
3
  export { PROJECT_VERSION };
4
- declare const PROJECT_VERSION: "0.4.149";
4
+ declare const PROJECT_VERSION: "0.4.150-commit-d9acc70";
5
5
  type PackageName = typeof projectInfo.npmPackageName;
6
6
  type ProjectVersion = typeof projectInfo.projectVersion;
7
7
  type ProjectTag = `[${PackageName}]` | `[${PackageName}@${ProjectVersion}]`;
8
8
  declare const projectInfo: {
9
9
  projectName: "Vike";
10
- projectVersion: "0.4.149";
10
+ projectVersion: "0.4.150-commit-d9acc70";
11
11
  npmPackageName: "vike";
12
12
  githubRepository: "https://github.com/vikejs/vike";
13
13
  };
@@ -1,7 +1,7 @@
1
1
  export { projectInfo };
2
2
  export { PROJECT_VERSION };
3
3
  import { onProjectInfo } from './assertSingleInstance.js';
4
- const PROJECT_VERSION = '0.4.149';
4
+ const PROJECT_VERSION = '0.4.150-commit-d9acc70';
5
5
  const projectInfo = {
6
6
  projectName: 'Vike',
7
7
  projectVersion: PROJECT_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vike",
3
- "version": "0.4.149",
3
+ "version": "0.4.150-commit-d9acc70",
4
4
  "scripts": {
5
5
  "dev": "tsc --watch",
6
6
  "build": "rimraf dist/ && pnpm run build:esm && pnpm run build:cjs",
@@ -162,7 +162,7 @@
162
162
  "vike": "./node/cli/bin-entry.js"
163
163
  },
164
164
  "devDependencies": {
165
- "@brillout/release-me": "^0.1.10",
165
+ "@brillout/release-me": "^0.1.11",
166
166
  "@types/estree": "^1.0.0",
167
167
  "@types/jest": "^27.4.1",
168
168
  "@types/node": "^20.1.0",