index.test.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. var path = require("path");
  2. var fs = require("fs");
  3. var should = require("should");
  4. var loader = require("../");
  5. function execLoader(filename, callback) {
  6. var async = false;
  7. var deps = [];
  8. var warns = [];
  9. var context = {
  10. context: path.dirname(filename),
  11. resolve: function(context, request, callback) {
  12. process.nextTick(function() {
  13. var p = path.isAbsolute(request) ? request : path.resolve(context, request);
  14. if(fs.existsSync(p))
  15. callback(null, p);
  16. else
  17. callback(new Error("File not found"));
  18. });
  19. },
  20. addDependency: function(dep) {
  21. deps.push(dep);
  22. },
  23. emitWarning: function(warn) {
  24. warns.push(warn);
  25. },
  26. callback: function(err, res, map) {
  27. async = true;
  28. callback(err, res, map, deps, warns);
  29. },
  30. async: function() {
  31. async = true;
  32. return this.callback;
  33. }
  34. };
  35. // Remove CRs to make test line ending invariant
  36. var fixtureContent = fs.readFileSync(filename, "utf-8").replace(/\r/g, '');
  37. var res = loader.call(context, fixtureContent);
  38. if(!async) return callback(null, res, null, deps, warns);
  39. }
  40. describe("source-map-loader", function() {
  41. const fixturesPath = path.join(__dirname, "fixtures");
  42. const dataPath = path.join(fixturesPath, "data");
  43. it("should leave normal files untouched", function(done) {
  44. execLoader(path.join(fixturesPath, "normal-file.js"), function(err, res, map, deps, warns) {
  45. should.equal(err, null);
  46. warns.should.be.eql([]);
  47. should.equal(res, "without SourceMap"),
  48. should.equal(map, null);
  49. deps.should.be.eql([]);
  50. done();
  51. });
  52. });
  53. it("should process inlined SourceMaps", function(done) {
  54. execLoader(path.join(fixturesPath, "inline-source-map.js"), function(err, res, map, deps, warns) {
  55. should.equal(err, null);
  56. warns.should.be.eql([]);
  57. should.equal(res, "with SourceMap\n// comment"),
  58. map.should.be.eql({
  59. "version":3,
  60. "file":"inline-source-map.js",
  61. "sources":[
  62. "inline-source-map.txt"
  63. ],
  64. "sourcesContent":["with SourceMap"],
  65. "mappings":"AAAA"
  66. });
  67. deps.should.be.eql([]);
  68. done();
  69. });
  70. });
  71. it("should process external SourceMaps", function(done) {
  72. execLoader(path.join(fixturesPath, "external-source-map.js"), function(err, res, map, deps, warns) {
  73. should.equal(err, null);
  74. warns.should.be.eql([]);
  75. should.equal(res, "with SourceMap\n// comment"),
  76. map.should.be.eql({
  77. "version":3,
  78. "file":"external-source-map.js",
  79. "sources":[
  80. "external-source-map.txt"
  81. ],
  82. "sourcesContent":["with SourceMap"],
  83. "mappings":"AAAA"
  84. });
  85. deps.should.be.eql([
  86. path.join(fixturesPath, "external-source-map.map")
  87. ]);
  88. done();
  89. });
  90. });
  91. it("should process external SourceMaps (external sources)", function(done) {
  92. execLoader(path.join(fixturesPath, "external-source-map2.js"), function(err, res, map, deps, warns) {
  93. should.equal(err, null);
  94. warns.should.be.eql([]);
  95. should.equal(res, "with SourceMap\n// comment"),
  96. map.should.be.eql({
  97. "version":3,
  98. "file":"external-source-map2.js",
  99. "sources":[
  100. path.join(fixturesPath, "external-source-map2.txt")
  101. ],
  102. "sourcesContent":["with SourceMap"],
  103. "mappings":"AAAA"
  104. });
  105. deps.should.be.eql([
  106. path.join(dataPath, "external-source-map2.map"),
  107. path.join(fixturesPath, "external-source-map2.txt")
  108. ]);
  109. done();
  110. });
  111. });
  112. it("should use last SourceMap directive", function (done) {
  113. execLoader(path.join(fixturesPath, "multi-source-map.js"), function (err, res, map, deps, warns) {
  114. should.equal(err, null);
  115. warns.should.be.eql([]);
  116. should.equal(res, "with SourceMap\nanInvalidDirective = \"\\n/*# sourceMappingURL=data:application/json;base64,\"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+\" */\";\n// comment"),
  117. map.should.be.eql({
  118. "version": 3,
  119. "file": "inline-source-map.js",
  120. "sources": [
  121. "inline-source-map.txt"
  122. ],
  123. "sourcesContent": ["with SourceMap"],
  124. "mappings": "AAAA"
  125. });
  126. deps.should.be.eql([]);
  127. done();
  128. });
  129. });
  130. it("should skip invalid base64 SourceMap", function (done) {
  131. execLoader(path.join(fixturesPath, "invalid-inline-source-map.js"), function (err, res, map, deps, warns) {
  132. should.equal(err, null);
  133. warns.should.be.eql([]);
  134. should.equal(res, "without SourceMap\n// @sourceMappingURL=data:application/source-map;base64,\"something invalid\"\n// comment");
  135. should.equal(map, null);
  136. deps.should.be.eql([]);
  137. done();
  138. });
  139. });
  140. it("should warn on invalid base64 SourceMap", function (done) {
  141. execLoader(path.join(fixturesPath, "invalid-inline-source-map2.js"), function (err, res, map, deps, warns) {
  142. should.equal(err, null);
  143. warns.should.matchEach(
  144. new RegExp("Cannot parse inline SourceMap 'invalid\/base64=': SyntaxError: Unexpected token")
  145. );
  146. should.equal(res, "without SourceMap\n// @sourceMappingURL=data:application/source-map;base64,invalid/base64=\n// comment");
  147. should.equal(map, null);
  148. deps.should.be.eql([]);
  149. done();
  150. });
  151. });
  152. it("should warn on invalid SourceMap", function (done) {
  153. execLoader(path.join(fixturesPath, "invalid-source-map.js"), function (err, res, map, deps, warns) {
  154. should.equal(err, null);
  155. warns.should.matchEach(
  156. new RegExp("Cannot parse SourceMap 'invalid-source-map.map': SyntaxError: Unexpected string in JSON at position 102")
  157. );
  158. should.equal(res, "with SourceMap\n//#sourceMappingURL=invalid-source-map.map\n// comment");
  159. should.equal(map, null);
  160. deps.should.be.eql([
  161. path.join(fixturesPath, "invalid-source-map.map")
  162. ]);
  163. done();
  164. });
  165. });
  166. it("should warn on missing SourceMap", function(done) {
  167. execLoader(path.join(fixturesPath, "missing-source-map.js"), function(err, res, map, deps, warns) {
  168. should.equal(err, null);
  169. warns.should.be.eql([
  170. "Cannot find SourceMap 'missing-source-map.map': Error: File not found"
  171. ]);
  172. should.equal(res, "with SourceMap\n//#sourceMappingURL=missing-source-map.map\n// comment"),
  173. should.equal(map, null);
  174. deps.should.be.eql([]);
  175. done();
  176. });
  177. });
  178. it("should warn on missing source file", function(done) {
  179. execLoader(path.join(fixturesPath, "missing-source-map2.js"), function(err, res, map, deps, warns) {
  180. should.equal(err, null);
  181. warns.should.be.eql([
  182. "Cannot find source file 'missing-source-map2.txt': Error: File not found"
  183. ]);
  184. should.equal(res, "with SourceMap\n// comment"),
  185. map.should.be.eql({
  186. "version":3,
  187. "file":"missing-source-map2.js",
  188. "sources":[
  189. "missing-source-map2.txt"
  190. ],
  191. "sourcesContent":[null],
  192. "mappings":"AAAA"
  193. });
  194. deps.should.be.eql([
  195. path.join(fixturesPath, "missing-source-map2.map")
  196. ]);
  197. done();
  198. });
  199. });
  200. it("should process inlined SourceMaps with charset", function(done) {
  201. execLoader(path.join(fixturesPath, "charset-inline-source-map.js"), function(err, res, map, deps, warns) {
  202. should.equal(err, null);
  203. warns.should.be.eql([]);
  204. should.equal(res, "with SourceMap\n// comment"),
  205. map.should.be.eql({
  206. "version":3,
  207. "file":"charset-inline-source-map.js",
  208. "sources":[
  209. "charset-inline-source-map.txt"
  210. ],
  211. "sourcesContent":["with SourceMap"],
  212. "mappings":"AAAA"
  213. });
  214. deps.should.be.eql([]);
  215. done();
  216. });
  217. });
  218. it("should support absolute sourceRoot paths in sourcemaps", (done) => {
  219. const sourceRoot = path.join(fixturesPath);
  220. const javaScriptFilename = "absolute-sourceRoot-source-map.js";
  221. const sourceFilename = "absolute-sourceRoot-source-map.txt";
  222. const rootRelativeSourcePath = path.join(sourceRoot, sourceFilename);
  223. const sourceMapPath = path.join(sourceRoot, "absolute-sourceRoot-source-map.map");
  224. // Create the sourcemap file
  225. const rawSourceMap = {
  226. "version": 3,
  227. "file": javaScriptFilename,
  228. "sourceRoot": sourceRoot,
  229. "sources": [
  230. sourceFilename
  231. ],
  232. "mappings": "AAAA"
  233. };
  234. fs.writeFileSync(sourceMapPath, JSON.stringify(rawSourceMap));
  235. execLoader(
  236. path.join(fixturesPath, javaScriptFilename),
  237. (err, res, map, deps, warns) => {
  238. should.equal(err, null);
  239. warns.should.be.eql([]);
  240. should.equal(res, "with SourceMap\n// comment"),
  241. map.should.be.eql({
  242. "version": 3,
  243. "file": javaScriptFilename,
  244. "sources": [
  245. rootRelativeSourcePath
  246. ],
  247. "sourcesContent": [
  248. "with SourceMap\n// comment"
  249. ],
  250. "mappings": "AAAA"
  251. });
  252. deps.should.be.eql([
  253. sourceMapPath,
  254. rootRelativeSourcePath
  255. ]);
  256. done();
  257. }
  258. );
  259. });
  260. it("should support relative sourceRoot paths in sourcemaps", (done) => {
  261. const javaScriptFilename = "relative-sourceRoot-source-map.js";
  262. const sourceFilename = "relative-sourceRoot-source-map.txt";
  263. const rootRelativeSourcePath = path.join(dataPath, sourceFilename);
  264. const sourceMapPath = path.join(fixturesPath, "relative-sourceRoot-source-map.map");
  265. execLoader(
  266. path.join(fixturesPath, javaScriptFilename),
  267. (err, res, map, deps, warns) => {
  268. should.equal(err, null);
  269. warns.should.be.eql([]);
  270. should.equal(res, "with SourceMap\n// comment"),
  271. map.should.be.eql({
  272. "version": 3,
  273. "file": javaScriptFilename,
  274. "sources": [
  275. rootRelativeSourcePath
  276. ],
  277. "sourcesContent": [
  278. "with SourceMap\n// comment"
  279. ],
  280. "mappings": "AAAA"
  281. });
  282. deps.should.be.eql([
  283. sourceMapPath,
  284. rootRelativeSourcePath
  285. ]);
  286. done();
  287. }
  288. );
  289. });
  290. });