nativeRequire vs require

I wondered what the difference is?

I have been reading/learning a lot recently about JavaScript and node.js, and specifically about modularising my huge custom module file.
As a result I’ve come to wonder why in OSC I have to use nativeRequire and if I use require I throw an error, yet all the coding outside of OSC that I read doesn’t understand nativeRequire

  • nativeRequire is node's original require
  • require is an o-s-c-specific function that mimics node's require but only for local files and treats imported files as custom modules (provides some functions and enables autorestart).

The naming is debatable and mainly the result of the order in which they appeared in o-s-c.

1 Like

Thanks, that clarifies a bit I think.

The difficulty that I have is knowing when to use require and when to use nativeRequire. Are there any rules or guidance or is it just trial and error?

Only use nativeRequire for node modules installed via npm in the custom module directoy or for native node modules such as fs or net. Use require for your own files.

1 Like

Sorry, but really struggling with this - I can't get the submodules to work correctly and I keep getting an error. My submodule is in a subdirectory of the main custom module and is called correctly but I can tell from the console.log that 'mainis null so I think that therequire` is not finding the main custom module called EnsembleTest.js.

Have I missed something??

Here is my submodule:

// JavaScript source code
// handleEnsembleFilterBtn.js
// Function to handle Ensemble Filter button press
const main = require('../EnsembleTest'); //  Does this correctly point to EnsembleTest.js??

console.log('EnsembleTest module:', main); //this currently returns null
function handleEnsembleFilterBtn(args) {
    if (!main) {
        console.error("Error: EnsembleTest module not properly initialized."); //due to the null above I end up here...
        return;
    }

    let {
        ensFilterInVal,
        ensFilterSwitchVal,
        ensFilterMidiChanRoot,
        ensFilterMidiChan,
        ensFilterccSendVal,
        showOnlySwitch,
        ensFilterMidiPort,
    } = main;

    ensFilterInVal = args[0].value;
    ensFilterccSendVal = ensFilterInVal;
    ensFilterMidiChan = ensFilterMidiChanRoot + ensFilterSwitchVal;

    switch (showOnlySwitch) {
        case 1: // Show Only Remote
            main.sendOscCommandWithShowAll(ensFilterccSendVal, ensFilterMidiChan, ensFilterMidiPort);
            break;
        case 2: // All Others
            main.sendOSCCommand(ensFilterccSendVal, ensFilterMidiChan, ensFilterMidiPort);
            break;
    }
}

module.exports = {
    handleEnsembleFilterBtn
};

The file extension is not added automatically (I should add that though,sorry), try

require('../EnsembleTest.js')

Circular dependencies can be tricky to handle (and lead to null returns when they cannot be resolved), better avoid them !

Thanks - useful to know.

I'm beginning to understand that - rethinking my solution now...

One way to make them work is to require() your main module in a setTimeout callback to make sure it has finished loading before requiring it (note that it will make main undefined for a tiny amount of time):

var main

setTimeout(()=>{
  main = require('module_that_required_this_file_first.js')
})
2 Likes

Thanks, I have refactored the majority of my code now so that it avoids circular references.
However...
I have run up against a require vs nativeRequire issue in one of my sub-modules that for some reason I cant resolve.

It appears if I put

const xlsx = nativeRequire('xlsx'); in my main.js module everything works, but if I but the same code in a sub-module ( that is held in a sub-directory) then the code fails. I can call the submodule if i don't use a node.js module, and I can call it if the sub-module is in the same root directory.

I've also tried const xlsx = require('xlsx'); in the sub-module and this doesn't work.

The only way I can see to resolve this is to install all the node dependencies into each of the sub-modules, that I am obviously keen to avoid...

It's nativeRequire that should be called here, definitely. I ran a quick test just to be sure and I can have both the main module and a submodule (in a subdirectory) load a node module (installed in the main module's directory only). Maybe there's something else causing this, can you upload your custom module entirely ?

I can, here you go - I have included the directory structure so you can see how I have it set up...

mainModule.js (3.5 KB)
configLoader.js (3.2 KB)
oscUtils.js (1.4 KB)
testModule.js (2.1 KB)
chordMatrixModule.js (2.1 KB)
excelVariablesConfig.js (1.6 KB)
midiConfig.js (3.3 KB)

project-root/
├── config/
│ ├── excelVariablesConfig.js
│ ├── midiConfig.js
│ ├── someConfigFile1.js
│ └── ...
├── functions/ <-- nothing in here currently

├── node_modules/
│ └── ... (any installed node modules)
├── modules/
│ └── chordMatrixModule.js
├── oscUtils.js
├── Variables.xlsx
├── mainModule.js
├── configLoader.js
├── package.json
└── README.md

It seems I can't load it without all the files, can you zip the entire structure (maybe excluding node_modules/ if it's too heavy) ?

Also, what's the error exactly when running nativeRequire('xlsx') in a submodule ?

I think that is all of it... excluding the node_modules

OSCDevelopmentProject.zip (67.7 KB)

(ERROR) Custom module loading error
C:\Users\treca\Open Stage Control\Custom Modules\OSCDevelopmentProject\config\excelVariablesConfig.js:2
const xlsx = nativeRequire('xlsx');
             ^

ReferenceError: nativeRequire is not defined
    at Object. (C:\Users\treca\Open Stage Control\Custom Modules\OSCDevelopmentProject\config\excelVariablesConfig.js:2:14)
    at Module._compile (node:internal/modules/cjs/loader:1110:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1140:10)
    at Module.load (node:internal/modules/cjs/loader:982:32)
    at Module._load (node:internal/modules/cjs/loader:823:12)
    at Function.c._load (node:electron/js2c/asar_bundle:5:13331)
    at Module.require (node:internal/modules/cjs/loader:1006:19)
    at require (node:internal/modules/cjs/helpers:93:18)
    at o (C:\Program Files\OSC\open-stage-control_1.26.2_win32-x64\resources\app\server\node_modules\browser-pack\_prelude.js:1:1)
    at C:\Program Files\OSC\open-stage-control_1.26.2_win32-x64\resources\app\server\node_modules\browser-pack\_prelude.js:1:1
(ERROR) Custom module loading error
C:\Users\treca\Open Stage Control\Custom Modules\OSCDevelopmentProject\mainModule.js:1
const { getConfig, getFunction, getStartUpConfig } = require('./configLoader.js');
        ^

TypeError: Cannot destructure property 'getConfig' of 'require(...)' as it is null.
    at C:\Users\treca\Open Stage Control\Custom Modules\OSCDevelopmentProject\mainModule.js:1:9

configLoader.js loads config files using nativeRequire which doesn't treat them as custom modules and doesn't pass osc-specific functions such as nativeRequire, hence the error

ReferenceError: nativeRequire is not defined

This one suggests there is still a circular dependency issue too:

C:\Users\treca\Open Stage Control\Custom Modules\OSCDevelopmentProject\mainModule.js:1
const { getConfig, getFunction, getStartUpConfig } = require('./configLoader.js');
        ^

TypeError: Cannot destructure property 'getConfig' of 'require(...)' as it is null.

Thanks, not sure how I deal with that other than putting the variable call using xlsx for the excel file data into the mainModule and pass it to the submodules directly for use - or I move away from excel file and create a .js variable for each of the sheets (which is not ideal as the idea of the excel sheet is that it can be user amended.

I also use a similar methodology to extract a lot of data from xml files but I've not started to refactor that code yet.

For the circular reference, I think that I'm ok with that as all the code works ok for the other modules I've created - it only fails if I start to try to use the nativeRequire node calls in submodules.

But equally, if I roll back on my code a bit, I have these lines in my configLoader.js

const path = nativeRequire('path');
const fs = nativeRequire('fs');

//some additional code to dynamically create a const getConfig

const chordChangeTarget = getConfig('chordChangeModule.target');

function getStartUpConfig() {


    // Check if chordChangeTarget is undefined
    if (typeof chordChangeTarget === 'undefined') {
        console.warn("Warning: chordChangeTarget is undefined")
    } else {
        // ChordChangeTarget MIDI Port
        receive('/SET', 'chordChangeTarget', chordChangeTarget)
        console.log("Chord change module MIDI device set as " + chordChangeTarget)
    }

    return
}

module.exports = {
    getConfig,
    getStartUpConfig
};

And yet this works as a submodule that as part of getStartUpConfg passes the chordchangetarget I assume via the mainModule as I can log the chordChangeTarget to the console using this in the chordMatrixModule (which is called from the mainModule:

const { target } = require('../config/midiConfig.js').chordModuleMidi;

function handleChordMatrixMessage(address, args) {
    if (address === '/SendChordTransposeBtn') {
        console.log("chordChangeModuleMidi " + target);

    }
}
module.exports = {
    handleChordMatrixMessage
};
1 Like

The issue I pointed out was specifically due to these calls in configLoader.js :

 const configData = nativeRequire(configPath);

that should use require instead.