@poveste/plugin-nuxt 0.5.3 → 0.6.1

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/index.d.ts CHANGED
@@ -1,2 +1,10 @@
1
1
  import type { Plugin } from 'poveste';
2
- export declare function HstNuxt(): Plugin;
2
+ export interface HstNuxtOptions {
3
+ /**
4
+ * Client plugins to drop from the story sandbox, matched against each
5
+ * plugin's resolved `src` (string `includes` or `RegExp`). Added on top of
6
+ * the built-in defaults, not replacing them.
7
+ */
8
+ excludePlugins?: (string | RegExp)[];
9
+ }
10
+ export declare function HstNuxt(options?: HstNuxtOptions): Plugin;
package/dist/index.js CHANGED
@@ -9,12 +9,17 @@ const ignorePlugins = [
9
9
  'nuxt:dynamic-base-path',
10
10
  'nuxt:import-protection',
11
11
  ];
12
- export function HstNuxt() {
12
+ // Some Nuxt modules register client plugins that assume a full Nuxt runtime the
13
+ // headless story sandbox can't provide, so they 500 the iframe on entry boot;
14
+ // @nuxtjs/i18n's are dropped by default (#65), and `excludePlugins` drops more
15
+ // without patching this package (#277).
16
+ const DEFAULT_EXCLUDED_PLUGINS = [/[\\/]@nuxtjs[\\/]i18n[\\/].*[\\/]plugins[\\/]/];
17
+ export function HstNuxt(options = {}) {
13
18
  let nuxt;
14
19
  return {
15
20
  name: '@poveste/plugin-nuxt',
16
21
  async defaultConfig() {
17
- const nuxtViteConfig = await useNuxtViteConfig();
22
+ const nuxtViteConfig = await useNuxtViteConfig([...DEFAULT_EXCLUDED_PLUGINS, ...(options.excludePlugins ?? [])]);
18
23
  const { viteConfig } = nuxtViteConfig;
19
24
  nuxt = nuxtViteConfig.nuxt; // We save it to close it later
20
25
  const plugins = viteConfig.plugins.filter((p) => !ignorePlugins.includes(p?.name));
@@ -98,7 +103,7 @@ export async function setupVue3 () {
98
103
  },
99
104
  };
100
105
  }
101
- async function useNuxtViteConfig() {
106
+ async function useNuxtViteConfig(excludePlugins) {
102
107
  const { loadNuxt, buildNuxt } = await import('@nuxt/kit');
103
108
  const nuxt = await loadNuxt({
104
109
  // cwd: process.cwd(),
@@ -128,6 +133,16 @@ async function useNuxtViteConfig() {
128
133
  }
129
134
  const runtimeDir = fileURLToPath(new URL('../runtime', import.meta.url));
130
135
  nuxt.options.build.templates.push({ src: join(runtimeDir, 'composables.mjs'), filename: 'poveste/composables.mjs' }, { src: join(runtimeDir, 'components.mjs'), filename: 'poveste/components.mjs' });
136
+ // Drop the sandbox-incompatible client plugins named by `excludePlugins`, so
137
+ // they don't 500 the story iframe on entry boot. See DEFAULT_EXCLUDED_PLUGINS.
138
+ nuxt.hook('app:resolve', (app) => {
139
+ app.plugins = app.plugins.filter((p) => {
140
+ const src = p.src ?? '';
141
+ // `match`, not `test`: it doesn't advance a caller's /g regex `lastIndex`, so
142
+ // matching stays stateless without mutating an input we don't own.
143
+ return !excludePlugins.some(pattern => typeof pattern === 'string' ? src.includes(pattern) : src.match(pattern) !== null);
144
+ });
145
+ });
131
146
  nuxt.hook('app:templates', (app) => {
132
147
  app.templates = app.templates.filter(template => template.filename !== 'app-component.mjs');
133
148
  app.templates.push({ src: join(runtimeDir, 'app-component.mjs'), filename: 'app-component.mjs' });
@@ -159,6 +174,14 @@ async function useNuxtViteConfig() {
159
174
  nuxt.hook('vite:configResolved', (config, { isClient }) => {
160
175
  if (isClient) {
161
176
  resolve(config);
177
+ // The resolved config is all this instance is for. Left running,
178
+ // `buildNuxt` goes on to build the Nuxt client, start Nitro and
179
+ // open Nuxt's own dev server — seconds of work on every `poveste
180
+ // dev` and `poveste build`, a `NUXT_B7005` warning for the entry
181
+ // it does not have, and an HMR socket on 24678 that collides with
182
+ // any other Nuxt dev server on the machine (#220, #221). The catch
183
+ // below has always known this sentinel; nothing threw it.
184
+ throw new Error('_stop_');
162
185
  }
163
186
  });
164
187
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@poveste/plugin-nuxt",
3
3
  "type": "module",
4
- "version": "0.5.3",
4
+ "version": "0.6.1",
5
5
  "description": "Poveste plugin to integrate with Nuxt 4",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan"
@@ -24,8 +24,8 @@
24
24
  "types": "./dist/index.d.ts",
25
25
  "peerDependencies": {
26
26
  "nuxt": "^4.5.0",
27
- "@poveste/plugin-vue": "^0.5.3",
28
- "poveste": "^0.5.3"
27
+ "@poveste/plugin-vue": "^0.6.1",
28
+ "poveste": "^0.6.1"
29
29
  },
30
30
  "dependencies": {
31
31
  "@nuxt/kit": "^4.5.2",
@@ -39,7 +39,7 @@
39
39
  "@types/node": "^22.10.1",
40
40
  "typescript": "5.6.3",
41
41
  "vite": "^8.2.0",
42
- "poveste": "0.5.3"
42
+ "poveste": "0.6.1"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "rimraf dist && tsc -d",
package/src/index.ts CHANGED
@@ -14,13 +14,28 @@ const ignorePlugins = [
14
14
  'nuxt:import-protection',
15
15
  ]
16
16
 
17
- export function HstNuxt(): Plugin {
17
+ export interface HstNuxtOptions {
18
+ /**
19
+ * Client plugins to drop from the story sandbox, matched against each
20
+ * plugin's resolved `src` (string `includes` or `RegExp`). Added on top of
21
+ * the built-in defaults, not replacing them.
22
+ */
23
+ excludePlugins?: (string | RegExp)[]
24
+ }
25
+
26
+ // Some Nuxt modules register client plugins that assume a full Nuxt runtime the
27
+ // headless story sandbox can't provide, so they 500 the iframe on entry boot;
28
+ // @nuxtjs/i18n's are dropped by default (#65), and `excludePlugins` drops more
29
+ // without patching this package (#277).
30
+ const DEFAULT_EXCLUDED_PLUGINS: (string | RegExp)[] = [/[\\/]@nuxtjs[\\/]i18n[\\/].*[\\/]plugins[\\/]/]
31
+
32
+ export function HstNuxt(options: HstNuxtOptions = {}): Plugin {
18
33
  let nuxt: Nuxt
19
34
  return {
20
35
  name: '@poveste/plugin-nuxt',
21
36
 
22
37
  async defaultConfig() {
23
- const nuxtViteConfig = await useNuxtViteConfig()
38
+ const nuxtViteConfig = await useNuxtViteConfig([...DEFAULT_EXCLUDED_PLUGINS, ...(options.excludePlugins ?? [])])
24
39
  const { viteConfig } = nuxtViteConfig
25
40
 
26
41
  nuxt = nuxtViteConfig.nuxt // We save it to close it later
@@ -111,7 +126,7 @@ export async function setupVue3 () {
111
126
  }
112
127
  }
113
128
 
114
- async function useNuxtViteConfig() {
129
+ async function useNuxtViteConfig(excludePlugins: (string | RegExp)[]) {
115
130
  const { loadNuxt, buildNuxt } = await import('@nuxt/kit')
116
131
  const nuxt = await loadNuxt({
117
132
  // cwd: process.cwd(),
@@ -145,6 +160,18 @@ async function useNuxtViteConfig() {
145
160
  { src: join(runtimeDir, 'components.mjs'), filename: 'poveste/components.mjs' },
146
161
  )
147
162
 
163
+ // Drop the sandbox-incompatible client plugins named by `excludePlugins`, so
164
+ // they don't 500 the story iframe on entry boot. See DEFAULT_EXCLUDED_PLUGINS.
165
+ nuxt.hook('app:resolve', (app) => {
166
+ app.plugins = app.plugins.filter((p) => {
167
+ const src = p.src ?? ''
168
+ // `match`, not `test`: it doesn't advance a caller's /g regex `lastIndex`, so
169
+ // matching stays stateless without mutating an input we don't own.
170
+ return !excludePlugins.some(pattern =>
171
+ typeof pattern === 'string' ? src.includes(pattern) : src.match(pattern) !== null)
172
+ })
173
+ })
174
+
148
175
  nuxt.hook('app:templates', (app) => {
149
176
  app.templates = app.templates.filter(template => template.filename !== 'app-component.mjs')
150
177
  app.templates.push({ src: join(runtimeDir, 'app-component.mjs'), filename: 'app-component.mjs' })
@@ -179,6 +206,14 @@ async function useNuxtViteConfig() {
179
206
  nuxt.hook('vite:configResolved', (config, { isClient }) => {
180
207
  if (isClient) {
181
208
  resolve(config as any)
209
+ // The resolved config is all this instance is for. Left running,
210
+ // `buildNuxt` goes on to build the Nuxt client, start Nitro and
211
+ // open Nuxt's own dev server — seconds of work on every `poveste
212
+ // dev` and `poveste build`, a `NUXT_B7005` warning for the entry
213
+ // it does not have, and an HMR socket on 24678 that collides with
214
+ // any other Nuxt dev server on the machine (#220, #221). The catch
215
+ // below has always known this sentinel; nothing threw it.
216
+ throw new Error('_stop_')
182
217
  }
183
218
  })
184
219
  })