vite-plugin-cross-origin-storage 1.2.0 → 1.3.1
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 +37 -0
- package/dist/index.js +1 -1
- package/dist/loader.js +94 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -43,6 +43,43 @@ export default defineConfig({
|
|
|
43
43
|
| `include` | `string \| RegExp \| Array` | `['**/*']` | Pattern to include chunks to be managed by COS. |
|
|
44
44
|
| `exclude` | `string \| RegExp \| Array` | `undefined` | Pattern to exclude chunks from being managed. |
|
|
45
45
|
|
|
46
|
+
## Recipe: Granular Vendor Splitting
|
|
47
|
+
|
|
48
|
+
To maximize caching benefits, it is recommended to split your `node_modules` dependencies into separate chunks. This ensures that updates to one package (e.g., `react`) do not invalidate the cache for others (e.g., `lodash`).
|
|
49
|
+
|
|
50
|
+
Add the following `manualChunks` configuration to your `vite.config.ts`:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// vite.config.ts
|
|
54
|
+
import { defineConfig } from 'vite';
|
|
55
|
+
import cosPlugin from 'vite-plugin-cross-origin-storage';
|
|
56
|
+
|
|
57
|
+
export default defineConfig({
|
|
58
|
+
build: {
|
|
59
|
+
rollupOptions: {
|
|
60
|
+
output: {
|
|
61
|
+
manualChunks(id) {
|
|
62
|
+
if (id.includes('node_modules')) {
|
|
63
|
+
// Split each package into its own chunk
|
|
64
|
+
// e.g. "node_modules/react/..." -> "vendor-react"
|
|
65
|
+
// e.g. "node_modules/@scope/pkg/..." -> "vendor-scope-pkg"
|
|
66
|
+
const parts = id.split('node_modules/')[1].split('/');
|
|
67
|
+
const packageName = parts[0].startsWith('@') ? `${parts[0]}/${parts[1]}` : parts[0];
|
|
68
|
+
return `vendor-${packageName.replace('@', '').replace('/', '-')}`;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
plugins: [
|
|
75
|
+
cosPlugin({
|
|
76
|
+
// Only manage these vendor chunks with COS
|
|
77
|
+
include: ['**/vendor-*'],
|
|
78
|
+
}),
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
46
83
|
## How It Works
|
|
47
84
|
|
|
48
85
|
1. **Build Time**:
|
package/dist/index.js
CHANGED
|
@@ -1621,7 +1621,7 @@ var hasStringIsWellFormed = "isWellFormed" in String.prototype;
|
|
|
1621
1621
|
function cosPlugin(options = {}) {
|
|
1622
1622
|
const filter = createFilter(options.include || ["**/*"], options.exclude);
|
|
1623
1623
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
1624
|
-
const loaderPath = path.resolve(__dirname, "loader.js");
|
|
1624
|
+
const loaderPath = path.resolve(__dirname, "./loader.js");
|
|
1625
1625
|
return {
|
|
1626
1626
|
name: "vite-plugin-cos",
|
|
1627
1627
|
apply: "build",
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
(async function () {
|
|
2
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
3
|
+
|
|
4
|
+
const isCOSAvailable = 'crossOriginStorage' in navigator;
|
|
5
|
+
console.log('COS Loader: isCOSAvailable =', isCOSAvailable);
|
|
6
|
+
|
|
7
|
+
// Manifest is injected by the Vite plugin
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
const manifest = __COS_MANIFEST__;
|
|
10
|
+
|
|
11
|
+
const mainEntry = manifest && manifest['index'];
|
|
12
|
+
if (!mainEntry) {
|
|
13
|
+
console.warn('COS Loader: Missing main entry in manifest.');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Identify managed chunks (anything with a hash)
|
|
18
|
+
const chunksToLoad = Object.values(manifest).filter(item => item.hash);
|
|
19
|
+
|
|
20
|
+
async function getBlobFromCOS(hash) {
|
|
21
|
+
if (!isCOSAvailable) return null;
|
|
22
|
+
try {
|
|
23
|
+
const handles = await navigator.crossOriginStorage.requestFileHandles([
|
|
24
|
+
{ algorithm: 'SHA-256', value: hash },
|
|
25
|
+
]);
|
|
26
|
+
if (handles && handles.length > 0) {
|
|
27
|
+
return await handles[0].getFile();
|
|
28
|
+
}
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.name !== 'NotFoundError') console.error('COS Loader: Error checking COS', err);
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function storeBlobInCOS(blob, hash) {
|
|
36
|
+
if (!isCOSAvailable) return;
|
|
37
|
+
try {
|
|
38
|
+
const handles = await navigator.crossOriginStorage.requestFileHandles(
|
|
39
|
+
[{ algorithm: 'SHA-256', value: hash }],
|
|
40
|
+
{ create: true }
|
|
41
|
+
);
|
|
42
|
+
if (handles && handles.length > 0) {
|
|
43
|
+
const writable = await handles[0].createWritable();
|
|
44
|
+
await writable.write(blob);
|
|
45
|
+
await writable.close();
|
|
46
|
+
console.log('COS Loader: Stored bundle in COS', hash);
|
|
47
|
+
}
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error('COS Loader: Failed to store in COS', err);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Load all managed chunks in parallel
|
|
54
|
+
// If COS is not available, we skip this and fall back to native network loading via the import() rewrites.
|
|
55
|
+
if (isCOSAvailable && chunksToLoad.length > 0) {
|
|
56
|
+
await Promise.all(chunksToLoad.map(async (chunk) => {
|
|
57
|
+
let url = null;
|
|
58
|
+
|
|
59
|
+
const cosBlob = await getBlobFromCOS(chunk.hash);
|
|
60
|
+
if (cosBlob) {
|
|
61
|
+
console.log(`COS Loader: Loaded ${chunk.file} from COS!`);
|
|
62
|
+
url = URL.createObjectURL(new Blob([cosBlob], { type: 'application/javascript' }));
|
|
63
|
+
} else {
|
|
64
|
+
console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
|
|
65
|
+
try {
|
|
66
|
+
const response = await fetch(chunk.file);
|
|
67
|
+
if (response.ok) {
|
|
68
|
+
const blob = await response.blob();
|
|
69
|
+
url = URL.createObjectURL(new Blob([blob], { type: 'application/javascript' }));
|
|
70
|
+
// Store in COS for next time
|
|
71
|
+
storeBlobInCOS(blob, chunk.hash);
|
|
72
|
+
} else {
|
|
73
|
+
console.error(`COS Loader: Fetch failed with status ${response.status}`);
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.error(`COS Loader: Network fetch failed for ${chunk.file}`, e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Set global variable if we have a URL
|
|
81
|
+
if (url && chunk.globalVar) {
|
|
82
|
+
window[chunk.globalVar] = url;
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Start App
|
|
88
|
+
try {
|
|
89
|
+
console.log('COS Loader: Starting app...');
|
|
90
|
+
await import(mainEntry.file);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error('COS Loader: Failed to start app', err);
|
|
93
|
+
}
|
|
94
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-cross-origin-storage",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Vite plugin to load chunks from Cross-Origin Storage",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vite",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"loader.js"
|
|
36
36
|
],
|
|
37
37
|
"scripts": {
|
|
38
|
-
"build": "tsup index.ts --format esm --dts --clean"
|
|
38
|
+
"build": "tsup index.ts --format esm --dts --clean && cp loader.js dist/"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|