Code cleanup; added FP rules for ESLint

dependabot/npm_and_yarn/BuildServer/eslint-7.2.0
Inga 🏳‍🌈 7 years ago
parent 68fea460ad
commit f24e6f7163
  1. 3
      BuildServer/app.js
  2. 18
      BuildServer/lib/builder.js
  3. 7
      BuildServer/lib/commenter.js
  4. 28
      BuildServer/lib/git/copy.js
  5. 16
      BuildServer/lib/git/loader.js
  6. 16
      BuildServer/lib/report-processor.js
  7. 42
      BuildServer/lib/status-processor.js
  8. 54
      BuildServer/lib/task-processor.js
  9. 5
      BuildServer/lib/tasks/cssnanoall.js
  10. 40
      BuildServer/lib/tasks/dotnetbuilderwrapper.js
  11. 24
      BuildServer/lib/tasks/dotnetbuildwithoutcleanup.js
  12. 6
      BuildServer/lib/tasks/dotnetcheckstyle.js
  13. 40
      BuildServer/lib/tasks/dotnetcompile.js
  14. 5
      BuildServer/lib/tasks/dotnetnunitall.js
  15. 28
      BuildServer/lib/tasks/dotnetrewrite.js
  16. 5
      BuildServer/lib/tasks/eslintbrowserall.js
  17. 6
      BuildServer/lib/tasks/uglifyjsall.js
  18. 24
      BuildServer/package.json
  19. 9
      BuildServer/routes/manual.js
  20. 88
      BuildServer/routes/status.js

@ -20,8 +20,7 @@ const settings = require("./settings");
const app = express();
// All environments
app.set("port", process.env.PORT || settings.port);
app.set("port", process.env.PORT || settings.port); // eslint-disable-line no-process-env
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");
app.set("gitpath", settings.gitpath);

