I have a library of Java code from Android development and I'd like to reuse that in a web-app version of the same. The library code is quite generic as it was always intended to be re-usable and has, in the past, been used to generate Android/Java, App-Engine/Java, iOS/ObjC, and GWT apps.
Looking around, I think the best framework for the web app would be Angular. Rewriting the library code to Kotlin should be a relatively minor task as there are tools to do most of the work. Then it can be compiled for the JVM (for native and backend apps) or JavaScript (for web apps).
While advice for/against this plan is welcome, my actual question is...
How do I set up an IntelliJ project to do this?
I thought the obvious answer would be two modules: one for the lib and one for the app but IntelliJ doesn't allow creating a Kotlin module, only a Kotlin project.
Instead, I made a Kotlin/JS project and used Angular/CLI to create the app module beneath it (with a backend app to sit beside it sometime in the future). The library builds and the sample app runs but I haven't been able to get the latter to include the generated JS (plus .d.ts) code of the former which sits in some deep directory under build/. So maybe I'm going about it all wrong...
Wow, that was rough! After many hours of Google searches and attempts that failed, here is something that works. Perhaps I'll find cleaner methods down the road but this is acceptable for now.
Note: I wasted too much time on gradle plugins for my liking. If they're not first-party, they tend to be unmaintained, poorly documented (largely assuming that the reader already knows what they're doing), and out of date. What follows is done only with first-party support programs.
Create a top-level project with Angular/CLI followed by sub-projects for the app and library:
ng new MyGeneralProject --no-create-application
ng generate library klib
ng generate application MyApp
Now set up Gradle in the top-level directory (or wait and do this via the IDE):
gradle init
gradle wrapper
Open the top-level in IntelliJ. Create/edit the top-level build.gradle.kts file:
buildscript {}
plugins {}
repositories {}
More important is the top-level settings.gradle.kts file:
rootProject.name = "MyProject"
include("projects:klib")
This will now access the projects/klib/gradle.build.kts file:
plugins {
kotlin("js") version "1.8.0"
}
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib-js"))
testImplementation(kotlin("test-js"))
}
kotlin {
sourceSets {
all {
// allow #JsExport without opt-in each time
languageSettings.optIn("kotlin.js.ExperimentalJsExport")
}
val main by getting {
kotlin.srcDir("src/main")
}
val test by getting {
kotlin.srcDir("src/test")
dependencies {
implementation(kotlin("test"))
}
}
}
js(IR) {
moduleName = "klib"
browser {
distribution {
directory = File("$projectDir/../../dist/$moduleName")
}
}
binaries.library()
}
}
The above uses the kotlin("js") plugin for Gradle but that's provided by JetBrains as part of IntelliJ who also maintain the Kotlin language libraries. Though the documentation for it also assumes you already know everything about Gradle, it nonetheless works just fine.
The built JS/TS library will be created under the top-level directory as "dist/klib". This naming could use some improvement but it's a reasonable starting point.
In the top-level tsconfig.json file, under "compilerOptions", look for "paths" and update as desired. I went with a leading "#" to indicate a top-level import location:
{
...
"compilerOptions": {
...
"paths": {
"#klib": [
"dist/klib"
]
}
...
},
...
}
In the projects/myapp/src/app/whatever.ts file, access the converted Kotlin library:
import * as klib from '#klib'
Then the entire library is available as klib.blah.blah.blah with IntelliJ providing full completion semantics.
2023-02-07: I've found that while this approach worked okay for creating the desired Angular project, it fell apart when trying to add a jvm-based backend with which it shared code. Now trying a different tactic...
Related
I have been trying to build my gatsby (react) site recently using an external package.
The link to this package is "https://github.com/transitive-bullshit/react-particle-animation".
As I only have the option to change the props from the components detail, I cannot read/write the package file where it all gets together in the end as it is not included in the public folder of 'gatsby-build'.
What I have tried:
Editing the package file locally, which worked only on my machine but when I push it to netlify, which just receives the public folder and the corresponding package.json files and not the 'node-modules folder', I cannot make netlify read the file that I myself changed, as it requests it directly from the github page.
As a solution I found from a comment to this question, we can use the "Patch-Package" to save our fixes to the node module and then use it wherever we want.
This actually worked for me!
To explain how I fixed it: (As most of it is already written in the "Patch Package DOCS), so mentioning the main points:
I first made changes to my local package files that were giving the error.(For me they were in my node_modules folder)
Then I used the Patch Package Documentation to guide my self through the rest.
It worked after I pushed my changes to github such that now, Patch Package always gives me my edited version of the node_module.
When dealing with third-party modules that use window in Gatsby you need to add a null loader to its own webpack configuration to avoid the transpilation during the SSR (Server-Side Rendering). This is because gatsby develop occurs in the browser (where there is a window) while gatsby build occurs in the Node server where obviously there isn't a window or other global objects (because they are not even defined yet).
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
if (stage === "build-html") {
actions.setWebpackConfig({
module: {
rules: [
{
test: /react-particle-animation/,
use: loaders.null(),
},
],
},
})
}
}
Keep in mind that the test value is a regular expression that will match a folder under node_modules so, ensure that the /react-particle-animation/ is the right name.
Using a patch-package may work but keep in mind that you are adding an extra package, another bundled file that could potentially affect the performance of the site. The proposed snippet is a built-in solution that is fired when you build your application.
The guides for ember.js are assuming one has the full ES6 support e.g. http://guides.emberjs.com/v2.2.0/routing/specifying-a-routes-model/ shows using the export default construct and doesn't specify any alternative way to achieve the goals. However the module feature is not implemented in all browsers that ember is supporting.
How can I use these features with a browser that doesn't support modules? How would the code in these examples translate to ES5?
Documentation assumes you are using a transpiling tool, because the recommended tool, ember-cli does. Unless you have good reasons not to use it, you definitely should look into it.
It is, however, perfectly fine to work without it. For instance, without a module system, Ember will map controller:posts.index to App.PostsIndexController. So this should work for the example you linked:
App.Router.map(function() {
this.route('favorite-posts');
});
App.FavoritePostsRoute = Ember.Route.extend({
model() {
return this.store.query('post', { favorite: true });
}
});
You may also use Ember with your own module support. I successfully have an Ember project based on rollup. It does require a bit more work though, to have the resolver find your classes (that resolver link also documents how ember relates does the name mapping). Nothing hard, but you must build a short script to generate registrations.
Edit for blessenm: Ember with rollup
Unfortunately I cannot share this code, but it works like this:
A script scans the project directory and compiles templates by invoking ember-template-compiler.js on every .hbs file it encounters.
A script (the same one, actually) scans the project directory and generates the main entry point. It's pretty simple, if it sees, say gallery/models/collection.js and `gallery/routes/picture.js', it will generate a main file that looks like this:
import r1 from 'gallery/models/collection.js';
import r2 from 'gallery/routes/picture/index.js';
// ...
Ember.Application.initializer({
name: 'registrations',
initialize: function (app) {
app.register("model:collection", r1);
app.register("route:picture.index", r2);
// ...
}
});
It should just map your filenames to resolver names. As a bonus, you get to control how your directories are organized.
Invoke rollup on the generated file. It will pull everything together. I use IIFE export format, skipping all the run-time resolution mess. I suggest you setup rollup to work with babel so you can use ES6 syntax.
I don't use any ember-specific module, but it should not be too hard to add. My guess is it's mostly a matter of setting up rollup import resolution properly. For all I know, it may work out of the box.
You should look into using Ember CLI http://ember-cli.com/
You write your code in ES6 and it transpiles down to ES5.
Given
3 Node.js projects Main - Framework - Repositories
Main has the two other projects connected via npm link.
In a test i wrapped the require in a method. I've got some problems resolving linked projects (details see below)
Simplified code looks like this:
module.export.resolve = function(file){
[...]//Some more logik to handle relative pathes
return require(file)
}
This works fine in most scenarios. I also worked out to get handled with relatives pathes (looking up for caller and apply pathes based on this path)
Now this is in Project Framework which is linked (npm link) to Project Main. Project main has also Project Repositories linked.
Now in Project Main i have:
require('ProjectRepositories/foo') // Works as expected
myRequire.resolve('ProjectRepositories/foo') // Returns MODULE_NOT_FOUND "Cannot find module 'ProjectRepositories/foo'
I assume the problem is that Repositories Project ist not linked in the Framework Project. But is there an other way than linking them ?
I'd prefer to have less dependencies. Any hints on that?
You are absolutely correct in that the reason why the Project Framework resolve does not work is because the requireFn being used from within that project only knows about the modules installed in that framework. This is because when you require a javascript file, node evaluates the script within the context of the module, and not the context of the current project (this is how dependency modules' requires work when from your top-level script).
What you can do, however, is provide a way for the framework resolver to use a user-specified require function to do its work, once it has transformed the paths.
module.exports.resolve = function(file, resolver) {
//Some more logik to handle relative pathes
resolver = typeof resolver == 'function' ? resolver : require;
return resolver(file)
}
Now in your code, you could do
myRequire.resolve('ProjectRepositories/foo', require);
So now your Project Main require will be used to resolve the file.
You can also take this a step further if you want and have the module be stateful and remember the resolver it's supposed to use.
var _requireFn = require;
module.exports = {
resolve: resolve,
setRequireFn: setRequireFn
};
function resolve(path) {
return _requireFn(path);
}
function setRequireFn(requireFn) {
_requireFn = requireFn;
}
On another note, I would be careful about using the term resolve because in node that's semantically used for looking up the correct file path to be required, a la require.resolve.
Finally, in terms of minimizing dependencies, I'd recommend including your subprojects in npm using github repos. This has worked pretty well for me in the past, unless your two subrepos are in a constant state of flux. See the install docs for more info.
I want to publish a module to several component manager systems: npmjs, bower, etc... plus I want to create downloadable builds as well, for example one in AMD style for requirejs, one in commonJS style, one for the global namespace in browser, minified for each of them, etc... These are more than 10 builds.
I currently have a single AMD build, and I wrote unit tests for it using karma, jasmine and requirejs in an amd style. What do you suggest, how to generate the other builds and the tests for them?
I mean I cannot decide what should I have as a base of transformations. There is a common part in every output package, and there is a package dependent part either.
AMD - requirejs (I am not sure about using the config options)
define(["module", "dependency"], function (module, dependency) {
var m = {
config: function (options){
//...
},
//...
//do something with the dependency
};
m.config(module.config()); //load config options set by require.config()
return m;
});
commonJS
var dependency = require("dependency");
module.exports = {
config: function (options){
//...
},
//...
//do something with the dependency
};
global
var m = (function (){
return {
config: function (options){
//...
},
//...
//do something with the dependency
};
})(dependency);
I don't know, should I develop the common code and build before every test, or should I develop one of the packages, test it, and write a transformation from that into the other builds?
I intend to use gulp for creating the builds and call unit tests automatically for each of them before automatically publishing them. Ohh and ofc I need an auto version number change as well. Btw. is it necessary to call unit tests after the building procedure, what do you think? I just want to be sure, that not a buggy code is published...
There are transformation libraries for gulp:
https://github.com/phated/gulp-wrap-amd (commonjs to amd)
https://github.com/gfranko/amdclean (amd to standard js)
https://github.com/phated/gulp-wrap-umd (commonjs to umd I guess)
https://github.com/adamayres/gulp-wrap (not sure of its capabilities yet, maybe universal with custom templates, maybe nothing)
So it is easy to transform one package format to the two other formats. This can be done with the tests and the code as well. The commonjs can be tested with node and jasmine-node, the standard js can be tested with karma and jasmine, the amd can be tested with karma, requirejs and jasmine.
It would be a poor choice to create a common descriptor and convert that before every test. I don't want to create another language, like coffeescript, etc... so conversion between packages is okay.
Calling unit tests before publishing is okay. There are no other common package types, so this 3 packages will be enough for every component manager I will use.
I am unsure about the versioning. It is not hard to set it manually, but maybe a system like travis or maven could help more... I'll figure out and edit this answer.
Since coming across RequireJS I have started to adopt it wholeheartedly as it seems a great way of organising dependencies etc.
For my current project I have created a 'package' of requireJS-organised modules, which will provide the needed database API, to many node.js applications.
But I have come across a stumbling block ... how can I allow third party applications to use my package, without needing to faff with requireJS?
My directory structure for my applications and API is currently as follows:
api_package/node_modules
api_package/controllers/*
api_package/views/*
api_package/helpers/*
api_package/models/*
api_package/main.js
application_1/node_modules
application_1/app.js
application_2/node_modules
application_2/app.js
I need my applications to be self-contained - so they can be easily deployed - so my current work around is to copy api_package/* into the 'node_modules' directory of application_1 & 2 and setting the their requirejs config to the following:
(function() {
var requirejs;
requirejs = require('requirejs').config({
baseUrl: __dirname,
nodeRequire: require,
packages: [
{
name: 'api_package',
location: './node_modules/api_package'
}
]
});
This feels a little dirty and wrong!
Is there a better way? Am I missing some packaging feature for requireJS?
Is it even possible to hide the implementation details of my api_package (the detail being that I am using requireJS) and allow applications to use it as they would any other module:
require('api_package')
You can use the amdefine package, which allows you to code to the AMD API and have the module work in node programs without requiring those other programs to use AMD.
In addition to the documentation on the amdefine github page, this is also documented on the RequireJS website.