Requirejs in Magento 2 gives me undefined instead of my library - requirejs

I'm using Requirejs in Magento 2 to load Babylonjs (version 3.3). But in my callback, requirejs gives me an undefined in my BABYLON object.
I have tried doing it with a requirejs-config.js to substitute names, but no luck.
require(['My_Module/js/babylon','My_Module/js/babylon.gui.min'], function(BABYLON, GUI) {
console.log(BABYLON); // undefined
new BABYLON.Color3(0,0,0,0) // TypeError BABYLON.Color3 is not a constructor
});
The files I am trying to load are in My/Module/view/frontend/web/js. and are named babylon.js and babylon.gui.min.js
I expected my BABYLON variable in my callback to be the BABYLON object as I would import it in a CommonJS environment.

Turns out the library defined itself as babylonjs. not as My_Module/js/babylonjs.

Related

Is there a way to import from this node module with 'import' instead of 'require'?

I'm using this node module 'deep-equal' () and so far the only way I've been able to access the function in it is with var equal = require('deep-equal');, as suggested in the documentation. I usually use import instead of require, and am wondering if it's possible to use import with this module. The main function is exported from the file in this line:
var deepEqual = module.exports = function (actual, expected, opts) {...}
Is it possible to import this function with import, or is it only possible with require?
Thanks!
Yes you actually can.
If you're using nodejs LTS then you'll have to use .mjs extension for the file where you're using import and pass experimental-modules flag while running node process.
// foo.mjs
import equal from 'deep-equal';
node --experimental-modules foo.mjs
As of nodejs 12.3.0 you can simply pass the experimental-modules. From docs
Once enabled, Node.js will treat the following as ES modules when passed to node as the initial input, or when referenced by import statements within ES module code
Also you can specify type as module in your package.json:
// package.json
{
"type": "module"
}
From docs
Files ending with .js or .mjs, or lacking any extension, will be loaded as ES modules when the nearest parent package.json file contains a top-level field "type" with a value of "module"

Equivalent for __dirname in TypeScript when using target/module=esnext

I need to calculate a pathname relative to the file-system location of the module. I am using the latest TypeScript on Node.js 12.x. For other reasons in tsconfig.json I have set
"target": "esnext",
"module": "esnext",
This triggers a mode that is strict to the Node.js support for ES6 Modules. In that mode the __dirname variable is not available because that global is not defined in the ESanything spec. What we're supposed to do instead is access the import.meta.url variable and extract the directory name.
For an example see the last answer on this question: Alternative for __dirname in node when using the --experimental-modules flag
But in the TypeScript DefinitelyTyped collection the ImportMeta class is not defined to include a url member. Therefore the code fails to compile:
tdn.ts:7:25 - error TS2339: Property 'url' does not exist on type 'ImportMeta'.
7 console.log(import.meta.url);
~~~
I wasn't able to find the definition of ImportMeta in the Definitely Typed repository. But it seems to be improperly defined.
UPDATE: In node_modules//typescript/lib/lib.es5.d.ts I found this:
/**
* The type of `import.meta`.
*
* If you need to declare that a given property exists on `import.meta`,
* this type may be augmented via interface merging.
*/
interface ImportMeta {
}
Ugh...
/UPDATE
In the Node.js 12.x documentation on the ES Modules page it clearly describes the shape of import.meta and that we're supposed to do something like:
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
'__dirname', '__filename' and 'require'...etc are NodeJS specific keywords, and by default typescript doesn't recognize them, although you need to know that the compiler compile the ts file to js file (by default) and it works fine, in order to clear the errors you can to run this on your terminal (or cmd on windows):
npm install --save #types/node
that would install nodejs types definitions and allow you to avoid this type of errors when writing your nodejs programs in typescript.
using import.meta and fileToURLupdate this works...
As per NODE.org, both CommonJs variables specially __dirname or __filename are not available in ES Modules.
We have to replicate those commonJs variables from via import.meta.url.
Source: https://nodejs.org/api/esm.html#esm_no_filename_or_dirname

how to use node module with es6 import syntax in typescript

