Skip to content

Replace Grunt with npm scripts and modern tooling #330

Description

@palcarazm

Short Description of the Feature

Replace the Grunt task runner with npm scripts and existing tooling (tsc, rollup, postcss). Grunt currently acts only as an orchestration layer—all actual build work is already delegated to modern tools. This migration removes Grunt entirely, simplifies the build pipeline, improves maintainability, adds a clean TypeScript compilation pipeline with temporary output isolation, and standardizes copyright banner injection across all output files.

Expected Benefits

  • Reduced dependencies: Remove outdated Grunt and its plugins (grunt, grunt-banner, grunt-contrib-clean, grunt-contrib-copy, grunt-exec)
  • Simpler maintenance: npm scripts are more transparent and don't require learning Grunt's API
  • Cleaner build artifacts: TypeScript outputs to dist/tmp/js (temporary) instead of src/main/js, preventing accidental commits of compiled files
  • Better watch mode: Use concurrently for parallel watching of CSS, TS, and JS
  • Cross-platform compatibility: All scripts use Node.js APIs or cross-platform CLI tools (rimraf)
  • Faster builds: Remove Grunt's orchestration overhead
  • Version workflow: Cleaner npm version integration using the version hook
  • Modern distribution: Add dist/bootstrap5-toggle.css alongside existing css/ for future migration path
  • Standardized banners: Dynamic copyright headers injected during build using scripts/package-banner.js

