Parcel packager plugin for handling raw URL references without transformation
npx @tessl/cli install tessl/npm-parcel--packager-raw-url@2.15.00
# Parcel Raw URL Packager
1
2
The @parcel/packager-raw-url package is a specialized Parcel packager plugin that handles raw URL assets by preserving their original URL references without applying any transformations. It ensures raw bundles maintain their absolute URL paths and contain exactly one asset per bundle.
3
4
## Package Information
5
6
- **Package Name**: @parcel/packager-raw-url
7
- **Package Type**: npm
8
- **Language**: JavaScript (Flow)
9
- **Installation**: `npm install @parcel/packager-raw-url`
10
- **Parcel Version**: Requires Parcel ^2.15.4
11
- **Node.js**: Requires Node.js >= 16.0.0
12
13
## Core Imports
14
15
```javascript
16
import RawUrlPackager from "@parcel/packager-raw-url";
17
```
18
19
For CommonJS:
20
21
```javascript
22
const RawUrlPackager = require("@parcel/packager-raw-url");
23
```
24
25
For Flow type annotations:
26
27
```flow
28
import type {Packager} from "@parcel/plugin";
29
import {Packager} from "@parcel/plugin";
30
import {replaceURLReferences} from "@parcel/utils";
31
```
32
33
## Basic Usage
34
35
This package is typically used through Parcel's plugin system rather than directly in application code. Parcel automatically uses this packager for raw URL asset types.
36
37
```javascript
38
// Parcel configuration (.parcelrc)
39
{
40
"extends": "@parcel/config-default",
41
"packagers": {
42
"raw-url:*": "@parcel/packager-raw-url"
43
}
44
}
45
```
46
47
## Architecture
48
49
The raw URL packager is built as a Parcel plugin that integrates with Parcel's bundling pipeline:
50
51
- **Plugin Architecture**: Extends Parcel's `Packager` base class from `@parcel/plugin`
52
- **Bundle Processing**: Handles bundles containing raw URL assets
53
- **URL Preservation**: Uses `@parcel/utils` URL replacement utilities to maintain absolute URLs
54
- **Single Asset Validation**: Enforces constraint that raw bundles contain exactly one asset
55
56
## Capabilities
57
58
### Raw URL Packager Plugin
59
60
The default export is a Packager plugin instance configured specifically for raw URL asset handling.
61
62
```flow { .api }
63
/**
64
* Default export: Packager plugin instance for raw URL assets
65
* Automatically configured with package method for processing raw URL bundles
66
*/
67
declare const RawUrlPackager: Packager;
68
69
/**
70
* Package method configuration within the Packager instance
71
* @param bundle - Bundle object containing assets to process
72
* @param bundleGraph - Complete bundle graph for URL resolution
73
* @returns Promise resolving to object with processed contents
74
*/
75
interface PackageMethodParams {
76
bundle: Bundle;
77
bundleGraph: BundleGraph;
78
}
79
80
interface PackageMethodReturn {
81
contents: string;
82
}
83
84
/**
85
* Internal package method (not directly callable)
86
* Processes raw URL bundles by preserving original URLs
87
*/
88
function package(params: PackageMethodParams): Promise<PackageMethodReturn>;
89
90
/**
91
* Utility function from @parcel/utils used for URL replacement
92
* Replaces URL references in asset contents with bundle URLs
93
*/
94
function replaceURLReferences({
95
bundle,
96
bundleGraph,
97
contents,
98
relative = true,
99
map,
100
getReplacement = s => s,
101
}: {|
102
bundle: Bundle,
103
bundleGraph: BundleGraph<Bundle>,
104
contents: string,
105
relative?: boolean,
106
map?: ?SourceMap,
107
getReplacement?: string => string,
108
|}): {|+contents: string, +map: ?SourceMap|};
109
```
110
111
## Types
112
113
```flow { .api }
114
/**
115
* Bundle object representing a collection of assets
116
* Provided by Parcel's bundling system
117
*/
118
interface Bundle {
119
/** The bundle id */
120
+id: string;
121
/** The type of the bundle */
122
+type: string;
123
/** The environment of the bundle */
124
+env: Environment;
125
/** The bundle's target */
126
+target: Target;
127
/** Indicates that the bundle's file name should be stable over time */
128
+needsStableName: ?boolean;
129
/** Controls the behavior of the bundle (inline, isolated) */
130
+bundleBehavior: ?BundleBehavior;
131
/** Whether the bundle can be split */
132
+isSplittable: ?boolean;
133
/** A placeholder for the bundle's content hash */
134
+hashReference: string;
135
/** Returns the assets that are executed immediately when the bundle is loaded */
136
getEntryAssets(): Array<Asset>;
137
/** Returns the main entry of the bundle, which will provide the bundle's exports */
138
getMainEntry(): ?Asset;
139
/** Returns whether the bundle includes the given asset */
140
hasAsset(asset: Asset): boolean;
141
/** Returns whether the bundle includes the given dependency */
142
hasDependency(dependency: Dependency): boolean;
143
/** Traverses the assets in the bundle */
144
traverseAssets<TContext>(
145
visit: GraphVisitor<Asset, TContext>,
146
startAsset?: Asset,
147
): ?TContext;
148
/** Traverses assets and dependencies in the bundle */
149
traverse<TContext>(
150
visit: GraphVisitor<BundleTraversable, TContext>,
151
): ?TContext;
152
/** Returns a hash of the contents of the bundle */
153
getContentHash(): string;
154
}
155
156
/**
157
* Asset object representing a single bundled asset
158
* Provided by Parcel's asset system
159
*/
160
interface Asset {
161
/** The id of the asset */
162
+id: string;
163
/** The file system where the source is located */
164
+fs: FileSystem;
165
/** The file path of the asset */
166
+filePath: FilePath;
167
/** The asset's type. This initially corresponds to the source file extension */
168
+type: string;
169
/** The transformer options for the asset from the dependency query string */
170
+query: URLSearchParams;
171
/** The environment of the asset */
172
+env: Environment;
173
/** Whether this asset is part of the project, and not an external dependency */
174
+isSource: boolean;
175
/** Plugin-specific metadata for the asset */
176
+meta: Meta;
177
/** Controls which bundle the asset is placed into (inline, isolated) */
178
+bundleBehavior: ?BundleBehavior;
179
/** If the asset is used as a bundle entry, controls whether that bundle can be split */
180
+isBundleSplittable: boolean;
181
/** Whether this asset can be omitted if none of its exports are being used */
182
+sideEffects: boolean;
183
/** Unique key to identify assets when a transformer returns multiple assets */
184
+uniqueKey: ?string;
185
/** The type of the AST */
186
+astGenerator: ?ASTGenerator;
187
/** The pipeline defined in .parcelrc that the asset should be processed with */
188
+pipeline: ?string;
189
/** The symbols that the asset exports */
190
+symbols: AssetSymbols;
191
/** Statistics about the asset */
192
+stats: Stats;
193
/** Returns the current AST */
194
getAST(): Promise<?AST>;
195
/** Returns the asset contents as a string */
196
getCode(): Promise<string>;
197
/** Returns the asset contents as a buffer */
198
getBuffer(): Promise<Buffer>;
199
/** Returns the asset contents as a stream */
200
getStream(): Readable;
201
/** Returns the source map for the asset, if available */
202
getMap(): Promise<?SourceMap>;
203
/** Returns a buffer representation of the source map, if available */
204
getMapBuffer(): Promise<?Buffer>;
205
/** Returns a list of dependencies for the asset */
206
getDependencies(): $ReadOnlyArray<Dependency>;
207
}
208
209
/**
210
* BundleGraph object representing the complete bundle graph
211
* Used for URL resolution and bundle relationships
212
*/
213
interface BundleGraph<TBundle: Bundle> {
214
/** Retrieves an asset by id */
215
getAssetById(id: string): Asset;
216
/** Returns the public (short) id for an asset */
217
getAssetPublicId(asset: Asset): string;
218
/** Returns a list of bundles in the bundle graph. By default, inline bundles are excluded */
219
getBundles(opts?: {|includeInline: boolean|}): Array<TBundle>;
220
/** Traverses the assets and dependencies in the bundle graph, in depth first order */
221
traverse<TContext>(
222
visit: GraphVisitor<BundleGraphTraversable, TContext>,
223
startAsset: ?Asset,
224
options?: {|skipUnusedDependencies?: boolean|},
225
): ?TContext;
226
/** Traverses all bundles in the bundle graph, including inline bundles, in depth first order */
227
traverseBundles<TContext>(
228
visit: GraphVisitor<TBundle, TContext>,
229
startBundle: ?Bundle,
230
): ?TContext;
231
/** Returns a list of bundle groups that load the given bundle */
232
getBundleGroupsContainingBundle(bundle: Bundle): Array<BundleGroup>;
233
/** Returns a list of bundles that load together in the given bundle group */
234
getBundlesInBundleGroup(
235
bundleGroup: BundleGroup,
236
opts?: {|
237
recursive?: boolean,
238
includeInline?: boolean,
239
includeIsolated?: boolean,
240
|},
241
): Array<TBundle>;
242
/** Returns a list of bundles that this bundle loads asynchronously */
243
getChildBundles(bundle: Bundle): Array<TBundle>;
244
/** Returns a list of bundles that load this bundle asynchronously */
245
getParentBundles(bundle: Bundle): Array<TBundle>;
246
/** Returns whether the bundle was loaded by another bundle of the given type */
247
hasParentBundleOfType(bundle: Bundle, type: string): boolean;
248
/** Returns a list of bundles that are referenced by this bundle */
249
getReferencedBundles(
250
bundle: Bundle,
251
opts?: {|
252
recursive?: boolean,
253
includeInline?: boolean,
254
includeIsolated?: boolean,
255
|},
256
): Array<TBundle>;
257
/** Returns a list of bundles that reference this bundle */
258
getReferencingBundles(bundle: Bundle): Array<TBundle>;
259
/** Get the dependencies that the asset requires */
260
getDependencies(asset: Asset): Array<Dependency>;
261
/** Get the dependencies that require the asset */
262
getIncomingDependencies(asset: Asset): Array<Dependency>;
263
/** Get the asset that created the dependency */
264
getAssetWithDependency(dep: Dependency): ?Asset;
265
/** Returns whether the given bundle group is an entry */
266
isEntryBundleGroup(bundleGroup: BundleGroup): boolean;
267
/** Returns whether a dependency was excluded because it had no used symbols */
268
isDependencySkipped(dependency: Dependency): boolean;
269
/** Returns the asset that the dependency resolved to */
270
getResolvedAsset(dependency: Dependency, bundle: ?Bundle): ?Asset;
271
/** Returns the bundle that a dependency in a given bundle references, if any */
272
getReferencedBundle(dependency: Dependency, bundle: Bundle): ?TBundle;
273
/** Returns a list of bundles that contain the given asset */
274
getBundlesWithAsset(asset: Asset): Array<TBundle>;
275
/** Returns a list of bundles that contain the given dependency */
276
getBundlesWithDependency(dependency: Dependency): Array<TBundle>;
277
/** Returns whether the given asset is reachable in a sibling or all possible ancestries of the given bundle */
278
isAssetReachableFromBundle(asset: Asset, bundle: Bundle): boolean;
279
/** Returns whether an asset is referenced outside the given bundle */
280
isAssetReferenced(bundle: Bundle, asset: Asset): boolean;
281
/** Returns a list of entry bundles */
282
getEntryBundles(): Array<TBundle>;
283
}
284
285
/**
286
* Packager base class from @parcel/plugin
287
* Base class for all Parcel packager plugins
288
*/
289
interface Packager {
290
// Packager implementation details handled by Parcel
291
}
292
293
// Supporting types used throughout the API
294
type Async<T> = T | Promise<T>;
295
type FilePath = string;
296
type Meta = {[string]: mixed};
297
type BundleBehavior = "inline" | "isolated";
298
299
interface Environment {
300
context: "node" | "browser" | "web-worker" | "service-worker" | "electron-main" | "electron-renderer";
301
engines: {[string]: string};
302
includeNodeModules: boolean | Array<string> | {[string]: boolean};
303
outputFormat: "global" | "esmodule" | "commonjs";
304
isLibrary: boolean;
305
shouldOptimize: boolean;
306
shouldScopeHoist: boolean;
307
sourceMap?: SourceMapOptions;
308
}
309
310
interface Target {
311
name: string;
312
distDir: FilePath;
313
distEntry?: ?FilePath;
314
publicUrl: string;
315
env: Environment;
316
sourceMap?: ?SourceMapOptions;
317
}
318
319
interface Dependency {
320
id: string;
321
specifier: string;
322
specifierType: DependencySpecifierType;
323
priority: DependencyPriority;
324
needsStableName: boolean;
325
bundleBehavior: ?BundleBehavior;
326
isEntry: boolean;
327
isOptional: boolean;
328
loc: ?SourceLocation;
329
env: Environment;
330
meta: Meta;
331
target: ?Target;
332
sourceAssetId: ?string;
333
sourcePath: ?FilePath;
334
resolveFrom: ?FilePath;
335
range: ?SemverRange;
336
symbols: ?Map<Symbol, {|local: Symbol, loc: ?SourceLocation, isWeak: boolean, meta?: ?Meta|}>;
337
pipeline: ?string;
338
}
339
340
interface BundleGroup {
341
target: Target;
342
entryAssetId: string;
343
}
344
345
interface Stats {
346
time: number;
347
size: number;
348
}
349
350
interface SourceMap {
351
// Source map structure
352
}
353
354
interface FileSystem {
355
// File system interface
356
}
357
358
interface AssetSymbols {
359
// Asset symbols structure
360
}
361
362
interface ASTGenerator {
363
// AST generator interface
364
}
365
366
interface AST {
367
// Abstract syntax tree structure
368
}
369
370
interface Buffer {
371
// Node.js Buffer interface
372
}
373
374
interface Readable {
375
// Node.js Readable stream interface
376
}
377
378
type GraphVisitor<TNode, TContext> = (
379
node: TNode,
380
context: ?TContext,
381
actions: TraversalActions,
382
) => ?TContext;
383
384
interface TraversalActions {
385
skipChildren(): void;
386
stop(): void;
387
}
388
389
type BundleTraversable = Asset | Dependency;
390
type BundleGraphTraversable = Asset | Dependency;
391
392
type DependencySpecifierType = "esm" | "commonjs" | "url" | "custom";
393
type DependencyPriority = "sync" | "parallel" | "lazy";
394
type Symbol = string;
395
type SourceLocation = {|
396
filePath: FilePath,
397
start: {|line: number, column: number|},
398
end: {|line: number, column: number|},
399
|};
400
type SemverRange = string;
401
type SourceMapOptions = {|
402
inline?: boolean,
403
inlineSources?: boolean,
404
sourceRoot?: string,
405
|};
406
```
407
408
## Implementation Details
409
410
### Bundle Validation
411
412
The packager enforces that raw URL bundles contain exactly one asset:
413
414
- Throws assertion error if bundle contains zero or multiple assets
415
- Uses Node.js `assert.equal` for validation
416
- Ensures bundle integrity for raw URL processing
417
418
### URL Reference Processing
419
420
URL references are processed using Parcel's utility functions:
421
422
- Uses `replaceURLReferences` from `@parcel/utils`
423
- Sets `relative: false` to maintain absolute URL paths
424
- Uses identity function `s => s` for URL replacement (no transformation)
425
- Preserves original URL structure in final bundle output
426
427
### Error Handling
428
429
- **Bundle Validation Error**: Throws `AssertionError` when bundle doesn't contain exactly one asset
430
- **Asset Processing Error**: Propagates errors from asset code retrieval
431
- **URL Processing Error**: Propagates errors from URL reference replacement