markdalgleish / static-site-generator-webpack-plugin

Minimal, unopinionated static site generator powered by webpack
MIT License
1.61k stars 97 forks source link

Problem with code splitting #10

Open ekosz opened 9 years ago

ekosz commented 9 years ago

In my code I have something that looks like this:

function retriveLocaleData(language, callback) {
  if (language === 'es') {
    require('bundle?name=spanish!../locales/es.js')(callback);
  } else {
    require('bundle?name=english!../locales/en.js')(callback);
  }
}

In other projects that would successfully create a separate bundle for my I18n data for that locale, but using this library I am constantly getting this error:

ReferenceError: window is not defined
    at evalmachine.<anonymous>:3:37
    at evalmachine.<anonymous>:94:10
    at ContextifyScript.Script.runInNewContext (vm.js:38:15)
    at module.exports ([snip]/node_modules/static-site-generator-webpack-plugin/node_modules/eval/eval.js:62:12)
    at Compiler.<anonymous> ([snip]/node_modules/static-site-generator-webpack-plugin/index.js:29:20)

I think I finally understand why it's happening. Webpack is adding code to the bundle that appends the second chunk to the dom under certain conditions. The code it generates is using window, which blows up while the static-site-generator-webpack-plugin is generating the HTML.

Has anyone encountered this before / can think of any solutions? It would be a real shame to lose the ability to code split while generating a static site.

VaclavSynacek commented 9 years ago

I don't believe these lines fail with the error listed. It fails somewhere else, where the window object, which is not available outside of browser, is actually used. Probably somewhere where the retrieveLocaleData function is called from. But anyway:

What locale should your static html files be in? If you can decide what the default is (and what all search engines and non-javascript browsers will only see), I would write something like this:

if (typeof document !== 'undefined') {
  // we are in browser
  // your original but failing client code goes here (not the one above, but the one that actually fails)
} else {
  // we are in static site generator / server
  // do not check for locale on the window object,
  // just require default locale
}

If done right, it just might work out in a way, that the static HTML files are in default language, but once they are loaded in a different locale browser and react initializes, it updates the DOM and translates everything automagically. Would be cool actually. Please share the results if you get there.

If you cannot decide on the default locale, than that's a different story. And one that does not seem so static to me. Theoretically it can be done with double generation of 2 sets of static HTML files, then apache/ngix/whatever-http-server configuration to serve from one or the other directory based on http headers. But that is out of my league. Suerte con esto. And also if you are going this dynamic server side route you might as well just drop the static-site-generator-webpack-pluging and do a dynamic express based web.

jamarparris commented 9 years ago

I'm also having this problem with code splitting. I'm using ReactRouter for routing. It does seems tied to the fact that whatever webpack does for code splitting does rely on a window object as the original posted mentioned. If I replace the require.ensure with a standard require (hence not making it dynamic anymore) it works as expected.

I'd also really love a code splitting solution as I'm using this for a blog and I want to be able to require individual blog entries on demand versus every entry being in a single bundle.js file.

Based on this output.js file at this webpack example, it seems like require.ensure does try perform functions on window...

https://github.com/webpack/webpack/tree/master/examples/code-splitting

My route config is below.

const routes = {
  path: '/',
  component: Root,
  indexRoute: { component: Index },
  childRoutes: [
    {
      path: 'about',
      getComponent(location, cb) {
        require.ensure([], (require) => {
          cb(null, require('./components/About.jsx'))
        })
      }
    },
    {
      path: 'post',
      getComponent(location, cb) {
          require.ensure([], (require) => {
            cb(null, require('./components/Post.jsx'))
        })
      }
    }
  ]
}
eliknebel commented 8 years ago

I am having a similar issue. When running Webpack, I am getting ERROR in ReferenceError: window is not defined. I believe I've narrowed it down to an issue with trying to compile sass style sheets while using the plugin.

bencooling commented 8 years ago

I have the same issue as described by jamarparris using require.ensure with React Router getComponent.

bencooling commented 8 years ago

I tried replacing require.ensure with Webpack 2 npm i webpack@beta and System.import, but now I am getting:

ERROR in ReferenceError: System is not defined

static-site-generator-webpack-plugin works fine with Webpack 2 & no code-splitting, but soon as you introduce System.import, it throws the above error.

jonathaningram commented 8 years ago

I believe the OP got it right, here's my understanding of why this fails:

  1. You want to include something on demand. E.g. your index.js can look as simple as this:

    require(['./SimpleComponent'], function(Com) {
     console.log(Com)
    })

    See https://webpack.github.io/docs/code-splitting.html BTW.

  2. The static-site-generator-webpack-plugin module will evaluate your index.js in it's compilation stage.

    var source = asset.source();
    // console.log(source);
    var render = evaluate(source, /* filename: */ self.renderSrc, /* scope: */ undefined, /* includeGlobals: */ true);
  3. You'll try to compile your app, but you'll get the window is not defined issue but it will appear vague.

    ERROR in ReferenceError: window is not defined
    at Object.defineProperty.value (main:13:37)
    at main:621:10
    at webpackUniversalModuleDefinition (main:3:20)
    at main:10:3
    at ContextifyScript.Script.runInNewContext (vm.js:18:15)
  4. This is because it comes from the evaluated file that is created in step 2 above (main.js). If you enable the log statement in step 2 you can see the generated main.js.
  5. At line 12 of main.js you'll see this on-demand requiring magic that webpack adds:

    /******/    // install a JSONP callback for chunk loading
    /******/    var parentJsonpFunction = window["webpackJsonp"];
    /******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
  6. And this is where the error actually comes from.

Please somebody correct me if I'm wrong though!

As for solving this, I am not sure. @markdalgleish don't suppose you have any ideas how to solve this? I am happy to provide a simple repo to reproduce this if it helps.

bguiz commented 8 years ago

@jonathaningram I'm facing a very similar problem (see here)... did you have any luck resolving yours?

adrienharnay commented 8 years ago

Same problem with code splitting, works without... Anyone managed to solve it?

highruned commented 8 years ago

@jonathaningram That's exactly what I'm seeing

oxpa commented 8 years ago

https://gist.github.com/oxpa/09b3ef3c289272ced8c7eba77c8a1f88 This is my approach. At the moment I have to run webpack twice: once to get all .js files generated (without SSGplugin) , then to run StaticSiteGeneratorPlugin with .js in place.

some comments: getElementsByTagName - is used only to get head tag and the only function from it that is used: appendChild which gets 'script' instance, sets up webpack callbacks, sets React requirement I need and requires module

So my question is: how can I postpone plugin invocation till the moment all chunks are in place?

oxpa commented 8 years ago

hey, try #55

coldpour commented 5 years ago

this worked for me

module.exports = {
  ...,
  plugins: [
    new StaticSiteGeneratorPlugin({
      globals: {
        window: {}
      }
    })
  ]
}

as documented https://github.com/markdalgleish/static-site-generator-webpack-plugin#globals