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 the
require` 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.