Skip to content

feat(project): default the site block's two build commands - #634

Merged
ronnyrin merged 3 commits into
mainfrom
feat/site-config-defaults
Sep 22, 2026
Merged

ronnyrin merged 3 commits into
mainfrom
feat/site-config-defaults

Conversation

@ronnyrin

@ronnyrin ronnyrin commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Note

Description

Gives the site block's two commands schema-level defaults (installCommandnpm install, buildCommandnpm run build) — the conventions base44 create already scaffolds — so a block only has to name what a project does differently. serveCommand and outputDirectory are deliberately left undefaulted: their absence is the signal that there is no frontend to run and nothing built to upload, so base44 dev still runs the backend alone and base44 deploy still skips the site step. site itself stays optional, so "is there a site?" still keys on the block. The practical effect is that a project with a partial site block can now run base44 build and deploy --build instead of being refused, and the deploy flow may offer to build first.

Related Issue

None

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Other (please describe):

Changes Made

  • core/project/schema.ts: SiteConfigSchema.buildCommand and installCommand change from .optional() to .default("npm run build") / .default("npm install"), with a comment explaining why serveCommand and outputDirectory are intentionally excluded.
  • cli/commands/project/eject.ts: the post-eject install-build-deploy offer now keys on the presence of a site block rather than on both commands being set — that guard went dead once the commands default, and having a block is the whole question, since a backend-only project has nothing to build.
  • cli/commands/project/site-build.ts: the "no build command" hint now tells the user to add a site block and notes that buildCommand defaults inside one.
  • docs/deployments.md: the site deploy build prompt is now described as firing whenever the project has a site block, since buildCommand defaults inside one.
  • CHANGELOG.md: entry under Changed describing the defaults and the deliberate omissions.

Testing

  • I have tested these changes locally
  • I have added/updated tests as needed
  • All tests pass (npm test)

Test changes:

  • New fixture with-site-defaults (empty "site": {} block plus a package.json build script that records VITE_BASE44_APP_ID).
  • tests/core/project.spec.ts: three cases — a partial block keeps its named field and picks up both command defaults, an empty block defaults both commands while leaving serveCommand/outputDirectory undefined, and a project with no site block still has no site at all.
  • tests/cli/build.spec.ts: the two "fails when the project has no site.buildCommand" cases are replaced by one asserting build now succeeds via the default command; the no-site-block failure case is kept.
  • tests/cli/deploy.spec.ts: guards the deliberate omission — a block with only a serveCommand deploys no site and never reports "Site from".

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (if applicable)
  • My changes generate no new warnings
  • I have updated docs/ (AGENTS.md) if I made architectural changes

Additional Notes

Behaviour does change for projects that already declared a partial site block: they now build and deploy on the defaults instead of being refused, which is why "Breaking change" is checked alongside "New feature" — no config becomes invalid, but an existing block can do more than it used to.

The asymmetry is the point and is worth reviewing on its own terms: defaulting outputDirectory to ./dist would silently turn any site block into an upload of whatever happened to be sitting there (or fail a previously-green deploy after every other resource had already been pushed), and defaulting serveCommand would spawn a dev server for every site block. Commands that exist only to serve or build a site supply their own fallback, where the intent is unambiguous.


🤖 Generated by Claude | 2026-09-22 10:50 UTC | 619a424

@github-actions

github-actions Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.1.16-pr.634.619a424

Prefer not to change any import paths? Install using npm alias so your code still imports base44:

npm i "base44@npm:@base44-preview/cli@0.1.16-pr.634.619a424"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.1.16-pr.634.619a424"
  }
}

Preview published to npm registry — try new features instantly!

A `site` block had to spell out every command, and the platform that runs
these projects kept its own copy of the same values to fill the gaps.
Defaults live here now, matching what `base44 create` scaffolds: install
`npm install`, build `npm run build`, output `./dist`. A block only names
what its project does differently.

`serveCommand` is deliberately left undefaulted: `base44 dev` reads its
absence as "no frontend to run here" and runs the backend alone, and a
default would spawn a dev server for every site block — one that fails
immediately takes the backend down with it. Commands that exist only to
serve a frontend default it themselves.

`site` itself stays optional, so "is there a site?" still keys on the
block: a backend-only project omits it and has no site. Inside a block the
build fields always resolve, which moves two refusals from "this field is
missing" to "there is no site block" — `base44 build` and `site deploy`
reword their hints, and `deploy` reads the block rather than one field.

