Skip to content

Commit

Permalink
v2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
richardtallent committed Feb 11, 2024
1 parent 70f812a commit 9ebddd5
Show file tree
Hide file tree
Showing 6 changed files with 437 additions and 373 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@
| 2023-02-11 | 0.13.3 | Bump build dependencies, update README |
| 2023-04-01 | 0.13.5 | Allow reporting of compressed bundle size (#70, #71, thanks @mojoaxel!) |
| | | Fix where polfill not removed when minify disabled (#72) |
| 2024-01-14 | 1.0.0 | Vite 5, Node 18 is now required. This is a breaking change! |
| 2024-01-14 | 1.0.0 | BREAKING: Vite 5, Node 18 is now required. This is a breaking change! |
| | | Fixed relative URLs (#75, #86, thanks @atomiechen!) |
| | | Switched from jest to vitest |
| | | Fix plugin type |
| 2024-02-11 | 2.0.0 | BREAKING: Vite 5.1+ is now required |
| | | Uses delegate for `assetsInlineLimit` |
| | | Removed unneeded `\n` around `<style>` and `<script>` tags (only needed w/CDATA) (#85) |
| | | More detailed output |
| | | Legacy UTF-8 charset declarations are now removed from inlined CSS files (#90) |
37 changes: 27 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,40 @@ This Vite build plugin allows you to _inline_ all JavaScript and CSS resources d

## Why?

Bundling your _entire_ site into one file certainly isn't recommended for most situations.
Bundling your _entire_ site into one file **isn't recommended for most situations**.

In particular, this is not a good idea, performance-wise, for a normal web site hosted on a web server.
In particular, this is not a good idea, performance-wise, for hosting a site on a normal web server.

However, this can be _very_ handy for _offline_ web applications-- apps bundled into a single HTML file that you can double-click and open directly in your web browser, no server needed. This might include utilities, expert system tools, documentation, demos, and other situations where you want the full power of a web browser, without the need for a Cordova or Electron wrapper or the pain of normal application installation.

## Limitations
**This is a _single file_ plugin. As in, it creates _one HTML file_ and _no other files_. Hence the name. So, this either _will not work_ or _will not be optimized for_ apps that require multiple "entry points" (HTML files). Please see issue #51 for details. Issues opened requesting multiple entry points will be closed as `wontfix`.**

Web applications running from a local file have some browser security limitations:
## What does work when running an HTML file locally

- No ability to access external domains -- no images, no API calls, etc.
- `<link />`, `<script>`, `<img>`, CSS `url()`, or similar HTML/CSS/JS features that expect to make a request for another file will not work. This plugin gets around that by bundling as many of these as possible into the single file output.
- No cookies. However, you can use `localStorage` and the newer experimental Persistent Storage APIs. You can also use the FileSystem API, with user permission.
- SPA routing requires using hash-based routes -- the web history API doesn't work for local files, and a web browser will not allow you to navigate between local HTML files.
- Any sourcemaps you generate will be useless, since this plugin bundles the compiled files after sourcemaps are generated. Turning off esbuild's minification in your vite config will at least ensure the code is legible when debugging.
Local HTML files are now for most purposes considered to be a "secure context" and thus now have far more capabilities than when this project started, which is good!

**This is a _single file_ plugin. As in, it creates _one HTML file_ and _no other files_. Hence the name. So, this _will not work_ with multi-page apps. Please see issue #51 for details. Issues opened requesting multiple entry points will be closed as `wontfix`.**
You can use:

- `localStorage`
- Newer experimental Persistent Storage APIs
- FileSystem API
- Requests for _local files_ relative to the same folder (_i.e._, for Vue, resources from your `public` folder)
- Requests for images from external web sites
- Requests for fonts from external web sites
- Requests to external APIs (requires `{ mode: 'no-cors'}` in your `fetch` call)
- Following links to other files relative to the same folder
- SPA hash-based routing
- WebXR

_I've only tested some of the above in Chromium-based browsers. YMMV for WebKit and other browser engines. Some may require explicit user permission._

## What doesn't work

- SPA routing via Web History API
- Cookies (passed via HTTP headers, which don't exist for `file:///` URIs)
- WebXR Immersive Mode (theoretically could work, but not currently supported)
- Worklets (theoretically could work, but not currently supported)
- Sourcemaps (useless, since inlining happens after they are generated)

## Installation

Expand Down
12 changes: 6 additions & 6 deletions __tests__/replaceScript.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import { replaceScript } from "../dist/esm/index.js"

describe("Replace Script", () => {
test("It should inline external scripts and preserve other script attributes", () => {
const outputMod = "<script module>\n\n</script>"
const outputMod = "<script module></script>"
expect(replaceScript(`<script module src="./foo.js"></script>`, "foo.js", "")).toEqual(outputMod)
expect(replaceScript(`<script src="./foo.js" module></script>`, "foo.js", "")).toEqual(outputMod)
const outAsync = "<script async module>\n\n</script>"
const outAsync = "<script async module></script>"
expect(replaceScript(`<script async src="./foo.js" module></script>`, "foo.js", "")).toEqual(outAsync)
expect(replaceScript(`<script src="./foo.js" async module></script>`, "foo.js", "")).toEqual(outAsync)
const outCrossOrigin = `<script async type="module" crossorigin>\n\n</script>`
const outCrossOrigin = `<script async type="module" crossorigin></script>`
expect(replaceScript(`<script async type="module" crossorigin src="/assets/foo.js"></script>`, "assets/foo.js", "")).toEqual(outCrossOrigin)
const outPolyfill = `<script type="module">\n`
const outPolyfill = `<script type="module">`
// Removing polyfill without minification
expect(replaceScript(`<script type="module" crossorigin>\n(function polyfill() {stuff here\nfoo})();`, "", "", true)).toEqual(outPolyfill)
expect(replaceScript(`<script type="module" crossorigin>(function polyfill() {stuff here\nfoo})();`, "", "", true)).toEqual(outPolyfill)
// Removing polyfill with minification
expect(replaceScript(`<script type="module" crossorigin>\n(function(){stuff here\nfoo})();`, "", "", true)).toEqual(outPolyfill)
expect(replaceScript(`<script type="module" crossorigin>(function(){stuff here\nfoo})();`, "", "", true)).toEqual(outPolyfill)
})
})
Loading

0 comments on commit 9ebddd5

Please sign in to comment.