vite-plugin-cross-origin-storage 1.3.17 → 1.3.19
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 +2 -2
- package/dist/loader.js +103 -80
- package/loader.js +103 -80
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1668,6 +1668,7 @@ function cosPlugin(options = {}) {
|
|
|
1668
1668
|
(c) => c.type === "chunk"
|
|
1669
1669
|
);
|
|
1670
1670
|
const managedChunkNames = new Set(Object.keys(managedChunks));
|
|
1671
|
+
const base = config.base.endsWith("/") ? config.base : config.base + "/";
|
|
1671
1672
|
const managedChunkInfo = {};
|
|
1672
1673
|
for (const fileName in managedChunks) {
|
|
1673
1674
|
const nameHash = crypto.createHash("sha256").update(fileName).digest("hex").substring(0, 8);
|
|
@@ -1690,7 +1691,7 @@ function cosPlugin(options = {}) {
|
|
|
1690
1691
|
/[.*+?^${}()|[\]\\]/g,
|
|
1691
1692
|
"\\$&"
|
|
1692
1693
|
);
|
|
1693
|
-
const bareSpecifier =
|
|
1694
|
+
const bareSpecifier = `./${depFileName}`;
|
|
1694
1695
|
const staticPattern = `(import|export)\\b\\s*((?:(?!\\bimport\\b|\\bexport\\b)[\\s\\S])*?\\bfrom\\b\\s*)?['"]${escapedRelPath}['"]\\s*;?`;
|
|
1695
1696
|
const staticRegex = new RegExp(staticPattern, "g");
|
|
1696
1697
|
targetChunk.code = targetChunk.code.replace(
|
|
@@ -1709,7 +1710,6 @@ function cosPlugin(options = {}) {
|
|
|
1709
1710
|
}
|
|
1710
1711
|
}
|
|
1711
1712
|
const manifest = {};
|
|
1712
|
-
const base = config.base.endsWith("/") ? config.base : config.base + "/";
|
|
1713
1713
|
for (const fileName in managedChunkInfo) {
|
|
1714
1714
|
const { chunk, globalVar } = managedChunkInfo[fileName];
|
|
1715
1715
|
const finalHash = crypto.createHash("sha256").update(chunk.code).digest("hex");
|
package/dist/loader.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
// Identify managed chunks (anything with a hash)
|
|
18
18
|
const chunksToLoad = Object.values(manifest).filter((item) => item.hash);
|
|
19
|
+
const chunksByFileName = {};
|
|
20
|
+
chunksToLoad.forEach(c => chunksByFileName[c.fileName] = c);
|
|
19
21
|
|
|
20
22
|
async function getBlobFromCOS(hash) {
|
|
21
23
|
if (!isCOSAvailable) return null;
|
|
@@ -51,93 +53,114 @@
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
|
|
66
|
+
let blob = await getBlobFromCOS(chunk.hash);
|
|
67
|
+
if (!blob) {
|
|
68
|
+
console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
|
|
69
|
+
try {
|
|
70
|
+
const resp = await fetch(chunk.file);
|
|
71
|
+
if (!resp.ok) throw new Error(`Status ${resp.status}`);
|
|
72
|
+
blob = await resp.blob();
|
|
73
|
+
storeBlobInCOS(blob, chunk.hash);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error(`COS Loader: Failed to fetch ${chunk.file}`, e);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
72
78
|
}
|
|
79
|
+
const text = await blob.text();
|
|
80
|
+
rawContent[chunk.fileName] = text;
|
|
81
|
+
return text;
|
|
82
|
+
}
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
url = URL.createObjectURL(
|
|
82
|
-
new Blob([cosBlob], { type: 'text/javascript' })
|
|
83
|
-
);
|
|
84
|
-
} else {
|
|
85
|
-
console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
|
|
86
|
-
try {
|
|
87
|
-
const response = await fetch(chunk.file);
|
|
88
|
-
if (response.ok) {
|
|
89
|
-
const blob = await response.blob();
|
|
90
|
-
url = URL.createObjectURL(
|
|
91
|
-
new Blob([blob], { type: 'text/javascript' })
|
|
92
|
-
);
|
|
93
|
-
// Store in COS for next time
|
|
94
|
-
storeBlobInCOS(blob, chunk.hash);
|
|
95
|
-
} else {
|
|
96
|
-
console.error(
|
|
97
|
-
`COS Loader: Fetch failed with status ${response.status}`
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
} catch (e) {
|
|
101
|
-
console.error(
|
|
102
|
-
`COS Loader: Network fetch failed for ${chunk.file}`,
|
|
103
|
-
e
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (url) {
|
|
109
|
-
// Use a Data URL shim to decouple the import graph.
|
|
110
|
-
// This ensures that the circular dependency between React and React-DOM
|
|
111
|
-
// doesn't cause a lockout/deadlock during the module graph instantiation.
|
|
112
|
-
const shim = `export * from "${url}";${chunk.hasDefault ? `export { default } from "${url}";` : ''}`;
|
|
113
|
-
const shimUrl = `data:text/javascript;base64,${btoa(shim)}`;
|
|
114
|
-
|
|
115
|
-
// Map the virtual bare specifier to the shim
|
|
116
|
-
importMap.imports[`cos-id_${chunk.fileName}`] = shimUrl;
|
|
117
|
-
|
|
118
|
-
// Also set global if anyone still needs it (legacy)
|
|
119
|
-
if (chunk.globalVar) {
|
|
120
|
-
window[chunk.globalVar] = url;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
})
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// Inject the importmap
|
|
127
|
-
if (Object.keys(importMap.imports).length > 0) {
|
|
128
|
-
const imScript = document.createElement('script');
|
|
129
|
-
imScript.type = 'importmap';
|
|
130
|
-
imScript.textContent = JSON.stringify(importMap);
|
|
131
|
-
document.head.appendChild(imScript);
|
|
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;
|
|
132
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
|
+
|
|
103
|
+
let code = await loadRawContent(chunk);
|
|
104
|
+
if (!code) return null;
|
|
105
|
+
|
|
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
|
+
}
|
|
114
|
+
|
|
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 });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
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}"`);
|
|
131
|
+
|
|
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);
|
|
139
|
+
|
|
140
|
+
// Create Shim
|
|
141
|
+
const shim = `export * from "${blobUrl}";${chunk.hasDefault ? `export { default } from "${blobUrl}";` : ''}`;
|
|
142
|
+
const shimUrl = `data:text/javascript;base64,${btoa(shim)}`;
|
|
143
|
+
|
|
144
|
+
resolvedUrls[fileName] = { blobUrl, shimUrl };
|
|
145
|
+
processing.delete(fileName);
|
|
146
|
+
return resolvedUrls[fileName];
|
|
133
147
|
}
|
|
134
148
|
|
|
135
|
-
//
|
|
149
|
+
// Initialize
|
|
136
150
|
try {
|
|
137
151
|
console.log('COS Loader: Starting app...');
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
// Resolve main entry
|
|
153
|
+
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);
|
|
157
|
+
|
|
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
|
+
}
|
|
141
164
|
} catch (err) {
|
|
142
165
|
console.error('COS Loader: Failed to start app', err);
|
|
143
166
|
}
|
package/loader.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
// Identify managed chunks (anything with a hash)
|
|
18
18
|
const chunksToLoad = Object.values(manifest).filter((item) => item.hash);
|
|
19
|
+
const chunksByFileName = {};
|
|
20
|
+
chunksToLoad.forEach(c => chunksByFileName[c.fileName] = c);
|
|
19
21
|
|
|
20
22
|
async function getBlobFromCOS(hash) {
|
|
21
23
|
if (!isCOSAvailable) return null;
|
|
@@ -51,93 +53,114 @@
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
|
|
66
|
+
let blob = await getBlobFromCOS(chunk.hash);
|
|
67
|
+
if (!blob) {
|
|
68
|
+
console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
|
|
69
|
+
try {
|
|
70
|
+
const resp = await fetch(chunk.file);
|
|
71
|
+
if (!resp.ok) throw new Error(`Status ${resp.status}`);
|
|
72
|
+
blob = await resp.blob();
|
|
73
|
+
storeBlobInCOS(blob, chunk.hash);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error(`COS Loader: Failed to fetch ${chunk.file}`, e);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
72
78
|
}
|
|
79
|
+
const text = await blob.text();
|
|
80
|
+
rawContent[chunk.fileName] = text;
|
|
81
|
+
return text;
|
|
82
|
+
}
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
url = URL.createObjectURL(
|
|
82
|
-
new Blob([cosBlob], { type: 'text/javascript' })
|
|
83
|
-
);
|
|
84
|
-
} else {
|
|
85
|
-
console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
|
|
86
|
-
try {
|
|
87
|
-
const response = await fetch(chunk.file);
|
|
88
|
-
if (response.ok) {
|
|
89
|
-
const blob = await response.blob();
|
|
90
|
-
url = URL.createObjectURL(
|
|
91
|
-
new Blob([blob], { type: 'text/javascript' })
|
|
92
|
-
);
|
|
93
|
-
// Store in COS for next time
|
|
94
|
-
storeBlobInCOS(blob, chunk.hash);
|
|
95
|
-
} else {
|
|
96
|
-
console.error(
|
|
97
|
-
`COS Loader: Fetch failed with status ${response.status}`
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
} catch (e) {
|
|
101
|
-
console.error(
|
|
102
|
-
`COS Loader: Network fetch failed for ${chunk.file}`,
|
|
103
|
-
e
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (url) {
|
|
109
|
-
// Use a Data URL shim to decouple the import graph.
|
|
110
|
-
// This ensures that the circular dependency between React and React-DOM
|
|
111
|
-
// doesn't cause a lockout/deadlock during the module graph instantiation.
|
|
112
|
-
const shim = `export * from "${url}";${chunk.hasDefault ? `export { default } from "${url}";` : ''}`;
|
|
113
|
-
const shimUrl = `data:text/javascript;base64,${btoa(shim)}`;
|
|
114
|
-
|
|
115
|
-
// Map the virtual bare specifier to the shim
|
|
116
|
-
importMap.imports[`cos-id_${chunk.fileName}`] = shimUrl;
|
|
117
|
-
|
|
118
|
-
// Also set global if anyone still needs it (legacy)
|
|
119
|
-
if (chunk.globalVar) {
|
|
120
|
-
window[chunk.globalVar] = url;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
})
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// Inject the importmap
|
|
127
|
-
if (Object.keys(importMap.imports).length > 0) {
|
|
128
|
-
const imScript = document.createElement('script');
|
|
129
|
-
imScript.type = 'importmap';
|
|
130
|
-
imScript.textContent = JSON.stringify(importMap);
|
|
131
|
-
document.head.appendChild(imScript);
|
|
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;
|
|
132
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
|
+
|
|
103
|
+
let code = await loadRawContent(chunk);
|
|
104
|
+
if (!code) return null;
|
|
105
|
+
|
|
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
|
+
}
|
|
114
|
+
|
|
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 });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
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}"`);
|
|
131
|
+
|
|
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);
|
|
139
|
+
|
|
140
|
+
// Create Shim
|
|
141
|
+
const shim = `export * from "${blobUrl}";${chunk.hasDefault ? `export { default } from "${blobUrl}";` : ''}`;
|
|
142
|
+
const shimUrl = `data:text/javascript;base64,${btoa(shim)}`;
|
|
143
|
+
|
|
144
|
+
resolvedUrls[fileName] = { blobUrl, shimUrl };
|
|
145
|
+
processing.delete(fileName);
|
|
146
|
+
return resolvedUrls[fileName];
|
|
133
147
|
}
|
|
134
148
|
|
|
135
|
-
//
|
|
149
|
+
// Initialize
|
|
136
150
|
try {
|
|
137
151
|
console.log('COS Loader: Starting app...');
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
// Resolve main entry
|
|
153
|
+
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);
|
|
157
|
+
|
|
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
|
+
}
|
|
141
164
|
} catch (err) {
|
|
142
165
|
console.error('COS Loader: Failed to start app', err);
|
|
143
166
|
}
|