Runs webpack loaders programmatically with full context support and dependency tracking
npx @tessl/cli install tessl/npm-loader-runner@4.3.00
# Loader Runner
1
2
Loader Runner is a JavaScript library that provides a runtime execution engine for webpack loaders. It enables programmatic execution of webpack's transformation pipeline outside of webpack itself, offering a complete API for running sequences of loaders against resources with full context support, dependency tracking, and error handling.
3
4
## Package Information
5
6
- **Package Name**: loader-runner
7
- **Package Type**: npm
8
- **Language**: JavaScript
9
- **Installation**: `npm install loader-runner`
10
11
## Core Imports
12
13
```javascript
14
import { runLoaders, getContext } from "loader-runner";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { runLoaders, getContext } = require("loader-runner");
21
```
22
23
## Basic Usage
24
25
```javascript
26
import { runLoaders } from "loader-runner";
27
import fs from "fs";
28
29
// Execute a simple loader chain
30
runLoaders({
31
resource: "/path/to/source.js",
32
loaders: [
33
"/path/to/babel-loader.js",
34
"/path/to/eslint-loader.js"
35
],
36
context: { minimize: true },
37
readResource: fs.readFile.bind(fs)
38
}, (err, result) => {
39
if (err) {
40
console.error("Loader execution failed:", err);
41
return;
42
}
43
44
console.log("Transformed code:", result.result.toString());
45
console.log("Dependencies:", result.fileDependencies);
46
});
47
```
48
49
## Architecture
50
51
Loader Runner implements webpack's loader execution model with these key components:
52
53
- **Loader Chain Execution**: Manages the two-phase loader execution (pitching and normal phases)
54
- **Context Management**: Provides comprehensive loader context with dependency tracking
55
- **Module Loading**: Handles both CommonJS and ES module loaders with retry logic
56
- **Error Handling**: Robust error handling with proper stack traces and loader isolation
57
- **Dependency Tracking**: Tracks file, context, and missing dependencies for caching systems
58
59
## Capabilities
60
61
### Loader Execution
62
63
Main function to execute sequences of loaders against resources with comprehensive result metadata.
64
65
```javascript { .api }
66
/**
67
* Execute a sequence of loaders against a resource
68
* @param {LoaderOptions} options - Configuration options for loader execution
69
* @param {function} callback - Completion callback with signature (err, result)
70
*/
71
function runLoaders(options, callback);
72
73
interface LoaderOptions {
74
/** Absolute path to the resource file (optionally including query string) */
75
resource?: string;
76
/** Array of loader paths or loader configuration objects */
77
loaders?: (string | LoaderConfig)[];
78
/** Additional loader context used as base context */
79
context?: object;
80
/** Custom function to process the resource */
81
processResource?: (loaderContext: LoaderContext, resourcePath: string, callback: Function) => void;
82
/** Custom function to read the resource (defaults to fs.readFile) */
83
readResource?: (path: string, callback: Function) => void;
84
}
85
86
interface LoaderConfig {
87
/** Absolute path to the loader */
88
loader: string;
89
/** Loader options object */
90
options?: object;
91
/** Loader identifier for option serialization */
92
ident?: string;
93
/** Fragment identifier */
94
fragment?: string;
95
/** Loader type (module or commonjs) */
96
type?: string;
97
}
98
99
interface LoaderResult {
100
/** The transformation result (array of results from loader chain) */
101
result?: any[];
102
/** The raw resource as Buffer (useful for SourceMaps) */
103
resourceBuffer?: Buffer;
104
/** Whether the result is cacheable */
105
cacheable: boolean;
106
/** Array of file paths the result depends on */
107
fileDependencies: string[];
108
/** Array of directory paths the result depends on */
109
contextDependencies: string[];
110
/** Array of missing file paths the result depends on */
111
missingDependencies: string[];
112
}
113
```
114
115
**Usage Examples:**
116
117
```javascript
118
import { runLoaders } from "loader-runner";
119
import fs from "fs";
120
121
// Simple loader execution
122
runLoaders({
123
resource: "/project/src/index.js",
124
loaders: ["/node_modules/babel-loader/lib/index.js"]
125
}, (err, result) => {
126
if (!err) {
127
console.log("Result:", result.result.toString());
128
}
129
});
130
131
// Complex loader chain with options
132
runLoaders({
133
resource: "/project/src/styles.scss?theme=dark",
134
loaders: [
135
{
136
loader: "/node_modules/sass-loader/dist/cjs.js",
137
options: { sourceMap: true }
138
},
139
{
140
loader: "/node_modules/css-loader/dist/cjs.js",
141
options: { modules: true }
142
}
143
],
144
context: {
145
mode: "production",
146
minimize: true
147
},
148
readResource: fs.readFile.bind(fs)
149
}, (err, result) => {
150
if (!err) {
151
console.log("CSS:", result.result.toString());
152
console.log("Dependencies:", result.fileDependencies);
153
console.log("Cacheable:", result.cacheable);
154
}
155
});
156
157
// Custom resource processing
158
runLoaders({
159
resource: "/project/data.json",
160
loaders: ["/custom/json-transformer.js"],
161
processResource: (loaderContext, resourcePath, callback) => {
162
// Custom resource reading logic
163
fs.readFile(resourcePath, 'utf8', (err, data) => {
164
if (err) return callback(err);
165
166
// Add dependency tracking
167
loaderContext.addDependency(resourcePath);
168
169
// Custom processing
170
const processed = JSON.parse(data);
171
callback(null, JSON.stringify(processed, null, 2));
172
});
173
}
174
}, (err, result) => {
175
// Handle result
176
});
177
```
178
179
### Context Utilities
180
181
Utility function to extract directory context from resource paths.
182
183
```javascript { .api }
184
/**
185
* Extract the directory context from a resource path
186
* @param {string} resource - Resource path with optional query and fragment
187
* @returns {string} Directory path of the resource
188
*/
189
function getContext(resource);
190
```
191
192
**Usage Examples:**
193
194
```javascript
195
import { getContext } from "loader-runner";
196
197
// Extract directory from resource path
198
const context = getContext("/project/src/components/Button.jsx?inline");
199
console.log(context); // "/project/src/components"
200
201
// Handle paths with complex queries
202
const context2 = getContext("/assets/image.png?width=200&height=100#section");
203
console.log(context2); // "/assets"
204
205
// Root directory handling
206
const context3 = getContext("/index.js");
207
console.log(context3); // "/"
208
```
209
210
## Loader Context
211
212
The loader context object provided to loaders during execution contains comprehensive information and utilities:
213
214
### Context Properties
215
216
```javascript { .api }
217
interface LoaderContext {
218
/** Directory of the resource being processed */
219
context: string;
220
/** Full resource path with query and fragment */
221
resource: string;
222
/** Path to the resource file */
223
resourcePath: string;
224
/** Query string portion of the resource */
225
resourceQuery: string;
226
/** Fragment portion of the resource */
227
resourceFragment: string;
228
/** Full loader chain request string */
229
request: string;
230
/** Remaining loaders in the chain */
231
remainingRequest: string;
232
/** Current loader and remaining chain */
233
currentRequest: string;
234
/** Previously executed loaders */
235
previousRequest: string;
236
/** Current loader index in the chain */
237
loaderIndex: number;
238
/** Current loader options/query */
239
query: object | string;
240
/** Shared data object for pitch/normal phases */
241
data: object;
242
/** Array of all loaders in the chain */
243
loaders: LoaderObject[];
244
/** Async callback function (null until async() is called) */
245
async: Function | null;
246
/** Callback function (null until async() is called) */
247
callback: Function | null;
248
}
249
```
250
251
### Context Methods
252
253
```javascript { .api }
254
interface LoaderContextMethods {
255
/**
256
* Make loader execution asynchronous
257
* @returns {function} Async callback function
258
*/
259
async(): Function;
260
261
/**
262
* Async callback function for returning results
263
* @param {Error} err - Error if execution failed
264
* @param {*} result - Transformation result
265
* @param {object} sourceMap - Optional source map
266
* @param {object} meta - Optional metadata
267
*/
268
callback(err?: Error, result?: any, sourceMap?: object, meta?: object): void;
269
270
/**
271
* Mark the result as cacheable or non-cacheable
272
* @param {boolean} flag - Cacheable flag (defaults to true)
273
*/
274
cacheable(flag?: boolean): void;
275
276
/**
277
* Add a file dependency for caching
278
* @param {string} file - File path to add as dependency
279
*/
280
addDependency(file: string): void;
281
282
/**
283
* Alias for addDependency (legacy compatibility)
284
* @param {string} file - File path to add as dependency
285
*/
286
dependency(file: string): void;
287
288
/**
289
* Add a directory dependency for caching
290
* @param {string} context - Directory path to watch
291
*/
292
addContextDependency(context: string): void;
293
294
/**
295
* Add a missing file dependency for caching
296
* @param {string} missing - Missing file path
297
*/
298
addMissingDependency(missing: string): void;
299
300
/**
301
* Get current file dependencies
302
* @returns {string[]} Array of file dependencies
303
*/
304
getDependencies(): string[];
305
306
/**
307
* Get current context dependencies
308
* @returns {string[]} Array of context dependencies
309
*/
310
getContextDependencies(): string[];
311
312
/**
313
* Get current missing dependencies
314
* @returns {string[]} Array of missing dependencies
315
*/
316
getMissingDependencies(): string[];
317
318
/**
319
* Clear all dependencies and reset cacheable flag
320
*/
321
clearDependencies(): void;
322
}
323
```
324
325
## Loader Object Structure
326
327
Internal structure representing individual loaders in the execution chain:
328
329
```javascript { .api }
330
interface LoaderObject {
331
/** Absolute path to the loader file */
332
path: string;
333
/** Query string from the loader request */
334
query: string;
335
/** Fragment from the loader request */
336
fragment: string;
337
/** Parsed options object */
338
options: object;
339
/** Loader identifier for options */
340
ident: string;
341
/** Normal phase loader function */
342
normal: Function;
343
/** Pitch phase loader function */
344
pitch: Function;
345
/** Whether loader expects raw Buffer input */
346
raw: boolean;
347
/** Shared data object for pitch/normal communication */
348
data: object;
349
/** Whether pitch phase has been executed */
350
pitchExecuted: boolean;
351
/** Whether normal phase has been executed */
352
normalExecuted: boolean;
353
/** Full loader request string */
354
request: string;
355
/** Loader type (module or commonjs) */
356
type?: string;
357
}
358
```
359
360
## Error Handling
361
362
Loader Runner provides comprehensive error handling with proper error propagation. Errors can occur during loader loading, resource processing, or loader execution:
363
364
**Common Error Scenarios:**
365
366
```javascript
367
runLoaders({
368
resource: "/path/to/file.js",
369
loaders: ["/invalid/loader.js"]
370
}, (err, result) => {
371
if (err) {
372
console.log("Execution error:", err.message);
373
// Errors may include loader loading failures,
374
// resource processing errors, or loader execution errors
375
}
376
});
377
```
378
379
## Advanced Usage
380
381
### Custom Resource Processing
382
383
```javascript
384
runLoaders({
385
resource: "/project/template.html",
386
loaders: ["/custom/template-loader.js"],
387
processResource: (loaderContext, resourcePath, callback) => {
388
// Custom resource processing with caching control
389
loaderContext.addDependency(resourcePath);
390
391
// Custom read logic (e.g., from memory, network, etc.)
392
customReadFunction(resourcePath, (err, content) => {
393
if (err) return callback(err);
394
callback(null, content);
395
});
396
}
397
}, callback);
398
```
399
400
### Loader Chain Optimization
401
402
```javascript
403
// Conditional loader application
404
const loaders = [];
405
if (process.env.NODE_ENV === 'development') {
406
loaders.push('/dev/source-map-loader.js');
407
}
408
loaders.push('/transpiler/babel-loader.js');
409
410
runLoaders({
411
resource: "/src/app.js",
412
loaders: loaders,
413
context: { mode: process.env.NODE_ENV }
414
}, callback);
415
```
416
417
### Dependency Management
418
419
```javascript
420
runLoaders(options, (err, result) => {
421
if (!err && result) {
422
// Use dependency information for build optimization
423
console.log("Watch these files:", result.fileDependencies);
424
console.log("Watch these directories:", result.contextDependencies);
425
console.log("Cache valid:", result.cacheable);
426
427
// Implement custom caching based on dependencies
428
if (result.cacheable) {
429
cache.set(cacheKey, result.result, result.fileDependencies);
430
}
431
}
432
});
433
```