npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

monopacker

v3.0.0-rc.0

Published

A tool for managing builds of monorepo frontend projects with eg. lerna or similar tools.

Downloads

132

Readme

Build Status npm version Commitizen friendly semantic-release

monopacker

A tool for managing builds of monorepo frontend projects with eg. lerna or similar tools. Developers who are working within monorepositories often have the problem that they need to deploy an application but don't want to deploy the whole repository. This is not possible by default, but there's monopacker - a tool for generating small bundles out of your monorepository applications which are straight deployable - with extras!

But why do I need such a thing?

These days handling big projects with monorepositories isn't easy at all. Most of the time you just want one bit of all your packages builded and deployed to a server without the whole overhead of the other packages, configs and so on. Lerna doesn't provide a solid way to do this, you always need to copy your whole repository to your target server because any package in your main entry point will require something else from another module (which you didn't even know it existed).

Monopacker does exactly solve this issue for you; it builds a single package inside the monorepository into a standalone application which runs without the overhead - and it uses the same syntax as npm pack so you don't need to learn anything new! Isn't that great?

The process steps in depth

  1. Aggregate all dependencies of the target application
  2. Detect which dependencies come from NPM and which are located in the repo
  3. Clone the target (only needed files, customizable) to the new target
  4. Create an artificial package.json with installable dependencies from the project, including the needed production dependencies from the internal dependencies (eg. main app (A) requires B and B requires express so express will also be a dependency of the packed application, quite smart huh?)
  5. Install all possible dependencies from NPM
  6. Copy all needed submodules to the packed target to "fake" the installation
  7. Done! Your application is ready to deploy individually as it is.

But I need to <insert your requirement here> ...

  • Monopacker provides a flexible programmatical API
  • Monopacker provides also a CLI implementation
  • Monopacker supports a hook system where you are able to interact within every step of the packing process
  • Monopacker can be configured what to copy and what not
  • Monopacker supports internal caching for repetetive processes and large repos with circular references

Installation

npm install monopacker --save-dev --save-exact
# or with yarn
yarn add monopacker --dev

CLI API

You can use the command as you would use npm pack <dir>, but with a lot more arguments. If you think the command is too complex, considering switching to the programmatic API would be worth it.

Note: the CLI does not support hooks (taping)! If you want to inject side-effects please use the programmatic API

global options

| Option |  Short | Default | Description | | ----------- | ------ | ------- | -------------------------- | | --debug | -d | false | Enable debug mode | | --verbose | -v | false | Sets the output to verbose |

analyze

| Option |  Short | Default | Description | | -------- | ------ | --------------- | -------------------------------------- | | --root | -r | process.cwd() | Set the root directory for the process |

# Will output a JSON graph of the packable application
$ monopacker analyze packages/apps/main
$ monopacker a packages/apps/main

pack

| Option |  Short | Default | Description | | ------------- | ------ | ------------------ | -------------------------------------------------------------------------------------------------------------- | | --root | -r | process.cwd() | Set the root directory for the process | | --copy | -c | **,!package.json | Globs for what to copy initiall to the target, comma separated | | --noCache | -nc | false | Disable all caching mechanisms | | --adapter | -a | lerna | Select a certain adapter for processing, we only support lerna atm. | | --no-pack | -np | false | Disable NPM packing of the final artefact | | --installer | -i | false | Create an installer file instead of the inline publish install script (recommended for complex custom scripts) |

# Will pack the application from `packages/apps/main` to `packed`
$ monopacker pack packages/apps/main
$ monopacker p packages/apps/main
# Will pack the application from `packages/apps/main` to `deploy/main-app`
$ monopacker pack packages/apps/main deploy/main-app
# Will pack a remote application from `packages/apps/main` to `deploy/main-app`
$ monopacker pack packages/apps/main deploy/main-app --root ../another-app
$ monopacker pack packages/apps/main deploy/main-app -r ../another-app
# Will not copy anything initially
$ monopacker pack packages/apps/main --copy !**
$ monopacker pack packages/apps/main -c !**
# Force use the lerna strategy
$ monopacker pack packages/apps/main --adapter lerna
$ monopacker pack packages/apps/main -a lerna
# Without caching
$ monopacker pack packages/apps/main --noCache
$ monopacker pack packages/apps/main -nc
# Generate `monopacker.installer.js` file
$ monopacker pack packages/apps/main --installer
$ monopacker pack packages/apps/main -i
# Don't pack the final artefact
$ monopacker pack packages/apps/main --noPack
$ monopacker pack packages/apps/main -np
# Complex example
$ monopacker pack packages/main packed/main --root ./test/fixtures/basic/ --noCache --noPack -i --copy src,dist -a lerna

Programmatic API

Options

interface IPackerOptions {
    /**
     * Source to pack (root is cwd)
     */
    source: string;
    /**
     * Target for the packed app (root is cwd)
     */
    target: string;
    /**
     * Monorepository type, at the moment only lerna support.
     * Default: auto-detected
     */
    type?: 'lerna' | 'nx';
    /**
     * Enable or disable the cache, default is true (enabled)
     */
    cache?: boolean;
    /**
     * Working directory, can be changed, default: process.cwd()
     */
    cwd?: string;
    /**
     * Expressions to match package names which are internally defined (optional)
     * Can be used for eg. rewriting globally available modules such as 'react-scripts'
     * to provide a custom implementation for.
     */
    internals?: string[];
    /**
     * The adapter for the analytics process, default: lerna
     */
    adapter?: IAdapterConstructable;
    /**
     * Optional copy settings, defaults to `['**', '!package.json', ...]`
     */
    copy?: string[];
    /**
     * Enable the debug mode, defaults to false
     */
    debug?: boolean;
    /**
     * Define opt-in hooks for certain steps
     */
    hooks?: Partial<{
        [HookPhase.INIT]: Array<(packer: Packer) => Promise<any>>;
        [HookPhase.PREANALYZE]: Array<(packer: Packer) => Promise<any>>;
        [HookPhase.POSTANALYZE]: Array<
            (
                packer: Packer,
                information: {
                    analytics: IAnalytics;
                    generateAnalyticsFile: boolean;
                    fromCache: boolean;
                }
            ) => Promise<any>
        >;
        [HookPhase.PRECOPY]: Array<(packer: Packer) => Promise<any>>;
        [HookPhase.POSTCOPY]: Array<(packer: Packer, copiedFiles: string[]) => Promise<any>>;
        [HookPhase.PRELINK]: Array<(packer: Packer, entries: ILernaPackageListEntry[]) => Promise<any>>;
        [HookPhase.POSTLINK]: Array<(packer: Packer, entries: ILernaPackageListEntry[]) => Promise<any>>;
        [HookPhase.PACKED]: Array<
            (
                packer: Packer,
                resume: {
                    analytics: IAnalytics;
                    artificalPackage: ArtificalPackage;
                    copiedFiles: string[];
                }
            ) => Promise<any>
        >;
    }>;
}

Debugging

import { Packer } from 'monopacker';

new Packer({
    source: 'packages/apps/my-app',
    target: 'packed/my-app',
    debug: true // uses the helper `useDebugHooks`
});

Multi taping

import { Packer, Taper, HookPhase } from 'monopacker';

const packer = new Packer({
    source: 'packages/apps/my-app',
    target: 'packed/my-app'
});

packer.subscribe(
    new Taper(HookPhase, {
        // add some other hooks here which will be run before the target hooks
        // can be used to separate taping logic conditionally.
        init: [async () => console.log('Hello from subscription:init!')]
    })
);

await packer.pack();

Simple example

import { Packer } from 'monopacker';

new Packer({
    source: 'packages/apps/my-app',
    target: 'packed/my-app'
}).pack();

Advanced example

import { resolve } from 'path';
import * as rimraf from 'rimraf';
import * as execa from 'execa';
import { Packer } from 'monopacker';

(async () => {
    new Packer({
        cwd: resolve(__dirname, 'my-repo-is-nested'),
        source: 'packages/apps/my-app',
        target: 'packed/my-app',
        copy: [
            '!.cache',
            '!lcov-report',
            '!.gitignore',
            '!tsconfig.json',
            '!README.md',
            '!junit.xml',
            '!jest.config.js'
        ],
        hooks: {
            init: [
                async () => {
                    await execa('npm', ['run', 'build']);
                }
            ],
            precopy: [
                async packer => {
                    // create a production build before copying things
                    await packer.runInSourceDir('npm', ['run', 'build', '--production']);
                }
            ]
        }
    });

    await packer.pack();

    console.log('done!');
})();
q;