vite-plugin-cross-origin-storage 1.6.0 → 1.8.0
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 +33 -20
- package/dist/index.js +314 -109
- package/dist/loader.js +8 -19
- package/loader.js +8 -19
- package/package.json +22 -11
package/README.md
CHANGED
|
@@ -95,26 +95,39 @@ export default defineConfig({
|
|
|
95
95
|
|
|
96
96
|
## How It Works
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
### Build Time
|
|
99
|
+
|
|
100
|
+
1. **Magic Externals**: For `include` patterns that resolve to npm package
|
|
101
|
+
names (e.g. `vendor-react` → `react`), the plugin externalizes the package
|
|
102
|
+
from Rollup and uses esbuild to produce a single self-contained ESM bundle.
|
|
103
|
+
This bundle is emitted as a hashed asset (e.g. `assets/react-a1b2c3d4.js`)
|
|
104
|
+
and treated as a managed chunk.
|
|
105
|
+
2. **Import Rewriting**: All inter-chunk imports are rewritten from relative
|
|
106
|
+
paths (`"./chunk.js"`) to bare specifiers (`"coschunk-assets-chunk-js"`).
|
|
107
|
+
This applies to both managed and unmanaged chunks, because managed chunks
|
|
108
|
+
run as Blob URLs and cannot resolve relative paths at runtime.
|
|
109
|
+
3. **Manifest Generation**: A JSON manifest is built containing the base path,
|
|
110
|
+
the entry chunk filename, a map of every managed chunk filename to its
|
|
111
|
+
SHA-256 hash, and a list of unmanaged chunks that need network-URL entries
|
|
112
|
+
in the import map.
|
|
113
|
+
4. **Loader Injection**: The plugin disables the default
|
|
114
|
+
`<script type="module">` entry tag and injects a `<script id="cos-loader">`
|
|
115
|
+
containing the runtime loader with the manifest inlined.
|
|
116
|
+
|
|
117
|
+
### Runtime
|
|
118
|
+
|
|
119
|
+
1. The loader checks for `navigator.crossOriginStorage`.
|
|
120
|
+
2. For each managed chunk it calls
|
|
121
|
+
`navigator.crossOriginStorage.requestFileHandles([{ algorithm: 'SHA-256', value: hash }])`:
|
|
122
|
+
- **Cache hit**: wraps the returned `File` as a Blob URL.
|
|
123
|
+
- **Cache miss**: fetches the chunk from the network, stores it in COS via
|
|
124
|
+
`requestFileHandles(..., { create: true })`, then wraps it as a Blob URL.
|
|
125
|
+
3. Unmanaged chunks receive absolute network URLs.
|
|
126
|
+
4. An `<script type="importmap">` is injected into `<head>` mapping every
|
|
127
|
+
`coschunk-*` bare specifier to its resolved URL.
|
|
128
|
+
5. The entry point is imported via its bare specifier inside a `setTimeout`
|
|
129
|
+
callback so the browser has time to register the import map before the
|
|
130
|
+
first module resolution occurs.
|
|
118
131
|
|
|
119
132
|
## Requirements
|
|
120
133
|
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var require_constants = __commonJS({
|
|
|
30
30
|
"use strict";
|
|
31
31
|
var WIN_SLASH = "\\\\/";
|
|
32
32
|
var WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
|
33
|
+
var DEFAULT_MAX_EXTGLOB_RECURSION = 0;
|
|
33
34
|
var DOT_LITERAL = "\\.";
|
|
34
35
|
var PLUS_LITERAL = "\\+";
|
|
35
36
|
var QMARK_LITERAL = "\\?";
|
|
@@ -80,6 +81,7 @@ var require_constants = __commonJS({
|
|
|
80
81
|
SEP: "\\"
|
|
81
82
|
};
|
|
82
83
|
var POSIX_REGEX_SOURCE = {
|
|
84
|
+
__proto__: null,
|
|
83
85
|
alnum: "a-zA-Z0-9",
|
|
84
86
|
alpha: "a-zA-Z",
|
|
85
87
|
ascii: "\\x00-\\x7F",
|
|
@@ -96,6 +98,7 @@ var require_constants = __commonJS({
|
|
|
96
98
|
xdigit: "A-Fa-f0-9"
|
|
97
99
|
};
|
|
98
100
|
module.exports = {
|
|
101
|
+
DEFAULT_MAX_EXTGLOB_RECURSION,
|
|
99
102
|
MAX_LENGTH: 1024 * 64,
|
|
100
103
|
POSIX_REGEX_SOURCE,
|
|
101
104
|
// regular expressions
|
|
@@ -646,6 +649,213 @@ var require_parse = __commonJS({
|
|
|
646
649
|
var syntaxError = (type, char) => {
|
|
647
650
|
return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
|
|
648
651
|
};
|
|
652
|
+
var splitTopLevel = (input) => {
|
|
653
|
+
const parts = [];
|
|
654
|
+
let bracket = 0;
|
|
655
|
+
let paren = 0;
|
|
656
|
+
let quote = 0;
|
|
657
|
+
let value = "";
|
|
658
|
+
let escaped = false;
|
|
659
|
+
for (const ch of input) {
|
|
660
|
+
if (escaped === true) {
|
|
661
|
+
value += ch;
|
|
662
|
+
escaped = false;
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
if (ch === "\\") {
|
|
666
|
+
value += ch;
|
|
667
|
+
escaped = true;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (ch === '"') {
|
|
671
|
+
quote = quote === 1 ? 0 : 1;
|
|
672
|
+
value += ch;
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (quote === 0) {
|
|
676
|
+
if (ch === "[") {
|
|
677
|
+
bracket++;
|
|
678
|
+
} else if (ch === "]" && bracket > 0) {
|
|
679
|
+
bracket--;
|
|
680
|
+
} else if (bracket === 0) {
|
|
681
|
+
if (ch === "(") {
|
|
682
|
+
paren++;
|
|
683
|
+
} else if (ch === ")" && paren > 0) {
|
|
684
|
+
paren--;
|
|
685
|
+
} else if (ch === "|" && paren === 0) {
|
|
686
|
+
parts.push(value);
|
|
687
|
+
value = "";
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
value += ch;
|
|
693
|
+
}
|
|
694
|
+
parts.push(value);
|
|
695
|
+
return parts;
|
|
696
|
+
};
|
|
697
|
+
var isPlainBranch = (branch) => {
|
|
698
|
+
let escaped = false;
|
|
699
|
+
for (const ch of branch) {
|
|
700
|
+
if (escaped === true) {
|
|
701
|
+
escaped = false;
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
if (ch === "\\") {
|
|
705
|
+
escaped = true;
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (/[?*+@!()[\]{}]/.test(ch)) {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return true;
|
|
713
|
+
};
|
|
714
|
+
var normalizeSimpleBranch = (branch) => {
|
|
715
|
+
let value = branch.trim();
|
|
716
|
+
let changed = true;
|
|
717
|
+
while (changed === true) {
|
|
718
|
+
changed = false;
|
|
719
|
+
if (/^@\([^\\()[\]{}|]+\)$/.test(value)) {
|
|
720
|
+
value = value.slice(2, -1);
|
|
721
|
+
changed = true;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (!isPlainBranch(value)) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
return value.replace(/\\(.)/g, "$1");
|
|
728
|
+
};
|
|
729
|
+
var hasRepeatedCharPrefixOverlap = (branches) => {
|
|
730
|
+
const values = branches.map(normalizeSimpleBranch).filter(Boolean);
|
|
731
|
+
for (let i = 0; i < values.length; i++) {
|
|
732
|
+
for (let j = i + 1; j < values.length; j++) {
|
|
733
|
+
const a = values[i];
|
|
734
|
+
const b = values[j];
|
|
735
|
+
const char = a[0];
|
|
736
|
+
if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
if (a === b || a.startsWith(b) || b.startsWith(a)) {
|
|
740
|
+
return true;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return false;
|
|
745
|
+
};
|
|
746
|
+
var parseRepeatedExtglob = (pattern, requireEnd = true) => {
|
|
747
|
+
if (pattern[0] !== "+" && pattern[0] !== "*" || pattern[1] !== "(") {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
let bracket = 0;
|
|
751
|
+
let paren = 0;
|
|
752
|
+
let quote = 0;
|
|
753
|
+
let escaped = false;
|
|
754
|
+
for (let i = 1; i < pattern.length; i++) {
|
|
755
|
+
const ch = pattern[i];
|
|
756
|
+
if (escaped === true) {
|
|
757
|
+
escaped = false;
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
if (ch === "\\") {
|
|
761
|
+
escaped = true;
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
if (ch === '"') {
|
|
765
|
+
quote = quote === 1 ? 0 : 1;
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (quote === 1) {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (ch === "[") {
|
|
772
|
+
bracket++;
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
if (ch === "]" && bracket > 0) {
|
|
776
|
+
bracket--;
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (bracket > 0) {
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
if (ch === "(") {
|
|
783
|
+
paren++;
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
if (ch === ")") {
|
|
787
|
+
paren--;
|
|
788
|
+
if (paren === 0) {
|
|
789
|
+
if (requireEnd === true && i !== pattern.length - 1) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
type: pattern[0],
|
|
794
|
+
body: pattern.slice(2, i),
|
|
795
|
+
end: i
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
var getStarExtglobSequenceOutput = (pattern) => {
|
|
802
|
+
let index = 0;
|
|
803
|
+
const chars = [];
|
|
804
|
+
while (index < pattern.length) {
|
|
805
|
+
const match = parseRepeatedExtglob(pattern.slice(index), false);
|
|
806
|
+
if (!match || match.type !== "*") {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
const branches = splitTopLevel(match.body).map((branch2) => branch2.trim());
|
|
810
|
+
if (branches.length !== 1) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
const branch = normalizeSimpleBranch(branches[0]);
|
|
814
|
+
if (!branch || branch.length !== 1) {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
chars.push(branch);
|
|
818
|
+
index += match.end + 1;
|
|
819
|
+
}
|
|
820
|
+
if (chars.length < 1) {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
const source = chars.length === 1 ? utils.escapeRegex(chars[0]) : `[${chars.map((ch) => utils.escapeRegex(ch)).join("")}]`;
|
|
824
|
+
return `${source}*`;
|
|
825
|
+
};
|
|
826
|
+
var repeatedExtglobRecursion = (pattern) => {
|
|
827
|
+
let depth = 0;
|
|
828
|
+
let value = pattern.trim();
|
|
829
|
+
let match = parseRepeatedExtglob(value);
|
|
830
|
+
while (match) {
|
|
831
|
+
depth++;
|
|
832
|
+
value = match.body.trim();
|
|
833
|
+
match = parseRepeatedExtglob(value);
|
|
834
|
+
}
|
|
835
|
+
return depth;
|
|
836
|
+
};
|
|
837
|
+
var analyzeRepeatedExtglob = (body, options) => {
|
|
838
|
+
if (options.maxExtglobRecursion === false) {
|
|
839
|
+
return { risky: false };
|
|
840
|
+
}
|
|
841
|
+
const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants.DEFAULT_MAX_EXTGLOB_RECURSION;
|
|
842
|
+
const branches = splitTopLevel(body).map((branch) => branch.trim());
|
|
843
|
+
if (branches.length > 1) {
|
|
844
|
+
if (branches.some((branch) => branch === "") || branches.some((branch) => /^[*?]+$/.test(branch)) || hasRepeatedCharPrefixOverlap(branches)) {
|
|
845
|
+
return { risky: true };
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
for (const branch of branches) {
|
|
849
|
+
const safeOutput = getStarExtglobSequenceOutput(branch);
|
|
850
|
+
if (safeOutput) {
|
|
851
|
+
return { risky: true, safeOutput };
|
|
852
|
+
}
|
|
853
|
+
if (repeatedExtglobRecursion(branch) > max) {
|
|
854
|
+
return { risky: true };
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return { risky: false };
|
|
858
|
+
};
|
|
649
859
|
var parse = (input, options) => {
|
|
650
860
|
if (typeof input !== "string") {
|
|
651
861
|
throw new TypeError("Expected a string");
|
|
@@ -776,6 +986,8 @@ var require_parse = __commonJS({
|
|
|
776
986
|
token.prev = prev;
|
|
777
987
|
token.parens = state.parens;
|
|
778
988
|
token.output = state.output;
|
|
989
|
+
token.startIndex = state.index;
|
|
990
|
+
token.tokensIndex = tokens.length;
|
|
779
991
|
const output = (opts.capture ? "(" : "") + token.open;
|
|
780
992
|
increment("parens");
|
|
781
993
|
push({ type, value: value2, output: state.output ? "" : ONE_CHAR });
|
|
@@ -783,6 +995,26 @@ var require_parse = __commonJS({
|
|
|
783
995
|
extglobs.push(token);
|
|
784
996
|
};
|
|
785
997
|
const extglobClose = (token) => {
|
|
998
|
+
const literal = input.slice(token.startIndex, state.index + 1);
|
|
999
|
+
const body = input.slice(token.startIndex + 2, state.index);
|
|
1000
|
+
const analysis = analyzeRepeatedExtglob(body, opts);
|
|
1001
|
+
if ((token.type === "plus" || token.type === "star") && analysis.risky) {
|
|
1002
|
+
const safeOutput = analysis.safeOutput ? (token.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0;
|
|
1003
|
+
const open = tokens[token.tokensIndex];
|
|
1004
|
+
open.type = "text";
|
|
1005
|
+
open.value = literal;
|
|
1006
|
+
open.output = safeOutput || utils.escapeRegex(literal);
|
|
1007
|
+
for (let i = token.tokensIndex + 1; i < tokens.length; i++) {
|
|
1008
|
+
tokens[i].value = "";
|
|
1009
|
+
tokens[i].output = "";
|
|
1010
|
+
delete tokens[i].suffix;
|
|
1011
|
+
}
|
|
1012
|
+
state.output = token.output + open.output;
|
|
1013
|
+
state.backtrack = true;
|
|
1014
|
+
push({ type: "paren", extglob: true, value, output: "" });
|
|
1015
|
+
decrement("parens");
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
786
1018
|
let output = token.close + (opts.capture ? ")" : "");
|
|
787
1019
|
let rest;
|
|
788
1020
|
if (token.type === "negate") {
|
|
@@ -1626,6 +1858,22 @@ function cosPlugin(options = {}) {
|
|
|
1626
1858
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
1627
1859
|
const loaderPath = path.resolve(__dirname, "./loader.js");
|
|
1628
1860
|
let config;
|
|
1861
|
+
const cwdRequire = createRequire(path.join(process.cwd(), "index.js"));
|
|
1862
|
+
const esbuildRequire = createRequire(import.meta.url);
|
|
1863
|
+
const esbuild = esbuildRequire("esbuild");
|
|
1864
|
+
const include = options.include || ["**/*"];
|
|
1865
|
+
const includeArray = Array.isArray(include) ? include : [include];
|
|
1866
|
+
function getPackageName(item) {
|
|
1867
|
+
if (typeof item === "string") {
|
|
1868
|
+
return item.startsWith("vendor-") ? item.replace("vendor-", "") : item;
|
|
1869
|
+
}
|
|
1870
|
+
if (item instanceof RegExp) {
|
|
1871
|
+
const source = item.source;
|
|
1872
|
+
const match = source.match(/vendor-([a-zA-Z0-9-@/]+?)(?:[-._]|\/|$)/);
|
|
1873
|
+
return match ? match[1] : null;
|
|
1874
|
+
}
|
|
1875
|
+
return null;
|
|
1876
|
+
}
|
|
1629
1877
|
return {
|
|
1630
1878
|
name: "vite-plugin-cos",
|
|
1631
1879
|
apply: "build",
|
|
@@ -1635,31 +1883,19 @@ function cosPlugin(options = {}) {
|
|
|
1635
1883
|
config2.build.rollupOptions = config2.build.rollupOptions || {};
|
|
1636
1884
|
const external = config2.build.rollupOptions.external || [];
|
|
1637
1885
|
const externalArray = Array.isArray(external) ? external : typeof external === "string" ? [external] : [];
|
|
1638
|
-
const include = options.include || ["**/*"];
|
|
1639
|
-
const includeArray = Array.isArray(include) ? include : [include];
|
|
1640
|
-
const require2 = createRequire(path.join(process.cwd(), "index.js"));
|
|
1641
|
-
function getPackageName(item) {
|
|
1642
|
-
if (typeof item === "string") {
|
|
1643
|
-
return item.startsWith("vendor-") ? item.replace("vendor-", "") : item;
|
|
1644
|
-
}
|
|
1645
|
-
if (item instanceof RegExp) {
|
|
1646
|
-
const source = item.source;
|
|
1647
|
-
const match = source.match(/vendor-([a-zA-Z0-9-@/]+?)(?:[-._]|\/|$)/);
|
|
1648
|
-
return match ? match[1] : null;
|
|
1649
|
-
}
|
|
1650
|
-
return null;
|
|
1651
|
-
}
|
|
1652
1886
|
for (const item of includeArray) {
|
|
1653
1887
|
const pkgName = getPackageName(item);
|
|
1654
1888
|
if (pkgName) {
|
|
1655
1889
|
try {
|
|
1656
|
-
|
|
1657
|
-
const
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1890
|
+
cwdRequire.resolve(pkgName);
|
|
1891
|
+
const externalPatterns = [pkgName, `${pkgName}/*`];
|
|
1892
|
+
for (const pattern of externalPatterns) {
|
|
1893
|
+
if (!externalArray.includes(pattern)) {
|
|
1894
|
+
console.log(
|
|
1895
|
+
`COS Plugin: [MAGIC] Externalizing pattern "${pattern}" (matched from ${item})`
|
|
1896
|
+
);
|
|
1897
|
+
externalArray.push(pattern);
|
|
1898
|
+
}
|
|
1663
1899
|
}
|
|
1664
1900
|
} catch (e) {
|
|
1665
1901
|
}
|
|
@@ -1674,7 +1910,7 @@ function cosPlugin(options = {}) {
|
|
|
1674
1910
|
order: "post",
|
|
1675
1911
|
handler(html) {
|
|
1676
1912
|
return html.replace(
|
|
1677
|
-
/<script\s+[^>]*type=["']module["'][^>]*src=["'][^"']
|
|
1913
|
+
/<script\s+[^>]*type=["']module["'][^>]*src=["'][^"']+["'][^>]*><\/script>/gi,
|
|
1678
1914
|
"<!-- Entry script disabled by COS Plugin -->"
|
|
1679
1915
|
);
|
|
1680
1916
|
}
|
|
@@ -1682,7 +1918,7 @@ function cosPlugin(options = {}) {
|
|
|
1682
1918
|
async generateBundle(_options, bundle) {
|
|
1683
1919
|
const managedChunks = {};
|
|
1684
1920
|
let mainChunk = null;
|
|
1685
|
-
|
|
1921
|
+
const htmlAssets = [];
|
|
1686
1922
|
for (const fileName in bundle) {
|
|
1687
1923
|
const chunk = bundle[fileName];
|
|
1688
1924
|
if (chunk.type === "chunk") {
|
|
@@ -1698,61 +1934,45 @@ function cosPlugin(options = {}) {
|
|
|
1698
1934
|
managedChunks[fileName] = chunk;
|
|
1699
1935
|
}
|
|
1700
1936
|
}
|
|
1701
|
-
if (fileName
|
|
1702
|
-
|
|
1937
|
+
if (fileName.endsWith(".html") && chunk.type === "asset") {
|
|
1938
|
+
htmlAssets.push(chunk);
|
|
1703
1939
|
}
|
|
1704
1940
|
}
|
|
1705
1941
|
const externalToFileName = {};
|
|
1706
|
-
const require2 = createRequire(path.join(process.cwd(), "index.js"));
|
|
1707
|
-
const include = options.include || ["**/*"];
|
|
1708
|
-
const includeArray = Array.isArray(include) ? include : [include];
|
|
1709
|
-
function getPackageName(item) {
|
|
1710
|
-
if (typeof item === "string") {
|
|
1711
|
-
return item.startsWith("vendor-") ? item.replace("vendor-", "") : item;
|
|
1712
|
-
}
|
|
1713
|
-
if (item instanceof RegExp) {
|
|
1714
|
-
const source = item.source;
|
|
1715
|
-
const match = source.match(/vendor-([a-zA-Z0-9-@/]+?)(?:[-._]|\/|$)/);
|
|
1716
|
-
return match ? match[1] : null;
|
|
1717
|
-
}
|
|
1718
|
-
return null;
|
|
1719
|
-
}
|
|
1720
|
-
const magicPackages = /* @__PURE__ */ new Set();
|
|
1721
1942
|
for (const item of includeArray) {
|
|
1722
1943
|
const pkgName = getPackageName(item);
|
|
1723
|
-
if (pkgName)
|
|
1944
|
+
if (!pkgName) continue;
|
|
1945
|
+
try {
|
|
1946
|
+
let pkgPath = "";
|
|
1724
1947
|
try {
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
for (const chunk of allChunks) {
|
|
1736
|
-
const allImports = [...chunk.imports, ...chunk.dynamicImports];
|
|
1737
|
-
for (const specifier of allImports) {
|
|
1738
|
-
for (const pkgName of magicPackages) {
|
|
1739
|
-
if (specifier === pkgName || specifier.startsWith(`${pkgName}/`)) {
|
|
1740
|
-
discoveredSpecifiers.add(specifier);
|
|
1741
|
-
break;
|
|
1948
|
+
const mainPath = cwdRequire.resolve(pkgName);
|
|
1949
|
+
let currentDir = path.dirname(mainPath);
|
|
1950
|
+
let pkgJsonPath = "";
|
|
1951
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
1952
|
+
const candidate = path.join(currentDir, "package.json");
|
|
1953
|
+
if (fs.existsSync(candidate)) {
|
|
1954
|
+
pkgJsonPath = candidate;
|
|
1955
|
+
break;
|
|
1956
|
+
}
|
|
1957
|
+
currentDir = path.dirname(currentDir);
|
|
1742
1958
|
}
|
|
1959
|
+
if (pkgJsonPath) {
|
|
1960
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
1961
|
+
const pkgDir = path.dirname(pkgJsonPath);
|
|
1962
|
+
if (pkgJson.module) {
|
|
1963
|
+
pkgPath = path.resolve(pkgDir, pkgJson.module);
|
|
1964
|
+
} else if (pkgJson.exports?.["."]?.import) {
|
|
1965
|
+
pkgPath = path.resolve(pkgDir, pkgJson.exports["."].import);
|
|
1966
|
+
} else if (pkgJson.exports?.import) {
|
|
1967
|
+
pkgPath = path.resolve(pkgDir, pkgJson.exports.import);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
if (!pkgPath) {
|
|
1971
|
+
pkgPath = mainPath;
|
|
1972
|
+
}
|
|
1973
|
+
} catch (e) {
|
|
1974
|
+
pkgPath = cwdRequire.resolve(pkgName);
|
|
1743
1975
|
}
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
const specifierToCode = {};
|
|
1747
|
-
for (const specifier of discoveredSpecifiers) {
|
|
1748
|
-
try {
|
|
1749
|
-
const pkgPath = require2.resolve(specifier);
|
|
1750
|
-
const pkgName = Array.from(magicPackages).find((p) => specifier === p || specifier.startsWith(`${p}/`));
|
|
1751
|
-
const otherSpecifiersFromSamePkg = Array.from(discoveredSpecifiers).filter(
|
|
1752
|
-
(s) => s !== specifier && (s === pkgName || s.startsWith(`${pkgName}/`))
|
|
1753
|
-
);
|
|
1754
|
-
const esbuildRequire = createRequire(import.meta.url);
|
|
1755
|
-
const esbuild = esbuildRequire("esbuild");
|
|
1756
1976
|
const buildResult = await esbuild.build({
|
|
1757
1977
|
entryPoints: [pkgPath],
|
|
1758
1978
|
bundle: true,
|
|
@@ -1760,32 +1980,19 @@ function cosPlugin(options = {}) {
|
|
|
1760
1980
|
minify: true,
|
|
1761
1981
|
platform: "browser",
|
|
1762
1982
|
write: false,
|
|
1983
|
+
// Don't write to disk
|
|
1763
1984
|
target: "esnext",
|
|
1764
|
-
//
|
|
1765
|
-
external: otherSpecifiersFromSamePkg,
|
|
1985
|
+
// Neutralize environment
|
|
1766
1986
|
define: {
|
|
1767
1987
|
"process.env.NODE_ENV": '"production"'
|
|
1768
1988
|
}
|
|
1769
1989
|
});
|
|
1770
|
-
|
|
1771
|
-
for (const otherSpec of otherSpecifiersFromSamePkg) {
|
|
1772
|
-
const escapedOtherSpec = otherSpec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1773
|
-
const bareSpecifier = `coschunk-${otherSpec.replace(/[/@]/g, "-")}`;
|
|
1774
|
-
const staticRegex = new RegExp(`(import|export)\\b\\s*((?:(?!\\bimport\\b|\\bexport\\b)[\\s\\S])*?\\bfrom\\b\\s*)?['"]${escapedOtherSpec}['"]\\s*;?`, "g");
|
|
1775
|
-
code = code.replace(staticRegex, (match, keyword, fromPart) => {
|
|
1776
|
-
return `${keyword}${fromPart ? " " + fromPart : " "}"${bareSpecifier}";`;
|
|
1777
|
-
});
|
|
1778
|
-
const dynamicRegex = new RegExp(`import\\s*\\(\\s*['"]${escapedOtherSpec}['"]\\s*\\)`, "g");
|
|
1779
|
-
code = code.replace(dynamicRegex, () => `import("${bareSpecifier}")`);
|
|
1780
|
-
}
|
|
1781
|
-
specifierToCode[specifier] = code;
|
|
1782
|
-
const content = Buffer.from(code);
|
|
1990
|
+
const content = buildResult.outputFiles[0].contents;
|
|
1783
1991
|
const hash = crypto.createHash("sha256").update(content).digest("hex");
|
|
1784
1992
|
const ext = ".js";
|
|
1785
|
-
const safeSpecifier = specifier.replace(/[/@]/g, "-").replace(/\.js$/, "");
|
|
1786
1993
|
const fileName = path.join(
|
|
1787
1994
|
config.build.assetsDir,
|
|
1788
|
-
`${
|
|
1995
|
+
`${pkgName}-${hash.slice(0, 8)}${ext}`
|
|
1789
1996
|
);
|
|
1790
1997
|
this.emitFile({
|
|
1791
1998
|
type: "asset",
|
|
@@ -1795,27 +2002,21 @@ function cosPlugin(options = {}) {
|
|
|
1795
2002
|
managedChunks[fileName] = {
|
|
1796
2003
|
type: "chunk",
|
|
1797
2004
|
fileName,
|
|
1798
|
-
code,
|
|
1799
|
-
name:
|
|
2005
|
+
code: Buffer.from(content).toString("utf-8"),
|
|
2006
|
+
name: item
|
|
1800
2007
|
};
|
|
1801
|
-
externalToFileName[
|
|
2008
|
+
externalToFileName[pkgName] = fileName;
|
|
1802
2009
|
} catch (e) {
|
|
1803
|
-
console.error(`COS Plugin: Failed to bundle magic specifier "${specifier}"`, e);
|
|
1804
2010
|
}
|
|
1805
2011
|
}
|
|
1806
2012
|
if (mainChunk) {
|
|
1807
|
-
const
|
|
1808
|
-
for (const specifier in externalToFileName) {
|
|
1809
|
-
const bareSpecifier = `coschunk-${specifier.replace(/[/@]/g, "-")}`;
|
|
1810
|
-
magicMapping[bareSpecifier] = externalToFileName[specifier];
|
|
1811
|
-
}
|
|
1812
|
-
const allChunks2 = Object.values(bundle).filter(
|
|
2013
|
+
const allChunks = Object.values(bundle).filter(
|
|
1813
2014
|
(c) => c.type === "chunk"
|
|
1814
2015
|
);
|
|
1815
2016
|
const managedChunkNames = new Set(Object.keys(managedChunks));
|
|
1816
2017
|
const unmanagedDependencies = /* @__PURE__ */ new Set();
|
|
1817
2018
|
const base = config.base.endsWith("/") ? config.base : config.base + "/";
|
|
1818
|
-
for (const targetChunk of
|
|
2019
|
+
for (const targetChunk of allChunks) {
|
|
1819
2020
|
const importerDir = path.dirname(targetChunk.fileName);
|
|
1820
2021
|
const deps = [...targetChunk.imports, ...targetChunk.dynamicImports];
|
|
1821
2022
|
for (const depFileName of deps) {
|
|
@@ -1848,7 +2049,8 @@ function cosPlugin(options = {}) {
|
|
|
1848
2049
|
}
|
|
1849
2050
|
}
|
|
1850
2051
|
for (const pkgName in externalToFileName) {
|
|
1851
|
-
const
|
|
2052
|
+
const fileName = externalToFileName[pkgName];
|
|
2053
|
+
const bareSpecifier = `coschunk-${fileName.replace(/\//g, "-")}`;
|
|
1852
2054
|
const staticPattern = `(import|export)\\b\\s*((?:(?!\\bimport\\b|\\bexport\\b)[\\s\\S])*?\\bfrom\\b\\s*)?['"]${pkgName}['"]\\s*;?`;
|
|
1853
2055
|
const staticRegex = new RegExp(staticPattern, "g");
|
|
1854
2056
|
targetChunk.code = targetChunk.code.replace(
|
|
@@ -1868,8 +2070,7 @@ function cosPlugin(options = {}) {
|
|
|
1868
2070
|
const manifest = {
|
|
1869
2071
|
base,
|
|
1870
2072
|
entry: mainChunk.fileName,
|
|
1871
|
-
chunks: {}
|
|
1872
|
-
magic: magicMapping
|
|
2073
|
+
chunks: {}
|
|
1873
2074
|
};
|
|
1874
2075
|
for (const fileName in managedChunks) {
|
|
1875
2076
|
const chunk = managedChunks[fileName];
|
|
@@ -1880,13 +2081,19 @@ function cosPlugin(options = {}) {
|
|
|
1880
2081
|
unmanagedDependencies.add(mainChunk.fileName);
|
|
1881
2082
|
}
|
|
1882
2083
|
manifest.unmanaged = Array.from(unmanagedDependencies);
|
|
1883
|
-
if (
|
|
2084
|
+
if (htmlAssets.length > 0) {
|
|
2085
|
+
let loaderCode;
|
|
1884
2086
|
try {
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
2087
|
+
loaderCode = fs.readFileSync(loaderPath, "utf-8");
|
|
2088
|
+
} catch (e) {
|
|
2089
|
+
console.error("COS Plugin: Failed to read loader.js", e);
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
loaderCode = loaderCode.replace(
|
|
2093
|
+
"__COS_MANIFEST__",
|
|
2094
|
+
JSON.stringify(manifest, null, 2)
|
|
2095
|
+
);
|
|
2096
|
+
for (const htmlAsset of htmlAssets) {
|
|
1890
2097
|
let htmlSource = htmlAsset.source;
|
|
1891
2098
|
htmlSource = htmlSource.replace(
|
|
1892
2099
|
/<link\s+[^>]*rel=["']modulepreload["'][^>]*>/gi,
|
|
@@ -1897,8 +2104,6 @@ function cosPlugin(options = {}) {
|
|
|
1897
2104
|
() => `<head>
|
|
1898
2105
|
<script id="cos-loader">${loaderCode}</script>`
|
|
1899
2106
|
);
|
|
1900
|
-
} catch (e) {
|
|
1901
|
-
console.error("COS Plugin: Failed to read loader.js", e);
|
|
1902
2107
|
}
|
|
1903
2108
|
}
|
|
1904
2109
|
}
|
package/dist/loader.js
CHANGED
|
@@ -111,18 +111,6 @@
|
|
|
111
111
|
// Inject Import Map
|
|
112
112
|
const script = document.createElement('script');
|
|
113
113
|
script.type = 'importmap';
|
|
114
|
-
|
|
115
|
-
// Add magic specifier mappings (e.g. coschunk-three -> assets/three-*.js)
|
|
116
|
-
if (manifest.magic) {
|
|
117
|
-
for (const [specifier, fileName] of Object.entries(manifest.magic)) {
|
|
118
|
-
const targetSpecifier = `coschunk-${fileName.replace(/\//g, '-')}`;
|
|
119
|
-
const targetUrl = importMap.imports[targetSpecifier];
|
|
120
|
-
if (targetUrl) {
|
|
121
|
-
importMap.imports[specifier] = targetUrl;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
114
|
script.textContent = JSON.stringify(importMap, null, 2);
|
|
127
115
|
document.head.appendChild(script);
|
|
128
116
|
|
|
@@ -132,13 +120,14 @@
|
|
|
132
120
|
// through the import map and can find other managed chunks.
|
|
133
121
|
const entrySpecifier = `coschunk-${mainEntry.replace(/\//g, '-')}`;
|
|
134
122
|
|
|
135
|
-
//
|
|
136
|
-
// import map before
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
123
|
+
// Yield to the event loop once so the browser registers the dynamically
|
|
124
|
+
// injected import map before the first module import.
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
126
|
+
try {
|
|
127
|
+
await import(entrySpecifier);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error('COS Loader: Failed to start app', err);
|
|
130
|
+
}
|
|
142
131
|
} catch (err) {
|
|
143
132
|
console.error('COS Loader: Initialization failed', err);
|
|
144
133
|
}
|
package/loader.js
CHANGED
|
@@ -111,18 +111,6 @@
|
|
|
111
111
|
// Inject Import Map
|
|
112
112
|
const script = document.createElement('script');
|
|
113
113
|
script.type = 'importmap';
|
|
114
|
-
|
|
115
|
-
// Add magic specifier mappings (e.g. coschunk-three -> assets/three-*.js)
|
|
116
|
-
if (manifest.magic) {
|
|
117
|
-
for (const [specifier, fileName] of Object.entries(manifest.magic)) {
|
|
118
|
-
const targetSpecifier = `coschunk-${fileName.replace(/\//g, '-')}`;
|
|
119
|
-
const targetUrl = importMap.imports[targetSpecifier];
|
|
120
|
-
if (targetUrl) {
|
|
121
|
-
importMap.imports[specifier] = targetUrl;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
114
|
script.textContent = JSON.stringify(importMap, null, 2);
|
|
127
115
|
document.head.appendChild(script);
|
|
128
116
|
|
|
@@ -132,13 +120,14 @@
|
|
|
132
120
|
// through the import map and can find other managed chunks.
|
|
133
121
|
const entrySpecifier = `coschunk-${mainEntry.replace(/\//g, '-')}`;
|
|
134
122
|
|
|
135
|
-
//
|
|
136
|
-
// import map before
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
123
|
+
// Yield to the event loop once so the browser registers the dynamically
|
|
124
|
+
// injected import map before the first module import.
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
126
|
+
try {
|
|
127
|
+
await import(entrySpecifier);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error('COS Loader: Failed to start app', err);
|
|
130
|
+
}
|
|
142
131
|
} catch (err) {
|
|
143
132
|
console.error('COS Loader: Initialization failed', err);
|
|
144
133
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-cross-origin-storage",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Vite plugin to load chunks from Cross-Origin Storage",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vite",
|
|
@@ -37,23 +37,34 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsup index.ts --format esm --dts --clean && cp loader.js dist/",
|
|
39
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
|
+
"test:simple:dev": "npm run build && vite testing/simple-test",
|
|
41
|
+
"test:simple:build": "npm run build && vite build testing/simple-test",
|
|
42
|
+
"test:simple:preview": "vite preview testing/simple-test",
|
|
43
|
+
"test:react-hello-world:dev": "npm run build && vite testing/react-hello-world",
|
|
44
|
+
"test:react-hello-world:build": "npm run build && vite build testing/react-hello-world",
|
|
45
|
+
"test:react-hello-world:preview": "vite preview testing/react-hello-world",
|
|
46
|
+
"test:react-todo:dev": "npm run build && vite testing/react-todo",
|
|
47
|
+
"test:react-todo:build": "npm run build && vite build testing/react-todo",
|
|
48
|
+
"test:react-todo:preview": "vite preview --port 4174 testing/react-todo"
|
|
43
49
|
},
|
|
44
50
|
"peerDependencies": {
|
|
45
|
-
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
51
|
+
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
46
52
|
},
|
|
47
53
|
"devDependencies": {
|
|
48
54
|
"@rollup/pluginutils": "^5.3.0",
|
|
49
|
-
"@types/node": "^25.2
|
|
50
|
-
"
|
|
51
|
-
"
|
|
55
|
+
"@types/node": "^25.6.2",
|
|
56
|
+
"@types/react": "^19.2.14",
|
|
57
|
+
"@types/react-dom": "^19.2.3",
|
|
58
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
59
|
+
"react": "^19.2.6",
|
|
60
|
+
"react-dom": "^19.2.6",
|
|
61
|
+
"rollup": "^4.60.3",
|
|
62
|
+
"three": "^0.184.0",
|
|
52
63
|
"tsup": "^8.5.1",
|
|
53
|
-
"typescript": "^
|
|
54
|
-
"vite": "^
|
|
64
|
+
"typescript": "^6.0.3",
|
|
65
|
+
"vite": "^8.0.11"
|
|
55
66
|
},
|
|
56
67
|
"dependencies": {
|
|
57
|
-
"esbuild": "^0.
|
|
68
|
+
"esbuild": "^0.28.0"
|
|
58
69
|
}
|
|
59
70
|
}
|