-
Notifications
You must be signed in to change notification settings - Fork 473
/
Copy pathrenderTemplate.ts
97 lines (81 loc) · 3.21 KB
/
renderTemplate.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
import * as fs from 'node:fs'
import * as path from 'node:path'
import { pathToFileURL } from 'node:url'
import deepMerge from './deepMerge'
import sortDependencies from './sortDependencies'
/**
* Renders a template folder/file to the file system,
* by recursively copying all files under the `src` directory,
* with the following exception:
* - `_filename` should be renamed to `.filename`
* - Fields in `package.json` should be recursively merged
* @param {string} src source filename to copy
* @param {string} dest destination filename of the copy operation
*/
function renderTemplate(src, dest, callbacks) {
const stats = fs.statSync(src)
if (stats.isDirectory()) {
// skip node_module
if (path.basename(src) === 'node_modules') {
return
}
// if it's a directory, render its subdirectories and files recursively
fs.mkdirSync(dest, { recursive: true })
for (const file of fs.readdirSync(src)) {
renderTemplate(path.resolve(src, file), path.resolve(dest, file), callbacks)
}
return
}
const filename = path.basename(src)
if (filename === 'package.json' && fs.existsSync(dest)) {
// merge instead of overwriting
const existing = JSON.parse(fs.readFileSync(dest, 'utf8'))
const newPackage = JSON.parse(fs.readFileSync(src, 'utf8'))
const pkg = sortDependencies(deepMerge(existing, newPackage))
fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) + '\n')
return
}
if (filename === 'extensions.json' && fs.existsSync(dest)) {
// merge instead of overwriting
const existing = JSON.parse(fs.readFileSync(dest, 'utf8'))
const newExtensions = JSON.parse(fs.readFileSync(src, 'utf8'))
const extensions = deepMerge(existing, newExtensions)
fs.writeFileSync(dest, JSON.stringify(extensions, null, 2) + '\n')
return
}
if (filename === 'settings.json' && fs.existsSync(dest)) {
// merge instead of overwriting
const existing = JSON.parse(fs.readFileSync(dest, 'utf8'))
const newSettings = JSON.parse(fs.readFileSync(src, 'utf8'))
const settings = deepMerge(existing, newSettings)
fs.writeFileSync(dest, JSON.stringify(settings, null, 2) + '\n')
return
}
if (filename.startsWith('_')) {
// rename `_file` to `.file`
dest = path.resolve(path.dirname(dest), filename.replace(/^_/, '.'))
}
if (filename === '_gitignore' && fs.existsSync(dest)) {
// append to existing .gitignore
const existing = fs.readFileSync(dest, 'utf8')
const newGitignore = fs.readFileSync(src, 'utf8')
fs.writeFileSync(dest, existing + '\n' + newGitignore)
return
}
// data file for EJS templates
if (filename.endsWith('.data.mjs')) {
// use dest path as key for the data store
dest = dest.replace(/\.data\.mjs$/, '')
// Add a callback to the array for late usage when template files are being processed
callbacks.push(async (dataStore) => {
const getData = (await import(pathToFileURL(src).toString())).default
// Though current `getData` are all sync, we still retain the possibility of async
dataStore[dest] = await getData({
oldData: dataStore[dest] || {},
})
})
return // skip copying the data file
}
fs.copyFileSync(src, dest)
}
export default renderTemplate