Skip to content

Commit b8a9473

Browse files
committed
fix: correctly trim extensions from routesFolder
fix #274
1 parent c67179c commit b8a9473

File tree

4 files changed

+79
-8
lines changed

4 files changed

+79
-8
lines changed

src/codegen/generateRouteMap.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
22
import { generateRouteNamedMap } from './generateRouteMap'
33
import { PrefixTree } from '../core/tree'
44
import { resolveOptions } from '../options'
5+
import { resolve } from 'node:path'
56

67
const DEFAULT_OPTIONS = resolveOptions({})
78

@@ -156,6 +157,31 @@ describe('generateRouteNamedMap', () => {
156157
}"
157158
`)
158159
})
160+
161+
// https://github.com/posva/unplugin-vue-router/issues/274
162+
it('removes routeFolders.extensions from the path', () => {
163+
const tree = new PrefixTree(
164+
resolveOptions({
165+
extensions: ['.pagina.vue'],
166+
routesFolder: [
167+
{ src: 'src/pages', extensions: ['.page.vue'] },
168+
{ src: 'src/paginas' },
169+
],
170+
})
171+
)
172+
173+
tree.insert('other.pagina.vue', resolve('src/paginas/other.pagina.vue'))
174+
tree.insert('index.page.vue', resolve('src/pages/index.page.vue'))
175+
tree.insert('about.page.vue', resolve('src/pages/about.page.vue'))
176+
177+
expect(formatExports(generateRouteNamedMap(tree))).toMatchInlineSnapshot(`
178+
"export interface RouteNamedMap {
179+
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
180+
'/about': RouteRecordInfo<'/about', '/about', Record<never, never>, Record<never, never>>,
181+
'/other': RouteRecordInfo<'/other', '/other', Record<never, never>, Record<never, never>>,
182+
}"
183+
`)
184+
})
159185
})
160186

161187
/**

src/core/context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ResolvedOptions } from '../options'
1+
import { ResolvedOptions, RoutesFolderOption } from '../options'
22
import { TreeNode, PrefixTree } from './tree'
33
import { promises as fs } from 'fs'
44
import { asRoutePath, ImportsMap, logTree, throttle } from './utils'

src/core/tree.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { ResolvedOptions } from '../options'
1+
import type { ResolvedOptions, RoutesFolderOption } from '../options'
22
import {
33
createTreeNodeValue,
44
TreeNodeValueOptions,
55
TreeRouteParam,
66
} from './treeNodeValue'
77
import type { TreeNodeValue } from './treeNodeValue'
8-
import { trimExtension } from './utils'
8+
import { resolveOverridableOption, trimExtension } from './utils'
99
import { CustomRouteBlock } from './customBlock'
1010
import { RouteMeta } from 'vue-router'
1111

@@ -69,9 +69,16 @@ export class TreeNode {
6969
* @param filePath - file path, defaults to path for convenience and testing
7070
*/
7171
insert(path: string, filePath: string = path): TreeNode {
72+
// find the `routesFolder` resolved option that matches the filepath
73+
const folderOptions = findFolderOptions(this.options.routesFolder, filePath)
74+
7275
const { tail, segment, viewName, isComponent } = splitFilePath(
7376
path,
74-
this.options
77+
// use the correct extensions for the folder
78+
resolveOverridableOption(
79+
this.options.extensions,
80+
folderOptions?.extensions
81+
)
7582
)
7683

7784
if (!this.children.has(segment)) {
@@ -161,10 +168,14 @@ export class TreeNode {
161168
* @param path - path segment of the file
162169
*/
163170
remove(path: string) {
171+
const folderOptions = findFolderOptions(this.options.routesFolder, path)
164172
// TODO: rename remove to removeChild
165173
const { tail, segment, viewName, isComponent } = splitFilePath(
166174
path,
167-
this.options
175+
resolveOverridableOption(
176+
this.options.extensions,
177+
folderOptions?.extensions
178+
)
168179
)
169180

170181
const child = this.children.get(segment)
@@ -319,15 +330,15 @@ export class PrefixTree extends TreeNode {
319330
*
320331
* @param filePath - filePath to split
321332
*/
322-
function splitFilePath(filePath: string, options: ResolvedOptions) {
333+
function splitFilePath(filePath: string, extensions: string[]) {
323334
const slashPos = filePath.indexOf('/')
324335
let head = slashPos < 0 ? filePath : filePath.slice(0, slashPos)
325336
const tail = slashPos < 0 ? '' : filePath.slice(slashPos + 1)
326337

327338
let segment = head
328339
// only the last segment can be a filename with an extension
329340
if (!tail) {
330-
segment = trimExtension(head, options.extensions)
341+
segment = trimExtension(head, extensions)
331342
}
332343
let viewName = 'default'
333344

@@ -348,3 +359,17 @@ function splitFilePath(filePath: string, options: ResolvedOptions) {
348359
isComponent,
349360
}
350361
}
362+
363+
/**
364+
* Find the folder options that match the file path.
365+
*
366+
* @param folderOptions `options.routesFolder` option
367+
* @param filePath resolved file path
368+
* @returns
369+
*/
370+
function findFolderOptions(
371+
folderOptions: RoutesFolderOption[],
372+
filePath: string
373+
): RoutesFolderOption | undefined {
374+
return folderOptions.find((folder) => filePath.includes(folder.src))
375+
}

src/core/utils.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { TreeNode } from './tree'
22
import type { RouteRecordOverride, TreeRouteParam } from './treeNodeValue'
33
import { pascalCase } from 'scule'
4-
import { ResolvedOptions, RoutesFolderOption } from '../options'
4+
import {
5+
ResolvedOptions,
6+
RoutesFolderOption,
7+
_OverridableOption,
8+
} from '../options'
59

610
export function warn(
711
msg: string,
@@ -77,6 +81,22 @@ export function trimExtension(
7781
return path
7882
}
7983

84+
/**
85+
* Resolves an overridable option by calling the function with the existing value if it's a function, otherwise
86+
* returning the passed `value`. If `value` is undefined, it returns the `defaultValue` instead.
87+
*
88+
* @param defaultValue default value for the option
89+
* @param value and overridable option
90+
*/
91+
export function resolveOverridableOption<T>(
92+
defaultValue: T,
93+
value?: _OverridableOption<T>
94+
): T {
95+
return typeof value === 'function'
96+
? (value as (existing: T) => T)(defaultValue)
97+
: value ?? defaultValue
98+
}
99+
80100
export function throttle(fn: () => void, wait: number, initialWait: number) {
81101
let pendingExecutionTimeout: ReturnType<typeof setTimeout> | null = null
82102
let pendingExecution = false

0 commit comments

Comments
 (0)