Skip to content

About

This plugin lets you import CoffeeScript files when bundling with Esbuild

Resources

Contributing

Stars

25 stars

Watchers

2 watching

Forks

Repository files navigation

esbuild-coffeescript

esbuild-coffeescript npm version Downloads

An esbuild plugin that compiles .coffee and .litcoffee files with CoffeeScript 2, so you can import and bundle them like any other module.

Install

pnpm add --dev esbuild-coffeescript esbuild
# or
npm install --save-dev esbuild-coffeescript esbuild

Requires Node.js 20+ and esbuild 0.25.9 or later.

Quick start

# main.coffee
answer = 42
console.log("the answer is #{answer}")
// build.js
import esbuild from "esbuild";
import coffeeScriptPlugin from "esbuild-coffeescript";

await esbuild.build({
  entryPoints: ["main.coffee"],
  bundle: true,
  outfile: "out.js",
  plugins: [coffeeScriptPlugin()],
});

CoffeeScript compiles main.coffee to this before esbuild bundles it:

(function () {
  var answer;
  answer = 42;
  console.log(`the answer is ${answer}`);
}).call(this);

Options

Pass options to the plugin: coffeeScriptPlugin({ bare: true }).

Option Type Default Description
bare boolean false Omit the top-level function safety wrapper.
header boolean false Add a "Generated by CoffeeScript" header.
inlineMap boolean false Append a base64-encoded source map comment.
literate boolean true for .litcoffee Parse every file as Literate CoffeeScript. Leave unset to detect it from the file extension.
transpile object undefined Babel options for transpilation.
// Development: source maps and a header
coffeeScriptPlugin({ bare: true, header: true, inlineMap: true });

// Production: bare output, no header
coffeeScriptPlugin({ bare: true });

// Transpile with Babel
coffeeScriptPlugin({
  bare: true,
  transpile: { presets: ["@babel/preset-env"], targets: { node: "18" } },
});

Examples

Literate CoffeeScript

.litcoffee files work without configuration. Indented blocks are code; everything else is Markdown.

<!-- docs.litcoffee -->

# My Application

This is documentation written in Markdown with embedded CoffeeScript:

    class Calculator
      add: (a, b) -> a + b
      multiply: (a, b) -> a * b

    calc = new Calculator()
    console.log calc.add(5, 3)
esbuild.build({
  entryPoints: ["docs.litcoffee"],
  bundle: true,
  outfile: "calculator.js",
  plugins: [coffeeScriptPlugin()],
});

Multiple files

# models/User.coffee
class User
  constructor: (@name, @email) ->

  greet: -> "Hello, I'm #{@name}"

module.exports = { User }
# main.coffee
{User} = require('./models/User.coffee')

user = new User('John', 'john@example.com')
console.log user.greet()
esbuild.build({
  entryPoints: ["main.coffee"],
  bundle: true,
  format: "esm",
  outfile: "app.js",
  plugins: [coffeeScriptPlugin({ bare: true })],
});

Web application bundle

esbuild.build({
  entryPoints: ["src/app.coffee"],
  bundle: true,
  format: "esm",
  minify: true,
  outdir: "dist",
  sourcemap: true,
  target: "es2020",
  plugins: [coffeeScriptPlugin()],
});

Alongside TypeScript

esbuild handles .ts itself; the plugin only touches CoffeeScript files.

esbuild.build({
  entryPoints: ["src/main.ts", "src/utils.coffee"],
  bundle: true,
  format: "esm",
  outdir: "dist",
  plugins: [coffeeScriptPlugin({ bare: true })],
});

Watch mode

const context = await esbuild.context({
  entryPoints: ["src/app.coffee"],
  bundle: true,
  outdir: "dist",
  plugins: [coffeeScriptPlugin()],
});

await context.watch();

Each plugin instance caches compiled output per file for the lifetime of the build context, so rebuilds only recompile files whose source changed.

Errors

CoffeeScript syntax errors are reported as esbuild errors with the file, line and column:

# invalid.coffee
hello . 8
✘ [ERROR] unexpected number [plugin coffeescript]

    invalid.coffee:1:8:
      1 │ hello . 8
        ╵         ^

Troubleshooting

  • Could not resolve "./utils.coffee": esbuild resolves imports relative to the importing file. Check that the path and extension are correct.

  • Source maps missing: enable esbuild's sourcemap option, and inlineMap: true on the plugin to map back to the CoffeeScript source:

    esbuild.build({
      sourcemap: true, // or "inline"
      plugins: [coffeeScriptPlugin({ inlineMap: true })],
    });

Still stuck? Search the GitHub issues.

Migrating

v3 → v4: the ast and filename options are removed. ast returned an AST object that esbuild rejects; the plugin now always passes each file's path to CoffeeScript, so error locations and source maps point at the right file.

v2 → v3: requires Node.js 20+ and esbuild 0.25.9+, uses CoffeeScript 2.7, and is ESM-first:

// v2
const coffeeScriptPlugin = require("esbuild-coffeescript");

// v3+
import coffeeScriptPlugin from "esbuild-coffeescript";

Contributing

See the Contributing Guide.

git clone https://github.com/johnie/esbuild-coffeescript.git
cd esbuild-coffeescript
pnpm install
pnpm test        # or test:watch, test:coverage
pnpm build

License

MIT © Johnie Hjelm

About

This plugin lets you import CoffeeScript files when bundling with Esbuild

Resources

Contributing

Stars

25 stars

Watchers

2 watching

Forks

Releases

Used by

Contributors

Languages