UNPKG

9.96 kBJavaScriptView Raw
1import path from 'path'
2
3import write_assets from './write assets'
4import notify_stats from './notify stats'
5
6import Log from './../tools/log'
7
8import { exists, clone, convert_from_camel_case, alias_properties_with_camel_case } from './../helpers'
9
10import { default_webpack_assets, normalize_options, verbosity_levels } from './../common'
11
12// a Webpack plugin
13export default function Webpack_isomorphic_tools_plugin(options)
14{
15 // take the passed in options
16 this.options = convert_from_camel_case(clone(options))
17
18 // add missing fields, etc
19 normalize_options(this.options)
20
21 // logging
22 this.log = new Log('webpack-isomorphic-tools/plugin', { debug: this.options.debug })
23
24 // assets regular expressions (based on extensions).
25 // will be used in loaders and in write_assets
26 this.regular_expressions = {}
27
28 // for each user defined asset type
29 for (let asset_type of Object.keys(this.options.assets))
30 {
31 const description = this.options.assets[asset_type]
32
33 // create a regular expression for this file extension (or these file extensions)
34 this.regular_expressions[asset_type] = description.regular_expression || Webpack_isomorphic_tools_plugin.regular_expression(description.extensions)
35 }
36}
37
38// starts HTTP service in development mode
39// https://github.com/halt-hammerzeit/webpack-isomorphic-tools/issues/92
40Webpack_isomorphic_tools_plugin.prototype.start_dev_server = function()
41{
42 const express = require('express')
43 const app = express()
44
45 app.get('/', (request, response) =>
46 {
47 if (!this.assets)
48 {
49 return response.status(404).send('Webpack assets not generated yet')
50 }
51
52 response.send(this.assets)
53 })
54
55 app.listen(this.options.port, () =>
56 {
57 this.log.info(`HTTP service listening on port ${this.options.port}`)
58 })
59}
60
61// creates a regular expression for this file extension (or these file extensions)
62Webpack_isomorphic_tools_plugin.prototype.regular_expression = function(asset_type)
63{
64 if (!exists(this.regular_expressions[asset_type]))
65 {
66 throw new Error(`There's no asset type "${asset_type}" defined in webpack-isomorphic-tools configuration. Perhaps you didn't spell it correctly.`)
67 }
68
69 return this.regular_expressions[asset_type]
70}
71
72// shorthand alias
73Webpack_isomorphic_tools_plugin.prototype.regexp = Webpack_isomorphic_tools_plugin.prototype.regular_expression
74
75// creates a regular expression for this file extension (or these file extensions)
76Webpack_isomorphic_tools_plugin.regular_expression = function(extensions)
77{
78 if (!Array.isArray(extensions))
79 {
80 throw new Error(`You were expected to pass a list of extensions (an array). Instead got: ${extensions}. Maybe you were looking for the instance method istead of the class method of this plugin?`)
81 }
82
83 let matcher
84 if (extensions.length > 1)
85 {
86 matcher = `(${extensions.join('|')})`
87 }
88 else
89 {
90 matcher = extensions
91 }
92
93 return new RegExp(`\\.${matcher}$`)
94}
95
96// sets development mode flag to whatever was passed (or true if nothing was passed)
97// (development mode allows asset hot reloading when used with webpack-dev-server)
98Webpack_isomorphic_tools_plugin.prototype.development = function(flag)
99{
100 // set development mode flag
101 this.options.development = exists(flag) ? flag : true
102
103 if (this.options.development)
104 {
105 this.log.debug('entering development mode')
106 }
107 else
108 {
109 this.log.debug('entering production mode')
110 }
111
112 // allows method chaining
113 return this
114}
115
116// applies the plugin to the Webpack build
117Webpack_isomorphic_tools_plugin.prototype.apply = function(compiler)
118{
119 // start HTTP service in development mode
120 // https://github.com/halt-hammerzeit/webpack-isomorphic-tools/issues/92
121 //
122 // (`.apply()` is only called once, so can start the dev server here)
123 //
124 if (this.options.development && this.options.port)
125 {
126 this.start_dev_server()
127 }
128
129 // Webpack configuration
130 const webpack_configuration = compiler.options
131
132 // validate webpack configuration
133 if (!webpack_configuration.context)
134 {
135 throw new Error('You must specify ".context" in your webpack configuration')
136 }
137
138 // project base path, required to output webpack-assets.json
139 this.options.project_path = webpack_configuration.context
140
141 // resolve webpack-assets.json file path
142 const webpack_assets_path = path.resolve(this.options.project_path, this.options.webpack_assets_file_path)
143
144 // resolve webpack-stats.json file path
145 const webpack_stats_path = path.resolve(this.options.project_path, this.options.webpack_stats_file_path)
146
147 // selfie
148 const plugin = this
149
150 // when all is done
151 // https://github.com/webpack/docs/wiki/plugins
152 compiler.plugin('done', function(stats)
153 {
154 plugin.log.debug('------------------- Started -------------------')
155
156 const json = stats.toJson
157 ({
158 context: webpack_configuration.context
159 })
160
161 // output some info to the console if in development mode
162 if (plugin.options.development && plugin.options.verbosity !== verbosity_levels.no_webpack_stats)
163 {
164 // outputs stats info to the console
165 // (only needed in development mode)
166 notify_stats(stats, json, plugin.options.verbosity === verbosity_levels.webpack_stats_for_each_build)
167 }
168
169 // assets base path (on disk or on the network)
170 //
171 // (first search for the `devServer.publicPath` setting,
172 // then fallback to the generic `publicPath`)
173 //
174 // (using `publicPath` from webpack stats here
175 // as opposed to `webpack_configuration.output.publicPath`
176 // because it is processed by webpack replacing things like `[hash]`)
177 //
178 const assets_base_url = (webpack_configuration.devServer && webpack_configuration.devServer.publicPath) ? webpack_configuration.devServer.publicPath : json.publicPath
179
180 // serve webpack assets from RAM rather than from disk
181 const serve_assets_from_memory = plugin.options.development && plugin.options.port
182
183 // write webpack-assets.json with assets info
184 // and cache them in plugin instance
185 // for later serving from HTTP service
186 plugin.assets = write_assets(json,
187 {
188 development : plugin.options.development,
189 debug : plugin.options.debug,
190 assets : plugin.options.assets,
191 alias : plugin.options.alias,
192 project_path : plugin.options.project_path,
193 assets_base_url,
194 webpack_assets_path : webpack_assets_path,
195 webpack_stats_path : webpack_stats_path,
196 output : default_webpack_assets(),
197 output_to_a_file : !serve_assets_from_memory,
198 regular_expressions : plugin.regular_expressions
199 },
200 plugin.log)
201
202 plugin.log.debug('------------------- Finished -------------------')
203 })
204}
205
206// a sample module source parser for webpack url-loader
207// (works for images, fonts, and i guess for everything else, should work for any file type)
208Webpack_isomorphic_tools_plugin.url_loader_parser = function(module, options, log)
209{
210 return module.source
211}
212
213// a sample module source parser for webpack css-loader
214// (without css-loader "modules" feature support)
215Webpack_isomorphic_tools_plugin.css_loader_parser = function(module, options, log)
216{
217 return module.source + '\n module.exports = module.exports.toString();'
218}
219
220// a sample module source parser for webpack css-loader
221// (with css-loader "modules" feature support)
222Webpack_isomorphic_tools_plugin.css_modules_loader_parser = function(module, options, log)
223{
224 return module.source + '\n module.exports = exports.locals || {}; module.exports._style = exports.toString();'
225}
226
227// a filter for getting a css module when using it with style-loader
228//
229// in development mode there's webpack "style-loader",
230// so the module with module.name equal to the asset path is not what's needed
231// (because what that module does is it creates a <style/> tag on the page).
232// the module with the CSS styles is the one with a long name:
233Webpack_isomorphic_tools_plugin.style_loader_filter = function(module, regular_expression, options, log)
234{
235 const css_loader = module.name.split('!')[0]
236 return regular_expression.test(module.name) &&
237 // The paths below have the form of "/~/css-loader"
238 // and not the form of "./~/css-loader"
239 // because in some (non-standard) cases
240 // Webpack project context can be set
241 // not to project root folder.
242 //
243 // For a discussion see:
244 // https://github.com/halt-hammerzeit/webpack-isomorphic-tools/pull/68
245 // (there the `context` is set to the "${project_root}/src" folder
246 // so that the asset paths in `webpack-assets.json` wouldn't
247 // contain the "./src" prefix and therefore they will be found
248 // when require()d from code in "./target"
249 // which is compiled with Babel from the "./src" folder)
250 //
251 // I personally don't compile sources on the server side,
252 // so I haven't thought of better ways of doing all that.
253 //
254 (css_loader.indexOf('/~/css-loader') > 0 ||
255 css_loader.indexOf('/~/.npminstall/css-loader') > 0 ||
256 css_loader.indexOf('/~/.store/css-loader') > 0)
257}
258
259// extracts css style file path
260Webpack_isomorphic_tools_plugin.style_loader_path_extractor = function(module, options, log)
261{
262 return module.name.slice(module.name.lastIndexOf('!') + 1)
263}
264
265// Doesn't work with Babel 6 compiler
266// // alias camel case for those who prefer it
267// alias_properties_with_camel_case(Webpack_isomorphic_tools_plugin.prototype)
268// alias_properties_with_camel_case(Webpack_isomorphic_tools_plugin)
269
270// camelCase aliases
271
272Webpack_isomorphic_tools_plugin.prototype.regularExpression = Webpack_isomorphic_tools_plugin.prototype.regular_expression
273
274Webpack_isomorphic_tools_plugin.urlLoaderParser = Webpack_isomorphic_tools_plugin.url_loader_parser
275Webpack_isomorphic_tools_plugin.cssLoaderParser = Webpack_isomorphic_tools_plugin.css_loader_parser
276Webpack_isomorphic_tools_plugin.cssModulesLoaderParser = Webpack_isomorphic_tools_plugin.css_modules_loader_parser
277Webpack_isomorphic_tools_plugin.styleLoaderFilter = Webpack_isomorphic_tools_plugin.style_loader_filter
278Webpack_isomorphic_tools_plugin.styleLoaderPathExtractor = Webpack_isomorphic_tools_plugin.style_loader_path_extractor
\No newline at end of file