Skip to content

Commit

Permalink
feat(db): Run db type generation on astro sync (#10438)
Browse files Browse the repository at this point in the history
* feat: db typegen on astro sync

* fix: avoid requiring db to be installed

* fix: make typegen optional for backwards compat

* chore: changeset

* fix: required -> optional

* fix: remove flags from sync API signature
  • Loading branch information
bholmesdev authored Mar 15, 2024
1 parent 0989cd3 commit 5b48cc0
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 13 deletions.
6 changes: 6 additions & 0 deletions .changeset/popular-radios-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"astro": patch
"@astrojs/db": patch
---

Generate Astro DB types when running `astro sync`.
2 changes: 2 additions & 0 deletions packages/astro/src/cli/install-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const require = createRequire(import.meta.url);

type GetPackageOptions = {
skipAsk?: boolean;
optional?: boolean;
cwd?: string;
};

Expand All @@ -37,6 +38,7 @@ export async function getPackage<T>(
const packageImport = await import(packageName);
return packageImport as T;
} catch (e) {
if (options.optional) return undefined;
logger.info(
'SKIP_FORMAT',
`To continue, Astro requires the following dependency to be installed: ${bold(packageName)}.`
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/core/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ class AstroBuilder {
);
await runHookConfigDone({ settings: this.settings, logger: logger });

const { syncInternal } = await import('../sync/index.js');
const syncRet = await syncInternal(this.settings, { logger: logger, fs });
const { syncContentCollections } = await import('../sync/index.js');
const syncRet = await syncContentCollections(this.settings, { logger: logger, fs });
if (syncRet !== 0) {
return process.exit(syncRet);
}
Expand Down
30 changes: 25 additions & 5 deletions packages/astro/src/core/sync/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { performance } from 'node:perf_hooks';
import { fileURLToPath } from 'node:url';
import { dim } from 'kleur/colors';
import { type HMRPayload, createServer } from 'vite';
import type { AstroInlineConfig, AstroSettings } from '../../@types/astro.js';
import type { AstroConfig, AstroInlineConfig, AstroSettings } from '../../@types/astro.js';
import { createContentTypesGenerator } from '../../content/index.js';
import { globalContentConfigObserver } from '../../content/utils.js';
import { telemetry } from '../../events/index.js';
Expand All @@ -20,6 +20,8 @@ import { AstroError, AstroErrorData, createSafeError, isAstroError } from '../er
import type { Logger } from '../logger/core.js';
import { formatErrorMessage } from '../messages.js';
import { ensureProcessNodeEnv } from '../util.js';
import { getPackage } from '../../cli/install-package.js';
import type { Arguments } from 'yargs-parser';

export type ProcessExit = 0 | 1;

Expand All @@ -34,6 +36,10 @@ export type SyncInternalOptions = SyncOptions & {
logger: Logger;
};

type DBPackage = {
typegen?: (args: Pick<AstroConfig, 'root' | 'integrations'>) => Promise<void>;
};

/**
* Generates TypeScript types for all Astro modules. This sets up a `src/env.d.ts` file for type inferencing,
* and defines the `astro:content` module for the Content Collections API.
Expand All @@ -57,8 +63,24 @@ export default async function sync(
command: 'build',
});

const timerStart = performance.now();
const dbPackage = await getPackage<DBPackage>(
'@astrojs/db',
logger,
{
optional: true,
cwd: inlineConfig.root,
},
[]
);

try {
return await syncInternal(settings, { ...options, logger });
await dbPackage?.typegen?.(astroConfig);
const exitCode = await syncContentCollections(settings, { ...options, logger });
if (exitCode !== 0) return exitCode;

logger.info(null, `Types generated ${dim(getTimeStat(timerStart, performance.now()))}`);
return 0;
} catch (err) {
const error = createSafeError(err);
logger.error(
Expand All @@ -83,11 +105,10 @@ export default async function sync(
* @param {LogOptions} options.logging Logging options
* @return {Promise<ProcessExit>}
*/
export async function syncInternal(
export async function syncContentCollections(
settings: AstroSettings,
{ logger, fs }: SyncInternalOptions
): Promise<ProcessExit> {
const timerStart = performance.now();
// Needed to load content config
const tempViteServer = await createServer(
await createVite(
Expand Down Expand Up @@ -150,7 +171,6 @@ export async function syncInternal(
await tempViteServer.close();
}

logger.info(null, `Types generated ${dim(getTimeStat(timerStart, performance.now()))}`);
await setUpEnvTs({ settings, logger, fs: fs ?? fsMod });

return 0;
Expand Down
6 changes: 3 additions & 3 deletions packages/db/src/core/integration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { existsSync } from 'fs';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import type { AstroIntegration } from 'astro';
import { mkdir, rm, writeFile } from 'fs/promises';
import { mkdir, writeFile } from 'fs/promises';
import { blue, yellow } from 'kleur/colors';
import parseArgs from 'yargs-parser';
import { CONFIG_FILE_NAMES, DB_PATH } from '../consts.js';
import { resolveDbConfig } from '../load-file.js';
import { type ManagedAppToken, getManagedAppTokenOrExit } from '../tokens.js';
import { type VitePlugin, getDbDirectoryUrl } from '../utils.js';
import { fileURLIntegration } from './file-url.js';
import { typegen } from './typegen.js';
import { typegenInternal } from './typegen.js';
import { type LateSeedFiles, type LateTables, vitePluginDb } from './vite-plugin-db.js';
import { vitePluginInjectEnvTs } from './vite-plugin-inject-env-ts.js';

Expand Down Expand Up @@ -88,7 +88,7 @@ function astroDBIntegration(): AstroIntegration {
await writeFile(localDbUrl, '');
}

await typegen({ tables: tables.get() ?? {}, root: config.root });
await typegenInternal({ tables: tables.get() ?? {}, root: config.root });
},
'astro:server:start': async ({ logger }) => {
// Wait for the server startup to log, so that this can come afterwards.
Expand Down
13 changes: 11 additions & 2 deletions packages/db/src/core/integration/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@ import { existsSync } from 'node:fs';
import { mkdir, writeFile } from 'node:fs/promises';
import { DB_TYPES_FILE, RUNTIME_IMPORT } from '../consts.js';
import type { DBTable, DBTables } from '../types.js';
import type { AstroConfig } from 'astro';
import { resolveDbConfig } from '../load-file.js';

export async function typegen({ tables, root }: { tables: DBTables; root: URL }) {
const content = `// This file is generated by \`studio sync\`
// Exported for use in Astro core CLI
export async function typegen(astroConfig: Pick<AstroConfig, 'root' | 'integrations'>) {
const { dbConfig } = await resolveDbConfig(astroConfig);

await typegenInternal({ tables: dbConfig.tables, root: astroConfig.root });
}

export async function typegenInternal({ tables, root }: { tables: DBTables; root: URL }) {
const content = `// This file is generated by Astro DB
declare module 'astro:db' {
export const db: import(${RUNTIME_IMPORT}).SqliteDB;
export const dbUrl: string;
Expand Down
5 changes: 4 additions & 1 deletion packages/db/src/core/load-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ const isDbIntegration = (integration: AstroIntegration): integration is AstroDbI
/**
* Load a user’s `astro:db` configuration file and additional configuration files provided by integrations.
*/
export async function resolveDbConfig({ root, integrations }: AstroConfig) {
export async function resolveDbConfig({
root,
integrations,
}: Pick<AstroConfig, 'root' | 'integrations'>) {
const { mod, dependencies } = await loadUserConfigFile(root);
const userDbConfig = dbConfigSchema.parse(mod?.default ?? {}, { errorMap });
/** Resolved `astro:db` config including tables provided by integrations. */
Expand Down
1 change: 1 addition & 0 deletions packages/db/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export type { ResolvedCollectionConfig, TableConfig } from './core/types.js';
export { cli } from './core/cli/index.js';
export { integration as default } from './core/integration/index.js';
export { typegen } from './core/integration/typegen.js';

0 comments on commit 5b48cc0

Please sign in to comment.