@despia/local 1.0.2 → 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 +59 -92
- package/generate-offline-manifest.js +58 -6
- package/package.json +1 -1
- package/src/core.js +14 -7
- package/src/next.js +11 -26
- package/src/webpack.js +113 -30
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,7 +432,20 @@ export default {
|
|
|
418
432
|
|
|
419
433
|
### Next.js
|
|
420
434
|
|
|
421
|
-
**
|
|
435
|
+
**Recommended: Client-Side Apps for Local/Offline Apps**
|
|
436
|
+
|
|
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:
|
|
438
|
+
|
|
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).
|
|
422
449
|
|
|
423
450
|
```javascript
|
|
424
451
|
// next.config.js
|
|
@@ -433,82 +460,15 @@ module.exports = withDespiaLocal({
|
|
|
433
460
|
});
|
|
434
461
|
```
|
|
435
462
|
|
|
436
|
-
**For
|
|
463
|
+
**For SSR Apps with Separate Static Build:**
|
|
437
464
|
|
|
438
|
-
|
|
439
|
-
// next.config.js
|
|
440
|
-
const withDespiaLocal = require('@despia/local/next');
|
|
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:
|
|
441
466
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
})({
|
|
446
|
-
output: 'export',
|
|
447
|
-
// ... rest of config
|
|
448
|
-
});
|
|
449
|
-
```
|
|
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
|
|
450
470
|
|
|
451
|
-
**
|
|
452
|
-
|
|
453
|
-
```javascript
|
|
454
|
-
// next.config.js
|
|
455
|
-
const DespiaLocalPlugin = require('@despia/local/webpack');
|
|
456
|
-
|
|
457
|
-
module.exports = {
|
|
458
|
-
output: 'export', // For static export
|
|
459
|
-
webpack: (config) => {
|
|
460
|
-
config.plugins.push(
|
|
461
|
-
new DespiaLocalPlugin({ outDir: 'out' })
|
|
462
|
-
);
|
|
463
|
-
return config;
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
```
|
|
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.
|
|
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.
|
|
512
472
|
|
|
513
473
|
### Nuxt
|
|
514
474
|
|
|
@@ -782,29 +742,36 @@ The generated manifest is then used by Despia during app hydration and updates t
|
|
|
782
742
|
- Paths starting with `/` are preserved as-is
|
|
783
743
|
- Windows backslashes are converted to forward slashes
|
|
784
744
|
|
|
785
|
-
### Next.js
|
|
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
|
|
786
754
|
|
|
787
|
-
**
|
|
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)
|
|
788
758
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
5. The plugin approach may not work reliably for SSR - prefer post-build script
|
|
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
|
|
794
763
|
|
|
795
|
-
**
|
|
764
|
+
**SSR Apps (Not Officially Supported):**
|
|
796
765
|
|
|
797
|
-
|
|
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/`
|
|
766
|
+
**Important**: This plugin does **not** officially support Next.js SSR apps. SSR requires custom tooling specific to your hosting provider.
|
|
801
767
|
|
|
802
|
-
|
|
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
|
|
803
773
|
|
|
804
|
-
|
|
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)
|
|
774
|
+
For production SSR apps, implement provider-specific solutions rather than relying on this plugin.
|
|
808
775
|
|
|
809
776
|
## Contributing
|
|
810
777
|
|
|
@@ -5,21 +5,64 @@
|
|
|
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
19
|
import { resolve, join } from 'path';
|
|
18
20
|
import { existsSync } from 'fs';
|
|
19
21
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
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
|
+
}
|
|
23
66
|
|
|
24
67
|
// Detect Next.js SSR context
|
|
25
68
|
const isNextJsDir = outputDir.includes('.next');
|
|
@@ -68,16 +111,25 @@ try {
|
|
|
68
111
|
const paths = generateManifest({
|
|
69
112
|
outputDir,
|
|
70
113
|
entryHtml,
|
|
71
|
-
skipEntryHtml
|
|
114
|
+
skipEntryHtml,
|
|
115
|
+
manifestOutputPath
|
|
72
116
|
});
|
|
73
117
|
|
|
74
|
-
|
|
118
|
+
const manifestLocation = manifestOutputPath || join(outputDir, 'despia', 'local.json');
|
|
119
|
+
console.log(`✓ Generated ${manifestLocation}`);
|
|
75
120
|
console.log(`✓ Included ${paths.length} assets`);
|
|
76
121
|
if (!skipEntryHtml) {
|
|
77
122
|
console.log(`✓ Entry HTML: /${entryHtml}`);
|
|
78
123
|
} else {
|
|
79
124
|
console.log(`✓ Skipped entry HTML (SSR mode)`);
|
|
80
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
|
+
}
|
|
81
133
|
} catch (error) {
|
|
82
134
|
console.error(`❌ Error: ${error.message}`);
|
|
83
135
|
console.error('💡 Please run this script after your build completes.');
|
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,15 +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
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)
|
|
55
56
|
* @returns {string[]} Array of all asset paths
|
|
56
57
|
*/
|
|
57
|
-
export function generateManifest({ outputDir, entryHtml = 'index.html', additionalPaths = [], skipEntryHtml = false }) {
|
|
58
|
+
export function generateManifest({ outputDir, entryHtml = 'index.html', additionalPaths = [], skipEntryHtml = false, manifestOutputPath = null }) {
|
|
58
59
|
const outputPath = resolve(process.cwd(), outputDir);
|
|
59
|
-
|
|
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');
|
|
60
65
|
|
|
61
66
|
// Check if output directory exists
|
|
62
67
|
if (!existsSync(outputPath)) {
|
|
@@ -83,10 +88,12 @@ export function generateManifest({ outputDir, entryHtml = 'index.html', addition
|
|
|
83
88
|
// Convert to sorted array
|
|
84
89
|
const sortedPaths = Array.from(assetPaths).sort();
|
|
85
90
|
|
|
86
|
-
// Create
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
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 });
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
// Write formatted JSON array
|
package/src/next.js
CHANGED
|
@@ -8,19 +8,19 @@
|
|
|
8
8
|
* outDir: 'out' // Next.js static export directory
|
|
9
9
|
* })({
|
|
10
10
|
* output: 'export',
|
|
11
|
-
* // your
|
|
11
|
+
* // your Next.js config
|
|
12
12
|
* });
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* For SSR apps, use the post-build script approach:
|
|
15
15
|
* // package.json
|
|
16
16
|
* {
|
|
17
17
|
* "scripts": {
|
|
18
18
|
* "build": "next build",
|
|
19
|
-
* "postbuild": "despia-local .next/static"
|
|
19
|
+
* "postbuild": "despia-local .next/static --output public/despia/local.json"
|
|
20
20
|
* }
|
|
21
21
|
* }
|
|
22
22
|
*
|
|
23
|
-
* Or use the webpack plugin approach
|
|
23
|
+
* Or use the webpack plugin approach:
|
|
24
24
|
* const DespiaLocalPlugin = require('@despia/local/webpack');
|
|
25
25
|
* module.exports = {
|
|
26
26
|
* webpack: (config) => {
|
|
@@ -41,33 +41,18 @@ export function withDespiaLocal(pluginOptions = {}) {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
return (nextConfig = {}) => {
|
|
44
|
-
// Detect if this is static export or SSR
|
|
45
|
-
const isStaticExport = nextConfig.output === 'export';
|
|
46
44
|
const existingWebpack = nextConfig.webpack;
|
|
47
45
|
|
|
48
46
|
return {
|
|
49
47
|
...nextConfig,
|
|
50
48
|
webpack: (config, options) => {
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
}
|
|
49
|
+
// Add Despia Local plugin
|
|
50
|
+
config.plugins.push(
|
|
51
|
+
new DespiaLocalPlugin({
|
|
52
|
+
outDir: localConfig.outDir,
|
|
53
|
+
entryHtml: localConfig.entryHtml
|
|
54
|
+
})
|
|
55
|
+
);
|
|
71
56
|
|
|
72
57
|
// Call existing webpack config if present
|
|
73
58
|
if (typeof existingWebpack === 'function') {
|
package/src/webpack.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
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 = {}) {
|
|
@@ -10,20 +12,57 @@ class DespiaLocalPlugin {
|
|
|
10
12
|
outDir: options.outDir || 'dist',
|
|
11
13
|
entryHtml: options.entryHtml || 'index.html',
|
|
12
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,
|
|
13
17
|
...options
|
|
14
18
|
};
|
|
15
19
|
}
|
|
16
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
|
+
|
|
17
49
|
apply(compiler) {
|
|
18
50
|
const pluginName = 'DespiaLocalPlugin';
|
|
51
|
+
const isNextJs = this.options.isNextJs || false;
|
|
52
|
+
const injectIntoAssets = this.options.injectIntoAssets || false;
|
|
53
|
+
|
|
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;
|
|
19
57
|
|
|
20
|
-
|
|
58
|
+
hook.tapAsync(pluginName, (compilation, callback) => {
|
|
21
59
|
// Detect if this is a Next.js server build
|
|
22
60
|
// Next.js server builds have specific compiler name patterns
|
|
23
61
|
const compilerName = compilation.compiler.name || '';
|
|
24
|
-
const isNextJsServerBuild = compilerName.includes('server') ||
|
|
62
|
+
const isNextJsServerBuild = (compilerName.includes('server') ||
|
|
25
63
|
compilerName.includes('Server') ||
|
|
26
|
-
compilation.compiler.options?.target === 'node'
|
|
64
|
+
compilation.compiler.options?.target === 'node') &&
|
|
65
|
+
isNextJs;
|
|
27
66
|
|
|
28
67
|
// Skip manifest generation for server builds (SSR apps don't need server-side assets)
|
|
29
68
|
if (isNextJsServerBuild) {
|
|
@@ -31,36 +70,80 @@ class DespiaLocalPlugin {
|
|
|
31
70
|
return;
|
|
32
71
|
}
|
|
33
72
|
|
|
34
|
-
|
|
35
|
-
const outputPath = compilation.compiler.outputPath || this.options.outDir;
|
|
36
|
-
const additionalPaths = [];
|
|
73
|
+
const assets = new Set();
|
|
37
74
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
+
}
|
|
43
89
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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);
|
|
51
146
|
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const paths = generateManifest({
|
|
56
|
-
outputDir: outputPath,
|
|
57
|
-
entryHtml: this.options.entryHtml,
|
|
58
|
-
additionalPaths,
|
|
59
|
-
skipEntryHtml: this.options.skipEntryHtml
|
|
60
|
-
});
|
|
61
|
-
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
62
|
-
} catch (error) {
|
|
63
|
-
console.error('Error generating despia/local.json:', error.message);
|
|
64
147
|
}
|
|
65
148
|
|
|
66
149
|
callback();
|