a couple months ago, i taught myself how to build the JavaScript runtime for Swift WebAssembly by studying the source code of the carton tool. this was not easy, but i at least understood what a “resource” in that context was and what strings needed to be substituted into those resources to make it load the compiled WebAssembly correctly.
it seems in recent weeks, the JavaScript runtime has undergone major changes, and the maintainer of the repo has written their own TypeScript build system in Swift which i am really struggling to understand the workings of, as it’s much more complex than what was living in the carton repo previously.
i don’t want to use a SwiftPM Package Plugin to build my application, i just want to learn how to compile these TypeScript files into a minified, standalone JavaScript file, with a magic string that i can substitute with the relative location of the wasm file. is there a guide for how to do this?
(the only existing guide i could find is using the Package Plugin, and i don’t want to be using SwiftPM to compile TypeScript.)
I’m curious about the motivation behind not wanting to use the plugin. The new plugin was designed to address several long-standing issues we had with carton, such as long build times of the tool itself and the fact that the output wasn’t very friendly to JS ecosystem bundlers like Vite.
That said, if you still prefer to handle the build manually, the files in Templates are processed by a simple preprocessor of fewer than 500 lines of Swift code. You can refer to this as a guide for how the substitution logic works.
We need this kind of preprocessing to emit the optimal code not just for basic WASI targets, but also for threads-enabled targets and Embedded Swift.
in general, i find SwiftPM plugins less productive to use as installable developer tools because the lifetimes of the actual compiled binaries are tied to the state of the scratch build directory, which means every time you clear .build, you force the plugins to go back through Dependency Resolution (which is slow, and might additionally involve cloning a git repo depending on SwiftPM cache settings), as well as compiling the plugin itself from source.
another issue with Plugins as Tools is that you are forced to match the version of the Swift Toolchain used to invoke the plugin (what ought to just be a command line tool) with the version of the Swift Toolchain used to build the project. so if you are using or testing a nightly Swift Toolchain with your project, you also have to remember to use the path to that same toolchain to invoke the plugin, or it will invalidate your incremental build and rebuild the plugin from scratch. it’s way easier to think about Swift Toolchain + SDK version when building WebAssembly, and then only think about JavaScript related things when building JavaScript, as a separate step.
but more specifically to WebAssembly, i couldn’t figure out how to get the plugin to minify or consolidate the runtime JavaScript files, or get it to work without the --use-cdn option. the only way i could get it to work was under the default release configuration, which performs five HTTP requests for unminified JavaScript files, including one request to an external domain (jsdelivr.net).
it sounds like Vite might help with the last issue, but i wasn’t aware of that article as it was not present in the documentation for the most recent release version of JavaScriptKit.
This is actually good and bad because we need to keep the tool and SwiftPM package versions in sync. Also, the new JavaScriptKit's package plugin is written with an awareness of its build time. If you give it a try, you will see significant build overhead reduction compared to carton.
With SwiftPM package plugin, they will never be different toolchains. I guess you are mixing up carton's problems.
ideally, a tool at this layer should be able to do several tasks individually. such as:
Build the WebAssembly
Build the TypeScript
Browse the Help
right now, i don’t think js can Build the TypeScript only, but it still stands that you should be able to fuzz the tool without passing parameters for WebAssembly compilation, including Browse the Help.
this means that there is a noticeable difference between running
if you mindlessly type in the latter, it will invalidate what was previously in .build, which really shouldn’t be coupled to just launching the tool without actually compiling any WebAssembly.
I don't know what's the best way to have a a magic string that i can substitute with the relative location of the wasm file, having a gitignored .ts file containing this constant that you can switch to the appropriate version before build should be a decent solution for this. (You would probably have debug/release/etc. templates in a folder and your npm scripts would start with copying over the appropriate one to the src folder)