How to use the Google Closure Compiler to optimize Kotlin JS - google-closure-compiler

Is it possible to use the Google Closure Compiler to minify Kotlin JS code further than what Webpack offers? If so, how can it be done?

First, you should declare an NPM dependency on the Closure Compiler (latest version):
dependencies {
implementation(devNpm("google-closure-compiler", "20210808.0.0"))
}
Then, create a task that will run after webpack minification:
tasks.create<Exec>("compileWithClosure") {
// browserProductionWebpack: production minified version by Webpack
// kotlinNodeJsSetup: needed to execute Node scripts (':' because it is on the root project)
dependsOn("browserProductionWebpack", ":kotlinNodeJsSetup")
// Get the Node installation directory
val kotlinNodeJsSetup = rootProject.tasks["kotlinNodeJsSetup"] as org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsSetupTask
workingDir = File(kotlinNodeJsSetup.destination, "bin")
// Execute the script
commandLine(
"node",
"${File(rootProject.buildDir, "js/node_modules/google-closure-compiler/cli.js")}",
"--js=${File(buildDir, "distributions/<your module name here>.js")}",
"--js_output_file=${File(buildDir, "distributions/optimized.js")}",
"-O=SIMPLE",
"--env=BROWSER",
"--warning_level=QUIET",
)
}
Full list of arguments:
-O=ADVANCED seems to break the Kotlin generated file (at least in my case with Kotlin JS 1.5.21 IR)
-O=SIMPLE removes ~600kB from the binary (2.1MB → 1.5MB, using Kotlin React, KotlinX.Serialization & Ktor)
--warning_level=QUIET because we are passing an already-minified file, console output is completely unreadable, and actually slows down compilation

Related

Sharing SASS variables from NPM package with another app (#use / #forward syntax)

Here's my problem.
First, there's a design system repo, with SASS files. Those files contain variables (declared with !default syntax), functions, mixins, and class-related styling. Here's the simplified repo architecture :
/src
/components
/component1
/_component1.scss
/component2
/_component2.scss
/scss
/general
/tokens
/colors
/_colors.scss
/fonts
/_fonts.scss
/styles.scss
All partials are forwarded (using #forward at-rule) in src/scss/styles.scss. Repo has been published as a NPM package.
Then, there's another app, which aims to use this design system package. Simplified repo architecture looks like this :
/scss
/general
/tokens
/colors
/_app-colors.scss
/_colors.scss
/fonts
/_app-fonts.scss
/_fonts.scss
/styles.scss
I'd like to declare app-related tokens (in my scss/general/tokens/* files), which would override design system tokens if declared with the same identifier. I used to do that with #import at-rules, but I can't make it work with #use and #forward at-rules anymore...
I thought I could achieve it by using that kind of syntax in my tokens files :
#use "./app-color" as app;
#use "#design-system-package/src/scss/styles.scss" as ds with {
$design-system-variable-1: blue; // hardcoded value
$design-system-variable-2: app.$app-variable // app variable as value
$design-system-variable-3: ds.$design-system-other-variable // design system variable as value
}
but it seems ds namespace is not recognized, and ds.$design-system-other-variable is undefined.
Could anyone help me to fix that ? Thank you :
Silvère MAZIERE

Premake5 android makefile declare prebuilt library

Using premake5 file to generate make files for android. I am trying to produce the prebuilt library declaration as described in https://developer.android.com/ndk/guides/prebuilts.html
More precisely
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
include $(PREBUILT_SHARED_LIBRARY)
What kind of project would introduce the include include $(PREBUILT_SHARED_LIBRARY) or what other option do I need to include?
I have little knowledge in this, but I was able to use android PREBUILT_SHARED_LIBRARY - in premake4 using the following lua script:
The script is customised for the example you've give above.
linklibs = {"foo"}
libdirpaths = {"../../path/to/libs/"}
project "project_using_foo"
language "C"
kind "SharedLib"
files (android.srcfiles)
includedirs (includepaths)
location "build"
links (linklibs)
libdirs (libdirpaths)
buildoptions (buildoptions)
androidappabi {"armeabi-v7a"}
androidsdk "android-19"
where libfoo.so will be in ../../path/to/libs/$(TARGET_ARCH_ABI)/libfoo.so
[$(TARGET_ARCH_ABI) in this case is "armeabi-v7a"]

How to call our own .class postprocessor after compile?

My company is switching from ant to sbt to ease Scala integration into our huge Java existing code (smart move if you ask me).
After compiling, we usually post-process all the generated .class with a tool of our own which is a result of the compilation.
I have been trying to do the same in sbt and it appears more complicated than expected.
I tried:
calling our postprocessor with fullRunTask. Works fine but we would like to pass "products.value" to look for the .class files and it does not work
another and even better solution would be to extend compile (compile in Compile ~= { result => ...). But I did not found how the code after "result =>" can call our postprocessor
we are looking at other solutions: multiple projects, one for the postprocessor, one for the rest of the code and this would clean but because the source code is entangled, this is not as easy as it seems (and we still would have the first problem)
Any help?
I would just write a simple plugin that runs after the other stages. It can inspect the target folder for all the .class files.
You could then do something like sbt clean compile myplugin in your build server.
This is the approach taken by the proguard plugin[1]. You could take a look at that as a starting point.
[1] https://github.com/sbt/sbt-proguard
Finally, I found a solution after reading "SBT in Action" and other documents. It is very simple but understanding SBT is not (at least for me).
name := "Foo"
version := "1.0"
scalaVersion := "2.11.0"
fork := true
lazy val foo = TaskKey[Unit]("foo")
val dynamic = Def.taskDyn {
val classDir = (classDirectory in Compile).value
val command = " Foo "+classDir
(runMain in Compile).toTask(command)
}
foo := {
dynamic.value
}
foo <<= foo triggeredBy(compile in Compile)
The sample project contains a Foo.scala with the main function

How to share version values between project/plugins.sbt and project/Build.scala?

I would like to share a common version variable between an sbtPlugin and the rest of the build
Here is what I am trying:
in project/Build.scala:
object Versions {
scalaJs = "0.5.0-M3"
}
object MyBuild extends Build {
//Use version number
}
in plugins.sbt:
addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % Versions.scalaJs)
results in
plugins.sbt:15: error: not found: value Versions
addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % Versions.scalaJs)
Is there a way to share the version number specification between plugins.sbt and the rest of the build, e.g. project/Build.scala?
sbt-buildinfo
If you need to share version number between build.sbt and hello.scala, what would you normally do? I don't know about you, but I would use sbt-buildinfo that I wrote.
This can be configured using buildInfoKeys setting to expose arbitrary key values like version or some custom String value. I understand this is not exactly what you're asking but bear with me.
meta-build (turtles all the way down)
As Jacek noted and stated in Getting Started Guide, the build in sbt is a project defined in the build located in project directory one level down. To distinguish the builds, let's define the normal build as the proper build, and the build that defines the proper build as meta-build. For example, we can say that an sbt plugin is a library of the root project in the meta build.
Now let's get back to your question. How can we share info between project/Build.scala and project/plugins.sbt?
using sbt-buildinfo for meta-build
We can just define another level of build by creating project/project and add sbt-buildinfo to the (meta-)meta-build.
Here are the files.
In project/project/buildinfo.sbt:
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.3.2")
In project/project/Dependencies.scala:
package metabuild
object Dependencies {
def scalaJsVersion = "0.5.0-M2"
}
In project/build.properties:
sbt.version=0.13.5
In project/buildinfo.sbt:
import metabuild.Dependencies._
buildInfoSettings
sourceGenerators in Compile <+= buildInfo
buildInfoKeys := Seq[BuildInfoKey]("scalaJsVersion" -> scalaJsVersion)
buildInfoPackage := "metabuild"
In project/scalajs.sbt:
import metabuild.Dependencies._
addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % scalaJsVersion)
In project/Build.scala:
import sbt._
import Keys._
import metabuild.BuildInfo._
object Builds extends Build {
println(s"test: $scalaJsVersion")
}
So there's a bit of a boilerplate in project/buildinfo.sbt, but the version info is shared across the build definition and the plugin declaration.
If you're curious where BuildInfo is defined, peek into project/target/scala-2.10/sbt-0.13/src_managed/.
For the project/plugins.sbt file you'd have to have another project under project with the Versions.scala file. That would make the definition of Versions.scalaJs visible.
The reason for doing it is that *.sbt files belong to a project build definition at the current level with *.scala files under project to expand on it. And it's...turtles all the way down, i.e. sbt is recursive.
I'm not sure how much the following can help, but it might be worth to try out - to share versions between projects - plugins and the main one - you'd have to use ProjectRef as described in the answer to RootProject and ProjectRef:
When you want to include other, separate builds directly instead of
using their published binaries, you use "source dependencies". This is
what RootProject and ProjectRef declare. ProjectRef is the most
general: you specify the location of the build (a URI) and the ID of
the project in the build (a String) that you want to depend on.
RootProject is a convenience that selects the root project for the
build at the URI you specify.
My proposal is to hack. For example, in build.sbt you can add a task:
val readPluginSbt = taskKey[String]("Read plugins.sbt file.")
readPluginSbt := {
val lineIterator = scala.io.Source.fromFile(new java.io.File("project","plugins.sbt")).getLines
val linesWithValIterator = lineIterator.filter(line => line.contains("scalaxbVersion"))
val versionString = linesWithValIterator.mkString("\n").split("=")(1).trim
val version = versionString.split("\n")(0) // only val declaration
println(version)
version
}
When you call readPluginSbt you will see the contents of plugins.sbt. You can parse this file and extract the variable.
For example:
resolvers += Resolver.sonatypeRepo("public")
val scalaxbVersion = "1.1.2"
addSbtPlugin("org.scalaxb" % "sbt-scalaxb" % scalaxbVersion)
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.5.1")
You can extract scalaxbVersion with regular expressions/split:
scala> val line = """val scalaxbVersion = "1.1.2""""
line: String = val scalaxbVersion = "1.1.2"
scala> line.split("=")(1).trim
res1: String = "1.1.2"

Does sbt have something like gradle's processResources task with ReplaceTokens support?

We are moving into Scala/SBT from a Java/Gradle stack. Our gradle builds were leveraging a task called processResources and some Ant filter thing named ReplaceTokens to dynamically replace tokens in a checked-in .properties file without actually changing the .properties file (just changing the output). The gradle task looks like:
processResources {
def whoami = System.getProperty( 'user.name' );
def hostname = InetAddress.getLocalHost().getHostName()
def buildTimestamp = new Date().format('yyyy-MM-dd HH:mm:ss z')
filter ReplaceTokens, tokens: [
"buildsig.version" : project.version,
"buildsig.classifier" : project.classifier,
"buildsig.timestamp" : buildTimestamp,
"buildsig.user" : whoami,
"buildsig.system" : hostname,
"buildsig.tag" : buildTag
]
}
This task locates all the template files in the src/main/resources directory, performs the requisite substitutions and outputs the results at build/resources/main. In other words it transforms src/main/resources/buildsig.properties from...
buildsig.version=#buildsig.version#
buildsig.classifier=#buildsig.classifier#
buildsig.timestamp=#buildsig.timestamp#
buildsig.user=#buildsig.user#
buildsig.system=#buildsig.system#
buildsig.tag=#buildsig.tag#
...to build/resources/main/buildsig.properties...
buildsig.version=1.6.5
buildsig.classifier=RELEASE
buildsig.timestamp=2013-05-06 09:46:52 PDT
buildsig.user=jenkins
buildsig.system=bobk-mbp.local
buildsig.tag=dev
Which, ultimately, finds its way into the WAR file at WEB-INF/classes/buildsig.properties. This works like a champ to record build specific information in a Properties file which gets loaded from the classpath at runtime.
What do I do in SBT to get something like this done? I'm new to Scala / SBT so please forgive me if this seems a stupid question. At the end of the day what I need is a means of pulling some information from the environment on which I build and placing that information into a properties file that is classpath loadable at runtime. Any insights you can give to help me get this done are greatly appreciated.
The sbt-buildinfo is a good option. The README shows an example of how to define custom mappings and mappings that should run on each compile. In addition to the straightforward addition of normal settings like version shown there, you want a section like this:
buildInfoKeys ++= Seq[BuildInfoKey](
"hostname" -> java.net.InetAddress.getLocalHost().getHostName(),
"whoami" -> System.getProperty("user.name"),
BuildInfoKey.action("buildTimestamp") {
java.text.DateFormat.getDateTimeInstance.format(new java.util.Date())
}
)
Would the following be what you're looking for:
sbt-editsource: An SBT plugin for editing files
sbt-editsource is a text substitution plugin for SBT 0.11.x and
greater. In a way, it’s a poor man’s sed(1), for SBT. It provides the
ability to apply line-by-line substitutions to a source text file,
producing an edited output file. It supports two kinds of edits:
Variable substitution, where ${var} is replaced by a value. sed-like
regular expression substitution.
This is from Community Plugins.

Resources