diff --git a/.github/workflows/package-smoke.yml b/.github/workflows/package-smoke.yml new file mode 100644 index 0000000..c0e0a8c --- /dev/null +++ b/.github/workflows/package-smoke.yml @@ -0,0 +1,62 @@ +name: Package smoke + +on: [push, pull_request] + +jobs: + verify: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: ['20.x', '22.x', '24.x'] + name: Package smoke (Node ${{ matrix.node-version }}) + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: yarn + - run: yarn build + - name: Verify packaged exports + run: | + set -euo pipefail + TGZ=$(npm pack) + TMPDIR=$(mktemp -d) + pushd "$TMPDIR" >/dev/null + npm init -y >/dev/null + npm install --silent "$GITHUB_WORKSPACE/$TGZ" typescript >/dev/null + node - <<'NODE' + const pkg = require('tree-visit') + if (typeof pkg.visit !== 'function') { + throw new Error('Expected visit export in CJS bundle') + } + console.log('CJS consumer OK') + NODE + node --input-type=module - <<'NODE' + import * as mod from 'tree-visit' + if (typeof mod.visit !== 'function') { + throw new Error('Expected visit export in ESM bundle') + } + console.log('ESM consumer OK') + NODE + cat <<'TS' > smoke.ts + import type { VisitOptions } from 'tree-visit' + import { visit } from 'tree-visit' + + type Node = { value: number; children?: Node[] } + + const tree: Node = { value: 1, children: [{ value: 2 }] } + + visit( + tree, + { + includeTraversalContext: true, + getChildren: (node) => node.children ?? [], + onEnter(node) { + console.log(node.value) + }, + } satisfies VisitOptions + ) + TS + npx tsc smoke.ts --moduleResolution nodenext --module nodenext --target es2020 --noEmit + popd >/dev/null + rm "$GITHUB_WORKSPACE/$TGZ" diff --git a/package.json b/package.json index befd6f0..32545da 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,10 @@ "testEnvironment": "node", "testPathIgnorePatterns": [ "lib" - ] + ], + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.js$": "$1" + } }, "exports": { ".": { diff --git a/src/access.ts b/src/access.ts index 7ecef55..8633238 100644 --- a/src/access.ts +++ b/src/access.ts @@ -1,5 +1,5 @@ -import { IndexPath } from './indexPath' -import { BaseOptions, TraversalContext } from './options' +import { IndexPath } from './indexPath.js' +import { BaseOptions, TraversalContext } from './options.js' /** * Returns a node by its `IndexPath`. diff --git a/src/ancestors.ts b/src/ancestors.ts index edbe10f..33cb3e6 100644 --- a/src/ancestors.ts +++ b/src/ancestors.ts @@ -1,6 +1,6 @@ -import { IndexPath } from './indexPath' -import { comparePathsByComponent } from './sort' -import { KeyPath } from './types' +import { IndexPath } from './indexPath.js' +import { comparePathsByComponent } from './sort.js' +import { KeyPath } from './types.js' type AncestorPathsOptions = { /** diff --git a/src/defineTree.ts b/src/defineTree.ts index bbffe6f..baff5e6 100644 --- a/src/defineTree.ts +++ b/src/defineTree.ts @@ -1,6 +1,6 @@ -import { access, accessPath, ancestors, get } from './access' -import { diagram, DiagramOptions } from './diagram' -import { entries } from './entries' +import { access, accessPath, ancestors, get } from './access.js' +import { diagram, DiagramOptions } from './diagram.js' +import { entries } from './entries.js' import { find, findAll, @@ -8,47 +8,50 @@ import { FindOptions, FindOptionsTyped, findPath, -} from './find' -import { flat } from './flat' -import { IndexPath } from './indexPath' +} from './find.js' +import { flat } from './flat.js' +import { IndexPath } from './indexPath.js' import { insert, InsertOptions, insertWithPathTracking, InsertWithPathTrackingOptions, -} from './insert' -import { flatMap, FlatMapOptions, map, MapOptions } from './map' -import { move, MoveOptions } from './move' -import { BaseOptions, MutationBaseOptions, TraversalContext } from './options' -import { reduce, ReduceOptions } from './reduce' +} from './insert.js' +import { flatMap, FlatMapOptions, map, MapOptions } from './map.js' +import { move, MoveOptions } from './move.js' +import { + BaseOptions, + MutationBaseOptions, + TraversalContext, +} from './options.js' +import { reduce, ReduceOptions } from './reduce.js' import { remove, RemoveOptions, removeWithPathTracking, RemoveWithPathTrackingOptions, -} from './remove' -import { replace, ReplaceOptions } from './replace' +} from './remove.js' +import { replace, ReplaceOptions } from './replace.js' import { splice, SpliceOptions, spliceWithPathTracking, SpliceWithPathTrackingOptions, -} from './splice' -import { ExtractRequiredKeys, OptionCheck, Prettify } from './types' -import { visit, VisitOptions } from './visit' +} from './splice.js' +import { OptionCheck, Prettify } from './types.js' +import { visit, VisitOptions } from './visit.js' type WithoutBase = Omit> type MutationOptions = WithoutBase> type DiagramOptionsWB = WithoutBase> -type DiagramRequiredOptions = Pick< - DiagramOptionsWB, - ExtractRequiredKeys> -> +type DiagramRequiredOptions = { + getLabel: DiagramOptionsWB['getLabel'] +} type DiagramOptionalOptions = Omit< DiagramOptionsWB, - ExtractRequiredKeys> + keyof DiagramRequiredOptions > type FindOptionsWB = WithoutBase> type VisitOptionsWB = WithoutBase> diff --git a/src/diagram.ts b/src/diagram.ts index a7a2e5a..9408fa8 100644 --- a/src/diagram.ts +++ b/src/diagram.ts @@ -1,7 +1,7 @@ -import { IndexPath } from './indexPath' -import { BaseOptions } from './options' -import { boxDiagram } from './diagram/boxDiagram' -import { directoryDiagram } from './diagram/directoryDiagram' +import { IndexPath } from './indexPath.js' +import { BaseOptions } from './options.js' +import { boxDiagram } from './diagram/boxDiagram.js' +import { directoryDiagram } from './diagram/directoryDiagram.js' export type DiagramType = 'directory' | 'box' diff --git a/src/diagram/boxDiagram.ts b/src/diagram/boxDiagram.ts index 825b945..462bf4b 100644 --- a/src/diagram/boxDiagram.ts +++ b/src/diagram/boxDiagram.ts @@ -1,5 +1,5 @@ -import { IndexPath } from '../indexPath' -import { DiagramOptions } from '../diagram' +import { IndexPath } from '../indexPath.js' +import { DiagramOptions } from '../diagram.js' enum BoxDrawing { TopLeft = '┌', diff --git a/src/diagram/directoryDiagram.ts b/src/diagram/directoryDiagram.ts index 6ee20c6..f1b3dfc 100644 --- a/src/diagram/directoryDiagram.ts +++ b/src/diagram/directoryDiagram.ts @@ -1,5 +1,5 @@ -import { DiagramOptions } from '../diagram' -import { IndexPath } from '../indexPath' +import { DiagramOptions } from '../diagram.js' +import { IndexPath } from '../indexPath.js' enum LinePrefix { Child = `├── `, diff --git a/src/entries.ts b/src/entries.ts index eef3d72..b2e2f64 100644 --- a/src/entries.ts +++ b/src/entries.ts @@ -1,6 +1,6 @@ -import { IndexPath } from './indexPath' -import { BaseOptions } from './options' -import { visit } from './visit' +import { IndexPath } from './indexPath.js' +import { BaseOptions } from './options.js' +import { visit } from './visit.js' export function entries(node: T, options: BaseOptions): [IndexPath, T][] { let result: [IndexPath, T][] = [] diff --git a/src/find.ts b/src/find.ts index a1a678b..8e8656e 100644 --- a/src/find.ts +++ b/src/find.ts @@ -1,6 +1,6 @@ -import { IndexPath } from './indexPath' -import { BaseOptions, TraversalDirection } from './options' -import { STOP, visit } from './visit' +import { IndexPath } from './indexPath.js' +import { BaseOptions, TraversalDirection } from './options.js' +import { STOP, visit } from './visit.js' export type FindOptions = BaseOptions & { /** diff --git a/src/flat.ts b/src/flat.ts index 27cb64e..57c8f4d 100644 --- a/src/flat.ts +++ b/src/flat.ts @@ -1,5 +1,5 @@ -import { BaseOptions } from './options' -import { reduce } from './reduce' +import { BaseOptions } from './options.js' +import { reduce } from './reduce.js' /** * Returns an array containing the root node and all of its descendants. diff --git a/src/index.ts b/src/index.ts index e253b59..c6c2dff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,20 +1,20 @@ -export * from './access' -export * from './ancestors' -export * from './defineTree' -export * from './diagram' -export * from './entries' -export * from './find' -export * from './flat' -export * from './indexPath' -export * from './insert' -export * from './map' -export * from './move' -export * from './options' -export * from './reduce' -export * from './remove' -export * from './replace' -export * from './sort' -export * from './splice' -export * from './transformPath' -export * from './visit' -export * from './withOptions' +export * from './access.js' +export * from './ancestors.js' +export * from './defineTree.js' +export * from './diagram.js' +export * from './entries.js' +export * from './find.js' +export * from './flat.js' +export * from './indexPath.js' +export * from './insert.js' +export * from './map.js' +export * from './move.js' +export * from './options.js' +export * from './reduce.js' +export * from './remove.js' +export * from './replace.js' +export * from './sort.js' +export * from './splice.js' +export * from './transformPath.js' +export * from './visit.js' +export * from './withOptions.js' diff --git a/src/insert.ts b/src/insert.ts index 97d54ac..8f97a5f 100644 --- a/src/insert.ts +++ b/src/insert.ts @@ -1,10 +1,10 @@ -import { IndexPath } from './indexPath' +import { IndexPath } from './indexPath.js' import { applyOperations, getInsertionOperations, transformPathsByOperations, -} from './operation' -import { MutationBaseOptions } from './options' +} from './operation.js' +import { MutationBaseOptions } from './options.js' export type InsertOptions = MutationBaseOptions & { nodes: T[] diff --git a/src/map.ts b/src/map.ts index 8720a80..0329015 100644 --- a/src/map.ts +++ b/src/map.ts @@ -1,6 +1,6 @@ -import { IndexPath } from './indexPath' -import { BaseOptions } from './options' -import { visit } from './visit' +import { IndexPath } from './indexPath.js' +import { BaseOptions } from './options.js' +import { visit } from './visit.js' export type MapOptions = BaseOptions & { /** diff --git a/src/move.ts b/src/move.ts index 2d753c3..1731e11 100644 --- a/src/move.ts +++ b/src/move.ts @@ -1,12 +1,12 @@ -import { access } from './access' -import { ancestorPaths } from './ancestors' -import { IndexPath } from './indexPath' +import { access } from './access.js' +import { ancestorPaths } from './ancestors.js' +import { IndexPath } from './indexPath.js' import { applyOperations, getInsertionOperations, getRemovalOperations, -} from './operation' -import { MutationBaseOptions } from './options' +} from './operation.js' +import { MutationBaseOptions } from './options.js' export type MoveOptions = MutationBaseOptions & { paths: IndexPath[] diff --git a/src/operation.ts b/src/operation.ts index 22da5ab..a5309d4 100644 --- a/src/operation.ts +++ b/src/operation.ts @@ -1,8 +1,8 @@ -import { ancestorPaths } from './ancestors' -import { IndexPath } from './indexPath' -import { map } from './map' -import { MutationBaseOptions } from './options' -import { transformPath } from './transformPath' +import { ancestorPaths } from './ancestors.js' +import { IndexPath } from './indexPath.js' +import { map } from './map.js' +import { MutationBaseOptions } from './options.js' +import { transformPath } from './transformPath.js' export type NodeOperation = | { diff --git a/src/options.ts b/src/options.ts index 23f19c6..40519f8 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,4 +1,4 @@ -import { IndexPath } from './indexPath' +import { IndexPath } from './indexPath.js' export type TraversalContext = { getRoot(): T diff --git a/src/package.json b/src/package.json new file mode 100644 index 0000000..52a3a1e --- /dev/null +++ b/src/package.json @@ -0,0 +1,4 @@ +{ + "type": "module" +} + diff --git a/src/reduce.ts b/src/reduce.ts index eb6c7de..9306fb7 100644 --- a/src/reduce.ts +++ b/src/reduce.ts @@ -1,6 +1,6 @@ -import { IndexPath } from './indexPath' -import { BaseOptions } from './options' -import { visit } from './visit' +import { IndexPath } from './indexPath.js' +import { BaseOptions } from './options.js' +import { visit } from './visit.js' export type ReduceOptions = BaseOptions & { /** diff --git a/src/remove.ts b/src/remove.ts index bb27c03..73cacfd 100644 --- a/src/remove.ts +++ b/src/remove.ts @@ -1,10 +1,10 @@ -import { IndexPath } from './indexPath' +import { IndexPath } from './indexPath.js' import { applyOperations, getRemovalOperations, transformPathsByOperations, -} from './operation' -import { MutationBaseOptions } from './options' +} from './operation.js' +import { MutationBaseOptions } from './options.js' export type RemoveOptions = MutationBaseOptions & { paths: IndexPath[] diff --git a/src/replace.ts b/src/replace.ts index 3dbcb47..c3c0e55 100644 --- a/src/replace.ts +++ b/src/replace.ts @@ -1,6 +1,6 @@ -import { IndexPath } from './indexPath' -import { applyOperations, getReplaceOperations } from './operation' -import { MutationBaseOptions } from './options' +import { IndexPath } from './indexPath.js' +import { applyOperations, getReplaceOperations } from './operation.js' +import { MutationBaseOptions } from './options.js' export type ReplaceOptions = MutationBaseOptions & { path: IndexPath diff --git a/src/sort.ts b/src/sort.ts index 7071794..90bf82a 100644 --- a/src/sort.ts +++ b/src/sort.ts @@ -1,5 +1,5 @@ -import { IndexPath } from './indexPath' -import { KeyPath } from './types' +import { IndexPath } from './indexPath.js' +import { KeyPath } from './types.js' export function comparePathsByComponent( a: TPath, diff --git a/src/splice.ts b/src/splice.ts index 25407c7..9b51399 100644 --- a/src/splice.ts +++ b/src/splice.ts @@ -1,11 +1,11 @@ -import { IndexPath } from './indexPath' +import { IndexPath } from './indexPath.js' import { applyOperations, getInsertionOperations, getRemovalOperations, transformPathsByOperations, -} from './operation' -import { MutationBaseOptions } from './options' +} from './operation.js' +import { MutationBaseOptions } from './options.js' export type SpliceOptions = MutationBaseOptions & { path: IndexPath diff --git a/src/transformPath.ts b/src/transformPath.ts index 1b244b3..a7e46e3 100644 --- a/src/transformPath.ts +++ b/src/transformPath.ts @@ -1,5 +1,5 @@ -import { IndexPath } from './indexPath' -import { comparePathsByComponent } from './sort' +import { IndexPath } from './indexPath.js' +import { comparePathsByComponent } from './sort.js' type TransformPathOperation = 'insert' | 'remove' diff --git a/src/types.ts b/src/types.ts index b3c031d..bb9bb73 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,16 +1,13 @@ export type KeyPath = string[] -export type ExtractRequiredKeys = { - [K in keyof T]-?: undefined extends T[K] ? never : K -}[keyof T] - export type OptionCheck< AppliedOptions, OptionKey extends string, - R extends { [K in OptionKey]: any } -> = AppliedOptions extends Record - ? { [K in OptionKey]?: R[K] } - : { [K in OptionKey]: R[K] } + R extends { [K in OptionKey]: any }, +> = + AppliedOptions extends Record + ? { [K in OptionKey]?: R[K] } + : { [K in OptionKey]: R[K] } /** * Improve the type displayed in tooltips by flattening utility types. diff --git a/src/visit.ts b/src/visit.ts index 4efd0e6..3ab4460 100644 --- a/src/visit.ts +++ b/src/visit.ts @@ -1,5 +1,5 @@ -import { IndexPath } from './indexPath' -import { BaseOptions, TraversalContext, TraversalDirection } from './options' +import { IndexPath } from './indexPath.js' +import { BaseOptions, TraversalContext, TraversalDirection } from './options.js' export const SKIP = 'skip' export const STOP = 'stop' diff --git a/src/withOptions.ts b/src/withOptions.ts index 8bb8e18..430d1cb 100644 --- a/src/withOptions.ts +++ b/src/withOptions.ts @@ -1,4 +1,4 @@ -import { defineTree } from './defineTree' +import { defineTree } from './defineTree.js' /** * Return every tree utility function with options partially applied. diff --git a/tsconfig.esm.json b/tsconfig.esm.json index ba10045..6abed2d 100644 --- a/tsconfig.esm.json +++ b/tsconfig.esm.json @@ -1,8 +1,8 @@ { "extends": "./tsconfig.base.json", "compilerOptions": { - "module": "esnext", - "moduleResolution": "node", + "module": "NodeNext", + "moduleResolution": "NodeNext", "outDir": "./lib/esm", "declaration": false, "emitDeclarationOnly": false