Giter VIP home page Giter VIP logo

universal-webpack's Introduction

universal-webpack

NPM Version Test Coverage

For beginners: consider trying Next.js first: it's user-friendly and is supposed to be a good start for people not wanting to deal with configuring Webpack manually. On the other hand, if you're an experienced Webpack user then setting up universal-webpack shouldn't be too difficult.

This library generates client-side and server-side configuration for Webpack therefore enabling seamless client-side/server-side Webpack builds. Requires some initial set up and some prior knowledge of Webpack.

Install

npm install universal-webpack --save-dev

Example project

You may refer to this sample project as a reference example of using this library (see webpack directory, package.json and rendering-service/main.js).

Use

Suppose you have a typical webpack.config.js file. Create two new files called webpack.config.client.babel.js and webpack.config.server.babel.js with the following contents:

webpack.config.client.babel.js

import { client } from 'universal-webpack/config'
import settings from './universal-webpack-settings'
import configuration from './webpack.config'

// Create client-side Webpack config.
export default client(configuration, settings)

webpack.config.server.babel.js

import { server } from 'universal-webpack/config'
import settings from './universal-webpack-settings'
import configuration from './webpack.config'

// Create server-side Webpack config.
export default server(configuration, settings)

Where ./universal-webpack-settings.json is a configuration file for universal-webpack (see below, leave empty for now).

Now, use webpack.config.client.babel.js instead of the old webpack.config.js for client side Webpack builds. Your setup also most likely differentiates between a "development" client side Webpack build and a "production" one, in which case webpack.config.client.babel.js is further split into two files — webpack.config.client.dev.babel.js and webpack.config.client.prod.babel.js — each of which inherits from webpack.config.client.babel.js and makes the necessary changes to it as defined by your particular setup.

And, webpack.config.server.babel.js file will be used for server-side Webpack builds. And, analogous to the client-side config, it also most likely is gonna be split into "development" and "production" configs, as defined by your particular setup.

Setting up the server side requires an additional step: creating the "entry" file for running the server. The reason is that client-side config is created from webpack.config.js which already has Webpack "entry" defined. Usually it's something like ./src/index.js which is the "main" file for the client-side application. Server-side needs such a "main" file too and the path to it must be configured in ./universal-webpack-settings.json as server.input:

universal-webpack-settings.json

{
  "server":
  {
    "input": "./source/server.js",
    "output": "./build/server/server.js"
  }
}

With the server-side "entry" file path configured, the server-side config created by this library will have the Webpack "entry" parameter set up properly. A "server-side" Webpack build will now produce a "server-side" bundle (./build/server/server.js) which can be run using Node.js. An example of an "entry" file:

source/server.js

import path from 'path'
import http from 'http'
import express from 'express'
import httpProxy from 'http-proxy'

// React routes.
// (shared with the client side)
import routes from '../client/routes.js'

// Redux reducers.
// (shared with the client side)
import reducers from '../client/reducers.js'

// Starts the server.
function startServer()
{
	// Create HTTP server.
	const app = new express()
	const server = new http.Server(app)

	// Serve static files.
	app.use(express.static(path.join(__dirname, '..', 'build/assets')))

	// Proxy API calls to API server.
	const proxy = httpProxy.createProxyServer({ target: 'http://localhost:xxxx' })
	app.use('/api', (req, res) => proxy.web(req, res))

	// React application rendering.
	app.use((req, res) => {
		// Match current URL to the corresponding React page.
		routerMatchURL(routes, req.originalUrl).then((error, routingResult) => {
			if (error) {
				throw error
			}
			// Render React page.
			const page = createPageElement(routingResult, reducers)
			res.status(200)
			res.send('<!doctype html><html>...' + ReactDOM.renderToString(page) + '...</html>')
		})
		.catch((error) => {
			res.status(500)
			return res.send('Server error')
		})
	})

	// Start the HTTP server.
	server.listen()
}

// Run the server.
startServer()

The main use-case for universal-webpack though is most likely "Server-Side Rendering" which means that the server is somehow gonna need to know the actual URLs for the compiled javascript and CSS files (which contain random-generated md5 hashes). Specifically for this case this library provides a special "runner" for the server-side bundle which requires that the server-side bundle just exports a "start server" function, without actually running the server, and then such "start server" function will be called with a special parameters argument which holds the actual URLs for the compiled javascript and CSS files (see the "Chunks" section below).

So in this case the changes to the server file are gonna be:

source/server.js

...
export default function startServer(parameters) {
	...
}
// Don't start the server manually.
// // Run the server.
// startServer()

And the server-side runner will be called like this:

source/start-server.js

// The runner.
var startServer = require('universal-webpack/server')

// The server-side bundle path info.
var settings = require('../universal-webpack-settings')

// Only `configuration.context` and `configuration.output.path`
// parameters are used from the whole Webpack config.
var configuration = require('../webpack.config')

// Run the server.
startServer(configuration, settings)

Running node source/start-server.js will basically call the function exported from source/server.js with the parameters argument.

Finally, to run all the things required for "development" mode (in parallel):

# Client-side build.
webpack-dev-server --hot --config ./webpack.config.client.dev.babel.js
# Server-side build.
webpack --watch --config ./webpack.config.server.dev.babel.js --colors --display-error-details
# Run the server.
nodemon ./source/start-server --watch ./build/server

For production mode the command sequence would be:

# Build the client.
webpack --config "./webpack.config.client.babel.js" --colors --display-error-details
# Build the server.
webpack --config "./webpack.config.server.babel.js" --colors --display-error-details
# Run the server.
node "./source/start-server"

Chunks

This library will pass the chunks() function parameter (inside the parameters argument of the server-side function) which returns webpack-compiled chunks filename info:

build/webpack-chunks.json

{
	javascript:
	{
		main: `/assets/main.785f110e7775ec8322cf.js`
	},

	styles:
	{
		main: `/assets/main.785f110e7775ec8322cf.css`
	}
}

These filenames are required for <script src=.../> and <link rel="style" href=.../> tags in case of isomorphic (universal) rendering on the server-side.

Gotchas

  • It emits no assets on the server side so make sure you include all assets on the client side (e.g. "favicon").
  • resolve.root won't work out-of-the-box while resolve.aliases do. For those using resolve.root I recommend switching to resolve.alias. By default no "modules" are bundled in a server-side bundle except for resolve.aliases and excludeFromExternals matches (see below).

Using mini-css-extract-plugin

The third argument – options object – may be passed to client() configuration function. If options.development is set to false, then it will apply mini-css-extract-plugin to CSS styles automatically, i.e. it will extract all CSS styles into separate *.css files (one for each Webpack "chunk"): this is considered a slightly better approach for production deployment instead of just leaving all CSS in *.js chunk files (due to static file caching in a browser). Using options.development=false option is therefore just a convenience shortcut which one may use instead of adding mini-css-extract-plugin to production client-side webpack configuration manually.

import { clientConfiguration } from 'universal-webpack'
import settings from './universal-webpack-settings'
import baseConfiguration from './webpack.config'

