vite-plugin-cross-origin-storage 1.3.19 → 1.3.20

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/dist/index.js CHANGED
@@ -1722,8 +1722,10 @@ function cosPlugin(options = {}) {
1722
1722
  hasDefault
1723
1723
  };
1724
1724
  }
1725
+ const entryFileName = mainChunk.fileName;
1725
1726
  manifest["index"] = {
1726
- file: `${config.base.endsWith("/") ? config.base : config.base + "/"}${mainChunk.fileName}`
1727
+ fileName: entryFileName,
1728
+ file: `${base}${entryFileName}`
1727
1729
  };
1728
1730
  if (htmlAsset) {
1729
1731
  try {
package/dist/loader.js CHANGED
@@ -1,6 +1,4 @@
1
1
  (async function () {
2
- await new Promise((resolve) => setTimeout(resolve, 100));
3
-
4
2
  const isCOSAvailable = 'crossOriginStorage' in navigator;
5
3
  console.log('COS Loader: isCOSAvailable =', isCOSAvailable);
6
4
 
@@ -16,8 +14,6 @@
16
14
 
17
15
  // Identify managed chunks (anything with a hash)
18
16
  const chunksToLoad = Object.values(manifest).filter((item) => item.hash);
19
- const chunksByFileName = {};
20
- chunksToLoad.forEach(c => chunksByFileName[c.fileName] = c);
21
17
 
22
18
  async function getBlobFromCOS(hash) {
23
19
  if (!isCOSAvailable) return null;
@@ -46,23 +42,14 @@
46
42
  const writable = await handles[0].createWritable();
47
43
  await writable.write(blob);
48
44
  await writable.close();
49
- console.log('COS Loader: Stored bundle in COS', hash);
45
+ console.log('COS Loader: Stored chunk in COS', hash);
50
46
  }
51
47
  } catch (err) {
52
48
  console.error('COS Loader: Failed to store in COS', err);
53
49
  }
54
50
  }
55
51
 
56
- // Cache for resolved URLs
57
- const resolvedUrls = {}; // fileName -> { blobUrl, shimUrl }
58
- const processing = new Set();
59
-
60
- // Cache for raw text content
61
- const rawContent = {}; // fileName -> string
62
-
63
- async function loadRawContent(chunk) {
64
- if (rawContent[chunk.fileName]) return rawContent[chunk.fileName];
65
-
52
+ async function getChunkDataUrl(chunk) {
66
53
  let blob = await getBlobFromCOS(chunk.hash);
67
54
  if (!blob) {
68
55
  console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
@@ -70,97 +57,50 @@
70
57
  const resp = await fetch(chunk.file);
71
58
  if (!resp.ok) throw new Error(`Status ${resp.status}`);
72
59
  blob = await resp.blob();
73
- storeBlobInCOS(blob, chunk.hash);
60
+ await storeBlobInCOS(blob, chunk.hash);
74
61
  } catch (e) {
75
62
  console.error(`COS Loader: Failed to fetch ${chunk.file}`, e);
76
63
  return null;
77
64
  }
78
65
  }
79
- const text = await blob.text();
80
- rawContent[chunk.fileName] = text;
81
- return text;
82
- }
83
-
84
- async function resolveChunk(fileName) {
85
- if (resolvedUrls[fileName]) return resolvedUrls[fileName];
86
- if (processing.has(fileName)) {
87
- console.warn(`COS Loader: Circular dependency detected for ${fileName}. Breaking cycle with placeholder (may fail).`);
88
- // We cannot solve cycles with Direct Data Injection easily.
89
- // Returning null might cause failure, but we hope merging chunks avoided this.
90
- return null;
91
- }
92
-
93
- processing.add(fileName);
94
- const chunk = chunksByFileName[fileName];
95
- if (!chunk) {
96
- // Unmanaged dependency? Should have been handled by absolute/base logic but we reverted to ./
97
- // If it's ./unmanaged.js, we don't have it in manifest.
98
- // We can't inject it.
99
- console.warn(`COS Loader: Unknown dependency ${fileName}`);
100
- return null;
101
- }
102
66
 
103
- let code = await loadRawContent(chunk);
104
- if (!code) return null;
67
+ // Convert blob to Data URL
68
+ return new Promise((resolve) => {
69
+ const reader = new FileReader();
70
+ reader.onloadend = () => resolve(reader.result);
71
+ reader.readAsDataURL(blob);
72
+ });
73
+ }
105
74
 
106
- // Find dependencies in the code: import ... from "./dep.js"
107
- // Regex matches the one used in build: `from "./..."`
108
- const depRegex = /from\s+['"]\.\/([^'"]+)['"]/g;
109
- let match;
110
- const deps = new Set();
111
- while ((match = depRegex.exec(code)) !== null) {
112
- deps.add(match[1]); // The filename relative to current
113
- }
75
+ // Initialize
76
+ try {
77
+ console.log('COS Loader: Starting app...');
114
78
 
115
- // Resolve dependencies recursively
116
- const replacements = [];
117
- for (const depName of deps) {
118
- const res = await resolveChunk(depName);
119
- if (res && res.shimUrl) {
120
- replacements.push({ depName, url: res.shimUrl });
79
+ // Resolve all chunks to Data URLs
80
+ const importMap = { imports: {} };
81
+ const loadPromises = chunksToLoad.map(async (chunk) => {
82
+ const dataUrl = await getChunkDataUrl(chunk);
83
+ if (dataUrl) {
84
+ // Map the relative path (as used in the plugin's bare specifier rewrite) to the data URL
85
+ importMap.imports[`./${chunk.fileName}`] = dataUrl;
121
86
  }
122
- }
87
+ });
123
88
 
124
- // Replace in code
125
- for (const { depName, url } of replacements) {
126
- // Replace ALL occurrences
127
- // We need to be careful with regex replacement safer:
128
- // Replace `from "./depName"` with `from "url"`
129
- code = code.split(`from "./${depName}"`).join(`from "${url}"`);
130
- code = code.split(`from './${depName}'`).join(`from "${url}"`);
89
+ await Promise.all(loadPromises);
131
90
 
132
- // Also dynamic imports: import("./depName")
133
- code = code.split(`import("./${depName}")`).join(`import("${url}")`);
134
- code = code.split(`import('./${depName}')`).join(`import("${url}")`);
135
- }
136
-
137
- const blob = new Blob([code], { type: 'text/javascript' });
138
- const blobUrl = URL.createObjectURL(blob);
91
+ // Inject Import Map
92
+ const script = document.createElement('script');
93
+ script.type = 'importmap';
94
+ script.textContent = JSON.stringify(importMap);
95
+ document.head.appendChild(script);
139
96
 
140
- // Create Shim
141
- const shim = `export * from "${blobUrl}";${chunk.hasDefault ? `export { default } from "${blobUrl}";` : ''}`;
142
- const shimUrl = `data:text/javascript;base64,${btoa(shim)}`;
97
+ console.log('COS Loader: Import Map injected');
143
98
 
144
- resolvedUrls[fileName] = { blobUrl, shimUrl };
145
- processing.delete(fileName);
146
- return resolvedUrls[fileName];
147
- }
148
-
149
- // Initialize
150
- try {
151
- console.log('COS Loader: Starting app...');
152
- // Resolve main entry
99
+ // Import the main entry.
100
+ // The main entry itself is also managed and rewritten to use bare specifiers.
153
101
  const entryFileName = mainEntry.fileName;
154
- // We assume mainEntry doesn't need to be shimmed for ITSELF, but its deps do.
155
- // Actually resolveChunk returns the shimUrl.
156
- const res = await resolveChunk(entryFileName);
102
+ await import(`./${entryFileName}`);
157
103
 
158
- if (res) {
159
- // Import the SHIM of the main entry
160
- await import(res.shimUrl);
161
- } else {
162
- console.error('COS Loader: Failed to resolve main entry');
163
- }
164
104
  } catch (err) {
165
105
  console.error('COS Loader: Failed to start app', err);
166
106
  }
package/loader.js CHANGED
@@ -1,6 +1,4 @@
1
1
  (async function () {
2
- await new Promise((resolve) => setTimeout(resolve, 100));
3
-
4
2
  const isCOSAvailable = 'crossOriginStorage' in navigator;
5
3
  console.log('COS Loader: isCOSAvailable =', isCOSAvailable);
6
4
 
@@ -16,8 +14,6 @@
16
14
 
17
15
  // Identify managed chunks (anything with a hash)
18
16
  const chunksToLoad = Object.values(manifest).filter((item) => item.hash);
19
- const chunksByFileName = {};
20
- chunksToLoad.forEach(c => chunksByFileName[c.fileName] = c);
21
17
 
22
18
  async function getBlobFromCOS(hash) {
23
19
  if (!isCOSAvailable) return null;
@@ -46,23 +42,14 @@
46
42
  const writable = await handles[0].createWritable();
47
43
  await writable.write(blob);
48
44
  await writable.close();
49
- console.log('COS Loader: Stored bundle in COS', hash);
45
+ console.log('COS Loader: Stored chunk in COS', hash);
50
46
  }
51
47
  } catch (err) {
52
48
  console.error('COS Loader: Failed to store in COS', err);
53
49
  }
54
50
  }
55
51
 
56
- // Cache for resolved URLs
57
- const resolvedUrls = {}; // fileName -> { blobUrl, shimUrl }
58
- const processing = new Set();
59
-
60
- // Cache for raw text content
61
- const rawContent = {}; // fileName -> string
62
-
63
- async function loadRawContent(chunk) {
64
- if (rawContent[chunk.fileName]) return rawContent[chunk.fileName];
65
-
52
+ async function getChunkDataUrl(chunk) {
66
53
  let blob = await getBlobFromCOS(chunk.hash);
67
54
  if (!blob) {
68
55
  console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
@@ -70,97 +57,50 @@
70
57
  const resp = await fetch(chunk.file);
71
58
  if (!resp.ok) throw new Error(`Status ${resp.status}`);
72
59
  blob = await resp.blob();
73
- storeBlobInCOS(blob, chunk.hash);
60
+ await storeBlobInCOS(blob, chunk.hash);
74
61
  } catch (e) {
75
62
  console.error(`COS Loader: Failed to fetch ${chunk.file}`, e);
76
63
  return null;
77
64
  }
78
65
  }
79
- const text = await blob.text();
80
- rawContent[chunk.fileName] = text;
81
- return text;
82
- }
83
-
84
- async function resolveChunk(fileName) {
85
- if (resolvedUrls[fileName]) return resolvedUrls[fileName];
86
- if (processing.has(fileName)) {
87
- console.warn(`COS Loader: Circular dependency detected for ${fileName}. Breaking cycle with placeholder (may fail).`);
88
- // We cannot solve cycles with Direct Data Injection easily.
89
- // Returning null might cause failure, but we hope merging chunks avoided this.
90
- return null;
91
- }
92
-
93
- processing.add(fileName);
94
- const chunk = chunksByFileName[fileName];
95
- if (!chunk) {
96
- // Unmanaged dependency? Should have been handled by absolute/base logic but we reverted to ./
97
- // If it's ./unmanaged.js, we don't have it in manifest.
98
- // We can't inject it.
99
- console.warn(`COS Loader: Unknown dependency ${fileName}`);
100
- return null;
101
- }
102
66
 
103
- let code = await loadRawContent(chunk);
104
- if (!code) return null;
67
+ // Convert blob to Data URL
68
+ return new Promise((resolve) => {
69
+ const reader = new FileReader();
70
+ reader.onloadend = () => resolve(reader.result);
71
+ reader.readAsDataURL(blob);
72
+ });
73
+ }
105
74
 
106
- // Find dependencies in the code: import ... from "./dep.js"
107
- // Regex matches the one used in build: `from "./..."`
108
- const depRegex = /from\s+['"]\.\/([^'"]+)['"]/g;
109
- let match;
110
- const deps = new Set();
111
- while ((match = depRegex.exec(code)) !== null) {
112
- deps.add(match[1]); // The filename relative to current
113
- }
75
+ // Initialize
76
+ try {
77
+ console.log('COS Loader: Starting app...');
114
78
 
115
- // Resolve dependencies recursively
116
- const replacements = [];
117
- for (const depName of deps) {
118
- const res = await resolveChunk(depName);
119
- if (res && res.shimUrl) {
120
- replacements.push({ depName, url: res.shimUrl });
79
+ // Resolve all chunks to Data URLs
80
+ const importMap = { imports: {} };
81
+ const loadPromises = chunksToLoad.map(async (chunk) => {
82
+ const dataUrl = await getChunkDataUrl(chunk);
83
+ if (dataUrl) {
84
+ // Map the relative path (as used in the plugin's bare specifier rewrite) to the data URL
85
+ importMap.imports[`./${chunk.fileName}`] = dataUrl;
121
86
  }
122
- }
87
+ });
123
88
 
124
- // Replace in code
125
- for (const { depName, url } of replacements) {
126
- // Replace ALL occurrences
127
- // We need to be careful with regex replacement safer:
128
- // Replace `from "./depName"` with `from "url"`
129
- code = code.split(`from "./${depName}"`).join(`from "${url}"`);
130
- code = code.split(`from './${depName}'`).join(`from "${url}"`);
89
+ await Promise.all(loadPromises);
131
90
 
132
- // Also dynamic imports: import("./depName")
133
- code = code.split(`import("./${depName}")`).join(`import("${url}")`);
134
- code = code.split(`import('./${depName}')`).join(`import("${url}")`);
135
- }
136
-
137
- const blob = new Blob([code], { type: 'text/javascript' });
138
- const blobUrl = URL.createObjectURL(blob);
91
+ // Inject Import Map
92
+ const script = document.createElement('script');
93
+ script.type = 'importmap';
94
+ script.textContent = JSON.stringify(importMap);
95
+ document.head.appendChild(script);
139
96
 
140
- // Create Shim
141
- const shim = `export * from "${blobUrl}";${chunk.hasDefault ? `export { default } from "${blobUrl}";` : ''}`;
142
- const shimUrl = `data:text/javascript;base64,${btoa(shim)}`;
97
+ console.log('COS Loader: Import Map injected');
143
98
 
144
- resolvedUrls[fileName] = { blobUrl, shimUrl };
145
- processing.delete(fileName);
146
- return resolvedUrls[fileName];
147
- }
148
-
149
- // Initialize
150
- try {
151
- console.log('COS Loader: Starting app...');
152
- // Resolve main entry
99
+ // Import the main entry.
100
+ // The main entry itself is also managed and rewritten to use bare specifiers.
153
101
  const entryFileName = mainEntry.fileName;
154
- // We assume mainEntry doesn't need to be shimmed for ITSELF, but its deps do.
155
- // Actually resolveChunk returns the shimUrl.
156
- const res = await resolveChunk(entryFileName);
102
+ await import(`./${entryFileName}`);
157
103
 
158
- if (res) {
159
- // Import the SHIM of the main entry
160
- await import(res.shimUrl);
161
- } else {
162
- console.error('COS Loader: Failed to resolve main entry');
163
- }
164
104
  } catch (err) {
165
105
  console.error('COS Loader: Failed to start app', err);
166
106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-cross-origin-storage",
3
- "version": "1.3.19",
3
+ "version": "1.3.20",
4
4
  "description": "Vite plugin to load chunks from Cross-Origin Storage",
5
5
  "keywords": [
6
6
  "vite",