Get URL for EcmaScript module after importing in browser - browser

My start HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<script type="module" src="/way/to/app.js" defer></script>
</head>
<body>...</body>
</html>
My app.js script is an EcmaScript Module and loads another EcmaScript Module:
import Mod from "./path/to/module.js";
...
this is module.js:
// const url = ???
export default class Module {
constructor() {
// console.log(url);
}
}
Does exist any method to get loading URL "/way/to/path/to/module.js" in module.js? Something like these variables in nodejs but for browser:
const dir = __dirname;
const file = __filename;
location.href gives an URL for start html page.

Google Chrome and Firefox both support import.meta:
<script type="module">
console.log(import.meta);
</script>
In my console that prints:
Object { url: "file:///D:/testImportMeta.html" }
I think it's not really suited for production, unless Babel supports it. But if you're not over-concerned with lazy browsers, it works well.
It does not work in Node.JS yet.

Related

Ejs doesn't load when I call a function from another file

Basicly I want to run a function when I clicked the button, but it works when I started the server and go to localhost one time, here's what's supposed to happen, after that localhost page doesn't load. (Unable to connect error)
If I remove the function there is no problem. How can I get it to work only when I click the button ?
Many thanks.
My func.js
const mongoose = require("mongoose");
const axios = require('axios');
async function func() {
//MyCodes
}
module.exports = {
func: func
}
My index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button type="button" onClick= <%= func.func() %> >Click</button>
//Other codes are independent the button
</body>
</html>
My res.render codeblocks in app.js
var func = require('./func');
app.get("/", (req, res) => {
res.render('index', {
cName: name,
symbol: symbol,
len: finds[0].result.length,
cPrice: price,
cDate: date,
func:func
});
});
});
})
You are misunderstanding. You cannot call an internal nodejs function(backend) from the html (frontend). If your frontend need to execute some backend operation like query to mongo, you have these options:
#1 client side rendering (Modern)
This is the most used in the modern world: Ajax & Api
your backend exposes a rest endpoints like /products/search who recieve a json and return another json
this endpoints should be consumed with javascript on some js file of your frontend:
html
<html lang="en">
<head>
<script src="./controller.js"></script>
</head>
<body>
<button type="button" onClick="search();" >Click</button>
</body>
</html>
controller.js
function search(){
$.ajax({
url:"./api/products/search",
type:"POST",
data:JSON.stringify(fooObject),
contentType:"application/json; charset=utf-8",
dataType:"json",
success: function(response){
...
}
})
}
Note 1: controller.js contains javascript for browser not for backend : nodejs
Note 2: ejs is only used to return the the initial html, so it is better to use another frameworks like:react, angular, vue
#2 server side rendering (Legacies)
In this case, ajax and js for browser are not strictly required.
Any event on your html should use <form> to trigger an entire page reload
You backend receives any parameter from the , make some operations like mongo queries and returns html instead json, using res.render in your case
Note
Ejs is for SSR = server side rendering, so add ajax could be complex for novices. In this case, use the option #2
You cannot use a nodejs function (javascript for server) in the client side (javascript for browser). Maybe some workaround are able to do that but, don't mix different things.

Chrome extension: DOMParser is not defined with Manifest v3

I have developped an extension to scrape some content from web page and up to now it was working fine but since I switched to manifest v3, the parsing doesn't work anymore.
I use the following script to read the source code:
chrome.scripting.executeScript(
{
target: {tabId: tab.id, allFrames: true},
files: ['GetSource.js'],
}, async function(results)
{
// GETTING HTML
parser = new DOMParser();
content = parser.parseFromString(results, "text/html");
... ETC ...
This code used to work fine but now I get the following message in my console:
Uncaught (in promise) ReferenceError: DOMParser is not defined
The code is part of a promise but I don't think the promise is the problem here. I basically need to load the source code into a variable so that I can parse it afterwards.
I've checked the documentation but I haven't found something mentionned that DOMParser was not going to work with v3.
Any idea?
Thanks
Since service workers don't have access to DOM, it's not possible for
an extension's service worker to access the DOMParser API or create an
to parse and traverse documents.
More detail
And I solve the problem by using library dom-parser.The code could be like this
import DomParser from "dom-parser";
const parser = new DomParser();
const dom = parser.parseFromString('you html string');
From the docs:
Since service workers don't have access to DOM, it's not possible for an extension's service worker to access the DOMParser API or create an to parse and traverse documents.
Using an external library just for doing what DomParser already does?? It is too heavy.
To work-around with it, we can use an offscreen document. It's just invisible webpage where you can run fetch, audio, DomParser, ... and communicate with background (service_worker) via chrome.runtime.
See an example below:
background.js
chrome.offscreen.createDocument({
url: chrome.runtime.getURL('xam.html'),
reasons: [chrome.offscreen.Reason.DOM_PARSER],
justification: 'reason for needing the document',
});
// Just a test
setTimeout(() => {
const onDone = (msg) => {
console.log(msg);
chrome.runtime.onMessage.removeListener(onDone);
};
chrome.runtime.onMessage.addListener(onDone);
chrome.runtime.sendMessage('from-background-page');
}, 3000);
xam.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="xam.js">
</script>
</body>
</html>
xam.js
async function main() {
const v = await fetch('https://......dev/').then((t) => t.text());
const d = new DOMParser().parseFromString(v, 'text/html');
const options = Array.from(d.querySelector('select').options)
.map((v) => `${v.value}|${v.text}`)
.join('\n');
chrome.runtime.sendMessage(options);
}
chrome.runtime.onMessage.addListener(async (msg) => {
console.log(msg);
main();
});
manifest.json
"permissions": [
// ...
"offscreen"
]
https://developer.chrome.com/docs/extensions/reference/offscreen/
The extension's permissions carry over to offscreen documents, but extension API access is heavily limited. Currently, an offscreen document can only use the chrome.runtime APIs to send and receive messages; all other extension APIs are not exposed.
Notes:
I haven't tested how long this offscreen document alive.
Just sample codes, it should work. Customzie as your own cases.

Node.js jsdom set document root

I am trying to load local javascripts with jsdom.
Now I am wondering how I can make jsdom loading the javascripts from "__dirname/../public".
Can someone help me?
My current code is:
var fs = require('fs');
var jsdom = require('jsdom');
jsdom.defaultDocumentFeatures = {
FetchExternalResources: ["script"],
ProcessExternalResources: ["script"],
MutationEvents : '2.0',
QuerySelector : false
};
exports.test = function(req, res) {
var html = fs.readFileSync(__dirname+'/../public/html/lame.html');
var document = jsdom.jsdom(html, null, {documentRoot: __dirname+'/../public/'});
var window = document.createWindow();
window.addEventListener('load', function () {
//window.$('script').remove();
//window.$('[id]').removeAttr('id');
res.send(window.document.innerHTML);
window.close();
});
}
The simple HTML page is:
<html>
<head>
<title id="title">bum</title>
<script type="text/javascript" src="/javascripts/jquery.min.js"></script>
<script type="text/javascript" src="/javascripts/stuff.js"></script>
</head>
<body>
<h1>hello world</h1>
<h2 id="bam">XXX</h2>
</body>
</html>
I've run into the same issue and got it to work. Try these, in order:
documentRoot is not documented. The option which is documented is url. So replace documentRoot with url.
If the above is not enough, then add a base element. I've set my templates like this:
<head>
<base href="#BASE#"></base>
<!-- ... -->
</head>
where #BASE# is replaced with the same value as the one passed to url.
The solutions above are extracted from actual code in use in a test suite.

How to share common function between server and client using node.js

Following are the structure of my application
Inside prototype.js file i have following code:
(function(exports) {
exports.foo = function() {
return 'bar';
};
})((typeof process === 'undefined' || !process.versions) ? window.common = window.common || {} : exports);
app.js contains
var express = require('express'),app = express(),server = require('http').createServer(app),io = require('socket.io').listen(server),port = 3000,path = require('path');
var common = require('common/prototype');
console.log(common.foo());
// listening to port...
server.listen(port);
//Object to save clients data
var users = [];
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', function (req, res) {
res.sendfile('/public/index.html');
});
index.html contains
<!doctype html>
<html lang="en">
<head>
<title>Socket.io Demo</title>
</head>
<body>
<script src="/socket.io/socket.io.js"></script>
<script src="/common/prototype.js"></script>
<script>
alert(window.common.foo()); //This line will gives me an error TypeError: window.common is undefined
</script>
</body>
</html>
Now i would like to print
Hello, I am bar from server and client as well.
Now i am able to print from server side using following line
var common = require('common/prototype');
console.log(common.foo());
But could not able to show alert on client side. could you please help me to find the root cause for the issue.
The root cause is that when you do <script src="/common/prototype.js"></script> in your HTML the file won't be fetched because the Express static middleware is only looking for files under your public folder.
A quick way to test this is to copy your prototype.js to your javascript folder inside public. Then update your script tag to reference the file as follows <script src="/javascripts/prototype.js"></script>
The thing to remember is that the JavaScript files that live under node_modules are not automatically available to the browser.

