vite-add-cdn-script 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +13 -23
- package/dist/index.js +328 -74
- package/dist/index.umd.cjs +24 -26
- package/index.d.ts +0 -2
- package/package.json +14 -4
package/README.md
CHANGED
@@ -17,13 +17,16 @@ vite.config.ts
|
|
17
17
|
```typescript
|
18
18
|
import { defineConfig } from "vite";
|
19
19
|
import react from "@vitejs/plugin-react";
|
20
|
-
import viteAddCdnScript from "
|
20
|
+
import viteAddCdnScript from "vite-add-cdn-script";
|
21
21
|
import externalGlobals from "rollup-plugin-external-globals";
|
22
22
|
|
23
|
-
// 需要使用cdn
|
23
|
+
// 需要使用cdn库,按顺序添加,如react-router-dom需要依赖react、@remix-run/router、react-router,因此需要放在最后
|
24
24
|
const externals = {
|
25
25
|
react: "React",
|
26
26
|
"react-dom": "ReactDOM",
|
27
|
+
"@remix-run/router": "@remix-run/router",
|
28
|
+
"react-router": "react-router",
|
29
|
+
"react-router-dom": "ReactRouterDOM",
|
27
30
|
};
|
28
31
|
|
29
32
|
export default defineConfig({
|
@@ -45,6 +48,7 @@ import react from "@vitejs/plugin-react";
|
|
45
48
|
import viteAddCdnScript from "../vite-add-cdn-script/lib/main";
|
46
49
|
import externalGlobals from "rollup-plugin-external-globals";
|
47
50
|
|
51
|
+
// 需要使用cdn的模块,会按顺序插入脚本,如
|
48
52
|
const externals = {
|
49
53
|
react: "React",
|
50
54
|
"react-dom": "ReactDOM",
|
@@ -73,27 +77,13 @@ export default defineConfig({
|
|
73
77
|
|
74
78
|
options
|
75
79
|
|
76
|
-
| 参数 | 解析 | 类型 | 默认值
|
77
|
-
| ------------ | ------------------- | --------------------------- |
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
| defaultCdns | 默认使用 cdn 的顺序 | string[] | ["bootcdn", "bytedance", "unpkg", "cdnjs", "jsdelivr", "staticfile"] |
|
80
|
+
| 参数 | 解析 | 类型 | 默认值 |
|
81
|
+
| ------------ | ------------------- | --------------------------- | --------------------- |
|
82
|
+
| customScript | 自定义 cdn 脚本 | { [*key*: string]: string } | 无 |
|
83
|
+
| retryTimes | 重试次数 | number | 3 |
|
84
|
+
| defaultCdns | 默认使用 cdn 的顺序 | string[] | ["jsdelivr", "unpkg"] |
|
82
85
|
|
83
86
|
## 注意事项
|
84
87
|
|
85
|
-
|
86
|
-
|
87
|
-
目前做了适配的非 xxx.min.js 适配的库如下,如果你有合适的 cdn 源或者,需要适配的库,欢迎提交 issue 或者 pr!!!
|
88
|
-
|
89
|
-
```
|
90
|
-
{
|
91
|
-
react: "umd/react.production.min.js",
|
92
|
-
"react-dom": "umd/react-dom.production.min.js",
|
93
|
-
"react-router-dom": "react-router-dom.production.min.js",
|
94
|
-
mobx: "dist/mobx.umd.production.min.js",
|
95
|
-
"mobx-react": "/dist/mobxreact.umd.production.min.js",
|
96
|
-
vue: "/dist/vue.global.prod.js",
|
97
|
-
"vue-router": "/dist/vue-router.global.prod.js",
|
98
|
-
}
|
99
|
-
```
|
88
|
+
- 接入了各大 cdn 的 api 接口进行请求,默认会保存一份 cdn 的缓存在你的根目录中`.cdn-cache.json`。如果发现缓存的资源有问题可以删除该文件,然后重新执行打包流程。
|
89
|
+
- 按顺序添加cdn,如react-router-dom需要依赖react、@remix-run/router、react-router,因此需要放在最后。
|
package/dist/index.js
CHANGED
@@ -1,90 +1,344 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
1
|
+
var D = Object.defineProperty;
|
2
|
+
var F = (n, t, e) => t in n ? D(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e;
|
3
|
+
var y = (n, t, e) => (F(n, typeof t != "symbol" ? t + "" : t, e), e);
|
4
|
+
import U from "node:path";
|
5
|
+
import w from "node:fs";
|
6
|
+
import b from "node:https";
|
7
|
+
class E {
|
8
|
+
constructor() {
|
9
|
+
y(this, "cdnCache", {});
|
10
|
+
y(this, "cdnCachePath", "");
|
11
|
+
this.cdnCachePath = U.resolve(process.cwd(), "./.cdn-cache.json");
|
12
|
+
}
|
13
|
+
// 初始化cdn缓存
|
14
|
+
async init() {
|
15
|
+
try {
|
16
|
+
const t = await w.readFileSync(this.cdnCachePath, "utf-8");
|
17
|
+
this.cdnCache = JSON.parse(t);
|
18
|
+
} catch {
|
19
|
+
console.log("cdn缓存文件不存在,创建缓存文件"), this.cdnCache = {}, await w.writeFileSync(this.cdnCachePath, "", "utf-8");
|
20
|
+
}
|
21
|
+
}
|
22
|
+
/**
|
23
|
+
* 获取cdn缓存
|
24
|
+
* @param packageName 包名
|
25
|
+
* @param version 版本
|
26
|
+
*/
|
27
|
+
getCdnCache(t, e) {
|
28
|
+
var r;
|
29
|
+
return (r = this.cdnCache[t]) == null ? void 0 : r[e];
|
30
|
+
}
|
31
|
+
/**
|
32
|
+
* 设置cdn缓存
|
33
|
+
* @param packageName 包名
|
34
|
+
* @param version 版本
|
35
|
+
* @param urls 地址列表
|
36
|
+
*/
|
37
|
+
setCdnCache(t, e, r) {
|
38
|
+
this.cdnCache[t] ? this.cdnCache[t][e] = r : this.cdnCache[t] = {
|
39
|
+
[e]: r
|
40
|
+
};
|
41
|
+
}
|
42
|
+
/**
|
43
|
+
* 更新cdn缓存
|
44
|
+
*/
|
45
|
+
async save() {
|
46
|
+
await w.writeFileSync(this.cdnCachePath, JSON.stringify(this.cdnCache), "utf-8");
|
47
|
+
}
|
48
|
+
}
|
49
|
+
let $;
|
50
|
+
const L = async () => ($ || ($ = new E(), await $.init()), $), f = {
|
51
|
+
//get请求封装
|
52
|
+
get: (n, t, e) => new Promise((r, s) => {
|
53
|
+
try {
|
54
|
+
b.get(n, (i) => {
|
55
|
+
let o = "";
|
56
|
+
i.on("data", (c) => {
|
57
|
+
o += c;
|
58
|
+
}), i.on("end", () => {
|
59
|
+
t == null || t(o), r(o);
|
60
|
+
});
|
61
|
+
});
|
62
|
+
} catch (i) {
|
63
|
+
e == null || e(i), s(i);
|
64
|
+
}
|
65
|
+
})
|
66
|
+
}, N = async (n) => {
|
67
|
+
const t = /^(https:\/\/.*\d+\.\d+\.\d+\/).+?\.js$/;
|
68
|
+
if (t.test(n)) {
|
69
|
+
const e = n.replace(t, (r, s) => `${s}package.json`);
|
70
|
+
return JSON.parse(await f.get(e));
|
71
|
+
} else
|
72
|
+
throw new Error(`${n} 不是正确的url`);
|
73
|
+
}, I = (n, t) => {
|
74
|
+
var e, r;
|
75
|
+
return ((e = n.dependencies) == null ? void 0 : e[t]) || ((r = n.devDependencies) == null ? void 0 : r[t]);
|
76
|
+
}, k = (n) => n.reduce((t, e) => (e.type === "file" ? t.push({ name: e.path }) : e.files && t.push(...k(e.files)), t), []), x = {
|
77
|
+
jsdelivr: {
|
78
|
+
getFileList: (n, t) => new Promise((e, r) => {
|
79
|
+
f.get(
|
80
|
+
`https://data.jsdelivr.com/v1/stats/packages/npm/${n}@${t}/files`,
|
81
|
+
(s) => {
|
82
|
+
const i = JSON.parse(s);
|
83
|
+
if (i.length === 0) {
|
84
|
+
r(new Error(`${n}@${t} not found`));
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
e({ fileList: i });
|
88
|
+
},
|
89
|
+
(s) => {
|
90
|
+
r(s);
|
91
|
+
}
|
92
|
+
);
|
93
|
+
}),
|
94
|
+
// 拼接url
|
95
|
+
getUrl: (n, t, e) => `https://cdn.jsdelivr.net/npm/${n}@${t}${e}`
|
96
|
+
},
|
97
|
+
unpkg: {
|
98
|
+
getFileList: (n, t) => new Promise((e, r) => {
|
99
|
+
f.get(
|
100
|
+
`https://unpkg.com/${n}@${t}/?meta`,
|
101
|
+
(s) => {
|
102
|
+
const i = JSON.parse(s);
|
103
|
+
e({ fileList: k(i.files || []) });
|
104
|
+
},
|
105
|
+
(s) => {
|
106
|
+
r(s);
|
107
|
+
}
|
108
|
+
);
|
109
|
+
}),
|
110
|
+
getUrl: (n, t, e) => `https://unpkg.com/${n}@${t}${e}`
|
111
|
+
},
|
112
|
+
bootcdn: {
|
113
|
+
getFileList: (n, t) => new Promise((e, r) => {
|
114
|
+
f.get(
|
115
|
+
`https://api.bootcdn.cn/libraries/${n}`,
|
116
|
+
(s) => {
|
117
|
+
const i = JSON.parse(s);
|
118
|
+
if (i.length === 0) {
|
119
|
+
r(new Error(`${n} not found in bootcdn`));
|
120
|
+
return;
|
121
|
+
}
|
122
|
+
const o = i[0], a = o.assets.find((d) => d.version === t);
|
123
|
+
if (!a) {
|
124
|
+
r(new Error(`${n}@${t} not found in ${o.name}`));
|
125
|
+
return;
|
126
|
+
}
|
127
|
+
const l = a.files.map((d) => ({
|
128
|
+
name: "/" + d
|
129
|
+
}));
|
130
|
+
e({ fileList: l, recommendFileName: o.filename });
|
131
|
+
},
|
132
|
+
(s) => {
|
133
|
+
r(s);
|
134
|
+
}
|
135
|
+
);
|
136
|
+
}),
|
137
|
+
getUrl: (n, t, e) => `https://cdn.bootcdn.net/ajax/libs/${n}/${t}${e}`
|
138
|
+
},
|
139
|
+
cdnjs: {
|
140
|
+
getFileList: (n, t) => new Promise((e, r) => {
|
141
|
+
f.get(
|
142
|
+
`https://api.cdnjs.com/libraries/${n}/${t}`,
|
143
|
+
(s) => {
|
144
|
+
const i = JSON.parse(s);
|
145
|
+
if (i.error) {
|
146
|
+
r(new Error(`cdnjs: ${n}@${t} not found`));
|
147
|
+
return;
|
148
|
+
}
|
149
|
+
e({
|
150
|
+
fileList: i.rawFiles.map((o) => ({
|
151
|
+
name: "/" + o
|
152
|
+
}))
|
153
|
+
});
|
154
|
+
},
|
155
|
+
(s) => {
|
156
|
+
r(s);
|
157
|
+
}
|
158
|
+
);
|
159
|
+
}),
|
160
|
+
getUrl: (n, t, e) => `https://cdnjs.cloudflare.com/ajax/libs/${n}/${t}${e}`
|
161
|
+
}
|
162
|
+
}, R = async (n, t, e) => {
|
163
|
+
var o;
|
164
|
+
const r = (o = t.match(/\d+(.\d+)?(.\d+)?/)) == null ? void 0 : o[0];
|
165
|
+
if (!r)
|
166
|
+
throw new Error(`${n} version ${t} is not valid`);
|
167
|
+
const s = await x[e].getFileList(n, r).catch((c) => {
|
168
|
+
throw c;
|
169
|
+
}), i = A(s, n);
|
170
|
+
if (!i)
|
171
|
+
throw new Error(`在 ${e} 中找不到 ${n}@${r} 文件,请检查包名或版本号`);
|
172
|
+
return x[e].getUrl(n, r, i);
|
173
|
+
}, A = ({ fileList: n }, t) => {
|
174
|
+
var i, o;
|
175
|
+
let e = [
|
176
|
+
`umd/${t}.production.min.js`,
|
177
|
+
/umd\/.+?\.production\.min\.js$/,
|
178
|
+
/dist\/.+?\.production\.min\.js$/,
|
179
|
+
/dist\/.+?\.umd\.min\.js$/,
|
180
|
+
`dist/${t}.prod.min.js`,
|
181
|
+
/dist\/.+?\.global.prod.min.js/,
|
182
|
+
`dist/${t}.min.js`,
|
183
|
+
/.+?\.global.prod.min.js/,
|
184
|
+
/.+?.global.prod.js/,
|
185
|
+
/lib\/.+?\.min\.js$/,
|
186
|
+
/dist\/.+?\.min\.js$/,
|
187
|
+
/index\.min\.js$/,
|
188
|
+
/index\.js$/,
|
189
|
+
/\.min\.js$/,
|
190
|
+
/\.js$/
|
191
|
+
];
|
192
|
+
const r = ["runtime", "compiler", ".esm", ".cjs", "development"].filter((c) => !t.includes(c));
|
193
|
+
let s = "";
|
194
|
+
for (let c of e)
|
195
|
+
if (c instanceof RegExp ? s = ((i = n.find((a) => c.test(a.name) && !r.some((l) => a.name.includes(l)))) == null ? void 0 : i.name) || "" : s = ((o = n.find((a) => a.name.includes(c) && !r.some((l) => a.name.includes(l)))) == null ? void 0 : o.name) || "", s)
|
196
|
+
break;
|
197
|
+
return s;
|
25
198
|
};
|
26
|
-
function
|
27
|
-
const
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
199
|
+
function M(n, t) {
|
200
|
+
const e = n.replace(/^\D/, "").split("."), r = t.replace(/^\D/, "").split("."), s = Math.max(e.length, r.length);
|
201
|
+
for (; e.length < s; )
|
202
|
+
e.push("0");
|
203
|
+
for (; r.length < s; )
|
204
|
+
r.push("0");
|
205
|
+
for (let i = 0; i < s; i++) {
|
206
|
+
const o = parseInt(e[i], 10), c = parseInt(r[i], 10);
|
207
|
+
if (o > c)
|
208
|
+
return 1;
|
209
|
+
if (o < c)
|
210
|
+
return -1;
|
211
|
+
}
|
212
|
+
return 0;
|
213
|
+
}
|
214
|
+
function V(n, t) {
|
215
|
+
for (let e in t)
|
216
|
+
Object.prototype.hasOwnProperty.call(t, e) && (n[e] ? M(n[e], t[e]) === -1 && (n[e] = t[e]) : n[e] = t[e]);
|
217
|
+
return n;
|
218
|
+
}
|
219
|
+
async function S({ external: n, packageData: t, customScript: e, defaultCdns: r }) {
|
220
|
+
let s = [], i = !1;
|
221
|
+
const o = await L();
|
222
|
+
return await Promise.all(
|
223
|
+
n.map(async (c) => {
|
224
|
+
const a = I(t, c);
|
225
|
+
if (e[c])
|
226
|
+
return {
|
227
|
+
urls: [],
|
228
|
+
key: c
|
229
|
+
};
|
230
|
+
if (!a) {
|
231
|
+
s.push(c);
|
232
|
+
return;
|
233
|
+
}
|
234
|
+
const l = o.getCdnCache(c, a);
|
235
|
+
if (l)
|
236
|
+
return {
|
237
|
+
urls: l,
|
238
|
+
key: c
|
239
|
+
};
|
240
|
+
{
|
241
|
+
i = !0;
|
242
|
+
const d = {
|
243
|
+
urls: await Promise.all(
|
244
|
+
r.map(async (h) => await R(c, a, h))
|
245
|
+
),
|
246
|
+
key: c
|
247
|
+
};
|
248
|
+
return o.setCdnCache(c, a, d.urls), d;
|
249
|
+
}
|
250
|
+
})
|
251
|
+
).then((c) => (i && o.save(), {
|
252
|
+
urls: c,
|
253
|
+
noVersionPackages: s
|
254
|
+
}));
|
255
|
+
}
|
256
|
+
function q(n) {
|
257
|
+
const { customScript: t = {}, retryTimes: e = 1, defaultCdns: r = ["jsdelivr", "unpkg"] } = n;
|
258
|
+
let s;
|
36
259
|
return {
|
37
260
|
name: "vite-add-cdn-script",
|
38
261
|
enforce: "pre",
|
39
262
|
apply: "build",
|
40
|
-
config(
|
41
|
-
|
263
|
+
config(i) {
|
264
|
+
s = i;
|
42
265
|
},
|
43
|
-
transformIndexHtml(
|
44
|
-
|
266
|
+
async transformIndexHtml(i) {
|
267
|
+
if (!r || r.length === 0)
|
268
|
+
throw new Error("defaultCdns不能为空");
|
269
|
+
const o = U.resolve(process.cwd(), "package.json");
|
45
270
|
try {
|
46
|
-
const
|
47
|
-
let
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
if (
|
74
|
-
|
271
|
+
const c = w.readFileSync(o, "utf-8"), a = JSON.parse(c), l = s.build.rollupOptions.external, d = {};
|
272
|
+
let h = "";
|
273
|
+
const { urls: C, noVersionPackages: P } = await S({
|
274
|
+
external: l,
|
275
|
+
packageData: a,
|
276
|
+
customScript: t,
|
277
|
+
defaultCdns: r
|
278
|
+
});
|
279
|
+
if (P.length > 0) {
|
280
|
+
const p = { dependencies: {} };
|
281
|
+
await Promise.all(
|
282
|
+
C.map(async (g) => {
|
283
|
+
if (!g)
|
284
|
+
return;
|
285
|
+
const { key: v, urls: O } = g, j = t[v] || O[0];
|
286
|
+
if (!j)
|
287
|
+
return;
|
288
|
+
const J = await N(j);
|
289
|
+
V(p.dependencies, J.dependencies);
|
290
|
+
})
|
291
|
+
);
|
292
|
+
const { urls: m, noVersionPackages: u } = await S({
|
293
|
+
external: P,
|
294
|
+
packageData: p,
|
295
|
+
customScript: t,
|
296
|
+
defaultCdns: r
|
297
|
+
});
|
298
|
+
if (C.push(...m), u.length > 0)
|
299
|
+
throw console.error(`找不到${u.join(",")}的版本`), new Error(`找不到${u.join(",")}的版本`);
|
300
|
+
}
|
301
|
+
return C.forEach((p) => {
|
302
|
+
if (!p)
|
303
|
+
return;
|
304
|
+
const { urls: m, key: u } = p;
|
305
|
+
if (t[u])
|
306
|
+
h += t[u];
|
75
307
|
else {
|
76
|
-
|
77
|
-
|
308
|
+
d[u] = m;
|
309
|
+
const g = m[0];
|
310
|
+
h += `<script src="${g}" type="text/javascript" crossorigin="anonymous" onerror="errorCDN(this)" data-cur="0" data-key="${u}"><\/script>
|
78
311
|
`;
|
79
312
|
}
|
80
|
-
}),
|
81
|
-
|
82
|
-
|
313
|
+
}), h = `<script>
|
314
|
+
function errorCDN(e) {
|
315
|
+
const packNameUrl = JSON.parse('${JSON.stringify(d)}');
|
316
|
+
const nextCur = parseInt(e.getAttribute("data-cur")) + 1;
|
317
|
+
if(nextCur>${e}){return;}
|
318
|
+
|
319
|
+
const key = e.getAttribute("data-key");
|
320
|
+
if(nextCur>=packNameUrl[key].length){return;}
|
321
|
+
// 新的cdn链接
|
322
|
+
const url = packNameUrl[key][nextCur]
|
323
|
+
// 克隆原标签
|
324
|
+
const tagName = e.tagName
|
325
|
+
const cdnDOM = document.createElement(tagName);
|
326
|
+
cdnDOM.setAttribute(tagName === 'SCRIPT' ?'src' : 'href', url);
|
327
|
+
Object.keys(e.dataset).forEach(_key => {
|
328
|
+
cdnDOM.setAttribute('data-'+_key, e.dataset[_key]);
|
329
|
+
})
|
330
|
+
cdnDOM.setAttribute("data-cur", nextCur.toString());
|
331
|
+
cdnDOM.setAttribute("onerror", "errorCDN(this)");
|
332
|
+
document.head.appendChild(cdnDOM);
|
333
|
+
e.remove();
|
334
|
+
}
|
335
|
+
<\/script>` + h, i = i.replace("</head>", `${h}</head>`), i;
|
336
|
+
} catch (c) {
|
337
|
+
console.error("获取dependencies出错:", c);
|
83
338
|
}
|
84
339
|
}
|
85
340
|
};
|
86
341
|
}
|
87
342
|
export {
|
88
|
-
|
89
|
-
x as npmProObj
|
343
|
+
q as default
|
90
344
|
};
|
package/dist/index.umd.cjs
CHANGED
@@ -1,26 +1,24 @@
|
|
1
|
-
(function(
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
<\/script>`;return x.forEach(t=>{if(d[t])s+=d[t];else{const f=(l.dependencies[t]?a[o]+l.dependencies[t].replace("^",""):"")+"/"+(m[t]?m[t]:`dist/${t}.min.js`),y=`${O}://${c[o]}/${t}${f}`;s+=`<script src="${y}" type="text/javascript" onerror="errorCDN(this)" data-cur="0" data-key="${t}" data-filename="${f}"><\/script>
|
26
|
-
`}}),n=n.replace("</head>",`${s}</head>`),n}catch(o){console.error("获取dependencies出错:",o)}}}}e.default=b,e.npmProObj=i,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
1
|
+
(function(d,a){typeof exports=="object"&&typeof module<"u"?module.exports=a(require("node:path"),require("node:fs"),require("node:https")):typeof define=="function"&&define.amd?define(["node:path","node:fs","node:https"],a):(d=typeof globalThis<"u"?globalThis:d||self,d.index=a(d.path,d.fs,d.https))})(this,function(d,a,f){"use strict";var V=Object.defineProperty;var T=(d,a,f)=>a in d?V(d,a,{enumerable:!0,configurable:!0,writable:!0,value:f}):d[a]=f;var P=(d,a,f)=>(T(d,typeof a!="symbol"?a+"":a,f),f);class O{constructor(){P(this,"cdnCache",{});P(this,"cdnCachePath","");this.cdnCachePath=d.resolve(process.cwd(),"./.cdn-cache.json")}async init(){try{const e=await a.readFileSync(this.cdnCachePath,"utf-8");this.cdnCache=JSON.parse(e)}catch{console.log("cdn缓存文件不存在,创建缓存文件"),this.cdnCache={},await a.writeFileSync(this.cdnCachePath,"","utf-8")}}getCdnCache(e,t){var r;return(r=this.cdnCache[e])==null?void 0:r[t]}setCdnCache(e,t,r){this.cdnCache[e]?this.cdnCache[e][t]=r:this.cdnCache[e]={[t]:r}}async save(){await a.writeFileSync(this.cdnCachePath,JSON.stringify(this.cdnCache),"utf-8")}}let w;const J=async()=>(w||(w=new O,await w.init()),w),g={get:(n,e,t)=>new Promise((r,s)=>{try{f.get(n,i=>{let o="";i.on("data",c=>{o+=c}),i.on("end",()=>{e==null||e(o),r(o)})})}catch(i){t==null||t(i),s(i)}})},D=async n=>{const e=/^(https:\/\/.*\d+\.\d+\.\d+\/).+?\.js$/;if(e.test(n)){const t=n.replace(e,(r,s)=>`${s}package.json`);return JSON.parse(await g.get(t))}else throw new Error(`${n} 不是正确的url`)},F=(n,e)=>{var t,r;return((t=n.dependencies)==null?void 0:t[e])||((r=n.devDependencies)==null?void 0:r[e])},x=n=>n.reduce((e,t)=>(t.type==="file"?e.push({name:t.path}):t.files&&e.push(...x(t.files)),e),[]),S={jsdelivr:{getFileList:(n,e)=>new Promise((t,r)=>{g.get(`https://data.jsdelivr.com/v1/stats/packages/npm/${n}@${e}/files`,s=>{const i=JSON.parse(s);if(i.length===0){r(new Error(`${n}@${e} not found`));return}t({fileList:i})},s=>{r(s)})}),getUrl:(n,e,t)=>`https://cdn.jsdelivr.net/npm/${n}@${e}${t}`},unpkg:{getFileList:(n,e)=>new Promise((t,r)=>{g.get(`https://unpkg.com/${n}@${e}/?meta`,s=>{const i=JSON.parse(s);t({fileList:x(i.files||[])})},s=>{r(s)})}),getUrl:(n,e,t)=>`https://unpkg.com/${n}@${e}${t}`},bootcdn:{getFileList:(n,e)=>new Promise((t,r)=>{g.get(`https://api.bootcdn.cn/libraries/${n}`,s=>{const i=JSON.parse(s);if(i.length===0){r(new Error(`${n} not found in bootcdn`));return}const o=i[0],u=o.assets.find(h=>h.version===e);if(!u){r(new Error(`${n}@${e} not found in ${o.name}`));return}const l=u.files.map(h=>({name:"/"+h}));t({fileList:l,recommendFileName:o.filename})},s=>{r(s)})}),getUrl:(n,e,t)=>`https://cdn.bootcdn.net/ajax/libs/${n}/${e}${t}`},cdnjs:{getFileList:(n,e)=>new Promise((t,r)=>{g.get(`https://api.cdnjs.com/libraries/${n}/${e}`,s=>{const i=JSON.parse(s);if(i.error){r(new Error(`cdnjs: ${n}@${e} not found`));return}t({fileList:i.rawFiles.map(o=>({name:"/"+o}))})},s=>{r(s)})}),getUrl:(n,e,t)=>`https://cdnjs.cloudflare.com/ajax/libs/${n}/${e}${t}`}},E=async(n,e,t)=>{var o;const r=(o=e.match(/\d+(.\d+)?(.\d+)?/))==null?void 0:o[0];if(!r)throw new Error(`${n} version ${e} is not valid`);const s=await S[t].getFileList(n,r).catch(c=>{throw c}),i=L(s,n);if(!i)throw new Error(`在 ${t} 中找不到 ${n}@${r} 文件,请检查包名或版本号`);return S[t].getUrl(n,r,i)},L=({fileList:n},e)=>{var i,o;let t=[`umd/${e}.production.min.js`,/umd\/.+?\.production\.min\.js$/,/dist\/.+?\.production\.min\.js$/,/dist\/.+?\.umd\.min\.js$/,`dist/${e}.prod.min.js`,/dist\/.+?\.global.prod.min.js/,`dist/${e}.min.js`,/.+?\.global.prod.min.js/,/.+?.global.prod.js/,/lib\/.+?\.min\.js$/,/dist\/.+?\.min\.js$/,/index\.min\.js$/,/index\.js$/,/\.min\.js$/,/\.js$/];const r=["runtime","compiler",".esm",".cjs","development"].filter(c=>!e.includes(c));let s="";for(let c of t)if(c instanceof RegExp?s=((i=n.find(u=>c.test(u.name)&&!r.some(l=>u.name.includes(l))))==null?void 0:i.name)||"":s=((o=n.find(u=>u.name.includes(c)&&!r.some(l=>u.name.includes(l))))==null?void 0:o.name)||"",s)break;return s};function b(n,e){const t=n.replace(/^\D/,"").split("."),r=e.replace(/^\D/,"").split("."),s=Math.max(t.length,r.length);for(;t.length<s;)t.push("0");for(;r.length<s;)r.push("0");for(let i=0;i<s;i++){const o=parseInt(t[i],10),c=parseInt(r[i],10);if(o>c)return 1;if(o<c)return-1}return 0}function N(n,e){for(let t in e)Object.prototype.hasOwnProperty.call(e,t)&&(n[t]?b(n[t],e[t])===-1&&(n[t]=e[t]):n[t]=e[t]);return n}async function U({external:n,packageData:e,customScript:t,defaultCdns:r}){let s=[],i=!1;const o=await J();return await Promise.all(n.map(async c=>{const u=F(e,c);if(t[c])return{urls:[],key:c};if(!u){s.push(c);return}const l=o.getCdnCache(c,u);if(l)return{urls:l,key:c};{i=!0;const h={urls:await Promise.all(r.map(async m=>await E(c,u,m))),key:c};return o.setCdnCache(c,u,h.urls),h}})).then(c=>(i&&o.save(),{urls:c,noVersionPackages:s}))}function I(n){const{customScript:e={},retryTimes:t=1,defaultCdns:r=["jsdelivr","unpkg"]}=n;let s;return{name:"vite-add-cdn-script",enforce:"pre",apply:"build",config(i){s=i},async transformIndexHtml(i){if(!r||r.length===0)throw new Error("defaultCdns不能为空");const o=d.resolve(process.cwd(),"package.json");try{const c=a.readFileSync(o,"utf-8"),u=JSON.parse(c),l=s.build.rollupOptions.external,h={};let m="";const{urls:j,noVersionPackages:k}=await U({external:l,packageData:u,customScript:e,defaultCdns:r});if(k.length>0){const $={dependencies:{}};await Promise.all(j.map(async y=>{if(!y)return;const{key:R,urls:A}=y,v=e[R]||A[0];if(!v)return;const M=await D(v);N($.dependencies,M.dependencies)}));const{urls:C,noVersionPackages:p}=await U({external:k,packageData:$,customScript:e,defaultCdns:r});if(j.push(...C),p.length>0)throw console.error(`找不到${p.join(",")}的版本`),new Error(`找不到${p.join(",")}的版本`)}return j.forEach($=>{if(!$)return;const{urls:C,key:p}=$;if(e[p])m+=e[p];else{h[p]=C;const y=C[0];m+=`<script src="${y}" type="text/javascript" crossorigin="anonymous" onerror="errorCDN(this)" data-cur="0" data-key="${p}"><\/script>
|
2
|
+
`}}),m=`<script>
|
3
|
+
function errorCDN(e) {
|
4
|
+
const packNameUrl = JSON.parse('${JSON.stringify(h)}');
|
5
|
+
const nextCur = parseInt(e.getAttribute("data-cur")) + 1;
|
6
|
+
if(nextCur>${t}){return;}
|
7
|
+
|
8
|
+
const key = e.getAttribute("data-key");
|
9
|
+
if(nextCur>=packNameUrl[key].length){return;}
|
10
|
+
// 新的cdn链接
|
11
|
+
const url = packNameUrl[key][nextCur]
|
12
|
+
// 克隆原标签
|
13
|
+
const tagName = e.tagName
|
14
|
+
const cdnDOM = document.createElement(tagName);
|
15
|
+
cdnDOM.setAttribute(tagName === 'SCRIPT' ?'src' : 'href', url);
|
16
|
+
Object.keys(e.dataset).forEach(_key => {
|
17
|
+
cdnDOM.setAttribute('data-'+_key, e.dataset[_key]);
|
18
|
+
})
|
19
|
+
cdnDOM.setAttribute("data-cur", nextCur.toString());
|
20
|
+
cdnDOM.setAttribute("onerror", "errorCDN(this)");
|
21
|
+
document.head.appendChild(cdnDOM);
|
22
|
+
e.remove();
|
23
|
+
}
|
24
|
+
<\/script>`+m,i=i.replace("</head>",`${m}</head>`),i}catch(c){console.error("获取dependencies出错:",c)}}}}return I});
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "vite-add-cdn-script",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.7",
|
4
4
|
"keywords": [
|
5
5
|
"vite",
|
6
6
|
"cdn",
|
@@ -30,20 +30,30 @@
|
|
30
30
|
},
|
31
31
|
"scripts": {
|
32
32
|
"dev": "vite",
|
33
|
-
"build": "tsc && vite build"
|
33
|
+
"build:page": "tsc && cross-env BUILD_MODE=page vite build",
|
34
|
+
"build": "tsc && cross-env BUILD_MODE=lib vite build",
|
35
|
+
"test": "jest"
|
34
36
|
},
|
35
37
|
"devDependencies": {
|
38
|
+
"@jest/globals": "^29.7.0",
|
36
39
|
"@types/node": "^20.12.11",
|
37
40
|
"@types/react": "^18.3.2",
|
38
41
|
"@types/react-dom": "^18.3.0",
|
39
42
|
"@vitejs/plugin-react": "^4.2.1",
|
43
|
+
"cross-env": "^7.0.3",
|
44
|
+
"jest": "^29.7.0",
|
40
45
|
"react": "^18.3.1",
|
41
46
|
"react-dom": "^18.3.1",
|
47
|
+
"react-router-dom": "^6.23.1",
|
48
|
+
"rollup-plugin-external-globals": "^0.10.0",
|
42
49
|
"rollup-plugin-node-externals": "^7.1.2",
|
50
|
+
"ts-jest": "^29.1.4",
|
51
|
+
"ts-node": "^10.9.2",
|
43
52
|
"typescript": "^5.4.5",
|
44
53
|
"vite": "^5.2.11"
|
45
54
|
},
|
46
55
|
"publishConfig": {
|
47
56
|
"registry": "https://registry.npmjs.org/"
|
48
|
-
}
|
49
|
-
}
|
57
|
+
},
|
58
|
+
"dependencies": {}
|
59
|
+
}
|