-
Notifications
You must be signed in to change notification settings - Fork 0
/
parse.ts
116 lines (97 loc) · 3.85 KB
/
parse.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 { existsSync } from 'node:fs'
import { join } from 'node:path'
import { it } from 'avait'
import { merge } from 'ts-deepmerge'
import type { Configuration, File, Option, Options } from './types'
const isExtension = async (value: string) => {
// NOTE dynamic import in tests will resolve relative to project node_modules and not fixture.
const { error } = await it(import(value))
if (!error) return true
const paths = [value, join(process.cwd(), value), join(process.cwd(), 'node_modules', value)]
const fileExists = paths.some(existsSync)
if (fileExists) return true
return false
}
function extendTemplate(option: Option, configuration: Configuration['configuration']) {
if (
typeof option !== 'object' ||
!(typeof option.extends === 'string' && configuration.templates && Object.hasOwn(configuration.templates, option.extends))
) {
if (typeof option === 'object' && Object.hasOwn(option, 'folder')) {
option.folder = undefined
}
return option
}
let template = configuration.templates[option.extends]
if (typeof template === 'function') {
template = template(option)
}
if (typeof template === 'string') {
option.extends = template
}
if (typeof template === 'object') {
const optionWithoutPluginProperties = Object.fromEntries(Object.entries(option).filter(([key]) => !['extends', 'folder'].includes(key)))
return merge(template, optionWithoutPluginProperties)
}
return option
}
function addFolderToFile(file: File | undefined, folder?: string | false) {
if (file?.name && folder) {
file.name = join(folder, file.name)
}
return file
}
async function getFiles(option: Option, configuration: Configuration['configuration']) {
// Template.
if (typeof option === 'string' && configuration.templates && Object.hasOwn(configuration.templates, option)) {
const template = configuration.templates[option as keyof typeof configuration.templates]
const configurationTemplate = typeof template === 'function' ? template(option) : template
return configuration.createFile(configurationTemplate)
}
if (typeof option === 'string' && (await isExtension(option)) && typeof configuration.extension === 'function') {
// File extension.
return configuration.createFile(configuration.extension(option))
}
if (option === true) {
return configuration.createFile(configuration.templates?.recommended)
}
return configuration.createFile(extendTemplate(option, configuration))
}
async function parseOption(option: Option, configuration: Configuration['configuration']) {
const folder = typeof option === 'object' && option.folder
let files: File | (File | undefined)[] | undefined = await getFiles(option, configuration)
if (Array.isArray(files)) {
files = files.map((file) => {
return addFolderToFile(file, folder)
})
} else if (typeof files === 'object') {
files = addFolderToFile(files, folder)
}
if (folder) {
for (const file of Array.isArray(files) ? files : [files]) {
if (file) {
file.folder = true
}
}
}
return files
}
const unnestFileArray = (values: (File | (File | undefined)[] | undefined)[]): File[] => {
if (values.length === 1 && !values[0]) {
return []
}
return values.reduce((result, value) => {
if (value === undefined) return result
if (Array.isArray(value)) {
const nestedClean = value.filter((innerValue): innerValue is File => innerValue !== undefined)
return (result as File[]).concat(nestedClean)
}
return (result as File[]).concat(value)
}, []) as File[]
}
export async function parse(options: Options, configuration: Configuration['configuration']): Promise<File[]> {
if (!Array.isArray(options)) {
return unnestFileArray([await parseOption(options, configuration)])
}
return unnestFileArray(await Promise.all(options.map((option) => parseOption(option, configuration))))
}