Webpack externals in both node and the browser - node.js

I have an isomorphic React application which runs in both the browser and on the server. I build the same code for both by running two separate Webpack builds through two different entry points and with different configs.
The problem is that the external file that exists on the browser window via an external script tag (Google Maps in this instance) obviously won't exist when running in node on the server. The code is identical except the entry point file.
index.html:
// index.html
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=XXX"></script>
Simplified config:
// Server Webpack config
{
entry: 'server.js',
target: 'node',
externals: {
google: google
}
}
// Client Webpack config
{
entry: 'client.js',
target: 'browser',
externals: {
google: google
}
}
The Component:
// The view which builds and runs fine in
// the client but doesn't run on the server.
var React = require('react'),
css = require('./style.css'),
google = require('google'); // Nope, not on the server obviously!
var Component = React.createClass({
render: function () {
return (
<div>
// Do maps stuff
</div>
);
}
});
module.exports = Component;
My question is how should I handle this?
Error: Cannot find module 'google'
I currently have a solution which I'm not at all keen on.
// Server Webpack config
{
entry: 'server.js',
target: 'node',
externals: {
google: google
},
plugins: [
new webpack.DefinePlugin({ 'ENV.browser': false }),
]
}
// Client Webpack config
{
entry: 'client.js',
target: 'browser',
externals: {
google: google
},
plugins: [
new webpack.DefinePlugin({ 'ENV.browser': true }),
]
}
// The component
var React = require('react'),
css = require('./style.css');
if (ENV.browser) {
var google = require('google');
}
var Component = React.createClass({
render: function () {
return (
<div>
if (ENV.browser) {
// Do maps stuff
}
</div>
);
}
});
module.exports = Component;

You can use NormalModuleReplacementPlugin to replace the module with a noop, as per an idea from Dustan Kasten:
{
plugins: [
new webpack.NormalModuleReplacementPlugin(/^google$/, 'node-noop'),
],
}

Related

Different builds based on targeting client vs server code

I currently have 2 separate webpack builds for server rendered vs client rendered code. Is there an easy way to change the build output based on server/client build?
For example something like this:
// Have some code like this
if(is_client){
console.log('x.y.z')
} else {
server.log('x.y.z')
}
// Webpack outputs:
// replaced code in client.js
console.log('x.y.z')
// replaced code in server.js
server.log('x.y.z')
Have you tried anything like this?
// webpack.config.js
module.exports = () => ['web', 'node'].map(target => {
const config = {
target,
context: path.resolve('__dirname', 'src'),
entry: {
[target]: ['./application.js'],
},
output: {
path: path.resolve(__dirname, 'dist', target),
filename: '[name].js'
},
modules: { rules: ... },
plugins: [
new webpack.DefinePlugin({
IS_NODE: JSON.stringify(target === 'node'),
IS_WEB: JSON.stringify(target === 'web'),
}),
],
};
return config;
});
// later in your code
import logger from 'logger';
if (IS_NODE) {
logger.log('this is node js');
}
if (IS_WEB) {
console.log('this is web');
}
how the compilation works?
// client.bundle.js
import logger from 'logger';
// DefinePlugin creates a constant expression which causes the code below to be unreachable
if (false) {
logger.log('this is node js');
}
if (true) {
console.log('this is web');
}
Finally you will produce your build in production mode, so webpack will include a plugin called UglifyJS, this has a feature called dead code removal (aka tree shaking), so it will delete any unused/unreachable code.
and the final result will look like:
// node.bundle.js
import logger from 'logger';
console.log('this is node js');
//web.bundle.js
console.log('this is node js');

SSR React - Load Class Names Before bundle.js loads

