Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/lean-trees-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@tanstack/table-core': patch
'@tanstack/table-devtools': patch
---

Improve tree-shaking. Importing one built-in `filterFn_*`, `sortFn_*`, or `aggregationFn_*` now bundles only that function instead of its whole module. The core row model, header building, and the filtered, sorted, and grouped row models no longer import code from features a table doesn't register, so a minimal table is about 0.6 kB (brotli) smaller and grouping without `rowAggregationFeature` no longer bundles the aggregation executor. Add `table.autoResetSorting()`, and fix the `Table_RowPagination` type to declare the `autoResetPageIndex` method the table actually has (it was typed as `_autoResetPageIndex`).

The devtools Features panel now shows measured minified + brotli sizes for every feature, row model, and built-in function, and its total accounts for code that features share.
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"lint:fix:all": "pnpm run format && nx run-many --targets=lint --fix",
"lint:fix": "nx affected --target=lint:fix --exclude=examples/**",
"size": "size-limit",
"size:devtools": "node scripts/generate-bundle-sizes.mjs",
"test": "pnpm run test:ci",
"test:build": "nx affected --target=test:build",
"test:ci": "pnpm run test:compiler-examples && nx run-many --targets=test:eslint,test:sherif,test:knip,test:lib,test:types,test:build,build",
Expand Down Expand Up @@ -62,8 +63,33 @@
},
"size-limit": [
{
"name": "@tanstack/table-core (everything)",
"path": "packages/table-core/dist/index.js",
"limit": "30 KB"
},
{
"name": "constructTable (core features only)",
"path": "packages/table-core/dist/index.js",
"import": "{ constructTable }",
"limit": "6 KB"
},
{
"name": "filterFn_includesString (tree-shaking)",
"path": "packages/table-core/dist/index.js",
"import": "{ filterFn_includesString }",
"limit": "300 B"
},
{
"name": "sortFn_text (tree-shaking)",
"path": "packages/table-core/dist/index.js",
"import": "{ sortFn_text }",
"limit": "300 B"
},
{
"name": "aggregationFn_sum (tree-shaking)",
"path": "packages/table-core/dist/index.js",
"import": "{ aggregationFn_sum }",
"limit": "300 B"
}
],
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function column_getLeafColumns<

return callMemoOrStaticFn(
column.table,
'getOrderColumns',
'getOrderColumnsFn',
table_getOrderColumnsFn,
)(leafColumns as any) as any
}
Expand Down Expand Up @@ -229,7 +229,7 @@ export function table_getAllLeafColumns<
)
return callMemoOrStaticFn(
table,
'getOrderColumns',
'getOrderColumnsFn',
table_getOrderColumnsFn,
)(leafColumns) as Array<Column<TFeatures, TData, unknown>>
}
Expand Down
128 changes: 128 additions & 0 deletions packages/table-core/src/core/featureStubs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import type { Table_CellSelection } from '../features/cell-selection/cellSelectionFeature.types'
import type {
Column_ColumnVisibility,
Table_ColumnVisibility,
} from '../features/column-visibility/columnVisibilityFeature.types'
import type {
Column_GlobalFiltering,
Table_GlobalFiltering,
} from '../features/global-filtering/globalFilteringFeature.types'
import type { FilterFn } from '../features/column-filtering/columnFilteringFeature.types'
import type { aggregateColumnValue } from '../features/row-aggregation/rowAggregationFeature.utils'
import type { Table_RowExpanding } from '../features/row-expanding/rowExpandingFeature.types'
import type { Table_RowPagination } from '../features/row-pagination/rowPaginationFeature.types'
import type { Table_RowSorting } from '../features/row-sorting/rowSortingFeature.types'
import type { Column } from '../types/Column'
import type { Table_Internal } from '../types/Table'
import type { TableFeatures } from '../types/TableFeatures'
import type { RowData } from '../types/type-utils'

/**
* Stubs for optional features, used by the core and by row models that
* another feature owns.
*
* Importing a feature's utils from shared code would bundle that feature into
* every table that uses the shared code, registered or not. Each stub calls
* the API the feature registers on the table or column when it is present,
* and otherwise behaves as if the feature were absent.
*
* Internal: not exported from any package entry point.
*/

// Column visibility

/** Whether a column is visible; every column is without the feature. */
export function getIsColumnVisible(column: object): boolean {
return (column as Partial<Column_ColumnVisibility>).getIsVisible?.() ?? true
}

