Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a HTML file as an entry point? #536

Open
jampy opened this issue Oct 14, 2014 · 143 comments
Open

Use a HTML file as an entry point? #536

jampy opened this issue Oct 14, 2014 · 143 comments

Comments

@jampy
Copy link

@jampy jampy commented Oct 14, 2014

I'm trying to figure out what's the best way to make webpack aware of the main HTML file (for a single page app).

AFAIK i have these options:

specify an .js file as entry point

That JS file needs to have require("./index.html"); so that the HTML file is identified as an dependency and included in the build.

To support that I have the following Webpack configuration:

loaders : {
  { test: /\.html/, loader: 'file?name=[name].[ext]' }
}

Problems:

  • Seems awkward to me to define the HTML file as a dependency for a JS file (instead of the other way round)
  • Additional dependencies in the HTML code (like images) are not being identified.

specify the .html file as entry point and using file-loader for HTML files

Problems:

  • Dependencies aren't being detected and thus no JavaScript code is bundled.

specify the .html file as entry point and using html-loader for HTML files

See sample config here: https://gist.github.com/jampy/44faf0c18fd64b6dd1fd
..and the HTML file: https://gist.github.com/jampy/a2dd493901cd6dc5ae8b

Problems:

  • The html-loader apparently detects dependencies like images, but seems to ignore the .js files referenced via <script> tags.
  • The .html file itself is converted to JavaScript and included in the .js file, which is useless.

What's correct?

What am I supposed to do to let webpack handle the HTML file and it's dependencies (including JavaScript).

@sokra
Copy link
Member

@sokra sokra commented Oct 17, 2014

Correct is to split your application into two parts:

  • client-side javascript (target: "web")
  • (pre)rending on server-side (target: "node") (this generates the HTML)

You can do this by exporting an array of configurations from the webpack.config.js.

For a simple static website you can do this:

module.exports = [
  {
    name: "client",
    target: "web",
    /* your client side configuration */
  },
  {
    name: "rendering",
    target: "node",
    entry: {
      "index.html": "./app/index.html",
    },
    output: {
      path: path.resolve("build")
      filename: "rendering/[name].js"
    }
  }
]
webpack && node -e "console.log(require('./build/render/index.html.js'))" > build/public/index.html

Maybe the script part can be moved into a webpack plugin. This would be more usable and dev-server combatible...

@nelix
Copy link

@nelix nelix commented Oct 19, 2014

You could try this plugin https://www.npmjs.org/package/html-webpack-plugin

@jampy
Copy link
Author

@jampy jampy commented Oct 20, 2014

Thanks. I forgot to mention that in the first post.

Still, that's similar to file-loader, meaning that additional dependencies in the HTML file (like images) aren't bundled (when using a custom template).

@randymorris
Copy link

@randymorris randymorris commented Jan 10, 2015

@jampy did you ever come up with an acceptable solution? I'm trying to wrap my head around this as well.

@jhnns
Copy link
Member

@jhnns jhnns commented Jan 11, 2015

I think this would require a change in the html-loader. It needed to turn this html:

<script src="./some/module.js"></script>

into

module.exports = "<script src=\"" + __webpack_public_path__ +
    JSON.stringify(urlToNewChunk) + "\"></script>";

while emitting a new chunk to the output folder with ./some/module.js as entry point.

@sokra Is that possible?

@kurtharriger
Copy link
Contributor

@kurtharriger kurtharriger commented Jan 12, 2015

While webpack is great at generating javascript if you want to use hashes for caching you need to do post build processing to update the html. It seems like this should be easier.
Having the html-loader be the entry point rather than the javascript makes a lot more sense to me for brower targets, since ultimately the entry point from a browser is as an html page.
It seems like generating processed html would make it easy to link to cachable resources and maybe even automatically apply extract text plugin for referanced css and such.
I tried using the html-webpack-plugin with some success. It doesnt seem to hot reload in the dev server, but I really like the idea of including l javascript source files straight from html and have webpack update the links automatically!

