Skip to content

🐛 Import Sorting is Unsafe (Breaks Subclass Dependencies) #7724

@humanchimp

Description

@humanchimp

Environment information

CLI:
  Version:                      2.2.5
  Color support:                true

Platform:
  CPU Architecture:             aarch64
  OS:                           macos

Environment:
  BIOME_LOG_PATH:               unset
  BIOME_LOG_PREFIX_NAME:        unset
  BIOME_CONFIG_PATH:            unset
  BIOME_THREADS:                unset
  NO_COLOR:                     unset
  TERM:                         xterm-256color
  JS_RUNTIME_VERSION:           v22.20.0
  JS_RUNTIME_NAME:              node
  NODE_PACKAGE_MANAGER:         npm/11.6.1

Biome Configuration:
  Status:                       Not set
  Path:                         unset

Workspace:
  Open Documents:               0

What happened?

Import Sorting Breaks Subclass Dependencies

ES module import order matters at runtime because ES modules use live bindings and respect top-down evaluation. Tools like Biome that automatically sort imports alphabetically are not aware of dependency order between exports, which can easily break code. Even a simple subclass can fail if its superclass is referenced before it is initialized.


Minimal Reproduction

Project Structure

├── dist
│   └── bundle.js
├── package-lock.json
├── package.json
├── rollup.config.js
└── src
    ├── Aardvark.js
    ├── Animal.js
    └── index.js

1️⃣ src/Animal.js

// src/Animal.js
export class Animal{}  // Base class

2️⃣ src/Aardvark.js

// src/Aardvark.js
export class Aardvark extends Animal{}  // Depends on Animal being initialized first

3️⃣ src/index.js

// src/index.js
export { Animal } from './Animal.js';
export { Aardvark } from './Aardvark.js';  // Depends on Animal being exported first

4️⃣ rollup.config.js

import path from 'path';

export default {
  input: path.resolve('./src/index.js'),
  output: {
    file: path.resolve('./dist/bundle.js'),
    format: 'esm'
  }
};

5️⃣ Build Steps

# Normal Rollup build preserves order -> works
rollup -c rollup.config.js

Generated dist/bundle.js:

const Animal$1 = class Animal {};  // Animal defined first

class Aardvark extends Animal {}   // Aardvark safe: Animal exists

export { Aardvark, Animal$1 as Animal };

✅ Works correctly because Animal is initialized before Aardvark.


6️⃣ Biome "fix" (alphabetic sorting)

npx biome check --fix
rollup -c rollup.config.js

Generated dist/bundle.js:

class Aardvark extends Animal {}    // ❌ Illegal forward reference
let Animal$1 = class Animal {};
export { Aardvark, Animal$1 as Animal };
  • Aardvark now references Animal before it is initialized.
  • Runtime error: Illegal forward reference of let variable.

Why Alphabetic Sorting Is Unsafe

  1. ES module evaluation is top-down. Classes and let/const cannot be referenced before initialization.
  2. Dependencies matter. Any subclass or code referencing another export must come after its dependency.
  3. Live bindings make re-exports sensitive. Even if it looks like a harmless re-export, evaluation order determines correctness.
  4. Alphabetic sorting ignores semantics. Tools cannot infer the correct initialization order, so automatic reordering can break code.

Conclusion: Sorting ES module imports alphabetically is fundamentally unsafe in the presence of subclassing, circular dependencies, or any dependent exports.

Expected result

It should not sort imports unless --unsafe is also used

Code of Conduct

  • I agree to follow Biome's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    S-Needs triageStatus: this issue needs to be triaged

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions