Skip to content

Customize Beachball Command [design question for PR] #651

@rajsite

Description

@rajsite

Overview

We would like to be able to configure the command beachball invokes to publish a package. We can contribute a PR to implement the behavior but would like a recommendation for the preferred implementation.

Use cases

Angular Libraries

We manage an Angular package in a monorepo. The package folder itself should not have npm publish run inside of it. Instead, we want to redirect to a different folder and run publish.

This is because running an Angular build generates a completely new folder with a package.json that the Angular build has customized. We also need to run the Angular build during publish so the result of the beachball bump version is captured in the Angular build output, for example:

{
    "name": "my-package",
    "scripts": {
        "custom-publish": "ng build && cd ../dist/my-package && npm publish"
    }
}

Non-NPM Libraries

We manage some libraries that are not npm-based but are useful for managing in a node monorepo. For example a library in the project generates some JSON metadata that we then use to publish packages based on that metadata to other package systems (nuget today but could be others i.e. Rust, Python, etc.).

Those packages have a package.json defined to manage them within beachball and that works great. The only issue is we don't want to actually publish that package instead we want to call to a different tool to publish. We can't just add a {"scripts": {"publish": ""}} command to the package.json because while that command will run, the cli will also try to publish the package to npm; you can't prevent the default npm publish behavior from a publish script.

{
    "name": "my-package",
    "scripts": {
        "custom-publish": "dotnet nuget push ./MyPackage"
    }
}

Solutions

We thought of a couple ways to solve this and could submit it as a PR, but wanted to discuss the solution preferred by beachball.

Configure custom npm command

Allow configuring the npm command that is run during publish. The command that is run would be assumed to take the same flags passed to npm publish. This would look like modifying the args for packagePublish to look like the following:

  const args = [
    ...(packageOptions.customPublishCommand ? [
        'run', // Run a script defined in the package.json
        packageOptions.customPublishCommand,
        '--' // Pass the arguments that will be added to the underlying script
    ] || ['publish']),
    '--registry',
    registry,
    '--tag',
    packageOptions.tag || packageOptions.defaultNpmTag,
    '--loglevel',
    'warn',
    ...getNpmAuthArgs(registry, token, authType),
  ];

Pros:

  1. Relatively small change
  2. Easy to consume in my scenarios as one scenario is just forwarding the npm publish command to a different folder
  3. Logic like evaluating the tag, getting the auth args, the access property, etc. unchanged for passthrough

Cons

  1. Not very flexible / coupled to npm cli specific behavior (having a run command and the -- flag for argument passthrough)
  2. API interface to the custom command not great for more customized use cases. Would have to parse the command line arguments instead of having an API.

Custom publish function

Define a custom function to override the packagePublish command invocation to call a custom function:

result = (options.customPackagePublish || originalPackagePublish)(packageInfo, options);

The above snippet assumes minor refactoring of the packagePublish command to take PackageOptions and BeachballOptions for a more concise API than what packagePublish is using today.

Pros:

  1. More configurable by implementers
  2. Does not have to embed more knowledge of a specific CLI tool (i.e. npm run syntax with the -- passthrough)

Cons:

  1. A lot more boiler plate for simple operations such as forwarding the publish command. IE users have to implement the logic for building args such as tag selection, formatting auth args, conditionally adding access arg.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions