Skip to content

Commit

Permalink
Improve codegen typing and reduce codegen bundle size
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieusieben committed Nov 14, 2024
1 parent 56741a1 commit 60a7fda
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 42 deletions.
5 changes: 5 additions & 0 deletions .changeset/eleven-knives-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@atproto/lex-cli": patch
---

Improve typing of isX and validateX return values
5 changes: 5 additions & 0 deletions .changeset/new-jobs-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@atproto/lex-cli": patch
---

Reduce bundle size of generated code
5 changes: 5 additions & 0 deletions .changeset/silly-starfishes-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@atproto/lexicon": patch
---

Add generic parameter to ValidationResult
30 changes: 21 additions & 9 deletions packages/lex-cli/src/codegen/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,21 +446,30 @@ const lexiconTs = (project, lexicons: Lexicons, lexiconDoc: LexiconDoc) =>
{ name: 'XRPCError' },
])
}
//= import {ValidationResult, BlobRef} from '@atproto/lexicon'
//= import {BlobRef, LexiconDoc, ValidationResult} from '@atproto/lexicon'
file
.addImportDeclaration({
moduleSpecifier: '@atproto/lexicon',
})
.addNamedImports([{ name: 'ValidationResult' }, { name: 'BlobRef' }])
//= import {isObj, hasProp} from '../../util.ts'

//= import {CID} from 'multiformats/cid'
file
.addImportDeclaration({
moduleSpecifier: 'multiformats/cid',
})
.addNamedImports([{ name: 'CID' }])

//= import {is$typed} from '../../util.ts'
file
.addImportDeclaration({
moduleSpecifier: `${lexiconDoc.id
.split('.')
.map((_str) => '..')
.join('/')}/util`,
})
.addNamedImports([{ name: 'isObj' }, { name: 'hasProp' }])
.addNamedImports([{ name: 'is$typed' }])

//= import {lexicons} from '../../lexicons.ts'
file
.addImportDeclaration({
Expand All @@ -470,12 +479,15 @@ const lexiconTs = (project, lexicons: Lexicons, lexiconDoc: LexiconDoc) =>
.join('/')}/lexicons`,
})
.addNamedImports([{ name: 'lexicons' }])
//= import {CID} from 'multiformats/cid'
file
.addImportDeclaration({
moduleSpecifier: 'multiformats/cid',
})
.addNamedImports([{ name: 'CID' }])

//= export const id = "{lexiconDoc.id}"
file.addVariableStatement({
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{ name: 'id', initializer: JSON.stringify(lexiconDoc.id) },
],
})

for (const defId in lexiconDoc.defs) {
const def = lexiconDoc.defs[defId]
Expand Down
36 changes: 29 additions & 7 deletions packages/lex-cli/src/codegen/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,37 @@ const PRETTIER_OPTS = {
export const utilTs = (project) =>
gen(project, '/util.ts', async (file) => {
file.replaceWithText(`
export function isObj(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null
export type $Type<Id extends string, Hash extends string> =
| (Hash extends 'main' ? Id : never)
| \`\${Id}#\${Hash}\`
function has$type<V>(v: V): v is V & object & { $type: unknown } {
return v != null && typeof v === 'object' && '$type' in v
}
function check$type<Id extends string, Hash extends string>(
$type: unknown,
id: Id,
hash: Hash,
): $type is $Type<Id, Hash> {
return (
typeof $type === 'string' &&
($type === id
? hash === 'main'
: // $type === \`\${id}#\${hash}\`
$type.length === id.length + 1 + hash.length &&
$type[id.length] === '#' &&
$type.startsWith(id) &&
$type.endsWith(hash))
)
}
export function hasProp<K extends PropertyKey>(
data: object,
prop: K,
): data is Record<K, unknown> {
return prop in data
export function is$typed<V, Id extends string, Hash extends string>(
v: V,
id: Id,
hash: Hash,
): v is V & object & { $type: $Type<Id, Hash> } {
return has$type(v) && check$type(v.$type, id, hash)
}
`)
})
Expand Down
27 changes: 12 additions & 15 deletions packages/lex-cli/src/codegen/lex-gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,35 +398,32 @@ export function genXrpcOutput(
export function genObjHelpers(
file: SourceFile,
lexUri: string,
ifaceName?: string,
ifaceName: string = toTitleCase(getHash(lexUri)),
) {
const hash = getHash(lexUri)

//= export function is{X}(v: unknown): v is X {...}
//= export function is{X}(v: unknown): v is X & { $type: NS } {...}
file
.addFunction({
name: toCamelCase(`is-${ifaceName || hash}`),
name: toCamelCase(`is-${ifaceName}`),
parameters: [{ name: `v`, type: `unknown` }],
returnType: `v is ${ifaceName || toTitleCase(hash)}`,
returnType: `v is ${ifaceName} & { $type: ${
hash === 'main' ? `'${lexUri}' | '${stripHash(lexUri)}'` : `'${lexUri}'`
} }`,
isExported: true,
})
.setBodyText(
hash === 'main'
? `return isObj(v) && hasProp(v, '$type') && (v.$type === "${lexUri}" || v.$type === "${stripHash(
lexUri,
)}")`
: `return isObj(v) && hasProp(v, '$type') && v.$type === "${lexUri}"`,
)
.setBodyText(`return is$typed(v, id, ${JSON.stringify(hash)})`)

//= export function validate{X}(v: unknown): ValidationResult {...}
//= export function validate{X}(v: unknown): ValidationResult<X> {...}
file
.addFunction({
name: toCamelCase(`validate-${ifaceName || hash}`),
name: toCamelCase(`validate-${ifaceName}`),
parameters: [{ name: `v`, type: `unknown` }],
returnType: `ValidationResult`,
isExported: true,
})
.setBodyText(`return lexicons.validate("${lexUri}", v)`)
.setBodyText(
`return lexicons.validate(\`\${id}#${hash}\`, v) as ValidationResult<${ifaceName}>`,
)
}

export function stripScheme(uri: string): string {
Expand Down
28 changes: 20 additions & 8 deletions packages/lex-cli/src/codegen/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,14 @@ const lexiconTs = (project, lexicons: Lexicons, lexiconDoc: LexiconDoc) =>
moduleSpecifier: '@atproto/lexicon',
})
.addNamedImports([{ name: 'ValidationResult' }, { name: 'BlobRef' }])

