mopub 0.0.2 → 0.0.4

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.
Files changed (2) hide show
  1. package/dist/publish.js +83 -7
  2. package/package.json +2 -2
package/dist/publish.js CHANGED
@@ -19,7 +19,11 @@ export const PublishInput = z
19
19
  .optional(),
20
20
  otp: z
21
21
  .string()
22
- .describe('npm OTP to pass through to `npm publish --otp`. Only needed for registries that still require OTP-based 2FA. Leave unset to use npm\'s default browser-based auth flow.')
22
+ .describe('npm OTP to pass through to `npm publish --otp`. If not provided you will be prompted for it (press enter at the prompt to try publishing without MFA, e.g. for registries that use browser-based auth).')
23
+ .optional(),
24
+ tag: z
25
+ .string()
26
+ .describe('tag to publish to. Leave unset to use the last tag from the registry.')
23
27
  .optional(),
24
28
  bump: z
25
29
  .enum(['major', 'minor', 'patch', 'premajor', 'preminor', 'prepatch', 'prerelease', 'other', 'independent'])
@@ -118,6 +122,21 @@ async function getRegistryVersions(pkg) {
118
122
  const registryVersionsStdout = StdoutShape.parse(JSON.parse(registryVersionsResult.stdout));
119
123
  return Array.isArray(registryVersionsStdout) ? registryVersionsStdout : [];
120
124
  }
