jlink: package kotlin.* in both merged module and kotlin.stdlib - kotlin

Building a Kotlin JavaFX app via gradle (7.5.1), and trying to get packaging/installing with jlink/jpackage working. The application has a module-info.java:
module com.foo.bar {
requires javafx.controls;
requires javafx.fxml;
requires de.jensd.fx.glyphs.fontawesome;
requires kotlin.stdlib;
requires com.github.ajalt.clikt;
requires com.google.gson;
requires java.sql;
opens com.foo.bar to javafx.fxml, com.google.gson;
exports com.foo.bar;
}
gradle jlink runs without any issues, but on executing the script, the following error happens:
Error occurred during initialization of boot layer
java.lang.LayerInstantiationException: Package kotlin.* in both module com.foo.merged.module and module kotlin.stdlib
where * is some library (it changes every time). The build.gradle is as follows:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.6.20'
id 'org.openjfx.javafxplugin' version '0.0.13'
id 'org.beryx.jlink' version '2.25.0'
id 'application'
}
group 'com.foo'
version '1.0'
repositories {
mavenCentral()
}
dependencies {
implementation platform('org.jetbrains.kotlin:kotlin-bom')
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
implementation 'com.github.ajalt.clikt:clikt:3.5.0'
implementation 'com.google.code.gson:gson:2.10'
implementation 'de.jensd:fontawesomefx-fontawesome:4.7.0-9.1.2'
implementation 'org.postgresql:postgresql:42.5.0'
implementation 'org.xerial:sqlite-jdbc:3.39.3.0'
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
application {
mainModule = 'com.foo.bar'
mainClass = 'com.foo.bar.MainKt'
}
javafx {
version = '17.0.2'
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
jlink {
imageZip = project.file("${buildDir}/distributions/bar-${javafx.platform.classifier}.zip")
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher {
name = 'bar'
}
jpackage {
outputDir = 'packages'
installerType = 'dmg'
}
}
When taking a look at the suggested merged modules (gradle suggestMergedModuleInfo), there is no mention of any kotlin libraries
> Task :suggestMergedModuleInfo
mergedModule {
requires 'java.management';
requires 'java.naming';
requires 'java.logging';
requires 'java.security.sasl';
requires 'java.sql';
requires 'java.xml';
requires 'java.desktop';
requires 'java.transaction.xa';
requires 'java.security.jgss';
provides 'java.sql.Driver' with 'org.postgresql.Driver',
'org.sqlite.JDBC';
}
Edit: so the actual merged module does not seem to have any references to any kotlin libraries:
open module com.foo.merged.module {
exports com.github.ajalt.clikt.completion;
exports com.github.ajalt.clikt.core;
exports com.github.ajalt.clikt.internal;
exports com.github.ajalt.clikt.mpp;
exports com.github.ajalt.clikt.output;
exports com.github.ajalt.clikt.parameters.arguments;
exports com.github.ajalt.clikt.parameters.groups;
exports com.github.ajalt.clikt.parameters.internal;
exports com.github.ajalt.clikt.parameters.options;
exports com.github.ajalt.clikt.parameters.types;
exports com.github.ajalt.clikt.parsers;
exports com.github.ajalt.clikt.sources;
exports org.checkerframework.checker.compilermsgs.qual;
exports org.checkerframework.checker.fenum.qual;
exports org.checkerframework.checker.formatter;
exports org.checkerframework.checker.formatter.qual;
exports org.checkerframework.checker.guieffect.qual;
exports org.checkerframework.checker.i18n.qual;
exports org.checkerframework.checker.i18nformatter;
exports org.checkerframework.checker.i18nformatter.qual;
exports org.checkerframework.checker.index.qual;
exports org.checkerframework.checker.initialization.qual;
exports org.checkerframework.checker.interning.qual;
exports org.checkerframework.checker.lock.qual;
exports org.checkerframework.checker.nullness;
exports org.checkerframework.checker.nullness.qual;
exports org.checkerframework.checker.optional.qual;
exports org.checkerframework.checker.propkey.qual;
exports org.checkerframework.checker.regex;
exports org.checkerframework.checker.regex.qual;
exports org.checkerframework.checker.signature.qual;
exports org.checkerframework.checker.signedness;
exports org.checkerframework.checker.signedness.qual;
exports org.checkerframework.checker.tainting.qual;
exports org.checkerframework.checker.units;
exports org.checkerframework.checker.units.qual;
exports org.checkerframework.common.aliasing.qual;
exports org.checkerframework.common.reflection.qual;
exports org.checkerframework.common.returnsreceiver.qual;
exports org.checkerframework.common.subtyping.qual;
exports org.checkerframework.common.util.report.qual;
exports org.checkerframework.common.value.qual;
exports org.checkerframework.dataflow.qual;
exports org.checkerframework.framework.qual;
exports org.checkerframework.framework.util;
exports org.intellij.lang.annotations;
exports org.jetbrains.annotations;
exports org.postgresql;
exports org.postgresql.copy;
exports org.postgresql.core;
exports org.postgresql.core.v3;
exports org.postgresql.core.v3.adaptivefetch;
exports org.postgresql.core.v3.replication;
exports org.postgresql.ds;
exports org.postgresql.ds.common;
exports org.postgresql.fastpath;
exports org.postgresql.geometric;
exports org.postgresql.gss;
exports org.postgresql.hostchooser;
exports org.postgresql.jdbc;
exports org.postgresql.jdbc2;
exports org.postgresql.jdbc2.optional;
exports org.postgresql.jdbc3;
exports org.postgresql.jre7.sasl;
exports org.postgresql.largeobject;
exports org.postgresql.osgi;
exports org.postgresql.plugin;
exports org.postgresql.replication;
exports org.postgresql.replication.fluent;
exports org.postgresql.replication.fluent.logical;
exports org.postgresql.replication.fluent.physical;
exports org.postgresql.shaded.com.ongres.saslprep;
exports org.postgresql.shaded.com.ongres.scram.client;
exports org.postgresql.shaded.com.ongres.scram.common;
exports org.postgresql.shaded.com.ongres.scram.common.bouncycastle.base64;
exports org.postgresql.shaded.com.ongres.scram.common.bouncycastle.pbkdf2;
exports org.postgresql.shaded.com.ongres.scram.common.exception;
exports org.postgresql.shaded.com.ongres.scram.common.gssapi;
exports org.postgresql.shaded.com.ongres.scram.common.message;
exports org.postgresql.shaded.com.ongres.scram.common.stringprep;
exports org.postgresql.shaded.com.ongres.scram.common.util;
exports org.postgresql.shaded.com.ongres.stringprep;
exports org.postgresql.ssl;
exports org.postgresql.ssl.jdbc4;
exports org.postgresql.sspi;
exports org.postgresql.translation;
exports org.postgresql.util;
exports org.postgresql.util.internal;
exports org.postgresql.xa;
exports org.postgresql.xml;
exports org.sqlite;
exports org.sqlite.core;
exports org.sqlite.date;
exports org.sqlite.javax;
exports org.sqlite.jdbc3;
exports org.sqlite.jdbc4;
exports org.sqlite.util;
requires java.management;
requires java.naming;
requires java.logging;
requires java.security.sasl;
requires java.sql;
requires java.xml;
requires java.desktop;
requires java.transaction.xa;
requires java.security.jgss;
provides java.sql.Driver with org.postgresql.Driver, org.sqlite.JDBC;
}

Not sure why this is needed, but specifying forceMerge 'kotlin' in the jlink block fixes the issue. See https://badass-jlink-plugin.beryx.org/releases/latest/#_methods

Related

unresolved supertypes: javax.sql.DataSource

I am seeing the following error when trying to compile using gradle.
Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
class com.zaxxer.hikari.HikariDataSource, unresolved supertypes: javax.sql.DataSource
Adding -Xextended-compiler-checks argument might provide additional information.
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
//kotlin("jvm") version "1.6.0"
}
group = "com.jasin"
version = "0.0.1"
dependencies {
//api(files("libs/jdbc-stdext-2.0.jar"))
implementation("org.ktorm:ktorm-core:3.4.1")
implementation("org.ktorm:ktorm-support-mysql:3.4.1")
implementation("mysql:mysql-connector-java:8.0.25")
implementation("com.zaxxer:HikariCP:5.0.1")
implementation("org.slf4j:slf4j-api:1.7.36")
implementation("org.slf4j:slf4j-simple:1.7.36")
testImplementation(kotlin("test"))
}
tasks.test {
useJUnit()
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "11"
}
If I uncomment the following line
//api(files("libs/jdbc-stdext-2.0.jar"))
I get the following type errors
module jdbc.stdext reads package javax.sql from both jdbc.stdext and java.sql
If I recomment this line and try to build again(without cleaning the project), it works. As soon as I clean the project the error reappears. What is exactly going on here and how can I fix this? Any help would be appreciated.
edit: I should add that this is a multi-module project and it wasn't till I tried to define module-info.java files in separate modules that this error appeared. But the error is specific to one module, the module that I use to access my database that uses connection pooling with hikaricp.
module-info.java file
module com.cobis.db {
requires kotlin.stdlib;
requires com.zaxxer.hikari;
requires ktorm.core;
exports com.cobis.db;
exports com.cobis.db.entities;
exports com.cobis.db.utilities;
}

How to use environment variables with static JS in public folder

I have VueJS app (Vue CLI 3) and additional static JS script in public folder. And I don't understand how I can use .env in this .js.
Let's say I have some specific environment variable, for example MY_URL and my JS file:
const myUrl = process.env.VUE_APP_MY_URL;
And it's not working, because static files from public folder don't processed by webpack as I understand.
Maybe someone knows good solution? Or maybe other solutions\workarounds?
In my case, I put .js to src and add new entry by chainWebpack:
config.entryPoints.delete('app')
config.entry('app')
.add('./src/main.ts')
.end()
.entry('myScript')
.add('./src/myScript.js')
.end()
And now webpack build the script as separate file, but injects to index.html with app.js. This is not what I really want.
So, main purpose - build separate static JS file with specific name without hash (for example, myScript.js) which would contain variable from .env (.env.production, .env.development)
Main fact about static files in public folder from docs:
Static assets placed in the public directory will simply be copied and not go through webpack
So, I cannot use .env with static files in public.
I haven't found a perfect solution, but at least 3 acceptable options:
JS file as entry, as Jesse Reza Khorasanee said in comments and gave a link to almost same question
The main idea:
configure vue.config.js for an additional entry and force webpack to process my file.
// vue.config.js
module.exports = {
configureWebpack: {
entry: {
public: "./public/main.js"
},
output: {
filename: "[name]/[name].main.js"
}
}
};
This solution would work with certain features:
at least two entry points: main entry for my SPA (main.js) and additional entry just for my static JS.
It's not good, because processed JS would contain a link to vendors.js chunk as one of the entries. But I need JS file processed by webpack only.
same output.filename and hash in filename with clear config (it's not work, because I use this script as 3rd party JS and load by static name), or different output.filename for my JS file but with dirty config:
configureWebpack: config => {
config.output.filename = (pathData) => {
return pathData.chunk.name === 'myScript'
? '[name].js' : '[name].[hash].js';
};
...
}
If I leave my JS in public folder I get two files after build: one in default js folder with other static assets and another in root folder near main.js
Multi-Page Application (configuration for Vue multi-page mode)
module.exports = {
pages: {
index: {
// entry for the page
entry: 'src/index/main.js',
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// when using the entry-only string format,
// template is inferred to be `public/myScript.html`
// and falls back to `public/index.html` if not found.
// Output filename is inferred to be `myScript.html`.
myScript: 'src/myScript.js'
}
}
This solution would work almost like the first solution and I get a clear config file. But still I have problem with vendors.js seems like pages option work directly with html-webpack-plugin and I can config chunks which would load with my page, and I tried different ways to setup this option but without success. Vendors is still part of myScript entry.
Build JS file as library
I chose this solution in my case. Because it's clear and short.
I put additional script to my package.json: vue-cli-service build --no-clean --target lib --name paysendPaymentLibrary src/payment.js and change main build script.
Final version of package.json:
...
"scripts": {
"build": "vue-cli-service build && npm run build-library",
"build-library": "vue-cli-service build --no-clean --target lib --name myScriptLibrary src/myScript.js"
},
...
After run npm run build I get static files for SPA and three files for my script:
myScriptLibrary.umd.min.js
myScriptLibrary.umd.js
myScriptLibrary.common.js
For 3rd party site I use myScriptLibrary.umd.js file.
If you choose this solution be careful when you build your application, because:
in Windows vue-cli-service build & npm run build-library scripts would run sequentially, but in Unix it runs in parallel. It can cause deletion of your SPA files. So be sure to use && instead of & (see discussions about environments and parallel\sequential script running)
size of processed files would be bigger than raw static JS. For example, in my case raw file size: 4 KiB, after build: 15.44 KiB, gzipped: 5.78 KiB.

Webpack plugin: how to read files of plugin consumer at build time?

Is it possible to read the contents of the repo which installed my Webpack plugin?
Essentially, I've written a particular JS library which requires the caller to use require.context in their application code to read some files at build time and pass them to the library:
import Library from 'library';
new Library({
requires: require.context('./foo', false, /\w+\.js$/),
});
I would like to take the burden off of the user and just make that require.context call inside the library to simplify the API to this:
import Library from 'library';
new Library();
However, it doesn't work because the library has no knowledge of when the application code's Webpack build ran.
I've tried writing a Webpack plugin inside the library:
const path = require('path');
class TestPlugin {
apply(compiler) {
compiler.hooks.beforeRun.tap('TestPlugin', () => {
console.log(
`Path where webpack was executed: ${compiler.options.context}`
);
});
}
}
module.exports = {
...
plugins: [new TestPlugin()],
};
However, this only gets called when the library itself is built not when the application is built.
How can I move the require.context call into the library (which is an NPM dependency) and still have it read the caller's files?

Using the Material Design package for Angular2

According to the angular/material2 site, component packages can be installed with commands like npm install #angular2-material/checkbox.
When I used this command, the #angular2-material/checkbox folder doesn't have a checkbox.ts file for the checkbox element. Instead, it has a checkbox.d.ts file that declares a regular class named MdCheckbox. This isn't a component, so I can't access it in my Angular2 template.
When I download the full angular/material2 archive, the src folder contains checkbox.ts, which defines MdCheckbox as a component. So is there a problem with the npm package or the GitHub archive?
In short, no there is no problems in it. The checkbox.d.ts file isn't a component file. It's just a declaration file. The actual file that has the component is checkbox.js, it's already been transpiled to javascript.
In order to use it, "assuming you have SystemJS as your module loader, which is the default". In your index.html
System.config({
map:{
'#angular2-material/checkbox':'node_modules/#angular2-material/checkbox'
},
packages: {
'#angular2-material/checkbox': {
format: 'cjs',
defaultExtension: 'js',
main: 'checkbox.js'
},
app:{...}
}
});
And then, in your component:
import {MdCheckbox} from '#angular2-material/checkbox';

How to create an "external module" typescript definition file to include with an npm package?

I recently added a typescript definition file for the open source redux-ui-router library, but I'm now getting errors like the following with Typescript 1.7.3:
error TS2656: Exported external package typings file
'C:/.../node_modules/redux-ui-router/index.d.ts' is
not a module. Please contact the package author to update the package
definition.
I am trying to import this library with code like this in my typescript files:
import ngReduxUiRouter from "redux-ui-router";
I'm new to Typescript, and I can't find a clear description of what exactly this definition file should look like when included with an npm package. There's a wiki entry that talks about typings for npm packages, but outside of repeating the direction that an external module should be used, there's not a concrete example to work from.
CORRECTION
I've tried removing the declare module "redux-ui-router" { code, and that seemed to work after restarting webpack, which I'm using to compile everything (I removed the comments for brevity):
export interface ReduxUIRouterAction {
type: string;
payload: any;
}
export interface ReduxUIRouterState {
currentState: Object;
currentParams: Object;
prevState: Object;
prevParams: Object;
}
export function router(state: ReduxUIRouterState, action: ReduxUIRouterAction): ReduxUIRouterState;
export var ngReduxUiRouter: string;
export function stateGo(to: string, params?: Object, options?: Object): ReduxUIRouterAction;
export function stateReload(state: any): ReduxUIRouterAction;
export function stateTransitionTo(to: string, params?: Object, options?: Object): ReduxUIRouterAction;
export default ngReduxUiRouter;
Is this set of changes what would be expected when including this in an npm package?
Is this set of changes what would be expected when including this in an npm package?
Yes. The exports need to be root level at the file.
In other words : an ambient file is not an external module