preact-cli icon indicating copy to clipboard operation
preact-cli copied to clipboard

How to build preact in one index.html file?

Open bratukh opened this issue 4 years ago • 27 comments

Hey. I use preact to create interactive pages that are hosted in the memory of the microcontroller. The preact size and features are really impressive, but for my use case it is still overloaded.

What can I do so that when I run the npm run build command, I get 1 file - index.html, where will all the styles and scripts be inside?

I used to have a working version, but when updating webpack and preact, it turned out to be not working:

package.json "build": "preact build --no-prerender --no-service-worker"

preact.config.js

export default (config, env, helpers) => {
    const chunks = helpers.getPluginsByName(config, 'CommonsChunkPlugin')[0]
    config.plugins[chunks.index].minChunks = 1
}

Thanks!

bratukh avatar Dec 07 '19 19:12 bratukh

I used to have a working version, but when updating webpack and preact, it turned out to be not working

What's the error message your getting?

marvinhagemeister avatar Dec 08 '19 05:12 marvinhagemeister

Try replacing your preact.config.js with this:

export default (config, env, helpers) => {
    config.optimization.splitChunks.minChunks = 1;
}

developit avatar Dec 09 '19 19:12 developit

Should this issue be migrated to the preact-cli repo?

developit avatar Dec 09 '19 19:12 developit

Hey @BRAGA96 did you end up making any progress with this? I am in the exact situation using a microcontroller, and am also interested in condensing the build files into a single html file

bsecker avatar Jun 17 '20 12:06 bsecker

I made this but it was not so easy as a one liner

https://gist.github.com/sebasjm/087df4b0b0a611ce409fe30a67a1d0d9

Any improvement would be greatly appreciated.

sebasjm avatar Jun 29 '21 13:06 sebasjm

Any solution yet? @sebasjm's solution doesnt look like a good way of doing this

caxapexac avatar Dec 12 '21 10:12 caxapexac

I just need to inline scripts and styles, nothing more than that

caxapexac avatar Dec 12 '21 10:12 caxapexac

Tried to use react-dev-utils/InlineChunkHtmlPlugin and my preact.config.js looks like this:

import HtmlWebpackPlugin from "html-webpack-plugin";
import InlineChunkHtmlPlugin from "react-dev-utils/InlineChunkHtmlPlugin";

export default (config, env, helpers) => {
    config.plugins.push(
        new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/.+[.]js/])
    );
}

Build goes:

 0% (0.0s) compiling× ERROR TypeError: this.htmlWebpackPlugin.getHooks is not a function
at \node_modules\react-dev-utils\InlineChunkHtmlPlugin.js:43:44
at SyncHook.eval [as call] (eval at create (\node_modules\tapable\lib\HookCodeFactory.js:19:10), <anonymous>:27:1)
at SyncHook.lazyCompileHook (\node_modules\tapable\lib\Hook.js:154:20)
at Compiler.newCompilation (\node_modules\webpack\lib\Compiler.js:631:26)
at \node_modules\webpack\lib\Compiler.js:667:29
at AsyncSeriesHook.eval [as callAsync] (eval at create (\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:6:1)
at AsyncSeriesHook.lazyCompileHook (\node_modules\tapable\lib\Hook.js:154:20)
at Compiler.compile (\node_modules\webpack\lib\Compiler.js:662:28)
at \node_modules\webpack\lib\Compiler.js:321:11
at Compiler.readRecords (\node_modules\webpack\lib\Compiler.js:529:11)
at \node_modules\webpack\lib\Compiler.js:318:10
at AsyncSeriesHook.eval [as callAsync] (eval at create (\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:6:1)
at AsyncSeriesHook.lazyCompileHook (\node_modules\tapable\lib\Hook.js:154:20)
at \node_modules\webpack\lib\Compiler.js:315:19
at AsyncSeriesHook.eval [as callAsync] (eval at create (\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:15:1)
at AsyncSeriesHook.lazyCompileHook (\node_modules\tapable\lib\Hook.js:154:20)

caxapexac avatar Dec 13 '21 00:12 caxapexac

So https://github.com/jantimon/html-webpack-plugin/issues/1068 says 'getHooks is new in html-webpack-plugin 4.0.0.alpha-2'

and you have "html-webpack-plugin": "^3.2.0",

and I mentioned is #1629 that you can't install another version

What should I do?

caxapexac avatar Dec 13 '21 00:12 caxapexac

@caxapexac You installed the latest version of html-webpack-plugin which isn't compatible, yes. You can try an older version, and you may need to downgrade whatever utility that is as well.

Is there anything about the above solution that makes you think it is a poor way of going about this (assuming it works as you'd like it to)?

rschristian avatar Dec 13 '21 00:12 rschristian

@rschristian it uses grep, I use windows It uses something strange while webpack plugins just casually exist

caxapexac avatar Dec 13 '21 00:12 caxapexac

Nothing strange about a post-build script, and I'd argue that is the correct way to go about this. Way less fragile than depending on Webpack plugins, that depend on specific versions of other dependencies, Webpack features, and our configuration.

You could swap grep and sed out for a Node script, fs.readFile and whatnot, it'd be just as effective.

rschristian avatar Dec 13 '21 01:12 rschristian

[email protected] doesn't work either cuz of html-webpack-plugin (https://stackoverflow.com/a/60473584/9398364)

html-webpack-inline-source-plugin ^0.0.10 doesn't work cuz its for webpack4

caxapexac avatar Dec 13 '21 01:12 caxapexac

We are using Webpack 4

rschristian avatar Dec 13 '21 01:12 rschristian

https://github.com/DustinJackson/html-webpack-inline-source-plugin/issues/79 with 0.0.10

caxapexac avatar Dec 13 '21 01:12 caxapexac

Nothing strange about a post-build script, and I'd argue that is the correct way to go about this. Way less fragile than depending on Webpack plugins, that depend on specific versions of other dependencies, Webpack features, and our configuration.

@rschristian its just so essential task (bundling 3 files into 1 file) so writing regexp with string replace looks like writing array sorting by hand, whats wrong if I want to write 1 string inside webpack config instead of remembering sick syntax of regexp?

caxapexac avatar Dec 13 '21 01:12 caxapexac

Ouf this is building with "html-webpack-inline-source-plugin": "^0.0.10",:

import HtmlWebpackPlugin from "html-webpack-plugin";
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');

export default (config, env, helpers) => {
    config.plugins.push(new HtmlWebpackPlugin({
        inlineSource: '.(js|css)$'
    }))
    config.plugins.push(new HtmlWebpackInlineSourcePlugin())
}

caxapexac avatar Dec 13 '21 01:12 caxapexac

Nothing's wrong with with that, but the fact of the matter is that you have a lot more moving pieces with Webpack plugins. As you've already encountered, you have a reliance upon not only your plugin version, but webpack, html-webpack-plugin, and preact-cli versions. Scripts remain static. Grep, for comparison, has remained the same for decades, and that's why I'd recommend that over plugins which may or may not work in the future, especially when it's as small and isolated as what's needed here. That's just my opinion though, you're free to do whatever you choose of course.

This likely isn't something we're looking to support out-of-the-box, as it's quite a bad practice if you can avoid it, so you'll need to do a bit of stitching things together if you cannot find a webpack plugin that'll work with our current dependencies.

There is #1608 in the pipeline which will upgrade us to html-webpack-plugin v4, but there's no ETA on when that might be ready or released.

rschristian avatar Dec 13 '21 01:12 rschristian

Okay I've re-read https://github.com/preactjs/preact-cli/issues/923#issuecomment-870605603 and it seems legit, sorry for all this stuff Ive said.

So if I use this solution, to what should I replace 'something.woff' and 'base64-inline-loader' on the 15-20th line to not to break everything and have truly single-file bundle (I can have js, css, png, svg in the project)? (it is good to inline js as base64? How to inline as plain text?)

caxapexac avatar Dec 13 '21 02:12 caxapexac

@rschristian my pipeline is updated and now its legit to use multiple files in build. Whats the best command for build then? I use preact for rendering game overlay gui so no need for prerendering and other stuff:

I suupose its preact build --no-sw --no-esm but maybe there is more to add?

I.e. css dedupe (#1631) - I can't fully disable inlined css cuz its needed for loading screen What about --prerender also?

The aim is to having folder: assets/ favicon.ico index.html (with inlined loading screen css) bundle.css (without loading screen css) index.min.js

P.S. is there anything wrong making game interface with react? Honestly I don't know, is there other legit solutions? (I've seen vue used for game ui only)

caxapexac avatar Dec 17 '21 06:12 caxapexac

Disabling prerendering would mean very little CSS could be inlined. Critters needs to be able to detect what styles you use in order to inline those CSS rules. If you don't prerender, you're handing Critters an empty doc; it will only end up inlining generic rules for body and html.

rschristian avatar Dec 17 '21 06:12 rschristian

@rschristian its okay about css and prerender, got it, thanks. What about 3-files build? There are still files I'd like to get rid off, is it possible?: image

I mean: prerender data now only contain {"url":"/"} and doesn't look useful push manifest and ssr doesn't look like used at all, what is it?

caxapexac avatar Dec 17 '21 07:12 caxapexac

ssr-build/ is used to prerender your app, push-manifest.json is beneficial with http2 push, see: https://github.com/GoogleChromeLabs/http2-push-manifest#whats-a-push-manifest

You can just run a post-build delete script for anything you don't want. I doubt you want the manifest.

rschristian avatar Dec 17 '21 07:12 rschristian

Oh I see, push manifest feels much useful now, thanks

caxapexac avatar Dec 17 '21 08:12 caxapexac

Sorry, push-manifest you could want, manifest.json is what I was referring to with that last comment. They're for PWAs, but seeing as how you've disabled the rest of the PWA stuff, you don't want it.

rschristian avatar Dec 17 '21 08:12 rschristian

What kinds of pwa stuff had I disabled? I really need manifest.json so I thought service worker is optional for it

caxapexac avatar Dec 17 '21 08:12 caxapexac

There's no hard requirement, though it'd be quite odd.

rschristian avatar Dec 17 '21 08:12 rschristian

Going to close out, this will never be supported out-of-the-box. Users can configure via preact.config.js if they require this.

rschristian avatar Dec 23 '22 09:12 rschristian