Acceptance Criteria

  • Remove all Grunt dependencies: Uninstall grunt, grunt-banner, grunt-contrib-clean, grunt-contrib-copy, grunt-exec from devDependencies
  • Delete Gruntfile.cjs: Remove the file entirely
  • Add new devDependencies: Install rimraf, concurrently, postcss-banner
  • Create scripts/package-banner.js: Export bannerContent function that reads package.json and returns formatted copyright banner
  • Update tsconfig.json:
    • Change "outDir": "dist/tmp/js" (was "src/main/js")
    • Keep "declaration": true and "declarationDir": "src/main/@types"
    • Keep "rootDir": "src/main/ts"
  • Update rollup.config.js:
    • Import bannerContent from ./scripts/package-banner.js
    • Use output.banner for all formats (UMD, CJS, ESM)
    • Change all input paths from src/main/js/*.js to dist/tmp/js/*.js
    • Keep existing output destinations: js/ (UMD) and dist/ (CJS/ESM)
    • Keep rollup-plugin-dts for TypeScript declaration bundling (input remains src/main/@types/index.d.ts)
  • Create postcss.config.js:
    • Import bannerContent from ./scripts/package-banner.js
    • Use postcss-banner plugin with bannerContent
    • Define dev and production outputs
  • Update package.json scripts:
    • "clean": "rimraf js css dist dist/tmp"
    • "prebuild": "npm run clean"
    • "build:ts": "tsc"
    • "build:copy-jquery": "cp src/main/js/index.jquery.js dist/tmp/js/index.jquery.js"
    • "build:rollup": "rollup -c"
    • "build:css": "postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.css --map"
    • "build:css-min": "postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.min.css --map --env production && postcss src/main/css/bootstrap5-toggle.css -o dist/bootstrap5-toggle.css --map --env production"
    • "build:clean-tmp": "rimraf dist/tmp"
    • "build": "npm run prebuild && npm run build:ts && npm run build:copy-jquery && npm run build:rollup && npm run build:css && npm run build:css-min && npm run build:clean-tmp"
    • "watch": "concurrently \"tsc --watch\" \"rollup -c --watch\" \"postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.css --map --watch\" \"postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.min.css --map --env production --watch\" \"postcss src/main/css/bootstrap5-toggle.css -o dist/bootstrap5-toggle.css --map --env production --watch\""
    • "readme": "node -e \"const fs=require('fs'); const pkg=require('./package.json'); fs.writeFileSync('README.md', fs.readFileSync('README.template.md','utf8').replaceAll('#version#', pkg.version))\""
    • "version": "npm run build && npm run readme && doctoc README.md --github && git add -A"
    • "prepack": "npm run build && npm run readme && doctoc README.md --github"
  • Update package.json files field: Keep ["css/*", "js/*", "dist"] (unchanged)
  • Add .gitignore entry: Add dist/tmp/ to ignore compiled temporary files
  • Remove src/main/js/ from version control: Only index.jquery.js should remain; all other .js and .js.map files are now generated in dist/tmp/
  • Build output verification:
    • js/bootstrap5-toggle.ecmas.js and .min.js unchanged (UMD)
    • js/bootstrap5-toggle.jquery.js and .min.js unchanged (UMD)
    • css/bootstrap5-toggle.css and .min.css unchanged (with banners)
    • dist/bootstrap5-toggle.cjs (existing, with banners)
    • dist/bootstrap5-toggle.mjs (existing, with banners)
    • dist/bootstrap5-toggle.d.ts (existing, with banners)
    • dist/bootstrap5-toggle.css (new, minified, with banner)
    • All files have copyright banners with correct version from package.json
  • Cleanup: dist/tmp removed after build (temporary TypeScript outputs and copied jQuery wrapper)
  • Testing: All existing tests pass (npm test)

Documentation

Build Pipeline Architecture

The new build pipeline consists of the following stages:

  1. Clean: Remove all output directories (js/, css/, dist/, dist/tmp/)
  2. TypeScript Compilation:
    • Compiles src/main/ts/*.ts → dist/tmp/js/*.js
    • Generates declarations in src/main/@types/*.d.ts
  3. Copy jQuery Wrapper:
    • Copies src/main/js/index.jquery.js → dist/tmp/js/index.jquery.js
    • (This file is hand-written and must remain in version control)
  4. Rollup Bundling:
    • Consumes dist/tmp/js/index.js (from TS) and dist/tmp/js/index.jquery.js (copied)
    • Outputs UMD bundles to js/ (ECMAS and jQuery versions)
    • Outputs CJS and ESM bundles to dist/
    • Bundles declarations from src/main/@types/index.d.ts → dist/bootstrap5-toggle.d.ts
    • Injects copyright banner via output.banner
  5. PostCSS Processing:
    • Processes src/main/css/bootstrap5-toggle.css
    • Outputs dev (unminified) and minified versions to css/
    • Outputs minified version to dist/bootstrap5-toggle.css
    • Injects copyright banner via postcss-banner
  6. Cleanup: Remove dist/tmp/ temporary directory
  7. Readme Generation: Injects version number from package.json into README.md

Package Distribution Strategy

The package maintains backward compatibility while adding modern distribution paths:

  • Canonical CSS: css/bootstrap5-toggle.min.css (via "style" field)
  • Additional CSS: dist/bootstrap5-toggle.css (minified, for future migration)
  • Canonical JS (CDN/browser): js/bootstrap5-toggle.ecmas.min.js (via "browser" field)
  • Modern JS (Node/bundlers): dist/bootstrap5-toggle.cjs and dist/bootstrap5-toggle.mjs (via "main" and "module" fields)
  • TypeScript Types: dist/bootstrap5-toggle.d.ts (via "types" and "typings" fields)

Watch Mode Behavior

npm run watch runs all tools in parallel using concurrently:

  • tsc --watch: Recompiles TypeScript on changes
  • rollup -c --watch: Rebundles on changes to dist/tmp/js/
  • postcss --watch (3 instances): Rebuilds CSS (dev, minified, and dist) on changes

Note: Loss of sequential dependency is accepted. Each tool watches independently. Developers must ensure TypeScript compiles successfully before Rollup can pick up changes (Rollup will retry on subsequent file system events).

Version Workflow

The version hook runs automatically during npm version:

  1. npm version verifies clean working directory
  2. Bumps version in package.json
  3. Runs version hook: npm run build && npm run readme && doctoc README.md --github && git add -A
  4. Commits changes with new version tag

All generated files (built outputs, README, table of contents) are automatically staged and committed with the version bump.

Additional Comments

Risks & Mitigations

Risk Mitigation
Broken Rollup inputs: Changing input path from src/main/js/ to dist/tmp/js/ could break if TypeScript compilation fails Build script runs tsc before Rollup; failure halts the pipeline
Missing jQuery wrapper: index.jquery.js must be copied to dist/tmp/js/ before Rollup Explicit copy step in build script; file remains in version control
Declaration bundling: rollup-plugin-dts expects input at src/main/@types/index.d.ts Unchanged from current configuration; declarations are correctly emitted
Stale dist/tmp: If cleanup fails, temporary files could be published dist/tmp/ is added to .gitignore and files field excludes it; cleanup step ensures removal
PostCSS watch duplication: Three separate PostCSS watch processes run concurrently Acceptable overhead; each process watches the same input file but outputs to different destinations
Version hook git add: git add -A could stage unintended changes npm version requires clean working directory; only build artifacts and README changes are staged
Copyright banner accuracy: Banner must reflect correct version and copyright holders scripts/package-banner.js reads package.json dynamically; version injected at build time

Migration Path for Users

  • Existing users: No breaking changes. All existing import paths (css/, js/) remain functional.
  • Modern bundler users: Can now import from dist/ for CJS/ESM modules with TypeScript support.
  • Future deprecation: The dist/bootstrap5-toggle.css file provides a migration path; css/ may be deprecated in a future major version.

Rollback Plan

If migration causes issues:

  1. Restore Gruntfile.cjs from git
  2. Restore previous package.json scripts and dependencies
  3. Reinstall Grunt dependencies: npm install grunt grunt-banner grunt-contrib-clean grunt-contrib-copy grunt-exec
  4. Revert tsconfig.json outDir to src/main/js
  5. Revert rollup.config.js inputs to src/main/js/
  6. Remove new dependencies: npm uninstall rimraf concurrently postcss-banner

The existing js/ and css/ outputs are untouched by this change, so CDN and browser users are unaffected. Only npm users with modern bundlers will notice improvements (TypeScript types, CJS/ESM modules).

Feature Request Checklist

  • Confirm that you agree to follow the project's code of conduct.
  • Confirm that you have reviewed open and rejected feature requests to ensure novelty.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions