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

Enable browser / cross-platform support #18

Closed
wants to merge 10 commits into from
Closed

Conversation

NullVoxPopuli
Copy link
Contributor

@NullVoxPopuli NullVoxPopuli commented Jul 29, 2023

Resolves: #11
Unblocks: NullVoxPopuli/limber#1162

Dist Branch: https://github.com/NullVoxPopuli/content-tag/tree/browser-support-dist

Demo of this working: https://4f4afaa6.limber-glimdown.pages.dev/
PR: NullVoxPopuli/limber#1162
image
(this screenshot reveals I have a concurrency issue because the wasm is loaded twice (this is a me problem, not a content-tag problem) 😅 )

pnpm:

"content-tag": "github:NullVoxPopuli/content-tag#browser-support-dist"

I used the term standalone similar to how @babel/standalone uses it -- in that the existing build of content-tag works in ESM, but is still meant for node usage. the standalone build is built using the browser option, which happens to then be compatible everywhere.


I'm new to Rust, so here are the learning materials I found / used

automated browser tests?

  • testem + qunit
    • way too much manual work to set up with ESM-based tests.
  • playwright
    • can't import anything to test / render / assert against (in ember, this is what ember-qunit is for)
    • playwright is a browser driver w/ assertions
    • also playwright downloads copies of browsers instead of using what's installed an the system.
  • vitest's browser mode
    • no documentation
    • "webdriverIO and playwright support", which means the same limitations as playwright native, probably (at least until they document more of how it's supposed to be used)
  • mocha (https://mochajs.org/#running-mocha-in-the-browser)
    • doesn't explain how to report from browser to node, which is what we need for CI

We do have a npm run web that we can manually run to verify :-\

If anyone knows of a decent web-testing setup with ESM / TS ... that'd be great.

@@ -9,6 +9,10 @@ edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]

[profile.release]
lto = true
opt-level = 'z'
Copy link
Contributor Author

@NullVoxPopuli NullVoxPopuli Jul 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the browser wasm module:

  • with only lto = true: 1MB
  • with only opt-level = 'z': 741K
  • with neither: 1MB
  • with both: 741K
  • with wee_alloc: 741K
  • with lol_alloc's LockedAllocator<FreeListAllocator>: 741K
  • with lol_alloc's AssumeSingleThreaded<FreeListAllocator>: 741K
  • with lol_alloc's FailAllocator: 374K (but also doesn't work 🙈 )

Copy link
Contributor Author

@NullVoxPopuli NullVoxPopuli Jul 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a separate machine now, it seems I'm getting different results.
I had thought that the current version of the optimization settings would give 741K, but on this machine:

❯ du -r ./pkg/
 2.6M └─┬ pkg                         │██████████████████████████ │ 100%
 1.3M   ├─┬ node                      │             █████████████ │  50%
 1.3M   │ ├── content_tag_bg.wasm     │             █████████████ │  49%
  16K   │ ├── content_tag.cjs         │             ░░░░░░░░░░░░█ │   1%
 4.0K   │ ├── content_tag_bg.wasm.d.ts│             ░░░░░░░░░░░░█ │   0%
 4.0K   │ ├── content_tag.d.ts        │             ░░░░░░░░░░░░█ │   0%
 4.0K   │ └── .gitignore              │             ░░░░░░░░░░░░█ │   0%
 1.3M   ├─┬ browser                   │             █████████████ │  50%
 1.2M   │ ├── content_tag_bg.wasm     │             █████████████ │  48%
  16K   │ ├── content_tag.js          │             ░░░░░░░░░░░░█ │   1%
 4.0K   │ ├── content_tag_bg.wasm.d.ts│             ░░░░░░░░░░░░█ │   0%
 4.0K   │ ├── content_tag.d.ts        │             ░░░░░░░░░░░░█ │   0%
 4.0K   │ └── .gitignore              │             ░░░░░░░░░░░░█ │   0%
 4.0K   ├── package.json              │                         █ │   0%
 4.0K   ├── README.md                 │                         █ │   0%
 4.0K   └── LICENSE                   │                         █ │   0%


the browser wasm is 1.2MB...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ended up being about 1.35MB today:
image

Hopefully it can shrink in the future (or I can ditch more and more babel stuff / not use @babel/standalone, perhaps)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GH says less:
image

🤷 could be MiB vs MB or something.

but, 487kB isn't terrible since accuracy is what we're doing here. I can finally divide numbers before <template>, which is good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that I opted out of all the different allocators -- seemed too risky, the more I read about them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today, tho:

❯ du -r ./pkg/
2.7M └─┬ pkg                         │█████████████████████ │ 100%
1.3M   ├─┬ standalone                │          ███████████ │  50%
1.3M   │ ├── content_tag_bg.wasm     │          ███████████ │  48%
 20K   │ ├── content_tag.js          │          ░░░░░░░░░░█ │   1%
4.0K   │ ├── standalone.js           │          ░░░░░░░░░░█ │   0%
4.0K   │ ├── standalone.d.ts         │          ░░░░░░░░░░█ │   0%
4.0K   │ ├── content_tag_bg.wasm.d.ts│          ░░░░░░░░░░█ │   0%
4.0K   │ ├── content_tag.d.ts        │          ░░░░░░░░░░█ │   0%
4.0K   │ └── .gitignore              │          ░░░░░░░░░░█ │   0%
1.3M   ├─┬ node                      │          ███████████ │  50%
1.3M   │ ├── content_tag_bg.wasm     │          ███████████ │  48%
 16K   │ ├── content_tag.cjs         │          ░░░░░░░░░░█ │   1%
4.0K   │ ├── content_tag_bg.wasm.d.ts│          ░░░░░░░░░░█ │   0%
4.0K   │ ├── content_tag.d.ts        │          ░░░░░░░░░░█ │   0%
4.0K   │ └── .gitignore              │          ░░░░░░░░░░█ │   0%
4.0K   ├── package.json              │                    █ │   0%
4.0K   ├── README.md                 │                    █ │   0%
4.0K   └── LICENSE                   │                    █ │   0%

with:
codegen-units = 1

❯ du -r ./pkg/
2.6M └─┬ pkg                         │████████████████████████████████ │ 100%
1.3M   ├─┬ standalone                │                ████████████████ │  50%
1.2M   │ ├── content_tag_bg.wasm     │                ████████████████ │  48%
 20K   │ ├── content_tag.js          │                ░░░░░░░░░░░░░░░█ │   1%
4.0K   │ ├── standalone.js           │                ░░░░░░░░░░░░░░░█ │   0%
4.0K   │ ├── standalone.d.ts         │                ░░░░░░░░░░░░░░░█ │   0%
4.0K   │ ├── content_tag_bg.wasm.d.ts│                ░░░░░░░░░░░░░░░█ │   0%
4.0K   │ ├── content_tag.d.ts        │                ░░░░░░░░░░░░░░░█ │   0%
4.0K   │ └── .gitignore              │                ░░░░░░░░░░░░░░░█ │   0%
1.3M   ├─┬ node                      │                ████████████████ │  49%
1.2M   │ ├── content_tag_bg.wasm     │                ████████████████ │  48%
 16K   │ ├── content_tag.cjs         │                ░░░░░░░░░░░░░░░█ │   1%
4.0K   │ ├── content_tag_bg.wasm.d.ts│                ░░░░░░░░░░░░░░░█ │   0%
4.0K   │ ├── content_tag.d.ts        │                ░░░░░░░░░░░░░░░█ │   0%
4.0K   │ └── .gitignore              │                ░░░░░░░░░░░░░░░█ │   0%
4.0K   ├── package.json              │                               █ │   0%
4.0K   ├── README.md                 │                               █ │   0%
4.0K   └── LICENSE                   │                               █ │   0%

@NullVoxPopuli
Copy link
Contributor Author

Most recently, I tried this using the new Git/dist branch (added to the PR description),
and the webpack config:

module: {
  rules: [
    {
      test: /\.wasm$/,
      type: "asset",
    },
  ]
},

(which seemed the least disruptive to wasm-pack's output, and still respected the native platform features of async wasm loading (a lot of folks seem to prefer asset/inline for wasm... which is a lot))

But I have an error about import names:
image

I think because webpack is renaming all the exports for some reason.
So I may hold off on this until I get limber/glimdown using Vite -- I don't really want to deal with webpack configs for too much longer.

…case

Shrink generate WASM by 25%

Save 9KB

Set up web test

Add more scripts

Restore node testing

Without wee_alloc, we grow ~ 450KB

With no allocator, we get down to 540KB, but the thing doesn't run

Back up to 1.2MB, but working in the browser

With no changes to the allocator, we are at 1.2MB

Give up on custom allocators since they all have problems

Switch to dual build

Update the README and document how to use Browser (ESM), Node (ESM) and Node (CJS)

Update ci.yml

Determine that it's simpler to not use a custom allocator -- there must be other ways to get the wasm size down tho

Remove browser test experiments

import init, { Preprocessor } from "./content_tag.js";

export async function createPreprocessor() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a tiny wrapper function since the default way rust wants us to build a wasm thing is ... goofy. Like, I get it, two things need to happen, load the wasm file and then instantiate the stuff.
maybe I should PR to them.

<script type="module">
import {createPreprocessor} from './pkg/standalone/standalone.js';

async function run() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is how I'll be using it in my REPL, tho it'll be import { createPreprocessor } from 'content-tag/standalone'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are still using the non-standalone build.

README.md Outdated Show resolved Hide resolved
@ef4
Copy link
Collaborator

ef4 commented Nov 30, 2023

I don't think we should really need a different import path and public API for the browser.

With conditional exports you could still have your standalone.js file but it would be invisible to consumers. It would be under the conditional export condition "browser".

And that file can use top-level await to await init(), so callers just see the same API as in node ESM.

@NullVoxPopuli
Copy link
Contributor Author

NullVoxPopuli commented Dec 1, 2023

I don't think we should really need a different import path and public API for the browser.

I can have a go at this, but (at least temporarily) it would make the project not consumable in ember at runtime 😅
See: embroider-build/embroider#1678

Standby! new PR incoming!

@NullVoxPopuli
Copy link
Contributor Author

Closing, because we went with a non-monorepo approach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ensure and test that running in the browser is appropriately supported
3 participants