Socket
Socket
Sign inDemoInstall

cssserve

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cssserve - npm Package Compare versions

Comparing version 1.5.0 to 2.0.0

13

CHANGELOG.md

@@ -7,2 +7,11 @@ # Change Log

## 2.0.0
_2022-09-19_
- **BREAKING** chore: Update min node version to 16
- **BREAKING** chore: Update fastify and other deps to most recent versions
- feat: Make "empty"/meta tokens visible in bundle output
- feat: Report invalid tokens as "invalid", instead of "ignored" (in prod)
## 1.5.0

@@ -14,3 +23,3 @@

## 1.4.0
## 1.4.0 — 1.4.1

@@ -38,3 +47,3 @@ _2022-02-05_

- feat: Allow `//` comments inside `@deps` blocks
- docs: Improved README and AppConfig docs.
- docs: Improved README and AppConfig docs

@@ -41,0 +50,0 @@ ## 1.1.3 – 1.1.4

18

package.json
{
"name": "cssserve",
"version": "1.5.0",
"version": "2.0.0",
"license": "MIT",
"dependencies": {
"@fastify/compress": "^6.1.1",
"@fastify/cors": "^8.1.0",
"@fastify/static": "^6.5.0",
"compression": "^1.7.4",
"fastify": "^2.12.0",
"fastify-compress": "^2.0.1",
"fastify-cors": "^3.0.2",
"fastify-static": "^2.6.0",
"glob": "^7.1.6",
"lru-cache": "^5.1.1",
"fastify": "^4.5.3",
"glob": "^8.0.3",
"lru-cache": "^7.14.0",
"rc": "^1.2.8"
},
"__devDependencies": {
"@hugsmidjan_is/qj": "^3.5.0"
"@hugsmidjan/qj": "^4.10.2"
},

@@ -28,4 +28,4 @@ "author": "Hugsmiðjan ehf (http://www.hugsmidjan.is)",

"engines": {
"node": ">=12"
"node": ">=16"
}
}

@@ -15,3 +15,3 @@ # cssserve – CSS Server

