Skip to content

feat(dev): write .env.local on first run#517

Open
yurynix wants to merge 4 commits into
mainfrom
feat/dev-writes-env-local
Open

feat(dev): write .env.local on first run#517
yurynix wants to merge 4 commits into
mainfrom
feat/dev-writes-env-local

Conversation

@yurynix
Copy link
Copy Markdown

@yurynix yurynix commented May 10, 2026

Note

Description

On first run of base44 dev, the frontend's @base44/vite-plugin had no app ID and fell back to the production backend instead of the local dev server. This PR makes dev write a .env.local with VITE_BASE44_APP_ID and VITE_BASE44_BACKEND_URL when one does not already exist, so a fresh project points at the dev server out of the box. Existing .env.local files are left untouched.

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

  • Added writeEnvLocalIfMissing to packages/cli/src/cli/commands/dev.ts that creates .env.local with VITE_BASE44_APP_ID (from initAppConfig) and VITE_BASE44_BACKEND_URL pointing at the resolved local dev server URL.
  • Skips writing when .env.local already exists so user-maintained values are preserved.
  • Extracted a localServerUrl(port) helper and reused it for both the outro message and the env file.
  • Captured project.root from loadResources to know where to write the env file.
  • Added two integration tests in packages/cli/tests/cli/dev.spec.ts: one verifies the file is created with the expected variables, the other verifies an existing file is not overwritten.

Testing

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

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

The variable names (VITE_BASE44_APP_ID, VITE_BASE44_BACKEND_URL) match what @base44/vite-plugin expects on the frontend side.


🤖 Generated by Claude | 2026-06-02 11:16 UTC | 5e3000d

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 10, 2026

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.0.52-pr.517.5e3000d

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.0.52-pr.517.5e3000d"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.0.52-pr.517.5e3000d"
  }
}

Preview published to npm registry — try new features instantly!

Comment on lines +35 to +45
if (projectRoot) {
const envLocalPath = join(projectRoot, ".env.local");
if (!(await pathExists(envLocalPath))) {
const { id: appId } = await initAppConfig();
await writeFile(
envLocalPath,
`VITE_BASE44_APP_ID=${appId}\nVITE_BASE44_APP_BASE_URL=http://localhost:${resolvedPort}\n`,
);
log.info("Created .env.local with app ID and dev server URL");
}
}
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.

you're dependent on value that is set in loadResources(), imo it means you can just run this code in loadResources() and not keep it outside.
Maybe you can wrap it in a standalone function though.

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.

☝️ what about this one?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Wrapped the post-server logic in a standalone writeEnvLocalIfMissing(projectRoot, port, log) function. projectRoot still comes from loadResources() (the resolved port is only known after the server starts, so the write itself has to happen afterwards), but the body is no longer inlined in devAction.

Comment thread packages/cli/src/cli/commands/dev.ts Outdated
Comment on lines +39 to +42
await writeFile(
envLocalPath,
`VITE_BASE44_APP_ID=${appId}\nVITE_BASE44_APP_BASE_URL=http://localhost:${resolvedPort}\n`,
);
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.

I'm not familiar with the needed of this file and its content.
Is it used somehow during the dev?
Why do you want to create it during the dev?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

My understanding is that it's required for npm run dev, the vite server

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.

Not sure I understand your answer.
Are you sure it's really required?
Which part is requiring it?
How it worked before?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch digging in — it turned out the original code had the wrong variable name. The frontend reads its config via @base44/vite-plugin, which consumes VITE_BASE44_APP_ID and VITE_BASE44_BACKEND_URL (same names the platform injects in clone_s3_repo.py / the sandbox provider). The previous revision wrote VITE_BASE44_APP_BASE_URL, which nothing reads — so the local-backend redirect silently had no effect and the SDK kept hitting production. Before this PR there was simply no .env.local on a fresh clone, so you had to write it by hand. Fixed to write VITE_BASE44_BACKEND_URL; PR description updated with the full rationale.

Comment thread packages/cli/src/cli/commands/dev.ts Outdated
const { id: appId } = await initAppConfig();
await writeFile(
envLocalPath,
`VITE_BASE44_APP_ID=${appId}\nVITE_BASE44_APP_BASE_URL=http://localhost:${resolvedPort}\n`,
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.

Let's introduce function that provides local address, since we output it in 2 places in this file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — extracted localServerUrl(port) and reuse it for both the .env.local contents and the outro message.

Comment on lines +35 to +45
if (projectRoot) {
const envLocalPath = join(projectRoot, ".env.local");
if (!(await pathExists(envLocalPath))) {
const { id: appId } = await initAppConfig();
await writeFile(
envLocalPath,
`VITE_BASE44_APP_ID=${appId}\nVITE_BASE44_APP_BASE_URL=http://localhost:${resolvedPort}\n`,
);
log.info("Created .env.local with app ID and dev server URL");
}
}
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.

☝️ what about this one?

Comment thread packages/cli/src/cli/commands/dev.ts Outdated
Comment on lines +39 to +42
await writeFile(
envLocalPath,
`VITE_BASE44_APP_ID=${appId}\nVITE_BASE44_APP_BASE_URL=http://localhost:${resolvedPort}\n`,
);
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.

Not sure I understand your answer.
Are you sure it's really required?
Which part is requiring it?
How it worked before?

yurynix and others added 3 commits June 2, 2026 11:35
When a project is cloned and `base44 dev` is run for the first time,
there is no .env.local so the Vite plugin cannot proxy API calls to the
local dev server and does not know the app ID.

Write .env.local (VITE_BASE44_APP_ID + VITE_BASE44_APP_BASE_URL) after
the dev server starts if the file is not already present. If .env.local
exists it is left untouched so custom values are never clobbered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The frontend's @base44/vite-plugin reads VITE_BASE44_APP_ID and
VITE_BASE44_BACKEND_URL; the previous VITE_BASE44_APP_BASE_URL was read
by nothing, so the backend redirect to the local dev server silently had
no effect and the SDK kept hitting production.

Also address review feedback: extract a localServerUrl() helper (used for
both the env file and the outro message) and move the env.local write into
a standalone writeEnvLocalIfMissing() function.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@yurynix yurynix force-pushed the feat/dev-writes-env-local branch from 0efe069 to d7bfe88 Compare June 2, 2026 10:55
Biome organizeImports wants node: builtins first; the Logger type import
must follow them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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