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 (Coming soon)
- shadcn-ui/ui codebase analysis: How is “Blocks” page built — Part 5 (Coming soon)
In part 3, I will explain how createTempSourceFile, createSourceFile and extractVariable work in order to understand getBlockCode completely. Keep in mind, we still need to get back to getBlock since this is used in BlockDisplay .
_getBlockCode function code:
async function \_getBlockContent(name: string, style: Style\["name"\]) {
const raw = await \_getBlockCode(name, style)
const tempFile = await createTempSourceFile(\`${name}.tsx\`)
const sourceFile = project.createSourceFile(tempFile, raw, {
scriptKind: ScriptKind.TSX,
})
// Extract meta.
const description = \_extractVariable(sourceFile, "description")
const iframeHeight = \_extractVariable(sourceFile, "iframeHeight")
const containerClassName = \_extractVariable(sourceFile, "containerClassName")
// Format the code.
let code = sourceFile.getText()
code = code.replaceAll(\`@/registry/${style}/\`, "@/components/")
code = code.replaceAll("export default", "export")
return {
description,
code,
container: {
height: iframeHeight,
className: containerClassName,
},
}
}
In part 2, we looked at _getBlockCode in great detail. Let’s understand createTempSourceFile.
createTempSourceFile
createTempSourceFile function creates a unique temporary directory.
async function createTempSourceFile(filename: string) {
const dir = await fs.mkdtemp(path.join(tmpdir(), "codex-"))
return path.join(dir, filename)
}
tempdir is imported from “os”, since shadcn-ui/ui uses Next.js, all this code is executed on server, hence it has access to “os” node.js package. lib/blocks.ts has “use server” at the top of file.
Basically, what this code means is that a temporary file with block file name is temporarily placed in a temporary folder.
createSourceFile
createSourceFile is a function called using project object
const sourceFile = project.createSourceFile(tempFile, raw, {
scriptKind: ScriptKind.TSX,
})
Hang on a minute, what is project object?
const project = new Project({
compilerOptions: {},
})
At the top of the file, this project variable is initiated with a Project instance. Now what is Project? Project is imported from ts-morph? What is ts-morph? Let’s find out.
ts-morph
ts-morph is a library that wraps the TypeScript compiler API to simplify setup, navigation and manipulation of the Typescript AST. This article has a good explanation what Typescript AST means.
When the TypeScript compiler compiles your code, it creates an Abstract Syntax Tree (AST) of it. Essentially, an AST can be thought of as a tree representation of the syntax of your source code, with each node being a data structure representing a construct in the relating source code. The tree is complete with nodes of all the elements of the source code.
I definitely need to more research on this ts-morph concept and also talk about this a bit more in part 4.
const sourceFile = project.createSourceFile(tempFile, raw, {
scriptKind: ScriptKind.TSX,
})
ts-morph.com documentation provides an example that uses project.createSourceFile, just like how shadcn-ui/ui does it.
We know how tempFile is created, raw is variable containing the file code returning by getBlockCode, for example, code sitting at https://github.com/shadcn-ui/ui/blob/main/apps/www/_registry__/new-york/block/authentication-04.tsx
I will provide an example project that uses ts-morph and performs the similar operations that shadcn-ui/ui does to understand createSourceFile and extractVariable. getBlock also uses these similar functions in part 4.
Conclusion:
shadcn-ui/ui performs some additional operations using ts-morph after reading the code from registry folder’s blocks file. Why not just read the file directly? this is an interesing question. I will find out why there is a need to use ts-morph to extract the code further from the read file in part 4.
This is the beauty of reading/studying OSS code, you are exposed to a lot of new technical concepts. I do not know what ts-morph is yet… but I will find out soon and provide an example project after running some experiments. I will keep this example limited to the similar operations used in shadcn-ui/ui.
This part 3 does provide insights into creating a temporary folder using “os” package since lib/block.ts has “use server” at the top of this file.
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.
Top comments (0)