diff --git a/packages/bundler-webpack/client/client/client.js b/packages/bundler-webpack/client/client/client.js index 98163038350d..e2924136021f 100644 --- a/packages/bundler-webpack/client/client/client.js +++ b/packages/bundler-webpack/client/client/client.js @@ -1,5 +1,5 @@ -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } -function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return exports; }; var exports = {}, Op = Object.prototype, hasOwn = Op.hasOwnProperty, defineProperty = Object.defineProperty || function (obj, key, desc) { obj[key] = desc.value; }, $Symbol = "function" == typeof Symbol ? Symbol : {}, iteratorSymbol = $Symbol.iterator || "@@iterator", asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator", toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; function define(obj, key, value) { return Object.defineProperty(obj, key, { value: value, enumerable: !0, configurable: !0, writable: !0 }), obj[key]; } try { define({}, ""); } catch (err) { define = function define(obj, key, value) { return obj[key] = value; }; } function wrap(innerFn, outerFn, self, tryLocsList) { var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator, generator = Object.create(protoGenerator.prototype), context = new Context(tryLocsList || []); return defineProperty(generator, "_invoke", { value: makeInvokeMethod(innerFn, self, context) }), generator; } function tryCatch(fn, obj, arg) { try { return { type: "normal", arg: fn.call(obj, arg) }; } catch (err) { return { type: "throw", arg: err }; } } exports.wrap = wrap; var ContinueSentinel = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var IteratorPrototype = {}; define(IteratorPrototype, iteratorSymbol, function () { return this; }); var getProto = Object.getPrototypeOf, NativeIteratorPrototype = getProto && getProto(getProto(values([]))); NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype); var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function (method) { define(prototype, method, function (arg) { return this._invoke(method, arg); }); }); } function AsyncIterator(generator, PromiseImpl) { function invoke(method, arg, resolve, reject) { var record = tryCatch(generator[method], generator, arg); if ("throw" !== record.type) { var result = record.arg, value = result.value; return value && "object" == _typeof(value) && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) { invoke("next", value, resolve, reject); }, function (err) { invoke("throw", err, resolve, reject); }) : PromiseImpl.resolve(value).then(function (unwrapped) { result.value = unwrapped, resolve(result); }, function (error) { return invoke("throw", error, resolve, reject); }); } reject(record.arg); } var previousPromise; defineProperty(this, "_invoke", { value: function value(method, arg) { function callInvokeWithMethodAndArg() { return new PromiseImpl(function (resolve, reject) { invoke(method, arg, resolve, reject); }); } return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(innerFn, self, context) { var state = "suspendedStart"; return function (method, arg) { if ("executing" === state) throw new Error("Generator is already running"); if ("completed" === state) { if ("throw" === method) throw arg; return doneResult(); } for (context.method = method, context.arg = arg;;) { var delegate = context.delegate; if (delegate) { var delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === ContinueSentinel) continue; return delegateResult; } } if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) { if ("suspendedStart" === state) throw state = "completed", context.arg; context.dispatchException(context.arg); } else "return" === context.method && context.abrupt("return", context.arg); state = "executing"; var record = tryCatch(innerFn, self, context); if ("normal" === record.type) { if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue; return { value: record.arg, done: context.done }; } "throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg); } }; } function maybeInvokeDelegate(delegate, context) { var methodName = context.method, method = delegate.iterator[methodName]; if (undefined === method) return context.delegate = null, "throw" === methodName && delegate.iterator.return && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method) || "return" !== methodName && (context.method = "throw", context.arg = new TypeError("The iterator does not provide a '" + methodName + "' method")), ContinueSentinel; var record = tryCatch(method, delegate.iterator, context.arg); if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel; var info = record.arg; return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel); } function pushTryEntry(locs) { var entry = { tryLoc: locs[0] }; 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry); } function resetTryEntry(entry) { var record = entry.completion || {}; record.type = "normal", delete record.arg, entry.completion = record; } function Context(tryLocsList) { this.tryEntries = [{ tryLoc: "root" }], tryLocsList.forEach(pushTryEntry, this), this.reset(!0); } function values(iterable) { if (iterable) { var iteratorMethod = iterable[iteratorSymbol]; if (iteratorMethod) return iteratorMethod.call(iterable); if ("function" == typeof iterable.next) return iterable; if (!isNaN(iterable.length)) { var i = -1, next = function next() { for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next; return next.value = undefined, next.done = !0, next; }; return next.next = next; } } return { next: doneResult }; } function doneResult() { return { value: undefined, done: !0 }; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, defineProperty(Gp, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), defineProperty(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) { var ctor = "function" == typeof genFun && genFun.constructor; return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name)); }, exports.mark = function (genFun) { return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun; }, exports.awrap = function (arg) { return { __await: arg }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () { return this; }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { void 0 === PromiseImpl && (PromiseImpl = Promise); var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) { return result.done ? result.value : iter.next(); }); }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () { return this; }), define(Gp, "toString", function () { return "[object Generator]"; }), exports.keys = function (val) { var object = Object(val), keys = []; for (var key in object) keys.push(key); return keys.reverse(), function next() { for (; keys.length;) { var key = keys.pop(); if (key in object) return next.value = key, next.done = !1, next; } return next.done = !0, next; }; }, exports.values = values, Context.prototype = { constructor: Context, reset: function reset(skipTempReset) { if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined); }, stop: function stop() { this.done = !0; var rootRecord = this.tryEntries[0].completion; if ("throw" === rootRecord.type) throw rootRecord.arg; return this.rval; }, dispatchException: function dispatchException(exception) { if (this.done) throw exception; var context = this; function handle(loc, caught) { return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught; } for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i], record = entry.completion; if ("root" === entry.tryLoc) return handle("end"); if (entry.tryLoc <= this.prev) { var hasCatch = hasOwn.call(entry, "catchLoc"), hasFinally = hasOwn.call(entry, "finallyLoc"); if (hasCatch && hasFinally) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } else if (hasCatch) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); } else { if (!hasFinally) throw new Error("try statement without catch or finally"); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } } } }, abrupt: function abrupt(type, arg) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { var finallyEntry = entry; break; } } finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null); var record = finallyEntry ? finallyEntry.completion : {}; return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record); }, complete: function complete(record, afterLoc) { if ("throw" === record.type) throw record.arg; return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel; }, finish: function finish(finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel; } }, catch: function _catch(tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { var record = entry.completion; if ("throw" === record.type) { var thrown = record.arg; resetTryEntry(entry); } return thrown; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(iterable, resultName, nextLoc) { return this.delegate = { iterator: values(iterable), resultName: resultName, nextLoc: nextLoc }, "next" === this.method && (this.arg = undefined), ContinueSentinel; } }, exports; } +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } import stripAnsi from '@umijs/utils/compiled/strip-ansi'; diff --git a/packages/preset-umi/.fatherrc.ts b/packages/preset-umi/.fatherrc.ts index 3305dd5a74a7..b976741a0db4 100644 --- a/packages/preset-umi/.fatherrc.ts +++ b/packages/preset-umi/.fatherrc.ts @@ -2,4 +2,18 @@ import { defineConfig } from 'father'; export default defineConfig({ extends: '../../.fatherrc.base.ts', + cjs: { + ignores: ['src/client/*'], + }, + umd: { + entry: 'src/client/preloadRouteFilesScp.ts', + output: 'templates/routePreloadOnLoad', + chainWebpack(memo) { + memo.output.filename('preloadRouteFilesScp.js'); + memo.output.delete('libraryTarget'); + memo.output.iife(true); + + return memo; + }, + }, }); diff --git a/packages/preset-umi/src/client/preloadRouteFilesScp.ts b/packages/preset-umi/src/client/preloadRouteFilesScp.ts new file mode 100644 index 000000000000..7232620e8c63 --- /dev/null +++ b/packages/preset-umi/src/client/preloadRouteFilesScp.ts @@ -0,0 +1,49 @@ +/** + * NOTE: DO NOT USE ADVANCED SYNTAX IN THIS FILE, TO AVOID INSERT HELPERS TO REDUCE SCRIPT SIZE. + */ + +import { getPreloadRouteFiles } from '../features/routePreloadOnLoad/utils'; + +// always add trailing slash for base +const basename = '{{basename}}'.replace(/([^/])$/, '$1/'); +const publicPath = '{{publicPath}}'; +const pathname = location.pathname; +const routePath = + pathname.startsWith(basename) && + decodeURI(`/${pathname.slice(basename.length)}`); + +// skip preload if basename not match +if (routePath) { + const map = '{{routeChunkFilesMap}}' as any; + const doc = document; + const head = doc.head; + const createElement = doc.createElement.bind(doc); + const files = getPreloadRouteFiles(routePath, map, { + publicPath, + }); + + files?.forEach((file) => { + const type = file.type; + const url = file.url; + let tag: HTMLLinkElement | HTMLScriptElement; + + if (type === 'js') { + tag = createElement('script'); + tag.src = url; + tag.async = true; + } else if (type === 'css') { + tag = createElement('link'); + tag.href = url; + tag.rel = 'preload'; + tag.as = 'style'; + } else { + return; + } + + file.attrs.forEach((attr) => { + tag.setAttribute(attr[0], attr[1] || ''); + }); + + head.appendChild(tag); + }); +} diff --git a/packages/preset-umi/src/features/okam/okam.ts b/packages/preset-umi/src/features/okam/okam.ts index 33b843f90763..f908338d06fa 100644 --- a/packages/preset-umi/src/features/okam/okam.ts +++ b/packages/preset-umi/src/features/okam/okam.ts @@ -2,11 +2,19 @@ import { checkVersion } from '@umijs/utils'; import { IApi } from '../../types'; export default (api: IApi) => { + api.describe({ + enableBy: () => Boolean(process.env.OKAM), + }); + api.onCheck(() => { // mako 仅支持 node 16+ // ref: https://github.com/umijs/mako/issues/300 - if (api.userConfig.mako) { - checkVersion(16, `Node 16 is required when using mako.`); - } + checkVersion(16, `Node 16 is required when using mako.`); + }); + + api.modifyAppData((memo) => { + memo.bundler = 'mako'; + + return memo; }); }; diff --git a/packages/preset-umi/src/features/routePreloadOnLoad/routePreloadOnLoad.ts b/packages/preset-umi/src/features/routePreloadOnLoad/routePreloadOnLoad.ts new file mode 100644 index 000000000000..d74d69a5c71e --- /dev/null +++ b/packages/preset-umi/src/features/routePreloadOnLoad/routePreloadOnLoad.ts @@ -0,0 +1,271 @@ +import type { StatsCompilation } from '@umijs/bundler-webpack/compiled/webpack'; +import { lodash, logger, winPath } from '@umijs/utils'; +import { readFileSync } from 'fs'; +import { dirname, isAbsolute, join, relative } from 'path'; +import { TEMPLATES_DIR } from '../../constants'; +import { createResolver } from '../../libs/scan'; +import type { IApi, IRoute } from '../../types'; +import { PRELOAD_ROUTE_MAP_SCP_TYPE } from './utils'; + +export interface IRouteChunkFilesMap { + /** + * script attr prefix (package.json name) + */ + p: string; + /** + * bundler type + */ + b: string; + /** + * all chunk files + */ + f: [string, string | number][]; + /** + * route files index map + */ + r: Record; +} + +/** + * forked from: https://github.com/remix-run/react-router/blob/fb0f1f94778f4762989930db209e6a111504aa63/packages/router/utils.ts#L688C1-L719 + */ +const routeScoreCache = new Map(); + +function computeRouteScore(path: string): number { + if (!routeScoreCache.get(path)) { + const paramRe = /^:[\w-]+$/; + const dynamicSegmentValue = 3; + const emptySegmentValue = 1; + const staticSegmentValue = 10; + const splatPenalty = -2; + const isSplat = (s: string) => s === '*'; + let segments = path.split('/'); + let initialScore = segments.length; + if (segments.some(isSplat)) { + initialScore += splatPenalty; + } + + routeScoreCache.set( + path, + segments + .filter((s) => !isSplat(s)) + .reduce( + (score, segment) => + score + + (paramRe.test(segment) + ? dynamicSegmentValue + : segment === '' + ? emptySegmentValue + : staticSegmentValue), + initialScore, + ), + ); + } + + return routeScoreCache.get(path)!; +} + +export default (api: IApi) => { + let routeChunkFilesMap: IRouteChunkFilesMap; + + // enable when package name available + // because preload script use package name as attribute prefix value + api.describe({ + enableBy: () => Boolean(api.pkg.name), + }); + + api.addHTMLHeadScripts(() => { + if (api.name === 'build') { + // internal tern app use map mode + return api.config.tern + ? // map mode + [ + { + type: PRELOAD_ROUTE_MAP_SCP_TYPE, + content: JSON.stringify(routeChunkFilesMap), + }, + ] + : // script mode + [ + { + content: readFileSync( + join( + TEMPLATES_DIR, + 'routePreloadOnLoad/preloadRouteFilesScp.js', + ), + 'utf-8', + ) + .replace( + '"{{routeChunkFilesMap}}"', + JSON.stringify(routeChunkFilesMap), + ) + .replace('{{basename}}', api.config.base) + .replace( + '"{{publicPath}}"', + `${ + // handle runtimePublicPath + api.config.runtimePublicPath ? 'window.publicPath||' : '' + }"${api.config.publicPath}"`, + ), + }, + ]; + } + + return []; + }); + + api.onBuildComplete(async ({ err, stats }) => { + if (!err && !stats.hasErrors()) { + const routeModulePath = join(api.paths.absTmpPath, 'core/route.tsx'); + const routeModuleName = winPath(relative(api.cwd, routeModulePath)); + const resolver = createResolver({ alias: api.config.alias }); + const { chunks = [] } = stats.toJson + ? // webpack + stats.toJson() + : // mako + (stats.compilation as unknown as StatsCompilation); + + // collect all chunk files and file chunks indexes + const chunkFiles: Record = + {}; + const fileChunksMap: Record< + string, + { files: string[]; indexes?: number[] } + > = {}; + const pickPreloadFiles = (files: string[]) => + files.filter((f) => f.endsWith('.js') || f.endsWith('.css')); + + for (const chunk of chunks) { + const routeOrigins = chunk.origins!.filter((origin) => + origin.moduleName?.endsWith(routeModuleName), + ); + + for (const origin of routeOrigins) { + const queue = [chunk.id!].concat(chunk.siblings!); + const visited: typeof queue = []; + const files: string[] = []; + let fileAbsPath: string; + + // resolve route file path + try { + fileAbsPath = await resolver.resolve( + dirname(routeModulePath), + origin.request!, + ); + } catch (err) { + logger.error( + `[routePreloadOnLoad]: route file resolve error, cannot preload for ${origin.request!}`, + ); + continue; + } + + // collect all related chunk files for route file + while (queue.length) { + const currentId = queue.shift()!; + + if (!visited.includes(currentId)) { + const currentChunk = chunks.find((c) => c.id === currentId)!; + + // skip sibling entry chunk + if (currentChunk.entry) continue; + + // merge files + pickPreloadFiles(chunk.files!).forEach((file) => { + chunkFiles[file] ??= { + index: Object.keys(chunkFiles).length, + id: currentId, + }; + }); + + // merge files + files.push(...pickPreloadFiles(currentChunk.files!)); + + // continue to search sibling chunks + queue.push(...currentChunk.siblings!); + + // mark as visited + visited.push(currentId); + } + } + + fileChunksMap[fileAbsPath] = { files }; + } + } + + // generate indexes for file chunks + Object.values(fileChunksMap).forEach((item) => { + item.indexes = item.files.map((f) => chunkFiles[f].index); + }); + + // generate map for path -> files (include parent route files) + const routeFilesMap: Record = {}; + + for (const route of Object.values(api.appData.routes)) { + // skip redirect route + if (!route.file) continue; + + let current = route; + const files: string[] = []; + + do { + // skip inline function route file + if (current.file && !current.file.startsWith('(')) { + try { + const fileReqPath = + isAbsolute(current.file) || current.file.startsWith('@/') + ? current.file + : // a => ./a + // .a => ./.a + current.file.replace(/^([^.]|\.[^./])/, './$1'); + const fileAbsPath = await resolver.resolve( + api.paths.absPagesPath, + fileReqPath, + ); + + files.push(fileAbsPath); + } catch { + logger.error( + `[routePreloadOnLoad]: route file resolve error, cannot preload for ${current.file}`, + ); + } + } + current = current.parentId && api.appData.routes[current.parentId]; + } while (current); + + const indexes = files.reduce((indexes, file) => { + // why fileChunksMap[file] may not existing? + // because Mako will merge minimal async chunk into entry chunk + // so the merged route chunk does not has to preload + return indexes.concat(fileChunksMap[file]?.indexes || []); + }, []); + const { absPath } = route; + + routeFilesMap[absPath] = + // why different route may has same absPath? + // because umi implement route.wrappers via nested routes way, the wrapper route will has same absPath with the nested route + // so we always select the longest file indexes for the nested route + !routeFilesMap[absPath] || + routeFilesMap[absPath].length < indexes.length + ? indexes + : routeFilesMap[absPath]; + } + + routeChunkFilesMap = { + p: api.pkg.name!, + b: api.appData.bundler!, + f: Object.entries(chunkFiles) + .sort((a, b) => a[1].index - b[1].index) + .map(([k, { id }]) => [k, id]), + // sort similar to react-router@6 + r: lodash(routeFilesMap) + .toPairs() + .sort( + ([a]: [string, number[]], [b]: [string, number[]]) => + computeRouteScore(a) - computeRouteScore(b), + ) + .fromPairs() + .value() as any, + }; + } + }); +}; diff --git a/packages/preset-umi/src/features/routePreloadOnLoad/utils.ts b/packages/preset-umi/src/features/routePreloadOnLoad/utils.ts new file mode 100644 index 000000000000..90076221f307 --- /dev/null +++ b/packages/preset-umi/src/features/routePreloadOnLoad/utils.ts @@ -0,0 +1,46 @@ +/** + * NOTE: DO NOT USE ADVANCED SYNTAX IN THIS FILE, TO AVOID INSERT HELPERS TO REDUCE SCRIPT SIZE. + */ + +import type { IRouteChunkFilesMap } from './routePreloadOnLoad'; + +interface IPreloadRouteFile { + type: 'js' | 'css' | (string & {}); + url: string; + attrs: ([string, string] | [string])[]; +} + +export const PRELOAD_ROUTE_MAP_SCP_TYPE = 'umi-route-chunk-files-map'; + +export function getPreloadRouteFiles( + path: string, + map: IRouteChunkFilesMap, + opts: { publicPath: string }, +): IPreloadRouteFile[] | undefined { + const matched: IRouteChunkFilesMap['r'][string] | undefined = + // search for static route + map.r[path] || + // search for dynamic route + Object.entries(map.r).find((p) => { + const route = p[0]; + const reg = new RegExp( + // replace /:id to /[^/]+ + // replace /* to /.+ + `^${route.replace(/\/:[^/]+/g, '/[^/]+').replace('/*', '/.+')}$`, + ); + + return reg.test(path); + })?.[1]; + + return matched?.map((i) => { + const id = map.f[i][1]; + const file = map.f[i][0]; + const ext = file.split('.').pop() as IPreloadRouteFile['type']; + + return { + type: ext, + url: `${opts.publicPath}${file}`, + attrs: [[`data-${map.b}`, `${map.p}:${id}`]], + }; + }); +} diff --git a/packages/preset-umi/src/index.ts b/packages/preset-umi/src/index.ts index a559625881d5..8787f6065108 100644 --- a/packages/preset-umi/src/index.ts +++ b/packages/preset-umi/src/index.ts @@ -58,6 +58,7 @@ export default () => { require.resolve('./features/swc/swc'), require.resolve('./features/ui/ui'), require.resolve('./features/hmrGuardian/hmrGuardian'), + require.resolve('./features/routePreloadOnLoad/routePreloadOnLoad'), // commands require.resolve('./commands/build'), diff --git a/packages/preset-umi/templates/routePreloadOnLoad/preloadRouteFilesScp.js b/packages/preset-umi/templates/routePreloadOnLoad/preloadRouteFilesScp.js new file mode 100644 index 000000000000..5b0001c71c9d --- /dev/null +++ b/packages/preset-umi/templates/routePreloadOnLoad/preloadRouteFilesScp.js @@ -0,0 +1 @@ +!function(){"use strict";var t="{{basename}}".replace(/([^/])$/,"$1/"),e=location.pathname,n=e.startsWith(t)&&decodeURI("/".concat(e.slice(t.length)));if(n){var a=document,c=a.head,r=a.createElement.bind(a),i=function(t,e,n){var a,c=e.r[t]||(null===(a=Object.entries(e.r).find((function(e){var n=e[0];return new RegExp("^".concat(n.replace(/\/:[^/]+/g,"/[^/]+").replace("/*","/.+"),"$")).test(t)})))||void 0===a?void 0:a[1]);return null==c?void 0:c.map((function(t){var a=e.f[t][1],c=e.f[t][0];return{type:c.split(".").pop(),url:"".concat(n.publicPath).concat(c),attrs:[["data-".concat(e.b),"".concat(e.p,":").concat(a)]]}}))}(n,"{{routeChunkFilesMap}}",{publicPath:"{{publicPath}}"});null==i||i.forEach((function(t){var e,n=t.type,a=t.url;if("js"===n)(e=r("script")).src=a,e.async=!0;else{if("css"!==n)return;(e=r("link")).href=a,e.rel="preload",e.as="style"}t.attrs.forEach((function(t){e.setAttribute(t[0],t[1]||"")})),c.appendChild(e)}))}}(); \ No newline at end of file