@despia/local 1.0.0
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/LICENSE +21 -0
- package/README.md +748 -0
- package/generate-offline-manifest.js +34 -0
- package/package.json +95 -0
- package/src/astro.js +51 -0
- package/src/core.js +94 -0
- package/src/esbuild.js +63 -0
- package/src/index.js +16 -0
- package/src/next.js +59 -0
- package/src/nuxt.js +64 -0
- package/src/parcel.js +50 -0
- package/src/remix.js +45 -0
- package/src/rollup.js +45 -0
- package/src/sveltekit.js +45 -0
- package/src/turbopack.js +20 -0
- package/src/vite.js +47 -0
- package/src/webpack.js +56 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Standalone script to generate despia/local.json manifest
|
|
5
|
+
* Can be used with any build system by running after build completes
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node generate-offline-manifest.js [outputDir] [entryHtml]
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* node generate-offline-manifest.js
|
|
12
|
+
* node generate-offline-manifest.js dist
|
|
13
|
+
* node generate-offline-manifest.js dist index.html
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { generateManifest } from './src/core.js';
|
|
17
|
+
import { resolve } from 'path';
|
|
18
|
+
|
|
19
|
+
// Get command line arguments
|
|
20
|
+
const outputDir = process.argv[2] || 'dist';
|
|
21
|
+
const entryHtml = process.argv[3] || 'index.html';
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
console.log(`Scanning ${resolve(process.cwd(), outputDir)} for assets...`);
|
|
25
|
+
const paths = generateManifest({ outputDir, entryHtml });
|
|
26
|
+
|
|
27
|
+
console.log(`✓ Generated despia/local.json`);
|
|
28
|
+
console.log(`✓ Included ${paths.length} assets`);
|
|
29
|
+
console.log(`✓ Entry HTML: /${entryHtml}`);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`Error: ${error.message}`);
|
|
32
|
+
console.error('Please run this script after your build completes.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@despia/local",
|
|
3
|
+
"version": "1.0.0",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"main": "./src/core.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"despia-local": "generate-offline-manifest.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./src/core.js"
|
|
13
|
+
},
|
|
14
|
+
"./vite": {
|
|
15
|
+
"import": "./src/vite.js"
|
|
16
|
+
},
|
|
17
|
+
"./webpack": {
|
|
18
|
+
"import": "./src/webpack.js"
|
|
19
|
+
},
|
|
20
|
+
"./rollup": {
|
|
21
|
+
"import": "./src/rollup.js"
|
|
22
|
+
},
|
|
23
|
+
"./next": {
|
|
24
|
+
"import": "./src/next.js"
|
|
25
|
+
},
|
|
26
|
+
"./nuxt": {
|
|
27
|
+
"import": "./src/nuxt.js"
|
|
28
|
+
},
|
|
29
|
+
"./sveltekit": {
|
|
30
|
+
"import": "./src/sveltekit.js"
|
|
31
|
+
},
|
|
32
|
+
"./astro": {
|
|
33
|
+
"import": "./src/astro.js"
|
|
34
|
+
},
|
|
35
|
+
"./remix": {
|
|
36
|
+
"import": "./src/remix.js"
|
|
37
|
+
},
|
|
38
|
+
"./esbuild": {
|
|
39
|
+
"import": "./src/esbuild.js"
|
|
40
|
+
},
|
|
41
|
+
"./parcel": {
|
|
42
|
+
"import": "./src/parcel.js"
|
|
43
|
+
},
|
|
44
|
+
"./turbopack": {
|
|
45
|
+
"import": "./src/turbopack.js"
|
|
46
|
+
},
|
|
47
|
+
"./cli": {
|
|
48
|
+
"import": "./generate-offline-manifest.js"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"files": [
|
|
52
|
+
"src",
|
|
53
|
+
"generate-offline-manifest.js",
|
|
54
|
+
"README.md",
|
|
55
|
+
"LICENSE"
|
|
56
|
+
],
|
|
57
|
+
"scripts": {
|
|
58
|
+
"generate-offline": "node generate-offline-manifest.js",
|
|
59
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
60
|
+
},
|
|
61
|
+
"keywords": [
|
|
62
|
+
"despia",
|
|
63
|
+
"local",
|
|
64
|
+
"manifest",
|
|
65
|
+
"build",
|
|
66
|
+
"plugin",
|
|
67
|
+
"vite",
|
|
68
|
+
"webpack",
|
|
69
|
+
"rollup",
|
|
70
|
+
"nextjs",
|
|
71
|
+
"nuxt",
|
|
72
|
+
"sveltekit",
|
|
73
|
+
"astro",
|
|
74
|
+
"remix",
|
|
75
|
+
"esbuild",
|
|
76
|
+
"parcel",
|
|
77
|
+
"turbopack",
|
|
78
|
+
"bundler",
|
|
79
|
+
"pwa",
|
|
80
|
+
"caching"
|
|
81
|
+
],
|
|
82
|
+
"author": "Despia",
|
|
83
|
+
"license": "MIT",
|
|
84
|
+
"repository": {
|
|
85
|
+
"type": "git",
|
|
86
|
+
"url": "git+https://github.com/despia-native/local.git"
|
|
87
|
+
},
|
|
88
|
+
"bugs": {
|
|
89
|
+
"url": "https://github.com/despia-native/local/issues"
|
|
90
|
+
},
|
|
91
|
+
"homepage": "https://github.com/despia-native/local#readme",
|
|
92
|
+
"engines": {
|
|
93
|
+
"node": ">=14.0.0"
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/astro.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astro integration for generating despia/local.json manifest
|
|
3
|
+
*
|
|
4
|
+
* Usage in astro.config.mjs:
|
|
5
|
+
* import { defineConfig } from 'astro/config';
|
|
6
|
+
* import despiaOffline from '@despia/local/astro';
|
|
7
|
+
*
|
|
8
|
+
* export default defineConfig({
|
|
9
|
+
* integrations: [
|
|
10
|
+
* despiaOffline({ entryHtml: 'index.html' })
|
|
11
|
+
* ]
|
|
12
|
+
* });
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { generateManifest } from './core.js';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
|
|
18
|
+
export default function despiaOfflineIntegration(options = {}) {
|
|
19
|
+
const { entryHtml = 'index.html', outDir = 'dist' } = options;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
name: 'despia-offline',
|
|
23
|
+
hooks: {
|
|
24
|
+
'astro:build:done': async ({ dir }) => {
|
|
25
|
+
// Astro provides dir as a URL object, convert to path
|
|
26
|
+
let outputDir = outDir;
|
|
27
|
+
if (dir) {
|
|
28
|
+
// Handle both URL and string paths
|
|
29
|
+
if (typeof dir === 'string') {
|
|
30
|
+
outputDir = dir;
|
|
31
|
+
} else if (dir.pathname) {
|
|
32
|
+
outputDir = dir.pathname;
|
|
33
|
+
} else if (dir.href) {
|
|
34
|
+
// Convert file:// URL to path
|
|
35
|
+
outputDir = fileURLToPath(dir);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const paths = generateManifest({
|
|
41
|
+
outputDir,
|
|
42
|
+
entryHtml
|
|
43
|
+
});
|
|
44
|
+
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Error generating despia/local.json:', error.message);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
package/src/core.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core utility functions for generating despia/local.json manifest
|
|
3
|
+
* Shared across all build tool plugins
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readdirSync, statSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
7
|
+
import { join, relative, resolve } from 'path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Recursively collect all files in a directory
|
|
11
|
+
* @param {string} dir - Directory to scan
|
|
12
|
+
* @param {string} baseDir - Base directory for relative paths
|
|
13
|
+
* @returns {string[]} Array of root-relative paths
|
|
14
|
+
*/
|
|
15
|
+
export function collectFiles(dir, baseDir = dir) {
|
|
16
|
+
const files = [];
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const entries = readdirSync(dir);
|
|
20
|
+
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
// Skip the despia directory itself to avoid recursion
|
|
23
|
+
if (entry === 'despia') {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const fullPath = join(dir, entry);
|
|
28
|
+
const stat = statSync(fullPath);
|
|
29
|
+
|
|
30
|
+
if (stat.isDirectory()) {
|
|
31
|
+
// Recursively scan subdirectories
|
|
32
|
+
files.push(...collectFiles(fullPath, baseDir));
|
|
33
|
+
} else if (stat.isFile()) {
|
|
34
|
+
// Get relative path from base directory
|
|
35
|
+
const relativePath = relative(baseDir, fullPath);
|
|
36
|
+
// Convert to root-relative path (starting with /)
|
|
37
|
+
const rootRelativePath = '/' + relativePath.replace(/\\/g, '/');
|
|
38
|
+
files.push(rootRelativePath);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
// Silently skip directories that can't be read
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return files;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate the offline manifest file
|
|
50
|
+
* @param {Object} options
|
|
51
|
+
* @param {string} options.outputDir - Output directory path
|
|
52
|
+
* @param {string} options.entryHtml - Entry HTML filename (default: 'index.html')
|
|
53
|
+
* @param {string[]} options.additionalPaths - Additional paths to include
|
|
54
|
+
* @returns {string[]} Array of all asset paths
|
|
55
|
+
*/
|
|
56
|
+
export function generateManifest({ outputDir, entryHtml = 'index.html', additionalPaths = [] }) {
|
|
57
|
+
const outputPath = resolve(process.cwd(), outputDir);
|
|
58
|
+
const manifestPath = join(outputPath, 'despia', 'local.json');
|
|
59
|
+
|
|
60
|
+
// Check if output directory exists
|
|
61
|
+
if (!existsSync(outputPath)) {
|
|
62
|
+
throw new Error(`Output directory "${outputPath}" does not exist.`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Collect all files from the output directory
|
|
66
|
+
const assetPaths = new Set(collectFiles(outputPath, outputPath));
|
|
67
|
+
|
|
68
|
+
// Add any additional paths provided
|
|
69
|
+
additionalPaths.forEach(path => {
|
|
70
|
+
const normalizedPath = path.startsWith('/') ? path : '/' + path.replace(/\\/g, '/');
|
|
71
|
+
assetPaths.add(normalizedPath);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Ensure entry HTML is included
|
|
75
|
+
const entryPath = entryHtml.startsWith('/')
|
|
76
|
+
? entryHtml
|
|
77
|
+
: '/' + entryHtml;
|
|
78
|
+
assetPaths.add(entryPath);
|
|
79
|
+
|
|
80
|
+
// Convert to sorted array
|
|
81
|
+
const sortedPaths = Array.from(assetPaths).sort();
|
|
82
|
+
|
|
83
|
+
// Create despia directory if it doesn't exist
|
|
84
|
+
const despiaDir = join(outputPath, 'despia');
|
|
85
|
+
if (!existsSync(despiaDir)) {
|
|
86
|
+
mkdirSync(despiaDir, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Write formatted JSON array
|
|
90
|
+
const jsonContent = JSON.stringify(sortedPaths, null, 2);
|
|
91
|
+
writeFileSync(manifestPath, jsonContent, 'utf-8');
|
|
92
|
+
|
|
93
|
+
return sortedPaths;
|
|
94
|
+
}
|
package/src/esbuild.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* esbuild plugin for generating despia/local.json manifest
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { build } from 'esbuild';
|
|
6
|
+
* import { despiaOfflineEsbuild } from '@despia/local/esbuild';
|
|
7
|
+
*
|
|
8
|
+
* await build({
|
|
9
|
+
* plugins: [despiaOfflineEsbuild({ outDir: 'dist' })]
|
|
10
|
+
* });
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { generateManifest } from './core.js';
|
|
14
|
+
import { relative } from 'path';
|
|
15
|
+
|
|
16
|
+
export function despiaOfflineEsbuild(options = {}) {
|
|
17
|
+
const { outDir = 'dist', entryHtml = 'index.html' } = options;
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
name: 'despia-offline',
|
|
21
|
+
setup(build) {
|
|
22
|
+
build.onEnd(async (result) => {
|
|
23
|
+
if (result.errors.length > 0) {
|
|
24
|
+
return; // Don't generate manifest if build failed
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const additionalPaths = [];
|
|
28
|
+
const actualOutDir = build.initialOptions.outdir || outDir;
|
|
29
|
+
const outDirPath = typeof actualOutDir === 'string' ? actualOutDir : outDir;
|
|
30
|
+
|
|
31
|
+
// Collect output files from esbuild result
|
|
32
|
+
if (result.outputFiles) {
|
|
33
|
+
for (const file of result.outputFiles) {
|
|
34
|
+
const filePath = file.path;
|
|
35
|
+
// Get relative path from output directory
|
|
36
|
+
try {
|
|
37
|
+
const relativePath = relative(outDirPath, filePath);
|
|
38
|
+
const rootRelativePath = '/' + relativePath.replace(/\\/g, '/');
|
|
39
|
+
additionalPaths.push(rootRelativePath);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
// If relative path calculation fails, use filename
|
|
42
|
+
const filename = filePath.split(/[/\\]/).pop();
|
|
43
|
+
if (filename) {
|
|
44
|
+
additionalPaths.push('/' + filename);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const paths = generateManifest({
|
|
52
|
+
outputDir: outDirPath,
|
|
53
|
+
entryHtml,
|
|
54
|
+
additionalPaths
|
|
55
|
+
});
|
|
56
|
+
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error('Error generating despia/local.json:', error.message);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for @despia/local
|
|
3
|
+
* Re-exports core functionality and all plugins
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { generateManifest, collectFiles } from './core.js';
|
|
7
|
+
export { despiaOfflinePlugin } from './vite.js';
|
|
8
|
+
export { default as DespiaOfflinePlugin } from './webpack.js';
|
|
9
|
+
export { despiaOffline } from './rollup.js';
|
|
10
|
+
export { withDespiaOffline } from './next.js';
|
|
11
|
+
export { default as DespiaOfflineModule } from './nuxt.js';
|
|
12
|
+
export { despiaOfflineSvelteKit } from './sveltekit.js';
|
|
13
|
+
export { default as despiaOfflineIntegration } from './astro.js';
|
|
14
|
+
export { despiaOfflineRemix } from './remix.js';
|
|
15
|
+
export { despiaOfflineEsbuild } from './esbuild.js';
|
|
16
|
+
export { default as DespiaOfflineParcel } from './parcel.js';
|
package/src/next.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js integration for generating despia/local.json manifest
|
|
3
|
+
*
|
|
4
|
+
* Usage in next.config.js:
|
|
5
|
+
* const withDespiaOffline = require('@despia/local/next');
|
|
6
|
+
* module.exports = withDespiaOffline({
|
|
7
|
+
* entryHtml: 'index.html',
|
|
8
|
+
* outDir: '.next' // or 'out' for static export
|
|
9
|
+
* })({
|
|
10
|
+
* // your next config
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* Or use the webpack plugin approach:
|
|
14
|
+
* const DespiaOfflinePlugin = require('@despia/local/webpack');
|
|
15
|
+
* module.exports = {
|
|
16
|
+
* webpack: (config) => {
|
|
17
|
+
* config.plugins.push(new DespiaOfflinePlugin({ outDir: '.next' }));
|
|
18
|
+
* return config;
|
|
19
|
+
* }
|
|
20
|
+
* };
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { generateManifest } from './core.js';
|
|
24
|
+
import DespiaOfflinePlugin from './webpack.js';
|
|
25
|
+
|
|
26
|
+
export function withDespiaOffline(pluginOptions = {}) {
|
|
27
|
+
const offlineConfig = {
|
|
28
|
+
outDir: pluginOptions.outDir || '.next',
|
|
29
|
+
entryHtml: pluginOptions.entryHtml || 'index.html',
|
|
30
|
+
...pluginOptions
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (nextConfig = {}) => {
|
|
34
|
+
const existingWebpack = nextConfig.webpack;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
...nextConfig,
|
|
38
|
+
webpack: (config, options) => {
|
|
39
|
+
// Add Despia Offline plugin
|
|
40
|
+
config.plugins.push(
|
|
41
|
+
new DespiaOfflinePlugin({
|
|
42
|
+
outDir: offlineConfig.outDir,
|
|
43
|
+
entryHtml: offlineConfig.entryHtml
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Call existing webpack config if present
|
|
48
|
+
if (typeof existingWebpack === 'function') {
|
|
49
|
+
return existingWebpack(config, options);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return config;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Also export as CommonJS for Next.js compatibility
|
|
59
|
+
export default withDespiaOffline;
|
package/src/nuxt.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nuxt module for generating despia/local.json manifest
|
|
3
|
+
*
|
|
4
|
+
* Usage in nuxt.config.js:
|
|
5
|
+
* export default {
|
|
6
|
+
* modules: ['@despia/local/nuxt'],
|
|
7
|
+
* despiaOffline: {
|
|
8
|
+
* entryHtml: 'index.html'
|
|
9
|
+
* }
|
|
10
|
+
* }
|
|
11
|
+
*
|
|
12
|
+
* Or use as a Nuxt module in modules/ directory:
|
|
13
|
+
* // modules/despia-offline.js
|
|
14
|
+
* import DespiaOfflineModule from '@despia/local/nuxt';
|
|
15
|
+
* export default DespiaOfflineModule;
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { generateManifest } from './core.js';
|
|
19
|
+
|
|
20
|
+
export default function DespiaOfflineModule(moduleOptions) {
|
|
21
|
+
const options = {
|
|
22
|
+
entryHtml: 'index.html',
|
|
23
|
+
...moduleOptions,
|
|
24
|
+
...(this.options?.despiaOffline || {})
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Hook into Nuxt build completion
|
|
28
|
+
this.nuxt.hook('build:done', async (builder) => {
|
|
29
|
+
// Determine output directory based on Nuxt mode
|
|
30
|
+
let outputDir;
|
|
31
|
+
|
|
32
|
+
if (this.options.generate) {
|
|
33
|
+
// Static site generation
|
|
34
|
+
outputDir = '.output/public';
|
|
35
|
+
} else if (this.options.target === 'static') {
|
|
36
|
+
outputDir = 'dist';
|
|
37
|
+
} else {
|
|
38
|
+
// SSR mode - assets are in .nuxt/dist/client
|
|
39
|
+
outputDir = '.nuxt/dist/client';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Try to generate manifest
|
|
43
|
+
const altDirs = [outputDir, '.output/public', 'dist', '.nuxt/dist'];
|
|
44
|
+
let generated = false;
|
|
45
|
+
|
|
46
|
+
for (const dir of altDirs) {
|
|
47
|
+
try {
|
|
48
|
+
const paths = generateManifest({
|
|
49
|
+
outputDir: dir,
|
|
50
|
+
entryHtml: options.entryHtml
|
|
51
|
+
});
|
|
52
|
+
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
53
|
+
generated = true;
|
|
54
|
+
break;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
// Continue to next directory
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!generated) {
|
|
61
|
+
console.warn('⚠ Could not find Nuxt build output directory');
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
package/src/parcel.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parcel plugin for generating despia/local.json manifest
|
|
3
|
+
*
|
|
4
|
+
* Note: Parcel 2.x uses a different plugin system. For Parcel projects,
|
|
5
|
+
* we recommend using the standalone CLI script instead:
|
|
6
|
+
*
|
|
7
|
+
* "scripts": {
|
|
8
|
+
* "build": "parcel build",
|
|
9
|
+
* "postbuild": "despia-offline dist"
|
|
10
|
+
* }
|
|
11
|
+
*
|
|
12
|
+
* For Parcel 1.x, you can use this plugin, but the API may vary.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { generateManifest } from './core.js';
|
|
16
|
+
|
|
17
|
+
// Parcel 2.x plugin format
|
|
18
|
+
export default function(api) {
|
|
19
|
+
const { outDir = 'dist', entryHtml = 'index.html' } = api.options || {};
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
name: 'despia-offline',
|
|
23
|
+
async bundleEnd({ bundleGraph }) {
|
|
24
|
+
const additionalPaths = [];
|
|
25
|
+
|
|
26
|
+
// Collect all bundles
|
|
27
|
+
if (bundleGraph && bundleGraph.getBundles) {
|
|
28
|
+
bundleGraph.getBundles().forEach(bundle => {
|
|
29
|
+
const filePath = bundle.filePath || bundle.name;
|
|
30
|
+
if (filePath) {
|
|
31
|
+
const rootRelativePath = '/' + filePath.replace(/\\/g, '/');
|
|
32
|
+
additionalPaths.push(rootRelativePath);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const paths = generateManifest({
|
|
39
|
+
outputDir: api.options?.outDir || outDir,
|
|
40
|
+
entryHtml,
|
|
41
|
+
additionalPaths
|
|
42
|
+
});
|
|
43
|
+
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Error generating despia/local.json:', error.message);
|
|
46
|
+
console.warn('💡 Tip: For Parcel projects, use the standalone CLI: "despia-offline dist"');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
package/src/remix.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remix integration for generating despia/local.json manifest
|
|
3
|
+
*
|
|
4
|
+
* Usage in remix.config.js or vite.config.js:
|
|
5
|
+
* import { remix } from '@remix-run/dev';
|
|
6
|
+
* import { despiaOfflineRemix } from '@despia/local/remix';
|
|
7
|
+
*
|
|
8
|
+
* export default {
|
|
9
|
+
* plugins: [
|
|
10
|
+
* remix(),
|
|
11
|
+
* despiaOfflineRemix({ entryHtml: 'index.html' })
|
|
12
|
+
* ]
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { generateManifest } from './core.js';
|
|
17
|
+
|
|
18
|
+
export function despiaOfflineRemix(options = {}) {
|
|
19
|
+
const { entryHtml = 'index.html', outDir = 'build/client' } = options;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
name: 'despia-offline-remix',
|
|
23
|
+
apply: 'build',
|
|
24
|
+
buildEnd() {
|
|
25
|
+
// Remix outputs to build/client for client assets
|
|
26
|
+
const outputDirs = ['build/client', 'build', 'public/build'];
|
|
27
|
+
|
|
28
|
+
for (const outputDir of outputDirs) {
|
|
29
|
+
try {
|
|
30
|
+
const paths = generateManifest({
|
|
31
|
+
outputDir,
|
|
32
|
+
entryHtml
|
|
33
|
+
});
|
|
34
|
+
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
35
|
+
return; // Success, exit
|
|
36
|
+
} catch (error) {
|
|
37
|
+
// Try next directory
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.warn('⚠ Could not find Remix build output directory');
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
package/src/rollup.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rollup plugin for generating despia/local.json manifest
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { generateManifest } from './core.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Rollup plugin to generate despia/local.json manifest
|
|
9
|
+
* @param {Object} options
|
|
10
|
+
* @param {string} options.outDir - Output directory (default: 'dist')
|
|
11
|
+
* @param {string} options.entryHtml - Entry HTML file (default: 'index.html')
|
|
12
|
+
*/
|
|
13
|
+
export function despiaOffline(options = {}) {
|
|
14
|
+
const { outDir = 'dist', entryHtml = 'index.html' } = options;
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
name: 'despia-offline',
|
|
18
|
+
writeBundle(outputOptions, bundle) {
|
|
19
|
+
const outputDir = outputOptions.dir || outDir;
|
|
20
|
+
const additionalPaths = [];
|
|
21
|
+
|
|
22
|
+
// Collect paths from bundle
|
|
23
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
24
|
+
if (chunk.type === 'asset' || chunk.type === 'chunk') {
|
|
25
|
+
const filePath = chunk.fileName || fileName;
|
|
26
|
+
const rootRelativePath = filePath.startsWith('/')
|
|
27
|
+
? filePath
|
|
28
|
+
: '/' + filePath.replace(/\\/g, '/');
|
|
29
|
+
additionalPaths.push(rootRelativePath);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const paths = generateManifest({
|
|
35
|
+
outputDir,
|
|
36
|
+
entryHtml,
|
|
37
|
+
additionalPaths
|
|
38
|
+
});
|
|
39
|
+
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Error generating despia/local.json:', error.message);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
package/src/sveltekit.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SvelteKit adapter for generating despia/local.json manifest
|
|
3
|
+
*
|
|
4
|
+
* Usage in vite.config.js:
|
|
5
|
+
* import { sveltekit } from '@sveltejs/kit/vite';
|
|
6
|
+
* import { despiaOfflineSvelteKit } from '@despia/local/sveltekit';
|
|
7
|
+
*
|
|
8
|
+
* export default {
|
|
9
|
+
* plugins: [
|
|
10
|
+
* sveltekit(),
|
|
11
|
+
* despiaOfflineSvelteKit({ entryHtml: 'index.html' })
|
|
12
|
+
* ]
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { generateManifest } from './core.js';
|
|
17
|
+
|
|
18
|
+
export function despiaOfflineSvelteKit(options = {}) {
|
|
19
|
+
const { entryHtml = 'index.html' } = options;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
name: 'despia-offline-sveltekit',
|
|
23
|
+
apply: 'build',
|
|
24
|
+
buildEnd() {
|
|
25
|
+
// SvelteKit outputs to build directory
|
|
26
|
+
const outputDirs = ['build', '.svelte-kit', 'dist'];
|
|
27
|
+
|
|
28
|
+
for (const outputDir of outputDirs) {
|
|
29
|
+
try {
|
|
30
|
+
const paths = generateManifest({
|
|
31
|
+
outputDir,
|
|
32
|
+
entryHtml
|
|
33
|
+
});
|
|
34
|
+
console.log(`✓ Generated despia/local.json with ${paths.length} assets`);
|
|
35
|
+
return; // Success, exit
|
|
36
|
+
} catch (error) {
|
|
37
|
+
// Try next directory
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.warn('⚠ Could not find SvelteKit build output directory');
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
package/src/turbopack.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turbopack plugin for generating despia/local.json manifest
|
|
3
|
+
* Note: Turbopack is Next.js's new bundler. Use the Next.js integration instead.
|
|
4
|
+
* This is a placeholder for future Turbopack-specific optimizations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { generateManifest } from './core.js';
|
|
8
|
+
|
|
9
|
+
export function despiaOfflineTurbopack(options = {}) {
|
|
10
|
+
const { entryHtml = 'index.html', outDir = '.next' } = options;
|
|
11
|
+
|
|
12
|
+
// Turbopack is used by Next.js, so we recommend using the Next.js integration
|
|
13
|
+
// This is kept for potential future Turbopack-specific features
|
|
14
|
+
console.warn('⚠ Turbopack: Use @despia/local/next instead for Next.js projects');
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
name: 'despia-offline-turbopack',
|
|
18
|
+
// Implementation would go here when Turbopack plugin API is stable
|
|
19
|
+
};
|
|
20
|
+
}
|