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
```