vite-plugin-cross-origin-storage 1.3.24 → 1.4.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/dist/index.js CHANGED
@@ -1619,7 +1619,7 @@ var hasStringIsWellFormed = "isWellFormed" in String.prototype;
1619
1619
 
1620
1620
  // index.ts
1621
1621
  function cosPlugin(options = {}) {
1622
- const filter = createFilter(options.include || ["**/*"], options.exclude);
1622
+ const filter = options.include || options.exclude ? createFilter(options.include || ["**/*"], options.exclude, { resolve: false }) : () => true;
1623
1623
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
1624
1624
  const loaderPath = path.resolve(__dirname, "./loader.js");
1625
1625
  let config;
@@ -1650,9 +1650,9 @@ function cosPlugin(options = {}) {
1650
1650
  console.log(`COS Plugin: [ENTRY] ${fileName}`);
1651
1651
  mainChunk = chunk;
1652
1652
  } else {
1653
- const res = filter(fileName);
1653
+ const res = filter(fileName) || filter(chunk.name);
1654
1654
  console.log(
1655
- `COS Plugin: [FILTER] ${fileName} -> ${res ? "INCLUDE" : "SKIP"}`
1655
+ `COS Plugin: [FILTER] ${fileName} (name: ${chunk.name}) -> ${res ? "INCLUDE" : "SKIP"}`
1656
1656
  );
1657
1657
  if (res) {
1658
1658
  managedChunks[fileName] = chunk;
@@ -1668,19 +1668,13 @@ function cosPlugin(options = {}) {
1668
1668
  (c) => c.type === "chunk"
1669
1669
  );
1670
1670
  const managedChunkNames = new Set(Object.keys(managedChunks));
1671
+ const unmanagedDependencies = /* @__PURE__ */ new Set();
1671
1672
  const base = config.base.endsWith("/") ? config.base : config.base + "/";
1672
- const managedChunkInfo = {};
1673
- for (const fileName in managedChunks) {
1674
- const nameHash = crypto.createHash("sha256").update(fileName).digest("hex").substring(0, 8);
1675
- managedChunkInfo[fileName] = {
1676
- globalVar: `__COS_CHUNK_${nameHash}__`,
1677
- chunk: managedChunks[fileName]
1678
- };
1679
- }
1680
1673
  for (const targetChunk of allChunks) {
1681
1674
  const isTargetManaged = managedChunkNames.has(targetChunk.fileName);
1682
1675
  const importerDir = path.dirname(targetChunk.fileName);
1683
- for (const depFileName in bundle) {
1676
+ const deps = [...targetChunk.imports, ...targetChunk.dynamicImports];
1677
+ for (const depFileName of deps) {
1684
1678
  const depChunk = bundle[depFileName];
1685
1679
  if (!depChunk || depChunk.type !== "chunk") continue;
1686
1680
  const isDepManaged = managedChunkNames.has(depFileName);
@@ -1694,45 +1688,38 @@ function cosPlugin(options = {}) {
1694
1688
  const bareSpecifier = `coschunk-${depFileName.replace(/\//g, "-")}`;
1695
1689
  const staticPattern = `(import|export)\\b\\s*((?:(?!\\bimport\\b|\\bexport\\b)[\\s\\S])*?\\bfrom\\b\\s*)?['"]${escapedRelPath}['"]\\s*;?`;
1696
1690
  const staticRegex = new RegExp(staticPattern, "g");
1697
- targetChunk.code = targetChunk.code.replace(
1698
- staticRegex,
1699
- (match, keyword, fromPart) => {
1700
- return `${keyword}${fromPart ? " " + fromPart : " "}"${bareSpecifier}";`;
1701
- }
1702
- );
1691
+ targetChunk.code = targetChunk.code.replace(staticRegex, (match, keyword, fromPart) => {
1692
+ return `${keyword}${fromPart ? " " + fromPart : " "}"${bareSpecifier}";`;
1693
+ });
1703
1694
  const dynamicPattern = `import\\s*\\(\\s*['"]${escapedRelPath}['"]\\s*\\)`;
1704
1695
  const dynamicRegex = new RegExp(dynamicPattern, "g");
1705
1696
  targetChunk.code = targetChunk.code.replace(
1706
1697
  dynamicRegex,
1707
1698
  () => `import("${bareSpecifier}")`
1708
1699
  );
1700
+ if (!isDepManaged) {
1701
+ unmanagedDependencies.add(depFileName);
1702
+ }
1709
1703
  }
1710
1704
  }
1711
1705
  }
1712
- const manifest = {};
1713
- for (const fileName in managedChunkInfo) {
1714
- const { chunk, globalVar } = managedChunkInfo[fileName];
1706
+ const manifest = {
1707
+ base,
1708
+ entry: mainChunk.fileName,
1709
+ chunks: {}
1710
+ };
1711
+ for (const fileName in managedChunks) {
1712
+ const chunk = managedChunks[fileName];
1715
1713
  const finalHash = crypto.createHash("sha256").update(chunk.code).digest("hex");
1716
- const hasDefault = chunk.exports.includes("default");
1717
- manifest[fileName] = {
1718
- fileName,
1719
- file: `${base}${fileName}`,
1720
- hash: finalHash,
1721
- globalVar,
1722
- hasDefault
1723
- };
1714
+ manifest.chunks[fileName] = finalHash;
1724
1715
  }
1725
- const entryFileName = mainChunk.fileName;
1726
- manifest["index"] = {
1727
- fileName: entryFileName,
1728
- file: `${base}${entryFileName}`
1729
- };
1716
+ manifest.unmanaged = Array.from(unmanagedDependencies);
1730
1717
  if (htmlAsset) {
1731
1718
  try {
1732
1719
  let loaderCode = fs.readFileSync(loaderPath, "utf-8");
1733
1720
  loaderCode = loaderCode.replace(
1734
1721
  "__COS_MANIFEST__",
1735
- JSON.stringify(manifest)
1722
+ JSON.stringify(manifest, null, 2)
1736
1723
  );
1737
1724
  let htmlSource = htmlAsset.source;
1738
1725
  htmlSource = htmlSource.replace(
package/dist/loader.js CHANGED
@@ -6,14 +6,20 @@
6
6
  // @ts-ignore
7
7
  const manifest = __COS_MANIFEST__;
8
8
 
9
- const mainEntry = manifest && manifest['index'];
9
+ const { base, entry, chunks } = manifest;
10
+ const mainEntry = entry;
11
+
10
12
  if (!mainEntry) {
11
- console.warn('COS Loader: Missing main entry in manifest.');
13
+ console.warn('COS Loader: Missing entry in manifest.');
12
14
  return;
13
15
  }
14
16
 
15
- // Identify managed chunks (anything with a hash)
16
- const chunksToLoad = Object.values(manifest).filter((item) => item.hash);
17
+ // Identify managed chunks
18
+ const chunksToLoad = Object.entries(chunks || {}).map(([fileName, hash]) => ({
19
+ fileName,
20
+ hash,
21
+ file: `${base}${fileName}`,
22
+ }));
17
23
 
18
24
  async function getBlobFromCOS(hash) {
19
25
  if (!isCOSAvailable) return null;
@@ -51,12 +57,17 @@
51
57
 
52
58
  async function getChunkDataUrl(chunk) {
53
59
  let blob = await getBlobFromCOS(chunk.hash);
60
+ if (blob) {
61
+ console.log(`COS Loader: ${chunk.fileName} found in COS`);
62
+ blob = new Blob([blob], { type: 'text/javascript' });
63
+ }
54
64
  if (!blob) {
55
65
  console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
56
66
  try {
57
67
  const resp = await fetch(chunk.file);
58
68
  if (!resp.ok) throw new Error(`Status ${resp.status}`);
59
- blob = await resp.blob();
69
+ const rawBlob = await resp.blob();
70
+ blob = new Blob([rawBlob], { type: 'text/javascript' });
60
71
  await storeBlobInCOS(blob, chunk.hash);
61
72
  } catch (e) {
62
73
  console.error(`COS Loader: Failed to fetch ${chunk.file}`, e);
@@ -64,12 +75,10 @@
64
75
  }
65
76
  }
66
77
 
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
- });
78
+ // Convert blob to Blob URL
79
+ // Blob URLs are synchronous and share the page origin,
80
+ // which helps with module resolution in complex graphs.
81
+ return URL.createObjectURL(blob);
73
82
  }
74
83
 
75
84
  // Initialize
@@ -78,6 +87,14 @@
78
87
 
79
88
  // Resolve all chunks to Data URLs
80
89
  const importMap = { imports: {} };
90
+
91
+ // Set up unmanaged dependencies correctly so Data URLs can resolve them.
92
+ for (const fileName of manifest.unmanaged || []) {
93
+ const bareSpecifier = `coschunk-${fileName.replace(/\//g, '-')}`;
94
+ importMap.imports[bareSpecifier] = window.location.origin + base + fileName;
95
+ }
96
+
97
+ console.log(`COS Loader: Loading ${chunksToLoad.length} chunks...`);
81
98
  const loadPromises = chunksToLoad.map(async (chunk) => {
82
99
  const dataUrl = await getChunkDataUrl(chunk);
83
100
  if (dataUrl) {
@@ -93,19 +110,22 @@
93
110
  // Inject Import Map
94
111
  const script = document.createElement('script');
95
112
  script.type = 'importmap';
96
- script.textContent = JSON.stringify(importMap);
113
+ script.textContent = JSON.stringify(importMap, null, 2);
97
114
  document.head.appendChild(script);
98
115
 
99
116
  console.log('COS Loader: Import Map injected');
100
117
 
101
118
  // Import the main entry.
102
- // If the entry itself were managed, we would use cos-chunk: prefix here too.
103
- // For now, it's loaded via its absolute path from the server,
104
- // and its internal imports (rewritten by the plugin) will use cos-chunk:
105
- const entryUrl = mainEntry.file;
106
- await import(entryUrl);
119
+ const entryUrl = `${base}${mainEntry}`;
107
120
 
121
+ // Small delay to ensure the browser has fully registered the
122
+ // import map before resolving the first module.
123
+ setTimeout(() => {
124
+ import(entryUrl).catch((err) => {
125
+ console.error('COS Loader: Failed to start app', err);
126
+ });
127
+ }, 0);
108
128
  } catch (err) {
109
- console.error('COS Loader: Failed to start app', err);
129
+ console.error('COS Loader: Initialization failed', err);
110
130
  }
111
131
  })();
package/loader.js CHANGED
@@ -6,14 +6,20 @@
6
6
  // @ts-ignore
7
7
  const manifest = __COS_MANIFEST__;
8
8
 
9
- const mainEntry = manifest && manifest['index'];
9
+ const { base, entry, chunks } = manifest;
10
+ const mainEntry = entry;
11
+
10
12
  if (!mainEntry) {
11
- console.warn('COS Loader: Missing main entry in manifest.');
13
+ console.warn('COS Loader: Missing entry in manifest.');
12
14
  return;
13
15
  }
14
16
 
15
- // Identify managed chunks (anything with a hash)
16
- const chunksToLoad = Object.values(manifest).filter((item) => item.hash);
17
+ // Identify managed chunks
18
+ const chunksToLoad = Object.entries(chunks || {}).map(([fileName, hash]) => ({
19
+ fileName,
20
+ hash,
21
+ file: `${base}${fileName}`,
22
+ }));
17
23
 
18
24
  async function getBlobFromCOS(hash) {
19
25
  if (!isCOSAvailable) return null;
@@ -51,12 +57,17 @@
51
57
 
52
58
  async function getChunkDataUrl(chunk) {
53
59
  let blob = await getBlobFromCOS(chunk.hash);
60
+ if (blob) {
61
+ console.log(`COS Loader: ${chunk.fileName} found in COS`);
62
+ blob = new Blob([blob], { type: 'text/javascript' });
63
+ }
54
64
  if (!blob) {
55
65
  console.log(`COS Loader: ${chunk.file} not in COS, fetching...`);
56
66
  try {
57
67
  const resp = await fetch(chunk.file);
58
68
  if (!resp.ok) throw new Error(`Status ${resp.status}`);
59
- blob = await resp.blob();
69
+ const rawBlob = await resp.blob();
70
+ blob = new Blob([rawBlob], { type: 'text/javascript' });
60
71
  await storeBlobInCOS(blob, chunk.hash);
61
72
  } catch (e) {
62
73
  console.error(`COS Loader: Failed to fetch ${chunk.file}`, e);
@@ -64,12 +75,10 @@
64
75
  }
65
76
  }
66
77
 
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
- });
78
+ // Convert blob to Blob URL
79
+ // Blob URLs are synchronous and share the page origin,
80
+ // which helps with module resolution in complex graphs.
81
+ return URL.createObjectURL(blob);
73
82
  }
74
83
 
75
84
  // Initialize
@@ -78,6 +87,14 @@
78
87
 
79
88
  // Resolve all chunks to Data URLs
80
89
  const importMap = { imports: {} };
90
+
91
+ // Set up unmanaged dependencies correctly so Data URLs can resolve them.
92
+ for (const fileName of manifest.unmanaged || []) {
93
+ const bareSpecifier = `coschunk-${fileName.replace(/\//g, '-')}`;
94
+ importMap.imports[bareSpecifier] = window.location.origin + base + fileName;
95
+ }
96
+
97
+ console.log(`COS Loader: Loading ${chunksToLoad.length} chunks...`);
81
98
  const loadPromises = chunksToLoad.map(async (chunk) => {
82
99
  const dataUrl = await getChunkDataUrl(chunk);
83
100
  if (dataUrl) {
@@ -93,19 +110,22 @@
93
110
  // Inject Import Map
94
111
  const script = document.createElement('script');
95
112
  script.type = 'importmap';
96
- script.textContent = JSON.stringify(importMap);
113
+ script.textContent = JSON.stringify(importMap, null, 2);
97
114
  document.head.appendChild(script);
98
115
 
99
116
  console.log('COS Loader: Import Map injected');
100
117
 
101
118
  // Import the main entry.
102
- // If the entry itself were managed, we would use cos-chunk: prefix here too.
103
- // For now, it's loaded via its absolute path from the server,
104
- // and its internal imports (rewritten by the plugin) will use cos-chunk:
105
- const entryUrl = mainEntry.file;
106
- await import(entryUrl);
119
+ const entryUrl = `${base}${mainEntry}`;
107
120
 
121
+ // Small delay to ensure the browser has fully registered the
122
+ // import map before resolving the first module.
123
+ setTimeout(() => {
124
+ import(entryUrl).catch((err) => {
125
+ console.error('COS Loader: Failed to start app', err);
126
+ });
127
+ }, 0);
108
128
  } catch (err) {
109
- console.error('COS Loader: Failed to start app', err);
129
+ console.error('COS Loader: Initialization failed', err);
110
130
  }
111
131
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-cross-origin-storage",
3
- "version": "1.3.24",
3
+ "version": "1.4.1",
4
4
  "description": "Vite plugin to load chunks from Cross-Origin Storage",
5
5
  "keywords": [
6
6
  "vite",
@@ -36,7 +36,10 @@
36
36
  ],
37
37
  "scripts": {
38
38
  "build": "tsup index.ts --format esm --dts --clean && cp loader.js dist/",
39
- "prepublishOnly": "npm run build"
39
+ "prepublishOnly": "npm run build",
40
+ "test:dev": "npm run build && vite testing",
41
+ "test:build": "npm run build && vite build testing",
42
+ "test:preview": "vite preview testing"
40
43
  },
41
44
  "peerDependencies": {
42
45
  "vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"