- [CSS dependency bundling and version resolution](#css-dependency-bundling-and-version-resolution)
- [Example request](#example-request)
- [Example request](#example-request)
- [Static assets](#static-assets)

@@ -93,4 +93,4 @@

<link
rel="stylesheet"
href="https://css.server/bundle/v1?m=_base,ModuleB,ModuleA"
rel="stylesheet"
href="https://css.server/bundle/v1?m=_base,ModuleB,ModuleA"
/>

@@ -129,5 +129,5 @@ ```

@media screen {
.ModuleA {
/* ...styles for ModuleA */
}
.ModuleA {
/* ...styles for ModuleA */
}
}

@@ -134,0 +134,0 @@ ```

#!/usr/bin/env node
'use strict';
var fastifyCompress = require('@fastify/compress');
var fastifyCors = require('@fastify/cors');
var fastifyStatic = require('@fastify/static');
var fastify = require('fastify');
var fastifyCompress = require('fastify-compress');
var fastifyStatic = require('fastify-static');
var fastifyCors = require('fastify-cors');
var fs = require('fs');
var glob = require('glob');
var path = require('path');
var rc = require('rc');
var LRUCache = require('lru-cache');
var path = require('path');
var glob = require('glob');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var fastify__default = /*#__PURE__*/_interopDefaultLegacy(fastify);
var fastifyCompress__default = /*#__PURE__*/_interopDefaultLegacy(fastifyCompress);
var fastifyCors__default = /*#__PURE__*/_interopDefaultLegacy(fastifyCors);
var fastifyStatic__default = /*#__PURE__*/_interopDefaultLegacy(fastifyStatic);
var fastifyCors__default = /*#__PURE__*/_interopDefaultLegacy(fastifyCors);
var fastify__default = /*#__PURE__*/_interopDefaultLegacy(fastify);
var rc__default = /*#__PURE__*/_interopDefaultLegacy(rc);
var LRUCache__default = /*#__PURE__*/_interopDefaultLegacy(LRUCache);
/*! *****************************************************************************
var name = "cssserve";
var ensureObject = function (cand) {
if (cand && typeof cand === 'object' && !Array.isArray(cand)) {
return cand;
}
};
var ensureNonEmptyString = function (cand) {
if (cand && typeof cand === 'string') {
return cand;
}
};
var ensureStringMap = function (cand) {
var obj = ensureObject(cand);
if (obj && Object.values(obj).every(ensureNonEmptyString)) {
return obj;
}
};
// ---------------------------------------------------------------------------
var parseRedirects = function (redirects, redirectsFile) {
if (redirects !== undefined) {
redirects = ensureStringMap(redirects);
if (!redirects) {
throw new Error("The redirects config field must contain a string\u2192string map");
}
}
if (redirectsFile) {
var fileContents = void 0;
try {
fileContents = fs.readFileSync(redirectsFile).toString();
}
catch (e) {
throw new Error("Could not read redirectsFile \"".concat(redirects, "\""));
}
var parsedContents = void 0;
try {
parsedContents = JSON.parse(fileContents);
}
catch (e) {
throw new Error("Invalid JSON found in redirectsFile \"".concat(redirects, "\""));
}
var fileRedirects_1 = ensureStringMap(parsedContents);
if (!fileRedirects_1) {
throw new Error("The redirectsFile \"".concat(redirects, "\" must contain a string\u2192string map"));
}
if (redirects) {
// Merge fileRedirects into redirects.
Object.keys(fileRedirects_1).forEach(function (key) {
redirects[key] = fileRedirects_1[key];
});
}
else {
redirects = fileRedirects_1;
}
}
return redirects;
};
var normalizePathSlash = function (path) { return path.replace(/\/*$/, '/'); };
var appName = name.split('/').pop();
var HOUR = 60 * 60;
var defaults = {
port: 4000,
staticFolder: 'public/',
ttl_static: 24 * HOUR,
ttl_bundle: 1 * HOUR,
proxied: false,
cache: true,
loudBadTokenErrors: process.env.NODE_ENV !== 'production',
};
var config = rc__default["default"](appName, defaults);
// enforce correct types
config.port = parseInt(process.env.NODE_PORT || process.env.PORT || '') || config.port;
config.staticFolder = normalizePathSlash(config.staticFolder.trim());
config.ttl_static = Number(config.ttl_static);
config.ttl_bundle = Number(config.ttl_bundle);
// config.cacheRefreshToken =
config.cache = Boolean(config.cache);
config.proxied = Boolean(config.proxied);
config.sslKeyPath = config.sslKeyPath && config.sslKeyPath.trim();
// config.sslCert =
// config.sslPrivkey =
config.loudBadTokenErrors = Boolean(config.loudBadTokenErrors);
config.preload =
config.preload || config.preload === false ? Boolean(config.preload) : true;
config.redirects = parseRedirects(config.redirects, config.redirectsFile);
config.redirectsFile = undefined;
/******************************************************************************
Copyright (c) Microsoft Corporation.

@@ -42,3 +130,3 @@

({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
function (d, b) { for (var p in b) { if (Object.prototype.hasOwnProperty.call(b, p)) { d[p] = b[p]; } } };
return extendStatics(d, b);

@@ -48,2 +136,4 @@ };

function __extends(d, b) {
if (typeof b !== "function" && b !== null)
{ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); }
extendStatics(d, b);

@@ -54,56 +144,3 @@ function __() { this.constructor = d; }

// ---------------------------------------------------------------------------
/**
* Checks if an URL token contains no spaces or funny characters.
* Only allows `[a-z0-9-_.]`
*/
var isSafeToken = function (token) {
return !token || !(/[^a-z0-9-_.]/i.test(token) || /\.\./.test(token));
};
var getAllValidCssVersions = function (staticFolder) {
var versions = {};
var cssFolder = staticFolder + 'css/';
var versionFolders = glob.sync('*/', { cwd: cssFolder })
// chop trailing "/" off directoryNames
.map(function (dirName) { return dirName.slice(0, -1); });
versionFolders.forEach(function (name) {
var superVersions = name
.split('.')
.slice(0, -1)
.reduce(function (superVersions, token, i, arr) {
return superVersions.concat([arr.slice(0, i + 1).join('.')]);
}, []);
superVersions.forEach(function (superVersion) {
if (!versions[superVersion]) {
var cutIdx_1 = superVersion.length + 1;
var topPointVersion = versionFolders
.filter(function (subName) { return subName.startsWith(superVersion + '.'); })
.map(function (subName) { return subName.slice(cutIdx_1); })
.filter(function (minorVersionSuffix) { return !/[^0-9.]/.test(minorVersionSuffix); })
.map(function (minorVersionSuffix) {
var arr = minorVersionSuffix.split('.').map(function (bit) { return parseInt(bit); });
arr.length = 4; // Normalize the length for saner sort.
return arr;
})
.sort(function (a, b) {
var idx = a.findIndex(function (_, i) { return a[i] !== b[i]; });
return (a[idx] || 0) > (b[idx] || 0) ? 1 : -1;
})
.pop();
if (topPointVersion) {
var pointVersionSuffix = topPointVersion.join('.').replace(/\.+$/, '');
versions[superVersion] = 'css/' + superVersion + '.' + pointVersionSuffix + '/';
}
}
});
versionFolders.filter(function (fname) { return fname.startsWith(name + '.'); });
});
versionFolders.forEach(function (name) {
versions[name] = versions[name] || 'css/' + name + '/';
});
return versions;
};
/**
Add a simple chainable `.on()`, `.off()`, `.emit()` interface to any object.

@@ -223,3 +260,3 @@

// import eventify from '@hugsmidjan_is/qj/eventify';
// import eventify from '@hugsmidjan/qj/eventify';
var _a = eventify({}), on = _a.on, emit = _a.emit;

@@ -233,113 +270,59 @@ var onCacheRefresh = function (callback) {

var name = "cssserve";
/**
* Compares strings and sorts them by lowercase first
* before doing a normal comparison.
*/
var lowercaseFirstCompare = function (a, b) {
var aL = a.toLocaleLowerCase();
var bL = b.toLocaleLowerCase();
return aL > bL ? 1 : aL < bL ? -1 : a > b ? 1 : -1;
};
var ensureObject = function (cand) {
if (cand && typeof cand === 'object' && !Array.isArray(cand)) {
return cand;
}
/**
* Returns the value of the first query parameter of a given name
* ...defaulting to an empty string if no parameter is found.
*/
var getParamArr = function (query, name) {
var val = query[name];
return val == null ? '' : typeof val === 'string' ? val : val[0];
};
var ensureNonEmptyString = function (cand) {
if (cand && typeof cand === 'string') {
return cand;
}
};
var ensureStringMap = function (cand) {
var obj = ensureObject(cand);
if (obj && Object.values(obj).every(ensureNonEmptyString)) {
return obj;
}
};
// ---------------------------------------------------------------------------
var parseRedirects = function (redirects, redirectsFile) {
if (redirects !== undefined) {
redirects = ensureStringMap(redirects);
if (!redirects) {
throw new Error("The redirects config field must contain a string\u2192string map");
}
}
if (redirectsFile) {
var fileContents = void 0;
try {
fileContents = fs.readFileSync(redirectsFile).toString();
}
catch (e) {
throw new Error("Could not read redirectsFile \"" + redirects + "\"");
}
var parsedContents = void 0;
try {
parsedContents = JSON.parse(fileContents);
}
catch (e) {
throw new Error("Invalid JSON found in redirectsFile \"" + redirects + "\"");
}
var fileRedirects_1 = ensureStringMap(parsedContents);
if (!fileRedirects_1) {
throw new Error("The redirectsFile \"" + redirects + "\" must contain a string\u2192string map");
}
if (redirects) {
// Merge fileRedirects into redirects.
Object.keys(fileRedirects_1).forEach(function (key) {
redirects[key] = fileRedirects_1[key];
});
}
else {
redirects = fileRedirects_1;
}
}
return redirects;
var getModuleListFromQuery = function (query) {
return getParamArr(query, 'm')
.trim()
.split(/\s*,\s*/)
.filter(function (token) { return token; })
.sort(lowercaseFirstCompare);
};
var normalizePathSlash = function (path) { return path.replace(/\/*$/, '/'); };
var appName = name.split('/').pop();
var HOUR = 60 * 60;
var defaults = {
port: 4000,
staticFolder: 'public/',
ttl_static: 24 * HOUR,
ttl_bundle: 1 * HOUR,
proxied: false,
cache: true,
loudBadTokenErrors: process.env.NODE_ENV !== 'production',
var makeCssFromModuleNames = function (versionFolder, modules) {
return modules
.map(function (moduleName) {
return typeof moduleName === 'string'
? "@import \"/".concat(versionFolder + moduleName, ".css\";\n")
: moduleName.invalid
? "/* token ".concat(JSON.stringify(moduleName.name), " is invalid */\n")
: "/* token ".concat(JSON.stringify(moduleName.name), " has no CSS */\n");
})
.join('');
};
var config = rc__default["default"](appName, defaults);
// enforce correct types
config.port = parseInt(process.env.NODE_PORT || process.env.PORT || '') || config.port;
config.staticFolder = normalizePathSlash(config.staticFolder.trim());
config.ttl_static = Number(config.ttl_static);
config.ttl_bundle = Number(config.ttl_bundle);
// config.cacheRefreshToken =
config.cache = Boolean(config.cache);
config.proxied = Boolean(config.proxied);
config.sslKeyPath = config.sslKeyPath && config.sslKeyPath.trim();
// config.sslCert =
// config.sslPrivkey =
config.loudBadTokenErrors = Boolean(config.loudBadTokenErrors);
config.preload =
config.preload || config.preload === false ? Boolean(config.preload) : true;
config.redirects = parseRedirects(config.redirects, config.redirectsFile);
config.redirectsFile = undefined;
var _validVersions = {};
onCacheRefresh(function () {
_validVersions = {};
});
var resolveCssVersionFolder = function (staticFolder, versionParam) {
if (!versionParam || !isSafeToken(versionParam)) {
return null;
}
var versions = config.cache && _validVersions[staticFolder];
if (!versions) {
versions = _validVersions[staticFolder] = getAllValidCssVersions(staticFolder);
}
return versions[versionParam] || null;
var makeLinkHeaderValue = function (versionFolder, modules) {
return modules
.filter(function (moduleName) { return typeof moduleName === 'string'; })
.map(function (moduleName) { return "</".concat(versionFolder + moduleName, ".css>;rel=preload;as=style"); })
.join(',') || undefined;
};
var isProd = process.env.NODE_ENV === 'production';
var isDebug = process.env.NODE_ENV === 'debug';
var isDev = isDebug || process.env.NODE_ENV === 'development';
// ---------------------------------------------------------------------------
/**
* Compares strings and sorts them by lowercase first
* before doing a normal comparison.
* Checks if an URL token contains no spaces or funny characters.
* Only allows `[a-z0-9-_.]`
*/
var lowercaseFirstCompare = function (a, b) {
var aL = a.toLocaleLowerCase();
var bL = b.toLocaleLowerCase();
return aL > bL ? 1 : aL < bL ? -1 : a > b ? 1 : -1;
var isSafeToken = function (token) {
return !token || !(/[^a-z0-9-_.]/i.test(token) || /\.\./.test(token));
};

@@ -369,6 +352,2 @@

var isProd = process.env.NODE_ENV === 'production';
var isDebug = process.env.NODE_ENV === 'debug';
var isDev = isDebug || process.env.NODE_ENV === 'development';
// ---------------------------------------------------------------------------

@@ -467,3 +446,3 @@ var NotFoundError = /** @class */ (function (_super) {

}
return list.concat({ ignored: moduleName });
return list.concat({ name: moduleName, invalid: true });
}

@@ -473,3 +452,5 @@ found[moduleName] = true;

var deps = getDepsFor(contextFile, opts.cache);
return deps.reduce(parseDepsTree, list).concat(deps.hasCSS ? [moduleName] : []);
return deps
.reduce(parseDepsTree, list)
.concat(deps.hasCSS ? [moduleName] : [{ name: moduleName, empty: true }]);
};

@@ -481,36 +462,61 @@ modules = modules.slice(0).sort(lowercaseFirstCompare);

/**
* Returns the value of the first query parameter of a given name
* ...defaulting to an empty string if no parameter is found.
*/
var getParamArr = function (query, name) {
var val = query[name];
return val == null ? '' : typeof val === 'string' ? val : val[0];
var getAllValidCssVersions = function (staticFolder) {
var versions = {};
var cssFolder = staticFolder + 'css/';
var versionFolders = glob.sync('*/', { cwd: cssFolder })
// chop trailing "/" off directoryNames
.map(function (dirName) { return dirName.slice(0, -1); });
versionFolders.forEach(function (name) {
var superVersions = name
.split('.')
.slice(0, -1)
.reduce(function (superVersions, token, i, arr) {
return superVersions.concat([arr.slice(0, i + 1).join('.')]);
}, []);
superVersions.forEach(function (superVersion) {
if (!versions[superVersion]) {
var cutIdx_1 = superVersion.length + 1;
var topPointVersion = versionFolders
.filter(function (subName) { return subName.startsWith(superVersion + '.'); })
.map(function (subName) { return subName.slice(cutIdx_1); })
.filter(function (minorVersionSuffix) { return !/[^0-9.]/.test(minorVersionSuffix); })
.map(function (minorVersionSuffix) {
var arr = minorVersionSuffix.split('.').map(function (bit) { return parseInt(bit); });
arr.length = 4; // Normalize the length for saner sort.
return arr;
})
.sort(function (a, b) {
var idx = a.findIndex(function (_, i) { return a[i] !== b[i]; });
return (a[idx] || 0) > (b[idx] || 0) ? 1 : -1;
})
.pop();
if (topPointVersion) {
var pointVersionSuffix = topPointVersion.join('.').replace(/\.+$/, '');
versions[superVersion] = 'css/' + superVersion + '.' + pointVersionSuffix + '/';
}
}
});
versionFolders.filter(function (fname) { return fname.startsWith(name + '.'); });
});
versionFolders.forEach(function (name) {
versions[name] = versions[name] || 'css/' + name + '/';
});
return versions;
};
// ---------------------------------------------------------------------------
var getModuleListFromQuery = function (query) {
return getParamArr(query, 'm')
.trim()
.split(/\s*,\s*/)
.filter(function (token) { return token; })
.sort(lowercaseFirstCompare);
};
var makeLinkHeaderValue = function (versionFolder, modules) {
return modules
.filter(function (moduleName) { return typeof moduleName === 'string'; })
.map(function (moduleName) { return "</" + (versionFolder + moduleName) + ".css>;rel=preload;as=style"; })
.join(',') || undefined;
var _validVersions = {};
onCacheRefresh(function () {
_validVersions = {};
});
var resolveCssVersionFolder = function (staticFolder, versionParam) {
if (!versionParam || !isSafeToken(versionParam)) {
return null;
}
var versions = config.cache && _validVersions[staticFolder];
if (!versions) {
versions = _validVersions[staticFolder] = getAllValidCssVersions(staticFolder);
}
return versions[versionParam] || null;
};
var makeCssFromModuleNames = function (versionFolder, modules) {
return modules
.map(function (moduleName) {
return typeof moduleName === 'string'
? "@import \"/" + (versionFolder + moduleName) + ".css\";\n"
: "/* token " + JSON.stringify(moduleName.ignored) + " ignored */\n";
})
.join('');
};
var ttl_bundle = config.ttl_bundle, staticFolder$1 = config.staticFolder, cacheRefreshToken = config.cacheRefreshToken, cache = config.cache, loudBadTokenErrors = config.loudBadTokenErrors, preload = config.preload;

@@ -544,3 +550,3 @@ var CACHE_CONTROL_VALUE = 'public, max-age=' + ttl_bundle + (ttl_bundle ? ', immutable' : '');

.then(function () {
var url = req.req.url;
var url = req.raw.url;
var cachedBundle = bundleCache.get(url);

@@ -601,3 +607,3 @@ if (cachedBundle) {

var cssBundler = function (req, res) {
var browserEtag = req.headers['If-None-Match'];
var browserEtag = Number(req.headers['If-None-Match']) || 0;
if (browserEtag >= lastModified) {

@@ -662,3 +668,3 @@ res

if (ttl !== null) {
res.header('Cache-Control', "public, max-age=" + ttl + ", immutable");
res.header('Cache-Control', "public, max-age=".concat(ttl, ", immutable"));
}

@@ -675,5 +681,5 @@ res.status(status).send('');

var sslKeyPath = config.sslKeyPath || __dirname + '/default-keys/';
var app = fastify__default["default"](proxied
? {}
: {
var app = proxied
? fastify__default["default"]({})
: fastify__default["default"]({
http2: true,

@@ -687,3 +693,3 @@ https: {

if (!proxied) {
app.register(fastifyCompress__default["default"], { global: true });
app.register(fastifyCompress__default["default"]);
}

@@ -701,3 +707,3 @@ app.register(fastifyCors__default["default"], {

// lastModified: false,
etag: false,
etag: false, // Auto-generated from the file's modification date.
});

@@ -713,10 +719,12 @@ app.setNotFoundHandler(function (req, res) {

app.get('/bundle/:version', cssBundler);
app.listen(port, '0.0.0.0', function (err) {
app
.listen({ port: port })
.then(function () {
var protocol = proxied ? 'http' : 'https';
console.info('CSS server listening on ' + protocol + '://localhost:' + port);
})
.catch(function (err) {
if (err) {
console.error(err);
}
else {
var protocol = proxied ? 'http' : 'https';
console.info('CSS server listening on ' + protocol + '://localhost:' + port);
}
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc