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 🙏

© 2026 – Pkg Stats / Ryan Hefner

sails-hook-shipwright

v1.1.0

Published

The modern asset pipeline for Sails

Downloads

909

Readme

Shipwright

The modern asset pipeline for Sails.js, powered by Rsbuild.

Shipwright replaces the legacy Grunt-based asset pipeline with a fast, modern bundler that supports TypeScript, ES modules, LESS/SASS, and Hot Module Replacement out of the box.

Why Shipwright?

| Feature | Grunt (legacy) | Shipwright | | ---------------------- | -------------- | ---------- | | Build speed | ~16s | ~1.4s | | JS bundle size | 3.0MB | 229KB | | CSS bundle size | 733KB | 551KB | | Hot Module Replacement | No | Yes | | TypeScript | No | Yes | | ES Modules | No | Yes | | Tree Shaking | No | Yes |

Benchmarks from fleetdm.com migration (fleetdm/fleet#38079)

Installation

npm install sails-hook-shipwright --save

Disable the grunt hook in .sailsrc:

{
  "hooks": {
    "grunt": false
  }
}

Quick Start

Shipwright works with zero configuration for most apps. Just create your entry point:

assets/
  js/
    app.js       # Auto-detected entry point
  styles/
    importer.less  # Auto-detected styles entry

In your layout, use the shipwright helpers:

<!DOCTYPE html>
<html>
<head>
  <%- shipwright.styles() %>
</head>
<body>
  <!-- your content -->
  <%- shipwright.scripts() %>
</body>
</html>

That's it! Shipwright will bundle your JS, compile your styles, and inject the appropriate tags.

Configuration

Create config/shipwright.js to customize behavior:

module.exports.shipwright = {
  js: {
    entry: 'assets/js/app.js' // optional, auto-detected by default
  },
  styles: {
    entry: 'assets/styles/app.css' // optional, auto-detected by default
  },
  build: {
    // Rsbuild configuration - see https://rsbuild.dev/config/
  }
}

Most apps don't need a config file at all - shipwright auto-detects entry points and uses sensible defaults:

  • JS inject default: ['dependencies/**/*.js']
  • CSS inject default: ['dependencies/**/*.css']

Entry Points

Shipwright auto-detects entry points in this order:

JavaScript:

  1. assets/js/app.js
  2. assets/js/main.js
  3. assets/js/index.js

Styles:

  1. assets/styles/importer.less
  2. assets/styles/importer.scss
  3. assets/styles/importer.css
  4. assets/styles/main.less
  5. assets/styles/main.scss
  6. assets/styles/main.css
  7. assets/styles/app.less
  8. assets/styles/app.scss
  9. assets/styles/app.css
  10. assets/css/app.css
  11. assets/css/main.css

Two Bundling Modes

Modern Mode (ES Modules)

For new apps or apps using import/export:

// assets/js/app.js
import { setupCloud } from './cloud.setup'
import { formatDate } from './utilities/format'

setupCloud()

Shipwright detects the single entry point and bundles all imports.

Legacy Mode (Glob Patterns)

For existing apps that concatenate scripts without ES modules (like Grunt's pipeline.js):

// config/shipwright.js
module.exports.shipwright = {
  js: {
    entry: [
      'js/cloud.setup.js',
      'js/components/**/*.js',
      'js/utilities/**/*.js',
      'js/pages/**/*.js'
    ]
  }
}

Files are concatenated in the specified order, preserving the global scope behavior of the legacy pipeline. This is a drop-in replacement for tasks/pipeline.js.

Inject vs Entry

  • entry - Files bundled together by Rsbuild (minified, tree-shaken, hashed)
  • inject - Files loaded as separate <script> or <link> tags before the bundle

Use inject for vendor libraries that need to be loaded separately:

module.exports.shipwright = {
  js: {
    inject: [
      'dependencies/sails.io.js',
      'dependencies/lodash.js',
      'dependencies/jquery.min.js',
      'dependencies/vue.js',
      'dependencies/**/*.js' // catch remaining dependencies
    ]
  }
}

The order is preserved, and duplicates are automatically removed.

TypeScript Support

Shipwright supports TypeScript out of the box. Just use .ts or .tsx files:

// config/shipwright.js
module.exports.shipwright = {
  js: {
    entry: 'assets/js/app.ts'
    // or with glob patterns:
    // entry: ['js/**/*.ts', 'js/**/*.tsx']
  }
}

No tsconfig.json required for basic usage. Add one if you want strict type checking.

LESS/SASS Support

Install the appropriate plugin:

# For LESS
npm install @rsbuild/plugin-less --save-dev

# For SASS/SCSS
npm install @rsbuild/plugin-sass --save-dev

Add the plugin to your config:

const { pluginLess } = require('@rsbuild/plugin-less')

module.exports.shipwright = {
  build: {
    plugins: [pluginLess()]
  }
}

Shipwright auto-detects your styles entry point (importer.less, main.scss, etc.).

Hot Module Replacement

In development, Shipwright provides HMR via Rsbuild's dev server. Changes to your JS and CSS files are instantly reflected in the browser without a full page reload.

HMR is enabled automatically when NODE_ENV !== 'production'.

Production Builds

In production (NODE_ENV=production), Shipwright:

  • Minifies JS and CSS
  • Adds content hashes for cache busting (app.a1b2c3d4.js)
  • Enables tree shaking to remove unused code
  • Generates a manifest for asset versioning

Output Structure

.tmp/public/
  js/
    app.js          # development
    app.a1b2c3d4.js # production (with hash)
  css/
    styles.css
    styles.b2c3d4e5.css
  manifest.json     # maps entry names to hashed filenames
  dependencies/     # copied from assets/dependencies
  images/           # copied from assets/images
  ...

Path Aliases

Shipwright configures these aliases by default:

  • @assets/js
  • ~assets
// In your JS files
import utils from '@/utilities/helpers'
import styles from '~/styles/components.css'

Advanced Configuration

Pass any Rsbuild configuration via the build key:

const { pluginLess } = require('@rsbuild/plugin-less')
const { pluginReact } = require('@rsbuild/plugin-react')

module.exports.shipwright = {
  build: {
    plugins: [pluginLess(), pluginReact()],
    output: {
      // Custom output options
    },
    performance: {
      // Custom performance options
    }
  }
}

See Rsbuild Configuration for all available options.

Migrating from Grunt

  1. Install shipwright and disable grunt:
npm install sails-hook-shipwright --save
npm install @rsbuild/plugin-less --save-dev  # if using LESS
// .sailsrc
{
  "hooks": {
    "grunt": false
  }
}
  1. Create config/shipwright.js based on your tasks/pipeline.js:
// If your pipeline.js has:
// var jsFilesToInject = [
//   'dependencies/sails.io.js',
//   'dependencies/lodash.js',
//   'js/cloud.setup.js',
//   'js/**/*.js'
// ]

// Your shipwright.js becomes:
const { pluginLess } = require('@rsbuild/plugin-less')

module.exports.shipwright = {
  js: {
    entry: [
      'js/cloud.setup.js',
      'js/components/**/*.js',
      'js/utilities/**/*.js',
      'js/pages/**/*.js'
    ],
    inject: [
      'dependencies/sails.io.js',
      'dependencies/lodash.js',
      'dependencies/**/*.js'
    ]
  },
  build: {
    plugins: [pluginLess()]
  }
}
  1. Update your layout to use shipwright helpers:
- <!--STYLES-->
- <!--STYLES END-->
+ <%- shipwright.styles() %>

- <!--SCRIPTS-->
- <!--SCRIPTS END-->
+ <%- shipwright.scripts() %>
  1. Remove the tasks/ directory (optional, but recommended).

API

View Helpers

shipwright.scripts()

Returns <script> tags for:

  1. Injected files (from js.inject patterns)
  2. Bundled files (from manifest)

shipwright.styles()

Returns <link> tags for:

  1. Injected files (from styles.inject patterns)
  2. Compiled styles (from manifest)

Troubleshooting

"Missing @rsbuild/plugin-less"

Install the required plugin:

npm install @rsbuild/plugin-less --save-dev

And add it to your config:

const { pluginLess } = require('@rsbuild/plugin-less')
module.exports.shipwright = {
  build: { plugins: [pluginLess()] }
}

Scripts loading twice

Check that your inject patterns don't overlap with files in the bundle. Shipwright automatically deduplicates, but explicit is better than implicit.

HMR not working

Ensure NODE_ENV is not set to production in development.

License

The Sails framework is free and open-source under the MIT License.