1 | # webpack-isomorphic-tools
|
2 |
|
3 | [![NPM Version][npm-image]][npm-url]
|
4 | [![NPM Downloads][downloads-image]][downloads-url]
|
5 | [![Build Status][travis-image]][travis-url]
|
6 | [![Test Coverage][coveralls-image]][coveralls-url]
|
7 |
|
8 |
|
9 | [![Gratipay][gratipay-image]][gratipay-url]
|
10 | -->
|
11 |
|
12 | Is a small helper module providing support for isomorphic (universal) rendering when using Webpack.
|
13 |
|
14 | ## What it does and why is it needed?
|
15 |
|
16 | Javascript allows you to run all your `.js` code (Views, Controllers, Stores, and so on) both on the client and the server, and Webpack gives you the ability to just `require()` your javascript modules both on the client and the server so that the same code works both on the client and the server automagically (I guess that was the main purpose of Webpack).
|
17 |
|
18 | When you write your web application in React, you create the main `style.css` where you describe all your base styles (h1, h2, a, p, nav, footer, fonts, etc).
|
19 |
|
20 | Then, you use inline styles to style each React component individually (use [react-styling](https://github.com/halt-hammerzeit/react-styling) for that).
|
21 |
|
22 | What about that `style.css` file? On the server in development mode it needs to be injected automagically through javascript to support hot module reload, so you don't need to know the exact path to it on disk because it isn't even a `.css` file on your disk: it's actually a javascript file because that's how Webpack [style-loader](https://github.com/webpack/style-loader) works. So you don't need to `require()` your styles in the server code because you simply can't because there are no such files. (You only need to require `style.css` in your `client-application.js` which is gonna be a Webpack entry point)
|
23 |
|
24 | What about fonts? Fonts are parsed correctly by Webpack [css-loader](https://github.com/webpack/css-loader) when it finds `url()` sections in your main `style.css`, so no issues there.
|
25 |
|
26 | What's left are images. Images are `require()`d in React components and then used like this:
|
27 |
|
28 | ```javascript
|
29 | // alternatively one can use import, but in this case hot reloading won't work
|
30 | // import image from '../image.png'
|
31 |
|
32 | // next you just `src` your image inside your `render()` method
|
33 | class Photo extends React.Component
|
34 | {
|
35 | render()
|
36 | {
|
37 | // when Webpack url-loader finds this `require()` call
|
38 | // it will copy `image.png` to your build folder
|
39 | // and name it something like `9059f094ddb49c2b0fa6a254a6ebf2ad.png`,
|
40 | // because we are using the `[hash]` file naming feature of Webpack url-loader
|
41 | // which (feature) is required to make browser caching work correctly
|
42 | const image = require('../image.png')
|
43 |
|
44 | return <img src={image}/>
|
45 | }
|
46 | }
|
47 | ```
|
48 |
|
49 | It works on the client because Webpack intelligently replaces all the `require()` calls for you.
|
50 | But it wouldn't work on the server because Node.js only knows how to `require()` javascript modules.
|
51 | What `webpack-isomorphic-tools` does is it makes the code above work on the server too (and much more), so that you can have your isomorphic (universal) rendering (e.g. React).
|
52 |
|
53 | What about javascripts on the Html page?
|
54 |
|
55 | When you render your Html page on the server you need to include all the client scripts using `<script src={...}/>` tags. And for that purpose you need to know the real paths to your Webpack compiled javascripts. Which are gonna have names like `main-9059f094ddb49c2b0fa6a254a6ebf2ad.js` because we are using the `[hash]` file naming feature of Webpack which is required to make browser caching work correctly. And `webpack-isomorphic-tools` tells you these filenames (see the [Usage](#usage) section).
|
56 |
|
57 | It also tells you real paths to your CSS styles in case you're using [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin) which is usually the case for production build.
|
58 |
|
59 | Aside all of that, `webpack-isomorphic-tools` is highly extensible, and finding the real paths for your assets is just the simplest example of what it's capable of. Using [custom configuration](#configuration) one can make `require()` calls return virtually anything (not just String, it may be a JSON object, for example). For example, if you're using Webpack [css-loader](https://github.com/webpack/css-loader) modules feature (also referred to as ["local styles"](https://medium.com/seek-ui-engineering/the-end-of-global-css-90d2a4a06284)) you can make `require(*.css)` calls return JSON objects with CSS class names like they do in [react-redux-universal-hot-example](https://github.com/erikras/react-redux-universal-hot-example#styles) (it's just a demonstration of what one can do with `webpack-isomorphic-tools`, and I'm not using this "modules" feature of `ccs-plugin` in my projects).
|
60 |
|
61 | ## Installation
|
62 |
|
63 | `webpack-isomorphic-tools` are required both for development and production
|
64 |
|
65 | ```bash
|
66 | $ npm install webpack-isomorphic-tools --save
|
67 | ```
|
68 |
|
69 | ## Usage
|
70 |
|
71 | First you add `webpack_isomorphic_tools` plugin to your Webpack configuration.
|
72 |
|
73 | ### webpack.config.js
|
74 |
|
75 | ```javascript
|
76 | var Webpack_isomorphic_tools_plugin = require('webpack-isomorphic-tools/plugin')
|
77 |
|
78 | var webpack_isomorphic_tools_plugin =
|
79 | // webpack-isomorphic-tools settings reside in a separate .js file
|
80 | // (because they will be used in the web server code too).
|
81 | new Webpack_isomorphic_tools_plugin(require('./webpack-isomorphic-tools-configuration'))
|
82 | // also enter development mode since it's a development webpack configuration
|
83 | // (see below for explanation)
|
84 | .development()
|
85 |
|
86 | // usual Webpack configuration
|
87 | module.exports =
|
88 | {
|
89 | context: '(required) your project path here',
|
90 |
|
91 | output:
|
92 | {
|
93 | publicPath: '(required) web path for static files here'
|
94 | },
|
95 |
|
96 | module:
|
97 | {
|
98 | loaders:
|
99 | [
|
100 | ...,
|
101 | {
|
102 | test: webpack_isomorphic_tools_plugin.regular_expression('images'),
|
103 | loader: 'url-loader?limit=10240', // any image below or equal to 10K will be converted to inline base64 instead
|
104 | }
|
105 | ]
|
106 | },
|
107 |
|
108 | plugins:
|
109 | [
|
110 | ...,
|
111 |
|
112 | webpack_isomorphic_tools_plugin
|
113 | ]
|
114 |
|
115 | ...
|
116 | }
|
117 | ```
|
118 |
|
119 | What does `.development()` method do? It enables development mode. In short, when in development mode, it disables asset caching (and enables asset hot reload). Just call it if you're developing your project with `webpack-dev-server` using this config (and don't call it for production webpack build).
|
120 |
|
121 | For each asset type managed by `webpack_isomorphic_tools` there should be a corresponding loader in your Webpack configuration. For this reason `webpack_isomorphic_tools/plugin` provides a `.regular_expression(asset_type)` method. The `asset_type` parameter is taken from your `webpack-isomorphic-tools` configuration:
|
122 |
|
123 | ### webpack-isomorphic-tools-configuration.js
|
124 |
|
125 | ```javascript
|
126 | import Webpack_isomorphic_tools_plugin from 'webpack-isomorphic-tools/plugin'
|
127 |
|
128 | export default
|
129 | {
|
130 | assets:
|
131 | {
|
132 | images:
|
133 | {
|
134 | extensions: ['png', 'jpg', 'gif', 'ico', 'svg'],
|
135 | parser: Webpack_isomorphic_tools_plugin.url_loader_parser // see Configuration and API sections for more info on this parameter
|
136 | }
|
137 | }
|
138 | }
|
139 | ```
|
140 |
|
141 | That's it for the client side. Next, the server side. You create your server side instance of `webpack-isomorphic-tools` in the very main server javascript file (and your web application code will reside in some `server.js` file which is `require()`d in the bottom)
|
142 |
|
143 | ### main.js
|
144 |
|
145 | ```javascript
|
146 | var Webpack_isomorphic_tools = require('webpack-isomorphic-tools')
|
147 |
|
148 | // this must be equal to your Webpack configuration "context" parameter
|
149 | var project_base_path = require('path').resolve(__dirname, '..')
|
150 |
|
151 | // this global variable will be used later in express middleware
|
152 | global.webpack_isomorphic_tools = new Webpack_isomorphic_tools(require('./webpack-isomorphic-tools-configuration'))
|
153 | // enter development mode if needed
|
154 | // (for example, based on a Webpack DefinePlugin variable)
|
155 | .development(_development_)
|
156 | // initializes a server-side instance of webpack-isomorphic-tools
|
157 | // (the first parameter is the base path for your project)
|
158 | .server(project_base_path, function()
|
159 | {
|
160 | // webpack-isomorphic-tools is all set now.
|
161 | // here goes all your web application code:
|
162 | require('./server')
|
163 | })
|
164 | ```
|
165 |
|
166 | Then you, for example, create an express middleware to render your pages on the server
|
167 |
|
168 | ```javascript
|
169 | import React from 'react'
|
170 |
|
171 | // html page markup
|
172 | import Html from './html'
|
173 |
|
174 | // will be used in express_application.use(...)
|
175 | export function page_rendering_middleware(request, response)
|
176 | {
|
177 | // clear require() cache if in development mode
|
178 | // (makes asset hot reloading work)
|
179 | if (_development_)
|
180 | {
|
181 | webpack_isomorphic_tools.refresh()
|
182 | }
|
183 |
|
184 | // for react-router example of determining current page by URL take a look at this:
|
185 | // https://github.com/halt-hammerzeit/cinema/blob/master/code/server/webpage%20rendering.js
|
186 | const page_component = [determine your page component here using request.path]
|
187 |
|
188 | // for a Redux Flux store implementation you can see the same example:
|
189 | // https://github.com/halt-hammerzeit/cinema/blob/master/code/server/webpage%20rendering.js
|
190 | const flux_store = [initialize and populate your flux store depending on the page being shown]
|
191 |
|
192 | // render the page to string and send it to the browser as text/html
|
193 | response.send('<!doctype html>\n' +
|
194 | React.renderToString(<Html assets={webpack_isomorphic_tools.assets()} component={page_component} store={flux_store}/>))
|
195 | }
|
196 | ```
|
197 |
|
198 | And finally you use the `assets` inside the `Html` component's `render()` method
|
199 |
|
200 | ```javascript
|
201 | import React, {Component, PropTypes} from 'react'
|
202 | import serialize from 'serialize-javascript'
|
203 |
|
204 | export default class Html extends Component
|
205 | {
|
206 | static propTypes =
|
207 | {
|
208 | assets : PropTypes.object,
|
209 | component : PropTypes.object,
|
210 | store : PropTypes.object
|
211 | }
|
212 |
|
213 | render()
|
214 | {
|
215 | const { assets, component, store } = this.props
|
216 |
|
217 | // "import" will work here too
|
218 | // but if you want hot reloading to work while developing your project
|
219 | // then you need to use require()
|
220 | // because import will only be executed a single time
|
221 | // (when the application launches)
|
222 | // you can refer to the "Require() vs import" section for more explanation
|
223 | const picture = require('../assets/images/cat.jpg')
|
224 |
|
225 | // favicon
|
226 | const icon = require('../assets/images/icon/32x32.png')
|
227 |
|
228 | const html =
|
229 | (
|
230 | <html lang="en-us">
|
231 | <head>
|
232 | <meta charSet="utf-8"/>
|
233 | <title>xHamster</title>
|
234 |
|
235 | {/* favicon */}
|
236 | <link rel="shortcut icon" href={icon} />
|
237 |
|
238 | {/* styles (will be present only in production with webpack extract text plugin) */}
|
239 | {Object.keys(assets.styles).map((style, i) =>
|
240 | <link href={assets.styles[style]} key={i} media="screen, projection"
|
241 | rel="stylesheet" type="text/css"/>)}
|
242 | </head>
|
243 |
|
244 | <body>
|
245 | {/* image requiring demonstration */}
|
246 | <img src={picture}/>
|
247 |
|
248 | {/* rendered React page */}
|
249 | <div id="content" dangerouslySetInnerHTML={{__html: React.renderToString(component)}}/>
|
250 |
|
251 | {/* Flux store data will be reloaded into the store on the client */}
|
252 | <script dangerouslySetInnerHTML={{__html: `window._flux_store_data=${serialize(store.getState())};`}} />
|
253 |
|
254 | {/* javascripts */}
|
255 | {/* (usually one for each "entry" in webpack configuration) */}
|
256 | {/* (for more informations on "entries" see https://github.com/petehunt/webpack-howto/) */}
|
257 | {Object.keys(assets.javascript).map((script, i) =>
|
258 | <script src={assets.javascript[script]} key={i}/>
|
259 | )}
|
260 | </body>
|
261 | </html>
|
262 | )
|
263 |
|
264 | return html
|
265 | }
|
266 | }
|
267 | ```
|
268 |
|
269 | `assets` in the code above are simply the contents of `webpack-assets.json` which is created by `webpack-isomorphic-tools` in your project base folder. `webpack-assets.json` (in the simplest case) keeps track of the real paths to your assets, e.g.
|
270 |
|
271 | ```javascript
|
272 | {
|
273 | javascript:
|
274 | {
|
275 | main: '/assets/main-d8c29e9b2a4623f696e8.js'
|
276 | },
|
277 |
|
278 | styles:
|
279 | {
|
280 | main: '/assets/main-d8c29e9b2a4623f696e8.css'
|
281 | },
|
282 |
|
283 | images:
|
284 | {
|
285 | './assets/images/cat.jpg': '/assets/9059f094ddb49c2b0fa6a254a6ebf2ad.jpg',
|
286 |
|
287 | './assets/images/icon/32x32.png': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffBhcWAg6gFw6bAAAB60lEQVRIx+3UTUjUQRzG8c+u/n2BDe3lIJtQSuYhsPTQG+TFYLulguStoA5dPHYogoKigoi8dIsOCd0iiC4JFYFQBAVZEUgklWVQqam4vu1uF111d1310qWe0/yemfnyzPyG4b8KllQl6jWqNuX3nFNun/0qjJpYGRB1TkyRWu0C76Q0uKhOkT1aDfqSP0uxTpetR1i9e2Iq3HVUCQKt7tuWP0GDmDOGkfJd3GEbhFwzg6T3alR5lg0Ip0fVPhhKV2+UqfNcMu28sjlXggVAXEQoXZVKmlC2aGXETH5Ary3q026zPg8dtGnOKXPIi/x3MCJwUtyUqBN2uarXTi1+Cql1yqibuTKElsCaHBFBn1v6sU67RoGkHl3GciVYDNiuWVSphDEJYaSkRBSbNqLHI7PZgML0qNIFrz3OwqZAuQ6BB8KqRL01nA3YbdCVRW3L1KxGTx1zQMI3p01nAkqN5NnOkBrXJZw1qlOlj5mAlTQuqluXcRGTSrOPsJJeajOQzphaOyDucy47vGrAMvqLgCLlS97HmgH17mgRzFWhbEAq43/M1EYF2p1XoVAgMW8vdKFfmx0+LbO9WJNut3W44Ze4r/MTC6cKHBczutDhJSrxwyWDAntt9cRANoCwqLKcgJApAyZXfV//mP4AWg969geZ6qgAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTUtMDYtMjNUMjI6MDI6MTQrMDI6MDBG88r0AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE1LTA2LTIzVDIyOjAyOjE0KzAyOjAwN65ySAAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAASUVORK5CYII='
|
288 | }
|
289 | }
|
290 | ```
|
291 |
|
292 | And that's it, now you can `require()` your assets "isomorphically" (both on client and server).
|
293 |
|
294 | ## A working example
|
295 |
|
296 | `webpack-isomorphic-tools` are featured in [react-redux-universal-hot-example](https://github.com/erikras/react-redux-universal-hot-example).
|
297 |
|
298 | Also for a comprehensive example of isomorphic React rendering you can look at this sample project:
|
299 |
|
300 | * clone [this repo](https://github.com/halt-hammerzeit/cinema)
|
301 | * `npm install`
|
302 | * `npm run dev`
|
303 | * wait a moment for Webpack to finish the first build (green stats will appear in the terminal)
|
304 | * go to `http://localhost:3000`
|
305 | * `Ctrl + C`
|
306 | * `npm run production`
|
307 | * go to `http://localhost:3000`
|
308 |
|
309 | Some source code guidance for this particular project:
|
310 |
|
311 | * [webpack-isomorphic-tools configuration](https://github.com/halt-hammerzeit/cinema/blob/master/webpack/isomorphic.js)
|
312 | * [webpack-isomorphic-tools client initialization](https://github.com/halt-hammerzeit/cinema/blob/master/webpack/development%20server.js)
|
313 | * [webpack-isomorphic-tools server initialization](https://github.com/halt-hammerzeit/cinema/blob/master/code/server/entry.js)
|
314 | * [webpage rendering express middleware](https://github.com/halt-hammerzeit/cinema/blob/master/code/server/webpage%20rendering.js)
|
315 | * [the Html file](https://github.com/halt-hammerzeit/cinema/blob/master/code/client/html.js)
|
316 |
|
317 | ## Configuration
|
318 |
|
319 | Available configuration parameters:
|
320 |
|
321 | ```javascript
|
322 | {
|
323 | // sets "development" mode flag.
|
324 | // see the API section below for method .development()
|
325 | // for more explanation about what "development" mode does
|
326 | // and when is it needed.
|
327 | development: true, // is false by default
|
328 |
|
329 | // debug mode.
|
330 | // when set to true, lets you see debugging messages in the console,
|
331 | // and also outputs 'webpack-stats.debug.json' file with Webpack stats.
|
332 | // (you'll be interested in the contents of this file
|
333 | // in case you're writing your own filter, naming or parser
|
334 | // for some asset type)
|
335 | debug: true, // is false by default
|
336 |
|
337 | // By default it creates 'webpack-assets.json' file at
|
338 | // webpack_configuration.context (which is your project folder).
|
339 | // You can change the assets file path as you wish
|
340 | // (therefore changing both folder and filename).
|
341 | //
|
342 | // The folder derived from this parameter will also be used
|
343 | // to store 'webpack-stats.debug.json' file in case you're in debug mode
|
344 | //
|
345 | // (relative to webpack_configuration.context which is your project folder)
|
346 | // (these aren't actually 'stats', these are some values derived from Webpack 'stats')
|
347 | webpack_assets_file_path: 'webpack-stats.json', // is 'webpack-assets.json' by default
|
348 |
|
349 | // here you are able to add some file paths
|
350 | // for which the require() call will bypass webpack-isomorphic-tools
|
351 | // (relative to the project base folder, e.g. ./sources/server/kitten.jpg.js)
|
352 | exceptions: [],
|
353 |
|
354 | // here you can define all your asset types
|
355 | assets:
|
356 | {
|
357 | // asset_type will appear in:
|
358 | // * webpack-assets.json
|
359 | // * .assets() method call result
|
360 | // * .regular_expression(asset_type) method call
|
361 | asset_type:
|
362 | {
|
363 | // which file types belong to this asset type
|
364 | extension: 'png', // or extensions: ['png', 'jpg', ...],
|
365 |
|
366 | // [optional]
|
367 | //
|
368 | // determines which webpack stats modules
|
369 | // belong to this asset type
|
370 | //
|
371 | // arguments:
|
372 | //
|
373 | // module - a webpack stats module
|
374 | //
|
375 | // regular_expression - a regular expression
|
376 | // composed of this asset type's extensions
|
377 | // e.g. /\.scss$/, /\.(ico|gif)$/
|
378 | //
|
379 | // options - various options
|
380 | // (development mode flag,
|
381 | // debug mode flag,
|
382 | // assets base path (on the disk or on the network),
|
383 | // regular_expressions{} for each asset type (by name),
|
384 | // webpack stats json object)
|
385 | //
|
386 | // log
|
387 | //
|
388 | // returns: a Boolean
|
389 | //
|
390 | // by default is: "return regular_expression.test(module.name)"
|
391 | //
|
392 | filter: function(module, regular_expression, options, log)
|
393 | {
|
394 | return regular_expression.test(module.name)
|
395 | },
|
396 |
|
397 | // [optional]
|
398 | //
|
399 | // transforms a webpack stats module name
|
400 | // to an asset name
|
401 | //
|
402 | // arguments:
|
403 | //
|
404 | // module - a webpack stats module
|
405 | //
|
406 | // options - various options
|
407 | // (development mode flag,
|
408 | // debug mode flag,
|
409 | // assets base path (on the disk or on the network),
|
410 | // regular_expressions{} for each asset type (by name),
|
411 | // webpack stats json object)
|
412 | //
|
413 | // log
|
414 | //
|
415 | // returns: a String
|
416 | //
|
417 | // by default is: "return module.name"
|
418 | //
|
419 | naming: function(module, options, log)
|
420 | {
|
421 | return module.name
|
422 | },
|
423 |
|
424 | // [required]
|
425 | //
|
426 | // parses a webpack stats module object
|
427 | // for an asset of this asset type
|
428 | // to whatever you need to get
|
429 | // when you require() these assets
|
430 | // in your code later on.
|
431 | //
|
432 | // in other words: require(...) = function(...) { ... return parser(...) }
|
433 | //
|
434 | // arguments:
|
435 | //
|
436 | // module - a webpack stats module
|
437 | //
|
438 | // options - various options
|
439 | // (development mode flag,
|
440 | // debug mode flag,
|
441 | // assets base path (on the disk or on the network),
|
442 | // regular_expressions{} for each asset type (by name),
|
443 | // webpack stats json object)
|
444 | //
|
445 | // log
|
446 | //
|
447 | // returns: whatever (could be a filename, could be a JSON object, etc)
|
448 | //
|
449 | parser: function(module, options, log)
|
450 | {
|
451 | log.info('# module name', module.name)
|
452 | log.info('# module source', module.source)
|
453 | log.info('# assets base path', options.assets_base_path)
|
454 | log.info('# regular expressions', options.regular_expressions)
|
455 | log.info('# debug mode', options.debug)
|
456 | log.info('# development mode', options.development)
|
457 | log.debug('debugging')
|
458 | log.warning('warning')
|
459 | log.error('error')
|
460 | }
|
461 | },
|
462 | ...
|
463 | },
|
464 | ...]
|
465 | }
|
466 | ```
|
467 |
|
468 | ## API
|
469 |
|
470 | #### Constructor
|
471 |
|
472 | (both Webpack plugin and server tools)
|
473 |
|
474 | Takes an object with options (see [Configuration](#configuration) section above)
|
475 |
|
476 | #### .development(true or false or undefined -> true)
|
477 |
|
478 | (both Webpack plugin and server tools)
|
479 |
|
480 | Is it development mode or is it production mode? By default it's production mode. But if you're instantiating `webpack-isomorphic-tools/plugin` for use in Webpack development configuration, or if you're instantiating `webpack-isomorphic-tools` on server when you're developing your project, then you should call this method to enable asset hot reloading (and disable asset caching). It should be called right after the constructor.
|
481 |
|
482 | #### .regular_expression(asset_type)
|
483 |
|
484 | (Webpack plugin)
|
485 |
|
486 | Returns the regular expression for this asset type (based on this asset type's `extension` (or `extensions`))
|
487 |
|
488 | #### Webpack_isomorphic_tools_plugin.url_loader_parser
|
489 |
|
490 | (Webpack plugin)
|
491 |
|
492 | A parser (see [Configuration](#configuration) section above) for Webpack [url-loader](https://github.com/webpack/url-loader), also works for Webpack [file-loader](https://github.com/webpack/file-loader). Use it for your images, fonts, etc.
|
493 |
|
494 | #### .server(project_path, callback)
|
495 |
|
496 | (server tools)
|
497 |
|
498 | Initializes a server-side instance of `webpack-isomorphic-tools` with the base path for your project and makes all the server-side `require()` calls work. The `project_path` parameter must be identical to the `context` parameter of your Webpack configuration and is needed to locate `webpack-assets.json` (contains the assets info) which is output by Webpack process. The callback is called when `webpack-assets.json` has been found (it's needed for development because `webpack-dev-server` and your application server are usually run in parallel).
|
499 |
|
500 | #### .refresh()
|
501 |
|
502 | (server tools)
|
503 |
|
504 | Refreshes your assets info (re-reads `webpack-assets.json` from disk) and also flushes cache for all the previously `require()`d assets
|
505 |
|
506 | #### .assets()
|
507 |
|
508 | (server tools)
|
509 |
|
510 | Returns the contents of `webpack-assets.json` which is created by `webpack-isomorphic-tools` in your project base folder
|
511 |
|
512 | ## Gotchas
|
513 |
|
514 | ### .gitignore
|
515 |
|
516 | Make sure you add this to your `.gitignore`
|
517 |
|
518 | ```
|
519 | # webpack-isomorphic-tools
|
520 | /webpack-stats.debug.json
|
521 | /webpack-assets.json
|
522 | ```
|
523 |
|
524 | ### Require() vs import
|
525 |
|
526 | In the image requiring examples above we could have wrote it like this:
|
527 |
|
528 | ```
|
529 | import picture from './cat.jpg'
|
530 | ```
|
531 |
|
532 | That would surely work. Much simpler and more modern. But, the disadvantage of the new ES6 module `import`ing is that by design it's static as opposed to dynamic nature of `require()`. Such a design decision was done on purpose and it's surely the right one:
|
533 |
|
534 | * it's static so it can be optimized by the compiler and you don't need to know which module depends on which and manually reorder them in the right order because the compiler does it for you
|
535 | * it's smart enough to resolve cyclic dependencies
|
536 | * it can load modules both synchronously and asynchronously if it wants to and you'll never know because it can do it all by itself behind the scenes without your supervision
|
537 | * the `export`s are static which means that your IDE can know exactly what each module is gonna export without compiling the code (and therefore it can autocomplete names, detect syntax errors, check types, etc); the compiler too has some benefits such as improved lookup speed and syntax and type checking
|
538 | * it's simple, it's transparent, it's sane
|
539 |
|
540 | If you wrote your code with just `import`s it would work fine. But imagine you're developing your website, so you're changing files constantly, and you would like it all refresh automagically when you reload your webpage (in development mode). `webpack-isomorphic-tools` gives you that. Remember this code in the express middleware example above?
|
541 |
|
542 | ```javascript
|
543 | if (_development_)
|
544 | {
|
545 | webpack_isomorhic_tools.refresh()
|
546 | }
|
547 | ```
|
548 |
|
549 | It does exactly as it says: it refreshes everything on page reload when you're in development mode. And to leverage this feature you need to use dynamic module loading as opposed to static one through `import`s. This can be done by `require()`ing your assets, and not at the top of the file where all `require()`s usually go but, say, inside the `reder()` method for React components.
|
550 |
|
551 | I also read on the internets that ES6 supports dynamic module loading too and it looks something like this:
|
552 |
|
553 | ```javascript
|
554 | System.import('some_module')
|
555 | .then(some_module =>
|
556 | {
|
557 | // Use some_module
|
558 | })
|
559 | .catch(error =>
|
560 | {
|
561 | ...
|
562 | })
|
563 | ```
|
564 |
|
565 | I'm currently unfamiliar with ES6 dynamic module loading system because I didn't research this question. Anyway it's still a draft specification so I guess good old `require()` is just fine to the time being.
|
566 |
|
567 | Also it's good to know that the way all this `require('./asset.whatever_extension')` magic is based on [Node.js require hooks](http://bahmutov.calepin.co/hooking-into-node-loader-for-fun-and-profit.html) and it works with `import`s only when your ES6 code is transpiled by Babel which simply replaces all the `import`s with `require()`s. For now, everyone out there uses Babel, both on client and server. But when the time comes for ES6 to be widely natively adopted, and when a good enough ES6 module loading specification is released, then I (or someone else) will step in and port this "require hook" to ES6 to work with `import`s.
|
568 |
|
569 | ## References
|
570 |
|
571 | Initially based on the code from [react-redux-universal-hot-example](https://github.com/erikras/react-redux-universal-hot-example) by Erik Rasmussen
|
572 |
|
573 | Also the same codebase (as in the project mentioned above) can be found in [isomorphic500](https://github.com/gpbl/isomorphic500) by Giampaolo Bellavite
|
574 |
|
575 | Also uses `require()` hooking techniques from [node-hook](https://github.com/bahmutov/node-hook) by Gleb Bahmutov
|
576 |
|
577 | ## Contributing
|
578 |
|
579 | After cloning this repo, ensure dependencies are installed by running:
|
580 |
|
581 | ```sh
|
582 | npm install
|
583 | ```
|
584 |
|
585 | This module is written in ES6 and uses [Babel](http://babeljs.io/) for ES5
|
586 | transpilation. Widely consumable JavaScript can be produced by running:
|
587 |
|
588 | ```sh
|
589 | npm run build
|
590 | ```
|
591 |
|
592 | Once `npm run build` has run, you may `import` or `require()` directly from
|
593 | node.
|
594 |
|
595 | After developing, the full test suite can be evaluated by running:
|
596 |
|
597 | ```sh
|
598 | npm test
|
599 | ```
|
600 |
|
601 | While actively developing, one can use
|
602 |
|
603 | ```sh
|
604 | npm run watch
|
605 | ```
|
606 |
|
607 | in a terminal. This will watch the file system and run tests automatically
|
608 | whenever you save a js file.
|
609 |
|
610 | ## License
|
611 |
|
612 | [MIT](LICENSE)
|
613 | [npm-image]: https://img.shields.io/npm/v/webpack-isomorphic-tools.svg
|
614 | [npm-url]: https://npmjs.org/package/webpack-isomorphic-tools
|
615 | [travis-image]: https://img.shields.io/travis/halt-hammerzeit/webpack-isomorphic-tools/master.svg
|
616 | [travis-url]: https://travis-ci.org/halt-hammerzeit/webpack-isomorphic-tools
|
617 | [downloads-image]: https://img.shields.io/npm/dm/webpack-isomorphic-tools.svg
|
618 | [downloads-url]: https://npmjs.org/package/webpack-isomorphic-tools
|
619 | [coveralls-image]: https://img.shields.io/coveralls/halt-hammerzeit/webpack-isomorphic-tools/master.svg
|
620 | [coveralls-url]: https://coveralls.io/r/halt-hammerzeit/webpack-isomorphic-tools?branch=master
|
621 |
|
622 |
|
623 | [gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg
|
624 | [gratipay-url]: https://gratipay.com/dougwilson/
|
625 | --> |
\ | No newline at end of file |