@ -62,6 +62,14 @@ const wrapGitLoader = (skipGitLoader) => {
return (gitLoaderOptions, gitLoaderCallback) => process.nextTick(gitLoaderCallback);
};
const safeParseJson = (data) => {
try {
return { "parsed": JSON.parse(data) };
} catch (err) {
return { err };
}
};
const build = (options, buildCallback) => {
const url = options.url;
const owner = options.owner;
@ -179,17 +187,15 @@ const build = (options, buildCallback) => {
return done(readErr, "MBSUnableToRead");
}
let task = null;
const { parsed, err } = safeParseJson(data);
try {
task = JSON.parse(data);
} catch (ex) {
if (err) {
console.log(`Malformed data: ${data}`);
return done(ex, "MBSMalformed");
return done(err, "MBSMalformed");
}
return processor.processTask(task, {
return processor.processTask(parsed, {
branch,
exported,
owner,

@ -1,5 +1,6 @@
"use strict";
const _ = require("underscore");
const reportProcessor = require("./report-processor");
const settings = require("../settings");
@ -135,9 +136,9 @@ const checkPullRequest = (options, callback) => {
});
};
exports.commentOnPullRequest = (options, callback) => {
options.github = settings.createGithub(options.baseRepoOptions.owner);
options.headRepoOptions.onTenthAttempt = () => writeComment(options, "Waiting for build to finish...");
exports.commentOnPullRequest = (originalOptions, callback) => {
const optionsGithub = _.extend(originalOptions, { "github": settings.createGithub(originalOptions.baseRepoOptions.owner) });
const options = _.extend(optionsGithub, { "onTenthAttempt": () => writeComment(optionsGithub, "Waiting for build to finish...") });
return checkPullRequest(options, () => reportProcessor.getStatusMessageFromRelease(options.app, options.headRepoOptions, (statusMessageErr, statusSuccessMessage) => {
const escapedErr = String(statusMessageErr || "").substring(0, maxCommentLength)

@ -1,11 +1,19 @@
"use strict";
const EventEmitter = require("events").EventEmitter;
const EventEmitter = require("events").EventEmitter; // eslint-disable-line fp/no-events
const path = require("path");
const fs = require("fs");
const async = require("async");
const Copier = require("recursive-tree-copy").Copier;
const safeGetEntries = (tree) => {
try {
return { "entries": tree.gitTree.entries() };
} catch (err) {
return { err };
}
};
const gitToFsCopier = new Copier({
"concurrency": 4,
"copyLeaf": (entry, targetDir, callback) => {
@ -36,19 +44,17 @@ const gitToFsCopier = new Copier({
const emitter = new EventEmitter();
process.nextTick(() => {
let entries = null;
const { entries, err } = safeGetEntries(tree);
try {
entries = tree.gitTree.entries();
} catch (err) {
if (err) {
return emitter.emit("error", err);
}
return async.parallel(entries.map((entry) => (callback) => {
if (entry.isTree()) {
return entry.getTree((err, subTree) => {
if (err) {
return callback(err);
return entry.getTree((getTreeErr, subTree) => {
if (getTreeErr) {
return callback(getTreeErr);
}
emitter.emit("tree", {
@ -67,9 +73,9 @@ const gitToFsCopier = new Copier({
}
return callback();
}), (err) => {
if (err) {
return emitter.emit("error", err);
}), (parallelErr) => {
if (parallelErr) {
return emitter.emit("error", parallelErr);
}
return emitter.emit("done");

@ -3,13 +3,23 @@
const nodegit = require("nodegit");
const fse = require("fs-extra");
const gitToFs = require("./copy").gitToFs;
const mkdirs = (path) => {
fse.mkdirsSync(path); // eslint-disable-line no-sync
};
const removedirs = (path) => {
fse.removeSync(path); // eslint-disable-line no-sync
};
const fixUrl = (url) => {
if (!url.startsWith("https://")) {
return url;
}
return `git://${url.substr("https://".length)}`;
};
/* Example:
options = {
"remote": "https://github.com/visionmedia/express.git",
@ -21,17 +31,13 @@ options = {
*/
module.exports = (options, globalCallback) => {
let url = options.remote;
const url = fixUrl(options.remote);
const path = `${options.local}/${options.hash}`;
const exported = options.exported;
removedirs(path);
mkdirs(path);
if (url.startsWith("https://")) {
url = `git://${url.substr("https://".length)}`;
}
console.log(`Cloning ${url} to ${path}`);
nodegit.Repository.init(path, 1)

@ -88,8 +88,6 @@ exports.loadReport = (app, options, callback) => {
const reportFile = path.join(releaseDir, reportFilename);
options.files = files;
return fs.exists(reportFile, (exists) => {
if (!exists) {
return callback("ReportFileNotFound", options);
@ -97,23 +95,23 @@ exports.loadReport = (app, options, callback) => {
return readReport(releaseDir, (readErr, report) => {
if (readErr) {
return callback(readErr, options);
return callback(readErr, _.extend(options, { files }));
}
options.report = report;
return callback(null, options);
return callback(null, _.extend(options, {
files,
report
}));
});
});
});
};
exports.getStatusMessageFromRelease = (app, options, callback) => {
exports.getStatusMessageFromRelease = (app, originalOptions, callback) => {
const options = _.extend(originalOptions, { "attemptsGetReport": (Number(originalOptions.attemptsGetReport) || Number()) + 1 });
const releaseDir = path.join(app.get("releasepath"), options.owner, options.reponame, options.branch, options.rev);
const reportFile = path.join(releaseDir, reportFilename);
options.attemptsGetReport = (Number(options.attemptsGetReport) || Number()) + 1;
fs.exists(reportFile, (exists) => {
if (!exists) {
return setTimeout(() => fs.exists(releaseDir, (dirExists) => {

@ -2,7 +2,7 @@
const path = require("path");
const fs = require("fs");
const _ = require("underscore");
const reportProcessor = require("./report-processor");
const addBranchInfo = (app, options, callback) => {
@ -17,10 +17,15 @@ const addBranchInfo = (app, options, callback) => {
if (err) {
return callback(err, options);
}
options.branch = data.toString();
options.branchName = options.branch.split("/").pop();
return callback(null, options);
const branch = data.toString();
const branchParts = branch.split("/");
const branchName = branchParts[branchParts.length - 1];
return callback(null, _.extend(options, {
branch,
branchName
}));
});
});
};
@ -37,39 +42,38 @@ const addRevInfo = (app, options, callback) => {
if (err) {
return callback(err, options);
}
options.rev = data.toString();
return callback(null, options);
const rev = data.toString();
return callback(null, _.extend(options, { rev }));
});
});
};
const parseOptions = (app, options, callback) => {
if (options.rev && !(/^[\da-f]{40}$/i).test(options.rev)) {
return callback(`Wrong rev format: ${options.rev}`, options);
}
const result = {
"owner": options.owner,
"reponame": options.reponame
};
if (options.rev && !(/^[\da-f]{40}$/i).test(options.rev)) {
return callback(`Wrong rev format: ${options.rev}`, options);
}
if (options.rev) {
result.rev = options.rev;
return addBranchInfo(app, result, callback);
return addBranchInfo(app, _.extend(result, { "rev": options.rev }), callback);
}
if (/^[\da-f]{40}$/i.test(options.branchName)) {
result.rev = options.branchName;
return addBranchInfo(app, result, callback);
return addBranchInfo(app, _.extend(result, { "rev": options.branchName }), callback);
}
result.branchName = options.branchName || "master";
result.branch = `refs/heads/${result.branchName}`;
const branchName = options.branchName || "master";
return addRevInfo(app, result, callback);
return addRevInfo(app, _.extend(result, {
"branch": `refs/heads/${branchName}`,
branchName
}), callback);
};
exports.getReport = (app, options, callback) => parseOptions(app, options, (err, result) => {

@ -1,5 +1,6 @@
"use strict";
const _ = require("underscore");
const tasks = require("./tasks");
// TaskProcessor does not look like EventEmitter, so no need to extend EventEmitter and use `emit' here.
@ -9,9 +10,9 @@ const TaskProcessor = function (task, outerProcessor, callback) {
}
const that = this;
let taskWorker = null;
const createTaskWorker = () => tasks[task.type](task.params || {}, that);
const errors = [];
const process = () => taskWorker.process();
const process = () => createTaskWorker().process();
const getOuterPrefix = (prefix) => {
if (task.name && prefix) {
return `${task.name}/${prefix}`;
@ -39,43 +40,48 @@ const TaskProcessor = function (task, outerProcessor, callback) {
that.processTask = processTask;
that.done = done;
that.context = outerProcessor.context;
const taskImpl = tasks[task.type];
taskWorker = taskImpl(task.params || {}, that);
return this;
};
const pushMessage = (list, message, prefix) => {
const parts = prefix.split("/");
let innerList = list;
const pushMessage = (list, message, parts, index) => {
if (!index) {
list.$allMessages = list.$allMessages || []; // eslint-disable-line fp/no-mutation
list.$allMessages.push({ // eslint-disable-line fp/no-mutating-methods
message,
"prefix": parts.join("/")
});
}
parts.forEach((part) => {
innerList = innerList[part] = innerList[part] || {};
});
list.$messages = list.$messages || []; // eslint-disable-line fp/no-mutation
if (index === parts.length) {
return list.$messages.push(message); // eslint-disable-line fp/no-mutating-methods
}
innerList.$messages = innerList.$messages || [];
innerList.$messages.push(message);
return pushMessage(list, message, parts, index + 1);
};
list.$allMessages = list.$allMessages || [];
list.$allMessages.push({
message,
prefix
});
const addFlag = (flags) => (flagName) => {
flags[flagName] = true; // eslint-disable-line fp/no-mutation
};
const containsFlag = (flags) => (flagName) => flags[flagName];
exports.processTask = (task, context, callback) => {
const errors = {};
const warns = {};
const infos = {};
const messages = {};
const messageProcessor = (list) => (message, prefix) => {
pushMessage(list, message, prefix);
pushMessage(messages, message, prefix);
const parts = prefix.split("/");
pushMessage(list, message, parts, 0);
pushMessage(messages, message, parts, 0);
};
const flags = {};
const processor = new TaskProcessor(task, {
context,
"context": _.extend(context, {
"addFlag": addFlag(flags),
"containsFlag": containsFlag(flags)
}),
"onError": messageProcessor(errors),
"onInfo": messageProcessor(infos),
"onWarn": messageProcessor(warns)

@ -1,14 +1,15 @@
"use strict";
const glob = require("glob");
const flagDoneName = "cssnanoallDone";
module.exports = (params, processor) => ({
"process": () => {
if (processor.context.cssnanoallDone) {
if (processor.context.containsFlag(flagDoneName)) {
processor.onWarn("cssnanoall task is executed more than once; this is probably a bug in your mbs.json");
}
processor.context.cssnanoallDone = true;
processor.context.addFlag(flagDoneName);
glob("**/*.css", {
"cwd": processor.context.exported,

@ -1,28 +1,37 @@
"use strict";
const spawn = require("child_process").spawn;
const streamBuffers = require("stream-buffers");
const settings = require("../../settings");
const wrapBuilder = (builder, input, onExit) => {
const resultBuffer = new streamBuffers.WritableStreamBuffer();
const errorBuffer = new streamBuffers.WritableStreamBuffer();
builder.stdout.on("data", (data) => {
resultBuffer.write(data);
});
builder.stderr.on("data", (data) => {
errorBuffer.write(data);
});
builder.on("exit", (code) => onExit(code, resultBuffer.getContentsAsString(), errorBuffer.getContentsAsString()));
builder.stdin.write(input);
builder.stdin.end();
};
module.exports = (params, processor) => ({
"process": () => {
let result = "";
let error = "";
const input = JSON.stringify(params);
const builder = spawn(settings.builderExecutable, [params.command]);
processor.onInfo(`DotNetBuilderWrapper processing (at ${new Date().toISOString()}): ${JSON.stringify(params, null, " ")}`);
processor.onInfo(`DotNetBuilderWrapper processing (at ${new Date().toISOString()}): ${input}`);
builder.stdout.on("data", (data) => {
result += data;
});
builder.stderr.on("data", (data) => {
error += data;
});
builder.on("exit", (code) => {
wrapBuilder(builder, input, (code, result, error) => {
if (code) {
error = `Return code is ${code}\r\n${error}`;
processor.onError(error);
processor.onError(`Return code is ${code}\r\n${error}`);
return processor.done();
}
@ -49,8 +58,5 @@ module.exports = (params, processor) => ({
return processor.done();
});
builder.stdin.write(JSON.stringify(params));
builder.stdin.end();
}
});

@ -2,29 +2,27 @@
const sequential = require("./sequential");
module.exports = (params, processor) => {
const tasks = [];
const createTasks = function *(params) {
if (!params.skipMbsCheckStyle) {
tasks.push({
yield {
params,
"type": "dotnetcheckstyle"
});
};
}
tasks.push({
yield {
params,
"type": "dotnetrewrite"
});
};
if (!params.skipNugetRestore) {
tasks.push({
yield {
params,
"type": "dotnetnugetrestore"
});
};
}
tasks.push({
yield {
"params": {
"configuration": params.configuration,
"forceCodeAnalysis": params.forceCodeAnalysis,
@ -34,7 +32,11 @@ module.exports = (params, processor) => {
"target": "Rebuild"
},
"type": "dotnetcompile"
});
};
};
module.exports = (params, processor) => {
const tasks = Array.from(createTasks(params));
return sequential({ tasks }, processor);
};

@ -9,13 +9,15 @@ const autoGeneratedMarker
= "//------------------------------------------------------------------------------\n"
+ "// <auto-generated>";
const flagDoneName = "dotnetcheckerDone";
module.exports = (params, processor) => ({
"process": () => {
if (processor.context.dotnetcheckerDone) {
if (processor.context.containsFlag(flagDoneName)) {
return processor.done();
}
processor.context.dotnetcheckerDone = true;
processor.context.addFlag(flagDoneName);
return glob("**/*.cs", { "cwd": processor.context.exported }, (globErr, files) => {
if (globErr) {

@ -1,35 +1,37 @@
"use strict";
const path = require("path");
const _ = require("underscore");
const settings = require("../../settings");
const dotnetbuilderwrapper = require("./dotnetbuilderwrapper");
module.exports = (params, processor) => {
const compileParams = {
"Configuration": params.configuration,
"OutputDirectory": params.overrideOutputDirectory,
"SolutionPath": path.join(processor.context.exported, params.solution),
"Target": params.target,
"command": "compile"
};
if (!settings.skipCodeSigning && !params.skipCodeSigning) {
compileParams.SigningKey = settings.codeSigningKeyFile;
}
if (settings.isCodeAnalysisUnsupported && params.forceCodeAnalysis) {
processor.onError("Code analysis is not supported");
return processor.done();
}
if (
settings.isCodeAnalysisUnsupported
const getAdditionalSigningParameters = () => {
if (settings.skipCodeSigning || params.skipCodeSigning) {
return {};
}
return { "SigningKey": settings.codeSigningKeyFile };
};
const skipCodeAnalysis = settings.isCodeAnalysisUnsupported
|| params.ignoreCodeAnalysis
|| (settings.ignoreCodeAnalysisByDefault && !params.forceCodeAnalysis)
) {
compileParams.SkipCodeAnalysis = true;
}
|| (settings.ignoreCodeAnalysisByDefault && !params.forceCodeAnalysis);
const compileParams = {
"Configuration": params.configuration,
"OutputDirectory": params.overrideOutputDirectory,
"SkipCodeAnalysis": skipCodeAnalysis,
"SolutionPath": path.join(processor.context.exported, params.solution),
"Target": params.target,
"command": "compile"
};
return dotnetbuilderwrapper(compileParams, processor);
return dotnetbuilderwrapper(_.extend(compileParams, getAdditionalSigningParameters()), processor);
};

@ -1,14 +1,15 @@
"use strict";
const glob = require("glob");
const flagDoneName = "dotnetnunitallDone";
module.exports = (params, processor) => ({
"process": () => {
if (processor.context.dotnetnunitallDone) {
if (processor.context.containsFlag(flagDoneName)) {
processor.onWarn("dotnetnunitall task is executed more than once; this is probably a bug in your mbs.json");
}
processor.context.dotnetnunitallDone = true;
processor.context.addFlag(flagDoneName);
glob("**/{bin,build}/**/*.{Tests,Test,UnitTests}.dll", {
"cwd": processor.context.exported,

@ -6,30 +6,38 @@ const async = require("async");
const glob = require("glob");
const settings = require("../../settings");
const flagDoneName = "dotnetrewriterDone";
const processAssemblyInfo = (params, processor, appendInformationalVersion) => (originalContent, cb) => {
let content = originalContent;
const processInternalsVisible = (content) => {
if (params.skipCodeSigning || settings.skipCodeSigning) {
return content;
}
if (!params.skipCodeSigning && !settings.skipCodeSigning) {
content = content.replace(
return content.replace(
/InternalsVisibleTo\s*\(\s*"([\w.]+)"\s*\)/g,
(match, p1) => `InternalsVisibleTo("${p1},PublicKey=${settings.codeSigningPublicKey}")`
);
}
};
if (appendInformationalVersion) {
content = `${content}\n[assembly: System.Reflection.AssemblyInformationalVersion("${processor.context.versionInfo}")]\n`;
}
const processInformationalVersion = (content) => {
if (!appendInformationalVersion) {
return content;
}
return `${content}\n[assembly: System.Reflection.AssemblyInformationalVersion("${processor.context.versionInfo}")]\n`;
};
return cb(null, content);
return cb(null, processInformationalVersion(processInternalsVisible(originalContent)));
};
module.exports = (params, processor) => ({
"process": () => {
if (processor.context.dotnetrewriterDone) {
if (processor.context.containsFlag(flagDoneName)) {
return processor.done();
}
processor.context.dotnetrewriterDone = true;
processor.context.addFlag(flagDoneName);
return glob("**/{InternalsVisible,AssemblyInfo}*.cs", { "cwd": processor.context.exported }, (globErr, files) => {
if (globErr) {

@ -1,14 +1,15 @@
"use strict";
const glob = require("glob");
const flagDoneName = "eslintbrowserallDone";
module.exports = (params, processor) => ({
"process": () => {
if (processor.context.eslintbrowserallDone) {
if (processor.context.containsFlag(flagDoneName)) {
processor.onWarn("eslintbrowserall task is executed more than once; this is probably a bug in your mbs.json");
}
processor.context.eslintbrowserallDone = true;
processor.context.addFlag(flagDoneName);
const excludeFiles = params.excludeFiles || [];

@ -2,13 +2,15 @@
const glob = require("glob");
const doneFlagName = "uglifyjsallDone";
module.exports = (params, processor) => ({
"process": () => {
if (processor.context.uglifyjsallDone) {
if (processor.context.containsFlag(doneFlagName)) {
processor.onWarn("dotnetnunitall task is executed more than once; this is probably a bug in your mbs.json");
}
processor.context.uglifyjsallDone = true;
processor.context.addFlag(doneFlagName);
glob("**/*.js", {
"cwd": processor.context.exported,

@ -3,7 +3,8 @@
"version": "0.0.1",
"private": true,
"scripts": {
"start": "forever -c node app.js"
"start": "forever -c node app.js",
"test": "./node_modules/.bin/eslint ."
},
"dependencies": {
"archiver": "^1.3.0",
@ -37,8 +38,23 @@
"parserOptions": {
"ecmaVersion": 6
},
"extends": "eslint:all",
"plugins": [
"fp"
],
"extends": [
"eslint:all",
"plugin:fp/recommended"
],
"rules": {
"fp/no-unused-expression": "off",
"fp/no-nil": "off",
"fp/no-mutation": [
"error",
{
"commonjs": true
}
],
"prefer-destructuring": "off",
"quotes": [
"warn",
"double"
@ -112,5 +128,9 @@
}
]
}
},
"devDependencies": {
"eslint": "^3.15.0",
"eslint-plugin-fp": "^2.3.0"
}
}

@ -1,14 +1,15 @@
"use strict";
const _ = require("underscore");
const builder = require("../lib/builder");
exports.get = (req, res) => res.render("manual");
exports.post = (req, res) => {
const options = req.body;
options.url = `https://pos-github.payonline.ru/${options.owner}/${options.reponame}`;
options.app = req.app;
const options = _.extend(req.body, {
"app": req.app,
"url": `https://pos-github.payonline.ru/${req.body.owner}/${req.body.reponame}`
});
builder.build(options, (err, result) => {
console.log("Done processing manual request");

@ -1,58 +1,92 @@
"use strict";
const url = require("url");
const _ = require("underscore");
const statusProcessor = require("../lib/status-processor");
const parseOptionsFromReferer = (path, callback) => {
const pathParts = path.split("/").filter((value) => value);
const result = {};
const [, secondPart, thirdPart] = pathParts;
if (!secondPart) {
return callback("BadRequest", result);
return callback("BadRequest", {});
}
if (thirdPart === "tree") {
[result.owner, result.reponame, , result.branchName, result.rev] = pathParts;
} else {
[result.owner, result.reponame, result.branchName, result.rev] = pathParts;
const [owner, reponame, , branchName, rev] = pathParts;
return callback(null, {
branchName,
owner,
reponame,
rev
});
}
return callback(null, result);
const [owner, reponame, branchName, rev] = pathParts;
return callback(null, {
branchName,
owner,
reponame,
rev
});
};
const createShowReport = (res) => (err, inputOptions) => {
const options = inputOptions || {};
const options = _.extendOwn(inputOptions || {}, { err });
options.err = err;
res.render("status", options);
};
exports.image = (req, res) => {
const handle = (err, options) => {
const getAdditionalOptions = (err, options) => {
if (err === "ReportFileNotFound") {
options.status = "Building";
} else if (err) {
options.status = "StatusError";
options.message = err;
} else if (options.report.result === "MBSNotFound") {
options.status = "MBSNotUsed";
} else if (options.report.err) {
options.status = "Error";
options.message = options.report.err;
} else if ((options.report.result.warns.$allMessages || []).length) {
return { "status": "Building" };
}
if (err) {
return {
"message": err,
"status": "StatusError"
};
}
if (options.report.result === "MBSNotFound") {
return { "status": "MBSNotUsed" };
}
if (options.report.err) {
return {
"message": options.report.err,
"status": "Error"
};
}
if ((options.report.result.warns.$allMessages || []).length) {
const [firstWarn] = options.report.result.warns.$allMessages;
options.status = "Warning";
options.message = firstWarn.message;
} else {
options.status = "OK";
if ((options.report.result.infos.$allMessages || []).length) {
options.message = options.report.result.infos.$allMessages[options.report.result.infos.$allMessages.length - 1].message;
}
return {
"message": firstWarn.message,
"status": "Warning"
};
}
const allInfos = options.report.result.infos.$allMessages || [];
if (allInfos.length) {
return {
"message": allInfos[allInfos.length - 1].message,
"status": "OK"
};
}
return { "status": "OK" };
};
const handle = (err, options) => {
res.setHeader("Content-Type", "image/svg+xml");
res.render("status-image", options);
res.render("status-image", _.extend(options, getAdditionalOptions(err, options)));
};
parseOptionsFromReferer(url.parse(req.headers.referer || "").pathname || "", (err, options) => {

Loading…
Cancel
Save