Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@rsnuxt/server

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rsnuxt/server - npm Package Compare versions

Comparing version
0.2.2
to
0.2.3
+697
-1
dist/server.js

@@ -1,1 +0,697 @@

export * from '../src/index'
/*!
* @rsnuxt/server v0.2.2 (c) 2016-2025
* Released under the MIT License
* Repository: https://github.com/nuxt/nuxt.js
* Website: https://nuxtjs.org
*/
'use strict';
const path = require('path');
const consola = require('consola');
const launchMiddleware = require('launch-editor-middleware');
const serveStatic = require('serve-static');
const servePlaceholder = require('serve-placeholder');
const connect = require('connect');
const compression = require('compression');
const utils = require('@rsnuxt/utils');
const vueRenderer = require('@rsnuxt/vue-renderer');
const generateETag = require('etag');
const fresh = require('fresh');
const ufo = require('ufo');
const fs = require('fs-extra');
const Youch = require('@nuxtjs/youch');
const http = require('http');
const https = require('https');
const enableDestroy = require('server-destroy');
const ip = require('ip');
const util = require('util');
const onHeaders = require('on-headers');
class ServerContext {
constructor(server) {
this.nuxt = server.nuxt;
this.globals = server.globals;
this.options = server.options;
this.resources = server.resources;
}
}
async function renderAndGetWindow(url = "http://localhost:3000", jsdomOpts = {}, {
loadedCallback,
loadingTimeout = 2e3,
globals
} = {}) {
const jsdom = await import('jsdom').then((m) => m.default || m).catch((e) => {
consola.error(`
jsdom is not installed. Please install jsdom with:
$ yarn add --dev jsdom
OR
$ npm install --dev jsdom
`);
throw e;
});
const options = Object.assign({
// Load subresources (https://github.com/tmpvar/jsdom#loading-subresources)
resources: "usable",
runScripts: "dangerously",
virtualConsole: true,
beforeParse(window2) {
window2.scrollTo = () => {
};
}
}, jsdomOpts);
const jsdomErrHandler = (err) => {
throw err;
};
if (options.virtualConsole) {
if (options.virtualConsole === true) {
options.virtualConsole = new jsdom.VirtualConsole().sendTo(consola);
}
options.virtualConsole.on("jsdomError", jsdomErrHandler);
} else {
delete options.virtualConsole;
}
const { window } = await jsdom.JSDOM.fromURL(url, options);
const nuxtExists = window.document.body.innerHTML.includes(`id="${globals.id}"`);
if (!nuxtExists) {
const error = new Error("Could not load the nuxt app");
error.body = window.document.body.innerHTML;
window.close();
throw error;
}
await utils.timeout(new Promise((resolve) => {
window[loadedCallback] = () => resolve(window);
}), loadingTimeout, `Components loading in renderAndGetWindow was not completed in ${loadingTimeout / 1e3}s`);
if (options.virtualConsole) {
options.virtualConsole.removeListener("jsdomError", jsdomErrHandler);
}
return window;
}
const nuxtMiddleware = ({ options, nuxt, renderRoute, resources }) => async function nuxtMiddleware(req, res, next) {
const context = utils.getContext(req, res);
try {
const url = ufo.normalizeURL(req.url);
res.statusCode = 200;
const result = await renderRoute(url, context);
if (!result) {
await nuxt.callHook("server:nuxt:renderLoading", req, res);
return;
}
await nuxt.callHook("render:route", url, result, context);
const {
html,
cspScriptSrcHashes,
error,
redirected,
preloadFiles
} = result;
if (redirected && context.target !== utils.TARGETS.static) {
await nuxt.callHook("render:routeDone", url, result, context);
return html;
}
if (error) {
res.statusCode = context.nuxt.error.statusCode || 500;
}
if (options.render.csp && cspScriptSrcHashes) {
const { allowedSources, policies } = options.render.csp;
const isReportOnly = !!options.render.csp.reportOnly;
const cspHeader = isReportOnly ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy";
res.setHeader(cspHeader, getCspString({ cspScriptSrcHashes, allowedSources, policies, isReportOnly }));
}
if (!error && options.render.etag) {
const { hash } = options.render.etag;
const etag = hash ? hash(html, options.render.etag) : generateETag(html, options.render.etag);
if (fresh(req.headers, { etag })) {
res.statusCode = 304;
await nuxt.callHook("render:beforeResponse", url, result, context);
res.end();
await nuxt.callHook("render:routeDone", url, result, context);
return;
}
res.setHeader("ETag", etag);
}
if (!error && options.render.http2.push) {
const { shouldPush, pushAssets } = options.render.http2;
const { publicPath } = resources.clientManifest;
const links = pushAssets ? pushAssets(req, res, publicPath, preloadFiles) : defaultPushAssets(preloadFiles, shouldPush, publicPath, options);
if (links.length > 0) {
res.setHeader("Link", links.join(", "));
}
}
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.setHeader("Accept-Ranges", "none");
res.setHeader("Content-Length", Buffer.byteLength(html));
await nuxt.callHook("render:beforeResponse", url, result, context);
res.end(html, "utf8");
await nuxt.callHook("render:routeDone", url, result, context);
return html;
} catch (err) {
if (context && context.redirected) {
consola.error(err);
return err;
}
if (err.name === "URIError") {
err.statusCode = 400;
}
next(err);
}
};
const defaultPushAssets = (preloadFiles, shouldPush, publicPath, options) => {
if (shouldPush && options.dev) {
consola.warn("http2.shouldPush is deprecated. Use http2.pushAssets function");
}
const links = [];
preloadFiles.forEach(({ file, asType, fileWithoutQuery, modern }) => {
if (!shouldPush && asType !== "script" && asType !== "style") {
return;
}
if (shouldPush && !shouldPush(fileWithoutQuery, asType)) {
return;
}
const { crossorigin } = options.render;
const cors = `${crossorigin ? ` crossorigin=${crossorigin};` : ""}`;
const rel = modern && asType === "script" ? "modulepreload" : "preload";
links.push(`<${publicPath}${file}>; rel=${rel};${cors} as=${asType}`);
});
return links;
};
const getCspString = ({ cspScriptSrcHashes, allowedSources, policies, isReportOnly }) => {
const joinedHashes = cspScriptSrcHashes.join(" ");
const baseCspStr = `script-src 'self' ${joinedHashes}`;
const policyObjectAvailable = typeof policies === "object" && policies !== null && !Array.isArray(policies);
if (Array.isArray(allowedSources) && allowedSources.length) {
return isReportOnly && policyObjectAvailable && !!policies["report-uri"] ? `${baseCspStr} ${allowedSources.join(" ")}; report-uri ${policies["report-uri"]};` : `${baseCspStr} ${allowedSources.join(" ")}`;
}
if (policyObjectAvailable) {
const transformedPolicyObject = transformPolicyObject(policies, cspScriptSrcHashes);
return Object.entries(transformedPolicyObject).map(([k, v]) => `${k} ${Array.isArray(v) ? v.join(" ") : v}`).join("; ");
}
return baseCspStr;
};
const transformPolicyObject = (policies, cspScriptSrcHashes) => {
const userHasDefinedScriptSrc = policies["script-src"] && Array.isArray(policies["script-src"]);
const additionalPolicies = userHasDefinedScriptSrc ? policies["script-src"] : [];
const hashAndPolicyList = cspScriptSrcHashes.concat("'self'", additionalPolicies);
return { ...policies, "script-src": hashAndPolicyList };
};
const errorMiddleware = ({ resources, options }) => async function errorMiddleware(_error, req, res, next) {
const error = normalizeError(_error, options);
const sendResponse = (content, type = "text/html") => {
res.statusCode = error.statusCode;
res.statusMessage = "RuntimeError";
res.setHeader("Content-Type", type + "; charset=utf-8");
res.setHeader("Content-Length", Buffer.byteLength(content));
res.setHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
if (error.headers) {
for (const name in error.headers) {
res.setHeader(name, error.headers[name]);
}
}
res.end(content, "utf-8");
};
const hasReqHeader = (header, includes) => req.headers[header] && req.headers[header].toLowerCase().includes(includes);
const isJson = hasReqHeader("accept", "application/json") || hasReqHeader("user-agent", "curl/");
if (!options.debug) {
if (error.statusCode !== 404) {
consola.error(error);
}
const json = {
status: error.statusCode,
message: error.message,
name: error.name
};
if (isJson) {
sendResponse(JSON.stringify(json, void 0, 2), "text/json");
return;
}
const html2 = resources.errorTemplate(json);
sendResponse(html2);
return;
}
const youch = new Youch(
error,
req,
readSource,
options.router.base,
true
);
if (isJson) {
const json = await youch.toJSON();
sendResponse(JSON.stringify(json, void 0, 2), "text/json");
return;
}
const html = await youch.toHTML();
sendResponse(html);
};
const sanitizeName = (name) => name ? name.replace("webpack:///", "").split("?")[0] : null;
const normalizeError = (_error, { srcDir, rootDir, buildDir }) => {
if (typeof _error === "string") {
_error = { message: _error };
} else if (!_error) {
_error = { message: "<empty>" };
}
const error = new Error(_error.message);
error.name = _error.name;
error.statusCode = _error.statusCode || 500;
error.headers = _error.headers;
const searchPath = [
srcDir,
rootDir,
path.join(buildDir, "dist", "server"),
buildDir,
process.cwd()
];
const findInPaths = (fileName) => {
for (const dir of searchPath) {
const fullPath = path.resolve(dir, fileName);
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
return fileName;
};
error.stack = (_error.stack || "").split("\n").map((line) => {
const match = line.match(/\(([^)]+)\)|([^\s]+\.[^\s]+):/);
if (!match) {
return line;
}
const src = match[1] || match[2] || "";
return line.replace(src, findInPaths(sanitizeName(src)));
}).join("\n");
return error;
};
async function readSource(frame) {
if (fs.existsSync(frame.fileName)) {
frame.fullPath = frame.fileName;
frame.contents = await fs.readFile(frame.fileName, "utf-8");
}
}
let RANDOM_PORT = "0";
class Listener {
constructor({ port, host, socket, https: https2, app, dev, baseURL }) {
this.port = port;
this.host = host;
this.socket = socket;
this.https = https2;
this.app = app;
this.dev = dev;
this.baseURL = baseURL;
this.listening = false;
this._server = null;
this.server = null;
this.address = null;
this.url = null;
}
async close() {
if (this.server && this.server.listening) {
await this.server.destroy();
consola.debug("server closed");
}
this.listening = false;
this._server = null;
this.server = null;
this.address = null;
this.url = null;
}
computeURL() {
const address = this.server.address();
if (!this.socket) {
switch (address.address) {
case "127.0.0.1":
this.host = "localhost";
break;
case "0.0.0.0":
this.host = ip.address();
break;
}
this.port = address.port;
this.url = `http${this.https ? "s" : ""}://${this.host}:${this.port}${this.baseURL}`;
this.url = decodeURI(this.url);
return;
}
this.url = `unix+http://${address}`;
}
async listen() {
if (this.listening) {
return;
}
const protocol = this.https ? https : http;
const protocolOpts = this.https ? [this.https] : [];
this._server = protocol.createServer.apply(protocol, protocolOpts.concat(this.app));
const listenArgs = this.socket ? { path: this.socket } : { host: this.host, port: this.port };
listenArgs.exclusive = false;
try {
this.server = await new Promise((resolve, reject) => {
this._server.once("error", reject);
this._server.listen(listenArgs, (error) => {
this._server.off("error", reject);
if (error) {
reject(error);
} else {
resolve(this._server);
}
});
});
} catch (error) {
return this.serverErrorHandler(error);
}
enableDestroy(this.server);
util.promisify(this.server.destroy);
this.computeURL();
this.listening = true;
}
async serverErrorHandler(error) {
const addressInUse = error.code === "EADDRINUSE";
if (addressInUse) {
const address = this.socket || `${this.host}:${this.port}`;
error.message = `Address \`${address}\` is already in use.`;
if (this.dev && !this.socket && this.port !== RANDOM_PORT) {
consola.warn(error.message);
consola.info("Trying a random port...");
this.port = RANDOM_PORT;
await this.close();
await this.listen();
RANDOM_PORT = this.port;
return;
}
}
throw error;
}
}
const createTimingMiddleware = (options) => (req, res, next) => {
if (res.timing) {
consola.warn("server-timing is already registered.");
}
res.timing = new ServerTiming();
if (options && options.total) {
res.timing.start("total", "Nuxt Server Time");
}
onHeaders(res, () => {
res.timing.end("total");
if (res.timing.headers.length > 0) {
res.setHeader(
"Server-Timing",
[].concat(res.getHeader("Server-Timing") || []).concat(res.timing.headers).join(", ")
);
}
res.timing.clear();
});
next();
};
class ServerTiming extends utils.Timer {
constructor(...args) {
super(...args);
this.headers = [];
}
end(...args) {
const time = super.end(...args);
if (time) {
this.headers.push(this.formatHeader(time));
}
return time;
}
clear() {
super.clear();
this.headers.length = 0;
}
formatHeader(time) {
const desc = time.description ? `;desc="${time.description}"` : "";
return `${time.name};dur=${time.duration}${desc}`;
}
}
class Server {
constructor(nuxt) {
this.nuxt = nuxt;
this.options = nuxt.options;
this.globals = utils.determineGlobals(nuxt.options.globalName, nuxt.options.globals);
this.publicPath = utils.isUrl(this.options.build.publicPath) ? this.options.build._publicPath : this.options.build.publicPath.replace(/^\.+\//, "/");
this.resources = {};
this.listeners = [];
this.app = connect();
this.nuxt.hook("close", () => this.close());
if (this.options.dev) {
this.nuxt.hook("server:devMiddleware", (devMiddleware) => {
this.devMiddleware = devMiddleware;
});
}
}
async ready() {
if (this._readyCalled) {
return this;
}
this._readyCalled = true;
await this.nuxt.callHook("render:before", this, this.options.render);
this.serverContext = new ServerContext(this);
this.renderer = new vueRenderer.VueRenderer(this.serverContext);
await this.renderer.ready();
await this.setupMiddleware();
await this.nuxt.callHook("render:done", this);
return this;
}
async setupMiddleware() {
await this.nuxt.callHook("render:setupMiddleware", this.app);
if (!this.options.dev) {
const { compressor } = this.options.render;
if (typeof compressor === "object") {
this.useMiddleware(compression(compressor));
} else if (compressor) {
this.useMiddleware(compressor);
}
}
if (this.options.server.timing) {
this.useMiddleware(createTimingMiddleware(this.options.server.timing));
}
if (this.options.render.static !== false) {
const staticMiddleware = serveStatic(
path.resolve(this.options.srcDir, this.options.dir.static),
this.options.render.static
);
staticMiddleware.prefix = this.options.render.static.prefix;
this.useMiddleware(staticMiddleware);
}
if (!this.options.dev) {
const distDir = path.resolve(this.options.buildDir, "dist", "client");
this.useMiddleware({
path: this.publicPath,
handler: serveStatic(
distDir,
this.options.render.dist
)
});
}
if (this.options.dev) {
this.useMiddleware((req, res, next) => {
if (!this.devMiddleware) {
return next();
}
if (req.url.startsWith(this.publicPath) && req.url.endsWith(".js")) {
res.setHeader("Vary", "*");
}
this.devMiddleware(req, res, next);
});
if (this.options.debug) {
this.useMiddleware({
path: "__open-in-editor",
handler: launchMiddleware(this.options.editor)
});
}
}
for (const m of this.options.serverMiddleware) {
this.useMiddleware(m);
}
const { fallback } = this.options.render;
if (fallback) {
if (fallback.dist) {
this.useMiddleware({
path: this.publicPath,
handler: servePlaceholder.servePlaceholder(fallback.dist)
});
}
if (fallback.static) {
this.useMiddleware({
path: "/",
handler: servePlaceholder.servePlaceholder(fallback.static)
});
}
}
this.useMiddleware(nuxtMiddleware({
options: this.options,
nuxt: this.nuxt,
renderRoute: this.renderRoute.bind(this),
resources: this.resources
}));
const routerBase = this.nuxt.options.router.base;
if (this.options.dev && routerBase !== "/") {
this.useMiddleware({
prefix: false,
handler: (req, res, next) => {
if (decodeURI(req.url).startsWith(decodeURI(routerBase))) {
return next();
}
const to = utils.urlJoin(routerBase, req.url);
consola.info(`[Development] Redirecting from \`${decodeURI(req.url)}\` to \`${decodeURI(to)}\` (router.base specified)`);
res.writeHead(302, {
Location: to
});
res.end();
}
});
}
await this.nuxt.callHook("render:errorMiddleware", this.app);
this.useMiddleware(errorMiddleware({
resources: this.resources,
options: this.options
}));
}
_normalizeMiddleware(middleware) {
if (typeof middleware === "function") {
middleware = { handle: middleware };
}
if (typeof middleware === "string") {
middleware = this._requireMiddleware(middleware);
}
middleware = Object.assign({}, middleware);
if (middleware.handler && !middleware.handle) {
middleware.handle = middleware.handler;
delete middleware.handler;
}
if (middleware.path && !middleware.route) {
middleware.route = middleware.path;
delete middleware.path;
}
if (typeof middleware.handle === "string") {
Object.assign(middleware, this._requireMiddleware(middleware.handle));
}
if (!middleware.handle) {
middleware.handle = (req, res, next) => {
next(new Error("ServerMiddleware should expose a handle: " + middleware.entry));
};
}
if (middleware.handle.prefix !== void 0 && middleware.prefix === void 0) {
middleware.prefix = middleware.handle.prefix;
}
if (typeof middleware.handle.handle === "function") {
const server = middleware.handle;
middleware.handle = server.handle.bind(server);
}
return middleware;
}
_requireMiddleware(entry) {
entry = this.nuxt.resolver.resolvePath(entry);
let middleware;
try {
middleware = this.nuxt.resolver.requireModule(entry);
} catch (error) {
consola.error("ServerMiddleware Error:", error);
middleware = (req, res, next) => {
next(error);
};
}
middleware = this._normalizeMiddleware(middleware);
middleware.entry = entry;
return middleware;
}
resolveMiddleware(middleware, fallbackRoute = "/") {
middleware = this._normalizeMiddleware(middleware);
if (!middleware.route) {
middleware.route = fallbackRoute;
}
middleware._originalRoute = middleware.route;
middleware.route = ((middleware.prefix !== false ? this.options.router.base : "") + (typeof middleware.route === "string" ? middleware.route : "")).replace(/\/\//g, "/");
if (middleware.route.endsWith("/")) {
middleware.route = middleware.route.slice(0, -1);
}
middleware.handle._middleware = middleware;
return middleware;
}
useMiddleware(middleware) {
const { route, handle } = this.resolveMiddleware(middleware);
this.app.use(route, handle);
}
replaceMiddleware(query, middleware) {
let serverStackItem;
if (typeof query === "string") {
serverStackItem = this.app.stack.find(({ handle: handle2 }) => handle2._middleware && handle2._middleware.entry === query);
} else {
serverStackItem = this.app.stack.find(({ handle: handle2 }) => handle2 === query);
}
if (!serverStackItem) {
return;
}
this.unloadMiddleware(serverStackItem);
const { route, handle } = this.resolveMiddleware(
middleware,
// #8584 pass the original route as fallback
serverStackItem.handle._middleware ? serverStackItem.handle._middleware._originalRoute : serverStackItem.route
);
serverStackItem.handle = handle;
serverStackItem.route = route;
return serverStackItem;
}
unloadMiddleware({ handle }) {
if (handle._middleware && typeof handle._middleware.unload === "function") {
handle._middleware.unload();
}
}
serverMiddlewarePaths() {
return this.app.stack.map(({ handle }) => handle._middleware && handle._middleware.entry).filter(Boolean);
}
renderRoute() {
return this.renderer.renderRoute.apply(this.renderer, arguments);
}
loadResources() {
return this.renderer.loadResources.apply(this.renderer, arguments);
}
renderAndGetWindow(url, opts = {}, {
loadingTimeout = 2e3,
loadedCallback = this.globals.loadedCallback,
globals = this.globals
} = {}) {
return renderAndGetWindow(url, opts, {
loadingTimeout,
loadedCallback,
globals
});
}
async listen(port, host, socket) {
await this.nuxt.ready();
const listener = new Listener({
port: isNaN(parseInt(port)) ? this.options.server.port : port,
host: host || this.options.server.host,
socket: socket || this.options.server.socket,
https: this.options.server.https,
app: this.app,
dev: this.options.dev,
baseURL: this.options.router.base
});
await listener.listen();
this.listeners.push(listener);
await this.nuxt.callHook("listen", listener.server, listener);
return listener;
}
async close() {
if (this.__closed) {
return;
}
this.__closed = true;
await Promise.all(this.listeners.map((l) => l.close()));
this.listeners = [];
if (typeof this.renderer.close === "function") {
await this.renderer.close();
}
this.app.stack.forEach(this.unloadMiddleware);
this.app.removeAllListeners();
this.app = null;
for (const key in this.resources) {
delete this.resources[key];
}
}
}
exports.Listener = Listener;
exports.Server = Server;
+4
-4
{
"name": "@rsnuxt/server",
"version": "0.2.2",
"version": "0.2.3",
"repository": {

@@ -16,4 +16,4 @@ "type": "git",

"@nuxtjs/youch": "^4.2.3",
"@rsnuxt/utils": "0.2.2",
"@rsnuxt/vue-renderer": "0.2.2",
"@rsnuxt/utils": "0.2.3",
"@rsnuxt/vue-renderer": "0.2.3",
"compression": "^1.7.4",

@@ -39,3 +39,3 @@ "connect": "^3.7.0",

},
"gitHead": "19faf6ad2a17cd919376be02f6c8ebb3fa2ce4dc"
"gitHead": "6a56ef32e8ce15cdbd684f1ccecba180fa78e8a1"
}