-
-
Notifications
You must be signed in to change notification settings - Fork 374
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #678 from w3c/use_nightmarejs
Use nightmarejs
- Loading branch information
Showing
4 changed files
with
172 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,141 +1,178 @@ | ||
#!/usr/bin/env phantomjs --ssl-protocol=any | ||
|
||
/*global phantom, respecEvents, respecConfig, require*/ | ||
// respec2html is a command line utility that converts a ReSpec source file to an HTML file. | ||
// Depends on PhantomJS <http://phantomjs.org>. | ||
#!/usr/local/bin/node | ||
|
||
/*jshint node: true, browser: false*/ | ||
"use strict"; | ||
var page = require("webpage").create(); | ||
var args = require("system").args.slice(); | ||
var fs = require("fs"); | ||
var timer; | ||
var reportErrors = false; | ||
var reportWarnings = false; | ||
var ignoreScripts = false; | ||
var errors = []; | ||
var warnings = []; | ||
var delay = 0; | ||
const async = require("marcosc-async"); | ||
const colors = require("colors"); | ||
const os = require("os"); | ||
colors.setTheme({ | ||
data: "grey", | ||
debug: "cyan", | ||
error: "red", | ||
help: "cyan", | ||
important: "red", | ||
info: "green", | ||
input: "grey", | ||
prompt: "grey", | ||
verbose: "cyan", | ||
warn: "yellow", | ||
}); | ||
const optionDefinitions = [{ | ||
alias: "h", | ||
defaultValue: false, | ||
description: "Display this usage guide.", | ||
name: "help", | ||
type: Boolean, | ||
}, { | ||
alias: "s", | ||
defaultOption: true, | ||
description: "URL to ReSpec source file.", | ||
multiple: false, | ||
name: "src", | ||
type: String, | ||
}, { | ||
alias: "o", | ||
defaultOption: false, | ||
description: "Path to output file. When omitted, just stdout.", | ||
multiple: false, | ||
name: "out", | ||
type: String, | ||
}, { | ||
alias: "t", | ||
defaultValue: 10, | ||
name: "timeout", | ||
type: Number, | ||
}, { | ||
alias: "e", | ||
default: false, | ||
description: "Report ReSpec errors on stderr.", | ||
name: "haltonerror", | ||
type: Boolean, | ||
}, { | ||
alias: "w", | ||
default: false, | ||
description: "Report ReSpec warnings on stderr.", | ||
name: "haltonwarn", | ||
type: Boolean, | ||
}]; | ||
|
||
// report console.error on stderr | ||
console.error = function() { | ||
require("system").stderr.write(Array.prototype.join.call(arguments, " ") + "\n"); | ||
const tasks = { | ||
showHelp() { | ||
const getUsage = require("command-line-usage"); | ||
const appDetails = { | ||
title: "respec2html", | ||
description: "Converts a ReSpec source file to HTML and prints to std out.", | ||
footer: "Project home: [underline]{https://github.com/w3c/respec}" | ||
}; | ||
console.log(getUsage(optionDefinitions, appDetails)); | ||
}, | ||
writeTo(outPath, data) { | ||
const fsp = require("fs-promise"); | ||
const path = require("path"); | ||
return async.task(function* () { | ||
let newFilePath = ""; | ||
if (path.isAbsolute(outPath)) { | ||
newFilePath = outPath; | ||
} else { | ||
newFilePath = path.resolve(process.cwd(), outPath); | ||
} | ||
try { | ||
yield fsp.writeFile(newFilePath, data, "utf-8"); | ||
} catch (err) { | ||
console.error(err, err.stack); | ||
process.exit(1); | ||
} | ||
}); | ||
}, | ||
makeTempDir(prefix) { | ||
const fs = require("fs"); | ||
return new Promise((resolve, reject) => { | ||
fs.mkdtemp(prefix, (err, folder) => { | ||
return (err) ? reject(err) : resolve(folder); | ||
}); | ||
}); | ||
}, | ||
}; | ||
|
||
var eOption = args.indexOf("-e"); | ||
if (eOption !== -1) { | ||
args.splice(args.indexOf("-e"), 1); | ||
reportErrors = true; | ||
} | ||
|
||
if (args.indexOf("-w") !== -1) { | ||
args.splice(args.indexOf("-w"), 1); | ||
reportWarnings = true; | ||
} | ||
|
||
if (args.indexOf("--delay") !== -1) { | ||
delay = args.splice(args.indexOf("--delay"), 2)[1]; | ||
} | ||
|
||
if (args.indexOf("--exclude-script") !== -1) { | ||
var idx = args.indexOf("--exclude-script"); | ||
var values = args.splice(idx, 2); | ||
ignoreScripts = values[1]; | ||
} | ||
|
||
// Reading other parameters | ||
var source = args[1]; | ||
var output = args[2]; | ||
var timeout = isNaN(args[3]) ? 5 : parseInt(args[3], 10); | ||
|
||
if (args.length < 2 || args.length > 4) { | ||
var usage = "Usage:\n phantomjs --ssl-protocol=any respec2html.js [-e] [-w] " + | ||
"[--exclude-script url] respec-source [html-output] [timeout]\n" + | ||
" respec-source ReSpec source file, or an URL to the file\n" + | ||
" [-e] Report ReSpec errors on stderr\n" + | ||
" [-w] Report ReSpec warnings on stderr\n" + | ||
" [--exclude-script url] Do not load scripts whose source\n" + | ||
" starts with the passed URL\n" + | ||
" [html-output] Name for the HTML file to be generated,\n" + | ||
" defaults to stdout\n" + | ||
" [timeout] An optional timeout in seconds, default is 10\n"; | ||
console.error(usage); | ||
phantom.exit(2); | ||
function makeConsoleMsgHandler(nightmare) { | ||
return function handleConsoleMessages(parsedArgs) { | ||
nightmare.on("console", (type, message) => { | ||
const abortOnWarning = parsedArgs.haltonwarn && type === "warn"; | ||
const abortOnError = parsedArgs.haltonerror && type === "error"; | ||
const output = `ReSpec ${type}: ${colors.debug(message)}`; | ||
switch (type) { | ||
case "error": | ||
console.error(colors.error(`😱 ${output}`)); | ||
break; | ||
case "warn": | ||
// Ignore Nightmare's poling of respecDone | ||
if (/document\.respecDone/.test(message)) { | ||
return; | ||
} | ||
console.error(colors.warn(`😳 ${output}`)); | ||
break; | ||
} | ||
if (abortOnError || abortOnWarning) { | ||
nightmare.proc.kill(); | ||
process.exit(1); | ||
} | ||
}); | ||
}; | ||
} | ||
|
||
// Dealing with ReSpec source being loaded with scheme-relative link | ||
// i.e. <script src='//www.w3.org/Tools/respec/respec-w3c-common'> | ||
page.onResourceRequested = function(requestData, networkRequest) { | ||
if (requestData.url === "file://www.w3.org/Tools/respec/respec-w3c-common") { | ||
networkRequest.changeUrl("https://www.w3.org/Tools/respec/respec-w3c-common"); | ||
} else if (ignoreScripts && requestData.url.indexOf(ignoreScripts) === 0) { | ||
networkRequest.abort(); | ||
} | ||
}; | ||
|
||
page.onConsoleMessage = function(msg) { | ||
if (msg.match(/^ERROR: /)) { | ||
errors.push(msg); | ||
} else if (msg.match(/^WARN: /)) { | ||
warnings.push(msg); | ||
async.task(function* run() { | ||
const cli = require("command-line-args")(optionDefinitions); | ||
let parsedArgs; | ||
try { | ||
parsedArgs = cli.parse(); | ||
} catch (err) { | ||
console.error(err.stack); | ||
tasks.showHelp(); | ||
process.exit(2); | ||
} | ||
}; | ||
|
||
page.onError = function(msg) { | ||
errors.push(msg); | ||
}; | ||
|
||
page.open(source, function(status) { | ||
if (status !== "success") { | ||
console.error("Unable to access ReSpec source file."); | ||
return phantom.exit(1); | ||
if (parsedArgs.help || !parsedArgs.src) { | ||
tasks.showHelp(); | ||
return; | ||
} | ||
setTimeout(function() { | ||
page.evaluateAsync(function() { | ||
$.ajaxSetup({ | ||
timeout: 4000 | ||
}); | ||
|
||
function saveToPhantom() { | ||
require(["core/ui", "ui/save-html"], function(ui, saver) { | ||
saver.show(ui, respecConfig, document, respecEvents); | ||
window.callPhantom({ | ||
html: saver.toString() | ||
}); | ||
}); | ||
} | ||
if (document.respecDone) { | ||
saveToPhantom(); | ||
} else { | ||
respecEvents.sub("end-all", saveToPhantom); | ||
} | ||
}, delay * 1000); | ||
}); | ||
timer = setInterval(function() { | ||
if (timeout === 0) { | ||
clearInterval(timer); | ||
console.error("Timeout loading " + source + ".\n" + | ||
" Is it a valid ReSpec source file?\n" + | ||
" Did you forget --ssl-protocol=any?"); | ||
phantom.exit(1); | ||
const userData = yield tasks.makeTempDir(os.tmpDir() + "/respec2html-"); | ||
const Nightmare = require("nightmare"); | ||
const nightmare = new Nightmare({ | ||
show: false, | ||
timeout: parsedArgs.timeout, | ||
webPreferences: { | ||
"images": false, | ||
"defaultEncoding": "utf-8", | ||
userData, | ||
} | ||
}, 1000); | ||
}); | ||
|
||
page.onCallback = function(data) { | ||
clearInterval(timer); | ||
var code = 0; | ||
if (warnings.length && reportWarnings) { | ||
console.error(warnings.join("\n")); | ||
code = 65; | ||
} | ||
if (errors.length && reportErrors) { | ||
console.error(errors.join("\n")); | ||
code = 64; | ||
}); | ||
nightmare.useragent("respec2html"); | ||
const url = require("url") | ||
.parse(parsedArgs.src) | ||
.href; | ||
const handleConsoleMessages = makeConsoleMsgHandler(nightmare); | ||
handleConsoleMessages(parsedArgs); | ||
const html = yield nightmare | ||
.goto(url) | ||
.wait(function () { | ||
return document.respecDone; | ||
}) | ||
.wait("#respec-modal-save-snapshot") | ||
.click("#respec-modal-save-snapshot") | ||
.wait(100) | ||
.evaluate(function () { | ||
var encodedText = document.querySelector("#respec-save-as-html").href; | ||
var decodedText = decodeURIComponent(encodedText); | ||
var cleanedUpText = decodedText.replace(/^data:text\/html;charset=utf-8,/, ""); | ||
return cleanedUpText; | ||
}) | ||
.end(); | ||
if (!parsedArgs.out) { | ||
return process.stdout.write(html); | ||
} | ||
if (output) { | ||
fs.write(output, data.html, "w"); | ||
} else { | ||
console.log(data.html); | ||
try { | ||
yield tasks.writeTo(parsedArgs.out, html); | ||
} catch (err) { | ||
console.error(err.stack); | ||
process.exit(1); | ||
} | ||
phantom.exit(code); | ||
}; | ||
}); |