I have been trying to use closure compiler to optimize and bundle a project for two weeks now.
The project is originally written in TypeScript. So I wanted to use Tsickle to transpile to JS that would be easily fed to the closure compiler Java app. When I was finally able to do that I stumbled on external Node modules problems. I tried all the solutions I was able to find in Google Groups, SO and in the closure compiler repo. Nothing worked.
Wanting to not let this go I decided to use gulp. This is my gulpfile.js, I tried keeping it as simple as possible.
const closureCompiler = require("google-closure-compiler").gulp();
gulp.task("js-compile", function () {
return gulp
.src("./src/**/*.js", { base: "./" })
.pipe(
closureCompiler(
{
compilation_level: "ADVANCED",
warning_level: "VERBOSE",
jscomp_off: "checkVars",
js_output_file: "output.min.js"
},
{
platform: ["native", "java", "javascript"]
}
)
)
.pipe(gulp.dest("./dist/js"));
});
There are way too many input files for me to put them. This time I used TSC instead of Tsickle to transpile.
The error when running gulp I get is
[JSC_REDECLARED_VARIABLE_ERROR] Illegal redeclared variable: *nameofvariable*
I have this for almost every file in my project, even if the name is not repeated twice in the same file.
You may try tscc. It uses tsickle under the hood and provides some solutions to external node_modules problems.
Try --env CUSTOM or --isolation_mode IIFE. I ran into this when compiling code that conflicted with browser interfaces like Node.
Related
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...
I have a nodejs project (in JS). Unfortunately, I have to utilize a lot of node global variables.
Everything works fine (even a lot of people are suggesting not to use globals) except thing:
There is no intellisense for globals. So every time I want to use, let's say, global function/object I need to look in its code and figure out what are the
parameters, what does it return, etc.
Let's say I have a global variable which is a pure object:
foo = {
bar: {
level2: {
level3: {
level4: "abc
}
}
}
}
It's quite annoying to deal with it since I can't "see" the structure of the object when using it and it's easy to make a mistake when writing code.
The reason why I posted this question is the ...npm packages
There are plenty of packages written in vanilla JS and most of them are utilizing the power d.ts files.
Once you install the package you can use it from any place in your projects and VS code will have intellisense for them. If you will click on tooltip (IDK how it's called... Type definition tooltip?) of VS code
you will be navigated to the d.ts file of the package (not the actual implementation of the command).
So my question is how to do the same in my project. I'm not going to publish it as npm I just want a d.ts file somewhere in the project so I can
use my global without looking into its implementation every time I need to recall what it does.
Let inside your .d.ts file be anything
To access variables, functions, interface add this line in your .ts file, VS code IntelliSense will suggest you
/// <reference path="./test.d.ts" />
If you want to use this test.d.ts all over your project not just on any particular file. Then add this line in tsconfig.json
"files" : [ "./src/test.d.ts" ]
Update as mentioned in the comment section
my js file which I am assuming similar to what you are trying to do
export const testString = 'aditya';
in your js file you
/// <reference path="test.js" />
Regardless of if it is possible or not, the worst drawback of what you are looking for is that the declaration file(s) must be kept updated by hand each time a global variable/function is changed.
There are plenty of packages written in vanilla JS and most of them are utilizing the power d.ts files.
Usually .d.ts files are not written by hand, but are produced by tsc: many of the packages you are speaking about are probably written in TypeScript and distributed as JavaScript packages (to be used in JavaScript projects as well) with an associated index.d.ts file (to be used in TypeScript projects)
even a lot of people are suggesting not to use globals
+1
This is just something I thought today and I didn't see a lot of information so I'm going to share this weird cases and how I personally solved them (if there's a better way please comment, but meanwhile this might help others ^^)
In a regular module, you would do something like this to export your function/library/object/data:
// regular NodeJS way:
module.exports = data;
// ES6 way
// (will get transpiled to the regular way using the module variable by webpack)
export data;
default export data;
When compiling the library usually babel or tsc are used, but if for any reason you want not only to compile (transpile) your library but also pack it using webpack, you will encounter this case.
As you know, in a webpack bundle the module variable is local to the bundle (every module/file gets wrapped with a function where module is a parameter = local variable), so nothing really gets exported outside the bundle, is just nicely managed by webpack.
That means that you can't also access the contents using the regular require/import methods.
In some case you might find necessary to export outside webpack. (i.e. you are trying to build a library using webpack and you want it to be accessible by other people). This basically means you need to access the original module variable, but webpack doesn't expose it like it happened with __non_webpack_require__.
See also: Importing runtime modules from outside webpack bundle
The solution is to create our own __non_webpack_module__ (as webpack does with __non_webpack_require__.
How I did it is using webpack.BannerPlugin to inject some code outside the bundle. This code is prepended to the build after the minification is done, so it's preserved safely.
In your webpack.config.js:
plugins: [
new BannerPlugin({
raw: true,
banner: `const __non_webpack_module__ = module;`,
}),
]
And again, if you are using TypeScript, in global.d.ts:
declare const __non_webpack_module__: NodeModule;
And now, you can do something like this in your code:
__non_webpack_module__.exports = /* your class/function/data/whatever */
This will allow to import it as usual from other files
Tip: You might want to look at BannerPlugin to check other options, like include or exclude so this variable is only generated on the desired files, etc.
My web project serves static web pages and scripts. There is no preprocessing done at all. All changes are done in the client.
It has a main page that lists some other pages. When the user clicks a link, jQuery-UI will load the associated HTML page and any linked Javascript/CSS files.
This works great, and gives us flexibility to add/remove new pages with ease. The problem is when we want to debug the loaded JS and the browser appears not to know about it.
Took me a while to find out about Source Maps, and then find out they are all geared towards framework projects like Angular and React.
We don't want that in this project. Just basic HTML & JS that we can plug in and reload. I realize we may need to run an external command to generate the source maps, but it must be a free standing tool - no NPM or frameworks.
It's an internal web project, so security/privacy is not a concern. We want the clients to see the source code if they need to.
I know there are a lot of Questions about JS source maps, but every single one that I've found assumes using some framework tools. We have no framework and do not want one in this project.
Any suggestions on how we can generate the source maps we need, or do you know of any alternative to debug simple JS loaded via jQuery?
First and foremost, you do not need to use Angular/React for sourcemaps to work. These are just a common use case.
Secondly, NPM is exactly what it says it is; a package manager. So you don't need NPM either.
What you need is a build process. You're quite clear that you don't want to minify the js, but you do want sourcemaps. This is a common configuration used to debug js, and is typically accomplished by "building" or "Uglifying" the code with all of the optimizations disabled.
You could likely avoid NPM entirely if you were willing to use the Closure Compiler, but that is a can of worms and I'd suggest you avoid.
Instead I suggest using installing Uglify* globally* (per dev machine) with NPM. This is a "once per machine" step.
npm install uglify-js -g
*: Hopefully this side steps your NPM-less requirement. I did experiment with cloning the Uglify repo directly, but even then you'd need to get it running, and to do that, at a minimum, you'd want to install its dependencies with NPM). I'd love to be proven wrong about this, but I figured it was very unrelated to this post.
And then writing a build script using that. I've attempted to gather the parts for you here:
File: gen-map.sh
#!/usr/bin/env bash
uglifyjs file1.js --config-file gen-map.json \
-o file1.min.js \
--source-map "root='http://foo.com/src',url='file1.min.js.map'"
cat file1.min.js
File: gen-map.json
{
"compress": false,
"output": {
"beautify": true
},
"sourceMap": {
"content": "content from file1.js.map",
"url": "file1.js.map"
}
}
File: file1.js
var b = function() {
console.log('b');
};
function c() {
console.log('c');
};
console.log('a');
b();
c();
(function() {
console.log('d');
})();
File: file1.min.js
var b = function() {
console.log("b");
};
function c() {
console.log("c");
}
console.log("a");
b();
c();
(function() {
console.log("d");
})();
//# sourceMappingURL=file1.min.js.map
File: file1.min.js.map
{"version":3,"sources":["file1.js"],"names":["b","console","log","c"],"mappings":"AAAA,IAAIA,IAAI;IACNC,QAAQC,IAAI;;;AAGd,SAASC;IACPF,QAAQC,IAAI;;;AAGdD,QAAQC,IAAI;;AACZF;;AACAG;;CACA;IACEF,QAAQC,IAAI;EADd","sourceRoot":"http://foo.com/src"}
*: Uglify-es if you're using ES6 features.
After that the only thing left to do would be to update the paths, filenames, and actual script tags. Using this config you must still serve the min.js file, although it seems possible that manually tagging your JS file to point to the map might work...
With this config, you'll need to keep your built files up to date by running:
🐚 ./gen-map.sh
Doing this with npm and gulp would be simpler, but, if you don't mind another package, there are 2 generic "files been changed watchers" that I can suggest;
Nodemon:
🐚 nodemon gen-map.sh
entr
🐚 entr gen-map.sh
I'm in the process of defining a seed node.js based server project for Typescript using Gulp and Mocha and am looking at the example at: https://www.typescriptlang.org/docs/handbook/gulp.html.
A gulpfile has been defined with the following code:
var gulp = require("gulp");
var ts = require("gulp-typescript");
var tsProject = ts.createProject("tsconfig.json");
gulp.task("default", function () {
return tsProject.src()
.pipe(tsProject())
.js.pipe(gulp.dest("dist"));
});
I know that this code compiles Typescript into javascript and places the resulting files in the "dist" folder. However, I would like to break this code down and understand what is happening at every point (I'm not sure which exact bit of code invokes the Typescript compiler).
1) So, which part of the code invokes the compiler?
2) What does the .js call do? (I'm guessing this is the call to invoke typescript compilation, but I don't know).
The problem I find with the gulp documentation is that its not very comprehensive. I would like to find the documentation for all of the api methods (eg, "src", "js", "createProject", "pipe" etc). And the documentation on gulp-typescript on https://www.npmjs.com/package/gulp-typescript provides you with code snippets without explaining the details.
I have already watched the video at https://gulpjs.org by markgdyr, but the other videos are for browser projects, which I'm not interested in, thanks.
Piping your source files through the TypeScript project tsProject you created executes the compile step.
This produces an object containing a set of JavaScript files js, and a set of TypeScript definition files dts. In this case, you are accessing the JavaScript files via .js and saving them in the output folder via gulp.dest.
js is not a function, but a simple property access.
More info in the gulp-typescript docs.