I have a typescript project which has uses one of our node modules which normally runs in our front-end. We are now looking to use this module in node on our server.
The module uses es6 import syntax import { props } from 'module/file'
When I include a ref in typescript using either of the following methods
import { props } from 'module/file';
var props = require('module/file');
I get the following error from typescript
unexpected token 'import'
(function (exports, require, module, __filename, __dirname) { import
It's a big job to re-write the module, and I've tried using babel with babel-plugin-dynamic-import-node, as well as SystemJS.
The problem with these systems is that they are all asynchronous, so I can't import the module in the standard fashion, so I would need to do a whole bunch of re-write when we get to the point that I can use import natively in node.js.
I can't be the first person to have this issue, but I can't seem to find a working solution.
--------------- update with set-up -------------
In response to #DanielKhoroshko's response. The original module I am trying to import is normally packaged by webpack in order to use on the front-end. I am now trying to use this same module both server-side and in the front-end (via webpack on the front-end) without re-writing the imports to use require and without running webpack to bundle the js to use on the server.
To be clear, the original module is written in JS, our service which is trying to use this module is written in typescript and transpiled. When the typescript tries to require the old module which uses import, it is at this point that we are running into the issue.
------------------ some progress ---------------------------
I've made some progress by creating a file in my imported module which uses babel in node.js to transpile the es6 code into commonJS modules.
I've done this via
var babel = require("babel-core")
var store = babel.transformFileSync(__dirname + '/store.js', {
plugins: ["transform-es2015-modules-commonjs"]
});
module.exports = {
store: store.code
}
I can now get the store in my new node.js project. However, the submodules within the store.js file are not included in the export.
So where in my module, it says
import activities from './reducers/activities';
I now get an error
Cannot find module './reducers/activities'
How can I get babel to do a deep traversal to include the sub-directories?
unexpected token 'import' means you are running es-modules code in environment that doesn't support import/export commands. If you are writing you code in TypeScript it's important to transpile it first before building for the browser or use ts-node to run it server-side.
If you are using webpack there are loaders ts-loader and awesome-typescript-loader
What is your setup?
To describe the module you would need to create an activities.d.ts file in the same folder where the js-version (I understood it is called activities.js and containers a reducer) resides with the following (approx.):
import { Reducer } from 'redux';
export const activities: Reducer<any>;
#Daniel Khoroshko was right in many ways, I ended up finding #std/esm which lets you import es6 modules and worked find for fetching the included imports as well.
var babel = require('babel-register')({
presets: ["env"]
});
require = require('#std/esm')(module);
var store = require('ayvri-viewer/src/store');
exports.default = {
store: store
}
I had to run babel to get a consistent build from es6 to node compatible es5

Unable to use requireJS and Node's Require in the same TypeScript project

I have a typescript project targeted at both Node and the browser. I'm using Node's require() in some scripts and requireJS's require() in others. My project directory looks like this:
myProject
\-- a.ts
\-- b.ts
\-- node.d.ts
\-- require.d.ts
where a.ts contains:
/// <reference path="./node.d.ts" />
var cp = require('child-process');
var a = 'hello world'
export = a
and b.ts contains:
/// <reference path="./require.d.ts" />
require('hello',function(x){console.log('world')});
var b = 'hello world'
export = b
and where require.d.ts and node.d.ts are obtained from DefinitlyTyped.
When I compile my project, I get these errors:
b.ts(2,1): error TS2346: Supplied parameters do not match any signature of call target.
require.d.ts(396,13): error TS2403: Subsequent variable declarations must have the same type. Variable 'require' must be of type 'NodeRequire', but here has type 'Require'.
I use this idiom to determine which modules to load, so I'm not loading a node module in the browser or vice versa.
if(typeof module !== 'undefined' && module.exports){
// We're in a Node process
}else{
// We're in an AMD module in the browser:
}
Is there a way to use both of these .d.ts files in the same project. It seems using them in separate modules is not enough.
Is there a way to use both of these .d.ts files in the same project
I highly recommend going with commonjs everywhere. That is what the React community has spearheaded and it's a much simpler workflow. Just use CommonJS + webpack (to get lazy require.ensure from here). 🌹
There's also a quickstart for TypeScript in the browser environment.
In the end what I needed was the ability to require() JS content that is compiled on the fly by the server -- which doesn't seem to work with web-pack.
To suppress the errors from the typescript compiler in the original question, I commented out this line from require.d.ts (the RequireJS declaration file):
declare var require: Require;
and I used the {foo:bar}['f'+'oo'] trick to get tsc to 'forget' the type of the ambient require variable when assigning it to the typed requirejs variable, like so:
var requirejs:Require; // requirejs
if(typeof module !== 'undefined' && module.exports){
// We're in a Node process
requirejs = require('requirejs');
}else{
// We're in an AMD module in the browser:
requirejs = {require:require}['req'+'ire'];
}
// use requirejs for dynamic require statements
requirejs(gather_requirements(),do_things);

Can't get "crossroads" to load as requirejs AMD module in typescript

I am trying to port a javascript app to typescript that uses requirejs. I am using the typedefinition from #mhegazy at DefinitelyTyped: https://github.com/borisyankov/DefinitelyTyped/tree/master/crossroads
Typescript compiler is giving me the following error...
error TS2307: Cannot find external module 'crossroads'.
This did all work in javascript using "defines" instead.
I just used this library as a complete example. What am I missing? Documentation is hard to dig up if TSC is really even paying attention to a requirejs config. I know crossroads depends on js-signals.
Is there a problem with definitions and ts files being in different directories?
From what I have read here I should not have to even put the reference comment. But if I then try to use a relative path, it cannot find it either.
Here is my app structure
index.html
Application ts/js
app/config.ts
app/bootup.ts
app/someclass.ts
Typescript definitions
typings/crossroads/crossroads.d.ts
typings/requirejs/require.d.ts
Javascript libraries
bower_components/crossroads/dist/crossroads.js
bower_components/requirejs/require.js
index.html has this script include
<script data-main="app/config" src="bower_components/requirejs/require.js>
</script>
/app/config.ts:
/// <reference path="../typings/requirejs/require.d.ts" />
(function () {
requirejs.config({
baseUrl: ".",
paths: {
"crossroads": "bower_components/crossroads/dist/crossroads"
}
})
require(["app/bootup"]);
}) ();
/app/someclass.ts:
/// <reference path="../typings/crossroads/crossroads.d.ts" />
import crossroads = require("crossroads");
class SomeClass{
// do something with crossroads
}
import crossroads = require("crossroads");
To use import/require you either need:
relative path pointing to a TypeScript file.
a declared external module.
In your case you need to declare an external module for TypeScript to know about it. E.g.:
declare module 'crossroads'{
export = crossroads;
}
UPDATE I've updated the definitions so you don't need to do this : https://github.com/borisyankov/DefinitelyTyped/pull/3866 Enjoy!

Resources