//= import {CID} from 'multiformats/cid'
file
.addImportDeclaration({
moduleSpecifier: 'multiformats/cid',
})
.addNamedImports([{ name: 'CID' }])

//= import {lexicons} from '../../lexicons.ts'
file
.addImportDeclaration({
Expand All @@ -357,21 +365,25 @@ const lexiconTs = (project, lexicons: Lexicons, lexiconDoc: LexiconDoc) =>
.join('/')}/lexicons`,
})
.addNamedImports([{ name: 'lexicons' }])
//= import {isObj, hasProp} from '../../util.ts'

//= import {is$typed} from '../../util.ts'
file
.addImportDeclaration({
moduleSpecifier: `${lexiconDoc.id
.split('.')
.map((_str) => '..')
.join('/')}/util`,
})
.addNamedImports([{ name: 'isObj' }, { name: 'hasProp' }])
//= import {CID} from 'multiformats/cid'
file
.addImportDeclaration({
moduleSpecifier: 'multiformats/cid',
})
.addNamedImports([{ name: 'CID' }])
.addNamedImports([{ name: 'is$typed' }])

//= export const id = "{lexiconDoc.id}"
file.addVariableStatement({
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{ name: 'id', initializer: JSON.stringify(lexiconDoc.id) },
],
})

for (const defId in lexiconDoc.defs) {
const def = lexiconDoc.defs[defId]
Expand Down
4 changes: 2 additions & 2 deletions packages/lexicon/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,10 +471,10 @@ export function parseLexiconDoc(v: unknown): LexiconDoc {
return v as LexiconDoc
}

export type ValidationResult =
export type ValidationResult<V = unknown> =
| {
success: true
value: unknown
value: V
}
| {
success: false
Expand Down
4 changes: 3 additions & 1 deletion packages/pds/src/read-after-write/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export class LocalViewer {
$type: 'app.bsky.embed.images#view',
images,
}
} else {
} else if (isEmbedExternal(embed)) {
const { uri, title, description, thumb } = embed.external
return {
$type: 'app.bsky.embed.external#view',
Expand All @@ -219,6 +219,8 @@ export class LocalViewer {
: undefined,
},
}
} else {
throw new TypeError(`Unexpected embed type: ${embed.$type}`)
}
}

Expand Down

0 comments on commit 60a7fda

Please sign in to comment.