Projects that declared a partial block change behaviour: they now build
and deploy on the defaults instead of being refused.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
@ronnyrin
ronnyrin force-pushed the feat/site-config-defaults branch from 6ab4533 to 93d2ceb Compare September 22, 2026 09:34
@ronnyrin ronnyrin changed the title feat(project): default the site block's commands feat(project): default the site block's build commands Sep 22, 2026
@ronnyrin ronnyrin changed the title feat(project): default the site block's build commands feat(project): default the site block's two build commands Sep 22, 2026
@ronnyrin
ronnyrin force-pushed the feat/site-config-defaults branch from 8a4b166 to 8d9279d Compare September 22, 2026 10:10
Review found that defaulting `outputDirectory` turns a field whose absence
means "nothing built to upload" into one that always resolves, and two callers
act on that:

`deployAll` still keyed the upload on the field, so `base44 deploy` on a
`"site": {"serveCommand": ...}` project (the shape of this repo's own
with-serve-command fixture) would resolve `./dist` and either fail a
previously-green deploy after pushing every other resource, or publish whatever
happened to sit in `./dist` — a backend bundle, typically — as the app's site.

`eject`'s guard `installCommand && buildCommand` became dead, so `eject --yes`
would run a network install, a build and a deploy for any ejected app with a
site block, unprompted.

So `outputDirectory` joins `serveCommand` as deliberately undefaulted, which is
the same rule applied consistently: only the two commands whose absence means
nothing default. `hasResourcesToDeploy` needs no change and drops out of the
diff; eject now keys on `outputDirectory` like every other caller. Added the
deploy test that would have caught the first one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
@ronnyrin
ronnyrin force-pushed the feat/site-config-defaults branch from 8d9279d to 9c57bb8 Compare September 22, 2026 10:13
ronnyrin added a commit that referenced this pull request Sep 22, 2026
Review findings on this PR, plus the merge of #634's outputDirectory change.

`--host`/`--port` were composed only onto an `npm run` invocation and otherwise
warned and continued with exit 0. For this command's audience — a hosted sandbox
— a warning in a stream nobody reads is not a signal: the dev server comes up on
whatever address it likes and the CLI reports success. An address that cannot be
delivered is now a refusal. `pnpm`, `yarn` and `bun run` join the forwarding
shapes, since all three pass trailing arguments to a script and dropping the
address for them was the common case.

`withServeAddress` returns whether it dropped the address, so the caller cannot
mistake "no address to append" for "the address went nowhere" — the difference
being a preview that never loads. That removes the second evaluation of the same
regex at the call site, and the predicate it needed.

The script token was `\S+`, which matches `dev;evil` and `$(id)`: the predicate
answered "this forwards arguments" for a line whose second command is what
received them. It is a charset now. The composition also used the untrimmed
string while the test used the trimmed one.

`--port` went through `Number()`, so an empty string became port 0 — a random
port, silently — and `0x10`, `1e3` and `5173.0` were all accepted. It is parsed
in a Commander argParser now, digits only and in range.

`devHostFlag` does not default in the schema, for the same reason `serveCommand`
and `outputDirectory` do not: `site dev` is the only reader, and a default
permanently erases the difference between a project that wrote `--host` and one
that wrote nothing. `site dev` supplies the fallback.

Also: knip was failing on an unused exported type, invisible because none of the
five code-checking workflows run on a PR that targets another branch. Tests
added for the unauthenticated path on both new commands, `--port` rejection, a
failing install command and a custom `devHostFlag`; one assertion that could
never fail is gone, and the fixture no longer claims the schema defaults
`serveCommand`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
Both commands default inside a `site` block, so install and build always work
there now — the earlier `outputDirectory` check was guarding a config neither
writer produces. `ensure_cli_configs` and `eject_service` both emit the full
block including `outputDirectory: "./dist"`, and the eject ZIP comes from the
latter, so the check could never be the condition that differed.

Having a site block is the whole question: a backend-only project has nothing
to build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL

@yurynix yurynix left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best of luck on your quest

@ronnyrin
ronnyrin merged commit 2802dca into main Sep 22, 2026
15 checks passed
@ronnyrin
ronnyrin deleted the feat/site-config-defaults branch September 22, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants