@despia/local 1.0.4 → 1.0.6
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 +108 -28
- package/package.json +1 -1
- package/src/astro.js +3 -2
- package/src/core.js +21 -11
- package/src/esbuild.js +3 -2
- package/src/nuxt.js +3 -2
- package/src/parcel.js +3 -2
- package/src/remix.js +3 -2
- package/src/rollup.js +3 -2
- package/src/sveltekit.js +3 -2
- package/src/vite.js +3 -2
- package/src/webpack.js +24 -5
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @despia/local
|
|
2
2
|
|
|
3
|
-
Universal build plugin to generate `despia/local.json` manifest for [Despia](https://despia.com) web-native apps. This manifest enables Despia
|
|
3
|
+
Universal build plugin to generate `despia/local.json` manifest for [Despia](https://despia.com) web-native apps. This manifest enables Despia's local server, which runs your web app from a local HTTP server on-device, providing full offline functionality with seamless updates.
|
|
4
4
|
|
|
5
|
-
**Note**: Despia
|
|
5
|
+
**Note**: Despia's local server is optional. Normally, the Despia runtime can run your web app directly from a URL. Despia's local server is for developers who need extra performance and full true native offline support.
|
|
6
6
|
|
|
7
|
-
**Store Compliant**: Despia
|
|
7
|
+
**Store Compliant**: Despia's local server is fully compliant with Apple App Store and Google Play Store guidelines. It downloads and caches web content (HTML, CSS, JavaScript) for offline display in a WebView—identical to how web browsers work. No native code or executables are downloaded. See [Store Compliance & Security](#store-compliance--security) section for detailed compliance information.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -15,11 +15,11 @@ Universal build plugin to generate `despia/local.json` manifest for [Despia](htt
|
|
|
15
15
|
- **Sorted Output** - Alphabetically sorted paths for consistent builds
|
|
16
16
|
- **Standalone Script** - Can be used with any build system via CLI
|
|
17
17
|
|
|
18
|
-
## About Despia Local Server
|
|
18
|
+
## About Despia's Local Server
|
|
19
19
|
|
|
20
|
-
**Note**: Despia
|
|
20
|
+
**Note**: Despia's local server is optional. Normally, the Despia runtime can run your web app directly from a URL. Despia's local server is for developers who need extra performance and full true native offline support.
|
|
21
21
|
|
|
22
|
-
Despia
|
|
22
|
+
Despia's local server enables your web app to run entirely from a local HTTP server on-device, providing a native app experience with full offline functionality.
|
|
23
23
|
|
|
24
24
|
### Architecture
|
|
25
25
|
|
|
@@ -28,13 +28,13 @@ The Despia native container consists of:
|
|
|
28
28
|
- **Native iOS/Android Container**: The native binary submitted to App Store/Play Store
|
|
29
29
|
- WebView for UI rendering (WKWebView on iOS, WebView on Android)
|
|
30
30
|
- JavaScript bridge for native API access (exposes native capabilities to web content via [`despia-native`](https://www.npmjs.com/package/despia-native))
|
|
31
|
-
- Local
|
|
31
|
+
- Local HTTP server infrastructure (runs on-device only)
|
|
32
32
|
- Storage and update management systems
|
|
33
33
|
|
|
34
34
|
- **Web Content Layer** (downloaded and cached):
|
|
35
35
|
- HTML, CSS, JavaScript files
|
|
36
36
|
- Images, fonts, and other static assets
|
|
37
|
-
- Served from `
|
|
37
|
+
- Served from `http://localhost` after initial hydration
|
|
38
38
|
|
|
39
39
|
**Key Architecture Principles:**
|
|
40
40
|
- Web content updates (HTML/CSS/JS only)
|
|
@@ -50,14 +50,14 @@ The App Store/Play Store binary contains the native iOS/Android container (see [
|
|
|
50
50
|
- Native code (Swift/Kotlin) for platform integration
|
|
51
51
|
- WebView component for UI rendering
|
|
52
52
|
- JavaScript bridge for native API access (exposes native capabilities to web content via [`despia-native`](https://www.npmjs.com/package/despia-native))
|
|
53
|
-
- Local
|
|
53
|
+
- Local HTTP server infrastructure (runs on-device only)
|
|
54
54
|
- Storage and update management systems
|
|
55
55
|
- No web app assets (HTML, CSS, JavaScript) are bundled in the binary
|
|
56
56
|
|
|
57
57
|
This approach keeps initial install sizes minimal and allows for rapid iteration of web UI without requiring app store resubmissions. **Important**: All native code, the JavaScript bridge, and app functionality are fixed at submission time and cannot be changed. Only web content (HTML/CSS/JS) can be updated.
|
|
58
58
|
|
|
59
59
|
**First Launch Hydration**
|
|
60
|
-
On first app launch, Despia securely downloads your latest web build (HTML, CSS, JavaScript, images, fonts) from your repository and stores it locally on the device. The local
|
|
60
|
+
On first app launch, Despia securely downloads your latest web build (HTML, CSS, JavaScript, images, fonts) from your repository and stores it locally on the device. The local HTTP server then begins serving your web content from `http://localhost`, providing a stable origin for your web application.
|
|
61
61
|
|
|
62
62
|
**Note on Compliance**: This process downloads and caches web content files only. No native code, executables, or binary files are downloaded. The JavaScript executes within the WebView's sandbox, identical to how Safari or Chrome render web pages.
|
|
63
63
|
|
|
@@ -67,7 +67,7 @@ After the initial hydration, your app operates completely offline:
|
|
|
67
67
|
- No network connectivity required
|
|
68
68
|
- Full SPA routing and navigation
|
|
69
69
|
- Deep linking support
|
|
70
|
-
- Stable
|
|
70
|
+
- Stable HTTP origin (`http://localhost`) for hybrid app flexibility
|
|
71
71
|
|
|
72
72
|
If the device goes offline permanently, the app continues to function using the last hydrated version, ensuring reliability even without connectivity.
|
|
73
73
|
|
|
@@ -84,7 +84,7 @@ When connectivity is available, Despia checks for updates in the background and
|
|
|
84
84
|
|
|
85
85
|
**Compliance Summary for App Reviewers:**
|
|
86
86
|
|
|
87
|
-
Despia
|
|
87
|
+
Despia's local server downloads and caches web content files (HTML, CSS, JavaScript, images) for offline display in a WebView. This is identical to how web browsers (Safari, Chrome) download and render web pages.
|
|
88
88
|
|
|
89
89
|
**Key Compliance Points:**
|
|
90
90
|
- No native code is downloaded or executed
|
|
@@ -133,7 +133,7 @@ See the [`despia-version-guard` documentation](https://www.npmjs.com/package/des
|
|
|
133
133
|
|
|
134
134
|
---
|
|
135
135
|
|
|
136
|
-
Despia
|
|
136
|
+
Despia's local server is fully compliant with both Apple App Store and Google Play Store guidelines. This section explains the compliance in detail.
|
|
137
137
|
|
|
138
138
|
#### Apple App Store Compliance
|
|
139
139
|
|
|
@@ -141,7 +141,7 @@ Despia Local Server is fully compliant with both Apple App Store and Google Play
|
|
|
141
141
|
> "Apps must not download, install, or execute code which introduces or changes features or functionality of the app, including other apps."
|
|
142
142
|
|
|
143
143
|
**Compliance Statement:**
|
|
144
|
-
Despia
|
|
144
|
+
Despia's local server complies with this guideline because:
|
|
145
145
|
|
|
146
146
|
1. **No Native Code Execution**: The app does not download, install, or execute any native code (Swift, Objective-C, C++, etc.). All native code is fixed at App Store submission time and cannot be modified.
|
|
147
147
|
|
|
@@ -159,7 +159,7 @@ Despia Local Server complies with this guideline because:
|
|
|
159
159
|
|
|
160
160
|
**Note**: This follows the same pattern as [Expo](https://expo.dev), which has been generally accepted by app stores. However, Apple's interpretation of "feature changes" under Guideline 3.3.2 is subjective, and your app could still face review challenges. It's recommended to be conservative with updates that significantly change app behavior, even when using existing native APIs.
|
|
161
161
|
|
|
162
|
-
4. **On-Device Execution**: All web content is served from a local
|
|
162
|
+
4. **On-Device Execution**: All web content is served from a local HTTP server running on-device (`localhost`). No remote code execution occurs.
|
|
163
163
|
|
|
164
164
|
**This approach is explicitly permitted** under App Store guidelines, as evidenced by:
|
|
165
165
|
- Safari and other browsers downloading and rendering web content
|
|
@@ -173,7 +173,7 @@ Despia Local Server complies with this guideline because:
|
|
|
173
173
|
> "Apps must not download executable code (e.g., dex, JAR, .so files) from a source other than Google Play."
|
|
174
174
|
|
|
175
175
|
**Compliance Statement:**
|
|
176
|
-
Despia
|
|
176
|
+
Despia's local server complies with this policy because:
|
|
177
177
|
|
|
178
178
|
1. **No Executable Code Downloads**: The app does not download any executable code (`.dex`, `.jar`, `.so`, `.apk`, or native binaries). Only web content files (HTML, CSS, JavaScript, images, fonts) are downloaded.
|
|
179
179
|
|
|
@@ -236,12 +236,87 @@ This follows the same pattern as established frameworks like [Expo](https://expo
|
|
|
236
236
|
- Consider submitting major feature additions through the normal App Store review process when possible
|
|
237
237
|
|
|
238
238
|
**Security Model:**
|
|
239
|
-
- All web content is served from `
|
|
239
|
+
- All web content is served from `http://localhost` (on-device only)
|
|
240
240
|
- WebView sandbox enforces same-origin policy
|
|
241
241
|
- No network access to external servers after initial hydration
|
|
242
242
|
- Content Security Policy (CSP) can be enforced
|
|
243
243
|
- All execution happens within WebView's security boundaries
|
|
244
244
|
|
|
245
|
+
### HTTP vs HTTPS for Local Apps
|
|
246
|
+
|
|
247
|
+
Despia's local server uses **HTTP** for serving local content. This design choice provides important flexibility for hybrid apps while maintaining full security through `localhost`'s inherent security guarantees.
|
|
248
|
+
|
|
249
|
+
#### Security Guarantees of localhost
|
|
250
|
+
|
|
251
|
+
`http://localhost` is a **secure context** in all modern browsers and provides inherent security guarantees:
|
|
252
|
+
|
|
253
|
+
1. **Reserved Hostname**: `localhost` is a reserved hostname that maps exclusively to `127.0.0.1` (IPv4) or `::1` (IPv6) on the local machine. It cannot be redirected or spoofed by external attackers.
|
|
254
|
+
|
|
255
|
+
2. **No Network Exposure**: Traffic to `localhost` never leaves the device. All communication stays within the local machine, eliminating network-based attack vectors.
|
|
256
|
+
|
|
257
|
+
3. **Secure Context**: `http://localhost` IS a secure context in browsers, meaning all modern Web APIs work perfectly:
|
|
258
|
+
- Service Workers
|
|
259
|
+
- Web Crypto API
|
|
260
|
+
- Geolocation API
|
|
261
|
+
- MediaDevices API
|
|
262
|
+
- And all other secure-context APIs
|
|
263
|
+
|
|
264
|
+
4. **Cannot Be Spoofed**: External attackers cannot redirect `localhost` to their servers. The hostname is hardcoded to point to the local machine, providing the same security guarantees as HTTPS for local-only content.
|
|
265
|
+
|
|
266
|
+
5. **On-Device Execution**: Combined with WebView sandbox isolation, same-origin policy, and Content Security Policy, `localhost` provides a secure execution environment without requiring encryption overhead.
|
|
267
|
+
|
|
268
|
+
#### Why HTTP is the Right Choice for Hybrid Apps
|
|
269
|
+
|
|
270
|
+
1. **Protocol Flexibility**: HTTP allows your app to load resources from both HTTPS (external APIs, CDNs) and HTTP (local server) origins without mixed content warnings. This is crucial for hybrid apps that need to:
|
|
271
|
+
- Load local assets from `http://localhost`
|
|
272
|
+
- Make API calls to `https://api.example.com`
|
|
273
|
+
- Embed external HTTPS resources (images, fonts, etc.)
|
|
274
|
+
- Work seamlessly across different network configurations
|
|
275
|
+
|
|
276
|
+
2. **Secure Context Support**: `http://localhost` is treated as a secure context by browsers, meaning all modern Web APIs (Service Workers, Web Crypto API, Geolocation, etc.) work without requiring HTTPS.
|
|
277
|
+
|
|
278
|
+
3. **Inherent Security**: `localhost` is a reserved hostname that can only point to the local machine (`127.0.0.1`). This means:
|
|
279
|
+
- Traffic never leaves the device (no network exposure)
|
|
280
|
+
- Cannot be spoofed or redirected by external attackers
|
|
281
|
+
- Provides the same security guarantees as HTTPS for local-only content
|
|
282
|
+
|
|
283
|
+
4. **Simplified Development**: HTTP eliminates certificate management complexity for local development and testing. No need to generate, install, or manage SSL certificates for localhost.
|
|
284
|
+
|
|
285
|
+
5. **Cross-Origin Resource Sharing (CORS)**: HTTP on localhost provides more predictable CORS behavior when mixing local and remote resources, reducing integration complexity.
|
|
286
|
+
|
|
287
|
+
6. **Performance**: HTTP has slightly lower overhead than HTTPS, which is beneficial for local file serving where encryption isn't necessary since all content is already on-device and `localhost` provides inherent security.
|
|
288
|
+
|
|
289
|
+
7. **Compatibility**: HTTP works consistently across all platforms and WebView implementations without requiring certificate pinning or trust store configuration.
|
|
290
|
+
|
|
291
|
+
#### When HTTPS Might Be Needed
|
|
292
|
+
|
|
293
|
+
While HTTP with `localhost` provides full security for local-only content, HTTPS might be required in specific scenarios:
|
|
294
|
+
|
|
295
|
+
1. **Enterprise Compliance**: Some enterprise security policies require all network traffic (including local) to be encrypted, making HTTPS a requirement for certain deployment scenarios.
|
|
296
|
+
|
|
297
|
+
2. **Certificate Validation**: HTTPS with proper certificate validation provides additional assurance that the local server hasn't been tampered with or replaced by malicious software. This can be important in high-security environments.
|
|
298
|
+
|
|
299
|
+
3. **Security-in-Depth**: HTTPS enforces encryption even for local traffic, following security-in-depth principles. This protects against potential local network attacks or device-level interception in shared or untrusted environments.
|
|
300
|
+
|
|
301
|
+
4. **Content Security Policy (CSP)**: HTTPS makes it easier to enforce strict CSP headers without mixed content warnings, though this is less relevant for local-only content.
|
|
302
|
+
|
|
303
|
+
5. **Browser Security Indicators**: HTTPS provides visual security indicators (lock icon) that can increase user trust, even for local content.
|
|
304
|
+
|
|
305
|
+
**Note**: For most hybrid app scenarios, HTTP with `localhost` provides the right balance of flexibility and security. The security comes from the reserved hostname and on-device execution, not encryption. However, if your use case requires HTTPS (e.g., enterprise compliance, strict security policies), you can configure your WebView to use HTTPS for the local server.
|
|
306
|
+
|
|
307
|
+
#### Current Implementation Rationale
|
|
308
|
+
|
|
309
|
+
Despia's local server uses HTTP because the primary use case is hybrid apps that need maximum flexibility to mix local and remote resources. The security model relies on:
|
|
310
|
+
|
|
311
|
+
- **Secure Context**: `http://localhost` IS a secure context, enabling all modern Web APIs
|
|
312
|
+
- **Reserved Hostname**: `localhost` is a reserved hostname (`127.0.0.1`) that cannot be spoofed
|
|
313
|
+
- **On-Device Execution**: All traffic stays on-device (no network exposure)
|
|
314
|
+
- **WebView Sandbox Isolation**: WebView enforces security boundaries
|
|
315
|
+
- **Same-Origin Policy**: Browser enforces same-origin policy
|
|
316
|
+
- **Content Security Policy**: CSP can be enforced when configured
|
|
317
|
+
|
|
318
|
+
Security comes from the reserved hostname and on-device execution, not encryption. For most hybrid app scenarios, HTTP provides the right balance of flexibility and security.
|
|
319
|
+
|
|
245
320
|
#### Comparison to Similar Technologies
|
|
246
321
|
|
|
247
322
|
This approach is conceptually and technically identical to:
|
|
@@ -286,7 +361,7 @@ This is analogous to:
|
|
|
286
361
|
|
|
287
362
|
#### Developer Responsibility
|
|
288
363
|
|
|
289
|
-
When submitting apps using Despia
|
|
364
|
+
When submitting apps using Despia's local server:
|
|
290
365
|
|
|
291
366
|
1. **App Store Submission**: Clearly state that the app uses a WebView to display web content that may be cached for offline use. This is standard practice and does not require special disclosure.
|
|
292
367
|
|
|
@@ -306,7 +381,7 @@ When submitting apps using Despia Local Server:
|
|
|
306
381
|
|
|
307
382
|
### The `local.json` Manifest
|
|
308
383
|
|
|
309
|
-
The `despia/local.json` manifest generated by this plugin serves as an asset inventory for Despia
|
|
384
|
+
The `despia/local.json` manifest generated by this plugin serves as an asset inventory for Despia's local server. It contains a complete list of all assets that need to be cached locally, enabling:
|
|
310
385
|
|
|
311
386
|
- **Complete Asset Discovery**: Ensures all files (JS, CSS, images, fonts, HTML) are properly cached
|
|
312
387
|
- **Efficient Updates**: Allows Despia to determine which assets have changed between builds
|
|
@@ -568,17 +643,22 @@ All plugins accept the following options:
|
|
|
568
643
|
|
|
569
644
|
## Output Format
|
|
570
645
|
|
|
571
|
-
The generated `despia/local.json` file contains a sorted
|
|
646
|
+
The generated `despia/local.json` file contains an object with the entry HTML path and a sorted array of asset paths:
|
|
572
647
|
|
|
573
648
|
```json
|
|
574
|
-
|
|
575
|
-
"/
|
|
576
|
-
"
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
649
|
+
{
|
|
650
|
+
"entry": "/index.html",
|
|
651
|
+
"assets": [
|
|
652
|
+
"/assets/app.abc123.css",
|
|
653
|
+
"/assets/app.def456.js",
|
|
654
|
+
"/assets/logo.xyz789.png"
|
|
655
|
+
]
|
|
656
|
+
}
|
|
580
657
|
```
|
|
581
658
|
|
|
659
|
+
- **`entry`**: The entry HTML file path (e.g., `/index.html`). **Required** - Local apps always need an entry point for client-side rendering. When `skipEntryHtml` is enabled, the entry is still required in the manifest but won't be included in the `assets` array.
|
|
660
|
+
- **`assets`**: A sorted array of all asset paths (excluding the entry file).
|
|
661
|
+
|
|
582
662
|
## Examples
|
|
583
663
|
|
|
584
664
|
### React + Vite
|
|
@@ -668,8 +748,8 @@ export default defineConfig({
|
|
|
668
748
|
2. **Scan Output Directory** - Recursively scans the build output directory for all files
|
|
669
749
|
3. **Collect Asset Paths** - Collects paths from both the build tool's bundle metadata and file system
|
|
670
750
|
4. **Normalize Paths** - Converts all paths to root-relative format (starting with `/`)
|
|
671
|
-
5. **
|
|
672
|
-
6. **Sort & Write** - Sorts paths alphabetically and writes to `despia/local.json`
|
|
751
|
+
5. **Separate Entry from Assets** - Identifies the entry HTML file and separates it from other assets
|
|
752
|
+
6. **Sort & Write** - Sorts asset paths alphabetically and writes object format `{ entry, assets }` to `despia/local.json`
|
|
673
753
|
|
|
674
754
|
The generated manifest is then used by Despia during app hydration and updates to ensure all assets are properly cached for offline operation.
|
|
675
755
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@despia/local",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Universal build plugin to generate despia/local.json manifest for offline caching in Despia web-native apps. Supports Vite, Webpack, Rollup, Nuxt, SvelteKit, Astro, Remix, esbuild, Parcel, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/core.js",
|
package/src/astro.js
CHANGED
|
@@ -37,11 +37,12 @@ export default function despiaLocalIntegration(options = {}) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
try {
|
|
40
|
-
const
|
|
40
|
+
const manifest = generateManifest({
|
|
41
41
|
outputDir,
|
|
42
42
|
entryHtml
|
|
43
43
|
});
|
|
44
|
-
|
|
44
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
45
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
45
46
|
} catch (error) {
|
|
46
47
|
console.error('Error generating despia/local.json:', error.message);
|
|
47
48
|
}
|
package/src/core.js
CHANGED
|
@@ -51,9 +51,9 @@ export function collectFiles(dir, baseDir = dir) {
|
|
|
51
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
|
|
54
|
+
* @param {boolean} options.skipEntryHtml - Skip adding entry HTML to assets array (entry is still required in manifest)
|
|
55
55
|
* @param {string} options.manifestOutputPath - Custom path for manifest file (default: outputDir/despia/local.json)
|
|
56
|
-
* @returns {string[]}
|
|
56
|
+
* @returns {{entry: string, assets: string[]}} Object with entry path and assets array
|
|
57
57
|
*/
|
|
58
58
|
export function generateManifest({ outputDir, entryHtml = 'index.html', additionalPaths = [], skipEntryHtml = false, manifestOutputPath = null }) {
|
|
59
59
|
const outputPath = resolve(process.cwd(), outputDir);
|
|
@@ -77,16 +77,26 @@ export function generateManifest({ outputDir, entryHtml = 'index.html', addition
|
|
|
77
77
|
assetPaths.add(normalizedPath);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// Determine entry path (always required for local apps)
|
|
81
|
+
const entryPath = entryHtml.startsWith('/')
|
|
82
|
+
? entryHtml
|
|
83
|
+
: '/' + entryHtml;
|
|
84
|
+
|
|
85
|
+
// Add entry HTML to assets if not skipped (for SSR apps, entry is still required but not in assets)
|
|
81
86
|
if (!skipEntryHtml) {
|
|
82
|
-
const entryPath = entryHtml.startsWith('/')
|
|
83
|
-
? entryHtml
|
|
84
|
-
: '/' + entryHtml;
|
|
85
87
|
assetPaths.add(entryPath);
|
|
86
88
|
}
|
|
87
89
|
|
|
88
|
-
//
|
|
89
|
-
const
|
|
90
|
+
// Include all assets (entry is included in assets array)
|
|
91
|
+
const assets = Array.from(assetPaths)
|
|
92
|
+
.filter(path => !skipEntryHtml || path !== entryPath)
|
|
93
|
+
.sort();
|
|
94
|
+
|
|
95
|
+
// Create manifest object (entry is always required for local client-side apps)
|
|
96
|
+
const manifest = {
|
|
97
|
+
entry: entryPath,
|
|
98
|
+
assets: assets
|
|
99
|
+
};
|
|
90
100
|
|
|
91
101
|
// Create directory for manifest if it doesn't exist
|
|
92
102
|
const manifestDir = manifestOutputPath
|
|
@@ -96,9 +106,9 @@ export function generateManifest({ outputDir, entryHtml = 'index.html', addition
|
|
|
96
106
|
mkdirSync(manifestDir, { recursive: true });
|
|
97
107
|
}
|
|
98
108
|
|
|
99
|
-
// Write formatted JSON
|
|
100
|
-
const jsonContent = JSON.stringify(
|
|
109
|
+
// Write formatted JSON object
|
|
110
|
+
const jsonContent = JSON.stringify(manifest, null, 2);
|
|
101
111
|
writeFileSync(manifestPath, jsonContent, 'utf-8');
|
|
102
112
|
|
|
103
|
-
return
|
|
113
|
+
return manifest;
|
|
104
114
|
}
|
package/src/esbuild.js
CHANGED
|
@@ -48,12 +48,13 @@ export function despiaLocalEsbuild(options = {}) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
try {
|
|
51
|
-
const
|
|
51
|
+
const manifest = generateManifest({
|
|
52
52
|
outputDir: outDirPath,
|
|
53
53
|
entryHtml,
|
|
54
54
|
additionalPaths
|
|
55
55
|
});
|
|
56
|
-
|
|
56
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
57
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
57
58
|
} catch (error) {
|
|
58
59
|
console.error('Error generating despia/local.json:', error.message);
|
|
59
60
|
}
|
package/src/nuxt.js
CHANGED
|
@@ -45,11 +45,12 @@ export default function DespiaLocalModule(moduleOptions) {
|
|
|
45
45
|
|
|
46
46
|
for (const dir of altDirs) {
|
|
47
47
|
try {
|
|
48
|
-
const
|
|
48
|
+
const manifest = generateManifest({
|
|
49
49
|
outputDir: dir,
|
|
50
50
|
entryHtml: options.entryHtml
|
|
51
51
|
});
|
|
52
|
-
|
|
52
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
53
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
53
54
|
generated = true;
|
|
54
55
|
break;
|
|
55
56
|
} catch (e) {
|
package/src/parcel.js
CHANGED
|
@@ -35,12 +35,13 @@ export default function(api) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
try {
|
|
38
|
-
const
|
|
38
|
+
const manifest = generateManifest({
|
|
39
39
|
outputDir: api.options?.outDir || outDir,
|
|
40
40
|
entryHtml,
|
|
41
41
|
additionalPaths
|
|
42
42
|
});
|
|
43
|
-
|
|
43
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
44
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
44
45
|
} catch (error) {
|
|
45
46
|
console.error('Error generating despia/local.json:', error.message);
|
|
46
47
|
console.warn('💡 Tip: For Parcel projects, use the standalone CLI: "despia-local dist"');
|
package/src/remix.js
CHANGED
|
@@ -27,11 +27,12 @@ export function despiaLocalRemix(options = {}) {
|
|
|
27
27
|
|
|
28
28
|
for (const outputDir of outputDirs) {
|
|
29
29
|
try {
|
|
30
|
-
const
|
|
30
|
+
const manifest = generateManifest({
|
|
31
31
|
outputDir,
|
|
32
32
|
entryHtml
|
|
33
33
|
});
|
|
34
|
-
|
|
34
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
35
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
35
36
|
return; // Success, exit
|
|
36
37
|
} catch (error) {
|
|
37
38
|
// Try next directory
|
package/src/rollup.js
CHANGED
|
@@ -31,12 +31,13 @@ export function despiaLocal(options = {}) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
try {
|
|
34
|
-
const
|
|
34
|
+
const manifest = generateManifest({
|
|
35
35
|
outputDir,
|
|
36
36
|
entryHtml,
|
|
37
37
|
additionalPaths
|
|
38
38
|
});
|
|
39
|
-
|
|
39
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
40
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
40
41
|
} catch (error) {
|
|
41
42
|
console.error('Error generating despia/local.json:', error.message);
|
|
42
43
|
}
|
package/src/sveltekit.js
CHANGED
|
@@ -27,11 +27,12 @@ export function despiaLocalSvelteKit(options = {}) {
|
|
|
27
27
|
|
|
28
28
|
for (const outputDir of outputDirs) {
|
|
29
29
|
try {
|
|
30
|
-
const
|
|
30
|
+
const manifest = generateManifest({
|
|
31
31
|
outputDir,
|
|
32
32
|
entryHtml
|
|
33
33
|
});
|
|
34
|
-
|
|
34
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
35
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
35
36
|
return; // Success, exit
|
|
36
37
|
} catch (error) {
|
|
37
38
|
// Try next directory
|
package/src/vite.js
CHANGED
|
@@ -33,12 +33,13 @@ export function despiaLocalPlugin(options = {}) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
try {
|
|
36
|
-
const
|
|
36
|
+
const manifest = generateManifest({
|
|
37
37
|
outputDir,
|
|
38
38
|
entryHtml,
|
|
39
39
|
additionalPaths
|
|
40
40
|
});
|
|
41
|
-
|
|
41
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
42
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
42
43
|
} catch (error) {
|
|
43
44
|
console.error('Error generating despia/local.json:', error.message);
|
|
44
45
|
}
|
package/src/webpack.js
CHANGED
|
@@ -93,8 +93,26 @@ class DespiaLocalPlugin {
|
|
|
93
93
|
this.scanPublicDir(this.options.publicDir, this.options.publicDir, assets);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
// 3. Generate
|
|
97
|
-
|
|
96
|
+
// 3. Generate object format (entry is included in assets array)
|
|
97
|
+
// Entry is always required for local client-side apps
|
|
98
|
+
const entryPath = this.options.entryHtml.startsWith('/')
|
|
99
|
+
? this.options.entryHtml
|
|
100
|
+
: '/' + this.options.entryHtml;
|
|
101
|
+
|
|
102
|
+
// Ensure entry is in assets if not skipped
|
|
103
|
+
if (!this.options.skipEntryHtml) {
|
|
104
|
+
assets.add(entryPath);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const assetList = Array.from(assets)
|
|
108
|
+
.filter(path => !this.options.skipEntryHtml || path !== entryPath)
|
|
109
|
+
.sort();
|
|
110
|
+
|
|
111
|
+
const manifestObj = {
|
|
112
|
+
entry: entryPath,
|
|
113
|
+
assets: assetList
|
|
114
|
+
};
|
|
115
|
+
const manifest = JSON.stringify(manifestObj, null, 2);
|
|
98
116
|
|
|
99
117
|
// 4. Inject into webpack output at despia/local.json
|
|
100
118
|
const manifestPath = this.options.manifestPath || 'despia/local.json';
|
|
@@ -103,7 +121,7 @@ class DespiaLocalPlugin {
|
|
|
103
121
|
size: () => Buffer.byteLength(manifest, 'utf8')
|
|
104
122
|
};
|
|
105
123
|
|
|
106
|
-
console.log(`✓ Injected despia/local.json into build with ${
|
|
124
|
+
console.log(`✓ Injected despia/local.json into build with ${assetList.length} assets and entry: ${entryPath}`);
|
|
107
125
|
} else {
|
|
108
126
|
// Traditional mode: Write to filesystem (for other bundlers)
|
|
109
127
|
const additionalPaths = new Set();
|
|
@@ -134,13 +152,14 @@ class DespiaLocalPlugin {
|
|
|
134
152
|
|
|
135
153
|
try {
|
|
136
154
|
const outputPath = compilation.compiler.outputPath || this.options.outDir;
|
|
137
|
-
const
|
|
155
|
+
const manifest = generateManifest({
|
|
138
156
|
outputDir: outputPath,
|
|
139
157
|
entryHtml: this.options.entryHtml,
|
|
140
158
|
additionalPaths: Array.from(additionalPaths),
|
|
141
159
|
skipEntryHtml: this.options.skipEntryHtml
|
|
142
160
|
});
|
|
143
|
-
|
|
161
|
+
const entryInfo = manifest.entry ? ` and entry: ${manifest.entry}` : '';
|
|
162
|
+
console.log(`✓ Generated despia/local.json with ${manifest.assets.length} assets${entryInfo}`);
|
|
144
163
|
} catch (error) {
|
|
145
164
|
console.error('Error generating despia/local.json:', error.message);
|
|
146
165
|
}
|