125
+ async function getRegistryDistTags(pkgName) {
126
+ const result = await execa('npm', ['view', pkgName, 'dist-tags', '--json'], { reject: false });
127
+ if (!result.stdout)
128
+ return {};
129
+ try {
130
+ const parsed = JSON.parse(result.stdout);
131
+ if (parsed && typeof parsed === 'object' && !('error' in parsed)) {
132
+ return parsed;
133
+ }
134
+ }
135
+ catch {
136
+ // ignore - fall through to empty
137
+ }
138
+ return {};
139
+ }
121
140
  const _breakpoint = (why = 'hmm') => ({
122
141
  title: `wait a bit because ${why}`,
123
142
  task: async (ctx, task) => {
@@ -440,7 +459,18 @@ export const publish = async (input) => {
440
459
  rendererOptions: { persistentOutput: true },
441
460
  task: async (ctx, task) => {
442
461
  const shouldActuallyPublish = input.publish;
443
- const publishTasks = createPublishTasks(ctx, { otp: input.otp });
462
+ let otp = input.otp;
463
+ if (shouldActuallyPublish) {
464
+ otp ||= await task.prompt(ListrEnquirerPromptAdapter).run({
465
+ message: 'Enter npm OTP (press enter to try publishing without MFA)',
466
+ type: 'Input',
467
+ validate: v => v === '' || (typeof v === 'string' && /^\d{6}$/.test(v)),
468
+ });
469
+ if (otp.length === 0) {
470
+ task.output = 'No OTP provided - publish will likely error unless you have disabled MFA.';
471
+ }
472
+ }
473
+ const publishTasks = createPublishTasks(ctx, { otp, tag: input.tag });
444
474
  if (!shouldActuallyPublish)
445
475
  publishTasks.forEach(t => (t.skip = true));
446
476
  return task.newListr(publishTasks, { rendererOptions: { collapseSubtasks: false } });
@@ -477,7 +507,11 @@ export const PrebuiltInput = z.tuple([
477
507
  z.object({
478
508
  otp: z
479
509
  .string()
480
- .describe('npm OTP to pass through to `npm publish --otp`. Only needed for registries that still require OTP-based 2FA. Leave unset to use npm\'s default browser-based auth flow.')
510
+ .describe('npm OTP to pass through to `npm publish --otp`. If not provided you will be prompted for it (press enter at the prompt to try publishing without MFA, e.g. for registries that use browser-based auth).')
511
+ .optional(),
512
+ tag: z
513
+ .string()
514
+ .describe('tag to publish to. Leave unset to use the last tag from the registry.')
481
515
  .optional(),
482
516
  }),
483
517
  ]);
@@ -487,7 +521,29 @@ export async function publishPrebuilt([folder, options]) {
487
521
  throw new Error(`Failed to get npm username: ${me.stderr}`);
488
522
  console.log(me.stdout, '<<<<<<< npm whoami');
489
523
  const ctx = loadContext(folder);
490
- const tasks = new Listr(createPublishTasks(ctx, options), { ctx });
524
+ const tasks = new Listr([
525
+ {
526
+ title: 'Get OTP',
527
+ enabled: () => !options.otp,
528
+ task: async (_ctx, task) => {
529
+ options.otp = await task.prompt(ListrEnquirerPromptAdapter).run({
530
+ message: 'Enter npm OTP (press enter to try publishing without MFA)',
531
+ type: 'Input',
532
+ validate: v => v === '' || (typeof v === 'string' && /^\d{6}$/.test(v)),
533
+ });
534
+ if (options.otp === '') {
535
+ const confirmed = await task.prompt(ListrEnquirerPromptAdapter).run({
536
+ message: 'This will fail unless you have disabled MFA, which is not recommended.',
537
+ type: 'confirm',
538
+ });
539
+ if (!confirmed) {
540
+ throw new Error('OTP not provided');
541
+ }
542
+ }
543
+ },
544
+ },
545
+ ...createPublishTasks(ctx, options),
546
+ ], { ctx });
491
547
  await tasks.run();
492
548
  }
493
549
  function createPublishTasks(ctx, options) {
@@ -502,10 +558,30 @@ function createPublishTasks(ctx, options) {
502
558
  const publishArgs = ['--access', 'public'];
503
559
  if (options.otp)
504
560
  publishArgs.push('--otp', options.otp);
505
- if (rhsPackageJson && semver.prerelease(rhsPackageJson.version)) {
561
+ let resolvedTag = options.tag;
562
+ // default to whichever dist-tag the previous comparable release was published under,
563
+ // when both versions are the same prerelease/non-prerelease type. e.g. if 0.0.1-1 was
564
+ // published as "latest", a follow-up 0.0.1-2 should also go to "latest" (not "next");
565
+ // if 4.0.0 was published as "next", 4.0.1 should also be "next" (not "latest").
566
+ if (!resolvedTag && lhsPackageJson?.version && rhsPackageJson?.version) {
567
+ const sameType = Boolean(semver.prerelease(lhsPackageJson.version)) ===
568
+ Boolean(semver.prerelease(rhsPackageJson.version));
569
+ if (sameType) {
570
+ const distTags = await getRegistryDistTags(pkg.name);
571
+ const matchingTags = Object.entries(distTags)
572
+ .filter(([, v]) => v === lhsPackageJson.version)
573
+ .map(([t]) => t);
574
+ if (matchingTags.length > 0) {
575
+ resolvedTag = matchingTags.find(t => t === 'latest') || matchingTags[0];
576
+ }
577
+ }
578
+ }
579
+ if (resolvedTag) {
580
+ publishArgs.push('--tag', resolvedTag);
581
+ }
582
+ else if (rhsPackageJson && semver.prerelease(rhsPackageJson.version)) {
506
583
  const first = semver.prerelease(rhsPackageJson.version)?.[0];
507
- const tag = typeof first === 'string' ? first : 'next';
508
- publishArgs.push('--tag', tag);
584
+ publishArgs.push('--tag', typeof first === 'string' ? first : 'next');
509
585
  }
510
586
  await pipeExeca(subtask, 'pnpm', ['publish', ...publishArgs], {
511
587
  cwd: path.dirname(packageJsonFilepath(pkg, RHS_FOLDER)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mopub",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Publish packages in a pnpm monorepo",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,7 +32,7 @@
32
32
  "typescript": "^6.0.3"
33
33
  },
34
34
  "scripts": {
35
- "dogfood": "tsx src/cli",
35
+ "clean": "rm -rf dist",
36
36
  "build": "tsc --noEmit false --outDir dist",
37
37
  "lint": "eslint .",
38
38
  "test": "echo maybe later"