Skip to content

Repository files navigation

create-tinyjoin

Scaffold a small local todo app with TinyJoin and Vite:

npm create tinyjoin@latest

The generated project is ready to run at its root and needs no database server, account, or credentials. Choose TypeScript or JavaScript; SQL, the Drizzle ORM, or the Kysely query builder for its queries; and whether its todos should remain after reloads or start fresh each time.

Your app should look something like this:

Todo App

  • Task list with add/complete/delete
  • Single TinyJoin database with one todos table
  • Demonstrates basic CRUD operations in SQL, or with Drizzle or Kysely
  • Automatic multi-tab access for saved data
  • Offline production app loading after the first online visit
  • Perfect starter example

It shares its markup and styling with the equivalent create-tinybase vanilla TypeScript starter, with the TinyJoin accent color and system fonts.

For agents and CI, every option can be supplied non-interactively:

npm create tinyjoin@latest -- --non-interactive \
  --projectName my-tinyjoin-app \
  --language typescript \
  --queries sql \
  --storage opfs \
  --installAndRun false

Use --list-options for the machine-readable option catalog or --help for usage.

npm run build assembles the publishable create-tinyjoin package under dist/.

Generated structure

The generated project is a single Vite application at its root (.ts below becomes .js when JavaScript is chosen):

  • index.html loads the app and defines its theme variables.
  • vite.config.ts enables production offline loading through tinyjoinOffline().
  • src/index.ts bootstraps the app on load.
  • src/app.ts builds the app shell, showing a spinner until the database opens.
  • src/database.ts opens the database once, and closes it with the page.
  • src/todos.ts sets up the todos table, adds the starter todos on the first visit, and runs every query the interface needs, behind one small API.
  • src/schema.ts, with Drizzle, is the Drizzle schema of the todos table.
  • src/todoInput.ts, src/todoList.ts, and src/todoItem.ts are the todo interface, each with a colocated stylesheet.
  • src/topBar.ts, src/title.ts, src/info.ts, src/loading.ts, src/button.ts, and src/input.ts are the shared interface pieces.

Queries

The three choices of queries generate the same app, whose interface calls the API that createTodos() in src/todos.ts returns. Only that module, the dependencies, and the docs differ:

  • SQL runs parameterized statements on the TinyJoin client. One atomic script creates the table and adds the starter todos.
  • Drizzle declares the table in src/schema.ts and queries it with the Drizzle ORM through tinyjoin/drizzle. push() makes the database hold the schema as each tab starts, and the starter todos are added only by the tab whose push() creates the table.
  • Kysely queries the table with the Kysely query builder through tinyjoin/kysely. Kysely's Migrator runs the app's migrations as each tab starts, one tab at a time; the first creates the table and adds the starter todos.

So in each, tabs opened together add the starter todos once, and a list emptied by the user stays empty. Drizzle adds about 21 KB of compressed script to the page, and Kysely about 31 KB; the Worker and the database engine are the same.

TypeScript and JavaScript

The templates are written once, in TypeScript. Choosing JavaScript strips their types with ts-blank-space and renames the outputs to .js, exactly as create-tinybase does: getFiles, and processIncludedFile for the files that templates include, mark each .ts template transpile when the JavaScript language is chosen, and tinycreate blanks the types during post-processing. A JavaScript project also drops tsconfig.json, the typescript devDependency, and the tsc --noEmit step from its build script.

As in create-tinybase, every file that depends on an option is included by a template, with includeFile, inside the condition that needs it, rather than chosen in src/cli.ts: the todos module and Drizzle schema by app.ts.hbs and todos.drizzle.ts.hbs, the language icon by info.ts.hbs, and tsconfig.json by package.json.hbs.

Offline builds

Generated applications use tinyjoinOffline() from tinyjoin/vite. The production build includes the application and complete database runtime in its offline copy, including files loaded only when persistent storage opens. There are no remote font requests. Development remains a normal Vite session; use npm run build and npm run preview to test offline behavior.

New application versions wait for open tabs to close before activating. This keeps existing pages on their matching build; it does not migrate database schemas. Applications with their own service worker can use the plugin's manifest mode. Offline loading and saved local data do not synchronize with a server or another device.

Development

npm run spell           # cspell over the sources, templates, and docs
npm run typecheck       # generator and test TypeScript
npm test                # CLI, template, snapshot, and package-boundary tests
npm run test:generated  # generated apps against published TinyJoin
npm run test:e2e        # real generated-app behavior in Chromium

npm run publishPackage runs all of the above before publishing. Add project vocabulary to cspell.json rather than disabling the check.

npm test compares every generated file, for both languages with both storage modes, and with each query builder, against the snapshots in test/__snapshots__. Run npx vitest run test/cli.test.ts -u to accept intentional template changes. npm run test:generated installs and builds eight apps: every language and storage combination with SQL, and each language with each query builder and saved data. npm run test:e2e then drives the six saved apps in Chromium and tests offline production loading for all eight.

Run the local generator from this repository's parent directory with:

npm --prefix create-tinyjoin run local

This builds only the generator. The generated app installs the published TinyJoin package, so its native build toolchain is not required.

To test unpublished changes from a neighboring TinyJoin source repository, use:

npm --prefix create-tinyjoin run local:source

That command builds and packs the sibling TinyJoin repository before starting the generator.

About

Create simple TinyJoin apps

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages