vite-add-cdn-script 0.0.11 → 1.0.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
@@ -1,562 +1,37 @@
1
- var q = Object.defineProperty;
2
- var G = (r, t, e) => t in r ? q(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
3
- var g = (r, t, e) => (G(r, typeof t != "symbol" ? t + "" : t, e), e);
4
- import A from "node:path";
5
- import E from "node:fs";
6
- import O from "node-fetch";
7
- import V from "semver";
8
- class B {
9
- constructor() {
10
- g(this, "cdnCache", {});
11
- g(this, "cdnCachePath", "");
12
- this.cdnCachePath = A.resolve(process.cwd(), "./.cdn-cache.json");
13
- }
14
- // 初始化cdn缓存
15
- async init() {
16
- try {
17
- const t = await E.readFileSync(this.cdnCachePath, "utf-8");
18
- this.cdnCache = JSON.parse(t);
19
- } catch {
20
- console.log("cdn缓存文件不存在,创建缓存文件"), this.cdnCache = {}, await E.writeFileSync(this.cdnCachePath, "", "utf-8");
21
- }
22
- }
23
- /**
24
- * 获取cdn缓存
25
- * @param packageName 包名
26
- * @param version 版本
27
- */
28
- getCdnCache(t, e) {
29
- var s;
30
- return (s = this.cdnCache[t]) == null ? void 0 : s[e];
31
- }
32
- /**
33
- * 设置cdn缓存
34
- * @param packageName 包名
35
- * @param version 版本
36
- * @param urls 地址列表
37
- */
38
- setCdnCache(t, e, s) {
39
- this.cdnCache[t] ? this.cdnCache[t][e] = s : this.cdnCache[t] = {
40
- [e]: s
41
- };
42
- }
43
- /**
44
- * 更新cdn缓存
45
- */
46
- async save() {
47
- await E.writeFileSync(this.cdnCachePath, JSON.stringify(this.cdnCache), "utf-8");
48
- }
49
- }
50
- let U;
51
- const z = async () => (U || (U = new B(), await U.init()), U);
52
- class u extends Error {
53
- constructor(t) {
54
- super(t), this.name = "NetworkError";
55
- }
56
- }
57
- class P extends u {
58
- constructor({ packageName: t, version: e, cdn: s }) {
59
- super(`${s} ${t}@${e} 网络请求失败`), this.name = "PackageNetworkError";
60
- }
61
- }
62
- class m extends Error {
63
- constructor({ packageName: t, version: e, cdn: s }) {
64
- super(`${s}上没有${t}@${e}的版本`), this.name = "NoVersionError";
65
- }
66
- }
67
- class K extends Error {
68
- constructor({ packageName: t, version: e, cdn: s }) {
69
- super(`在 ${s} 中找不到 ${t}@${e} 文件,请检查包名或版本号`), this.name = "GetFileListError";
70
- }
71
- }
72
- class N {
73
- constructor(t) {
74
- g(this, "_max");
75
- g(this, "_count");
76
- g(this, "_taskQueue");
77
- this._max = t || 5, this._count = 0, this._taskQueue = [];
78
- }
79
- /**
80
- * 请求封装
81
- * @param caller 请求函数
82
- * @param args 请求参数
83
- * @returns {Promise<any>} 返回一个promise
84
- */
85
- call(t, ...e) {
86
- return new Promise((s, n) => {
87
- const o = this._createTask(t, e, s, n);
88
- this._count >= this._max ? this._taskQueue.push(o) : o();
89
- });
90
- }
91
- /**
92
- * 创建一个任务
93
- * @param caller 实际执行的函数
94
- * @param args 执行函数的参数
95
- * @param resolve
96
- * @param reject
97
- * @returns {Function} 返回一个任务函数
98
- * @private
99
- */
100
- _createTask(t, e, s, n) {
101
- return () => {
102
- t(...e).then(s).catch(n).finally(() => {
103
- this._count--, this._taskQueue.length && this._taskQueue.shift()();
104
- }), this._count++;
105
- };
106
- }
107
- }
108
- const _ = new N(5), J = async (r) => {
109
- try {
110
- const t = await O(r, { method: "HEAD", redirect: "manual" });
111
- return t.status >= 300 && t.status < 400 ? await J(t.headers.get("location") || "") : r;
112
- } catch (t) {
113
- throw Promise.reject(new u(t.message));
114
- }
115
- }, W = {
116
- //get请求封装
117
- get: async (r) => {
118
- try {
119
- const t = await O(r);
120
- if (t.ok) {
121
- const e = t.headers.get("content-type"), s = await t.text();
122
- return e && e.includes("application/json") ? JSON.parse(s) : s;
123
- } else
124
- throw new u(`请求失败,状态码:${t.status}`);
125
- } catch (t) {
126
- throw new u(t.message);
127
- }
128
- }
129
- }, $ = {
130
- get: _.call.bind(_, W.get)
131
- }, X = async (r, t) => {
132
- try {
133
- const e = await $.get(`https://api.bootcdn.cn/libraries/${r}`);
134
- if (e.length === 0)
135
- throw new m({
136
- packageName: r,
137
- version: t,
138
- cdn: "bootcdn"
139
- });
140
- const s = e[0], o = s.assets.reverse().find((c) => {
141
- if (V.satisfies(c.version, t))
142
- return !0;
143
- });
144
- if (!o)
145
- throw new m({
146
- packageName: r,
147
- version: t,
148
- cdn: "bootcdn"
149
- });
150
- return { fileList: o.files.map((c) => ({
151
- name: "/" + c
152
- })), recommendFileName: s.filename, version: o.version };
153
- } catch (e) {
154
- throw e instanceof u ? new P({
155
- packageName: r,
156
- version: t,
157
- cdn: "unpkg"
158
- }) : e;
159
- }
160
- }, Y = (r, t, e) => `https://cdn.bootcdn.net/ajax/libs/${r}/${t}${e}`, Z = {
161
- getFileList: X,
162
- getUrl: Y
163
- };
164
- async function M(r, t, e = !1) {
165
- try {
166
- if (!e && t.match(/^\D/)) {
167
- const n = await et(r, t);
168
- for (let o of n)
169
- if (V.satisfies(o, t))
170
- return M(r, o, !0);
171
- throw new m({
172
- packageName: r,
173
- version: t,
174
- cdn: "cdnjs"
175
- });
176
- }
177
- const s = await $.get(`https://api.cdnjs.com/libraries/${r}/${t}`);
178
- if (s.error)
179
- throw new m({
180
- packageName: r,
181
- version: t,
182
- cdn: "cdnjs"
183
- });
184
- return {
185
- fileList: s.rawFiles.map((n) => ({
186
- name: "/" + n
187
- })),
188
- version: t
189
- };
190
- } catch (s) {
191
- throw s instanceof u ? new P({
192
- packageName: r,
193
- version: t,
194
- cdn: "unpkg"
195
- }) : s;
196
- }
197
- }
198
- const tt = (r, t, e) => `https://cdnjs.cloudflare.com/ajax/libs/${r}/${t}${e}`, et = async (r, t) => {
199
- try {
200
- return (await $.get(`https://api.cdnjs.com/libraries/${r}?fields=versions`)).versions;
201
- } catch (e) {
202
- throw e instanceof u ? new P({
203
- packageName: r,
204
- version: t,
205
- cdn: "unpkg"
206
- }) : e;
207
- }
208
- }, rt = {
209
- getFileList: M,
210
- getUrl: tt
211
- }, I = (r, t = "") => r.reduce((e, s) => (s.type === "file" ? e.push({ name: `${t}/${s.name}` }) : s.files && e.push(...I(s.files, `${t}/${s.name}`)), e), []);
212
- async function Q(r, t, e = !1) {
213
- try {
214
- if (!e && t.match(/^\D/)) {
215
- const n = await nt(r, t);
216
- if (typeof n == "string")
217
- return Q(r, n, !0);
218
- throw new m({
219
- packageName: r,
220
- version: t,
221
- cdn: "jsdelivr"
222
- });
223
- }
224
- const s = await $.get(`https://data.jsdelivr.com/v1/packages/npm/${r}@${t}`);
225
- if (s.status)
226
- throw new m({
227
- packageName: r,
228
- version: t,
229
- cdn: "jsdelivr"
230
- });
231
- return { fileList: I(s.files), version: t };
232
- } catch (s) {
233
- throw s instanceof u ? new P({
234
- packageName: r,
235
- version: t,
236
- cdn: "unpkg"
237
- }) : s;
238
- }
239
- }
240
- const st = (r, t, e) => `https://cdn.jsdelivr.net/npm/${r}@${t}${e}`, nt = async (r, t) => {
241
- try {
242
- return (await $.get(
243
- `https://data.jsdelivr.com/v1/packages/npm/${r}/resolved?specifier=${t}`
244
- )).version;
245
- } catch (e) {
246
- throw e instanceof u ? new P({
247
- packageName: r,
248
- version: t,
249
- cdn: "unpkg"
250
- }) : e;
251
- }
252
- }, ct = {
253
- getFileList: Q,
254
- // 拼接url
255
- getUrl: st
256
- }, T = (r) => r.reduce((t, e) => (e.type === "file" ? t.push({ name: e.path }) : e.files && t.push(...T(e.files)), t), []);
257
- async function ot(r, t) {
258
- var e;
259
- try {
260
- const n = (e = (await J(`https://unpkg.com/${r}@${t}/?meta`)).match(new RegExp("(?<=@)\\d+\\.\\d+\\.\\d+(?=\\/\\?meta)"))) == null ? void 0 : e[0];
261
- if (n) {
262
- const o = await $.get(`https://unpkg.com/${r}@${n}/?meta`);
263
- return { fileList: T(o.files || []), version: n };
264
- } else
265
- throw new m({
266
- packageName: r,
267
- version: t,
268
- cdn: "unpkg"
269
- });
270
- } catch (s) {
271
- throw s instanceof u ? new P({
272
- packageName: r,
273
- version: t,
274
- cdn: "unpkg"
275
- }) : s;
276
- }
277
- }
278
- function it(r, t, e) {
279
- return `https://unpkg.com/${r}@${t}${e}`;
280
- }
281
- const at = {
282
- getFileList: ot,
283
- getUrl: it
284
- }, lt = async (r) => {
285
- try {
286
- const t = /^(https?:\/\/.*\d+\.\d+\.\d+\/).+?\.js$/;
287
- if (t.test(r)) {
288
- const e = r.replace(t, (s, n) => `${n}package.json`);
289
- return await $.get(e);
290
- } else
291
- throw new Error(`${r} 不是正确的url`);
292
- } catch (t) {
293
- throw t;
294
- }
295
- }, ut = (r, t) => {
296
- var e, s;
297
- return ((e = r.dependencies) == null ? void 0 : e[t]) || ((s = r.devDependencies) == null ? void 0 : s[t]);
298
- }, dt = async (r, t, e) => {
299
- if (!t.match(/\d+(.\d+)?(.\d+)?/))
300
- throw new Error(`${r} version ${t} is not valid`);
301
- const n = await D[e].getFileList(r, t), o = ht(n, r);
302
- if (!o)
303
- throw new K({
304
- packageName: r,
305
- version: t,
306
- cdn: e
307
- });
308
- return D[e].getUrl(r, n.version, o);
309
- }, ht = ({ fileList: r }, t) => {
310
- var o, a;
311
- let e = [
312
- `umd/${t}.production.min.js`,
313
- /umd\/.+?\.production\.min\.js$/,
314
- /dist\/.+?\.production\.min\.js$/,
315
- /dist\/.+?\.umd\.min\.js$/,
316
- `dist/${t}.prod.min.js`,
317
- /dist\/.+?\.global.prod.min.js/,
318
- `dist/${t}.min.js`,
319
- /.+?\.global.prod.min.js/,
320
- /.+?.global.prod.js/,
321
- /lib\/.+?\.min\.js$/,
322
- /dist\/.+?\.min\.js$/,
323
- /index\.min\.js$/,
324
- /index\.js$/,
325
- /\.min\.js$/,
326
- /\.js$/
327
- ];
328
- const s = ["runtime", "compiler", ".esm", ".cjs", "development"].filter((c) => !t.includes(c));
329
- let n = "";
330
- for (let c of e)
331
- if (c instanceof RegExp ? n = ((o = r.find((i) => c.test(i.name) && !s.some((f) => i.name.includes(f)))) == null ? void 0 : o.name) || "" : n = ((a = r.find((i) => i.name.includes(c) && !s.some((f) => i.name.includes(f)))) == null ? void 0 : a.name) || "", n)
332
- break;
333
- return n;
334
- }, D = {
335
- jsdelivr: ct,
336
- bootcdn: Z,
337
- cdnjs: rt,
338
- unpkg: at
339
- };
340
- function ft(r, t) {
341
- const e = r.replace(/^\D/, "").split("."), s = t.replace(/^\D/, "").split("."), n = Math.max(e.length, s.length);
342
- for (; e.length < n; )
343
- e.push("0");
344
- for (; s.length < n; )
345
- s.push("0");
346
- for (let o = 0; o < n; o++) {
347
- const a = parseInt(e[o], 10), c = parseInt(s[o], 10);
348
- if (a > c)
349
- return 1;
350
- if (a < c)
351
- return -1;
352
- }
353
- return 0;
354
- }
355
- function pt(r, t) {
356
- for (let e in t)
357
- Object.prototype.hasOwnProperty.call(t, e) && (r[e] ? ft(r[e], t[e]) === -1 && (r[e] = t[e]) : r[e] = t[e]);
358
- return r;
359
- }
360
- class wt {
361
- constructor() {
362
- // 打印记录
363
- g(this, "logList", []);
364
- }
365
- // 打印方法
366
- log(t) {
367
- this.logList.push({
368
- type: "log",
369
- message: t
370
- });
371
- }
372
- warn(t) {
373
- this.logList.push({
374
- type: "warn",
375
- message: t
376
- });
377
- }
378
- error(t) {
379
- this.logList.push({
380
- type: "error",
381
- message: t
382
- });
383
- }
384
- info(t) {
385
- this.logList.push({
386
- type: "info",
387
- message: t
388
- });
389
- }
390
- // 打印全部
391
- consoleAll() {
392
- this.logList.forEach((t) => {
393
- console[t.type](`${H} ${t.message}`);
394
- });
395
- }
396
- // 清除打印记录
397
- clear() {
398
- this.logList = [];
399
- }
400
- }
401
- const H = "vite-add-cdn-script", b = new wt();
402
- async function R({
403
- external: r,
404
- packageData: t,
405
- customScript: e,
406
- defaultCdns: s
407
- }) {
408
- let n = [], o = !1;
409
- const a = await z();
410
- return await Promise.all(
411
- r.map(async (c) => {
412
- const i = ut(t, c);
413
- if (e[c])
414
- return {
415
- urls: [],
416
- key: c
417
- };
418
- if (!i)
419
- return n.push(c), {
420
- urls: [],
421
- key: c
422
- };
423
- const f = a.getCdnCache(c, i);
424
- if (f)
425
- return {
426
- urls: f,
427
- key: c
428
- };
429
- {
430
- o = !0, console.log(`从网络获取${c}${i}的cdn地址`);
431
- const d = await Promise.allSettled(
432
- s.map(async (j) => await dt(c, i, j))
433
- ).then((j) => j.filter((l) => {
434
- if (l.status === "fulfilled")
435
- return l.value, !0;
436
- b.warn(l.reason.toString());
437
- }).map((l) => l.value));
438
- if (d.length === 0)
439
- throw new Error(`获取${c} ${i}的cdn地址失败`);
440
- const y = {
441
- urls: d,
442
- key: c
443
- };
444
- return a.setCdnCache(c, i, y.urls), y;
445
- }
446
- })
447
- ).then((c) => (o && a.save(), {
448
- urls: c,
449
- noVersionPackages: n
450
- }));
451
- }
452
- function Pt(r) {
453
- const { customScript: t = {}, defaultCdns: e = ["jsdelivr", "unpkg"] } = r;
454
- let s;
1
+ import l from "cdn-script-core";
2
+ var d = /* @__PURE__ */ ((e) => (e.PRE = "pre", e.POST = "post", e))(d || {});
3
+ const s = "vite-add-cdn-script";
4
+ function f(e) {
5
+ const { customScript: p = {}, defaultCdns: t = ["jsdelivr", "unpkg"] } = e;
6
+ let o;
455
7
  return {
456
- name: H,
457
- enforce: "pre",
8
+ name: s,
9
+ enforce: d.PRE,
458
10
  apply: "build",
459
- config(n) {
460
- s = n;
11
+ config(r) {
12
+ o = r;
461
13
  },
462
- async transformIndexHtml(n) {
463
- var a, c;
464
- if (!e || e.length === 0)
14
+ async transformIndexHtml(r) {
15
+ var c, i;
16
+ if (!t || t.length === 0)
465
17
  throw new Error("defaultCdns不能为空");
466
- const o = A.resolve(process.cwd(), "package.json");
18
+ const a = (i = (c = o.build) == null ? void 0 : c.rollupOptions) == null ? void 0 : i.external;
19
+ if (!a)
20
+ return r;
467
21
  try {
468
- const i = E.readFileSync(o, "utf-8"), f = JSON.parse(i), d = (c = (a = s.build) == null ? void 0 : a.rollupOptions) == null ? void 0 : c.external;
469
- if (!d)
470
- return n;
471
- let y = [];
472
- if (typeof d == "string")
473
- y = [d];
474
- else if (Array.isArray(d))
475
- y = d.filter((w) => typeof w == "string");
476
- else if (typeof d == "object")
477
- return n;
478
- const j = {};
479
- let l = "";
480
- const { urls: S, noVersionPackages: F } = await R({
481
- external: y,
482
- packageData: f,
483
- customScript: t,
484
- defaultCdns: e
22
+ const n = await l({
23
+ libName: s,
24
+ customScript: p,
25
+ external: a,
26
+ defaultCdns: t
485
27
  });
486
- if (F.length > 0) {
487
- const w = { dependencies: {} };
488
- await Promise.allSettled(
489
- S.map(async (h) => {
490
- if (!h)
491
- return;
492
- const { key: C, urls: v } = h, k = t[C] || v[0];
493
- if (!k)
494
- return;
495
- const x = await lt(k);
496
- pt(w.dependencies, x.dependencies);
497
- })
498
- ).then((h) => {
499
- h.forEach((C) => {
500
- C.status === "rejected" && b.warn(C.reason.toString());
501
- });
502
- });
503
- const { urls: L, noVersionPackages: p } = await R({
504
- external: F,
505
- packageData: w,
506
- customScript: t,
507
- defaultCdns: e
508
- });
509
- if (L.map((h) => {
510
- var k;
511
- if (!h)
512
- return;
513
- const { urls: C, key: v } = h;
514
- (k = S.find((x) => (x == null ? void 0 : x.key) === v)) == null || k.urls.push(...C);
515
- }), p.length > 0)
516
- throw console.error(`找不到${p.join(",")}的版本`), new Error(`找不到${p.join(",")}的版本`);
517
- }
518
- return b.consoleAll(), S.forEach((w) => {
519
- if (!w)
520
- return;
521
- const { urls: L, key: p } = w;
522
- if (t[p])
523
- l += t[p];
524
- else {
525
- j[p] = L;
526
- const h = L[0];
527
- l += `<script src="${h}" type="text/javascript" crossorigin="anonymous" onerror="errorCDN(this)" data-cur="0" data-key="${p}"><\/script>
528
- `;
529
- }
530
- }), l = `<script>
531
- function errorCDN(e) {
532
- const packNameUrl = JSON.parse('${JSON.stringify(j)}');
533
- const nextCur = parseInt(e.getAttribute("data-cur")) + 1;
534
-
535
- const key = e.getAttribute("data-key");
536
- const curPackNameUrl = packNameUrl[key]
537
- if(nextCur>=curPackNameUrl.length){return;}
538
- // 新的cdn链接
539
- const url = curPackNameUrl[nextCur]
540
- // 克隆原标签
541
- const tagName = e.tagName
542
- const cdnDOM = document.createElement(tagName);
543
- cdnDOM.setAttribute(tagName === 'SCRIPT' ?'src' : 'href', url);
544
- Object.keys(e.dataset).forEach(_key => {
545
- cdnDOM.setAttribute('data-'+_key, e.dataset[_key]);
546
- })
547
- cdnDOM.setAttribute("data-cur", nextCur.toString());
548
- cdnDOM.setAttribute("onerror", "errorCDN(this)");
549
- document.head.appendChild(cdnDOM);
550
- e.remove();
551
- }
552
- <\/script>` + l, n = n.replace("</head>", `${l}</head>`), n;
553
- } catch (i) {
554
- b.consoleAll(), console.error("vite-add-cdn-script error:", i.message), process.exit(1);
28
+ return r = r.replace("</head>", `${n}</head>`), r;
29
+ } catch (n) {
30
+ console.error("vite-add-cdn-script error:", n.message), process.exit(1);
555
31
  }
556
32
  }
557
33
  };
558
34
  }
559
35
  export {
560
- Pt as default,
561
- H as libName
36
+ f as default
562
37
  };
@@ -1,24 +1 @@
1
- (function(o,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("node:path"),require("node:fs"),require("node-fetch"),require("semver")):typeof define=="function"&&define.amd?define(["exports","node:path","node:fs","node-fetch","semver"],l):(o=typeof globalThis<"u"?globalThis:o||self,l(o.index={},o.path,o.fs,o.fetch,o.semver))})(this,function(o,l,h,O,R){"use strict";var pt=Object.defineProperty;var wt=(o,l,h)=>l in o?pt(o,l,{enumerable:!0,configurable:!0,writable:!0,value:h}):o[l]=h;var C=(o,l,h)=>(wt(o,typeof l!="symbol"?l+"":l,h),h);class G{constructor(){C(this,"cdnCache",{});C(this,"cdnCachePath","");this.cdnCachePath=l.resolve(process.cwd(),"./.cdn-cache.json")}async init(){try{const t=await h.readFileSync(this.cdnCachePath,"utf-8");this.cdnCache=JSON.parse(t)}catch{console.log("cdn缓存文件不存在,创建缓存文件"),this.cdnCache={},await h.writeFileSync(this.cdnCachePath,"","utf-8")}}getCdnCache(t,e){var n;return(n=this.cdnCache[t])==null?void 0:n[e]}setCdnCache(t,e,n){this.cdnCache[t]?this.cdnCache[t][e]=n:this.cdnCache[t]={[e]:n}}async save(){await h.writeFileSync(this.cdnCachePath,JSON.stringify(this.cdnCache),"utf-8")}}let b;const B=async()=>(b||(b=new G,await b.init()),b);class f extends Error{constructor(t){super(t),this.name="NetworkError"}}class P extends f{constructor({packageName:t,version:e,cdn:n}){super(`${n} ${t}@${e} 网络请求失败`),this.name="PackageNetworkError"}}class $ extends Error{constructor({packageName:t,version:e,cdn:n}){super(`${n}上没有${t}@${e}的版本`),this.name="NoVersionError"}}class N extends Error{constructor({packageName:t,version:e,cdn:n}){super(`在 ${n} 中找不到 ${t}@${e} 文件,请检查包名或版本号`),this.name="GetFileListError"}}class z{constructor(t){C(this,"_max");C(this,"_count");C(this,"_taskQueue");this._max=t||5,this._count=0,this._taskQueue=[]}call(t,...e){return new Promise((n,s)=>{const c=this._createTask(t,e,n,s);this._count>=this._max?this._taskQueue.push(c):c()})}_createTask(t,e,n,s){return()=>{t(...e).then(n).catch(s).finally(()=>{this._count--,this._taskQueue.length&&this._taskQueue.shift()()}),this._count++}}}const A=new z(5),V=async r=>{try{const t=await O(r,{method:"HEAD",redirect:"manual"});return t.status>=300&&t.status<400?await V(t.headers.get("location")||""):r}catch(t){throw Promise.reject(new f(t.message))}},K={get:async r=>{try{const t=await O(r);if(t.ok){const e=t.headers.get("content-type"),n=await t.text();return e&&e.includes("application/json")?JSON.parse(n):n}else throw new f(`请求失败,状态码:${t.status}`)}catch(t){throw new f(t.message)}}},y={get:A.call.bind(A,K.get)},W={getFileList:async(r,t)=>{try{const e=await y.get(`https://api.bootcdn.cn/libraries/${r}`);if(e.length===0)throw new $({packageName:r,version:t,cdn:"bootcdn"});const n=e[0],c=n.assets.reverse().find(i=>{if(R.satisfies(i.version,t))return!0});if(!c)throw new $({packageName:r,version:t,cdn:"bootcdn"});return{fileList:c.files.map(i=>({name:"/"+i})),recommendFileName:n.filename,version:c.version}}catch(e){throw e instanceof f?new P({packageName:r,version:t,cdn:"unpkg"}):e}},getUrl:(r,t,e)=>`https://cdn.bootcdn.net/ajax/libs/${r}/${t}${e}`};async function J(r,t,e=!1){try{if(!e&&t.match(/^\D/)){const s=await Y(r,t);for(let c of s)if(R.satisfies(c,t))return J(r,c,!0);throw new $({packageName:r,version:t,cdn:"cdnjs"})}const n=await y.get(`https://api.cdnjs.com/libraries/${r}/${t}`);if(n.error)throw new $({packageName:r,version:t,cdn:"cdnjs"});return{fileList:n.rawFiles.map(s=>({name:"/"+s})),version:t}}catch(n){throw n instanceof f?new P({packageName:r,version:t,cdn:"unpkg"}):n}}const X=(r,t,e)=>`https://cdnjs.cloudflare.com/ajax/libs/${r}/${t}${e}`,Y=async(r,t)=>{try{return(await y.get(`https://api.cdnjs.com/libraries/${r}?fields=versions`)).versions}catch(e){throw e instanceof f?new P({packageName:r,version:t,cdn:"unpkg"}):e}},Z={getFileList:J,getUrl:X},M=(r,t="")=>r.reduce((e,n)=>(n.type==="file"?e.push({name:`${t}/${n.name}`}):n.files&&e.push(...M(n.files,`${t}/${n.name}`)),e),[]);async function I(r,t,e=!1){try{if(!e&&t.match(/^\D/)){const s=await et(r,t);if(typeof s=="string")return I(r,s,!0);throw new $({packageName:r,version:t,cdn:"jsdelivr"})}const n=await y.get(`https://data.jsdelivr.com/v1/packages/npm/${r}@${t}`);if(n.status)throw new $({packageName:r,version:t,cdn:"jsdelivr"});return{fileList:M(n.files),version:t}}catch(n){throw n instanceof f?new P({packageName:r,version:t,cdn:"unpkg"}):n}}const tt=(r,t,e)=>`https://cdn.jsdelivr.net/npm/${r}@${t}${e}`,et=async(r,t)=>{try{return(await y.get(`https://data.jsdelivr.com/v1/packages/npm/${r}/resolved?specifier=${t}`)).version}catch(e){throw e instanceof f?new P({packageName:r,version:t,cdn:"unpkg"}):e}},rt={getFileList:I,getUrl:tt},T=r=>r.reduce((t,e)=>(e.type==="file"?t.push({name:e.path}):e.files&&t.push(...T(e.files)),t),[]);async function nt(r,t){var e;try{const s=(e=(await V(`https://unpkg.com/${r}@${t}/?meta`)).match(new RegExp("(?<=@)\\d+\\.\\d+\\.\\d+(?=\\/\\?meta)")))==null?void 0:e[0];if(s){const c=await y.get(`https://unpkg.com/${r}@${s}/?meta`);return{fileList:T(c.files||[]),version:s}}else throw new $({packageName:r,version:t,cdn:"unpkg"})}catch(n){throw n instanceof f?new P({packageName:r,version:t,cdn:"unpkg"}):n}}function st(r,t,e){return`https://unpkg.com/${r}@${t}${e}`}const it={getFileList:nt,getUrl:st},ct=async r=>{try{const t=/^(https?:\/\/.*\d+\.\d+\.\d+\/).+?\.js$/;if(t.test(r)){const e=r.replace(t,(n,s)=>`${s}package.json`);return await y.get(e)}else throw new Error(`${r} 不是正确的url`)}catch(t){throw t}},ot=(r,t)=>{var e,n;return((e=r.dependencies)==null?void 0:e[t])||((n=r.devDependencies)==null?void 0:n[t])},at=async(r,t,e)=>{if(!t.match(/\d+(.\d+)?(.\d+)?/))throw new Error(`${r} version ${t} is not valid`);const s=await q[e].getFileList(r,t),c=lt(s,r);if(!c)throw new N({packageName:r,version:t,cdn:e});return q[e].getUrl(r,s.version,c)},lt=({fileList:r},t)=>{var c,u;let e=[`umd/${t}.production.min.js`,/umd\/.+?\.production\.min\.js$/,/dist\/.+?\.production\.min\.js$/,/dist\/.+?\.umd\.min\.js$/,`dist/${t}.prod.min.js`,/dist\/.+?\.global.prod.min.js/,`dist/${t}.min.js`,/.+?\.global.prod.min.js/,/.+?.global.prod.js/,/lib\/.+?\.min\.js$/,/dist\/.+?\.min\.js$/,/index\.min\.js$/,/index\.js$/,/\.min\.js$/,/\.js$/];const n=["runtime","compiler",".esm",".cjs","development"].filter(i=>!t.includes(i));let s="";for(let i of e)if(i instanceof RegExp?s=((c=r.find(a=>i.test(a.name)&&!n.some(g=>a.name.includes(g))))==null?void 0:c.name)||"":s=((u=r.find(a=>a.name.includes(i)&&!n.some(g=>a.name.includes(g))))==null?void 0:u.name)||"",s)break;return s},q={jsdelivr:rt,bootcdn:W,cdnjs:Z,unpkg:it};function ut(r,t){const e=r.replace(/^\D/,"").split("."),n=t.replace(/^\D/,"").split("."),s=Math.max(e.length,n.length);for(;e.length<s;)e.push("0");for(;n.length<s;)n.push("0");for(let c=0;c<s;c++){const u=parseInt(e[c],10),i=parseInt(n[c],10);if(u>i)return 1;if(u<i)return-1}return 0}function dt(r,t){for(let e in t)Object.prototype.hasOwnProperty.call(t,e)&&(r[e]?ut(r[e],t[e])===-1&&(r[e]=t[e]):r[e]=t[e]);return r}class ht{constructor(){C(this,"logList",[])}log(t){this.logList.push({type:"log",message:t})}warn(t){this.logList.push({type:"warn",message:t})}error(t){this.logList.push({type:"error",message:t})}info(t){this.logList.push({type:"info",message:t})}consoleAll(){this.logList.forEach(t=>{console[t.type](`${_} ${t.message}`)})}clear(){this.logList=[]}}const _="vite-add-cdn-script",v=new ht;async function Q({external:r,packageData:t,customScript:e,defaultCdns:n}){let s=[],c=!1;const u=await B();return await Promise.all(r.map(async i=>{const a=ot(t,i);if(e[i])return{urls:[],key:i};if(!a)return s.push(i),{urls:[],key:i};const g=u.getCdnCache(i,a);if(g)return{urls:g,key:i};{c=!0,console.log(`从网络获取${i}${a}的cdn地址`);const p=await Promise.allSettled(n.map(async x=>await at(i,a,x))).then(x=>x.filter(d=>{if(d.status==="fulfilled")return d.value,!0;v.warn(d.reason.toString())}).map(d=>d.value));if(p.length===0)throw new Error(`获取${i} ${a}的cdn地址失败`);const k={urls:p,key:i};return u.setCdnCache(i,a,k.urls),k}})).then(i=>(c&&u.save(),{urls:i,noVersionPackages:s}))}function ft(r){const{customScript:t={},defaultCdns:e=["jsdelivr","unpkg"]}=r;let n;return{name:_,enforce:"pre",apply:"build",config(s){n=s},async transformIndexHtml(s){var u,i;if(!e||e.length===0)throw new Error("defaultCdns不能为空");const c=l.resolve(process.cwd(),"package.json");try{const a=h.readFileSync(c,"utf-8"),g=JSON.parse(a),p=(i=(u=n.build)==null?void 0:u.rollupOptions)==null?void 0:i.external;if(!p)return s;let k=[];if(typeof p=="string")k=[p];else if(Array.isArray(p))k=p.filter(j=>typeof j=="string");else if(typeof p=="object")return s;const x={};let d="";const{urls:F,noVersionPackages:H}=await Q({external:k,packageData:g,customScript:t,defaultCdns:e});if(H.length>0){const j={dependencies:{}};await Promise.allSettled(F.map(async w=>{if(!w)return;const{key:L,urls:D}=w,U=t[L]||D[0];if(!U)return;const E=await ct(U);dt(j.dependencies,E.dependencies)})).then(w=>{w.forEach(L=>{L.status==="rejected"&&v.warn(L.reason.toString())})});const{urls:S,noVersionPackages:m}=await Q({external:H,packageData:j,customScript:t,defaultCdns:e});if(S.map(w=>{var U;if(!w)return;const{urls:L,key:D}=w;(U=F.find(E=>(E==null?void 0:E.key)===D))==null||U.urls.push(...L)}),m.length>0)throw console.error(`找不到${m.join(",")}的版本`),new Error(`找不到${m.join(",")}的版本`)}return v.consoleAll(),F.forEach(j=>{if(!j)return;const{urls:S,key:m}=j;if(t[m])d+=t[m];else{x[m]=S;const w=S[0];d+=`<script src="${w}" type="text/javascript" crossorigin="anonymous" onerror="errorCDN(this)" data-cur="0" data-key="${m}"><\/script>
2
- `}}),d=`<script>
3
- function errorCDN(e) {
4
- const packNameUrl = JSON.parse('${JSON.stringify(x)}');
5
- const nextCur = parseInt(e.getAttribute("data-cur")) + 1;
6
-
7
- const key = e.getAttribute("data-key");
8
- const curPackNameUrl = packNameUrl[key]
9
- if(nextCur>=curPackNameUrl.length){return;}
10
- // 新的cdn链接
11
- const url = curPackNameUrl[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>`+d,s=s.replace("</head>",`${d}</head>`),s}catch(a){v.consoleAll(),console.error("vite-add-cdn-script error:",a.message),process.exit(1)}}}}o.default=ft,o.libName=_,Object.defineProperties(o,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
1
+ (function(e,r){typeof exports=="object"&&typeof module<"u"?module.exports=r(require("cdn-script-core")):typeof define=="function"&&define.amd?define(["cdn-script-core"],r):(e=typeof globalThis<"u"?globalThis:e||self,e.index=r(e.getExternalScript))})(this,function(e){"use strict";var r=(n=>(n.PRE="pre",n.POST="post",n))(r||{});const c="vite-add-cdn-script";function f(n){const{customScript:a={},defaultCdns:i=["jsdelivr","unpkg"]}=n;let o;return{name:c,enforce:r.PRE,apply:"build",config(t){o=t},async transformIndexHtml(t){var p,u;if(!i||i.length===0)throw new Error("defaultCdns不能为空");const s=(u=(p=o.build)==null?void 0:p.rollupOptions)==null?void 0:u.external;if(!s)return t;try{const d=await e({libName:c,customScript:a,external:s,defaultCdns:i});return t=t.replace("</head>",`${d}</head>`),t}catch(d){console.error("vite-add-cdn-script error:",d.message),process.exit(1)}}}}return f});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-add-cdn-script",
3
- "version": "0.0.11",
3
+ "version": "1.0.1",
4
4
  "keywords": [
5
5
  "vite",
6
6
  "cdn",
@@ -52,13 +52,12 @@
52
52
  "registry": "https://registry.npmjs.org/"
53
53
  },
54
54
  "dependencies": {
55
- "node-fetch": "2",
56
- "semver": "^7.6.2"
55
+ "cdn-script-core": "1.0.0"
57
56
  },
58
57
  "scripts": {
59
58
  "dev": "vite",
60
59
  "build:page": "tsc && cross-env BUILD_MODE=page vite build",
61
60
  "build": "tsc && cross-env BUILD_MODE=lib vite build",
62
- "test": "jest"
61
+ "test": ""
63
62
  }
64
63
  }