版博士V2.0程序
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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