Skip to content

Re-enable Javascript support #1347

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

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

smocherla-brex
Copy link

@smocherla-brex smocherla-brex commented Jun 29, 2025

JS support was dropped in #1185 due to Kotlin 2 switching to klibs from JARS for JS libraries and artifacts. This PR aims to re-enable the the JS rules in a way that works with the Kotlin 2 architecture (and the new IR compiler).

Implementation details:

  • Add back kt_js_import and kt_js_library rules. The rules do not deal with JARs at all and klibs are the only way artifacts are passed is through klibs, and the final intended binary will be js
  • Some of the stuff is restored from Drop support for JavaScript #1185 but I've done cleanup to remove any JAR related stuff
  • Switch to rules_js with js_binary instead of nodejs_binary that was being used earlier from build_bazel_rules_nodejs (which is deprecated as it is switched to rules_nodejs where only the NodeJS toolchain exists)

Note: I've not pulled in changes to add the kotlin-native toolchain so as to avoid increasing the scope of this PR.

Added an example that works with pnpm/kt_js_library with express with rules_js/pnpm below.

~/src/rules_kotlin_oss/examples/node git:[smocherla/re-enable-kotlin-js]
bazel run //express
INFO: Analyzed target //express:express (0 packages loaded, 0 targets configured).
INFO: From Compiling Kotlin to JS //express:app { kt: 2 }:
info: produce executable: /private/var/folders/hl/w7gm71y14p34449xg6sm0f980000gn/T/pwd8287918242901126544
info: cache directory: null
info: executable production duration: 681ms
....
INFO: Found 1 target...
Target //express:express up-to-date:
  bazel-bin/express/express_/express
INFO: Elapsed time: 1.171s, Critical Path: 0.91s
INFO: 2 processes: 1 internal, 1 worker.
INFO: Build completed successfully, 2 total actions
INFO: Running command line: bazel-bin/express/express_/express
Server started on http://localhost:3000

I've not tested with advanced/complex usecases yet, but this is a good start.

Additionally, Kotlin 2/js external deps in maven are solely published as klibs now and cannot be consumed with rules (I'm looking to fix that in rules_jvm_external here bazel-contrib/rules_jvm_external#1391)

Checklist:

  • Add unit tests.
  • Get js_binary integration working with rules_js.
  • Integration test with a node workspace/module added in examples/

@smocherla-brex smocherla-brex changed the title Try re-enabling Javascript support and add basic support for kotlin-native toolchain Re-enable Javascript support and add basic support for kotlin-native toolchain Jun 30, 2025
@smocherla-brex smocherla-brex changed the title Re-enable Javascript support and add basic support for kotlin-native toolchain Re-enable Javascript support Jul 1, 2025
@smocherla-brex smocherla-brex marked this pull request as ready for review July 1, 2025 22:52
@restingbull restingbull self-assigned this Jul 2, 2025
Copy link
Collaborator

@restingbull restingbull left a comment

Choose a reason for hiding this comment

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

first pass. Have some concerns about the api of kt_js_library.

)

