版博士V2.0程序
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

1629 linhas
51 KiB

  1. "use strict";
  2. var __create = Object.create;
  3. var __defProp = Object.defineProperty;
  4. var __defProps = Object.defineProperties;
  5. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  6. var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
  7. var __getOwnPropNames = Object.getOwnPropertyNames;
  8. var __getOwnPropSymbols = Object.getOwnPropertySymbols;
  9. var __getProtoOf = Object.getPrototypeOf;
  10. var __hasOwnProp = Object.prototype.hasOwnProperty;
  11. var __propIsEnum = Object.prototype.propertyIsEnumerable;
  12. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  13. var __spreadValues = (a, b) => {
  14. for (var prop in b || (b = {}))
  15. if (__hasOwnProp.call(b, prop))
  16. __defNormalProp(a, prop, b[prop]);
  17. if (__getOwnPropSymbols)
  18. for (var prop of __getOwnPropSymbols(b)) {
  19. if (__propIsEnum.call(b, prop))
  20. __defNormalProp(a, prop, b[prop]);
  21. }
  22. return a;
  23. };
  24. var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
  25. var __export = (target, all) => {
  26. for (var name in all)
  27. __defProp(target, name, { get: all[name], enumerable: true });
  28. };
  29. var __copyProps = (to, from, except, desc) => {
  30. if (from && typeof from === "object" || typeof from === "function") {
  31. for (let key of __getOwnPropNames(from))
  32. if (!__hasOwnProp.call(to, key) && key !== except)
  33. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  34. }
  35. return to;
  36. };
  37. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  38. // If the importer is in node compatibility mode or this is not an ESM
  39. // file that has been converted to a CommonJS file using a Babel-
  40. // compatible transform (i.e. "__esModule" has not been set), then set
  41. // "default" to the CommonJS "module.exports" for node compatibility.
  42. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  43. mod
  44. ));
  45. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  46. // src/webpack.ts
  47. var webpack_exports = {};
  48. __export(webpack_exports, {
  49. default: () => webpack_default
  50. });
  51. module.exports = __toCommonJS(webpack_exports);
  52. // src/index.ts
  53. var import_unplugin = require("unplugin");
  54. // src/core/utils.ts
  55. var import_scule = require("scule");
  56. function warn(msg, type = "warn") {
  57. console[type](`\u26A0\uFE0F [unplugin-vue-router]: ${msg}`);
  58. }
  59. function logTree(tree, log) {
  60. log(printTree(tree));
  61. }
  62. var MAX_LEVEL = 1e3;
  63. function printTree(tree, level = 0, parentPre = "", treeStr = "") {
  64. if (typeof tree !== "object" || level >= MAX_LEVEL)
  65. return "";
  66. if (tree instanceof Map) {
  67. const total = tree.size;
  68. let index = 0;
  69. for (const [_key, child] of tree) {
  70. const hasNext = index++ < total - 1;
  71. const { children } = child;
  72. treeStr += `${`${parentPre}${hasNext ? "\u251C" : "\u2514"}${"\u2500" + (children.size > 0 ? "\u252C" : "")} `}${child}
  73. `;
  74. if (children) {
  75. treeStr += printTree(
  76. children,
  77. level + 1,
  78. `${parentPre}${hasNext ? "\u2502" : " "} `
  79. );
  80. }
  81. }
  82. } else {
  83. const children = tree.children;
  84. treeStr = `${tree}
  85. `;
  86. if (children) {
  87. treeStr += printTree(children, level + 1);
  88. }
  89. }
  90. return treeStr;
  91. }
  92. var isArray = Array.isArray;
  93. function trimExtension(path, extensions) {
  94. for (const extension of extensions) {
  95. const lastDot = path.endsWith(extension) ? -extension.length : 0;
  96. if (lastDot < 0) {
  97. return path.slice(0, lastDot);
  98. }
  99. }
  100. return path;
  101. }
  102. function throttle(fn, wait, initialWait) {
  103. let pendingExecutionTimeout = null;
  104. let pendingExecution = false;
  105. let executionTimeout = null;
  106. return () => {
  107. if (pendingExecutionTimeout == null) {
  108. pendingExecutionTimeout = setTimeout(() => {
  109. pendingExecutionTimeout = null;
  110. if (pendingExecution) {
  111. pendingExecution = false;
  112. fn();
  113. }
  114. }, wait);
  115. executionTimeout = setTimeout(() => {
  116. executionTimeout = null;
  117. fn();
  118. }, initialWait);
  119. } else if (executionTimeout == null) {
  120. pendingExecution = true;
  121. }
  122. };
  123. }
  124. var LEADING_SLASH_RE = /^\//;
  125. var TRAILING_SLASH_RE = /\/$/;
  126. function joinPath(...paths) {
  127. let result = "";
  128. for (const path of paths) {
  129. result = result.replace(TRAILING_SLASH_RE, "") + // check path to avoid adding a trailing slash when joining an empty string
  130. (path && "/" + path.replace(LEADING_SLASH_RE, ""));
  131. }
  132. return result;
  133. }
  134. function getFileBasedRouteName(node) {
  135. if (!node.parent)
  136. return "";
  137. return getFileBasedRouteName(node.parent) + "/" + (node.value.rawSegment === "index" ? "" : node.value.rawSegment);
  138. }
  139. function mergeRouteRecordOverride(a, b) {
  140. var _a;
  141. const merged = {};
  142. const keys = [
  143. .../* @__PURE__ */ new Set([
  144. ...Object.keys(a),
  145. ...Object.keys(b)
  146. ])
  147. ];
  148. for (const key of keys) {
  149. if (key === "alias") {
  150. const newAlias = [];
  151. merged[key] = newAlias.concat(a.alias || [], b.alias || []);
  152. } else if (key === "meta") {
  153. merged[key] = mergeDeep(a[key] || {}, b[key] || {});
  154. } else {
  155. merged[key] = (_a = b[key]) != null ? _a : a[key];
  156. }
  157. }
  158. return merged;
  159. }
  160. function isObject(obj) {
  161. return obj && typeof obj === "object";
  162. }
  163. function mergeDeep(...objects) {
  164. return objects.reduce((prev, obj) => {
  165. Object.keys(obj).forEach((key) => {
  166. const pVal = prev[key];
  167. const oVal = obj[key];
  168. if (Array.isArray(pVal) && Array.isArray(oVal)) {
  169. prev[key] = pVal.concat(...oVal);
  170. } else if (isObject(pVal) && isObject(oVal)) {
  171. prev[key] = mergeDeep(pVal, oVal);
  172. } else {
  173. prev[key] = oVal;
  174. }
  175. });
  176. return prev;
  177. }, {});
  178. }
  179. function asRoutePath({ src, path = "" }, filePath) {
  180. return (
  181. // add the path prefix if any
  182. path + // remove the absolute path to the pages folder
  183. filePath.slice(src.length + 1)
  184. );
  185. }
  186. function appendExtensionListToPattern(filePatterns, extensions) {
  187. const extensionPattern = extensions.length === 1 ? extensions[0] : `.{${extensions.map((extension) => extension.replace(".", "")).join(",")}}`;
  188. return Array.isArray(filePatterns) ? filePatterns.map((filePattern) => `${filePattern}${extensionPattern}`) : `${filePatterns}${extensionPattern}`;
  189. }
  190. // src/core/treeNodeValue.ts
  191. var EDITS_OVERRIDE_NAME = "@@edits";
  192. var _TreeNodeValueBase = class {
  193. constructor(rawSegment, parent, pathSegment = rawSegment, subSegments = [pathSegment]) {
  194. /**
  195. * Overrides defined by each file. The map is necessary to handle named views.
  196. */
  197. this._overrides = /* @__PURE__ */ new Map();
  198. /**
  199. * Should we add the loader guard to the route record.
  200. */
  201. this.includeLoaderGuard = false;
  202. /**
  203. * View name (Vue Router feature) mapped to their corresponding file. By default, the view name is `default` unless
  204. * specified with a `@` e.g. `index@aux.vue` will have a view name of `aux`.
  205. */
  206. this.components = /* @__PURE__ */ new Map();
  207. this._type = 0;
  208. this.rawSegment = rawSegment;
  209. this.pathSegment = pathSegment;
  210. this.subSegments = subSegments;
  211. const parentPath = parent == null ? void 0 : parent.path;
  212. this.path = // both the root record and the index record have a path of /
  213. (!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment);
  214. }
  215. toString() {
  216. return this.pathSegment || "<index>";
  217. }
  218. isParam() {
  219. return !!(this._type & 1 /* param */);
  220. }
  221. isStatic() {
  222. return this._type === 0 /* static */;
  223. }
  224. get overrides() {
  225. return [...this._overrides.entries()].sort(
  226. ([nameA], [nameB]) => nameA === nameB ? 0 : (
  227. // EDITS_OVERRIDE_NAME should always be last
  228. nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1
  229. )
  230. ).reduce((acc, [_path, routeBlock]) => {
  231. return mergeRouteRecordOverride(acc, routeBlock);
  232. }, {});
  233. }
  234. setOverride(path, routeBlock) {
  235. this._overrides.set(path, routeBlock || {});
  236. }
  237. /**
  238. * Remove all overrides for a given key.
  239. *
  240. * @param key - key to remove from the override
  241. */
  242. removeOverride(key) {
  243. this._overrides.forEach((routeBlock) => {
  244. delete routeBlock[key];
  245. });
  246. }
  247. mergeOverride(path, routeBlock) {
  248. const existing = this._overrides.get(path) || {};
  249. this._overrides.set(path, mergeRouteRecordOverride(existing, routeBlock));
  250. }
  251. addEditOverride(routeBlock) {
  252. return this.mergeOverride(EDITS_OVERRIDE_NAME, routeBlock);
  253. }
  254. setEditOverride(key, value) {
  255. if (!this._overrides.has(EDITS_OVERRIDE_NAME)) {
  256. this._overrides.set(EDITS_OVERRIDE_NAME, {});
  257. }
  258. const existing = this._overrides.get(EDITS_OVERRIDE_NAME);
  259. existing[key] = value;
  260. }
  261. };
  262. var TreeNodeValueStatic = class extends _TreeNodeValueBase {
  263. constructor(rawSegment, parent, pathSegment = rawSegment) {
  264. super(rawSegment, parent, pathSegment);
  265. this._type = 0 /* static */;
  266. }
  267. };
  268. var TreeNodeValueParam = class extends _TreeNodeValueBase {
  269. constructor(rawSegment, parent, params, pathSegment, subSegments) {
  270. super(rawSegment, parent, pathSegment, subSegments);
  271. this._type = 1 /* param */;
  272. this.params = params;
  273. }
  274. };
  275. function createTreeNodeValue(segment, parent) {
  276. if (!segment || segment === "index") {
  277. return new TreeNodeValueStatic(segment, parent, "");
  278. }
  279. const [pathSegment, params, subSegments] = parseSegment(segment);
  280. if (params.length) {
  281. return new TreeNodeValueParam(
  282. segment,
  283. parent,
  284. params,
  285. pathSegment,
  286. subSegments
  287. );
  288. }
  289. return new TreeNodeValueStatic(segment, parent, pathSegment);
  290. }
  291. function parseSegment(segment) {
  292. let buffer = "";
  293. let state = 0 /* static */;
  294. const params = [];
  295. let pathSegment = "";
  296. const subSegments = [];
  297. let currentTreeRouteParam = createEmptyRouteParam();
  298. function consumeBuffer() {
  299. if (state === 0 /* static */) {
  300. pathSegment += buffer;
  301. subSegments.push(buffer);
  302. } else if (state === 3 /* modifier */) {
  303. currentTreeRouteParam.paramName = buffer;
  304. currentTreeRouteParam.modifier = currentTreeRouteParam.optional ? currentTreeRouteParam.repeatable ? "*" : "?" : currentTreeRouteParam.repeatable ? "+" : "";
  305. buffer = "";
  306. pathSegment += `:${currentTreeRouteParam.paramName}${currentTreeRouteParam.isSplat ? "(.*)" : ""}${currentTreeRouteParam.modifier}`;
  307. params.push(currentTreeRouteParam);
  308. subSegments.push(currentTreeRouteParam);
  309. currentTreeRouteParam = createEmptyRouteParam();
  310. }
  311. buffer = "";
  312. }
  313. for (let pos = 0; pos < segment.length; pos++) {
  314. const c = segment[pos];
  315. if (state === 0 /* static */) {
  316. if (c === "[") {
  317. consumeBuffer();
  318. state = 1 /* paramOptional */;
  319. } else {
  320. buffer += c === "." ? "/" : c;
  321. }
  322. } else if (state === 1 /* paramOptional */) {
  323. if (c === "[") {
  324. currentTreeRouteParam.optional = true;
  325. } else if (c === ".") {
  326. currentTreeRouteParam.isSplat = true;
  327. pos += 2;
  328. } else {
  329. buffer += c;
  330. }
  331. state = 2 /* param */;
  332. } else if (state === 2 /* param */) {
  333. if (c === "]") {
  334. if (currentTreeRouteParam.optional) {
  335. pos++;
  336. }
  337. state = 3 /* modifier */;
  338. } else if (c === ".") {
  339. currentTreeRouteParam.isSplat = true;
  340. pos += 2;
  341. } else {
  342. buffer += c;
  343. }
  344. } else if (state === 3 /* modifier */) {
  345. if (c === "+") {
  346. currentTreeRouteParam.repeatable = true;
  347. } else {
  348. pos--;
  349. }
  350. consumeBuffer();
  351. state = 0 /* static */;
  352. }
  353. }
  354. if (state === 2 /* param */ || state === 1 /* paramOptional */) {
  355. throw new Error(`Invalid segment: "${segment}"`);
  356. }
  357. if (buffer) {
  358. consumeBuffer();
  359. }
  360. return [pathSegment, params, subSegments];
  361. }
  362. function createEmptyRouteParam() {
  363. return {
  364. paramName: "",
  365. modifier: "",
  366. optional: false,
  367. repeatable: false,
  368. isSplat: false
  369. };
  370. }
  371. // src/core/tree.ts
  372. var TreeNode = class {
  373. constructor(options, filePath, parent) {
  374. /**
  375. * children of the node
  376. */
  377. this.children = /* @__PURE__ */ new Map();
  378. /**
  379. * Should this page import the page info
  380. */
  381. this.hasDefinePage = false;
  382. this.options = options;
  383. this.parent = parent;
  384. this.value = createTreeNodeValue(filePath, parent == null ? void 0 : parent.value);
  385. }
  386. /**
  387. * Adds a path to the tree. `path` cannot start with a `/`.
  388. *
  389. * @param path - path segment to insert. **It must contain the file extension** this allows to
  390. * differentiate between folders and files.
  391. * @param filePath - file path, defaults to path for convenience and testing
  392. */
  393. insert(path, filePath = path) {
  394. const { tail, segment, viewName, isComponent } = splitFilePath(
  395. path,
  396. this.options
  397. );
  398. if (!this.children.has(segment)) {
  399. this.children.set(segment, new TreeNode(this.options, segment, this));
  400. }
  401. const child = this.children.get(segment);
  402. if (isComponent) {
  403. child.value.components.set(viewName, filePath);
  404. }
  405. if (tail) {
  406. return child.insert(tail, filePath);
  407. }
  408. return child;
  409. }
  410. setCustomRouteBlock(path, routeBlock) {
  411. this.value.setOverride(path, routeBlock);
  412. }
  413. getSortedChildren() {
  414. return Array.from(this.children.values()).sort(
  415. (a, b) => a.path.localeCompare(b.path)
  416. );
  417. }
  418. /**
  419. * Delete and detach itself from the tree.
  420. */
  421. delete() {
  422. if (!this.parent) {
  423. throw new Error("Cannot delete the root node.");
  424. }
  425. this.parent.children.delete(this.value.rawSegment);
  426. this.parent = void 0;
  427. }
  428. /**
  429. * Remove a route from the tree. The path shouldn't start with a `/` but it can be a nested one. e.g. `foo/bar.vue`.
  430. * The `path` should be relative to the page folder.
  431. *
  432. * @param path - path segment of the file
  433. */
  434. remove(path) {
  435. const { tail, segment, viewName, isComponent } = splitFilePath(
  436. path,
  437. this.options
  438. );
  439. const child = this.children.get(segment);
  440. if (!child) {
  441. throw new Error(
  442. `Cannot Delete "${path}". "${segment}" not found at "${this.path}".`
  443. );
  444. }
  445. if (tail) {
  446. child.remove(tail);
  447. if (child.children.size === 0 && child.value.components.size === 0) {
  448. this.children.delete(segment);
  449. }
  450. } else {
  451. if (isComponent) {
  452. child.value.components.delete(viewName);
  453. }
  454. if (child.children.size === 0 && child.value.components.size === 0) {
  455. this.children.delete(segment);
  456. }
  457. }
  458. }
  459. /**
  460. * Returns the route path of the node without parent paths. If the path was overridden, it returns the override.
  461. */
  462. get path() {
  463. var _a, _b;
  464. return (_b = this.value.overrides.path) != null ? _b : (((_a = this.parent) == null ? void 0 : _a.isRoot()) ? "/" : "") + this.value.pathSegment;
  465. }
  466. /**
  467. * Returns the route path of the node including parent paths.
  468. */
  469. get fullPath() {
  470. var _a;
  471. return (_a = this.value.overrides.path) != null ? _a : this.value.path;
  472. }
  473. /**
  474. * Returns the route name of the node. If the name was overridden, it returns the override.
  475. */
  476. get name() {
  477. return this.value.overrides.name || this.options.getRouteName(this);
  478. }
  479. /**
  480. * Returns the meta property as an object.
  481. */
  482. get metaAsObject() {
  483. const meta = __spreadValues({}, this.value.overrides.meta);
  484. if (this.value.includeLoaderGuard) {
  485. meta._loaderGuard = true;
  486. }
  487. return meta;
  488. }
  489. /**
  490. * Returns the JSON string of the meta object of the node. If the meta was overridden, it returns the override. If
  491. * there is no override, it returns an empty string.
  492. */
  493. get meta() {
  494. const overrideMeta = this.metaAsObject;
  495. return Object.keys(overrideMeta).length > 0 ? JSON.stringify(overrideMeta, null, 2) : "";
  496. }
  497. get params() {
  498. const params = this.value.isParam() ? [...this.value.params] : [];
  499. let node = this.parent;
  500. while (node) {
  501. if (node.value.isParam()) {
  502. params.unshift(...node.value.params);
  503. }
  504. node = node.parent;
  505. }
  506. return params;
  507. }
  508. /**
  509. * Returns wether this tree node is the root node of the tree.
  510. *
  511. * @returns true if the node is the root node
  512. */
  513. isRoot() {
  514. return this.value.path === "/" && !this.value.components.size;
  515. }
  516. toString() {
  517. return `${this.value}${// either we have multiple names
  518. this.value.components.size > 1 || // or we have one name and it's not default
  519. this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`;
  520. }
  521. };
  522. var PrefixTree = class extends TreeNode {
  523. constructor(options) {
  524. super(options, "");
  525. this.map = /* @__PURE__ */ new Map();
  526. }
  527. insert(path, filePath = path) {
  528. const node = super.insert(path, filePath);
  529. this.map.set(filePath, node);
  530. return node;
  531. }
  532. getChild(filePath) {
  533. return this.map.get(filePath);
  534. }
  535. /**
  536. *
  537. * @param filePath -
  538. */
  539. removeChild(filePath) {
  540. if (this.map.has(filePath)) {
  541. this.map.get(filePath).delete();
  542. this.map.delete(filePath);
  543. }
  544. }
  545. };
  546. function createPrefixTree(options) {
  547. return new PrefixTree(options);
  548. }
  549. function splitFilePath(filePath, options) {
  550. const slashPos = filePath.indexOf("/");
  551. let head = slashPos < 0 ? filePath : filePath.slice(0, slashPos);
  552. const tail = slashPos < 0 ? "" : filePath.slice(slashPos + 1);
  553. let segment = head;
  554. if (!tail) {
  555. segment = trimExtension(head, options.extensions);
  556. }
  557. let viewName = "default";
  558. const namedSeparatorPos = segment.indexOf("@");
  559. if (namedSeparatorPos > 0) {
  560. viewName = segment.slice(namedSeparatorPos + 1);
  561. segment = segment.slice(0, namedSeparatorPos);
  562. }
  563. const isComponent = segment !== head;
  564. return {
  565. segment,
  566. tail,
  567. viewName,
  568. isComponent
  569. };
  570. }
  571. // src/core/context.ts
  572. var import_fs3 = require("fs");
  573. // src/codegen/generateRouteParams.ts
  574. function generateRouteParams(node, isRaw) {
  575. const nodeParams = node.params;
  576. return node.params.length > 0 ? `{ ${node.params.map(
  577. (param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `ParamValueZeroOrOne<${isRaw}>` : `ParamValue<${isRaw}>`)
  578. ).join(", ")} }` : (
  579. // no params allowed
  580. "Record<never, never>"
  581. );
  582. }
  583. // src/codegen/generateRouteMap.ts
  584. function generateRouteNamedMap(node) {
  585. if (node.isRoot()) {
  586. return `export interface RouteNamedMap {
  587. ${node.getSortedChildren().map(generateRouteNamedMap).join("")}}`;
  588. }
  589. return (
  590. // if the node has a filePath, it's a component, it has a routeName and it should be referenced in the RouteNamedMap
  591. // otherwise it should be skipped to avoid navigating to a route that doesn't render anything
  592. (node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)},
  593. ` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : "")
  594. );
  595. }
  596. function generateRouteRecordInfo(node) {
  597. return `RouteRecordInfo<'${node.name}', '${node.fullPath}', ${generateRouteParams(node, true)}, ${generateRouteParams(node, false)}>`;
  598. }
  599. // src/core/moduleConstants.ts
  600. var MODULE_VUE_ROUTER = "vue-router/auto";
  601. var MODULE_ROUTES_PATH = `${MODULE_VUE_ROUTER}/routes`;
  602. var VIRTUAL_PREFIX = "virtual:";
  603. var ROUTE_BLOCK_ID = `${VIRTUAL_PREFIX}/vue-router/auto/route-block`;
  604. function getVirtualId(id) {
  605. return id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : null;
  606. }
  607. var routeBlockQueryRE = /\?vue&type=route/;
  608. function asVirtualId(id) {
  609. return VIRTUAL_PREFIX + id;
  610. }
  611. // src/codegen/generateRouteRecords.ts
  612. function generateRouteRecord(node, options, importList, indent = 0) {
  613. if (node.value.path === "/" && indent === 0) {
  614. return `[
  615. ${node.getSortedChildren().map((child) => generateRouteRecord(child, options, importList, indent + 1)).join(",\n")}
  616. ]`;
  617. }
  618. const startIndent = " ".repeat(indent * 2);
  619. const indentStr = " ".repeat((indent + 1) * 2);
  620. const overrides = node.value.overrides;
  621. const routeRecord = `${startIndent}{
  622. ${indentStr}path: '${node.path}',
  623. ${indentStr}${node.value.components.size ? `name: '${node.name}',` : `/* internal name: '${node.name}' */`}
  624. ${// component
  625. indentStr}${node.value.components.size ? generateRouteRecordComponent(
  626. node,
  627. indentStr,
  628. options.importMode,
  629. importList
  630. ) : "/* no component */"}
  631. ${overrides.props != null ? indentStr + `props: ${overrides.props},
  632. ` : ""}${overrides.alias != null ? indentStr + `alias: ${JSON.stringify(overrides.alias)},
  633. ` : ""}${// children
  634. indentStr}${node.children.size > 0 ? `children: [
  635. ${node.getSortedChildren().map((child) => generateRouteRecord(child, options, importList, indent + 2)).join(",\n")}
  636. ${indentStr}],` : "/* no children */"}${formatMeta(node, indentStr)}
  637. ${startIndent}}`;
  638. if (node.hasDefinePage) {
  639. const definePageDataList = [];
  640. for (const [name, filePath] of node.value.components) {
  641. const pageDataImport = `_definePage_${name}_${importList.size}`;
  642. definePageDataList.push(pageDataImport);
  643. importList.set(pageDataImport, `${filePath}?definePage&vue`);
  644. }
  645. if (definePageDataList.length) {
  646. return ` _mergeRouteRecord(
  647. ${routeRecord},
  648. ${definePageDataList.join(",\n")}
  649. )`;
  650. }
  651. }
  652. return routeRecord;
  653. }
  654. function generateRouteRecordComponent(node, indentStr, importMode, importList) {
  655. const files = Array.from(node.value.components);
  656. const isDefaultExport = files.length === 1 && files[0][0] === "default";
  657. return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : (
  658. // files has at least one entry
  659. `components: {
  660. ${files.map(
  661. ([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport(
  662. path,
  663. importMode,
  664. importList
  665. )}`
  666. ).join(",\n")}
  667. ${indentStr}},`
  668. );
  669. }
  670. function generatePageImport(filepath, importMode, importList) {
  671. const mode = typeof importMode === "function" ? importMode(filepath) : importMode;
  672. if (mode === "async") {
  673. return `() => import('${filepath}')`;
  674. } else {
  675. const importName = `_page_${importList.size}`;
  676. importList.set(importName, filepath);
  677. return importName;
  678. }
  679. }
  680. function generateImportList(node, indentStr) {
  681. const files = Array.from(node.value.components);
  682. return `[
  683. ${files.map(([_key, path]) => `${indentStr} () => import('${path}')`).join(",\n")}
  684. ${indentStr}]`;
  685. }
  686. var LOADER_GUARD_RE = /['"]_loaderGuard['"]:.*$/;
  687. function formatMeta(node, indent) {
  688. const meta = node.meta;
  689. const formatted = meta && meta.split("\n").map(
  690. (line) => indent + line.replace(
  691. LOADER_GUARD_RE,
  692. "[_HasDataLoaderMeta]: " + generateImportList(node, indent + " ") + ","
  693. )
  694. ).join("\n");
  695. return formatted ? "\n" + indent + "meta: " + formatted.trimStart() : "";
  696. }
  697. // src/core/context.ts
  698. var import_fast_glob = __toESM(require("fast-glob"));
  699. var import_pathe2 = require("pathe");
  700. // src/core/customBlock.ts
  701. var import_compiler_sfc = require("@vue/compiler-sfc");
  702. var import_fs = require("fs");
  703. var import_json5 = __toESM(require("json5"));
  704. var import_yaml = require("yaml");
  705. async function getRouteBlock(path, options) {
  706. const content = await import_fs.promises.readFile(path, "utf8");
  707. const parsedSFC = await (0, import_compiler_sfc.parse)(content, { pad: "space" }).descriptor;
  708. const blockStr = parsedSFC == null ? void 0 : parsedSFC.customBlocks.find((b) => b.type === "route");
  709. if (!blockStr)
  710. return;
  711. let result = parseCustomBlock(blockStr, path, options);
  712. if (result) {
  713. if (result.path != null && !result.path.startsWith("/")) {
  714. warn(`Overridden path must start with "/". Found in "${path}".`);
  715. }
  716. }
  717. return result;
  718. }
  719. function parseCustomBlock(block, filePath, options) {
  720. var _a;
  721. const lang = (_a = block.lang) != null ? _a : options.routeBlockLang;
  722. if (lang === "json5") {
  723. try {
  724. return import_json5.default.parse(block.content);
  725. } catch (err) {
  726. warn(
  727. `Invalid JSON5 format of <${block.type}> content in ${filePath}
  728. ${err.message}`
  729. );
  730. }
  731. } else if (lang === "json") {
  732. try {
  733. return JSON.parse(block.content);
  734. } catch (err) {
  735. warn(
  736. `Invalid JSON format of <${block.type}> content in ${filePath}
  737. ${err.message}`
  738. );
  739. }
  740. } else if (lang === "yaml" || lang === "yml") {
  741. try {
  742. return (0, import_yaml.parse)(block.content);
  743. } catch (err) {
  744. warn(
  745. `Invalid YAML format of <${block.type}> content in ${filePath}
  746. ${err.message}`
  747. );
  748. }
  749. } else {
  750. warn(
  751. `Language "${lang}" for <${block.type}> is not supported. Supported languages are: json5, json, yaml, yml. Found in in ${filePath}.`
  752. );
  753. }
  754. }
  755. // src/core/RoutesFolderWatcher.ts
  756. var import_chokidar = __toESM(require("chokidar"));
  757. var import_pathe = require("pathe");
  758. var RoutesFolderWatcher = class {
  759. constructor(routesFolder, options) {
  760. this.src = routesFolder.src;
  761. this.pathPrefix = routesFolder.path || "";
  762. this.options = options;
  763. this.watcher = import_chokidar.default.watch(this.src, {
  764. ignoreInitial: true,
  765. // disableGlobbing: true,
  766. ignorePermissionErrors: true,
  767. ignored: options.exclude
  768. // useFsEvents: true,
  769. // TODO: allow user options
  770. });
  771. }
  772. on(event, handler) {
  773. this.watcher.on(event, (filePath) => {
  774. filePath = (0, import_pathe.normalize)(filePath);
  775. if (this.options.extensions.every(
  776. (extension) => !filePath.endsWith(extension)
  777. )) {
  778. return;
  779. }
  780. handler({
  781. filePath,
  782. routePath: asRoutePath(
  783. { src: this.src, path: this.pathPrefix },
  784. filePath
  785. )
  786. });
  787. });
  788. return this;
  789. }
  790. close() {
  791. this.watcher.close();
  792. }
  793. };
  794. // src/codegen/generateDTS.ts
  795. function generateDTS({
  796. vueRouterModule,
  797. routesModule,
  798. routeNamedMap
  799. }) {
  800. return `// Generated by unplugin-vue-router. \u203C\uFE0F DO NOT MODIFY THIS FILE \u203C\uFE0F
  801. // It's recommended to commit this file.
  802. // Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry.
  803. /// <reference types="unplugin-vue-router/client" />
  804. import type {
  805. // type safe route locations
  806. RouteLocationTypedList,
  807. RouteLocationResolvedTypedList,
  808. RouteLocationNormalizedTypedList,
  809. RouteLocationNormalizedLoadedTypedList,
  810. RouteLocationAsString,
  811. RouteLocationAsRelativeTypedList,
  812. RouteLocationAsPathTypedList,
  813. // helper types
  814. // route definitions
  815. RouteRecordInfo,
  816. ParamValue,
  817. ParamValueOneOrMore,
  818. ParamValueZeroOrMore,
  819. ParamValueZeroOrOne,
  820. // vue-router extensions
  821. _RouterTyped,
  822. RouterLinkTyped,
  823. NavigationGuard,
  824. UseLinkFnTyped,
  825. // data fetching
  826. _DataLoader,
  827. _DefineLoaderOptions,
  828. } from 'unplugin-vue-router'
  829. declare module '${routesModule}' {
  830. ${routeNamedMap}
  831. }
  832. declare module '${vueRouterModule}' {
  833. import type { RouteNamedMap } from '${routesModule}'
  834. export type RouterTyped = _RouterTyped<RouteNamedMap>
  835. /**
  836. * Type safe version of \`RouteLocationNormalized\` (the type of \`to\` and \`from\` in navigation guards).
  837. * Allows passing the name of the route to be passed as a generic.
  838. */
  839. export type RouteLocationNormalized<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationNormalizedTypedList<RouteNamedMap>[Name]
  840. /**
  841. * Type safe version of \`RouteLocationNormalizedLoaded\` (the return type of \`useRoute()\`).
  842. * Allows passing the name of the route to be passed as a generic.
  843. */
  844. export type RouteLocationNormalizedLoaded<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name]
  845. /**
  846. * Type safe version of \`RouteLocationResolved\` (the returned route of \`router.resolve()\`).
  847. * Allows passing the name of the route to be passed as a generic.
  848. */
  849. export type RouteLocationResolved<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationResolvedTypedList<RouteNamedMap>[Name]
  850. /**
  851. * Type safe version of \`RouteLocation\` . Allows passing the name of the route to be passed as a generic.
  852. */
  853. export type RouteLocation<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationTypedList<RouteNamedMap>[Name]
  854. /**
  855. * Type safe version of \`RouteLocationRaw\` . Allows passing the name of the route to be passed as a generic.
  856. */
  857. export type RouteLocationRaw<Name extends keyof RouteNamedMap = keyof RouteNamedMap> =
  858. | RouteLocationAsString<RouteNamedMap>
  859. | RouteLocationAsRelativeTypedList<RouteNamedMap>[Name]
  860. | RouteLocationAsPathTypedList<RouteNamedMap>[Name]
  861. /**
  862. * Generate a type safe params for a route location. Requires the name of the route to be passed as a generic.
  863. */
  864. export type RouteParams<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['params']
  865. /**
  866. * Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic.
  867. */
  868. export type RouteParamsRaw<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['paramsRaw']
  869. export function useRouter(): RouterTyped
  870. export function useRoute<Name extends keyof RouteNamedMap = keyof RouteNamedMap>(name?: Name): RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name]
  871. export const useLink: UseLinkFnTyped<RouteNamedMap>
  872. export function onBeforeRouteLeave(guard: NavigationGuard<RouteNamedMap>): void
  873. export function onBeforeRouteUpdate(guard: NavigationGuard<RouteNamedMap>): void
  874. export const RouterLink: RouterLinkTyped<RouteNamedMap>
  875. // Experimental Data Fetching
  876. export function defineLoader<
  877. P extends Promise<any>,
  878. Name extends keyof RouteNamedMap = keyof RouteNamedMap,
  879. isLazy extends boolean = false,
  880. >(
  881. name: Name,
  882. loader: (route: RouteLocationNormalizedLoaded<Name>) => P,
  883. options?: _DefineLoaderOptions<isLazy>,
  884. ): _DataLoader<Awaited<P>, isLazy>
  885. export function defineLoader<
  886. P extends Promise<any>,
  887. isLazy extends boolean = false,
  888. >(
  889. loader: (route: RouteLocationNormalizedLoaded) => P,
  890. options?: _DefineLoaderOptions<isLazy>,
  891. ): _DataLoader<Awaited<P>, isLazy>
  892. export {
  893. _definePage as definePage,
  894. _HasDataLoaderMeta as HasDataLoaderMeta,
  895. _setupDataFetchingGuard as setupDataFetchingGuard,
  896. _stopDataFetchingScope as stopDataFetchingScope,
  897. } from 'unplugin-vue-router/runtime'
  898. }
  899. declare module 'vue-router' {
  900. import type { RouteNamedMap } from '${routesModule}'
  901. export interface TypesConfig {
  902. beforeRouteUpdate: NavigationGuard<RouteNamedMap>
  903. beforeRouteLeave: NavigationGuard<RouteNamedMap>
  904. $route: RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[keyof RouteNamedMap]
  905. $router: _RouterTyped<RouteNamedMap>
  906. RouterLink: RouterLinkTyped<RouteNamedMap>
  907. }
  908. }
  909. `;
  910. }
  911. // src/codegen/vueRouterModule.ts
  912. function generateVueRouterProxy(routesModule, options) {
  913. return `
  914. import { routes } from '${routesModule}'
  915. import { createRouter as _createRouter } from 'vue-router'
  916. export * from 'vue-router'
  917. export {
  918. _defineLoader as defineLoader,
  919. _definePage as definePage,
  920. _HasDataLoaderMeta as HasDataLoaderMeta,
  921. _setupDataFetchingGuard as setupDataFetchingGuard,
  922. _stopDataFetchingScope as stopDataFetchingScope,
  923. } from 'unplugin-vue-router/runtime'
  924. export function createRouter(options) {
  925. const { extendRoutes } = options
  926. // use Object.assign for better browser support
  927. const router = _createRouter(Object.assign(
  928. options,
  929. { routes: typeof extendRoutes === 'function' ? extendRoutes(routes) : routes },
  930. ))
  931. return router
  932. }
  933. `;
  934. }
  935. // src/data-fetching/parse.ts
  936. var import_fs2 = require("fs");
  937. var import_mlly = require("mlly");
  938. async function hasNamedExports(file) {
  939. const code = await import_fs2.promises.readFile(file, "utf8");
  940. const exportedNames = (0, import_mlly.findExports)(code).filter(
  941. (e) => e.type !== "default" && e.type !== "star"
  942. );
  943. return exportedNames.length > 0;
  944. }
  945. // src/core/definePage.ts
  946. var import_common = require("@vue-macros/common");
  947. var import_ast_walker_scope = require("ast-walker-scope");
  948. var MACRO_DEFINE_PAGE = "definePage";
  949. var MACRO_DEFINE_PAGE_QUERY = /[?&]definePage\b/;
  950. function definePageTransform({
  951. code,
  952. id
  953. }) {
  954. if (!code.includes(MACRO_DEFINE_PAGE))
  955. return;
  956. const sfc = (0, import_common.parseSFC)(code, id);
  957. if (!sfc.scriptSetup)
  958. return;
  959. const isExtractingDefinePage = MACRO_DEFINE_PAGE_QUERY.test(id);
  960. const { script, scriptSetup, getSetupAst } = sfc;
  961. const setupAst = getSetupAst();
  962. const definePageNodes = ((setupAst == null ? void 0 : setupAst.body) || []).map((node) => {
  963. if (node.type === "ExpressionStatement")
  964. node = node.expression;
  965. return (0, import_common.isCallOf)(node, MACRO_DEFINE_PAGE) ? node : null;
  966. }).filter((node) => !!node);
  967. if (!definePageNodes.length) {
  968. return isExtractingDefinePage ? (
  969. // e.g. index.vue?definePage that contains a commented `definePage()
  970. "export default {}"
  971. ) : (
  972. // e.g. index.vue that contains a commented `definePage()
  973. null
  974. );
  975. } else if (definePageNodes.length > 1) {
  976. throw new SyntaxError(`duplicate definePage() call`);
  977. }
  978. const definePageNode = definePageNodes[0];
  979. const setupOffset = scriptSetup.loc.start.offset;
  980. if (isExtractingDefinePage) {
  981. const s = new import_common.MagicString(code);
  982. const routeRecord = definePageNode.arguments[0];
  983. const scriptBindings = (setupAst == null ? void 0 : setupAst.body) ? getIdentifiers(setupAst.body) : [];
  984. (0, import_common.checkInvalidScopeReference)(routeRecord, MACRO_DEFINE_PAGE, scriptBindings);
  985. s.remove(setupOffset + routeRecord.end, code.length);
  986. s.remove(0, setupOffset + routeRecord.start);
  987. s.prepend(`export default `);
  988. return (0, import_common.getTransformResult)(s, id);
  989. } else {
  990. const s = new import_common.MagicString(code);
  991. s.remove(
  992. setupOffset + definePageNode.start,
  993. setupOffset + definePageNode.end
  994. );
  995. return (0, import_common.getTransformResult)(s, id);
  996. }
  997. }
  998. function extractDefinePageNameAndPath(sfcCode, id) {
  999. var _a;
  1000. if (!sfcCode.includes(MACRO_DEFINE_PAGE))
  1001. return;
  1002. const sfc = (0, import_common.parseSFC)(sfcCode, id);
  1003. if (!sfc.scriptSetup)
  1004. return;
  1005. const { getSetupAst } = sfc;
  1006. const setupAst = getSetupAst();
  1007. const definePageNodes = ((_a = setupAst == null ? void 0 : setupAst.body) != null ? _a : []).map((node) => {
  1008. if (node.type === "ExpressionStatement")
  1009. node = node.expression;
  1010. return (0, import_common.isCallOf)(node, MACRO_DEFINE_PAGE) ? node : null;
  1011. }).filter((node) => !!node);
  1012. if (!definePageNodes.length) {
  1013. return;
  1014. } else if (definePageNodes.length > 1) {
  1015. throw new SyntaxError(`duplicate definePage() call`);
  1016. }
  1017. const definePageNode = definePageNodes[0];
  1018. const routeRecord = definePageNode.arguments[0];
  1019. if (routeRecord.type !== "ObjectExpression") {
  1020. throw new SyntaxError(
  1021. `[${id}]: definePage() expects an object expression as its only argument`
  1022. );
  1023. }
  1024. const routeInfo = {};
  1025. for (const prop of routeRecord.properties) {
  1026. if (prop.type === "ObjectProperty" && prop.key.type === "Identifier") {
  1027. if (prop.key.name === "name") {
  1028. if (prop.value.type !== "StringLiteral") {
  1029. warn(`route name must be a string literal. Found in "${id}".`);
  1030. } else {
  1031. routeInfo.name = prop.value.value;
  1032. }
  1033. } else if (prop.key.name === "path") {
  1034. if (prop.value.type !== "StringLiteral") {
  1035. warn(`route path must be a string literal. Found in "${id}".`);
  1036. } else {
  1037. routeInfo.path = prop.value.value;
  1038. }
  1039. }
  1040. }
  1041. }
  1042. return routeInfo;
  1043. }
  1044. var getIdentifiers = (stmts) => {
  1045. let ids = [];
  1046. (0, import_ast_walker_scope.walkAST)(
  1047. {
  1048. type: "Program",
  1049. body: stmts,
  1050. directives: [],
  1051. sourceType: "module",
  1052. sourceFile: ""
  1053. },
  1054. {
  1055. enter(node) {
  1056. if (node.type === "BlockStatement") {
  1057. this.skip();
  1058. }
  1059. },
  1060. leave(node) {
  1061. if (node.type !== "Program")
  1062. return;
  1063. ids = Object.keys(this.scope);
  1064. }
  1065. }
  1066. );
  1067. return ids;
  1068. };
  1069. // src/core/extendRoutes.ts
  1070. var EditableTreeNode = class {
  1071. // private _parent?: EditableTreeNode
  1072. constructor(node) {
  1073. this.node = node;
  1074. }
  1075. /**
  1076. * Remove and detach the current route node from the tree. Subsequently, its children will be removed as well.
  1077. */
  1078. delete() {
  1079. return this.node.delete();
  1080. }
  1081. /**
  1082. * Inserts a new route as a child of this route. This route cannot use `definePage()`. If it was meant to be included,
  1083. * add it to the `routesFolder` option.
  1084. */
  1085. insert(path, filePath) {
  1086. const extDotIndex = filePath.lastIndexOf(".");
  1087. const ext = filePath.slice(extDotIndex);
  1088. if (!path.endsWith(ext)) {
  1089. path += ext;
  1090. }
  1091. let addBackLeadingSlash = false;
  1092. if (path.startsWith("/")) {
  1093. path = path.slice(1);
  1094. addBackLeadingSlash = !this.node.isRoot();
  1095. }
  1096. const node = this.node.insert(path, filePath);
  1097. const editable = new EditableTreeNode(node);
  1098. if (addBackLeadingSlash) {
  1099. editable.path = "/" + node.path;
  1100. }
  1101. return editable;
  1102. }
  1103. /**
  1104. * Get an editable version of the parent node if it exists.
  1105. */
  1106. get parent() {
  1107. return this.node.parent && new EditableTreeNode(this.node.parent);
  1108. }
  1109. /**
  1110. * Return a Map of the files associated to the current route. The key of the map represents the name of the view (Vue
  1111. * Router feature) while the value is the file path. By default, the name of the view is `default`.
  1112. */
  1113. get components() {
  1114. return this.node.value.components;
  1115. }
  1116. /**
  1117. * Name of the route. Note that **all routes are named** but when the final `routes` array is generated, routes
  1118. * without a `component` will not include their `name` property to avoid accidentally navigating to them and display
  1119. * nothing. {@see isPassThrough}
  1120. */
  1121. get name() {
  1122. return this.node.name;
  1123. }
  1124. /**
  1125. * Override the name of the route.
  1126. */
  1127. set name(name) {
  1128. this.node.value.addEditOverride({ name });
  1129. }
  1130. /**
  1131. * Whether the route is a pass-through route. A pass-through route is a route that does not have a component and is
  1132. * used to group other routes under the same prefix `path` and/or `meta` properties.
  1133. */
  1134. get isPassThrough() {
  1135. return this.node.value.components.size === 0;
  1136. }
  1137. /**
  1138. * Meta property of the route as an object. Note this property is readonly and will be serialized as JSON. It won't contain the meta properties defined with `definePage()` as it could contain expressions **but it does contain the meta properties defined with `<route>` blocks**.
  1139. */
  1140. get meta() {
  1141. return this.node.metaAsObject;
  1142. }
  1143. /**
  1144. * Override the meta property of the route. This will discard any other meta property defined with `<route>` blocks or
  1145. * through other means.
  1146. */
  1147. set meta(meta) {
  1148. this.node.value.removeOverride("meta");
  1149. this.node.value.setEditOverride("meta", meta);
  1150. }
  1151. /**
  1152. * Add meta properties to the route keeping the existing ones. The passed object will be deeply merged with the
  1153. * existing meta object if any. Note that the meta property is later on serialized as JSON so you can't pass functions
  1154. * or any other non-serializable value.
  1155. */
  1156. addToMeta(meta) {
  1157. this.node.value.addEditOverride({ meta });
  1158. }
  1159. /**
  1160. * Path of the route without parent paths.
  1161. */
  1162. get path() {
  1163. return this.node.path;
  1164. }
  1165. /**
  1166. * Override the path of the route. You must ensure `params` match with the existing path.
  1167. */
  1168. set path(path) {
  1169. if (!path.startsWith("/")) {
  1170. warn(
  1171. `Only absolute paths are supported. Make sure that "${path}" starts with a slash "/".`
  1172. );
  1173. return;
  1174. }
  1175. this.node.value.addEditOverride({ path });
  1176. }
  1177. /**
  1178. * Alias of the route.
  1179. */
  1180. get alias() {
  1181. return this.node.value.overrides.alias;
  1182. }
  1183. /**
  1184. * Add an alias to the route.
  1185. *
  1186. * @param alias - Alias to add to the route
  1187. */
  1188. addAlias(alias) {
  1189. this.node.value.addEditOverride({ alias });
  1190. }
  1191. /**
  1192. * Array of the route params and all of its parent's params.
  1193. */
  1194. get params() {
  1195. return this.node.params;
  1196. }
  1197. /**
  1198. * Path of the route including parent paths.
  1199. */
  1200. get fullPath() {
  1201. return this.node.fullPath;
  1202. }
  1203. /**
  1204. * DFS traversal of the tree.
  1205. * @example
  1206. * ```ts
  1207. * for (const node of tree) {
  1208. * // ...
  1209. * }
  1210. * ```
  1211. */
  1212. *traverseDFS() {
  1213. if (!this.node.isRoot()) {
  1214. yield this;
  1215. }
  1216. for (const [_name, child] of this.node.children) {
  1217. yield* new EditableTreeNode(child).traverseDFS();
  1218. }
  1219. }
  1220. *[Symbol.iterator]() {
  1221. yield* this.traverseBFS();
  1222. }
  1223. /**
  1224. * BFS traversal of the tree as a generator.
  1225. *
  1226. * @example
  1227. * ```ts
  1228. * for (const node of tree) {
  1229. * // ...
  1230. * }
  1231. * ```
  1232. */
  1233. *traverseBFS() {
  1234. for (const [_name, child] of this.node.children) {
  1235. yield new EditableTreeNode(child);
  1236. }
  1237. for (const [_name, child] of this.node.children) {
  1238. yield* new EditableTreeNode(child).traverseBFS();
  1239. }
  1240. }
  1241. };
  1242. // src/core/context.ts
  1243. function createRoutesContext(options) {
  1244. const { dts: preferDTS, root, routesFolder } = options;
  1245. const dts = preferDTS === false ? false : preferDTS === true ? (0, import_pathe2.resolve)(root, "typed-router.d.ts") : (0, import_pathe2.resolve)(root, preferDTS);
  1246. const routeTree = createPrefixTree(options);
  1247. const editableRoutes = new EditableTreeNode(routeTree);
  1248. function log(...args) {
  1249. if (options.logs) {
  1250. console.log(...args);
  1251. }
  1252. }
  1253. const watchers = [];
  1254. async function scanPages(startWatchers = true) {
  1255. var _a;
  1256. if (options.extensions.length < 1) {
  1257. throw new Error(
  1258. '"extensions" cannot be empty. Please specify at least one extension.'
  1259. );
  1260. }
  1261. if (watchers.length > 0) {
  1262. return;
  1263. }
  1264. const globalPattern = appendExtensionListToPattern(
  1265. options.filePatterns,
  1266. options.extensions
  1267. );
  1268. await Promise.all(
  1269. routesFolder.map((folder) => {
  1270. if (startWatchers) {
  1271. watchers.push(setupWatcher(new RoutesFolderWatcher(folder, options)));
  1272. }
  1273. const pattern = folder.filePatterns ? appendExtensionListToPattern(
  1274. folder.filePatterns,
  1275. // also override the extensions if the folder has a custom extensions
  1276. folder.extensions || options.extensions
  1277. ) : globalPattern;
  1278. return (0, import_fast_glob.default)(pattern, {
  1279. cwd: folder.src,
  1280. // TODO: do they return the symbolic link path or the original file?
  1281. // followSymbolicLinks: false,
  1282. ignore: folder.exclude || options.exclude
  1283. }).then((files) => files.map((file) => (0, import_pathe2.resolve)(folder.src, file))).then(
  1284. (files) => Promise.all(
  1285. files.map(
  1286. (file) => addPage({
  1287. routePath: asRoutePath(folder, file),
  1288. filePath: file
  1289. })
  1290. )
  1291. )
  1292. );
  1293. })
  1294. );
  1295. for (const route of editableRoutes) {
  1296. await ((_a = options.extendRoute) == null ? void 0 : _a.call(options, route));
  1297. }
  1298. await _writeConfigFiles();
  1299. }
  1300. async function writeRouteInfoToNode(node, path) {
  1301. const content = await import_fs3.promises.readFile(path, "utf8");
  1302. node.hasDefinePage = content.includes("definePage");
  1303. const [definedPageNameAndPath, routeBlock] = await Promise.all([
  1304. extractDefinePageNameAndPath(content, path),
  1305. getRouteBlock(path, options)
  1306. ]);
  1307. node.setCustomRouteBlock(path, __spreadValues(__spreadValues({}, routeBlock), definedPageNameAndPath));
  1308. node.value.includeLoaderGuard = options.dataFetching && await hasNamedExports(path);
  1309. }
  1310. async function addPage({ filePath, routePath }, triggerExtendRoute = false) {
  1311. var _a;
  1312. log(`added "${routePath}" for "${filePath}"`);
  1313. const node = routeTree.insert(routePath, filePath);
  1314. await writeRouteInfoToNode(node, filePath);
  1315. if (triggerExtendRoute) {
  1316. await ((_a = options.extendRoute) == null ? void 0 : _a.call(options, new EditableTreeNode(node)));
  1317. }
  1318. }
  1319. async function updatePage({ filePath, routePath }) {
  1320. var _a;
  1321. log(`updated "${routePath}" for "${filePath}"`);
  1322. const node = routeTree.getChild(filePath);
  1323. if (!node) {
  1324. console.warn(`Cannot update "${filePath}": Not found.`);
  1325. return;
  1326. }
  1327. await writeRouteInfoToNode(node, filePath);
  1328. await ((_a = options.extendRoute) == null ? void 0 : _a.call(options, new EditableTreeNode(node)));
  1329. }
  1330. function removePage({ filePath, routePath }) {
  1331. log(`remove "${routePath}" for "${filePath}"`);
  1332. routeTree.removeChild(filePath);
  1333. }
  1334. function setupWatcher(watcher) {
  1335. log(`\u{1F916} Scanning files in ${watcher.src}`);
  1336. return watcher.on("change", async (ctx) => {
  1337. await updatePage(ctx);
  1338. writeConfigFiles();
  1339. }).on("add", async (ctx) => {
  1340. await addPage(ctx, true);
  1341. writeConfigFiles();
  1342. }).on("unlink", async (ctx) => {
  1343. await removePage(ctx);
  1344. writeConfigFiles();
  1345. });
  1346. }
  1347. function generateRoutes() {
  1348. const importList = /* @__PURE__ */ new Map();
  1349. const routesExport = `export const routes = ${generateRouteRecord(
  1350. routeTree,
  1351. options,
  1352. importList
  1353. )}`;
  1354. let imports = "";
  1355. if (true) {
  1356. imports += `import { _HasDataLoaderMeta, _mergeRouteRecord } from 'unplugin-vue-router/runtime'
  1357. `;
  1358. }
  1359. for (const [name, path] of importList) {
  1360. imports += `import ${name} from '${path}'
  1361. `;
  1362. }
  1363. if (imports) {
  1364. imports += "\n";
  1365. }
  1366. return `${imports}${routesExport}
  1367. `;
  1368. }
  1369. function generateDTS2() {
  1370. return generateDTS({
  1371. vueRouterModule: MODULE_VUE_ROUTER,
  1372. routesModule: MODULE_ROUTES_PATH,
  1373. routeNamedMap: generateRouteNamedMap(routeTree).split("\n").filter((line) => line).map((line) => " " + line).join("\n")
  1374. });
  1375. }
  1376. function generateVueRouterProxy2() {
  1377. return generateVueRouterProxy(MODULE_ROUTES_PATH, options);
  1378. }
  1379. let lastDTS;
  1380. async function _writeConfigFiles() {
  1381. log("\u{1F4BE} writing...");
  1382. if (options.beforeWriteFiles) {
  1383. await options.beforeWriteFiles(editableRoutes);
  1384. }
  1385. logTree(routeTree, log);
  1386. if (dts) {
  1387. const content = generateDTS2();
  1388. if (lastDTS !== content) {
  1389. await import_fs3.promises.writeFile(dts, content, "utf-8");
  1390. lastDTS = content;
  1391. server == null ? void 0 : server.invalidate(MODULE_ROUTES_PATH);
  1392. server == null ? void 0 : server.invalidate(MODULE_VUE_ROUTER);
  1393. server == null ? void 0 : server.reload();
  1394. }
  1395. }
  1396. }
  1397. const writeConfigFiles = throttle(_writeConfigFiles, 500, 100);
  1398. function stopWatcher() {
  1399. if (watchers.length) {
  1400. if (options.logs) {
  1401. console.log("\u{1F6D1} stopping watcher");
  1402. }
  1403. watchers.forEach((watcher) => watcher.close());
  1404. }
  1405. }
  1406. let server;
  1407. function setServerContext(_server) {
  1408. server = _server;
  1409. }
  1410. return {
  1411. scanPages,
  1412. writeConfigFiles,
  1413. setServerContext,
  1414. stopWatcher,
  1415. generateRoutes,
  1416. generateVueRouterProxy: generateVueRouterProxy2,
  1417. definePageTransform(code, id) {
  1418. return definePageTransform({
  1419. code,
  1420. id
  1421. });
  1422. }
  1423. };
  1424. }
  1425. // src/options.ts
  1426. var import_local_pkg = require("local-pkg");
  1427. var import_pathe3 = require("pathe");
  1428. var DEFAULT_OPTIONS = {
  1429. extensions: [".vue"],
  1430. exclude: [],
  1431. routesFolder: [{ src: "src/pages" }],
  1432. filePatterns: "**/*",
  1433. routeBlockLang: "json5",
  1434. getRouteName: getFileBasedRouteName,
  1435. dataFetching: false,
  1436. importMode: "async",
  1437. root: process.cwd(),
  1438. dts: (0, import_local_pkg.isPackageExists)("typescript"),
  1439. logs: false,
  1440. _inspect: false
  1441. };
  1442. function normalizeRoutesFolderOption(routesFolder) {
  1443. return (isArray(routesFolder) ? routesFolder : [routesFolder]).map(
  1444. (routeOption) => typeof routeOption === "string" ? { src: routeOption } : routeOption
  1445. );
  1446. }
  1447. function resolveOptions(options) {
  1448. const root = options.root || DEFAULT_OPTIONS.root;
  1449. const routesFolder = normalizeRoutesFolderOption(
  1450. options.routesFolder || DEFAULT_OPTIONS.routesFolder
  1451. ).map((routeOption) => __spreadProps(__spreadValues({}, routeOption), {
  1452. src: (0, import_pathe3.resolve)(root, routeOption.src)
  1453. }));
  1454. if (options.extensions) {
  1455. options.extensions = options.extensions.map((ext) => {
  1456. if (!ext.startsWith(".")) {
  1457. warn(`Invalid extension "${ext}". Extensions must start with a dot.`);
  1458. return "." + ext;
  1459. }
  1460. return ext;
  1461. }).sort((a, b) => b.length - a.length);
  1462. }
  1463. return __spreadProps(__spreadValues(__spreadValues({}, DEFAULT_OPTIONS), options), {
  1464. routesFolder
  1465. });
  1466. }
  1467. // src/core/vite/index.ts
  1468. function createViteContext(server) {
  1469. function invalidate(path) {
  1470. const { moduleGraph } = server;
  1471. const foundModule = moduleGraph.getModuleById(asVirtualId(path));
  1472. if (foundModule) {
  1473. moduleGraph.invalidateModule(foundModule);
  1474. }
  1475. return !!foundModule;
  1476. }
  1477. function reload() {
  1478. if (server.ws) {
  1479. server.ws.send({
  1480. type: "full-reload",
  1481. path: "*"
  1482. });
  1483. }
  1484. }
  1485. return {
  1486. invalidate,
  1487. reload
  1488. };
  1489. }
  1490. // src/index.ts
  1491. var import_pluginutils = require("@rollup/pluginutils");
  1492. var import_pathe4 = require("pathe");
  1493. var src_default = (0, import_unplugin.createUnplugin)((opt = {}, meta) => {
  1494. const options = resolveOptions(opt);
  1495. const ctx = createRoutesContext(options);
  1496. function getVirtualId2(id) {
  1497. if (options._inspect)
  1498. return id;
  1499. return getVirtualId(id);
  1500. }
  1501. function asVirtualId2(id) {
  1502. if (options._inspect)
  1503. return id;
  1504. return asVirtualId(id);
  1505. }
  1506. const pageFilePattern = `**/*` + (options.extensions.length === 1 ? options.extensions[0] : `.{${options.extensions.map((extension) => extension.replace(".", "")).join(",")}}`);
  1507. const filterPageComponents = (0, import_pluginutils.createFilter)(
  1508. [
  1509. ...options.routesFolder.map(
  1510. (routeOption) => (0, import_pathe4.join)(routeOption.src, pageFilePattern)
  1511. ),
  1512. // importing the definePage block
  1513. /definePage\&vue$/
  1514. ],
  1515. options.exclude
  1516. );
  1517. return {
  1518. name: "unplugin-vue-router",
  1519. enforce: "pre",
  1520. resolveId(id) {
  1521. if (id === MODULE_ROUTES_PATH) {
  1522. return asVirtualId2(id);
  1523. }
  1524. if (id === MODULE_VUE_ROUTER) {
  1525. return asVirtualId2(id);
  1526. }
  1527. if (routeBlockQueryRE.test(id)) {
  1528. return ROUTE_BLOCK_ID;
  1529. }
  1530. },
  1531. buildStart() {
  1532. return ctx.scanPages(true);
  1533. },
  1534. buildEnd() {
  1535. ctx.stopWatcher();
  1536. },
  1537. // we only need to transform page components
  1538. transformInclude(id) {
  1539. return filterPageComponents(id);
  1540. },
  1541. transform(code, id) {
  1542. return ctx.definePageTransform(code, id);
  1543. },
  1544. // loadInclude is necessary for webpack
  1545. loadInclude(id) {
  1546. if (id === ROUTE_BLOCK_ID)
  1547. return true;
  1548. const resolvedId = getVirtualId2(id);
  1549. return resolvedId === MODULE_ROUTES_PATH || resolvedId === MODULE_VUE_ROUTER;
  1550. },
  1551. load(id) {
  1552. if (id === ROUTE_BLOCK_ID) {
  1553. return {
  1554. code: `export default {}`,
  1555. map: null
  1556. };
  1557. }
  1558. const resolvedId = getVirtualId2(id);
  1559. if (resolvedId === MODULE_ROUTES_PATH) {
  1560. return ctx.generateRoutes();
  1561. }
  1562. if (resolvedId === MODULE_VUE_ROUTER) {
  1563. return ctx.generateVueRouterProxy();
  1564. }
  1565. },
  1566. // improves DX
  1567. vite: {
  1568. configureServer(server) {
  1569. ctx.setServerContext(createViteContext(server));
  1570. }
  1571. }
  1572. };
  1573. });
  1574. // src/webpack.ts
  1575. var webpack_default = src_default.webpack;
  1576. // Annotate the CommonJS export names for ESM import in node:
  1577. 0 && (module.exports = {});