After some very helpful tips learning about javascript promises and how they behave, I am looking for some help with properly importing/exporting functions from one javascript file into another which is run via NodeJS.
Essentially I want basicNode.js to open an html doc, and then once that is open (and the promise implied via the open(html) statement is 100% complete/finalized) run the function from change.js to alter the html to show "Hello" in the place of "Welcome to JavaScript". This will provide proof of concept that can then be extrapolated for a project dealing with automating reports at my internship, so any help is greatly appreciated
basic.html
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Date/Time: <span id="datetime"></span></p>
<p id="here">Welcome to JavaScript</p>
<form>
<input type="button" value="click" onclick="changeThis()"/>
</form>
</body>
<script>
var dt = new Date();
document.getElementById("datetime").innerHTML = dt.toLocaleString();
</script>
<script type="text/javascript" src="change.js"></script>
<!--
<script>waitForElementToDisplay("#here",function(){alert("Hi");},1000,9000);</script>
-->
</html>
change.js
function changeThis() {
document.getElementById("here").innerHTML = "Hello";
}
module.exports={changeThis};
basicNode.js
const open = require('open');
var { change } = require('./change')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
global.document = new JSDOM('./basic.html').window.document;
const main = async () => {
await open('./basic.html');
change.changeThis();
}
main();
EDIT:after some tinkering, this is how I accomplished my task: in change.js, I set up the following:
const fs = require('fs');
const html = fs.readFileSync('./basic.html');
const dom = new JSDOM(html);
function changeThis() {
console.log("called changeThis() function, should alter the html")
var x = dom.window.document.getElementById('here').textContent;
console.log(x);
dom.window.document.getElementById('here').innerHTML = 'Hello'
var x = dom.window.document.getElementById('here').textContent;
console.log(x);
}
module.exports.changeThis = changeThis
Then, to call/import this function in my basicNode.js file:
var change = require('./change');
change.changeThis();
In your change.js-file you are exporting your function as a named export. A named export is normally used if you are only exporting one function from a file (this is not a rule), and named exports are mainly used when you are exporting several functions from a file.
This is a named export
module.exports= { changeThis };
A named export is imported as
var { changeThis } = require('./change')
The opposite to a named export is a default export.
module.exports = changeThis
This is imported as
var changeThis = require("./change")
Edit:
If what you want is to read the file, moodify the DOM and then write it to a file again, then you can do it like this.
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
// Read the file from the system
const fs = require('fs');
const html = fs.readFileSync('./basic.html');
// Create a jsdom and modify the file
const dom = new JSDOM(html);
dom.window.document.getElementById('here').innerHTML = 'Hello';
// write the file to disc
fs.writeFileSync(
'./basicUpdated.html',
dom.window.document.documentElement.outerHTML
);
Related
I need to test some javascript I have in a php file (cannot be turned in any other extension file, for a few reasons, including it also contains php).
How can I achieve this using Jest?
For example, I have tried the following:
const parse = require('regenerator-runtime/runtime');
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
test("test JavaScript code in PHP file", async () => {
//create a mock of the DOM
const dom = new JSDOM(`<!DOCTYPE html><body>
<script>
// Add JS code from php file in here
let sum = function sum(a, b) {
return a + b
}
</script>
</body>`);
global.document = dom.window.document;
global.window = dom.window;
global.navigator = dom.window.navigator;
// run your tests here
expect(sum(1, 2)).toBe(3);
});
Unfortunately gives me an error:
ReferenceError: sum is not defined
How can I test the JS within the mock of the DOM since it is in it's own scope?
Is there a better way to test javascript in a php file?
I want to know how to export the npm module to another file without using the required function on that file.
-index.js-
const cv = require("opencv4nodejs");
const add = require("./addon");
cv.imshowWait("", add.img());
my main javascript file show like this
-addon.js-
const cv = require("opencv4nodejs");
exports.img = () =>{
return cv.imread("lenna.jpg")
}
my exports javascript file shows like this
simply in hear addon.js file read image file and exports to the index.js file and in hear it shows the image. it is working fine
But I want to remove the require function
const cv = require("opencv4nodejs");
on the addon.js file
and exports that npm module from the index.js file to the addon.js file.
Something like this in the index.js file
const cv = require("opencv4nodejs");
const add = require("./addon")(cv);
How to do that guys...
You need to export a function which accepts the cv and makes it available in your module.
So something like bellow:
// addon.js
var cv;
const img = () =>{
return cv.imread("lenna.jpg")
}
module.exports = function (opencv4nodejs) {
cv = opencv4nodejs;
return { img };
}
Then use it as you already shown:
// index.js
const cv = require("opencv4nodejs");
const add = require("./addon")(cv);
cv.imshowWait("", add.img());
If some JS code has this
import("path/to/file.js")
and then file.js has this
export default async function() {
// I want to get "path/to" here
return {};
}
How can I get the directory of where file.js is?
export default async function() {
var current_file = import.meta.url;
var dir_path = import.meta.url.substring(0, import.meta.url.lastIndexOf("/"));
return {};
}
See compatibility here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta
I'm writing a simple server for Node.js and I'm using my own class called User which looks like:
function User(socket) {
this.socket = socket;
this.nickname = null;
/* ... just the typical source code like functions, variables and bugs ... */
this.write = function(object) {
this.socket.write(JSON.stringify(object));
}
};
and then later in the process I'm instantiating it a lot:
var server = net.createServer(function (socket) {
/* other bugs */
var user = new User(socket);
/* more bugs and bad practise */
});
Can I move my User class definition to another javascript file and "include" it somehow?
You can simply do this:
user.js
class User {
//...
}
module.exports = User // 👈 Export class
server.js
const User = require('./user.js')
let user = new User()
This is called CommonJS module.
ES Modules
Since Node.js version 14 it's possible to use ES Modules with CommonJS. Read more about it in the ESM documentation.
user.mjs (👈 extension is important)
export default class User {}
server.mjs
import User from './user.mjs'
let user = new User()
Using ES6, you can have user.js:
export default class User {
constructor() {
...
}
}
And then use it in server.js
const User = require('./user.js').default;
const user = new User();
Modify your class definition to read like this:
exports.User = function (socket) {
...
};
Then rename the file to user.js. Assuming it's in the root directory of your main script, you can include it like this:
var user = require('./user');
var someUser = new user.User();
That's the quick and dirty version. Read about CommonJS Modules if you'd like to learn more.
Another way in addition to the ones provided here for ES6
module.exports = class TEST{
constructor(size) {
this.map = new MAp();
this.size = size;
}
get(key) {
return this.map.get(key);
}
length() {
return this.map.size;
}
}
and include the same as
var TEST= require('./TEST');
var test = new TEST(1);
If you append this to user.js:
exports.User = User;
then in server.js you can do:
var userFile = require('./user.js');
var User = userFile.User;
http://nodejs.org/docs/v0.4.10/api/globals.html#require
Another way is:
global.User = User;
then this would be enough in server.js:
require('./user.js');
In MyModule folder, I have this two JS files.
SayHello.js
module.exports.SayHello = function() {
return('Hello !');
}
SayByeBye.js
module.exports.SayByeBye = function() {
return('Bye Bye!');
}
In Node.js, I want to require all files in MyModule folder and call function SayHello & SayByeBye directly something like:
require(./MyModule)
console.log(SayHello());
console.log(SayByeBye());
EDIT:
With answer of #Yanick Rochon,I do this :
> ./app/my-module/index.js
global.SayHello = require('./my-module/SayHello').SayHello;
global.SayByeBye = require('./my-module/SayByeBye').SayByeBye;
> ./app/my-module/say-hello.js
module.exports.SayHello = function() {
return('Hello !');
};
> ./app/my-module/say-byebye.js
module.exports.SayByeBye = function() {
return('Bye Bye !');
};
> ./app/main.js
require('./my-module');
console.log(SayHello());
console.log(SayByeBye());
There's a section about global objects in the node documentation.
However, globals should be used with care. By adding modules to the global space I reduce testability and encapsulation. But in this case, I think using this method is acceptable.
First thing first...
I believe you are mistaking Node.js with PHP or .Net, in the sense that you don't "import" into the current module what is exported in other modules. Not unless you manually do it anyway. For example, when you call
require('./my-module');
(Note that I renamed your MyModule into Node.js naming convention.)
You don't load things into the current context; you just load the script and don't assign it to anything. To access what my-module exposes, you need to assign it, or use it directly. For example :
require('./my-module').someFunction();
or
var myModule = require('./my-module');
myModule.someFunction();
Modules are not namespaces, but JavaScript objects that exposes public properties outside of their own contexts (i.e. using module.exports = ...)
Answer
You have two most popular ways to accomplish this :
Solution 1
Create an index.json file inside your folder where you want to load all of your scripts. The returned JSON object should be all the modules to load automatically :
> ./app/index.json
[
"say-hello.js",
"say-goodbye.js"
]
You should also consider having all your files API compatible :
> ./app/say-hello.js
module.exports = function sayHello() {
return 'Hello !';
};
> ./app/say-goodbye.js
module.exports.sayGoodbye = function () {
return 'Goodbye !';
};
Then load and execute everything like this :
var path = require('path');
var basePath = './app/';
var files = require(basePath);
var mods = files.forEach(function (loaded, file) {
var mod = require(path.join(basePath, file));
// mod is a function with a name, so use it!
if (mod instanceof Function) {
loaded[mod.name] = mod;
} else {
Object.keys(mod).forEach(function (property) {
loaded[property] = mod.property;
});
}
}, {});
mods.sayHello();
mods.sayGoodbye();
Solution 2
Read all .js files inside your folder and import them. I highly recommend you use glob for this.
var glob = require("glob")
var path = require('path');
var basePath = './app/';
var mods = glob.sync(path.join(basePath, '*.js')).reduce(function (loaded, file) {
var mod = require(file);
// mod is a function with a name, so use it!
if (mod instanceof Function) {
loaded[mod.name] = mod;
} else {
Object.keys(mod).forEach(function (property) {
loaded[property] = mod.property;
});
}
return loaded;
}, {});
mods.sayHello();
mods.sayGoodbye();
Note on the difference between module.exports and exports
Typically module.exports === exports, but it is recommended to use module.exports for the following reason
exports = function Foo() { } // will not do anything
module.exports = function Foo() { } // but this will do what you expect
// however these two lines produce the same result
exports.foo = 'Bar';
module.exports.foo = 'Bar';
For this reason, module.exports is recommended in all cases.
It's not perfect, but something like this should help you accomplish this:
var fs = require('fs');
var path = require('path');
var files = fs.readdirSync(__dirname);
var ownFilename = __filename.substr(__filename.lastIndexOf(path.delimiter) + 1);
var modules = {};
for (var i = 0; i < files.length; i++) {
var filename = files[i];
if (filename.substr(-3) === '.js' && filename !== ownFilename) {
modules[filename.slice(0, -3)] = require('./' + filename);
}
}
console.log(modules.SayByeBye());
console.log(modules.SayHello());