Skip to content

Task 3: Bundle build — vendor @herb-tools/linter as Node-free JS #5

Description

@jleo3

Goal

Build and commit vendor/herb-linter.js — a 1.8MB IIFE bundle of @herb-tools/linter@0.10.3 with zero Node built-in references. Node is required to build; never to use.

Files

  • rakelib/bundle.rakerake bundle:build
  • stubs/config.mjs — replaces @herb-tools/config (which needs fs)
  • js/entry.mjs — bundle entry point (excludes cli.js, lint-worker.js, custom-rule-loader.js)
  • lib/herb/embedded/bundle.rbBundle.source, .linter_version, .herb_versions
  • vendor/herb-linter.js — generated, committed
  • spec/bundle_spec.rb

Interface produced

  • Bundle.source → String (the vendored JS)
  • Bundle.linter_version"0.10.3"
  • Bundle.herb_versions["0.10.3"] — the validated herb gem versions for AST drift detection

Why deep dist/ imports

The bundle entry imports directly from dist/linter.js and dist/rules.js, bypassing the package exports map. This excludes the three files with Node built-in references: cli.js, lint-worker.js, and custom-rule-loader.js.

Note: dedent is an undeclared dependency of @herb-tools/linter@0.10.3 (reported upstream at marcoroth/herb#2213, fixed in marcoroth/herb#2214). Anyone importing dist/rules.js directly hits ERR_MODULE_NOT_FOUND. The bundle build must install dedent explicitly until that fix ships.

Bundle build guard

rake bundle:build must fail if any Node built-in survives into the output. This is enforced by scanning the result for require("node:*") / require("fs") etc. before committing.

Steps (TDD)

  • Write failing bundle tests (source size, linter version, herb versions, no Node builtins)
  • Run to confirm failure (no bundle yet)
  • Create stubs/config.mjs
  • Create js/entry.mjs
  • Create rakelib/bundle.rake
  • Run rake bundle:build (requires Node + esbuild)
  • Implement bundle.rb
  • Run to confirm all bundle tests pass
  • Commit bundle + source together

Depends on

Nothing from prior tasks. Node + esbuild required at build time only.

Acceptance Criteria

  • rake bundle:build produces vendor/herb-linter.js, an IIFE bundle of @herb-tools/linter@0.10.3, committed to the repo.
  • rake bundle:build fails (nonzero exit) if the output contains any require("node:..."), require("fs"), or other Node built-in reference — enforced by a scan step that runs before the file is written/committed.
  • Bundle.source returns the vendored JS as a String; Bundle.linter_version == "0.10.3"; Bundle.herb_versions == ["0.10.3"].
  • The bundle entry (js/entry.mjs) excludes cli.js, lint-worker.js, and custom-rule-loader.js (the three files with Node built-in references), importing directly from dist/linter.js / dist/rules.js.
  • stubs/config.mjs stands in for @herb-tools/config in the bundle (no fs access at bundle-eval time).
  • The build installs dedent explicitly (undeclared dependency of @herb-tools/linter@0.10.3, dedent is not declared as a dependency, breaking direct dist/ imports marcoroth/herb#2213) so dist/rules.js resolves without ERR_MODULE_NOT_FOUND.
  • All spec/bundle_spec.rb tests (source presence, linter version, herb versions, no-Node-builtins) pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions