Using modules, imports, requires


#1

As part of my porting exercise from iFrame to native app I’m picking lots of pieces of JS spread across several files. I’d prefer to keep these modules separate. I’m confused about how best to access these components from my native application.

I’m thinking that the ideas documented (https://manual.os-js.org/v3/guide/framework/#misc) for dealing with frameworks are relevant. But so far I’ve failed to make this work, my module code appears either not to loaded or is not accessible - not sure which. I’m also confused about whether I should be using imports or requires.

Here’s what I have:

in dep.js (currently in application folder alongside index.js)

function doThing() {
// stuff here
}

in index,js:

import ‘./dep.js’; // I know that the build sees this, if I mis-spell I get build errors


.render ( doThing )

in metadata.json

files": [
“main.js”,
“main.css”,
“dep.js”
]

in webpack.config.json

 new CopyWebpackPlugin([
  {from: './dep.js', to: 'dep.js'},
]),

and I see my dep.js appear in the dist folder

However when I run the app the doThing() function is not found.


#2

The best would be to rewrite them into javascript modules. Not global definitions.

Don’t do this when loading libraries that you load with metadata.json. It will then duplicate the code most likely.


If you can’t use javascript modules, then at least define a global namespace for your stuff:

// dep.js
window.myNamespace = {
  doThing: function() {
  }
};

// index.js
.render(window.myNamespace.doThing);

// metadata.json
{
  files: [
  “main.js”,
  “main.css”,
  “dep.js”
  ]
}

// webpack.config.js
new CopyWebpackPlugin([
  {from: './dep.js', to: 'dep.js'},
]),

Keep an eye on the browser console and network logs to see if any errors occurred.


By javascript modules I mean, skip the metadata stuff and just do:

// dep.js
export function doThing() {
}

// index.js
import {doThing} from './dep.js';
.render(doThing)

#3

Also, since when using imports the code inside dep.js will be encapsulated with a self-executing function (Webpack does this).

So it won’t be assigned to the global namespace unless you explicitly say so:

// dep.js
function wontWork() {
}

window.willWork = function() {};

// index.js
import './dep.js';
wontWork();
willWork();

None of these are however recommended when using imports, use export if you plan on doing it with javascript modules.


#4

I want to understand the approaches, realise that formal exporting modules is the way to go, but I’m going to start by trying to make sure I understand the global approach.

I’ve not understand this comment

I’m not clear on when, or indeed why, we use the metadata.json file.

If my dep file has the global assignment (possibly namespaced)

window.willWork = function() {};

and my index.js has this

import ‘./dep.js’;
willWork();

I presumably do need the webpack entries to copy dep.js to dist. Is that all I need? No need for metadata.json entry? This does seem to work just fine.

You say loading libraries that you load with metadata.json in that case how are the libraries loaded?


#5

Okay… I’ll try to explain again.

Package Metadata

Most of this file should not need any introduction, but the {files: []} array is what is used whenever a package is launched in OS.js.

This array contains a list of filenames and/or URLs that will be loaded before the application/theme starts up. Basically adds <script> tags etc. to the DOM.

The main.js and main.css you normally see there are the bundled output from Webpack.

Package Builds

As mentioned above, Webpack is the main method of building and bundling your package. This way you can simply use export and import statements in your file(s) and you’re good to go – everything will be in main.js and main.css.

Javascript modules

As mentioned above, this is the standard way to do this:

// dependency.js
export function feature() {
  console.log("Hello World")
}

// index.js
import {feature} from './depenceny.js';
feature(); // -> "Hello World"

Global modules

if you have modules that assigns stuff to the global namespace (i.e. window), you have two alternatives:

Using metadata

Add the dependency.js file to the metadata files section and use the webpack copy plugin.

// dependency.js
function feature() {
  console.log("Hello World")
}

// index.js
feature(); // -> "Hello World"

// metadata.json
{
  "main.js",
  "main.css",
  "dependency.js"
}

// webpack.config.js
 new CopyWebpackPlugin([
  {from: './dependency.js', to: 'dependency.js'},
]),

When not using webpack, any functions defined in dependency.js will be added into window by the browser.

Using webpack

You can also use import but then your dependency needs to assign functions to the global scope explicitly:

// dependency.js
window.feature = function () {
  console.log("Hello World")
}

// index.js
feature(); // -> "Hello World"

Notes on global modules

Assigning methods etc directly to window is bad. If you load a lot of dependency files across several packages, you’ll probably end up with collisions.

/// dependency.js
window.myNamespace = {
  feature: function() {
    console.log("Hello World")
  }
};

// index.js
const {feature} = window.myNamespace;
feature(); // -> "Hello World"

This applies to both the webpack way, or the metadata way.

If you’re doing the global assignment approach and you use the same files across several packages, consider loading the files in the base OS.js project instead with the method described in the manual.


#6

Thanks for that, really helps.


#7

This should really be:

// dependency.js
window.feature = function () {
  console.log("Hello World")
}

// index.js
import './dependency.js';
feature(); // -&gt; "Hello World"

But I figure you got that already :smiley: