webpack and amd modules integration - requirejs

I have the following scenario in a web application:
I have some external js files containing multiple amd modules (they've been generated by typescript, but I don't want to use ts-loader, for now).
each one of these files contain more than one amd module, such as:
define("Namespace/Module1", ["require", "exports"], function (require, exports) {
....
}
define("Namespace/Module2", ["require", "exports"], function (require, exports) {
....
}
Inside my main application I request modules exported by these files using
var a = require("Namespace/Module1").
I used SystemJs until now, and using correct configuration, everything's working, but I would like to start using webpack for bundling and I don't know how to add these files as "require" dependencies.
I tried to add them as script tags after importing requireJS, but (of course), webpack loader uses its own "require" loader and it doesn't work.
Using externals in webpack configuration makes webpack consider that they are not modules, but globals, so it excludes require("aaa") from the generated bundle and expects a global aaa variable
function(module, exports) {
module.exports = aaa;
}
How can I tell webpack that when he's generating my bundle he should still use require("Namespace/Module") but before, calling all "define"s in my external files?
I hope my explanation is enough and If I am doing something wrong, if am following bad practices, or something, please explain.
Thanks in advance

Related

More issues with TypeScript modules

... yep. It's that time of year again. TypeScript is wonderful, except for the mess that is the module system. Presently, I'm struggling with a scenario where I'm dealing with two separate repositories / projects (what ever you wanna call it). I've got a library and a program using said library. I'm using Node's subpath exports feature in the package.json to indicate multiple submodules within the library. From the NodeJS (and the bundler's perspective) this is fine, but not in TypeScript's world. TypeScript is adamant that none of the modules exist.
Here's a quick summary of the library
/utils
package.json
tsconfig.json
build automatically generated build directory
iter.js
iterSync.js
...
lib autogenerated declarations files
iter.d.ts
iterSync.d.ts
...
src source files
iter.ts
iter_sync.ts
...
lib manually written definitions files
iter.d.ts
iterSync.d.ts
It's package.json has an exports field which defines a list of exports and their respective declaration file.
library/package.json
...
"exports": {
"./iter": "build/iter.js",
"./iterSync": "build/iterSync.js",
...
}
...
I've tried various alterations of the above, such as
Node16 module resolution strategy
Adding a types property to each export
Using the main and typings fields in library/package.json
Adding "lib" to the includes of library/tsconfig.json
Upgrading to TypeScript 4.6.4
Restarting the TS Language Server through the VSCode command menu
Basically all permutations of the above
How I use the library
I don't think it makes a difference, but I'm referencing the module through a pnpm link dependency statement for development, and a git://... url for production:
program/package.json
...
"dependencies": {
"jcake-utils": "link:../library", // dev
"jcake-utils": "github:J-Cake/jcake-utils", // prod
...
}
Regardless of what I do, TypeScript refuses to locate my submodules in the program
...
import Iter, * as iter from 'jcake-utils/iter';
import IterSync, * as iterSync from 'jcake-utils/iterSync';
...
$ pnpm exec tsc -v
Version 4.6.4
$ pnpm exec tsc -p program/tsconfig.json
src/build.ts:2:29 - error TS2307: Cannot find module 'jcake-utils/iter' or its corresponding type declarations.
... (A bajillion errors saying exactly the same thing, just across different files)
I alluded to this being a common issue (because from my experience it is) but I would greatly appreciate some very specific feedback as to how I can correct this issue and/or if I should potentially restructure either program or library, and how best to do so. I've been unable to find documentation that is helpful in my situation, as many of the suggestions given by the TypeScript community have not worked.
Thanks kindly

If an npm package ships with multiple dist folders, how do I know which one is used when my App is built?

I am using an npm package (aurelia-google-maps) in my application. The package ships with AMD, System, CommonJS, Native Modules, and ES2015 dist folders like this:
/node_modules/
/aurelia-google-maps/
/dist/
/amd
/system
/native-modules
/es2015
/commonjs
In my typescript app I am simply importing all the classes and functions as:
import {Configure} from "aurelia-google-maps"
Is there a way that I can find out which distribution is used when I build my application?
I don't think you can determine it without the help of your build tool. It can be done in 2 steps:
using type of to check the availability of a variable in runtime code:
const distType = typeof DIST_TYPE !== 'undefined' ? DIST_TYPE : void 0;
configure the build tool to replace DIST_TYPE with the distribution target
And then you can use it in normal code, via distType variable.
For typescript, you just need an extra declaration
declare const DIST_TYPE: string | undefined;
I have figured it out. It seems like Aurelia automatically registers it's own plugin called DistPlugin that resolves dependencies using the native-modules folder. See this wiki for an explanation on how to change this behaviour.

Loading Nodejs Module at runtime in electron app

Currently I am playing around with electron using vue-cli-plugin-electron-builder along side a simple vue project. This is the project https://github.com/nklayman/vue-cli-plugin-electron-builder .
vue create my-project
cd my-project
vue add electron-builder
npm run electron:serve
My goal is to add a simple plugin-like architecture. The app serves only base functionality but can be extended with "plugins". Those plugins therefore are not included in the built, but will be loaded at runtime by electron. I would prefere when those plugins just behave like node modules ( module.exports = ) with its own dependencies ( probably with a package.json file inside ). I would locate those plugins at app.getPath('userData') + '/Plugins.
I looked at a few approaches on how to tackle this problem :
1. Using Nodejs vm module
First, I tried using Nodejs vm module to read and execute a script from an external file, all at runtime. It works great so far, although I would not be able to use external dependencies inside those loaded scripts. If I want to use external dependencies inside the plugin scripts, those dependencies must have been included in the electron build beforehand. Somehow defeats the whole purpose of having plugins ... only vanilla js + nodejs base modules would be possible .
2. using global.require
I saw this solution in another SO answer.
Using node require with Electron and Webpack
Webpack/electron require dynamic module
They say to use global.require but it throws an error saying global.require is not a function. The solution looked promising first, but somehow I can't get it to work.
3. simply use require
Of course I had to try it. When I try to require an external module from a non-project location it won't find the module, even if the path is correct. Again, the path I am trying to locate the module should be at app.getPath("userData"), not in the projects root directory. When however, I locate the plugins inside the root directory of the project it gets included in the built. This again defeats the purpose of having plugins.
Goal
So far, I haven't found a viable solution to this. I simply want my electron app to be extendible with basic node modules at runtime ( following a pre-defined schema to simplify ) . Of course there is atom, made with electron, using their own apm manager to install and load plugins, but this seems way to overpowered. Its enough for me to only have plugin files located locally, to have a public "marketplace" is no goal. Also, it's ok if the app has to reload / restart to load plugins.
Any ideas ?
After more and more research I stumbled over 2 packages :
https://www.npmjs.com/package/live-plugin-manager
https://github.com/getstation/electron-package-manager
both integrating npm to programmatically handle package installation at runtime. I settled for live-plugin-manager for now since its better documented and even allow package installation from local file system.
Pro
I was able to integrate the system out-of-the-box into a vanilla electron app. Works like a charm.
Cons
.I was not able to use it inside a vue electron boilerplate (like the one I said I was using in OP), since webpack is interferring with the require environment. But there sure is a solution to this.
Update : I was able to get it to work eventually inside a webpack bundled electron vue boilerplate. I accidentally mixed import and require . The following code works for me using live-plugin-manager
// plugin-loader.js
const path = require('path');
const { PluginManager } = require('live-plugin-manager');
const pluginInstallFolder = path.resolve(app.getPath('userData'), '.plugins');
const pluginManager = new PluginManager();
module.exports = async (pkg) => {
// installs pkg from npm
await pluginManager.install(pkg);
const package = pluginManager.require(pkg);
return package
}
// main.js
const pluginLoader = require('./plugin-loader');
pluginLoader("moment").then((moment) => {
console.log(moment().format());
})
This will install "moment" package from npm during runtime into a local directory and load it into the app, without bundling it into the executable files.

Is there a way to check if the current export is imported in typescript?

In commonjs (node) I can do this:
module.exports = {...}
if (module.parent) {...}
Is there something similar in typescript? I'm writing a web app and it would allow me to not bind a port during tests.
TypeScript is not responsible for providing such functionality. It is entirely the responsibility of your module loader (e.g. Node's built-in module loading code, SystemJS, RequireJS) or bundler (e.g. Webpack, Browserify) to provide such functionality. If you compile your TypeScript code and run it in Node, then yes, module.parent will be there though it is not declared in the TypeScript declarations by default. If you install the NPM package #types/node, you'll get a declaration for it though. (npm install #types/node) Or you can declare it yourself if you prefer.

How to fetch modules from npm as standalone AMD/CommonJS modules

So, I'm not using Node or WebPack serverside, but would still like to use modules from npm every now and then. My clientside uses requirejs, so I would need the modules in either AMD (preferred) or CommonJS.
What I want to archieve is a script that takes the module name + "external dependencies" as arguments and creates a module that contains all the other deps.
E.g.
sh npmtoamd.sh draft-js react react-dom
It creates an ES5 AMD module that contains draft-js and all of it's dependencies excluding react and react-dom. If it's not possible to eg. include css files and other non-js content in the module, providing them in eg. draft-js.css is tolerable.
While I don't use Node or Webpack serverside, we can use npm and webpack in the said script.
Fetching the module from npm is the trivial part, but I'm pretty lost in how to do the webpack parts. I know for a fact that it's possible though, as I managed to do it earlier with help, just don't have it down anywhere and no idea how it went.
I think as elmigranto commented, Browserify is what you're looking for. Unlike its name implies, it can be used in both the browser environment and the node environment. In a nutshell, it does this:
Browserify starts at the entry point files that you give it and searches for any require() calls it finds using static analysis of the source code's abstract syntax tree.
For every require() call with a string in it, browserify resolves those module strings to file paths and then searches those file paths for require() calls recursively until the entire dependency graph is visited.
Each file is concatenated into a single javascript file with a minimal require() definition that maps the statically-resolved names to internal IDs.
This means that the bundle you generate is completely self-contained and has everything your application needs to work with a pretty negligible overhead.
If you check out some of the demos you can see that all the dependencies (and their co-dependencies) are bundled into one file.
A simple example:
browserify main.js -o bundle.js
In regards to using AMD as well Browserify supports it by using deamdify.
Using AMD modules:
browserify -t deamdify main.js -o bundle.js
I ended up doing the npm fetch thingy in java instead of a batch script and finally got it working. Didn't get browserify to work however.
Heres what I do:
creating the following webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js',
library:"<modulename>",
libraryTarget:"amd"
},
externals: {
react: "React",
"react-dom": "ReactDOM",
immutable:"Immutable",
moment:"Moment"
}
};
npm install <modulename>
Creating the following main.js
define('FOOBAR', ['<modulename>'], function(Foo) {
return Foo;
});
Running webpack main.js

Resources