const configuration = clientConfiguration(baseConfiguration, settings, {
  // Extract all CSS into separate `*.css` files (one for each chunk)
  // using `mini-css-extract-plugin`
  // instead of leaving that CSS embedded directly in `*.js` chunk files.
  development: false
})

Advanced configuration

./universal-webpack-settings.json configuration file also supports the following optional configuration parameters:

{
	// By default, all `require()`d packages
	// (e.g. everything from `node_modules`, `resolve.modules`),
	// except for `resolve.alias`ed ones,
	// are marked as `external` for server-side Webpack build
	// which means they won't be processed and bundled by Webpack,
	// instead being processed and `require()`d at runtime by Node.js.
	//
	// With this setting one can explicitly define which modules
	// aren't gonna be marked as `external` dependencies.
	// (and therefore are gonna be compiled and bundled by Webpack)
	//
	// Can be used, for example, for ES6-only `node_modules`.
	// ( a more intelligent solution would be accepted
	//   https://github.com/catamphetamine/universal-webpack/issues/10 )
	//
	excludeFromExternals:
	[
		'lodash-es',
		/^some-other-es6-only-module(\/.*)?$/
	],

	// As stated above, all files inside `node_modules`, when `require()`d,
	// would be resolved as "externals" which means Webpack wouldn't use
	// loaders to process them, and therefore `require()`ing them
	// would result in an error when running the server-side bundle.
	//
	// E.g. for CSS files Node.js would just throw `SyntaxError: Unexpected token .`
	// because these CSS files need to be compiled by Webpack's `css-loader` first.
	//
	// Hence the "exclude from externals" file extensions list
	// which by default is initialized with some common asset types:
	//
	loadExternalModuleFileExtensions:
	[
		'css',
		'png',
		'jpg',
		'svg',
		'xml'
	],

	// Enable `silent` flag to prevent client side webpack build
	// from outputting chunk stats to the console.
	silent: true,

	// By default, chunk_info_filename is `webpack-chunks.json`
	chunk_info_filename: 'submodule-webpack-chunks.json'
}

Source maps

I managed to get source maps working in my Node.js server-side code using source-map-support module.

source/start-server.js

// Enables proper source map support in Node.js
require('source-map-support/register')

// The rest is the same as in the above example

var startServer = require('universal-webpack/server')
var settings = require('../universal-webpack-settings')
var configuration = require('../webpack.config')

startServer(configuration, settings)

Without source-map-support enabled it would give me No element indexed by XXX error (which means that by default Node.js thinks there are references to other source maps and tries to load them but there are no such source maps).

devtool is set to source-map for server-side builds.

Nodemon

I recommend using nodemon for running server-side Webpack bundle. Nodemon has a --watch <directory> command line parameter which restarts Node.js process each time the <directory> is updated (e.g. each time any file in that directory is modified).

In other words, Nodemon will relaunch the server every time the code is rebuilt with Webpack.

There's one little gotcha though: for the --watch feature to work the watched folder needs to exist by the time Nodemon is launched. That means that the server must be started only after the settings.server.output path folder has been created.

To accomplish that this library provides a command line tool: universal-webpack. No need to install in globally: it is supposed to work locally through npm scripts. Usage example:

package.json

...
  "scripts": {
    "start": "npm-run-all prepare-server-build start-development-workflow",
    "start-development-workflow": "npm-run-all --parallel development-webpack-build-for-client development-webpack-build-for-server development-start-server",
    "prepare-server-build": "universal-webpack --settings ./universal-webpack-settings.json prepare",
    ...

The prepare command creates settings.server.output path folder, or clears it if it already exists.

Note: In a big React project server restart times can reach ~10 seconds.

Flash of unstyled content

A "flash of unstyled content" is a well-known dev-mode Webpack phenomenon. One can observe it when refreshing the page in development mode: because Webpack's style-loader adds styles to the page dynamically there's a short period of time (a second maybe) when there are no CSS styles applied to the webpage (in production mode mini-css-extract-plugin is used instead of style-loader so there's no "flash of unstyled content").

It's not really a bug, because it's only for development mode. Still, if you're a perfectionist then it can be annoying. The most basic workaround for this is to simply show a white "smoke screen" and then hide it after a pre-defined timeout.

import { smokeScreen, hideSmokeScreenAfter } from 'universal-webpack'

<body>
  ${smokeScreen}
</body>

<script>
  ${hideSmokeScreenAfter(100)}
</script>

resolve.moduleDirectories

If you were using resolve.moduleDirectories for global paths instead of relative paths in your code then consider using resolve.alias instead

resolve:
{
  alias:
  {
    components: path.resolve(__dirname, '../src/components'),
    ...
  }
}

universal-webpack vs webpack-isomorphic-tools

Note: If you never heard of webpack-isomorphic-tools then you shouldn't read this section.

webpack-isomorphic-tools runs on the server-side and hooks into Node.js require() function with the help of require-hacker and does what needs to be done.

universal-webpack doesn't hook into require() function - it's just a helper for transforming client-side Webpack configuration to a server-side Webpack configuration. It doesn't run on the server-side or something. It's just a Webpack configuration generator - turned out that Webpack has a target: "node" parameter which makes it output code that runs on Node.js without any issues.

I wrote webpack-isomorphic-tools before universal-webpack, so universal-webpack is the recommended tool. However many people still use webpack-isomorphic-tools (including me) and find it somewhat less complicated for beginners.

GitHub

On March 9th, 2020, GitHub, Inc. silently banned my account (and all my libraries) without any notice. I opened a support ticked but they didn't answer. Because of that, I had to move all my libraries to GitLab.

License

MIT

universal-webpack's People

Contributors

alecf avatar alexicum avatar audreyburk avatar catamphetamine avatar gustavo-gimenez avatar indeyets avatar lixiaoyan avatar lonelyclick avatar lukesheard avatar papigers avatar purecatamphetamine avatar rogeres avatar springuper avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

universal-webpack's Issues

Problems with build

Hello. I am trying to use this library in this cool template. I made a lot of modifications and attempts. But I am newbie in all this node and webpack stuff and I cannot make this work. I have created webpack\universal-webpack-settings.js file with code:

module.exports =
{
  server:
  {
    input: './server/index',
    output: './build/server.js'
  }
}

Then I have modified webpack\webpack.dev.babel.js and now it is:

const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const logger = require('../../server/logger');
const cheerio = require('cheerio');
const pkg = require(path.resolve(process.cwd(), 'package.json'));
const dllPlugin = pkg.dllPlugin;

// PostCSS plugins
const cssnext = require('postcss-cssnext');
const postcssFocus = require('postcss-focus');
const postcssReporter = require('postcss-reporter');

const plugins = [
];

module.exports = {

  entry: [
    path.join(process.cwd(), 'app/app.js'), // Start with js/app.js
  ],

  // Don't use hashes in dev mode for better performance
  output: {
    filename: '[name].js',
    chunkFilename: '[name].chunk.js',
  },

  // Add development plugins
  plugins: dependencyHandlers().concat(plugins), // eslint-disable-line no-use-before-define

  // Load the CSS in a style tag in development
  cssLoaders: 'style-loader!css-loader?localIdentName=[local]__[path][name]__[hash:base64:5]&modules&importLoaders=1&sourceMap!postcss-loader',

  // Process the CSS with PostCSS
  postcssPlugins: [
    postcssFocus(), // Add a :focus to every :hover
    cssnext({ // Allow future CSS features to be used, also auto-prefixes the CSS...
      browsers: ['last 2 versions', 'IE > 10'], // ...based on this browser list
    }),
    postcssReporter({ // Posts messages from plugins to the terminal
      clearMessages: true,
    }),
  ],

  // Tell babel that we want to hot-reload
  babelQuery: {      
  },

  // Emit a source map for easier debugging
  devtool: 'cheap-module-eval-source-map',
};

/**
 * Select which plugins to use to optimize the bundle's handling of
 * third party dependencies.
 *
 * If there is a dllPlugin key on the project's package.json, the
 * Webpack DLL Plugin will be used.  Otherwise the CommonsChunkPlugin
 * will be used.
 *
 */
function dependencyHandlers() {
  // Don't do anything during the DLL Build step
  if (process.env.BUILDING_DLL) { return []; }

  // If the package.json does not have a dllPlugin property, use the CommonsChunkPlugin
  if (!dllPlugin) {
    return [
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        children: true,
        minChunks: 2,
        async: true,
      }),
    ];
  }

  const dllPath = path.resolve(process.cwd(), dllPlugin.path || 'node_modules/react-boilerplate-dlls');

  /**
   * If DLLs aren't explicitly defined, we assume all production dependencies listed in package.json
   * Reminder: You need to exclude any server side dependencies by listing them in dllConfig.exclude
   *
   * @see https://github.com/mxstbr/react-boilerplate/tree/master/docs/general/webpack.md
   */
  if (!dllPlugin.dlls) {
    const manifestPath = path.resolve(dllPath, 'reactBoilerplateDeps.json');

    if (!fs.existsSync(manifestPath)) {
      logger.error('The DLL manifest is missing. Please run `npm run build:dll`');
      process.exit(0);
    }

    return [
      new webpack.DllReferencePlugin({
        context: process.cwd(),
        manifest: require(manifestPath), // eslint-disable-line global-require
      }),
    ];
  }

  // If DLLs are explicitly defined, we automatically create a DLLReferencePlugin for each of them.
  const dllManifests = Object.keys(dllPlugin.dlls).map((name) => path.join(dllPath, `/${name}.json`));

  return dllManifests.map((manifestPath) => {
    if (!fs.existsSync(path)) {
      if (!fs.existsSync(manifestPath)) {
        logger.error(`The following Webpack DLL manifest is missing: ${path.basename(manifestPath)}`);
        logger.error(`Expected to find it in ${dllPath}`);
        logger.error('Please run: npm run build:dll');

        process.exit(0);
      }
    }

    return new webpack.DllReferencePlugin({
      context: process.cwd(),
      manifest: require(manifestPath), // eslint-disable-line global-require
    });
  });
}