# required with Bazel 8/workspace
http_archive(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need rules_python?

Copy link
Author

Choose a reason for hiding this comment

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

This was needed only for this example (not the root module) with Bazel 8 workspace. It appears related to this change with Bazel 8 bazelbuild/bazel#23043. Without this, I get the error

USE_BAZEL_VERSION=8.2.1 bazel query //... --enable_workspace --noenable_bzlmod
WARNING: WORKSPACE support will be removed in Bazel 9 (late 2025), please migrate to Bzlmod, see https://bazel.build/external/migration.
ERROR: /private/var/tmp/_bazel_smocherla/bfd93a74a834f9acb8d10b4323733a2c/external/rules_python/python/py_cc_link_params_info.bzl:6:84: name 'PyCcLinkParamsProvider' is not defined
ERROR: error loading package under directory '': error loading package '': Internal error while loading Starlark builtins: Failed to autoload external symbols: compilation of module 'python/py_cc_link_params_info.bzl' failed Most likely you need to upgrade the version of rules repository in the WORKSPACE file.

It gets fixed with

USE_BAZEL_VERSION=8.2.1 bazel query //... --enable_workspace --noenable_bzlmod --incompatible_autoload_externally=-@rules_python
WARNING: WORKSPACE support will be removed in Bazel 9 (late 2025), please migrate to Bzlmod, see https://bazel.build/external/migration.
//:.aspect_rules_js/node_modules/accepts@1.3.8
//:.aspect_rules_js/node_modules/accepts@1.3.8/dir
//:.aspect_rules_js/node_modules/accepts@1.3.8/pkg
//:.aspect_rules_js/node_modules/accepts@1.3.8/ref
...

I can add that flag as well (it appears some internal bazel repos rely on rules_python) and because this is WORKSPACE, it breaks unless it's already pulled in.

doc = "A list of other kt_js_library that this library depends on for compilation",
providers = [_KtJsInfo],
),
"output_kind": attr.string(
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the rationale of changing the outputs via attributes (output_kind, sourcemap)?

It's not a common pattern for bazel.

Copy link
Author

@smocherla-brex smocherla-brex Jul 6, 2025

Choose a reason for hiding this comment

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

It was added to avoid creating .js outputs on every kt_js_library target as it's only really required in the final target when running it as Node binary (as an example) - only klibs are required for sharing between targets for compilation. The JS creation part seemed quite slow so I made it conditional to avoid slowing down the builds - I can remove it though and just have it generated by default if desired.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If generating the js outputs is too slow, the usual approach is to move them to a separate action. The outputs will only be generated if the artifact is used.

The problem with changing the outputs via attributes is that consumers of the library do not have the option to use the library in a different context without modifying the rule, or duplicating it. If the library is being consumed from a repository or, usually in large code bases, is not owned by the consumer, this becomes a problem.

Additionally, there is the additional cognitive load that developer must know the state of each library when adding them as dependencies. Given that Bazel already requires developers to understand and assemble the build graph in addition to writing code (and declaring dependencies again in the source), it's preferable to make the rules as simple and straight forward as possible.

So, let's generate all the outputs for the first pr -- then, optimize for build time by splitting the slow outputs from the fast in other pr. This will complicate the compile process, so some care will need to be taken to avoid increasing the maintenance burden and complexity of the optimization.

@restingbull
Copy link
Collaborator

I'd almost prefer to start with a PR that adds kotlin native toolchain. When I last reviewed getting js working, it appeared to be saner to start with native and then add js as an output.

Copy link
Collaborator

@restingbull restingbull left a comment

Choose a reason for hiding this comment

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

Please add starlark unittests, similar to the existing compile tests

@smocherla-brex
Copy link
Author

smocherla-brex commented Jul 6, 2025

I'd almost prefer to start with a PR that adds kotlin native toolchain. When I last reviewed getting js working, it appeared to be saner to start with native and then add js as an output.

I didn't do it because kotlinc-js is not part of that, it still seems bundled with the regular Kotlin compiler and there weren't any hooks to invoke the K2JS compiler with kotlin-native (they seem to be just sharing the klib infra for the most part). Specifically the flag required to create the JS outputs seemingly exists only with kotlinc-js from my understanding. I can create a separate PR for adding the kotlin-native toolchain though.

@restingbull
Copy link
Collaborator

I'd almost prefer to start with a PR that adds kotlin native toolchain. When I last reviewed getting js working, it appeared to be saner to start with native and then add js as an output.

I didn't do it because kotlinc-js is not part of that, it still seems bundled with the regular Kotlin compiler and there weren't any hooks to invoke the K2JS compiler with kotlin-native (they seem to be just sharing the klib infra for the most part). Specifically the flag required to create the JS outputs seemingly exists only with kotlinc-js from my understanding. I can create a separate PR for adding the kotlin-native toolchain though.

As I understand it, the core build graph of non-jvm compilation produces klibs until a leaf node (js, native binary) is needed. I expect jvm compilation will eventually reach that state. Therefore, it makes more sense to ensure a simple klib pipeline, and then look at how the js and native "binaries" are produced.

I'd like to see the native pr to understand how the underlying compilation will change, and see where things can be simplified or are fundamentally complex.

@restingbull
Copy link
Collaborator

Please hit the "Re-request review" button when you'd like another pass.

@smocherla-brex
Copy link
Author

smocherla-brex commented Jul 13, 2025

I'd almost prefer to start with a PR that adds kotlin native toolchain. When I last reviewed getting js working, it appeared to be saner to start with native and then add js as an output.

I took a stab at bootstrapping support for the Kotlin/Native toolchain and producing klibs generically to potentially extend it to other platforms (like JS here) #1351. It's in draft because it has a couple of test failures (which I'm looking into it) but would be interested to see your thoughts on it and if that's the direction you were intending.

@restingbull restingbull self-requested a review July 15, 2025 04:24
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.

2 participants