Socket
Socket
Sign inDemoInstall

jackspeak

Package Overview
Dependencies
12
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.2.3 to 2.3.0

10

dist/cjs/index.d.ts

@@ -117,3 +117,3 @@ /// <reference types="node" />

level: number;
pre?: false;
pre?: boolean;
}

@@ -223,3 +223,5 @@ /**

*/
heading(text: string, level?: 1 | 2 | 3 | 4 | 5 | 6): Jack<C>;
heading(text: string, level?: 1 | 2 | 3 | 4 | 5 | 6, { pre }?: {
pre?: boolean;
}): Jack<C>;
/**

@@ -266,2 +268,6 @@ * Add a long-form description to the usage output at this position.

/**
* Return the usage banner markdown for the given configuration
*/
usageMarkdown(): string;
/**
* Return the configuration options as a plain object

@@ -268,0 +274,0 @@ */

166

dist/cjs/index.js

@@ -248,2 +248,3 @@ "use strict";

#usage;
#usageMarkdown;
constructor(options = {}) {

@@ -430,7 +431,7 @@ this.#options = options;

*/
heading(text, level) {
heading(text, level, { pre = false } = {}) {
if (level === undefined) {
level = this.#fields.some(r => isHeading(r)) ? 2 : 1;
}
this.#fields.push({ type: 'heading', text, level });
this.#fields.push({ type: 'heading', text, level, pre });
return this;

@@ -599,2 +600,112 @@ }

}
const { rows, maxWidth } = this.#usageRows(start);
// every heading/description after the first gets indented by 2
// extra spaces.
for (const row of rows) {
if (row.left) {
// If the row is too long, don't wrap it
// Bump the right-hand side down a line to make room
const configIndent = indent(Math.max(headingLevel, 2));
if (row.left.length > maxWidth - 3) {
ui.div({ text: row.left, padding: [0, 0, 0, configIndent] });
ui.div({ text: row.text, padding: [0, 0, 0, maxWidth] });
}
else {
ui.div({
text: row.left,
padding: [0, 1, 0, configIndent],
width: maxWidth,
}, { padding: [0, 0, 0, 0], text: row.text });
}
if (row.skipLine) {
ui.div({ padding: [0, 0, 0, 0], text: '' });
}
}
else {
if (isHeading(row)) {
const { level } = row;
headingLevel = level;
// only h1 and h2 have bottom padding
// h3-h6 do not
const b = level <= 2 ? 1 : 0;
ui.div({ ...row, padding: [0, 0, b, indent(level)] });
}
else {
ui.div({ ...row, padding: [0, 0, 1, indent(headingLevel + 1)] });
}
}
}
return (this.#usage = ui.toString());
}
/**
* Return the usage banner markdown for the given configuration
*/
usageMarkdown() {
if (this.#usageMarkdown)
return this.#usageMarkdown;
const out = [];
let headingLevel = 1;
const first = this.#fields[0];
let start = first?.type === 'heading' ? 1 : 0;
if (first?.type === 'heading') {
out.push(`# ${normalizeOneLine(first.text)}`);
}
out.push('Usage:');
if (this.#options.usage) {
out.push(normalizeMarkdown(this.#options.usage, true));
}
else {
const cmd = (0, node_path_1.basename)(process.argv[1]);
const shortFlags = [];
const shorts = [];
const flags = [];
const opts = [];
for (const [field, config] of Object.entries(this.#configSet)) {
if (config.short) {
if (config.type === 'boolean')
shortFlags.push(config.short);
else
shorts.push([config.short, config.hint || field]);
}
else {
if (config.type === 'boolean')
flags.push(field);
else
opts.push([field, config.hint || field]);
}
}
const sf = shortFlags.length ? ' -' + shortFlags.join('') : '';
const so = shorts.map(([k, v]) => ` --${k}=<${v}>`).join('');
const lf = flags.map(k => ` --${k}`).join('');
const lo = opts.map(([k, v]) => ` --${k}=<${v}>`).join('');
const usage = `${cmd}${sf}${so}${lf}${lo}`.trim();
out.push(normalizeMarkdown(usage, true));
}
const maybeDesc = this.#fields[start];
if (isDescription(maybeDesc)) {
out.push(normalizeMarkdown(maybeDesc.text, maybeDesc.pre));
start++;
}
const { rows } = this.#usageRows(start);
// heading level in markdown is number of # ahead of text
for (const row of rows) {
if (row.left) {
out.push('#'.repeat(headingLevel + 1) +
' ' +
normalizeOneLine(row.left, true));
if (row.text)
out.push(normalizeMarkdown(row.text));
}
else if (isHeading(row)) {
const { level } = row;
headingLevel = level;
out.push(`${'#'.repeat(headingLevel)} ${normalizeOneLine(row.text, row.pre)}`);
}
else {
out.push(normalizeMarkdown(row.text, !!row.pre));
}
}
return (this.#usageMarkdown = out.join('\n\n') + '\n');
}
#usageRows(start) {
// turn each config type into a row, and figure out the width of the

@@ -647,39 +758,3 @@ // left hand indentation for the option descriptions.

}
// every heading/description after the first gets indented by 2
// extra spaces.
for (const row of rows) {
if (row.left) {
// If the row is too long, don't wrap it
// Bump the right-hand side down a line to make room
const configIndent = indent(Math.max(headingLevel, 2));
if (row.left.length > maxWidth - 3) {
ui.div({ text: row.left, padding: [0, 0, 0, configIndent] });
ui.div({ text: row.text, padding: [0, 0, 0, maxWidth] });
}
else {
ui.div({
text: row.left,
padding: [0, 1, 0, configIndent],
width: maxWidth,
}, { padding: [0, 0, 0, 0], text: row.text });
}
if (row.skipLine) {
ui.div({ padding: [0, 0, 0, 0], text: '' });
}
}
else {
if (isHeading(row)) {
const { level } = row;
headingLevel = level;
// only h1 and h2 have bottom padding
// h3-h6 do not
const b = level <= 2 ? 1 : 0;
ui.div({ ...row, padding: [0, 0, b, indent(level)] });
}
else {
ui.div({ ...row, padding: [0, 0, 1, indent(headingLevel + 1)] });
}
}
}
return (this.#usage = ui.toString());
return { rows, maxWidth };
}

@@ -727,2 +802,15 @@ /**

.trim();
// normalize for markdown printing, remove leading spaces on lines
const normalizeMarkdown = (s, pre = false) => {
const n = normalize(s, pre).replace(/\\/g, '\\\\');
return pre
? `\`\`\`\n${n.replace(/\u200b/g, '')}\n\`\`\``
: n.replace(/\n +/g, '\n').trim();
};
const normalizeOneLine = (s, pre = false) => {
const n = normalize(s, pre)
.replace(/[\s\u200b]+/g, ' ')
.trim();
return pre ? `\`${n}\`` : n;
};
/**

@@ -729,0 +817,0 @@ * Main entry point. Create and return a {@link Jack} object.

@@ -117,3 +117,3 @@ /// <reference types="node" />

level: number;
pre?: false;
pre?: boolean;
}

@@ -223,3 +223,5 @@ /**

*/
heading(text: string, level?: 1 | 2 | 3 | 4 | 5 | 6): Jack<C>;
heading(text: string, level?: 1 | 2 | 3 | 4 | 5 | 6, { pre }?: {
pre?: boolean;
}): Jack<C>;
/**

@@ -266,2 +268,6 @@ * Add a long-form description to the usage output at this position.

/**
* Return the usage banner markdown for the given configuration
*/
usageMarkdown(): string;
/**
* Return the configuration options as a plain object

@@ -268,0 +274,0 @@ */

@@ -240,2 +240,3 @@ import { inspect } from 'node:util';

#usage;
#usageMarkdown;
constructor(options = {}) {

@@ -422,7 +423,7 @@ this.#options = options;

*/
heading(text, level) {
heading(text, level, { pre = false } = {}) {
if (level === undefined) {
level = this.#fields.some(r => isHeading(r)) ? 2 : 1;
}
this.#fields.push({ type: 'heading', text, level });
this.#fields.push({ type: 'heading', text, level, pre });
return this;

@@ -591,2 +592,112 @@ }

}
const { rows, maxWidth } = this.#usageRows(start);
// every heading/description after the first gets indented by 2
// extra spaces.
for (const row of rows) {
if (row.left) {
// If the row is too long, don't wrap it
// Bump the right-hand side down a line to make room
const configIndent = indent(Math.max(headingLevel, 2));
if (row.left.length > maxWidth - 3) {
ui.div({ text: row.left, padding: [0, 0, 0, configIndent] });
ui.div({ text: row.text, padding: [0, 0, 0, maxWidth] });
}
else {
ui.div({
text: row.left,
padding: [0, 1, 0, configIndent],
width: maxWidth,
}, { padding: [0, 0, 0, 0], text: row.text });
}
if (row.skipLine) {
ui.div({ padding: [0, 0, 0, 0], text: '' });
}
}
else {
if (isHeading(row)) {
const { level } = row;
headingLevel = level;
// only h1 and h2 have bottom padding
// h3-h6 do not
const b = level <= 2 ? 1 : 0;
ui.div({ ...row, padding: [0, 0, b, indent(level)] });
}
else {
ui.div({ ...row, padding: [0, 0, 1, indent(headingLevel + 1)] });
}
}
}
return (this.#usage = ui.toString());
}
/**
* Return the usage banner markdown for the given configuration
*/
usageMarkdown() {
if (this.#usageMarkdown)
return this.#usageMarkdown;
const out = [];
let headingLevel = 1;
const first = this.#fields[0];
let start = first?.type === 'heading' ? 1 : 0;
if (first?.type === 'heading') {
out.push(`# ${normalizeOneLine(first.text)}`);
}
out.push('Usage:');
if (this.#options.usage) {
out.push(normalizeMarkdown(this.#options.usage, true));
}
else {
const cmd = basename(process.argv[1]);
const shortFlags = [];
const shorts = [];
const flags = [];
const opts = [];
for (const [field, config] of Object.entries(this.#configSet)) {
if (config.short) {
if (config.type === 'boolean')
shortFlags.push(config.short);
else
shorts.push([config.short, config.hint || field]);
}
else {
if (config.type === 'boolean')
flags.push(field);
else
opts.push([field, config.hint || field]);
}
}
const sf = shortFlags.length ? ' -' + shortFlags.join('') : '';
const so = shorts.map(([k, v]) => ` --${k}=<${v}>`).join('');
const lf = flags.map(k => ` --${k}`).join('');
const lo = opts.map(([k, v]) => ` --${k}=<${v}>`).join('');
const usage = `${cmd}${sf}${so}${lf}${lo}`.trim();
out.push(normalizeMarkdown(usage, true));
}
const maybeDesc = this.#fields[start];
if (isDescription(maybeDesc)) {
out.push(normalizeMarkdown(maybeDesc.text, maybeDesc.pre));
start++;
}
const { rows } = this.#usageRows(start);
// heading level in markdown is number of # ahead of text
for (const row of rows) {
if (row.left) {
out.push('#'.repeat(headingLevel + 1) +
' ' +
normalizeOneLine(row.left, true));
if (row.text)
out.push(normalizeMarkdown(row.text));
}
else if (isHeading(row)) {
const { level } = row;
headingLevel = level;
out.push(`${'#'.repeat(headingLevel)} ${normalizeOneLine(row.text, row.pre)}`);
}
else {
out.push(normalizeMarkdown(row.text, !!row.pre));
}
}
return (this.#usageMarkdown = out.join('\n\n') + '\n');
}
#usageRows(start) {
// turn each config type into a row, and figure out the width of the

@@ -639,39 +750,3 @@ // left hand indentation for the option descriptions.

}
// every heading/description after the first gets indented by 2
// extra spaces.
for (const row of rows) {
if (row.left) {
// If the row is too long, don't wrap it
// Bump the right-hand side down a line to make room
const configIndent = indent(Math.max(headingLevel, 2));
if (row.left.length > maxWidth - 3) {
ui.div({ text: row.left, padding: [0, 0, 0, configIndent] });
ui.div({ text: row.text, padding: [0, 0, 0, maxWidth] });
}
else {
ui.div({
text: row.left,
padding: [0, 1, 0, configIndent],
width: maxWidth,
}, { padding: [0, 0, 0, 0], text: row.text });
}
if (row.skipLine) {
ui.div({ padding: [0, 0, 0, 0], text: '' });
}
}
else {
if (isHeading(row)) {
const { level } = row;
headingLevel = level;
// only h1 and h2 have bottom padding
// h3-h6 do not
const b = level <= 2 ? 1 : 0;
ui.div({ ...row, padding: [0, 0, b, indent(level)] });
}
else {
ui.div({ ...row, padding: [0, 0, 1, indent(headingLevel + 1)] });
}
}
}
return (this.#usage = ui.toString());
return { rows, maxWidth };
}

@@ -718,2 +793,15 @@ /**

.trim();
// normalize for markdown printing, remove leading spaces on lines
const normalizeMarkdown = (s, pre = false) => {
const n = normalize(s, pre).replace(/\\/g, '\\\\');
return pre
? `\`\`\`\n${n.replace(/\u200b/g, '')}\n\`\`\``
: n.replace(/\n +/g, '\n').trim();
};
const normalizeOneLine = (s, pre = false) => {
const n = normalize(s, pre)
.replace(/[\s\u200b]+/g, ' ')
.trim();
return pre ? `\`${n}\`` : n;
};
/**

@@ -720,0 +808,0 @@ * Main entry point. Create and return a {@link Jack} object.

{
"name": "jackspeak",
"version": "2.2.3",
"version": "2.3.0",
"description": "A very strict and proper argument parser.",

@@ -5,0 +5,0 @@ "main": "./dist/cjs/index.js",

@@ -233,2 +233,9 @@ # jackspeak

### `Jack.usageMarkdown(): string`
Returns the compiled `usage` string, with all option descriptions
and heading/description text, but as markdown instead of
formatted for a terminal, for generating HTML documentation for
your CLI.
## Some Example Code

@@ -235,0 +242,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc