According to apple's documentation I can import one JS file into another with an import statement. And yes I am able to use JS functions and recursively call other JS functions.
But can I include node modules into my automation. Node/npm module seems to have a lot of tools that makes life easier and avoid code duplication.
And actually I was able to use one node module called moment.js through the following call in my code
#import "../node_modules/moment/moment.js"
But I am not have the same luck with other npm modules. I tried couple Faker.js, Charlatan.js and I getting the following error in Faker.js
Script threw an uncaught JavaScript error: Can't find variable: window
on line 618 of Faker.js
Looking at *.js files it looks like it has something to do with the way these modules are packaged. My JS knowledge isn't getting me anywhere.
The last few lines of moment js file
// CommonJS module is defined
if (hasModule) {
module.exports = moment;
}
/*global ender:false */
if (typeof ender === 'undefined') {
// here, `this` means `window` in the browser, or `global` on the server
// add `moment` as a global object via a string identifier,
// for Closure Compiler "advanced" mode
this['moment'] = moment;
}
/*global define:false */
if (typeof define === "function" && define.amd) {
define("moment", [], function () {
return moment;
});
}
Last few lines of Faker js file
if (typeof define == 'function'){
define(function(){
return Faker;
});
}
else if(typeof module !== 'undefined' && module.exports) {
module.exports = Faker;
}
else {
window.Faker = Faker;
}
I am perfectly able to play with these modules in node console, so nothing wrong with the modules, it just how to include/require them in my JS files.
Had to do two things for Faker to work for me
remove 'use strict'
Check if window is undefined
Add this statement
this['Faker'] = Faker;
Related
Modules generally start something like this
(function(root, factory)
{
/* globals define */
if (typeof define === 'function' && define.amd)
{
// AMD. Register as an anonymous module.
define([], factory);
}
else if (typeof module === 'object' && typeof exports !== 'undefined')
{
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
}
else
{
// Browser globals (root is window)
root.Papa = factory();
}
I'm trying to implement require to handle node style CommonJS-like modules.
Find the package folder, parse package.json to learn the entrypoint and dependencies, recursively descent with a shared cache to load dependencies... that stuff works.
But having loaded the script for a module, I'm having trouble executing it in such a way as to have it populate module.exports
This will all end up running on Jint (a JS interpreter) so node isn't supplying the usual furniture, I have to build it all myself. There's no step-debug with Jint so I'm using node from VS Code and faking Jint like this.
import * as fs from "fs";
var code = fs.readFileSync("node_modules/papaparse/papaparse.js").toString();
let x = 3;
console.log(eval("x*x"))
let result = eval(`
let module = { exports: "dunno" };
${code}
console.log(module.exports);
`);
This is in a file test.js and package.json nominates this file as main and specifies a type of module. It launches, reads the module code, creates module and runs the code, which promptly complains that it Cannot set properties of undefined (setting 'Papa').
Looking at the snippet above, that tells us it's executing the last else clause, which means it's not seeing module. I thought it might be some sort of scope thing for eval which is where this came from
let x = 3;
console.log(eval("x*x"))
but that duly writes 9 to the console so the question is why module isn't in scope.
This is one of those JavaScript closure weirdnesses; can anyone guide me on how to provide the module object so that the second clause will take effect populating module.exports with the result of factory()?
I know I said it's running in the absence of Node, but I'm debugging it using Node mostly because that's what you get when you launch a js file in VS Code. As already mentioned the production target is Jint. The debug environment is as close an approximation as I can manage, and I'm refraining from using facilities that won't be available in production.
In the debug environment I've created a package.json file that governs whether it's treated as CommonJS or ES6. Experiments show that the production environment behaves like more ES6 than commonjs. The trouble is that most packages expect that they will run either in a browser or in node. They don't consider the possibility of another headless environment. The code above decides it's browser hosted and tries to to install Papa in a DOM that isn't there.
The solution is to wrap the module like so (I'm constructing a string in C#).
string wrapped =
#"(function () {
var module = { exports: { } }; " +
jsSource + #"
var strays = Object.keys(this).filter(itemName => itemName !== 'module');
if (strays.length === 1)
module.exports = this[strays[0]]
else
strays.forEach(itemName => module.exports[itemName] = this[itemName]);
return module.exports;
}).apply({});";
Wrapping it in an anonymous function and using apply to set this for the call allows the "DOM write" to succeed, and a little follow-up code looks for these strays, gathering them up into module exports before returning to normal operation.
I'm currently trying to get a simple example running in which jQuery can be used by kotlin code compiled to JS with the help of gradle. So far I pieced together the following elements which should be enough according to older descriptions using the kotlin.js.externals:kotlin-js-jquery:3.2.0-0 package by adding it like this to the build.gradle.kts of the project:
repositories {
...
mavenCentral()
maven(url = uri("https://kotlin.bintray.com/js-externals"))
}
dependencies {
implementation("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
}
After adding the package I can sucessfully import the corresponding kotlin package in a simple Kotlin file like this:
import js.externals.jquery.jQuery
fun main() {
console.log(jQuery("div"))
}
But this sadly fails if executed with the following stack trace in the JS console:
Uncaught TypeError: $module$jquery is not a function
at main (simple.kt?9d2a:4)
at Object.eval (yet_another_one.js:14)
at eval (yet_another_one.js:5)
at eval (yet_another_one.js:8)
at Object../kotlin-dce-dev/yet_another_one.js (yet_another_one.js:515)
at __webpack_require__ (yet_another_one.js:30)
at Object.0 (yet_another_one.js:527)
at __webpack_require__ (yet_another_one.js:30)
at yet_another_one.js:94
at yet_another_one.js:97
After this I checked the packages webpack used for the bundling and I found out that as soon as the kotlin.js.externals:kotlin-js-jquery:3.2.0-0 package is used the following jquery.js file gets used instead of the one with the actual code:
(function (root, factory) {
if (typeof define === 'function' && define.amd)
define(['exports', 'kotlin'], factory);
else if (typeof exports === 'object')
factory(module.exports, require('kotlin'));
else {
if (typeof kotlin === 'undefined') {
throw new Error("Error loading module 'jquery'. Its dependency 'kotlin' was not found. Please, check whether 'kotlin' is loaded prior to 'jquery'.");
}
root.jquery = factory(typeof jquery === 'undefined' ? {} : jquery, kotlin);
}
}(this, function (_, Kotlin) {
'use strict';
Kotlin.defineModule('jquery', _);
return _;
}));
//# sourceMappingURL=jquery.js.map
As this looks a bit strange to me as the jquery code seems not to be found anywhere I also tried adding it as a NPM dependency to the build.gradle.kts like this:
dependencies {
...
implementation(npm("jquery", "3.6.0"))
}
But doing so didn't change a thing. Webpack still uses the jquery.js file shown above (and maybe rightfully so), but it also still doesn't work.
I would appreciate any help to get this working as I already invested a lot of time in it and I'm currently kinda hopeless :(.
Thanks a lot!
To create a simple example, start a multiplatform project from the IntelliJ IDEA:
Select: File -> New Project
In the new window on the left hand select: Kotlin
In the main pane select the project template "Full-stack Web Application"
Use Build-System "Gradle Kotlin" to have best compatibility.
After this, Dukat is installed to generate your externals for jQuery:
Simply go to build.kts (your gradle build file) and under dependencies of jsMain add:
val jsMain by getting {
dependencies {
/// ... ommitted pre-installed directives
implementation(npm("#types/jquery","3.5.1", generateExternals = true))
implementation(npm("jquery","3.5.1"))
}
}
After this you can simply access jquery using jQuery as a static import:
import jQuery
And use it like a kotlin library: val xhr = jQuery.get("http://example.com")
In vanilla browser-based JavaScript, I can do this:
<html>
<head>
</head>
<body>
<script type="text/javascript" src="file1.js"></script>
<script type="text/javascript" src="file2.js"></script>
</body>
</html>
Then:
// file1.js
function abc(foo) {
console.log("abc received:", foo);
}
And:
// file2.js
abc(36);
...and things behave as expected. (I.e., "abc received 36" is printed to the console.)
How can I include functions from file1.js in file2.js in node.js, so as to preserve the functionality above, while also keeping the web page as-is? Specifically, I would like to keep the html as it appears above, while being able to run
node file2.js
at the command line, and getting abc received 36 at the terminal.
Edit
I found this question with a very similar motivation, by a seemingly more advanced user. But the main link in the answer to that question is dead, and I don't understand how the stated answer works, or what it's supposed to do. Maybe someone can provide me with a MWE tailored to my file1.js, file2.js?
In order for scripts to be functional as both Node.js modules and browser vanilla (non-modular) scripts, they should be defined as UMD modules.
UMD is basically a boilerplate factory function that detects modular environment and falls back to global variables if needed.
E.g. for CommonJS and globals (no AMD) the definition would be:
file1.js
(function (root, factory) {
if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// Node
module.exports = factory();
// Global
} else {
root.file1 = factory();
}
}(this, function () {
// module body
function abc(foo) {
console.log("abc received:", foo);
}
return { abc: abc };
}));
file2.js
(function (root, factory) {
if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// Node
module.exports = factory(require('./file1'));
// Global
} else {
root.file2 = factory(root.file1);
}
}(this, function (file1) {
// module body
file1.abc(36);
}));
Omitting this boilerplate is the task that bundling tools are supposed to solve, notably Browserify that is intended specifically to bundle CommonJS modules to browser scripts.
I have to know what variables and functions are defined by a script file.
I have to use it in scenario where,I'm using cdn to load first,if it fails the variables and functions will not be defined, so i will load the script file from local.
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/core.min.js"></script>
<script>
if (typeof ($.Core) == 'undefined') {
// local script source
}
</script>
$ is the jquery object, so that wouldn't make any sense.
You want to look in the source file at the bottom to see what it defines. This is obviously going to be very hard on a minified file so start by getting the non-minified version. So change the url to be '.js' instead of '.min.js' (and paste that into your browser adress bar to download the non-minified version).
If you are out of luck and a non-minified version isn't available, try the 'beautifyjs' service.
At the bottom of the file we find
// CommonJS export
if(typeof module != 'undefined' && module.exports)module.exports = __e;
// RequireJS export
else if(typeof define == 'function' && define.amd)define(function(){return __e});
// Export to global object
else __g.core = __e;
We are looking for the //Export to global object in this case, and we find they called it core
So you want to test for typeof (core) == 'undefined'
I'm having some difficulty making angular-translate-interpolation-messageformat play nice with the imports-loader for MessageFormat. I outline the issue in this issue.
Copied:
Even though the module is exposed using UMD (yay) it is actually using the global MessageFormat object here. This is forcing me to either expose MessageFormat onto window (which I would prefer not to do), or make a workaround with webpack (which is also cumbersome). The real solution is to use UMD correctly and not depend on globals but rather require things correctly.
Here's what the UMD format looks like now:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module unless amdModuleId is set
define([], function () {
return (factory());
});
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
factory();
}
}(this, function () {
// interpolation-messageformat code that uses the global MessageFormat variable
}));
Here's what it should look like:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module unless amdModuleId is set
define(['messageformat'], function (MessageFormat) { // <-- changed line
return (factory(MessageFormat)); // <-- changed line
});
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory(require('messageformat')); // <-- changed line
} else {
factory(root.MessageFormat); // <-- changed line
}
}(this, function (MessageFormat) { // <-- changed line
// interpolation-messageformat code that uses the global MessageFormat variable
}));
Thanks!
So, until that issue gets resolved, I need to do a workaround. I'd really prefer to avoid globals. Here's my current solution with the imports-loader:
require('imports?MessageFormat=messageformat!angular-translate/dist/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat');
With that, everything builds fine, however, when I open Chrome, the app breaks when running the angular-translate-interpolation-messageformat function that uses MessageFormat saying that MessageFormat is not defined
Here's where things get weird...
If I open the app in any other browser (other than Chrome) it works just fine. Also, if I open the app when it's deployed, it works fine (even in Chrome).
Here's where things get weirder...
If I open my Chrome DevTools and then open the local app in Chrome, everything works just fine. o_O
So, anyway, I'm wondering if maybe I'm using the imports-loader incorrectly or something. Any help appreciated!
Are you using by any chance devtool: 'eval'? I've seen that same weird behavior you describe, and it went away switching to devtool: 'source-map'.