Socket
Socket
Sign inDemoInstall

smartwrap

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

smartwrap - npm Package Compare versions

Comparing version 1.2.3 to 1.2.4

.huskyrc.js

103

.eslintrc.js
module.exports = {
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"double"
],
"semi": [
"error",
"never"
],
"brace-style": "error",
"space-before-blocks": "error",
"no-case-declarations": "off",
"no-trailing-spaces": "error",
"key-spacing": ["error", {
"beforeColon": false,
"afterColon": true
}],
"prefer-template": "error",
"semi-spacing": ["error", {"before": false, "after": true}],
"no-fallthrough": ["error", { "commentPattern": "caution: falls through" }]
}
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"brace-style": "error",
"comma-spacing": ["error", {"before": false, "after": true}],
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"linebreak-style": [
"error",
"unix"
],
"key-spacing": ["error", {
"beforeColon": false,
"afterColon": true
}],
"no-case-declarations": "off",
"no-trailing-spaces": "error",
"prefer-template": "error",
"semi": [
"error",
"never"
],
"semi-spacing": ["error", {"before": false, "after": true}],
"space-before-blocks": "error",
"spaced-comment": ["error", "always", { "exceptions": ["-", "+"] }],
"quotes": [
"error",
"double"
]
}
}
{
"name": "smartwrap",
"version": "1.2.3",
"version": "1.2.4",
"description": "Textwrap for javascript/nodejs. Correctly handles wide characters (宽字符) and emojis (😃). Wraps strings with option to break on words.",

@@ -26,2 +26,4 @@ "main": "src/main.js",

"breakword": "^1.0.5",
"grapheme-splitter": "^1.0.4",
"strip-ansi": "^6.0.0",
"wcwidth": "^1.0.1",

@@ -36,4 +38,5 @@ "yargs": "^15.1.0"

"grunt-mocha-test": "^0.13.3",
"husky": "^4.2.3",
"mocha": "^5.2.0"
}
}

@@ -1,17 +0,12 @@

