Skip to content

Instantly share code, notes, and snippets.

@CameronProbert
Created October 20, 2021 20:12
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CameronProbert/85b7d60fa9572d93566f5c5ee62441e0 to your computer and use it in GitHub Desktop.
Save CameronProbert/85b7d60fa9572d93566f5c5ee62441e0 to your computer and use it in GitHub Desktop.
Using environment variables in Kotlin/JS + Gradle

How it works

Note: This gist is using the Kotlin legacy compiler, and is set to use Webpack version 4 in gradle.properties. It may or may not work with the Kotlin IR compiler and Webpack version 5+.

build.gradle.kts

build.gradle.kts accepts a 2 custom build arguments, -PbuildMode and -PenvTarget.

buildMode can be "DEBUG" (default) or "RELEASE". This determines whether the build process will run webpack in dev or prod configuration. Dev mode creates source maps, while prod mode minifies and does tree shaking and so on.

envTarget can be "DEV" (default) or "PROD". We will use this as an argument to the webpack build to select the correct set of env variables. If you need more options you can add more to the when statement. This envTarget is set as arguments to the webpack build and run tasks in kotlin { js { browser { } } }

envConfig.js

This adds to the generated webpack.config a webpack.EnvironmentPlugin that will allow you to access the environment variables in your code. We can set different variables for "DEV" vs "PROD".

Process.kt

This is a wrapper for the process global variable in Javascript, so that we do not need to access our variables using js("process.env.<variable name>").

ProcessEnvVariables

Definitions for each of the env variables so that they can be strictly typed and auto-completed in our Kotlin code.

/* <rootdir>/build.gradle.kts */
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode.*
/**
* The build type argument
*
* Supports `DEBUG` and `RELEASE`
* Defaults to `DEBUG` if not specified
*/
val buildModeArg = (project.findProperty("buildMode") as String?)?.toUpperCase()
// Convert the argument to the Webpack Build Mode
val buildMode = when (buildModeArg) {
null, "DEBUG" -> DEVELOPMENT
"RELEASE" -> PRODUCTION
else -> throw Exception("Invalid buildMode: '$buildModeArg'. Expected 'DEBUG' or 'RELEASE'.")
}
/**
* The environment to target for target database, analytics, etc...
*
* Supports `DEV` and `PROD`
* Defaults to `DEV` if not specified
*/
val envTargetArg = (project.findProperty("envTarget") as String?)?.toUpperCase()
val envTarget = when (envTargetArg) {
"DEV", "PROD" -> envTargetArg
null -> "DEV"
else -> throw Exception("Invalid envTarget: '$envTargetArg'. Expected 'DEV' or 'PROD'.")
}
logger.lifecycle("Building with buildMode = $buildMode and envTarget = $envTarget")
buildscript {
// ...
}
plugins {
// ...
}
repositories {
// ...
}
group = "..."
version = "..."
kotlin {
js {
browser {
commonWebpackConfig {
mode = buildMode
// stop default export of config object in Webpack code because we do it in envConfig.js webpack extension
export = false
}
// from https://discuss.kotlinlang.org/t/kotlin-js-react-accessing-configuring-environment-variables/16906/9
// Set the webpack variables for the build task (webpackTask - for compiling your code to run elsewhere) and
// the run task (runTask - for running your code locally)
val envTargetWebpackArgs = listOf("--env.envTarget=$envTarget")
webpackTask { args.plusAssign(envTargetWebpackArgs) }
runTask { args.plusAssign(envTargetWebpackArgs) }
}
// ...
}
// ...
}
dependencies {
// ...
}
/* <rootdir>/webpack.config.d */
// from https://discuss.kotlinlang.org/t/kotlin-js-react-accessing-configuring-environment-variables/16906/7
module.exports = env => {
const webpack = require("webpack");
var apiKey;
var projectId;
var appId;
var authDomain;
var databaseUrl;
var messagingSenderId;
var measurementId;
if (env.envTarget === "PROD") {
apiKey = "...";
projectId = "...";
appId = "...";
authDomain = "...";
databaseUrl = "...";
messagingSenderId = "...";
measurementId = "...";
} else {
apiKey = "...";
projectId = "...";
appId = "...";
authDomain = "...";
databaseUrl = "...";
messagingSenderId = "...";
measurementId = "...";
}
const environmentPlugin = new webpack.EnvironmentPlugin(
{
// Firebase Config
FIREBASE_API_KEY: apiKey,
FIREBASE_PROJECT_ID: projectId,
FIREBASE_APP_ID: appId,
FIREBASE_AUTH_DOMAIN: authDomain,
FIREBASE_DATABASE_URL: databaseUrl,
FIREBASE_MESSAGING_SENDER_ID: messagingSenderId,
FIREBASE_MEASUREMENT_ID: measurementId,
})
config.plugins.push(environmentPlugin)
return config
};
/* <rootdir>/src/.../Process.kt */
package js
/**
* Wrapper for the Javascript `process` object.
*/
public external val process: Process
public external interface Process {
/**
* Wrapper for the Javascript `process.env` object.
*
* Environment variables are able to be set into this object during webpack compilation from
* `<root dir>/webpack.config.d`.
*/
public val env: ProcessEnvVariables
}
/* <rootdir>/src/.../Process.kt */
package js
/**
* Environment variables, set in `<rootdir>/webpack.config.d/envConfig.js`.
*/
public external object ProcessEnvVariables {
public val FIREBASE_API_KEY: String
public val FIREBASE_PROJECT_ID: String
public val FIREBASE_APP_ID: String
public val FIREBASE_AUTH_DOMAIN: String
public val FIREBASE_DATABASE_URL: String
public val FIREBASE_MESSAGING_SENDER_ID: String
public val FIREBASE_MEASUREMENT_ID: String
}
@keith-miller
Copy link

Thanks for this! For Webpack 5, I had to change how the command line arguments are passed from Gradle to

args.plusAssign(listOf("--env", "buildMode=$buildMode"))

@DFedonnikov
Copy link

Perfect, exactly what I was looking for. Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment