Embedded images (files) in Kotlin JS (MPP)

Hi all,

I’m trying really hard to embed an image into kotlin JS library bundle. Summary: I’m developing a chart library for a list of platforms (JVM, Android, JS) with kotlin MPP. Have been quite successful so far, but now I need to find a way to use images while rendering (in my case - UI icons, in future also fonts), so I need to somehow embed the icons (png, byte sequences) into the bundle. Also need to warn you, I’m really a newbie with JS and not always can tell if I’m doing something correct.

First about the project setup.

kotlin version: 1.4.30

JS compiler: IR

jsMain dependencies:

                implementation(kotlin("stdlib-js"))
                implementation("org.jetbrains.kotlinx:kotlinx-html-js:$kotlinxHtmlVersion")
                implementation(npm("is-mobile", "3.0.0"))
                implementation(npm("url-loader", "4.1.1"))
                implementation(npm("file-loader", "6.2.0"))
                implementation("org.jetbrains:kotlin-react:16.13.1-pre.113-kotlin-1.4.0")
                implementation("org.jetbrains:kotlin-react-dom:16.13.1-pre.113-kotlin-1.4.0")

webpack config:

config.module.rules.push({
    test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
    use: [
          {
            loader: 'url-loader',
            options: {
              limit: false,
              encoding: true,
              name: '[name].[ext]',
            },
          },
    ],
});

config.module.rules.push({
    exclude: [
     /\.html$/,
                                   /\.(js|jsx)$/,
                                   /\.css$/,
                                   /\.json$/,
                                   /\.bmp$/,
                                   /\.gif$/,
                                   /\.jpe?g$/,
                                   /\.png$/,
                                 ],
                                 loader: require.resolve('file-loader'),
                                 options: {
                                   name: 'static/media/[name].[hash:8].[ext]',
                                 },
                               });

So, with all that, I am (or I suppose I am) telling webpack to include needed files (for now images only) into the bundle. url-loader needs file-loader, and I also tell file-loader not to include anything (to avoid duplication). However, i get duplicated files anyway; at ‘build/distributions’ I get the following structure:
images
| cam.png
cam.png
library.js
library.js.map

So there are two images, I can’t understand why.

The next problem is, when I require this ‘module’ in kotlin code:
val b = kotlinext.js.require(“cam.png”)
val a = JSON.stringify(b)

When I print ‘a’, it says:
{“default”:“cam.png”}

The resulting problems list:

  1. kotlinext.js.require only available with react dependencies. In standard kotlin JS there is only a require(Boolean): Unit method, it’s not what I need (I need just file content)
  2. kotlinext.js.require works only with full absolute path. With used above it can’t find my file
  3. resulting file (b) is empty, or I just don’t know how to get it’s contents properly.
  4. (already mentioned) with my webpack config images are duplicated. Moreover, I would like embedded files to be base64 encrypted, so I set url-loader’s ‘encoding’ to true (base64 default as far as I know)
  5. when I try to build a showcase bundle using my library (Kotlin JS app), it also needs the same webpack config for itself, without it my ‘module’ (the image) can’t be detected. Will my library users need to always copy this config at every usage level? If so, it would be not very pleasant, maybe there is a way to avoid this?

Please help. Looking forward to any replies

Hello @s0larm00n,
I have encountered the same problem recently. The detailed solution is as follows:

Webpack config:

config.resolve.modules.push("src/main/resources");
// webpack config...

Define camImage:

@JsModule("cam.png")
@JsNonModule
external val camImage: dynamic

Usage:

val camImageUrl = camImage.default