UNPKG

4.9 kBJavaScriptView Raw
1// @flow strict-local
2
3import type SourceMap from '@parcel/source-map';
4import type {
5 Async,
6 Blob,
7 Bundle,
8 BundleGraph,
9 Dependency,
10 NamedBundle,
11} from '@parcel/types';
12
13import invariant from 'assert';
14import {Readable} from 'stream';
15import nullthrows from 'nullthrows';
16import URL from 'url';
17import {bufferStream, relativeBundlePath, urlJoin} from '../';
18
19type ReplacementMap = Map<
20 string /* dependency id */,
21 {|from: string, to: string|},
22>;
23
24/*
25 * Replaces references to dependency ids for URL dependencies with:
26 * - in the case of an unresolvable url dependency, the original moduleSpecifier.
27 * These are external requests that Parcel did not bundle.
28 * - in the case of a reference to another bundle, the relative url to that
29 * bundle from the current bundle.
30 */
31export function replaceURLReferences({
32 bundle,
33 bundleGraph,
34 contents,
35 map,
36 relative = true,
37}: {|
38 bundle: NamedBundle,
39 bundleGraph: BundleGraph<NamedBundle>,
40 contents: string,
41 relative?: boolean,
42 map?: ?SourceMap,
43|}): {|+contents: string, +map: ?SourceMap|} {
44 let replacements = new Map();
45 let urlDependencies = [];
46 bundle.traverse(node => {
47 if (node.type === 'dependency' && node.value.isURL) {
48 urlDependencies.push(node.value);
49 }
50 });
51
52 for (let dependency of urlDependencies) {
53 if (!dependency.isURL) {
54 continue;
55 }
56
57 let resolved = bundleGraph.resolveExternalDependency(dependency, bundle);
58 if (resolved == null) {
59 replacements.set(dependency.id, {
60 from: dependency.id,
61 to: dependency.moduleSpecifier,
62 });
63 continue;
64 }
65
66 invariant(resolved.type === 'bundle_group');
67 let entryBundle = bundleGraph.getBundlesInBundleGroup(resolved.value).pop();
68 if (entryBundle.isInline) {
69 // If a bundle is inline, it should be replaced with inline contents,
70 // not a URL.
71 continue;
72 }
73
74 replacements.set(
75 dependency.id,
76 getURLReplacement({
77 dependency,
78 fromBundle: bundle,
79 toBundle: entryBundle,
80 relative,
81 }),
82 );
83 }
84
85 return performReplacement(replacements, contents, map);
86}
87
88/*
89 * Replaces references to dependency ids for inline bundles with the packaged
90 * contents of that bundle.
91 */
92export async function replaceInlineReferences({
93 bundle,
94 bundleGraph,
95 contents,
96 map,
97 getInlineReplacement,
98 getInlineBundleContents,
99}: {|
100 bundle: Bundle,
101 bundleGraph: BundleGraph<NamedBundle>,
102 contents: string,
103 getInlineReplacement: (
104 Dependency,
105 ?'string',
106 string,
107 ) => {|from: string, to: string|},
108 getInlineBundleContents: (
109 Bundle,
110 BundleGraph<NamedBundle>,
111 ) => Async<{|contents: Blob|}>,
112 map?: ?SourceMap,
113|}): Promise<{|+contents: string, +map: ?SourceMap|}> {
114 let replacements = new Map();
115
116 let dependencies = [];
117 bundle.traverse(node => {
118 if (node.type === 'dependency') {
119 dependencies.push(node.value);
120 }
121 });
122
123 for (let dependency of dependencies) {
124 let resolved = bundleGraph.resolveExternalDependency(dependency, bundle);
125 if (resolved == null || resolved.type === 'asset') {
126 continue;
127 }
128
129 let [entryBundle] = bundleGraph.getBundlesInBundleGroup(resolved.value);
130 if (!entryBundle.isInline) {
131 continue;
132 }
133
134 let packagedBundle = await getInlineBundleContents(
135 entryBundle,
136 bundleGraph,
137 );
138 let packagedContents = (packagedBundle.contents instanceof Readable
139 ? await bufferStream(packagedBundle.contents)
140 : packagedBundle.contents
141 ).toString();
142
143 let inlineType = nullthrows(entryBundle.getMainEntry()).meta.inlineType;
144 if (inlineType == null || inlineType === 'string') {
145 replacements.set(
146 dependency.id,
147 getInlineReplacement(dependency, inlineType, packagedContents),
148 );
149 }
150 }
151
152 return performReplacement(replacements, contents, map);
153}
154
155function getURLReplacement({
156 dependency,
157 fromBundle,
158 toBundle,
159 relative,
160}: {|
161 dependency: Dependency,
162 fromBundle: NamedBundle,
163 toBundle: NamedBundle,
164 relative: boolean,
165|}) {
166 let url = URL.parse(dependency.moduleSpecifier);
167 let to;
168 if (relative) {
169 url.pathname = relativeBundlePath(fromBundle, toBundle, {
170 leadingDotSlash: false,
171 });
172 to = URL.format(url);
173 } else {
174 url.pathname = nullthrows(toBundle.name);
175 to = urlJoin(toBundle.target.publicUrl, URL.format(url));
176 }
177
178 return {
179 from: dependency.id,
180 to,
181 };
182}
183
184function performReplacement(
185 replacements: ReplacementMap,
186 contents: string,
187 map?: ?SourceMap,
188): {|+contents: string, +map: ?SourceMap|} {
189 let finalContents = contents;
190 for (let {from, to} of replacements.values()) {
191 // Perform replacement
192 finalContents = finalContents.split(from).join(to);
193 }
194
195 return {
196 contents: finalContents,
197 // TODO: Update sourcemap with adjusted contents
198 map,
199 };
200}