Skip to content

willcrichton/model-js-workspace

Repository files navigation

Model Javascript workspace

This repository provides an example that embodies my standard for how to setup a JS workspace. The goal is to envision a "JS workspace of the future": I want a stack of libraries, scripts, and websites that contain minimal boilerplate and are interoperable. That goal starts by throwing away old standards and tools: no CommonJS, no Webpack, no jQuery, and so on.

This current design reflects my experience on what works best for structuring a JS project. My recent JS experience comes from big projects like Nota and lots of smaller ones like mdbook-quiz, Aquascope, and Flowistry.

Some notable aspects of the design:

  • Static typing: all source files are written in Typescript. Static types are good, what else is there to say? And the JS community has coalesced around Typescript, so that's what I use. The annoying part with Typescript is that if you're writing a CLI script (build script, test, etc.) then it has to be transpiled before being executed by Node. I've had trouble get ts-node to work with ESM, but in theory that would solve the issue.
  • Package management: pnpm is the package manager and workspace executor. Its interface is similar to npm, but its nested/symlinked node_modules structure is a lot more sane. It helps catch errors where package A can use package B without listing B as a dependency (because B is a transitive dependency of a listed dependency C). It also reduces the disk usage of node_modules directories.
  • Websites: the main principle for websites is to provide as little configuration as possible, and specify everything within the TS source files. I use Vite for bundling since it provides a number of nice conventions for this purpose, e.g. adding ?url to an import path to bundle a file and get a URL to it.
    • UI framework: I use React as a UI framework, and sometimes mobx for complex data structures. React's super stable and there's a lot of libraries that integrate with it. It's easy to write modular React components. I've also heard good things about Svelte but haven't tried it yet.
    • CSS: I use Sass to write CSS, mostly for nesting rules and mixins. I used to use Sass variables, but these days I use CSS variables instead. I'm starting to use CSS modules to keep the CSS rules close to the components they describe.
  • Libraries: the main principle for libraries is to do as little processing of the source as possible. A library should leave bundling to downstream applications, instead providing unminified & fully-documented files to support the IDE experience and debugging. Therefore my libraries simply use tsc to transpile each file individually into a directory-structure-equivalent output, with corresponding adjacent .d.ts files.
    • Import paths: with careful use of the exports field of package.json, this strategy enables nested imports. For example, if a file is at browser-lib/src/components/button.ts, this gets transpiled to browser-lib/dist/components/button.js. A re-export rule then allows downstream consumers to import browser-lib/components/button.
    • Static assets: the principle here is for libraries to use the same convention as the downstream bundler (currently Vite). So a library that needs an image imports the file with the path icon.jpg?file, and Vite is responsible for bundling the image. The library builder just needs to copy the image from the source directory to the appropriate place in the destination. A similar principle applies for CSS.
    • Tests: I use vitest, which integrates well with Vite. I recently switched from Jest, which has fragile support for both Typescript and ESM, while both are first-class in vitest. For frontend testing, I use JSDom and testing-library. Fingers crossed that vitest actually works well!
  • Scripts: for scripts, i.e. top-level entry points to JS either for Node or the browser, I also use Vite to bundle the source files into a single JS file that can be run as a Node executable, or provided to a <script> tag in the browser. I previously used esbuild, but Vite seems to subsume that use case (and it uses esbuild internally anyway).
  • Linting, formatting, documentation generation: I use eslint, prettier, and typedoc to do each, respectively.

The long-term goal for this workspace is to provide a gold standard for the kind of repo that Graco can automatically generate.

About

My personal standard for how to set up a Javascript workspace

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published