1 | const URL = require('url');
|
2 | const path = require('path');
|
3 | const clone = require('clone');
|
4 | const fs = require('@parcel/fs');
|
5 | const md5 = require('./utils/md5');
|
6 | const isURL = require('./utils/is-url');
|
7 | const config = require('./utils/config');
|
8 | const syncPromise = require('./utils/syncPromise');
|
9 | const logger = require('@parcel/logger');
|
10 | const Resolver = require('./Resolver');
|
11 | const objectHash = require('./utils/objectHash');
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | class Asset {
|
20 | constructor(name, options) {
|
21 | this.id = null;
|
22 | this.name = name;
|
23 | this.basename = path.basename(this.name);
|
24 | this.relativeName = path
|
25 | .relative(options.rootDir, this.name)
|
26 | .replace(/\\/g, '/');
|
27 | this.options = options;
|
28 | this.encoding = 'utf8';
|
29 | this.type = path.extname(this.name).slice(1);
|
30 | this.hmrPageReload = false;
|
31 |
|
32 | this.processed = false;
|
33 | this.contents = options.rendition ? options.rendition.value : null;
|
34 | this.ast = null;
|
35 | this.generated = null;
|
36 | this.hash = null;
|
37 | this.sourceMaps = null;
|
38 | this.parentDeps = new Set();
|
39 | this.dependencies = new Map();
|
40 | this.depAssets = new Map();
|
41 | this.parentBundle = null;
|
42 | this.bundles = new Set();
|
43 | this.cacheData = {};
|
44 | this.startTime = 0;
|
45 | this.endTime = 0;
|
46 | this.buildTime = 0;
|
47 | this.bundledSize = 0;
|
48 | this.resolver = new Resolver(options);
|
49 | }
|
50 |
|
51 | shouldInvalidate() {
|
52 | return false;
|
53 | }
|
54 |
|
55 | async loadIfNeeded() {
|
56 | if (this.contents == null) {
|
57 | this.contents = await this.load();
|
58 | }
|
59 | }
|
60 |
|
61 | async parseIfNeeded() {
|
62 | await this.loadIfNeeded();
|
63 | if (!this.ast) {
|
64 | this.ast = await this.parse(this.contents);
|
65 | }
|
66 | }
|
67 |
|
68 | async getDependencies() {
|
69 | if (
|
70 | this.options.rendition &&
|
71 | this.options.rendition.hasDependencies === false
|
72 | ) {
|
73 | return;
|
74 | }
|
75 |
|
76 | await this.loadIfNeeded();
|
77 |
|
78 | if (this.contents && this.mightHaveDependencies()) {
|
79 | await this.parseIfNeeded();
|
80 | await this.collectDependencies();
|
81 | }
|
82 | }
|
83 |
|
84 | addDependency(name, opts) {
|
85 | this.dependencies.set(name, Object.assign({name}, opts));
|
86 | }
|
87 |
|
88 | resolveDependency(url, from = this.name) {
|
89 | const parsed = URL.parse(url);
|
90 | let depName;
|
91 | let resolved;
|
92 | let dir = path.dirname(from);
|
93 | const filename = decodeURIComponent(parsed.pathname);
|
94 |
|
95 | if (filename[0] === '~' || filename[0] === '/') {
|
96 | if (dir === '.') {
|
97 | dir = this.options.rootDir;
|
98 | }
|
99 | depName = resolved = this.resolver.resolveFilename(filename, dir);
|
100 | } else {
|
101 | resolved = path.resolve(dir, filename);
|
102 | depName = './' + path.relative(path.dirname(this.name), resolved);
|
103 | }
|
104 |
|
105 | return {depName, resolved};
|
106 | }
|
107 |
|
108 | addURLDependency(url, from = this.name, opts) {
|
109 | if (!url || isURL(url)) {
|
110 | return url;
|
111 | }
|
112 |
|
113 | if (typeof from === 'object') {
|
114 | opts = from;
|
115 | from = this.name;
|
116 | }
|
117 |
|
118 | const {depName, resolved} = this.resolveDependency(url, from);
|
119 |
|
120 | this.addDependency(depName, Object.assign({dynamic: true, resolved}, opts));
|
121 |
|
122 | const parsed = URL.parse(url);
|
123 | parsed.pathname = this.options.parser
|
124 | .getAsset(resolved, this.options)
|
125 | .generateBundleName();
|
126 |
|
127 | return URL.format(parsed);
|
128 | }
|
129 |
|
130 | get package() {
|
131 | logger.warn(
|
132 | '`asset.package` is deprecated. Please use `await asset.getPackage()` instead.'
|
133 | );
|
134 | return syncPromise(this.getPackage());
|
135 | }
|
136 |
|
137 | async getPackage() {
|
138 | if (!this._package) {
|
139 | this._package = await this.resolver.findPackage(path.dirname(this.name));
|
140 | }
|
141 |
|
142 | return this._package;
|
143 | }
|
144 |
|
145 | async getConfig(filenames, opts = {}) {
|
146 | if (opts.packageKey) {
|
147 | let pkg = await this.getPackage();
|
148 | if (pkg && pkg[opts.packageKey]) {
|
149 | return clone(pkg[opts.packageKey]);
|
150 | }
|
151 | }
|
152 |
|
153 |
|
154 | let conf = await config.resolve(opts.path || this.name, filenames);
|
155 | if (conf) {
|
156 |
|
157 |
|
158 | this.addDependency(conf, {includedInParent: true});
|
159 | if (opts.load === false) {
|
160 | return conf;
|
161 | }
|
162 |
|
163 | return config.load(opts.path || this.name, filenames);
|
164 | }
|
165 |
|
166 | return null;
|
167 | }
|
168 |
|
169 | mightHaveDependencies() {
|
170 | return true;
|
171 | }
|
172 |
|
173 | async load() {
|
174 | return fs.readFile(this.name, this.encoding);
|
175 | }
|
176 |
|
177 | parse() {
|
178 |
|
179 | }
|
180 |
|
181 | collectDependencies() {
|
182 |
|
183 | }
|
184 |
|
185 | async pretransform() {
|
186 |
|
187 | }
|
188 |
|
189 | async transform() {
|
190 |
|
191 | }
|
192 |
|
193 | async generate() {
|
194 | return {
|
195 | [this.type]: this.contents
|
196 | };
|
197 | }
|
198 |
|
199 | async process() {
|
200 |
|
201 |
|
202 |
|
203 |
|
204 | if (!this.id) {
|
205 | this.id =
|
206 | this.options.production || this.options.scopeHoist
|
207 | ? md5(this.relativeName, 'base64').slice(0, 4)
|
208 | : this.relativeName;
|
209 | }
|
210 |
|
211 | if (!this.generated) {
|
212 | await this.loadIfNeeded();
|
213 | await this.pretransform();
|
214 | await this.getDependencies();
|
215 | await this.transform();
|
216 | this.generated = await this.generate();
|
217 | }
|
218 |
|
219 | return this.generated;
|
220 | }
|
221 |
|
222 | async postProcess(generated) {
|
223 | return generated;
|
224 | }
|
225 |
|
226 | generateHash() {
|
227 | return objectHash(this.generated);
|
228 | }
|
229 |
|
230 | invalidate() {
|
231 | this.processed = false;
|
232 | this.contents = null;
|
233 | this.ast = null;
|
234 | this.generated = null;
|
235 | this.hash = null;
|
236 | this.dependencies.clear();
|
237 | this.depAssets.clear();
|
238 | }
|
239 |
|
240 | invalidateBundle() {
|
241 | this.parentBundle = null;
|
242 | this.bundles.clear();
|
243 | this.parentDeps.clear();
|
244 | }
|
245 |
|
246 | generateBundleName() {
|
247 |
|
248 |
|
249 | return md5(this.relativeName) + '.' + this.type;
|
250 | }
|
251 |
|
252 | replaceBundleNames(bundleNameMap) {
|
253 | let copied = false;
|
254 | for (let key in this.generated) {
|
255 | let value = this.generated[key];
|
256 | if (typeof value === 'string') {
|
257 |
|
258 | let newValue = value;
|
259 | for (let [name, map] of bundleNameMap) {
|
260 | newValue = newValue.split(name).join(map);
|
261 | }
|
262 |
|
263 |
|
264 | if (newValue !== value && !copied) {
|
265 | this.generated = Object.assign({}, this.generated);
|
266 | copied = true;
|
267 | }
|
268 |
|
269 | this.generated[key] = newValue;
|
270 | }
|
271 | }
|
272 | }
|
273 |
|
274 | generateErrorMessage(err) {
|
275 | return err;
|
276 | }
|
277 | }
|
278 |
|
279 | module.exports = Asset;
|