-
-
Notifications
You must be signed in to change notification settings - Fork 48
/
scan.ts
116 lines (96 loc) · 4.25 KB
/
scan.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { basename, extname, join, dirname, relative } from 'upath'
import globby from 'globby'
import { pascalCase, splitByCase } from 'scule'
import type { ScanDir, Component } from './types'
export function sortDirsByPathLength ({ path: pathA }: ScanDir, { path: pathB }: ScanDir): number {
return pathB.split(/[\\/]/).filter(Boolean).length - pathA.split(/[\\/]/).filter(Boolean).length
}
// vue@2 src/shared/util.js
function hyphenate (str: string):string {
return str.replace(/\B([A-Z])/g, '-$1').toLowerCase()
}
export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise<Component[]> {
const components: Component[] = []
const filePaths = new Set<string>()
const scannedPaths: string[] = []
for (const { path, pattern, ignore = [], prefix, extendComponent, pathPrefix, level, prefetch = false, preload = false, isAsync: dirIsAsync } of dirs.sort(sortDirsByPathLength)) {
const resolvedNames = new Map<string, string>()
for (const _file of await globby(pattern!, { cwd: path, ignore })) {
const filePath = join(path, _file)
if (scannedPaths.find(d => filePath.startsWith(d))) {
continue
}
if (filePaths.has(filePath)) { continue }
filePaths.add(filePath)
// Resolve componentName
const prefixParts = ([] as string[]).concat(
prefix ? splitByCase(prefix) : [],
(pathPrefix !== false) ? splitByCase(relative(path, dirname(filePath))) : []
)
let fileName = basename(filePath, extname(filePath))
if (fileName.toLowerCase() === 'index') {
fileName = pathPrefix === false ? basename(dirname(filePath)) : '' /* inherits from path */
}
const isAsync = (fileName.endsWith('.async') ? true : dirIsAsync) || null
fileName = fileName.replace(/\.async$/, '')
const fileNameParts = splitByCase(fileName)
const componentNameParts: string[] = []
while (prefixParts.length &&
(prefixParts[0] || '').toLowerCase() !== (fileNameParts[0] || '').toLowerCase()
) {
componentNameParts.push(prefixParts.shift()!)
}
const componentName = pascalCase(componentNameParts).replace(/^\d+/, '') +
pascalCase(fileNameParts).replace(/^\d+/, '')
if (resolvedNames.has(componentName)) {
// eslint-disable-next-line no-console
console.warn(`Two component files resolving to the same name \`${componentName}\`:\n` +
`\n - ${filePath}` +
`\n - ${resolvedNames.get(componentName)}`
)
continue
}
resolvedNames.set(componentName, filePath)
const pascalName = pascalCase(componentName)
const kebabName = hyphenate(componentName)
const shortPath = relative(srcDir, filePath)
const chunkName = 'components/' + kebabName
let component: Component = {
filePath,
pascalName,
kebabName,
chunkName,
shortPath,
isAsync,
import: '',
asyncImport: '',
export: 'default',
global: Boolean(global),
level: Number(level),
prefetch: Boolean(prefetch),
preload: Boolean(preload)
}
if (typeof extendComponent === 'function') {
component = (await extendComponent(component)) || component
}
component.import = component.import || `require('${component.filePath}').${component.export}`
component.asyncImport = component.asyncImport || `function () { return import('${component.filePath}' /* webpackChunkName: "${component.chunkName}" */).then(function(m) { return m['${component.export}'] || m }) }`
// Check if component is already defined, used to overwite if level is inferiour
const definedComponent = components.find(c => c.pascalName === component.pascalName)
if (definedComponent && component.level < definedComponent.level) {
Object.assign(definedComponent, component)
} else if (!definedComponent) {
components.push(component)
}
}
scannedPaths.push(path)
}
return components
}
export function matcher (tags: string[], components: Component[]) {
return tags.reduce((matches, tag) => {
const match = components.find(({ pascalName, kebabName }) => [pascalName, kebabName].includes(tag))
match && matches.push(match)
return matches
}, [] as Component[])
}