Leon
Leon

Reputation: 1995

can javascript be inline with webpack?

I need to import a library to my project, the library should is a javascript file, which need to be inlined to html.

for example:

library code:

(function(){
   var a = 0;
})();

I need to make this code inline in html.

html:

<html>
  <head>
    <script>
      (function(){
         var a = 0;
      })();
    </script>
  </head>

  <body>
  </body>
</html>

can I implement this with webpack? I find script-loader, but it run the script, not make it inline.

Upvotes: 22

Views: 17261

Answers (4)

Redman
Redman

Reputation: 662

for webpack 4, I tried the following 3 plugins but no luck

  1. html-webpack-inline-plugin
  2. html-webpack-inline-source-plugin
  3. script-ext-html-webpack-plugin

then I found this react-dev-utils/InlineChunkHtmlPlugin so borrow it and modify to fit my needs:

// use it like this
            ...
            new HtmlWebpackPlugin(...),
            new MiniCssExtractPlugin(...),
            new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/[.]js$/, /[.]css$/]),
            ...

// modify InlineChunkHtmlPlugin to inline both script and style
class InlineChunkHtmlPlugin {
    constructor(htmlWebpackPlugin, tests) {
        this.htmlWebpackPlugin = htmlWebpackPlugin;
        this.tests = tests;
    }

    isScript(tag) {
        return tag.tagName === 'script' && tag.attributes && tag.attributes.src;
    }

    isCss(tag) {
        return tag.tagName === 'link' && tag.attributes && tag.attributes.href && tag.attributes.rel === "stylesheet";
    }

    getInlinedTag(publicPath, assets, tag) {
        //debugger;
        const isScript = this.isScript(tag);
        const isCss = this.isCss(tag);
        if (!isScript && !isCss) {
            return tag;
        }

        const href = isScript ? tag.attributes.src : tag.attributes.href;

        const scriptName = publicPath
            ? href.replace(publicPath, '')
            : href;
        if (!this.tests.some(test => scriptName.match(test))) {
            return tag;
        }
        const asset = assets[scriptName];
        if (asset == null) {
            return tag;
        }
        return { tagName: isScript ? 'script' : 'style', innerHTML: asset.source(), closeTag: true };
    }

    apply(compiler) {
        let publicPath = compiler.options.output.publicPath || '';
        if (publicPath && !publicPath.endsWith('/')) {
            publicPath += '/';
        }

        compiler.hooks.compilation.tap('InlineChunkHtmlPlugin', compilation => {
            const tagFunction = tag =>
                this.getInlinedTag(publicPath, compilation.assets, tag);

            const hooks = this.htmlWebpackPlugin.getHooks(compilation);
            hooks.alterAssetTagGroups.tap('InlineChunkHtmlPlugin', assets => {
                assets.headTags = assets.headTags.map(tagFunction);
                assets.bodyTags = assets.bodyTags.map(tagFunction);
            });

            // Still emit the runtime chunk for users who do not use our generated
            // index.html file.
            // hooks.afterEmit.tap('InlineChunkHtmlPlugin', () => {
            //   Object.keys(compilation.assets).forEach(assetName => {
            //     if (this.tests.some(test => assetName.match(test))) {
            //       delete compilation.assets[assetName];
            //     }
            //   });
            // });
        });
    }
}

Upvotes: 0

Dustin Jackson
Dustin Jackson

Reputation: 387

I've started working on a plugin for html webpack to support this. You specify regex to match files that you want to embed inline.

plugins: [
    new HtmlWebpackPlugin({
        inlineSource: '.(js|css)$' // embed all javascript and css inline
    }),
    new HtmlWebpackInlineSourcePlugin()
] 

Upvotes: 25

Leon
Leon

Reputation: 1995

At last, we solve this problem by combining webpack and gulp. (with two plugins: gulp-html-replace and gulp-inline-source.)

html:

 <html>
  <head>
    <!-- build:js -->
    <!-- endbuild -->
  </head>

  <body>
  </body>
</html>

gulpfile:

  gulp.task('replace-and-inline', function () {
      return gulp.src('./dist/index.html')
        .pipe(htmlreplace({
          'js': {
            src: [your libs which you want to be inline],
            tpl: '<script src="%s" inline></script>'
          }
        }))
        .pipe(inlinesource())
        .pipe(gulp.dest('./dist/'));
    });

In package.json, define a task, which will compile the project using webpack and then inject the js file as inline.

"build": "rimraf dist && webpack --progress --hide-modules --config build/webpack.prod.conf.js;gulp replace-and-inline"

When you want to release your project, just run npm run build


update on July.20 2018

we have made a webpack plugin to solve this issue.

https://github.com/QuellingBlade/html-webpack-inline-plugin

Upvotes: 7

Itay Radotzki
Itay Radotzki

Reputation: 857

I did it without gulp, with the help of inline-source:

  1. Install inline-source-cli: npm install inline-source-cli
  2. Add an inline attribute to the script tag in your html file: <script inline src="path/to/index.js"></script>
  3. Run inline-source --root ./dist dist/path/to/index-pre.html > dist/path/to/index.html

Upvotes: 8

Related Questions