Skip to content

Commit

Permalink
Merge branch 'main' into chris/web-vitals
Browse files Browse the repository at this point in the history
  • Loading branch information
delucis authored May 3, 2024
2 parents f494238 + befbda7 commit 758e96b
Show file tree
Hide file tree
Showing 14 changed files with 220 additions and 191 deletions.
6 changes: 6 additions & 0 deletions .changeset/young-pots-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@astrojs/db": minor
---

- Fix duplicate table recreations when you start your dev server.
- Remove eager re-seeding when updating your seed file in development. Seeding still runs on dev server startup for SQLite inspector tools.
4 changes: 1 addition & 3 deletions packages/db/src/core/cli/commands/execute/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from '../../../integration/vite-plugin-db.js';
import { bundleFile, importBundledFile } from '../../../load-file.js';
import { getManagedAppTokenOrExit } from '../../../tokens.js';
import { type DBConfig } from '../../../types.js';
import type { DBConfig } from '../../../types.js';

export async function cmd({
astroConfig,
Expand Down Expand Up @@ -51,8 +51,6 @@ export async function cmd({
virtualModContents = getLocalVirtualModContents({
tables: dbConfig.tables ?? {},
root: astroConfig.root,
shouldSeed: false,
seedFiles: [],
});
}
const { code } = await bundleFile({ virtualModContents, root: astroConfig.root, fileUrl });
Expand Down
10 changes: 8 additions & 2 deletions packages/db/src/core/cli/commands/shell/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {
createLocalDatabaseClient,
createRemoteDatabaseClient,
} from '../../../../runtime/db-client.js';
import { normalizeDatabaseUrl } from '../../../../runtime/index.js';
import { DB_PATH } from '../../../consts.js';
import { SHELL_QUERY_MISSING_ERROR } from '../../../errors.js';
import { getManagedAppTokenOrExit } from '../../../tokens.js';
import type { DBConfigInput } from '../../../types.js';
import { getRemoteDatabaseUrl } from '../../../utils.js';
import { getAstroEnv, getRemoteDatabaseUrl } from '../../../utils.js';

export async function cmd({
flags,
Expand All @@ -31,7 +32,12 @@ export async function cmd({
await appToken.destroy();
console.log(result);
} else {
const db = createLocalDatabaseClient({ dbUrl: new URL(DB_PATH, astroConfig.root).href });
const { ASTRO_DATABASE_FILE } = getAstroEnv();
const dbUrl = normalizeDatabaseUrl(
ASTRO_DATABASE_FILE,
new URL(DB_PATH, astroConfig.root).href
);
const db = createLocalDatabaseClient({ dbUrl });
const result = await db.run(sql.raw(query));
console.log(result);
}
Expand Down
10 changes: 5 additions & 5 deletions packages/db/src/core/cli/migration-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import * as color from 'kleur/colors';
import { customAlphabet } from 'nanoid';
import stripAnsi from 'strip-ansi';
import { hasPrimaryKey } from '../../runtime/index.js';
import { isSerializedSQL } from '../../runtime/types.js';
import { safeFetch } from '../../runtime/utils.js';
import { MIGRATION_VERSION } from '../consts.js';
import { RENAME_COLUMN_ERROR, RENAME_TABLE_ERROR } from '../errors.js';
import {
getCreateIndexQueries,
getCreateTableQuery,
Expand All @@ -12,11 +16,7 @@ import {
getReferencesConfig,
hasDefault,
schemaTypeToSqlType,
} from '../../runtime/queries.js';
import { isSerializedSQL } from '../../runtime/types.js';
import { safeFetch } from '../../runtime/utils.js';
import { MIGRATION_VERSION } from '../consts.js';
import { RENAME_COLUMN_ERROR, RENAME_TABLE_ERROR } from '../errors.js';
} from '../queries.js';
import { columnSchema } from '../schemas.js';
import {
type BooleanColumn,
Expand Down
131 changes: 89 additions & 42 deletions packages/db/src/core/integration/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
import { existsSync } from 'fs';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import { LibsqlError } from '@libsql/client';
import type { AstroConfig, AstroIntegration } from 'astro';
import { mkdir, writeFile } from 'fs/promises';
import { blue, yellow } from 'kleur/colors';
import { loadEnv } from 'vite';
import {
type HMRPayload,
type UserConfig,
type ViteDevServer,
createServer,
loadEnv,
mergeConfig,
} from 'vite';
import parseArgs from 'yargs-parser';
import { SEED_DEV_FILE_NAME } from '../../runtime/queries.js';
import { AstroDbError } from '../../runtime/utils.js';
import { CONFIG_FILE_NAMES, DB_PATH } from '../consts.js';
import { EXEC_DEFAULT_EXPORT_ERROR, EXEC_ERROR } from '../errors.js';
import { resolveDbConfig } from '../load-file.js';
import { SEED_DEV_FILE_NAME } from '../queries.js';
import { type ManagedAppToken, getManagedAppTokenOrExit } from '../tokens.js';
import { type VitePlugin, getDbDirectoryUrl } from '../utils.js';
import { fileURLIntegration } from './file-url.js';
import { typegenInternal } from './typegen.js';
import { type LateSeedFiles, type LateTables, resolved, vitePluginDb } from './vite-plugin-db.js';
import {
type LateSeedFiles,
type LateTables,
type SeedHandler,
resolved,
vitePluginDb,
} from './vite-plugin-db.js';
import { vitePluginInjectEnvTs } from './vite-plugin-inject-env-ts.js';

function astroDBIntegration(): AstroIntegration {
let connectToStudio = false;
let configFileDependencies: string[] = [];
let root: URL;
let appToken: ManagedAppToken | undefined;
// Used during production builds to load seed files.
let tempViteServer: ViteDevServer | undefined;

// Make table loading "late" to pass to plugins from `config:setup`,
// but load during `config:done` to wait for integrations to settle.
Expand All @@ -35,6 +52,13 @@ function astroDBIntegration(): AstroIntegration {
throw new Error('[astro:db] INTERNAL Seed files not loaded yet');
},
};
let seedHandler: SeedHandler = {
execute: () => {
throw new Error('[astro:db] INTERNAL Seed handler not loaded yet');
},
inProgress: false,
};

let command: 'dev' | 'build' | 'preview';
let output: AstroConfig['output'] = 'server';
return {
Expand All @@ -60,6 +84,7 @@ function astroDBIntegration(): AstroIntegration {
root: config.root,
srcDir: config.srcDir,
output: config.output,
seedHandler,
});
} else {
dbPlugin = vitePluginDb({
Expand All @@ -69,6 +94,8 @@ function astroDBIntegration(): AstroIntegration {
root: config.root,
srcDir: config.srcDir,
output: config.output,
logger,
seedHandler,
});
}

Expand Down Expand Up @@ -98,6 +125,9 @@ function astroDBIntegration(): AstroIntegration {
await typegenInternal({ tables: tables.get() ?? {}, root: config.root });
},
'astro:server:setup': async ({ server, logger }) => {
seedHandler.execute = async (fileUrl) => {
await executeSeedFile({ fileUrl, viteServer: server });
};
const filesToWatch = [
...CONFIG_FILE_NAMES.map((c) => new URL(c, getDbDirectoryUrl(root))),
...configFileDependencies.map((c) => new URL(c, root)),
Expand All @@ -118,46 +148,11 @@ function astroDBIntegration(): AstroIntegration {
const localSeedPaths = SEED_DEV_FILE_NAME.map(
(name) => new URL(name, getDbDirectoryUrl(root))
);
let seedInFlight = false;
// Load seed file on dev server startup.
// Eager load astro:db module on startup
if (seedFiles.get().length || localSeedPaths.find((path) => existsSync(path))) {
loadSeedModule();
}
const eagerReloadIntegrationSeedPaths = seedFiles
.get()
// Map integration seed paths to URLs, if possible.
// Module paths like `@example/seed` will be ignored
// from eager reloading.
.map((s) => (typeof s === 'string' && s.startsWith('.') ? new URL(s, root) : s))
.filter((s): s is URL => s instanceof URL);
const eagerReloadSeedPaths = [...eagerReloadIntegrationSeedPaths, ...localSeedPaths];
server.watcher.on('all', (event, relativeEntry) => {
if (event === 'unlink' || event === 'unlinkDir') return;
// When a seed file changes, load manually
// to track when seeding finishes and log a message.
const entry = new URL(relativeEntry, root);
if (eagerReloadSeedPaths.find((path) => entry.href === path.href)) {
loadSeedModule();
}
});

function loadSeedModule() {
if (seedInFlight) return;

seedInFlight = true;
const mod = server.moduleGraph.getModuleById(resolved.seedVirtual);
if (mod) server.moduleGraph.invalidateModule(mod);
server
.ssrLoadModule(resolved.seedVirtual)
.then(() => {
logger.info('Seeded database.');
})
.catch((e) => {
logger.error(e instanceof Error ? e.message : String(e));
})
.finally(() => {
seedInFlight = false;
});
server.ssrLoadModule(resolved.module).catch((e) => {
logger.error(e instanceof Error ? e.message : String(e));
});
}
}, 100);
},
Expand All @@ -175,8 +170,15 @@ function astroDBIntegration(): AstroIntegration {

logger.info('database: ' + (connectToStudio ? yellow('remote') : blue('local database.')));
},
'astro:build:setup': async ({ vite }) => {
tempViteServer = await getTempViteServer({ viteConfig: vite });
seedHandler.execute = async (fileUrl) => {
await executeSeedFile({ fileUrl, viteServer: tempViteServer! });
};
},
'astro:build:done': async ({}) => {
await appToken?.destroy();
await tempViteServer?.close();
},
},
};
Expand All @@ -190,3 +192,48 @@ function databaseFileEnvDefined() {
export function integration(): AstroIntegration[] {
return [astroDBIntegration(), fileURLIntegration()];
}

async function executeSeedFile({
fileUrl,
viteServer,
}: {
fileUrl: URL;
viteServer: ViteDevServer;
}) {
const mod = await viteServer.ssrLoadModule(fileUrl.pathname);
if (typeof mod.default !== 'function') {
throw new AstroDbError(EXEC_DEFAULT_EXPORT_ERROR(fileURLToPath(fileUrl)));
}
try {
await mod.default();
} catch (e) {
if (e instanceof LibsqlError) {
throw new AstroDbError(EXEC_ERROR(e.message));
}
throw e;
}
}

/**
* Inspired by Astro content collection config loader.
*/
async function getTempViteServer({ viteConfig }: { viteConfig: UserConfig }) {
const tempViteServer = await createServer(
mergeConfig(viteConfig, {
server: { middlewareMode: true, hmr: false, watch: null },
optimizeDeps: { noDiscovery: true },
ssr: { external: [] },
logLevel: 'silent',
})
);

const hotSend = tempViteServer.hot.send;
tempViteServer.hot.send = (payload: HMRPayload) => {
if (payload.type === 'error') {
throw payload.err;
}
return hotSend(payload);
};

return tempViteServer;
}
Loading

0 comments on commit 758e96b

Please sign in to comment.