/** Visible leaf columns; all leaf columns without the feature. */
export function getVisibleLeafColumns<
TFeatures extends TableFeatures,
TData extends RowData,
>(
table: Table_Internal<TFeatures, TData>,
): Array<Column<TFeatures, TData, unknown>> {
return (
(
table as Partial<Table_ColumnVisibility<TFeatures, TData>>
).getVisibleLeafColumns?.() ?? table.getAllLeafColumns()
)
}

// Global filtering

/** The resolved global filter function, if the feature is registered. */
export function getGlobalFilterFn<
TFeatures extends TableFeatures,
TData extends RowData,
>(
table: Table_Internal<TFeatures, TData>,
): FilterFn<TFeatures, TData> | undefined {
return (
table as Partial<Table_GlobalFiltering<TFeatures, TData>>
).getGlobalFilterFn?.()
}

/** Whether a column takes part in global filtering; none do without the feature. */
export function getCanGlobalFilter(column: object): boolean {
return (
(column as Partial<Column_GlobalFiltering>).getCanGlobalFilter?.() ?? false
)
}

// Auto resets

/** Resets expanded state if row expanding is registered. */
export function autoResetExpanded<
TFeatures extends TableFeatures,
TData extends RowData,
>(table: Table_Internal<TFeatures, TData>): void {
;(
table as Partial<Table_RowExpanding<TFeatures, TData>>
).autoResetExpanded?.()
}

/** Resets the page index if row pagination is registered. */
export function autoResetPageIndex<
TFeatures extends TableFeatures,
TData extends RowData,
>(table: Table_Internal<TFeatures, TData>): void {
;(
table as Partial<Table_RowPagination<TFeatures, TData>>
).autoResetPageIndex?.()
}

/** Resets sorting if row sorting is registered. */
export function autoResetSorting<
TFeatures extends TableFeatures,
TData extends RowData,
>(table: Table_Internal<TFeatures, TData>): void {
;(table as Partial<Table_RowSorting<TFeatures, TData>>).autoResetSorting?.()
}

/** Resets cell selection if cell selection is registered. */
export function autoResetCellSelection<
TFeatures extends TableFeatures,
TData extends RowData,
>(table: Table_Internal<TFeatures, TData>): void {
;(
table as Partial<Table_CellSelection<TFeatures, TData>>
).autoResetCellSelection?.()
}

// Row aggregation

/**
* The aggregation executor `rowAggregationFeature` stores on the table, or
* `undefined` when the feature is not registered.
*/
export function getAggregateColumnValue<
TFeatures extends TableFeatures,
TData extends RowData,
>(
table: Table_Internal<TFeatures, TData>,
): typeof aggregateColumnValue | undefined {
return (table as { _aggregateColumnValue?: typeof aggregateColumnValue })
._aggregateColumnValue
}
16 changes: 4 additions & 12 deletions packages/table-core/src/core/headers/buildHeaderGroups.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { callMemoOrStaticFn } from '../../utils'
import { column_getIsVisible } from '../../features/column-visibility/columnVisibilityFeature.utils'
import { getIsColumnVisible } from '../featureStubs'
import { constructHeader } from './constructHeader'
import type { Table_Internal } from '../../types/Table'
import type { CellData, RowData } from '../../types/type-utils'
Expand All @@ -19,10 +18,7 @@ function getMaxHeaderDepth<

for (let i = 0; i < columns.length; i++) {
const column = columns[i]!
if (
callMemoOrStaticFn(column, 'getIsVisible', column_getIsVisible) &&
column.columns.length
) {
if (getIsColumnVisible(column) && column.columns.length) {
maxDepth = Math.max(
maxDepth,
getMaxHeaderDepth(column.columns, depth + 1),
Expand Down Expand Up @@ -168,9 +164,7 @@ function updateHeaderSpans<
>(headers: Array<Header<TFeatures, TData, TValue>>): void {
for (let i = 0; i < headers.length; i++) {
const header = headers[i]!
if (
!callMemoOrStaticFn(header.column, 'getIsVisible', column_getIsVisible)
) {
if (!getIsColumnVisible(header.column)) {
continue
}

Expand All @@ -181,9 +175,7 @@ function updateHeaderSpans<

for (let j = 0; j < header.subHeaders.length; j++) {
const child = header.subHeaders[j]!
if (
!callMemoOrStaticFn(child.column, 'getIsVisible', column_getIsVisible)
) {
if (!getIsColumnVisible(child.column)) {
continue
}

Expand Down
30 changes: 9 additions & 21 deletions packages/table-core/src/core/headers/coreHeadersFeature.utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import { getDefaultColumnPinningState } from '../../features/column-pinning/columnPinningFeature.utils'
import {
column_getIsVisible,
table_getVisibleLeafColumns,
} from '../../features/column-visibility/columnVisibilityFeature.utils'
import { callMemoOrStaticFn } from '../../utils'
import { getIsColumnVisible, getVisibleLeafColumns } from '../featureStubs'
import { buildHeaderGroups } from './buildHeaderGroups'
import type { Table_Internal } from '../../types/Table'
import type { Header } from '../../types/Header'
Expand Down Expand Up @@ -86,14 +81,13 @@ export function table_getHeaderGroups<
TFeatures extends TableFeatures,
TData extends RowData,
>(table: Table_Internal<TFeatures, TData>) {
const { start, end } =
table.atoms.columnPinning?.get() ?? getDefaultColumnPinningState()
// Pinning and visibility are optional features; without them nothing is
// pinned and every leaf column is visible
const columnPinning = table.atoms.columnPinning?.get()
const start = columnPinning?.start ?? []
const end = columnPinning?.end ?? []
const allColumns = table.getAllColumns()
const leafColumns = callMemoOrStaticFn(
table,
'getVisibleLeafColumns',
table_getVisibleLeafColumns,
)
const leafColumns = getVisibleLeafColumns(table)

// Fast path: no columns are pinned — skip per-side lookups, partition, and spread.
if (!start.length && !end.length) {
Expand All @@ -105,21 +99,15 @@ export function table_getHeaderGroups<
const leftColumns: typeof leafColumns = []
for (let i = 0; i < start.length; i++) {
const column = leafColumnsById[start[i]!]
if (
column &&
callMemoOrStaticFn(column, 'getIsVisible', column_getIsVisible)
) {
if (column && getIsColumnVisible(column)) {
leftColumns.push(column)
}
}

const rightColumns: typeof leafColumns = []
for (let i = 0; i < end.length; i++) {
const column = leafColumnsById[end[i]!]
if (
column &&
callMemoOrStaticFn(column, 'getIsVisible', column_getIsVisible)
) {
if (column && getIsColumnVisible(column)) {
rightColumns.push(column)
}
}
Expand Down
18 changes: 10 additions & 8 deletions packages/table-core/src/core/row-models/createCoreRowModel.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { constructRow } from '../rows/constructRow'
import { makeObjectMap, skipFirstRun, tableMemo } from '../../utils'
import { table_autoResetCellSelection } from '../../features/cell-selection/cellSelectionFeature.utils'
import { table_autoResetExpanded } from '../../features/row-expanding/rowExpandingFeature.utils'
import { table_autoResetPageIndex } from '../../features/row-pagination/rowPaginationFeature.utils'
import { table_autoResetSorting } from '../../features/row-sorting/rowSortingFeature.utils'
import {
autoResetCellSelection,
autoResetExpanded,
autoResetPageIndex,
autoResetSorting,
} from '../featureStubs'
import type { Table_Internal } from '../../types/Table'
import type { RowModel } from './coreRowModelsFeature.types'
import type { TableFeatures } from '../../types/TableFeatures'
Expand All @@ -29,10 +31,10 @@ export function createCoreRowModel<
memoDeps: () => [table.options.data],
fn: () => _createCoreRowModel(table, table.options.data),
onAfterUpdate: skipFirstRun(() => {
table_autoResetExpanded(table)
table_autoResetPageIndex(table)
table_autoResetSorting(table)
table_autoResetCellSelection(table)
autoResetExpanded(table)
autoResetPageIndex(table)
autoResetSorting(table)
autoResetCellSelection(table)
}),
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import type {

// Span index

const EMPTY_ROW_SPANS: Record<string, Int32Array> = makeObjectMap()
const EMPTY_COLUMN_INDEXES: Record<string, number> = makeObjectMap()
const EMPTY_ROW_SPANS: Record<string, Int32Array> =
/* @__PURE__ */ makeObjectMap()
const EMPTY_COLUMN_INDEXES: Record<string, number> =
/* @__PURE__ */ makeObjectMap()
const EMPTY_COL_SPANS: Array<Int32Array | undefined> = []
const NO_SECTION_STARTS: ReadonlyArray<number> = []

Expand Down
Loading
Loading