
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@tangible/roller
Advanced tools
The purpose of this tool is to compile JavaScript, TypeScript, Sass, and other file types into minified bundles with source maps.
https://github.com/tangibleinc/tangible-roller
Node.js version 18 or higher is required.
Install in your project as a dependency for development.
npm install --save-dev @tangible/roller
This provides a local command called roll, which can be run using npm or npx.
Add the following NPM scripts in your project's package.json file.
{
"scripts": {
"dev": "roll dev",
"build": "roll build",
"format": "roll format"
}
}
These can be run from the terminal when you're inside the project folder.
Build for development and watch files for changes
npm run dev
Press CTRL + C to stop.
Build for production
npm run build
Format files to code standard
npm run format
Run TypeScript file
Builds the given file and runs it.
npx roll run [file] [...options]
Example:
npx roll run index.ts
Create zip package for archive
npx roll archive
Other commands
Use npx, which is bundled with Node.js, to run other builder commands.
npx roll [command]
Run the above without any command to see a help screen.
Project directory
Optionally specify a child directory as project root
npm run [command] [folder]
npx roll [command] [folder]
The build and format commands support multiple projects.
npm run build [...folders]
npm run format [...folders]
Before starting, the builder needs a configuration file.
Create a file called tangible.config.js in your project folder.
Example:
export default {
build: [
{
src: 'src/index.js',
dest: 'build/app.min.js'
},
{
src: 'src/index.scss',
dest: 'build/app.min.css'
},
],
format: 'src'
}
The config file exports an object with the following properties.
The required config property build is an array of tasks.
Each task is an object with the following properties:
src - Source file with extension js, jsx, ts, tsx, scss, or htmldest - Destination file with extension min.js, min.css, or htmlDuring development, the source files are watched for any changes, and rebuilt as needed.
Files with React JSX syntax must have extension jsx or tsx. They will automatically import React.
The optional task property react sets the React mode.
Its value is one of:
react (default)preact - Import modules react and react-dom are aliased to preact/compatwp - Import modules react and react-dom are aliased to global variable wp.element. Also, import modules @wordpress/* are aliased to properties under global variable wp.The following optional task properties perform various substitutions.
Using alias you can map an import module name to target another module name or file path. This uses rollup's alias plugin under the hood.
Here's an example use of the alias parameter:
export default {
build: [
{
src: 'src/index.js',
dest: 'build/app.min.js',
alias: [
{
find: 'src',
replacement: path.resolve(projectRootDir, 'src')
}
]
},
// ...
}
Using importToGlobal you can map import module names to global variable names. It supports dynamic names such as @example/*, for which a function should be given that takes the module name and returns the variable name:
export default {
build: [
{
src: 'src/index.js',
dest: 'build/app.min.js',
importToGlobal: {
'@my-namespace/*': 'myGlobalVar.*'
}
},
// ...
}
In WordPress mode Roller imports @wordpress/* into the global variable wp.*.
Using globalToImport would be a reverse situation of using importToGlobal. Here you can map a global variable name to an import module name. This uses Rollup's inject plugin under the hood.
export default {
build: [
{
src: 'src/index.js',
dest: 'build/app.min.js',
globalToImport: {
// import { Promise } from 'es6-promise'
Promise: [ 'es6-promise', 'Promise' ],
// import { Promise as P } from 'es6-promise'
P: [ 'es6-promise', 'Promise' ],
// import $ from 'jquery'
$: 'jquery',
// import * as fs from 'fs'
fs: [ 'fs', '*' ],
// use a local module instead of a third-party one
'Object.assign': path.resolve( 'src/helpers/object-assign.js' ),
}
},
// ...
}
Using replaceStrings you can map a string to another string during script processing. It can be handy for replacing placeholders with actual values at build time.
export default {
build: [
{
src: 'src/index.js',
dest: 'build/app.min.js',
replaceStrings: {
'process.env.NODE_ENV': 'production',
__buildDate__: () => new Date(),
__buildVersion__: 15
},
replaceInclude: [
path.resolve( 'some/path/' ),
/\.[jt]sx?$/,
/node_modules/
]
},
// ...
}
The given values are encoded with JSON.stringify().
By default, the string replacement applies to all files. Optionally use replaceInclude to specify an array of include file patterns.
The above replacements will convert this:
const buildTimeValues = {
date: __buildDate__,
version: __buildVersion__
}
..into..
const buildTimeValues = {
date: "2025-09-16T15:26:54.280Z",
version: 15
}
This is similar to replaceStrings, but it replaces the source with literal code instead of JSON string. It's useful for replacing a placeholder with code that evaluates to a value at run time.
To demonstrate the difference:
{
replaceCode: {
'process.env.NODE_ENV': '"production"'
__currentDate__: 'new Date()'
__currentVersion__: '15'
}
}
The date constant will be replaced by the given code to get a new Date instance, instead of a string value evaluated at build time. The version constant will be replaced by the code 15.
The above replacements will convert this:
const runTimeValues = {
date: __currentDate__,
version: __currentVersion__
}
..into..
const runTimeValues = {
date: new Date(),
version: 15
}
HTML files are compiled using a template engine called eta. Visit the link for its documentation.
For the HTML build task, the src property can be a single file name or glob syntax for multiple files. In the latter case, the dest property must specify the destination directory name. (It can be also for single file.)
If an optional config property serve is defined, a static file server is started during the dev and serve command.
It is an object with:
dir - Serve from directory - Relative path such as .port - Serve from port - Optional: default is 3000To start your own server, define the node property.
node - Require script file pathThis can be used with or without the dir property.
Run the format command to automatically format files to code standard.
It requires the config property format, which is a string or an array of path patterns to match.
Use * as wildcard, ** to match any directory levels, and ! to exclude pattern. Use {} and a comma-separated list to match multiple items.
Folders named node_modules and vendor are excluded by default.
format: 'src'
format: [
'assets',
'!assets/build',
'**/*.php',
'!test'
]
Run the archive command to create a zip package of the project.
It requires the config property archive. It is an object with
src - Source of all files: string or an array of path patterns to matchdest - Path to destination file with extension .zipexclude - Optional: String or an array of path patterns to match folders and files to exclude from the packageRun the install command to install dependencies for production or development.
npx roll install
This requires the config property install. It is a list of objects which define a dependency.
zip - Zip package URL, orgit - Git repository URLbranch - Git branch - Optional: default is maindest - Path to destination folderIt can be useful to run this as postinstall script, to prepare a project.
An optional config property installDev defines dependencies needed only during development, such as third-party plugins or companion libraries.
These can be installed with the --dev option.
npx roll install --dev
To update the dependencies, run the update command. Git repositories pull from remote origin, and zip sources are downloaded optionally by prompting yes or no.
Use the --dev option to update dev dependencies also.
Tangible Roller is the next generation of the build tool. It's much faster, and better compatible with Node.js version 12 and above.
The configuration schema in tangible.config.js is almost the same, except:
JS files with React JSX syntax must have file extension .jsx
For each build task's config, the watch property is no longer needed and can be removed. All imported files are automatically watched for changes.
Similarly, the task property (js/sass/html) can be removed. The task type is automatically inferred from the file extension in src property.
FAQs
Build project assets using Rollup and ESBuild
We found that @tangible/roller demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.