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

neo-builder

v4.1.13-a

Published

the fastest tiny script packager written in javascript and supporting iife dynamic chaining w/o extra runtime

Downloads

152

Readme

neo-builder

npm npm

The simplest javascript builder based on regular expressions, started as research project

Features:

  • Lite weight (less then 6 kb zipped)
  • Iife wrapping of each modules (without dublicating)
  • Support of node_modules (reexports, direct imports from node-modules, etc)
  • Dynamic imports iife support with common modules shareing
  • Supports variables in dynamic imports (via template strings) out of the box (look up limitaions)
  • fast build speed (commensurate with esbuild or vite in release mode).
  • particullary pnpm support
  • tree shaking (now it is only for function expressions) with esm and cjs support (but still w/o deep diving)

Benchmarks

Benchmarks of various popular bundlers for random packages:

package | webpack | neo-builder | esbuild | parcel -|-|- |-|- @uppy/dashboard | 1272ms | 67ms | 47ms | - @codemirror/lang-javascript | 1513ms | 70ms | 68ms | 75ms codemirror | 1520ms | 77ms | 72ms | 81ms | msw | 1179ms | 32ms | 48ms | 265ms swiper | 911ms | 26ms | 27ms | 86ms

Bundle sizes

A comparative table of the sizes of the received bundles allows us to indirectly draw conclusions about the quality of the builder's three shaking feature:

package | webpack | neo-builder* | esbuild | parcel -|-|- |-|- @uppy | 371 kb | 382 kb | 346 kb | - @codemirror-javascript | 1016 kb | 1040 kb | 649 kb | 942 kb codemirror | 1007 kb | 1034 kb | 751 kb | 942 kb | msw | 85 kb | 72 kb | 66 kb | 1184 kb swiper | 303 kb | 170 kb | 166 kb | 342 kb The same packages minified: @uppy/dashboard | 181kb | 182kb | 179kb | - @codemirror/lang-javascript | 326kb | 360kb | 322kb | 433kb codemirror | 344kb | 344kb | 338kb | 391kb | msw | 27kb | 22kb | 27kb | error swiper | 77kb | 75kb | 75kb | 164kb

  • */ was used fast mode of tree shaking

Issues:

  • Doesn't support some difficult imports (for example the module and default import in one line, like this: import * as module, defaultImport from 'module') because of unuselessness. At last what is the reason to import default when module at the same time consists of the default?)
  • Tree shaking in a progress (just shallow experimental tree-shaking)
  • Incremental mode in a progress (may be, will not be done)
  • Does not create meaningful AST tree. So it can easily break down where linter is not used or complex strings and nested structures are used in the exports

Installation:

local

npm i neo-builder -D --omit=dev

or global

npm i neo-builder -g

Usage via cli:

neo index.js -t target.js --time

Possible options:

  • -s - source file name (could be passed as first arg without the flag -s)
  • -t - target file name (required)
  • -m - generate sourcemap file (optional)
  • --time - verbose build time (optional)

Usage via api:

const packFile = require('neo-builder').packFile
let r = packFile(sourceFile, targetFile, {
    // options
});

Possible options:

  • release? - remove one line comments and plain console.log expressions
  • purgeDebug? - remove /**@debug*/ ... /**@end_debug*/ code blocks
  • getContent? - custom getContent implementation
  • logStub? - logs with source filename and line in runtime
  • sourceMaps?: { encode: Function, external?: true } - option for source map passing the encode method from the sourcemap-codec library or its independent implementation. Look tests for example
  • getSourceMap?: Function - an alternative method for obtaining unencrypted line-by-line source maps for its further programmatic use (works 30% faster than the classical generation of char by char sourcemaps)

build example:

__common.ts file:

let r = 7
function asd(){}

export let months = ['Jan', 'Feb', 'Mar', 'Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
export var a = 6;
export function Ads(arg){}
export class Asde{}

and init.ts:

import * as com from "./__common"

var a = com.a;
var c = 7540;

turn out the content inside init.js in the same directory:

//@modules:


const $$__commonExports = (function (exports) {
 let r = 7
	function asd() { }
	
	let months = ['Jan', 'Feb', 'Mar', 'Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
	var a = 6;
	function Ads(arg) { }
	class Asde { }
	
	exports = { months, a, Ads, Asde };
	
	return exports 
})({})


//@init.ts: 
const com = $$__commonExports;

var a = com.a;
var c = 7540;

Code Splitting

Neo-builder supports dynamic imports (with iife) under browser mode out of the box, what makes it unique. Let's consider the following code:

const langs = ['python', 'javascript'];
for (const key in langs) {
    const lang = langs[key];
    import(`@codemirror/lang-${lang}`).then(exp => {
        console.log(exp[lang]());
    })
}

It'll be built into the following code:

const langs = ['python', 'javascript'];
for (const key in langs) {
    const lang = langs[key];
    fetch(`./dist/$_lang-${lang}_1702744262895.js`).then(r => r.text()).then(content => new Function(content)()).then(exp => {
        console.log(exp[lang]());
    })
}

Simultaneously it'll build relevant files into the distination folder ($_lang-javascript_1702744262895.js and $_lang-python_1702744262895.js). But be careful: this files will be created at the same folder where main app file is built. If you want to manage path, specified into the fetch request, you should use advanced.dynamicImportsRoot option in your build config.

As you can see, in the example above it is used variable inside template string. Neo-builder supports variables by dynamic imports out of the box and allows import packages directly from node_modules. These features also unique feature at this time (mainstream builders haven't variables support out of the box, and direct imports from node_modules haven't support, at all). But you should be awared there are limitations of the usage:

  • Package name format, specified in the import, should have at least two chars on left or right side from variable insertion. That's way to be sure you know what you do. (now this rule refers just to direct imports, not to relative ones.)
  • No more then ten packages recommended to be matched corresponding pattern (else you'll get apropriate warning)

Tree shaking

Tree shaking cuts unused functions from code bundle. But by default is disabled (because of in alpha). To use it, set advanced.treeShaking option into true.

Consider following files:

app.js:

import { default as A } from "./routes";
console.log(A)

routes.js

function _func(params) {
    return params.length
}

export function func() {
    return _func(arguments);
}

function createArray(length) {
    return Array(length)
}

export default function() {
    return createArray(0);
}

With enabled treeShaking unused functions _func and func will be cutted in the resulting bundle:

const $__routesExports = (function (exports) {
	function createArray(length) {
	    return Array(length)
	}
	
	function $default() {
	    return createArray(0);
	}
	
	exports = { default: $default };
	
	return exports 
})({})

Plugins usage:

neo-builder also supports custom plugins. Below there is example how itcan be used:

const uglify = require("uglify-js");

const neoMinify = {
	name: 'neo-minify-plugin',
	bundle: (/** @type {string} */ code, { maps, rawMap }) => {            
		const result = uglify.minify({ target: code }, {
			sourceMap: sourcemap ? {
				content: JSON.stringify(maps),
				url: sourcemapInline ? "inline" : (target + ".map")
			} : undefined
		});

		if (sourcemap && !sourcemapInline) {
			fs.writeFileSync(target + '.map', result.map)
			// fs.writeFileSync(target + '.map', JSON.stringify(result.map))
		}

		return result.code
	}
}


let r = packFile(sourceFile, targetFile, {
    // options
	plugins: [neoMinify]
});