I am attempting to create a SSR react app with Firebase hosting and Cloud Functions. My components are using className to declare classes. My server-rendered html does not include these, it only has the data-reactid elements.
It is not until the bundle.js is loaded that the real class="example-class" is loaded.
I do not want to wait for the bundle.js to download before the classes are loaded. I'd rather not code with both
class="kitten-image" className="kitten-image"
because that seems like a waste. I have not been able to find anything that either transforms the CSS files to have the data-reactid identifiers, or to automatically include the class="kitten-image" on the server-side during the compile process with babel.
Overview: My server-side compiled code injects the babel compiled react components into an index.html template file, which is sent via express app on http request on Firebase functions. The index.html file includes hard-coded references to the webpack processed styles.css and bundle.js in the firebase hosting public folder.
Thus, my server-side rendered HTML should immediately be able to reference the styles.css sheet - however, the classes are not in the html until the bundle.js is loaded (which is the problem).
Server-side rendered HTML before bundle.js loads
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React Server Side Rendering - Firebase Hosting</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="root"><div data-reactroot="" data-reactid="1" data-react-checksum="1473597379"><h1 data-reactid="2">Hello World!</h1><p data-reactid="3"><!-- react-text: 4 -->This is a kitten: <!-- /react-text --><br data-reactid="5"/><img src="/media/kitten.jpg" alt="Kitten" data-reactid="6"/></p></div></div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
HTML after bundle.js loads
Note that class="kitten-image" has been added.
<!DOCTYPE html>
<html><head>
<meta charset="UTF-8">
<title>React Server Side Rendering - Firebase Hosting</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="root"><div data-reactroot="" data-reactid="1"><h1 data-reactid="2">Hello World!</h1><p class="intro" data-reactid="3"><!-- react-text: 4 -->This is a kitten: <!-- /react-text --><br data-reactid="5"><img src="/media/kitten.jpg" alt="Kitten" class="kitten-image" data-reactid="6"></p></div></div>
<script type="text/javascript" src="bundle.js"></script>
</body></html>
Folder Structure
App Component Example
see className
import React, { Component } from 'react';
import kitten from "./kitten.jpg";
import "./App.scss";
class App extends Component {
render() {
return (
<div class="main">
<h1>Hello World!</h1>
<p className="intro">This is a kitten: <br /><img src={kitten} alt="Kitten" className="kitten-image" /></p>
</div>
);
}
}
export default App;
Babel Compiled Component
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var kitten = "/media/kitten.jpg";
var App = function (_Component) {
_inherits(App, _Component);
function App() {
_classCallCheck(this, App);
return _possibleConstructorReturn(this, (App.__proto__ || Object.getPrototypeOf(App)).apply(this, arguments));
}
_createClass(App, [{
key: "render",
value: function render() {
return _react2.default.createElement(
"div",
{ "class": "main" },
_react2.default.createElement(
"h1",
null,
"Hello World!"
),
_react2.default.createElement(
"p",
null,
"This is a kitten: ",
_react2.default.createElement("br", null),
_react2.default.createElement("img", { src: kitten, alt: "Kitten" })
)
);
}
}]);
return App;
}(_react.Component);
exports.default = App;
Server index.js
import React from "react";
import { renderToString } from "react-dom/server";
import App from "../shared/App";
import express from "express";
import * as fs from "fs";
import * as functions from "firebase-functions";
const index = fs.readFileSync(__dirname + '/../../index.template.html', 'utf8');
const app = express();
app.get('**', (req, res) => {
const html = renderToString(<App />);
const finalHtml = index.replace('<!-- ::APP:: -->', html);
res.set('Cache-Control', 'public, max-age=600, s-maxage=1200');
res.send(finalHtml);
});
export let ssrapp = functions.https.onRequest(app);
//app.listen(3006, () => { console.log('Listening on 3006.'); });
Server index.js babel compiled
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ssrapp = undefined;
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
var _server = require("react-dom/server");
var _App = require("../shared/App");
var _App2 = _interopRequireDefault(_App);
var _express = require("express");
var _express2 = _interopRequireDefault(_express);
var _fs = require("fs");
var fs = _interopRequireWildcard(_fs);
var _firebaseFunctions = require("firebase-functions");
var functions = _interopRequireWildcard(_firebaseFunctions);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var index = fs.readFileSync(__dirname + '/../../index.template.html', 'utf8');
var app = (0, _express2.default)();
app.get('**', function (req, res) {
var html = (0, _server.renderToString)(_react2.default.createElement(_App2.default, null));
var finalHtml = index.replace('<!-- ::APP:: -->', html);
res.set('Cache-Control', 'public, max-age=600, s-maxage=1200');
res.send(finalHtml);
});
var ssrapp = exports.ssrapp = functions.https.onRequest(app);
//app.listen(3006, () => { console.log('Listening on 3006.'); });
Webpack
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const autoprefixer = require("autoprefixer");
const extractSass = new ExtractTextPlugin({
filename: "public/styles.css",
disable: process.env.NODE_ENV === "development"
});
// Webpack settings unique to browser-side script
const browserConfig = {
entry: './src/browser/index.js',
devtool: "source-map",
module: {
rules: [
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: [/\.svg$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: "file-loader",
options: {
name: "public/media/[name].[ext]",
publicPath: url => url.replace(/public/, "")
}
},
{
test: /\.scss$/,
use: extractSass.extract({
use: [
{ loader: 'css-loader', options: { sourceMap: true } },
{
loader: 'postcss-loader',
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
sourceMap: true,
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
{ loader: 'sass-loader', options: { sourceMap: true } }
],
// use style-loader in development
fallback: "style-loader"
})
}
]
},
plugins: [
extractSass
],
output: {
filename: './public/bundle.js',
path: __dirname
}
}
module.exports = [browserConfig];
I was initially using Babel because I was having issues compiling via webpack with the Google Cloud / firebase modules. It was trying to bundle in everything unnecessarily.
I created a separate webpack server-side configuration. This does a few things to work correctly.
1.I'm using the "webpack-node-externals" package which is designed to exclude node modules for backend compilation. Without this, my generated JS file was enormous. My backend has a whole node_modules folder, so it does not need these items bundled.
2.I added the false statements to __dirname and __filename - I don't know what this does or how it works, but it fixed my issue with opening and reading my html template file server-side.
3.The file-loader does not actually copy the files, with emit: false
The Real Fix: The .scss tester uses css-loader/locals This was key! It generates the correct class names on the server and places them in the components when rendering! It also does not bundle / copy and files in this configuration, since the browser side config does that.
I was using the following plugin in my .babelrc file with the babel compile method. This breaks the image / file transfer process in webpack and must be removed from .babelrc
"plugins": [["transform-assets-import-to-string", {
"baseDir": "",
"baseUri": "/media"
}]],
Revised Webpack
const serverConfig = {
entry: "./src/server/index.js",
target: "node",
externals: [nodeExternals()], // exclude node_modules
node: {
__dirname: false,
__filename: false
},
output: {
filename: "./functions/src/server/server.js",
libraryTarget: "commonjs2"
},
module: {
rules: [
{
test: [/\.svg$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: "file-loader",
options: {
name: "public/media/[name].[ext]",
publicPath: url => url.replace(/public/, ""),
emit: false
}
},
{
test: /\.scss$/,
use: [
{ loader: 'css-loader/locals' },
{ loader: 'sass-loader' }
]
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};

React code not compiling with webpack

My code wont compile for some reason. The weird thing is that is will work when I use the in browser interpreter.
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/react#15/dist/react.js"></script>
<script src="https://unpkg.com/react-dom#15/dist/react-dom.min.js"></script>
React code:
var Cookies = require('cookies');
var cookieParser = require('cookie-parser');
var name = document.getElementById('name').innerHTML;
//var name = req.user.name;
var start = false;
var Assets = React.createClass({
getInitialState: function(){
return({
assets: [],
secondsElapsed: 0
});
},
tick: function() {
//this.setState({secondsElapsed: this.state.secondsElapsed + 1});
if(start === true){
console.log(name);
var myHeaders = new Headers();
var token = new Cookies(req,res).get('access_token');
myHeaders.append('acess_token', token);
var myInit = { method: 'GET',
headers: myHeaders};
fetch('/api/user/all/?name='+name, myInit).then(function(data){
return data.json();
}).then( json => {
this.setState({
assets: json
});
});
}
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function(){
var assets = this.state.assets;
assets = assets.map(function(asseti,index){
return(
asseti.map(function(asset, index){
return(
<li key={index}>
<span className={asset.active}></span>
<span>{asset.name}</span>
<span >{asset.description}</span>
<span>{asset.location.coordinates[0]}{asset.location.coordinates[1]}</span>
</li>
)
})
)
});
return(
<div>
<form onSubmit={this.handleSubmit}>
<input type="submit" value="Find assets" />
</form>
{assets}
</div>
);
},
handleSubmit: function(e){
e.preventDefault();
start = true;
// name = this.refs.name.value;
fetch('/api/user/all/?name='+name).then(function(data){
return data.json();
}).then( json => {
this.setState({
assets: json
});
});
}
});
ReactDOM.render(<Assets />, document.getElementById('assets'));
Webpack.config.js:
var path = require('path');
module.exports = {
entry: path.resolve(__dirname, 'puplic') + '\\js\\baseReact.js',
output: {
path: path.resolve(__dirname, 'dist') + '/app',
filename: 'bundle.js',
publicPath: '/app/'
},
module: {
loaders: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'public/js'),
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0']
}
},
{
test: /\.css$/,
loader: 'style-loader!css-loader'
}
]
},
devServer: {
historyApiFallback: true
}
};
Error:
ERROR in ./puplic/js/baseReact.js
Module parse failed: C:\Users\test\Documents\GPSTracker\puplic\js\baseReact.js Unexpected token (
53:14)
You may need an appropriate loader to handle this file type.
| asseti.map(function(asset, index){
| return(
| <li key={index}>
| <span className={asset.active}></span>
| <span>{asset.name}</span>
I figure I must be doing something dumb like missing something as it runs in browser which is weird. Does that gloss over some errors as it is interpreted as opposed to been compiled before been run?
Based on the comments, you may be missing the fetch import. Fetch is not readily available in all browsers.
The npm package whatwg-fetch mentions specifically how to get fetch working in a webpack-enabled environment.
Installation
npm install whatwg-fetch --save
or
bower install fetch.
You will also need a Promise polyfill for older browsers. We recommend
taylorhakes/promise-polyfill for its small size and Promises/A+
compatibility.
For use with webpack, add this package in the entry configuration
option before your application entry point:
entry: ['whatwg-fetch', ...]
For Babel and ES2015+, make sure to
import the file (in your react-components):
import 'whatwg-fetch'
Also, looking at your code, which is deviating a tad from regular javascript style guides in terms of spacings, I'd look into getting eslint up and running in your environment for better feedback for errors like these. If you had eslint enabled, you'd get fetch is undefined as soon as you tried something like this without importing fetch first.
Another personal note from me, try just importing whatwg-fetch in your file, before tampering with your webpack config. You may not need to add it as an entry.
Best of luck!

Hot Module Reloading is making initial page request take 10-20s, with Webpack, Koa, Vue.js

For some reason most page refreshes re-request the bundle.js file and it takes about 10-15-20 seconds to download from localhost. This all from localhost, and the bundle.js file is about 1mb in size. The request for this file only seems to crawl, loading a few kilobytes at a time.
Some observations:
After some digging it seems to be stalling out on the initial call to the server from __webpack_hmr, but I'm not sure as this call happens after the call to bundle.js. Below is the log of the server request flow.
It is only slow on pages that have more than one or two components, ie. anything other than the homepage. This alludes to the idea that it might be related to the hot module reloading.
The homepage will still take > 5s (sometimes 10-20) just like the other pages, but if I refresh the page with Ctrl+R, it comes back nearly instantly. If I do an address-bar refresh, it takes longer. The other pages still take just as long no matter if I Ctrl+R or do an address-bar reload...
Update: I removed the hot module replacement, and it definitely seems to be the source of the issue, as the pages load instantly without it.
Request log:
-- Response time GET / = 609ms
--> GET / 200 647ms 2.55kb
<-- GET /main.aafc9fb7f6a0c7f127edb04734d29547.css
--> GET /main.aafc9fb7f6a0c7f127edb04734d29547.css 200 17ms 3.43kb
<-- /bundle.js
--> GET /bundle.js 200 18ms 1.29mb
<-- GET /__webpack_hmr
And then in the chrome console, for this request it shows:
Here's my setup:
Using Koa as the server environment (using streaming/chunking in initial response)
Using webpack with hot module reloading
Using Vue.js as a frontend framework, with server-side rendering
bundle.js is served through the typical serve-static package
bundle.js doesn't seem to be being cached at all. Why is this?
On the Koa side of things, I started with some boilerplate package to do all this server-side rendering and such. This has been happening since I started messing around with this setup, and webpack in general, so I'm trying to get to the bottom of it. It seems to be a little random, where sometimes it will come back in < 1s, but most times it takes 10+ seconds. Sometimes 30+ seconds?!
I've also tried to use different libraries to serve the static files, but they all seem to do this.
Here is my main webpack config ('webpack.client', extended below):
'use strict'
const path = require('path')
const webpack = require('webpack')
const AssetsPlugin = require('assets-webpack-plugin')
const assetsPluginInstance = new AssetsPlugin({path: path.join(process.cwd(), 'build')})
const postcss = [
require('precss')()
//require('autoprefixer')({browsers: ['last 2 versions']}),
]
module.exports = {
entry: [
'./src/client-entry.js'
],
output: {
path: path.join(process.cwd(), 'build'),
filename: 'bundle.js',
publicPath: '/'
},
resolve: {
extensions: ['', '.vue', '.js', '.json']
},
module: {
loaders: [
{
test: /\.vue$/,
loaders: ['vue']
},
{
test: /\.js$/,
loaders: ['babel'],
exclude: [/node_modules/]
},
{
test: /\.json$/,
loaders: ['json'],
exclude: [/node_modules/]
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'url?limit=10000&name=images/[hash].[ext]',
include: path.src,
},
{
test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/,
loader: 'url-loader',
include: path.src,
}
]
},
node: { net: 'empty', dns: 'empty' },
postcss,
vue: {
postcss,
loaders: {}
},
plugins: [
assetsPluginInstance
]
}
And also this (extends the previous):
'use strict'
const webpack = require('webpack')
const config = require('./webpack.client')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
config.entry.push('webpack-hot-middleware/client')
//config.devtool = 'inline-eval-cheap-source-map'
config.plugins = config.plugins.concat([
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
'__DEV__': true,
'process.env.NODE_ENV': JSON.stringify('development')
}),
new ExtractTextPlugin('[name].[contenthash].css')
])
config.vue.loaders = {
postcss: ExtractTextPlugin.extract(
'vue-style-loader',
'css-loader?sourceMap'
),
css: ExtractTextPlugin.extract(
'vue-style-loader',
'css-loader?sourceMap'
)
}
module.exports = config
Here is my server index.js file for Koa:
import path from 'path'
import fs from 'fs'
import Koa from 'koa'
import convert from 'koa-convert'
//import serve from 'koa-static-server'
import serveStatic from 'koa-static'
import {PassThrough} from 'stream'
import {createBundleRenderer} from 'vue-server-renderer'
import serialize from 'serialize-javascript'
import MFS from 'memory-fs'
import assets from '../build/webpack-assets'
import cookie from 'koa-cookie'
let renderer
const createRenderer = fs => {
const bundlePath = path.resolve(process.cwd(), 'build/server-bundle.js')
return createBundleRenderer(fs.readFileSync(bundlePath, 'utf-8'))
}
const app = new Koa();
app.use(cookie());
if (process.env.NODE_ENV === 'development') {
// DEVELOPMENT, with hot reload
const webpack = require('webpack')
const webpackConfig = require('../config/webpack.client.dev')
const compiler = webpack(webpackConfig)
const devMiddleware = require('koa-webpack-dev-middleware')
const hotMiddleware = require('koa-webpack-hot-middleware')
app.use(convert(devMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath,
stats: {
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}
})))
app.use(convert(hotMiddleware(compiler)))
// server renderer
const serverBundleConfig = require('../config/webpack.bundle')
const serverBundleCompiler = webpack(serverBundleConfig)
const mfs = new MFS()
serverBundleCompiler.outputFileSystem = mfs
serverBundleCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
stats.errors.forEach(err => console.error(err))
stats.warnings.forEach(err => console.warn(err))
renderer = createRenderer(mfs)
})
}
else {
// PRODUCTION
// use nginx to serve static files in real
//app.use(convert(serve({rootDir: path.join(process.cwd(), 'build'), rootPath: '/static'})))
app.use(serveStatic(path.join(process.cwd(), 'build')));
renderer = createRenderer(fs)
}
app.use(ctx => {
var start = new Date;
ctx.type = 'text/html; charset=utf-8'
const context = {url: ctx.url}
const title = 'Tripora';
const stream = new PassThrough()
console.log("Checking if server-side cookie exists...");
// See if request sent over an authentication token in their cookies
if(ctx.cookie && ctx.cookie.token) {
console.log("Found cookie token.");
context.token = ctx.cookie.token;
}
stream.write(`<!DOCTYPE html><html style="min-height: 100%;"><head><meta charset="utf-8"/><title>${title}</title>${assets.main.css ? `<link rel="stylesheet" href="${assets.main.css}"/>` : ''}</head><body style="min-height: 100%;">`)
const renderStream = renderer.renderToStream(context)
let firstChunk = true
renderStream.on('data', chunk => {
// we tell the request to ignore files as an initial reuqest
var isPage = ctx.url.split(".").length == 1;
if (firstChunk && context.initialState && isPage) {
stream.write(`<script>window.__INITIAL_STATE__=${serialize(context.initialState, {isJSON: true})}</script>${chunk}`)
firstChunk = false
} else {
stream.write(chunk)
}
})
renderStream.on('end', () => {
stream.write(`<script src="${assets.main.js}"></script></body></html>`)
var ms = new Date - start;
//ctx.set('X-Response-Time', ms + 'ms');
console.log("-- Response time %s %s = %sms", ctx.method, ctx.originalUrl, ms);
ctx.res.end()
})
renderStream.on('error', err => {
console.log("ERROR", err.stack);
throw new Error(`something bad happened when renderToStream: ${err}`)
})
ctx.status = 200
ctx.body = stream
})
const port = process.env.NODE_PORT || 80
app.listen(port, () => {
console.log(`==> Listening at http://localhost:${port}`)
})
Anyone know why the HMR initial request would take so long, and seem to be so random (sometimes 5s, sometimes 30 seconds)? Techneregy.

