Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

sqip

Package Overview
Dependencies
Maintainers
2
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sqip - npm Package Compare versions

Comparing version 1.0.0-alpha.47 to 1.0.0-alpha.48

11

CHANGELOG.md

@@ -6,2 +6,13 @@ # Change Log

# [1.0.0-alpha.48](https://github.com/axe312ger/sqip/compare/sqip@1.0.0-alpha.47...sqip@1.0.0-alpha.48) (2024-03-22)
### Bug Fixes
* BREAKING - up minimum node version to 18.12.1 and configure typescript to output for that node version ([f1e051f](https://github.com/axe312ger/sqip/commit/f1e051f4962e094308116a9bbf47f063abf7dc8b))
# [1.0.0-alpha.47](https://github.com/axe312ger/sqip/compare/sqip@1.0.0-alpha.46...sqip@1.0.0-alpha.47) (2024-03-22)

@@ -8,0 +19,0 @@

101

dist/helpers.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -46,4 +14,4 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

const debug = (0, debug_1.default)('sqip');
const loadSVG = (svg) => __awaiter(void 0, void 0, void 0, function* () {
const { createSVGWindow } = yield Promise.resolve().then(() => __importStar(require('svgdom')));
const loadSVG = async (svg) => {
const { createSVGWindow } = await import('svgdom');
const window = createSVGWindow();

@@ -53,3 +21,3 @@ const document = window.document;

return (0, svg_js_1.SVG)(svg);
});
};
exports.loadSVG = loadSVG;

@@ -60,33 +28,32 @@ function isError(error) {

exports.isError = isError;
function locateFiles(input) {
return __awaiter(this, void 0, void 0, function* () {
const enhancedInput = (0, expand_tilde_1.default)(input);
let globPattern = enhancedInput;
try {
const stat = yield fs_extra_1.default.lstat(enhancedInput);
if (stat.isFile()) {
debug(`input ${input} is a file. Skip file search.`);
return [enhancedInput];
}
if (stat.isDirectory()) {
debug(`input ${input} is a directory. Enhancing with * to match all files.`);
globPattern = `${path_1.default.resolve(enhancedInput)}/*`;
}
async function locateFiles(input) {
const enhancedInput = (0, expand_tilde_1.default)(input);
let globPattern = enhancedInput;
try {
const stat = await fs_extra_1.default.lstat(enhancedInput);
if (stat.isFile()) {
debug(`input ${input} is a file. Skip file search.`);
return [enhancedInput];
}
catch (err) {
if (isError(err) && err instanceof TypeError) {
if (err.code === 'ENOENT') {
throw err;
}
if (stat.isDirectory()) {
debug(`input ${input} is a directory. Enhancing with * to match all files.`);
globPattern = `${path_1.default.resolve(enhancedInput)}/*`;
}
}
catch (err) {
if (isError(err) && err instanceof TypeError) {
if (err.code === 'ENOENT') {
throw err;
}
}
// Find all files matching the enhanced glob
const files = yield (0, fast_glob_1.default)(globPattern, {
onlyFiles: true,
extglob: true,
absolute: true
});
// Test if files are found
if (!files.length) {
throw new Error(`Unable to find any files via ${globPattern}. Make sure the file exists.
}
// Find all files matching the enhanced glob
const files = await (0, fast_glob_1.default)(globPattern, {
onlyFiles: true,
extglob: true,
absolute: true
});
// Test if files are found
if (!files.length) {
throw new Error(`Unable to find any files via ${globPattern}. Make sure the file exists.

@@ -96,13 +63,11 @@ If you are using globbing patterns, the following features are supported:

https://github.com/micromatch/micromatch#matching-features`);
}
return files;
});
}
return files;
}
exports.locateFiles = locateFiles;
function parseColor({ palette, color }) {
var _a;
// @todo test, fallback to or detect transparent as color (for bg)
return ((_a = palette[color]) === null || _a === void 0 ? void 0 : _a.hex) || color;
return palette[color]?.hex || color;
}
exports.parseColor = parseColor;
//# sourceMappingURL=helpers.js.map

@@ -28,11 +28,2 @@ "use strict";

};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -76,2 +67,5 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

class SqipPlugin {
sqipConfig;
options;
static cliOptions;
constructor(options) {

@@ -90,277 +84,269 @@ const { sqipConfig } = options;

// Array of plugin names or config objects, even mixed.
function resolvePlugins(plugins) {
return __awaiter(this, void 0, void 0, function* () {
return Promise.all(plugins.map((plugin) => __awaiter(this, void 0, void 0, function* () {
if (typeof plugin === 'string') {
plugin = { name: plugin };
}
const { name } = plugin;
if (!name) {
throw new Error(`Unable to read plugin name from:\n${JSON.stringify(plugin, null, 2)}`);
}
const moduleName = name.indexOf('sqip-plugin-') !== -1 ? name : `sqip-plugin-${name}`;
try {
debug(`Loading ${moduleName}`);
const Plugin = yield Promise.resolve(`${moduleName}`).then(s => __importStar(require(s)));
return Object.assign(Object.assign({}, plugin), { Plugin: Plugin.default });
}
catch (err) {
console.error(err);
throw new Error(`Unable to load plugin "${moduleName}". Try installing it via:\n\n npm install ${moduleName}`);
}
})));
});
async function resolvePlugins(plugins) {
return Promise.all(plugins.map(async (plugin) => {
if (typeof plugin === 'string') {
plugin = { name: plugin };
}
const { name } = plugin;
if (!name) {
throw new Error(`Unable to read plugin name from:\n${JSON.stringify(plugin, null, 2)}`);
}
const moduleName = name.indexOf('sqip-plugin-') !== -1 ? name : `sqip-plugin-${name}`;
try {
debug(`Loading ${moduleName}`);
const Plugin = await import(moduleName);
return { ...plugin, Plugin: Plugin.default };
}
catch (err) {
console.error(err);
throw new Error(`Unable to load plugin "${moduleName}". Try installing it via:\n\n npm install ${moduleName}`);
}
}));
}
exports.resolvePlugins = resolvePlugins;
function processFile({ filePath, buffer, outputFileName, config }) {
return __awaiter(this, void 0, void 0, function* () {
const { output, silent, parseableOutput, print } = config;
const result = yield processImage({ filePath, buffer, config });
const { content, metadata } = result;
let outputPath;
debug(`Processed ${outputFileName}`);
// Write result svg if desired
if (output) {
try {
// Test if output path already exists
const stats = yield fs_extra_1.default.stat(output);
// Throw if it is a file and already exists
if (!stats.isDirectory()) {
throw new Error(`File ${output} already exists. Overwriting is not yet supported.`);
}
outputPath = path_1.default.resolve(output, `${outputFileName}.svg`);
async function processFile({ filePath, buffer, outputFileName, config }) {
const { output, silent, parseableOutput, print } = config;
const result = await processImage({ filePath, buffer, config });
const { content, metadata } = result;
let outputPath;
debug(`Processed ${outputFileName}`);
// Write result svg if desired
if (output) {
try {
// Test if output path already exists
const stats = await fs_extra_1.default.stat(output);
// Throw if it is a file and already exists
if (!stats.isDirectory()) {
throw new Error(`File ${output} already exists. Overwriting is not yet supported.`);
}
catch (err) {
// Output directory or file does not exist. We will create it later on.
outputPath = output;
}
debug(`Writing ${outputPath}`);
yield fs_extra_1.default.writeFile(outputPath, content);
outputPath = path_1.default.resolve(output, `${outputFileName}.svg`);
}
// Gather CLI output information
if (!silent) {
if (outputPath) {
console.log(`Stored at: ${outputPath}`);
}
// Generate preview
if (!parseableOutput) {
// Convert to png for image preview
const preview = yield (0, sharp_1.default)(Buffer.from(content)).png().toBuffer();
try {
(0, term_img_1.default)(preview, {
fallback: () => {
// SVG results can still be outputted as string
if (metadata.type === 'svg') {
console.log(content.toString());
return;
}
// No fallback preview solution yet for non-svg files.
console.log(`Unable to render a preview for ${metadata.type} files on this machine. Try using https://iterm2.com/`);
catch (err) {
// Output directory or file does not exist. We will create it later on.
outputPath = output;
}
debug(`Writing ${outputPath}`);
await fs_extra_1.default.writeFile(outputPath, content);
}
// Gather CLI output information
if (!silent) {
if (outputPath) {
console.log(`Stored at: ${outputPath}`);
}
// Generate preview
if (!parseableOutput) {
// Convert to png for image preview
const preview = await (0, sharp_1.default)(Buffer.from(content)).png().toBuffer();
try {
(0, term_img_1.default)(preview, {
fallback: () => {
// SVG results can still be outputted as string
if (metadata.type === 'svg') {
console.log(content.toString());
return;
}
});
}
catch (err) {
if (err instanceof term_img_1.UnsupportedTerminalError) {
if (err.name === 'UnsupportedTerminalError') {
throw err;
}
// No fallback preview solution yet for non-svg files.
console.log(`Unable to render a preview for ${metadata.type} files on this machine. Try using https://iterm2.com/`);
}
}
});
}
// Metadata
const tableConfig = parseableOutput
? {
chars: {
top: '',
'top-mid': '',
'top-left': '',
'top-right': '',
bottom: '',
'bottom-mid': '',
'bottom-left': '',
'bottom-right': '',
left: '',
'left-mid': '',
mid: '',
'mid-mid': '',
right: '',
'right-mid': '',
middle: ' '
},
style: { 'padding-left': 0, 'padding-right': 0 }
catch (err) {
if (err instanceof term_img_1.UnsupportedTerminalError) {
if (err.name === 'UnsupportedTerminalError') {
throw err;
}
}
: undefined;
// Figure out which metadata keys to show
// @todo why is this unused?
// const allKeys = [...mainKeys, 'palette']
const mainTable = new cli_table3_1.default(tableConfig);
mainTable.push(mainKeys);
mainTable.push(mainKeys.map((key) => String(metadata[key]) || 'can not display'));
console.log(mainTable.toString());
// Show color palette
const paletteTable = new cli_table3_1.default(tableConfig);
paletteTable.push(PALETTE_KEYS);
paletteTable.push(PALETTE_KEYS.map((key) => { var _a; return (_a = metadata.palette[key]) === null || _a === void 0 ? void 0 : _a.hex; })
.filter((hex) => typeof hex === 'string')
.map((hex) => chalk_1.default.hex(hex)(hex)));
console.log(paletteTable.toString());
Object.keys(metadata)
.filter((key) => ![...mainKeys, 'palette'].includes(key))
.forEach((key) => {
console.log(chalk_1.default.bold(`${key}:`));
console.log(metadata[key]);
});
if (metadata.type === 'svg' && print) {
console.log(`Resulting SVG:\n${result.content.toString()}`);
}
}
return result;
});
}
function processImage({ filePath, buffer, config }) {
return __awaiter(this, void 0, void 0, function* () {
// Extract the palette from the image. We delegate to node-vibrant (which is
// using jimp internally), and it only supports some image formats. In
// particular, it does not support WebP and HEIC yet.
//
// So we try with the given image buffer, and if the code throws an exception
// we try again after converting to TIFF. If that fails again we give up.
const paletteResult = yield (() => __awaiter(this, void 0, void 0, function* () {
const getPalette = (buffer) => sharp_vibrant_1.default.from(buffer).quality(0).getPalette();
try {
return yield getPalette(buffer);
// Metadata
const tableConfig = parseableOutput
? {
chars: {
top: '',
'top-mid': '',
'top-left': '',
'top-right': '',
bottom: '',
'bottom-mid': '',
'bottom-left': '',
'bottom-right': '',
left: '',
'left-mid': '',
mid: '',
'mid-mid': '',
right: '',
'right-mid': '',
middle: ' '
},
style: { 'padding-left': 0, 'padding-right': 0 }
}
catch (_a) {
return getPalette(yield (0, sharp_1.default)(buffer).tiff().toBuffer());
}
}))();
const { name: filename } = path_1.default.parse(filePath);
const mimeType = mime_1.default.getType(filePath) || 'unknown';
const metadata = {
filename,
mimeType,
originalWidth: paletteResult.imageDimensions.width,
originalHeight: paletteResult.imageDimensions.height,
palette: paletteResult.palette,
// @todo this should be set by plugins and detected initially
type: 'unknown',
width: 0,
height: 0
};
// Load plugins
const plugins = yield resolvePlugins(config.plugins);
// Determine output image size
if (config.width && config.width > 0) {
// Resize to desired output width
try {
buffer = yield (0, sharp_1.default)(buffer).resize(config.width).toBuffer();
const resizedMetadata = yield (0, sharp_1.default)(buffer).metadata();
metadata.width = resizedMetadata.width || 0;
metadata.height = resizedMetadata.height || 0;
}
catch (err) {
throw new Error('Unable to resize');
}
: undefined;
// Figure out which metadata keys to show
// @todo why is this unused?
// const allKeys = [...mainKeys, 'palette']
const mainTable = new cli_table3_1.default(tableConfig);
mainTable.push(mainKeys);
mainTable.push(mainKeys.map((key) => String(metadata[key]) || 'can not display'));
console.log(mainTable.toString());
// Show color palette
const paletteTable = new cli_table3_1.default(tableConfig);
paletteTable.push(PALETTE_KEYS);
paletteTable.push(PALETTE_KEYS.map((key) => metadata.palette[key]?.hex)
.filter((hex) => typeof hex === 'string')
.map((hex) => chalk_1.default.hex(hex)(hex)));
console.log(paletteTable.toString());
Object.keys(metadata)
.filter((key) => ![...mainKeys, 'palette'].includes(key))
.forEach((key) => {
console.log(chalk_1.default.bold(`${key}:`));
console.log(metadata[key]);
});
if (metadata.type === 'svg' && print) {
console.log(`Resulting SVG:\n${result.content.toString()}`);
}
else {
// Fall back to original size, keep image as is
metadata.width = metadata.originalWidth;
metadata.height = metadata.originalHeight;
}
return result;
}
async function processImage({ filePath, buffer, config }) {
// Extract the palette from the image. We delegate to node-vibrant (which is
// using jimp internally), and it only supports some image formats. In
// particular, it does not support WebP and HEIC yet.
//
// So we try with the given image buffer, and if the code throws an exception
// we try again after converting to TIFF. If that fails again we give up.
const paletteResult = await (async () => {
const getPalette = (buffer) => sharp_vibrant_1.default.from(buffer).quality(0).getPalette();
try {
return await getPalette(buffer);
}
// Interate through plugins and apply them to last returned image
for (const { name, options: pluginOptions, Plugin } of plugins) {
try {
debug(`Construct ${name}`);
const plugin = new Plugin({
sqipConfig: config,
pluginOptions: pluginOptions || {},
options: {}
});
debug(`Apply ${name}`);
buffer = yield plugin.apply(buffer, metadata);
}
catch (err) {
console.log(`Error thrown in plugin ${name}.`);
console.dir({ metadata }, { depth: 3 });
throw err;
}
catch {
return getPalette(await (0, sharp_1.default)(buffer).tiff().toBuffer());
}
return { content: buffer, metadata };
});
}
function sqip(options) {
return __awaiter(this, void 0, void 0, function* () {
// Build configuration based on passed options and default options
const defaultOptions = {
plugins: [
{ name: 'primitive', options: { numberOfPrimitives: 8, mode: 0 } },
'blur',
'svgo',
'data-uri'
],
width: 300,
parseableOutput: false,
silent: true,
print: false
};
const config = Object.assign({}, defaultOptions, options);
const { input, outputFileName, parseableOutput, silent } = config;
if (parseableOutput) {
chalk_1.default.level = 0;
})();
const { name: filename } = path_1.default.parse(filePath);
const mimeType = mime_1.default.getType(filePath) || 'unknown';
const metadata = {
filename,
mimeType,
originalWidth: paletteResult.imageDimensions.width,
originalHeight: paletteResult.imageDimensions.height,
palette: paletteResult.palette,
// @todo this should be set by plugins and detected initially
type: 'unknown',
width: 0,
height: 0
};
// Load plugins
const plugins = await resolvePlugins(config.plugins);
// Determine output image size
if (config.width && config.width > 0) {
// Resize to desired output width
try {
buffer = await (0, sharp_1.default)(buffer).resize(config.width).toBuffer();
const resizedMetadata = await (0, sharp_1.default)(buffer).metadata();
metadata.width = resizedMetadata.width || 0;
metadata.height = resizedMetadata.height || 0;
}
// Validate configuration
if (!input || input.length === 0) {
throw new Error('Please provide an input image, e.g. sqip({ input: "input.jpg" })');
catch (err) {
throw new Error('Unable to resize');
}
// If input is a Buffer
if (Buffer.isBuffer(input)) {
if (!outputFileName) {
throw new Error('OutputFileName is required when passing image as buffer');
}
return processFile({
filePath: '-',
buffer: input,
outputFileName,
config
}
else {
// Fall back to original size, keep image as is
metadata.width = metadata.originalWidth;
metadata.height = metadata.originalHeight;
}
// Interate through plugins and apply them to last returned image
for (const { name, options: pluginOptions, Plugin } of plugins) {
try {
debug(`Construct ${name}`);
const plugin = new Plugin({
sqipConfig: config,
pluginOptions: pluginOptions || {},
options: {}
});
debug(`Apply ${name}`);
buffer = await plugin.apply(buffer, metadata);
}
const files = yield (0, helpers_1.locateFiles)(input);
debug('Found files:');
debug(files);
// Test if all files are accessable
for (const file of files) {
try {
debug('check file ' + file);
yield fs_extra_1.default.access(file, fs_extra_1.default.constants.R_OK);
}
catch (err) {
throw new Error(`Unable to read file ${file}`);
}
catch (err) {
console.log(`Error thrown in plugin ${name}.`);
console.dir({ metadata }, { depth: 3 });
throw err;
}
// Iterate over all files
const results = [];
for (const filePath of files) {
// Apply plugins to files
if (!silent) {
console.log(`Processing: ${filePath}`);
}
else {
debug(`Processing ${filePath}`);
}
const buffer = yield fs_extra_1.default.readFile(filePath);
const result = yield processFile({
filePath,
buffer,
outputFileName: outputFileName || path_1.default.parse(filePath).name,
config
});
results.push(result);
}
return { content: buffer, metadata };
}
async function sqip(options) {
// Build configuration based on passed options and default options
const defaultOptions = {
plugins: [
{ name: 'primitive', options: { numberOfPrimitives: 8, mode: 0 } },
'blur',
'svgo',
'data-uri'
],
width: 300,
parseableOutput: false,
silent: true,
print: false
};
const config = Object.assign({}, defaultOptions, options);
const { input, outputFileName, parseableOutput, silent } = config;
if (parseableOutput) {
chalk_1.default.level = 0;
}
// Validate configuration
if (!input || input.length === 0) {
throw new Error('Please provide an input image, e.g. sqip({ input: "input.jpg" })');
}
// If input is a Buffer
if (Buffer.isBuffer(input)) {
if (!outputFileName) {
throw new Error('OutputFileName is required when passing image as buffer');
}
debug(`Finished`);
// Return as array when input was array or results is only one file
if (Array.isArray(input) || results.length === 0) {
return results;
return processFile({
filePath: '-',
buffer: input,
outputFileName,
config
});
}
const files = await (0, helpers_1.locateFiles)(input);
debug('Found files:');
debug(files);
// Test if all files are accessable
for (const file of files) {
try {
debug('check file ' + file);
await fs_extra_1.default.access(file, fs_extra_1.default.constants.R_OK);
}
return results[0];
});
catch (err) {
throw new Error(`Unable to read file ${file}`);
}
}
// Iterate over all files
const results = [];
for (const filePath of files) {
// Apply plugins to files
if (!silent) {
console.log(`Processing: ${filePath}`);
}
else {
debug(`Processing ${filePath}`);
}
const buffer = await fs_extra_1.default.readFile(filePath);
const result = await processFile({
filePath,
buffer,
outputFileName: outputFileName || path_1.default.parse(filePath).name,
config
});
results.push(result);
}
debug(`Finished`);
// Return as array when input was array or results is only one file
if (Array.isArray(input) || results.length === 0) {
return results;
}
return results[0];
}

@@ -367,0 +353,0 @@ exports.sqip = sqip;

{
"name": "sqip",
"version": "1.0.0-alpha.47",
"version": "1.0.0-alpha.48",
"description": "> TODO: description",

@@ -14,3 +14,3 @@ "author": "Benedikt Rötsch <info@benedikt-roetsch.de>",

"engines": {
"node": ">=14.0.0"
"node": ">=18.12.1"
},

@@ -71,3 +71,3 @@ "types": "dist/sqip.d.ts",

},
"gitHead": "bdc25c885ea57659ea00cd0a9e85403b74cd9f3e"
"gitHead": "5a90f4e138f6682391ba551b94d0c16c931b4801"
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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