Socket
Socket
Sign inDemoInstall

retext-smartypants

Package Overview
Dependencies
15
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 5.1.0 to 5.2.0

21

index.d.ts

@@ -19,2 +19,15 @@ /**

/**
* Quote characters.
*/
export type QuoteCharacterMap = {
/**
* Character to use for double quotes.
*/
double: string
/**
* Character to use for single quotes.
*/
single: string
}
/**
* Configuration.

@@ -31,2 +44,10 @@ */

/**
* Characters to use for opening double and single quotes.
*/
openingQuotes?: QuoteCharacterMap | undefined
/**
* Characters to use for closing double and single quotes.
*/
closingQuotes?: QuoteCharacterMap | undefined
/**
* Create smart ellipses.

@@ -33,0 +54,0 @@ *

385

index.js

@@ -9,2 +9,9 @@ /**

*
* @typedef QuoteCharacterMap
* Quote characters.
* @property {string} double
* Character to use for double quotes.
* @property {string} single
* Character to use for single quotes.
*
* @typedef Options

@@ -17,2 +24,6 @@ * Configuration.

* quotes.
* @property {QuoteCharacterMap} [openingQuotes]
* Characters to use for opening double and single quotes.
* @property {QuoteCharacterMap} [closingQuotes]
* Characters to use for closing double and single quotes.
* @property {boolean} [ellipses=true]

@@ -55,205 +66,219 @@ * Create smart ellipses.

const closingQuotes = {'"': '”', "'": '’'}
const openingQuotes = {'"': '“', "'": '‘'}
const defaultClosingQuotes = {'"': '”', "'": '’'}
const defaultOpeningQuotes = {'"': '“', "'": '‘'}
const educators = {
dashes: {
/**
* Transform two dahes into an em-dash.
*
* @type {Method}
*/
true(node) {
if (node.value === '--') {
node.value = '—'
/**
* @param {Options} options
*/
function createEducators(options) {
const closingQuotes = options.closingQuotes
? {'"': options.closingQuotes.double, "'": options.closingQuotes.single}
: defaultClosingQuotes
const openingQuotes = options.openingQuotes
? {'"': options.openingQuotes.double, "'": options.openingQuotes.single}
: defaultOpeningQuotes
const educators = {
dashes: {
/**
* Transform two dahes into an em-dash.
*
* @type {Method}
*/
true(node) {
if (node.value === '--') {
node.value = '—'
}
},
/**
* Transform three dahes into an em-dash, and two into an en-dash.
*
* @type {Method}
*/
oldschool(node) {
if (node.value === '---') {
node.value = '—'
} else if (node.value === '--') {
node.value = '–'
}
},
/**
* Transform three dahes into an en-dash, and two into an em-dash.
*
* @type {Method}
*/
inverted(node) {
if (node.value === '---') {
node.value = '–'
} else if (node.value === '--') {
node.value = '—'
}
}
},
/**
* Transform three dahes into an em-dash, and two into an en-dash.
*
* @type {Method}
*/
oldschool(node) {
if (node.value === '---') {
node.value = '—'
} else if (node.value === '--') {
node.value = '–'
backticks: {
/**
* Transform double backticks and single quotes into smart quotes.
*
* @type {Method}
*/
true(node) {
if (node.value === '``') {
node.value = '“'
} else if (node.value === "''") {
node.value = '”'
}
},
/**
* Transform single and double backticks and single quotes into smart quotes.
*
* @type {Method}
*/
all(node, index, parent) {
educators.backticks.true(node, index, parent)
if (node.value === '`') {
node.value = '‘'
} else if (node.value === "'") {
node.value = '’'
}
}
},
/**
* Transform three dahes into an en-dash, and two into an em-dash.
*
* @type {Method}
*/
inverted(node) {
if (node.value === '---') {
node.value = '–'
} else if (node.value === '--') {
node.value = '—'
}
}
},
backticks: {
/**
* Transform double backticks and single quotes into smart quotes.
*
* @type {Method}
*/
true(node) {
if (node.value === '``') {
node.value = '“'
} else if (node.value === "''") {
node.value = '”'
}
},
/**
* Transform single and double backticks and single quotes into smart quotes.
*
* @type {Method}
*/
all(node, index, parent) {
educators.backticks.true(node, index, parent)
ellipses: {
/**
* Transform multiple dots into unicode ellipses.
*
* @type {Method}
*/
true(node, index, parent) {
const value = node.value
const siblings = parent.children
if (node.value === '`') {
node.value = '‘'
} else if (node.value === "'") {
node.value = '’'
}
}
},
ellipses: {
/**
* Transform multiple dots into unicode ellipses.
*
* @type {Method}
*/
true(node, index, parent) {
const value = node.value
const siblings = parent.children
// Simple node with three dots and without white-space.
if (/^\.{3,}$/.test(node.value)) {
node.value = '…'
return
}
// Simple node with three dots and without white-space.
if (/^\.{3,}$/.test(node.value)) {
node.value = '…'
return
}
if (!/^\.+$/.test(value)) {
return
}
if (!/^\.+$/.test(value)) {
return
}
// Search for dot-nodes with white-space between.
/** @type {Array<SentenceContent>} */
const nodes = []
let position = index
let count = 1
// Search for dot-nodes with white-space between.
/** @type {SentenceContent[]} */
const nodes = []
let position = index
let count = 1
// It’s possible that the node is merged with an adjacent word-node. In that
// code, we cannot transform it because there’s no reference to the
// grandparent.
while (--position > 0) {
let sibling = siblings[position]
// It’s possible that the node is merged with an adjacent word-node. In that
// code, we cannot transform it because there’s no reference to the
// grandparent.
while (--position > 0) {
let sibling = siblings[position]
if (sibling.type !== 'WhiteSpaceNode') {
break
}
if (sibling.type !== 'WhiteSpaceNode') {
break
}
const queue = sibling
sibling = siblings[--position]
const queue = sibling
sibling = siblings[--position]
if (
sibling &&
(sibling.type === 'PunctuationNode' ||
sibling.type === 'SymbolNode') &&
/^\.+$/.test(sibling.value)
) {
nodes.push(queue, sibling)
if (
sibling &&
(sibling.type === 'PunctuationNode' ||
sibling.type === 'SymbolNode') &&
/^\.+$/.test(sibling.value)
) {
nodes.push(queue, sibling)
count++
count++
continue
}
continue
break
}
break
}
if (count < 3) {
return
}
if (count < 3) {
return
siblings.splice(index - nodes.length, nodes.length)
node.value = '…'
}
},
quotes: {
/**
* Transform straight single- and double quotes into smart quotes.
*
* @type {Method}
*/
// eslint-disable-next-line complexity
true(node, index, parent) {
const siblings = parent.children
const value = node.value
siblings.splice(index - nodes.length, nodes.length)
if (value !== '"' && value !== "'") {
return
}
node.value = '…'
}
},
quotes: {
/**
* Transform straight single- and double quotes into smart quotes.
*
* @type {Method}
*/
// eslint-disable-next-line complexity
true(node, index, parent) {
const siblings = parent.children
const value = node.value
const previous = siblings[index - 1]
const next = siblings[index + 1]
const nextNext = siblings[index + 2]
const nextValue = next && toString(next)
if (value !== '"' && value !== "'") {
return
if (
next &&
nextNext &&
(next.type === 'PunctuationNode' || next.type === 'SymbolNode') &&
nextNext.type !== 'WordNode'
) {
// Special case if the very first character is a quote followed by
// punctuation at a non-word-break. Close the quotes by brute force.
node.value = closingQuotes[value]
} else if (
nextNext &&
(nextValue === '"' || nextValue === "'") &&
nextNext.type === 'WordNode'
) {
// Special case for double sets of quotes:
// `He said, "'Quoted' words in a larger quote."`
node.value = openingQuotes[value]
// @ts-expect-error: it’s a literal.
next.value = openingQuotes[nextValue]
} else if (next && /^\d\ds$/.test(nextValue)) {
// Special case for decade abbreviations: `the '80s`
node.value = closingQuotes[value]
} else if (
previous &&
next &&
(previous.type === 'WhiteSpaceNode' ||
previous.type === 'PunctuationNode' ||
previous.type === 'SymbolNode') &&
next.type === 'WordNode'
) {
// Get most opening single quotes.
node.value = openingQuotes[value]
} else if (
previous &&
previous.type !== 'WhiteSpaceNode' &&
previous.type !== 'SymbolNode' &&
previous.type !== 'PunctuationNode'
) {
// Closing quotes.
node.value = closingQuotes[value]
} else if (
!next ||
next.type === 'WhiteSpaceNode' ||
(value === "'" && nextValue === 's')
) {
node.value = closingQuotes[value]
} else {
node.value = openingQuotes[value]
}
}
const previous = siblings[index - 1]
const next = siblings[index + 1]
const nextNext = siblings[index + 2]
const nextValue = next && toString(next)
if (
next &&
nextNext &&
(next.type === 'PunctuationNode' || next.type === 'SymbolNode') &&
nextNext.type !== 'WordNode'
) {
// Special case if the very first character is a quote followed by
// punctuation at a non-word-break. Close the quotes by brute force.
node.value = closingQuotes[value]
} else if (
nextNext &&
(nextValue === '"' || nextValue === "'") &&
nextNext.type === 'WordNode'
) {
// Special case for double sets of quotes:
// `He said, "'Quoted' words in a larger quote."`
node.value = openingQuotes[value]
// @ts-expect-error: it’s a literal.
next.value = openingQuotes[nextValue]
} else if (next && /^\d\ds$/.test(nextValue)) {
// Special case for decade abbreviations: `the '80s`
node.value = closingQuotes[value]
} else if (
previous &&
next &&
(previous.type === 'WhiteSpaceNode' ||
previous.type === 'PunctuationNode' ||
previous.type === 'SymbolNode') &&
next.type === 'WordNode'
) {
// Get most opening single quotes.
node.value = openingQuotes[value]
} else if (
previous &&
previous.type !== 'WhiteSpaceNode' &&
previous.type !== 'SymbolNode' &&
previous.type !== 'PunctuationNode'
) {
// Closing quotes.
node.value = closingQuotes[value]
} else if (
!next ||
next.type === 'WhiteSpaceNode' ||
(value === "'" && nextValue === 's')
) {
node.value = closingQuotes[value]
} else {
node.value = openingQuotes[value]
}
}
}
return educators
}

@@ -268,3 +293,3 @@

export default function retextSmartypants(options = {}) {
/** @type {Array.<Method>} */
/** @type {Array<Method>} */
const methods = []

@@ -360,2 +385,4 @@ /** @type {Options['quotes']} */

const educators = createEducators(options)
if (quotes !== false) {

@@ -362,0 +389,0 @@ methods.push(educators.quotes.true)

{
"name": "retext-smartypants",
"version": "5.1.0",
"version": "5.2.0",
"description": "retext plugin to implement SmartyPants",

@@ -51,3 +51,3 @@ "license": "MIT",

"typescript": "^4.0.0",
"xo": "^0.44.0"
"xo": "^0.50.0"
},

@@ -54,0 +54,0 @@ "scripts": {

@@ -11,11 +11,34 @@ # retext-smartypants

[**retext**][retext] plugin to implement [SmartyPants][].
**[retext][]** plugin to apply [SmartyPants][].
## Contents
* [What is this?](#what-is-this)
* [When should I use this?](#when-should-i-use-this)
* [Install](#install)
* [Use](#use)
* [API](#api)
* [`unified().use(retextSmartypants[, options])`](#unifieduseretextsmartypants-options)
* [Types](#types)
* [Compatibility](#compatibility)
* [Contribute](#contribute)
* [License](#license)
## What is this?
This package is a [unified][] ([retext][]) plugin to apply [SmartyPants][] to
the syntax tree.
It replaces straight/typewriter punctuation marks and symbols with smart/curly
marks and symbols.
## When should I use this?
You can use this plugin any time there straight marks and symbols in prose,
but you want to use smart ones instead.
## Install
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c):
Node 12+ is needed to use it and it must be `import`ed instead of `require`d.
This package is [ESM only][esm].
In Node.js (version 12.20+, 14.14+, 16.0+, or 18.0+), install with [npm][]:
[npm][]:
```sh

@@ -25,2 +48,16 @@ npm install retext-smartypants

In Deno with [`esm.sh`][esmsh]:
```js
import retextSmartypants from 'https://esm.sh/retext-smartypants@5'
```
In browsers with [`esm.sh`][esmsh]:
```html
<script type="module">
import retextSmartypants from 'https://esm.sh/retext-smartypants@5?bundle'
</script>
```
## Use

@@ -32,5 +69,5 @@

const file = retext()
const file = await retext()
.use(retextSmartypants)
.processSync('He said, "A \'simple\' english sentence. . ."')
.process('He said, "A \'simple\' english sentence. . ."')

@@ -53,7 +90,8 @@ console.log(String(file))

Replaces dumb/straight/typewriter punctuation marks with smart/curly punctuation
marks.
Apply [SmartyPants][].
##### `options`
Configuration (optional).
###### `options.quotes`

@@ -64,3 +102,13 @@

Converts straight double and single quotes to smart double or single quotes.
The options `options.openingQuotes` and `options.closingQuotes` affect which
quotes are considered smart.
###### `options.openingQuotes`
Characters to use for opening quotes `{single: '‘', double: '“'}`.
###### `options.closingQuotes`
Characters to use for closing quotes `{single: '’', double: '”'}`.
###### `options.ellipses`

@@ -70,4 +118,4 @@

Converts triple dot characters (with or without spaces between) into a single
Unicode ellipsis character
Converts triple dot characters (with or without spaces) into a single unicode
ellipsis character.

@@ -78,10 +126,10 @@ ###### `options.backticks`

When `true`, converts double back-ticks into an opening double quote, and
When `true`, converts double backticks into an opening double quote, and
double straight single quotes into a closing double quote.
When `'all'`: does the preceding and converts single back-ticks into an
opening single quote, and a straight single quote into a closing single
smart quote.
When `'all'`: does the what `true` does with the addition of converting single
backticks into an opening single quote, and a straight single quote into a
closing single smart quote.
> **Note**: Quotes can not be `true` when `backticks` is `'all'`;
> 👉 **Note**: `options.quotes` can not be `true` when `backticks` is `'all'`.

@@ -92,10 +140,22 @@ ###### `options.dashes`

When `true`, converts two dashes into an em-dash character.
When `true`, converts two dashes into an em dash character.
When `'oldschool'`, converts two dashes into an en-dash, and three dashes into
an em-dash.
When `'oldschool'`, converts two dashes into an en dash, and three dashes into
an em dash.
When `'inverted'`, converts two dashes into an em-dash, and three dashes into
an en-dash.
When `'inverted'`, converts two dashes into an em dash, and three dashes into
an en dash.
## Types
This package is fully typed with [TypeScript][].
It exports the additional types `Options` and `QuoteCharacterMap`.
## Compatibility
Projects maintained by the unified collective are compatible with all maintained
versions of Node.js.
As of now, that is Node.js 12.20+, 14.14+, 16.0+, and 18.0+.
Our projects sometimes work with older versions, but this is not guaranteed.
## Contribute

@@ -145,9 +205,15 @@

[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[esmsh]: https://esm.sh
[typescript]: https://www.typescriptlang.org
[health]: https://github.com/retextjs/.github
[contributing]: https://github.com/retextjs/.github/blob/HEAD/contributing.md
[contributing]: https://github.com/retextjs/.github/blob/main/contributing.md
[support]: https://github.com/retextjs/.github/blob/HEAD/support.md
[support]: https://github.com/retextjs/.github/blob/main/support.md
[coc]: https://github.com/retextjs/.github/blob/HEAD/code-of-conduct.md
[coc]: https://github.com/retextjs/.github/blob/main/code-of-conduct.md

@@ -158,4 +224,6 @@ [license]: license

[unified]: https://github.com/unifiedjs/unified
[retext]: https://github.com/retextjs/retext
[smartypants]: https://daringfireball.net/projects/smartypants
SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc