or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# PostCSS Modules Extract Imports

1

2

PostCSS Modules Extract Imports is a CSS Modules PostCSS plugin that transforms `composes` declarations by extracting import statements and converting them into `:import` pseudo-selectors. It processes CSS Modules compose declarations that reference external CSS files and transforms them into standardized import statements with temporary class name aliases, enabling CSS Modules bundlers to properly handle cross-file composition dependencies.

3

4

## Package Information

5

6

- **Package Name**: postcss-modules-extract-imports

7

- **Package Type**: npm

8

- **Language**: JavaScript

9

- **Installation**: `npm install postcss-modules-extract-imports`

10

11

## Core Imports

12

13

```javascript

14

const extractImports = require("postcss-modules-extract-imports");

15

```

16

17

For PostCSS integration:

18

19

```javascript

20

const postcss = require("postcss");

21

const extractImports = require("postcss-modules-extract-imports");

22

```

23

24

For standalone topological sort utility:

25

26

```javascript

27

const topologicalSort = require("postcss-modules-extract-imports/src/topologicalSort");

28

```

29

30

## Basic Usage

31

32

```javascript

33

const postcss = require("postcss");

34

const extractImports = require("postcss-modules-extract-imports");

35

36

// Basic plugin usage

37

const processor = postcss([extractImports()]);

38

39

// Process CSS with compose declarations

40

const input = `

41

:local(.continueButton) {

42

composes: button from "library/button.css";

43

color: green;

44

}

45

`;

46

47

const result = processor.process(input);

48

console.log(result.css);

49

// Output:

50

// :import("library/button.css") {

51

// button: i__imported_button_0;

52

// }

53

// :local(.continueButton) {

54

// composes: i__imported_button_0;

55

// color: green;

56

// }

57

```

58

59

**Complete Processing Example:**

60

61

```javascript

62

const postcss = require("postcss");

63

const extractImports = require("postcss-modules-extract-imports");

64

65

// Configure plugin with custom options

66

const processor = postcss([

67

extractImports({

68

createImportedName: (name, path) => `imported_${name}_${Math.random().toString(36).substr(2, 5)}`,

69

failOnWrongOrder: false // Allow flexible import ordering

70

})

71

]);

72

73

// Complex CSS with multiple imports and compositions

74

const complexInput = `

75

.header {

76

composes: container from "./layout.css";

77

composes: primary-theme from "./themes.css";

78

}

79

80

.button {

81

composes: btn base-button from "./components.css";

82

composes: shadow from "./effects.css";

83

}

84

85

.navigation {

86

composes: flex-row from "./layout.css";

87

composes: navbar from global;

88

}

89

`;

90

91

const result = processor.process(complexInput);

92

console.log(result.css);

93

```

94

95

## Capabilities

96

97

### Plugin Factory Function

98

99

Creates a PostCSS plugin instance that transforms CSS Modules compose declarations.

100

101

```javascript { .api }

102

/**

103

* Creates a PostCSS plugin that extracts and transforms CSS Modules compose declarations

104

* @param options - Configuration options for the plugin

105

* @returns PostCSS plugin object

106

*/

107

function extractImports(options = {}): PostCSSPlugin;

108

```

109

110

**Usage Example:**

111

112

```javascript

113

const plugin = extractImports({

114

failOnWrongOrder: true,

115

createImportedName: (importName, path) => `custom_${importName}_from_${path.replace(/[^a-zA-Z0-9]/g, '_')}`

116

});

117

118

const processor = postcss([plugin]);

119

```

120

121

### Configuration Options

122

123

The plugin accepts an options object with the following properties:

124

125

```javascript { .api }

126

interface ExtractImportsOptions {

127

/** Custom function to generate imported symbol names */

128

createImportedName?: (importName: string, path: string) => string;

129

/** When true, throws exception for unpredictable import order dependencies */

130

failOnWrongOrder?: boolean;

131

}

132

```

133

134

#### Custom Import Name Generation

135

136

Override the default import name generation strategy.

137

138

```javascript { .api }

139

/**

140

* Custom function to generate imported symbol names

141

* @param importName - The original class name being imported

142

* @param path - The file path being imported from

143

* @returns Custom generated name for the imported symbol

144

*/

145

type CreateImportedNameFunction = (importName: string, path: string) => string;

146

```

147

148

**Default behavior:** Generates names like `i__imported_button_0`, `i__imported_card_1`, etc. Non-word characters in import names are replaced with underscores.

149

150

**Usage Example:**

151

152

```javascript

153

const plugin = extractImports({

154

createImportedName: (importName, path) => {

155

const cleanPath = path.replace(/[^a-zA-Z0-9]/g, '_');

156

return `${importName}_from_${cleanPath}`;

157

}

158

});

159

160

// Input: composes: button from "ui/button.css"

161

// Output: button_from_ui_button_css instead of i__imported_button_0

162

```

163

164

**Advanced Usage Examples:**

165

166

```javascript

167

// Multiple classes from same file

168

const input = `

