create-pkgbld 1.8.2 → 2.0.1

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