Using lodash module in babel-node REPL? - ecmascript-6

I am trying to test lodash, and apparently the following line won't work in the REPL:
import curry from 'lodash/curry';
See, e.g., babel-node es6 "Modules aren't supported in the REPL"
Why does babel-node not support module loading in the REPL?
Is there a way that I can pre-load a module like lodash into babel-node? (e.g. via a command line option or a configuration file)
If not, is there another way of evaluating ES6 with lodash preloaded?
So far, I've tried the online REPL at https://babeljs.io/repl/, and evaluation in the Console in Firefox. None worked.

import surely won't work, because the package should be installed first and bundled in order to be imported, which isn't the duty of Babel REPL.
Lodash is already loaded and used in Babel REPL, it can be used in REPL as _ global:
const { curry } = _;
If the question isn't limited to Lodash, the quickest way to get third-party library in REPL is to load the script manually. And since Babel website has jQuery loaded (as almost any website), the shortest way to do this is jQuery.getScript, in console:
$.getScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js');
After that Lodash _ global becomes available in REPL.
The whole code can be wrapped with callback to skip console part:
$.getScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js', () => {
console.log(_.VERSION);
});
babel-node REPL doesn't support imports, as the links in the original post say. Instead, require should be used, like anywhere else in Node.
var { curry } = require('lodash')
This requires to have lodash installed in node_modules that exists in current working directory.

Related

Forge Viewer property database userFunction not found due to Vue webpack mangling (terser)

I am using a userFunction to query the property database in a custom Forge Viewer extension. This works great while testing the site locally using npm run serve. However, when I deploy the website to the web (which uses npm run build), the function no longer executes. The error says: SyntaxError: Function statements require a function name. This is because, according to the documentation, the function executed through executeUserFunction has to be named userFunction.
Upon further inspection I discovered that this was because of Vue & Webpack's mangling feature (executed by terser-webpack-plugin), where it renames variables and removes function names to decrease file size.
I have tried many different things, from making the function part of the extension's class to moving it to the global JS scope, but nothing helped. I also tried to exclude objects.js (which is the name of the extension I wrote) from mangling, but this didn't work either.
How do I configure terser to stop mangling this one variable?
I eventually figured out a way to do this which worked:
Add the following to vue.config.js:
module.exports = {
...
chainWebpack: config => {
config.optimization.minimizer('terser').tap(args => {
// eslint-disable-next-line no-param-reassign
args[0].terserOptions.keep_fnames = true;
return args;
});
},
};
This will prevent terser from removing function names, and will make it so userFunction still works. Weird design choice by Autodesk to require a function name, but at least it works now :)

How can I stop the ClojureScript compiler from resolving certain `require`s?

In my ClojureScript code I am requiring a JavaScript module called seedrandom which is in the node_modules folder, like this:
(ns something.core
(:require ["seedrandom" :as rnd]))
(js/console.log (.quick (rnd "x")))
According to the seedrandom documentation it is intended for both nodejs and the browser, and I've previously included and used it successfully in ClojureScript code via a <script> tag, confirming it works in the browser.
Running this cljs file in lumo on the command line works well and outputs a deterministically random number.
When I try to use this same cljs file in my Reagent frontend project I see the following error:
Compiling build :app to "public/js/app.js" from ["src" "env/dev/cljs"]...
events.js:183
throw er; // Unhandled 'error' event
^
Error: module not found: "crypto" from file /home/chrism/dev/something/node_modules/seedrandom/seedrandom.js
at onresolve (/home/chrism/dev/something/node_modules/#cljs-oss/module-deps/index.js:181:30)
...
Inside seedrandom.js we see the following:
// When in node.js, try using crypto package for autoseeding.
try {
nodecrypto = require('crypto');
} catch (ex) {}
Clearly this code is intended to ignore the built-in nodejs crypto module when running in the browser. The problem, as far as I can tell, is that the ClojureScript compiler does not know that - it sees that require('crypto') and tries to pull it into the compilation phase, but can't find it because it's a nodejs built-in.
Is there some way I can tell the compiler to ignore that particular require? Or can I shim the 'crypto' module somehow? What is the cleanest way to solve this?
Note: I have previously experienced this same issue with JavaScript modules which check for the fs node module. Hope we can find a general solution to use again in future. Thanks!
Relevant versions: [org.clojure/clojurescript "1.10.520"] and [reagent "0.8.1"].
This answer is related, asking a similar question from the perspective of Google Closure, which ClojureScript uses, but I'm looking for an answer I can use specifically with cljs.

Using ES6 `import` with CSS/HTML files in Meteor project: bug or feature?

I am currently learning Meteor and I found out something that intrigued me.
I can load HTML and CSS assets from a JS file using the import statement.
import '../imports/hello/myapp.html';
import '../imports/hello/myapp.css';
import * as myApp from '../imports/hello/myapp.js';
This was a surprise to me so I ran to google but could not find this behavior documented in the specification for ES6 import or in Meteor's Docs.
So my questions are:
Can I rely on this behavior to build my apps?
Will my app will break when Meteor gets around to fix it -- if it's a bug --?
Notes
I am using Meteor v1.3, not sure if this works also with previous versions.
You can download the app to see this behavior from Github
After going through the implementation of the built files for my app
I found out why this works.
HTML
Files are read from the file system and their contents added to the global Template object, e.g.,
== myapp.html ==
<body>
<h1>Welcome to Meteor!</h1>
{{> hello}}
</body>
results in the following JS code:
Template.body.addContent((function () {
var view = this;
return [
HTML.Raw("<h1>Welcome to Meteor!</h1>\n\n "),
Spacebars.include(view.lookupTemplate("hello"))
];
}));
Which is wrapped in a function with the name of the file as it's key:
"myapp.html": function (require, exports, module) {
Template.body.addContent((function () {
var view = this;
return [
HTML.Raw("<h1>Welcome to Meteor!</h1>\n\n "),
Spacebars.include(view.lookupTemplate("hello"))];
}));
Meteor.startup(Template.body.renderToDocument);
Template.__checkName("hello");
Template["hello"] = new Template("Template.hello", (
function () {
var view = this;
return [
HTML.Raw("<button>Click Me</button>\n "),
HTML.P("You've pressed the button ",
Blaze.View("lookup:counter",
function () {
return Spacebars.mustache(view.lookup("counter"));
}), " times.")
];
}));
},
So all of our HTML is now pure JS code which will be included by using require like any other module.
CSS
The files are also read from the file system and their contents are embedded also in JS functions, e.g.
== myapp.css ==
/* CSS declarations go here */
body {
background-color: lightblue;
}
Gets transformed into:
"myapp.css": ["meteor/modules", function (require, exports, module) {
module.exports = require("meteor/modules").addStyles("/* CSS declarations go here */\n\nbody {\n background-color: lightblue;\n}\n");
}]
So all of our CSS is also now a JS module that's again imported later on by using require.
Conclusion
All files are in one way or another converted to JS modules that follow similar rules for inclusion as AMD/CommonJS modules.
They will be included/bundled if another module refers to them. And since all of them are transformed to JS code
there's no magic behind the deceitful syntax:
import '../imports/hello/myapp.html';
import '../imports/hello/myapp.css';
They both are transpiled to their equivalent forms with require once the assets have been transformed to JS modules.
Whereas the approach of placing static assets in the imports directory is not mentioned in the official documentation,
this way of importing static assets works.
This seems to be at the core of how Meteor works so I'd bet this functionality is going to be there for a long while.
I don't know if to call this a feature maybe a more appropriate description is unexpected consequence but that would
only be true from the user's perspective, I assume the people who wrote the code understood this would happen and perhaps even
designed it purposely this way.
One of the features in Meteor 1.3 is lazy-loading where you place your files in the /imports folder and will not be evaluated eagerly.
Quote from Meteor Guide:
To fully use the module system and ensure that our code only runs when
we ask it to, we recommend that all of your application code should be
placed inside the imports/ directory. This means that the Meteor build
system will only bundle and include that file if it is referenced from
another file using an import.
So you can lazy load your css files by importing them from the /imports folder. I would say it's a feature.
ES6 export and import functionally are available in Meteor 1.3. You should not be importing HTML and CSS files if you are using Blaze, the current default templating enginge. The import/export functionality is there, but you may be using the wrong approach for building your views.

Export objects and classes before their declaration makes them undefined

I try to repeat example from Mozilla Hacks (Export lists subtitle):
//export.js
export {detectCats, Kittydar};
function detectCats() {}
class Kittydar {}
//import.js
import {detectCats, Kittydar} from "./export.js";
console.log(detectCats); // function detectCats() {}
console.log(Kittydar); // undefined
Oops: Kittydar is undefined (btw., the problem is the same with simple Object).
But if I put export after Kittydar declaration it's ok:
//export.js
class Kittydar {}
export {Kittydar};
//import.js
import {Kittydar} from "./export.js";
console.log(Kittydar); // function Kittydar() {_classCallCheck(this, Kittydar);}
Is this a typo in the article?
I transpile this with babel and bundle with browserify. Then I include output bundle in a usual .html file (with <script> tag).
The standard is hard to follow on this, but the article is correct. This code works in es6draft and in the SpiderMonkey shell: both the function and the class are initialized by the time those console.log calls run.
Here's how it's supposed to work, in minute detail:
The JS engine parses import.js. It sees the import declaration, so then it loads export.js and parses it as well.
Before actually running any of the code, the system creates both module scopes and populates them with all the top-level bindings that are declared in each module. (The spec calls this ModuleDeclarationInstantiation.) A Kittydar binding is created in export.js, but it's uninitialized for now.
In import.js, a Kittydar import binding is created. It's an alias for the Kittydar binding in export.js.
export.js runs. The class is created. Kittydar is initialized.
import.js runs. Both console.log() calls work fine.
Babel's implementation of ES6 modules is nonstandard.
I think it's deliberate. Babel aims to compile ES6 modules into ES5 code that works with an existing module system of your choice: you can have it spit out AMD modules, UMD, CommonJS, etc. So if you ask for AMD output, your code might be ES6 modules, but the ES5 output is an AMD module and it's going to behave like an AMD module.
Babel could probably be more standard-compliant while still integrating nicely with the various module systems, but there are tradeoffs.

Loading a global module in ECMAScript 6

Some old Javascript libraries directly attach an object to the global scope (no AMD, no UMD, no commonJS).
Is there a nice way to "include" a global module in ECMAScript 6 code?
I'm just aware of the following line:
import './globallib.js';
And then access the global variable directly.
Example: to load QUnit test function:
import 'qunit';
test('my test', function () {
ok(true, 'QUnit loaded');
});
Is this the right way?
PS. I encountered this problem while working with QUnit 1.8 in a project that compiles to ES 5 using Babel and Browserify. In QUnit 2 they're gonna avoid globals. But I have this question in general.
As far the QUnit exports its methods into window (according source code), you right, import expression is enough.
But anyway, you can't use raw imports in browsers, so you have to use some preprocessing. Webpack and browserify will work for you, but it would not be so for another build systems.