169

.navigation {

170

composes: button primary large from "./ui/buttons.css";

171

margin: 10px;

172

}

173

`;

174

175

// Global composition

176

const globalInput = `

177

.localButton {

178

composes: btn-primary btn-large from global;

179

color: blue;

180

}

181

`;

182

183

// Mixed local and external composition

184

const mixedInput = `

185

.complexButton {

186

composes: button from "./base.css", primary-style;

187

composes: hover-effect from "./animations.css";

188

}

189

`;

190

```

191

192

#### Import Order Validation

193

194

Enable strict validation of import dependency order.

195

196

```javascript { .api }

197

/**

198

* When true, throws exception for unpredictable import order dependencies

199

* @default undefined (falsy)

200

*/

201

failOnWrongOrder?: boolean;

202

```

203

204

**Usage Example:**

205

206

```javascript

207

// This will throw an error due to inconsistent import order

208

const plugin = extractImports({ failOnWrongOrder: true });

209

210

const problematicCSS = `

211

.aa {

212

composes: b from "./b.css";

213

composes: c from "./c.css";

214

}

215

216

.bb {

217

/* c.css should come before b.css based on .aa rule */

218

composes: c from "./c.css";

219

composes: b from "./b.css";

220

}

221

`;

222

223

// Throws: "Failed to resolve order of composed modules `./b.css`, `./c.css`"

224

```

225

226

### PostCSS Plugin Object

227

228

The plugin factory function returns a standard PostCSS plugin object.

229

230

```javascript { .api }

231

interface PostCSSPlugin {

232

/** Plugin identifier for PostCSS */

233

postcssPlugin: "postcss-modules-extract-imports";

234

/** Plugin preparation function */

235

prepare(): PluginMethods;

236

}

237

238

interface PluginMethods {

239

/** Main processing function called once per CSS root */

240

Once(root: PostCSSRoot, postcss: PostCSSAPI): void;

241

}

242

```

243

244

### Transformation Behavior

245

246

The plugin processes CSS according to these rules:

247

248

#### Supported Compose Syntax

249

250

```css

251

/* Single class from external file */

252

composes: button from "library/button.css";

253

254

/* Multiple classes from external file */

255

composes: button primary from "library/button.css";

256

257

/* Mixed composition (external and local) */

258

composes: button from "library/button.css", local-class;

259

260

/* Global composition */

261

composes: button from global;

262

263

/* Multiple files in one rule */

264

composes: button from "ui/button.css", card from "ui/card.css";

265

```

266

267

#### Output Format

268

269

**Input:**

270

```css

271

:local(.continueButton) {

272

composes: button primary from "library/button.css";

273

color: green;

274

}

275

```

276

277

**Output:**

278

```css

279

:import("library/button.css") {

280

button: i__imported_button_0;

281

primary: i__imported_primary_1;

282

}

283

:local(.continueButton) {

284

composes: i__imported_button_0 i__imported_primary_1;

285

color: green;

286

}

287

```

288

289

#### Global Compose Handling

290

291

```css

292

/* Input */

293

.button {

294

composes: btn-primary from global;

295

}

296

297

/* Output */

298

.button {

299

composes: global(btn-primary);

300

}

301

```

302

303

### Error Handling

304

305

The plugin throws PostCSS CssSyntaxError instances for import order conflicts when `failOnWrongOrder` is enabled.

306

307

```javascript { .api }

308

interface ImportOrderError extends CssSyntaxError {

309

/** Plugin identifier */

310

plugin: "postcss-modules-extract-imports";

311

/** The CSS property that caused the error */

312

word: "composes";

313

/** Error message format */

314

message: string; // "Failed to resolve order of composed modules `path1`, `path2`."

315

}

316

```

317

318

**Error Examples:**

319

320

```javascript

321

const postcss = require("postcss");

322

const extractImports = require("postcss-modules-extract-imports");

323

324

try {

325

const processor = postcss([extractImports({ failOnWrongOrder: true })]);

326

327

// This CSS creates conflicting import order

328

const result = processor.process(`

329

.classA {

330

composes: btn from "./buttons.css";

331

composes: card from "./cards.css";

332

}

333

334

.classB {

335

composes: card from "./cards.css";

336

composes: btn from "./buttons.css"; /* Different order - creates conflict */

337

}

338

`);

339

340

} catch (error) {

341

console.log(error.plugin); // "postcss-modules-extract-imports"

342

console.log(error.word); // "composes"

343

console.log(error.message); // "Failed to resolve order of composed modules `./buttons.css`, `./cards.css`."

344

}

345

```

346

347

### Topological Sort Utility

348

349

The package includes a standalone topological sort utility that can be used independently for dependency graph resolution.

350

351