/**
 * We dynamically generate the HTML content in development so that the different
 * DLL Javascript files are loaded in script tags and available to our application.
 */
function templateContent() {
  const html = fs.readFileSync(
    path.resolve(process.cwd(), 'app/index.html')
  ).toString();

  if (!dllPlugin) { return html; }

  const doc = cheerio(html);
  const body = doc.find('body');
  const dllNames = !dllPlugin.dlls ? ['reactBoilerplateDeps'] : Object.keys(dllPlugin.dlls);

  dllNames.forEach(dllName => body.append(`<script data-dll='true' src='/${dllName}.dll.js'></script>`));

  return doc.toString();
}

Then I have created such webpack\webpack.dev.client.babel.js:

var  client_configuration = require('universal-webpack').clientConfiguration; 
var settings = require('./universal-webpack-settings');
var base_configuration = require('./webpack.dev.babel');


const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
const pkg = require(path.resolve(process.cwd(), 'package.json'));
const dllPlugin = pkg.dllPlugin;
const logger = require('../../server/logger');
const cheerio = require('cheerio');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const plugins = [
  new webpack.HotModuleReplacementPlugin(), // Tell webpack we want hot reloading
  new webpack.NoErrorsPlugin(),
  new HtmlWebpackPlugin({
    inject: true, // Inject all files that are generated by webpack, e.g. bundle.js
    templateContent: templateContent(), // eslint-disable-line no-use-before-define
  }),
];

/**
 * We dynamically generate the HTML content in development so that the different
 * DLL Javascript files are loaded in script tags and available to our application.
 */
function templateContent() {
  const html = fs.readFileSync(
    path.resolve(process.cwd(), 'app/index.html')
  ).toString();

  if (!dllPlugin) { return html; }

  const doc = cheerio(html);
  const body = doc.find('body');
  const dllNames = !dllPlugin.dlls ? ['reactBoilerplateDeps'] : Object.keys(dllPlugin.dlls);

  dllNames.forEach(dllName => body.append(`<script data-dll='true' src='/${dllName}.dll.js'></script>`));

  return doc.toString();
}


base_configuration.entry.splice(0,0,'eventsource-polyfill','webpack-hot-middleware/client');

base_configuration.plugins = base_configuration.plugins.concat(plugins);

base_configuration.babelQuery =  {
  presets: ['react-hmre'],
};

var final_configuration = require('./webpack.base.babel')(base_configuration);


module.exports = client_configuration(final_configuration, settings);

Then I have created webpack\webpack.dev.server.babel.js:

var  server_configuration = require('universal-webpack').server_configuration;
var settings = require('./universal-webpack-settings');
var base_configuration = require('./webpack.dev.babel');

var configuration = require('./webpack.base.babel')(base_configuration);

module.exports = server_configuration(configuration, settings)

Then I have modifyed server\index.js:

const express = require('express');
const logger = require('./logger');

const argv = require('minimist')(process.argv.slice(2));
const setup = require('./middlewares/frontendMiddleware');
const isDev = process.env.NODE_ENV !== 'production';
const ngrok = (isDev && process.env.ENABLE_TUNNEL) || argv.tunnel ? require('ngrok') : false;
const resolve = require('path').resolve;

// If you need a backend, e.g. an API, add your custom backend-specific middleware here
// app.use('/api', myApi);

// In production we need to pass these values in instead of relying on webpack
const app = setup(express(), {
  outputPath: resolve(process.cwd(), 'build'),
  publicPath: '/',
});

// get the intended port number, use port 3000 if not provided
const port = argv.port || process.env.PORT || 3000;



// Start your app.
module.exports = function()
{
  console.log('server callback');
  app.listen(port, (err) => {
    console.log('server callback');
    if (err) {
      return logger.error(err.message);
    }

    // Connect to ngrok in dev mode
    if (ngrok) {
      ngrok.connect(port, (innerErr, url) => {
        if (innerErr) {
          return logger.error(innerErr);
        }

        logger.appStarted(port, url);
      });
    } else {
      logger.appStarted(port);
    }
  });
}

Finally I have created server\start-server.js file:

var server = require('universal-webpack').server;
var settings =  require('../internals/webpack/universal-webpack-settings')
var configuration = require('../internals/webpack/webpack.dev.babel');

var final_conf = require('../internals/webpack/webpack.base.babel')(configuration);
server(final_conf, settings)

After this I have modified server\middlewares\frontendMiddleware.js file and replaced this:

//const webpackConfig = require('../../internals/webpack/webpack.dev.babel');
const webpackConfig = require('../../internals/webpack/webpack.dev.client.babel');

Also I have modified start task in package.json:

"start": "cross-env NODE_ENV=development webpack --config internals/webpack/webpack.dev.server.babel.js && node server/start-server",

After alll this stuff, when I run start task I have following ouput (and server is not started):

cross-env NODE_ENV=development webpack --config internals/webpack/webpack.dev.server.babel.js && node server/start-server
./internals/webpack 160 bytes {0} [built]
./server/middlewares 160 bytes {0} [built]
+ 35 hidden modules

When I have tried to run result bundle file manually, I am getting the following error:

Error: Cannot find module 'components/Img'

If you have time, maybe you will help me. Or maybe it is bug in libarary, I do not know. And by the way, you should remove russian comments from sample application code ))) I am speaking about хз, нужно ли сжатие в node.js: мб лучше поставить впереди nginx'ы, . Espessialy you shoukd remove хз))

Webpack loaders do not work when using `require`/`import` in nested components

I have a react app which uses react-router and babel. I use code splitting in my router.
Each route is assigned to some component which then includes other components and assets (.scss, .png).

When I include just components without any assets all works perfect.
When I include assets (e.g. logo.png) directly on a code-split component (e.g. Home.js) it works perfect, too.

However, the problem appears when I include assets in a component that is used by Home.js, e.g. Navigation.js includes logo.png.

Is this a bug, or is it not supported?
Could you guide me where in the code loaders are applied to require/import? Perhaps I could do a fork.

Wait for file triggering before file is finished writing

I reported this before at some point, but it's back and is happening more often now for my growing project. When I am compiling my project using 'npm run dev' I run into this error every few times I build:

[universal-webpack] Your server source file must export a function. Got '{}'

I find that if I add a 1 second timeout to 'wait for file.js' I get rid of the error:

//around line 47
if (condition_is_met)
{
  setTimeout(done, 1000);
  // instead of return done()
}

I believe what is happening is while the file is writing, the filesystem will report that it exists. But then when the file is loaded via 'require', the contents are not fully written, so it fails and returns an empty {}. By putting in the 1 second delay, the file is able to finish writing.

An alternative solution would be to make server.js retry requiring 'starter' (after a delay) when it doesn't return a function. Let me know if you need further info.

Upon following the instructions and running "webpack --watch --config "./webpack.config.server.js" --colors --display-error-details" I receive error "Unexpected token import"

It is complaining about the first line in