@jhnns
Copy link
Member

@jhnns jhnns commented Jan 12, 2015

Related discussion #220

@mako-taco
Copy link

@mako-taco mako-taco commented Feb 3, 2015

I am running in to the same issue, where I'd like to inline scripts into an HTML entry point. As @jhnns brought up, html-loader could be modified to produce output that looks like

module.exports = "<script src=\"" + __webpack_public_path__ +
    JSON.stringify(urlToNewChunk) + "\"></script>";

It is, however, wrapped in a webpackJsonp function. For my purposes, i'd like to get the output of this as plain HTML (This is for an iframe which gets requested 80,000 times a second, so it's important to not have the iframe link to external resources). Changing around the html loader's output is easy enough, but then I am still left with the resulting webpackJsonP function wrapping my output...

Does anyone know how to prevent that so I can output plain HTML?

@kurtharriger
Copy link
Contributor

@kurtharriger kurtharriger commented Feb 3, 2015

Might be able to use and/or copy bits from extract-text-webpack-plugin

@mogelbrod
Copy link

@mogelbrod mogelbrod commented Apr 6, 2015

Since HTML files are the entry points for browsers being able to do this just seems logical.
I'm experimenting with different ways of doing this, and have so far been able to compile a jade file (html like) entry point, with some issues.

webpack.config.ls

require! <[ webpack path ]>
ExtractTextPlugin = require "extract-text-webpack-plugin"

# Webpack plugins
css-extractor  = new ExtractTextPlugin "css", "[name].[id].css"
html-extractor = new ExtractTextPlugin "html", "[name].html"

module.exports = do
  output:
    path: path.join __dirname, "build"
    public-path: "/assets"
    filename: "[name].js"
    chunk-filename: "[name].id.js"

  entry:
    index: "./client/index.jade"

  devtool: "source-map"

  plugins: [css-extractor, html-extractor]

  resolve:
    extensions: ['', ".js", ".ls", ".css", ".styl", ".html", ".jade"]

  module:
    loaders:
      * test: /\.ls$/
        loader: 'livescript'
      * test: /\.styl$/
        loader: css-extractor.extract "style", "css!stylus"
      * test: /\.jade$/
        loader: html-extractor.extract "html", "apply!jade"

Gist for apply loader

Current problems:

  • Compiled html includes //# sourceMappingURL=index.html.map due to devtool option. Haven't found a way to disable this without modifying the ExtractTextPlugin. It's inserted by the Webpack EvalDevToolModulePlugin, which is injected before any loader code is executed.
  • Can't figure out how to require() scripts in index.jade:
    • script(src=require('./common.ls')) inserts src="[Object object]"
    • script(src=require('file?name=[name]-[hash].js!./common.ls')) correctly inserts src="/assets/common-938e5a0f3a70d579b6d485dbb56f5fa9.js", but require()s inside it doesn't seem to be resolved.

@AndrewRayCode
Copy link

@AndrewRayCode AndrewRayCode commented Apr 8, 2015

Since HTML files require scripts, css files, etc that are built by webpack, there needs to be a way to manage HTML files this way with webpack. There should be a way to treat <script src="../local/script.js"></script> as any other webpack require where it inlines the hashed name of that file and builds it to an html file.

This plugin: https://github.com/skozin/webpack-path-rewriter comes close, except it doesn't reload when you change files and webpack is watching (so your html doesn't update until you entirely re-run webpack), and the way you require entry point files is by having to prefix them with something-[hash] and then in the html file doing <script src="[[something-*]]"></script> which is less than ideal.

The HTML file a likely candidate for a true entry point to an application, so it should be easy for it to access the dependency graph.

@tcurdt
Copy link

@tcurdt tcurdt commented May 4, 2015

I've spent a whole afternoon on this until I realized this all breaks down with the current webpack-dever-server implementation generating the html.

@sokra I am willing to work on this if you could provide some guidance. This should really be easier.

@screendriver
Copy link

