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

feat(wasm): Support more targets #848

Merged
merged 1 commit into from
Jan 27, 2024
Merged

feat(wasm): Support more targets #848

merged 1 commit into from
Jan 27, 2024

Conversation

magic-akari
Copy link
Contributor

@JohnnyMorganz
Copy link
Owner

One of my biggest concerns with our current WASM package is it is very large in size (it packages all the WASM code twice, and now it will do it for a third time).

How big is the package after this change? I wonder if it is better to release these three as independent npm packages

Copy link

codecov bot commented Jan 16, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (bc3ce88) 97.03% compared to head (59a6ae1) 97.03%.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #848   +/-   ##
=======================================
  Coverage   97.03%   97.03%           
=======================================
  Files          16       16           
  Lines        5995     5995           
=======================================
  Hits         5817     5817           
  Misses        178      178           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@magic-akari
Copy link
Contributor Author

magic-akari commented Jan 16, 2024

How big is the package after this change?

❯ npm pack            
npm notice 
npm notice 📦  @johnnymorganz/stylua@0.19.1
npm notice === Tarball Contents === 
npm notice 16.7kB LICENSE.md                            
npm notice 17.6kB README.md                             
npm notice 2.2kB  package.json                          
npm notice 16.7kB stylua.bundler/LICENSE.md             
npm notice 17.6kB stylua.bundler/README.md              
npm notice 20.1kB stylua.bundler/stylua_lib_bg.js       
npm notice 3.0MB  stylua.bundler/stylua_lib_bg.wasm     
npm notice 3.3kB  stylua.bundler/stylua_lib_bg.wasm.d.ts
npm notice 6.9kB  stylua.bundler/stylua_lib.d.ts        
npm notice 81B    stylua.bundler/stylua_lib.js          
npm notice 16.7kB stylua.node/LICENSE.md                
npm notice 17.6kB stylua.node/README.md                 
npm notice 3.0MB  stylua.node/stylua_lib_bg.wasm        
npm notice 3.3kB  stylua.node/stylua_lib_bg.wasm.d.ts   
npm notice 6.9kB  stylua.node/stylua_lib.d.ts           
npm notice 20.4kB stylua.node/stylua_lib.js             
npm notice 16.7kB stylua.web/LICENSE.md                 
npm notice 17.6kB stylua.web/README.md                  
npm notice 3.0MB  stylua.web/stylua_lib_bg.wasm         
npm notice 3.3kB  stylua.web/stylua_lib_bg.wasm.d.ts    
npm notice 10.7kB stylua.web/stylua_lib.d.ts            
npm notice 22.0kB stylua.web/stylua_lib.js              
npm notice === Tarball Details === 
npm notice name:          @johnnymorganz/stylua                   
npm notice version:       0.19.1                                  
npm notice filename:      johnnymorganz-stylua-0.19.1.tgz         
npm notice package size:  2.6 MB                                  
npm notice unpacked size: 9.2 MB                                  
npm notice shasum:        51c8e9bbb3149a1f508172e1922b74d26f09cfcd
npm notice integrity:     sha512-Ix690sKlUx6EE[...]dznvR2eIBcITA==
npm notice total files:   22                                      
npm notice 
johnnymorganz-stylua-0.19.1.tgz

I believe the WASM generated by the three methods should be identical (although they are not), with the only difference being the JavaScript glue code. However, this requires improvements in wasm-bindgen.

@JohnnyMorganz
Copy link
Owner

Current unpacked size is 6.11MB, and indeed it gets increased to 9.2MB, representing 1/3 of the package. 9.2MB is a really big package size imo (6.11MB is already large)

Yeah, it is a shame that it is not exactly 1 WASM output shared for each different platform right now. In the output you show, all the WASMs have the same file size, so I'd be curious if there is indeed any difference. Maybe we could optimise it a bit here.

@magic-akari
Copy link
Contributor Author

magic-akari commented Jan 18, 2024

I made another attempt, check here:
https://github.com/JohnnyMorganz/StyLua/compare/main...magic-akari:StyLua:feat/wasm?expand=1

