Giter Site home page Giter Site logo

Comments (17)

sunyang713 avatar sunyang713 commented on May 1, 2024 11

Thanks for the response @SpaceK33z . I want to be very clear about my use case, so this response is pretty long. I would be very happy to see if there's a nicer, /cleaner/ way to do this that the community has established, but at the time I hadn't found one, and I haven't found one since.

I do use html-webpack-plugin. I use it to add a hash after my javascript builds. At the same time, I use server-side rendering with react. I need to render some initial content, AFTER the server is running and index.html is generated with the correctly linked files. The solution I am trying to do is using html-webpack-plugin to generate index.ejs; I start with a base template.ejs and a generate a new template. Before I run the server, I start with some base template:

. . .
<%= "\<% if (content) { %\>" %>
<div id="root"><%= "\<%- content %\>" %></div>
<%= "\<% } else { %\>" %>
. . .

At the bottom of the file, the correct index.js?1231243hash file is linked. Then, when I run the server with webpack (in devo; prod vs devo explained later), html-webpack-plugin converts this to:

. . .
<% if (content) { %>
<div id="root"><%- content %></div>
<% } else { %>
. . .

So I've generated a template from a template, and I will then use this to server-render like so:

app.set('view engine', 'ejs')
. . .
// on 200
res.render('index.ejs', appInjections())

where appInjections is a function that renders my react app server-side as a string, and returns an object such as:

{
    "content": "<react id stuff stuff stuff> ... ... . > .> "
}

And res.render will pass this in nicely.

... except it doesn't - here is the problem. In prod, I would build all of my javascript files before running my server. The document lives in the filesystem and is easily accessed using the static-middleware provided by express.

But in devo, I use webpack-dev-middleware which means all files are served from memory. This also means that the generated index.ejs is in memory. res.render("index.ejs") looks for 'index.ejs' in the regular filesystem, not the memoryfs. So for the time being, I've been accessing the memoryfs as a hack. I rewrite the request to / to return:

// the return value of a new function renderApp()
ejs.render(getDocument(), { content })

where getDocument() is a function that looks like this in devo:

function getDocument() {
  const memoryFs = compiler.outputFileSystem // 'compiler' is the webpack compiler
  return memoryFs.readFileSync('index.ejs', 'utf8')
}

In prod it looks like this:

import fs from 'fs'
import path from 'path'

export default function getDocument() {
  return fs.readFileSync('index.ejs', 'utf8')
}

and the response command is changed to:

// on 200
res.send(renderApp())

I would like for getDocument() to look almost identical (more like the prod version), or somehow get rid of the need for external functions with a split between devo/prod, so I could maybe do res.render() for both devo and prod.

from webpack-dev-middleware.

SpaceK33z avatar SpaceK33z commented on May 1, 2024 9

@axelpale, can't you do something like this:

var webpackMiddleware = require("webpack-dev-middleware");
var historyApiFallback = require("connect-history-api-fallback");
var instance = webpackMiddleware(...);
app.use(instance);
app.use(historyApiFallback());
app.use(instance);

Note that using app.use(instance) twice is on purpose, without this rewriting the request doesn't work.

from webpack-dev-middleware.

eduardoleal avatar eduardoleal commented on May 1, 2024 4

I tried to do something like @SpaceK33z 's comment but the express server does not respond.

const express = require('express');
const path = require('path');
const app = express();
const historyApiFallback = require('connect-history-api-fallback');

if (process.env.NODE_ENV !== 'production') {
    const webpackMiddleware = require('webpack-dev-middleware');
    const webpack = require('webpack');
    const webpackConfig = require('./webpack.config.js');
    const webpackInstance = webpackMiddleware(webpack(webpackConfig));
    app.use(webpackInstance);
    app.use(historyApiFallback);
    app.use(webpackInstance);
} else {
    app.use(express.static('dist'));
    app.get('*', (req, res) => {
        res.sendFile(path.join(__dirname, 'dist/index.html'));
    })
}

app.listen(process.env.PORT || 3050, () => console.log('Listening...'));

I can't access react routes using browserHistory. It only works if i use hashHistory.

from webpack-dev-middleware.

axelpale avatar axelpale commented on May 1, 2024 2

@SpaceK33z Interesting! The first app.use(instance) catches /index.html. If not caught, then app.use(historyApiFallback()) will catch /any/thing and rewrite it to /index.html, which is finally caught and served by the second app.use(instance). I get it, cool, thanks!

from webpack-dev-middleware.

robbyemmert avatar robbyemmert commented on May 1, 2024 2

There really needs to be a more intuitive solution. Adding the instance twice seems really hacky. Is there a good reason that there isn't an equivalent historyApiFallback option in webpack-dev-middleware? This seems like a really common use case that deserves a setting.

from webpack-dev-middleware.

grrowl avatar grrowl commented on May 1, 2024

You can access the filesystem directly through the fileSystem property on the devMiddleware instance:

const compiler = webpack(config),
  devMiddleware = webpackDevMiddleware(compiler)

app.use(devMiddleware)

app.use((req, res) => {
  // rewrite the request to serve the index page
  console.log('serving index.html for', req.url)

  res.set('Content-Type', 'text/html')
  res.send(
    devMiddleware.fileSystem.readFileSync(config.output.path + 'index.html')
  )
})

edit: oops, apparently "You should not access devMiddleware.fileSystem. It's private. Instead rewrite requests."

from webpack-dev-middleware.

SpaceK33z avatar SpaceK33z commented on May 1, 2024

@sunyang713, does the link @grrowl posted above answer your question? So you'd need to rewrite the request.

from webpack-dev-middleware.

sunyang713 avatar sunyang713 commented on May 1, 2024

Not quite @SpaceK33z, rewriting requests alone doesn't achieve what I need, because I would still need to access the memoryFs. I also posted in the issue that grrowl linked (no response yet). I've pasted below

hi, so just to be clear, is it still okay to manually serve a file from devMiddleware.fileSystem? For my project, this is pretty important - i.e. I need to modify the index.html request handler that devMiddleware automatically implements. How exactly can I "rewrite requests" for devMiddleware.fileSystem files?

from webpack-dev-middleware.

SpaceK33z avatar SpaceK33z commented on May 1, 2024

@sunyang713, could you tell me why you need to rewrite the request to index.html? Just understanding your usecase would help me. If you need to set a variable in your index.html, I would suggest to use html-webpack-plugin instead. It allows you to pass custom variables and all other crazy stuff, without hacking the filesystem.

from webpack-dev-middleware.

SpaceK33z avatar SpaceK33z commented on May 1, 2024

Wow. Okay, thanks for your detailed response. Did you take a look at #118? I'm not sure if it completely fits your usecase, but it has some similarities.

Otherwise maybe it would be an idea to enhance the feature from #118? A PR is welcome if it's not too specific.

from webpack-dev-middleware.

axelpale avatar axelpale commented on May 1, 2024

Thanks @sunyang713 for sharing your solution. I stumbled upon this inconvenient issue myself too with a pretty similar tech stack. In my case, I have a single-page app (SPA) with client-side routing and URLs like /login, /invite, /item/123, and the root / of course. To provide a boring use case, if a user bookmarks the /item/123 and browses to it later on, the URL needs to work. The express server should response the request /item/123 with index.html in a manner identical to the requests /, /login, and /invite. Their routing is then made on the client.

However, in prod env this is easy as you showed but not so in dev env where I use the webpack-dev-middleware. The server-side code should have a file access to the webpacked index.html, then to be able to serve it for any valid URL the user throws at the server.

Fortunately in my case the solution was simple. Because my index.html is completely static in the first place, I am able to serve it without messing with webpack's temporary files. I lose the benefits of html uglification and other webpack feats but in dev environment that is ok. For a reference, the server-side catch-all route for the SPA then becomes:

app.get('/*', function (req, res) {
  res.sendFile(path.resolve(__dirname, 'public', 'index.html');
});

I hope this comment to expand the perspective and further describe the issue.

from webpack-dev-middleware.

anthonator avatar anthonator commented on May 1, 2024

@eduardoleal were you able to resolve this?

from webpack-dev-middleware.

anthonator avatar anthonator commented on May 1, 2024

Oops, you need to pass historyApiFallback in as a function.

@SpaceK33z's example is correct.

from webpack-dev-middleware.

eduardoleal avatar eduardoleal commented on May 1, 2024

Yes, @anthonator!
I'm using the following code:

/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
import 'babel-polyfill';
import express from 'express';
import path from 'path';
import webpackMiddleware from 'webpack-dev-middleware';
import webpack from 'webpack';
import prerender from 'prerender-node';
import webpackConfig from './webpack.config';

const app = express();

if (process.env.NODE_ENV !== 'production') {
  app.use(webpackMiddleware(webpack(webpackConfig)));
} else {
  app.use(prerender);
  app.use(express.static('build'));
  app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname, 'build/index.html'));
  });
}

app.listen(process.env.PORT || 10090, () => {
  console.log('Listening...')
});

from webpack-dev-middleware.

shellscape avatar shellscape commented on May 1, 2024

Closing as there seems to be a good resolution here. If anyone would like to add this to the documentation, please open a PR 😄

from webpack-dev-middleware.

robbyemmert avatar robbyemmert commented on May 1, 2024

Also, could there be performance concerns with adding webpack twice?

from webpack-dev-middleware.

shellscape avatar shellscape commented on May 1, 2024

@robbyemmert it looks like you may be new to Github, so please do note that it's a best practice to edit a previous comment to add more info, rather than posting a comment shortly after a previously posted comment.

I'm not sure you're fully understanding the solutions provided in this thread, as neither of the latest examples "add webpack" . Regardless, there's a new feature in writeToDisk you may want to take a look at. You can find more information in the README.

from webpack-dev-middleware.

Related Issues (20)

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.