@despia/local 1.0.1 → 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
@@ -418,19 +418,22 @@ 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
425
  const withDespiaLocal = require('@despia/local/next');
424
426
 
425
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
@@ -445,22 +448,68 @@ module.exports = withDespiaLocal({
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
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 DespiaLocalPlugin({ 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
@@ -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.1",
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/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/next.js CHANGED
@@ -1,16 +1,26 @@
1
1
  /**
2
2
  * Next.js integration for generating despia/local.json manifest
3
3
  *
4
- * Usage in next.config.js:
4
+ * Usage for static export:
5
5
  * const withDespiaLocal = require('@despia/local/next');
6
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
+ * 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):
14
24
  * const DespiaLocalPlugin = require('@despia/local/webpack');
15
25
  * module.exports = {
16
26
  * webpack: (config) => {
@@ -31,18 +41,33 @@ export function withDespiaLocal(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 Local plugin
40
- config.plugins.push(
41
- new DespiaLocalPlugin({
42
- outDir: localConfig.outDir,
43
- entryHtml: localConfig.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') {
package/src/webpack.js CHANGED
@@ -9,6 +9,7 @@ class DespiaLocalPlugin {
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
  }
@@ -17,6 +18,19 @@ class DespiaLocalPlugin {
17
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 DespiaLocalPlugin {
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) {