11

I need to import a JavaScript module from an in memory variable. I know that this can be done using SystemJS and Webpack.

But nowhere can I find a good working example nor documentation for the same. The documentations mostly talks of dynamic import of .js files.

Basically I need to import the module like below

let moduleData = "export function doSomething(string) { return string + '-done'; };"
//Need code to import that moduleData into memory

If anyone can point to documentation that will be great

Erik Philips
  • 48,663
  • 7
  • 112
  • 142
Guru Kara
  • 5,636
  • 2
  • 35
  • 46
  • 1
    Do you have babel in your toolchain already? If so, I guess you already have something like https://webpack.js.org/loaders/babel-loader/ where you can configure plugins. Now, I did not find any plugin suiting your needs. But using babel-generator to parse and generate code (https://babeljs.io/docs/en/babel-generator) and this docs https://github.com/jamiebuilds/babel-handbook you may write your own plugin. – bubblez Jul 22 '19 at 16:01
  • 3
    This Q has the feel of an [XY problem](https://meta.stackexchange.com/a/233676). I've used module loaders and module bundlers with JavaScript for years, and contributed to relevant projects, but never ever have I run into a problem that *required* a solution entailing importing a string as a module. There have been cases where I could have solved my problem by doing this, but there were *better* solutions available. The way the question is currently written though, we don't know the X part of the XY problem. Why must the module be imported from a string containing the source of the module? – Louis Jul 22 '19 at 16:39
  • I agree with @Louis. This is an _incredibly_ odd thing to ask, and explain why you _need_ to do this could certainly help you find a solution. – jhpratt Jul 23 '19 at 08:10
  • Basically we have an Angular App, where in we let users create Angular components including HTML Template, TS file and CSS. Once they type those we need to compile and load that in same Angular App. We have figured out how to merge and compile the HTML TS and CSS into a JS module, now the loading part is let – Guru Kara Jul 27 '19 at 09:11
  • If you need to do this entirely within the browser (as you've indicated in comments on answers), I don't believe this is possible. Keep in mind that running arbitrary code is _extremely_ dangerous as well. – jhpratt Jul 27 '19 at 23:07
  • You can check this example, maybe it fit your expectation https://github.com/SaifJerbi/odessajs19 – Saif Jerbi Jul 29 '19 at 10:05

4 Answers4

6

There are limitations in the import syntax that make it difficult to do if not impossible without using external libraries.

The closest I could get is by using the Dynamic Import syntax. An example follows:

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<script>
    var moduleData = "export function hello() { alert('hello'); };";
    var b64moduleData = "data:text/javascript;base64," + btoa(moduleData);

</script>
<script type="module">

    async function doimport() {
      const module = await import(b64moduleData);
      module.hello();
    }

    doimport();

</script>

</body>
</html>

You will however notice that this has some limitations on the way the import code is constructed, which may not precisely match what you need. The simplest solution is probably to send the code of the module on the server for it to generate a temporary script to be then imported using a more conventional syntax.

Filippo Possenti
  • 928
  • 5
  • 15
2

Use nodejs flag --experimental-modules to use import ... from ...;

node --experimental-modules index.mjs
index.mjs:
import fs from 'fs';
let fileContents = "export function doSomething(string) { return string + '-done'; };"
let tempFileName = './.__temporaryFile.mjs';

fs.writeFileSync(tempFileName, fileContents);
console.log('Temporary mjs file was created!');

import(tempFileName)
    .then((loadedModule) => loadedModule.doSomething('It Works!'))
    .then(console.log)


Further reading here

How It Works:

  1. I first create the file with fs.writeFileSync
  2. then I use import method's promise to load module and
  3. pipe doSomething method call with "It Works!"
  4. and then log the result of doSomething.

Credits: https://stackoverflow.com/a/45854500/3554534, https://v8.dev/features/dynamic-import, @Louis

pegasuspect
  • 965
  • 4
  • 14
  • 1
    `eval` is your solution here tough it is not recommended to use it. https://www.w3schools.com/js/js_best_practices.asp read some more about it here – Kashkashio Jul 22 '19 at 15:32
  • eval = evil [good explanation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Do_not_ever_use_eval!) – Grzegorz T. Jul 22 '19 at 16:03
  • The objection "eval = evil" is not a good one when it comes to dealing with executing module code. The default mode of operation of SystemJS, Webpack, RequireJS and any other JS module loader I've used is to run the code with the exact same privileges as doing `eval` would. In other words, *`require("foo")` is not any safer than fetching the source of the module `foo` and then `eval`ing it.* Some module loaders even have `eval(sourceOfModule)` as part of the module loading process. This being said, `eval` won't handle the OP's `export` statement and so it not *enough* to do what the OP wants. – Louis Jul 22 '19 at 16:16
  • 1
    Post-edit, this answer is still not hitting the mark. Do a search for "es6 dynamic import". Static imports may be hoisted but dynamic imports are not hoisted. I don't think dynamic imports are implemented to allow passing to the `import()` call a module's *source code*, but there's *could* be a plugin for SystemJS or Webpack that allows it. Even if such plugin does not currently exist, I'm sure one could be developed. I would not call what the OP wants to do "impossible". – Louis Jul 22 '19 at 20:11
  • @Louis yes you were right. I got the answer by searching google about the message. I will include the source as well. Thank you! – pegasuspect Jul 22 '19 at 20:28
  • Basically we have an Angular App, where in we let users create Angular components including HTML Template, TS file and CSS. Once they type those we need to compile and load that in same Angular App. We have figured out how to merge and compile the HTML TS and CSS into a JS module, now the loading part is left – Guru Kara Jul 27 '19 at 09:49
2

You can create component and Module on fly. But not from string. Here is an example:

name = "Dynamic Module on Fly";
const tmpCmp = Component({
  template:
    '<span (click)="doSomething()" >generated on the fly: {{name}}</span>'
})(
  class {
    doSomething(string) {
      console.log("log from function call");
      return string + "-done";
    }
  }
);
const tmpModule = NgModule({ declarations: [tmpCmp] })(class {});

this._compiler.compileModuleAndAllComponentsAsync(tmpModule).then(factories => {
  const f = factories.componentFactories[0];
  const cmpRef = this.container.createComponent(f);
  cmpRef.instance.name = "dynamic";
});

Here is the stackblitz

Feroz Ahmed
  • 74
  • 1
  • 8
  • Wont work We let the users create the module also on Fly – Guru Kara Jul 27 '19 at 09:06
  • Basically we have an Angular App, where in we let users create Angular components including HTML Template, TS file and CSS. Once they type those we need to compile and load that in same Angular App. We have figured out how to merge and compile the HTML TS and CSS into a JS module, now the loading part is left – Guru Kara Jul 27 '19 at 09:49
-3
<script type="module">
    import { myfun } from './myModule.js';
    myfun();
</script>

/* myModule.js  */
export function myfun() {
    console.log('this is my module function');
}