@poveste/plugin-nuxt 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,5 +19,5 @@ export default defineConfig({
19
19
  })
20
20
  ```
21
21
 
22
- [Nuxt guide](https://poveste.dev/guide/vue/getting-started.html#nuxt) ·
22
+ [Nuxt guide](https://poveste.dev/guide/nuxt/getting-started) ·
23
23
  [Documentation](https://poveste.dev)
package/dist/index.js CHANGED
@@ -48,8 +48,8 @@ export function HstNuxt(options = {}) {
48
48
  }
49
49
  return {
50
50
  name: '@poveste/plugin-nuxt',
51
- async defaultConfig() {
52
- const nuxtViteConfig = await useNuxtViteConfig([...DEFAULT_EXCLUDED_PLUGINS, ...(options.excludePlugins ?? [])]);
51
+ async defaultConfig(_defaultConfig, mode) {
52
+ const nuxtViteConfig = await useNuxtViteConfig([...DEFAULT_EXCLUDED_PLUGINS, ...(options.excludePlugins ?? [])], mode === 'build');
53
53
  const { viteConfig } = nuxtViteConfig;
54
54
  nuxt = nuxtViteConfig.nuxt;
55
55
  // Registered here rather than in a command hook: this instance exists from
@@ -60,7 +60,7 @@ export function HstNuxt(options = {}) {
60
60
  return {
61
61
  vite: {
62
62
  server: {
63
- watch: viteConfig.server.watch,
63
+ ...viteConfig.server.watch !== undefined ? { watch: viteConfig.server.watch } : {},
64
64
  fs: {
65
65
  allow: viteConfig.server.fs.allow,
66
66
  },
@@ -94,7 +94,7 @@ export function HstNuxt(options = {}) {
94
94
  optimizeDeps: {
95
95
  ...viteConfig.optimizeDeps,
96
96
  exclude: [
97
- ...viteConfig.optimizeDeps.exclude,
97
+ ...viteConfig.optimizeDeps.exclude ?? [],
98
98
  '@poveste/plugin-nuxt',
99
99
  ],
100
100
  },
@@ -102,6 +102,11 @@ export function HstNuxt(options = {}) {
102
102
  vue: viteConfig.vue,
103
103
  logLevel: 'info',
104
104
  },
105
+ // The generated hook keeps the canonical name. `getSetupFnGroups` treats
106
+ // `setupFn[0]` as canonical and only that name is re-exported from the
107
+ // generated setup module, so an unnumbered `setupVue` here resolves to
108
+ // nothing and every Nuxt story loses its app setup — three `render-story`
109
+ // specs fail (#157).
105
110
  setupCode: [
106
111
  `${nuxt.options.css.map(file => `import '${file}'`).join('\n')}`,
107
112
  `import { setupNuxtApp } from '@poveste/plugin-nuxt/dist/runtime/app-setup.js'
@@ -135,7 +140,7 @@ export async function setupVue3 () {
135
140
  },
136
141
  };
137
142
  }
