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
2 changes: 1 addition & 1 deletion packages/table-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"test:lib:dev": "pnpm test:lib --watch",
"test:types": "tsc && tsc -p tests/tsconfig.declaration-emit.json",
"test:build": "publint --strict",
"build": "tsdown"
"build": "tsdown && node ../../scripts/rewrite-table-core-dts.mjs"
},
"dependencies": {
"@tanstack/store": "^0.11.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CellData, RowData } from '../../types/type-utils'
import type { TableFeatures } from '../../types/TableFeatures'
import type { Table } from '../../types/Table'
import type { Table, Table_Internal } from '../../types/Table'
import type { Header } from '../../types/Header'
import type { HeaderGroup } from '../../types/HeaderGroup'
import type { Column } from '../../types/Column'
Expand Down Expand Up @@ -96,7 +96,7 @@ export interface Header_CoreProperties<
/**
* Reference to the parent table instance.
*/
table: Table<TFeatures, TData>
table: Table_Internal<TFeatures, TData>
}

export interface Header_Header<
Expand Down
4 changes: 3 additions & 1 deletion packages/table-core/src/types/Column.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Table_Internal } from './Table'
import type { Column_RowSorting } from '../features/row-sorting/rowSortingFeature.types'
import type { Column_ColumnFaceting } from '../features/column-faceting/columnFacetingFeature.types'
import type { Column_ColumnFiltering } from '../features/column-filtering/columnFilteringFeature.types'
Expand Down Expand Up @@ -46,6 +47,7 @@ export interface Column_Internal<
in out TFeatures extends TableFeatures,
in out TData extends RowData,
TValue = unknown,
> extends Omit<Column_Core<TFeatures, TData, TValue>, 'columnDef'> {
> extends Omit<Column_Core<TFeatures, TData, TValue>, 'columnDef' | 'table'> {
columnDef: ColumnDefBase_All<TFeatures, TData, TValue>
table: Table_Internal<TFeatures, TData>
}
191 changes: 191 additions & 0 deletions scripts/rewrite-table-core-dts.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'

const packageRoot = fileURLToPath(
new URL('../packages/table-core/', import.meta.url),
)
const distDir = join(packageRoot, 'dist')
const forbiddenTypeNames = ['Table_Internal', 'Column_Internal']

function walkDeclarationFiles(dir) {
const files = []

for (const entry of readdirSync(dir)) {
const path = join(dir, entry)
const stat = statSync(path)

if (stat.isDirectory()) {
files.push(...walkDeclarationFiles(path))
continue
}

if (path.endsWith('.d.ts') || path.endsWith('.d.cts')) {
files.push(path)
}
}

return files
}

function removeExportedInterface(source, interfaceName) {
let start = source.indexOf(`export interface ${interfaceName}`)

if (start === -1) {
start = source.indexOf(`interface ${interfaceName}`)
}

if (start === -1) {
return source
}

const bodyStart = source.indexOf('{', start)

if (bodyStart === -1) {
throw new Error(`Could not find body for ${interfaceName}`)
}

let depth = 0

for (let i = bodyStart; i < source.length; i++) {
const char = source[i]

if (char === '{') {
depth++
} else if (char === '}') {
depth--

if (depth === 0) {
let end = i + 1

while (source[end] === '\n' || source[end] === '\r') {
end++
}

return source.slice(0, start) + source.slice(end)
}
}
}

throw new Error(`Could not find end of ${interfaceName}`)
}

function removeTypeAlias(source, typeName) {
const aliasPattern = new RegExp(
String.raw`\b(?:export\s+)?type\s+${typeName}\b`,
)
const match = aliasPattern.exec(source)

if (!match) {
return source
}

const start = match.index
const end = source.indexOf(';', start)

if (end === -1) {
throw new Error(`Could not find end of ${typeName}`)
}

return source.slice(0, start) + source.slice(end + 1)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

function getSpecifierName(specifier) {
return specifier
.replace(/^type\s+/, '')
.split(/\s+as\s+/)[0]
?.trim()
}

function removeNamedSpecifiers(source, names) {
return source
.replace(
/\b(import|export)(\s+type)?\s+\{([^}]+)\}\s+from\s+([^;\n]+);/g,
(statement, kind, typeKeyword = '', specifiers, fromClause) => {
const nextSpecifiers = specifiers
.split(',')
.map((specifier) => specifier.trim())
.filter(Boolean)
.filter((specifier) => {
const importedName = getSpecifierName(specifier)
return importedName && !names.includes(importedName)
})

if (!nextSpecifiers.length) {
return ''
}

return `${kind}${typeKeyword} { ${nextSpecifiers.join(
', ',
)} } from ${fromClause};`
},
)
.replace(
/\bexport(\s+type)?\s+\{([^}]+)\};/g,
(statement, typeKeyword = '', specifiers) => {
const nextSpecifiers = specifiers
.split(',')
.map((specifier) => specifier.trim())
.filter(Boolean)
.filter((specifier) => {
const exportedName = getSpecifierName(specifier)
return exportedName && !names.includes(exportedName)
})

if (!nextSpecifiers.length) {
return ''
}

return `export${typeKeyword} { ${nextSpecifiers.join(', ')} };`
},
)
}

function rewriteDeclaration(source) {
let next = source

for (const typeName of forbiddenTypeNames) {
next = removeExportedInterface(next, typeName)
}

next = removeTypeAlias(next, 'Table_InternalBroadenedKeys')
next = removeNamedSpecifiers(next, forbiddenTypeNames)

next = next.replaceAll('Table_Internal<', 'Table<')
next = next.replaceAll('Column_Internal<', 'Column<')
next = next.replaceAll('Table_Internal', 'Table')
next = next.replaceAll('Column_Internal', 'Column')

return next
}

const files = walkDeclarationFiles(distDir)

for (const file of files) {
const source = readFileSync(file, 'utf8')
const next = rewriteDeclaration(source)

if (next !== source) {
writeFileSync(file, next)
}
}

const leaks = []

for (const file of files) {
const source = readFileSync(file, 'utf8')

for (const typeName of forbiddenTypeNames) {
if (source.includes(typeName)) {
leaks.push(`${file}: ${typeName}`)
}
}
}

if (leaks.length) {
throw new Error(
`Internal table-core types leaked into emitted declarations:\n${leaks.join(
'\n',
)}`,
)
}
Loading