1 |
|
2 |
|
3 | import type SourceMap from '@parcel/source-map';
|
4 | import type {
|
5 | Async,
|
6 | Blob,
|
7 | Bundle,
|
8 | BundleGraph,
|
9 | Dependency,
|
10 | NamedBundle,
|
11 | } from '@parcel/types';
|
12 |
|
13 | import invariant from 'assert';
|
14 | import {Readable} from 'stream';
|
15 | import nullthrows from 'nullthrows';
|
16 | import URL from 'url';
|
17 | import {bufferStream, relativeBundlePath, urlJoin} from '../';
|
18 |
|
19 | type ReplacementMap = Map<
|
20 | string ,
|
21 | {|from: string, to: string|},
|
22 | >;
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | export 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 |
|
70 |
|
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 |
|
90 |
|
91 |
|
92 | export 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 |
|
155 | function 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 |
|
184 | function 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 |
|
192 | finalContents = finalContents.split(from).join(to);
|
193 | }
|
194 |
|
195 | return {
|
196 | contents: finalContents,
|
197 |
|
198 | map,
|
199 | };
|
200 | }
|