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

Import with require does not work anymore for transpiled library #261

Open
rbsdc opened this issue Jan 31, 2025 · 8 comments
Open

Import with require does not work anymore for transpiled library #261

rbsdc opened this issue Jan 31, 2025 · 8 comments

Comments

@rbsdc
Copy link

rbsdc commented Jan 31, 2025

We have a Java library that is transpiled with j2cl to JavaScript, resulting in a "mylib.js". Until now, we were able to use the transpiled Java-classes and libraries as follows:

const MyUtils = require("mylib").MyUtils;
...
MyUtils.doSomething();

However, since a change in recent days (it seems to be this commit here: bazelbuild/rules_closure@b159f54), this kind of import does not work anymore. What now works is

import "mylib";
...
globalThis.MyUtils.doSomething();

We use browserify for bundling. I see now the following options:

  1. Replace all our imports and usages with globalThis like above.
  2. Add some post-processing for the transpiled mylib.js (most probably not future-proof)
  3. Find some transpiler argument or config that allows imports as before.

Is globalThis the way to go now? Or is there a reasonable and future-proof way to keep our require imports (e.g. some transpiler argument?) with the given setting (browserify)? Maybe, the way we use require was never intended like this for j2cl-transpiled libraries?

bazel version 7.4.0
bazelrc (like in the samples)

build --watchfs

build --spawn_strategy=local
build --strategy=J2cl=worker
build --strategy=Closure=worker
build --strategy=Javac=worker
build --strategy=JavaIjar=local
build --strategy=JavaDeployJar=local
build --strategy=JavaSourceJar=local
build --strategy=Turbine=local

# --experimental_inprocess_symlink_creation is used to workaround the missing
# support for paths with spaces https://github.com/bazelbuild/bazel/issues/4327.
# Remove the two flags after this issue is fixed in bazel new release.
build --enable_platform_specific_config
build:macos --experimental_inprocess_symlink_creation

test --test_output=errors

# Enable Java 11
build --java_language_version=11
build --java_runtime_version=11

# Enable Java 11 for J2CL compiler itself
build --tool_java_language_version=11
build --tool_java_runtime_version=11

BUILD

load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library")
load("@com_google_j2cl//build_defs:rules.bzl", "j2cl_application")

package(
    licenses = ["notice"],  # Apache 2.0
)

closure_js_library(
    name = "lib",
    srcs = ["lib.js"],
    deps = [
        "//src/main/java/org/mylib/lib"
    ],
)

j2cl_application(
    name = "mylib",
    entry_points = [
        "org.mylib"
    ],
    deps = [":lib"],
    extra_production_args = [
        '--compilation_level=SIMPLE'
    ]
)

lib.js

goog.module('org.mylib.lib');

const MyClass = goog.require('org.mylib.lib.MyUtils');

goog.exportSymbol('MyUtils', MyClass);
goog.exportSymbol('MyUtils.doSometing', MyUtils.doSomething);
@gkdn
Copy link
Member

gkdn commented Jan 31, 2025

To double check; you are using the compiled output, not the dev bundle from j2cl_application target, right?

@rbsdc
Copy link
Author

rbsdc commented Jan 31, 2025

@gkdn. If you mean the transpiled library (above called mylib.js), then it is the one contained in bazel-bin/src/main/javascript/mylib.js after the build completed (The build command is bazel build src/main/javascript:mylib.)

@rbsdc
Copy link
Author

rbsdc commented Jan 31, 2025

More context: With the newest updates, the transpiled mylib.js contains the following function:

goog.global = globalThis;
goog.exportPath_ = function (a, b, c, d) {
    a = a.split(".");
    d = d || globalThis;
    ...
};

When I replace goog.global = globalThis; with goog.global = this || self; and d = d || globalThis; with d = d || goog.global; (as it was previously), then the require imports works as before.

@gkdn
Copy link
Member

gkdn commented Feb 1, 2025

Ah I see. It seems like the code is loaded in different context than globalThis in commonjs setup.

I'm talking Closure folks it will be ok with reverting this behavior back.

Do you know if there a good way to reproduce this problem in rules_closure without introducing new deps so we can add some tests?

@rbsdc
Copy link
Author

rbsdc commented Feb 4, 2025

@gkdn Thank you for looking into it. Unfortunately, I am not very familiar with rules_closure. I have tried, but unfortunately - at least in the short time available - could not provide a meaningful test. I'm already looking forward to hearing how things are going.

@gkdn
Copy link
Member

gkdn commented Feb 5, 2025

Can you try bazelbuild/rules_closure#654 to double check if it fixes the problem?

(Haven't tried but pointing to commit c32bb7c37d532155ba4bbfe3d56383e4d519bc53 in rules_closure dep might do the job.)

@gkdn
Copy link
Member

gkdn commented Feb 5, 2025

Hmm I think it will be difficult to try it out since rules_closure head version switched to Bazel 8 and we haven't yet (pretty soon) though it seems like should fix the issue.

I think I will proceed with the patch. In the meantime, note that Closure Compiler team suggested a different setup for common.js: explicitly declare an extern for the commonjs 'module' object, and then explicitly export things onto the module.exports object, sort of like module["exports"]["className"] = thing;

Something to consider in case this might break again in the future.

@rbsdc
Copy link
Author

rbsdc commented Feb 7, 2025

@gkdn Thank you again! As mentioned, I am not too familar with closure, which is why I maybe do not fully understand your comment on the extern. What I tried is to the following: create an externs.js

/** @externs */
var module = {};
module.exports = {};

include it in the BUILD

... // as above
j2cl_application(
    .... // as above
    extra_production_args = [
        '--compilation_level=SIMPLE',
        '--externs=externs.js'
    ]
)

and then add to the following line the lib.js:

... // as above
module["exports"]["MyUtils"] = MyUtils;

when running bazel build, then I get the error could not determine the type of this expression module["exports"]["MyUtils"] = MyUtils;, pointing ^ to module. I also tried defining externs.js as separate closure_js_library and adding it to the deps of lib, but with the same result. What am I doing wrong?

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

No branches or pull requests

2 participants