Node.js jsdom set document root - node.js

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.

Related

Get URL for EcmaScript module after importing in 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.

Accessing script content with cheerio

I'm using Cheerio and request with Node.js to do some basic web scraping, but can't seem to figure out how to access the data. The page loads via request and I'm able to console.log the page title using Cheerio, but when I get to the scripts it's a complex mess of objects.
In the body section of the page it looks like..
<body>
<script src="someUrl" script type="text/javascript" />
<script src="someUrl" script type="text/javascript" />
<script src="someUrl" script type="text/javascript" />
<script type="text/javascript">var months = [6,12,24,36,48,60]; var amounts = [5000,10000,15000,20000,25000]</script>
I'm trying to get to the variables in the last script to store them as variables in my node script for use, but I can't seem to access them, even as text.
When I try this in node, I get the page title followed by some huge object response in the console, not the variable text to parse. Suggestions?
$ = cheerio.load(body);
console.log($('title').text());
var text = $('script');
console.dir(text[3]);
You can parse those variables with regex but cheerio is a little messy:
var cheerio = require('cheerio')
var html = `
<body>
<script src="someUrl" type="text/javascript" />
<script src="someUrl" type="text/javascript" />
<script src="someUrl" type="text/javascript" />
<script type="text/javascript">var months = [6,12,24,36,48,60]; var amounts = [5000,10000,15000,20000,25000]</script>
</body>
`
var str, $ = cheerio.load(html, {xmlMode: true}); // xmlMode: true is a workaround for many cheerio bugs.
console.log(str = $('script:not([src])')[0].children[0].data) // no cleaner way to do this, cheerio?
// var months = [6,12,24,36,48,60]; var amounts = [5000,10000,15000,20000,25000]
var months = JSON.parse(str.match(/months = (\[.*?\])/)[1])
console.log(months)
// [ 6, 12, 24, 36, 48, 60 ]
var amounts = JSON.parse(str.match(/amounts = (\[.*?\])/)[1])
console.log(amounts)
// [ 5000, 10000, 15000, 20000, 25000 ]

Express only serving css file in main page

I am learning node and using expressjs the problem is that the css file is only working on the main page and not any other page following is the code for the app:
var express = require("express");
var app = express();
// assets
app.use(express.static("public"));
// simple file testing
app.get("/anything/:stuff", function(req, res){
var stuff = req.params.stuff;
res.render("love.ejs", {stuff: stuff});
});
// posts page
var books = [
{title:"Harry Potter",author:"JK Rowling",editions:7},
{title:"Peere Kamil",author:"Humaira Saeed",editions:4},
{title:"Mushaf",author:"Abdullah khan",editions:2}
];
app.get("/books", function(req, res){
res.render("books.ejs",{books:books});
});
app.listen(3000, process.env.IP, function(){
console.log("Serving Now!");
});
The following is the code for the page where it is not working.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="app.css" />
<title>demo app</title>
</head>
<body>
<h1>You fell in love with : <%= stuff %> </h1>
<%
if(stuff.toUpperCase() === "WAQAS"){ %>
<P>GOOD CHOICE HE IS THE BEST</p>
<% } %>
<p>P.S This is the love.ejs file</p>
</body>
</html>
The file is under public directory.
Use an absolute URL for the CSS file, so the browser doesn't look for it relative to the current URL:
<link rel="stylesheet" href="/app.css" />
Explanation: say that you open the URL /anything/helloworld in your browser. When your HTML contains just app.css, without the leading slash in front of it, the browser will try and load /anything/app.css (because without that slash it's relative to the current URL), which doesn't point to your CSS file.
When you add the leading slash, express.static will be able to find the CSS file in public/.

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.

Change Google Analytics code using Plates

Is there a way to change the Google Analytics code included in a template using Plates?
For example, for the below template:
<!DOCTYPE html>
<html>
<head></head>
<body>
<script id="googleAnalytics">
var _gaq=[['_setAccount','GA_ACCOUNT_CODE'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
</body>
</html>
I would like to use a different GA_ACCOUNT_CODE depending on the environment the code runs in.
Is this possible with Plates? If not, what is the common way one would solve this issue in NodeJS & Flatiron?
Plates' idea is great but far from complete. Here is a solution.
app.js
var fs = require('fs');
var plates = require('plates');
var flatiron = require('flatiron');
var app = flatiron.app;
app.use(flatiron.plugins.http);
app.router.get('/', function(){
var html = fs.readFileSync('index.html', 'utf-8');
var map = plates.Map();
var data = {"GA_ACCOUNT_CODE": "YOUR_CODE_FROM_CONFIG"}
map.where('data-ga').is('GA_ACCOUNT_CODE').insert('GA_ACCOUNT_CODE');
html = plates.bind(html, data, map);
this.res.html(html);
});
app.start(3000);
index.html
<!DOCTYPE html>
<html>
<head></head>
<body>
<script id="googleAnalytics" data-ga="GA_ACCOUNT_CODE">
var GA_ACCOUNT_CODE = document.getElementById('googleAnalytics').getAttribute('data-ga');
var _gaq=[['_setAccount',GA_ACCOUNT_CODE],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
</body>
</html>
Another way would just be string.replace() like this:
var html = fs.readFileSync('index.html', 'utf-8');
html = html.replace('GA_ACCOUNT_CODE', 'YOUR_CODE_FROM_CONFIG');
this.res.html(html);
Have a look at cheerio for support. It's like any other css selector and dom manipulation frontend library, just backends.

Resources