Commands, settings, SDK functions, anything in the docs.

Build apps

The project

The files of an app project: ledable.json, the README, and what stays local.

On this page

An app project is a directory with ledable.json at its root. Every ledable developer command except init runs in that directory. ledable developer init creates one; this page goes through what is in it.

FileKept in version control
ledable.jsonyes
The entry module, such as index.tsx, and the modules it importsyes
README.mdyes
Assets: images, animations, the store icon and screenshotsyes
package.json, package-lock.json, tsconfig.json, .gitignoreyes
.ledable/no
ledable.dev.jsonno
node_modules/no

ledable.json

ledable.json says what the app is: its id and version, how the store lists it, which files make it up, and what it may reach. It holds declarations only; settings are code, in the entry module. A complete one looks like this:

ledable.json
{
  "app_id": "tide-times",
  "version": "1.2.0",
  "entry": "./index.tsx",
  "name": "Tide Times",
  "description": "The next high and low tide at a harbour you pick.",
  "category": "travel",
  "tags": ["tides", "sea", "sailing"],
  "website": "https://example.com/tide-times",
  "contact": "tides@example.com",
  "readme": "./README.md",
  "icon": { "path": "assets/icon.webp" },
  "screenshots": [
    { "path": "assets/screenshots/high-tide.webp" },
    { "path": "assets/screenshots/low-tide.webp" }
  ],
  "secrets": ["TIDES_API_KEY"],
  "network": { "allowed_hosts": ["api.tides.example.com"] }
}

The ledable.json reference gives every field's exact rules. In short:

  • app_id is the app's id in the store, claimed once with ledable developer create and never changed.
  • version is the version an upload publishes. A published version never changes, so anything that changes what the app draws needs a new number.
  • entry is the module whose default export is the app.
  • name, description, category and tags are how the store lists the app. There is no author field: the store names the account that owns the app.
  • website and contact tell people where to find out more and how to reach you.
  • readme is the text of the app's store page, below.
  • icon and screenshots are the listing's images, below.
  • secrets names the keys of your own that the app reads from ctx.secrets; see Secrets.
  • network.allowed_hosts lists every host the app requests, and an app that requests none lists none: "allowed_hosts": []. See Data and the network.

Paths in ledable.json are relative to the project and must stay inside it.

Listing limits

Every store client lays a listing out in the same space, so the store holds it to these limits, counted in characters. The CLI checks them before it builds, and the store checks every upload again:

PartAt most
name30 characters
description120 characters
README4000 characters
tags5 tags of 20 characters each
screenshots6

The entry module

The entry module's default export is what defineApp from @ledable/sdk/app returns, as How apps work describes. It may import other modules of the project, packages it installs, and asset files, which become references the app reads through ctx.assets (Images and assets). Every TypeScript file of the project is type-checked in strict mode before every build, so a type error anywhere stops a build, a preview and an upload; Testing shows how to keep test files out of it. The build bundles what the entry module imports into one module and leaves out everything else.

The README

The file readme names is the app's page in the store: the LEDABLE app and the app's share page show it. The store reads a small part of Markdown, and shows everything else as plain text:

  • A line starting with #, ## or ### and a space is a heading.
  • Lines starting with - or * make a bulleted list, lines starting with a number, a full stop and a space (1. ) a numbered list.
  • Other lines that follow each other make a paragraph; an empty line ends it.
  • Within a line: **bold**, *italic* or _italic_, `code`, and [text](https://…) links. A link must be HTTPS.

HTML, images, tables, quotes and code blocks are not read: they appear as the characters you wrote. The example apps on this site have READMEs written to this subset:

door-sign/README.md
# Door Sign

A sign for the outside of your door. Pick whether you are **open**,
**busy** or **away**, choose a message to go with it and, while you are
away, the time you will be back.

## Settings

- **Status** and **Message**: the messages on offer follow the status.
- **Back at**: shown only while you are away.
- **Colour** and **Border**: how the sign looks.

The listing's images

icon and each of the screenshots name a WebP file in the project. They are uploaded with the version as assets, and unlike the app's other assets, the store serves them publicly with the listing. ledable images convert turns a PNG, JPEG or GIF into a WebP the size of the panel, which suits a screenshot; see Pictures.

Local files

Two things in a project belong to your machine only, and the .gitignore that developer init writes keeps them out of version control:

  • .ledable/ is written by the CLI: the compiler settings your tsconfig.json extends, the type checker's state, the build developer dev runs and what its preview page is set to. Every developer check writes it again, so a fresh clone of a project needs one ledable developer check, or any build, before an editor finds its types.
  • ledable.dev.json holds your own keys and tokens for the local preview: values for the secrets the app declares, account tokens and credentials. See Local preview.

The SDK and the CLI versions

A project depends on @ledable/sdk, and developer init adds @ledable/cli beside it, so npm run dev uses the CLI the project names. Imports resolve from the project as your editor resolves them, so a build bundles the SDK version the project installed.

A CLI builds only an SDK of its own major version, and while the SDK is at 0.x, of its own minor version too. Any other stops the build and says which side to upgrade: @ledable/sdk in the project when it is older, @ledable/cli when it is newer. Every upload records the SDK version it was built with, and the store refuses an upload built with an SDK older than the oldest it still accepts.