create-pkgbld 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export default function getGitRoot(): Promise<string>;
package/dist/index.cjs CHANGED
@@ -6,93 +6,528 @@ var path = require('path');
6
6
  var fs = require('fs/promises');
7
7
  var userName = require('git-user-name');
8
8
  var gitConfig = require('parse-git-config');
9
- var minimist = require('minimist');
9
+ var cleye = require('cleye');
10
+ var kleur = require('kleur');
11
+ require('typia');
12
+ var stringArgv = require('string-argv');
13
+ var childProcess = require('node:child_process');
14
+ var util = require('node:util');
10
15
 
16
+ async function getGitRoot() {
17
+ const exec = util.promisify(childProcess.exec);
18
+ const { stdout } = await exec('git rev-parse --show-toplevel');
19
+ return stdout.trim();
20
+ }
21
+
22
+ const done = Symbol('done');
23
+ const formats = ['amd', 'cjs', 'es', 'iife', 'system', 'umd'];
24
+ const pkgbldBinaries = ['pkgbld', 'pkgbld-internal', 'node ../pkgbld/dist/index.js'];
11
25
  async function execute() {
12
- // report version
13
- {
14
- const createPkgBldPackage = await readPackage(path.resolve(__dirname, '..'));
15
- console.log('create-pkgbld ' + createPkgBldPackage.pkg.version);
16
- }
17
- const parsedArgs = minimist(process.argv.slice(2));
18
- const targetDir = path.join(process.cwd(), parsedArgs._[0] ?? '.');
26
+ const version = await reportVersion();
27
+ const args = cleye.cli({
28
+ name: 'create-pkgbld',
29
+ version,
30
+ parameters: [
31
+ '[package name]'
32
+ ],
33
+ flags: {
34
+ quite: {
35
+ type: Boolean,
36
+ description: 'Quite mode',
37
+ default: false
38
+ }
39
+ }
40
+ });
41
+ args.showVersion();
42
+ const targetDir = path.join(process.cwd(), args._.packageName ?? '.');
43
+ console.log(kleur.grey(pad16plus('Target Directory', 0)) + kleur.white(targetDir));
44
+ const pkg = await readPackage(targetDir);
45
+ console.log(kleur.grey(pad16plus('Mode', 0)) + kleur.white(pkg.mode));
19
46
  const packageName = path.basename(targetDir);
20
47
  let cancelled = false;
21
- const response = await prompts([{
22
- type: 'text',
23
- name: 'name',
24
- message: 'package name',
25
- initial: packageName
26
- }, {
27
- type: 'text',
28
- name: 'version',
29
- message: 'version',
30
- initial: '0.0.1'
31
- }, {
32
- type: 'text',
33
- name: 'description',
34
- message: 'description',
35
- initial: ''
36
- }, {
37
- type: 'text',
38
- name: 'license',
39
- message: 'license',
40
- initial: 'MIT'
41
- }, {
42
- type: 'text',
43
- name: 'author',
44
- message: 'author',
45
- initial: userName() ?? ''
46
- }], { onCancel });
47
- if (cancelled) {
48
- process.exit(-1);
48
+ const options = getBasicOptions(packageName, pkg);
49
+ options.push(...await getGitOptions(targetDir));
50
+ options.push(...getPkgbldOptions(pkg.pkg));
51
+ const state = getOptionsValue(options);
52
+ if (!args.flags.quite) {
53
+ for (;;) {
54
+ const topLevelAction = await prompts({
55
+ type: 'select',
56
+ name: 'value',
57
+ message: 'Select an option to change, Done to execute, Escape to cancel',
58
+ choices: [
59
+ { title: kleur.green('Done'), description: `${pkg.mode === 'update' ? 'Update' : 'Create'} package`, value: done },
60
+ ...options.map(mapOption(state))
61
+ ],
62
+ initial: 0
63
+ }, { onCancel });
64
+ if (cancelled) {
65
+ process.exit(-1);
66
+ }
67
+ if (topLevelAction.value === done) {
68
+ break;
69
+ }
70
+ let option = options.find(item => item.field === topLevelAction.value);
71
+ let mutateObject = state;
72
+ while ('items' in option) {
73
+ const nextLevelAction = await prompts([{
74
+ type: 'select',
75
+ name: 'value',
76
+ message: option.title,
77
+ choices: option.items.map(mapOption(option.mutateInnerObject ? state[option.field] : state))
78
+ }], { onCancel });
79
+ if (cancelled) {
80
+ process.exit(-1);
81
+ }
82
+ if (option.mutateInnerObject) {
83
+ mutateObject = state[option.field];
84
+ }
85
+ option = option.items.find(item => item.field === nextLevelAction.value);
86
+ }
87
+ const action = await prompts(getPromptOption(option, mutateObject), { onCancel });
88
+ if (cancelled) {
89
+ process.exit(-1);
90
+ }
91
+ mutateObject[option.field] = action[option.field];
92
+ }
93
+ if (cancelled) {
94
+ process.exit(-1);
95
+ }
96
+ }
97
+ updatePackage(pkg, state);
98
+ pkg.readme ??= `# ${state.name}`;
99
+ await writePackage(targetDir, pkg);
100
+ function onCancel() {
101
+ cancelled = true;
102
+ }
103
+ }
104
+ execute();
105
+ function updatePackage(pkg, options) {
106
+ const order = new Set([
107
+ 'private',
108
+ 'type',
109
+ 'version',
110
+ 'name',
111
+ 'description',
112
+ 'license',
113
+ 'author',
114
+ 'bin',
115
+ 'main',
116
+ 'module',
117
+ 'exports',
118
+ 'types',
119
+ 'typings',
120
+ 'files',
121
+ 'engines',
122
+ 'repository',
123
+ 'bugs',
124
+ 'homepage',
125
+ 'keywords',
126
+ 'scripts',
127
+ 'dependencies',
128
+ 'devDependencies',
129
+ 'peerDependencies',
130
+ 'optionalDependencies',
131
+ 'publishConfig'
132
+ ]);
133
+ const newPkg = {};
134
+ for (const key of order) {
135
+ if (key in options || key in pkg.pkg) {
136
+ newPkg[key] = treatKey(key);
137
+ }
138
+ }
139
+ for (const key in pkg.pkg) {
140
+ if (!order.has(key)) {
141
+ newPkg[key] = pkg.pkg[key];
142
+ }
143
+ }
144
+ pkg.pkg = newPkg;
145
+ function treatKey(key) {
146
+ if (key === 'scripts') {
147
+ ({ ...pkg.pkg.scripts });
148
+ console.log(options);
149
+ getScriptValue(options.pkgbld);
150
+ }
151
+ return options[key] ?? pkg.pkg[key];
49
152
  }
50
- const gitInfo = {
51
- homepage: '',
52
- repository: undefined,
53
- bugs: undefined
153
+ }
154
+ function getScriptValue(pkgbld) {
155
+ const binary = pkgbld.pkgbldBinary;
156
+ const extraArgs = pkgbld.extraParameters;
157
+ const pkgBldCopy = { ...pkgbld };
158
+ delete pkgBldCopy['pkgbldBinary'];
159
+ delete pkgBldCopy['extraParameters'];
160
+ return `${binary} ${asCommandLineArgs(pkgBldCopy)} ${extraArgs}`;
161
+ }
162
+ function getPromptOption(option, mutateObject) {
163
+ const value = mutateObject[option.field];
164
+ const type = option.type ?? 'text';
165
+ const promptOption = {
166
+ type,
167
+ name: option.field,
168
+ message: option.title,
169
+ initial: (Array.isArray(value) ? value.join(',') : value) ?? ''
170
+ };
171
+ if (type === 'multiselect') {
172
+ promptOption.choices = 'list' in option ? option.list.map(item => ({
173
+ title: item,
174
+ value: item,
175
+ selected: value.includes(item)
176
+ })) : [];
177
+ }
178
+ if (type === 'select') {
179
+ promptOption.choices = 'list' in option ? option.list.map(item => ({
180
+ title: item,
181
+ value: item
182
+ })) : [];
183
+ promptOption.initial = promptOption.choices.findIndex(item => item.value === promptOption.initial);
184
+ }
185
+ return promptOption;
186
+ }
187
+ function mapOption(state) {
188
+ return (option) => {
189
+ const fieldValue = state[option.field];
190
+ return {
191
+ title: pad16plus(option.title) + kleur.grey('items' in option ? getPrintString(option.items, (option.mutateInnerObject ? fieldValue : state)) : fieldValue ?? ''),
192
+ value: option.field
193
+ };
54
194
  };
195
+ }
196
+ function getPrintString(items, json) {
197
+ return items
198
+ .filter(item => item.field in json && json[item.field] && (Array.isArray(json[item.field]) ? json[item.field].length > 0 : true))
199
+ .map(item => kleur.grey(item.title) + ' ' + (kleur.white('items' in item ?
200
+ `[${getPrintString(item.items, (item.mutateInnerObject ? json[item.field] :
201
+ json))}]` : json[item.field])))
202
+ .join(', ');
203
+ }
204
+ function getOptionsValue(options) {
205
+ const result = {};
206
+ for (const item of options) {
207
+ if ('items' in item) {
208
+ const value = getOptionsValue(item.items);
209
+ if (item.mutateInnerObject) {
210
+ result[item.field] = value;
211
+ }
212
+ else {
213
+ Object.assign(result, value);
214
+ }
215
+ }
216
+ else {
217
+ result[item.field] = item.initialValue;
218
+ }
219
+ }
220
+ return result;
221
+ }
222
+ async function reportVersion() {
223
+ const createPkgBldPackage = await readPackage(path.resolve(__dirname, '..'));
224
+ const version = createPkgBldPackage.pkg.version ?? '<unknown>';
225
+ return version;
226
+ }
227
+ async function getGitOptions(targetDir) {
55
228
  try {
56
229
  const gitCfg = await gitConfig();
57
230
  if (gitCfg) {
58
231
  const url = gitCfg['remote "origin"'].url;
59
- gitInfo.homepage = url.replace('.git', `/blob/main/${packageName}/README.md`);
60
- gitInfo.repository = {
61
- type: 'git',
62
- url: `git+${url}`
63
- };
64
- gitInfo.bugs = {
65
- url: url.replace('.git', '/issues')
66
- };
232
+ const root = await getGitRoot();
233
+ const directory = path.relative(root, targetDir);
234
+ return [{
235
+ title: 'Git',
236
+ field: 'git',
237
+ mutateInnerObject: false,
238
+ items: [{
239
+ title: 'Homepage',
240
+ field: 'homepage',
241
+ initialValue: url.replace('.git', `/blob/main${directory ? '/' + directory : ''}/README.md`)
242
+ }, {
243
+ title: 'Repository',
244
+ field: 'repository',
245
+ items: [{
246
+ title: 'Type',
247
+ field: 'type',
248
+ initialValue: 'git'
249
+ }, {
250
+ title: 'Url',
251
+ field: 'url',
252
+ initialValue: `git+${url}`
253
+ }, {
254
+ title: 'Directory',
255
+ field: 'directory',
256
+ initialValue: directory || undefined
257
+ }],
258
+ mutateInnerObject: true
259
+ }, {
260
+ title: 'Bugs',
261
+ field: 'bugs',
262
+ initialValue: url.replace('.git', '/issues')
263
+ }]
264
+ }];
67
265
  }
68
266
  }
69
267
  catch (e) {
70
268
  /* ignore */
71
269
  }
72
- const pkg = await readPackage(targetDir);
73
- Object.assign(pkg.pkg, response, gitInfo);
74
- pkg.readme = `# ${response.name}`;
75
- await writePackage(targetDir, pkg);
76
- function onCancel() {
77
- cancelled = true;
270
+ return [];
271
+ }
272
+ function getPkgbldOptions(pkg) {
273
+ let args = {
274
+ formats: ['es', 'cjs'],
275
+ umd: [],
276
+ compress: ['umd'],
277
+ sourcemaps: ['umd'],
278
+ preprocess: [],
279
+ dir: 'dist',
280
+ sourceDir: 'src',
281
+ bin: undefined,
282
+ includeExternals: false,
283
+ eject: false,
284
+ noTsConfig: false,
285
+ noUpdatePackageJson: false
286
+ };
287
+ function CommaSeparatedString(value) {
288
+ return value.split(',').map((arg) => arg.trim());
78
289
  }
290
+ const cmd = pkg.scripts?.build ?? '';
291
+ let binary = 'pkgbld';
292
+ if (cmd) {
293
+ if (cmd.startsWith('pkgbld-internal')) {
294
+ binary = 'pkgbld-internal';
295
+ }
296
+ else if (cmd.startsWith('node ../pkgbld/dist/index.js')) {
297
+ binary = 'node ../pkgbld/dist/index.js';
298
+ }
299
+ else if (!cmd.startsWith('pkgbld')) {
300
+ const naiveArgs = cmd.split(' ');
301
+ if (naiveArgs.length > 1 && naiveArgs[0] === 'node') {
302
+ binary = naiveArgs[0] + ' ' + naiveArgs[1];
303
+ }
304
+ else if (naiveArgs.length > 0) {
305
+ binary = naiveArgs[0]; // ?????
306
+ }
307
+ }
308
+ const parsedArgs = cleye.cli({
309
+ help: false,
310
+ flags: {
311
+ umd: {
312
+ type: CommaSeparatedString,
313
+ default: args.umd
314
+ },
315
+ compress: {
316
+ type: CommaSeparatedString,
317
+ default: args.compress
318
+ },
319
+ sourcemaps: {
320
+ type: CommaSeparatedString,
321
+ default: args.sourcemaps
322
+ },
323
+ formats: {
324
+ type: CommaSeparatedString,
325
+ default: args.formats
326
+ },
327
+ preprocess: {
328
+ type: CommaSeparatedString,
329
+ default: args.preprocess
330
+ },
331
+ dir: {
332
+ type: String,
333
+ default: args.dir
334
+ },
335
+ sourceDir: {
336
+ type: String,
337
+ default: args.sourceDir
338
+ },
339
+ bin: {
340
+ type: CommaSeparatedString,
341
+ default: args.bin
342
+ },
343
+ includeExternals: {
344
+ type: Boolean,
345
+ default: args.includeExternals
346
+ },
347
+ eject: {
348
+ type: Boolean,
349
+ default: args.eject
350
+ },
351
+ noTsConfig: {
352
+ type: Boolean,
353
+ default: args.noTsConfig
354
+ },
355
+ noUpdatePackageJson: {
356
+ type: Boolean,
357
+ default: args.noUpdatePackageJson
358
+ }
359
+ }
360
+ }, undefined, stringArgv.parseArgsStringToArgv(cmd));
361
+ args = parsedArgs.flags;
362
+ args.extraParameters = asCommandLineArgs(parsedArgs.unknownFlags);
363
+ }
364
+ return [{
365
+ title: 'pkgbld',
366
+ field: 'pkgbld',
367
+ mutateInnerObject: true,
368
+ items: [{
369
+ title: 'Destination folder',
370
+ field: 'dest',
371
+ initialValue: args.dir
372
+ }, {
373
+ title: 'Source folder',
374
+ field: 'src',
375
+ initialValue: args.sourceDir
376
+ }, {
377
+ title: 'UMD exports',
378
+ field: 'umd',
379
+ initialValue: args.umd,
380
+ type: 'list'
381
+ }, {
382
+ title: 'Compress formats',
383
+ field: 'compress',
384
+ initialValue: args.compress,
385
+ type: 'multiselect',
386
+ list: formats
387
+ }, {
388
+ title: 'Sorcemaps formats',
389
+ field: 'sourcemaps',
390
+ initialValue: args.sourcemaps,
391
+ type: 'multiselect',
392
+ list: formats
393
+ }, {
394
+ title: 'Formats',
395
+ field: 'formats',
396
+ initialValue: args.formats,
397
+ type: 'multiselect',
398
+ list: formats
399
+ }, {
400
+ title: 'Preprocess formats',
401
+ field: 'preprocess',
402
+ initialValue: args.preprocess,
403
+ type: 'multiselect',
404
+ list: formats
405
+ }, {
406
+ title: 'Binaries',
407
+ field: 'bin',
408
+ initialValue: args.bin,
409
+ type: 'list'
410
+ }, {
411
+ title: 'Include externals',
412
+ field: 'includeExternals',
413
+ type: 'toggle',
414
+ initialValue: args.includeExternals
415
+ }, {
416
+ title: 'Eject config',
417
+ field: 'eject',
418
+ type: 'toggle',
419
+ initialValue: args.eject
420
+ }, {
421
+ title: 'Create tsconfig',
422
+ field: 'tsConfig',
423
+ type: 'toggle',
424
+ initialValue: !args.noTsConfig
425
+ }, {
426
+ title: 'Update package.json',
427
+ field: 'updatePackageJson',
428
+ type: 'toggle',
429
+ initialValue: !args.noUpdatePackageJson
430
+ }, {
431
+ title: 'Extra parameters',
432
+ field: 'extraParameters',
433
+ type: 'text',
434
+ initialValue: args.extraParameters
435
+ }, {
436
+ title: 'Pkgbld Binary',
437
+ field: 'pkgbldBinary',
438
+ type: 'select',
439
+ list: pkgbldBinaries.includes(binary) ? pkgbldBinaries : [...pkgbldBinaries, binary],
440
+ initialValue: binary
441
+ }]
442
+ }];
443
+ }
444
+ function asCommandLineArgs(parsedArgs) {
445
+ return Object.entries(parsedArgs)
446
+ .flatMap(([key, value]) => asArray(value)
447
+ .filter(Boolean)
448
+ .map(value => `--${key}${typeof value === 'string' ? `=${value}` : ''}`)).join(' ');
449
+ }
450
+ function asArray(value) {
451
+ return Array.isArray(value) ? value : [value];
452
+ }
453
+ function getBasicOptions(packageName, pkg) {
454
+ return [{
455
+ title: 'General',
456
+ field: 'general',
457
+ items: [{
458
+ title: 'Package Name',
459
+ field: 'name',
460
+ initialValue: chooseValue(pkg.pkg.name, packageName)
461
+ }, {
462
+ title: 'Version',
463
+ field: 'version',
464
+ initialValue: chooseValue(pkg.pkg.version, '0.0.1')
465
+ }, {
466
+ title: 'Description',
467
+ field: 'description',
468
+ initialValue: chooseValue(pkg.pkg.description, '')
469
+ }, {
470
+ title: 'License',
471
+ field: 'license',
472
+ initialValue: chooseValue(pkg.pkg.license, 'MIT')
473
+ }, {
474
+ title: 'Author',
475
+ field: 'author',
476
+ initialValue: chooseValue(pkg.pkg.author, userName() ?? '')
477
+ }],
478
+ mutateInnerObject: false
479
+ }];
480
+ function chooseValue(pkgValue, defaultValue) {
481
+ return pkg.mode === 'update' ? pkgValue : defaultValue;
482
+ }
483
+ }
484
+ function pad16plus(value, indent = 4, offset = 3) {
485
+ return value + ''.padEnd(offset - Math.floor((value.length + indent) / 8), '\t');
79
486
  }
80
- execute();
81
487
  async function readPackage(dir) {
82
488
  const packageFileName = path.resolve(dir, 'package.json');
83
489
  const readmeFileName = path.resolve(dir, 'README.md');
490
+ const defaultPkg = {};
84
491
  try {
85
492
  const pkgFile = await fs.readFile(packageFileName);
86
493
  const readmeFile = await fs.readFile(readmeFileName);
494
+ const pkg = JSON.parse(pkgFile.toString());
495
+ const isValidPackageJson = (input => {
496
+ const $io0 = input => (undefined === input.private || "boolean" === typeof input.private) && (undefined === input.version || "string" === typeof input.version) && (undefined === input.name || "string" === typeof input.name) && (undefined === input.license || "string" === typeof input.license) && (undefined === input.author || "string" === typeof input.author) && (undefined === input.description || "string" === typeof input.description) && (undefined === input.scripts || "object" === typeof input.scripts && null !== input.scripts && false === Array.isArray(input.scripts) && $io1(input.scripts)) && (undefined === input.dependencies || "object" === typeof input.dependencies && null !== input.dependencies && false === Array.isArray(input.dependencies) && $io2(input.dependencies)) && (undefined === input.devDependencies || "object" === typeof input.devDependencies && null !== input.devDependencies && false === Array.isArray(input.devDependencies) && $io2(input.devDependencies)) && (undefined === input.peerDependencies || "object" === typeof input.peerDependencies && null !== input.peerDependencies && false === Array.isArray(input.peerDependencies) && $io2(input.peerDependencies)) && Object.keys(input).every(key => {
497
+ if (["private", "version", "name", "license", "author", "description", "scripts", "dependencies", "devDependencies", "peerDependencies"].some(prop => key === prop))
498
+ return true;
499
+ const value = input[key];
500
+ if (undefined === value)
501
+ return true;
502
+ if (RegExp(/(.*)/).test(key))
503
+ return true;
504
+ return true;
505
+ });
506
+ const $io1 = input => (undefined === input.build || "string" === typeof input.build) && (undefined === input.lint || "string" === typeof input.lint);
507
+ const $io2 = input => Object.keys(input).every(key => {
508
+ const value = input[key];
509
+ if (undefined === value)
510
+ return true;
511
+ if (RegExp(/(.*)/).test(key))
512
+ return "string" === typeof value;
513
+ return true;
514
+ });
515
+ return "object" === typeof input && null !== input && false === Array.isArray(input) && $io0(input);
516
+ })(pkg);
517
+ if (!isValidPackageJson) {
518
+ console.error('Invalid package.json');
519
+ }
87
520
  return {
88
- pkg: JSON.parse(pkgFile.toString()),
89
- readme: readmeFile.toString()
521
+ pkg: isValidPackageJson ? pkg : defaultPkg,
522
+ readme: readmeFile.toString(),
523
+ mode: 'update'
90
524
  };
91
525
  }
92
526
  catch (e) { /**/ }
93
527
  return {
94
- pkg: {},
95
- readme: ''
528
+ pkg: defaultPkg,
529
+ readme: '',
530
+ mode: 'create'
96
531
  };
97
532
  }
98
533
  async function writePackage(dir, pkg) {
package/dist/index.mjs CHANGED
@@ -3,93 +3,528 @@ import path from 'path';
3
3
  import fs from 'fs/promises';
4
4
  import userName from 'git-user-name';
5
5
  import gitConfig from 'parse-git-config';
6
- import minimist from 'minimist';
6
+ import { cli } from 'cleye';
7
+ import kleur from 'kleur';
8
+ import 'typia';
9
+ import { parseArgsStringToArgv } from 'string-argv';
10
+ import childProcess from 'node:child_process';
11
+ import util from 'node:util';
7
12
 
13
+ async function getGitRoot() {
14
+ const exec = util.promisify(childProcess.exec);
15
+ const { stdout } = await exec('git rev-parse --show-toplevel');
16
+ return stdout.trim();
17
+ }
18
+
19
+ const done = Symbol('done');
20
+ const formats = ['amd', 'cjs', 'es', 'iife', 'system', 'umd'];
21
+ const pkgbldBinaries = ['pkgbld', 'pkgbld-internal', 'node ../pkgbld/dist/index.js'];
8
22
  async function execute() {
9
- // report version
10
- {
11
- const createPkgBldPackage = await readPackage(path.resolve(__dirname, '..'));
12
- console.log('create-pkgbld ' + createPkgBldPackage.pkg.version);
13
- }
14
- const parsedArgs = minimist(process.argv.slice(2));
15
- const targetDir = path.join(process.cwd(), parsedArgs._[0] ?? '.');
23
+ const version = await reportVersion();
24
+ const args = cli({
25
+ name: 'create-pkgbld',
26
+ version,
27
+ parameters: [
28
+ '[package name]'
29
+ ],
30
+ flags: {
31
+ quite: {
32
+ type: Boolean,
33
+ description: 'Quite mode',
34
+ default: false
35
+ }
36
+ }
37
+ });
38
+ args.showVersion();
39
+ const targetDir = path.join(process.cwd(), args._.packageName ?? '.');
40
+ console.log(kleur.grey(pad16plus('Target Directory', 0)) + kleur.white(targetDir));
41
+ const pkg = await readPackage(targetDir);
42
+ console.log(kleur.grey(pad16plus('Mode', 0)) + kleur.white(pkg.mode));
16
43
  const packageName = path.basename(targetDir);
17
44
  let cancelled = false;
18
- const response = await prompts([{
19
- type: 'text',
20
- name: 'name',
21
- message: 'package name',
22
- initial: packageName
23
- }, {
24
- type: 'text',
25
- name: 'version',
26
- message: 'version',
27
- initial: '0.0.1'
28
- }, {
29
- type: 'text',
30
- name: 'description',
31
- message: 'description',
32
- initial: ''
33
- }, {
34
- type: 'text',
35
- name: 'license',
36
- message: 'license',
37
- initial: 'MIT'
38
- }, {
39
- type: 'text',
40
- name: 'author',
41
- message: 'author',
42
- initial: userName() ?? ''
43
- }], { onCancel });
44
- if (cancelled) {
45
- process.exit(-1);
45
+ const options = getBasicOptions(packageName, pkg);
46
+ options.push(...await getGitOptions(targetDir));
47
+ options.push(...getPkgbldOptions(pkg.pkg));
48
+ const state = getOptionsValue(options);
49
+ if (!args.flags.quite) {
50
+ for (;;) {
51
+ const topLevelAction = await prompts({
52
+ type: 'select',
53
+ name: 'value',
54
+ message: 'Select an option to change, Done to execute, Escape to cancel',
55
+ choices: [
56
+ { title: kleur.green('Done'), description: `${pkg.mode === 'update' ? 'Update' : 'Create'} package`, value: done },
57
+ ...options.map(mapOption(state))
58
+ ],
59
+ initial: 0
60
+ }, { onCancel });
61
+ if (cancelled) {
62
+ process.exit(-1);
63
+ }
64
+ if (topLevelAction.value === done) {
65
+ break;
66
+ }
67
+ let option = options.find(item => item.field === topLevelAction.value);
68
+ let mutateObject = state;
69
+ while ('items' in option) {
70
+ const nextLevelAction = await prompts([{
71
+ type: 'select',
72
+ name: 'value',
73
+ message: option.title,
74
+ choices: option.items.map(mapOption(option.mutateInnerObject ? state[option.field] : state))
75
+ }], { onCancel });
76
+ if (cancelled) {
77
+ process.exit(-1);
78
+ }
79
+ if (option.mutateInnerObject) {
80
+ mutateObject = state[option.field];
81
+ }
82
+ option = option.items.find(item => item.field === nextLevelAction.value);
83
+ }
84
+ const action = await prompts(getPromptOption(option, mutateObject), { onCancel });
85
+ if (cancelled) {
86
+ process.exit(-1);
87
+ }
88
+ mutateObject[option.field] = action[option.field];
89
+ }
90
+ if (cancelled) {
91
+ process.exit(-1);
92
+ }
93
+ }
94
+ updatePackage(pkg, state);
95
+ pkg.readme ??= `# ${state.name}`;
96
+ await writePackage(targetDir, pkg);
97
+ function onCancel() {
98
+ cancelled = true;
99
+ }
100
+ }
101
+ execute();
102
+ function updatePackage(pkg, options) {
103
+ const order = new Set([
104
+ 'private',
105
+ 'type',
106
+ 'version',
107
+ 'name',
108
+ 'description',
109
+ 'license',
110
+ 'author',
111
+ 'bin',
112
+ 'main',
113
+ 'module',
114
+ 'exports',
115
+ 'types',
116
+ 'typings',
117
+ 'files',
118
+ 'engines',
119
+ 'repository',
120
+ 'bugs',
121
+ 'homepage',
122
+ 'keywords',
123
+ 'scripts',
124
+ 'dependencies',
125
+ 'devDependencies',
126
+ 'peerDependencies',
127
+ 'optionalDependencies',
128
+ 'publishConfig'
129
+ ]);
130
+ const newPkg = {};
131
+ for (const key of order) {
132
+ if (key in options || key in pkg.pkg) {
133
+ newPkg[key] = treatKey(key);
134
+ }
135
+ }
136
+ for (const key in pkg.pkg) {
137
+ if (!order.has(key)) {
138
+ newPkg[key] = pkg.pkg[key];
139
+ }
140
+ }
141
+ pkg.pkg = newPkg;
142
+ function treatKey(key) {
143
+ if (key === 'scripts') {
144
+ ({ ...pkg.pkg.scripts });
145
+ console.log(options);
146
+ getScriptValue(options.pkgbld);
147
+ }
148
+ return options[key] ?? pkg.pkg[key];
46
149
  }
47
- const gitInfo = {
48
- homepage: '',
49
- repository: undefined,
50
- bugs: undefined
150
+ }
151
+ function getScriptValue(pkgbld) {
152
+ const binary = pkgbld.pkgbldBinary;
153
+ const extraArgs = pkgbld.extraParameters;
154
+ const pkgBldCopy = { ...pkgbld };
155
+ delete pkgBldCopy['pkgbldBinary'];
156
+ delete pkgBldCopy['extraParameters'];
157
+ return `${binary} ${asCommandLineArgs(pkgBldCopy)} ${extraArgs}`;
158
+ }
159
+ function getPromptOption(option, mutateObject) {
160
+ const value = mutateObject[option.field];
161
+ const type = option.type ?? 'text';
162
+ const promptOption = {
163
+ type,
164
+ name: option.field,
165
+ message: option.title,
166
+ initial: (Array.isArray(value) ? value.join(',') : value) ?? ''
167
+ };
168
+ if (type === 'multiselect') {
169
+ promptOption.choices = 'list' in option ? option.list.map(item => ({
170
+ title: item,
171
+ value: item,
172
+ selected: value.includes(item)
173
+ })) : [];
174
+ }
175
+ if (type === 'select') {
176
+ promptOption.choices = 'list' in option ? option.list.map(item => ({
177
+ title: item,
178
+ value: item
179
+ })) : [];
180
+ promptOption.initial = promptOption.choices.findIndex(item => item.value === promptOption.initial);
181
+ }
182
+ return promptOption;
183
+ }
184
+ function mapOption(state) {
185
+ return (option) => {
186
+ const fieldValue = state[option.field];
187
+ return {
188
+ title: pad16plus(option.title) + kleur.grey('items' in option ? getPrintString(option.items, (option.mutateInnerObject ? fieldValue : state)) : fieldValue ?? ''),
189
+ value: option.field
190
+ };
51
191
  };
192
+ }
193
+ function getPrintString(items, json) {
194
+ return items
195
+ .filter(item => item.field in json && json[item.field] && (Array.isArray(json[item.field]) ? json[item.field].length > 0 : true))
196
+ .map(item => kleur.grey(item.title) + ' ' + (kleur.white('items' in item ?
197
+ `[${getPrintString(item.items, (item.mutateInnerObject ? json[item.field] :
198
+ json))}]` : json[item.field])))
199
+ .join(', ');
200
+ }
201
+ function getOptionsValue(options) {
202
+ const result = {};
203
+ for (const item of options) {
204
+ if ('items' in item) {
205
+ const value = getOptionsValue(item.items);
206
+ if (item.mutateInnerObject) {
207
+ result[item.field] = value;
208
+ }
209
+ else {
210
+ Object.assign(result, value);
211
+ }
212
+ }
213
+ else {
214
+ result[item.field] = item.initialValue;
215
+ }
216
+ }
217
+ return result;
218
+ }
219
+ async function reportVersion() {
220
+ const createPkgBldPackage = await readPackage(path.resolve(__dirname, '..'));
221
+ const version = createPkgBldPackage.pkg.version ?? '<unknown>';
222
+ return version;
223
+ }
224
+ async function getGitOptions(targetDir) {
52
225
  try {
53
226
  const gitCfg = await gitConfig();
54
227
  if (gitCfg) {
55
228
  const url = gitCfg['remote "origin"'].url;
56
- gitInfo.homepage = url.replace('.git', `/blob/main/${packageName}/README.md`);
57
- gitInfo.repository = {
58
- type: 'git',
59
- url: `git+${url}`
60
- };
61
- gitInfo.bugs = {
62
- url: url.replace('.git', '/issues')
63
- };
229
+ const root = await getGitRoot();
230
+ const directory = path.relative(root, targetDir);
231
+ return [{
232
+ title: 'Git',
233
+ field: 'git',
234
+ mutateInnerObject: false,
235
+ items: [{
236
+ title: 'Homepage',
237
+ field: 'homepage',
238
+ initialValue: url.replace('.git', `/blob/main${directory ? '/' + directory : ''}/README.md`)
239
+ }, {
240
+ title: 'Repository',
241
+ field: 'repository',
242
+ items: [{
243
+ title: 'Type',
244
+ field: 'type',
245
+ initialValue: 'git'
246
+ }, {
247
+ title: 'Url',
248
+ field: 'url',
249
+ initialValue: `git+${url}`
250
+ }, {
251
+ title: 'Directory',
252
+ field: 'directory',
253
+ initialValue: directory || undefined
254
+ }],
255
+ mutateInnerObject: true
256
+ }, {
257
+ title: 'Bugs',
258
+ field: 'bugs',
259
+ initialValue: url.replace('.git', '/issues')
260
+ }]
261
+ }];
64
262
  }
65
263
  }
66
264
  catch (e) {
67
265
  /* ignore */
68
266
  }
69
- const pkg = await readPackage(targetDir);
70
- Object.assign(pkg.pkg, response, gitInfo);
71
- pkg.readme = `# ${response.name}`;
72
- await writePackage(targetDir, pkg);
73
- function onCancel() {
74
- cancelled = true;
267
+ return [];
268
+ }
269
+ function getPkgbldOptions(pkg) {
270
+ let args = {
271
+ formats: ['es', 'cjs'],
272
+ umd: [],
273
+ compress: ['umd'],
274
+ sourcemaps: ['umd'],
275
+ preprocess: [],
276
+ dir: 'dist',
277
+ sourceDir: 'src',
278
+ bin: undefined,
279
+ includeExternals: false,
280
+ eject: false,
281
+ noTsConfig: false,
282
+ noUpdatePackageJson: false
283
+ };
284
+ function CommaSeparatedString(value) {
285
+ return value.split(',').map((arg) => arg.trim());
75
286
  }
287
+ const cmd = pkg.scripts?.build ?? '';
288
+ let binary = 'pkgbld';
289
+ if (cmd) {
290
+ if (cmd.startsWith('pkgbld-internal')) {
291
+ binary = 'pkgbld-internal';
292
+ }
293
+ else if (cmd.startsWith('node ../pkgbld/dist/index.js')) {
294
+ binary = 'node ../pkgbld/dist/index.js';
295
+ }
296
+ else if (!cmd.startsWith('pkgbld')) {
297
+ const naiveArgs = cmd.split(' ');
298
+ if (naiveArgs.length > 1 && naiveArgs[0] === 'node') {
299
+ binary = naiveArgs[0] + ' ' + naiveArgs[1];
300
+ }
301
+ else if (naiveArgs.length > 0) {
302
+ binary = naiveArgs[0]; // ?????
303
+ }
304
+ }
305
+ const parsedArgs = cli({
306
+ help: false,
307
+ flags: {
308
+ umd: {
309
+ type: CommaSeparatedString,
310
+ default: args.umd
311
+ },
312
+ compress: {
313
+ type: CommaSeparatedString,
314
+ default: args.compress
315
+ },
316
+ sourcemaps: {
317
+ type: CommaSeparatedString,
318
+ default: args.sourcemaps
319
+ },
320
+ formats: {
321
+ type: CommaSeparatedString,
322
+ default: args.formats
323
+ },
324
+ preprocess: {
325
+ type: CommaSeparatedString,
326
+ default: args.preprocess
327
+ },
328
+ dir: {
329
+ type: String,
330
+ default: args.dir
331
+ },
332
+ sourceDir: {
333
+ type: String,
334
+ default: args.sourceDir
335
+ },
336
+ bin: {
337
+ type: CommaSeparatedString,
338
+ default: args.bin
339
+ },
340
+ includeExternals: {
341
+ type: Boolean,
342
+ default: args.includeExternals
343
+ },
344
+ eject: {
345
+ type: Boolean,
346
+ default: args.eject
347
+ },
348
+ noTsConfig: {
349
+ type: Boolean,
350
+ default: args.noTsConfig
351
+ },
352
+ noUpdatePackageJson: {
353
+ type: Boolean,
354
+ default: args.noUpdatePackageJson
355
+ }
356
+ }
357
+ }, undefined, parseArgsStringToArgv(cmd));
358
+ args = parsedArgs.flags;
359
+ args.extraParameters = asCommandLineArgs(parsedArgs.unknownFlags);
360
+ }
361
+ return [{
362
+ title: 'pkgbld',
363
+ field: 'pkgbld',
364
+ mutateInnerObject: true,
365
+ items: [{
366
+ title: 'Destination folder',
367
+ field: 'dest',
368
+ initialValue: args.dir
369
+ }, {
370
+ title: 'Source folder',
371
+ field: 'src',
372
+ initialValue: args.sourceDir
373
+ }, {
374
+ title: 'UMD exports',
375
+ field: 'umd',
376
+ initialValue: args.umd,
377
+ type: 'list'
378
+ }, {
379
+ title: 'Compress formats',
380
+ field: 'compress',
381
+ initialValue: args.compress,
382
+ type: 'multiselect',
383
+ list: formats
384
+ }, {
385
+ title: 'Sorcemaps formats',
386
+ field: 'sourcemaps',
387
+ initialValue: args.sourcemaps,
388
+ type: 'multiselect',
389
+ list: formats
390
+ }, {
391
+ title: 'Formats',
392
+ field: 'formats',
393
+ initialValue: args.formats,
394
+ type: 'multiselect',
395
+ list: formats
396
+ }, {
397
+ title: 'Preprocess formats',
398
+ field: 'preprocess',
399
+ initialValue: args.preprocess,
400
+ type: 'multiselect',
401
+ list: formats
402
+ }, {
403
+ title: 'Binaries',
404
+ field: 'bin',
405
+ initialValue: args.bin,
406
+ type: 'list'
407
+ }, {
408
+ title: 'Include externals',
409
+ field: 'includeExternals',
410
+ type: 'toggle',
411
+ initialValue: args.includeExternals
412
+ }, {
413
+ title: 'Eject config',
414
+ field: 'eject',
415
+ type: 'toggle',
416
+ initialValue: args.eject
417
+ }, {
418
+ title: 'Create tsconfig',
419
+ field: 'tsConfig',
420
+ type: 'toggle',
421
+ initialValue: !args.noTsConfig
422
+ }, {
423
+ title: 'Update package.json',
424
+ field: 'updatePackageJson',
425
+ type: 'toggle',
426
+ initialValue: !args.noUpdatePackageJson
427
+ }, {
428
+ title: 'Extra parameters',
429
+ field: 'extraParameters',
430
+ type: 'text',
431
+ initialValue: args.extraParameters
432
+ }, {
433
+ title: 'Pkgbld Binary',
434
+ field: 'pkgbldBinary',
435
+ type: 'select',
436
+ list: pkgbldBinaries.includes(binary) ? pkgbldBinaries : [...pkgbldBinaries, binary],
437
+ initialValue: binary
438
+ }]
439
+ }];
440
+ }
441
+ function asCommandLineArgs(parsedArgs) {
442
+ return Object.entries(parsedArgs)
443
+ .flatMap(([key, value]) => asArray(value)
444
+ .filter(Boolean)
445
+ .map(value => `--${key}${typeof value === 'string' ? `=${value}` : ''}`)).join(' ');
446
+ }
447
+ function asArray(value) {
448
+ return Array.isArray(value) ? value : [value];
449
+ }
450
+ function getBasicOptions(packageName, pkg) {
451
+ return [{
452
+ title: 'General',
453
+ field: 'general',
454
+ items: [{
455
+ title: 'Package Name',
456
+ field: 'name',
457
+ initialValue: chooseValue(pkg.pkg.name, packageName)
458
+ }, {
459
+ title: 'Version',
460
+ field: 'version',
461
+ initialValue: chooseValue(pkg.pkg.version, '0.0.1')
462
+ }, {
463
+ title: 'Description',
464
+ field: 'description',
465
+ initialValue: chooseValue(pkg.pkg.description, '')
466
+ }, {
467
+ title: 'License',
468
+ field: 'license',
469
+ initialValue: chooseValue(pkg.pkg.license, 'MIT')
470
+ }, {
471
+ title: 'Author',
472
+ field: 'author',
473
+ initialValue: chooseValue(pkg.pkg.author, userName() ?? '')
474
+ }],
475
+ mutateInnerObject: false
476
+ }];
477
+ function chooseValue(pkgValue, defaultValue) {
478
+ return pkg.mode === 'update' ? pkgValue : defaultValue;
479
+ }
480
+ }
481
+ function pad16plus(value, indent = 4, offset = 3) {
482
+ return value + ''.padEnd(offset - Math.floor((value.length + indent) / 8), '\t');
76
483
  }
77
- execute();
78
484
  async function readPackage(dir) {
79
485
  const packageFileName = path.resolve(dir, 'package.json');
80
486
  const readmeFileName = path.resolve(dir, 'README.md');
487
+ const defaultPkg = {};
81
488
  try {
82
489
  const pkgFile = await fs.readFile(packageFileName);
83
490
  const readmeFile = await fs.readFile(readmeFileName);
491
+ const pkg = JSON.parse(pkgFile.toString());
492
+ const isValidPackageJson = (input => {
493
+ const $io0 = input => (undefined === input.private || "boolean" === typeof input.private) && (undefined === input.version || "string" === typeof input.version) && (undefined === input.name || "string" === typeof input.name) && (undefined === input.license || "string" === typeof input.license) && (undefined === input.author || "string" === typeof input.author) && (undefined === input.description || "string" === typeof input.description) && (undefined === input.scripts || "object" === typeof input.scripts && null !== input.scripts && false === Array.isArray(input.scripts) && $io1(input.scripts)) && (undefined === input.dependencies || "object" === typeof input.dependencies && null !== input.dependencies && false === Array.isArray(input.dependencies) && $io2(input.dependencies)) && (undefined === input.devDependencies || "object" === typeof input.devDependencies && null !== input.devDependencies && false === Array.isArray(input.devDependencies) && $io2(input.devDependencies)) && (undefined === input.peerDependencies || "object" === typeof input.peerDependencies && null !== input.peerDependencies && false === Array.isArray(input.peerDependencies) && $io2(input.peerDependencies)) && Object.keys(input).every(key => {
494
+ if (["private", "version", "name", "license", "author", "description", "scripts", "dependencies", "devDependencies", "peerDependencies"].some(prop => key === prop))
495
+ return true;
496
+ const value = input[key];
497
+ if (undefined === value)
498
+ return true;
499
+ if (RegExp(/(.*)/).test(key))
500
+ return true;
501
+ return true;
502
+ });
503
+ const $io1 = input => (undefined === input.build || "string" === typeof input.build) && (undefined === input.lint || "string" === typeof input.lint);
504
+ const $io2 = input => Object.keys(input).every(key => {
505
+ const value = input[key];
506
+ if (undefined === value)
507
+ return true;
508
+ if (RegExp(/(.*)/).test(key))
509
+ return "string" === typeof value;
510
+ return true;
511
+ });
512
+ return "object" === typeof input && null !== input && false === Array.isArray(input) && $io0(input);
513
+ })(pkg);
514
+ if (!isValidPackageJson) {
515
+ console.error('Invalid package.json');
516
+ }
84
517
  return {
85
- pkg: JSON.parse(pkgFile.toString()),
86
- readme: readmeFile.toString()
518
+ pkg: isValidPackageJson ? pkg : defaultPkg,
519
+ readme: readmeFile.toString(),
520
+ mode: 'update'
87
521
  };
88
522
  }
89
523
  catch (e) { /**/ }
90
524
  return {
91
- pkg: {},
92
- readme: ''
525
+ pkg: defaultPkg,
526
+ readme: '',
527
+ mode: 'create'
93
528
  };
94
529
  }
95
530
  async function writePackage(dir, pkg) {
@@ -0,0 +1,44 @@
1
+ export type Option = ({
2
+ title: string;
3
+ field: string;
4
+ type?: undefined | 'toggle' | 'list';
5
+ } | {
6
+ title: string;
7
+ field: string;
8
+ type: 'multiselect';
9
+ list: string[];
10
+ } | {
11
+ title: string;
12
+ field: string;
13
+ type: 'select';
14
+ list: string[];
15
+ }) & ({
16
+ initialValue?: string;
17
+ } | {
18
+ items: Option[];
19
+ mutateInnerObject: boolean;
20
+ });
21
+ export type PackageJson = {
22
+ private?: boolean;
23
+ version?: string;
24
+ name?: string;
25
+ license?: string;
26
+ author?: string;
27
+ description?: string;
28
+ scripts?: {
29
+ build?: string;
30
+ lint?: string;
31
+ };
32
+ dependencies?: Record<string, string>;
33
+ devDependencies?: Record<string, string>;
34
+ peerDependencies?: Record<string, string>;
35
+ [key: string]: any;
36
+ };
37
+ export type PkgInfo = {
38
+ readme: string;
39
+ pkg: PackageJson;
40
+ mode: 'create' | 'update';
41
+ };
42
+ export type OptionsValue = {
43
+ [key: string]: undefined | null | number | boolean | string | OptionsValue;
44
+ };
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.0",
2
+ "version": "1.4.0",
3
3
  "license": "MIT",
4
4
  "name": "create-pkgbld",
5
5
  "author": "Konstantin Shutkin",
@@ -37,17 +37,20 @@
37
37
  "@types/git-user-name": "2.0.1",
38
38
  "@types/parse-git-config": "3.0.1",
39
39
  "@types/prompts": "2.4.4",
40
- "@types/minimist": "1.2.2",
41
- "pkgbld": "1.16.6"
40
+ "@total-typescript/ts-reset": "^0.5.1",
41
+ "pkgbld": "1.17.0"
42
42
  },
43
43
  "dependencies": {
44
+ "typia": "^5.0.1",
45
+ "string-argv": "^0.3.2",
44
46
  "git-user-name": "2.0.0",
45
- "minimist": "1.2.8",
47
+ "cleye": "^1.3.2",
46
48
  "parse-git-config": "3.0.0",
47
- "prompts": "2.4.2"
49
+ "prompts": "2.4.2",
50
+ "kleur": "4.1.5"
48
51
  },
49
52
  "scripts": {
50
- "build": "node ../pkgbld/dist/index.js --formats=cjs,es",
53
+ "build": "node ../pkgbld/dist/index.js",
51
54
  "lint": "eslint ./src"
52
55
  }
53
56
  }