Giter Club home page Giter Club logo

monopacker's Introduction

Build Status npm version Commitizen friendly semantic-release


⚠️ Major revision in progress

The current version of monopacker is outdated but still in use in a couple of projects. As there were a lot of changes in architectural design of monorepos in the last 2 years I'll continue to refactor the whole monopacker toolset into a new major revision.


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!

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

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

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.
# 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
# Complex example
$ monopacker pack packages/main packed/main --root ./test/fixtures/basic/ --noCache --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.PREINSTALL]: Array<(packer: Packer, artificalPkg: ArtificalPackage) => Promise<any>>;
		[HookPhase.POSTINSTALL]: Array<(packer: Packer, artificalPkg: ArtificalPackage) => 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

monopacker's People

Contributors

janbiasi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

monopacker's Issues

Npm pack instead of copy

Hi Jan,

thanks for this project.

Currently it’s needed to define which files should or should not be copied to the packed package. Instead of this you could use npm pack.

Because this projects „simulates“ an npm publish it would be more familiar to use npm pack.

Npm pack creates an zip file including the files which are included if it would be published to npm.

Optimize documentation

Required

  • explain what the tool does in-depth
  • explain why and for what cases the tool is needed
  • add a solid API documentation

Optional

  • add a visualization of the monopacker steps and how the graph's working internally
  • add a beautiful logo
  • generate an API documentation with a tool like tsdoc

Nice to have

  • publish the docs via GitHub pages

Debugging and verbosity

  • Expose some kind of debug hooks to enable debugging also programmatically
  • Add a -v, --verbose flag for silent packing

Error: Cannot find module 'commander'

Describe the bug
When trying the CLI, Error: Cannot find module 'commander' is thrown.

To Reproduce
npx monopacker

Expected behavior
Not to throw an error.

Screenshots
N/A

Desktop (please complete the following information):

  • OS: Any
  • Version: 2.x, 3.x

Additional context
The CLI src/bin/monopacker-cli.js requires commander but it is not installed nor present in package.json. Therefore, trying to execute the CLI fails. Previously commander was a deep dependency of semantic-release and that helped the build and CLI not to fail. But this is no longer the case.

Handling invalid lerna packages

Some lerna packages are not valid or empty, we might try to get around that by using an artifical package or just skip children resolver if no package was specified.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.