requirejs optimization with gulp

I am using requirejs and gulp to build angular app. I am using amd-optimize and gulp-requirejs-optimize to add all js files into single file. Here is my main.js file:
require.config(
{
paths: {
app : 'app',
angular : '../bower_components/angular/angular',
jquery : '../bower_components/jquery/dist/jquery',
angularResource : '../bower_components/angular-resource/angular-resource',
angularRoute : '../bower_components/angular-route/angular-route',
publicModule : 'public_module',
route : 'route'
},
shim: {
'app': {
deps: ['angular']
},
'angularRoute': ['angular'],
angular : {exports : 'angular'}
}
}
);
And gulpfile.js
var gulp = require('gulp');
var rjs = require('gulp-requirejs');
var connect = require('gulp-connect');
var requirejsOptimize = require('gulp-requirejs-optimize');
var amdOptimize = require('amd-optimize');
var concat = require('gulp-concat');
// using amd-optimize.
gulp.task('bundle', function () {
return gulp.src('app/**/*.js')
.pipe(amdOptimize('main'))
.pipe(concat('main-bundle.js'))
.pipe(gulp.dest('dist'));
});
// using gulp-requirejs-optimize.
gulp.task('scripts', function () {
return gulp.src('app/main.js')
.pipe(requirejsOptimize())
.pipe(gulp.dest('dist'));
});
When I run gulp bundle or gulp scripts, it shows me same content of main.js file in output file(not showing all js template in one output file).
The output file is:
require.config({
paths: {
angular: '../bower_components/angular/angular',
jquery: '../bower_components/jquery/dist/jquery',
angularResource: '../bower_components/angular-resource/angular-resource',
angularRoute: '../bower_components/angular-route/angular-route',
publicModule: 'public_module',
route: 'route'
},
shim: {
'app': { deps: ['angular'] },
'angularRoute': ['angular'],
angular: { exports: 'angular' }
}
});
define('main', [], function () {
return;
});
How can I configure gulp to put every js template into one js file?
check the docs for all the options for amdoptimize. For example you can point to your config file or add paths.
I always have trouble getting all the paths to line up, so make sure to check them diligently.
here is how you can start to put the options in:
gulp.task('requirejsBuild', function() {
gulp.src('app/**/*.js',{ base: 'app' })
.pipe(amdOptimize("app",{
baseUrl: config.app,
configFile: 'app/app-config.js',
findNestedDependencies: true,
}))
.pipe(concat('app.js'))
.pipe(gulp.dest('dist'))
});
You are not requiring any files - you just define an empty module named main.
You need to kick off you app by requiring a module, eg.
require(['app'], function (App) {
new App().init();
});

Resources