-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.js
181 lines (154 loc) · 5.94 KB
/
setup.js
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
* This script performs various tasks that setup the template.
*
* It replaces instances of placeholder words, runs `npm i`, etc.
*
* To run this script, run `node setup.js` from the package template root directory.
*/
const { exec } = require('child_process')
const fs = require('fs')
const path = require('path')
const readline = require('readline')
const FILES_WITH_TOKENS = [
'./README.md',
'./package.json',
'./LICENSE',
'./src/types.ts',
'./src/index.ts',
'./src/npm-package-name/index.ts',
'./src/npm-package-name/types.ts',
'./src/npm-package-name/index.spec.ts',
'./examples/index.ts',
]
const r1 = readline.createInterface({ input: process.stdin, output: process.stdout })
const dashToCamel = dashCase => (
dashCase.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase())
)
const removeDirectoryRecursively = dirPath => {
const files = fs.readdirSync(dirPath)
files.forEach(file => {
const filePath = path.join(dirPath, file)
if (fs.statSync(filePath).isDirectory())
removeDirectoryRecursively(filePath)
else
fs.unlinkSync(filePath)
})
fs.rmdirSync(dirPath)
}
const VALIDATORS = {
isNonEmptyString: { op: s => s != null && s.length > 0, errMsg: 'Cannot be empty' },
hasNoSpaces: { op: s => s.indexOf(' ') === -1, errMsg: 'Cannot have whitespace' },
isValidDashCaseName: { op: s => /^[a-zA-Z0-9\-@]+$/.test(s), errMsg: 'Must contain only letters, numbers, \'-\' and \'@\'' },
}
const _getInput = (question, validators, defaultIfEmpty, onComplete) => {
const _question = defaultIfEmpty != null ? `${question} [${defaultIfEmpty}]: ` : `${question}: `
r1.question(_question, name => {
const isEmpty = !VALIDATORS.isNonEmptyString.op(name)
if (isEmpty && defaultIfEmpty != null) {
onComplete(defaultIfEmpty)
}
else if (validators != null) {
const errMsgList = validators.reduce((acc, validator) => (validator.op(name) ? acc : acc.concat(validator.errMsg)), [])
if (errMsgList.length === 0) {
onComplete(name)
}
else {
console.log('Error:', errMsgList.join(', '))
_getInput(question, validators, defaultIfEmpty, onComplete)
}
}
else {
onComplete(name)
}
})
}
const getInput = (question, validators, defaultIfEmpty) => new Promise(res => {
_getInput(question, validators, defaultIfEmpty, res)
})
const getToken = async (name, validators, defaultIfEmpty) => {
const value = await getInput(name, validators, defaultIfEmpty)
return { name, value }
}
const _replaceTokensInFiles = (filePaths, tokenMapEntries, i, onComplete) => {
if (i >= filePaths.length) {
onComplete()
return
}
const filePath = filePaths[i]
console.log(`--> ${filePath}`)
const fileText = fs.readFileSync(filePath, 'utf8')
let newFileText = fileText
tokenMapEntries.forEach(tokenMapEntry => {
newFileText = newFileText.replace(new RegExp(tokenMapEntry[0], 'g'), tokenMapEntry[1])
})
fs.writeFileSync(filePath, newFileText)
_replaceTokensInFiles(filePaths, tokenMapEntries, i + 1, onComplete)
}
const replaceTokensInFiles = (filePaths, tokenMap) => new Promise(res => {
console.log('\n==> Replacing placeholder words in files...')
const tokenMapEntries = Object.entries(tokenMap)
_replaceTokensInFiles(filePaths, tokenMapEntries, 0, res)
})
const npmInstall = () => new Promise((res, rej) => {
console.log('==> Installing npm dependencies...')
exec('npm i', err => {
if (err != null) {
console.log(err)
rej(err)
}
else {
res()
}
})
})
const renameTokensInFileNames = (filePaths, tokenMap) => {
console.log('\n==> Replacing placeholder words in file names...')
const tokenMapEntries = Object.entries(tokenMap)
filePaths.forEach(filePath => {
let newFilePath = filePath
tokenMapEntries.forEach(tokenMapEntry => {
newFilePath = newFilePath.replace(new RegExp(tokenMapEntry[0], 'g'), tokenMapEntry[1])
})
if (!fs.existsSync(path.dirname(newFilePath)))
fs.mkdirSync(path.dirname(newFilePath), { recursive: true })
fs.renameSync(path.resolve(filePath), path.resolve(newFilePath))
})
}
const createTokenMap = tokens => {
const map = {}
tokens.forEach(token => {
map[`{{${token.name}}}`] = token.value
map[token.name] = token.value
})
return map
}
// ------------------------
// -- Token collectors
// ------------------------
const getRepoName = () => getToken('repo-name', [VALIDATORS.isValidDashCaseName], 'repo-name')
const getNpmPackageName = defaultValue => getToken('npm-package-name', [VALIDATORS.isValidDashCaseName], defaultValue)
const getLicenseName = () => getToken('license-name', [], 'Joe Bloggs')
const getLicenseEmail = () => getToken('license-email', [VALIDATORS.hasNoSpaces], '[email protected]')
const getGithubUserName = () => getToken('github-user-name', [VALIDATORS.hasNoSpaces], 'joebloggs')
const getPackageSlogan = () => getToken('package-slogan', [], 'Delightful Typescript Package')
const main = async () => {
// -- Get token map
const repoName = await getRepoName()
const npmPackageName = await getNpmPackageName(repoName.value)
const camelCaseNpmPackageName = { name: 'npmPackageName', value: dashToCamel(npmPackageName.value) }
const licenseName = await getLicenseName()
const licenseEmail = await getLicenseEmail()
const githubUserName = await getGithubUserName()
const packageSlogan = await getPackageSlogan()
r1.close()
const tokenMap = createTokenMap([repoName, npmPackageName, camelCaseNpmPackageName, licenseName, licenseEmail, githubUserName, packageSlogan])
// Replace tokens and install NPM dependencies
await replaceTokensInFiles(FILES_WITH_TOKENS, tokenMap)
renameTokensInFileNames(FILES_WITH_TOKENS, tokenMap)
removeDirectoryRecursively('./src/npm-package-name/')
await npmInstall()
console.log('\n------------------------------\n\nSetup complete! You may delete this script now.')
console.log('Try running `npm run unit-tests` and `npm run check`.')
console.log('\nHappy hacking! :)')
}
main()