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 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.2.0",
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"