In the OSS community, it is quite common to come across monorepo projects where the workspaces and packages are all in a single repository. CodeMirror has a different style to the way packages are managed, each package that is used is a repository on its own. In this article, we analyse what these packages are that CodeMirror uses and how they are installed when you want to setup CodeMirror v6 locally to contribute.
bin/packages.js
Before we analyse the install function, let’s first understand the variours packages that make up the CodeMirror. You
will find these packages listed in bin/packages.js file
These below are the core packages:
exports.core = [
"state",
"view",
"language",
"commands",
"search",
"autocomplete",
"lint",
"collab",
"language-data",
"codemirror",
]
These below are the non-core packages.
exports.nonCore = [
"lang-javascript",
"lang-java",
"lang-json",
"lang-cpp",
"lang-php",
"lang-python",
"lang-go",
"lang-css",
"lang-sass",
"lang-html",
"lang-sql",
"lang-rust",
"lang-xml",
"lang-markdown",
"lang-lezer",
"lang-wast",
"lang-angular",
"lang-vue",
"lang-liquid",
"lang-less",
"lang-yaml",
"legacy-modes",
"theme-one-dark",
"merge"
]
You will find repositories in the CodeMirror organisation for all these packages shown above. There is no single repository containing all these packages like in a monorepo, instead you have all these repositories that make up the CodeMirror.
exports.core and exports.nonCore
You will find that these packages are accessed via a function named loadPackages.
You might be wondering what exports.all here is in the above image, at line 42. It is interesting how the concat is made on exports.core and exports.nonCore and this you have all these packages merged into one array and assigned to exports.all.
exports.loadPackages
let packages = exports.all.map(n => new Pkg(n))
Using map on exports.all, new Pkg class is instantiated using the Pkg class that’s shown below:
class Pkg {
constructor(name) {
this.name = name
this.dir = join(__dirname, "..", name)
this.main = null
if (name != "legacy-modes" && fs.existsSync(this.dir)) {
let files = fs.readdirSync(join(this.dir, "src")).filter(f => /^[^.]+\.ts$/.test(f))
let main = files.length == 1 ? files[0] : files.includes("index.ts") ? "index.ts"
: files.includes(name.replace(/^(theme-|lang-)/, "") + ".ts") ? name.replace(/^(theme-|lang-)/, "") + ".ts" : null
if (!main) throw new Error("Couldn't find a main script for " + name)
this.main = join(this.dir, "src", main)
}
}
}
It technically sets name
, dir
and main
.
let packageNames = Object.create(null)
for (let p of packages) packageNames[p.name] = p
Object.create(null) creates an empty object, that’s fancy way of creating a obj that would otherwise be just obj = {}
and finally below is the return statement.
return {
packages,
packageNames,
buildPackages: packages.filter(p => p.main)
}
About me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.
I am open to work on an interesting project. Send me an email at ramu.narasinga@gmail.com
My Github - https://github.com/ramu-narasinga
My website - https://ramunarasinga.com
My Youtube channel - https://www.youtube.com/@thinkthroo
Learning platform - https://thinkthroo.com
Codebase Architecture - https://app.thinkthroo.com/architecture
Best practices - https://app.thinkthroo.com/best-practices
Production-grade projects - https://app.thinkthroo.com/production-grade-projects
Top comments (0)