@screendriver screendriver commented May 13, 2015

Same here. At the moment my build/ directory contains only a bundle.js file but not my index.html.
Fortunately there is a plugin for this: html-webpack-plugin. But why we have to use a plugin? Is there a different approach in webpack I am missing?

@jhnns
Copy link
Member

@jhnns jhnns commented May 15, 2015

This is not as trivial as you think, because it breaks one major assumption in webpack: Every "webmodule" can be represented as JavaScript.

Sure, it is possible, to represent HTML as JavaScript. The html-loader does that, it turns something like

<img src="./some-image.jpg">

into

module.exports = "<img src=\"" + require("./some-image.jpg") + "\">";

which can be interpreted by webpack easily. It's another case with index.html. It must stay HTML.

That's why the extract-text-webpack-plugin or the html-webpack-plugin is probably the right approach.

@screendriver
Copy link

@screendriver screendriver commented May 17, 2015

Thank you for clarifying 👍
Could you add a description or a example or something to the webpack website that explains the wohle thing? For beginners it's a little bit confusing.

@tony-kerz
Copy link

@tony-kerz tony-kerz commented Jun 8, 2015

+1 on a little info around this in the documentation, i just burned an hour or two trying to grok this before stumbling across this thread...

@wmertens
Copy link
Contributor

@wmertens wmertens commented Jun 11, 2015

I had the same idea, using index.html as the entry point for the app is the most logical. It would also allow changing some URLs to CDN URLs and basically do everything that grunt/gulp does, but dependency-driven.

@mogelbrod I think you need to do "file!js?path/to/app.js" inside the html so that it parses the js as js first, resolving all require calls. It would be cool if you could specify sub-loaders, meaning a loader configuration that only applies for files that are matched in the regular loader configuration.

Then the only problem is getting an actual index.html file out of it, I'm looking at extract-text-webpack-plugin now. Alternatively I could make an entry.js file that simply requires the index.html file.

@wmertens
Copy link
Contributor

@wmertens wmertens commented Jun 11, 2015

@mogelbrod what is that apply loader you use in the html loader configuration?

@mogelbrod
Copy link

@mogelbrod mogelbrod commented Jun 11, 2015

@wmertens Gist for a simple apply loader
Looking forward to reading about any findings you do! =)

@wmertens
Copy link
Contributor

@wmertens wmertens commented Jun 11, 2015

@mogelbrod I ended up not needing it, I used your text extractor configuration and configured my main script to be written to file after converting. Note that the html loader doesn't support a loader configuration in the source attributes so I had to do it in the webpack configuration.

This setup almost works, but the included script doesn't have the webpack bootstrap code 😢. So the html text extractor should in fact replace all to-be-bundled script tags with a single script tag that loads the bundle js and then requires all the removed scripts in the same order.

Right now, my resulting index.html has <script src="a66188bc09c9607710dbfa1c54f85a98.coffee"> and the bundled "index.html" entry point is eval("// removed by extract-text-webpack-plugin\n\n/*****************\n ** WEBPACK FOOTER\n ** ./client/index.html\n ** module id = 3\n ** module chunks = 0\n **/\n//# sourceURL=webpack:///./client/index.html?");. The script is simply the webpackified script.

So it's feasible by combining the html loader and text extractor plugin, provided the loader knows what the js bundle path will be and the text extractor can leave some require statements in place. Then when visiting index.html the resulting bundle js will be loaded, executed, require the index.html entry point which loads the desired scripts.

It would be really cool if this worked, because it opens the door for lots of html, script and image processing using minimal configuration.

My setup:

index.html:

<!DOCTYPE html>
<html lang=en>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <link href='//fonts.googleapis.com/css?family=Roboto:500,300,400' rel='stylesheet' type='text/css'>
  <body>
    <div id="app">
        <p>LOADING&hellip;</p>
    </div>
    <script type="text/javascript" src="./main.coffee" charset="utf-8"></script>
  </body>
</html>

webpack.config.coffee:

webpack = require('webpack')
ExtractTextPlugin = require "extract-text-webpack-plugin"

# Webpack plugins
cssExtractor  = new ExtractTextPlugin "css", "[name].[id].css"
htmlExtractor = new ExtractTextPlugin "html", "[name].html"

isProd = (process.env.NODE_ENV is "production")
console.log "Configuring webpack for #{if isProd then "production" else "dev"}"

entries = if isProd then [] else [
    'webpack-dev-server/client?http://0.0.0.0:8080'
    'webpack/hot/only-dev-server'
]

module.exports =
  # use eval-source-map if you want coffeescript in the browser dev tools
  devtool: if not isProd then 'eval' # 'eval-source-map'
  devServer:
    # allow everyone in
    host: '0.0.0.0'
  entry: app: entries.concat [
    # './client/main.coffee'
    './client/index.html'
  ]
  output:
    pathinfo: true
    path: './build'
    filename: 'bundle-[name].js'
    "chunk-filename": "[name].id.js"
  plugins: [cssExtractor, htmlExtractor]
  resolve:
    modulesDirectories: [ 'node_modules', 'client']
    # List of automatically tested extensions
    extensions: [
      ''
      '.js'
      '.json'
      '.cson'
      '.coffee'
    ]
  module: loaders: [
    {
      test: /\.jsx$/
      loader: 'react-hot!jsx?harmony'
      # exclude: /node_modules/
    }
    {
      test: /\.coffee$/
      loader: 'react-hot!coffee!cjsx'
      exclude: /node_modules/
    }
    {
      test: /main\.coffee$/
      loader: 'file!react-hot!coffee!cjsx'
      exclude: /node_modules/
    }
    {
      test: /\.json$/
      loader: 'json'
    }
    {
      test: /\.cson$/
      loader: 'cson'
    }
    {
      test: /\.css$/
      loader: 'style!css'
    }
    {
      test: /\.less$/
      loader: 'style!css!less'
    }
    {
      test: /\.html$/
      loader: htmlExtractor.extract 'html?attrs=script:src'
      #  'file?name=[path][name].[ext]&context=./client!html?attrs=script:src'
    }
    {
      # Just reference all the rest
      test: /\.(png|otf|eot|svg|ttf|woff2?)(\?.*)?$/
      loader: 'url?limit=8192&name=[path][name].[ext]&context=./client'
    }
  ]

@wmertens
Copy link
Contributor

@wmertens wmertens commented Jun 12, 2015

So as a TL;DR: You cannot use the HTML as an entry point right now.

To do it, there needs to be a loader+plugin, performing these steps:

  • loader: like html loader
    • create javascript code that emits the html as a string
    • convert resource tags into require calls
      • script tags should be stripped and replaced with a single placeholder script tag loading the bundle
        • the html loader does not do this
      • other resources should be forced to pass through the file loader, or optionally converted to inline (e.g. font css)
        • the html loader does not do this
      • optionally: leave third-party scripts alone or change their URL (e.g. libraries on CDN) or inline them (e.g. google font loader), as long as they don't use require.
      • obviously this allows transforming less, sass, coffeescript, images, ...
  • plugin: like extract-text plugin
    • Replace the placeholder script tag src with the path to the relevant bundle file (possibly chunked)
      • the extract-text plugin does not do this
    • Emit the HTML as a file and in the module source require() all the script resources
      • the extract-text plugin removes everything from the module source

The webpack configuration would only require setting up the plugin since that can set up the loader.

I don't feel up to making this but I would be super grateful if someone would. This is IMHO the missing functionality to completely replace gulp/grunt (npm run allows running scripts).

@mogelbrod
Copy link

@mogelbrod mogelbrod commented Jun 12, 2015

Nice findings @wmertens! I'm not yet familiar enough with Webpack to implement what you're suggesting, but totally agree on your last point and am very interested in any further progress on this issue.

@wmertens
Copy link
Contributor

@wmertens wmertens commented Jun 18, 2015

