版博士V2.0程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

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