mopub 0.0.3 → 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 +75 -8
  2. package/package.json +1 -1
package/dist/publish.js CHANGED
@@ -19,7 +19,7 @@ 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
23
  .optional(),
24
24
  tag: z
25
25
  .string()
@@ -122,6 +122,21 @@ async function getRegistryVersions(pkg) {
122
122
  const registryVersionsStdout = StdoutShape.parse(JSON.parse(registryVersionsResult.stdout));
123
123
  return Array.isArray(registryVersionsStdout) ? registryVersionsStdout : [];
124
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
+ }
125
140
  const _breakpoint = (why = 'hmm') => ({
126
141
  title: `wait a bit because ${why}`,
127
142
  task: async (ctx, task) => {
@@ -444,7 +459,18 @@ export const publish = async (input) => {
444
459
  rendererOptions: { persistentOutput: true },
445
460
  task: async (ctx, task) => {
446
461
  const shouldActuallyPublish = input.publish;
447
- const publishTasks = createPublishTasks(ctx, { otp: input.otp, tag: input.tag });
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 });
448
474
  if (!shouldActuallyPublish)
449
475
  publishTasks.forEach(t => (t.skip = true));
450
476
  return task.newListr(publishTasks, { rendererOptions: { collapseSubtasks: false } });
@@ -481,7 +507,7 @@ export const PrebuiltInput = z.tuple([
481
507
  z.object({
482
508
  otp: z
483
509
  .string()
484
- .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).')
485
511
  .optional(),
486
512
  tag: z
487
513
  .string()
@@ -495,7 +521,29 @@ export async function publishPrebuilt([folder, options]) {
495
521
  throw new Error(`Failed to get npm username: ${me.stderr}`);
496
522
  console.log(me.stdout, '<<<<<<< npm whoami');
497
523
  const ctx = loadContext(folder);
498
- 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 });
499
547
  await tasks.run();
500
548
  }
501
549
  function createPublishTasks(ctx, options) {
@@ -510,11 +558,30 @@ function createPublishTasks(ctx, options) {
510
558
  const publishArgs = ['--access', 'public'];
511
559
  if (options.otp)
512
560
  publishArgs.push('--otp', options.otp);
513
- 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)) {
514
583
  const first = semver.prerelease(rhsPackageJson.version)?.[0];
515
- let tag = options.tag;
516
- tag ||= typeof first === 'string' ? first : 'next';
517
- publishArgs.push('--tag', tag);
584
+ publishArgs.push('--tag', typeof first === 'string' ? first : 'next');
518
585
  }
519
586
  await pipeExeca(subtask, 'pnpm', ['publish', ...publishArgs], {
520
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.3",
3
+ "version": "0.0.4",
4
4
  "description": "Publish packages in a pnpm monorepo",
5
5
  "type": "module",
6
6
  "bin": {