@despia/local 1.0.1 → 1.0.3
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 +68 -28
- package/generate-offline-manifest.js +123 -11
- package/package.json +1 -1
- package/src/core.js +22 -12
- package/src/next.js +13 -3
- package/src/webpack.js +124 -26
package/README.md
CHANGED
|
@@ -343,7 +343,21 @@ Add to your `package.json`:
|
|
|
343
343
|
Or run manually:
|
|
344
344
|
|
|
345
345
|
```bash
|
|
346
|
-
npx despia-local [outputDir] [entryHtml]
|
|
346
|
+
npx despia-local [outputDir] [entryHtml] [--output|-o manifestPath]
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Options:**
|
|
350
|
+
- `--output`, `-o <path>` - Custom output path for manifest file (useful for hosting providers)
|
|
351
|
+
- `--help`, `-h` - Show help message
|
|
352
|
+
|
|
353
|
+
**Examples:**
|
|
354
|
+
```bash
|
|
355
|
+
# Default: generates manifest in outputDir/despia/local.json
|
|
356
|
+
npx despia-local dist
|
|
357
|
+
|
|
358
|
+
# Custom output location (e.g., for Vercel/Netlify)
|
|
359
|
+
npx despia-local .next/static --output public/despia/local.json
|
|
360
|
+
npx despia-local dist -o public/manifest.json
|
|
347
361
|
```
|
|
348
362
|
|
|
349
363
|
## Framework Support
|
|
@@ -418,48 +432,43 @@ export default {
|
|
|
418
432
|
|
|
419
433
|
### Next.js
|
|
420
434
|
|
|
421
|
-
|
|
422
|
-
// next.config.js
|
|
423
|
-
const withDespiaLocal = require('@despia/local/next');
|
|
435
|
+
**Recommended: Client-Side Apps for Local/Offline Apps**
|
|
424
436
|
|
|
425
|
-
|
|
426
|
-
entryHtml: 'index.html',
|
|
427
|
-
outDir: '.next' // or 'out' for static export
|
|
428
|
-
})({
|
|
429
|
-
// your Next.js config
|
|
430
|
-
});
|
|
431
|
-
```
|
|
437
|
+
For local/offline apps, we **recommend using client-side frameworks** like React + Vite or Create React App instead of Next.js. Client-side apps are better suited for offline/local deployment because:
|
|
432
438
|
|
|
433
|
-
|
|
439
|
+
- All features are client-side by default
|
|
440
|
+
- No server-side dependencies
|
|
441
|
+
- Simpler build and deployment
|
|
442
|
+
- Better offline support
|
|
443
|
+
|
|
444
|
+
See the [React/Vite](#react--vite) section for examples.
|
|
445
|
+
|
|
446
|
+
**Supported: Static Export Only**
|
|
447
|
+
|
|
448
|
+
If you're using Next.js, this plugin supports apps using `output: 'export'` (static export mode).
|
|
434
449
|
|
|
435
450
|
```javascript
|
|
436
451
|
// next.config.js
|
|
437
452
|
const withDespiaLocal = require('@despia/local/next');
|
|
438
453
|
|
|
439
454
|
module.exports = withDespiaLocal({
|
|
440
|
-
|
|
441
|
-
|
|
455
|
+
entryHtml: 'index.html',
|
|
456
|
+
outDir: 'out' // Next.js static export directory
|
|
442
457
|
})({
|
|
443
458
|
output: 'export',
|
|
444
|
-
//
|
|
459
|
+
// your Next.js config
|
|
445
460
|
});
|
|
446
461
|
```
|
|
447
462
|
|
|
448
|
-
**
|
|
463
|
+
**For SSR Apps with Separate Static Build:**
|
|
449
464
|
|
|
450
|
-
|
|
451
|
-
// next.config.js
|
|
452
|
-
const DespiaLocalPlugin = require('@despia/local/webpack');
|
|
465
|
+
If you need SSR for your main site but want a static build for the local app, Next.js can easily generate a separate static build. You can set up a mini CI/CD pipeline to:
|
|
453
466
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
return config;
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
```
|
|
467
|
+
- Keep your main site as SSR (better for SEO, dynamic content)
|
|
468
|
+
- Generate a separate static export build for the local/offline app
|
|
469
|
+
- Deploy both builds independently
|
|
470
|
+
|
|
471
|
+
**Note**: Setting up the CI/CD pipeline for dual builds (SSR main site + static local app) requires custom configuration based on your hosting provider and build setup. You'll need to figure out the deployment strategy yourself - this plugin only handles manifest generation for the static export build.
|
|
463
472
|
|
|
464
473
|
### Nuxt
|
|
465
474
|
|
|
@@ -733,6 +742,37 @@ The generated manifest is then used by Despia during app hydration and updates t
|
|
|
733
742
|
- Paths starting with `/` are preserved as-is
|
|
734
743
|
- Windows backslashes are converted to forward slashes
|
|
735
744
|
|
|
745
|
+
### Next.js Troubleshooting
|
|
746
|
+
|
|
747
|
+
**Static Export Issues:**
|
|
748
|
+
|
|
749
|
+
**Manifest not generated:**
|
|
750
|
+
1. Ensure you're using `output: 'export'` in your `next.config.js`
|
|
751
|
+
2. Verify the plugin is correctly configured with `withDespiaLocal()`
|
|
752
|
+
3. Check that `out/` directory exists after build
|
|
753
|
+
4. Ensure `entryHtml` matches your actual entry HTML file
|
|
754
|
+
|
|
755
|
+
**Wrong directory:**
|
|
756
|
+
- For static export: Use `out/` directory (Next.js default for static export)
|
|
757
|
+
- Never use `.next/` for static export (that's for SSR builds)
|
|
758
|
+
|
|
759
|
+
**Manifest not accessible:**
|
|
760
|
+
- Manifest is generated in `out/despia/local.json`
|
|
761
|
+
- Ensure your hosting provider serves files from the `out/` directory
|
|
762
|
+
- Static export output should be served as static files
|
|
763
|
+
|
|
764
|
+
**SSR Apps (Not Officially Supported):**
|
|
765
|
+
|
|
766
|
+
**Important**: This plugin does **not** officially support Next.js SSR apps. SSR requires custom tooling specific to your hosting provider.
|
|
767
|
+
|
|
768
|
+
If you choose to use post-build scripts for SSR (unsupported):
|
|
769
|
+
1. Use `.next/static/` directory (client assets only)
|
|
770
|
+
2. Never include `.next/server/` (server code, not needed)
|
|
771
|
+
3. Customize the approach based on your hosting provider's requirements
|
|
772
|
+
4. This is experimental and not guaranteed to work reliably
|
|
773
|
+
|
|
774
|
+
For production SSR apps, implement provider-specific solutions rather than relying on this plugin.
|
|
775
|
+
|
|
736
776
|
## Contributing
|
|
737
777
|
|
|
738
778
|
Contributions welcome! Please open an issue or submit a pull request.
|
|
@@ -5,30 +5,142 @@
|
|
|
5
5
|
* Can be used with any build system by running after build completes
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* node generate-offline-manifest.js [outputDir] [entryHtml]
|
|
8
|
+
* node generate-offline-manifest.js [outputDir] [entryHtml] [--output|-o manifestPath]
|
|
9
9
|
*
|
|
10
10
|
* Examples:
|
|
11
11
|
* node generate-offline-manifest.js
|
|
12
12
|
* node generate-offline-manifest.js dist
|
|
13
13
|
* node generate-offline-manifest.js dist index.html
|
|
14
|
+
* node generate-offline-manifest.js .next/static --output public/despia/local.json
|
|
15
|
+
* node generate-offline-manifest.js dist -o public/despia/local.json
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
18
|
import { generateManifest } from './src/core.js';
|
|
17
|
-
import { resolve } from 'path';
|
|
19
|
+
import { resolve, join } from 'path';
|
|
20
|
+
import { existsSync } from 'fs';
|
|
18
21
|
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
// Parse command line arguments
|
|
23
|
+
let outputDir = 'dist';
|
|
24
|
+
let entryHtml = 'index.html';
|
|
25
|
+
let manifestOutputPath = null;
|
|
26
|
+
|
|
27
|
+
// Check for --help flag
|
|
28
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
29
|
+
console.log(`
|
|
30
|
+
Usage: despia-local [outputDir] [entryHtml] [options]
|
|
31
|
+
|
|
32
|
+
Arguments:
|
|
33
|
+
outputDir Directory to scan for assets (default: 'dist')
|
|
34
|
+
entryHtml Entry HTML filename (default: 'index.html')
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
--output, -o <path> Custom output path for manifest file
|
|
38
|
+
--help, -h Show this help message
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
despia-local
|
|
42
|
+
despia-local dist
|
|
43
|
+
despia-local dist index.html
|
|
44
|
+
despia-local .next/static --output public/despia/local.json
|
|
45
|
+
despia-local dist -o public/manifest.json
|
|
46
|
+
`);
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Parse arguments
|
|
51
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
52
|
+
const arg = process.argv[i];
|
|
53
|
+
|
|
54
|
+
if (arg === '--output' || arg === '-o') {
|
|
55
|
+
manifestOutputPath = process.argv[++i];
|
|
56
|
+
if (!manifestOutputPath) {
|
|
57
|
+
console.error('❌ Error: --output/-o requires a path argument');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
} else if (outputDir === 'dist' && !arg.startsWith('-')) {
|
|
61
|
+
outputDir = arg;
|
|
62
|
+
} else if (!arg.startsWith('-') && entryHtml === 'index.html' && manifestOutputPath === null) {
|
|
63
|
+
entryHtml = arg;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Detect Next.js SSR context
|
|
68
|
+
const isNextJsDir = outputDir.includes('.next');
|
|
69
|
+
const isNextJsStatic = outputDir.includes('.next/static') || outputDir === '.next/static';
|
|
70
|
+
const isNextJsRoot = outputDir === '.next';
|
|
71
|
+
const isNextJsServer = outputDir.includes('.next/server');
|
|
72
|
+
|
|
73
|
+
// Determine if we should skip entryHtml (for SSR apps)
|
|
74
|
+
const skipEntryHtml = isNextJsStatic || isNextJsServer;
|
|
75
|
+
|
|
76
|
+
// Provide helpful messages for Next.js
|
|
77
|
+
if (isNextJsDir && !isNextJsStatic && !isNextJsServer) {
|
|
78
|
+
if (isNextJsRoot) {
|
|
79
|
+
console.warn('⚠ Warning: Scanning .next/ directory directly.');
|
|
80
|
+
console.warn('💡 For SSR apps, use: despia-local .next/static');
|
|
81
|
+
console.warn('💡 For static export, use: despia-local out');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (isNextJsServer) {
|
|
86
|
+
console.error('❌ Error: .next/server/ contains server-side code, not client assets.');
|
|
87
|
+
console.error('💡 For SSR apps, use: despia-local .next/static');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
22
90
|
|
|
23
91
|
try {
|
|
24
|
-
|
|
25
|
-
|
|
92
|
+
const resolvedPath = resolve(process.cwd(), outputDir);
|
|
93
|
+
console.log(`Scanning ${resolvedPath} for assets...`);
|
|
94
|
+
|
|
95
|
+
// Check if directory exists, and provide helpful suggestions for Next.js
|
|
96
|
+
if (!existsSync(resolvedPath)) {
|
|
97
|
+
if (isNextJsRoot) {
|
|
98
|
+
console.error(`❌ Directory "${resolvedPath}" does not exist.`);
|
|
99
|
+
console.error('💡 For SSR apps, try: despia-local .next/static');
|
|
100
|
+
console.error('💡 For static export, try: despia-local out');
|
|
101
|
+
}
|
|
102
|
+
throw new Error(`Output directory "${resolvedPath}" does not exist.`);
|
|
103
|
+
}
|
|
26
104
|
|
|
27
|
-
|
|
105
|
+
// Check for Next.js static directory and suggest if scanning wrong location
|
|
106
|
+
if (isNextJsRoot && existsSync(join(resolvedPath, 'static'))) {
|
|
107
|
+
console.warn('⚠ Found .next/static/ subdirectory.');
|
|
108
|
+
console.warn('💡 For SSR apps, consider scanning .next/static directly for better results.');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const paths = generateManifest({
|
|
112
|
+
outputDir,
|
|
113
|
+
entryHtml,
|
|
114
|
+
skipEntryHtml,
|
|
115
|
+
manifestOutputPath
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const manifestLocation = manifestOutputPath || join(outputDir, 'despia', 'local.json');
|
|
119
|
+
console.log(`✓ Generated ${manifestLocation}`);
|
|
28
120
|
console.log(`✓ Included ${paths.length} assets`);
|
|
29
|
-
|
|
121
|
+
if (!skipEntryHtml) {
|
|
122
|
+
console.log(`✓ Entry HTML: /${entryHtml}`);
|
|
123
|
+
} else {
|
|
124
|
+
console.log(`✓ Skipped entry HTML (SSR mode)`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Provide helpful hint if using default location for Next.js SSR
|
|
128
|
+
if (isNextJsStatic && !manifestOutputPath) {
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log('💡 Tip: For hosting providers (Vercel, Netlify, etc.), consider using:');
|
|
131
|
+
console.log(` despia-local .next/static --output public/despia/local.json`);
|
|
132
|
+
}
|
|
30
133
|
} catch (error) {
|
|
31
|
-
console.error(
|
|
32
|
-
console.error('Please run this script after your build completes.');
|
|
134
|
+
console.error(`❌ Error: ${error.message}`);
|
|
135
|
+
console.error('💡 Please run this script after your build completes.');
|
|
136
|
+
|
|
137
|
+
// Additional help for Next.js
|
|
138
|
+
if (isNextJsDir) {
|
|
139
|
+
console.error('');
|
|
140
|
+
console.error('Next.js tips:');
|
|
141
|
+
console.error(' - SSR apps: Use "despia-local .next/static"');
|
|
142
|
+
console.error(' - Static export: Use "despia-local out"');
|
|
143
|
+
}
|
|
144
|
+
|
|
33
145
|
process.exit(1);
|
|
34
146
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@despia/local",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
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
|
@@ -48,14 +48,20 @@ export function collectFiles(dir, baseDir = dir) {
|
|
|
48
48
|
/**
|
|
49
49
|
* Generate the offline manifest file
|
|
50
50
|
* @param {Object} options
|
|
51
|
-
* @param {string} options.outputDir - Output directory path
|
|
51
|
+
* @param {string} options.outputDir - Output directory path (where to scan for assets)
|
|
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)
|
|
55
|
+
* @param {string} options.manifestOutputPath - Custom path for manifest file (default: outputDir/despia/local.json)
|
|
54
56
|
* @returns {string[]} Array of all asset paths
|
|
55
57
|
*/
|
|
56
|
-
export function generateManifest({ outputDir, entryHtml = 'index.html', additionalPaths = [] }) {
|
|
58
|
+
export function generateManifest({ outputDir, entryHtml = 'index.html', additionalPaths = [], skipEntryHtml = false, manifestOutputPath = null }) {
|
|
57
59
|
const outputPath = resolve(process.cwd(), outputDir);
|
|
58
|
-
|
|
60
|
+
|
|
61
|
+
// Use custom manifest output path if provided, otherwise default to outputDir/despia/local.json
|
|
62
|
+
const manifestPath = manifestOutputPath
|
|
63
|
+
? resolve(process.cwd(), manifestOutputPath)
|
|
64
|
+
: join(outputPath, 'despia', 'local.json');
|
|
59
65
|
|
|
60
66
|
// Check if output directory exists
|
|
61
67
|
if (!existsSync(outputPath)) {
|
|
@@ -71,19 +77,23 @@ export function generateManifest({ outputDir, entryHtml = 'index.html', addition
|
|
|
71
77
|
assetPaths.add(normalizedPath);
|
|
72
78
|
});
|
|
73
79
|
|
|
74
|
-
// Ensure entry HTML is included
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
// Ensure entry HTML is included (unless skipped for SSR apps)
|
|
81
|
+
if (!skipEntryHtml) {
|
|
82
|
+
const entryPath = entryHtml.startsWith('/')
|
|
83
|
+
? entryHtml
|
|
84
|
+
: '/' + entryHtml;
|
|
85
|
+
assetPaths.add(entryPath);
|
|
86
|
+
}
|
|
79
87
|
|
|
80
88
|
// Convert to sorted array
|
|
81
89
|
const sortedPaths = Array.from(assetPaths).sort();
|
|
82
90
|
|
|
83
|
-
// Create
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
// Create directory for manifest if it doesn't exist
|
|
92
|
+
const manifestDir = manifestOutputPath
|
|
93
|
+
? resolve(manifestPath, '..')
|
|
94
|
+
: join(outputPath, 'despia');
|
|
95
|
+
if (!existsSync(manifestDir)) {
|
|
96
|
+
mkdirSync(manifestDir, { recursive: true });
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
// Write formatted JSON array
|
package/src/next.js
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Next.js integration for generating despia/local.json manifest
|
|
3
3
|
*
|
|
4
|
-
* Usage
|
|
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: '
|
|
8
|
+
* outDir: 'out' // Next.js static export directory
|
|
9
9
|
* })({
|
|
10
|
-
*
|
|
10
|
+
* output: 'export',
|
|
11
|
+
* // your Next.js config
|
|
11
12
|
* });
|
|
12
13
|
*
|
|
14
|
+
* For SSR apps, use the post-build script approach:
|
|
15
|
+
* // package.json
|
|
16
|
+
* {
|
|
17
|
+
* "scripts": {
|
|
18
|
+
* "build": "next build",
|
|
19
|
+
* "postbuild": "despia-local .next/static --output public/despia/local.json"
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
13
23
|
* Or use the webpack plugin approach:
|
|
14
24
|
* const DespiaLocalPlugin = require('@despia/local/webpack');
|
|
15
25
|
* module.exports = {
|
package/src/webpack.js
CHANGED
|
@@ -3,49 +3,147 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { generateManifest } from './core.js';
|
|
6
|
+
import { readdirSync } from 'fs';
|
|
7
|
+
import { join, relative, extname } from 'path';
|
|
6
8
|
|
|
7
9
|
class DespiaLocalPlugin {
|
|
8
10
|
constructor(options = {}) {
|
|
9
11
|
this.options = {
|
|
10
12
|
outDir: options.outDir || 'dist',
|
|
11
13
|
entryHtml: options.entryHtml || 'index.html',
|
|
14
|
+
skipEntryHtml: options.skipEntryHtml || false,
|
|
15
|
+
extensions: options.extensions || ['.js', '.css', '.mjs', '.woff', '.woff2', '.ttf', '.eot', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico', '.json', '.xml', '.txt'],
|
|
16
|
+
publicDir: options.publicDir || null,
|
|
12
17
|
...options
|
|
13
18
|
};
|
|
14
19
|
}
|
|
15
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Scan public directory for static assets
|
|
23
|
+
*/
|
|
24
|
+
scanPublicDir(dir, baseDir, assets) {
|
|
25
|
+
try {
|
|
26
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
27
|
+
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
const fullPath = join(dir, entry.name);
|
|
30
|
+
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
// Skip despia folder to avoid circular reference
|
|
33
|
+
if (entry.name !== 'despia') {
|
|
34
|
+
this.scanPublicDir(fullPath, baseDir, assets);
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
const ext = extname(entry.name).toLowerCase();
|
|
38
|
+
if (this.options.extensions.includes(ext)) {
|
|
39
|
+
const relativePath = '/' + relative(baseDir, fullPath).replace(/\\/g, '/');
|
|
40
|
+
assets.add(relativePath);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// Ignore permission errors
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
16
49
|
apply(compiler) {
|
|
17
50
|
const pluginName = 'DespiaLocalPlugin';
|
|
51
|
+
const isNextJs = this.options.isNextJs || false;
|
|
52
|
+
const injectIntoAssets = this.options.injectIntoAssets || false;
|
|
18
53
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
54
|
+
// For Next.js: Use 'emit' phase to inject into compilation.assets
|
|
55
|
+
// For other bundlers: Use 'afterEmit' phase to write to filesystem
|
|
56
|
+
const hook = injectIntoAssets ? compiler.hooks.emit : compiler.hooks.afterEmit;
|
|
57
|
+
|
|
58
|
+
hook.tapAsync(pluginName, (compilation, callback) => {
|
|
59
|
+
// Detect if this is a Next.js server build
|
|
60
|
+
// Next.js server builds have specific compiler name patterns
|
|
61
|
+
const compilerName = compilation.compiler.name || '';
|
|
62
|
+
const isNextJsServerBuild = (compilerName.includes('server') ||
|
|
63
|
+
compilerName.includes('Server') ||
|
|
64
|
+
compilation.compiler.options?.target === 'node') &&
|
|
65
|
+
isNextJs;
|
|
23
66
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
additionalPaths.push(rootRelativePath);
|
|
29
|
-
}
|
|
67
|
+
// Skip manifest generation for server builds (SSR apps don't need server-side assets)
|
|
68
|
+
if (isNextJsServerBuild) {
|
|
69
|
+
callback();
|
|
70
|
+
return;
|
|
30
71
|
}
|
|
31
72
|
|
|
32
|
-
|
|
33
|
-
if (compilation.getAssets) {
|
|
34
|
-
for (const asset of compilation.getAssets()) {
|
|
35
|
-
const rootRelativePath = '/' + asset.name.replace(/\\/g, '/');
|
|
36
|
-
additionalPaths.push(rootRelativePath);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
73
|
+
const assets = new Set();
|
|
39
74
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
75
|
+
if (injectIntoAssets && isNextJs) {
|
|
76
|
+
// Next.js mode: Collect from webpack compilation and public folder
|
|
77
|
+
|
|
78
|
+
// 1. Collect all webpack-generated assets from .next/static
|
|
79
|
+
for (const filename of Object.keys(compilation.assets)) {
|
|
80
|
+
if (filename === this.options.manifestPath) {
|
|
81
|
+
continue; // Skip the manifest file itself
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ext = extname(filename).toLowerCase();
|
|
85
|
+
if (this.options.extensions.includes(ext)) {
|
|
86
|
+
// Next.js serves these at /_next/static/...
|
|
87
|
+
assets.add(`/_next/static/${filename}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 2. Scan /public folder for static assets
|
|
92
|
+
if (this.options.publicDir) {
|
|
93
|
+
this.scanPublicDir(this.options.publicDir, this.options.publicDir, assets);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 3. Generate sorted array (Despia format - just a JSON array)
|
|
97
|
+
const manifest = JSON.stringify([...assets].sort(), null, 2);
|
|
98
|
+
|
|
99
|
+
// 4. Inject into webpack output at despia/local.json
|
|
100
|
+
const manifestPath = this.options.manifestPath || 'despia/local.json';
|
|
101
|
+
compilation.assets[manifestPath] = {
|
|
102
|
+
source: () => manifest,
|
|
103
|
+
size: () => Buffer.byteLength(manifest, 'utf8')
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
console.log(`✓ Injected despia/local.json into build with ${assets.size} assets`);
|
|
107
|
+
} else {
|
|
108
|
+
// Traditional mode: Write to filesystem (for other bundlers)
|
|
109
|
+
const additionalPaths = new Set();
|
|
110
|
+
|
|
111
|
+
// Collect all emitted assets from compilation
|
|
112
|
+
for (const [filename, asset] of Object.entries(compilation.assets)) {
|
|
113
|
+
if (asset && filename !== this.options.manifestPath) {
|
|
114
|
+
let rootRelativePath = filename.replace(/\\/g, '/');
|
|
115
|
+
if (!rootRelativePath.startsWith('/')) {
|
|
116
|
+
rootRelativePath = '/' + rootRelativePath;
|
|
117
|
+
}
|
|
118
|
+
additionalPaths.add(rootRelativePath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Also collect from compilation.getAssets() if available (webpack 5)
|
|
123
|
+
if (compilation.getAssets) {
|
|
124
|
+
for (const asset of compilation.getAssets()) {
|
|
125
|
+
if (asset.name !== this.options.manifestPath) {
|
|
126
|
+
let assetPath = asset.name.replace(/\\/g, '/');
|
|
127
|
+
if (!assetPath.startsWith('/')) {
|
|
128
|
+
assetPath = '/' + assetPath;
|
|
129
|
+
}
|
|
130
|
+
additionalPaths.add(assetPath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const outputPath = compilation.compiler.outputPath || this.options.outDir;
|
|
137
|
+
const paths = generateManifest({
|
|
138
|
+
outputDir: outputPath,
|
|
139
|
+
entryHtml: this.options.entryHtml,
|
|
140
|
+
additionalPaths: Array.from(additionalPaths),
|
|
141
|
+
skipEntryHtml: this.options.skipEntryHtml
|
|
142
|
+
});
|
|
143
|
+
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error('Error generating despia/local.json:', error.message);
|
|
146
|
+
}
|
|
49
147
|
}
|
|
50
148
|
|
|
51
149
|
callback();
|