From c38360b1fb2185f53b9072b0887ac0191c8e884e Mon Sep 17 00:00:00 2001 From: immitsu Date: Sat, 11 Oct 2025 17:51:45 +0300 Subject: [PATCH 1/3] Add `transform` option to `flatParse` --- .size-limit.js | 2 +- lib/parse/flat-parse.js | 7 ++++--- readme.md | 11 ++++++----- test/parse/flat-parse.spec.js | 36 ++++++++++++++++++++++++++++++++++ test/parse/flat-parse.types.ts | 19 +++++++++++++++++- types/parse/flat-parse.d.ts | 10 ++++++---- 6 files changed, 71 insertions(+), 14 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index a32e7e4..3c777ee 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -28,7 +28,7 @@ export default [ name: 'merge' }, { - limit: '2.005 kB', + limit: '2.02 kB', name: 'all', path: 'index.js' } diff --git a/lib/parse/flat-parse.js b/lib/parse/flat-parse.js index ff47d56..f28db74 100644 --- a/lib/parse/flat-parse.js +++ b/lib/parse/flat-parse.js @@ -13,14 +13,14 @@ const itemEnhancer = opts => { } } -const bookmarksBuilder = ({ add, describe, enhance }) => { +const bookmarksBuilder = ({ add, describe, enhance, transform }) => { const foldersStack = [] let currFolder return { addBookmark: (title, attrs) => { const bookmark = enhance({ folder: currFolder, title }, attrs) - add(bookmark) + add(transform(bookmark)) }, closeFolder: () => { foldersStack.pop() @@ -40,7 +40,8 @@ export const flatParse = (txt, opts = {}) => { const add = b => bookmarks.push(b) const describe = d => (bookmarks.at(-1).description = d) const enhance = itemEnhancer(opts) - const handlers = bookmarksBuilder({ add, describe, enhance }) + const transform = opts.transform || (x => x) + const handlers = bookmarksBuilder({ add, describe, enhance, transform }) traverse(txt, handlers) return bookmarks } diff --git a/readme.md b/readme.md index 409543a..4fdba20 100644 --- a/readme.md +++ b/readme.md @@ -135,10 +135,11 @@ const bookmarks = flatParse(html) You can configure the output by activating options passed as the second argument. -| Option | Type | Description | -| -------------- | ---------- | ------------------------------------------------------------------------------------------- | -| `excludeAttrs` | `string[]` | Excludes specified attributes from output. See [attributes definition](./types/attrs.d.ts). | -| `withId` | `boolean` | Adds incremental numeric `id` to items. | +| Option | Type | Description | +| -------------- | --------------------------- | ------------------------------------------------------------------------------------------- | +| `excludeAttrs` | `string[]` | Excludes specified attributes from output. See [attributes definition](./types/attrs.d.ts). | +| `withId` | `boolean` | Adds incremental numeric `id` to items. | +| `transform` | `(item: FlatBookmark) => T` | Function to transform bookmark item structure. | ### `customParse` @@ -183,7 +184,7 @@ const stringified = stringify(parsed) Converts the flat list from `flatParse` back into an HTML string. -> It requires using `flatParse` with `{ withId: true }` to ensure unique item IDs. +> It requires using `flatParse` with `{ withId: true }` to ensure unique item IDs, and without `transform` to ensure a known structure. ```js import { flatParse, flatStringify } from 'nbff-parser' diff --git a/test/parse/flat-parse.spec.js b/test/parse/flat-parse.spec.js index db8464c..1896ed6 100644 --- a/test/parse/flat-parse.spec.js +++ b/test/parse/flat-parse.spec.js @@ -185,6 +185,42 @@ describe('flat-parse', () => { deepEqual(actual, expected) }) + test('with transform', () => { + const initial = ` +

JavaScript

+

+

TC39 - Specifying JavaScript. +

Engines

+

+

V8 JavaScript engine +

+

+ ` + + const transform = item => { + if (item.personal_toolbar_folder) return null + + return { + tag: item.folder.map(f => f.title).join(' / '), + title: item.title, + url: item.href + } + } + + const actual = flatParse(initial, { transform }) + + const expected = [ + null, + { + tag: 'JavaScript / Engines', + title: 'V8 JavaScript engine', + url: 'https://v8.dev/' + } + ] + + deepEqual(actual, expected) + }) + describe('bookmarks-1.html', () => { const initial = readFile('./bookmarks-1.html') diff --git a/test/parse/flat-parse.types.ts b/test/parse/flat-parse.types.ts index ad48807..2282b75 100644 --- a/test/parse/flat-parse.types.ts +++ b/test/parse/flat-parse.types.ts @@ -1,4 +1,4 @@ -import { FlatBookmark, FlatBookmarkWithId } from '../../index.js' +import { FlatBookmark, FlatBookmarkWithId, flatParse } from '../../index.js' // We check the relevance of types here via `pnpm test:types`. @@ -45,3 +45,20 @@ const bookmarkWithId: Required = { ], id: 0 } + +const parser = flatParse('..') +parser satisfies FlatBookmark[] + +const parserWithId = flatParse('..', { withId: true }) +parserWithId satisfies FlatBookmark[] +parserWithId satisfies FlatBookmarkWithId[] + +const parserWithTransform = flatParse('..', { transform: item => ({ name: item.title }) }) +parserWithTransform satisfies { name: string }[] + +const parserWithIdent = flatParse('..', { transform: item => item }) +parserWithIdent satisfies FlatBookmark[] + +const parserWithIdAndIdent = flatParse('..', { withId: true, transform: item => item }) +parserWithIdAndIdent satisfies FlatBookmark[] +parserWithIdAndIdent satisfies FlatBookmarkWithId[] diff --git a/types/parse/flat-parse.d.ts b/types/parse/flat-parse.d.ts index d42cfeb..dfe1ae7 100644 --- a/types/parse/flat-parse.d.ts +++ b/types/parse/flat-parse.d.ts @@ -16,17 +16,19 @@ export type FlatBookmarkWithId = WithId< > // Overload signatures. -export function flatParse( +export function flatParse( text: string, options?: Partial<{ excludeAttrs: AllAttrKeys[] withId: false + transform: (item: FlatBookmark) => T }> -): FlatBookmark[] -export function flatParse( +): T[] +export function flatParse( text: string, options: { excludeAttrs?: AllAttrKeys[] withId: true + transform?: (item: FlatBookmarkWithId) => T } -): FlatBookmarkWithId[] +): T[] From 8a3273832007fc6037dff668de72b98290c631d0 Mon Sep 17 00:00:00 2001 From: immitsu Date: Sun, 12 Oct 2025 13:42:20 +0300 Subject: [PATCH 2/3] Filter null/undefined from transform --- .size-limit.js | 2 +- lib/parse/flat-parse.js | 3 ++- readme.md | 10 +++++----- test/parse/flat-parse.spec.js | 3 +-- test/parse/flat-parse.types.ts | 9 +++++++-- types/parse/flat-parse.d.ts | 10 ++++++---- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index 3c777ee..93b2ba6 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -28,7 +28,7 @@ export default [ name: 'merge' }, { - limit: '2.02 kB', + limit: '2.04 kB', name: 'all', path: 'index.js' } diff --git a/lib/parse/flat-parse.js b/lib/parse/flat-parse.js index f28db74..2a090f1 100644 --- a/lib/parse/flat-parse.js +++ b/lib/parse/flat-parse.js @@ -20,7 +20,8 @@ const bookmarksBuilder = ({ add, describe, enhance, transform }) => { return { addBookmark: (title, attrs) => { const bookmark = enhance({ folder: currFolder, title }, attrs) - add(transform(bookmark)) + const transformed = transform(bookmark) + transformed && add(transformed) }, closeFolder: () => { foldersStack.pop() diff --git a/readme.md b/readme.md index 4fdba20..b37bd08 100644 --- a/readme.md +++ b/readme.md @@ -135,11 +135,11 @@ const bookmarks = flatParse(html) You can configure the output by activating options passed as the second argument. -| Option | Type | Description | -| -------------- | --------------------------- | ------------------------------------------------------------------------------------------- | -| `excludeAttrs` | `string[]` | Excludes specified attributes from output. See [attributes definition](./types/attrs.d.ts). | -| `withId` | `boolean` | Adds incremental numeric `id` to items. | -| `transform` | `(item: FlatBookmark) => T` | Function to transform bookmark item structure. | +| Option | Type | Description | +| -------------- | ------------------------------------------------ | --------------------------------------------------- | +| `excludeAttrs` | `string[]` | Excludes specified attributes from output. | +| `withId` | `boolean` | Adds incremental numeric `id` to items. | +| `transform` | `(item: FlatBookmark) => T \| null \| undefined` | Transforms items, filtering out `null`/`undefined`. | ### `customParse` diff --git a/test/parse/flat-parse.spec.js b/test/parse/flat-parse.spec.js index 1896ed6..35447d6 100644 --- a/test/parse/flat-parse.spec.js +++ b/test/parse/flat-parse.spec.js @@ -198,7 +198,7 @@ describe('flat-parse', () => { ` const transform = item => { - if (item.personal_toolbar_folder) return null + if (item.personal_toolbar_folder) return return { tag: item.folder.map(f => f.title).join(' / '), @@ -210,7 +210,6 @@ describe('flat-parse', () => { const actual = flatParse(initial, { transform }) const expected = [ - null, { tag: 'JavaScript / Engines', title: 'V8 JavaScript engine', diff --git a/test/parse/flat-parse.types.ts b/test/parse/flat-parse.types.ts index 2282b75..df5ddbb 100644 --- a/test/parse/flat-parse.types.ts +++ b/test/parse/flat-parse.types.ts @@ -53,8 +53,13 @@ const parserWithId = flatParse('..', { withId: true }) parserWithId satisfies FlatBookmark[] parserWithId satisfies FlatBookmarkWithId[] -const parserWithTransform = flatParse('..', { transform: item => ({ name: item.title }) }) -parserWithTransform satisfies { name: string }[] +const parserWithTransform = flatParse('..', { + transform: item => { + if (!item.href) return + return ({ name: item.title, url: item.href }) + } +}) +parserWithTransform satisfies { name: string, url: string }[] const parserWithIdent = flatParse('..', { transform: item => item }) parserWithIdent satisfies FlatBookmark[] diff --git a/types/parse/flat-parse.d.ts b/types/parse/flat-parse.d.ts index dfe1ae7..5266c15 100644 --- a/types/parse/flat-parse.d.ts +++ b/types/parse/flat-parse.d.ts @@ -15,20 +15,22 @@ export type FlatBookmarkWithId = WithId< } > +type TransformFn = (item: I) => R | null | undefined + // Overload signatures. export function flatParse( text: string, options?: Partial<{ excludeAttrs: AllAttrKeys[] withId: false - transform: (item: FlatBookmark) => T + transform: TransformFn }> -): T[] +): NonNullable[] export function flatParse( text: string, options: { excludeAttrs?: AllAttrKeys[] withId: true - transform?: (item: FlatBookmarkWithId) => T + transform?: TransformFn } -): T[] +): NonNullable[] From f86a0b03e5efc8b17e3e8480d5d8a938609f7273 Mon Sep 17 00:00:00 2001 From: immitsu Date: Sun, 12 Oct 2025 13:58:47 +0300 Subject: [PATCH 3/3] Use `Truthy` to handle falsy values in `flatParse` --- readme.md | 10 +++++----- types/parse/flat-parse.d.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index b37bd08..78188eb 100644 --- a/readme.md +++ b/readme.md @@ -135,11 +135,11 @@ const bookmarks = flatParse(html) You can configure the output by activating options passed as the second argument. -| Option | Type | Description | -| -------------- | ------------------------------------------------ | --------------------------------------------------- | -| `excludeAttrs` | `string[]` | Excludes specified attributes from output. | -| `withId` | `boolean` | Adds incremental numeric `id` to items. | -| `transform` | `(item: FlatBookmark) => T \| null \| undefined` | Transforms items, filtering out `null`/`undefined`. | +| Option | Type | Description | +| -------------- | --------------------------- | ------------------------------------------ | +| `excludeAttrs` | `string[]` | Excludes specified attributes from output. | +| `withId` | `boolean` | Adds incremental numeric `id` to items. | +| `transform` | `(item: FlatBookmark) => T` | Transforms items, omitting falsy returns. | ### `customParse` diff --git a/types/parse/flat-parse.d.ts b/types/parse/flat-parse.d.ts index 5266c15..61e430b 100644 --- a/types/parse/flat-parse.d.ts +++ b/types/parse/flat-parse.d.ts @@ -15,7 +15,7 @@ export type FlatBookmarkWithId = WithId< } > -type TransformFn = (item: I) => R | null | undefined +type Truthy = V extends null | undefined | false | 0 | "" ? never : V // Overload signatures. export function flatParse( @@ -23,14 +23,14 @@ export function flatParse( options?: Partial<{ excludeAttrs: AllAttrKeys[] withId: false - transform: TransformFn + transform: (item: FlatBookmark) => T }> -): NonNullable[] +): Truthy[] export function flatParse( text: string, options: { excludeAttrs?: AllAttrKeys[] withId: true - transform?: TransformFn + transform?: (item: FlatBookmarkWithId) => T } -): NonNullable[] +): Truthy[]