source: trip-planner-front/node_modules/loader-runner/lib/LoaderRunner.js@ fa375fe

Last change on this file since fa375fe was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 12.3 KB
RevLine 
[6a3a178]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5var fs = require("fs");
6var readFile = fs.readFile.bind(fs);
7var loadLoader = require("./loadLoader");
8
9function utf8BufferToString(buf) {
10 var str = buf.toString("utf-8");
11 if(str.charCodeAt(0) === 0xFEFF) {
12 return str.substr(1);
13 } else {
14 return str;
15 }
16}
17
18const PATH_QUERY_FRAGMENT_REGEXP = /^((?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
19
20/**
21 * @param {string} str the path with query and fragment
22 * @returns {{ path: string, query: string, fragment: string }} parsed parts
23 */
24function parsePathQueryFragment(str) {
25 var match = PATH_QUERY_FRAGMENT_REGEXP.exec(str);
26 return {
27 path: match[1].replace(/\0(.)/g, "$1"),
28 query: match[2] ? match[2].replace(/\0(.)/g, "$1") : "",
29 fragment: match[3] || ""
30 };
31}
32
33function dirname(path) {
34 if(path === "/") return "/";
35 var i = path.lastIndexOf("/");
36 var j = path.lastIndexOf("\\");
37 var i2 = path.indexOf("/");
38 var j2 = path.indexOf("\\");
39 var idx = i > j ? i : j;
40 var idx2 = i > j ? i2 : j2;
41 if(idx < 0) return path;
42 if(idx === idx2) return path.substr(0, idx + 1);
43 return path.substr(0, idx);
44}
45
46function createLoaderObject(loader) {
47 var obj = {
48 path: null,
49 query: null,
50 fragment: null,
51 options: null,
52 ident: null,
53 normal: null,
54 pitch: null,
55 raw: null,
56 data: null,
57 pitchExecuted: false,
58 normalExecuted: false
59 };
60 Object.defineProperty(obj, "request", {
61 enumerable: true,
62 get: function() {
63 return obj.path.replace(/#/g, "\0#") + obj.query.replace(/#/g, "\0#") + obj.fragment;
64 },
65 set: function(value) {
66 if(typeof value === "string") {
67 var splittedRequest = parsePathQueryFragment(value);
68 obj.path = splittedRequest.path;
69 obj.query = splittedRequest.query;
70 obj.fragment = splittedRequest.fragment;
71 obj.options = undefined;
72 obj.ident = undefined;
73 } else {
74 if(!value.loader)
75 throw new Error("request should be a string or object with loader and options (" + JSON.stringify(value) + ")");
76 obj.path = value.loader;
77 obj.fragment = value.fragment || "";
78 obj.type = value.type;
79 obj.options = value.options;
80 obj.ident = value.ident;
81 if(obj.options === null)
82 obj.query = "";
83 else if(obj.options === undefined)
84 obj.query = "";
85 else if(typeof obj.options === "string")
86 obj.query = "?" + obj.options;
87 else if(obj.ident)
88 obj.query = "??" + obj.ident;
89 else if(typeof obj.options === "object" && obj.options.ident)
90 obj.query = "??" + obj.options.ident;
91 else
92 obj.query = "?" + JSON.stringify(obj.options);
93 }
94 }
95 });
96 obj.request = loader;
97 if(Object.preventExtensions) {
98 Object.preventExtensions(obj);
99 }
100 return obj;
101}
102
103function runSyncOrAsync(fn, context, args, callback) {
104 var isSync = true;
105 var isDone = false;
106 var isError = false; // internal error
107 var reportedError = false;
108 context.async = function async() {
109 if(isDone) {
110 if(reportedError) return; // ignore
111 throw new Error("async(): The callback was already called.");
112 }
113 isSync = false;
114 return innerCallback;
115 };
116 var innerCallback = context.callback = function() {
117 if(isDone) {
118 if(reportedError) return; // ignore
119 throw new Error("callback(): The callback was already called.");
120 }
121 isDone = true;
122 isSync = false;
123 try {
124 callback.apply(null, arguments);
125 } catch(e) {
126 isError = true;
127 throw e;
128 }
129 };
130 try {
131 var result = (function LOADER_EXECUTION() {
132 return fn.apply(context, args);
133 }());
134 if(isSync) {
135 isDone = true;
136 if(result === undefined)
137 return callback();
138 if(result && typeof result === "object" && typeof result.then === "function") {
139 return result.then(function(r) {
140 callback(null, r);
141 }, callback);
142 }
143 return callback(null, result);
144 }
145 } catch(e) {
146 if(isError) throw e;
147 if(isDone) {
148 // loader is already "done", so we cannot use the callback function
149 // for better debugging we print the error on the console
150 if(typeof e === "object" && e.stack) console.error(e.stack);
151 else console.error(e);
152 return;
153 }
154 isDone = true;
155 reportedError = true;
156 callback(e);
157 }
158
159}
160
161function convertArgs(args, raw) {
162 if(!raw && Buffer.isBuffer(args[0]))
163 args[0] = utf8BufferToString(args[0]);
164 else if(raw && typeof args[0] === "string")
165 args[0] = Buffer.from(args[0], "utf-8");
166}
167
168function iteratePitchingLoaders(options, loaderContext, callback) {
169 // abort after last loader
170 if(loaderContext.loaderIndex >= loaderContext.loaders.length)
171 return processResource(options, loaderContext, callback);
172
173 var currentLoaderObject = loaderContext.loaders[loaderContext.loaderIndex];
174
175 // iterate
176 if(currentLoaderObject.pitchExecuted) {
177 loaderContext.loaderIndex++;
178 return iteratePitchingLoaders(options, loaderContext, callback);
179 }
180
181 // load loader module
182 loadLoader(currentLoaderObject, function(err) {
183 if(err) {
184 loaderContext.cacheable(false);
185 return callback(err);
186 }
187 var fn = currentLoaderObject.pitch;
188 currentLoaderObject.pitchExecuted = true;
189 if(!fn) return iteratePitchingLoaders(options, loaderContext, callback);
190
191 runSyncOrAsync(
192 fn,
193 loaderContext, [loaderContext.remainingRequest, loaderContext.previousRequest, currentLoaderObject.data = {}],
194 function(err) {
195 if(err) return callback(err);
196 var args = Array.prototype.slice.call(arguments, 1);
197 // Determine whether to continue the pitching process based on
198 // argument values (as opposed to argument presence) in order
199 // to support synchronous and asynchronous usages.
200 var hasArg = args.some(function(value) {
201 return value !== undefined;
202 });
203 if(hasArg) {
204 loaderContext.loaderIndex--;
205 iterateNormalLoaders(options, loaderContext, args, callback);
206 } else {
207 iteratePitchingLoaders(options, loaderContext, callback);
208 }
209 }
210 );
211 });
212}
213
214function processResource(options, loaderContext, callback) {
215 // set loader index to last loader
216 loaderContext.loaderIndex = loaderContext.loaders.length - 1;
217
218 var resourcePath = loaderContext.resourcePath;
219 if(resourcePath) {
220 options.processResource(loaderContext, resourcePath, function(err, buffer) {
221 if(err) return callback(err);
222 options.resourceBuffer = buffer;
223 iterateNormalLoaders(options, loaderContext, [buffer], callback);
224 });
225 } else {
226 iterateNormalLoaders(options, loaderContext, [null], callback);
227 }
228}
229
230function iterateNormalLoaders(options, loaderContext, args, callback) {
231 if(loaderContext.loaderIndex < 0)
232 return callback(null, args);
233
234 var currentLoaderObject = loaderContext.loaders[loaderContext.loaderIndex];
235
236 // iterate
237 if(currentLoaderObject.normalExecuted) {
238 loaderContext.loaderIndex--;
239 return iterateNormalLoaders(options, loaderContext, args, callback);
240 }
241
242 var fn = currentLoaderObject.normal;
243 currentLoaderObject.normalExecuted = true;
244 if(!fn) {
245 return iterateNormalLoaders(options, loaderContext, args, callback);
246 }
247
248 convertArgs(args, currentLoaderObject.raw);
249
250 runSyncOrAsync(fn, loaderContext, args, function(err) {
251 if(err) return callback(err);
252
253 var args = Array.prototype.slice.call(arguments, 1);
254 iterateNormalLoaders(options, loaderContext, args, callback);
255 });
256}
257
258exports.getContext = function getContext(resource) {
259 var path = parsePathQueryFragment(resource).path;
260 return dirname(path);
261};
262
263exports.runLoaders = function runLoaders(options, callback) {
264 // read options
265 var resource = options.resource || "";
266 var loaders = options.loaders || [];
267 var loaderContext = options.context || {};
268 var processResource = options.processResource || ((readResource, context, resource, callback) => {
269 context.addDependency(resource);
270 readResource(resource, callback);
271 }).bind(null, options.readResource || readFile);
272
273 //
274 var splittedResource = resource && parsePathQueryFragment(resource);
275 var resourcePath = splittedResource ? splittedResource.path : undefined;
276 var resourceQuery = splittedResource ? splittedResource.query : undefined;
277 var resourceFragment = splittedResource ? splittedResource.fragment : undefined;
278 var contextDirectory = resourcePath ? dirname(resourcePath) : null;
279
280 // execution state
281 var requestCacheable = true;
282 var fileDependencies = [];
283 var contextDependencies = [];
284 var missingDependencies = [];
285
286 // prepare loader objects
287 loaders = loaders.map(createLoaderObject);
288
289 loaderContext.context = contextDirectory;
290 loaderContext.loaderIndex = 0;
291 loaderContext.loaders = loaders;
292 loaderContext.resourcePath = resourcePath;
293 loaderContext.resourceQuery = resourceQuery;
294 loaderContext.resourceFragment = resourceFragment;
295 loaderContext.async = null;
296 loaderContext.callback = null;
297 loaderContext.cacheable = function cacheable(flag) {
298 if(flag === false) {
299 requestCacheable = false;
300 }
301 };
302 loaderContext.dependency = loaderContext.addDependency = function addDependency(file) {
303 fileDependencies.push(file);
304 };
305 loaderContext.addContextDependency = function addContextDependency(context) {
306 contextDependencies.push(context);
307 };
308 loaderContext.addMissingDependency = function addMissingDependency(context) {
309 missingDependencies.push(context);
310 };
311 loaderContext.getDependencies = function getDependencies() {
312 return fileDependencies.slice();
313 };
314 loaderContext.getContextDependencies = function getContextDependencies() {
315 return contextDependencies.slice();
316 };
317 loaderContext.getMissingDependencies = function getMissingDependencies() {
318 return missingDependencies.slice();
319 };
320 loaderContext.clearDependencies = function clearDependencies() {
321 fileDependencies.length = 0;
322 contextDependencies.length = 0;
323 missingDependencies.length = 0;
324 requestCacheable = true;
325 };
326 Object.defineProperty(loaderContext, "resource", {
327 enumerable: true,
328 get: function() {
329 if(loaderContext.resourcePath === undefined)
330 return undefined;
331 return loaderContext.resourcePath.replace(/#/g, "\0#") + loaderContext.resourceQuery.replace(/#/g, "\0#") + loaderContext.resourceFragment;
332 },
333 set: function(value) {
334 var splittedResource = value && parsePathQueryFragment(value);
335 loaderContext.resourcePath = splittedResource ? splittedResource.path : undefined;
336 loaderContext.resourceQuery = splittedResource ? splittedResource.query : undefined;
337 loaderContext.resourceFragment = splittedResource ? splittedResource.fragment : undefined;
338 }
339 });
340 Object.defineProperty(loaderContext, "request", {
341 enumerable: true,
342 get: function() {
343 return loaderContext.loaders.map(function(o) {
344 return o.request;
345 }).concat(loaderContext.resource || "").join("!");
346 }
347 });
348 Object.defineProperty(loaderContext, "remainingRequest", {
349 enumerable: true,
350 get: function() {
351 if(loaderContext.loaderIndex >= loaderContext.loaders.length - 1 && !loaderContext.resource)
352 return "";
353 return loaderContext.loaders.slice(loaderContext.loaderIndex + 1).map(function(o) {
354 return o.request;
355 }).concat(loaderContext.resource || "").join("!");
356 }
357 });
358 Object.defineProperty(loaderContext, "currentRequest", {
359 enumerable: true,
360 get: function() {
361 return loaderContext.loaders.slice(loaderContext.loaderIndex).map(function(o) {
362 return o.request;
363 }).concat(loaderContext.resource || "").join("!");
364 }
365 });
366 Object.defineProperty(loaderContext, "previousRequest", {
367 enumerable: true,
368 get: function() {
369 return loaderContext.loaders.slice(0, loaderContext.loaderIndex).map(function(o) {
370 return o.request;
371 }).join("!");
372 }
373 });
374 Object.defineProperty(loaderContext, "query", {
375 enumerable: true,
376 get: function() {
377 var entry = loaderContext.loaders[loaderContext.loaderIndex];
378 return entry.options && typeof entry.options === "object" ? entry.options : entry.query;
379 }
380 });
381 Object.defineProperty(loaderContext, "data", {
382 enumerable: true,
383 get: function() {
384 return loaderContext.loaders[loaderContext.loaderIndex].data;
385 }
386 });
387
388 // finish loader context
389 if(Object.preventExtensions) {
390 Object.preventExtensions(loaderContext);
391 }
392
393 var processOptions = {
394 resourceBuffer: null,
395 processResource: processResource
396 };
397 iteratePitchingLoaders(processOptions, loaderContext, function(err, result) {
398 if(err) {
399 return callback(err, {
400 cacheable: requestCacheable,
401 fileDependencies: fileDependencies,
402 contextDependencies: contextDependencies,
403 missingDependencies: missingDependencies
404 });
405 }
406 callback(null, {
407 result: result,
408 resourceBuffer: processOptions.resourceBuffer,
409 cacheable: requestCacheable,
410 fileDependencies: fileDependencies,
411 contextDependencies: contextDependencies,
412 missingDependencies: missingDependencies
413 });
414 });
415};
Note: See TracBrowser for help on using the repository browser.