Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: snapshot types that we want to not accidentally deprecate or remove #1705

Merged
merged 7 commits into from
Jan 30, 2025

Conversation

pauldambra
Copy link
Member

we accidentally removed some options from a config in another PR

computers should protect us

this is 99% ChatGPT so review accordingly 🙈

this converts provided types into a dict and snapshots that

this should let us known when we're accidentally breaking types for consumers

Copy link

vercel bot commented Jan 30, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
posthog-js ✅ Ready (Inspect) Visit Preview Jan 30, 2025 2:28pm

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR introduces type snapshot testing to prevent accidental breaking changes to public TypeScript interfaces, using the TypeScript Compiler API to extract and validate type definitions.

  • Added src/__tests__/config-snapshot.test.ts to snapshot test critical interfaces like PostHogConfig, AutocaptureConfig, and SessionRecordingOptions
  • Implemented type extraction using TypeScript's Compiler API to convert interface definitions into serializable JSON structures
  • Test focuses on three key configuration interfaces that are most important for external consumers
  • Type extraction logic could be enhanced to better handle complex types like unions and intersections

1 file(s) reviewed, 4 comment(s)
Edit PR Review Bot Settings | Greptile

import ts from 'typescript'

function extractTypeInfo(filePath: string, typeName: string): string {
const program = ts.createProgram([filePath], {})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: createProgram() without compiler options may miss important type information. Should include at least noImplicitAny and strictNullChecks

Comment on lines 16 to 30
function processType(type: ts.Type): any {
if (type.isUnion() || type.isIntersection()) {
return type.types.map(getTypeString)
} else if (type.isClassOrInterface()) {
const result: Record<string, any> = {}
type.getProperties().forEach((symbol) => {
const propType = checker.getTypeOfSymbol(symbol)
result[symbol.getName()] = processType(propType)
})
return result
} else if (type.symbol && type.symbol.valueDeclaration) {
return getTypeString(type)
}
return getTypeString(type)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: processType() doesn't handle type aliases, enums, or literal types correctly. These types will be stringified instead of preserving their structure

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be good to fix :)

Comment on lines +34 to +39
ts.forEachChild(sourceFile, (node) => {
if (ts.isInterfaceDeclaration(node) && node.name.text === typeName) {
const type = checker.getTypeAtLocation(node)
result = processType(type)
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: forEachChild only processes the first matching interface. If there are multiple interfaces with the same name in different namespaces/modules, this could miss them

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad I'm as smart as an AI, I noticed the same. I don't think this is ever going to happen - it shouldn't - so I'm fine to keep this as is

src/__tests__/config-snapshot.test.ts Outdated Show resolved Hide resolved
Copy link

github-actions bot commented Jan 30, 2025

Size Change: 0 B

Total Size: 3.28 MB

ℹ️ View Unchanged
Filename Size
dist/all-external-dependencies.js 215 kB
dist/array.full.es5.js 266 kB
dist/array.full.js 369 kB
dist/array.full.no-external.js 368 kB
dist/array.js 181 kB
dist/array.no-external.js 180 kB
dist/customizations.full.js 13.8 kB
dist/dead-clicks-autocapture.js 14.5 kB
dist/exception-autocapture.js 9.48 kB
dist/external-scripts-loader.js 2.64 kB
dist/main.js 182 kB
dist/module.full.js 369 kB
dist/module.full.no-external.js 368 kB
dist/module.js 181 kB
dist/module.no-external.js 180 kB
dist/recorder-v2.js 115 kB
dist/recorder.js 115 kB
dist/surveys-preview.js 68.8 kB
dist/surveys.js 71.8 kB
dist/tracing-headers.js 1.76 kB
dist/web-vitals.js 10.4 kB

compressed-size-action

Copy link
Member

@rafaeelaudibert rafaeelaudibert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's very cool, thank you for looking into it!

I'd love if the snapshot file was slightly more readable.

This one is just ridiculous, for example
image

I think one of my suggestions will improve this slightly by removing the infinitely-many escapeslashes we have for nested types

Comment on lines 26 to 29
} else if (type.symbol && type.symbol.valueDeclaration) {
return getTypeString(type)
}
return getTypeString(type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last else isn't needed, we're just doing the same as the default clause below


function processType(type: ts.Type): any {
if (type.isUnion() || type.isIntersection()) {
return type.types.map(getTypeString)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than calling getTypeString we should processType recursively, this will allow us to capture nested unions like {a: {b: number}} | {a: {b: string}}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, and that picked up things like AutocaptureConfig without them needing a separate test 👍

Comment on lines +34 to +39
ts.forEachChild(sourceFile, (node) => {
if (ts.isInterfaceDeclaration(node) && node.name.text === typeName) {
const type = checker.getTypeAtLocation(node)
result = processType(type)
}
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad I'm as smart as an AI, I noticed the same. I don't think this is ever going to happen - it shouldn't - so I'm fine to keep this as is

pauldambra and others added 3 commits January 30, 2025 13:53
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
We don't need to look inside `Regexp`, it's fine to render it as is
Copy link
Member

@rafaeelaudibert rafaeelaudibert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an extra commit to simplify Regexp object, looking neat, thanks!

@pauldambra pauldambra merged commit 8624f85 into main Jan 30, 2025
28 checks passed
@pauldambra pauldambra deleted the chore/snapshot-types branch January 30, 2025 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants