NormalModuleMixin.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. var RawSource = require("./RawSource");
  6. var OriginalSource = require("./OriginalSource");
  7. var SourceMapSource = require("./SourceMapSource");
  8. var LineToLineMappedSource = require("./LineToLineMappedSource");
  9. var path = require("path"); // TODO refactor
  10. var ModuleBuildError = require("./ModuleBuildError");
  11. var ModuleError = require("./ModuleError");
  12. var ModuleWarning = require("./ModuleWarning");
  13. function utf8BufferToString(buf) {
  14. var str = buf.toString("utf-8");
  15. if(str.charCodeAt(0) === 0xFEFF) {
  16. return str.substr(1);
  17. } else {
  18. return str;
  19. }
  20. }
  21. function NormalModuleMixin(loaders, resource) {
  22. this.resource = resource;
  23. this.loaders = loaders;
  24. var resourcePath = this.splitQuery(this.resource)[0];
  25. this.context = resourcePath ? path.dirname(resourcePath) : null;
  26. this.fileDependencies = [];
  27. this.contextDependencies = [];
  28. this.warnings = [];
  29. this.errors = [];
  30. this.error = null;
  31. this._source = null;
  32. }
  33. module.exports = NormalModuleMixin;
  34. NormalModuleMixin.mixin = function(pt) {
  35. for(var name in NormalModuleMixin.prototype)
  36. pt[name] = NormalModuleMixin.prototype[name];
  37. };
  38. NormalModuleMixin.prototype.splitQuery = function splitQuery(req) {
  39. var i = req.indexOf("?");
  40. if(i < 0) return [req, ""];
  41. return [req.substr(0, i), req.substr(i)];
  42. };
  43. NormalModuleMixin.prototype.doBuild = function doBuild(options, moduleContext, resolver, fs, callback) {
  44. var splitQuery = this.splitQuery.bind(this);
  45. var module = this;
  46. this.cacheable = true;
  47. // Prepare context
  48. var loaders = [];
  49. function addLoaderToList(loader) {
  50. var l = splitQuery(loader);
  51. loaders.push({
  52. request: loader,
  53. path: l[0],
  54. query: l[1],
  55. module: null
  56. });
  57. }
  58. this.loaders.forEach(addLoaderToList);
  59. var loaderContextCacheable;
  60. var loaderContext = {
  61. version: 1,
  62. context: this.context,
  63. loaders: loaders,
  64. loaderIndex: 0,
  65. resource: this.resource,
  66. resourcePath: splitQuery(this.resource)[0],
  67. resourceQuery: this.resource ? splitQuery(this.resource)[1] || null : undefined,
  68. emitWarning: function(warning) {
  69. this.warnings.push(new ModuleWarning(this, warning));
  70. }.bind(this),
  71. emitError: function(error) {
  72. this.errors.push(new ModuleError(this, error));
  73. }.bind(this),
  74. exec: function(code, filename) {
  75. if(typeof __webpack_modules__ === "undefined") {
  76. // TODO: check if in enhanced-require
  77. var Module = require("module");
  78. var m = new Module(filename, module);
  79. m.paths = Module._nodeModulePaths(loaderContext.context);
  80. m.filename = filename;
  81. m._compile(code, filename);
  82. return m.exports;
  83. } else {
  84. throw new Error("loaderContext.exec is not supported");
  85. }
  86. },
  87. resolve: function(context, request, callback) {
  88. resolver.resolve(context, request, callback);
  89. },
  90. resolveSync: function(context, request) {
  91. return resolver.resolveSync(context, request);
  92. },
  93. cacheable: function(flag) {
  94. loaderContextCacheable = flag !== false;
  95. },
  96. dependency: function(file) {
  97. this.fileDependencies.push(file);
  98. }.bind(this),
  99. addDependency: function(file) {
  100. this.fileDependencies.push(file);
  101. }.bind(this),
  102. addContextDependency: function(context) {
  103. this.contextDependencies.push(context);
  104. }.bind(this),
  105. clearDependencies: function() {
  106. this.fileDependencies.length = 0;
  107. this.contextDependencies.length = 0;
  108. module.cacheable = true;
  109. }.bind(this),
  110. inputValue: undefined,
  111. value: null,
  112. options: options,
  113. debug: options.debug
  114. };
  115. this.fillLoaderContext(loaderContext, options, moduleContext);
  116. if(options.loader) for(var key in options.loader)
  117. loaderContext[key] = options.loader[key];
  118. function runSyncOrAsync(fn, context, args, callback) {
  119. var isSync = true;
  120. var isDone = false;
  121. var isError = false; // internal error
  122. var reportedError = false;
  123. if(!context.async) context.async = function async() {
  124. if(isDone) {
  125. if(reportedError) return; // ignore
  126. throw new Error("async(): The callback was already called.");
  127. }
  128. isSync = false;
  129. return context.callback;
  130. };
  131. context.callback = function() {
  132. if(isDone) {
  133. if(reportedError) return; // ignore
  134. throw new Error("callback(): The callback was already called.");
  135. }
  136. isDone = true;
  137. isSync = false;
  138. try {
  139. callback.apply(null, arguments);
  140. } catch(e) {
  141. isError = true;
  142. throw e;
  143. }
  144. };
  145. try {
  146. var result = (function WEBPACK_CORE_LOADER_EXECUTION() { return fn.apply(context, args) }());
  147. if(isSync) {
  148. isDone = true;
  149. if(result === undefined)
  150. return callback();
  151. return callback(null, result);
  152. }
  153. } catch(e) {
  154. if(isError) throw e;
  155. if(isDone) {
  156. // loader is already "done", so we cannot use the callback function
  157. // for better debugging we print the error on the console
  158. if(typeof e === "object" && e.stack) console.error(e.stack);
  159. else console.error(e);
  160. return;
  161. }
  162. isDone = true;
  163. reportedError = true;
  164. callback(e);
  165. }
  166. }
  167. // Load and pitch loaders
  168. (function loadPitch() {
  169. var l = loaderContext.loaders[loaderContext.loaderIndex];
  170. if(!l) {
  171. return onLoadPitchDone.call(this);
  172. }
  173. if(l.module) {
  174. loaderContext.loaderIndex++;
  175. return loadPitch.call(this);
  176. }
  177. if(typeof __webpack_modules__ === "undefined") {
  178. if(require.supportQuery) {
  179. l.module = require(l.request);
  180. } else {
  181. try {
  182. l.module = require(l.path);
  183. } catch (e) {
  184. // it is possible for node to choke on a require if the FD descriptor
  185. // limit has been reached. give it a chance to recover.
  186. if (e instanceof Error && e.code === 'EMFILE') {
  187. if (typeof setImmediate === 'function') {
  188. // node >= 0.9.0
  189. return setImmediate(loadPitch.bind(this));
  190. } else {
  191. // node < 0.9.0
  192. return process.nextTick(loadPitch.bind(this));
  193. }
  194. }
  195. return callback(e)
  196. }
  197. }
  198. } else if(typeof __webpack_require_loader__ === "function") {
  199. l.module = __webpack_require_loader__(l.request);
  200. } else {
  201. return callback(new Error("Cannot load loader, __webpack_require_loader__ not provided."));
  202. }
  203. if(typeof l.module !== "function")
  204. return callback(new Error("Loader " + l.request + " didn't return a function"));
  205. var pitchedLoaders = [];
  206. var remaining = [];
  207. for(var i = 0; i < loaderContext.loaderIndex; i++)
  208. pitchedLoaders.push(loaderContext.loaders[i].request);
  209. for(i = loaderContext.loaderIndex + 1; i < loaderContext.loaders.length; i++)
  210. remaining.push(loaderContext.loaders[i].request);
  211. remaining.push(loaderContext.resource);
  212. if(typeof l.module.pitch !== "function") return loadPitch.call(this);
  213. loaderContextCacheable = false;
  214. var privateLoaderContext = Object.create(loaderContext);
  215. privateLoaderContext.query = l.query;
  216. runSyncOrAsync(l.module.pitch, privateLoaderContext, [remaining.join("!"), pitchedLoaders.join("!"), l.data = {}], function(err) {
  217. if(err) return onModuleBuildFailed.call(this, err);
  218. if(!loaderContextCacheable) this.cacheable = false;
  219. var args = Array.prototype.slice.call(arguments, 1);
  220. loaderContext.resourcePath = privateLoaderContext.resourcePath;
  221. loaderContext.resourceQuery = privateLoaderContext.resourceQuery;
  222. loaderContext.resource = privateLoaderContext.resource;
  223. loaderContext.loaderIndex = privateLoaderContext.loaderIndex;
  224. if(args.length > 0) {
  225. nextLoader.apply(this, [null].concat(args));
  226. } else {
  227. loadPitch.call(this);
  228. }
  229. }.bind(this));
  230. }.call(this));
  231. var resourceBuffer;
  232. function onLoadPitchDone() {
  233. loaderContext.loaderIndex = loaderContext.loaders.length;
  234. var request = [];
  235. for(var i = 0; i < loaderContext.loaders.length; i++)
  236. request.push(loaderContext.loaders[i].request);
  237. request.push(loaderContext.resource);
  238. loaderContext.request = request.join("!");
  239. var resourcePath = loaderContext.resourcePath;
  240. loaderContextCacheable = true;
  241. if(resourcePath) {
  242. loaderContext.addDependency(resourcePath);
  243. fs.readFile(resourcePath, function(err, buffer) {
  244. if(err) return nextLoader(err);
  245. if(module.lineToLine)
  246. resourceBuffer = buffer;
  247. nextLoader(null, buffer);
  248. });
  249. } else
  250. nextLoader(null, null);
  251. }
  252. function nextLoader(err/*, paramBuffer1, param2, ...*/) {
  253. if(!loaderContextCacheable) module.cacheable = false;
  254. var args = Array.prototype.slice.call(arguments, 1);
  255. if(err) {
  256. // a loader emitted an error
  257. return onModuleBuildFailed.call(module, err);
  258. }
  259. if(loaderContext.loaderIndex === 0) {
  260. if(Buffer.isBuffer(args[0]))
  261. args[0] = utf8BufferToString(args[0]);
  262. return onModuleBuild.apply(module, args);
  263. }
  264. loaderContext.loaderIndex--;
  265. var l = loaderContext.loaders[loaderContext.loaderIndex];
  266. if(!l.module) return nextLoader.apply(null, [null].concat(args));
  267. var privateLoaderContext = Object.create(loaderContext);
  268. privateLoaderContext.data = l.data;
  269. privateLoaderContext.inputValue = loaderContext.inputValue;
  270. privateLoaderContext.query = l.query;
  271. if(!l.module.raw && Buffer.isBuffer(args[0])) {
  272. args[0] = utf8BufferToString(args[0]);
  273. } else if(l.module.raw && typeof args[0] === "string") {
  274. args[0] = new Buffer(args[0], "utf-8");
  275. }
  276. loaderContextCacheable = false;
  277. runSyncOrAsync(l.module, privateLoaderContext, args, function() {
  278. loaderContext.inputValue = privateLoaderContext.value;
  279. nextLoader.apply(null, arguments);
  280. });
  281. }
  282. function onModuleBuild(source, sourceMap) {
  283. if(!Buffer.isBuffer(source) && typeof source !== "string")
  284. return onModuleBuildFailed.call(this, new Error("Final loader didn't return a Buffer or String"));
  285. if(this.identifier && this.lineToLine && resourceBuffer) {
  286. this._source = new LineToLineMappedSource(source, this.identifier(),
  287. resourceBuffer.toString("utf-8"));
  288. } else if(this.identifier && this.useSourceMap && sourceMap) {
  289. this._source = new SourceMapSource(source, this.identifier(), sourceMap);
  290. } else if(this.identifier) {
  291. this._source = new OriginalSource(source, this.identifier());
  292. } else {
  293. this._source = new RawSource(source);
  294. }
  295. return callback();
  296. }
  297. function onModuleBuildFailed(err) {
  298. this.error = err;
  299. return callback(new ModuleBuildError(this, err));
  300. }
  301. };
  302. NormalModuleMixin.prototype.fillLoaderContext = function fillLoaderContext() {};