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 · 126 comments
Open

Use a HTML file as an entry point? #536

jampy opened this issue Oct 14, 2014 · 126 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

This comment has been minimized.

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

This comment has been minimized.

Copy link

@nelix nelix commented Oct 19, 2014

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

@jampy

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Member

@jhnns jhnns commented Jan 12, 2015

Related discussion #220

@mako-taco

This comment has been minimized.

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

This comment has been minimized.

Copy link
Contributor

@kurtharriger kurtharriger commented Feb 3, 2015

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

@mogelbrod

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Contributor

@wmertens wmertens commented Jun 11, 2015

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

@mogelbrod

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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)

@Bnaya

This comment has been minimized.

Copy link

@Bnaya Bnaya commented Jan 13, 2017

That would be extremely powerful if one day you will be able to give webpack index.html as entry, or even app manifest.json, declare the target platform(s), output directory, and he will do all the rest with sane defaults.
My feelings are that the day is getting closer :)

@resistdesign

This comment has been minimized.

Copy link

@resistdesign resistdesign commented Jan 14, 2017

@Bnaya That is the goal of this CLI http://rdx.resist.design

@pribilinskiy

This comment has been minimized.

Copy link

@pribilinskiy pribilinskiy commented Feb 3, 2017

@resistdesign why it's called rdx? It's even hard to spell )

Would be nice to see some alternatives in this direction.

@nixpulvis

This comment has been minimized.

Copy link

@nixpulvis nixpulvis commented Feb 3, 2017

I dont see how rdx is hard to spell... i also dont have any other opinions of the tool itself.

@pribilinskiy

This comment has been minimized.

Copy link

@pribilinskiy pribilinskiy commented Feb 5, 2017

it's are-dee-ex?

@resistdesign

This comment has been minimized.

Copy link

@resistdesign resistdesign commented Feb 5, 2017

@pribilinskiy It's R.D.X. as in Resist Design X. X as in cross/trans-piler. As far as alternatives, what kinds of features do you need?

@pribilinskiy

This comment has been minimized.

Copy link

@pribilinskiy pribilinskiy commented Feb 6, 2017

Well, it's not about features, it's about documentation. There's a repo with example, but no explanation on API, how it should work, possibilities with examples, limitations, plans, changelog, etc. Currently it looks like an alpha-version, an experiment.

@resistdesign

This comment has been minimized.

Copy link

@resistdesign resistdesign commented Feb 7, 2017

@pribilinskiy Well, it's a CLI so it doesn't have an API per se. So the Usage, Commands, Configuration, Supported Features and Technologies are documented instead. And how it works is simple: Make an HTML page with scripts and it either serves or outputs an app for you. Here is the change log: https://github.com/resistdesign/rdx/commits/master . And as far as plans, it's being maintained and issues are addressed as best as possible. :)

@monokrome

This comment has been minimized.

Copy link

@monokrome monokrome commented Feb 27, 2017

@resistdesign While rdx may be cool, it's definitely not the same as having this working in the webpack ecosystem.

@resistdesign

This comment has been minimized.

Copy link

@resistdesign resistdesign commented Feb 28, 2017

@monokrome I totally agree.

@gwuhaolin

This comment has been minimized.

Copy link

@gwuhaolin gwuhaolin commented Mar 7, 2017

see this:

web-webpack-plugin

A good alternatives for html-webpack-plugin, can make webpack use HTML as entry.

use html template demo

webpack config

module.exports = {
    entry: {
        A: './a',
        B: './b',
    },
    plugins: [
        new WebPlugin({
            filename: 'index.html',
            // html template file path(full path relative to webpack.config.js)
            template: './template.html',
            requires: ['A', 'B'],
        }),
    ]
};

html template

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <!--load a chunk file config and output in webpack-->
    <script src="B"></script>
    <!--load a local reset style file direct without local var webpack-->
    <link rel="stylesheet" href="./reset.min.css?_inline">
    <!--load a local google analyze file direct without local var webpack-->
    <script src="./google-analyze.js"></script>
</head>
<body>
<!--SCRIPT-->
<footer>web-webpack-plugin</footer>
</body>
</html>
  • use <script src="B"></script> in html template to load required entry, the B in src="B" means entry name config in webpack.config.js
  • comment <!--SCRIPT--> means a inject position ,except for resource load by <script src></script> left required resource config in WebPlugin's requires option. if there has no <!--SCRIPT--> in html template left required script will be inject ad end of body tag.

output html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <!--load a chunk file config and output in webpack-->
    <script src="B.js"></script>
    <!--load a local reset style file direct without local var webpack-->
    <style>body {
        background-color: rebeccapurple;
    }</style>
    <!--load a local google analyze file direct without local var webpack-->
    <script src="google-analyze.js"></script>
</head>
<body>
<script src="A.js"></script>
<footer>web-webpack-plugin</footer>

</body>
</html>

config resource attribute demo

every resource required by html,it can config some attribute as below:

  • _dist only load in production environment
  • _dev only load in dev environment
  • _inline inline resource content info html,inline script and css
  • _ie resource only required IE browser,to achieve by [if IE]>resource<![endif] comment

html template

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <script src="inline?_inline"></script>
    <script src="ie-polyfill?_ie"></script>
</head>
<body>
<script src="dev?_dev"></script>
<!--load a local google analyze file direct without local var webpack-->
<script async src="./google-analytics.js?_dist"></script>
</body>
</html>

output html file

@japgolly

This comment has been minimized.

Copy link

@japgolly japgolly commented Apr 4, 2017

You might want to check out webtamp.
I'm the author (hi!) and I created it to do exactly this kind of thing.

@fstanis

This comment has been minimized.

Copy link

@fstanis fstanis commented Oct 16, 2017

I know there wasn't any activity here in a while, but since this issue ranks highly on Google, I wanted to share my solution for an HTML entry:

https://gist.github.com/fstanis/773110b12d91f42d3c3a22b71cbf6c42

It relies on spawn-loader to load the JS files and ExtractTextPlugin to allow an HTML output.

@GrosSacASac

This comment has been minimized.

Copy link

@GrosSacASac GrosSacASac commented Dec 15, 2017

also have a look at https://parceljs.org/ to use html file as entry point , as of now it is not mature enough: that is the only problem

@pribilinskiy

This comment has been minimized.

Copy link

@pribilinskiy pribilinskiy commented Dec 16, 2017

I suggest to look at POI and there's even a small course at egghead.io

@notlmn

This comment has been minimized.

Copy link

@notlmn notlmn commented Jun 25, 2018

Related issue by me, opened without knowing this issue existed!

@lakshmivelakaturi

This comment was marked as spam.

Copy link

@lakshmivelakaturi lakshmivelakaturi commented Dec 11, 2018

For each module, the required path should be specified means like,
capture

Account-->Type of Account-->Creaeate accounts

@panlina

This comment has been minimized.

Copy link

@panlina panlina commented Dec 29, 2018

Totally agree with @notlmn on #7589. Is this under consideration of webpack team?

@monokrome

This comment has been minimized.

Copy link

@monokrome monokrome commented Feb 6, 2019

Yo! Any progress here? Any documentation on how this may look? Does it already work? Sup ❤️

@evilebottnawi

This comment has been minimized.

Copy link
Member

@evilebottnawi evilebottnawi commented Feb 7, 2019

Yes, it is will be implement in future, a lot of work right now (full pipeline fro html, same for css)

@glen-84

This comment has been minimized.

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.