@despia/local 1.0.0 → 1.0.2

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
@@ -353,12 +353,12 @@ npx despia-local [outputDir] [entryHtml]
353
353
  ```javascript
354
354
  // vite.config.js
355
355
  import { defineConfig } from 'vite';
356
- import { despiaOfflinePlugin } from '@despia/local/vite';
356
+ import { despiaLocalPlugin } from '@despia/local/vite';
357
357
 
358
358
  export default defineConfig({
359
359
  plugins: [
360
360
  // ... your other plugins
361
- despiaOfflinePlugin({
361
+ despiaLocalPlugin({
362
362
  outDir: 'dist', // optional, default: 'dist'
363
363
  entryHtml: 'index.html' // optional, default: 'index.html'
364
364
  })
@@ -378,13 +378,13 @@ export default defineConfig({
378
378
 
379
379
  ```javascript
380
380
  // webpack.config.js
381
- const DespiaOfflinePlugin = require('@despia/local/webpack');
381
+ const DespiaLocalPlugin = require('@despia/local/webpack');
382
382
 
383
383
  module.exports = {
384
384
  // ... your config
385
385
  plugins: [
386
386
  // ... your other plugins
387
- new DespiaOfflinePlugin({
387
+ new DespiaLocalPlugin({
388
388
  outDir: 'dist', // optional, default: 'dist'
389
389
  entryHtml: 'index.html' // optional, default: 'index.html'
390
390
  })
@@ -402,13 +402,13 @@ module.exports = {
402
402
 
403
403
  ```javascript
404
404
  // rollup.config.js
405
- import { despiaOffline } from '@despia/local/rollup';
405
+ import { despiaLocal } from '@despia/local/rollup';
406
406
 
407
407
  export default {
408
408
  // ... your config
409
409
  plugins: [
410
410
  // ... your other plugins
411
- despiaOffline({
411
+ despiaLocal({
412
412
  outDir: 'dist',
413
413
  entryHtml: 'index.html'
414
414
  })
@@ -418,25 +418,28 @@ export default {
418
418
 
419
419
  ### Next.js
420
420
 
421
+ **For static export (recommended for static sites):**
422
+
421
423
  ```javascript
422
424
  // next.config.js
423
- const withDespiaOffline = require('@despia/local/next');
425
+ const withDespiaLocal = require('@despia/local/next');
424
426
 
425
- module.exports = withDespiaOffline({
427
+ module.exports = withDespiaLocal({
426
428
  entryHtml: 'index.html',
427
- outDir: '.next' // or 'out' for static export
429
+ outDir: 'out' // Next.js static export directory
428
430
  })({
431
+ output: 'export',
429
432
  // your Next.js config
430
433
  });
431
434
  ```
432
435
 
433
- **For static export:**
436
+ **For static export (alternative):**
434
437
 
435
438
  ```javascript
436
439
  // next.config.js
437
- const withDespiaOffline = require('@despia/local/next');
440
+ const withDespiaLocal = require('@despia/local/next');
438
441
 
439
- module.exports = withDespiaOffline({
442
+ module.exports = withDespiaLocal({
440
443
  outDir: 'out', // Next.js static export directory
441
444
  entryHtml: 'index.html'
442
445
  })({
@@ -445,29 +448,75 @@ module.exports = withDespiaOffline({
445
448
  });
446
449
  ```
447
450
 
448
- **Alternative: Webpack Plugin Approach**
451
+ **Alternative: Webpack Plugin Approach (works best for static export):**
449
452
 
450
453
  ```javascript
451
454
  // next.config.js
452
- const DespiaOfflinePlugin = require('@despia/local/webpack');
455
+ const DespiaLocalPlugin = require('@despia/local/webpack');
453
456
 
454
457
  module.exports = {
458
+ output: 'export', // For static export
455
459
  webpack: (config) => {
456
460
  config.plugins.push(
457
- new DespiaOfflinePlugin({ outDir: '.next' })
461
+ new DespiaLocalPlugin({ outDir: 'out' })
458
462
  );
459
463
  return config;
460
464
  }
461
465
  };
462
466
  ```
463
467
 
468
+ **Note**: The webpack plugin approach works best for static export. For SSR apps, use the post-build script approach described below.
469
+
470
+ **For SSR (Server-Side Rendering) apps:**
471
+
472
+ SSR Next.js apps require special handling because:
473
+ - Client assets are in `.next/static/` directory (not `.next/`)
474
+ - No static HTML files exist (pages are server-rendered)
475
+ - Manifest should only include client-side assets (JS, CSS, images)
476
+ - Server-side code in `.next/server/` should NOT be included
477
+
478
+ **Recommended approach for SSR (most reliable):**
479
+
480
+ Use a post-build script in your `package.json`:
481
+
482
+ ```json
483
+ {
484
+ "scripts": {
485
+ "build": "next build",
486
+ "postbuild": "despia-local .next/static"
487
+ }
488
+ }
489
+ ```
490
+
491
+ This approach:
492
+ - Runs after Next.js build completes
493
+ - Targets `.next/static/` where all client assets are stored
494
+ - Works reliably for both SSR and static export
495
+ - No `entryHtml` needed (SSR apps don't have static HTML files)
496
+
497
+ **Alternative: Using the plugin (may have timing issues):**
498
+
499
+ ```javascript
500
+ // next.config.js
501
+ const withDespiaLocal = require('@despia/local/next');
502
+
503
+ module.exports = withDespiaLocal({
504
+ // entryHtml is optional for SSR (not used)
505
+ outDir: '.next/static' // Target client assets directory
506
+ })({
507
+ // your Next.js config (SSR mode - no output: 'export')
508
+ });
509
+ ```
510
+
511
+ **Note**: The plugin approach may not work reliably for SSR because Next.js doesn't provide a reliable build completion hook. The post-build script approach is recommended for SSR apps.
512
+
464
513
  ### Nuxt
465
514
 
466
515
  ```javascript
467
516
  // nuxt.config.js
468
517
  export default {
469
518
  modules: ['@despia/local/nuxt'],
470
- despiaOffline: {
519
+ despiaLocal: {
471
520
  entryHtml: 'index.html'
472
521
  }
473
522
  }
@@ -476,9 +525,9 @@ export default {
476
525
  **Or as a local module:**
477
526
 
478
527
  ```javascript
479
- // modules/despia-offline.js
480
- import DespiaOfflineModule from '@despia/local/nuxt';
481
- export default DespiaOfflineModule;
528
+ // modules/despia-local.js
529
+ import DespiaLocalModule from '@despia/local/nuxt';
530
+ export default DespiaLocalModule;
482
531
  ```
483
532
 
484
533
  ### SvelteKit
@@ -486,12 +535,12 @@ export default DespiaOfflineModule;
486
535
  ```javascript
487
536
  // vite.config.js
488
537
  import { sveltekit } from '@sveltejs/kit/vite';
489
- import { despiaOfflineSvelteKit } from '@despia/local/sveltekit';
538
+ import { despiaLocalSvelteKit } from '@despia/local/sveltekit';
490
539
 
491
540
  export default {
492
541
  plugins: [
493
542
  sveltekit(),
494
- despiaOfflineSvelteKit({
543
+ despiaLocalSvelteKit({
495
544
  entryHtml: 'index.html'
496
545
  })
497
546
  ]
@@ -503,11 +552,11 @@ export default {
503
552
  ```javascript
504
553
  // astro.config.mjs
505
554
  import { defineConfig } from 'astro/config';
506
- import despiaOffline from '@despia/local/astro';
555
+ import despiaLocal from '@despia/local/astro';
507
556
 
508
557
  export default defineConfig({
509
558
  integrations: [
510
- despiaOffline({
559
+ despiaLocal({
511
560
  entryHtml: 'index.html',
512
561
  outDir: 'dist'
513
562
  })
@@ -520,12 +569,12 @@ export default defineConfig({
520
569
  ```javascript
521
570
  // vite.config.js (Remix uses Vite)
522
571
  import { remix } from '@remix-run/dev';
523
- import { despiaOfflineRemix } from '@despia/local/remix';
572
+ import { despiaLocalRemix } from '@despia/local/remix';
524
573
 
525
574
  export default {
526
575
  plugins: [
527
576
  remix(),
528
- despiaOfflineRemix({
577
+ despiaLocalRemix({
529
578
  entryHtml: 'index.html',
530
579
  outDir: 'build/client'
531
580
  })
@@ -537,13 +586,13 @@ export default {
537
586
 
538
587
  ```javascript
539
588
  import { build } from 'esbuild';
540
- import { despiaOfflineEsbuild } from '@despia/local/esbuild';
589
+ import { despiaLocalEsbuild } from '@despia/local/esbuild';
541
590
 
542
591
  await build({
543
592
  entryPoints: ['src/index.js'],
544
593
  outdir: 'dist',
545
594
  plugins: [
546
- despiaOfflineEsbuild({
595
+ despiaLocalEsbuild({
547
596
  outDir: 'dist',
548
597
  entryHtml: 'index.html'
549
598
  })
@@ -618,12 +667,12 @@ The generated `despia/local.json` file contains a sorted JSON array of root-rela
618
667
  // vite.config.js
619
668
  import { defineConfig } from 'vite';
620
669
  import react from '@vitejs/plugin-react';
621
- import { despiaOfflinePlugin } from '@despia/local/vite';
670
+ import { despiaLocalPlugin } from '@despia/local/vite';
622
671
 
623
672
  export default defineConfig({
624
673
  plugins: [
625
674
  react(),
626
- despiaOfflinePlugin()
675
+ despiaLocalPlugin()
627
676
  ]
628
677
  });
629
678
  ```
@@ -634,12 +683,12 @@ export default defineConfig({
634
683
  // vite.config.js
635
684
  import { defineConfig } from 'vite';
636
685
  import vue from '@vitejs/plugin-vue';
637
- import { despiaOfflinePlugin } from '@despia/local/vite';
686
+ import { despiaLocalPlugin } from '@despia/local/vite';
638
687
 
639
688
  export default defineConfig({
640
689
  plugins: [
641
690
  vue(),
642
- despiaOfflinePlugin()
691
+ despiaLocalPlugin()
643
692
  ]
644
693
  });
645
694
  ```
@@ -668,11 +717,11 @@ export default defineConfig({
668
717
 
669
718
  ```javascript
670
719
  // webpack.config.js
671
- const DespiaOfflinePlugin = require('@despia/local/webpack');
720
+ const DespiaLocalPlugin = require('@despia/local/webpack');
672
721
 
673
722
  module.exports = {
674
723
  plugins: [
675
- new DespiaOfflinePlugin({ outDir: 'dist' })
724
+ new DespiaLocalPlugin({ outDir: 'dist' })
676
725
  ]
677
726
  };
678
727
  ```
@@ -683,12 +732,12 @@ module.exports = {
683
732
  // vite.config.js
684
733
  import { defineConfig } from 'vite';
685
734
  import { svelte } from '@sveltejs/vite-plugin-svelte';
686
- import { despiaOfflinePlugin } from '@despia/local/vite';
735
+ import { despiaLocalPlugin } from '@despia/local/vite';
687
736
 
688
737
  export default defineConfig({
689
738
  plugins: [
690
739
  svelte(),
691
- despiaOfflinePlugin()
740
+ despiaLocalPlugin()
692
741
  ]
693
742
  });
694
743
  ```
@@ -733,6 +782,30 @@ The generated manifest is then used by Despia during app hydration and updates t
733
782
  - Paths starting with `/` are preserved as-is
734
783
  - Windows backslashes are converted to forward slashes
735
784
 
785
+ ### Next.js SSR Issues
786
+
787
+ **Manifest not generated for SSR app:**
788
+
789
+ 1. Ensure you're targeting `.next/static/` directory (not `.next/`)
790
+ 2. Use the post-build script approach: `"postbuild": "despia-local .next/static"`
791
+ 3. Verify build completed successfully: `next build` should finish without errors
792
+ 4. Check that `.next/static/` directory exists after build
793
+ 5. The plugin approach may not work reliably for SSR - prefer post-build script
794
+
795
+ **Missing assets in SSR manifest:**
796
+
797
+ - SSR apps only need client assets (JS, CSS, images)
798
+ - Server-side code in `.next/server/` is NOT included (correct behavior)
799
+ - Only assets in `.next/static/` should be in the manifest
800
+ - Verify you're scanning `.next/static/` not `.next/` or `.next/server/`
801
+
802
+ **Wrong directory for SSR:**
803
+
804
+ If you see errors about missing directories:
805
+ - For SSR: Use `.next/static/` (client assets only)
806
+ - For static export: Use `out/` (full static site)
807
+ - Never use `.next/server/` (server code, not needed for manifest)
808
+
736
809
  ## Contributing
737
810
 
738
811
  Contributions welcome! Please open an issue or submit a pull request.
@@ -14,21 +14,81 @@
14
14
  */
15
15
 
16
16
  import { generateManifest } from './src/core.js';
17
- import { resolve } from 'path';
17
+ import { resolve, join } from 'path';
18
+ import { existsSync } from 'fs';
18
19
 
19
20
  // Get command line arguments
20
21
  const outputDir = process.argv[2] || 'dist';
21
22
  const entryHtml = process.argv[3] || 'index.html';
22
23
 
24
+ // Detect Next.js SSR context
25
+ const isNextJsDir = outputDir.includes('.next');
26
+ const isNextJsStatic = outputDir.includes('.next/static') || outputDir === '.next/static';
27
+ const isNextJsRoot = outputDir === '.next';
28
+ const isNextJsServer = outputDir.includes('.next/server');
29
+
30
+ // Determine if we should skip entryHtml (for SSR apps)
31
+ const skipEntryHtml = isNextJsStatic || isNextJsServer;
32
+
33
+ // Provide helpful messages for Next.js
34
+ if (isNextJsDir && !isNextJsStatic && !isNextJsServer) {
35
+ if (isNextJsRoot) {
36
+ console.warn('⚠ Warning: Scanning .next/ directory directly.');
37
+ console.warn('💡 For SSR apps, use: despia-local .next/static');
38
+ console.warn('💡 For static export, use: despia-local out');
39
+ }
40
+ }
41
+
42
+ if (isNextJsServer) {
43
+ console.error('❌ Error: .next/server/ contains server-side code, not client assets.');
44
+ console.error('💡 For SSR apps, use: despia-local .next/static');
45
+ process.exit(1);
46
+ }
47
+
23
48
  try {
24
- console.log(`Scanning ${resolve(process.cwd(), outputDir)} for assets...`);
25
- const paths = generateManifest({ outputDir, entryHtml });
49
+ const resolvedPath = resolve(process.cwd(), outputDir);
50
+ console.log(`Scanning ${resolvedPath} for assets...`);
51
+
52
+ // Check if directory exists, and provide helpful suggestions for Next.js
53
+ if (!existsSync(resolvedPath)) {
54
+ if (isNextJsRoot) {
55
+ console.error(`❌ Directory "${resolvedPath}" does not exist.`);
56
+ console.error('💡 For SSR apps, try: despia-local .next/static');
57
+ console.error('💡 For static export, try: despia-local out');
58
+ }
59
+ throw new Error(`Output directory "${resolvedPath}" does not exist.`);
60
+ }
61
+
62
+ // Check for Next.js static directory and suggest if scanning wrong location
63
+ if (isNextJsRoot && existsSync(join(resolvedPath, 'static'))) {
64
+ console.warn('⚠ Found .next/static/ subdirectory.');
65
+ console.warn('💡 For SSR apps, consider scanning .next/static directly for better results.');
66
+ }
67
+
68
+ const paths = generateManifest({
69
+ outputDir,
70
+ entryHtml,
71
+ skipEntryHtml
72
+ });
26
73
 
27
74
  console.log(`✓ Generated despia/local.json`);
28
75
  console.log(`✓ Included ${paths.length} assets`);
29
- console.log(`✓ Entry HTML: /${entryHtml}`);
76
+ if (!skipEntryHtml) {
77
+ console.log(`✓ Entry HTML: /${entryHtml}`);
78
+ } else {
79
+ console.log(`✓ Skipped entry HTML (SSR mode)`);
80
+ }
30
81
  } catch (error) {
31
- console.error(`Error: ${error.message}`);
32
- console.error('Please run this script after your build completes.');
82
+ console.error(`❌ Error: ${error.message}`);
83
+ console.error('💡 Please run this script after your build completes.');
84
+
85
+ // Additional help for Next.js
86
+ if (isNextJsDir) {
87
+ console.error('');
88
+ console.error('Next.js tips:');
89
+ console.error(' - SSR apps: Use "despia-local .next/static"');
90
+ console.error(' - Static export: Use "despia-local out"');
91
+ }
92
+
33
93
  process.exit(1);
34
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@despia/local",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Universal build plugin to generate despia/local.json manifest for offline caching in Despia web-native apps. Supports Vite, Webpack, Rollup, Next.js, Nuxt, SvelteKit, Astro, Remix, esbuild, Parcel, and more.",
5
5
  "type": "module",
6
6
  "main": "./src/core.js",
package/src/astro.js CHANGED
@@ -3,11 +3,11 @@
3
3
  *
4
4
  * Usage in astro.config.mjs:
5
5
  * import { defineConfig } from 'astro/config';
6
- * import despiaOffline from '@despia/local/astro';
6
+ * import despiaLocal from '@despia/local/astro';
7
7
  *
8
8
  * export default defineConfig({
9
9
  * integrations: [
10
- * despiaOffline({ entryHtml: 'index.html' })
10
+ * despiaLocal({ entryHtml: 'index.html' })
11
11
  * ]
12
12
  * });
13
13
  */
@@ -15,11 +15,11 @@
15
15
  import { generateManifest } from './core.js';
16
16
  import { fileURLToPath } from 'url';
17
17
 
18
- export default function despiaOfflineIntegration(options = {}) {
18
+ export default function despiaLocalIntegration(options = {}) {
19
19
  const { entryHtml = 'index.html', outDir = 'dist' } = options;
20
20
 
21
21
  return {
22
- name: 'despia-offline',
22
+ name: 'despia-local',
23
23
  hooks: {
24
24
  'astro:build:done': async ({ dir }) => {
25
25
  // Astro provides dir as a URL object, convert to path
package/src/core.js CHANGED
@@ -51,9 +51,10 @@ export function collectFiles(dir, baseDir = dir) {
51
51
  * @param {string} options.outputDir - Output directory path
52
52
  * @param {string} options.entryHtml - Entry HTML filename (default: 'index.html')
53
53
  * @param {string[]} options.additionalPaths - Additional paths to include
54
+ * @param {boolean} options.skipEntryHtml - Skip adding entry HTML to manifest (for SSR apps)
54
55
  * @returns {string[]} Array of all asset paths
55
56
  */
56
- export function generateManifest({ outputDir, entryHtml = 'index.html', additionalPaths = [] }) {
57
+ export function generateManifest({ outputDir, entryHtml = 'index.html', additionalPaths = [], skipEntryHtml = false }) {
57
58
  const outputPath = resolve(process.cwd(), outputDir);
58
59
  const manifestPath = join(outputPath, 'despia', 'local.json');
59
60
 
@@ -71,11 +72,13 @@ export function generateManifest({ outputDir, entryHtml = 'index.html', addition
71
72
  assetPaths.add(normalizedPath);
72
73
  });
73
74
 
74
- // Ensure entry HTML is included
75
- const entryPath = entryHtml.startsWith('/')
76
- ? entryHtml
77
- : '/' + entryHtml;
78
- assetPaths.add(entryPath);
75
+ // Ensure entry HTML is included (unless skipped for SSR apps)
76
+ if (!skipEntryHtml) {
77
+ const entryPath = entryHtml.startsWith('/')
78
+ ? entryHtml
79
+ : '/' + entryHtml;
80
+ assetPaths.add(entryPath);
81
+ }
79
82
 
80
83
  // Convert to sorted array
81
84
  const sortedPaths = Array.from(assetPaths).sort();
package/src/esbuild.js CHANGED
@@ -3,21 +3,21 @@
3
3
  *
4
4
  * Usage:
5
5
  * import { build } from 'esbuild';
6
- * import { despiaOfflineEsbuild } from '@despia/local/esbuild';
6
+ * import { despiaLocalEsbuild } from '@despia/local/esbuild';
7
7
  *
8
8
  * await build({
9
- * plugins: [despiaOfflineEsbuild({ outDir: 'dist' })]
9
+ * plugins: [despiaLocalEsbuild({ outDir: 'dist' })]
10
10
  * });
11
11
  */
12
12
 
13
13
  import { generateManifest } from './core.js';
14
14
  import { relative } from 'path';
15
15
 
16
- export function despiaOfflineEsbuild(options = {}) {
16
+ export function despiaLocalEsbuild(options = {}) {
17
17
  const { outDir = 'dist', entryHtml = 'index.html' } = options;
18
18
 
19
19
  return {
20
- name: 'despia-offline',
20
+ name: 'despia-local',
21
21
  setup(build) {
22
22
  build.onEnd(async (result) => {
23
23
  if (result.errors.length > 0) {
package/src/index.js CHANGED
@@ -4,13 +4,13 @@
4
4
  */
5
5
 
6
6
  export { generateManifest, collectFiles } from './core.js';
7
- export { despiaOfflinePlugin } from './vite.js';
8
- export { default as DespiaOfflinePlugin } from './webpack.js';
9
- export { despiaOffline } from './rollup.js';
10
- export { withDespiaOffline } from './next.js';
11
- export { default as DespiaOfflineModule } from './nuxt.js';
12
- export { despiaOfflineSvelteKit } from './sveltekit.js';
13
- export { default as despiaOfflineIntegration } from './astro.js';
14
- export { despiaOfflineRemix } from './remix.js';
15
- export { despiaOfflineEsbuild } from './esbuild.js';
16
- export { default as DespiaOfflineParcel } from './parcel.js';
7
+ export { despiaLocalPlugin } from './vite.js';
8
+ export { default as DespiaLocalPlugin } from './webpack.js';
9
+ export { despiaLocal } from './rollup.js';
10
+ export { withDespiaLocal } from './next.js';
11
+ export { default as DespiaLocalModule } from './nuxt.js';
12
+ export { despiaLocalSvelteKit } from './sveltekit.js';
13
+ export { default as despiaLocalIntegration } from './astro.js';
14
+ export { despiaLocalRemix } from './remix.js';
15
+ export { despiaLocalEsbuild } from './esbuild.js';
16
+ export { default as DespiaLocalParcel } from './parcel.js';
package/src/next.js CHANGED
@@ -1,48 +1,73 @@
1
1
  /**
2
2
  * Next.js integration for generating despia/local.json manifest
3
3
  *
4
- * Usage in next.config.js:
5
- * const withDespiaOffline = require('@despia/local/next');
6
- * module.exports = withDespiaOffline({
4
+ * Usage for static export:
5
+ * const withDespiaLocal = require('@despia/local/next');
6
+ * module.exports = withDespiaLocal({
7
7
  * entryHtml: 'index.html',
8
- * outDir: '.next' // or 'out' for static export
8
+ * outDir: 'out' // Next.js static export directory
9
9
  * })({
10
+ * output: 'export',
10
11
  * // your next config
11
12
  * });
12
13
  *
13
- * Or use the webpack plugin approach:
14
- * const DespiaOfflinePlugin = require('@despia/local/webpack');
14
+ * Usage for SSR (recommended: use post-build script):
15
+ * // package.json
16
+ * {
17
+ * "scripts": {
18
+ * "build": "next build",
19
+ * "postbuild": "despia-local .next/static"
20
+ * }
21
+ * }
22
+ *
23
+ * Or use the webpack plugin approach (works best for static export):
24
+ * const DespiaLocalPlugin = require('@despia/local/webpack');
15
25
  * module.exports = {
16
26
  * webpack: (config) => {
17
- * config.plugins.push(new DespiaOfflinePlugin({ outDir: '.next' }));
27
+ * config.plugins.push(new DespiaLocalPlugin({ outDir: '.next' }));
18
28
  * return config;
19
29
  * }
20
30
  * };
21
31
  */
22
32
 
23
33
  import { generateManifest } from './core.js';
24
- import DespiaOfflinePlugin from './webpack.js';
34
+ import DespiaLocalPlugin from './webpack.js';
25
35
 
26
- export function withDespiaOffline(pluginOptions = {}) {
27
- const offlineConfig = {
36
+ export function withDespiaLocal(pluginOptions = {}) {
37
+ const localConfig = {
28
38
  outDir: pluginOptions.outDir || '.next',
29
39
  entryHtml: pluginOptions.entryHtml || 'index.html',
30
40
  ...pluginOptions
31
41
  };
32
42
 
33
43
  return (nextConfig = {}) => {
44
+ // Detect if this is static export or SSR
45
+ const isStaticExport = nextConfig.output === 'export';
34
46
  const existingWebpack = nextConfig.webpack;
35
47
 
36
48
  return {
37
49
  ...nextConfig,
38
50
  webpack: (config, options) => {
39
- // Add Despia Offline plugin
40
- config.plugins.push(
41
- new DespiaOfflinePlugin({
42
- outDir: offlineConfig.outDir,
43
- entryHtml: offlineConfig.entryHtml
44
- })
45
- );
51
+ // Only add webpack plugin for client builds (not server builds)
52
+ // For SSR, the webpack plugin will target .next/static during client build
53
+ // For static export, it works normally
54
+ if (!options.isServer) {
55
+ // Determine the correct output directory
56
+ let targetOutDir = localConfig.outDir;
57
+
58
+ // For SSR apps, target .next/static where client assets are stored
59
+ if (!isStaticExport && targetOutDir === '.next') {
60
+ targetOutDir = '.next/static';
61
+ }
62
+
63
+ config.plugins.push(
64
+ new DespiaLocalPlugin({
65
+ outDir: targetOutDir,
66
+ entryHtml: localConfig.entryHtml,
67
+ skipEntryHtml: !isStaticExport // Skip entryHtml for SSR
68
+ })
69
+ );
70
+ }
46
71
 
47
72
  // Call existing webpack config if present
48
73
  if (typeof existingWebpack === 'function') {
@@ -56,4 +81,4 @@ export function withDespiaOffline(pluginOptions = {}) {
56
81
  }
57
82
 
58
83
  // Also export as CommonJS for Next.js compatibility
59
- export default withDespiaOffline;
84
+ export default withDespiaLocal;
package/src/nuxt.js CHANGED
@@ -4,24 +4,24 @@
4
4
  * Usage in nuxt.config.js:
5
5
  * export default {
6
6
  * modules: ['@despia/local/nuxt'],
7
- * despiaOffline: {
7
+ * despiaLocal: {
8
8
  * entryHtml: 'index.html'
9
9
  * }
10
10
  * }
11
11
  *
12
12
  * Or use as a Nuxt module in modules/ directory:
13
- * // modules/despia-offline.js
14
- * import DespiaOfflineModule from '@despia/local/nuxt';
15
- * export default DespiaOfflineModule;
13
+ * // modules/despia-local.js
14
+ * import DespiaLocalModule from '@despia/local/nuxt';
15
+ * export default DespiaLocalModule;
16
16
  */
17
17
 
18
18
  import { generateManifest } from './core.js';
19
19
 
20
- export default function DespiaOfflineModule(moduleOptions) {
20
+ export default function DespiaLocalModule(moduleOptions) {
21
21
  const options = {
22
22
  entryHtml: 'index.html',
23
23
  ...moduleOptions,
24
- ...(this.options?.despiaOffline || {})
24
+ ...(this.options?.despiaLocal || {})
25
25
  };
26
26
 
27
27
  // Hook into Nuxt build completion
package/src/parcel.js CHANGED
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * "scripts": {
8
8
  * "build": "parcel build",
9
- * "postbuild": "despia-offline dist"
9
+ * "postbuild": "despia-local dist"
10
10
  * }
11
11
  *
12
12
  * For Parcel 1.x, you can use this plugin, but the API may vary.
@@ -19,7 +19,7 @@ export default function(api) {
19
19
  const { outDir = 'dist', entryHtml = 'index.html' } = api.options || {};
20
20
 
21
21
  return {
22
- name: 'despia-offline',
22
+ name: 'despia-local',
23
23
  async bundleEnd({ bundleGraph }) {
24
24
  const additionalPaths = [];
25
25
 
@@ -43,7 +43,7 @@ export default function(api) {
43
43
  console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
44
44
  } catch (error) {
45
45
  console.error('Error generating despia/local.json:', error.message);
46
- console.warn('💡 Tip: For Parcel projects, use the standalone CLI: "despia-offline dist"');
46
+ console.warn('💡 Tip: For Parcel projects, use the standalone CLI: "despia-local dist"');
47
47
  }
48
48
  }
49
49
  };
package/src/remix.js CHANGED
@@ -3,23 +3,23 @@
3
3
  *
4
4
  * Usage in remix.config.js or vite.config.js:
5
5
  * import { remix } from '@remix-run/dev';
6
- * import { despiaOfflineRemix } from '@despia/local/remix';
6
+ * import { despiaLocalRemix } from '@despia/local/remix';
7
7
  *
8
8
  * export default {
9
9
  * plugins: [
10
10
  * remix(),
11
- * despiaOfflineRemix({ entryHtml: 'index.html' })
11
+ * despiaLocalRemix({ entryHtml: 'index.html' })
12
12
  * ]
13
13
  * }
14
14
  */
15
15
 
16
16
  import { generateManifest } from './core.js';
17
17
 
18
- export function despiaOfflineRemix(options = {}) {
18
+ export function despiaLocalRemix(options = {}) {
19
19
  const { entryHtml = 'index.html', outDir = 'build/client' } = options;
20
20
 
21
21
  return {
22
- name: 'despia-offline-remix',
22
+ name: 'despia-local-remix',
23
23
  apply: 'build',
24
24
  buildEnd() {
25
25
  // Remix outputs to build/client for client assets
package/src/rollup.js CHANGED
@@ -10,11 +10,11 @@ import { generateManifest } from './core.js';
10
10
  * @param {string} options.outDir - Output directory (default: 'dist')
11
11
  * @param {string} options.entryHtml - Entry HTML file (default: 'index.html')
12
12
  */
13
- export function despiaOffline(options = {}) {
13
+ export function despiaLocal(options = {}) {
14
14
  const { outDir = 'dist', entryHtml = 'index.html' } = options;
15
15
 
16
16
  return {
17
- name: 'despia-offline',
17
+ name: 'despia-local',
18
18
  writeBundle(outputOptions, bundle) {
19
19
  const outputDir = outputOptions.dir || outDir;
20
20
  const additionalPaths = [];
package/src/sveltekit.js CHANGED
@@ -3,23 +3,23 @@
3
3
  *
4
4
  * Usage in vite.config.js:
5
5
  * import { sveltekit } from '@sveltejs/kit/vite';
6
- * import { despiaOfflineSvelteKit } from '@despia/local/sveltekit';
6
+ * import { despiaLocalSvelteKit } from '@despia/local/sveltekit';
7
7
  *
8
8
  * export default {
9
9
  * plugins: [
10
10
  * sveltekit(),
11
- * despiaOfflineSvelteKit({ entryHtml: 'index.html' })
11
+ * despiaLocalSvelteKit({ entryHtml: 'index.html' })
12
12
  * ]
13
13
  * }
14
14
  */
15
15
 
16
16
  import { generateManifest } from './core.js';
17
17
 
18
- export function despiaOfflineSvelteKit(options = {}) {
18
+ export function despiaLocalSvelteKit(options = {}) {
19
19
  const { entryHtml = 'index.html' } = options;
20
20
 
21
21
  return {
22
- name: 'despia-offline-sveltekit',
22
+ name: 'despia-local-sveltekit',
23
23
  apply: 'build',
24
24
  buildEnd() {
25
25
  // SvelteKit outputs to build directory
package/src/turbopack.js CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { generateManifest } from './core.js';
8
8
 
9
- export function despiaOfflineTurbopack(options = {}) {
9
+ export function despiaLocalTurbopack(options = {}) {
10
10
  const { entryHtml = 'index.html', outDir = '.next' } = options;
11
11
 
12
12
  // Turbopack is used by Next.js, so we recommend using the Next.js integration
@@ -14,7 +14,7 @@ export function despiaOfflineTurbopack(options = {}) {
14
14
  console.warn('⚠ Turbopack: Use @despia/local/next instead for Next.js projects');
15
15
 
16
16
  return {
17
- name: 'despia-offline-turbopack',
17
+ name: 'despia-local-turbopack',
18
18
  // Implementation would go here when Turbopack plugin API is stable
19
19
  };
20
20
  }
package/src/vite.js CHANGED
@@ -11,11 +11,11 @@ import { generateManifest } from './core.js';
11
11
  * @param {string} options.outDir - Output directory (default: 'dist')
12
12
  * @param {string} options.entryHtml - Entry HTML file (default: 'index.html')
13
13
  */
14
- export function despiaOfflinePlugin(options = {}) {
14
+ export function despiaLocalPlugin(options = {}) {
15
15
  const { outDir = 'dist', entryHtml = 'index.html' } = options;
16
16
 
17
17
  return {
18
- name: 'despia-offline',
18
+ name: 'despia-local',
19
19
  apply: 'build',
20
20
  writeBundle(bundleOptions, bundle) {
21
21
  const outputDir = bundleOptions.dir || outDir;
package/src/webpack.js CHANGED
@@ -4,19 +4,33 @@
4
4
 
5
5
  import { generateManifest } from './core.js';
6
6
 
7
- class DespiaOfflinePlugin {
7
+ class DespiaLocalPlugin {
8
8
  constructor(options = {}) {
9
9
  this.options = {
10
10
  outDir: options.outDir || 'dist',
11
11
  entryHtml: options.entryHtml || 'index.html',
12
+ skipEntryHtml: options.skipEntryHtml || false,
12
13
  ...options
13
14
  };
14
15
  }
15
16
 
16
17
  apply(compiler) {
17
- const pluginName = 'DespiaOfflinePlugin';
18
+ const pluginName = 'DespiaLocalPlugin';
18
19
 
19
20
  compiler.hooks.afterEmit.tapAsync(pluginName, (compilation, callback) => {
21
+ // Detect if this is a Next.js server build
22
+ // Next.js server builds have specific compiler name patterns
23
+ const compilerName = compilation.compiler.name || '';
24
+ const isNextJsServerBuild = compilerName.includes('server') ||
25
+ compilerName.includes('Server') ||
26
+ compilation.compiler.options?.target === 'node';
27
+
28
+ // Skip manifest generation for server builds (SSR apps don't need server-side assets)
29
+ if (isNextJsServerBuild) {
30
+ callback();
31
+ return;
32
+ }
33
+
20
34
  // Get output path from webpack compiler
21
35
  const outputPath = compilation.compiler.outputPath || this.options.outDir;
22
36
  const additionalPaths = [];
@@ -41,7 +55,8 @@ class DespiaOfflinePlugin {
41
55
  const paths = generateManifest({
42
56
  outputDir: outputPath,
43
57
  entryHtml: this.options.entryHtml,
44
- additionalPaths
58
+ additionalPaths,
59
+ skipEntryHtml: this.options.skipEntryHtml
45
60
  });
46
61
  console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
47
62
  } catch (error) {
@@ -53,4 +68,4 @@ class DespiaOfflinePlugin {
53
68
  }
54
69
  }
55
70
 
56
- export default DespiaOfflinePlugin;
71
+ export default DespiaLocalPlugin;