```javascript { .api }

352

/**

353

* Performs topological sort on a dependency graph

354

* @param graph - Object where keys are nodes and values are arrays of dependencies

355

* @param strict - When true, throws error for circular dependencies; when false, resolves best possible order

356

* @returns Array of nodes in topological order, or Error object if circular dependency detected in strict mode

357

*/

358

function topologicalSort(graph: DependencyGraph, strict?: boolean): string[] | TopologicalSortError;

359

360

interface DependencyGraph {

361

[node: string]: string[];

362

}

363

364

interface TopologicalSortError extends Error {

365

/** Error message */

366

message: "Nondeterministic import's order";

367

/** Array of conflicting nodes that create circular dependency */

368

nodes: [string, string];

369

}

370

```

371

372

**Usage Example:**

373

374

```javascript

375

const topologicalSort = require("postcss-modules-extract-imports/src/topologicalSort");

376

377

// Define dependency graph

378

const graph = {

379

"file-a.css": ["file-b.css", "file-c.css"], // file-a depends on file-b and file-c

380

"file-b.css": [], // file-b has no dependencies

381

"file-c.css": ["file-b.css"], // file-c depends on file-b

382

};

383

384

// Get topological order

385

const order = topologicalSort(graph, true);

386

console.log(order); // ["file-b.css", "file-c.css", "file-a.css"]

387

388

// Example with circular dependency

389

const cyclicGraph = {

390

"file-a.css": ["file-b.css"],

391

"file-b.css": ["file-a.css"], // Creates cycle

392

};

393

394

const result = topologicalSort(cyclicGraph, true);

395

if (result instanceof Error) {

396

console.log(result.message); // "Nondeterministic import's order"

397

console.log(result.nodes); // ["file-a.css", "file-b.css"]

398

}

399

```

400

401

## PostCSS Plugin Identification

402

403

The plugin exports a PostCSS identification marker:

404

405

```javascript { .api }

406

/**

407

* PostCSS plugin identification marker

408

* @type {boolean}

409

*/

410

extractImports.postcss = true;

411

```

412

413

This marker allows PostCSS to identify the function as a valid PostCSS plugin.

414

415

## Types

416

417

```javascript { .api }

418

/**

419

* PostCSS plugin configuration options

420

*/

421

interface ExtractImportsOptions {

422

createImportedName?: (importName: string, path: string) => string;

423

failOnWrongOrder?: boolean;

424

}

425

426

/**

427

* PostCSS plugin factory function type

428

*/

429

type ExtractImportsFactory = (options?: ExtractImportsOptions) => PostCSSPlugin;

430

431

/**

432

* Topological sort function type

433

*/

434

type TopologicalSortFunction = (graph: DependencyGraph, strict?: boolean) => string[] | TopologicalSortError;

435

436

/**

437

* Standard PostCSS plugin interface

438

*/

439

interface PostCSSPlugin {

440

postcssPlugin: string;

441

prepare(): {

442

Once(root: PostCSSRoot, postcss: PostCSSAPI): void;

443

};

444

}

445

446

/**

447

* PostCSS AST Root node

448

*/

449

interface PostCSSRoot {

450

/** Walk through all rules in the CSS */

451

walkRules(callback: (rule: PostCSSRule) => void): void;

452

/** Walk through all declarations in the CSS */

453

walkDecls(pattern: RegExp | string, callback: (decl: PostCSSDeclaration) => void): void;

454

/** Prepend a node to the beginning */

455

prepend(node: PostCSSNode): void;

456

/** Insert a node after another node */

457

insertAfter(target: PostCSSNode, newNode: PostCSSNode): void;

458

/** Get the index of a child node */

459

index(child: PostCSSNode): number;

460

}

461

462

/**

463

* PostCSS API object passed to plugin methods

464

*/

465

interface PostCSSAPI {

466

/** Create a new CSS rule */

467

rule(props: { selector: string; raws?: { after?: string } }): PostCSSRule;

468

/** Create a new CSS declaration */

469

decl(props: { prop: string; value: string; raws?: { before?: string } }): PostCSSDeclaration;

470

}

471

472

/**

473

* PostCSS Rule node

474

*/

475

interface PostCSSRule {

476

/** CSS selector */

477

selector: string;

478

/** Parent node */

479

parent: PostCSSNode;

480

/** Append a declaration to this rule */

481

append(decl: PostCSSDeclaration): void;

482

}

483

484

/**

485

* PostCSS Declaration node

486

*/

487

interface PostCSSDeclaration {

488

/** CSS property name */

489

prop: string;

490

/** CSS property value */

491

value: string;

492

/** Parent rule */

493

parent: PostCSSRule;

494

/** Create an error associated with this declaration */

495

error(message: string, options?: { plugin?: string; word?: string }): Error;

496

}

497

498

/**

499

* Base PostCSS node

500

*/

501

interface PostCSSNode {

502

/** Node type */

503

type: string;

504

/** Parent node */

505

parent?: PostCSSNode;

506

}

507

508

/**

509

* Topological sort utility types

510

*/

511

interface DependencyGraph {

512

[node: string]: string[];

513

}

514

515

interface TopologicalSortError extends Error {

516

message: "Nondeterministic import's order";

517

nodes: [string, string];

518

}

519

```