138
- async function useNuxtViteConfig(excludePlugins) {
143
+ async function useNuxtViteConfig(excludePlugins, isBuild) {
139
144
  const { loadNuxt, buildNuxt } = await import('@nuxt/kit');
140
145
  const nuxt = await loadNuxt({
141
146
  // cwd: process.cwd(),
@@ -150,6 +155,12 @@ async function useNuxtViteConfig(excludePlugins) {
150
155
  ssr: false,
151
156
  experimental: {
152
157
  appManifest: false,
158
+ // A build has nothing to watch for. Nuxt's default watchers are started
159
+ // by `buildNuxt`, keep multiplying after `_stop_` aborts it, and survive
160
+ // `nuxt.close()`, so a failed build hung on ~115 `FSEventWrap` handles
161
+ // (#434). `builder` attaches to the Vite dev server instead, which this
162
+ // instance never creates.
163
+ ...isBuild ? { watcher: 'builder' } : {},
153
164
  },
154
165
  app: {
155
166
  rootId: 'nuxt-test',
@@ -164,7 +175,7 @@ async function useNuxtViteConfig(excludePlugins) {
164
175
  throw new Error(`Poveste only supports Vite bundler, but Nuxt builder is currently set to '${nuxt.options.builder}'.`);
165
176
  }
166
177
  const runtimeDir = fileURLToPath(new URL('../runtime', import.meta.url));
167
- nuxt.options.build.templates.push({ src: join(runtimeDir, 'composables.mjs'), filename: 'poveste/composables.mjs' }, { src: join(runtimeDir, 'components.mjs'), filename: 'poveste/components.mjs' }, { src: join(runtimeDir, 'tolerant-plugins.mjs'), filename: 'poveste/tolerant-plugins.mjs' });
178
+ nuxt.options.build.templates.push({ src: join(runtimeDir, 'components.mjs'), filename: 'poveste/components.mjs' }, { src: join(runtimeDir, 'tolerant-plugins.mjs'), filename: 'poveste/tolerant-plugins.mjs' });
168
179
  // Drop the sandbox-incompatible client plugins named by `excludePlugins`, so
169
180
  // they don't 500 the story iframe on entry boot. See DEFAULT_EXCLUDED_PLUGINS.
170
181
  nuxt.hook('app:resolve', (app) => {
@@ -188,20 +199,20 @@ async function useNuxtViteConfig(excludePlugins) {
188
199
  template.getContents = async (ctx) => wrapPluginsForTolerantBoot(await original(ctx));
189
200
  }
190
201
  });
202
+ // `useNuxtApp` was stubbed here until #439, which cost every story the real
203
+ // one: `$config` read as undefined and a value a plugin provided was
204
+ // unreachable, which is the case histoire#666 was about. The polyfills stay —
205
+ // they are dropped from the `#app` presets and deliberately not replaced.
191
206
  nuxt.hook('imports:sources', (presets) => {
192
207
  const polyfills = ['requestIdleCallback', 'cancelIdleCallback'];
193
- const stubbedComposables = ['useNuxtApp'];
194
208
  for (const appPreset of presets.filter(p => p && typeof p === 'object' && 'from' in p && p.from?.startsWith('#app'))) {
195
209
  if ('imports' in appPreset && Array.isArray(appPreset.imports)) {
196
- appPreset.imports = appPreset.imports.filter(i => typeof i !== 'string' || (!stubbedComposables.includes(i) && !polyfills.includes(i)));
210
+ appPreset.imports = appPreset.imports.filter(i => typeof i !== 'string' || !polyfills.includes(i));
197
211
  }
198
212
  }
199
- presets.push({
200
- from: '#build/poveste/composables.mjs',
201
- imports: stubbedComposables,
202
- });
203
213
  });
204
214
  return {
215
+ // Captured from `vite:configResolved`, so it is the resolved config.
205
216
  viteConfig: await new Promise((resolve, reject) => {
206
217
  nuxt.hook('modules:done', () => {
207
218
  nuxt.hook('components:extend', (components) => {
@@ -214,6 +225,7 @@ async function useNuxtViteConfig(excludePlugins) {
214
225
  });
215
226
  nuxt.hook('vite:configResolved', (config, { isClient }) => {
216
227
  if (isClient) {
228
+ // Nuxt types the hook argument as its user config; at this hook it is resolved.
217
229
  resolve(config);
218
230
  // The resolved config is all this instance is for. Left running,
219
231
  // `buildNuxt` goes on to build the Nuxt client, start Nitro and
@@ -25,11 +25,8 @@ export async function setupNuxtApp(publicConfig) {
25
25
  const h3App = createApp();
26
26
  const registry = new Set();
27
27
  if (__HST_COLLECT__) {
28
- const { toNodeListener, } = await import('h3');
29
- const { createCall, createFetch: createLocalFetch, } = await import('unenv/runtime/fetch/index');
30
- // @ts-expect-error TODO: fix in h3
31
- const localCall = createCall(toNodeListener(h3App));
32
- const localFetch = createLocalFetch(localCall, globalThis.fetch);
28
+ const { createLocalFetch } = await import('./local-fetch.js');
29
+ const localFetch = createLocalFetch(h3App);
33
30
  win.fetch = (init, options) => {
34
31
  if (typeof init === 'string' && registry.has(init)) {
35
32
  init = `/_${init}`;
@@ -0,0 +1,14 @@
1
+ import type { App } from 'h3';
2
+ /**
3
+ * A `fetch` that answers a path from the in-memory h3 app and sends any other
4
+ * URL to the real `fetch`.
5
+ *
6
+ * unenv 1 provided this as `createCall` and `createFetch`, and unenv 2 removed
7
+ * both (#74). An h3 app already answers a web `Request` through `toWebHandler`,
8
+ * so no Node request and response shims are needed to reach it. The rule for
9
+ * what stays local is unenv's: a URL starting with `/`, except a protocol-
10
+ * relative one — `//cdn.example.com/x` starts with a slash and names another
11
+ * host, and answering it here would return this app's 404 for a resource that
12
+ * exists on the network.
13
+ */
14
+ export declare function createLocalFetch(app: App, fallback?: typeof fetch): (input: string | URL, init?: RequestInit) => Promise<Response>;
@@ -0,0 +1,23 @@
1
+ import { toWebHandler } from 'h3';
2
+ /**
3
+ * A `fetch` that answers a path from the in-memory h3 app and sends any other
4
+ * URL to the real `fetch`.
5
+ *
6
+ * unenv 1 provided this as `createCall` and `createFetch`, and unenv 2 removed
7
+ * both (#74). An h3 app already answers a web `Request` through `toWebHandler`,
8
+ * so no Node request and response shims are needed to reach it. The rule for
9
+ * what stays local is unenv's: a URL starting with `/`, except a protocol-
10
+ * relative one — `//cdn.example.com/x` starts with a slash and names another
11
+ * host, and answering it here would return this app's 404 for a resource that
12
+ * exists on the network.
13
+ */
14
+ export function createLocalFetch(app, fallback = globalThis.fetch) {
15
+ const handle = toWebHandler(app);
16
+ return (input, init) => {
17
+ const url = input.toString();
18
+ if (!url.startsWith('/') || url.startsWith('//')) {
19
+ return fallback(url, init);
20
+ }
21
+ return handle(new Request(new URL(url, 'http://localhost'), init));
22
+ };
23
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@poveste/plugin-nuxt",
3
3
  "type": "module",
4
- "version": "0.14.0",
4
+ "version": "0.16.0",
5
5
  "description": "Poveste plugin to integrate with Nuxt 4",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan",
@@ -46,27 +46,26 @@
46
46
  "runtime"
47
47
  ],
48
48
  "engines": {
49
- "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
49
+ "node": ">=22.22.2"
50
50
  },
51
51
  "peerDependencies": {
52
- "@poveste/plugin-vue": "^0.14.0",
52
+ "@poveste/plugin-vue": "^0.16.0",
53
53
  "nuxt": "^4.5.0",
54
- "poveste": "^0.14.0"
54
+ "poveste": "^0.16.0"
55
55
  },
56
56
  "dependencies": {
57
57
  "@nuxt/kit": "^4.5.2",
58
58
  "@rollup/plugin-replace": "^6.0.1",
59
59
  "h3": "^1.13.0",
60
- "ofetch": "^1.4.1",
61
- "unenv": "^1.10.0"
60
+ "ofetch": "^1.4.1"
62
61
  },
63
62
  "devDependencies": {
64
63
  "@nuxt/schema": "^4.5.2",
65
- "@types/node": "^22.10.1",
66
- "poveste": "0.14.0",
67
- "typescript": "5.6.3",
68
- "vite": "^8.2.0",
69
- "vitest": "^5.0.0"
64
+ "@types/node": "^26.6.3",
65
+ "poveste": "0.16.0",
66
+ "typescript": "6.0.3",
67
+ "vite": "^8.3.1",
68
+ "vitest": "^5.0.2"
70
69
  },
71
70
  "scripts": {
72
71
  "build": "rimraf dist && tsc -d",
@@ -1 +0,0 @@
1
- export const useNuxtApp = () => ({ runWithContext: async fn => await fn() })