(function (exports, require, module, __filename, __dirname) { import { clientConfiguration } from 'universal-webpack'

Now I know node doesn't support es6 imports and I've used babel-import in my project but it doesn't get a chance to load once I've integrated universal-webpack. I've followed the instructions to a tea and it doesn't mention anything about babel so I'm curious, is something missing from the instructions or am I doing something wrong?

Feature request - return promise from server.js

It would be useful for me if you could return the wait_for_file promise from server.js (line 20 of server.js). Then I could access the server I am creating and do things with it (such as mounting it into a koa parent instance). I think it might be useful in general.

I am trying to have a page-server run as a separate process in development, the way you have it setup in webpack-react-redux-isomorphic-render-example, but then mount the page-server into the web-server in production, so it can all run under one port. This is so I can deploy to Heroku servers, which only allow you to use one port.

What do you think?

Optional jsdom

Lot of projects use the window object to add events etc. to the DOM. When we have an option in the universal settings like "jsdom": true it could mimic the DOM in a certain way (it's then a fake DOM) to prevent errors.

var jsdom = require("jsdom").jsdom;
var doc = jsdom(markup, options);
var window = doc.defaultView;

https://github.com/tmpvar/jsdom

The document object holds en empty document of course but when it's available i prevents from errors.

When you don't want to implement this in this library maybe an hint would be nice to the users. Because it makes the development in react easier without caring about the availability of the window/document object etc.

modules config in webpack 2

I have a webpack config with the following:

resolve: {
    modules: ['app', 'node_modules'],
}

But it seems to not work with universal-webpack. I've tried using alias, but it still doesn't work.

Seems there would be one more drawback. How to handle fs?

Using universal-webpack, seems the whole server will be bundled.
In my case, there is a file using require-directory which may lookup a directory to require all the files.
Since the server is bundled, the lookup path will be different and of course the system cannot find the right directory to require the files as they are also bundled.
What is the recommend way to handle this?

Remove webpack dependency on production

This isn't really an issue, since it really solvable without any changes in the code, but I'll explain. I want to remove the dependency of webpack in the production build, since i deploy the built bundle already.

My problem is that universal-webpack forces me to put webpack in my (non dev-)dependencies:

import { server_configuration } from 'universal-webpack'
import settings from './universal-webpack-settings'
import configuration from './webpack.config' // THIS requires webpack and another things which should belong only to dev: postcss plugins etc...

export default server_configuration(configuration, settings)

I've looked into the server.js file and it seems it only requires the context and the output paths, so it is possible to pass just those paths and be done with it, requiring the entire webpack config is a bit unnecessary.

So like I said, it really isn't an issue since we can just change it to:

import { server_configuration } from 'universal-webpack'
import settings from './universal-webpack-settings'

export default server_configuration({
  context: /* context path */,
  output: {
    path: /* server build output */
  }
}, settings)

But maybe document it?

Resolving css node_modules

I typically use normalize.css to reset styles in my apps, in the client config I can just add require('normalize.css') which runs fine, however in the server configuration it does not get parsed as it's seen as an external module which can just be "required", however this breaks the build. I've created a repo to illustrate the issue here: https://github.com/LukeSheard/universal-webpack-bug.

You'll see in server.js that we simply just have a require('normalize.css') statement still in the code, but this obviously fails when you run the build with

$PATH_TO_PROJECT/node_modules/normalize.css/normalize.css:13
html {
     ^
SyntaxError: Unexpected token { ...

How to handle externals in server build

From what I gather this library will make all server modules "externals" yes? My server build file now contains references such as:

/*!*****************************************!*\
  !*** external "server/components/Html" ***!
  \*****************************************/
/***/ function(module, exports) {
  module.exports = require("server/components/Html");
/***/ },

That it cannot resolve as server/components/Html doesn't exist in the build directory. Ive peeked at the sample repo but could not locate how to handle this. What is the recommended process to get these files build or integrated with the server build?

Thanks

How do we handle Multiple Entry points.

Hi @halt-hammerzeit

I know this might not be an issue , just putting as discussion thread.

Say I have multiple entry points in my webpack.config.js. e.g Home.js , About.js

Now on server side we have only one HTML file , how do we stop loading About.js while visiting Home ?

universal-webpack setup for current webpack project, need help

Thanks for this project.

I'm trying to follow the readme and have almost the same settings, but I'm having the following error when I run babel-node "./src/server/startServer"

TypeError: Cannot read property 'path' of undefined
at chunk_info_file_path (/Users/roy/development/projects/react-redux-boilerplate/node_modules/universal-webpack/source/chunks.js:5:19)
at /Users/roy/development/projects/react-redux-boilerplate/node_modules/universal-webpack/source/server.js:22:37

And also, I don't know if I'm setting up correctly ( the webpack.config.client.js and webpack.config.server.js seems okay, at least not throwing error )

Here is my start server

import { server } from 'universal-webpack'
import settings from '../../universal-webpack-settings'
// `configuration.context` and `configuration.output.path` are used
import configuration from '../../webpack.config.babel'

// somehow I need this or it will throw error that my config has no context
configuration.context = configuration.default.context

server(configuration, settings)

Thanks for helping, I wonder if there is any gitter or chat channel to talk about problems ?

Is there a way to make webpack's resolve work both on client and server?

In my app, the main folder is src, and when I want to import a component, I would like to write
import {Test} from 'components', rather than import {Test} from '../../components'.

The webpack's resolve.moduleDirectories along with NODE_PATH env works, but only for the client build, whereas when I am trying to start the server, it throws an Unexpected token error when trying to import anything but js (styles, images, etc..).

Using relative paths is not that crucial for me, but if there is a chance to set up my project with the resolve rule, I would like to do so.
Thanks.

Define enviroment variable whether if it's a server or client build

With webpacks DefinePlugin

There are serveral use cases this can be helpful to include some piece of code from the shared libararies only in a specific build.

It could be named "TARGET" = "client" || "server"

In the code then you can use for example:

if (process.env.TARGET == 'server') {
    apiBaseURL = '127.0.0.1:80'
    ...handleCookie passing...
}

etc.

This don't exposes server side code to the user.

Production question

Hello @halt-hammerzeit,

Thanks for universal-webpack, it's been working great for me in development.

For production usage, it seems that you're running your server through import {server} from 'universal-webpack', same as in dev.

In my case, I'd like to avoid if possible running my production server through an extra layer, if possible.

The solution I'm using at the moment is a start-prod-server.js script. It does the following:

var server = require('./build/server/server').default;
var chunks = require('./build/assets/webpack-chunks.json');
server({chunks: () => chunks});

It seems to be working well at the moment. Do you see anything wrong with this?

More detailed example for separating a react rendering service from the main code

I can't grasp how to separate those things and the docs are a bit confusing. My initial understanding was to have a nodemon server for monitoring a server rendering and then have another node server with a proxy configured to serve requests from the nodemon server. But I can't understand how this would help with making things more faster, because there is still a node server with nodemon to watch the files.

Can you provide a more detailed example on how to do this or maybe a more detailed explanation for how things should work?

Help with favicon

I'm having some trouble getting a favicon to populate correctly in the client bundle.

It's required as follows:

const favicon = require('../images/favicon.png')

But the file gets bundled in the server folder of my build directory, and not the /assets folder.

Build structure:

dist/
|__server/
|__assets/

Also my webpack config uses:

// ...
output: {
    path: PATHS.build, // path.join(__dirname, 'dist/assets')
    publicPath: '/assets/',
    filename: "bundle_[hash].js",
    sourceMapFilename: "bundle_[hash].js.map"
  },

Cannot find module css/sass-loader

When starting the server:

Error: Cannot find module '!!./../../../node_modules/css-loader/index.js!./../../../node_modules/sass-loader/index.js!./style.scss'
    at Function.Module._resolveFilename (module.js:438:15)
    at Function.Module._load (module.js:386:25)
    at Module.require (module.js:466:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (server.js:775:19)
    at __webpack_require__ (server.js:21:30)
    at Object.<anonymous> (server.js:762:16)
    at __webpack_require__ (server.js:21:30)
    at Object.<anonymous> (server.js:459:2)
    at __webpack_require__ (server.js:21:30)

[nodemon] app crashed - waiting for file changes before starting...

readme.md commands do not work

In all of your command examples i.e.
webpack-dev-server --hot --inline --config "./webpack.config.client.js" --port XXXX --colors --display-error-details

you should postpone babel-node otherwise node.js does not support import ES6 stuff. Unless i am missing something ?

Running into issues with modules not resolving on server script

Errors are as follows:

Module not found: Error: Cannot resolve module 'webpack/hot/dev-server' in /opt
resolve module webpack/hot/dev-server in /opt
  looking for modules in /node_modules
    /node_modules/webpack doesn't exist (module as directory)
[/node_modules/webpack]
 @ multi app
multi vendor
Module not found: Error: Cannot resolve module 'webpack/hot/dev-server' in /opt
resolve module webpack/hot/dev-server in /opt
  looking for modules in /node_modules
    /node_modules/webpack doesn't exist (module as directory)
[/node_modules/webpack]
 @ multi vendor
multi vendor
Module not found: Error: Cannot resolve module 'react' in /opt...

And

ERROR in Entry module not found: Error: Cannot resolve directory './app/server' in /opt
resolve directory ./app/server in /opt
  /opt/app/server/package.json doesn't exist (directory description file)
  /opt/app/server doesn't exist (directory default file)

It seems like the issues are coming from the requires in my app. In the repo description you mentioned that this project doesn't hack server side requires, so does that mean that these issues should be best solved with the isomorphic tools version?

Thanks in advance!

__dirname is / in server modules

Hey, thanks for this module, trying to get css modules and webpack working with some server side rendered react components at the moment.

I set it up just like the instructions in the readme and now my express code can't find the jade views as __dirname is now '/'

my express code looks something like:

app.set('views', __dirname + 'views')

// ... other stuff

res.status(200).render('index.ejs', {reactOutput, initialState})

which of course now fails because my views directory can't be found.

What have I done wrong? :)

webpack-chunks.json configurable request

I have two entrys in my webpack file and start it separately, but webpack-chunks.json always be covered,so at 'universal.settings.js':

module.exports = {
  server: {input: 'xxx', output: 'xxx'}
  chunkFilename: 'xxxx-webpack-chunk.json' 
}

please support it #43

Using ReduxAsyncConnect with universal-webpack

I'm porting my code over from using webpack-isomorphic-tools to universal-webpack. I based my project off of https://github.com/erikras/react-redux-universal-hot-example . As far as I can tell, ReduxAsyncConnect expects certain properties to be passed to it from react-router's match (https://www.npmjs.com/package/redux-async-connect), but I'm not sure how to get access to those properties from within my react-isomorphic-render file.

Is there any way to get access to those properties?

in head element node, only can insert one child, but i got a lot cdn link to append

import React from 'react'
import webpageServer from 'react-isomorphic-render/server'
import { devtools } from 'universal-webpack'

import common from '../client/reactIsomorphicRender'
import Log from '../common/log'

const log = Log('webpage renderer')


export default function(parameters) {

    const server = webpageServer({
        assets: (url) => {
            const result = clone(parameters.chunks())
            result.entry = 'main'

            // if(_development_){
            //     delete require.cache[require.resolve('../../assets/images/icon/cat_64x64.png')]
            // }

            // Add favicon
            result.icon = require('../../assets/images/icon/cat_64x64.png')

            return result

        },

        application: {
            host: configuration.web_server.http.host,
            port: configuration.web_server.http.port
        },

        html: {
            head: (url) => {
                // if(_development_) {
                //     const script = devtools({ ...parameters, entry: 'main' })
                //     return <script dangerouslySetInnerHTML = {{ __html: script }} />
                // }
                return (
                        <link href="//cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet"></link>
                        <link rel="stylesheet" href="https://npmcdn.com/react-bootstrap-table/dist/react-bootstrap-table-all.min.css"></link>
                )

            }
        },
        middleware: [function(context, next){
            global.navigator = {userAgent: context.request.header['user-agent']};
            next()
        }]
    },
    common)

    server.listen(configuration.webpage_server.http.port, function(error){

        if(error) {
            log.error('Webpage rendering server shutdown due to an error', error)
            throw error
        }

        log.info(`Webpage server is listening at http://localhost:${configuration.webpage_server.http.port}`)
    })
}

can't insert two links in head by config html.head

html: {
            head: (url) => {
                // if(_development_) {
                //     const script = devtools({ ...parameters, entry: 'main' })
                //     return <script dangerouslySetInnerHTML = {{ __html: script }} />
                // }
                return (
                        <link href="//cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet"></link>
                        <link rel="stylesheet" href="https://npmcdn.com/react-bootstrap-table/dist/react-bootstrap-table-all.min.css"></link>
                )

            }
        },

Webpack-chunks written to root dir

I followed the instructions from the readme and I get this error:
Unhandled rejection Error: EACCES: permission denied, open '/webpack-chunks.json'

I am running webpack-dev-server --config webpack.client.config.js. It turns out that webpack_config.output.path get overwritten to /.

webpack.client.config.js:

const clientConfig = client_configuration(configuration, settings);
console.log('client config', clientConfig.output.path);

export default clientConfig;

chunk.js

function chunk_info_file_path(webpack_configuration) {
  console.log('chunk location', webpack_configuration.output.path)

output:

client config /home/myproject/dist/public
chunk location /

When I run webpack --config webpack.client.config.js I have no issue.

import token from .scss file illegal

I keep running into this error. This is my start-server.js file.

const { server } = require('universal-webpack');
const settings = require('./config/universal-webpack-settings');
const serverConfig = require('./config/webpack.config.server');

server(serverConfig, settings);

This is my settings file:

const { DIST, SRC } = require('./paths');
const path = require('path');

module.exports = {
  server:
  {
    input: path.join(SRC, 'server-entry.js'),
    output: path.join(DIST, 'server.js')
  }
};

This is my server-entry.js file. If i comment out the require('./server/server.js'), and the setupServer() part, it will work flawlessly, but I need that setupServer part.

require('babel-polyfill');
import express from 'express';
import SwaggerExpress from 'swagger-express-mw';

import {SWAGGER} from './config/paths';
import './config/environment';

function startServer (parameters) {
  const app = express();

  var config = {
    appRoot: SWAGGER
  };
  SwaggerExpress.create(config, (err, swaggerExpress) => {
    if (err) {
      throw err;
    }
    const setupServer = require('./server/server');
    // This installs the middleware
    swaggerExpress.register(app);
    setupServer(app);

    app.listen(process.env.PORT, () => {
      console.log(`listening at http://localhost:${process.env.PORT}`); // eslint-disable-line
    });

  });
}

module.exports = startServer;

Here is my setupSever.js file

import React from 'react';
import { renderToString } from 'react-dom/server';
import express from 'express';
import debug from 'debug';
import morgan from 'morgan';

import compression from 'compression';
import Error500 from './templates/Error500';
import { routingApp, setRoutes } from './router';
import initFirebase from './api/firebase';

const setUpServer = (server, assets) => {
  const log = debug('preact-lego.js, setting up server.js');
  log('starting');
  initFirebase();

  server.set('etag', true);
  server.use((req, res, next) => {
    res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
    res.header('Pragma', 'no-cache');
    res.header('Expires', 0);
    next();
  });
  server.use(morgan('combined'));
  server.use(compression());
  server.enable('view cache');
  server.enable('strict routing');

  Object.assign(express.response, {
    renderPageToString(page) {
      return `<!doctype html>${renderToString(page)}`;
    },
    render500(e) {
      console.log(" This is a 500 error", e);
      log('render500', e);
      this.status(500).send(this.renderPageToString(<Error500 />));
    }
  });
  server.use('/', routingApp);

  setRoutes(assets);
};
export default setUpServer;
/node_modules/react-toolbox/lib/ripple/theme.scss:1
(function (exports, require, module, __filename, __dirname) { @import "../colors";
                                                                                                 ^
SyntaxError: Unexpected token ILLEGAL
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:511:25)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:456:32)
    at tryModuleLoad (module.js:415:12)
    at Function.Module._load (module.js:407:3)
    at Module.require (module.js:466:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (.........../node_modules/react-toolbox/lib/ripple/index.js:13:14)
    at Module._compile (module.js:541:32)
SyntaxError: Unexpected token ILLEGAL
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:511:25)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:456:32)
    at tryModuleLoad (module.js:415:12)
    at Function.Module._load (module.js:407:3)
    at Module.require (module.js:466:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (...../node_modules/react-toolbox/lib/ripple/index.js:13:14)
    at Module._compile (module.js:541:32)

Also, this is my package.json file and I'm currently running npm run start:dev:

 "build:dev": "NODE_CONFIG_DIR=src/config NODE_ENV=development webpack-dev-server --hot --inline --config src/config/webpack.config.dev.js --port 3001",
    "build:server": "NODE_CONFIG_DIR=src/config webpack --config src/config/webpack.config.server.js",
    "start": "NODE_CONFIG_DIR=src/config NODE_ENV=production node compiled/server.js",
    "start:dev": "NODE_CONFIG_DIR=src/config npm-run-all --parallel build:dev build:server development-server",
    "development-server": "NODE_CONFIG_DIR=src/config nodemon src/start-server.js --watch compiled/dist --ignore src/app --exec babel-node",
    "prep-server": "universal-webpack --settings src/config/universal-webpack-settings.js",

Can you help me figure out what is going on?

Error: [universal-webpack] Your server source file must export a function

I try to start the server build server like following:

nodemon -L ./start-server.js --exec babel-node --watch ./build/server

The server.js (built with webpack located in ./build/server/server.js) have a special webpack exported format and start-server.js and universal-webpack/server.js run in a normal node environment and tries to include the buit server.js here

https://github.com/halt-hammerzeit/universal-webpack/blob/master/source/server.js#L41

The result is that the "starter" variable become following:

[ 0, 1 ]

What did I miss?

server object is empty

In import { server } from 'universal-webpack'; the server object is empty.

universal-webpack exports:

{ server: undefined,
  server_configuration: [Function: server_configuration],
  client_configuration: undefined,
  prepare: undefined,
  serverConfiguration: [Function: server_configuration],
  clientConfiguration: undefined }

update to 0.1.20 error running the production script

I upgraded to 0.1.20 to use the stylesheet optional paramenters.

Now when I run in production (that it was perfectly working before), I have this error:

          | /usr/src/app/webpack/webpack.config.client.production.js:105
          | scss_loader.loader = _extractTextWebpackPlugin2.default.extract(scss_loader.loaders.shift(), scss_loader.loaders.join('!'));
          |                                                                                    ^
          | 
          | TypeError: Cannot read property 'shift' of undefined
          |     at Object.<anonymous> (webpack.config.client.production.js:83:50)
          |     at Module._compile (module.js:409:26)
          |     at loader (/usr/src/app/node_modules/babel-register/lib/node.js:158:5)
          |     at Object.require.extensions.(anonymous function) [as .js] (/usr/src/app/node_modules/babel-register/lib/node.js:168:7)
          |     at Module.load (module.js:343:32)
          |     at Function.Module._load (module.js:300:12)
          |     at Module.require (module.js:353:17)
          |     at require (internal/module.js:12:17)
          |     at Object.<anonymous> (/usr/src/app/webpack/webpack.config.client.production.entry.js:3:18)
          |     at Module._compile (module.js:409:26)
          |     at Object.Module._extensions..js (module.js:416:10)
          |     at Module.load (module.js:343:32)
          |     at Function.Module._load (module.js:300:12)
          |     at Module.require (module.js:353:17)
          |     at require (internal/module.js:12:17)
          |     at module.exports (/usr/src/app/node_modules/webpack/bin/convert-argv.js:80:13)
          | 
          | npm info react-frost@ Failed to exec production-build-client script
          | npm ERR! Linux 4.4.0-31-generic
          | npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "run-script" "production-build-client"
          | npm ERR! node v4.4.7
          | npm ERR! npm  v2.15.8
          | npm ERR! code ELIFECYCLE
          | npm ERR! react-frost@ production-build-client: `webpack --colors --display-error-details --config ./webpack/webpack.config.client.production.entry.js`
          | npm ERR! Exit status 1
          | npm ERR! 
          | npm ERR! Failed at the react-frost@ production-build-client script 'webpack --colors --display-error-details --config ./webpack/webpack.config.client.production.entry.js'.
          | npm ERR! This is most likely a problem with the react-frost package,
          | npm ERR! not with npm itself.
          | npm ERR! Tell the author that this fails on your system:
          | npm ERR!     webpack --colors --display-error-details --config ./webpack/webpack.config.client.production.entry.js
          | npm ERR! You can get information on how to open an issue for this project with:
          | npm ERR!     npm bugs react-frost
          | npm ERR! Or if that isn't available, you can get their info via:
          | npm ERR! 
          | npm ERR!     npm owner ls react-frost
          | npm ERR! There is likely additional logging output above.
          | 
          | npm ERR! Please include the following file with any support request:
          | npm ERR!     /usr/src/app/npm-debug.log
          | ERROR: "production-build-client" exited with 1.
          | 
          | npm info react-frost@ Failed to exec production script
          | npm ERR! Linux 4.4.0-31-generic
          | npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "production"
          | npm ERR! node v4.4.7
          | npm ERR! npm  v2.15.8
          | npm ERR! code ELIFECYCLE
          | npm ERR! react-frost@ production: `npm-run-all production-build-client production-build-server production-server`
          | npm ERR! Exit status 1
          | npm ERR! 
          | npm ERR! Failed at the react-frost@ production script 'npm-run-all production-build-client production-build-server production-server'.
          | npm ERR! This is most likely a problem with the react-frost package,
          | npm ERR! not with npm itself.
          | npm ERR! Tell the author that this fails on your system:
          | npm ERR!     npm-run-all production-build-client production-build-server production-server
          | npm ERR! You can get information on how to open an issue for this project with:
          | npm ERR!     npm bugs react-frost
          | npm ERR! Or if that isn't available, you can get their info via:
          | npm ERR! 
          | npm ERR!     npm owner ls react-frost
          | npm ERR! There is likely additional logging output above.
          | 
          | npm ERR! Please include the following file with any support request:
          | npm ERR!     /usr/src/app/npm-debug.log

it seems that the loaders property undefined.

Do you think that it is connected to your last modifications?

This is not for noobs...

Yeah, this is definitely hard to understand by novices like me, I've been reading and trying for a couple of hours but no bueno :/

Errors during server startup don't get printed

My server was throwing an error at startup due to an environment variable that was missing. When I started the server using universal-webpack this error was not shown whatsoever. This results in the server stopping on start without any output in the console.

I don't have time to do a proper PR, so for now I'll submit this issue.

More instructions about exclude_from_externals

Hi @halt-hammerzeit,

Thanks for this awesome lib first.

I ran into a problem when my app requires some css file from a library in node_modules, it has taken me quite some time to figure out exclude_from_externals can deal with this

(I had to look in to server configuration.js's source code, seen your comments there)

From the docs I read, I can understand its about webpack, people shall learn this setting from webpack docs, but still a little more instructions can make people aren't webpack experts like me 's life easier

maybe something like:

When requiring non-js files from node_modules, you must mark them as non-external so webpack buildings for server side can process them

Node failing when it reaches scss imports in react components

Hi. I have attempted to set up universal webpack for my project but I cannot seem to get it running. I keep running into the following error:

SyntaxError: /Users/Aaron/Documents/Frontend/src_react/app/common/bootstrap-components/Container.scss: Unexpected token, expected ( (1:8)
> 1 | @import "sass/variables";
    |         ^
  2 | 
  3 | .container,
  4 | .container-fluid {
    at Parser.pp$5.raise (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:4215:13)
    at Parser.pp.unexpected (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:1599:8)
    at Parser.pp$3.parseExprAtom(/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:3442:14)
    at Parser.parseExprAtom (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:6307:22)
    at Parser.pp$3.parseExprSubscripts (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:3305:19)
    at Parser.pp$3.parseMaybeUnary (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:3285:19)
    at Parser.pp$3.parseExprOps (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:3215:19)
    at Parser.pp$3.parseMaybeConditional (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:3192:19)
    at Parser.pp$3.parseMaybeAssign (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:3155:19)
    at Parser.parseMaybeAssign (/Users/Aaron/Documents/Frontend/node_modules/babylon/lib/index.js:5603:20)

I import scss files into my react components which is what causes this error. It seems that node gets confused when that happens. How can I fix this?

This is my webpack.config.client.js:

import { clientConfiguration } from 'universal-webpack'
import settings from './universal-webpack-settings'
import configuration from './webpack.config'

export default clientConfiguration(configuration, settings)

This is my webpack.config.server.js:

import { serverConfiguration } from 'universal-webpack'
import settings from './universal-webpack-settings'
import configuration from './webpack.config'

export default serverConfiguration(configuration, settings)

This is my start-server.js:

import { server } from 'universal-webpack'
import settings from './universal-webpack-settings'
import configuration from './webpack.config'

server(configuration, settings);

This is my server.js:

import React from 'react';
import path from 'path';
import http from 'http';
import express from 'express';
import httpProxy from 'http-proxy';
import { match, createRoutes, RouterContext } from 'react-router';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';

import configureStore from 'app/configureStore';
import HomeRouter from 'app/home/components/HomeRouter';
import template from 'app/template';

export default function(parameters) {
  const app = new express()
  const server = new http.Server(app)
  const routes = createRoutes(HomeRouter());
  const preloadedState = {
    session: {
      currentUser: null
    }
  };
  const store = configureStore(preloadedState);
  app.use(express.static(path.join(__dirname, 'build/assets')))
  const backend_url = process.env.BACKEND_URL || "http://localhost:8000/";
  const proxy = httpProxy.createProxyServer({ target: backend_url, changeOrigin: true })
  app.use('/api', (req, res) => proxy.web(req, res))

  app.use((req, res) => {
    match(
      { routes, location: req.url },
      (error, redirectLocation, renderProps) => {
        if (error) {
            res.status(500)
            return res.send('Server error')
        }
        const page = renderToString(
          <Provider store={store}>
            <RouterContext {...renderProps} />
          </Provider>
        )
        res.status(200)
        res.send(template({
          body: page
        }))
      }
    )
  })

  server.listen()
}

and this is my webpack.config.js:

const path = require('path');
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const context = path.resolve(__dirname);

var config = {
  entry: path.join(__dirname, './src_react/app/entry.jsx'),
  context: context,
  output: {
    path: path.join(__dirname, '/build'),
    filename: '[name]-[hash].js',
    publicPath: '/'
  },
  module: {
    loaders: [
      {
        test: /\.json$/,
        loader: 'json'
      },
      {
        test: [/\.jsx?$/, /\.js?$/],
        exclude: /(node_modules|bower_components)/,
        loader: 'babel'
      },
      {
        test: /\.png$/,
        loader: 'url-loader?limit=100000'
      },
      {
        test: /\.jpg$/,
        loader: 'file-loader'
      },
      {
        test: /\.gif$/,
        loaders: [
          'file?hash=sha512&digest=hex&name=[hash].[ext]',
          'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'
        ]
      },
      {
        test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'url?limit=10000&mimetype=application/font-woff'
      },
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'url?limit=10000&mimetype=application/octet-stream'
      },
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'file'
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'url?limit=10000&mimetype=image/svg+xml'
      },
      {
        test: /\.otf(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'file-loader?name=fonts/[name].[ext]'
      },
      {
        test: /\.scss$/,
        loaders: ['style', 'css', 'resolve-url', 'sass?sourceMap']
      },
      {
        test: /\.css$/,
        loader: 'style-loader!css-loader'
      }
    ]
  },
  resolve: {
    root: [
      path.resolve('./src_react')
    ],
    extensions: ['', '.js', '.jsx', '.css', '.scss'],
    alias: {
      'ie': 'component-ie'
    }
  },
  sassLoader: {
    includePaths: [path.resolve(__dirname, "./src_react")]
  },
  postcss: [
    autoprefixer
  ],
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, '/src_react/index.tmpl.html')
    }),
    new webpack.DefinePlugin({
      'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development') }
    }),
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    })
  ]
};

module.exports = config;

Webpack 2 tree-shaking

Hello @halt-hammerzeit!

First of all I'd like to thank you for the isomorphic tools - splendid work.

I'm now looking into how well I could incorporate this repo into my future work and it seems I stumbled upon something I can't really find a way around.

I created a fork of your example repo here and I did some changing around. One notable thing to notice is that I switched off module parsing using babel for es2015 modules, because WP2 natively understands it and it's how it does treeshaking.

Now - the problem.

TypeError: Cannot read property 'a' of undefined
    at Object.<anonymous> (/Users/elodszopos/study/webpack-react-redux-isomorphic-render-example/build/server/webpack:/code/client/react-isomorphic-render.js:11:3)
    at Object.module.exports.module.exports.apiServer.http.host (/Users/elodszopos/study/webpack-react-redux-isomorphic-render-example/build/server/server.js:256:30)
    at __webpack_require__ (/Users/elodszopos/study/webpack-react-redux-isomorphic-render-example/build/server/webpack:/webpack/bootstrap 5b78232aefaf71c0b543:19:1)
    at Object.<anonymous> (/Users/elodszopos/study/webpack-react-redux-isomorphic-render-example/build/server/server.js:1686:90)
    at __webpack_require__ (/Users/elodszopos/study/webpack-react-redux-isomorphic-render-example/build/server/webpack:/webpack/bootstrap 5b78232aefaf71c0b543:19:1)
    at /Users/elodszopos/study/webpack-react-redux-isomorphic-render-example/build/server/webpack:/webpack/bootstrap 5b78232aefaf71c0b543:65:1
    at Object.<anonymous> (/Users/elodszopos/study/webpack-react-redux-isomorphic-render-example/build/server/server.js:70:10)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.require (module.js:483:17)
    at require (internal/module.js:20:19)
    at /Users/elodszopos/study/webpack-react-redux-isomorphic-render-example/node_modules/universal-webpack/source/server.js:42:19

Upon doing npm run development-page-server I run into this nasty little bugger. From what I understand and if I'm not mistaken, the server part of universal-webpack is expecting the bundled server.js to be in a specific format - and since I switched to WebPack2 it's not exactly that. So something goes wrong.

Do you maybe have any insight into what's going on?

Usage with davezuko's starter kit

I'm trying to create a universal React Redux project based on some of the fundamentals of davezuko's starter kit. Found your stuff but it seems to be very unclear how to achieve it so I'm turning to you here for some instructions with the configuration.
I also opened a question here
Thanks in advance!

`style` loader not replaced with fake-style-loader

In a webpack configuration i can refer to a named loader as name-loader or just with its name. If i do the latter, universal webpack will fail replacing style with fake-style-loader while generating server conf.

i.e.

...
    loader: 'style!css'
...

This bug results in style loader to be included in the built output and node.js won't be able to run it as style-loader only works in the browser.

Provide a webpack 2 example

Hi.
I have been using universal-webpack for a while and recently became curious in migrating my project to webpack 2. However, even though in one of the closed issues you mention that universal-webpack should be working with webpack 2 - it doesn't, since passing one raw configuration through webpack 2 works and trying to wrap it in the client_configuration - throws an error "TypeError: undefined is not iterable!".

I couldn't find a source of that error in the configuration though, but I think that it would be really useful if you could try to migrate this project to webpack 2.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.