Extra puzzle piece: In a plugin you can access the filenames of chunks, see https://github.com/sporto/assets-webpack-plugin/blob/master/index.js#L62

@wmertens
Copy link
Contributor

@wmertens wmertens commented Jun 21, 2015

Ideally you'd be able to do something like:

  • assets/index.html:

    <!DOCTYPE html>
    <html>
    <!-- browser assets -->
    <link rel="apple-touch-icon" sizes="114x114" href="apple-touch-icon-114.png">
    <meta name="msapplication-TileImage" content="windows-tile.png">
    <link rel="icon" sizes="any" mask href="favicon.svg">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <link href="../src/style.css" media="all" rel="stylesheet" />
    
    The content
    
    <script src="../src/app.js"></script>
    </html>
  • src/style.css:

    /* special entry that text-extract-plugin should place extracted CSS in */
    /* text-extract-plugin "style.css" begin */
    /* text-extract-plugin "style.css" end */
  • src/app.js:

    // Your app

and then in webpack you make the entry be "file?name=index.html!val!html!assets/index.html", which would result in a html like:

<!DOCTYPE html>
<html>
    <!-- browser assets -->
    <link rel="apple-touch-icon" sizes="114x114" href="hash1.png">
    <meta name="msapplication-TileImage" content="hash2.png">
    <link rel="icon" sizes="any" mask href="hash3.svg">
    <link rel="icon" type="image/x-icon" href="hash4.ico">
    <link href="hash5.css" media="all" rel="stylesheet" />

    The content

    <script src="hash6.js"></script>
</html>

and the hash6.js should be a bundle with the webpack preamble. Then you can copy the entire build folder onto your web server (index.html last) and the hashes will make sure browsers get the correct version of everything, no caching issues.

The html loader should allow processing urls so you can CDNify them, inline them, ...

@sokra is this feasible? Any pointers on how to implement?

@jiangfengming
Copy link

@jiangfengming jiangfengming commented Jun 25, 2015

Though not very convenient, you can do it without a plugin.

It doesn't work with UglifyJsPlugin. I had to add extract-text plugin back..

Here is my build script:

process.chdir(__dirname);
var fs = require('fs');
var util = require('util');
var path = require('path');
var Getopt = require('node-getopt');
var webpack = require('webpack');
var crypto = require('crypto');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var child_process = require('child_process');

var opt = new Getopt([
  ['c', 'config=CONFIG', 'Specify the config file name in the conf folder. Default: default'],
  ['r', 'run=COMMAND', 'The command to run after the compilation is complete.'],
  ['h', 'help', 'Display this help.']
]).bindHelp().parseSystem();

var conf = require('./conf/' + (opt.options.config || 'default'));

var webpackOpts = {
  resolve: {
    root: [path.join(__dirname, 'bower_components')],
  },

  entry: {
    'app.html': './app.html',
    app: './app.js',
    vendor: './vendor.js'
  },

  output: {
    path: './dist',
    publicPath: conf.resource + 'dist/',
    filename: '[name].js?[chunkhash]',
    chunkFilename: '[name].js?[chunkhash]',
    sourceMapFilename: '[file].map'
  },

  module: {
    loaders: [
      { test: /app\.html$/, loader: ExtractTextPlugin.extract('html?attrs=link:href') },
      { test: /favicon\.png$/, loader: 'file' },
      { test: /\.css$/, loader: 'style!css' },
      { test: /\.styl$/, loader: 'style!css!stylus' },
      { test: /\.html$/, exclude: /app\.html$/, loader: 'html' },
      { test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/, exclude: /favicon\.png$/, loader: 'url?limit=10000' }
    ]
  },

  stylus: {
    use: [require('nib')()],
    import: ['nib']
  },

  devtool: 'source-map',
  debug: conf.debug,

  plugins: [
    new ExtractTextPlugin('app.html'),

    new webpack.DefinePlugin({
      __CONF__: JSON.stringify(conf)
    }),

    new webpack.ResolverPlugin(
        new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main'])
    ),

    new webpack.optimize.OccurenceOrderPlugin(),

    // long-term caching
    // http://webpack.github.io/docs/long-term-caching.html
    function() {
      this.plugin('done', function(stats) {
        var chunks = stats.toJson().assetsByChunkName;

        // replace the js file path of app.html
        appHtml = fs.readFileSync('./dist/app.html', { encoding: 'utf8' });
        for (var entry in chunks) {
          var src = entry + '.js';

          // Code splitting and chunkhash problem
          // https://github.com/webpack/webpack/issues/1209
          chunkhash = crypto.createHash('md5').update(fs.readFileSync('./dist/' + src)).digest('hex');
          var dest = conf.resource + 'dist/' + src + '?' + chunkhash;
          appHtml = appHtml.replace(src, dest);
        }
        fs.writeFileSync('./dist/app.html', appHtml);
      });
    }
  ]
};

