1 | var _ = require("lodash");
|
2 | var path = require("path");
|
3 | var async = require("async");
|
4 | var webpackDevMiddleware = require("webpack-dev-middleware");
|
5 | var webpack = require("webpack");
|
6 | var SingleEntryDependency = require("webpack/lib/dependencies/SingleEntryDependency");
|
7 |
|
8 | function Plugin(
|
9 | /* config.webpack */webpackOptions,
|
10 | /* config.webpackServer */webpackServerOptions,
|
11 | /* config.webpackMiddleware */webpackMiddlewareOptions,
|
12 | /* config.basePath */basePath,
|
13 | /* config.files */files,
|
14 | /* config.frameworks */frameworks,
|
15 | customFileHandlers,
|
16 | emitter) {
|
17 | webpackOptions = _.clone(webpackOptions) || {};
|
18 | webpackMiddlewareOptions = _.clone(webpackMiddlewareOptions || webpackServerOptions) || {};
|
19 |
|
20 | var applyOptions = Array.isArray(webpackOptions) ? webpackOptions : [webpackOptions];
|
21 | var includeIndex = applyOptions.length > 1;
|
22 |
|
23 | applyOptions.forEach(function(webpackOptions, index) {
|
24 |
|
25 | webpackOptions.watch = true;
|
26 |
|
27 | if(!webpackOptions.output) webpackOptions.output = {};
|
28 |
|
29 |
|
30 |
|
31 | var indexPath = includeIndex ? index + "/" : "";
|
32 |
|
33 |
|
34 |
|
35 | webpackOptions.output.path = "/_karma_webpack_/" + indexPath;
|
36 | webpackOptions.output.publicPath = "/_karma_webpack_/" + indexPath + "/";
|
37 | webpackOptions.output.filename = "[name]";
|
38 | if(includeIndex)
|
39 | webpackOptions.output.jsonpFunction = "webpackJsonp" + index;
|
40 | webpackOptions.output.chunkFilename = "[id].chunk.js";
|
41 | });
|
42 |
|
43 | this.emitter = emitter;
|
44 | this.wrapMocha = frameworks.indexOf('mocha') >= 0 && includeIndex;
|
45 | this.optionsCount = applyOptions.length;
|
46 | this.files = [];
|
47 | this.basePath = basePath;
|
48 | this.waiting = [];
|
49 |
|
50 | var compiler = webpack(webpackOptions);
|
51 | var applyPlugins = compiler.compilers || [compiler];
|
52 | applyPlugins.forEach(function(compiler) {
|
53 | compiler.plugin("this-compilation", function(compilation, params) {
|
54 | compilation.dependencyFactories.set(SingleEntryDependency, params.normalModuleFactory);
|
55 | });
|
56 | compiler.plugin("make", this.make.bind(this));
|
57 | }, this);
|
58 |
|
59 | compiler.plugin("done", function(stats) {
|
60 | var applyStats = Array.isArray(stats.stats) ? stats.stats : [stats];
|
61 | var assets = [];
|
62 | var noAssets = false;
|
63 | applyStats.forEach(function(stats) {
|
64 | stats = stats.toJson();
|
65 |
|
66 | assets.push.apply(assets, stats.assets);
|
67 | if(stats.assets.length === 0)
|
68 | noAssets = true;
|
69 | });
|
70 |
|
71 | if(!this.waiting || this.waiting.length === 0) {
|
72 | this.notifyKarmaAboutChanges();
|
73 | }
|
74 |
|
75 | if(this.waiting && !noAssets) {
|
76 | var w = this.waiting;
|
77 | this.waiting = null;
|
78 | w.forEach(function(cb) {
|
79 | cb();
|
80 | });
|
81 | }
|
82 | }.bind(this));
|
83 | compiler.plugin("invalid", function() {
|
84 | if(!this.waiting) this.waiting = [];
|
85 | }.bind(this));
|
86 |
|
87 | webpackMiddlewareOptions.publicPath = "/_karma_webpack_/";
|
88 | var middleware = this.middleware = new webpackDevMiddleware(compiler, webpackMiddlewareOptions);
|
89 |
|
90 | customFileHandlers.push({
|
91 | urlRegex: /^\/_karma_webpack_\/.*/,
|
92 | handler: function(req, res) {
|
93 | middleware(req, res, function() {
|
94 | res.statusCode = 404;
|
95 | res.end('Not found');
|
96 | });
|
97 | }
|
98 | });
|
99 |
|
100 | emitter.on("exit", function (done) {
|
101 | middleware.close();
|
102 | done();
|
103 | });
|
104 | }
|
105 |
|
106 | Plugin.prototype.notifyKarmaAboutChanges = function() {
|
107 |
|
108 | this.emitter.refreshFiles();
|
109 | };
|
110 |
|
111 | Plugin.prototype.addFile = function(entry) {
|
112 | if(this.files.indexOf(entry) >= 0) return;
|
113 | this.files.push(entry);
|
114 | return true;
|
115 | };
|
116 |
|
117 | Plugin.prototype.make = function(compilation, callback) {
|
118 | async.forEach(this.files.slice(), function(file, callback) {
|
119 | var entry = file;
|
120 | if (this.wrapMocha) {
|
121 | entry = require.resolve("./mocha-env-loader") + "!" + entry;
|
122 | }
|
123 |
|
124 | var dep = new SingleEntryDependency(entry);
|
125 | compilation.addEntry("", dep, path.relative(this.basePath, file).replace(/\\/g, "/"), function() {
|
126 |
|
127 | if(dep.module && dep.module.error && dep.module.error.error && dep.module.error.error.code === "ENOENT") {
|
128 | this.files = this.files.filter(function(f) {
|
129 | return file !== f;
|
130 | });
|
131 | this.middleware.invalidate();
|
132 | }
|
133 | callback();
|
134 | }.bind(this));
|
135 | }.bind(this), callback);
|
136 | };
|
137 |
|
138 | Plugin.prototype.readFile = function(file, callback) {
|
139 | var middleware = this.middleware;
|
140 | var optionsCount = this.optionsCount;
|
141 |
|
142 | function doRead() {
|
143 | if(optionsCount > 1) {
|
144 | async.times(optionsCount, function(idx, callback) {
|
145 | middleware.fileSystem.readFile("/_karma_webpack_/" + idx + "/" + file.replace(/\\/g, "/"), callback);
|
146 | }, function(err, contents) {
|
147 | if(err) return callback(err);
|
148 | contents = contents.reduce(function(arr, x) {
|
149 | if(!arr) return [x];
|
150 | arr.push(new Buffer("\n"), x);
|
151 | return arr;
|
152 | }, null);
|
153 | callback(null, Buffer.concat(contents));
|
154 | });
|
155 | } else {
|
156 | middleware.fileSystem.readFile("/_karma_webpack_/" + file.replace(/\\/g, "/"), callback);
|
157 | }
|
158 | }
|
159 | if(!this.waiting)
|
160 | doRead();
|
161 | else
|
162 |
|
163 |
|
164 | this.waiting.push(process.nextTick.bind(process, this.readFile.bind(this, file, callback)));
|
165 | };
|
166 |
|
167 | function createPreprocesor(/* config.basePath */basePath, webpackPlugin) {
|
168 | return function(content, file, done) {
|
169 |
|
170 | if (webpackPlugin.addFile(file.path)) {
|
171 |
|
172 | webpackPlugin.middleware.invalidate();
|
173 | }
|
174 |
|
175 |
|
176 | webpackPlugin.readFile(path.relative(basePath, file.path), function(err, content) {
|
177 | if (err) {
|
178 | throw err;
|
179 | }
|
180 |
|
181 | done(err, content && content.toString());
|
182 | });
|
183 | };
|
184 | }
|
185 |
|
186 | module.exports = {
|
187 | "webpackPlugin": ["type", Plugin],
|
188 | "preprocessor:webpack": ["factory", createPreprocesor]
|
189 | };
|