In this article, I discuss how Blocks page is built on ui.shadcn.com. Blocks page has a lot of utilities used, hence I broke down this Blocks page analysis into 5 parts.
- shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 1
- shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 2
- shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 3
- shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 4
- shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 5 (Coming soon)
In part 3, I mentioned that I would provide an example that demonstrates ts-morph usage in shadcn-ui/ui. I made efforts to create this ts-morph-usage-in-shadcn-ui repository and put in the minimum required files to log the response of execution flow when _getBlockContent is called.
In this article, I will discuss the functions _extractVariable and project.createSourceFile.
_extractVariable
_extractVariable function contains the below code:
function \_extractVariable(sourceFile: SourceFile, name: string) {
const variable = sourceFile.getVariableDeclaration(name)
if (!variable) {
return null
}
const value = variable
.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral)
.getLiteralValue()
variable.remove()
return value
}
In order to understand this API, I had to read ts-morph documentation for variables. To explain in simple terms, ts-morph can be used to manipulate Javascript and Typescript code and comes handy when you are trying refactor a large code base. This Refactoring-Typescript-code-with-ts-morph has some great examples to demonstrate ts-morph power.
// Extract meta.
const description = \_extractVariable(sourceFile, "description")
const iframeHeight = \_extractVariable(sourceFile, "iframeHeight")
const containerClassName = \_extractVariable(sourceFile, "containerClassName")
These variables can be found in registry/new-york/authentication-04.tsx example. This code basically removes the variables declared from the authentication-04.tsx but keeps the other typescript code.
Since there are a lot of files in registry and they could involve this sort of extraction (aka Typescript code manipulation), using ts-morph sounds like the right choice here.
The image below points to ts-ast-viewer, uploaded with authentication-04.tsx code
In order to get access to TypeSscript AST API, it is now apparent that we need SourceFile API provided ts-morph
project.createSourceFile
I found this createSourceFile API reference in the ts-morph documentation. It all makes sense now.
const raw = await \_getBlockCode(name, style)
const tempFile = await createTempSourceFile(\`${name}.tsx\`)
const sourceFile = project.createSourceFile(tempFile, raw, {
scriptKind: ScriptKind.TSX,
})
To get access to AST API, we create a project with a source file and code so we could perform additional operations such as removing variables (like we saw in _extractVariableNames). Because blocks examples have some additional variables that needed to removed from the original file, example: authentication-01.tsx has some variables too and these variables are used in “Blocks” page to set iframe height and parent class name. This is a very useful technique when dealing with multiple files that require some form code formatting and variable extractions.
This is what _getBlockContent responds with, when called in getBlock. We can now move on to understand the code inside getBlock, which is used BlockDisplay component by providing the getBlock result into BlockPreview
In the final part 5, I will discuss the getBlock function, BlockDisplay and BlockPreview components and that brings this journey to understand how “Blocks” page is built to an end.
Conclusion:
In order to understand the API used in _extractVariable function in shadcn-ui/ui, I had to read ts-morph documentation for variables. To explain in simple terms, ts-morph can be used to manipulate Javascript and Typescript code and comes handy when you are trying refactor a large code base. This Refactoring-Typescript-code-with-ts-morph(https://blog.kaleidos.net/Refactoring-Typescript-code-with-ts-morph/) has some great examples to demonstrate ts-morph power.
To understand project.createSourceFile, I also had to setup a Github repository (https://github.com/Ramu-Narasinga/ts-morph-usage-in-shadcn-ui) and write the minimal code required to set up the ts-morph related functions used in lib/blocks.ts. To access AST API, we create a project with a source file and some code extracted from a file so we could perform additional operations such as removing variables (like we saw in _extractVariableNames). Because blocks examples have some additional variables that needed to removed from the original file, example: authentication-01.tsx (https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/new-york/block/authentication-01.tsx) has some variables too and these variables are used in “Blocks” page to set iframe height and parent class names. This is a very useful technique when dealing with multiple files that require some form code formatting and variable extractions. I have to admit, this is some advanced form of Typescript usage that I have never seen or used before.
In the final part 5, I will discuss the getBlock function, BlockDisplay and BlockPreview components and that brings this journey to understand how “Blocks” page is built to an end.
Get free courses inspired by the best practices used in open source.
About me:
Website: https://ramunarasinga.com/
Linkedin: https://www.linkedin.com/in/ramu-narasinga-189361128/
Github: https://github.com/Ramu-Narasinga
Email: ramu.narasinga@gmail.com
Learn the best practices used in open source.
References:
- https://github.com/shadcn-ui/ui/blob/main/apps/www/components/block-display.tsx#L5
- https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L27
- https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/blocks.ts#L107
- https://blog.kaleidos.net/Refactoring-Typescript-code-with-ts-morph/
- https://ts-ast-viewer.com/
- https://mariodante.medium.com/unveiling-the-power-of-abstract-syntax-trees-ast-and-typescript-973321947bbc
- https://www.satellytes.com/blog/post/typescript-ast-type-checker/
Top comments (0)