Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/guard-process-global.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/table-core': patch
---

Guard the dev-only `process.env.NODE_ENV` checks with `typeof process !== 'undefined'` so the published ESM build runs in environments without a `process` global, such as browsers loading the package through an import map.
11 changes: 9 additions & 2 deletions packages/table-core/src/core/columns/constructColumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ export function constructColumn<
for (let i = 0; i < keys.length; i++) {
const key = keys[i]!
result = result?.[key]
if (process.env.NODE_ENV === 'development' && result === undefined) {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
result === undefined
) {
console.warn(
`"${key}" in deeply nested key "${accessorKey}" returned undefined.`,
)
Expand All @@ -90,7 +94,10 @@ export function constructColumn<
}

if (!id) {
if (process.env.NODE_ENV === 'development') {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development'
) {
throw new Error(
resolvedColumnDef.accessorFn
? `coreColumnsFeature require an id when using an accessorFn`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,11 @@ export function table_getColumn<
): Column<TFeatures, TData, unknown> | undefined {
const column = table.getAllFlatColumnsById()[columnId]

if (process.env.NODE_ENV === 'development' && !column) {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
!column
) {
console.warn(`[Table] Column with id '${columnId}' does not exist.`)
}

Expand Down
5 changes: 4 additions & 1 deletion packages/table-core/src/core/rows/coreRowsFeature.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,10 @@ export function table_getRow<
if (!row) {
row = table.getCoreRowModel().rowsById[rowId]
if (!row) {
if (process.env.NODE_ENV === 'development') {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development'
) {
throw new Error(`getRow could not find row with ID: ${rowId}`)
}
throw new Error()
Expand Down
1 change: 1 addition & 0 deletions packages/table-core/src/core/table/constructTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export function constructTable<
}

if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
(tableOptions.debugAll || tableOptions.debugTable)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ export function column_getAutoFilterFn<

const filterFn = filterFns?.[filterFnName]

if (process.env.NODE_ENV === 'development' && !filterFn) {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
!filterFn
) {
console.warn(
`filterFn '${filterFnName}' (auto) for column '${column.id}' is not registered`,
)
Expand Down Expand Up @@ -123,6 +127,7 @@ export function column_getFilterFn<
: filterFns?.[column.columnDef.filterFn as string]

if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
!filterFn &&
column.columnDef.filterFn !== 'auto' // the auto picker warns on its own
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function table_getGlobalFilterFn<
: filterFns?.[globalFilterFn as string]

if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
!filterFn &&
globalFilterFn != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ function isAggregationFnDescriptor(
}

function warn(message: string) {
if (process.env.NODE_ENV === 'development') {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development'
) {
console.warn(message)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ export function column_getAutoSortFn<
let sortFn = sortFns?.[sortFnName]

if (!sortFn) {
if (process.env.NODE_ENV === 'development') {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development'
) {
console.warn(
`sortFn '${sortFnName}' (auto) for column '${column.id}' is not registered`,
)
Expand Down Expand Up @@ -230,7 +233,11 @@ export function column_getSortFn<

const sortFn = sortFns?.[column.columnDef.sortFn as string]

if (process.env.NODE_ENV === 'development' && !sortFn) {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
!sortFn
) {
console.warn(
`sortFn '${String(column.columnDef.sortFn)}' for column '${column.id}' is not registered`,
)
Expand Down
7 changes: 5 additions & 2 deletions packages/table-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,10 @@ export function tableMemo<
let debug: boolean | undefined
let debugCache: boolean | undefined

if (process.env.NODE_ENV === 'development') {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development'
) {
const { debugAll } = table.options
const { parentName } = getFunctionNameInfo(fnName, '.')

Expand Down Expand Up @@ -467,7 +470,7 @@ export function tableMemo<
}

const debugOptions =
process.env.NODE_ENV === 'development'
typeof process !== 'undefined' && process.env.NODE_ENV === 'development'
? {
onBeforeCompare: () => {
if (debugCache) {
Expand Down
6 changes: 5 additions & 1 deletion packages/table-core/src/worker/createWorkerRowModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ export function createWorkerRowModel(
let warned = false

const warnOnce = (message: string) => {
if (process.env.NODE_ENV === 'development' && !warned) {
if (
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
!warned
) {
warned = true
console.warn(`[table-worker] ${message}`)
}
Expand Down
218 changes: 218 additions & 0 deletions packages/table-core/tests/unit/core/table/processGlobal.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
columnFilteringFeature,
constructTable,
createFilteredRowModel,
createSortedRowModel,
globalFilteringFeature,
rowAggregationFeature,
rowSortingFeature,
} from '../../../../src'
import { testFeatures } from '../../../fixtures/features'
import {
createTableWorker,
createWorkerRowModel,
workerRowModelsFeature,
} from '../../../../src/experimental-worker-plugin'
import type { ColumnDef } from '../../../../src'

const features = testFeatures({})

const processingFeatures = testFeatures({
columnFilteringFeature,
globalFilteringFeature,
rowAggregationFeature,
rowSortingFeature,
filteredRowModel: createFilteredRowModel(),
sortedRowModel: createSortedRowModel(),
})

type Person = { firstName: string; age: number }

const data: Array<Person> = [
{ firstName: 'alice', age: 30 },
{ firstName: 'bob', age: 40 },
]

// No `filterFns`/`sortFns`/`aggregationFns` registries are provided, so every
// lookup misses and takes the dev-only warning branch.
const columns: Array<ColumnDef<typeof processingFeatures, Person, any>> = [
{ accessorKey: 'firstName', id: 'firstName' },
{
accessorKey: 'age',
id: 'age',
aggregationFn: 'unregistered' as any,
filterFn: 'unregistered' as any,
sortFn: 'unregistered' as any,
},
]

afterEach(() => {
vi.unstubAllGlobals()
})

describe('dev-only guards without a `process` global', () => {
it('constructs a table when `process` is not defined', () => {
vi.stubGlobal('process', undefined)

expect(() =>
constructTable({
features,
columns: [],
data: [],
debugTable: true,
}),
).not.toThrow()
})

it('looks up a missing column when `process` is not defined', () => {
const table = constructTable({
features,
columns: [],
data: [],
})

vi.stubGlobal('process', undefined)

expect(() => table.getColumn('missing')).not.toThrow()
})

it('looks up a missing row when `process` is not defined', () => {
const table = constructTable({ features, columns: [], data: [] })

vi.stubGlobal('process', undefined)

expect(() => table.getRow('missing')).not.toThrow(TypeError)
})

it('constructs columns with unresolvable accessors when `process` is not defined', () => {
vi.stubGlobal('process', undefined)

const table = constructTable<typeof features, { details: {} }>({
features,
columns: [{ accessorKey: 'details.missing.deep', id: 'deep' }],
data: [{ details: {} }],
})

expect(() =>
table.getCoreRowModel().flatRows[0]!.getValue('deep'),
).not.toThrow()

expect(() =>
constructTable({
features,
// @ts-expect-error - an accessorFn column needs an explicit id
columns: [{ accessorFn: (row: Person) => row.firstName }],
data,
}).getAllColumns(),
).not.toThrow(TypeError)
})

it('sorts by unregistered sort fns when `process` is not defined', () => {
const table = constructTable({
features: processingFeatures,
columns,
data,
})

vi.stubGlobal('process', undefined)

table.setSorting([
{ id: 'firstName', desc: false },
{ id: 'age', desc: true },
])

expect(() => table.getSortedRowModel()).not.toThrow()
})

it('applies column filters with unregistered filter fns when `process` is not defined', () => {
const table = constructTable({
features: processingFeatures,
columns,
data,
})

vi.stubGlobal('process', undefined)

table.setColumnFilters([
{ id: 'firstName', value: 'a' },
{ id: 'age', value: 30 },
])

expect(() => table.getFilteredRowModel()).not.toThrow()
})

it('applies a global filter with an unregistered filter fn when `process` is not defined', () => {
const table = constructTable({
features: processingFeatures,
columns,
data,
globalFilterFn: 'unregistered' as any,
})

vi.stubGlobal('process', undefined)

table.setGlobalFilter('a')

expect(() => table.getFilteredRowModel()).not.toThrow()
})

it('resolves unregistered aggregation fns when `process` is not defined', () => {
const table = constructTable({
features: processingFeatures,
columns,
data,
})

vi.stubGlobal('process', undefined)

expect(() => table.getColumn('age')!.getAggregationFns()).not.toThrow()
expect(() =>
table.getColumn('firstName')!.getAutoAggregationFn(),
).not.toThrow()
})

it('reads a worker row model when `process` is not defined', () => {
const posted: Array<any> = []
class FakeWorker {
onmessage: ((event: { data: any }) => void) | null = null
postMessage(message: any) {
posted.push(message)
}
terminate() {}
}
let worker: FakeWorker
const tableWorker = createTableWorker({
createWorker: () => (worker = new FakeWorker()) as unknown as Worker,
})
vi.stubGlobal('Worker', FakeWorker)
const table = constructTable({
features: testFeatures({
columnFilteringFeature,
workerRowModelsFeature,
filteredRowModel: createWorkerRowModel(tableWorker, 'filtered'),
}),
columns: [{ accessorKey: 'firstName', id: 'firstName' }],
data,
})

vi.stubGlobal('process', undefined)

table.getFilteredRowModel()
const request = posted
.filter((message) => message.type === 'process')
.at(-1)
worker!.onmessage?.({
data: {
type: 'result',
requestId: request.requestId,
dataVersion: request.dataVersion,
stages: {},
computeMs: 1,
},
})
table.setOptions((prev) => ({ ...prev, data: [...data] }))

expect(() => table.getFilteredRowModel()).not.toThrow()
})
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.