mrkeyoor.com_
Sat 08 Aug 20:59 UTC
npmUtilsupdated 08 Aug 2026

strip-outer

strip-outer is a dependency-free JavaScript function that removes one exact substring from the start of a string and then one exact copy from the end. If only one side matches, it removes that side; if neither matches, it returns the original string. Matching is case-sensitive and uses startsWith and endsWith, so the argument is a literal substring rather than a regular expression or set of trim characters. Version 2 is an ESM-only package for Node 12.20 or newer lines.

Verdict

Do not add strip-outer to a new project for a handful of calls; the local implementation is clearer, typeable, and lets you fix the empty-substring case. Keep it only where its exact one-per-side ESM behavior is already part of the contract.

API stability4/5The package exports one default function with two string parameters, and its literal prefix-then-suffix behavior is easy to inspect. Version 2's switch to ESM was a real breaking change from older CommonJS usage, and the empty-substring result is an awkward observable edge case. With 2.0.0 pinned, the tiny frozen implementation is otherwise unlikely to surprise existing callers.
Docs2/5The README gives the install command and two examples showing removal from both sides and from only the start. It does not document ESM-only loading, supported Node engines, thrown TypeError behavior, case sensitivity, one-copy semantics, overlapping matches, TypeScript absence, or the destructive empty-substring edge case visible in the seven-line source.
Maintenance1/5Version 2.0.0 was published August 19, 2021 and the GitHub repository's last push was July 9, 2022. The project is not archived, has no open issues or pull requests, and its behavior may need little change, but 23 stars and no release activity for roughly five years provide no evidence of active maintenance or current-runtime testing.
Ecosystem2/5The function is dependency-free, tree-shakeable ESM and usable in modern Node or browser bundles, while the author's package portfolio makes it a common transitive utility. It has no types, CommonJS export, integrations, extension points, or related tooling. Millions of weekly downloads are likely dependency-tree reuse rather than an ecosystem built around this single string operation.

Use it if

  • You repeatedly remove one known multi-character wrapper from both ends and want that intent named
  • Your project already uses ESM and supports the Node versions declared by version 2
  • You specifically want at most one removal per side, with literal case-sensitive matching
  • You inherited the package transitively or directly and want to preserve its exact behavior during a focused refactor
Skip it if

Setup reality

npm install strip-outer adds no transitive dependencies, but version 2.0.0 is an ESM-only package with type: module and an exports entry pointing to index.js. Use import stripOuter from 'strip-outer'; require('strip-outer') in a CommonJS file will not work without an asynchronous import bridge or a build step. The declared engines are Node 12.20, Node 14.13.1 or newer 14.x, or Node 16 and newer. The package includes no TypeScript declarations, so strict TypeScript projects may need a local one-line module declaration or should simply inline a typed helper. Both arguments must be primitive strings; any other type throws TypeError with the generic message Expected a string. Matching is literal, case-sensitive, and performed in sequence: first startsWith, then endsWith on the already shortened value. It removes one copy from each side, not all copies. That order matters for short overlapping strings. The most important edge case is an empty substring. Every string starts and ends with an empty string; the prefix slice changes nothing, then slice(0, -substring.length) receives negative zero, which behaves as zero, and returns an empty string. Guard substring.length before calling if empty input is possible. A substring equal to the full input returns an empty string normally. There is no Unicode normalization or locale-aware comparison, though slicing by substring.length is correct when startsWith or endsWith matched the same UTF-16 sequence. The package's last release was August 2021 and last repository push was July 2022. Its implementation is seven executable lines, so vendoring or writing a typed local function is usually a better maintenance trade for new projects.

Patterns

Remove one matching wrapper from both sidesstrip-both-sides

import stripOuter from 'strip-outer'

const value = stripOuter('foobarfoo', 'foo')
// 'bar'

Matching is literal and case-sensitive, and only one copy is removed from each side.

Remove the wrapper when only the start matchesstrip-prefix-only

const value = stripOuter('unicorncake', 'unicorn')
// 'cake'

The same function also checks the shortened result's end, but cake does not end with unicorn.

Remove the wrapper when only the end matchesstrip-suffix-only

const value = stripOuter('unicorncake', 'cake')
// 'unicorn'

This is exact substring removal, not trimming any of the characters c, a, k, and e.

Remove one pair of matching quote tokensunwrap-quoted-token

const unquoted = stripOuter('\"draft\"', '\"')
// 'draft'

It does not parse escapes or require both sides to match; a lone opening quote is removed too.

Remove one leading and trailing separatorstrip-path-separators

const segment = stripOuter('/api/users/', '/')
// 'api/users'

Repeated slashes remain because only one slash per side is removed. Do not treat this as path normalization.

Reject an empty wrapper before callingguard-empty-substring

function safeStripOuter(value, wrapper) {
  if (wrapper.length === 0) return value
  return stripOuter(value, wrapper)
}

safeStripOuter('keep me', '')
// 'keep me'

Without this guard, version 2.0.0 returns an empty string for a non-empty value and empty wrapper.

Repeat explicitly when every layer must goremove-repeated-wrappers

function stripAllOuter(value, wrapper) {
  if (wrapper.length === 0) return value
  let next = stripOuter(value, wrapper)
  while (next !== value) {
    value = next
    next = stripOuter(value, wrapper)
  }
  return value
}

stripAllOuter('foofoobarfoofoo', 'foo')
// 'bar'

The package itself removes one layer only; the empty-wrapper guard also prevents a bad or non-terminating helper.

Leave a non-matching string unchangedpreserve-nonmatching-input

const input = 'Hello'
const output = stripOuter(input, 'hello')
console.log(output === input)
// true

Matching uses startsWith and endsWith without case folding or Unicode normalization.

Alternatives

PackageRegistryPick it when
lodashnpmYou already depend on Lodash and want trim, trimStart, or trimEnd by a set of characters rather than one exact substring
trimnpmYou support old JavaScript environments and only need whitespace trimming rather than wrapper removal
escape-string-regexpnpmYou need to build a safe anchored regular expression for repeated or more flexible literal-wrapper removal