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

packen

v0.3.3

Published

Pack isomorphic JavaScript

Downloads

3

Readme

types version GitHub Workflow Status

packen collects isomorphic JavaScript code when it runs on the server, and prepares it for shipping to and running on the client. Useful for server side rendering (SSR), static site generation (SSG), etc.

// isomorphic code:
import { packMe } from 'packen'

packMe()

// ...
node -r packen/r server-code.js

Installation

You need Node.js for using packen.

npm i packen

Usage

To use packen, you need to:

  • Mark your isomorphic code
  • Run your server code, which may use (some of) your isomorphic code

packen will keep track of what was used and bundle it for you (or help you bundle it using your own toolchain).

👉 Use packMe() to mark your isomorphic code:

import { packMe } from 'packen'

packMe()
// this file will be included in the bundle

You can also conditionally mark a file:

if (condition()) {
  packMe()
}

// this file will be included in the bundle
// only if condition() is true.
export function func() {
  packMe()
  // this file will be included in the bundle
  // only when func() is called.
  
  // ...
}

You can create the bundle either programmatically or using the CLI. packen is mainly designed to be used programmatically (as part of other tooling), but the CLI route offers a convenient out-of-the-box method suitable for static site generation (SSG), albeit with limited configuration options.

CLI

node -r packen/r sever_code.js
ts-node -r packen/r server_code.ts

☝️ This will execute server_code.js, and bundle any marked isomorphic code in an output bundle.js. You can customize the output file by providing the PACKEN_TO environment variable:

PACKEN_TO=dist/chunk.js node -r packen/r server_code.js
PACKEN_TO=dist/chunk.js ts-node -r packen/r server_code.ts

Programmatic API

import { Bundle, build } from 'packen/server'

//
// 👉 STEP 1: create a bundle
//
const bundle = new Bundle()

//
// 👉 STEP 2: run your isomorphic code
//
import './my/iso.js'

...

//
// 👉 STEP 3: build the bundle
//
await build(bundle, 'dist/chunk.js')

A Bundle MUST have been created before the isomorphic code is executed. Calling packMe() when no bundle is created will have no effect. Additionally, when you build a bundle, it is closed, which means it can no longer collect isomorphic code, and you need to make a new bundle.

packen provides various methods for building bundles:

  • build(): creates a bundle from collected code using esbuild and writes it to given file.
  • pack(): creates a bundle from collected code using esbuild and returns it as a string.
  • write(): creates an entry point from collected code and writes it to given file. You might need to bundle this entry file using your own bundler before shipping it to client (tools such as Vite can consume it directly).
  • serialize(): creates an entry point from collected code and returns it as a string.

Build

build(bundle: Bundle, path: string, processor?: Processor): void

Builds given bundle, bundles and minifies it (using esbuild) and writes it to given path. If a processor is provided, will be used for processing earmarked entries (see Extension).

import { Bundle, build } from 'packen/server'

const bundle = new Bundle()

// ...

build(bundle, 'dist/bundle.js')

Pack

pack(bundle: Bundle, processor?: Processor): string

Builds given bundle, returning the bundled and minified code as a strin (uses esbuild). If a processor is provided, will be used for processing earmarked entries (see Extension).

import { Bundle, pack } from 'packen/server'

const bundle = new Bundle()

// ...

const code = pack(bundle)

This method can be used for generating server-side HTML:

const myHTML = html`
  <html>
    <head>
      <script type="module">
        ${pack(bundle)}
      </script>
    </head>
    <body>
      <!-- ... -->
    </body>
  </html>
`

Write

write(bundle: Bundle, path: string, processor?: Processor): void

Builds an entry file for given bundle, and writes it to given path. If a processor is provided, will be used for processing earmarked entries (see Extension). DOES NOT bundle or minify the code.

import { Bundle, write } from 'packen/server'

const bundle = new Bundle()

// ...

write(bundle, 'dist/entry.js')

This entry file can be used with other bundlers, like Vite:

<!-- index.html -->
<script type="module" src="dist/entry.js"></script>
<!-- ... -->
vite build

Serialize

serialize(bundle: Bundle, processor?: Processor): string

Builds an entry file for given bundle, returning the code as a string. If a processor is provided, will be used for processing earmarked entries (see Extension). DOES NOT bundle or minify the code.

import { Bundle, serialize } from 'packen/server'

const bundle = new Bundle()

// ...

const code = serialize(bundle)

This is particularly useful when you want to use other bundlers programmatically (or even using esbuild with some custom configuration):

import { build } from 'esbuild'

  await build({
    stdin: {
      contents: serialize(bundle, processor),
      resolveDir: process.cwd(),
    },
    // your esbuild configuration
  })

Advanced Usage

👉 Use packCaller() from inside a function to pack the code that called that function:

import { packCaller } from 'packen'

// any code calling func will now be packed automatically.
export function func() {
  packCaller()

  // ...
}

👉 Use mark() and collect() to create a mark that can be called later (perhaps due to some later event):

import { mark, collect } from 'packen'


const flag = mark()

// when laterCallback is called, this file will be collected and bundled.
export function laterCallback() {
  collect(flag)

  // ...
}

👉 Use markCaller() and collect() to create a mark for the calling code that can be called later (perhaps due to some later event):

import { markCaller, collect } from 'packen'


function someFunc() {
  const flag = markCaller()

  // when this is called, the code that called someFunc will be collected and bundled.
  return () => collect(flag)
}

Extension

By default, packen will use bare imports, collecting files as side-effect:

// entry file

import '/Path/to/some/isomorphic.js'

// ...

You can change this behavior by providing a processor function to bundling functions. This processor will be passed a CallSite, and should turn it into some valid JavaScript string. For example, the following custom processor allows collecting specific functions, which then will be executed on client side:

export dryRun = entry => {
  const func = entry.getFunctionName()
  const file = entry.getFileName()
  
  return `
    import { ${func} } from '${file}';
    ${func}();
  `
}

This processor can be used like this:

// ...

build(bundle, 'dist/bundle.js', dryRun)
// isomorphic code

import { packMe } from 'packen'

export const myFunc = () => {

  // this function will be collected for client side bundling
  // and executed on client bootstrap.
  packMe()
  
  // do other stuff
}

Contribution

You need node, NPM to start and git to start.

# clone the code
git clone [email protected]:loreanvictor/packen.git
# install stuff
npm i

Make sure all checks are successful on your PRs. This includes all tests passing, high code coverage, correct typings and abiding all the linting rules. The code is typed with TypeScript, Jest is used for testing and coverage reports, ESLint and TypeScript ESLint are used for linting. Subsequently, IDE integrations for TypeScript and ESLint would make your life much easier (for example, VSCode supports TypeScript out of the box and has this nice ESLint plugin), but you could also use the following commands:

# run tests
npm test
# check code coverage
npm run coverage
# run linter
npm run lint