diff options
| author | netop://ウィビ <paul@webb.page> | 2026-04-11 14:39:25 -0700 |
|---|---|---|
| committer | netop://ウィビ <paul@webb.page> | 2026-04-11 14:39:25 -0700 |
| commit | db69b25e856fb448a2118d3adffc33c1e41bab0f (patch) | |
| tree | c420967b7977e858c9003766210c6128546606cf | |
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | LICENSE | 21 | ||||
| -rw-r--r-- | README.md | 7 | ||||
| -rw-r--r-- | jsr.json | 6 | ||||
| -rw-r--r-- | mod.ts | 122 |
5 files changed, 158 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fafff2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +Thumbs.db @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 netop://ウィビ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ce09f4b --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# dedent + +The [original dedent repo](https://github.com/dmnd/dedent) has too many files and I already use this all-in-one file across my projects. + +Might as well make it a module. + +Find it on [JSR](https://jsr.io/@netopwibby/dedent)! diff --git a/jsr.json b/jsr.json new file mode 100644 index 0000000..bf2540d --- /dev/null +++ b/jsr.json @@ -0,0 +1,6 @@ +{ + "exports": "./mod.ts", + "license": "MIT", + "name": "@netopwibby/dedent", + "version": "0.1.0" +} @@ -0,0 +1,122 @@ +/* + * SPDX-License-Identifier: MIT + * Copyright © 2015-2025 Desmond Brand + * Copyright © 2026 Paul Anthony Webb (modifications) + * + * Dap is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. + */ + +/*** UTILITY ------------------------------------------ ***/ + +const dedent: Dedent = createDedent({}); + +interface Dedent { + (literals: string): string; + (strings: TemplateStringsArray, ...values: unknown[]): string; + withOptions: CreateDedent; +} + +interface DedentOptions { + alignValues?: boolean; + escapeSpecialCharacters?: boolean; + trimWhitespace?: boolean; +} + +type CreateDedent = (options: DedentOptions) => Dedent; + +/*** EXPORT ------------------------------------------- ***/ + +export default dedent; + +/*** PROGRAM ------------------------------------------ ***/ + +function createDedent(options: DedentOptions): Dedent { + function dedent(literals: string): string; + function dedent(strings: TemplateStringsArray, ...values: unknown[]): string; + + function dedent(strings: TemplateStringsArray | string, ...values: unknown[]) { + const raw = typeof strings === "string" ? + [strings] : + strings.raw; + + const { + alignValues = false, + escapeSpecialCharacters = Array.isArray(strings), + trimWhitespace = true + } = options; + + let result = ""; + + for (let i = 0; i < raw.length; i++) { + let next = raw[i]; + + if (escapeSpecialCharacters) { + next = next + .replace(/\\\n[ \t]*/g, "") + .replace(/\\`/g, "`") + .replace(/\\\$/g, "$") + .replace(/\\\{/g, "{"); + } + + result += next; + + if (i < values.length) { + result += alignValues ? + alignValue(values[i], result) : + values[i]; + } + } + + const lines = result.split("\n"); + let minIndent: number | null = null; + + for (const line of lines) { + const match = line.match(/^(\s+)\S+/); + + if (match) { + const indent = match[1].length; + + minIndent = minIndent === null ? + indent : + Math.min(minIndent, indent); + } + } + + if (minIndent !== null) { + result = lines + .map((line) => (line[0] === " " || line[0] === "\t" ? line.slice(minIndent) : line)) + .join("\n"); + } + + if (trimWhitespace) + result = result.trim(); + + if (escapeSpecialCharacters) + result = result.replace(/\\n/g, "\n"); + + return result; + } + + dedent.withOptions = (newOptions: DedentOptions): Dedent => createDedent({ ...options, ...newOptions }); + + return dedent; +} + +/*** HELPER ------------------------------------------- ***/ + +function alignValue(value: unknown, precedingText: string) { + if (typeof value !== "string" || !value.includes("\n")) + return value; + + const currentLine = precedingText.slice(precedingText.lastIndexOf("\n") + 1); + const indentMatch = currentLine.match(/^(\s+)/); + + if (indentMatch) + return value.replace(/\n/g, `\n${indentMatch[1]}`); + + return value; +} + +/*** adapted from https://github.com/dmnd/dedent ***/ |