if (!conf.debug)
  webpackOpts.plugins.push(new webpack.optimize.UglifyJsPlugin());

var compiler = webpack(webpackOpts);

if (conf.debug) {
  compiler.watch(null, compilerCb);
} else {
  // Bug: UglifyJsPlugin will compile <input type="text" requred="{{foo}}"> to <input type="text" required>
  // https://github.com/webpack/webpack/issues/752
  // Webpack 2.0 will fix this issue
  compiler.plugin("compilation", function(compilation) {
    compilation.plugin("normal-module-loader", function(context) {
      context.minimize = false;
    });
  });

  compiler.run(compilerCb);
}

function compilerCb(err, stats) {
  if (err)
    return console.error(err);

  var jsonStats = stats.toJson();

  if (jsonStats.errors.length > 0) {
    jsonStats.errors.forEach(function(err) {
      console.error(err);
    });
    return;
  }

  if (jsonStats.warnings.length > 0) {
    jsonStats.warnings.forEach(function(err) {
      console.error(err);
    });
  }

  jsonStats.modules.forEach(function(module) {
    console.log(module.name);
  });

  if (opt.options.run) {
    child_process.execSync(opt.options.run, {
      stdio: 'inherit'
    });
  }

  console.log(new Date().toLocaleString());
}

conf/default.js:

module.exports = {
  base: '/',
  resource: '//app-dev-res.example.com/',
  api: 'https://app-dev-api.example.com/',
  debug: true
};

app.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title v-text="title"></title>
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
    <link rel="icon" type="image/png" href="favicon.png">
  </head>

  <body>
    <script src="vendor.js"></script>
    <script src="app.js"></script>
  </body>
</html>

run:

node build -r "rsync -rptz --exclude .git* --exclude .DS_Store ./ USER@app-dev.example.com:/home/USER/app-frontend"

@wmertens
Copy link
Contributor

@wmertens wmertens commented Jun 25, 2015

looks to me like you wrote a plugin :)

It doesn't handle my entire wishlist (coalesce script tags etc) but it does
make the js point to hashed files, which is great.

I just don't understand how come the resulting chunks can load, do all
chunks include the webpack loader stub? Is it because they are entries?

Did you try adding the script:src tags to the things html should parse? Do
those then not include the stub?

Why do you name the files with the URL query syntax and not simply
file-hash.js?

Good stuff, thanks!

On Thu, Jun 25, 2015 at 8:41 AM Fenix notifications@github.com wrote:

Though not very convenient, you can do it without a plugin. Here is my
build script:

process.chdir(__dirname);
var fs = require('fs');
var util = require('util');
var path = require('path');
var Getopt = require('node-getopt');
var webpack = require('webpack');
var crypto = require('crypto');
var child_process = require('child_process');

var opt = new Getopt([
['c', 'config=CONFIG', 'Specify the config file name in the conf folder. Default: default'],
['r', 'run=COMMAND', 'The command to run after the compilation is complete.'],
['h', 'help', 'Display this help.']
]).bindHelp().parseSystem();

var conf = require('./conf/' + (opt.options.config || 'default'));

var webpackOpts = {
resolve: {
root: [path.join(__dirname, 'bower_components')],
},

entry: {
'app.html': './app.html',
app: './app.js',
vendor: './vendor.js'
},

output: {
path: './dist',
publicPath: conf.resource + 'dist/',
filename: '[name].js?[chunkhash]',
chunkFilename: '[name].js?[chunkhash]',
sourceMapFilename: '[file].map'
},

module: {
loaders: [
{ test: /app.html$/, loader: 'html?attrs=link:href' },
{ test: /.css$/, loader: 'style!css' },
{ test: /.styl$/, loader: 'style!css!stylus' },
{ test: /.html$/, exclude: /app.html$/, loader: 'html' },
{ test: /.(png|jpg|jpeg|gif|ico|eot|ttf|woff|woff2|svg|svgz)(?.+)?$/, loader: 'url?limit=10000' }
]
},

stylus: {
use: [require('nib')()],
import: ['nib']
},

devtool: 'source-map',
debug: conf.debug,

plugins: [
new webpack.DefinePlugin({
CONF: JSON.stringify(conf)
}),

new webpack.ResolverPlugin(
    new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main'])
),

new webpack.optimize.OccurenceOrderPlugin(),

// long-term caching
// http://webpack.github.io/docs/long-term-caching.html
function() {
  this.plugin('done', function(stats) {
    var chunks = stats.toJson().assetsByChunkName;

    // replace the js file path of app.html
    appHtml = eval(fs.readFileSync('./dist/app.html.js', { encoding: 'utf8' }));
    for (var entry in chunks) {
      var src = entry + '.js';
      var dest = conf.resource + 'dist/' + chunks[entry][0];
      appHtml = appHtml.replace(src, dest);
    }
    fs.writeFileSync('./dist/app.html', appHtml);
  });
}

]
};

if (!conf.debug)
webpackOpts.plugins.push(new webpack.optimize.UglifyJsPlugin());

var compiler = webpack(webpackOpts);

if (conf.debug) {
compiler.watch(null, compilerCb);
} else {
// Bug: UglifyJsPlugin will compile to
// #752
// Webpack 2.0 will fix this issue
compiler.plugin("compilation", function(compilation) {
compilation.plugin("normal-module-loader", function(context) {
context.minimize = false;
});
});

compiler.run(compilerCb);
}

function compilerCb(err, stats) {
if (err)
return console.error(err);

var jsonStats = stats.toJson();

if (jsonStats.errors.length > 0) {
jsonStats.errors.forEach(function(err) {
console.error(err);
});
return;
}

if (jsonStats.warnings.length > 0) {
jsonStats.warnings.forEach(function(err) {
console.error(err);
});
}

jsonStats.modules.forEach(function(module) {
console.log(module.name);
});

if (opt.options.run) {
child_process.execSync(opt.options.run, {
stdio: 'inherit'
});
}

console.log(new Date().toLocaleString());
}

app.html:

<title v-text="title"></title> <script src="vendor.js"></script> <script src="app.js"></script>

run:

node build -r "rsync -rptz --exclude .git* --exclude .DS_Store ./ USER@app-dev.example.com:/home/USER/app-frontend"


Reply to this email directly or view it on GitHub
#536 (comment).

Wout.
(typed on mobile, excuse terseness)

@glen-84
Copy link

@glen-84 glen-84 commented Feb 27, 2019

It's now possible to vote for this feature @ https://webpack.js.org/vote (HTML as entrypoint).

@resistdesign
Copy link
Contributor

@resistdesign resistdesign commented May 30, 2020

Just dumped a few points on this in the vote.

If you're here and you see this, go vote!

image

See the link from @glen-84 ...

It's now possible to vote for this feature @ https://webpack.js.org/vote (HTML as entrypoint).

@chriscalo
Copy link

@chriscalo chriscalo commented May 30, 2020

Love the developer experience for this very problem in Vite. Here's what that looks like, adapted to .js files instead of .vue files:

index.html:

<!doctype html>
<html>
  <head>
    <title>html entry point FTW!</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module">
      import { init } from "./index.js";
      init(document.querySelector("#app"));
    </script>
  </body>
</html>

Similarly, Parcel has supported HTML entry points for a while. From their docs:

index.html:

<html>
<body>
  <script src="./index.js"></script>
</body>
</html>

From the Parcel CLI:

parcel index.html

It would be lovely if you could write .html files that use <script type="module"> or <script src="foo.js"> tags, point webpack at all HTML files (*.html), and everything just worked in the output.

@sokra
Copy link
Member

@sokra sokra commented May 31, 2020

Yes as @evilebottnawi said this is planned similar to parcel. If you think this should get higher priority, cast your vote on the voting page...

@monokrome
Copy link

@monokrome monokrome commented Jun 6, 2020

Where's this voting page at?

@sokra
Copy link
Member

@sokra sokra commented Jun 6, 2020

@chriscalo
Copy link

@chriscalo chriscalo commented Jun 6, 2020

Is this voting page documented anywhere? Any time I've tried to click the up arrows, nothing happens.

@resistdesign
Copy link
Contributor

@resistdesign resistdesign commented Jun 6, 2020

@chriscalo you have to sign in with GitHub to vote. You vote with your influence points.

@chriscalo
Copy link

@chriscalo chriscalo commented Jun 6, 2020

I am signed in because I see the up and down arrows. But when I click them nothing happens.

@kevincox
Copy link

@kevincox kevincox commented Jun 6, 2020

Can we please take this discussion elsewhere? It would good to keep this bug focused, it isn't the best place to discuss the voting system.

@trusktr
Copy link

@trusktr trusktr commented Jan 28, 2021

@pribilinskiy mentioned Poi (a "zero-config" wrapper on top of Webpack) in the above comment which apparently everyone skipped because it has zero thumbs-up.

Poi has a built-in html-entry-loader. Looks really easy to use or adapt. Maybe someone can publish it to NPM separately from Poi...

@Airkro
Copy link

@Airkro Airkro commented Jul 5, 2021

Using html-webpack-plugin needs too many plugins to handle custom tags inject, they have many mutex logic. and that list keep became longer.

I think vite is doing good about HTML handing. It proves HTML entry could really save the day.

@trusktr
Copy link

@trusktr trusktr commented Oct 9, 2021

@Mitscherlich published a new html-entry-loader on npm.

@Mitscherlich
Copy link

@Mitscherlich Mitscherlich commented Oct 9, 2021

@Mitscherlich published a new html-entry-loader on npm.

Yeap it's okay to consider html-entry-loader as a possible solution but not hurry to use it in production since it will only process <script> and <link> and may generate wrong html fragment while transforming.

html-entry-loader is a personal try-out and simply combined html-loader and html-webpack-plugin but add a little bit presume, which may not meet general propose. Any PR is welcome. 😄

@cmonti-bc
Copy link

@cmonti-bc cmonti-bc commented Mar 9, 2022

Darn I really need this to work :(

@chriscalo
Copy link

@chriscalo chriscalo commented Mar 9, 2022

(If it's an option for you, HTML entry points work in both Vite and Parcel)

@wmertens
Copy link
Contributor

@wmertens wmertens commented Mar 9, 2022

@cmonti-bc actually, having a server endpoint that outputs the HTML entry dynamically is very nice, it plays well with SSR and allows you to fine-tune every detail. It's also straightforward to cache so performance can be similar to static files.

@cmonti-bc
Copy link

@cmonti-bc cmonti-bc commented Mar 10, 2022

@cmonti-bc actually, having a server endpoint that outputs the HTML entry dynamically is very nice, it plays well with SSR and allows you to fine-tune every detail. It's also straightforward to cache so performance can be similar to static files.

Sorry but the project I'm working on cannot afford to have an endpoint to serve the html. Thanks anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: To be discussed
Development

No branches or pull requests