They share the same WebAssembly file.
The main idea is to build upon the Web target and then implement other targets. This implementation includes support for CommonJS and ESM, and theoretically, it should also work on Deno and Bun.

Some clever hacks were used for the Bundler target. I'm concerned whether these hacks will become a maintenance burden in the future, so I would like to know your opinion.

@magic-akari magic-akari marked this pull request as ready for review January 19, 2024 00:50
@magic-akari
Copy link
Contributor Author

❯ npm pack     
npm notice 
npm notice 📦  @johnnymorganz/stylua@0.19.1
npm notice === Tarball Contents === 
npm notice 16.7kB LICENSE.md                        
npm notice 17.6kB README.md                         
npm notice 1.9kB  package.json                      
npm notice 75B    stylua_lib_bundler_wbg.cjs        
npm notice 84B    stylua_lib_bundler.d.ts           
npm notice 200B   stylua_lib_bundler.js             
npm notice 84B    stylua_lib_node.d.mts             
npm notice 234B   stylua_lib_node.mjs               
npm notice 20.9kB stylua_lib.cjs                    
npm notice 84B    stylua_lib.d.cts                  
npm notice 16.7kB stylua.web/LICENSE.md             
npm notice 17.6kB stylua.web/README.md              
npm notice 3.0MB  stylua.web/stylua_lib_bg.wasm     
npm notice 3.3kB  stylua.web/stylua_lib_bg.wasm.d.ts
npm notice 10.7kB stylua.web/stylua_lib.d.ts        
npm notice 22.1kB stylua.web/stylua_lib.js          
npm notice === Tarball Details === 
npm notice name:          @johnnymorganz/stylua                   
npm notice version:       0.19.1                                  
npm notice filename:      johnnymorganz-stylua-0.19.1.tgz         
npm notice package size:  872.5 kB                                
npm notice unpacked size: 3.1 MB                                  
npm notice shasum:        c2e3c2922759d491f69618ab232ec00a2ddb3e52
npm notice integrity:     sha512-qcRCwV1b44F0f[...]TPLpMiKLmda+g==
npm notice total files:   16                                      
npm notice 
johnnymorganz-stylua-0.19.1.tgz

@magic-akari
Copy link
Contributor Author

magic-akari commented Jan 20, 2024

  • node esm
  • node cjs
  • webpack target=web
  • webpack target=node

@magic-akari magic-akari marked this pull request as draft January 20, 2024 08:11
@magic-akari
Copy link
Contributor Author

All done.

@magic-akari magic-akari marked this pull request as ready for review January 20, 2024 09:39
@magic-akari magic-akari changed the title Add WebAssembly web target Add WebAssembly more targets Jan 20, 2024
@magic-akari magic-akari changed the title Add WebAssembly more targets feat(wasm): Support more targets Jan 20, 2024
@JohnnyMorganz
Copy link
Owner

This looks pretty awesome, thanks for taking the time! You are right though, I don't completely follow what is happening yet 😅, so might be a pain to maintain in the future.

I'm open to merging it in though. We don't have any (smoke)tests to see if the wasm code actually works, so I would want to verify it still works correctly on all the platforms.

However, when I was first thinking about this, I was wondering whether it would be as simple as:

npx wasm-pack@0.10.3 build --target nodejs --out-dir wasm/stylua.node -- --features lua52,lua53,lua54,luau
npx wasm-pack@0.10.3 build --target bundler --out-dir wasm/stylua.bundler -- --features lua52,lua53,lua54,luau
npx wasm-pack@0.10.3 build --target web--out-dir wasm/stylua.web -- --features lua52,lua53,lua54,luau

rm wasm/stylua.node/stylua_lib_bg.wasm
rm wasm/stylua.bundler/stylua_lib_bg.wasm

# and then some sed-like commands to replace references of stylua_lib_bg.wasm in the above 2 folders to the stylua.web version

Did you happen to see if that was possible? IMO it seems a bit simpler, but unsure whether it is feasible. Also unsure whether it would still support the other new targets you mentioned - so if your work is more flexible I'm happy to keep it as-is

@magic-akari
Copy link
Contributor Author

Did you happen to see if that was possible?

