unplugin-vue-router
Advanced tools
Comparing version 0.0.0-beta.0 to 0.0.1
import * as unplugin from 'unplugin'; | ||
import { Options } from './types.js'; | ||
import { RouteParamsRaw, RouteParams, RouteMeta, RouteLocationNormalized, RouteRecordName, RouteLocationNormalizedLoaded, RouteQueryAndHash, RouteLocationOptions, NavigationGuardNext, Router, NavigationFailure, RouterLinkProps as RouterLinkProps$1, RouteLocation } from 'vue-router'; | ||
import { Ref, AllowedComponentProps, ComponentCustomProps, VNodeProps, VNode } from 'vue'; | ||
declare type LiteralStringUnion<LiteralType, BaseType extends string = string> = LiteralType | (BaseType & Record<never, never>); | ||
interface RouteRecordInfo<Name extends string = string, Path extends string = string, ParamsRaw extends RouteParamsRaw = RouteParamsRaw, Params extends RouteParams = RouteParams, Meta extends RouteMeta = RouteMeta> { | ||
name: Name; | ||
path: Path; | ||
paramsRaw: ParamsRaw; | ||
params: Params; | ||
meta: Meta; | ||
} | ||
declare type _RouteMapGeneric = Record<string, RouteRecordInfo>; | ||
interface RouteLocationNormalizedTyped<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>, Name extends keyof RouteMap = keyof RouteMap> extends RouteLocationNormalized { | ||
name: Extract<Name, RouteRecordName>; | ||
params: RouteMap[Name]['params']; | ||
} | ||
declare type RouteLocationNormalizedTypedList<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>> = { | ||
[N in keyof RouteMap]: RouteLocationNormalizedTyped<RouteMap, N>; | ||
}; | ||
interface RouteLocationNormalizedLoadedTyped<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>, Name extends keyof RouteMap = keyof RouteMap> extends RouteLocationNormalizedLoaded { | ||
name: Extract<Name, RouteRecordName>; | ||
params: RouteMap[Name]['params']; | ||
} | ||
declare type RouteLocationNormalizedLoadedTypedList<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>> = { | ||
[N in keyof RouteMap]: RouteLocationNormalizedLoadedTyped<RouteMap, N>; | ||
}; | ||
interface RouteLocationAsRelativeTyped<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>, Name extends keyof RouteMap = keyof RouteMap> extends RouteQueryAndHash, RouteLocationOptions { | ||
name?: Name; | ||
params?: RouteMap[Name]['paramsRaw']; | ||
} | ||
declare type RouteLocationAsRelativeTypedList<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>> = { | ||
[N in keyof RouteMap]: RouteLocationAsRelativeTyped<RouteMap, N>; | ||
}; | ||
interface RouteLocationAsPathTyped<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>, Name extends keyof RouteMap = keyof RouteMap> extends RouteQueryAndHash, RouteLocationOptions { | ||
path: LiteralStringUnion<RouteMap[Name]['path']>; | ||
} | ||
declare type RouteLocationAsPathTypedList<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>> = { | ||
[N in keyof RouteMap]: RouteLocationAsPathTyped<RouteMap, N>; | ||
}; | ||
declare type RouteLocationAsString<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>> = LiteralStringUnion<RouteMap[keyof RouteMap]['path'], string>; | ||
interface RouteLocationTyped<RouteMap extends _RouteMapGeneric, Name extends keyof RouteMap> extends RouteLocation { | ||
name: Extract<Name, RouteRecordName>; | ||
params: RouteMap[Name]['params']; | ||
} | ||
interface RouteLocationResolvedTyped<RouteMap extends _RouteMapGeneric, Name extends keyof RouteMap> extends RouteLocationTyped<RouteMap, Name> { | ||
href: string; | ||
} | ||
declare type RouteLocationResolvedTypedList<RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>> = { | ||
[N in keyof RouteMap]: RouteLocationResolvedTyped<RouteMap, N>; | ||
}; | ||
declare type NavigationGuardReturn<RouteMap extends _RouteMapGeneric> = void | boolean | RouteLocationAsString<RouteMap> | RouteLocationAsRelativeTypedList<RouteMap>[keyof RouteMap] | RouteLocationAsPathTypedList<RouteMap>[keyof RouteMap]; | ||
interface NavigationGuardWithThis<T, RouteMap extends _RouteMapGeneric> { | ||
(this: T, to: RouteLocationNormalizedTypedList<RouteMap>[keyof RouteMap], from: RouteLocationNormalizedLoadedTypedList<RouteMap>[keyof RouteMap], next: NavigationGuardNext): NavigationGuardReturn<RouteMap> | Promise<NavigationGuardReturn<RouteMap>>; | ||
} | ||
interface NavigationGuard<RouteMap extends _RouteMapGeneric> { | ||
(to: RouteLocationNormalizedTypedList<RouteMap>[keyof RouteMap], from: RouteLocationNormalizedLoadedTypedList<RouteMap>[keyof RouteMap], next: NavigationGuardNext): NavigationGuardReturn<RouteMap> | Promise<NavigationGuardReturn<RouteMap>>; | ||
} | ||
interface NavigationHookAfter<RouteMap extends _RouteMapGeneric = _RouteMapGeneric> { | ||
(to: RouteLocationNormalizedTypedList<RouteMap>[keyof RouteMap], from: RouteLocationNormalizedLoadedTypedList<RouteMap>[keyof RouteMap], failure?: NavigationFailure | void): any; | ||
} | ||
interface _RouterTyped<RouteMap extends _RouteMapGeneric = _RouteMapGeneric> extends Omit<Router, 'resolve' | 'push' | 'replace' | 'beforeEach' | 'beforeResolve' | 'afterEach'> { | ||
currentRoute: Ref<RouteLocationNormalizedLoadedTypedList<RouteMap>[keyof RouteMap]>; | ||
push<Name extends keyof RouteMap = keyof RouteMap>(to: RouteLocationAsString<RouteMap> | RouteLocationAsRelativeTyped<RouteMap, Name> | RouteLocationAsPathTyped<RouteMap, Name>): ReturnType<Router['push']>; | ||
replace<Name extends keyof RouteMap = keyof RouteMap>(to: RouteLocationAsString<RouteMap> | RouteLocationAsRelativeTyped<RouteMap, Name> | RouteLocationAsPathTyped<RouteMap, Name>): ReturnType<Router['replace']>; | ||
resolve<Name extends keyof RouteMap = keyof RouteMap>(to: RouteLocationAsString<RouteMap> | RouteLocationAsRelativeTyped<RouteMap, Name> | RouteLocationAsPathTyped<RouteMap, Name>, currentLocation?: RouteLocationNormalizedLoaded): RouteLocationResolvedTypedList<RouteMap>[Name]; | ||
beforeEach(guard: NavigationGuardWithThis<undefined, RouteMap>): ReturnType<Router['beforeEach']>; | ||
beforeResolve(guard: NavigationGuardWithThis<undefined, RouteMap>): ReturnType<Router['beforeEach']>; | ||
afterEach(guard: NavigationHookAfter<RouteMap>): ReturnType<Router['beforeEach']>; | ||
} | ||
interface RouterLinkProps<RouteMap extends _RouteMapGeneric> extends Omit<RouterLinkProps$1, 'to'> { | ||
to: RouteLocationAsString<RouteMap> | RouteLocationAsRelativeTypedList<RouteMap>[keyof RouteMap] | RouteLocationAsPathTypedList<RouteMap>[keyof RouteMap]; | ||
} | ||
interface RouterLinkTyped<RouteMap extends _RouteMapGeneric> { | ||
new (): { | ||
$props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps<RouteMap>; | ||
$slots: { | ||
default: (arg: ReturnType<_RouterTyped<RouteMap>['resolve']>) => VNode[]; | ||
}; | ||
}; | ||
} | ||
/** | ||
* Utility type for raw and non raw params like :id+ | ||
* | ||
* @internal | ||
*/ | ||
declare type _ParamValueOneOrMore<isRaw extends boolean> = true extends isRaw ? [string | number, ...(string | number)[]] : [string, ...string[]]; | ||
/** | ||
* Utility type for raw and non raw params like :id* | ||
* | ||
* @internal | ||
*/ | ||
declare type _ParamValueZeroOrMore<isRaw extends boolean> = true extends isRaw ? (string | number)[] | undefined | null : string[] | undefined | null; | ||
/** | ||
* Utility type for raw and non raw params like :id? | ||
* | ||
* @internal | ||
*/ | ||
declare type _ParamValueZeroOrOne<isRaw extends boolean> = true extends isRaw ? string | number | null | undefined : string; | ||
/** | ||
* Utility type for raw and non raw params like :id | ||
* | ||
* @internal | ||
*/ | ||
declare type _ParamValue<isRaw extends boolean> = true extends isRaw ? string | number : string; | ||
declare const _default: unplugin.UnpluginInstance<Options>; | ||
export { _default as default }; | ||
export { NavigationGuard, RouteLocationAsPathTyped, RouteLocationAsPathTypedList, RouteLocationAsRelativeTyped, RouteLocationAsRelativeTypedList, RouteLocationAsString, RouteLocationNormalizedLoadedTyped, RouteLocationNormalizedLoadedTypedList, RouteLocationNormalizedTyped, RouteRecordInfo, RouterLinkTyped, _ParamValue, _ParamValueOneOrMore, _ParamValueZeroOrMore, _ParamValueZeroOrOne, _RouteMapGeneric, _RouterTyped, _default as default }; |
@@ -0,5 +1,21 @@ | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __export = (target, all) => { | ||
@@ -17,2 +33,3 @@ for (var name in all) | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
@@ -27,12 +44,476 @@ | ||
var import_unplugin = require("unplugin"); | ||
var src_default = (0, import_unplugin.createUnplugin)((options) => ({ | ||
name: "unplugin-vue-router", | ||
transformInclude(id) { | ||
return id.endsWith("main.ts"); | ||
}, | ||
transform(code) { | ||
return code.replace("__UNPLUGIN__", `Hello Unplugin! ${options}`); | ||
// src/core/context.ts | ||
var import_chokidar = __toESM(require("chokidar")); | ||
var import_path = require("path"); | ||
// src/core/utils.ts | ||
var MAX_LEVEL = 1e3; | ||
var LogTree = class { | ||
setPre(hasNext, parentPre = "") { | ||
return `${parentPre}${hasNext ? "\u251C" : "\u2514"}\u2500\u2500 `; | ||
} | ||
})); | ||
setTransferPre(parentPre, hasNext) { | ||
return `${parentPre}${hasNext ? "\u2502" : " "} `; | ||
} | ||
parse(tree, level = 0, parentPre = "", treeStr = "") { | ||
if (!this.check(tree, level)) | ||
return ""; | ||
if (tree instanceof Map) { | ||
const total = tree.size; | ||
let index = 0; | ||
for (const [key, child] of tree) { | ||
const hasNext = index++ < total - 1; | ||
const { children } = child; | ||
treeStr += `${this.setPre(hasNext, parentPre)}${child} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1, this.setTransferPre(parentPre, hasNext)); | ||
} | ||
} | ||
} else { | ||
const children = tree.children; | ||
treeStr = `${tree} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1); | ||
} | ||
} | ||
return treeStr; | ||
} | ||
check(tree, level = 0) { | ||
if (typeof tree !== "object") | ||
return false; | ||
if (level >= MAX_LEVEL) | ||
return false; | ||
return true; | ||
} | ||
}; | ||
function logTree(tree) { | ||
const log = new LogTree(); | ||
console.log(log.parse(tree)); | ||
} | ||
var isArray = Array.isArray; | ||
function trimExtension(path) { | ||
const lastDot = path.lastIndexOf("."); | ||
return lastDot < 0 ? path : path.slice(0, lastDot); | ||
} | ||
function throttle(fn, wait) { | ||
let timeout = null; | ||
let pendingExecution = false; | ||
return () => { | ||
if (!timeout) { | ||
timeout = setTimeout(() => { | ||
timeout = null; | ||
if (pendingExecution) { | ||
pendingExecution = false; | ||
fn(); | ||
} | ||
}, wait); | ||
fn(); | ||
} else { | ||
pendingExecution = true; | ||
} | ||
}; | ||
} | ||
var LEADING_SLASH_RE = /^\//; | ||
var TRAILING_SLASH_RE = /\/$/; | ||
function joinPath(...paths) { | ||
let result = ""; | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + "/" + path.replace(LEADING_SLASH_RE, ""); | ||
} | ||
return result; | ||
} | ||
// src/core/treeLeafValue.ts | ||
var _TreeLeafValueBase = class { | ||
constructor(rawSegment, parent, pathSegment = rawSegment) { | ||
this._type = 0; | ||
this.rawSegment = rawSegment; | ||
this.pathSegment = pathSegment; | ||
this.path = !(parent == null ? void 0 : parent.path) && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.routeName = parent ? parent.routeName + "/" + rawSegment : ""; | ||
} | ||
toString() { | ||
return this.pathSegment || "<index>"; | ||
} | ||
isParam() { | ||
return !!(this._type & 1 /* param */); | ||
} | ||
isStatic() { | ||
return this._type === 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueStatic = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent) { | ||
super(rawSegment, parent); | ||
this._type = 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueParam = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent, params, pathSegment) { | ||
super(rawSegment, parent, pathSegment); | ||
this._type = 1 /* param */; | ||
this.params = params; | ||
} | ||
}; | ||
function createTreeLeafValue(segment, parent) { | ||
const trimmedSegment = trimExtension(segment); | ||
if (!trimmedSegment || trimmedSegment === "index") { | ||
return new TreeLeafValueStatic("", parent); | ||
} | ||
const [pathSegment, params] = parseSegment(trimmedSegment); | ||
if (params.length) { | ||
return new TreeLeafValueParam(trimmedSegment, parent, params, pathSegment); | ||
} | ||
return new TreeLeafValueStatic(trimmedSegment, parent); | ||
} | ||
function parseSegment(segment) { | ||
let buffer = ""; | ||
let state = 0 /* static */; | ||
const params = []; | ||
let pathSegment = ""; | ||
let currentTreeRouteParam = createEmptyRouteParam(); | ||
function consumeBuffer() { | ||
if (state === 0 /* static */) { | ||
pathSegment += buffer; | ||
} else if (state === 3 /* modifier */) { | ||
currentTreeRouteParam.paramName = buffer; | ||
currentTreeRouteParam.modifier = currentTreeRouteParam.optional ? currentTreeRouteParam.repeatable ? "*" : "?" : currentTreeRouteParam.repeatable ? "+" : ""; | ||
buffer = ""; | ||
pathSegment += `:${currentTreeRouteParam.paramName}${currentTreeRouteParam.isSplat ? "(.*)" : ""}${currentTreeRouteParam.modifier}`; | ||
params.push(currentTreeRouteParam); | ||
currentTreeRouteParam = createEmptyRouteParam(); | ||
} | ||
buffer = ""; | ||
} | ||
for (let pos = 0; pos < segment.length; pos++) { | ||
const c = segment[pos]; | ||
if (state === 0 /* static */) { | ||
if (c === "[") { | ||
consumeBuffer(); | ||
state = 1 /* paramOptional */; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 1 /* paramOptional */) { | ||
if (c === "[") { | ||
currentTreeRouteParam.optional = true; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
state = 2 /* param */; | ||
} else if (state === 2 /* param */) { | ||
if (c === "]") { | ||
if (currentTreeRouteParam.optional) { | ||
pos++; | ||
} | ||
state = 3 /* modifier */; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 3 /* modifier */) { | ||
if (c === "+") { | ||
currentTreeRouteParam.repeatable = true; | ||
} else { | ||
pos--; | ||
} | ||
consumeBuffer(); | ||
state = 0 /* static */; | ||
} | ||
} | ||
if (state === 2 /* param */ || state === 1 /* paramOptional */) { | ||
throw new Error(`Invalid segment: "${segment}"`); | ||
} | ||
if (buffer) { | ||
consumeBuffer(); | ||
} | ||
return [pathSegment, params]; | ||
} | ||
function createEmptyRouteParam() { | ||
return { | ||
paramName: "", | ||
modifier: "", | ||
optional: false, | ||
repeatable: false, | ||
isSplat: false | ||
}; | ||
} | ||
// src/core/tree.ts | ||
var TreeLeaf = class { | ||
constructor(value, parent) { | ||
this.children = /* @__PURE__ */ new Map(); | ||
this.value = createTreeLeafValue(value, parent == null ? void 0 : parent.value); | ||
} | ||
insert(path, filePath = path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
if (!this.children.has(segment)) { | ||
this.children.set(segment, new TreeLeaf(head, this)); | ||
} | ||
const child = this.children.get(segment); | ||
if (isComponent) { | ||
child.value.filePath = filePath; | ||
} | ||
if (tail) { | ||
child.insert(tail, filePath); | ||
} | ||
} | ||
remove(path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
const child = this.children.get(segment); | ||
if (!child) { | ||
throw new Error(`Cannot Delete "${path}". "${head}" not found at "${this.value.path}".`); | ||
} | ||
if (tail) { | ||
child.remove(tail); | ||
if (child.children.size === 0 && !child.value.filePath) { | ||
this.children.delete(segment); | ||
} | ||
} else { | ||
if (isComponent) { | ||
child.value.filePath = void 0; | ||
} | ||
if (child.children.size === 0) { | ||
this.children.delete(segment); | ||
} | ||
} | ||
} | ||
isRoot() { | ||
return this.value.path === "/" && !this.value.filePath; | ||
} | ||
toString() { | ||
return `${this.value}${this.value.filePath ? " \u{1F4C4}" : ""}`; | ||
} | ||
}; | ||
function createPrefixTree() { | ||
const tree = new TreeLeaf(""); | ||
return tree; | ||
} | ||
// src/core/context.ts | ||
var import_fs = require("fs"); | ||
// src/codegen/generateRouteParams.ts | ||
function generateRouteParams(node, isRaw) { | ||
return node.value.isParam() ? `{ ${node.value.params.map((param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `_ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `_ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `_ParamValueZeroOrOne<${isRaw}>` : `_ParamValue<${isRaw}>`)).join(", ")} }` : "Record<never, never>"; | ||
} | ||
// src/codegen/generateRouteMap.ts | ||
function generateRouteNamedMap(node) { | ||
if (node.isRoot()) { | ||
return `export interface RouteNamedMap { | ||
${Array.from(node.children.values()).map(generateRouteNamedMap).join("")}}`; | ||
} | ||
return (node.value.filePath ? ` '${node.value.routeName}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? Array.from(node.children.values()).map(generateRouteNamedMap).join("\n") : ""); | ||
} | ||
function generateRouteRecordInfo(node) { | ||
return `RouteRecordInfo<'${node.value.routeName}', '${node.value.path}', ${generateRouteParams(node, true)}, ${generateRouteParams(node, false)}>`; | ||
} | ||
// src/core/moduleConstants.ts | ||
var MODULE_VUE_ROUTER = "@vue-router"; | ||
var MODULE_ROUTES_PATH = `${MODULE_VUE_ROUTER}/routes`; | ||
var VIRTUAL_PREFIX = "virtual:"; | ||
// src/codegen/generateRouteRecords.ts | ||
function generateRouteRecord(node, indent = 0, parent = null) { | ||
if (node.value.path === "/" && indent === 0) { | ||
return `[ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 1)).join(",\n")} | ||
]`; | ||
} | ||
const startIndent = " ".repeat(indent * 2); | ||
const indentStr = " ".repeat((indent + 1) * 2); | ||
const name = node.value.routeName; | ||
return `${startIndent}{ | ||
${indentStr}path: "${(parent ? "" : "/") + node.value.pathSegment}", | ||
${indentStr}${node.value.filePath ? `name: "${name}",` : "/* no name */"} | ||
${indentStr}${node.value.filePath ? `component: () => import('${node.value.filePath}'),` : "/* no component */"} | ||
${indentStr}${node.children.size > 0 ? `children: [ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 2, node)).join(",\n")} | ||
${indentStr}],` : "/* no children */"} | ||
${startIndent}}`; | ||
} | ||
// src/core/context.ts | ||
function createRoutesContext(options) { | ||
const { dts: preferDTS, root } = options; | ||
const dts = preferDTS === false ? false : preferDTS === true ? (0, import_path.resolve)(root, "typed-router.d.ts") : (0, import_path.resolve)(root, preferDTS); | ||
const routeTree = createPrefixTree(); | ||
const resolvedRoutesFolder = (0, import_path.resolve)(root, options.routesFolder); | ||
const serverWatcher = import_chokidar.default.watch(resolvedRoutesFolder, { | ||
disableGlobbing: true, | ||
ignorePermissionErrors: true | ||
}); | ||
function stripRouteFolder(path) { | ||
return path.slice(resolvedRoutesFolder.length + 1); | ||
} | ||
function setupWatcher() { | ||
serverWatcher.on("change", (path) => { | ||
console.log("change", path); | ||
writeConfigFiles(); | ||
}).on("add", (path) => { | ||
console.log("added", path); | ||
routeTree.insert(stripRouteFolder(path), (0, import_path.resolve)(root, path)); | ||
writeConfigFiles(); | ||
}).on("unlink", (path) => { | ||
console.log("remove", path); | ||
routeTree.remove(stripRouteFolder(path)); | ||
writeConfigFiles(); | ||
}); | ||
} | ||
function stop() { | ||
serverWatcher.close(); | ||
} | ||
function generateRoutes() { | ||
return `export const routes = ${generateRouteRecord(routeTree)}`; | ||
} | ||
function generateDTS() { | ||
return ` | ||
// Generated by unplugin-vue-router. \u203C\uFE0F DO NOT MODIFY THIS FILE \u203C\uFE0F | ||
// It's recommended to commit this file. | ||
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry. | ||
/// <reference types="unplugin-vue-router/client" /> | ||
import type { | ||
_RouterTyped, | ||
RouteRecordInfo, | ||
RouterLinkTyped, | ||
RouteLocationNormalizedLoadedTypedList, | ||
RouteLocationAsString, | ||
NavigationGuard, | ||
_ParamValue, | ||
_ParamValueOneOrMore, | ||
_ParamValueZeroOrMore, | ||
_ParamValueZeroOrOne, | ||
} from 'unplugin-vue-router' | ||
declare module '${MODULE_ROUTES_PATH}' { | ||
${generateRouteNamedMap(routeTree).split("\n").filter((line) => line).map((line) => " " + line).join("\n")} | ||
} | ||
declare module '${MODULE_VUE_ROUTER}' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export function useRoute<Name extends keyof RouteNamedMap = keyof RouteNamedMap>(name?: Name): RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
export type RouterTyped = _RouterTyped<RouteNamedMap> | ||
/** | ||
* Generate a type safe route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type Route<Name extends keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
/** | ||
* Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParams<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['params'] | ||
/** | ||
* Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParamsRaw<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['paramsRaw'] | ||
export function useRouter(): RouterTyped | ||
export function onBeforeRouteLeave(guard: NavigationGuard<RouteMap>): void | ||
export function onBeforeRouteUpdate(guard: NavigationGuard<RouteMap>): void | ||
} | ||
declare module 'vue' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export interface GlobalComponents { | ||
RouterLink: RouterLinkTyped<RouteNamedMap> | ||
} | ||
} | ||
`; | ||
} | ||
let lastDTS; | ||
async function _writeConfigFiles() { | ||
console.log("writing"); | ||
logTree(routeTree); | ||
if (dts) { | ||
const content = generateDTS(); | ||
if (lastDTS !== content) { | ||
await import_fs.promises.writeFile(dts, content, "utf-8"); | ||
lastDTS = content; | ||
} | ||
} | ||
} | ||
const writeConfigFiles = throttle(_writeConfigFiles, 500); | ||
setupWatcher(); | ||
return { | ||
stop, | ||
writeConfigFiles, | ||
generateRoutes | ||
}; | ||
} | ||
// src/types.ts | ||
var import_local_pkg = require("local-pkg"); | ||
var DEFAULT_OPTIONS = { | ||
extensions: [".vue", ".js", ".jsx", ".ts", ".tsx"], | ||
exclude: [], | ||
routesFolder: "src/routes", | ||
importMode: "async", | ||
root: process.cwd(), | ||
dts: (0, import_local_pkg.isPackageExists)("typescript"), | ||
_inspect: false | ||
}; | ||
// src/index.ts | ||
var src_default = (0, import_unplugin.createUnplugin)((opt) => { | ||
const options = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), opt); | ||
const ctx = createRoutesContext(options); | ||
const root = process.cwd(); | ||
function getVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : null; | ||
} | ||
function asVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return VIRTUAL_PREFIX + id; | ||
} | ||
return { | ||
name: "unplugin-vue-router", | ||
enforce: "pre", | ||
resolveId(id) { | ||
if (id === MODULE_ROUTES_PATH) { | ||
return asVirtualId(id); | ||
} | ||
if (id === MODULE_VUE_ROUTER) { | ||
return "unplugin-vue-router/@vue-router/index.js"; | ||
} | ||
return null; | ||
}, | ||
buildStart() { | ||
}, | ||
load(id) { | ||
const resolvedId = getVirtualId(id); | ||
if (resolvedId === MODULE_ROUTES_PATH) { | ||
return ctx.generateRoutes(); | ||
} | ||
return null; | ||
} | ||
}; | ||
}); | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = {}); |
497
dist/nuxt.js
@@ -0,5 +1,21 @@ | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __export = (target, all) => { | ||
@@ -17,2 +33,3 @@ for (var name in all) | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
@@ -29,12 +46,476 @@ | ||
var import_unplugin = require("unplugin"); | ||
var src_default = (0, import_unplugin.createUnplugin)((options) => ({ | ||
name: "unplugin-vue-router", | ||
transformInclude(id) { | ||
return id.endsWith("main.ts"); | ||
}, | ||
transform(code) { | ||
return code.replace("__UNPLUGIN__", `Hello Unplugin! ${options}`); | ||
// src/core/context.ts | ||
var import_chokidar = __toESM(require("chokidar")); | ||
var import_path = require("path"); | ||
// src/core/utils.ts | ||
var MAX_LEVEL = 1e3; | ||
var LogTree = class { | ||
setPre(hasNext, parentPre = "") { | ||
return `${parentPre}${hasNext ? "\u251C" : "\u2514"}\u2500\u2500 `; | ||
} | ||
})); | ||
setTransferPre(parentPre, hasNext) { | ||
return `${parentPre}${hasNext ? "\u2502" : " "} `; | ||
} | ||
parse(tree, level = 0, parentPre = "", treeStr = "") { | ||
if (!this.check(tree, level)) | ||
return ""; | ||
if (tree instanceof Map) { | ||
const total = tree.size; | ||
let index = 0; | ||
for (const [key, child] of tree) { | ||
const hasNext = index++ < total - 1; | ||
const { children } = child; | ||
treeStr += `${this.setPre(hasNext, parentPre)}${child} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1, this.setTransferPre(parentPre, hasNext)); | ||
} | ||
} | ||
} else { | ||
const children = tree.children; | ||
treeStr = `${tree} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1); | ||
} | ||
} | ||
return treeStr; | ||
} | ||
check(tree, level = 0) { | ||
if (typeof tree !== "object") | ||
return false; | ||
if (level >= MAX_LEVEL) | ||
return false; | ||
return true; | ||
} | ||
}; | ||
function logTree(tree) { | ||
const log = new LogTree(); | ||
console.log(log.parse(tree)); | ||
} | ||
var isArray = Array.isArray; | ||
function trimExtension(path) { | ||
const lastDot = path.lastIndexOf("."); | ||
return lastDot < 0 ? path : path.slice(0, lastDot); | ||
} | ||
function throttle(fn, wait) { | ||
let timeout = null; | ||
let pendingExecution = false; | ||
return () => { | ||
if (!timeout) { | ||
timeout = setTimeout(() => { | ||
timeout = null; | ||
if (pendingExecution) { | ||
pendingExecution = false; | ||
fn(); | ||
} | ||
}, wait); | ||
fn(); | ||
} else { | ||
pendingExecution = true; | ||
} | ||
}; | ||
} | ||
var LEADING_SLASH_RE = /^\//; | ||
var TRAILING_SLASH_RE = /\/$/; | ||
function joinPath(...paths) { | ||
let result = ""; | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + "/" + path.replace(LEADING_SLASH_RE, ""); | ||
} | ||
return result; | ||
} | ||
// src/core/treeLeafValue.ts | ||
var _TreeLeafValueBase = class { | ||
constructor(rawSegment, parent, pathSegment = rawSegment) { | ||
this._type = 0; | ||
this.rawSegment = rawSegment; | ||
this.pathSegment = pathSegment; | ||
this.path = !(parent == null ? void 0 : parent.path) && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.routeName = parent ? parent.routeName + "/" + rawSegment : ""; | ||
} | ||
toString() { | ||
return this.pathSegment || "<index>"; | ||
} | ||
isParam() { | ||
return !!(this._type & 1 /* param */); | ||
} | ||
isStatic() { | ||
return this._type === 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueStatic = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent) { | ||
super(rawSegment, parent); | ||
this._type = 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueParam = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent, params, pathSegment) { | ||
super(rawSegment, parent, pathSegment); | ||
this._type = 1 /* param */; | ||
this.params = params; | ||
} | ||
}; | ||
function createTreeLeafValue(segment, parent) { | ||
const trimmedSegment = trimExtension(segment); | ||
if (!trimmedSegment || trimmedSegment === "index") { | ||
return new TreeLeafValueStatic("", parent); | ||
} | ||
const [pathSegment, params] = parseSegment(trimmedSegment); | ||
if (params.length) { | ||
return new TreeLeafValueParam(trimmedSegment, parent, params, pathSegment); | ||
} | ||
return new TreeLeafValueStatic(trimmedSegment, parent); | ||
} | ||
function parseSegment(segment) { | ||
let buffer = ""; | ||
let state = 0 /* static */; | ||
const params = []; | ||
let pathSegment = ""; | ||
let currentTreeRouteParam = createEmptyRouteParam(); | ||
function consumeBuffer() { | ||
if (state === 0 /* static */) { | ||
pathSegment += buffer; | ||
} else if (state === 3 /* modifier */) { | ||
currentTreeRouteParam.paramName = buffer; | ||
currentTreeRouteParam.modifier = currentTreeRouteParam.optional ? currentTreeRouteParam.repeatable ? "*" : "?" : currentTreeRouteParam.repeatable ? "+" : ""; | ||
buffer = ""; | ||
pathSegment += `:${currentTreeRouteParam.paramName}${currentTreeRouteParam.isSplat ? "(.*)" : ""}${currentTreeRouteParam.modifier}`; | ||
params.push(currentTreeRouteParam); | ||
currentTreeRouteParam = createEmptyRouteParam(); | ||
} | ||
buffer = ""; | ||
} | ||
for (let pos = 0; pos < segment.length; pos++) { | ||
const c = segment[pos]; | ||
if (state === 0 /* static */) { | ||
if (c === "[") { | ||
consumeBuffer(); | ||
state = 1 /* paramOptional */; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 1 /* paramOptional */) { | ||
if (c === "[") { | ||
currentTreeRouteParam.optional = true; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
state = 2 /* param */; | ||
} else if (state === 2 /* param */) { | ||
if (c === "]") { | ||
if (currentTreeRouteParam.optional) { | ||
pos++; | ||
} | ||
state = 3 /* modifier */; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 3 /* modifier */) { | ||
if (c === "+") { | ||
currentTreeRouteParam.repeatable = true; | ||
} else { | ||
pos--; | ||
} | ||
consumeBuffer(); | ||
state = 0 /* static */; | ||
} | ||
} | ||
if (state === 2 /* param */ || state === 1 /* paramOptional */) { | ||
throw new Error(`Invalid segment: "${segment}"`); | ||
} | ||
if (buffer) { | ||
consumeBuffer(); | ||
} | ||
return [pathSegment, params]; | ||
} | ||
function createEmptyRouteParam() { | ||
return { | ||
paramName: "", | ||
modifier: "", | ||
optional: false, | ||
repeatable: false, | ||
isSplat: false | ||
}; | ||
} | ||
// src/core/tree.ts | ||
var TreeLeaf = class { | ||
constructor(value, parent) { | ||
this.children = /* @__PURE__ */ new Map(); | ||
this.value = createTreeLeafValue(value, parent == null ? void 0 : parent.value); | ||
} | ||
insert(path, filePath = path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
if (!this.children.has(segment)) { | ||
this.children.set(segment, new TreeLeaf(head, this)); | ||
} | ||
const child = this.children.get(segment); | ||
if (isComponent) { | ||
child.value.filePath = filePath; | ||
} | ||
if (tail) { | ||
child.insert(tail, filePath); | ||
} | ||
} | ||
remove(path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
const child = this.children.get(segment); | ||
if (!child) { | ||
throw new Error(`Cannot Delete "${path}". "${head}" not found at "${this.value.path}".`); | ||
} | ||
if (tail) { | ||
child.remove(tail); | ||
if (child.children.size === 0 && !child.value.filePath) { | ||
this.children.delete(segment); | ||
} | ||
} else { | ||
if (isComponent) { | ||
child.value.filePath = void 0; | ||
} | ||
if (child.children.size === 0) { | ||
this.children.delete(segment); | ||
} | ||
} | ||
} | ||
isRoot() { | ||
return this.value.path === "/" && !this.value.filePath; | ||
} | ||
toString() { | ||
return `${this.value}${this.value.filePath ? " \u{1F4C4}" : ""}`; | ||
} | ||
}; | ||
function createPrefixTree() { | ||
const tree = new TreeLeaf(""); | ||
return tree; | ||
} | ||
// src/core/context.ts | ||
var import_fs = require("fs"); | ||
// src/codegen/generateRouteParams.ts | ||
function generateRouteParams(node, isRaw) { | ||
return node.value.isParam() ? `{ ${node.value.params.map((param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `_ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `_ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `_ParamValueZeroOrOne<${isRaw}>` : `_ParamValue<${isRaw}>`)).join(", ")} }` : "Record<never, never>"; | ||
} | ||
// src/codegen/generateRouteMap.ts | ||
function generateRouteNamedMap(node) { | ||
if (node.isRoot()) { | ||
return `export interface RouteNamedMap { | ||
${Array.from(node.children.values()).map(generateRouteNamedMap).join("")}}`; | ||
} | ||
return (node.value.filePath ? ` '${node.value.routeName}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? Array.from(node.children.values()).map(generateRouteNamedMap).join("\n") : ""); | ||
} | ||
function generateRouteRecordInfo(node) { | ||
return `RouteRecordInfo<'${node.value.routeName}', '${node.value.path}', ${generateRouteParams(node, true)}, ${generateRouteParams(node, false)}>`; | ||
} | ||
// src/core/moduleConstants.ts | ||
var MODULE_VUE_ROUTER = "@vue-router"; | ||
var MODULE_ROUTES_PATH = `${MODULE_VUE_ROUTER}/routes`; | ||
var VIRTUAL_PREFIX = "virtual:"; | ||
// src/codegen/generateRouteRecords.ts | ||
function generateRouteRecord(node, indent = 0, parent = null) { | ||
if (node.value.path === "/" && indent === 0) { | ||
return `[ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 1)).join(",\n")} | ||
]`; | ||
} | ||
const startIndent = " ".repeat(indent * 2); | ||
const indentStr = " ".repeat((indent + 1) * 2); | ||
const name = node.value.routeName; | ||
return `${startIndent}{ | ||
${indentStr}path: "${(parent ? "" : "/") + node.value.pathSegment}", | ||
${indentStr}${node.value.filePath ? `name: "${name}",` : "/* no name */"} | ||
${indentStr}${node.value.filePath ? `component: () => import('${node.value.filePath}'),` : "/* no component */"} | ||
${indentStr}${node.children.size > 0 ? `children: [ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 2, node)).join(",\n")} | ||
${indentStr}],` : "/* no children */"} | ||
${startIndent}}`; | ||
} | ||
// src/core/context.ts | ||
function createRoutesContext(options) { | ||
const { dts: preferDTS, root } = options; | ||
const dts = preferDTS === false ? false : preferDTS === true ? (0, import_path.resolve)(root, "typed-router.d.ts") : (0, import_path.resolve)(root, preferDTS); | ||
const routeTree = createPrefixTree(); | ||
const resolvedRoutesFolder = (0, import_path.resolve)(root, options.routesFolder); | ||
const serverWatcher = import_chokidar.default.watch(resolvedRoutesFolder, { | ||
disableGlobbing: true, | ||
ignorePermissionErrors: true | ||
}); | ||
function stripRouteFolder(path) { | ||
return path.slice(resolvedRoutesFolder.length + 1); | ||
} | ||
function setupWatcher() { | ||
serverWatcher.on("change", (path) => { | ||
console.log("change", path); | ||
writeConfigFiles(); | ||
}).on("add", (path) => { | ||
console.log("added", path); | ||
routeTree.insert(stripRouteFolder(path), (0, import_path.resolve)(root, path)); | ||
writeConfigFiles(); | ||
}).on("unlink", (path) => { | ||
console.log("remove", path); | ||
routeTree.remove(stripRouteFolder(path)); | ||
writeConfigFiles(); | ||
}); | ||
} | ||
function stop() { | ||
serverWatcher.close(); | ||
} | ||
function generateRoutes() { | ||
return `export const routes = ${generateRouteRecord(routeTree)}`; | ||
} | ||
function generateDTS() { | ||
return ` | ||
// Generated by unplugin-vue-router. \u203C\uFE0F DO NOT MODIFY THIS FILE \u203C\uFE0F | ||
// It's recommended to commit this file. | ||
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry. | ||
/// <reference types="unplugin-vue-router/client" /> | ||
import type { | ||
_RouterTyped, | ||
RouteRecordInfo, | ||
RouterLinkTyped, | ||
RouteLocationNormalizedLoadedTypedList, | ||
RouteLocationAsString, | ||
NavigationGuard, | ||
_ParamValue, | ||
_ParamValueOneOrMore, | ||
_ParamValueZeroOrMore, | ||
_ParamValueZeroOrOne, | ||
} from 'unplugin-vue-router' | ||
declare module '${MODULE_ROUTES_PATH}' { | ||
${generateRouteNamedMap(routeTree).split("\n").filter((line) => line).map((line) => " " + line).join("\n")} | ||
} | ||
declare module '${MODULE_VUE_ROUTER}' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export function useRoute<Name extends keyof RouteNamedMap = keyof RouteNamedMap>(name?: Name): RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
export type RouterTyped = _RouterTyped<RouteNamedMap> | ||
/** | ||
* Generate a type safe route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type Route<Name extends keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
/** | ||
* Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParams<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['params'] | ||
/** | ||
* Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParamsRaw<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['paramsRaw'] | ||
export function useRouter(): RouterTyped | ||
export function onBeforeRouteLeave(guard: NavigationGuard<RouteMap>): void | ||
export function onBeforeRouteUpdate(guard: NavigationGuard<RouteMap>): void | ||
} | ||
declare module 'vue' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export interface GlobalComponents { | ||
RouterLink: RouterLinkTyped<RouteNamedMap> | ||
} | ||
} | ||
`; | ||
} | ||
let lastDTS; | ||
async function _writeConfigFiles() { | ||
console.log("writing"); | ||
logTree(routeTree); | ||
if (dts) { | ||
const content = generateDTS(); | ||
if (lastDTS !== content) { | ||
await import_fs.promises.writeFile(dts, content, "utf-8"); | ||
lastDTS = content; | ||
} | ||
} | ||
} | ||
const writeConfigFiles = throttle(_writeConfigFiles, 500); | ||
setupWatcher(); | ||
return { | ||
stop, | ||
writeConfigFiles, | ||
generateRoutes | ||
}; | ||
} | ||
// src/types.ts | ||
var import_local_pkg = require("local-pkg"); | ||
var DEFAULT_OPTIONS = { | ||
extensions: [".vue", ".js", ".jsx", ".ts", ".tsx"], | ||
exclude: [], | ||
routesFolder: "src/routes", | ||
importMode: "async", | ||
root: process.cwd(), | ||
dts: (0, import_local_pkg.isPackageExists)("typescript"), | ||
_inspect: false | ||
}; | ||
// src/index.ts | ||
var src_default = (0, import_unplugin.createUnplugin)((opt) => { | ||
const options = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), opt); | ||
const ctx = createRoutesContext(options); | ||
const root = process.cwd(); | ||
function getVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : null; | ||
} | ||
function asVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return VIRTUAL_PREFIX + id; | ||
} | ||
return { | ||
name: "unplugin-vue-router", | ||
enforce: "pre", | ||
resolveId(id) { | ||
if (id === MODULE_ROUTES_PATH) { | ||
return asVirtualId(id); | ||
} | ||
if (id === MODULE_VUE_ROUTER) { | ||
return "unplugin-vue-router/@vue-router/index.js"; | ||
} | ||
return null; | ||
}, | ||
buildStart() { | ||
}, | ||
load(id) { | ||
const resolvedId = getVirtualId(id); | ||
if (resolvedId === MODULE_ROUTES_PATH) { | ||
return ctx.generateRoutes(); | ||
} | ||
return null; | ||
} | ||
}; | ||
}); | ||
// src/nuxt.ts | ||
@@ -41,0 +522,0 @@ function nuxt_default(options) { |
@@ -0,5 +1,21 @@ | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __export = (target, all) => { | ||
@@ -17,2 +33,3 @@ for (var name in all) | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
@@ -29,12 +46,476 @@ | ||
var import_unplugin = require("unplugin"); | ||
var src_default = (0, import_unplugin.createUnplugin)((options) => ({ | ||
name: "unplugin-vue-router", | ||
transformInclude(id) { | ||
return id.endsWith("main.ts"); | ||
}, | ||
transform(code) { | ||
return code.replace("__UNPLUGIN__", `Hello Unplugin! ${options}`); | ||
// src/core/context.ts | ||
var import_chokidar = __toESM(require("chokidar")); | ||
var import_path = require("path"); | ||
// src/core/utils.ts | ||
var MAX_LEVEL = 1e3; | ||
var LogTree = class { | ||
setPre(hasNext, parentPre = "") { | ||
return `${parentPre}${hasNext ? "\u251C" : "\u2514"}\u2500\u2500 `; | ||
} | ||
})); | ||
setTransferPre(parentPre, hasNext) { | ||
return `${parentPre}${hasNext ? "\u2502" : " "} `; | ||
} | ||
parse(tree, level = 0, parentPre = "", treeStr = "") { | ||
if (!this.check(tree, level)) | ||
return ""; | ||
if (tree instanceof Map) { | ||
const total = tree.size; | ||
let index = 0; | ||
for (const [key, child] of tree) { | ||
const hasNext = index++ < total - 1; | ||
const { children } = child; | ||
treeStr += `${this.setPre(hasNext, parentPre)}${child} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1, this.setTransferPre(parentPre, hasNext)); | ||
} | ||
} | ||
} else { | ||
const children = tree.children; | ||
treeStr = `${tree} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1); | ||
} | ||
} | ||
return treeStr; | ||
} | ||
check(tree, level = 0) { | ||
if (typeof tree !== "object") | ||
return false; | ||
if (level >= MAX_LEVEL) | ||
return false; | ||
return true; | ||
} | ||
}; | ||
function logTree(tree) { | ||
const log = new LogTree(); | ||
console.log(log.parse(tree)); | ||
} | ||
var isArray = Array.isArray; | ||
function trimExtension(path) { | ||
const lastDot = path.lastIndexOf("."); | ||
return lastDot < 0 ? path : path.slice(0, lastDot); | ||
} | ||
function throttle(fn, wait) { | ||
let timeout = null; | ||
let pendingExecution = false; | ||
return () => { | ||
if (!timeout) { | ||
timeout = setTimeout(() => { | ||
timeout = null; | ||
if (pendingExecution) { | ||
pendingExecution = false; | ||
fn(); | ||
} | ||
}, wait); | ||
fn(); | ||
} else { | ||
pendingExecution = true; | ||
} | ||
}; | ||
} | ||
var LEADING_SLASH_RE = /^\//; | ||
var TRAILING_SLASH_RE = /\/$/; | ||
function joinPath(...paths) { | ||
let result = ""; | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + "/" + path.replace(LEADING_SLASH_RE, ""); | ||
} | ||
return result; | ||
} | ||
// src/core/treeLeafValue.ts | ||
var _TreeLeafValueBase = class { | ||
constructor(rawSegment, parent, pathSegment = rawSegment) { | ||
this._type = 0; | ||
this.rawSegment = rawSegment; | ||
this.pathSegment = pathSegment; | ||
this.path = !(parent == null ? void 0 : parent.path) && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.routeName = parent ? parent.routeName + "/" + rawSegment : ""; | ||
} | ||
toString() { | ||
return this.pathSegment || "<index>"; | ||
} | ||
isParam() { | ||
return !!(this._type & 1 /* param */); | ||
} | ||
isStatic() { | ||
return this._type === 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueStatic = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent) { | ||
super(rawSegment, parent); | ||
this._type = 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueParam = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent, params, pathSegment) { | ||
super(rawSegment, parent, pathSegment); | ||
this._type = 1 /* param */; | ||
this.params = params; | ||
} | ||
}; | ||
function createTreeLeafValue(segment, parent) { | ||
const trimmedSegment = trimExtension(segment); | ||
if (!trimmedSegment || trimmedSegment === "index") { | ||
return new TreeLeafValueStatic("", parent); | ||
} | ||
const [pathSegment, params] = parseSegment(trimmedSegment); | ||
if (params.length) { | ||
return new TreeLeafValueParam(trimmedSegment, parent, params, pathSegment); | ||
} | ||
return new TreeLeafValueStatic(trimmedSegment, parent); | ||
} | ||
function parseSegment(segment) { | ||
let buffer = ""; | ||
let state = 0 /* static */; | ||
const params = []; | ||
let pathSegment = ""; | ||
let currentTreeRouteParam = createEmptyRouteParam(); | ||
function consumeBuffer() { | ||
if (state === 0 /* static */) { | ||
pathSegment += buffer; | ||
} else if (state === 3 /* modifier */) { | ||
currentTreeRouteParam.paramName = buffer; | ||
currentTreeRouteParam.modifier = currentTreeRouteParam.optional ? currentTreeRouteParam.repeatable ? "*" : "?" : currentTreeRouteParam.repeatable ? "+" : ""; | ||
buffer = ""; | ||
pathSegment += `:${currentTreeRouteParam.paramName}${currentTreeRouteParam.isSplat ? "(.*)" : ""}${currentTreeRouteParam.modifier}`; | ||
params.push(currentTreeRouteParam); | ||
currentTreeRouteParam = createEmptyRouteParam(); | ||
} | ||
buffer = ""; | ||
} | ||
for (let pos = 0; pos < segment.length; pos++) { | ||
const c = segment[pos]; | ||
if (state === 0 /* static */) { | ||
if (c === "[") { | ||
consumeBuffer(); | ||
state = 1 /* paramOptional */; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 1 /* paramOptional */) { | ||
if (c === "[") { | ||
currentTreeRouteParam.optional = true; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
state = 2 /* param */; | ||
} else if (state === 2 /* param */) { | ||
if (c === "]") { | ||
if (currentTreeRouteParam.optional) { | ||
pos++; | ||
} | ||
state = 3 /* modifier */; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 3 /* modifier */) { | ||
if (c === "+") { | ||
currentTreeRouteParam.repeatable = true; | ||
} else { | ||
pos--; | ||
} | ||
consumeBuffer(); | ||
state = 0 /* static */; | ||
} | ||
} | ||
if (state === 2 /* param */ || state === 1 /* paramOptional */) { | ||
throw new Error(`Invalid segment: "${segment}"`); | ||
} | ||
if (buffer) { | ||
consumeBuffer(); | ||
} | ||
return [pathSegment, params]; | ||
} | ||
function createEmptyRouteParam() { | ||
return { | ||
paramName: "", | ||
modifier: "", | ||
optional: false, | ||
repeatable: false, | ||
isSplat: false | ||
}; | ||
} | ||
// src/core/tree.ts | ||
var TreeLeaf = class { | ||
constructor(value, parent) { | ||
this.children = /* @__PURE__ */ new Map(); | ||
this.value = createTreeLeafValue(value, parent == null ? void 0 : parent.value); | ||
} | ||
insert(path, filePath = path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
if (!this.children.has(segment)) { | ||
this.children.set(segment, new TreeLeaf(head, this)); | ||
} | ||
const child = this.children.get(segment); | ||
if (isComponent) { | ||
child.value.filePath = filePath; | ||
} | ||
if (tail) { | ||
child.insert(tail, filePath); | ||
} | ||
} | ||
remove(path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
const child = this.children.get(segment); | ||
if (!child) { | ||
throw new Error(`Cannot Delete "${path}". "${head}" not found at "${this.value.path}".`); | ||
} | ||
if (tail) { | ||
child.remove(tail); | ||
if (child.children.size === 0 && !child.value.filePath) { | ||
this.children.delete(segment); | ||
} | ||
} else { | ||
if (isComponent) { | ||
child.value.filePath = void 0; | ||
} | ||
if (child.children.size === 0) { | ||
this.children.delete(segment); | ||
} | ||
} | ||
} | ||
isRoot() { | ||
return this.value.path === "/" && !this.value.filePath; | ||
} | ||
toString() { | ||
return `${this.value}${this.value.filePath ? " \u{1F4C4}" : ""}`; | ||
} | ||
}; | ||
function createPrefixTree() { | ||
const tree = new TreeLeaf(""); | ||
return tree; | ||
} | ||
// src/core/context.ts | ||
var import_fs = require("fs"); | ||
// src/codegen/generateRouteParams.ts | ||
function generateRouteParams(node, isRaw) { | ||
return node.value.isParam() ? `{ ${node.value.params.map((param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `_ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `_ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `_ParamValueZeroOrOne<${isRaw}>` : `_ParamValue<${isRaw}>`)).join(", ")} }` : "Record<never, never>"; | ||
} | ||
// src/codegen/generateRouteMap.ts | ||
function generateRouteNamedMap(node) { | ||
if (node.isRoot()) { | ||
return `export interface RouteNamedMap { | ||
${Array.from(node.children.values()).map(generateRouteNamedMap).join("")}}`; | ||
} | ||
return (node.value.filePath ? ` '${node.value.routeName}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? Array.from(node.children.values()).map(generateRouteNamedMap).join("\n") : ""); | ||
} | ||
function generateRouteRecordInfo(node) { | ||
return `RouteRecordInfo<'${node.value.routeName}', '${node.value.path}', ${generateRouteParams(node, true)}, ${generateRouteParams(node, false)}>`; | ||
} | ||
// src/core/moduleConstants.ts | ||
var MODULE_VUE_ROUTER = "@vue-router"; | ||
var MODULE_ROUTES_PATH = `${MODULE_VUE_ROUTER}/routes`; | ||
var VIRTUAL_PREFIX = "virtual:"; | ||
// src/codegen/generateRouteRecords.ts | ||
function generateRouteRecord(node, indent = 0, parent = null) { | ||
if (node.value.path === "/" && indent === 0) { | ||
return `[ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 1)).join(",\n")} | ||
]`; | ||
} | ||
const startIndent = " ".repeat(indent * 2); | ||
const indentStr = " ".repeat((indent + 1) * 2); | ||
const name = node.value.routeName; | ||
return `${startIndent}{ | ||
${indentStr}path: "${(parent ? "" : "/") + node.value.pathSegment}", | ||
${indentStr}${node.value.filePath ? `name: "${name}",` : "/* no name */"} | ||
${indentStr}${node.value.filePath ? `component: () => import('${node.value.filePath}'),` : "/* no component */"} | ||
${indentStr}${node.children.size > 0 ? `children: [ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 2, node)).join(",\n")} | ||
${indentStr}],` : "/* no children */"} | ||
${startIndent}}`; | ||
} | ||
// src/core/context.ts | ||
function createRoutesContext(options) { | ||
const { dts: preferDTS, root } = options; | ||
const dts = preferDTS === false ? false : preferDTS === true ? (0, import_path.resolve)(root, "typed-router.d.ts") : (0, import_path.resolve)(root, preferDTS); | ||
const routeTree = createPrefixTree(); | ||
const resolvedRoutesFolder = (0, import_path.resolve)(root, options.routesFolder); | ||
const serverWatcher = import_chokidar.default.watch(resolvedRoutesFolder, { | ||
disableGlobbing: true, | ||
ignorePermissionErrors: true | ||
}); | ||
function stripRouteFolder(path) { | ||
return path.slice(resolvedRoutesFolder.length + 1); | ||
} | ||
function setupWatcher() { | ||
serverWatcher.on("change", (path) => { | ||
console.log("change", path); | ||
writeConfigFiles(); | ||
}).on("add", (path) => { | ||
console.log("added", path); | ||
routeTree.insert(stripRouteFolder(path), (0, import_path.resolve)(root, path)); | ||
writeConfigFiles(); | ||
}).on("unlink", (path) => { | ||
console.log("remove", path); | ||
routeTree.remove(stripRouteFolder(path)); | ||
writeConfigFiles(); | ||
}); | ||
} | ||
function stop() { | ||
serverWatcher.close(); | ||
} | ||
function generateRoutes() { | ||
return `export const routes = ${generateRouteRecord(routeTree)}`; | ||
} | ||
function generateDTS() { | ||
return ` | ||
// Generated by unplugin-vue-router. \u203C\uFE0F DO NOT MODIFY THIS FILE \u203C\uFE0F | ||
// It's recommended to commit this file. | ||
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry. | ||
/// <reference types="unplugin-vue-router/client" /> | ||
import type { | ||
_RouterTyped, | ||
RouteRecordInfo, | ||
RouterLinkTyped, | ||
RouteLocationNormalizedLoadedTypedList, | ||
RouteLocationAsString, | ||
NavigationGuard, | ||
_ParamValue, | ||
_ParamValueOneOrMore, | ||
_ParamValueZeroOrMore, | ||
_ParamValueZeroOrOne, | ||
} from 'unplugin-vue-router' | ||
declare module '${MODULE_ROUTES_PATH}' { | ||
${generateRouteNamedMap(routeTree).split("\n").filter((line) => line).map((line) => " " + line).join("\n")} | ||
} | ||
declare module '${MODULE_VUE_ROUTER}' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export function useRoute<Name extends keyof RouteNamedMap = keyof RouteNamedMap>(name?: Name): RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
export type RouterTyped = _RouterTyped<RouteNamedMap> | ||
/** | ||
* Generate a type safe route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type Route<Name extends keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
/** | ||
* Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParams<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['params'] | ||
/** | ||
* Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParamsRaw<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['paramsRaw'] | ||
export function useRouter(): RouterTyped | ||
export function onBeforeRouteLeave(guard: NavigationGuard<RouteMap>): void | ||
export function onBeforeRouteUpdate(guard: NavigationGuard<RouteMap>): void | ||
} | ||
declare module 'vue' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export interface GlobalComponents { | ||
RouterLink: RouterLinkTyped<RouteNamedMap> | ||
} | ||
} | ||
`; | ||
} | ||
let lastDTS; | ||
async function _writeConfigFiles() { | ||
console.log("writing"); | ||
logTree(routeTree); | ||
if (dts) { | ||
const content = generateDTS(); | ||
if (lastDTS !== content) { | ||
await import_fs.promises.writeFile(dts, content, "utf-8"); | ||
lastDTS = content; | ||
} | ||
} | ||
} | ||
const writeConfigFiles = throttle(_writeConfigFiles, 500); | ||
setupWatcher(); | ||
return { | ||
stop, | ||
writeConfigFiles, | ||
generateRoutes | ||
}; | ||
} | ||
// src/types.ts | ||
var import_local_pkg = require("local-pkg"); | ||
var DEFAULT_OPTIONS = { | ||
extensions: [".vue", ".js", ".jsx", ".ts", ".tsx"], | ||
exclude: [], | ||
routesFolder: "src/routes", | ||
importMode: "async", | ||
root: process.cwd(), | ||
dts: (0, import_local_pkg.isPackageExists)("typescript"), | ||
_inspect: false | ||
}; | ||
// src/index.ts | ||
var src_default = (0, import_unplugin.createUnplugin)((opt) => { | ||
const options = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), opt); | ||
const ctx = createRoutesContext(options); | ||
const root = process.cwd(); | ||
function getVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : null; | ||
} | ||
function asVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return VIRTUAL_PREFIX + id; | ||
} | ||
return { | ||
name: "unplugin-vue-router", | ||
enforce: "pre", | ||
resolveId(id) { | ||
if (id === MODULE_ROUTES_PATH) { | ||
return asVirtualId(id); | ||
} | ||
if (id === MODULE_VUE_ROUTER) { | ||
return "unplugin-vue-router/@vue-router/index.js"; | ||
} | ||
return null; | ||
}, | ||
buildStart() { | ||
}, | ||
load(id) { | ||
const resolvedId = getVirtualId(id); | ||
if (resolvedId === MODULE_ROUTES_PATH) { | ||
return ctx.generateRoutes(); | ||
} | ||
return null; | ||
} | ||
}; | ||
}); | ||
// src/rollup.ts | ||
@@ -41,0 +522,0 @@ var rollup_default = src_default.rollup; |
interface Options { | ||
extensions?: string[]; | ||
routesFolder?: string; | ||
importMode?: 'sync' | 'async' | ((path: string, resolvedOptions: Options) => 'sync' | 'async'); | ||
exclude?: string[]; | ||
root?: string; | ||
/** | ||
* Should generate d.ts files. Defaults to `true` if `typescript` is installed. | ||
*/ | ||
dts?: boolean; | ||
/** | ||
* Allows inspection by vite-plugin-inspect by not adding the leading `\0` to the id of virtual modules. | ||
* @internal | ||
*/ | ||
_inspect?: boolean; | ||
} | ||
declare const DEFAULT_OPTIONS: Required<Options>; | ||
export { Options }; | ||
export { DEFAULT_OPTIONS, Options }; |
@@ -5,2 +5,6 @@ var __defProp = Object.defineProperty; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __export = (target, all) => { | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __copyProps = (to, from, except, desc) => { | ||
@@ -18,2 +22,19 @@ if (from && typeof from === "object" || typeof from === "function") { | ||
var types_exports = {}; | ||
__export(types_exports, { | ||
DEFAULT_OPTIONS: () => DEFAULT_OPTIONS | ||
}); | ||
module.exports = __toCommonJS(types_exports); | ||
var import_local_pkg = require("local-pkg"); | ||
var DEFAULT_OPTIONS = { | ||
extensions: [".vue", ".js", ".jsx", ".ts", ".tsx"], | ||
exclude: [], | ||
routesFolder: "src/routes", | ||
importMode: "async", | ||
root: process.cwd(), | ||
dts: (0, import_local_pkg.isPackageExists)("typescript"), | ||
_inspect: false | ||
}; | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
DEFAULT_OPTIONS | ||
}); |
497
dist/vite.js
@@ -0,5 +1,21 @@ | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __export = (target, all) => { | ||
@@ -17,2 +33,3 @@ for (var name in all) | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
@@ -29,12 +46,476 @@ | ||
var import_unplugin = require("unplugin"); | ||
var src_default = (0, import_unplugin.createUnplugin)((options) => ({ | ||
name: "unplugin-vue-router", | ||
transformInclude(id) { | ||
return id.endsWith("main.ts"); | ||
}, | ||
transform(code) { | ||
return code.replace("__UNPLUGIN__", `Hello Unplugin! ${options}`); | ||
// src/core/context.ts | ||
var import_chokidar = __toESM(require("chokidar")); | ||
var import_path = require("path"); | ||
// src/core/utils.ts | ||
var MAX_LEVEL = 1e3; | ||
var LogTree = class { | ||
setPre(hasNext, parentPre = "") { | ||
return `${parentPre}${hasNext ? "\u251C" : "\u2514"}\u2500\u2500 `; | ||
} | ||
})); | ||
setTransferPre(parentPre, hasNext) { | ||
return `${parentPre}${hasNext ? "\u2502" : " "} `; | ||
} | ||
parse(tree, level = 0, parentPre = "", treeStr = "") { | ||
if (!this.check(tree, level)) | ||
return ""; | ||
if (tree instanceof Map) { | ||
const total = tree.size; | ||
let index = 0; | ||
for (const [key, child] of tree) { | ||
const hasNext = index++ < total - 1; | ||
const { children } = child; | ||
treeStr += `${this.setPre(hasNext, parentPre)}${child} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1, this.setTransferPre(parentPre, hasNext)); | ||
} | ||
} | ||
} else { | ||
const children = tree.children; | ||
treeStr = `${tree} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1); | ||
} | ||
} | ||
return treeStr; | ||
} | ||
check(tree, level = 0) { | ||
if (typeof tree !== "object") | ||
return false; | ||
if (level >= MAX_LEVEL) | ||
return false; | ||
return true; | ||
} | ||
}; | ||
function logTree(tree) { | ||
const log = new LogTree(); | ||
console.log(log.parse(tree)); | ||
} | ||
var isArray = Array.isArray; | ||
function trimExtension(path) { | ||
const lastDot = path.lastIndexOf("."); | ||
return lastDot < 0 ? path : path.slice(0, lastDot); | ||
} | ||
function throttle(fn, wait) { | ||
let timeout = null; | ||
let pendingExecution = false; | ||
return () => { | ||
if (!timeout) { | ||
timeout = setTimeout(() => { | ||
timeout = null; | ||
if (pendingExecution) { | ||
pendingExecution = false; | ||
fn(); | ||
} | ||
}, wait); | ||
fn(); | ||
} else { | ||
pendingExecution = true; | ||
} | ||
}; | ||
} | ||
var LEADING_SLASH_RE = /^\//; | ||
var TRAILING_SLASH_RE = /\/$/; | ||
function joinPath(...paths) { | ||
let result = ""; | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + "/" + path.replace(LEADING_SLASH_RE, ""); | ||
} | ||
return result; | ||
} | ||
// src/core/treeLeafValue.ts | ||
var _TreeLeafValueBase = class { | ||
constructor(rawSegment, parent, pathSegment = rawSegment) { | ||
this._type = 0; | ||
this.rawSegment = rawSegment; | ||
this.pathSegment = pathSegment; | ||
this.path = !(parent == null ? void 0 : parent.path) && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.routeName = parent ? parent.routeName + "/" + rawSegment : ""; | ||
} | ||
toString() { | ||
return this.pathSegment || "<index>"; | ||
} | ||
isParam() { | ||
return !!(this._type & 1 /* param */); | ||
} | ||
isStatic() { | ||
return this._type === 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueStatic = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent) { | ||
super(rawSegment, parent); | ||
this._type = 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueParam = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent, params, pathSegment) { | ||
super(rawSegment, parent, pathSegment); | ||
this._type = 1 /* param */; | ||
this.params = params; | ||
} | ||
}; | ||
function createTreeLeafValue(segment, parent) { | ||
const trimmedSegment = trimExtension(segment); | ||
if (!trimmedSegment || trimmedSegment === "index") { | ||
return new TreeLeafValueStatic("", parent); | ||
} | ||
const [pathSegment, params] = parseSegment(trimmedSegment); | ||
if (params.length) { | ||
return new TreeLeafValueParam(trimmedSegment, parent, params, pathSegment); | ||
} | ||
return new TreeLeafValueStatic(trimmedSegment, parent); | ||
} | ||
function parseSegment(segment) { | ||
let buffer = ""; | ||
let state = 0 /* static */; | ||
const params = []; | ||
let pathSegment = ""; | ||
let currentTreeRouteParam = createEmptyRouteParam(); | ||
function consumeBuffer() { | ||
if (state === 0 /* static */) { | ||
pathSegment += buffer; | ||
} else if (state === 3 /* modifier */) { | ||
currentTreeRouteParam.paramName = buffer; | ||
currentTreeRouteParam.modifier = currentTreeRouteParam.optional ? currentTreeRouteParam.repeatable ? "*" : "?" : currentTreeRouteParam.repeatable ? "+" : ""; | ||
buffer = ""; | ||
pathSegment += `:${currentTreeRouteParam.paramName}${currentTreeRouteParam.isSplat ? "(.*)" : ""}${currentTreeRouteParam.modifier}`; | ||
params.push(currentTreeRouteParam); | ||
currentTreeRouteParam = createEmptyRouteParam(); | ||
} | ||
buffer = ""; | ||
} | ||
for (let pos = 0; pos < segment.length; pos++) { | ||
const c = segment[pos]; | ||
if (state === 0 /* static */) { | ||
if (c === "[") { | ||
consumeBuffer(); | ||
state = 1 /* paramOptional */; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 1 /* paramOptional */) { | ||
if (c === "[") { | ||
currentTreeRouteParam.optional = true; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
state = 2 /* param */; | ||
} else if (state === 2 /* param */) { | ||
if (c === "]") { | ||
if (currentTreeRouteParam.optional) { | ||
pos++; | ||
} | ||
state = 3 /* modifier */; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 3 /* modifier */) { | ||
if (c === "+") { | ||
currentTreeRouteParam.repeatable = true; | ||
} else { | ||
pos--; | ||
} | ||
consumeBuffer(); | ||
state = 0 /* static */; | ||
} | ||
} | ||
if (state === 2 /* param */ || state === 1 /* paramOptional */) { | ||
throw new Error(`Invalid segment: "${segment}"`); | ||
} | ||
if (buffer) { | ||
consumeBuffer(); | ||
} | ||
return [pathSegment, params]; | ||
} | ||
function createEmptyRouteParam() { | ||
return { | ||
paramName: "", | ||
modifier: "", | ||
optional: false, | ||
repeatable: false, | ||
isSplat: false | ||
}; | ||
} | ||
// src/core/tree.ts | ||
var TreeLeaf = class { | ||
constructor(value, parent) { | ||
this.children = /* @__PURE__ */ new Map(); | ||
this.value = createTreeLeafValue(value, parent == null ? void 0 : parent.value); | ||
} | ||
insert(path, filePath = path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
if (!this.children.has(segment)) { | ||
this.children.set(segment, new TreeLeaf(head, this)); | ||
} | ||
const child = this.children.get(segment); | ||
if (isComponent) { | ||
child.value.filePath = filePath; | ||
} | ||
if (tail) { | ||
child.insert(tail, filePath); | ||
} | ||
} | ||
remove(path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
const child = this.children.get(segment); | ||
if (!child) { | ||
throw new Error(`Cannot Delete "${path}". "${head}" not found at "${this.value.path}".`); | ||
} | ||
if (tail) { | ||
child.remove(tail); | ||
if (child.children.size === 0 && !child.value.filePath) { | ||
this.children.delete(segment); | ||
} | ||
} else { | ||
if (isComponent) { | ||
child.value.filePath = void 0; | ||
} | ||
if (child.children.size === 0) { | ||
this.children.delete(segment); | ||
} | ||
} | ||
} | ||
isRoot() { | ||
return this.value.path === "/" && !this.value.filePath; | ||
} | ||
toString() { | ||
return `${this.value}${this.value.filePath ? " \u{1F4C4}" : ""}`; | ||
} | ||
}; | ||
function createPrefixTree() { | ||
const tree = new TreeLeaf(""); | ||
return tree; | ||
} | ||
// src/core/context.ts | ||
var import_fs = require("fs"); | ||
// src/codegen/generateRouteParams.ts | ||
function generateRouteParams(node, isRaw) { | ||
return node.value.isParam() ? `{ ${node.value.params.map((param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `_ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `_ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `_ParamValueZeroOrOne<${isRaw}>` : `_ParamValue<${isRaw}>`)).join(", ")} }` : "Record<never, never>"; | ||
} | ||
// src/codegen/generateRouteMap.ts | ||
function generateRouteNamedMap(node) { | ||
if (node.isRoot()) { | ||
return `export interface RouteNamedMap { | ||
${Array.from(node.children.values()).map(generateRouteNamedMap).join("")}}`; | ||
} | ||
return (node.value.filePath ? ` '${node.value.routeName}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? Array.from(node.children.values()).map(generateRouteNamedMap).join("\n") : ""); | ||
} | ||
function generateRouteRecordInfo(node) { | ||
return `RouteRecordInfo<'${node.value.routeName}', '${node.value.path}', ${generateRouteParams(node, true)}, ${generateRouteParams(node, false)}>`; | ||
} | ||
// src/core/moduleConstants.ts | ||
var MODULE_VUE_ROUTER = "@vue-router"; | ||
var MODULE_ROUTES_PATH = `${MODULE_VUE_ROUTER}/routes`; | ||
var VIRTUAL_PREFIX = "virtual:"; | ||
// src/codegen/generateRouteRecords.ts | ||
function generateRouteRecord(node, indent = 0, parent = null) { | ||
if (node.value.path === "/" && indent === 0) { | ||
return `[ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 1)).join(",\n")} | ||
]`; | ||
} | ||
const startIndent = " ".repeat(indent * 2); | ||
const indentStr = " ".repeat((indent + 1) * 2); | ||
const name = node.value.routeName; | ||
return `${startIndent}{ | ||
${indentStr}path: "${(parent ? "" : "/") + node.value.pathSegment}", | ||
${indentStr}${node.value.filePath ? `name: "${name}",` : "/* no name */"} | ||
${indentStr}${node.value.filePath ? `component: () => import('${node.value.filePath}'),` : "/* no component */"} | ||
${indentStr}${node.children.size > 0 ? `children: [ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 2, node)).join(",\n")} | ||
${indentStr}],` : "/* no children */"} | ||
${startIndent}}`; | ||
} | ||
// src/core/context.ts | ||
function createRoutesContext(options) { | ||
const { dts: preferDTS, root } = options; | ||
const dts = preferDTS === false ? false : preferDTS === true ? (0, import_path.resolve)(root, "typed-router.d.ts") : (0, import_path.resolve)(root, preferDTS); | ||
const routeTree = createPrefixTree(); | ||
const resolvedRoutesFolder = (0, import_path.resolve)(root, options.routesFolder); | ||
const serverWatcher = import_chokidar.default.watch(resolvedRoutesFolder, { | ||
disableGlobbing: true, | ||
ignorePermissionErrors: true | ||
}); | ||
function stripRouteFolder(path) { | ||
return path.slice(resolvedRoutesFolder.length + 1); | ||
} | ||
function setupWatcher() { | ||
serverWatcher.on("change", (path) => { | ||
console.log("change", path); | ||
writeConfigFiles(); | ||
}).on("add", (path) => { | ||
console.log("added", path); | ||
routeTree.insert(stripRouteFolder(path), (0, import_path.resolve)(root, path)); | ||
writeConfigFiles(); | ||
}).on("unlink", (path) => { | ||
console.log("remove", path); | ||
routeTree.remove(stripRouteFolder(path)); | ||
writeConfigFiles(); | ||
}); | ||
} | ||
function stop() { | ||
serverWatcher.close(); | ||
} | ||
function generateRoutes() { | ||
return `export const routes = ${generateRouteRecord(routeTree)}`; | ||
} | ||
function generateDTS() { | ||
return ` | ||
// Generated by unplugin-vue-router. \u203C\uFE0F DO NOT MODIFY THIS FILE \u203C\uFE0F | ||
// It's recommended to commit this file. | ||
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry. | ||
/// <reference types="unplugin-vue-router/client" /> | ||
import type { | ||
_RouterTyped, | ||
RouteRecordInfo, | ||
RouterLinkTyped, | ||
RouteLocationNormalizedLoadedTypedList, | ||
RouteLocationAsString, | ||
NavigationGuard, | ||
_ParamValue, | ||
_ParamValueOneOrMore, | ||
_ParamValueZeroOrMore, | ||
_ParamValueZeroOrOne, | ||
} from 'unplugin-vue-router' | ||
declare module '${MODULE_ROUTES_PATH}' { | ||
${generateRouteNamedMap(routeTree).split("\n").filter((line) => line).map((line) => " " + line).join("\n")} | ||
} | ||
declare module '${MODULE_VUE_ROUTER}' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export function useRoute<Name extends keyof RouteNamedMap = keyof RouteNamedMap>(name?: Name): RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
export type RouterTyped = _RouterTyped<RouteNamedMap> | ||
/** | ||
* Generate a type safe route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type Route<Name extends keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
/** | ||
* Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParams<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['params'] | ||
/** | ||
* Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParamsRaw<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['paramsRaw'] | ||
export function useRouter(): RouterTyped | ||
export function onBeforeRouteLeave(guard: NavigationGuard<RouteMap>): void | ||
export function onBeforeRouteUpdate(guard: NavigationGuard<RouteMap>): void | ||
} | ||
declare module 'vue' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export interface GlobalComponents { | ||
RouterLink: RouterLinkTyped<RouteNamedMap> | ||
} | ||
} | ||
`; | ||
} | ||
let lastDTS; | ||
async function _writeConfigFiles() { | ||
console.log("writing"); | ||
logTree(routeTree); | ||
if (dts) { | ||
const content = generateDTS(); | ||
if (lastDTS !== content) { | ||
await import_fs.promises.writeFile(dts, content, "utf-8"); | ||
lastDTS = content; | ||
} | ||
} | ||
} | ||
const writeConfigFiles = throttle(_writeConfigFiles, 500); | ||
setupWatcher(); | ||
return { | ||
stop, | ||
writeConfigFiles, | ||
generateRoutes | ||
}; | ||
} | ||
// src/types.ts | ||
var import_local_pkg = require("local-pkg"); | ||
var DEFAULT_OPTIONS = { | ||
extensions: [".vue", ".js", ".jsx", ".ts", ".tsx"], | ||
exclude: [], | ||
routesFolder: "src/routes", | ||
importMode: "async", | ||
root: process.cwd(), | ||
dts: (0, import_local_pkg.isPackageExists)("typescript"), | ||
_inspect: false | ||
}; | ||
// src/index.ts | ||
var src_default = (0, import_unplugin.createUnplugin)((opt) => { | ||
const options = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), opt); | ||
const ctx = createRoutesContext(options); | ||
const root = process.cwd(); | ||
function getVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : null; | ||
} | ||
function asVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return VIRTUAL_PREFIX + id; | ||
} | ||
return { | ||
name: "unplugin-vue-router", | ||
enforce: "pre", | ||
resolveId(id) { | ||
if (id === MODULE_ROUTES_PATH) { | ||
return asVirtualId(id); | ||
} | ||
if (id === MODULE_VUE_ROUTER) { | ||
return "unplugin-vue-router/@vue-router/index.js"; | ||
} | ||
return null; | ||
}, | ||
buildStart() { | ||
}, | ||
load(id) { | ||
const resolvedId = getVirtualId(id); | ||
if (resolvedId === MODULE_ROUTES_PATH) { | ||
return ctx.generateRoutes(); | ||
} | ||
return null; | ||
} | ||
}; | ||
}); | ||
// src/vite.ts | ||
@@ -41,0 +522,0 @@ var vite_default = src_default.vite; |
@@ -0,5 +1,21 @@ | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __export = (target, all) => { | ||
@@ -17,2 +33,3 @@ for (var name in all) | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
@@ -29,12 +46,476 @@ | ||
var import_unplugin = require("unplugin"); | ||
var src_default = (0, import_unplugin.createUnplugin)((options) => ({ | ||
name: "unplugin-vue-router", | ||
transformInclude(id) { | ||
return id.endsWith("main.ts"); | ||
}, | ||
transform(code) { | ||
return code.replace("__UNPLUGIN__", `Hello Unplugin! ${options}`); | ||
// src/core/context.ts | ||
var import_chokidar = __toESM(require("chokidar")); | ||
var import_path = require("path"); | ||
// src/core/utils.ts | ||
var MAX_LEVEL = 1e3; | ||
var LogTree = class { | ||
setPre(hasNext, parentPre = "") { | ||
return `${parentPre}${hasNext ? "\u251C" : "\u2514"}\u2500\u2500 `; | ||
} | ||
})); | ||
setTransferPre(parentPre, hasNext) { | ||
return `${parentPre}${hasNext ? "\u2502" : " "} `; | ||
} | ||
parse(tree, level = 0, parentPre = "", treeStr = "") { | ||
if (!this.check(tree, level)) | ||
return ""; | ||
if (tree instanceof Map) { | ||
const total = tree.size; | ||
let index = 0; | ||
for (const [key, child] of tree) { | ||
const hasNext = index++ < total - 1; | ||
const { children } = child; | ||
treeStr += `${this.setPre(hasNext, parentPre)}${child} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1, this.setTransferPre(parentPre, hasNext)); | ||
} | ||
} | ||
} else { | ||
const children = tree.children; | ||
treeStr = `${tree} | ||
`; | ||
if (children) { | ||
treeStr += this.parse(children, level + 1); | ||
} | ||
} | ||
return treeStr; | ||
} | ||
check(tree, level = 0) { | ||
if (typeof tree !== "object") | ||
return false; | ||
if (level >= MAX_LEVEL) | ||
return false; | ||
return true; | ||
} | ||
}; | ||
function logTree(tree) { | ||
const log = new LogTree(); | ||
console.log(log.parse(tree)); | ||
} | ||
var isArray = Array.isArray; | ||
function trimExtension(path) { | ||
const lastDot = path.lastIndexOf("."); | ||
return lastDot < 0 ? path : path.slice(0, lastDot); | ||
} | ||
function throttle(fn, wait) { | ||
let timeout = null; | ||
let pendingExecution = false; | ||
return () => { | ||
if (!timeout) { | ||
timeout = setTimeout(() => { | ||
timeout = null; | ||
if (pendingExecution) { | ||
pendingExecution = false; | ||
fn(); | ||
} | ||
}, wait); | ||
fn(); | ||
} else { | ||
pendingExecution = true; | ||
} | ||
}; | ||
} | ||
var LEADING_SLASH_RE = /^\//; | ||
var TRAILING_SLASH_RE = /\/$/; | ||
function joinPath(...paths) { | ||
let result = ""; | ||
for (const path of paths) { | ||
result = result.replace(TRAILING_SLASH_RE, "") + "/" + path.replace(LEADING_SLASH_RE, ""); | ||
} | ||
return result; | ||
} | ||
// src/core/treeLeafValue.ts | ||
var _TreeLeafValueBase = class { | ||
constructor(rawSegment, parent, pathSegment = rawSegment) { | ||
this._type = 0; | ||
this.rawSegment = rawSegment; | ||
this.pathSegment = pathSegment; | ||
this.path = !(parent == null ? void 0 : parent.path) && this.pathSegment === "" ? "/" : joinPath((parent == null ? void 0 : parent.path) || "", this.pathSegment); | ||
this.routeName = parent ? parent.routeName + "/" + rawSegment : ""; | ||
} | ||
toString() { | ||
return this.pathSegment || "<index>"; | ||
} | ||
isParam() { | ||
return !!(this._type & 1 /* param */); | ||
} | ||
isStatic() { | ||
return this._type === 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueStatic = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent) { | ||
super(rawSegment, parent); | ||
this._type = 0 /* static */; | ||
} | ||
}; | ||
var TreeLeafValueParam = class extends _TreeLeafValueBase { | ||
constructor(rawSegment, parent, params, pathSegment) { | ||
super(rawSegment, parent, pathSegment); | ||
this._type = 1 /* param */; | ||
this.params = params; | ||
} | ||
}; | ||
function createTreeLeafValue(segment, parent) { | ||
const trimmedSegment = trimExtension(segment); | ||
if (!trimmedSegment || trimmedSegment === "index") { | ||
return new TreeLeafValueStatic("", parent); | ||
} | ||
const [pathSegment, params] = parseSegment(trimmedSegment); | ||
if (params.length) { | ||
return new TreeLeafValueParam(trimmedSegment, parent, params, pathSegment); | ||
} | ||
return new TreeLeafValueStatic(trimmedSegment, parent); | ||
} | ||
function parseSegment(segment) { | ||
let buffer = ""; | ||
let state = 0 /* static */; | ||
const params = []; | ||
let pathSegment = ""; | ||
let currentTreeRouteParam = createEmptyRouteParam(); | ||
function consumeBuffer() { | ||
if (state === 0 /* static */) { | ||
pathSegment += buffer; | ||
} else if (state === 3 /* modifier */) { | ||
currentTreeRouteParam.paramName = buffer; | ||
currentTreeRouteParam.modifier = currentTreeRouteParam.optional ? currentTreeRouteParam.repeatable ? "*" : "?" : currentTreeRouteParam.repeatable ? "+" : ""; | ||
buffer = ""; | ||
pathSegment += `:${currentTreeRouteParam.paramName}${currentTreeRouteParam.isSplat ? "(.*)" : ""}${currentTreeRouteParam.modifier}`; | ||
params.push(currentTreeRouteParam); | ||
currentTreeRouteParam = createEmptyRouteParam(); | ||
} | ||
buffer = ""; | ||
} | ||
for (let pos = 0; pos < segment.length; pos++) { | ||
const c = segment[pos]; | ||
if (state === 0 /* static */) { | ||
if (c === "[") { | ||
consumeBuffer(); | ||
state = 1 /* paramOptional */; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 1 /* paramOptional */) { | ||
if (c === "[") { | ||
currentTreeRouteParam.optional = true; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
state = 2 /* param */; | ||
} else if (state === 2 /* param */) { | ||
if (c === "]") { | ||
if (currentTreeRouteParam.optional) { | ||
pos++; | ||
} | ||
state = 3 /* modifier */; | ||
} else if (c === ".") { | ||
currentTreeRouteParam.isSplat = true; | ||
pos += 2; | ||
} else { | ||
buffer += c; | ||
} | ||
} else if (state === 3 /* modifier */) { | ||
if (c === "+") { | ||
currentTreeRouteParam.repeatable = true; | ||
} else { | ||
pos--; | ||
} | ||
consumeBuffer(); | ||
state = 0 /* static */; | ||
} | ||
} | ||
if (state === 2 /* param */ || state === 1 /* paramOptional */) { | ||
throw new Error(`Invalid segment: "${segment}"`); | ||
} | ||
if (buffer) { | ||
consumeBuffer(); | ||
} | ||
return [pathSegment, params]; | ||
} | ||
function createEmptyRouteParam() { | ||
return { | ||
paramName: "", | ||
modifier: "", | ||
optional: false, | ||
repeatable: false, | ||
isSplat: false | ||
}; | ||
} | ||
// src/core/tree.ts | ||
var TreeLeaf = class { | ||
constructor(value, parent) { | ||
this.children = /* @__PURE__ */ new Map(); | ||
this.value = createTreeLeafValue(value, parent == null ? void 0 : parent.value); | ||
} | ||
insert(path, filePath = path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
if (!this.children.has(segment)) { | ||
this.children.set(segment, new TreeLeaf(head, this)); | ||
} | ||
const child = this.children.get(segment); | ||
if (isComponent) { | ||
child.value.filePath = filePath; | ||
} | ||
if (tail) { | ||
child.insert(tail, filePath); | ||
} | ||
} | ||
remove(path) { | ||
const slashPos = path.indexOf("/"); | ||
const head = slashPos < 0 ? path : path.slice(0, slashPos); | ||
const tail = slashPos < 0 ? "" : path.slice(slashPos + 1); | ||
const segment = trimExtension(head); | ||
const isComponent = segment !== head; | ||
const child = this.children.get(segment); | ||
if (!child) { | ||
throw new Error(`Cannot Delete "${path}". "${head}" not found at "${this.value.path}".`); | ||
} | ||
if (tail) { | ||
child.remove(tail); | ||
if (child.children.size === 0 && !child.value.filePath) { | ||
this.children.delete(segment); | ||
} | ||
} else { | ||
if (isComponent) { | ||
child.value.filePath = void 0; | ||
} | ||
if (child.children.size === 0) { | ||
this.children.delete(segment); | ||
} | ||
} | ||
} | ||
isRoot() { | ||
return this.value.path === "/" && !this.value.filePath; | ||
} | ||
toString() { | ||
return `${this.value}${this.value.filePath ? " \u{1F4C4}" : ""}`; | ||
} | ||
}; | ||
function createPrefixTree() { | ||
const tree = new TreeLeaf(""); | ||
return tree; | ||
} | ||
// src/core/context.ts | ||
var import_fs = require("fs"); | ||
// src/codegen/generateRouteParams.ts | ||
function generateRouteParams(node, isRaw) { | ||
return node.value.isParam() ? `{ ${node.value.params.map((param) => `${param.paramName}${param.optional ? "?" : ""}: ` + (param.modifier === "+" ? `_ParamValueOneOrMore<${isRaw}>` : param.modifier === "*" ? `_ParamValueZeroOrMore<${isRaw}>` : param.modifier === "?" ? `_ParamValueZeroOrOne<${isRaw}>` : `_ParamValue<${isRaw}>`)).join(", ")} }` : "Record<never, never>"; | ||
} | ||
// src/codegen/generateRouteMap.ts | ||
function generateRouteNamedMap(node) { | ||
if (node.isRoot()) { | ||
return `export interface RouteNamedMap { | ||
${Array.from(node.children.values()).map(generateRouteNamedMap).join("")}}`; | ||
} | ||
return (node.value.filePath ? ` '${node.value.routeName}': ${generateRouteRecordInfo(node)}, | ||
` : "") + (node.children.size > 0 ? Array.from(node.children.values()).map(generateRouteNamedMap).join("\n") : ""); | ||
} | ||
function generateRouteRecordInfo(node) { | ||
return `RouteRecordInfo<'${node.value.routeName}', '${node.value.path}', ${generateRouteParams(node, true)}, ${generateRouteParams(node, false)}>`; | ||
} | ||
// src/core/moduleConstants.ts | ||
var MODULE_VUE_ROUTER = "@vue-router"; | ||
var MODULE_ROUTES_PATH = `${MODULE_VUE_ROUTER}/routes`; | ||
var VIRTUAL_PREFIX = "virtual:"; | ||
// src/codegen/generateRouteRecords.ts | ||
function generateRouteRecord(node, indent = 0, parent = null) { | ||
if (node.value.path === "/" && indent === 0) { | ||
return `[ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 1)).join(",\n")} | ||
]`; | ||
} | ||
const startIndent = " ".repeat(indent * 2); | ||
const indentStr = " ".repeat((indent + 1) * 2); | ||
const name = node.value.routeName; | ||
return `${startIndent}{ | ||
${indentStr}path: "${(parent ? "" : "/") + node.value.pathSegment}", | ||
${indentStr}${node.value.filePath ? `name: "${name}",` : "/* no name */"} | ||
${indentStr}${node.value.filePath ? `component: () => import('${node.value.filePath}'),` : "/* no component */"} | ||
${indentStr}${node.children.size > 0 ? `children: [ | ||
${Array.from(node.children.values()).map((child) => generateRouteRecord(child, indent + 2, node)).join(",\n")} | ||
${indentStr}],` : "/* no children */"} | ||
${startIndent}}`; | ||
} | ||
// src/core/context.ts | ||
function createRoutesContext(options) { | ||
const { dts: preferDTS, root } = options; | ||
const dts = preferDTS === false ? false : preferDTS === true ? (0, import_path.resolve)(root, "typed-router.d.ts") : (0, import_path.resolve)(root, preferDTS); | ||
const routeTree = createPrefixTree(); | ||
const resolvedRoutesFolder = (0, import_path.resolve)(root, options.routesFolder); | ||
const serverWatcher = import_chokidar.default.watch(resolvedRoutesFolder, { | ||
disableGlobbing: true, | ||
ignorePermissionErrors: true | ||
}); | ||
function stripRouteFolder(path) { | ||
return path.slice(resolvedRoutesFolder.length + 1); | ||
} | ||
function setupWatcher() { | ||
serverWatcher.on("change", (path) => { | ||
console.log("change", path); | ||
writeConfigFiles(); | ||
}).on("add", (path) => { | ||
console.log("added", path); | ||
routeTree.insert(stripRouteFolder(path), (0, import_path.resolve)(root, path)); | ||
writeConfigFiles(); | ||
}).on("unlink", (path) => { | ||
console.log("remove", path); | ||
routeTree.remove(stripRouteFolder(path)); | ||
writeConfigFiles(); | ||
}); | ||
} | ||
function stop() { | ||
serverWatcher.close(); | ||
} | ||
function generateRoutes() { | ||
return `export const routes = ${generateRouteRecord(routeTree)}`; | ||
} | ||
function generateDTS() { | ||
return ` | ||
// Generated by unplugin-vue-router. \u203C\uFE0F DO NOT MODIFY THIS FILE \u203C\uFE0F | ||
// It's recommended to commit this file. | ||
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry. | ||
/// <reference types="unplugin-vue-router/client" /> | ||
import type { | ||
_RouterTyped, | ||
RouteRecordInfo, | ||
RouterLinkTyped, | ||
RouteLocationNormalizedLoadedTypedList, | ||
RouteLocationAsString, | ||
NavigationGuard, | ||
_ParamValue, | ||
_ParamValueOneOrMore, | ||
_ParamValueZeroOrMore, | ||
_ParamValueZeroOrOne, | ||
} from 'unplugin-vue-router' | ||
declare module '${MODULE_ROUTES_PATH}' { | ||
${generateRouteNamedMap(routeTree).split("\n").filter((line) => line).map((line) => " " + line).join("\n")} | ||
} | ||
declare module '${MODULE_VUE_ROUTER}' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export function useRoute<Name extends keyof RouteNamedMap = keyof RouteNamedMap>(name?: Name): RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
export type RouterTyped = _RouterTyped<RouteNamedMap> | ||
/** | ||
* Generate a type safe route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type Route<Name extends keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name] | ||
/** | ||
* Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParams<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['params'] | ||
/** | ||
* Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic. | ||
*/ | ||
export type RouteParamsRaw<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['paramsRaw'] | ||
export function useRouter(): RouterTyped | ||
export function onBeforeRouteLeave(guard: NavigationGuard<RouteMap>): void | ||
export function onBeforeRouteUpdate(guard: NavigationGuard<RouteMap>): void | ||
} | ||
declare module 'vue' { | ||
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' | ||
export interface GlobalComponents { | ||
RouterLink: RouterLinkTyped<RouteNamedMap> | ||
} | ||
} | ||
`; | ||
} | ||
let lastDTS; | ||
async function _writeConfigFiles() { | ||
console.log("writing"); | ||
logTree(routeTree); | ||
if (dts) { | ||
const content = generateDTS(); | ||
if (lastDTS !== content) { | ||
await import_fs.promises.writeFile(dts, content, "utf-8"); | ||
lastDTS = content; | ||
} | ||
} | ||
} | ||
const writeConfigFiles = throttle(_writeConfigFiles, 500); | ||
setupWatcher(); | ||
return { | ||
stop, | ||
writeConfigFiles, | ||
generateRoutes | ||
}; | ||
} | ||
// src/types.ts | ||
var import_local_pkg = require("local-pkg"); | ||
var DEFAULT_OPTIONS = { | ||
extensions: [".vue", ".js", ".jsx", ".ts", ".tsx"], | ||
exclude: [], | ||
routesFolder: "src/routes", | ||
importMode: "async", | ||
root: process.cwd(), | ||
dts: (0, import_local_pkg.isPackageExists)("typescript"), | ||
_inspect: false | ||
}; | ||
// src/index.ts | ||
var src_default = (0, import_unplugin.createUnplugin)((opt) => { | ||
const options = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), opt); | ||
const ctx = createRoutesContext(options); | ||
const root = process.cwd(); | ||
function getVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : null; | ||
} | ||
function asVirtualId(id) { | ||
if (options._inspect) | ||
return id; | ||
return VIRTUAL_PREFIX + id; | ||
} | ||
return { | ||
name: "unplugin-vue-router", | ||
enforce: "pre", | ||
resolveId(id) { | ||
if (id === MODULE_ROUTES_PATH) { | ||
return asVirtualId(id); | ||
} | ||
if (id === MODULE_VUE_ROUTER) { | ||
return "unplugin-vue-router/@vue-router/index.js"; | ||
} | ||
return null; | ||
}, | ||
buildStart() { | ||
}, | ||
load(id) { | ||
const resolvedId = getVirtualId(id); | ||
if (resolvedId === MODULE_ROUTES_PATH) { | ||
return ctx.generateRoutes(); | ||
} | ||
return null; | ||
} | ||
}; | ||
}); | ||
// src/webpack.ts | ||
@@ -41,0 +522,0 @@ var webpack_default = src_default.webpack; |
{ | ||
"name": "unplugin-vue-router", | ||
"version": "0.0.0-beta.0", | ||
"version": "0.0.1", | ||
"packageManager": "pnpm@7.1.7", | ||
@@ -54,28 +54,20 @@ "description": "Register global imports on demand for Vite and Webpack", | ||
"dist", | ||
"@vue-router", | ||
"*.d.ts" | ||
], | ||
"scripts": { | ||
"build": "tsup", | ||
"dev": "tsup --watch src", | ||
"build:fix": "esno scripts/postbuild.ts", | ||
"lint": "eslint .", | ||
"play": "npm -C playground run dev", | ||
"prepublishOnly": "npm run build", | ||
"release": "bumpp --commit --push --tag && pnpm publish", | ||
"start": "esno src/index.ts", | ||
"test": "vitest" | ||
}, | ||
"dependencies": { | ||
"chokidar": "^3.5.3", | ||
"local-pkg": "^0.4.1", | ||
"unplugin": "^0.7.0" | ||
}, | ||
"devDependencies": { | ||
"@antfu/eslint-config": "^0.25.1", | ||
"@types/node": "^17.0.34", | ||
"bumpp": "^8.1.0", | ||
"eslint": "^8.15.0", | ||
"esno": "^0.16.3", | ||
"fast-glob": "^3.2.11", | ||
"nodemon": "^2.0.16", | ||
"prettier": "^2.7.1", | ||
"rimraf": "^3.0.2", | ||
"rollup": "^2.74.0", | ||
"ts-expect": "^1.3.0", | ||
"tsup": "^6.1.2", | ||
@@ -85,4 +77,16 @@ "typescript": "^4.6.4", | ||
"vitest": "^0.15.1", | ||
"vue": "^3.2.37", | ||
"vue-router": "^4.0.16", | ||
"webpack": "^5.72.1" | ||
}, | ||
"scripts": { | ||
"build": "tsup", | ||
"dev": "tsup --watch src", | ||
"build:fix": "esno scripts/postbuild.ts", | ||
"lint": "prettier -c src/**/*.ts", | ||
"play": "npm -C playground run dev", | ||
"release": "bumpp --commit --push --tag && pnpm publish", | ||
"start": "esno src/index.ts", | ||
"test": "vitest" | ||
} | ||
} | ||
} |
# unplugin-vue-router | ||
[![NPM version](https://img.shields.io/npm/v/unplugin-vue-router?color=a1b858&label=)](https://www.npmjs.com/package/unplugin-vue-router) | ||
[![NPM version](https://img.shields.io/npm/v/unplugin-vue-router?color=black&label=)](https://www.npmjs.com/package/unplugin-vue-router) | ||
Starter template for [unplugin](https://github.com/unjs/unplugin). | ||
> Zero-config File based type safe Routing | ||
## Template Usage | ||
This build-time plugin simplifies your routing setup **and** makes it safer and easier to use thanks to TypeScript. | ||
To use this template, clone it down using: | ||
⚠️ This package is still experimental. If you found any issue, flow, or have ideas to improve it, please, [open an issue](https://github.com/posva/unplugin-vue-router/issues/new/choose). | ||
```bash | ||
npx degit antfu/unplugin-vue-router my-unplugin | ||
``` | ||
And do a global replace of `unplugin-vue-router` with your plugin name. | ||
Then you can start developing your unplugin 🔥 | ||
To test your plugin, run: `pnpm run dev` | ||
To release a new version, run: `pnpm run release` | ||
## Install | ||
@@ -33,7 +22,9 @@ | ||
// vite.config.ts | ||
import Starter from 'unplugin-vue-router/vite' | ||
import VueRouter from 'unplugin-vue-router/vite' | ||
export default defineConfig({ | ||
plugins: [ | ||
Starter({ /* options */ }), | ||
VueRouter({ | ||
/* options */ | ||
}), | ||
], | ||
@@ -52,7 +43,9 @@ }) | ||
// rollup.config.js | ||
import Starter from 'unplugin-vue-router/rollup' | ||
import VueRouter from 'unplugin-vue-router/rollup' | ||
export default { | ||
plugins: [ | ||
Starter({ /* options */ }), | ||
VueRouter({ | ||
/* options */ | ||
}), | ||
], | ||
@@ -64,3 +57,2 @@ } | ||
<details> | ||
@@ -74,4 +66,6 @@ <summary>Webpack</summary><br> | ||
plugins: [ | ||
require('unplugin-vue-router/webpack')({ /* options */ }) | ||
] | ||
require('unplugin-vue-router/webpack')({ | ||
/* options */ | ||
}), | ||
], | ||
} | ||
@@ -89,3 +83,8 @@ ``` | ||
buildModules: [ | ||
['unplugin-vue-router/nuxt', { /* options */ }], | ||
[ | ||
'unplugin-vue-router/nuxt', | ||
{ | ||
/* options */ | ||
}, | ||
], | ||
], | ||
@@ -107,3 +106,5 @@ } | ||
plugins: [ | ||
require('unplugin-vue-router/webpack')({ /* options */ }), | ||
require('unplugin-vue-router/webpack')({ | ||
/* options */ | ||
}), | ||
], | ||
@@ -115,1 +116,13 @@ }, | ||
<br></details> | ||
## Rationale | ||
This project idea came from trying to [type the router directly using Typescript](https://github.com/vuejs/router/pull/1397/commits/c577998f3edaa6a1eb9474c27ab6c58f6e2d7c8a), finding it out it's not fast enough to be pleasant to use and recurring to build-based tools, taking some Inspiration from other projects like: | ||
- [Nuxt](https://nuxtjs.org/) - The Vue.js Framework | ||
- [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages) - Framework agnostic file based routing | ||
- [Typed Router for Nuxt](https://github.com/victorgarciaesgi/nuxt-typed-router) - A module to add typed routing to Nuxt | ||
## License | ||
[MIT](http://opensource.org/licenses/MIT) |
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
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
119321
31
3245
122
3
16
6
1
+ Addedchokidar@^3.5.3
+ Addedlocal-pkg@^0.4.1
+ Addedlocal-pkg@0.4.3(transitive)