unplugin-vue-router
Advanced tools
Comparing version 0.5.0 to 0.5.1
import * as esbuild from 'esbuild'; | ||
import { O as Options } from './options-f6626ea0.js'; | ||
import { O as Options } from './options-dc4b959a.js'; | ||
import 'vue-router'; | ||
@@ -4,0 +4,0 @@ |
@@ -38,2 +38,6 @@ "use strict"; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
@@ -129,3 +133,4 @@ mod | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + (path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
result = result.replace(TRAILING_SLASH_RE, "") + // check path to avoid adding a trailing slash when joining an empty string | ||
(path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
} | ||
@@ -180,3 +185,7 @@ return result; | ||
function asRoutePath({ src, path = "" }, filePath) { | ||
return path + filePath.slice(src.length + 1); | ||
return ( | ||
// add the path prefix if any | ||
path + // remove the absolute path to the pages folder | ||
filePath.slice(src.length + 1) | ||
); | ||
} | ||
@@ -188,4 +197,14 @@ | ||
constructor(rawSegment, parent, pathSegment = rawSegment, subSegments = [pathSegment]) { | ||
/** | ||
* Overrides defined by each file. The map is necessary to handle named views. | ||
*/ | ||
this._overrides = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should we add the loader guard to the route record. | ||
*/ | ||
this.includeLoaderGuard = false; | ||
/** | ||
* View name (Vue Router feature) mapped to their corresponding file. By default, the view name is `default` unless | ||
* specified with a `@` e.g. `index@aux.vue` will have a view name of `aux`. | ||
*/ | ||
this.components = /* @__PURE__ */ new Map(); | ||
@@ -197,3 +216,4 @@ this._type = 0; | ||
const parentPath = parent == null ? void 0 : parent.path; | ||
this.path = (!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.path = // both the root record and the index record have a path of / | ||
(!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
} | ||
@@ -211,3 +231,6 @@ toString() { | ||
return [...this._overrides.entries()].sort( | ||
([nameA], [nameB]) => nameA === nameB ? 0 : nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
([nameA], [nameB]) => nameA === nameB ? 0 : ( | ||
// EDITS_OVERRIDE_NAME should always be last | ||
nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
) | ||
).reduce((acc, [_path, routeBlock]) => { | ||
@@ -220,2 +243,7 @@ return mergeRouteRecordOverride(acc, routeBlock); | ||
} | ||
/** | ||
* Remove all overrides for a given key. | ||
* | ||
* @param key - key to remove from the override | ||
*/ | ||
removeOverride(key) { | ||
@@ -354,3 +382,9 @@ this._overrides.forEach((routeBlock) => { | ||
constructor(options, filePath, parent) { | ||
/** | ||
* children of the node | ||
*/ | ||
this.children = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should this page import the page info | ||
*/ | ||
this.hasDefinePage = false; | ||
@@ -361,2 +395,9 @@ this.options = options; | ||
} | ||
/** | ||
* Adds a path to the tree. `path` cannot start with a `/`. | ||
* | ||
* @param path - path segment to insert. **It must contain the file extension** this allows to | ||
* differentiate between folders and files. | ||
* @param filePath - file path, defaults to path for convenience and testing | ||
*/ | ||
insert(path, filePath = path) { | ||
@@ -387,2 +428,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Delete and detach itself from the tree. | ||
*/ | ||
delete() { | ||
@@ -395,2 +439,8 @@ if (!this.parent) { | ||
} | ||
/** | ||
* 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`. | ||
* The `path` should be relative to the page folder. | ||
* | ||
* @param path - path segment of the file | ||
*/ | ||
remove(path) { | ||
@@ -421,2 +471,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Returns the route path of the node without parent paths. If the path was overridden, it returns the override. | ||
*/ | ||
get path() { | ||
@@ -426,2 +479,5 @@ var _a, _b; | ||
} | ||
/** | ||
* Returns the route path of the node including parent paths. | ||
*/ | ||
get fullPath() { | ||
@@ -431,5 +487,11 @@ var _a; | ||
} | ||
/** | ||
* Returns the route name of the node. If the name was overridden, it returns the override. | ||
*/ | ||
get name() { | ||
return this.value.overrides.name || this.options.getRouteName(this); | ||
} | ||
/** | ||
* Returns the meta property as an object. | ||
*/ | ||
get metaAsObject() { | ||
@@ -442,2 +504,6 @@ const meta = __spreadValues({}, this.value.overrides.meta); | ||
} | ||
/** | ||
* Returns the JSON string of the meta object of the node. If the meta was overridden, it returns the override. If | ||
* there is no override, it returns an empty string. | ||
*/ | ||
get meta() { | ||
@@ -458,2 +524,7 @@ const overrideMeta = this.metaAsObject; | ||
} | ||
/** | ||
* Returns wether this tree node is the root node of the tree. | ||
* | ||
* @returns true if the node is the root node | ||
*/ | ||
isRoot() { | ||
@@ -463,3 +534,5 @@ return this.value.path === "/" && !this.value.components.size; | ||
toString() { | ||
return `${this.value}${this.value.components.size > 1 || this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
return `${this.value}${// either we have multiple names | ||
this.value.components.size > 1 || // or we have one name and it's not default | ||
this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
} | ||
@@ -480,2 +553,6 @@ }; | ||
} | ||
/** | ||
* | ||
* @param filePath - | ||
*/ | ||
removeChild(filePath) { | ||
@@ -522,3 +599,6 @@ if (this.map.has(filePath)) { | ||
(param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `ParamValueZeroOrOne<${isRaw}>` : `ParamValue<${isRaw}>`) | ||
).join(", ")} }` : "Record<never, never>"; | ||
).join(", ")} }` : ( | ||
// no params allowed | ||
"Record<never, never>" | ||
); | ||
} | ||
@@ -532,4 +612,8 @@ | ||
} | ||
return (node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : ""); | ||
return ( | ||
// if the node has a filePath, it's a component, it has a routeName and it should be referenced in the RouteNamedMap | ||
// otherwise it should be skipped to avoid navigating to a route that doesn't render anything | ||
(node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : "") | ||
); | ||
} | ||
@@ -566,3 +650,4 @@ function generateRouteRecordInfo(node) { | ||
${indentStr}${node.value.components.size ? `name: '${node.name}',` : `/* internal name: '${node.name}' */`} | ||
${indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
${// component | ||
indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
node, | ||
@@ -575,3 +660,4 @@ indentStr, | ||
` : ""}${overrides.alias != null ? indentStr + `alias: ${JSON.stringify(overrides.alias)}, | ||
` : ""}${indentStr}${node.children.size > 0 ? `children: [ | ||
` : ""}${// children | ||
indentStr}${node.children.size > 0 ? `children: [ | ||
${node.getSortedChildren().map((child) => generateRouteRecord(child, options, importList, indent + 2)).join(",\n")} | ||
@@ -599,11 +685,14 @@ ${indentStr}],` : "/* no children */"}${formatMeta(node, indentStr)} | ||
const isDefaultExport = files.length === 1 && files[0][0] === "default"; | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : `components: { | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : ( | ||
// files has at least one entry | ||
`components: { | ||
${files.map( | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},`; | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},` | ||
); | ||
} | ||
@@ -708,4 +797,7 @@ function generatePageImport(filepath, importMode, importList) { | ||
ignoreInitial: true, | ||
// disableGlobbing: true, | ||
ignorePermissionErrors: true, | ||
ignored: options.exclude | ||
// useFsEvents: true, | ||
// TODO: allow user options | ||
}); | ||
@@ -941,3 +1033,9 @@ } | ||
if (!definePageNodes.length) { | ||
return isExtractingDefinePage ? "export default {}" : null; | ||
return isExtractingDefinePage ? ( | ||
// e.g. index.vue?definePage that contains a commented `definePage() | ||
"export default {}" | ||
) : ( | ||
// e.g. index.vue that contains a commented `definePage() | ||
null | ||
); | ||
} else if (definePageNodes.length > 1) { | ||
@@ -1040,8 +1138,16 @@ throw new SyntaxError(`duplicate definePage() call`); | ||
var EditableTreeNode = class { | ||
// private _parent?: EditableTreeNode | ||
constructor(node) { | ||
this.node = node; | ||
} | ||
/** | ||
* Remove and detach the current route node from the tree. Subsequently, its children will be removed as well. | ||
*/ | ||
delete() { | ||
return this.node.delete(); | ||
} | ||
/** | ||
* Inserts a new route as a child of this route. This route cannot use `definePage()`. If it was meant to be included, | ||
* add it to the `routesFolder` option. | ||
*/ | ||
insert(path, filePath) { | ||
@@ -1065,20 +1171,46 @@ const extDotIndex = filePath.lastIndexOf("."); | ||
} | ||
/** | ||
* Get an editable version of the parent node if it exists. | ||
*/ | ||
get parent() { | ||
return this.node.parent && new EditableTreeNode(this.node.parent); | ||
} | ||
/** | ||
* Return a Map of the files associated to the current route. The key of the map represents the name of the view (Vue | ||
* Router feature) while the value is the file path. By default, the name of the view is `default`. | ||
*/ | ||
get components() { | ||
return this.node.value.components; | ||
} | ||
/** | ||
* Name of the route. Note that **all routes are named** but when the final `routes` array is generated, routes | ||
* without a `component` will not include their `name` property to avoid accidentally navigating to them and display | ||
* nothing. {@see isPassThrough} | ||
*/ | ||
get name() { | ||
return this.node.name; | ||
} | ||
/** | ||
* Override the name of the route. | ||
*/ | ||
set name(name) { | ||
this.node.value.addEditOverride({ name }); | ||
} | ||
/** | ||
* Whether the route is a pass-through route. A pass-through route is a route that does not have a component and is | ||
* used to group other routes under the same prefix `path` and/or `meta` properties. | ||
*/ | ||
get isPassThrough() { | ||
return this.node.value.components.size === 0; | ||
} | ||
/** | ||
* 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**. | ||
*/ | ||
get meta() { | ||
return this.node.metaAsObject; | ||
} | ||
/** | ||
* Override the meta property of the route. This will discard any other meta property defined with `<route>` blocks or | ||
* through other means. | ||
*/ | ||
set meta(meta) { | ||
@@ -1088,8 +1220,19 @@ this.node.value.removeOverride("meta"); | ||
} | ||
/** | ||
* Add meta properties to the route keeping the existing ones. The passed object will be deeply merged with the | ||
* existing meta object if any. Note that the meta property is later on serialized as JSON so you can't pass functions | ||
* or any other non-serializable value. | ||
*/ | ||
addToMeta(meta) { | ||
this.node.value.addEditOverride({ meta }); | ||
} | ||
/** | ||
* Path of the route without parent paths. | ||
*/ | ||
get path() { | ||
return this.node.path; | ||
} | ||
/** | ||
* Override the path of the route. You must ensure `params` match with the existing path. | ||
*/ | ||
set path(path) { | ||
@@ -1104,14 +1247,37 @@ if (!path.startsWith("/")) { | ||
} | ||
/** | ||
* Alias of the route. | ||
*/ | ||
get alias() { | ||
return this.node.value.overrides.alias; | ||
} | ||
/** | ||
* Add an alias to the route. | ||
* | ||
* @param alias - Alias to add to the route | ||
*/ | ||
addAlias(alias) { | ||
this.node.value.addEditOverride({ alias }); | ||
} | ||
/** | ||
* Array of the route params and all of its parent's params. | ||
*/ | ||
get params() { | ||
return this.node.params; | ||
} | ||
/** | ||
* Path of the route including parent paths. | ||
*/ | ||
get fullPath() { | ||
return this.node.fullPath; | ||
} | ||
/** | ||
* DFS traversal of the tree. | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseDFS() { | ||
@@ -1128,2 +1294,12 @@ if (!this.node.isRoot()) { | ||
} | ||
/** | ||
* BFS traversal of the tree as a generator. | ||
* | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseBFS() { | ||
@@ -1169,2 +1345,4 @@ for (const [_name, child] of this.node.children) { | ||
cwd: folder.src, | ||
// TODO: do they return the symbolic link path or the original file? | ||
// followSymbolicLinks: false, | ||
ignore: options.exclude | ||
@@ -1397,2 +1575,3 @@ }).then((files) => files.map((file) => (0, import_pathe2.resolve)(folder.src, file))).then( | ||
), | ||
// importing the definePage block | ||
/definePage\&vue$/ | ||
@@ -1425,2 +1604,3 @@ ], | ||
}, | ||
// we only need to transform page components | ||
transformInclude(id) { | ||
@@ -1432,2 +1612,3 @@ return filterPageComponents(id); | ||
}, | ||
// loadInclude is necessary for webpack | ||
loadInclude(id) { | ||
@@ -1454,2 +1635,3 @@ if (id === ROUTE_BLOCK_ID) | ||
}, | ||
// improves DX | ||
vite: { | ||
@@ -1456,0 +1638,0 @@ configureServer(server) { |
import * as unplugin from 'unplugin'; | ||
import { R as ResolvedOptions, S as ServerContext, L as LiteralStringUnion, O as Options } from './options-f6626ea0.js'; | ||
export { T as TreeNode, d as TreeNodeValueParam, e as TreeNodeValueStatic, c as createPrefixTree, b as createTreeNodeValue, g as getFileBasedRouteName, a as getPascalCaseRouteName } from './options-f6626ea0.js'; | ||
import { R as ResolvedOptions, S as ServerContext, L as LiteralStringUnion, O as Options } from './options-dc4b959a.js'; | ||
export { E as EditableTreeNode, T as TreeNode, d as TreeNodeValueParam, e as TreeNodeValueStatic, c as createPrefixTree, b as createTreeNodeValue, g as getFileBasedRouteName, a as getPascalCaseRouteName } from './options-dc4b959a.js'; | ||
import { RouteParamsRaw, RouteParams, RouteMeta, RouteLocationNormalized, RouteRecordName, RouteLocationNormalizedLoaded, RouteQueryAndHash, RouteLocationOptions, RouteLocation, NavigationGuardNext, NavigationFailure, Router, RouteLocationRaw, RouterLinkProps as RouterLinkProps$1 } from 'vue-router'; | ||
import { Ref, AllowedComponentProps, ComponentCustomProps, VNodeProps, UnwrapRef, VNode, ComputedRef } from 'vue'; | ||
export { a as _DataLoader, D as _DefineLoaderOptions } from './defineLoader-b1055382.js'; | ||
export { a as _DataLoader, D as _DefineLoaderOptions } from './defineLoader-bde635fd.js'; | ||
@@ -8,0 +8,0 @@ declare function createRoutesContext(options: ResolvedOptions): { |
@@ -38,2 +38,6 @@ "use strict"; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
@@ -47,2 +51,3 @@ mod | ||
__export(src_exports, { | ||
EditableTreeNode: () => EditableTreeNode, | ||
TreeNode: () => TreeNode, | ||
@@ -138,3 +143,4 @@ TreeNodeValueParam: () => TreeNodeValueParam, | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + (path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
result = result.replace(TRAILING_SLASH_RE, "") + // check path to avoid adding a trailing slash when joining an empty string | ||
(path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
} | ||
@@ -208,3 +214,7 @@ return result; | ||
function asRoutePath({ src, path = "" }, filePath) { | ||
return path + filePath.slice(src.length + 1); | ||
return ( | ||
// add the path prefix if any | ||
path + // remove the absolute path to the pages folder | ||
filePath.slice(src.length + 1) | ||
); | ||
} | ||
@@ -216,4 +226,14 @@ | ||
constructor(rawSegment, parent, pathSegment = rawSegment, subSegments = [pathSegment]) { | ||
/** | ||
* Overrides defined by each file. The map is necessary to handle named views. | ||
*/ | ||
this._overrides = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should we add the loader guard to the route record. | ||
*/ | ||
this.includeLoaderGuard = false; | ||
/** | ||
* View name (Vue Router feature) mapped to their corresponding file. By default, the view name is `default` unless | ||
* specified with a `@` e.g. `index@aux.vue` will have a view name of `aux`. | ||
*/ | ||
this.components = /* @__PURE__ */ new Map(); | ||
@@ -225,3 +245,4 @@ this._type = 0; | ||
const parentPath = parent == null ? void 0 : parent.path; | ||
this.path = (!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.path = // both the root record and the index record have a path of / | ||
(!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
} | ||
@@ -239,3 +260,6 @@ toString() { | ||
return [...this._overrides.entries()].sort( | ||
([nameA], [nameB]) => nameA === nameB ? 0 : nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
([nameA], [nameB]) => nameA === nameB ? 0 : ( | ||
// EDITS_OVERRIDE_NAME should always be last | ||
nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
) | ||
).reduce((acc, [_path, routeBlock]) => { | ||
@@ -248,2 +272,7 @@ return mergeRouteRecordOverride(acc, routeBlock); | ||
} | ||
/** | ||
* Remove all overrides for a given key. | ||
* | ||
* @param key - key to remove from the override | ||
*/ | ||
removeOverride(key) { | ||
@@ -382,3 +411,9 @@ this._overrides.forEach((routeBlock) => { | ||
constructor(options, filePath, parent) { | ||
/** | ||
* children of the node | ||
*/ | ||
this.children = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should this page import the page info | ||
*/ | ||
this.hasDefinePage = false; | ||
@@ -389,2 +424,9 @@ this.options = options; | ||
} | ||
/** | ||
* Adds a path to the tree. `path` cannot start with a `/`. | ||
* | ||
* @param path - path segment to insert. **It must contain the file extension** this allows to | ||
* differentiate between folders and files. | ||
* @param filePath - file path, defaults to path for convenience and testing | ||
*/ | ||
insert(path, filePath = path) { | ||
@@ -415,2 +457,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Delete and detach itself from the tree. | ||
*/ | ||
delete() { | ||
@@ -423,2 +468,8 @@ if (!this.parent) { | ||
} | ||
/** | ||
* 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`. | ||
* The `path` should be relative to the page folder. | ||
* | ||
* @param path - path segment of the file | ||
*/ | ||
remove(path) { | ||
@@ -449,2 +500,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Returns the route path of the node without parent paths. If the path was overridden, it returns the override. | ||
*/ | ||
get path() { | ||
@@ -454,2 +508,5 @@ var _a, _b; | ||
} | ||
/** | ||
* Returns the route path of the node including parent paths. | ||
*/ | ||
get fullPath() { | ||
@@ -459,5 +516,11 @@ var _a; | ||
} | ||
/** | ||
* Returns the route name of the node. If the name was overridden, it returns the override. | ||
*/ | ||
get name() { | ||
return this.value.overrides.name || this.options.getRouteName(this); | ||
} | ||
/** | ||
* Returns the meta property as an object. | ||
*/ | ||
get metaAsObject() { | ||
@@ -470,2 +533,6 @@ const meta = __spreadValues({}, this.value.overrides.meta); | ||
} | ||
/** | ||
* Returns the JSON string of the meta object of the node. If the meta was overridden, it returns the override. If | ||
* there is no override, it returns an empty string. | ||
*/ | ||
get meta() { | ||
@@ -486,2 +553,7 @@ const overrideMeta = this.metaAsObject; | ||
} | ||
/** | ||
* Returns wether this tree node is the root node of the tree. | ||
* | ||
* @returns true if the node is the root node | ||
*/ | ||
isRoot() { | ||
@@ -491,3 +563,5 @@ return this.value.path === "/" && !this.value.components.size; | ||
toString() { | ||
return `${this.value}${this.value.components.size > 1 || this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
return `${this.value}${// either we have multiple names | ||
this.value.components.size > 1 || // or we have one name and it's not default | ||
this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
} | ||
@@ -508,2 +582,6 @@ }; | ||
} | ||
/** | ||
* | ||
* @param filePath - | ||
*/ | ||
removeChild(filePath) { | ||
@@ -550,3 +628,6 @@ if (this.map.has(filePath)) { | ||
(param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `ParamValueZeroOrOne<${isRaw}>` : `ParamValue<${isRaw}>`) | ||
).join(", ")} }` : "Record<never, never>"; | ||
).join(", ")} }` : ( | ||
// no params allowed | ||
"Record<never, never>" | ||
); | ||
} | ||
@@ -560,4 +641,8 @@ | ||
} | ||
return (node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : ""); | ||
return ( | ||
// if the node has a filePath, it's a component, it has a routeName and it should be referenced in the RouteNamedMap | ||
// otherwise it should be skipped to avoid navigating to a route that doesn't render anything | ||
(node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : "") | ||
); | ||
} | ||
@@ -594,3 +679,4 @@ function generateRouteRecordInfo(node) { | ||
${indentStr}${node.value.components.size ? `name: '${node.name}',` : `/* internal name: '${node.name}' */`} | ||
${indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
${// component | ||
indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
node, | ||
@@ -603,3 +689,4 @@ indentStr, | ||
` : ""}${overrides.alias != null ? indentStr + `alias: ${JSON.stringify(overrides.alias)}, | ||
` : ""}${indentStr}${node.children.size > 0 ? `children: [ | ||
` : ""}${// children | ||
indentStr}${node.children.size > 0 ? `children: [ | ||
${node.getSortedChildren().map((child) => generateRouteRecord(child, options, importList, indent + 2)).join(",\n")} | ||
@@ -627,11 +714,14 @@ ${indentStr}],` : "/* no children */"}${formatMeta(node, indentStr)} | ||
const isDefaultExport = files.length === 1 && files[0][0] === "default"; | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : `components: { | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : ( | ||
// files has at least one entry | ||
`components: { | ||
${files.map( | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},`; | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},` | ||
); | ||
} | ||
@@ -736,4 +826,7 @@ function generatePageImport(filepath, importMode, importList) { | ||
ignoreInitial: true, | ||
// disableGlobbing: true, | ||
ignorePermissionErrors: true, | ||
ignored: options.exclude | ||
// useFsEvents: true, | ||
// TODO: allow user options | ||
}); | ||
@@ -969,3 +1062,9 @@ } | ||
if (!definePageNodes.length) { | ||
return isExtractingDefinePage ? "export default {}" : null; | ||
return isExtractingDefinePage ? ( | ||
// e.g. index.vue?definePage that contains a commented `definePage() | ||
"export default {}" | ||
) : ( | ||
// e.g. index.vue that contains a commented `definePage() | ||
null | ||
); | ||
} else if (definePageNodes.length > 1) { | ||
@@ -1068,8 +1167,16 @@ throw new SyntaxError(`duplicate definePage() call`); | ||
var EditableTreeNode = class { | ||
// private _parent?: EditableTreeNode | ||
constructor(node) { | ||
this.node = node; | ||
} | ||
/** | ||
* Remove and detach the current route node from the tree. Subsequently, its children will be removed as well. | ||
*/ | ||
delete() { | ||
return this.node.delete(); | ||
} | ||
/** | ||
* Inserts a new route as a child of this route. This route cannot use `definePage()`. If it was meant to be included, | ||
* add it to the `routesFolder` option. | ||
*/ | ||
insert(path, filePath) { | ||
@@ -1093,20 +1200,46 @@ const extDotIndex = filePath.lastIndexOf("."); | ||
} | ||
/** | ||
* Get an editable version of the parent node if it exists. | ||
*/ | ||
get parent() { | ||
return this.node.parent && new EditableTreeNode(this.node.parent); | ||
} | ||
/** | ||
* Return a Map of the files associated to the current route. The key of the map represents the name of the view (Vue | ||
* Router feature) while the value is the file path. By default, the name of the view is `default`. | ||
*/ | ||
get components() { | ||
return this.node.value.components; | ||
} | ||
/** | ||
* Name of the route. Note that **all routes are named** but when the final `routes` array is generated, routes | ||
* without a `component` will not include their `name` property to avoid accidentally navigating to them and display | ||
* nothing. {@see isPassThrough} | ||
*/ | ||
get name() { | ||
return this.node.name; | ||
} | ||
/** | ||
* Override the name of the route. | ||
*/ | ||
set name(name) { | ||
this.node.value.addEditOverride({ name }); | ||
} | ||
/** | ||
* Whether the route is a pass-through route. A pass-through route is a route that does not have a component and is | ||
* used to group other routes under the same prefix `path` and/or `meta` properties. | ||
*/ | ||
get isPassThrough() { | ||
return this.node.value.components.size === 0; | ||
} | ||
/** | ||
* 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**. | ||
*/ | ||
get meta() { | ||
return this.node.metaAsObject; | ||
} | ||
/** | ||
* Override the meta property of the route. This will discard any other meta property defined with `<route>` blocks or | ||
* through other means. | ||
*/ | ||
set meta(meta) { | ||
@@ -1116,8 +1249,19 @@ this.node.value.removeOverride("meta"); | ||
} | ||
/** | ||
* Add meta properties to the route keeping the existing ones. The passed object will be deeply merged with the | ||
* existing meta object if any. Note that the meta property is later on serialized as JSON so you can't pass functions | ||
* or any other non-serializable value. | ||
*/ | ||
addToMeta(meta) { | ||
this.node.value.addEditOverride({ meta }); | ||
} | ||
/** | ||
* Path of the route without parent paths. | ||
*/ | ||
get path() { | ||
return this.node.path; | ||
} | ||
/** | ||
* Override the path of the route. You must ensure `params` match with the existing path. | ||
*/ | ||
set path(path) { | ||
@@ -1132,14 +1276,37 @@ if (!path.startsWith("/")) { | ||
} | ||
/** | ||
* Alias of the route. | ||
*/ | ||
get alias() { | ||
return this.node.value.overrides.alias; | ||
} | ||
/** | ||
* Add an alias to the route. | ||
* | ||
* @param alias - Alias to add to the route | ||
*/ | ||
addAlias(alias) { | ||
this.node.value.addEditOverride({ alias }); | ||
} | ||
/** | ||
* Array of the route params and all of its parent's params. | ||
*/ | ||
get params() { | ||
return this.node.params; | ||
} | ||
/** | ||
* Path of the route including parent paths. | ||
*/ | ||
get fullPath() { | ||
return this.node.fullPath; | ||
} | ||
/** | ||
* DFS traversal of the tree. | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseDFS() { | ||
@@ -1156,2 +1323,12 @@ if (!this.node.isRoot()) { | ||
} | ||
/** | ||
* BFS traversal of the tree as a generator. | ||
* | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseBFS() { | ||
@@ -1197,2 +1374,4 @@ for (const [_name, child] of this.node.children) { | ||
cwd: folder.src, | ||
// TODO: do they return the symbolic link path or the original file? | ||
// followSymbolicLinks: false, | ||
ignore: options.exclude | ||
@@ -1425,2 +1604,3 @@ }).then((files) => files.map((file) => (0, import_pathe2.resolve)(folder.src, file))).then( | ||
), | ||
// importing the definePage block | ||
/definePage\&vue$/ | ||
@@ -1453,2 +1633,3 @@ ], | ||
}, | ||
// we only need to transform page components | ||
transformInclude(id) { | ||
@@ -1460,2 +1641,3 @@ return filterPageComponents(id); | ||
}, | ||
// loadInclude is necessary for webpack | ||
loadInclude(id) { | ||
@@ -1482,2 +1664,3 @@ if (id === ROUTE_BLOCK_ID) | ||
}, | ||
// improves DX | ||
vite: { | ||
@@ -1496,2 +1679,4 @@ configureServer(server) { | ||
"onBeforeRouteLeave" | ||
// NOTE: the typing seems broken locally, so instead we export it directly from unplugin-vue-router/runtime | ||
// 'definePage', | ||
]; | ||
@@ -1504,2 +1689,3 @@ var VueRouterAutoImports = { | ||
0 && (module.exports = { | ||
EditableTreeNode, | ||
TreeNode, | ||
@@ -1506,0 +1692,0 @@ TreeNodeValueParam, |
@@ -1,2 +0,2 @@ | ||
export { D as DEFAULT_OPTIONS, O as Options, R as ResolvedOptions, h as RoutesFolder, f as RoutesFolderOption, S as ServerContext, i as _OptionsImportMode, _ as _RoutesFolder, r as resolveOptions } from './options-f6626ea0.js'; | ||
export { D as DEFAULT_OPTIONS, O as Options, R as ResolvedOptions, h as RoutesFolder, f as RoutesFolderOption, S as ServerContext, i as _OptionsImportMode, _ as _RoutesFolder, r as resolveOptions } from './options-dc4b959a.js'; | ||
import 'vue-router'; |
export { } |
import * as rollup from 'rollup'; | ||
import { O as Options } from './options-f6626ea0.js'; | ||
import { O as Options } from './options-dc4b959a.js'; | ||
import 'vue-router'; | ||
@@ -4,0 +4,0 @@ |
@@ -38,2 +38,6 @@ "use strict"; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
@@ -129,3 +133,4 @@ mod | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + (path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
result = result.replace(TRAILING_SLASH_RE, "") + // check path to avoid adding a trailing slash when joining an empty string | ||
(path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
} | ||
@@ -180,3 +185,7 @@ return result; | ||
function asRoutePath({ src, path = "" }, filePath) { | ||
return path + filePath.slice(src.length + 1); | ||
return ( | ||
// add the path prefix if any | ||
path + // remove the absolute path to the pages folder | ||
filePath.slice(src.length + 1) | ||
); | ||
} | ||
@@ -188,4 +197,14 @@ | ||
constructor(rawSegment, parent, pathSegment = rawSegment, subSegments = [pathSegment]) { | ||
/** | ||
* Overrides defined by each file. The map is necessary to handle named views. | ||
*/ | ||
this._overrides = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should we add the loader guard to the route record. | ||
*/ | ||
this.includeLoaderGuard = false; | ||
/** | ||
* View name (Vue Router feature) mapped to their corresponding file. By default, the view name is `default` unless | ||
* specified with a `@` e.g. `index@aux.vue` will have a view name of `aux`. | ||
*/ | ||
this.components = /* @__PURE__ */ new Map(); | ||
@@ -197,3 +216,4 @@ this._type = 0; | ||
const parentPath = parent == null ? void 0 : parent.path; | ||
this.path = (!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.path = // both the root record and the index record have a path of / | ||
(!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
} | ||
@@ -211,3 +231,6 @@ toString() { | ||
return [...this._overrides.entries()].sort( | ||
([nameA], [nameB]) => nameA === nameB ? 0 : nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
([nameA], [nameB]) => nameA === nameB ? 0 : ( | ||
// EDITS_OVERRIDE_NAME should always be last | ||
nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
) | ||
).reduce((acc, [_path, routeBlock]) => { | ||
@@ -220,2 +243,7 @@ return mergeRouteRecordOverride(acc, routeBlock); | ||
} | ||
/** | ||
* Remove all overrides for a given key. | ||
* | ||
* @param key - key to remove from the override | ||
*/ | ||
removeOverride(key) { | ||
@@ -354,3 +382,9 @@ this._overrides.forEach((routeBlock) => { | ||
constructor(options, filePath, parent) { | ||
/** | ||
* children of the node | ||
*/ | ||
this.children = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should this page import the page info | ||
*/ | ||
this.hasDefinePage = false; | ||
@@ -361,2 +395,9 @@ this.options = options; | ||
} | ||
/** | ||
* Adds a path to the tree. `path` cannot start with a `/`. | ||
* | ||
* @param path - path segment to insert. **It must contain the file extension** this allows to | ||
* differentiate between folders and files. | ||
* @param filePath - file path, defaults to path for convenience and testing | ||
*/ | ||
insert(path, filePath = path) { | ||
@@ -387,2 +428,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Delete and detach itself from the tree. | ||
*/ | ||
delete() { | ||
@@ -395,2 +439,8 @@ if (!this.parent) { | ||
} | ||
/** | ||
* 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`. | ||
* The `path` should be relative to the page folder. | ||
* | ||
* @param path - path segment of the file | ||
*/ | ||
remove(path) { | ||
@@ -421,2 +471,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Returns the route path of the node without parent paths. If the path was overridden, it returns the override. | ||
*/ | ||
get path() { | ||
@@ -426,2 +479,5 @@ var _a, _b; | ||
} | ||
/** | ||
* Returns the route path of the node including parent paths. | ||
*/ | ||
get fullPath() { | ||
@@ -431,5 +487,11 @@ var _a; | ||
} | ||
/** | ||
* Returns the route name of the node. If the name was overridden, it returns the override. | ||
*/ | ||
get name() { | ||
return this.value.overrides.name || this.options.getRouteName(this); | ||
} | ||
/** | ||
* Returns the meta property as an object. | ||
*/ | ||
get metaAsObject() { | ||
@@ -442,2 +504,6 @@ const meta = __spreadValues({}, this.value.overrides.meta); | ||
} | ||
/** | ||
* Returns the JSON string of the meta object of the node. If the meta was overridden, it returns the override. If | ||
* there is no override, it returns an empty string. | ||
*/ | ||
get meta() { | ||
@@ -458,2 +524,7 @@ const overrideMeta = this.metaAsObject; | ||
} | ||
/** | ||
* Returns wether this tree node is the root node of the tree. | ||
* | ||
* @returns true if the node is the root node | ||
*/ | ||
isRoot() { | ||
@@ -463,3 +534,5 @@ return this.value.path === "/" && !this.value.components.size; | ||
toString() { | ||
return `${this.value}${this.value.components.size > 1 || this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
return `${this.value}${// either we have multiple names | ||
this.value.components.size > 1 || // or we have one name and it's not default | ||
this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
} | ||
@@ -480,2 +553,6 @@ }; | ||
} | ||
/** | ||
* | ||
* @param filePath - | ||
*/ | ||
removeChild(filePath) { | ||
@@ -522,3 +599,6 @@ if (this.map.has(filePath)) { | ||
(param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `ParamValueZeroOrOne<${isRaw}>` : `ParamValue<${isRaw}>`) | ||
).join(", ")} }` : "Record<never, never>"; | ||
).join(", ")} }` : ( | ||
// no params allowed | ||
"Record<never, never>" | ||
); | ||
} | ||
@@ -532,4 +612,8 @@ | ||
} | ||
return (node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : ""); | ||
return ( | ||
// if the node has a filePath, it's a component, it has a routeName and it should be referenced in the RouteNamedMap | ||
// otherwise it should be skipped to avoid navigating to a route that doesn't render anything | ||
(node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : "") | ||
); | ||
} | ||
@@ -566,3 +650,4 @@ function generateRouteRecordInfo(node) { | ||
${indentStr}${node.value.components.size ? `name: '${node.name}',` : `/* internal name: '${node.name}' */`} | ||
${indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
${// component | ||
indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
node, | ||
@@ -575,3 +660,4 @@ indentStr, | ||
` : ""}${overrides.alias != null ? indentStr + `alias: ${JSON.stringify(overrides.alias)}, | ||
` : ""}${indentStr}${node.children.size > 0 ? `children: [ | ||
` : ""}${// children | ||
indentStr}${node.children.size > 0 ? `children: [ | ||
${node.getSortedChildren().map((child) => generateRouteRecord(child, options, importList, indent + 2)).join(",\n")} | ||
@@ -599,11 +685,14 @@ ${indentStr}],` : "/* no children */"}${formatMeta(node, indentStr)} | ||
const isDefaultExport = files.length === 1 && files[0][0] === "default"; | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : `components: { | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : ( | ||
// files has at least one entry | ||
`components: { | ||
${files.map( | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},`; | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},` | ||
); | ||
} | ||
@@ -708,4 +797,7 @@ function generatePageImport(filepath, importMode, importList) { | ||
ignoreInitial: true, | ||
// disableGlobbing: true, | ||
ignorePermissionErrors: true, | ||
ignored: options.exclude | ||
// useFsEvents: true, | ||
// TODO: allow user options | ||
}); | ||
@@ -941,3 +1033,9 @@ } | ||
if (!definePageNodes.length) { | ||
return isExtractingDefinePage ? "export default {}" : null; | ||
return isExtractingDefinePage ? ( | ||
// e.g. index.vue?definePage that contains a commented `definePage() | ||
"export default {}" | ||
) : ( | ||
// e.g. index.vue that contains a commented `definePage() | ||
null | ||
); | ||
} else if (definePageNodes.length > 1) { | ||
@@ -1040,8 +1138,16 @@ throw new SyntaxError(`duplicate definePage() call`); | ||
var EditableTreeNode = class { | ||
// private _parent?: EditableTreeNode | ||
constructor(node) { | ||
this.node = node; | ||
} | ||
/** | ||
* Remove and detach the current route node from the tree. Subsequently, its children will be removed as well. | ||
*/ | ||
delete() { | ||
return this.node.delete(); | ||
} | ||
/** | ||
* Inserts a new route as a child of this route. This route cannot use `definePage()`. If it was meant to be included, | ||
* add it to the `routesFolder` option. | ||
*/ | ||
insert(path, filePath) { | ||
@@ -1065,20 +1171,46 @@ const extDotIndex = filePath.lastIndexOf("."); | ||
} | ||
/** | ||
* Get an editable version of the parent node if it exists. | ||
*/ | ||
get parent() { | ||
return this.node.parent && new EditableTreeNode(this.node.parent); | ||
} | ||
/** | ||
* Return a Map of the files associated to the current route. The key of the map represents the name of the view (Vue | ||
* Router feature) while the value is the file path. By default, the name of the view is `default`. | ||
*/ | ||
get components() { | ||
return this.node.value.components; | ||
} | ||
/** | ||
* Name of the route. Note that **all routes are named** but when the final `routes` array is generated, routes | ||
* without a `component` will not include their `name` property to avoid accidentally navigating to them and display | ||
* nothing. {@see isPassThrough} | ||
*/ | ||
get name() { | ||
return this.node.name; | ||
} | ||
/** | ||
* Override the name of the route. | ||
*/ | ||
set name(name) { | ||
this.node.value.addEditOverride({ name }); | ||
} | ||
/** | ||
* Whether the route is a pass-through route. A pass-through route is a route that does not have a component and is | ||
* used to group other routes under the same prefix `path` and/or `meta` properties. | ||
*/ | ||
get isPassThrough() { | ||
return this.node.value.components.size === 0; | ||
} | ||
/** | ||
* 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**. | ||
*/ | ||
get meta() { | ||
return this.node.metaAsObject; | ||
} | ||
/** | ||
* Override the meta property of the route. This will discard any other meta property defined with `<route>` blocks or | ||
* through other means. | ||
*/ | ||
set meta(meta) { | ||
@@ -1088,8 +1220,19 @@ this.node.value.removeOverride("meta"); | ||
} | ||
/** | ||
* Add meta properties to the route keeping the existing ones. The passed object will be deeply merged with the | ||
* existing meta object if any. Note that the meta property is later on serialized as JSON so you can't pass functions | ||
* or any other non-serializable value. | ||
*/ | ||
addToMeta(meta) { | ||
this.node.value.addEditOverride({ meta }); | ||
} | ||
/** | ||
* Path of the route without parent paths. | ||
*/ | ||
get path() { | ||
return this.node.path; | ||
} | ||
/** | ||
* Override the path of the route. You must ensure `params` match with the existing path. | ||
*/ | ||
set path(path) { | ||
@@ -1104,14 +1247,37 @@ if (!path.startsWith("/")) { | ||
} | ||
/** | ||
* Alias of the route. | ||
*/ | ||
get alias() { | ||
return this.node.value.overrides.alias; | ||
} | ||
/** | ||
* Add an alias to the route. | ||
* | ||
* @param alias - Alias to add to the route | ||
*/ | ||
addAlias(alias) { | ||
this.node.value.addEditOverride({ alias }); | ||
} | ||
/** | ||
* Array of the route params and all of its parent's params. | ||
*/ | ||
get params() { | ||
return this.node.params; | ||
} | ||
/** | ||
* Path of the route including parent paths. | ||
*/ | ||
get fullPath() { | ||
return this.node.fullPath; | ||
} | ||
/** | ||
* DFS traversal of the tree. | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseDFS() { | ||
@@ -1128,2 +1294,12 @@ if (!this.node.isRoot()) { | ||
} | ||
/** | ||
* BFS traversal of the tree as a generator. | ||
* | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseBFS() { | ||
@@ -1169,2 +1345,4 @@ for (const [_name, child] of this.node.children) { | ||
cwd: folder.src, | ||
// TODO: do they return the symbolic link path or the original file? | ||
// followSymbolicLinks: false, | ||
ignore: options.exclude | ||
@@ -1397,2 +1575,3 @@ }).then((files) => files.map((file) => (0, import_pathe2.resolve)(folder.src, file))).then( | ||
), | ||
// importing the definePage block | ||
/definePage\&vue$/ | ||
@@ -1425,2 +1604,3 @@ ], | ||
}, | ||
// we only need to transform page components | ||
transformInclude(id) { | ||
@@ -1432,2 +1612,3 @@ return filterPageComponents(id); | ||
}, | ||
// loadInclude is necessary for webpack | ||
loadInclude(id) { | ||
@@ -1454,2 +1635,3 @@ if (id === ROUTE_BLOCK_ID) | ||
}, | ||
// improves DX | ||
vite: { | ||
@@ -1456,0 +1638,0 @@ configureServer(server) { |
import { Router, RouteLocationNormalized, RouteRecordRaw } from 'vue-router'; | ||
import { a as DataLoader } from './defineLoader-b1055382.js'; | ||
export { D as DefineLoaderOptions, d as _defineLoader, s as _stopDataFetchingScope } from './defineLoader-b1055382.js'; | ||
import { A as Awaitable } from './options-f6626ea0.js'; | ||
import { a as DataLoader } from './defineLoader-bde635fd.js'; | ||
export { D as DefineLoaderOptions, d as _defineLoader, s as _stopDataFetchingScope } from './defineLoader-bde635fd.js'; | ||
import { A as Awaitable } from './options-dc4b959a.js'; | ||
import 'vue'; | ||
@@ -40,5 +40,19 @@ | ||
*/ | ||
declare const _definePage: (route: Partial<Omit<RouteRecordRaw, 'children' | 'components' | 'component'>>) => Partial<Omit<RouteRecordRaw, "components" | "component" | "children">>; | ||
declare const _definePage: (route: DefinePage) => DefinePage; | ||
/** | ||
* Merges route records. | ||
* | ||
* @internal | ||
* | ||
* @param main - main route record | ||
* @param routeRecords - route records to merge | ||
* @returns merged route record | ||
*/ | ||
declare function _mergeRouteRecord(main: RouteRecordRaw, ...routeRecords: Partial<RouteRecordRaw>[]): RouteRecordRaw; | ||
/** | ||
* Type to define a page. Can be augmented to add custom properties. | ||
*/ | ||
interface DefinePage extends Partial<Omit<RouteRecordRaw, 'children' | 'components' | 'component'>> { | ||
} | ||
export { DataLoader, HasDataLoaderMeta as _HasDataLoaderMeta, _definePage, _mergeRouteRecord, setupDataFetchingGuard as _setupDataFetchingGuard }; | ||
export { DataLoader, DefinePage, HasDataLoaderMeta as _HasDataLoaderMeta, _definePage, _mergeRouteRecord, setupDataFetchingGuard as _setupDataFetchingGuard }; |
@@ -56,4 +56,8 @@ "use strict"; | ||
const { cacheTime } = options; | ||
return !cacheTime || Date.now() - entry.when >= cacheTime || Array.from(entry.children).some( | ||
(childEntry) => isCacheExpired(childEntry, options) | ||
return ( | ||
// cacheTime == 0 means no cache | ||
!cacheTime || // did we hit the expiration time | ||
Date.now() - entry.when >= cacheTime || Array.from(entry.children).some( | ||
(childEntry) => isCacheExpired(childEntry, options) | ||
) | ||
); | ||
@@ -65,8 +69,12 @@ } | ||
error: (0, import_vue.ref)(), | ||
// set to 0 to when there is an initialData so the next request will always trigger the data loaders | ||
when: initialData === void 0 ? Date.now() : 0, | ||
children: /* @__PURE__ */ new Set(), | ||
// @ts-expect-error: data always start as empty | ||
data: (0, import_vue.ref)(initialData), | ||
params: {}, | ||
query: {}, | ||
// hash: null, | ||
isReady: false | ||
// this was just too annoying to type | ||
})); | ||
@@ -122,3 +130,5 @@ } | ||
cacheTime: 1e3 * 5, | ||
// 5s | ||
lazy: false, | ||
// same as no key | ||
key: "" | ||
@@ -138,3 +148,7 @@ }; | ||
const route = _route || (0, import_vue_router.useRoute)(); | ||
if (!entries.has(router) || parentEntry) { | ||
if ( | ||
// no cache: we need to load | ||
!entries.has(router) || // invoked by the parent, we should try to load again | ||
parentEntry | ||
) { | ||
load(route, router, parentEntry); | ||
@@ -184,6 +198,13 @@ } | ||
const isExpired = isCacheExpired(entry, options); | ||
if (pendingPromise && !needsNewLoad && currentNavigation === route && (!isReady || !isExpired)) { | ||
if (pendingPromise && // if we need to fetch again due to param/query changes | ||
!needsNewLoad && // if it's a new navigation and there is no entry, we cannot rely on the pendingPromise as we don't know what | ||
// params and query were used and could have changed. If we had an entry, then we can rely on the result of | ||
// `needsNewLoad` | ||
currentNavigation === route && // if we are not ready but we have a pendingPromise, we are already fetching so we can reuse it | ||
(!isReady || !isExpired)) { | ||
return lazy ? Promise.resolve() : pendingPromise; | ||
} | ||
if (needsNewLoad || !isReady && currentNavigation !== route || isReady && isExpired) { | ||
if (needsNewLoad || // if we never finished loading we cannot rely on needsNewLoad | ||
!isReady && currentNavigation !== route || // we did a load but the cache expired | ||
isReady && isExpired) { | ||
pending.value = true; | ||
@@ -211,5 +232,10 @@ error.value = null; | ||
} | ||
return lazy || !pendingPromise ? Promise.resolve() : pendingPromise; | ||
return lazy || // lazy resolves immediately to not block navigation guards | ||
!pendingPromise ? Promise.resolve() : ( | ||
// pendingPromise is thisPromise | ||
pendingPromise | ||
); | ||
} | ||
dataLoader._ = { | ||
// loader, | ||
entries, | ||
@@ -223,4 +249,7 @@ load, | ||
function shouldFetchAgain(entry, route) { | ||
return !entry.when || !includesParams(route.params, entry.params) || !includesParams(route.query, entry.query) || entry.hash != null && entry.hash !== route.hash || Array.from(entry.children).some( | ||
(childEntry) => shouldFetchAgain(childEntry, route) | ||
return ( | ||
// manually invalidated | ||
!entry.when || !includesParams(route.params, entry.params) || !includesParams(route.query, entry.query) || entry.hash != null && entry.hash !== route.hash || Array.from(entry.children).some( | ||
(childEntry) => shouldFetchAgain(childEntry, route) | ||
) | ||
); | ||
@@ -238,2 +267,3 @@ } | ||
__spreadProps(__spreadValues({}, route), { | ||
// track the hash | ||
get hash() { | ||
@@ -281,2 +311,3 @@ return hash.v = route.hash; | ||
return Promise.all( | ||
// retrieve all loaders as a flat array | ||
to.matched.flatMap((route) => route.meta[HasDataLoaderMeta]).filter(Boolean).map( | ||
@@ -286,2 +317,3 @@ (moduleImport) => moduleImport().then((mod) => { | ||
return Promise.all( | ||
// load will ensure only one request is happening at a time | ||
loaders.map((loader) => { | ||
@@ -297,2 +329,3 @@ const { | ||
initialData | ||
// FIXME: could the data.value be passed as an argument here? | ||
).then(() => { | ||
@@ -299,0 +332,0 @@ if (!initialData) { |
import * as vite from 'vite'; | ||
import { O as Options } from './options-f6626ea0.js'; | ||
import { O as Options } from './options-dc4b959a.js'; | ||
import 'vue-router'; | ||
@@ -4,0 +4,0 @@ |
220
dist/vite.js
@@ -38,2 +38,6 @@ "use strict"; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
@@ -129,3 +133,4 @@ mod | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + (path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
result = result.replace(TRAILING_SLASH_RE, "") + // check path to avoid adding a trailing slash when joining an empty string | ||
(path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
} | ||
@@ -180,3 +185,7 @@ return result; | ||
function asRoutePath({ src, path = "" }, filePath) { | ||
return path + filePath.slice(src.length + 1); | ||
return ( | ||
// add the path prefix if any | ||
path + // remove the absolute path to the pages folder | ||
filePath.slice(src.length + 1) | ||
); | ||
} | ||
@@ -188,4 +197,14 @@ | ||
constructor(rawSegment, parent, pathSegment = rawSegment, subSegments = [pathSegment]) { | ||
/** | ||
* Overrides defined by each file. The map is necessary to handle named views. | ||
*/ | ||
this._overrides = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should we add the loader guard to the route record. | ||
*/ | ||
this.includeLoaderGuard = false; | ||
/** | ||
* View name (Vue Router feature) mapped to their corresponding file. By default, the view name is `default` unless | ||
* specified with a `@` e.g. `index@aux.vue` will have a view name of `aux`. | ||
*/ | ||
this.components = /* @__PURE__ */ new Map(); | ||
@@ -197,3 +216,4 @@ this._type = 0; | ||
const parentPath = parent == null ? void 0 : parent.path; | ||
this.path = (!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.path = // both the root record and the index record have a path of / | ||
(!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
} | ||
@@ -211,3 +231,6 @@ toString() { | ||
return [...this._overrides.entries()].sort( | ||
([nameA], [nameB]) => nameA === nameB ? 0 : nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
([nameA], [nameB]) => nameA === nameB ? 0 : ( | ||
// EDITS_OVERRIDE_NAME should always be last | ||
nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
) | ||
).reduce((acc, [_path, routeBlock]) => { | ||
@@ -220,2 +243,7 @@ return mergeRouteRecordOverride(acc, routeBlock); | ||
} | ||
/** | ||
* Remove all overrides for a given key. | ||
* | ||
* @param key - key to remove from the override | ||
*/ | ||
removeOverride(key) { | ||
@@ -354,3 +382,9 @@ this._overrides.forEach((routeBlock) => { | ||
constructor(options, filePath, parent) { | ||
/** | ||
* children of the node | ||
*/ | ||
this.children = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should this page import the page info | ||
*/ | ||
this.hasDefinePage = false; | ||
@@ -361,2 +395,9 @@ this.options = options; | ||
} | ||
/** | ||
* Adds a path to the tree. `path` cannot start with a `/`. | ||
* | ||
* @param path - path segment to insert. **It must contain the file extension** this allows to | ||
* differentiate between folders and files. | ||
* @param filePath - file path, defaults to path for convenience and testing | ||
*/ | ||
insert(path, filePath = path) { | ||
@@ -387,2 +428,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Delete and detach itself from the tree. | ||
*/ | ||
delete() { | ||
@@ -395,2 +439,8 @@ if (!this.parent) { | ||
} | ||
/** | ||
* 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`. | ||
* The `path` should be relative to the page folder. | ||
* | ||
* @param path - path segment of the file | ||
*/ | ||
remove(path) { | ||
@@ -421,2 +471,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Returns the route path of the node without parent paths. If the path was overridden, it returns the override. | ||
*/ | ||
get path() { | ||
@@ -426,2 +479,5 @@ var _a, _b; | ||
} | ||
/** | ||
* Returns the route path of the node including parent paths. | ||
*/ | ||
get fullPath() { | ||
@@ -431,5 +487,11 @@ var _a; | ||
} | ||
/** | ||
* Returns the route name of the node. If the name was overridden, it returns the override. | ||
*/ | ||
get name() { | ||
return this.value.overrides.name || this.options.getRouteName(this); | ||
} | ||
/** | ||
* Returns the meta property as an object. | ||
*/ | ||
get metaAsObject() { | ||
@@ -442,2 +504,6 @@ const meta = __spreadValues({}, this.value.overrides.meta); | ||
} | ||
/** | ||
* Returns the JSON string of the meta object of the node. If the meta was overridden, it returns the override. If | ||
* there is no override, it returns an empty string. | ||
*/ | ||
get meta() { | ||
@@ -458,2 +524,7 @@ const overrideMeta = this.metaAsObject; | ||
} | ||
/** | ||
* Returns wether this tree node is the root node of the tree. | ||
* | ||
* @returns true if the node is the root node | ||
*/ | ||
isRoot() { | ||
@@ -463,3 +534,5 @@ return this.value.path === "/" && !this.value.components.size; | ||
toString() { | ||
return `${this.value}${this.value.components.size > 1 || this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
return `${this.value}${// either we have multiple names | ||
this.value.components.size > 1 || // or we have one name and it's not default | ||
this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
} | ||
@@ -480,2 +553,6 @@ }; | ||
} | ||
/** | ||
* | ||
* @param filePath - | ||
*/ | ||
removeChild(filePath) { | ||
@@ -522,3 +599,6 @@ if (this.map.has(filePath)) { | ||
(param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `ParamValueZeroOrOne<${isRaw}>` : `ParamValue<${isRaw}>`) | ||
).join(", ")} }` : "Record<never, never>"; | ||
).join(", ")} }` : ( | ||
// no params allowed | ||
"Record<never, never>" | ||
); | ||
} | ||
@@ -532,4 +612,8 @@ | ||
} | ||
return (node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : ""); | ||
return ( | ||
// if the node has a filePath, it's a component, it has a routeName and it should be referenced in the RouteNamedMap | ||
// otherwise it should be skipped to avoid navigating to a route that doesn't render anything | ||
(node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : "") | ||
); | ||
} | ||
@@ -566,3 +650,4 @@ function generateRouteRecordInfo(node) { | ||
${indentStr}${node.value.components.size ? `name: '${node.name}',` : `/* internal name: '${node.name}' */`} | ||
${indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
${// component | ||
indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
node, | ||
@@ -575,3 +660,4 @@ indentStr, | ||
` : ""}${overrides.alias != null ? indentStr + `alias: ${JSON.stringify(overrides.alias)}, | ||
` : ""}${indentStr}${node.children.size > 0 ? `children: [ | ||
` : ""}${// children | ||
indentStr}${node.children.size > 0 ? `children: [ | ||
${node.getSortedChildren().map((child) => generateRouteRecord(child, options, importList, indent + 2)).join(",\n")} | ||
@@ -599,11 +685,14 @@ ${indentStr}],` : "/* no children */"}${formatMeta(node, indentStr)} | ||
const isDefaultExport = files.length === 1 && files[0][0] === "default"; | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : `components: { | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : ( | ||
// files has at least one entry | ||
`components: { | ||
${files.map( | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},`; | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},` | ||
); | ||
} | ||
@@ -708,4 +797,7 @@ function generatePageImport(filepath, importMode, importList) { | ||
ignoreInitial: true, | ||
// disableGlobbing: true, | ||
ignorePermissionErrors: true, | ||
ignored: options.exclude | ||
// useFsEvents: true, | ||
// TODO: allow user options | ||
}); | ||
@@ -941,3 +1033,9 @@ } | ||
if (!definePageNodes.length) { | ||
return isExtractingDefinePage ? "export default {}" : null; | ||
return isExtractingDefinePage ? ( | ||
// e.g. index.vue?definePage that contains a commented `definePage() | ||
"export default {}" | ||
) : ( | ||
// e.g. index.vue that contains a commented `definePage() | ||
null | ||
); | ||
} else if (definePageNodes.length > 1) { | ||
@@ -1040,8 +1138,16 @@ throw new SyntaxError(`duplicate definePage() call`); | ||
var EditableTreeNode = class { | ||
// private _parent?: EditableTreeNode | ||
constructor(node) { | ||
this.node = node; | ||
} | ||
/** | ||
* Remove and detach the current route node from the tree. Subsequently, its children will be removed as well. | ||
*/ | ||
delete() { | ||
return this.node.delete(); | ||
} | ||
/** | ||
* Inserts a new route as a child of this route. This route cannot use `definePage()`. If it was meant to be included, | ||
* add it to the `routesFolder` option. | ||
*/ | ||
insert(path, filePath) { | ||
@@ -1065,20 +1171,46 @@ const extDotIndex = filePath.lastIndexOf("."); | ||
} | ||
/** | ||
* Get an editable version of the parent node if it exists. | ||
*/ | ||
get parent() { | ||
return this.node.parent && new EditableTreeNode(this.node.parent); | ||
} | ||
/** | ||
* Return a Map of the files associated to the current route. The key of the map represents the name of the view (Vue | ||
* Router feature) while the value is the file path. By default, the name of the view is `default`. | ||
*/ | ||
get components() { | ||
return this.node.value.components; | ||
} | ||
/** | ||
* Name of the route. Note that **all routes are named** but when the final `routes` array is generated, routes | ||
* without a `component` will not include their `name` property to avoid accidentally navigating to them and display | ||
* nothing. {@see isPassThrough} | ||
*/ | ||
get name() { | ||
return this.node.name; | ||
} | ||
/** | ||
* Override the name of the route. | ||
*/ | ||
set name(name) { | ||
this.node.value.addEditOverride({ name }); | ||
} | ||
/** | ||
* Whether the route is a pass-through route. A pass-through route is a route that does not have a component and is | ||
* used to group other routes under the same prefix `path` and/or `meta` properties. | ||
*/ | ||
get isPassThrough() { | ||
return this.node.value.components.size === 0; | ||
} | ||
/** | ||
* 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**. | ||
*/ | ||
get meta() { | ||
return this.node.metaAsObject; | ||
} | ||
/** | ||
* Override the meta property of the route. This will discard any other meta property defined with `<route>` blocks or | ||
* through other means. | ||
*/ | ||
set meta(meta) { | ||
@@ -1088,8 +1220,19 @@ this.node.value.removeOverride("meta"); | ||
} | ||
/** | ||
* Add meta properties to the route keeping the existing ones. The passed object will be deeply merged with the | ||
* existing meta object if any. Note that the meta property is later on serialized as JSON so you can't pass functions | ||
* or any other non-serializable value. | ||
*/ | ||
addToMeta(meta) { | ||
this.node.value.addEditOverride({ meta }); | ||
} | ||
/** | ||
* Path of the route without parent paths. | ||
*/ | ||
get path() { | ||
return this.node.path; | ||
} | ||
/** | ||
* Override the path of the route. You must ensure `params` match with the existing path. | ||
*/ | ||
set path(path) { | ||
@@ -1104,14 +1247,37 @@ if (!path.startsWith("/")) { | ||
} | ||
/** | ||
* Alias of the route. | ||
*/ | ||
get alias() { | ||
return this.node.value.overrides.alias; | ||
} | ||
/** | ||
* Add an alias to the route. | ||
* | ||
* @param alias - Alias to add to the route | ||
*/ | ||
addAlias(alias) { | ||
this.node.value.addEditOverride({ alias }); | ||
} | ||
/** | ||
* Array of the route params and all of its parent's params. | ||
*/ | ||
get params() { | ||
return this.node.params; | ||
} | ||
/** | ||
* Path of the route including parent paths. | ||
*/ | ||
get fullPath() { | ||
return this.node.fullPath; | ||
} | ||
/** | ||
* DFS traversal of the tree. | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseDFS() { | ||
@@ -1128,2 +1294,12 @@ if (!this.node.isRoot()) { | ||
} | ||
/** | ||
* BFS traversal of the tree as a generator. | ||
* | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseBFS() { | ||
@@ -1169,2 +1345,4 @@ for (const [_name, child] of this.node.children) { | ||
cwd: folder.src, | ||
// TODO: do they return the symbolic link path or the original file? | ||
// followSymbolicLinks: false, | ||
ignore: options.exclude | ||
@@ -1397,2 +1575,3 @@ }).then((files) => files.map((file) => (0, import_pathe2.resolve)(folder.src, file))).then( | ||
), | ||
// importing the definePage block | ||
/definePage\&vue$/ | ||
@@ -1425,2 +1604,3 @@ ], | ||
}, | ||
// we only need to transform page components | ||
transformInclude(id) { | ||
@@ -1432,2 +1612,3 @@ return filterPageComponents(id); | ||
}, | ||
// loadInclude is necessary for webpack | ||
loadInclude(id) { | ||
@@ -1454,2 +1635,3 @@ if (id === ROUTE_BLOCK_ID) | ||
}, | ||
// improves DX | ||
vite: { | ||
@@ -1456,0 +1638,0 @@ configureServer(server) { |
import * as webpack from 'webpack'; | ||
import { O as Options } from './options-f6626ea0.js'; | ||
import { O as Options } from './options-dc4b959a.js'; | ||
import 'vue-router'; | ||
@@ -4,0 +4,0 @@ |
@@ -38,2 +38,6 @@ "use strict"; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
@@ -129,3 +133,4 @@ mod | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + (path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
result = result.replace(TRAILING_SLASH_RE, "") + // check path to avoid adding a trailing slash when joining an empty string | ||
(path && "/" + path.replace(LEADING_SLASH_RE, "")); | ||
} | ||
@@ -180,3 +185,7 @@ return result; | ||
function asRoutePath({ src, path = "" }, filePath) { | ||
return path + filePath.slice(src.length + 1); | ||
return ( | ||
// add the path prefix if any | ||
path + // remove the absolute path to the pages folder | ||
filePath.slice(src.length + 1) | ||
); | ||
} | ||
@@ -188,4 +197,14 @@ | ||
constructor(rawSegment, parent, pathSegment = rawSegment, subSegments = [pathSegment]) { | ||
/** | ||
* Overrides defined by each file. The map is necessary to handle named views. | ||
*/ | ||
this._overrides = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should we add the loader guard to the route record. | ||
*/ | ||
this.includeLoaderGuard = false; | ||
/** | ||
* View name (Vue Router feature) mapped to their corresponding file. By default, the view name is `default` unless | ||
* specified with a `@` e.g. `index@aux.vue` will have a view name of `aux`. | ||
*/ | ||
this.components = /* @__PURE__ */ new Map(); | ||
@@ -197,3 +216,4 @@ this._type = 0; | ||
const parentPath = parent == null ? void 0 : parent.path; | ||
this.path = (!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.path = // both the root record and the index record have a path of / | ||
(!parentPath || parentPath === "/") && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
} | ||
@@ -211,3 +231,6 @@ toString() { | ||
return [...this._overrides.entries()].sort( | ||
([nameA], [nameB]) => nameA === nameB ? 0 : nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
([nameA], [nameB]) => nameA === nameB ? 0 : ( | ||
// EDITS_OVERRIDE_NAME should always be last | ||
nameA !== EDITS_OVERRIDE_NAME && (nameA < nameB || nameB === EDITS_OVERRIDE_NAME) ? -1 : 1 | ||
) | ||
).reduce((acc, [_path, routeBlock]) => { | ||
@@ -220,2 +243,7 @@ return mergeRouteRecordOverride(acc, routeBlock); | ||
} | ||
/** | ||
* Remove all overrides for a given key. | ||
* | ||
* @param key - key to remove from the override | ||
*/ | ||
removeOverride(key) { | ||
@@ -354,3 +382,9 @@ this._overrides.forEach((routeBlock) => { | ||
constructor(options, filePath, parent) { | ||
/** | ||
* children of the node | ||
*/ | ||
this.children = /* @__PURE__ */ new Map(); | ||
/** | ||
* Should this page import the page info | ||
*/ | ||
this.hasDefinePage = false; | ||
@@ -361,2 +395,9 @@ this.options = options; | ||
} | ||
/** | ||
* Adds a path to the tree. `path` cannot start with a `/`. | ||
* | ||
* @param path - path segment to insert. **It must contain the file extension** this allows to | ||
* differentiate between folders and files. | ||
* @param filePath - file path, defaults to path for convenience and testing | ||
*/ | ||
insert(path, filePath = path) { | ||
@@ -387,2 +428,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Delete and detach itself from the tree. | ||
*/ | ||
delete() { | ||
@@ -395,2 +439,8 @@ if (!this.parent) { | ||
} | ||
/** | ||
* 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`. | ||
* The `path` should be relative to the page folder. | ||
* | ||
* @param path - path segment of the file | ||
*/ | ||
remove(path) { | ||
@@ -421,2 +471,5 @@ const { tail, segment, viewName, isComponent } = splitFilePath( | ||
} | ||
/** | ||
* Returns the route path of the node without parent paths. If the path was overridden, it returns the override. | ||
*/ | ||
get path() { | ||
@@ -426,2 +479,5 @@ var _a, _b; | ||
} | ||
/** | ||
* Returns the route path of the node including parent paths. | ||
*/ | ||
get fullPath() { | ||
@@ -431,5 +487,11 @@ var _a; | ||
} | ||
/** | ||
* Returns the route name of the node. If the name was overridden, it returns the override. | ||
*/ | ||
get name() { | ||
return this.value.overrides.name || this.options.getRouteName(this); | ||
} | ||
/** | ||
* Returns the meta property as an object. | ||
*/ | ||
get metaAsObject() { | ||
@@ -442,2 +504,6 @@ const meta = __spreadValues({}, this.value.overrides.meta); | ||
} | ||
/** | ||
* Returns the JSON string of the meta object of the node. If the meta was overridden, it returns the override. If | ||
* there is no override, it returns an empty string. | ||
*/ | ||
get meta() { | ||
@@ -458,2 +524,7 @@ const overrideMeta = this.metaAsObject; | ||
} | ||
/** | ||
* Returns wether this tree node is the root node of the tree. | ||
* | ||
* @returns true if the node is the root node | ||
*/ | ||
isRoot() { | ||
@@ -463,3 +534,5 @@ return this.value.path === "/" && !this.value.components.size; | ||
toString() { | ||
return `${this.value}${this.value.components.size > 1 || this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
return `${this.value}${// either we have multiple names | ||
this.value.components.size > 1 || // or we have one name and it's not default | ||
this.value.components.size === 1 && !this.value.components.get("default") ? ` \u2388(${Array.from(this.value.components.keys()).join(", ")})` : ""}${this.hasDefinePage ? " \u2691 definePage()" : ""}`; | ||
} | ||
@@ -480,2 +553,6 @@ }; | ||
} | ||
/** | ||
* | ||
* @param filePath - | ||
*/ | ||
removeChild(filePath) { | ||
@@ -522,3 +599,6 @@ if (this.map.has(filePath)) { | ||
(param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `ParamValueZeroOrOne<${isRaw}>` : `ParamValue<${isRaw}>`) | ||
).join(", ")} }` : "Record<never, never>"; | ||
).join(", ")} }` : ( | ||
// no params allowed | ||
"Record<never, never>" | ||
); | ||
} | ||
@@ -532,4 +612,8 @@ | ||
} | ||
return (node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : ""); | ||
return ( | ||
// if the node has a filePath, it's a component, it has a routeName and it should be referenced in the RouteNamedMap | ||
// otherwise it should be skipped to avoid navigating to a route that doesn't render anything | ||
(node.value.components.size ? ` '${node.name}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? node.getSortedChildren().map(generateRouteNamedMap).join("\n") : "") | ||
); | ||
} | ||
@@ -566,3 +650,4 @@ function generateRouteRecordInfo(node) { | ||
${indentStr}${node.value.components.size ? `name: '${node.name}',` : `/* internal name: '${node.name}' */`} | ||
${indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
${// component | ||
indentStr}${node.value.components.size ? generateRouteRecordComponent( | ||
node, | ||
@@ -575,3 +660,4 @@ indentStr, | ||
` : ""}${overrides.alias != null ? indentStr + `alias: ${JSON.stringify(overrides.alias)}, | ||
` : ""}${indentStr}${node.children.size > 0 ? `children: [ | ||
` : ""}${// children | ||
indentStr}${node.children.size > 0 ? `children: [ | ||
${node.getSortedChildren().map((child) => generateRouteRecord(child, options, importList, indent + 2)).join(",\n")} | ||
@@ -599,11 +685,14 @@ ${indentStr}],` : "/* no children */"}${formatMeta(node, indentStr)} | ||
const isDefaultExport = files.length === 1 && files[0][0] === "default"; | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : `components: { | ||
return isDefaultExport ? `component: ${generatePageImport(files[0][1], importMode, importList)},` : ( | ||
// files has at least one entry | ||
`components: { | ||
${files.map( | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},`; | ||
([key, path]) => `${indentStr + " "}'${key}': ${generatePageImport( | ||
path, | ||
importMode, | ||
importList | ||
)}` | ||
).join(",\n")} | ||
${indentStr}},` | ||
); | ||
} | ||
@@ -708,4 +797,7 @@ function generatePageImport(filepath, importMode, importList) { | ||
ignoreInitial: true, | ||
// disableGlobbing: true, | ||
ignorePermissionErrors: true, | ||
ignored: options.exclude | ||
// useFsEvents: true, | ||
// TODO: allow user options | ||
}); | ||
@@ -941,3 +1033,9 @@ } | ||
if (!definePageNodes.length) { | ||
return isExtractingDefinePage ? "export default {}" : null; | ||
return isExtractingDefinePage ? ( | ||
// e.g. index.vue?definePage that contains a commented `definePage() | ||
"export default {}" | ||
) : ( | ||
// e.g. index.vue that contains a commented `definePage() | ||
null | ||
); | ||
} else if (definePageNodes.length > 1) { | ||
@@ -1040,8 +1138,16 @@ throw new SyntaxError(`duplicate definePage() call`); | ||
var EditableTreeNode = class { | ||
// private _parent?: EditableTreeNode | ||
constructor(node) { | ||
this.node = node; | ||
} | ||
/** | ||
* Remove and detach the current route node from the tree. Subsequently, its children will be removed as well. | ||
*/ | ||
delete() { | ||
return this.node.delete(); | ||
} | ||
/** | ||
* Inserts a new route as a child of this route. This route cannot use `definePage()`. If it was meant to be included, | ||
* add it to the `routesFolder` option. | ||
*/ | ||
insert(path, filePath) { | ||
@@ -1065,20 +1171,46 @@ const extDotIndex = filePath.lastIndexOf("."); | ||
} | ||
/** | ||
* Get an editable version of the parent node if it exists. | ||
*/ | ||
get parent() { | ||
return this.node.parent && new EditableTreeNode(this.node.parent); | ||
} | ||
/** | ||
* Return a Map of the files associated to the current route. The key of the map represents the name of the view (Vue | ||
* Router feature) while the value is the file path. By default, the name of the view is `default`. | ||
*/ | ||
get components() { | ||
return this.node.value.components; | ||
} | ||
/** | ||
* Name of the route. Note that **all routes are named** but when the final `routes` array is generated, routes | ||
* without a `component` will not include their `name` property to avoid accidentally navigating to them and display | ||
* nothing. {@see isPassThrough} | ||
*/ | ||
get name() { | ||
return this.node.name; | ||
} | ||
/** | ||
* Override the name of the route. | ||
*/ | ||
set name(name) { | ||
this.node.value.addEditOverride({ name }); | ||
} | ||
/** | ||
* Whether the route is a pass-through route. A pass-through route is a route that does not have a component and is | ||
* used to group other routes under the same prefix `path` and/or `meta` properties. | ||
*/ | ||
get isPassThrough() { | ||
return this.node.value.components.size === 0; | ||
} | ||
/** | ||
* 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**. | ||
*/ | ||
get meta() { | ||
return this.node.metaAsObject; | ||
} | ||
/** | ||
* Override the meta property of the route. This will discard any other meta property defined with `<route>` blocks or | ||
* through other means. | ||
*/ | ||
set meta(meta) { | ||
@@ -1088,8 +1220,19 @@ this.node.value.removeOverride("meta"); | ||
} | ||
/** | ||
* Add meta properties to the route keeping the existing ones. The passed object will be deeply merged with the | ||
* existing meta object if any. Note that the meta property is later on serialized as JSON so you can't pass functions | ||
* or any other non-serializable value. | ||
*/ | ||
addToMeta(meta) { | ||
this.node.value.addEditOverride({ meta }); | ||
} | ||
/** | ||
* Path of the route without parent paths. | ||
*/ | ||
get path() { | ||
return this.node.path; | ||
} | ||
/** | ||
* Override the path of the route. You must ensure `params` match with the existing path. | ||
*/ | ||
set path(path) { | ||
@@ -1104,14 +1247,37 @@ if (!path.startsWith("/")) { | ||
} | ||
/** | ||
* Alias of the route. | ||
*/ | ||
get alias() { | ||
return this.node.value.overrides.alias; | ||
} | ||
/** | ||
* Add an alias to the route. | ||
* | ||
* @param alias - Alias to add to the route | ||
*/ | ||
addAlias(alias) { | ||
this.node.value.addEditOverride({ alias }); | ||
} | ||
/** | ||
* Array of the route params and all of its parent's params. | ||
*/ | ||
get params() { | ||
return this.node.params; | ||
} | ||
/** | ||
* Path of the route including parent paths. | ||
*/ | ||
get fullPath() { | ||
return this.node.fullPath; | ||
} | ||
/** | ||
* DFS traversal of the tree. | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseDFS() { | ||
@@ -1128,2 +1294,12 @@ if (!this.node.isRoot()) { | ||
} | ||
/** | ||
* BFS traversal of the tree as a generator. | ||
* | ||
* @example | ||
* ```ts | ||
* for (const node of tree) { | ||
* // ... | ||
* } | ||
* ``` | ||
*/ | ||
*traverseBFS() { | ||
@@ -1169,2 +1345,4 @@ for (const [_name, child] of this.node.children) { | ||
cwd: folder.src, | ||
// TODO: do they return the symbolic link path or the original file? | ||
// followSymbolicLinks: false, | ||
ignore: options.exclude | ||
@@ -1397,2 +1575,3 @@ }).then((files) => files.map((file) => (0, import_pathe2.resolve)(folder.src, file))).then( | ||
), | ||
// importing the definePage block | ||
/definePage\&vue$/ | ||
@@ -1425,2 +1604,3 @@ ], | ||
}, | ||
// we only need to transform page components | ||
transformInclude(id) { | ||
@@ -1432,2 +1612,3 @@ return filterPageComponents(id); | ||
}, | ||
// loadInclude is necessary for webpack | ||
loadInclude(id) { | ||
@@ -1454,2 +1635,3 @@ if (id === ROUTE_BLOCK_ID) | ||
}, | ||
// improves DX | ||
vite: { | ||
@@ -1456,0 +1638,0 @@ configureServer(server) { |
{ | ||
"name": "unplugin-vue-router", | ||
"version": "0.5.0", | ||
"packageManager": "pnpm@7.26.2", | ||
"version": "0.5.1", | ||
"packageManager": "pnpm@7.28.0", | ||
"description": "File based typed routing for Vue Router", | ||
@@ -79,5 +79,5 @@ "keywords": [ | ||
"dependencies": { | ||
"@babel/types": "^7.20.7", | ||
"@babel/types": "^7.21.2", | ||
"@rollup/pluginutils": "^5.0.2", | ||
"@vue-macros/common": "^1.0.0", | ||
"@vue-macros/common": "^1.1.0", | ||
"ast-walker-scope": "^0.4.0", | ||
@@ -88,6 +88,6 @@ "chokidar": "^3.5.3", | ||
"local-pkg": "^0.4.3", | ||
"mlly": "^1.1.0", | ||
"mlly": "^1.1.1", | ||
"pathe": "^1.1.0", | ||
"scule": "^1.0.0", | ||
"unplugin": "^1.0.1", | ||
"unplugin": "^1.1.0", | ||
"yaml": "^2.2.1" | ||
@@ -104,4 +104,4 @@ }, | ||
"devDependencies": { | ||
"@volar/vue-language-core": "^1.0.24", | ||
"c8": "^7.12.0", | ||
"@volar/vue-language-core": "^1.1.7", | ||
"c8": "^7.13.0", | ||
"chalk": "^5.2.0", | ||
@@ -111,18 +111,18 @@ "conventional-changelog-cli": "^2.2.2", | ||
"esno": "^0.16.3", | ||
"execa": "^6.1.0", | ||
"lint-staged": "^13.1.0", | ||
"minimist": "^1.2.7", | ||
"execa": "^7.0.0", | ||
"lint-staged": "^13.1.2", | ||
"minimist": "^1.2.8", | ||
"nodemon": "^2.0.20", | ||
"p-series": "^3.0.0", | ||
"prettier": "^2.8.3", | ||
"prettier": "^2.8.4", | ||
"rimraf": "^4.1.2", | ||
"rollup": "^3.12.0", | ||
"rollup": "^3.17.3", | ||
"semver": "^7.3.8", | ||
"ts-expect": "^1.3.0", | ||
"tsup": "^6.5.0", | ||
"tsup": "^6.6.3", | ||
"typescript": "^4.9.5", | ||
"unplugin-auto-import": "^0.13.0", | ||
"vite": "^4.0.4", | ||
"vitest": "^0.28.3", | ||
"vue": "^3.2.45", | ||
"unplugin-auto-import": "^0.15.0", | ||
"vite": "^4.1.4", | ||
"vitest": "^0.29.1", | ||
"vue": "^3.2.47", | ||
"vue-router": "^4.1.6", | ||
@@ -129,0 +129,0 @@ "webpack": "^5.75.0", |
@@ -117,4 +117,6 @@ # unplugin-vue-router | ||
Then, you can run your dev server (usually `npm run dev` to generate the first version of the types) you can replace your imports from `vue-router` to `vue-router/auto`: | ||
## Setup | ||
After installing, **you should run your dev server** (usually `npm run dev`) **to generate the first version of the types**. Then, you should replace your imports from `vue-router` to `vue-router/auto`: | ||
```diff | ||
@@ -131,2 +133,15 @@ -import { createRouter, createWebHistory } from 'vue-router' | ||
> **Note** | ||
> You can exclude `vue-router` from VSCode import suggestions by adding this setting to your `.vscode/settings.json`: | ||
```json | ||
{ | ||
"typescript.preferences.autoImportFileExcludePatterns": [ | ||
"vue-router" | ||
] | ||
} | ||
``` | ||
This will ensure VSCode only suggests `vue-router/auto` for imports. Alternatively, you can also configure [auto imports](#auto-imports). | ||
Alternatively, **you can also import the `routes` array** and create the router manually or pass it to some plugin. Here is an example with [Vitesse starter](https://github.com/antfu/vitesse/blob/main/src/main.ts): | ||
@@ -166,2 +181,4 @@ | ||
### Auto Imports | ||
If you are using [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import), make sure to remove the `vue-router` preset and use the one exported by `unplugin-vue-router`: | ||
@@ -168,0 +185,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1516712
42265
600
Updated@babel/types@^7.21.2
Updated@vue-macros/common@^1.1.0
Updatedmlly@^1.1.1
Updatedunplugin@^1.1.0