lorem-ipsum
Version:
Generates lorem ipsum placeholder text for Node.js, Deno, browsers, and React Native. Developed with Bun.
388 lines (375 loc) • 9.47 kB
JavaScript
#!/usr/bin/env node
// src/cli.js
import { exec } from "node:child_process";
import { readFileSync, realpathSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
// src/constants.js
var FORMAT_HTML = "html";
var FORMAT_PLAIN = "plain";
var FORMATS = [FORMAT_HTML, FORMAT_PLAIN];
var UNIT_WORDS = "words";
var UNIT_WORD = "word";
var UNIT_SENTENCES = "sentences";
var UNIT_SENTENCE = "sentence";
var UNIT_PARAGRAPHS = "paragraphs";
var UNIT_PARAGRAPH = "paragraph";
var LINE_ENDINGS = {
POSIX: `
`,
WIN32: `\r
`
};
var PLATFORMS = {
DARWIN: "darwin",
LINUX: "linux",
WIN32: "win32"
};
var COPY_COMMANDS = {
DARWIN: "pbcopy",
LINUX: "xclip -selection clipboard",
WIN32: "clip"
};
var REGEX = {
UNITS: /^(paragraphs|paragraph|sentences|sentence|words|word)$/i
};
// src/words.js
var WORDS = [
"ad",
"adipisicing",
"aliqua",
"aliquip",
"amet",
"anim",
"aute",
"cillum",
"commodo",
"consectetur",
"consequat",
"culpa",
"cupidatat",
"deserunt",
"do",
"dolor",
"dolore",
"duis",
"ea",
"eiusmod",
"elit",
"enim",
"esse",
"est",
"et",
"eu",
"ex",
"excepteur",
"exercitation",
"fugiat",
"id",
"in",
"incididunt",
"ipsum",
"irure",
"labore",
"laboris",
"laborum",
"Lorem",
"magna",
"minim",
"mollit",
"nisi",
"non",
"nostrud",
"nulla",
"occaecat",
"officia",
"pariatur",
"proident",
"qui",
"quis",
"reprehenderit",
"sint",
"sit",
"sunt",
"tempor",
"ullamco",
"ut",
"velit",
"veniam",
"voluptate"
];
// src/generator.js
var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
var makeArrayOfLength = (length = 0) => Array.from({ length }, (_, index) => index);
class Generator {
constructor({
sentencesPerParagraph = { max: 7, min: 3 },
wordsPerSentence = { max: 15, min: 5 },
random,
words = WORDS
} = {}) {
if (sentencesPerParagraph.min > sentencesPerParagraph.max) {
throw new Error(`Minimum number of sentences per paragraph (${sentencesPerParagraph.min}) cannot exceed maximum (${sentencesPerParagraph.max}).`);
}
if (wordsPerSentence.min > wordsPerSentence.max) {
throw new Error(`Minimum number of words per sentence (${wordsPerSentence.min}) cannot exceed maximum (${wordsPerSentence.max}).`);
}
this.sentencesPerParagraph = sentencesPerParagraph;
this.words = words;
this.wordsPerSentence = wordsPerSentence;
this.random = random || Math.random;
}
generateRandomInteger(min, max) {
return Math.floor(this.random() * (max - min + 1) + min);
}
generateRandomWords(num) {
const { min, max } = this.wordsPerSentence;
const length = num || this.generateRandomInteger(min, max);
return makeArrayOfLength(length).reduce((accumulator) => `${this.pluckRandomWord()} ${accumulator}`, "").trim();
}
generateRandomSentence(num) {
return `${capitalize(this.generateRandomWords(num))}.`;
}
generateRandomParagraph(num) {
const { min, max } = this.sentencesPerParagraph;
const length = num || this.generateRandomInteger(min, max);
return makeArrayOfLength(length).reduce((accumulator) => `${this.generateRandomSentence()} ${accumulator}`, "").trim();
}
pluckRandomWord() {
const index = this.generateRandomInteger(0, this.words.length - 1);
return this.words[index];
}
}
var generator_default = Generator;
// src/lorem-ipsum.js
var isReactNative = () => {
try {
return navigator.product === "ReactNative";
} catch {
return false;
}
};
var isNodeCompatible = () => typeof process !== "undefined" && typeof process.platform === "string";
var isWindows = () => {
try {
return process.platform === "win32";
} catch {
return false;
}
};
class LoremIpsum {
constructor(options = {}, format = FORMAT_PLAIN, suffix) {
if (FORMATS.indexOf(format.toLowerCase()) === -1) {
throw new Error(`${format} is an invalid format. Please use ${FORMATS.join(" or ")}.`);
}
this.format = format;
this.generator = new generator_default(options);
this.suffix = suffix;
}
getLineEnding() {
if (this.suffix) {
return this.suffix;
}
if (!isReactNative() && isNodeCompatible() && isWindows()) {
return LINE_ENDINGS.WIN32;
}
return LINE_ENDINGS.POSIX;
}
formatString(value) {
if (this.format === FORMAT_HTML) {
return `<p>${value}</p>`;
}
return value;
}
formatStrings(values) {
return values.map((value) => this.formatString(value));
}
generateWords(num) {
return this.formatString(this.generator.generateRandomWords(num));
}
generateSentences(num) {
return this.formatString(this.generator.generateRandomParagraph(num));
}
generateParagraphs(num) {
return this.formatStrings(Array.from({ length: num }, () => this.generator.generateRandomParagraph())).join(this.getLineEnding());
}
}
var lorem_ipsum_default = LoremIpsum;
// src/index.js
var loremIpsum = ({
count = 1,
format = FORMAT_PLAIN,
paragraphLowerBound = 3,
paragraphUpperBound = 7,
random,
sentenceLowerBound = 5,
sentenceUpperBound = 15,
units = UNIT_SENTENCES,
words = WORDS,
suffix = ""
} = {}) => {
const lorem = new lorem_ipsum_default({
random,
sentencesPerParagraph: {
max: paragraphUpperBound,
min: paragraphLowerBound
},
words,
wordsPerSentence: {
max: sentenceUpperBound,
min: sentenceLowerBound
}
}, format, suffix);
switch (units) {
case UNIT_PARAGRAPHS:
case UNIT_PARAGRAPH:
return lorem.generateParagraphs(count);
case UNIT_SENTENCES:
case UNIT_SENTENCE:
return lorem.generateSentences(count);
case UNIT_WORDS:
case UNIT_WORD:
return lorem.generateWords(count);
default:
return "";
}
};
// src/cli.js
var DESCRIPTION = "Generates one or more words|sentences|paragraphs";
var USAGE = "3 words [options]";
var HELP = `${DESCRIPTION}
Usage:
lorem-ipsum ${USAGE}
Arguments:
count The number of units
units Words, sentences, or paragraphs
Options:
-c, --copy Copy output to the clipboard
-f, --format Output format: ${FORMATS.join(", ")}
-h, --help Display help
-v, --version Display version
`;
var getVersion = () => {
const root = dirname(dirname(fileURLToPath(import.meta.url)));
return JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version;
};
var getCopyCommand = (platform = "") => {
switch (platform.toLowerCase()) {
case PLATFORMS.DARWIN:
return COPY_COMMANDS.DARWIN;
case PLATFORMS.WIN32:
return COPY_COMMANDS.WIN32;
case PLATFORMS.LINUX:
default:
return COPY_COMMANDS.LINUX;
}
};
var shellQuote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
var copyToClipboard = (text, run = exec) => new Promise((resolve, reject) => {
const platform = process.platform;
if (Object.values(PLATFORMS).indexOf(platform) === -1) {
reject(new Error(`Copy is not supported for ${platform}`));
return;
}
run(`printf %s ${shellQuote(text)} | ${getCopyCommand(platform)}`, (error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
if (stderr) {
reject(new Error(stderr));
return;
}
resolve(text);
});
});
var parseArgs = (args) => {
const options = {
copy: false,
format: FORMAT_PLAIN,
help: false,
values: [],
version: false
};
for (let index = 0;index < args.length; index += 1) {
const arg = args[index];
switch (arg) {
case "-c":
case "--copy":
options.copy = true;
break;
case "-f":
case "--format":
options.format = args[index + 1];
index += 1;
break;
case "-h":
case "--help":
options.help = true;
break;
case "-v":
case "--version":
options.version = true;
break;
default:
options.values.push(arg);
break;
}
}
return options;
};
var run = async (args = process.argv.slice(2)) => {
const options = parseArgs(args);
if (options.version) {
console.log(getVersion());
return;
}
if (options.help) {
console.log(HELP);
return;
}
if (FORMATS.indexOf(options.format) === -1) {
console.error(`${options.format} is not valid. Choose from ${FORMATS.join(" or ")}.`);
process.exit(1);
}
const num = options.values[0] || "1";
const units = options.values[1] || "sentence";
if (REGEX.UNITS.test(units) === false) {
console.error(`${units} is not valid. Choose from paragraph(s), sentence(s), or word(s).`);
process.exit(1);
}
const count = parseInt(num, 10);
if (!count || count < 1) {
console.error(`${num} is not valid. Choose a number greater than 0.`);
process.exit(1);
}
const output = loremIpsum({ count, format: options.format, units });
console.log(output);
if (options.copy === true) {
try {
await copyToClipboard(output);
console.log("");
console.log("copied");
} catch (error) {
console.log(error.message);
}
}
};
var isMain = () => {
try {
return process.argv[1] && realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
} catch {
return false;
}
};
if (isMain()) {
run().catch((error) => {
console.error(error.message);
process.exit(1);
});
}
export {
run,
parseArgs,
copyToClipboard
};