"use strict"
const breakword = require("breakword")
const stripansi = require("strip-ansi")
const wcwidth = require("wcwidth")
let wcwidth = require("wcwidth")
let breakword = require("breakword")
function smartWrap(input, options) {
//in case a template literal was passed that has newling characters,
//split string by newlines and process each resulting string
const str = input.toString()
const strArr = str.split("\n").map( string => {
return wrap(string, options)
})
const ANSIPattern = [
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"
].join("|")
const ANSIRegex = new RegExp(ANSIPattern, "g")
return strArr.join("\n")
}
const defaults = () => {

@@ -21,21 +16,12 @@ let obj = {}

obj.breakword = false
obj.calculateSpaceRemaining = function(obj) {
return Math.max(obj.lineLength - obj.spacesUsed - obj.paddingLeft - obj.paddingRight, 0)
} //function to set starting line length
obj.currentLine = 0 //index of current line in 'lines[]'
obj.input = [] //input string split by whitespace
obj.lines = [
[]
] //assume at least one line
obj.minWidth = 2 //fallback to if width set too narrow
obj.input = [] // input string split by whitespace
obj.minWidth = 2 // fallback to if width set too narrow
obj.paddingLeft = 0
obj.paddingRight = 0
obj.errorChar = "�"
obj.returnFormat = "string" //or 'array'
obj.skipPadding = false //set to true when padding set too wide for line length
obj.spacesUsed = 0 //spaces used so far on current line
obj.splitAt = [" ","\t"]
obj.returnFormat = "string" // or 'array'
obj.skipPadding = false // set to true when padding set too wide for line length
obj.splitAt = [" ", "\t"]
obj.trim = true
obj.width = 10
obj.words = []

@@ -45,124 +31,262 @@ return obj

function wrap(text,options) {
options = options || {}
const calculateSpaceRemaining = function(lineLength, spacesUsed, config) {
return Math.max(lineLength - spacesUsed - config.paddingLeft - config.paddingRight, 0)
} // function to set starting line length
if (options.errorChar) {
const validateInput = (text, options) => {
// options validation
let config = Object.assign({}, defaults(), options || {})
if (config.errorChar) {
// only allow a single errorChar
options.errorChar = options.errorChar.split('')[0]
config.errorChar = config.errorChar.split("")[0]
// errorChar must not be wide character
if (wcwidth(options.errorChar) > 1)
throw new Error(`Error character cannot be a wide character (${options.errorChar})`)
if (wcwidth(config.errorChar) > 1)
throw new Error(`Error character cannot be a wide character (${config.errorChar})`)
}
let wrapObj = Object.assign({},defaults(),options)
//make sure correct sign on padding
wrapObj.paddingLeft = Math.abs(wrapObj.paddingLeft)
wrapObj.paddingRight = Math.abs(wrapObj.paddingRight)
// make sure correct sign on padding
config.paddingLeft = Math.abs(config.paddingLeft)
config.paddingRight = Math.abs(config.paddingRight)
wrapObj.lineLength = wrapObj.width -
wrapObj.paddingLeft -
wrapObj.paddingRight
let lineLength = config.width
- config.paddingLeft
- config.paddingRight
if(wrapObj.lineLength < wrapObj.minWidth) {
//skip padding if lineLength too narrow
wrapObj.skipPadding = true
wrapObj.lineLength = wrapObj.minWidth
if(lineLength < config.minWidth) {
// skip padding if lineLength too narrow
config.skipPadding = true
lineLength = config.minWidth
}
//Break input into array of characters split by whitespace and/or tabs
let wordArray = []
//to trim or not to trim...
let modifiedText = text.toString()
if(wrapObj.trim) {
modifiedText = modifiedText.trim()
// to trim or not to trim...
if(config.trim) {
text = text.trim()
}
if(!wrapObj.breakword){
//break string into words
if(wrapObj.splitAt.indexOf("\t")!==-1) {
//split at both spaces and tabs
wordArray = modifiedText.split(/ |\t/i)
return { text, config, lineLength }
}
const wrap = (input, options) => {
let { text, config, lineLength } = validateInput(input, options)
// array of characters split by whitespace and/or tabs
let words = []
if(!config.breakword) {
// break string into words
if(config.splitAt.indexOf("\t")!==-1) {
// split at both spaces and tabs
words = text.split(/ |\t/i)
} else{
//split at whitespace
wordArray = modifiedText.split(" ")
// split at whitespace
words = text.split(" ")
}
} else {
// do not break string into words
words = [text]
}
else {
//do not break string into words
wordArray = [modifiedText]
}
//remove empty array elements
wrapObj.words = wordArray.filter(val => {
// remove empty array elements
words = words.filter(val => {
if (val.length > 0) {
return true
return true
}
})
let spaceRemaining, splitIndex, word, wordlength
// assume at least one line
let lines = [
[]
]
while(wrapObj.words.length > 0) {
spaceRemaining = wrapObj.calculateSpaceRemaining(wrapObj)
word = wrapObj.words.shift()
let spaceRemaining, splitIndex, word
let currentLine = 0 // index of current line in 'lines[]'
let spacesUsed = 0 // spaces used so far on current line
while(words.length > 0) {
spaceRemaining = calculateSpaceRemaining(lineLength, spacesUsed, config)
word = words.shift()
let wordLength = wcwidth(word)
switch(true) {
// Too long for an empty line and is a single character
case(wrapObj.lineLength < wordLength && [...word].length === 1):
wrapObj.words.unshift(wrapObj.errorChar)
break
// Too long for an empty line, must be broken between 2 lines
case(wrapObj.lineLength < wordLength):
//Break it, then re-insert its parts into wrapObj.words
//so can loop back to re-handle each word
splitIndex = breakword(word,wrapObj.lineLength)
// too long for an empty line and is a single character
case(lineLength < wordLength && [...word].length === 1):
words.unshift(config.errorChar)
break
// too long for an empty line, must be broken between 2 lines
case(lineLength < wordLength):
// break it, then re-insert its parts into words
// so can loop back to re-handle each word
splitIndex = breakword(word, lineLength)
let splitWord = [...word]
wrapObj.words.unshift(splitWord.slice(0, splitIndex + 1).join(""))
wrapObj.words.splice(1,0,splitWord.slice(splitIndex + 1).join("")) //+1 for substr fn
words.unshift(splitWord.slice(0, splitIndex + 1).join(""))
words.splice(1, 0, splitWord.slice(splitIndex + 1).join("")) // +1 for substr fn
break
// Not enough space remaining in line, must be wrapped to next line
// not enough space remaining in line, must be wrapped to next line
case(spaceRemaining < wordLength):
//add a new line to our array of lines
wrapObj.lines.push([])
//note carriage to new line in counter
wrapObj.currentLine++
//reset the spacesUsed to 0
wrapObj.spacesUsed = 0
// add a new line to our array of lines
lines.push([])
// note carriage to new line in counter
currentLine++
// reset the spacesUsed to 0
spacesUsed = 0
/* falls through */
// Fits on current line
// fits on current line
// eslint-disable-next-line
default:
//add word to line
wrapObj.lines[wrapObj.currentLine].push(word)
//reduce space remaining (add a space between words)
wrapObj.spacesUsed += wordLength + 1
// add word to line
lines[currentLine].push(word)
// reduce space remaining (add a space between words)
spacesUsed += wordLength + 1
}
}
if(wrapObj.returnFormat === "array") {
return wrapObj.lines
} else{
let lines = wrapObj.lines.map(function(line) {
//restore spaces to line
line = line.join(" ")
//add padding to ends of line
if(!wrapObj.skipPadding) {
line = Array(wrapObj.paddingLeft+1).join(" ") +
line +
Array(wrapObj.paddingRight+1).join(" ")
}
return line
lines = lines.map( line => {
// restore spaces to line
line = line.join(" ")
// add padding to ends of line
if(!config.skipPadding) {
line = Array(config.paddingLeft + 1).join(" ")
+ line
+ Array(config.paddingRight + 1).join(" ")
}
return line
})
return lines.join("\n")
}
const splitAnsiInput = (text) => {
// get start and end positions for matches
let matches = []
let textArr = [...text]
/* eslint-disable */
while((result = ANSIRegex.exec(text)) !== null) {
matches.push({
start: result.index,
end: result.index + result[0].length,
match: result[0],
length: result[0].length
})
//return as string
return lines.join("\n")
}
/* eslint-enable */
if (matches.length < 1) return [] // we have no ANSI escapes, we're done here
// add start and end positions for non matches
matches = matches.reduce((prev, curr) => {
// check if space exists between this and last match
// get end of previous match
let prevEnd = prev[prev.length -1]
if (prevEnd.end < curr.start) {
// insert placeholder
prev.push({
start: prevEnd.end,
end: curr.start,
length: curr.start - prevEnd.end,
expand: true
}, curr)
} else {
prev.push(curr)
}
return prev
}, [{start: 0, end: 0}])
.splice(1) // removes starting accumulator object
// add trailing match if necessary
let lastMatchEnd = matches[matches.length - 1].end
if (lastMatchEnd < textArr.length - 1) {
matches.push({
start: lastMatchEnd,
end: textArr.length,
expand: true
})
}
let savedArr = matches.map(match => {
let value = text.substring(match.start, match.end)
return (match.expand) ? [...value] : [value]
}).flat(2)
return savedArr
}
module.exports = smartWrap
const restoreANSI = (savedArr, processedArr) => {
return processedArr.map((char) => {
let result
if (char === "\n") {
result = [char]
} else {
// add everything saved before character match
let splicePoint = savedArr.findIndex(element => element === char ) + 1
result = savedArr.splice(0, splicePoint)
}
// add all following, consecutive closing tags in case linebreak inerted next
const ANSIClosePattern = "^\\x1b\\[([0-9]+)*m"
const ANSICloseRegex = new RegExp(ANSIClosePattern) // eslint-disable-line no-control-regex
const closeCodes = ["0", "21", "22", "23", "24", "25", "27", "28", "29", "39", "49", "54", "55"]
let match
while (savedArr.length && (match = savedArr[0].match(ANSICloseRegex))) {
if (!closeCodes.includes(match[1])) break
result.push(savedArr.shift())
}
return result.join("")
}).concat(savedArr)
}
module.exports = (input, options) => {
// in case a template literal was passed that has newling characters,
// split string by newlines and process each resulting string
let str = input.toString()
// save input ANSI escape codes to be restored later
const savedANSI = splitAnsiInput(str)
// strip ANSI
str = stripansi(str)
// convert input to array, each element a line
const linesArr = str.split("\n").map( string => {
return wrap(string, options)
})
// return linesArr.join("\n")
// --- following code re-applies ANSI
// convert line arrays to a single string broken by return characters
const wrappedStr = linesArr.join("\n")
// and an ANSI closing character, so style never bleeds
// const wrappedStr = linesArr.join(`\u001b\[0m\n`)
// break that line into single characters
let wrappedArr = [...wrappedStr]
// restore input ANSI escape codes if needed
wrappedArr = (savedANSI.length > 0) ? restoreANSI(savedANSI, wrappedArr) : wrappedArr
return wrappedArr.join("")
}

@@ -15,3 +15,3 @@ #!/usr/bin/env node

yargs.option("minWidth", {
choices: [1,2],
choices: [1, 2],
default: 2,

@@ -29,3 +29,3 @@ describe: "Never change this unless you are certin you are not using wide characters and you want a column 1 space wide, then change to 1"

yargs.option("splitAt", {
default: [" ","\t"],
default: [" ", "\t"],
describe: "Characters at which to split input"

@@ -50,3 +50,3 @@ })

//create options object
// create options object
let options = {};

@@ -69,7 +69,7 @@ [

process.stdin.on("data", function(chunk) {
let out = Smartwrap(chunk,options)
let out = Smartwrap(chunk, options)
console.log(out)
})
//yargs = yargs('h').argv;
// yargs = yargs('h').argv;
yargs.argv = yargs.help("h").argv
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