diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..dc53fe8 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: Test +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Biome + uses: biomejs/setup-biome@v2 + with: + version: "1.8.3" + + - name: Lint and formatting + run: biome ci . + + - name: Set up Deno + uses: denoland/setup-deno@v1 + with: + deno-version: 1.x + + - name: Run tests + run: deno test diff --git a/.gitignore b/.gitignore index b511493..7d427b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ dist node_modules -.vscode package-lock.json yarn.lock diff --git a/.npmignore b/.npmignore deleted file mode 100644 index f069d9c..0000000 --- a/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -.babelrc -.eslintrc -src -tests -text-clipper.sublime-* -yarn.lock diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index b8834a8..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -12.16.3 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index cdcdfc9..0000000 --- a/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: node_js -node_js: - - "12" diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..3bb06c3 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["biomejs.biome", "denoland.vscode-deno"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..84a4199 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "deno.enable": true, + "editor.defaultFormatter": "biomejs.biome" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 0824db3..30d30ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## 3.0.0 + +- `text-clipper` has become a Deno-first library and is now available on [Jsr.io](https://jsr.io). + Instructions for installation on Node.js/Bun are still included. + ## 2.2.0 - Implement #14: Add `stripTags` option. diff --git a/LICENSE b/LICENSE index ca53944..533b291 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Arend van Beelen jr., Speakap B.V. +Copyright (c) 2016-2024 Arend van Beelen jr., Speakap B.V. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f86ab8b..666ad11 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,6 @@ Fast and correct clip functions for HTML and plain text. -[![Build Status](https://travis-ci.org/arendjr/text-clipper.svg?branch=master)](https://travis-ci.org/arendjr/text-clipper) -[![text-clipper on NPM](https://img.shields.io/npm/v/text-clipper.svg)](https://www.npmjs.com/package/text-clipper) - ## Why use text-clipper? text-clipper offers the following advantages over similar libraries that allow clipping HTML: @@ -22,43 +19,46 @@ text-clipper offers the following advantages over similar libraries that allow c ## Usage -### Node.js +### Deno -First install the `text-clipper` package: +First install the package: ```sh -$ yarn add text-clipper # or: npm install --save text-clipper +$ deno add @arendjr/text-clipper ``` -If compatibility with Internet Explorer is required, make sure you have a polyfill for -`Array.prototype.includes()`. - Once installed, you can use it as follows: ```js -import clip from "text-clipper"; // or: const clip = require("text-clipper").default; +import clip from "@arendjr/text-clipper"; const clippedString = clip(string, 80); // returns a string of at most 80 characters const clippedHtml = clip(htmlString, 140, { html: true, maxLines: 5 }); ``` -### Deno +### Bun -When using Deno, you can import right away: +Install using the following command instead: -```js -import clip from "https://raw.githubusercontent.com/arendjr/text-clipper/master/mod.ts"; +```sh +$ bunx jsr add @arendjr/text-clipper ``` -And use it like this: +For usage instructions, see above. -```js -const clippedString = clip(string, 80); // returns a string of at most 80 characters +### Node.js -const clippedHtml = clip(htmlString, 140, { html: true, maxLines: 5 }); +Install using one of the following commands, depending on your package manager: + +```sh +$ npx jsr add @arendjr/text-clipper # If using NPM +$ yarn dlx jsr add @arendjr/text-clipper # If using Yarn +$ pnpm dlx jsr add @arendjr/text-clipper # If using PNPM ``` +For usage instructions, see above. + ## Options ### breakWords @@ -107,3 +107,13 @@ clip(input, 140, { html: true, stripTags: ["img", "svg"] }); ``` Tag names must be specified in lowercase. + +## Changelog + +See [CHANGELOG.md](CHANGELOG.md). + +## License + +Licensed under the MIT License. + +See [LICENSE](LICENSE). diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..f6946d7 --- /dev/null +++ b/deno.json @@ -0,0 +1,12 @@ +{ + "name": "@arendjr/text-clipper", + "version": "3.0.0", + "compilerOptions": { + "allowJs": true, + "strict": true + }, + "lock": false, + "publish": { + "include": ["src"] + } +} diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 7f7b484..0000000 --- a/jest.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - preset: "ts-jest", - testEnvironment: "node", -}; diff --git a/mod.ts b/mod.ts deleted file mode 100644 index 65362ff..0000000 --- a/mod.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./src/index.ts"; diff --git a/package.json b/package.json deleted file mode 100644 index 7c72fca..0000000 --- a/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "text-clipper", - "version": "2.2.0", - "description": "Fast and correct clip functions for HTML and plain text.", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup", - "lint": "biome check", - "prepublish": "yarn build", - "test": "jest" - }, - "author": "Speakap B.V.", - "repository": { - "type": "git", - "url": "https://github.com/arendjr/text-clipper.git" - }, - "files": ["dist"], - "keywords": ["clip", "html", "string", "text", "trim", "truncate"], - "license": "MIT", - "devDependencies": { - "@biomejs/biome": "1.8.3", - "@tsconfig/node18": "^18.2.2", - "@types/jest": "^25.2.2", - "@typescript-eslint/eslint-plugin": "^2.33.0", - "@typescript-eslint/parser": "^2.33.0", - "jest": "^25.0.0", - "prepush": "^3.1.11", - "ts-jest": "^25.5.1", - "tsup": "^7.2.0", - "typescript": "^5.2.2" - }, - "prepush": { - "tasks": ["yarn lint", "yarn test"] - } -} diff --git a/tests/unit/examples.spec.ts b/tests/unit/examples.spec.ts deleted file mode 100644 index 0c7e7b2..0000000 --- a/tests/unit/examples.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -import clip from "../../src"; - -test("examples: test examples from the README", () => { - expect(clip("foo", 3)).toBe("foo"); - expect(clip("foo", 2)).toBe("f…"); - expect(clip("foo bar", 5)).toBe("foo …"); - expect(clip("foo\nbar", 5)).toBe("foo"); -}); diff --git a/tests/unit/examples.test.ts b/tests/unit/examples.test.ts new file mode 100644 index 0000000..9c82a6a --- /dev/null +++ b/tests/unit/examples.test.ts @@ -0,0 +1,10 @@ +import { assertEquals } from "jsr:@std/assert"; + +import clip from "../../src/index.ts"; + +Deno.test("examples: test examples from the README", () => { + assertEquals(clip("foo", 3), "foo"); + assertEquals(clip("foo", 2), "f…"); + assertEquals(clip("foo bar", 5), "foo …"); + assertEquals(clip("foo\nbar", 5), "foo"); +}); diff --git a/tests/unit/html.spec.ts b/tests/unit/html.spec.ts deleted file mode 100644 index 86aaa43..0000000 --- a/tests/unit/html.spec.ts +++ /dev/null @@ -1,452 +0,0 @@ -import clip from "../../src"; - -test("html: test basic HTML", () => { - const options = { html: true }; - - expect(clip("

Lorum ipsum

", 5, options)).toBe("

Loru\u2026

"); - expect(clip("

Lorum ipsum

", 5, options)).toBe("

Loru\u2026

"); - expect(clip("

Lorum ipsum

", 6, options)).toBe("

Lorum\u2026

"); - expect(clip("

Lorum ipsum

", 7, options)).toBe("

Lorum \u2026

"); - expect(clip("

Lorum\nipsum

", 5, options)).toBe("

Lorum

"); - expect(clip("

Lorum
ipsum

", 5, options)).toBe("

Lorum

"); - - expect(clip("

Lorum

", 5, options)).toBe("

Lorum

"); - - expect(clip("

Loruma

", 5, options)).toBe("

Loru\u2026

"); - expect(clip("

Lorum

a", 5, options)).toBe("

Loru\u2026

"); - expect(clip("

Loruma

", 6, options)).toBe("

Loruma

"); - expect(clip("

Lorum

a", 6, options)).toBe("

Lorum

a"); - expect(clip("

LorumaA

", 6, options)).toBe("

Lorum\u2026

"); - expect(clip("

Lorum

aA", 6, options)).toBe("

Lorum

\u2026"); - expect(clip("

Loruma

", 7, options)).toBe("

Loruma

"); - expect(clip("

Lorum

a", 7, options)).toBe("

Lorum

a"); - expect(clip("

LorumaA

", 7, options)).toBe("

LorumaA

"); - expect(clip("

Lorum

aA", 7, options)).toBe("

Lorum

aA"); - - expect(clip("

Lorum

", 5, options)).toBe("

Loru\u2026

"); - expect(clip("

Lorum

", 5, options)).toBe("

Loru\u2026

"); - expect(clip("

Lorum

", 6, options)).toBe("

Lorum

"); - expect(clip("

Lorum

", 6, options)).toBe("

Lorum

"); - expect(clip("

Lorum

", 6, options)).toBe("

Lorum\u2026

"); - expect(clip("

Lorum

", 6, options)).toBe("

Lorum

\u2026"); - expect(clip("

Lorum

", 7, options)).toBe("

Lorum

"); - expect(clip("

Lorum

", 7, options)).toBe("

Lorum

"); - expect(clip("

Lorum

", 7, options)).toBe("

Lorum

"); - expect(clip("

Lorum

", 7, options)).toBe("

Lorum

"); - - expect(clip("Lorum", 4, options)).toBe("Lor\u2026"); - expect(clip("Lorum", 4, options)).toBe("Lor\u2026"); - - expect(clip('Just a link', 8, options)).toBe( - 'Just a \u2026', - ); - - expect(clip('Just a link, yo', 13, options)).toBe( - 'Just a link,\u2026', - ); -}); - -test("html: test HTML comments", () => { - const options = { html: true }; - - expect(clip("bold", 4, options)).toBe( - "bold", - ); - - expect(clip("bold", 3, options)).toBe( - "bo\u2026", - ); -}); - -test("html: test special characters in attribute values", () => { - const options = { html: true }; - - expect(clip('bold', 4, options)).toBe('bold'); - expect(clip('bold', 3, options)).toBe('bo\u2026'); - expect(clip("bold", 4, options)).toBe("bold"); - - expect(clip("bold", 3, options)).toBe( - "bo\u2026", - ); - - expect(clip("bold", 4, options)).toBe( - "bold", - ); - - expect(clip("bold", 3, options)).toBe( - "bo\u2026", - ); -}); - -test("html: test embedded SVG", () => { - expect( - clip( - "

" + - '\n' + - "\n" + - "\n" + - '\n' + - "test\n" + - "

", - 9, - { html: true, imageWeight: 5 }, - ), - ).toBe( - "

" + - '\n' + - "\n" + - "\n" + - '\n' + - "test" + - "

", - ); -}); - -test("html: test unicode surrogate pairs", () => { - const options = { html: true }; - - expect(clip("Lorum 𝌆", 7, options)).toBe("Lorum 𝌆"); - expect(clip("𝌆𝌆𝌆𝌆", 4, options)).toBe("𝌆𝌆𝌆𝌆"); - expect(clip("𝌆𝌆𝌆𝌆", 3, options)).toBe("𝌆𝌆…"); - expect(clip("😔🙏👙😃🏧", 6, options)).toBe("😔🙏👙😃🏧"); - expect(clip("😔🙏👙😃🏧", 5, options)).toBe("😔🙏👙😃🏧"); - expect(clip("😔🙏👙😃🏧", 4, options)).toBe("😔🙏👙…"); - expect(clip("😔🙏👙😃🏧", 3, options)).toBe("😔🙏…"); -}); - -test("html: test plain text", () => { - const options = { html: true }; - - expect(clip("Lorum ipsum", 5, options)).toBe("Loru\u2026"); - expect(clip("Lorum ipsum", 6, options)).toBe("Lorum\u2026"); - expect(clip("Lorum ipsum", 7, options)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 8, options)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 9, options)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 10, options)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 11, options)).toBe("Lorum ipsum"); - - expect(clip("Lorum\nipsum", 10, options)).toBe("Lorum"); - - expect(clip("Lorum i", 7, options)).toBe("Lorum i"); - expect(clip("Lorum \u2026", 7, options)).toBe("Lorum \u2026"); -}); - -test("html: test word breaking", () => { - const options = { breakWords: true, html: true }; - - expect(clip("Lorum ipsum", 5, options)).toBe("Loru\u2026"); - expect(clip("Lorum ipsum", 6, options)).toBe("Lorum\u2026"); - expect(clip("Lorum ipsum", 7, options)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 8, options)).toBe("Lorum i\u2026"); - expect(clip("Lorum ipsum", 9, options)).toBe("Lorum ip\u2026"); - expect(clip("Lorum ipsum", 10, options)).toBe("Lorum ips\u2026"); - expect(clip("Lorum ipsum", 11, options)).toBe("Lorum ipsum"); -}); - -test("html: test word breaking without indicator", () => { - const options = { breakWords: true, html: true, indicator: "" }; - - expect(clip("Lorum ipsum", 5, options)).toBe("Lorum"); - expect(clip("Lorum ipsum", 6, options)).toBe("Lorum "); - expect(clip("Lorum ipsum", 7, options)).toBe("Lorum i"); - expect(clip("Lorum ipsum", 8, options)).toBe("Lorum ip"); - expect(clip("Lorum ipsum", 9, options)).toBe("Lorum ips"); - expect(clip("Lorum ipsum", 10, options)).toBe("Lorum ipsu"); - expect(clip("Lorum ipsum", 11, options)).toBe("Lorum ipsum"); -}); - -test("html: test max lines", () => { - expect(clip("Lorum\nipsum", 100, { html: true, maxLines: 2 })).toBe("Lorum\nipsum"); - expect(clip("Lorum\nipsum", 100, { html: true, maxLines: 1 })).toBe("Lorum"); - expect(clip("Lorum
ipsum", 100, { html: true, maxLines: 1 })).toBe("Lorum"); - expect(clip("Lorum\n\nipsum", 100, { html: true, maxLines: 2 })).toBe("Lorum\n"); - - expect(clip("Lorum\nipsum\n", 100, { html: true, maxLines: 2 })).toBe("Lorum\nipsum"); - expect(clip("Lorum\nipsum\n\n", 100, { html: true, maxLines: 2 })).toBe("Lorum\nipsum"); - - expect( - clip("

Lorem ipsum

Lorem ipsum

", 100, { - html: true, - maxLines: 2, - }), - ).toBe("

Lorem ipsum

Lorem ipsum

"); - expect( - clip("

Lorem ipsum

Lorem ipsum

", 100, { - html: true, - maxLines: 1, - }), - ).toBe("

Lorem ipsum

"); - expect( - clip("
Lorem ipsum
Lorem ipsum
", 100, { - html: true, - maxLines: 2, - }), - ).toBe("
Lorem ipsum
Lorem ipsum
"); - expect( - clip("
Lorem ipsum
Lorem ipsum
", 100, { - html: true, - maxLines: 1, - }), - ).toBe("
Lorem ipsum
"); -}); - -test("html: test odd HTML", () => { - const options = { html: true }; - - expect(clip("

foo > bar

", 9, options)).toBe("

foo > bar

"); - expect(clip("

Lorum>>> ipsum

", 7, options)).toBe( - "

Lorum>\u2026

", - ); -}); - -test("html: test ampersand", () => { - const options = { html: true }; - - expect(clip("&", 1, options)).toBe("&"); - expect(clip("&", 2, options)).toBe("&"); - expect(clip("<", 1, options)).toBe("<"); - expect(clip("<", 2, options)).toBe("<"); - expect(clip("&", 1, options)).toBe("&"); - expect(clip("&", 2, options)).toBe("&"); - expect(clip("

&

", 1, options)).toBe(""); - expect(clip("

&

", 2, options)).toBe("

&

"); - expect(clip("

<

", 1, options)).toBe(""); - expect(clip("

<

", 2, options)).toBe("

<

"); - expect(clip("

&

", 1, options)).toBe(""); - expect(clip("

&

", 2, options)).toBe("

&

"); - - expect(clip("foo & bar", 5, options)).toBe("foo \u2026"); - expect(clip("foo & bar", 9, options)).toBe("foo & bar"); - expect(clip("foo&bar", 5, options)).toBe("foo&\u2026"); - expect(clip("foo&bar", 7, options)).toBe("foo&bar"); - expect(clip("foo&&& bar", 5, options)).toBe("foo&\u2026"); - expect(clip("foo&&& bar", 10, options)).toBe("foo&&& bar"); - - expect(clip('foo', 3, options)).toBe( - 'foo', - ); - expect(clip("&123", 4, options)).toBe("&123"); - expect(clip("&abc", 4, options)).toBe("&abc"); - expect(clip("foo &0 bar", 10, options)).toBe("foo &0 bar"); - expect(clip("foo &lolwat bar", 15, options)).toBe("foo &lolwat bar"); -}); - -test("html: test ampersand without indicator", () => { - const options = { html: true, indicator: "" }; - - expect(clip("&", 1, options)).toBe("&"); - expect(clip("&", 2, options)).toBe("&"); - expect(clip("<", 1, options)).toBe("<"); - expect(clip("<", 2, options)).toBe("<"); - expect(clip("&", 1, options)).toBe("&"); - expect(clip("&", 2, options)).toBe("&"); - expect(clip("

&

", 1, options)).toBe("

&

"); - expect(clip("

&

", 2, options)).toBe("

&

"); - expect(clip("

<

", 1, options)).toBe("

<

"); - expect(clip("

<

", 2, options)).toBe("

<

"); - expect(clip("

&

", 1, options)).toBe("

&

"); - expect(clip("

&

", 2, options)).toBe("

&

"); - - expect(clip("foo & bar", 5, options)).toBe("foo &"); - expect(clip("foo & bar", 9, options)).toBe("foo & bar"); - // Ideally "bar" wouldn't have been broken, but we accept this - // limitation when encountering tags during backtracking: - expect(clip("foo&bar", 5, options)).toBe("foo&b"); - expect(clip("foo&bar", 7, options)).toBe("foo&bar"); - expect(clip("foo&&& bar", 5, options)).toBe("foo&&"); - expect(clip("foo&&& bar", 10, options)).toBe("foo&&& bar"); - - expect(clip('foo', 3, options)).toBe( - 'foo', - ); - expect(clip("&123", 4, options)).toBe("&123"); - expect(clip("&abc", 4, options)).toBe("&abc"); - expect(clip("foo &0 bar", 10, options)).toBe("foo &0 bar"); - expect(clip("foo &lolwat bar", 15, options)).toBe("foo &lolwat bar"); -}); - -test("html: test ampersand without indicator and break words", () => { - const options = { breakWords: true, html: true, indicator: "" }; - - expect(clip("&", 1, options)).toBe("&"); - expect(clip("&", 2, options)).toBe("&"); - expect(clip("<", 1, options)).toBe("<"); - expect(clip("<", 2, options)).toBe("<"); - expect(clip("&", 1, options)).toBe("&"); - expect(clip("&", 2, options)).toBe("&"); - expect(clip("

&

", 1, options)).toBe("

&

"); - expect(clip("

&

", 2, options)).toBe("

&

"); - expect(clip("

<

", 1, options)).toBe("

<

"); - expect(clip("

<

", 2, options)).toBe("

<

"); - expect(clip("

&

", 1, options)).toBe("

&

"); - expect(clip("

&

", 2, options)).toBe("

&

"); - - expect(clip("foo & bar", 5, options)).toBe("foo &"); - expect(clip("foo & bar", 9, options)).toBe("foo & bar"); - expect(clip("foo&bar", 5, options)).toBe("foo&b"); - expect(clip("foo&bar", 7, options)).toBe("foo&bar"); - expect(clip("foo&&& bar", 5, options)).toBe("foo&&"); - expect(clip("foo&&& bar", 10, options)).toBe("foo&&& bar"); - - expect(clip('foo', 3, options)).toBe( - 'foo', - ); - expect(clip("&123", 4, options)).toBe("&123"); - expect(clip("&abc", 4, options)).toBe("&abc"); - expect(clip("foo &0 bar", 10, options)).toBe("foo &0 bar"); - expect(clip("foo &lolwat bar", 15, options)).toBe("foo &lolwat bar"); -}); - -test("html: test edge cases", () => { - const options = { breakWords: true, html: true, indicator: "..." }; - - expect(clip('one two - three
four
five', 0, options)).toBe("..."); - expect(clip('

one two - three
four
five

', 0, options)).toBe(""); - expect(clip('

one two - three
four
five

', 6, options)).toBe( - "

one...

", - ); -}); - -test("html: issue #12: split tables", () => { - const html = ` - - - - - - - - - - - - - - -
fbfbfbfb
googletwitter
intelamazon
`; - - expect(clip(html, 26, { html: true, breakWords: true })).toBe(` - - - - - - - - - - -
fbfbfbfb
googletwitter
intel
`); - - expect(clip(html, 25, { html: true, breakWords: true })).toBe(` - - - - - - - - - - -
fbfbfbfb
googletwitter
int\u2026
`); - - expect(clip(html, 25, { html: true, breakWords: true, maxLines: 2 })).toBe(` - - - - - - - - -
fbfbfbfb
googletwitter
`); -}); - -test("html: test strip tags", () => { - // Basic stripping of tags: - const htmlWithImage = '

Image blup and such

'; - expect(clip(htmlWithImage, 12, { html: true, stripTags: [] })).toBe( - clip(htmlWithImage, 12, { html: true }), - ); - expect(clip(htmlWithImage, 12, { html: true, stripTags: ["img"] })).toBe( - "

Image and \u2026

", - ); - expect(clip(htmlWithImage, 12, { html: true, stripTags: ["img", "p"] })).toBe( - "Image and \u2026", - ); - expect(clip(htmlWithImage, 12, { html: true, stripTags: true })).toBe("Image and \u2026"); - expect(clip(htmlWithImage, 15, { html: true, stripTags: ["img"] })).toBe( - "

Image and such

", - ); - - // Links are stripped (but content is preserved): - const htmlWithLink = 'foo'; - expect(clip(htmlWithLink, 3, { html: true, stripTags: ["a"] })).toBe("foo"); - expect(clip(htmlWithLink, 3, { html: true, stripTags: ["b"] })).toBe(htmlWithLink); - - // Same for tables, but whitespace is also simplified: - const htmlWithTable = `hello - - - - - - - - - - - - - - -
fbfbfbfb
googletwitter
intelamazon
world`; - expect(clip(htmlWithTable, 10, { html: true, stripTags: true })).toBe("hello fb \u2026"); - expect(clip(htmlWithTable, 16, { html: true, stripTags: true })).toBe("hello fb fbfbfb "); - expect(clip(htmlWithTable, 24, { html: true, stripTags: true })).toBe( - "hello fb fbfbfb google \u2026", - ); - - // SVG's `imageWeight` should not be counted when stripped: - const htmlWithSvg = - "

" + - '\n' + - "\n" + - "\n" + - '\n' + - "test\n" + - "

"; - expect(clip(htmlWithSvg, 3, { html: true, stripTags: ["svg"] })).toBe("

te\u2026

"); - expect(clip(htmlWithSvg, 4, { html: true, stripTags: ["svg"] })).toBe("

test

"); -}); diff --git a/tests/unit/html.test.ts b/tests/unit/html.test.ts new file mode 100644 index 0000000..2c7480f --- /dev/null +++ b/tests/unit/html.test.ts @@ -0,0 +1,486 @@ +import { assertEquals } from "jsr:@std/assert"; + +import clip from "../../src/index.ts"; + +Deno.test("html: test basic HTML", () => { + const options = { html: true }; + + assertEquals(clip("

Lorum ipsum

", 5, options), "

Loru\u2026

"); + assertEquals(clip("

Lorum ipsum

", 5, options), "

Loru\u2026

"); + assertEquals(clip("

Lorum ipsum

", 6, options), "

Lorum\u2026

"); + assertEquals( + clip("

Lorum ipsum

", 7, options), + "

Lorum \u2026

", + ); + assertEquals(clip("

Lorum\nipsum

", 5, options), "

Lorum

"); + assertEquals(clip("

Lorum
ipsum

", 5, options), "

Lorum

"); + + assertEquals(clip("

Lorum

", 5, options), "

Lorum

"); + + assertEquals(clip("

Loruma

", 5, options), "

Loru\u2026

"); + assertEquals(clip("

Lorum

a", 5, options), "

Loru\u2026

"); + assertEquals(clip("

Loruma

", 6, options), "

Loruma

"); + assertEquals(clip("

Lorum

a", 6, options), "

Lorum

a"); + assertEquals(clip("

LorumaA

", 6, options), "

Lorum\u2026

"); + assertEquals(clip("

Lorum

aA", 6, options), "

Lorum

\u2026"); + assertEquals(clip("

Loruma

", 7, options), "

Loruma

"); + assertEquals(clip("

Lorum

a", 7, options), "

Lorum

a"); + assertEquals(clip("

LorumaA

", 7, options), "

LorumaA

"); + assertEquals(clip("

Lorum

aA", 7, options), "

Lorum

aA"); + + assertEquals(clip("

Lorum

", 5, options), "

Loru\u2026

"); + assertEquals(clip("

Lorum

", 5, options), "

Loru\u2026

"); + assertEquals(clip("

Lorum

", 6, options), "

Lorum

"); + assertEquals(clip("

Lorum

", 6, options), "

Lorum

"); + assertEquals(clip("

Lorum

", 6, options), "

Lorum\u2026

"); + assertEquals(clip("

Lorum

", 6, options), "

Lorum

\u2026"); + assertEquals(clip("

Lorum

", 7, options), "

Lorum

"); + assertEquals(clip("

Lorum

", 7, options), "

Lorum

"); + assertEquals(clip("

Lorum

", 7, options), "

Lorum

"); + assertEquals(clip("

Lorum

", 7, options), "

Lorum

"); + + assertEquals(clip("Lorum", 4, options), "Lor\u2026"); + assertEquals(clip("Lorum", 4, options), "Lor\u2026"); + + assertEquals( + clip('Just a link', 8, options), + 'Just a \u2026', + ); + + assertEquals( + clip('Just a link, yo', 13, options), + 'Just a link,\u2026', + ); +}); + +Deno.test("html: test HTML comments", () => { + const options = { html: true }; + + assertEquals( + clip("bold", 4, options), + "bold", + ); + + assertEquals( + clip("bold", 3, options), + "bo\u2026", + ); +}); + +Deno.test("html: test special characters in attribute values", () => { + const options = { html: true }; + + assertEquals(clip('bold', 4, options), 'bold'); + assertEquals(clip('bold', 3, options), 'bo\u2026'); + assertEquals(clip("bold", 4, options), "bold"); + + assertEquals( + clip("bold", 3, options), + "bo\u2026", + ); + + assertEquals( + clip("bold", 4, options), + "bold", + ); + + assertEquals( + clip("bold", 3, options), + "bo\u2026", + ); +}); + +Deno.test("html: test embedded SVG", () => { + assertEquals( + clip( + "

" + + '\n' + + "\n" + + "\n" + + '\n' + + "test\n" + + "

", + 9, + { html: true, imageWeight: 5 }, + ), + "

" + + '\n' + + "\n" + + "\n" + + '\n' + + "test" + + "

", + ); +}); + +Deno.test("html: test unicode surrogate pairs", () => { + const options = { html: true }; + + assertEquals(clip("Lorum 𝌆", 7, options), "Lorum 𝌆"); + assertEquals(clip("𝌆𝌆𝌆𝌆", 4, options), "𝌆𝌆𝌆𝌆"); + assertEquals(clip("𝌆𝌆𝌆𝌆", 3, options), "𝌆𝌆…"); + assertEquals(clip("😔🙏👙😃🏧", 6, options), "😔🙏👙😃🏧"); + assertEquals(clip("😔🙏👙😃🏧", 5, options), "😔🙏👙😃🏧"); + assertEquals(clip("😔🙏👙😃🏧", 4, options), "😔🙏👙…"); + assertEquals(clip("😔🙏👙😃🏧", 3, options), "😔🙏…"); +}); + +Deno.test("html: test plain text", () => { + const options = { html: true }; + + assertEquals(clip("Lorum ipsum", 5, options), "Loru\u2026"); + assertEquals(clip("Lorum ipsum", 6, options), "Lorum\u2026"); + assertEquals(clip("Lorum ipsum", 7, options), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 8, options), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 9, options), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 10, options), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 11, options), "Lorum ipsum"); + + assertEquals(clip("Lorum\nipsum", 10, options), "Lorum"); + + assertEquals(clip("Lorum i", 7, options), "Lorum i"); + assertEquals(clip("Lorum \u2026", 7, options), "Lorum \u2026"); +}); + +Deno.test("html: test word breaking", () => { + const options = { breakWords: true, html: true }; + + assertEquals(clip("Lorum ipsum", 5, options), "Loru\u2026"); + assertEquals(clip("Lorum ipsum", 6, options), "Lorum\u2026"); + assertEquals(clip("Lorum ipsum", 7, options), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 8, options), "Lorum i\u2026"); + assertEquals(clip("Lorum ipsum", 9, options), "Lorum ip\u2026"); + assertEquals(clip("Lorum ipsum", 10, options), "Lorum ips\u2026"); + assertEquals(clip("Lorum ipsum", 11, options), "Lorum ipsum"); +}); + +Deno.test("html: test word breaking without indicator", () => { + const options = { breakWords: true, html: true, indicator: "" }; + + assertEquals(clip("Lorum ipsum", 5, options), "Lorum"); + assertEquals(clip("Lorum ipsum", 6, options), "Lorum "); + assertEquals(clip("Lorum ipsum", 7, options), "Lorum i"); + assertEquals(clip("Lorum ipsum", 8, options), "Lorum ip"); + assertEquals(clip("Lorum ipsum", 9, options), "Lorum ips"); + assertEquals(clip("Lorum ipsum", 10, options), "Lorum ipsu"); + assertEquals(clip("Lorum ipsum", 11, options), "Lorum ipsum"); +}); + +Deno.test("html: test max lines", () => { + assertEquals(clip("Lorum\nipsum", 100, { html: true, maxLines: 2 }), "Lorum\nipsum"); + assertEquals(clip("Lorum\nipsum", 100, { html: true, maxLines: 1 }), "Lorum"); + assertEquals(clip("Lorum
ipsum", 100, { html: true, maxLines: 1 }), "Lorum"); + assertEquals(clip("Lorum\n\nipsum", 100, { html: true, maxLines: 2 }), "Lorum\n"); + + assertEquals(clip("Lorum\nipsum\n", 100, { html: true, maxLines: 2 }), "Lorum\nipsum"); + assertEquals(clip("Lorum\nipsum\n\n", 100, { html: true, maxLines: 2 }), "Lorum\nipsum"); + + assertEquals( + clip("

Lorem ipsum

Lorem ipsum

", 100, { + html: true, + maxLines: 2, + }), + "

Lorem ipsum

Lorem ipsum

", + ); + assertEquals( + clip("

Lorem ipsum

Lorem ipsum

", 100, { + html: true, + maxLines: 1, + }), + "

Lorem ipsum

", + ); + assertEquals( + clip("
Lorem ipsum
Lorem ipsum
", 100, { + html: true, + maxLines: 2, + }), + "
Lorem ipsum
Lorem ipsum
", + ); + assertEquals( + clip("
Lorem ipsum
Lorem ipsum
", 100, { + html: true, + maxLines: 1, + }), + "
Lorem ipsum
", + ); +}); + +Deno.test("html: test odd HTML", () => { + const options = { html: true }; + + assertEquals(clip("

foo > bar

", 9, options), "

foo > bar

"); + assertEquals( + clip("

Lorum>>> ipsum

", 7, options), + "

Lorum>\u2026

", + ); +}); + +Deno.test("html: test ampersand", () => { + const options = { html: true }; + + assertEquals(clip("&", 1, options), "&"); + assertEquals(clip("&", 2, options), "&"); + assertEquals(clip("<", 1, options), "<"); + assertEquals(clip("<", 2, options), "<"); + assertEquals(clip("&", 1, options), "&"); + assertEquals(clip("&", 2, options), "&"); + assertEquals(clip("

&

", 1, options), ""); + assertEquals(clip("

&

", 2, options), "

&

"); + assertEquals(clip("

<

", 1, options), ""); + assertEquals(clip("

<

", 2, options), "

<

"); + assertEquals(clip("

&

", 1, options), ""); + assertEquals(clip("

&

", 2, options), "

&

"); + + assertEquals(clip("foo & bar", 5, options), "foo \u2026"); + assertEquals(clip("foo & bar", 9, options), "foo & bar"); + assertEquals(clip("foo&bar", 5, options), "foo&\u2026"); + assertEquals(clip("foo&bar", 7, options), "foo&bar"); + assertEquals(clip("foo&&& bar", 5, options), "foo&\u2026"); + assertEquals(clip("foo&&& bar", 10, options), "foo&&& bar"); + + assertEquals( + clip('foo', 3, options), + 'foo', + ); + assertEquals(clip("&123", 4, options), "&123"); + assertEquals(clip("&abc", 4, options), "&abc"); + assertEquals(clip("foo &0 bar", 10, options), "foo &0 bar"); + assertEquals(clip("foo &lolwat bar", 15, options), "foo &lolwat bar"); +}); + +Deno.test("html: test ampersand without indicator", () => { + const options = { html: true, indicator: "" }; + + assertEquals(clip("&", 1, options), "&"); + assertEquals(clip("&", 2, options), "&"); + assertEquals(clip("<", 1, options), "<"); + assertEquals(clip("<", 2, options), "<"); + assertEquals(clip("&", 1, options), "&"); + assertEquals(clip("&", 2, options), "&"); + assertEquals(clip("

&

", 1, options), "

&

"); + assertEquals(clip("

&

", 2, options), "

&

"); + assertEquals(clip("

<

", 1, options), "

<

"); + assertEquals(clip("

<

", 2, options), "

<

"); + assertEquals(clip("

&

", 1, options), "

&

"); + assertEquals(clip("

&

", 2, options), "

&

"); + + assertEquals(clip("foo & bar", 5, options), "foo &"); + assertEquals(clip("foo & bar", 9, options), "foo & bar"); + // Ideally "bar" wouldn't have been broken, but we accept this + // limitation when encountering tags during backtracking: + assertEquals(clip("foo&bar", 5, options), "foo&b"); + assertEquals(clip("foo&bar", 7, options), "foo&bar"); + assertEquals(clip("foo&&& bar", 5, options), "foo&&"); + assertEquals(clip("foo&&& bar", 10, options), "foo&&& bar"); + + assertEquals( + clip('foo', 3, options), + 'foo', + ); + assertEquals(clip("&123", 4, options), "&123"); + assertEquals(clip("&abc", 4, options), "&abc"); + assertEquals(clip("foo &0 bar", 10, options), "foo &0 bar"); + assertEquals(clip("foo &lolwat bar", 15, options), "foo &lolwat bar"); +}); + +Deno.test("html: test ampersand without indicator and break words", () => { + const options = { breakWords: true, html: true, indicator: "" }; + + assertEquals(clip("&", 1, options), "&"); + assertEquals(clip("&", 2, options), "&"); + assertEquals(clip("<", 1, options), "<"); + assertEquals(clip("<", 2, options), "<"); + assertEquals(clip("&", 1, options), "&"); + assertEquals(clip("&", 2, options), "&"); + assertEquals(clip("

&

", 1, options), "

&

"); + assertEquals(clip("

&

", 2, options), "

&

"); + assertEquals(clip("

<

", 1, options), "

<

"); + assertEquals(clip("

<

", 2, options), "

<

"); + assertEquals(clip("

&

", 1, options), "

&

"); + assertEquals(clip("

&

", 2, options), "

&

"); + + assertEquals(clip("foo & bar", 5, options), "foo &"); + assertEquals(clip("foo & bar", 9, options), "foo & bar"); + assertEquals(clip("foo&bar", 5, options), "foo&b"); + assertEquals(clip("foo&bar", 7, options), "foo&bar"); + assertEquals(clip("foo&&& bar", 5, options), "foo&&"); + assertEquals(clip("foo&&& bar", 10, options), "foo&&& bar"); + + assertEquals( + clip('foo', 3, options), + 'foo', + ); + assertEquals(clip("&123", 4, options), "&123"); + assertEquals(clip("&abc", 4, options), "&abc"); + assertEquals(clip("foo &0 bar", 10, options), "foo &0 bar"); + assertEquals(clip("foo &lolwat bar", 15, options), "foo &lolwat bar"); +}); + +Deno.test("html: test edge cases", () => { + const options = { breakWords: true, html: true, indicator: "..." }; + + assertEquals(clip('one two - three
four
five', 0, options), "..."); + assertEquals(clip('

one two - three
four
five

', 0, options), ""); + assertEquals( + clip('

one two - three
four
five

', 6, options), + "

one...

", + ); +}); + +Deno.test("html: issue #12: split tables", () => { + const html = ` + + + + + + + + + + + + + + +
fbfbfbfb
googletwitter
intelamazon
`; + + assertEquals( + clip(html, 26, { html: true, breakWords: true }), + ` + + + + + + + + + + +
fbfbfbfb
googletwitter
intel
`, + ); + + assertEquals( + clip(html, 25, { html: true, breakWords: true }), + ` + + + + + + + + + + +
fbfbfbfb
googletwitter
int\u2026
`, + ); + + assertEquals( + clip(html, 25, { html: true, breakWords: true, maxLines: 2 }), + ` + + + + + + + + +
fbfbfbfb
googletwitter
`, + ); +}); + +Deno.test("html: test strip tags", () => { + // Basic stripping of tags: + const htmlWithImage = '

Image blup and such

'; + assertEquals( + clip(htmlWithImage, 12, { html: true, stripTags: [] }), + clip(htmlWithImage, 12, { html: true }), + ); + assertEquals( + clip(htmlWithImage, 12, { html: true, stripTags: ["img"] }), + "

Image and \u2026

", + ); + assertEquals( + clip(htmlWithImage, 12, { html: true, stripTags: ["img", "p"] }), + "Image and \u2026", + ); + assertEquals(clip(htmlWithImage, 12, { html: true, stripTags: true }), "Image and \u2026"); + assertEquals( + clip(htmlWithImage, 15, { html: true, stripTags: ["img"] }), + "

Image and such

", + ); + + // Links are stripped (but content is preserved): + const htmlWithLink = 'foo'; + assertEquals(clip(htmlWithLink, 3, { html: true, stripTags: ["a"] }), "foo"); + assertEquals(clip(htmlWithLink, 3, { html: true, stripTags: ["b"] }), htmlWithLink); + + // Same for tables, but whitespace is also simplified: + const htmlWithTable = `hello + + + + + + + + + + + + + + +
fbfbfbfb
googletwitter
intelamazon
world`; + assertEquals(clip(htmlWithTable, 10, { html: true, stripTags: true }), "hello fb \u2026"); + assertEquals(clip(htmlWithTable, 16, { html: true, stripTags: true }), "hello fb fbfbfb "); + assertEquals( + clip(htmlWithTable, 24, { html: true, stripTags: true }), + "hello fb fbfbfb google \u2026", + ); + + // SVG's `imageWeight` should not be counted when stripped: + const htmlWithSvg = + "

" + + '\n' + + "\n" + + "\n" + + '\n' + + "test\n" + + "

"; + assertEquals(clip(htmlWithSvg, 3, { html: true, stripTags: ["svg"] }), "

te\u2026

"); + assertEquals(clip(htmlWithSvg, 4, { html: true, stripTags: ["svg"] }), "

test

"); +}); diff --git a/tests/unit/plain-text.spec.ts b/tests/unit/plain-text.spec.ts deleted file mode 100644 index d11fd65..0000000 --- a/tests/unit/plain-text.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import clip from "../../src"; - -test("plain-text: test basics", () => { - expect(clip("Lorum ipsum", 5)).toBe("Loru\u2026"); - expect(clip("Lorum ipsum", 6)).toBe("Lorum\u2026"); - expect(clip("Lorum ipsum", 7)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 8)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 9)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 10)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 11)).toBe("Lorum ipsum"); - - expect(clip("Lorum\nipsum", 10)).toBe("Lorum"); - - expect(clip("Lorum i", 7)).toBe("Lorum i"); - expect(clip("Lorum \u2026", 7)).toBe("Lorum \u2026"); -}); - -test("plain-text: test unicode surrogate pairs", () => { - expect(clip("Lorum 𝌆", 7)).toBe("Lorum 𝌆"); - expect(clip("𝌆𝌆𝌆𝌆", 4)).toBe("𝌆𝌆𝌆𝌆"); - expect(clip("𝌆𝌆𝌆𝌆", 3)).toBe("𝌆𝌆…"); - expect(clip("😔🙏👙😃🏧", 6)).toBe("😔🙏👙😃🏧"); - expect(clip("😔🙏👙😃🏧", 5)).toBe("😔🙏👙😃🏧"); - expect(clip("😔🙏👙😃🏧", 4)).toBe("😔🙏👙…"); - expect(clip("😔🙏👙😃🏧", 3)).toBe("😔🙏…"); -}); - -test("plain-text: test word breaking", () => { - const options = { breakWords: true }; - - expect(clip("Lorum ipsum", 5, options)).toBe("Loru\u2026"); - expect(clip("Lorum ipsum", 6, options)).toBe("Lorum\u2026"); - expect(clip("Lorum ipsum", 7, options)).toBe("Lorum \u2026"); - expect(clip("Lorum ipsum", 8, options)).toBe("Lorum i\u2026"); - expect(clip("Lorum ipsum", 9, options)).toBe("Lorum ip\u2026"); - expect(clip("Lorum ipsum", 10, options)).toBe("Lorum ips\u2026"); - expect(clip("Lorum ipsum", 11, options)).toBe("Lorum ipsum"); -}); - -test("plain-text: test word breaking without indicator", () => { - const options = { breakWords: true, indicator: "" }; - - expect(clip("Lorum ipsum", 5, options)).toBe("Lorum"); - expect(clip("Lorum ipsum", 6, options)).toBe("Lorum "); - expect(clip("Lorum ipsum", 7, options)).toBe("Lorum i"); - expect(clip("Lorum ipsum", 8, options)).toBe("Lorum ip"); - expect(clip("Lorum ipsum", 9, options)).toBe("Lorum ips"); - expect(clip("Lorum ipsum", 10, options)).toBe("Lorum ipsu"); - expect(clip("Lorum ipsum", 11, options)).toBe("Lorum ipsum"); -}); - -test("plain-text: test max lines", () => { - expect(clip("Lorum\nipsum", 100, { maxLines: 2 })).toBe("Lorum\nipsum"); - expect(clip("Lorum\nipsum", 100, { maxLines: 1 })).toBe("Lorum"); - expect(clip("Lorum\n\nipsum", 100, { maxLines: 2 })).toBe("Lorum\n"); - - expect(clip("Lorum\nipsum\n", 100, { maxLines: 2 })).toBe("Lorum\nipsum"); - expect(clip("Lorum\nipsum\n\n", 100, { maxLines: 2 })).toBe("Lorum\nipsum"); -}); - -test("plain-text: test edge cases", () => { - const options = { breakWords: true, html: true, indicator: "..." }; - - expect(clip("one two - three \nfour five", 0, options)).toBe("..."); - expect(clip("one two - three \nfour five", 6, options)).toBe("one..."); -}); diff --git a/tests/unit/plain-text.test.ts b/tests/unit/plain-text.test.ts new file mode 100644 index 0000000..3304b97 --- /dev/null +++ b/tests/unit/plain-text.test.ts @@ -0,0 +1,68 @@ +import { assertEquals } from "jsr:@std/assert"; + +import clip from "../../src/index.ts"; + +Deno.test("plain-text: test basics", () => { + assertEquals(clip("Lorum ipsum", 5), "Loru\u2026"); + assertEquals(clip("Lorum ipsum", 6), "Lorum\u2026"); + assertEquals(clip("Lorum ipsum", 7), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 8), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 9), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 10), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 11), "Lorum ipsum"); + + assertEquals(clip("Lorum\nipsum", 10), "Lorum"); + + assertEquals(clip("Lorum i", 7), "Lorum i"); + assertEquals(clip("Lorum \u2026", 7), "Lorum \u2026"); +}); + +Deno.test("plain-text: test unicode surrogate pairs", () => { + assertEquals(clip("Lorum 𝌆", 7), "Lorum 𝌆"); + assertEquals(clip("𝌆𝌆𝌆𝌆", 4), "𝌆𝌆𝌆𝌆"); + assertEquals(clip("𝌆𝌆𝌆𝌆", 3), "𝌆𝌆…"); + assertEquals(clip("😔🙏👙😃🏧", 6), "😔🙏👙😃🏧"); + assertEquals(clip("😔🙏👙😃🏧", 5), "😔🙏👙😃🏧"); + assertEquals(clip("😔🙏👙😃🏧", 4), "😔🙏👙…"); + assertEquals(clip("😔🙏👙😃🏧", 3), "😔🙏…"); +}); + +Deno.test("plain-text: test word breaking", () => { + const options = { breakWords: true }; + + assertEquals(clip("Lorum ipsum", 5, options), "Loru\u2026"); + assertEquals(clip("Lorum ipsum", 6, options), "Lorum\u2026"); + assertEquals(clip("Lorum ipsum", 7, options), "Lorum \u2026"); + assertEquals(clip("Lorum ipsum", 8, options), "Lorum i\u2026"); + assertEquals(clip("Lorum ipsum", 9, options), "Lorum ip\u2026"); + assertEquals(clip("Lorum ipsum", 10, options), "Lorum ips\u2026"); + assertEquals(clip("Lorum ipsum", 11, options), "Lorum ipsum"); +}); + +Deno.test("plain-text: test word breaking without indicator", () => { + const options = { breakWords: true, indicator: "" }; + + assertEquals(clip("Lorum ipsum", 5, options), "Lorum"); + assertEquals(clip("Lorum ipsum", 6, options), "Lorum "); + assertEquals(clip("Lorum ipsum", 7, options), "Lorum i"); + assertEquals(clip("Lorum ipsum", 8, options), "Lorum ip"); + assertEquals(clip("Lorum ipsum", 9, options), "Lorum ips"); + assertEquals(clip("Lorum ipsum", 10, options), "Lorum ipsu"); + assertEquals(clip("Lorum ipsum", 11, options), "Lorum ipsum"); +}); + +Deno.test("plain-text: test max lines", () => { + assertEquals(clip("Lorum\nipsum", 100, { maxLines: 2 }), "Lorum\nipsum"); + assertEquals(clip("Lorum\nipsum", 100, { maxLines: 1 }), "Lorum"); + assertEquals(clip("Lorum\n\nipsum", 100, { maxLines: 2 }), "Lorum\n"); + + assertEquals(clip("Lorum\nipsum\n", 100, { maxLines: 2 }), "Lorum\nipsum"); + assertEquals(clip("Lorum\nipsum\n\n", 100, { maxLines: 2 }), "Lorum\nipsum"); +}); + +Deno.test("plain-text: test edge cases", () => { + const options = { breakWords: true, html: true, indicator: "..." }; + + assertEquals(clip("one two - three \nfour five", 0, options), "..."); + assertEquals(clip("one two - three \nfour five", 6, options), "one..."); +}); diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index cda1be3..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "@tsconfig/node18/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "declaration": true, - "esModuleInterop": true, - "lib": ["es2017"], - "module": "commonjs", - "moduleResolution": "node", - "outDir": "dist", - "strict": true, - "target": "ES5" - }, - "include": ["src/**/*"] -} diff --git a/tsup.config.ts b/tsup.config.ts deleted file mode 100644 index f9b4730..0000000 --- a/tsup.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import fsp from "node:fs/promises"; -import path from "node:path"; - -import { defineConfig } from "tsup"; - -export default defineConfig({ - format: ["cjs", "esm"], // generate cjs and esm files - entry: ["src/index.ts"], - clean: true, // rimraf dis - dts: true, // generate dts file for main module - skipNodeModulesBundle: true, - splitting: true, - target: "es2017", - cjsInterop: true, -});