Unfortunately, this approach does not work. if we open three generated WebAssembly files to examine their contents and observe that their import paths are all different.

( From top to bottom, they are node, web, and bundler. )

image

@magic-akari
Copy link
Contributor Author

The next step is to choose one as the reference and implement the other two.


image When examining three JavaScript glue files, it can be observed that both bundler and node initialization are synchronous. However, web initialization has two options: synchronous and asynchronous, encapsulated within functions. Therefore, choosing web as the foundation makes it easier to implement.

I noticed that the finalizeInit function is not exported, so we will add an export statement in our build process.

# workaround for bundler usage
echo "export { getImports as __getImports, finalizeInit as __finalizeInit }" >> wasm/stylua.web/stylua_lib.js

In the above WebAssembly code, we can observe that the import namespace for the bundler is a JavaScript (js) file. However, for our web build (wbg), we use the browser to instruct the bundler to locate the required files.

StyLua/wasm/package.json

Lines 68 to 70 in 59a6ae1

"browser": {
"wbg": "./stylua_lib_bundler_wbg.cjs"
},

@magic-akari
Copy link
Contributor Author

magic-akari commented Jan 20, 2024

Reasons for needing the web target:
Sometimes you may need to delay initialization or handle the loading of wasm files yourself (e.g. in a VS Code extension).

With the web target available, we can use the following approach:

import init, { formatCode, Config, IndentType, OutputVerification } from "@johnnymorganz/stylua/web";

export async function fmt_init() {
  const wasm_uri = vscode.Uri.joinPath(context.extensionUri, "stylua_lib_bg.wasm");
  const bits = await vscode.workspace.fs.readFile(wasm_uri);
  await init(bits);
}

export function formattingSubscription() {
  return vscode.languages.registerDocumentFormattingEditProvider("lua", {
    async provideDocumentFormattingEdits(document, options, token) {
      const text = model.getValue();

      const indent_type = options.insertSpaces ? IndentType.Spaces : IndentType.Tabs;
      const indent_width = options.tabSize;

      const config = Config.new().with_indent_type(indent_type).with_indent_width(indent_width);

      const formatted = formatCode(text, config, undefined, OutputVerification.None);

      const range = document.validateRange(new vscode.Range(document.positionAt(0), document.positionAt(text.length)));
      return [vscode.TextEdit.replace(range, formatted)];
    }
  });
}

@magic-akari
Copy link
Contributor Author

I noticed that there is a VSCode extension in the stylua project.
Currently, it uses child_process to spawn external command-line processes for task execution.
If you switch to wasm, it's possible to eliminate the dependency on the Node environment entirely and run directly in the browser (e.g. on vscode.dev and github.dev).
You can find more information by visiting this link: https://code.visualstudio.com/api/extension-guides/web-extensions


For my personal use, I primarily use it on the Monaco Editor.
I have to support multiple languages, so delayed initialization is necessary.

@magic-akari
Copy link
Contributor Author

I don't completely follow what is happening yet 😅, so might be a pain to maintain in the future.

If you encounter any issues in the future, feel free to reach out to me.

@JohnnyMorganz
Copy link
Owner

Thank you for the detailed description! This makes a lot more sense now, much appreciated.

I noticed that there is a VSCode extension in the stylua project.
Currently, it uses child_process to spawn external command-line processes for task execution.
If you switch to wasm, it's possible to eliminate the dependency on the Node environment entirely and run directly in the browser (e.g. on vscode.dev and github.dev).

Yep, this is something I have considered before. The main reason why I haven't done this yet is because we allow users to choose different stylua versions and upgrade stylua versions without also upgrading the extension version. I am not sure how to cleanly support this when moving to wasm. (Maybe we could also attach the wasm build on the releases page? But I don't know if vscode.dev supports downloading)

Copy link
Owner

@JohnnyMorganz JohnnyMorganz left a comment

Choose a reason for hiding this comment

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

Going to assume this all works good :)

We should probably write some smoketests for the wasm packages sometime.

@JohnnyMorganz JohnnyMorganz merged commit 049bb7c into JohnnyMorganz:main Jan 27, 2024
19 checks passed
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.

[Feat Request] Add web target for wasm build
2 participants