vite-angular-filesort
v1.0.0
Published
Vite plugin to sort AngularJS files by module dependencies (gulp-angular-filesort replacement)
Maintainers
Readme
⚡ vite-angular-filesort
Vite plugin that sorts AngularJS source files by module dependencies — a modern, zero-dependency replacement for gulp-angular-filesort.
🤔 Who is this for?
If you maintain a legacy AngularJS (1.x) application and are migrating your build tooling from Gulp to Vite, this plugin is for you. It replaces the gulp-angular-filesort + gulp-inject pipeline with a single Vite plugin that automatically scans your source files, resolves angular.module() dependencies, and injects <script> tags into your index.html in the correct order.
No need to rewrite your AngularJS code or manually manage script loading — just drop in the plugin and remove Gulp from your stack.
✨ Features
- 🔍 Parses
angular.module()declarations and references - 🔀 Topological sort ensures dependencies load before dependents
- 💉 Automatic
<script>injection intoindex.htmlviatransformIndexHtml - 🛠️ Programmatic API for custom build pipelines
- 📦 Zero runtime dependencies — uses only Node.js native APIs
- 🏷️ Written in TypeScript with full type definitions
📥 Installation
npm install vite-angular-filesort --save-dev🚀 Usage
Vite plugin
Add the plugin to your vite.config.ts. It automatically scans your source directory, sorts all AngularJS files by module dependencies, and injects <script> tags into your index.html in the correct order.
// vite.config.ts
import { defineConfig } from "vite";
import { angularFilesort } from "vite-angular-filesort";
export default defineConfig({
plugins: [
angularFilesort({
scanRoot: "src", // directory to scan (relative to project root)
include: ["**/*.js"], // file patterns to include
exclude: [ // file patterns to exclude
"node_modules/**",
"bower_components/**",
"**/*.spec.js",
"**/*.test.js",
],
}),
],
});That's it. The plugin injects sorted <script> tags into the <body> of your index.html:
<!-- Generated by vite-angular-filesort -->
<script src="/src/shared/shared.module.js"></script>
<script src="/src/shared/api.service.js"></script>
<script src="/src/home/home.module.js"></script>
<script src="/src/home/home.controller.js"></script>
<script src="/src/app.module.js"></script>🧩 Programmatic API
sortAngularFiles(filePaths: string[]): Promise<string[]>
Reads files from disk and returns paths sorted by dependency order.
import { sortAngularFiles } from "vite-angular-filesort";
const sorted = await sortAngularFiles([
"src/app.module.js",
"src/home/home.module.js",
"src/shared/shared.module.js",
]);
// => ['src/shared/shared.module.js', 'src/home/home.module.js', 'src/app.module.js']sortAngularFilesSync(files: { filePath: string; content: string }[]): string[]
Sorts pre-read files by dependency order. Useful when you already have file contents in memory.
import { sortAngularFilesSync } from "vite-angular-filesort";
const sorted = sortAngularFilesSync([
{ filePath: "app.js", content: "angular.module('app', ['service'])" },
{ filePath: "service.js", content: "angular.module('service', [])" },
]);
// => ['service.js', 'app.js']parseNgDependencies(source: string): NgDependencyInfo
Low-level parser that extracts AngularJS module information from source code.
import { parseNgDependencies } from "vite-angular-filesort";
const info = parseNgDependencies("angular.module('app', ['service', 'utils'])");
// => {
// modules: { app: ['service', 'utils'] },
// dependencies: ['service', 'utils']
// }⚙️ How it works
🔍 Parse — Each file is scanned for
angular.module()calls:angular.module('name', ['dep1', 'dep2'])— setter (module declaration with dependencies)angular.module('name')— getter (reference to an existing module)
🕸️ Build dependency graph — Maps module names to the files that declare them, then creates directed edges between files based on module dependencies.
🔀 Topological sort — DFS-based topological sort produces an ordering where every dependency appears before its dependents.
🧪 Edge cases
| Scenario | Behavior |
|---------------------------------------------------|---------------------------------------------------|
| File without angular.module() | Included in output, no ordering constraints |
| Dependency on external module (e.g., ui.router) | Silently ignored — no file declares it |
| Multiple modules in one file | All declarations tracked; intra-file deps resolved |
| The ng module | Always excluded from dependency tracking |
| Circular dependencies | Throws CyclicDependencyError |
🔄 Migrating from Gulp
Replace this Gulp pipeline:
// gulpfile.js (before)
const gulp = require("gulp");
const inject = require("gulp-inject");
const angularFilesort = require("gulp-angular-filesort");
gulp.task("index", function () {
return gulp.src("./src/index.html")
.pipe(inject(
gulp.src("./src/app/**/*.js").pipe(angularFilesort())
))
.pipe(gulp.dest("./dist"));
});With this Vite config:
// vite.config.ts (after)
import { defineConfig } from "vite";
import { angularFilesort } from "vite-angular-filesort";
export default defineConfig({
plugins: [
angularFilesort({ scanRoot: "src/app" }),
],
});🧑💻 Development
pnpm install
pnpm test # run tests
pnpm run coverage # run tests with 100% coverage check
pnpm run lint # lint with oxlint
pnpm run format # check formatting with oxfmt
pnpm run typecheck # type-check with tsc
pnpm run build # build for distribution📝 Commit conventions
Commits must follow the Conventional Commits format:
feat: add new parser option
fix: handle empty module arrays
docs: update migration guideThis is enforced by commitlint via a husky commit-msg hook — commits that don't match the format will be rejected automatically.
🏷️ Versioning & releasing
This project uses changesets to manage versions and changelogs.
Adding a changeset
After making changes, describe them with a changeset:
pnpm changesetFollow the prompts to select a semver bump (patch, minor, or major) and write a summary of the change. This creates a markdown file in .changeset/ that should be committed alongside your code.
Releasing
On push to main, the CI workflow automatically:
- Detects pending changesets → opens a "Version Packages" PR that bumps
package.json, updatesCHANGELOG.md, and consumes the changeset files. - When that PR is merged → builds the package and publishes the new version to npm.
To release manually:
pnpm run version-packages # bump version + update changelog
pnpm run release # build + publish to npmNote: Automated publishing requires an
NPM_TOKENsecret configured in the GitHub repository settings.
📄 License
MIT
