jiek 2.0.1 → 2.0.2-alpha.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,373 +0,0 @@
1
- import fs from 'node:fs'
2
- import path from 'node:path'
3
-
4
- import { program } from 'commander'
5
- import detectIndent from 'detect-indent'
6
- import inquirer from 'inquirer'
7
- import type { Config, InitNamed } from 'jiek'
8
- import { applyEdits, modify } from 'jsonc-parser'
9
- import { isMatch } from 'micromatch'
10
-
11
- import { getWD } from '../utils/getWD'
12
- import { loadConfig } from '../utils/loadConfig'
13
-
14
- declare module 'jiek' {
15
- export type InitNamedFunction = (
16
- argument: string,
17
- paths: {
18
- full: string
19
- relative: string
20
- basename?: string
21
- }
22
- ) => [name?: string, path?: string]
23
- export type InitNamed =
24
- | InitNamedFunction
25
- | {
26
- [key: string]: string | InitNamedFunction
27
- }
28
- export interface Config {
29
- init?: {
30
- /**
31
- * the package.json template file path or file content
32
- *
33
- * if it can be parsed as json, it will be parsed
34
- * if it is a relative file path, it will be resolved to an absolute path based on the current working directory
35
- * if it is an absolute file path, it will be used directly
36
- * @default '.jiek.template.package.json'
37
- */
38
- template?: string
39
- /**
40
- * the readme content
41
- *
42
- * $name will be replaced with the package name
43
- * $license will be replaced with the license
44
- */
45
- readme?:
46
- | string
47
- | ((ctx: {
48
- dir: string
49
- packageJson: Record<string, any>
50
- }) => string)
51
- /**
52
- * the readme template file path
53
- * @default '.jiek.template.readme.md'
54
- */
55
- readmeTemplate?: string
56
- bug?: {
57
- /**
58
- * @default 'bug_report.yml'
59
- */
60
- template?: string
61
- /**
62
- * @default ['bug']
63
- */
64
- labels?:
65
- | string[]
66
- | ((ctx: {
67
- name: string
68
- dir: string
69
- }) => string[])
70
- }
71
- named?: InitNamed
72
- }
73
- }
74
- }
75
-
76
- const PACKAGE_JSON_TEMPLATE = `{
77
- "name": "",
78
- "version": "0.0.1",
79
- "description": "",
80
- "license": "",
81
- "author": "",
82
- "files": ["dist"],
83
- "exports": {
84
- ".": "./src/index.ts"
85
- },
86
- "scripts": {
87
- },
88
- "homepage": "",
89
- "repository": "",
90
- "bugs": ""
91
- }`.trimStart()
92
- const README_TEMPLATE = `# $name
93
-
94
- ## Installation
95
-
96
- \`\`\`bash
97
- npm install $name
98
- # or
99
- pnpm install $name
100
- # or
101
- yarn add $name
102
- \`\`\`
103
-
104
- ## Usage
105
-
106
-
107
- ## License
108
-
109
- $license
110
- `.trimStart()
111
-
112
- function getTemplateStr(wd: string, template: string | undefined) {
113
- let templateString = template ?? PACKAGE_JSON_TEMPLATE
114
- let isTemplateFile = false
115
- try {
116
- if (template) JSON.parse(template)
117
- } catch (e) {
118
- isTemplateFile = true
119
- }
120
- if (isTemplateFile) {
121
- const templatePath = path.resolve(wd, template!)
122
- templateString = fs.readFileSync(templatePath, 'utf-8')
123
- }
124
- return templateString
125
- }
126
- const wdCache = new Map<string, Record<string, any>>()
127
- function getWDPackageJSONFiled(wd: string, field: string) {
128
- if (wdCache.has(wd)) {
129
- return wdCache.get(wd)![field]
130
- }
131
- const packageJSONPath = path.resolve(wd, 'package.json')
132
- const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, 'utf-8'))
133
- wdCache.set(wd, packageJSON)
134
- return packageJSON[field]
135
- }
136
- async function getName(
137
- named: InitNamed | undefined,
138
- name: string,
139
- {
140
- wd,
141
- cwd,
142
- workspaceName
143
- }: {
144
- wd: string
145
- cwd: string
146
- workspaceName: string
147
- }
148
- ): Promise<[name?: string, path?: string]> {
149
- const relativePath = cwd.replace(`${wd}/`, '')
150
- let basename = path.basename(cwd)
151
-
152
- if (typeof named === 'function') {
153
- return named(name, {
154
- full: wd,
155
- relative: cwd
156
- })
157
- }
158
-
159
- let isParentMatched = false
160
- let matchedKey: string | undefined
161
- let matchedRule: NonNullable<typeof named>[string] | undefined
162
- if (typeof named === 'object') {
163
- const isWD = cwd === wd
164
- if (isWD) {
165
- const { rule } = await inquirer.prompt<{ rule: string }>({
166
- type: 'list',
167
- name: 'rule',
168
- message: 'choose a rule',
169
- default: 'default',
170
- choices: ['default'].concat(Object.keys(named))
171
- })
172
- if (rule !== 'default') {
173
- matchedKey = rule
174
- matchedRule = named[rule]
175
- }
176
- } else {
177
- for (const [key, value] of Object.entries(named)) {
178
- if (isMatch(relativePath, key)) {
179
- matchedKey = key
180
- matchedRule = value
181
- break
182
- }
183
- if (isMatch(`${relativePath}/jiek_ignore_dont_use_same_file_name`, key)) {
184
- isParentMatched = true
185
- matchedKey = key
186
- matchedRule = value
187
- break
188
- }
189
- }
190
- }
191
- }
192
- if (!matchedRule) {
193
- matchedKey = 'packages/*'
194
- matchedRule = `@${workspaceName}/$basename`
195
- }
196
- if (!matchedRule) {
197
- throw new Error('no matched rule')
198
- }
199
- if (!name && isParentMatched) {
200
- basename = await inquirer.prompt<{ name: string }>({
201
- type: 'input',
202
- name: 'name',
203
- message: `the matched rule is \`${String(matchedRule)}\`, please input the basename\n`
204
- }).then(({ name }) => name)
205
- }
206
-
207
- if (typeof matchedRule === 'function') {
208
- return matchedRule(name, {
209
- full: wd,
210
- relative: cwd,
211
- basename: basename
212
- })
213
- }
214
- if (typeof matchedRule === 'string') {
215
- const dirName = name ?? basename
216
- return [
217
- matchedRule.replace(/\$basename/g, dirName),
218
- matchedKey?.replace(/\/\*$/g, `/${dirName}`)
219
- ]
220
- }
221
- throw new Error('no matched rule')
222
- }
223
-
224
- program
225
- .command('init [name]')
226
- .option('-t, --template <template>', 'the package.json template file path or file content')
227
- .action(async () => {
228
- const [, name] = program.args
229
- const cwd = process.cwd()
230
- const { init = {} }: Config = loadConfig() ?? {}
231
- const { wd } = getWD()
232
- const workspaceName = path.basename(wd)
233
-
234
- const {
235
- named,
236
- template,
237
- bug = {},
238
- readme: _readme = README_TEMPLATE,
239
- readmeTemplate
240
- } = init
241
- const resolvedBug = {
242
- template: 'bug_report.yml',
243
- labels: ['bug'],
244
- ...bug
245
- }
246
- let readme = _readme
247
- if (readmeTemplate) {
248
- const readmeTemplatePath = path.resolve(wd, readmeTemplate)
249
- readme = fs.readFileSync(readmeTemplatePath, 'utf-8')
250
- }
251
-
252
- const templateString = getTemplateStr(wd, template)
253
- // TODO detectIndent by editorconfig
254
- const { indent = ' ' } = detectIndent(templateString)
255
- const formattingOptions = {
256
- tabSize: indent.length,
257
- insertSpaces: true
258
- }
259
- const passFields = [
260
- 'license',
261
- 'author'
262
- ]
263
- let newJSONString = templateString
264
- for (const field of passFields) {
265
- newJSONString = applyEdits(
266
- newJSONString,
267
- modify(
268
- newJSONString,
269
- [field],
270
- getWDPackageJSONFiled(wd, field),
271
- { formattingOptions }
272
- )
273
- )
274
- }
275
- let [pkgName, pkgDir] = await getName(named, name, {
276
- wd,
277
- cwd,
278
- workspaceName
279
- })
280
- if (!pkgDir) {
281
- const { dir } = await inquirer.prompt<{ dir: string }>({
282
- type: 'input',
283
- name: 'dir',
284
- message: 'package directory',
285
- default: name
286
- })
287
- pkgDir = dir
288
- }
289
- if (!pkgName) {
290
- const { name: inputName } = await inquirer.prompt<{
291
- name: string
292
- }>({
293
- type: 'input',
294
- name: 'name',
295
- message: 'package name',
296
- default: name
297
- })
298
- pkgName = inputName
299
- }
300
- newJSONString = applyEdits(newJSONString, modify(newJSONString, ['name'], pkgName, { formattingOptions }))
301
-
302
- let pkgRepo = getWDPackageJSONFiled(wd, 'repository')
303
- if (typeof pkgRepo === 'string') {
304
- pkgRepo = {
305
- type: 'git',
306
- url: pkgRepo,
307
- directory: pkgDir
308
- }
309
- }
310
- newJSONString = applyEdits(
311
- newJSONString,
312
- modify(
313
- newJSONString,
314
- ['repository'],
315
- pkgRepo,
316
- { formattingOptions }
317
- )
318
- )
319
- const homepage = `${pkgRepo?.url}/blob/master/${pkgDir}/README.md`
320
- newJSONString = applyEdits(
321
- newJSONString,
322
- modify(
323
- newJSONString,
324
- ['homepage'],
325
- homepage,
326
- { formattingOptions }
327
- )
328
- )
329
- let labels = resolvedBug.labels
330
- if (typeof labels === 'function') {
331
- labels = labels({
332
- name: pkgName,
333
- dir: pkgDir
334
- })
335
- }
336
- labels.push(`scope:${pkgName}`)
337
- const bugs = `${pkgRepo?.url}/issues/new?template=${resolvedBug.template}&labels=${labels.join(',')}`
338
- newJSONString = applyEdits(
339
- newJSONString,
340
- modify(
341
- newJSONString,
342
- ['bugs'],
343
- bugs,
344
- { formattingOptions }
345
- )
346
- )
347
-
348
- function pkgDirTo(to: string) {
349
- if (!pkgDir) throw new Error('pkgDir is not defined')
350
-
351
- return path.resolve(pkgDir, to)
352
- }
353
- if (!fs.existsSync(pkgDir)) fs.mkdirSync(pkgDir)
354
- const pkgJSONFilePath = pkgDirTo('package.json')
355
- if (fs.existsSync(pkgJSONFilePath)) {
356
- throw new Error('package.json already exists')
357
- }
358
- fs.writeFileSync(pkgJSONFilePath, newJSONString)
359
- console.log(newJSONString, 'written to', pkgJSONFilePath)
360
-
361
- const license = getWDPackageJSONFiled(wd, 'license')
362
- const readmeFilePath = pkgDirTo('README.md')
363
- if (typeof readme === 'function') {
364
- readme = readme({
365
- dir: pkgDir,
366
- packageJson: JSON.parse(newJSONString)
367
- })
368
- }
369
- const readmeContent = readme
370
- .replace(/\$name/g, pkgName)
371
- .replace(/\$license/g, license)
372
- fs.writeFileSync(readmeFilePath, readmeContent)
373
- })