Testing NodeJS with Mocha - "Require is not defined"

I've been having trouble setting up a test framework for a NodeJS + Backbone app with the constant "require is not defined" error. I finally got it working using an in-browser test framework which picks up all of the dependencies I need and running a test.js file.
Currently, I'm only doing basic testing of my Backbone models, views, and collections. Now, I want to add in API testing but I'm back to the same "require is not defined" error. What is causing this? It's clear that I'm missing something fundamental here. I just want to add:
var request = require('supertest')
, express = require('express');
var app = express();
Snippet of test.js:
describe('Application', function(){
it("creates a global variable for the namespace", function() {
should.exist(App);
})
});
describe('Models', function() {
describe('SearchFormModel', function() {
beforeEach(function() {
this.SearchFormModel = new App.Model.SearchFormModel();
this.defaultFields = this.SearchFormModel.attributes;
})
it("created a SearchFormModel", function() {
should.exist(this.SearchFormModel);
})
it("should have 7 default fields", function() {
Object.keys(this.SearchFormModel).length.should.equal(7);
})
it("should default all fields to empty string", function() {
for (var key in this.defaultFields) {
this.defaultFields[key].should.equal("");
}
})
});
});
test-runner.html:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Title & Meta -->
<title>Frontend tests</title>
<meta charset="utf-8">
<!-- Stylesheets -->
<link rel="stylesheet" href="../node_modules/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<!-- Testing Libraries -->
<script src="../node_modules/mocha/mocha.js"></script>
<script src="../node_modules/chai/chai.js"></script>
<script>
// Use the expect version of chai assertions - http://chaijs.com/api/bdd
var should = chai.should();
// Tell mocha we want TDD syntax
mocha.setup('tdd');
</script>
<!-- Libs -->
<script src="../public/lib/jquery-1.8.2.min.js"></script>
<script src="../public/lib/underscore-min.js"></script>
<script src="../public/lib/backbone-min.js"></script>
<script src="../public/lib/bootstrap.min.js"></script>
<script src="../public/lib/highcharts.js"></script>
<script src="../public/lib/bootstrap-datepicker.js"></script>
<script src="../public/js/modules/exporting.js"></script>
<!-- Source files -->
<script src="../public/js/namespace.js"></script>
<script src="../public/js/jst.js"></script>
<script src="../public/js/utils.js"></script>
<script src="../public/js/models/models.js"></script>
<script src="../public/js/models/search.js"></script>
<script src="../public/js/models/plot.js"></script>
<script src="../public/js/models/search_result.js"></script>
<script src="../public/js/views/header.js"></script>
<script src="../public/js/views/plot.js"></script>
<script src="../public/js/views/list.js"></script>
<script src="../public/js/views/search.js"></script>
<script src="../public/js/router.js"></script>
<script src="../public/js/app.js"></script>
<!-- Test -->
<script src="test.js"></script>
<script>
mocha.run();
</script>
</body>
</html>
require and commonjs only works in Node.js
If you run Browser test, then you need to code it like you'll run it in the browser. Also note that Unit Test should be done in isolation, you shouldn't need to load you app server (express) to run your test.
I'd like to point you to an easy solution from there, but there's just too many choices. Very basically, you should start running browser test in the browser by loading an html file.
Then, you'll want to automatize this and run browser test from the terminal. That's when you want to run test in PhantomJs and the likes and output browser results on the terminal. Around this, you can checkout Karma and Testem who're two browser test runner (remember here Mocha alone won't run browser test via command line).
As you're using Backbone, you might be interested in the Backbone-Boilerplate Karma + Grunt test setup as a starting point. See more on this here: https://github.com/backbone-boilerplate/backbone-boilerplate

Resources