-
Notifications
You must be signed in to change notification settings - Fork 443
feat: begin integrating @netlify/dev
#7950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c47e845
ec90dc0
697bcb1
6be22d6
bd20f1c
39aa3d9
118af61
e343281
086ae21
b5aa01f
1328a3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,13 @@ import { | |
| import detectServerSettings, { getConfigWithPlugins } from '../../utils/detect-server-settings.js' | ||
| import { parseAIGatewayContext, setupAIGateway } from '@netlify/ai/bootstrap' | ||
|
|
||
| import { UNLINKED_SITE_MOCK_ID, getDotEnvVariables, getSiteInformation, injectEnvVariables } from '../../utils/dev.js' | ||
| import { | ||
| UNLINKED_SITE_MOCK_ID, | ||
| getDotEnvVariables, | ||
| getSiteInformation, | ||
| injectEnvVariables, | ||
| processOnExit, | ||
| } from '../../utils/dev.js' | ||
| import { getEnvelopeEnv } from '../../utils/env/index.js' | ||
| import { ensureNetlifyIgnore } from '../../utils/gitignore.js' | ||
| import { getLiveTunnelSlug, startLiveTunnel } from '../../utils/live-tunnel.js' | ||
|
|
@@ -35,6 +41,7 @@ import { getBaseOptionValues } from '../base-command.js' | |
| import type { NetlifySite } from '../types.js' | ||
|
|
||
| import type { DevConfig } from './types.js' | ||
| import { startNetlifyDev as startProgrammaticNetlifyDev } from './programmatic-netlify-dev.js' | ||
| import { doesProjectRequireLinkedSite } from '../../lib/extensions.js' | ||
|
|
||
| const handleLiveTunnel = async ({ | ||
|
|
@@ -174,6 +181,16 @@ export const dev = async (options: OptionValues, command: BaseCommand) => { | |
|
|
||
| injectEnvVariables(env) | ||
|
|
||
| const programmaticNetlifyDev = await startProgrammaticNetlifyDev({ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may know this already, but for completeness: this would need to also be hooked up in the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I'll do that in a follow up. Having this on dev for now is fine. |
||
| projectRoot: command.workingDir, | ||
| apiToken: api.accessToken ?? undefined, | ||
| env, | ||
| }) | ||
|
|
||
| if (programmaticNetlifyDev) { | ||
| processOnExit(() => programmaticNetlifyDev.stop()) | ||
| } | ||
|
|
||
| await promptEditorHelper({ chalk, config, log, NETLIFYDEVLOG, repositoryRoot, state }) | ||
|
|
||
| let settings: ServerSettings | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import process from 'process' | ||
|
|
||
| import { NetlifyDev } from '@netlify/dev' | ||
|
|
||
| import { NETLIFYDEVWARN, log } from '../../utils/command-helpers.js' | ||
| import type { EnvironmentVariables } from '../../utils/types.js' | ||
|
|
||
| interface StartNetlifyDevOptions { | ||
| projectRoot: string | ||
| apiToken: string | undefined | ||
| env: EnvironmentVariables | ||
| } | ||
|
|
||
| /** | ||
| * Much of the core of local dev emulation of the Netlify platform was extracted | ||
| * (duplicated) to https://github.com/netlify/primitives. This is a shim that | ||
| * gradually enables *some* of this extracted functionality while falling back | ||
| * to the legacy copy in this codebase for the rest. | ||
| * | ||
| * TODO: Hook this up to the request chain and fall through to the existing handler. | ||
| * TODO: `@netlify/images` follows a different pattern (it is used directly). | ||
| * Move that here. | ||
| */ | ||
| export const startNetlifyDev = async ({ | ||
eduardoboucas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| apiToken, | ||
| env, | ||
| projectRoot, | ||
| }: StartNetlifyDevOptions): Promise<NetlifyDev | undefined> => { | ||
| if (process.env.EXPERIMENTAL_NETLIFY_DB_ENABLED !== '1') { | ||
| return | ||
| } | ||
|
|
||
| const netlifyDev = new NetlifyDev({ | ||
| projectRoot, | ||
| apiToken, | ||
| ...(process.env.NETLIFY_API_URL && { apiURL: process.env.NETLIFY_API_URL }), | ||
|
|
||
| aiGateway: { enabled: false }, | ||
| blobs: { enabled: false }, | ||
| edgeFunctions: { enabled: false }, | ||
| environmentVariables: { enabled: false }, | ||
| functions: { enabled: false }, | ||
| geolocation: { enabled: false }, | ||
| headers: { enabled: false }, | ||
| images: { enabled: false }, | ||
| redirects: { enabled: false }, | ||
| staticFiles: { enabled: false }, | ||
| serverAddress: null, | ||
| }) | ||
|
|
||
| try { | ||
| await netlifyDev.start() | ||
| } catch (error) { | ||
| log(`${NETLIFYDEVWARN} Failed to start @netlify/dev: ${error instanceof Error ? error.message : String(error)}`) | ||
| } | ||
|
|
||
| if (process.env.NETLIFY_DB_URL) { | ||
| env.NETLIFY_DB_URL = { sources: ['internal'], value: process.env.NETLIFY_DB_URL } | ||
| } | ||
|
|
||
| return netlifyDev | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import fetch from 'node-fetch' | ||
| import { describe, test } from 'vitest' | ||
|
|
||
| import { withDevServer } from '../../utils/dev-server.js' | ||
| import { withSiteBuilder } from '../../utils/site-builder.js' | ||
|
|
||
| describe('@netlify/dev integration', () => { | ||
| test('Makes DB available to functions when EXPERIMENTAL_NETLIFY_DB_ENABLED is set', async (t) => { | ||
| await withSiteBuilder(t, async (builder) => { | ||
| builder | ||
| .withPackageJson({ | ||
| packageJson: { | ||
| dependencies: { '@netlify/db': '0.1.0', '@netlify/db-dev': '0.2.0' }, | ||
| }, | ||
| }) | ||
| .withCommand({ command: ['npm', 'install'] }) | ||
| .withContentFile({ | ||
| path: 'netlify/functions/db-test.mjs', | ||
| content: ` | ||
| import { getDatabase } from "@netlify/db"; | ||
| export default async () => { | ||
| try { | ||
| const { sql } = getDatabase(); | ||
| const rows = await sql\`SELECT 1 + 1 AS sum\`; | ||
| return Response.json({ sum: rows[0].sum }); | ||
| } catch (error) { | ||
| return Response.json({ error: error.message }, { status: 500 }); | ||
| } | ||
| }; | ||
| export const config = { path: "/db-test" }; | ||
| `, | ||
| }) | ||
|
|
||
| await builder.build() | ||
|
|
||
| await withDevServer({ cwd: builder.directory, env: { EXPERIMENTAL_NETLIFY_DB_ENABLED: '1' } }, async (server) => { | ||
| const response = await fetch(`${server.url}/db-test`) | ||
| const body = await response.text() | ||
| console.log(body) | ||
| t.expect(body).toEqual(JSON.stringify({ sum: 2 })) | ||
| }) | ||
| }) | ||
| }) | ||
|
|
||
| test('Does not set NETLIFY_DB_URL when EXPERIMENTAL_NETLIFY_DB_ENABLED is not set', async (t) => { | ||
| await withSiteBuilder(t, async (builder) => { | ||
| builder.withFunction({ | ||
| path: 'db-url.mjs', | ||
| pathPrefix: 'netlify/functions', | ||
| runtimeAPIVersion: 2, | ||
| config: { path: '/db-url' }, | ||
| handler: () => Response.json({ url: process.env.NETLIFY_DB_URL ?? '' }), | ||
| }) | ||
|
|
||
| await builder.build() | ||
|
|
||
| await withDevServer({ cwd: builder.directory }, async (server) => { | ||
| const response = await fetch(`${server.url}/db-url`) | ||
| const body = await response.text() | ||
| console.log(body) | ||
|
|
||
| t.expect(response.status).toBe(200) | ||
| t.expect(body).toEqual(JSON.stringify({ url: '' })) | ||
| }) | ||
| }) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a wrong type we should be fixing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Linting started failing for this, randomly. I haven't yet had the time to understand why, since I haven't touched this part of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll look into this as a follow-up.