Skip to content

Commit 03abbde

Browse files
committed
feat: handle long extensions
Close unplugin#820
1 parent 2efdaac commit 03abbde

File tree

4 files changed

+34
-14
lines changed

4 files changed

+34
-14
lines changed

src/core/options.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,30 @@ function resolveGlobsExclude(root: string, glob: string) {
3333
export function resolveOptions(options: Options, root: string): ResolvedOptions {
3434
const resolved = Object.assign({}, defaultOptions, options) as ResolvedOptions
3535
resolved.resolvers = normalizeResolvers(resolved.resolvers)
36-
resolved.extensions = toArray(resolved.extensions)
36+
resolved.resolvedExtensions = toArray(resolved.extensions)
37+
.map(i => i.startsWith('.') ? i : `.${i}`)
38+
// sort extensions by length to ensure that the longest one is used first
39+
// e.g. ['.vue', '.page.vue'] -> ['.page.vue', '.vue'] as both would match and order matters
40+
.sort((a, b) => b.length - a.length)
3741

3842
if (resolved.globs) {
3943
resolved.globs = toArray(resolved.globs).map((glob: string) => slash(resolveGlobsExclude(root, glob)))
4044
resolved.resolvedDirs = []
4145
}
4246
else {
43-
const extsGlob = resolved.extensions.length === 1
44-
? resolved.extensions
45-
: `{${resolved.extensions.join(',')}}`
47+
const extsGlob = resolved.resolvedExtensions.length === 1
48+
? resolved.resolvedExtensions
49+
: `{${resolved.resolvedExtensions.join(',')}}`
4650

4751
resolved.dirs = toArray(resolved.dirs)
4852
resolved.resolvedDirs = resolved.dirs.map(i => slash(resolveGlobsExclude(root, i)))
4953

5054
resolved.globs = resolved.resolvedDirs.map(i => resolved.deep
51-
? slash(join(i, `**/*.${extsGlob}`))
52-
: slash(join(i, `*.${extsGlob}`)),
55+
? slash(join(i, `**/*${extsGlob}`))
56+
: slash(join(i, `*${extsGlob}`)),
5357
)
5458

55-
if (!resolved.extensions.length)
59+
if (!resolved.resolvedExtensions.length)
5660
throw new Error('[unplugin-vue-components] `extensions` option is required to search for components')
5761
}
5862

src/core/utils.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { FilterPattern } from '@rollup/pluginutils'
22
import type { ComponentInfo, ImportInfo, ImportInfoLegacy, Options, ResolvedOptions } from '../types'
33
import type { Context } from './context'
4-
import { parse } from 'node:path'
4+
import { basename, parse } from 'node:path'
55
import process from 'node:process'
66
import { slash, toArray } from '@antfu/utils'
77
import {
@@ -114,7 +114,7 @@ export function stringifyComponentImport({ as: name, from: path, name: importNam
114114
}
115115

116116
export function getNameFromFilePath(filePath: string, options: ResolvedOptions): string {
117-
const { resolvedDirs, directoryAsNamespace, globalNamespaces, collapseSamePrefixes, root } = options
117+
const { resolvedDirs, directoryAsNamespace, globalNamespaces, collapseSamePrefixes, root, resolvedExtensions } = options
118118

119119
const parsedFilePath = parse(slash(filePath))
120120

@@ -129,11 +129,14 @@ export function getNameFromFilePath(filePath: string, options: ResolvedOptions):
129129
}
130130

131131
let folders = strippedPath.slice(1).split('/').filter(Boolean)
132-
let filename = parsedFilePath.name
132+
// when using `globs` option, `resolvedDirs` will always empty, and ignoring extensions is the expected behavior
133+
let filename = isEmpty(resolvedDirs)
134+
? parsedFilePath.name
135+
: basename(parsedFilePath.base, resolvedExtensions?.find(ext => parsedFilePath.base.endsWith(ext)))
133136

134137
// set parent directory as filename if it is index
135138
if (filename === 'index' && !directoryAsNamespace) {
136-
// when use `globs` option, `resolvedDirs` will always empty, and `folders` will also empty
139+
// when using `globs` option, `resolvedDirs` will always empty, and `folders` will also empty
137140
if (isEmpty(folders))
138141
folders = parsedFilePath.dir.slice(root.length + 1).split('/').filter(Boolean)
139142

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export type ResolvedOptions = Omit<
189189
> & {
190190
resolvers: ComponentResolverObject[]
191191
extensions: string[]
192+
resolvedExtensions: string[]
192193
dirs: string[]
193194
resolvedDirs: string[]
194195
globs: string[]

test/utils.test.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import type { ResolvedOptions } from '../src'
22
import { describe, expect, it } from 'vitest'
3+
import { resolveOptions } from '../src/core/options'
34
import { getNameFromFilePath } from '../src/core/utils'
45

56
describe('getNameFromFilePath', () => {
6-
const options: Partial<ResolvedOptions> = {
7+
const options: Partial<ResolvedOptions> = resolveOptions({
78
directoryAsNamespace: true,
89
globalNamespaces: [],
910
collapseSamePrefixes: false,
10-
resolvedDirs: ['/src/components'],
11-
}
11+
dirs: ['/src/components'],
12+
extensions: ['vue', 'ce.vue'],
13+
}, '/')
1214

1315
it('normal name', () => {
1416
const inComponentFilePath = '/src/components/a/b.vue'
@@ -19,4 +21,14 @@ describe('getNameFromFilePath', () => {
1921
const inComponentFilePath = '/src/components/[a1]/b_2/c 3/d.4/[...ef]/ghi.vue'
2022
expect(getNameFromFilePath(inComponentFilePath, options as ResolvedOptions)).toBe('a1-b2-c3-d4-ef-ghi')
2123
})
24+
25+
it(('long extensions'), () => {
26+
const inComponentFilePath = '/src/components/b.ce.vue'
27+
expect(getNameFromFilePath(inComponentFilePath, options as ResolvedOptions)).toBe('b')
28+
})
29+
30+
it(('long extensions and nested'), () => {
31+
const inComponentFilePath = '/src/components/a/b.ce.vue'
32+
expect(getNameFromFilePath(inComponentFilePath, options as ResolvedOptions)).toBe('a-b')
33+
})
2234
})

0 commit comments

Comments
 (0)