question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Enable Closure when emitting an ES module.

See original GitHub issue

When the ES module option is active:

scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) }

the js file obtained trough fullOptJS isn’t minified and project-opt.js is only 3% smaller in size than project-fast-opt.js

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:2
  • Comments:25 (21 by maintainers)

github_iconTop GitHub Comments

3reactions
yuriquecommented, Aug 22, 2022

@gzm0 I’ve tried playing with it a bit.

To be honest I’m not sure if I know what I’m doing =); nevertheless, I managed to get the JSC_INVALID_MODULE_PATH. Invalid module path "foo.js" error.

This error happens when we try to do import from "file.js" with the BROWSER resolution mode enabled (which is by default, apparently) (described here).

Changing the import to be import from "./file.js" (note the ./) makes the error go away.

There is also the NODE resolution mode (it is also described at the page linked above):

options.setModuleResolutionMode(ResolutionMode.NODE)

It will also want the ./ for the “local” imports, but it will go into the node_modules if there is no ./ (/cc @armanbilge).

I also tried another option: Transpiling Dynamic Import

options.setDynamicImportAlias("__scala_js_import__")

Which is supposed to make closure “support” dynamic imports even when the output is lower than ECMASCRIPT_2020:

The presence of this flag instructs the compiler to allow dynamic imports even when the output language is lower than ECMASCRIPT_2020

But it didn’t work for me - it complains:

SEVERE: test.js:5:6: ERROR - [JSC_LANGUAGE_FEATURE] This language feature is only supported for ECMASCRIPT_2020 mode or better: Dynamic module import.
  5|       import("./bar.js").then((bar) => bar.q(z))

I think this is why: https://github.com/google/closure-compiler/issues/3941

Not sure if this is helpful at all. I hope it is 😃


P.S. When running this:

  import com.google.javascript.jscomp.{
    SourceFile => ClosureSource,
    Compiler => ClosureCompiler,
    CompilerOptions => ClosureOptions,
    _
  }

  val options = new ClosureOptions
  options.setPrettyPrint(true)
  CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options)

  options.setLanguage(ClosureOptions.LanguageMode.ECMASCRIPT_2015)
  options.setWarningLevel(DiagnosticGroups.GLOBAL_THIS, CheckLevel.OFF)
  options.setWarningLevel(DiagnosticGroups.DUPLICATE_VARS, CheckLevel.OFF)
  options.setWarningLevel(DiagnosticGroups.CHECK_REGEXP, CheckLevel.OFF)
  options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.OFF)
  options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, CheckLevel.OFF)

  val ScalaJSExterns = 
    """
    function callMe(x) {};    
    """

    val chunk = new JSChunk("chunk1")
    chunk.add(
      ClosureSource.builder().withPath("test.js").withContent("""
      import { x as y } from './foo.js'
      const z = y();
      callMe(z);
      """
    ).build())
    chunk.add(
      ClosureSource.builder().withPath("foo.js").withContent("""
      export const x = () => "Hi!"
      """
    ).build())

  compiler.compileModules(
    Arrays.asList(ClosureSource.fromCode("ScalaJSExterns.js", ScalaJSExterns)),    
    Arrays.asList(chunk),
    options
  )

  val source = compiler.toSource
  println(source)

it prints:

'use strict';
callMe("Hi!");

P.P.S.

I also tried publishLocaling the compiler and linking a simple project with these settings:

      _.withModuleKind(ModuleKind.ESModule)
      .withESFeatures(_.withESVersion(ESVersion.ES5_1))
      .withModuleSplitStyle(ModuleSplitStyle.SmallModulesFor(List("myapp")))
      .withClosureCompiler(true) 

Had to add this to the ClosureAstTransformer (as well as the case ImportNamespace(binding, from) => ... that @sjrd posted above):

      case Export(bindings) => 
        val specs = new Node(Token.EXPORT_SPECS)
        bindings.foreach { case (ident, exportName) =>
          specs.addChildToBack(
            new Node(
              Token.EXPORT_SPEC, 
              Node.newString(ident.name),
              Node.newString(exportName.name)
            )
          )
        } 
        new Node(Token.EXPORT, specs)

I think it worked (although I messed something and it looks like fastLinkJS and fullLinkJS switched roles 😃 ). But in this small project there are no imports, so that doesn’t say anything.

Also when I tried .withModuleSplitStyle(ModuleSplitStyle.SmallestModules) (hoping to get those imports) scalajs refused to link it:

(Compile / fastLinkJS) java.lang.IllegalArgumentException: requirement failed: Cannot use multiple modules with the Closure Compiler
2reactions
sjrdcommented, May 20, 2022

There’s apparently this thing now in GCC: https://github.com/google/closure-compiler/blob/be4a58341ee03da20fa2104ba90792745ccb3cfb/src/com/google/javascript/jscomp/CompilerOptions.java#L1164-L1171 We could try and set it to false in ClosureLinkerBackend.closureOptions and see what happens. 🤷‍♂️

Read more comments on GitHub >

github_iconTop Results From Across the Web

Closure compiler generates very long variable names in output
I am using the Closure compiler to compile a JavaScript file together with a couple of very simple ES6 modules. ... Is this...
Read more >
Compiler Options - ClojureScript
A new option for emitting Google Closure Modules. Closure Modules supports splitting up an optimized build into N different modules.
Read more >
Importing external ES6 modules - Google Groups
I have a project being built with the 20160315 version of the compiler, I use the closure compilers own module mechanism to group...
Read more >
file-loader - webpack - JS.ORG
This will emit file.png as a file in the output directory (with the ... By default, file-loader generates JS modules that use the...
Read more >
Documentation - ECMAScript Modules in Node.js - TypeScript
This setting controls whether .js files are interpreted as ES modules or ... When TypeScript emits these to JavaScript files, it will emit...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found