From d7e5c87cf2e817f537accd1ef38495f2fac9b906 Mon Sep 17 00:00:00 2001 From: Alexis Rico Date: Mon, 6 May 2024 08:39:06 +0200 Subject: [PATCH] Breaking changes to codegen (#1371) --- cli/src/base.ts | 14 +- cli/src/commands/codegen/index.ts | 23 +- cli/src/commands/init/index.test.ts | 123 +++++----- cli/src/commands/shell/index.ts | 2 +- cli/src/config.ts | 3 +- cli/src/migrations/pgroll.ts | 4 +- packages/client/src/api/client.ts | 3 +- packages/client/src/client.ts | 29 +-- packages/client/src/index.ts | 2 +- packages/client/src/types/global-deno.d.ts | 7 - packages/client/src/types/global-node.d.ts | 5 - .../client/src/types/global-variables.d.ts | 4 - packages/client/src/util/environment.ts | 153 ++----------- packages/codegen/example/build-example.mjs | 4 +- packages/codegen/example/types.d.ts | 1 - packages/codegen/example/xata.cjs | 28 +-- packages/codegen/example/xata.js | 24 +- packages/codegen/example/xata.ts | 27 +-- packages/codegen/src/codegen.ts | 116 ++++------ packages/importer/test/utils.ts | 3 +- test/__snapshots__/codegen.test.ts.snap | 212 +++++++++--------- 21 files changed, 279 insertions(+), 508 deletions(-) delete mode 100644 packages/client/src/types/global-deno.d.ts delete mode 100644 packages/client/src/types/global-node.d.ts delete mode 100644 packages/client/src/types/global-variables.d.ts diff --git a/cli/src/base.ts b/cli/src/base.ts index 7249532cd..6faecd83c 100644 --- a/cli/src/base.ts +++ b/cli/src/base.ts @@ -1,13 +1,5 @@ import { Command, Flags, Interfaces } from '@oclif/core'; -import { - buildClient, - getAPIKey, - getBranch, - getHostUrl, - parseWorkspacesUrlParts, - Schemas, - XataApiPlugin -} from '@xata.io/client'; +import { buildClient, getHostUrl, parseWorkspacesUrlParts, Schemas, XataApiPlugin } from '@xata.io/client'; import { XataImportPlugin } from '@xata.io/importer'; import ansiRegex from 'ansi-regex'; import chalk from 'chalk'; @@ -192,7 +184,7 @@ export abstract class BaseCommand extends Command { const { flags } = await this.parseCommand(); const profileName = flags.profile || getEnvProfileName(); - const apiKey = getAPIKey(); + const apiKey = process.env.XATA_API_KEY; const useEnv = !ignoreEnv || profileName === 'default'; if (useEnv && apiKey) return buildProfile({ name: 'default', apiKey }); @@ -531,7 +523,7 @@ export abstract class BaseCommand extends Command { } getCurrentBranchName() { - return getBranch() ?? 'main'; + return process.env.XATA_BRANCH ?? 'main'; } async updateConfig() { diff --git a/cli/src/commands/codegen/index.ts b/cli/src/commands/codegen/index.ts index 1741efab6..7a0b2539f 100644 --- a/cli/src/commands/codegen/index.ts +++ b/cli/src/commands/codegen/index.ts @@ -1,11 +1,12 @@ import { Flags } from '@oclif/core'; import { generate, isValidJavascriptTarget, javascriptTargets } from '@xata.io/codegen'; import chalk from 'chalk'; -import { mkdir, readFile, writeFile } from 'fs/promises'; +import { mkdir, writeFile } from 'fs/promises'; import path, { dirname, extname, relative } from 'path'; import { BaseCommand } from '../../base.js'; import { ProjectConfig } from '../../config.js'; import { getBranchDetailsWithPgRoll } from '../../migrations/pgroll.js'; +import { safeReadFile } from '../../utils/files.js'; export const languages: Record = { '.js': 'javascript', @@ -46,9 +47,6 @@ export default class Codegen extends BaseCommand { }), 'worker-id': Flags.string({ description: 'Xata worker deployment id' - }), - 'experimental-incremental-build': Flags.boolean({ - description: 'Experimental: Keep the source code in the generated file and only update the parts that changed' }) }; @@ -84,27 +82,16 @@ export default class Codegen extends BaseCommand { } const xata = await this.getXataClient(); - const { workspace, region, database, branch, databaseURL } = await this.getParsedDatabaseURLWithBranch( - flags.db, - flags.branch - ); + const { workspace, region, database, branch } = await this.getParsedDatabaseURLWithBranch(flags.db, flags.branch); const { schema } = await getBranchDetailsWithPgRoll(xata, { workspace, region, database, branch }); - - const codegenBranch = flags['inject-branch'] ? branch : undefined; - - // Experimental: Keep the source code in the generated file and only update the parts that changed - const incrementalBuild = - flags['experimental-incremental-build'] ?? this.projectConfig?.experimental?.incrementalBuild ?? false; - const existingCode = incrementalBuild ? await readFile(output, 'utf8').catch(() => undefined) : undefined; + const existingCode = await safeReadFile(output); const result = await generate({ schema, - databaseURL, language, moduleType, javascriptTarget, - branch: codegenBranch, - existingCode + existingCode: existingCode ?? '' }); const { typescript, javascript, types } = result; diff --git a/cli/src/commands/init/index.test.ts b/cli/src/commands/init/index.test.ts index 2c38ba70e..b4f78d993 100644 --- a/cli/src/commands/init/index.test.ts +++ b/cli/src/commands/init/index.test.ts @@ -157,8 +157,7 @@ describe('xata init', () => { }", "package.json": "{"name":"test","version":"1.0.0"}", "readme.md": "", - "xataCustom.ts": "// Generated by Xata Codegen 0.29.4. Please do not edit. - import { buildClient } from "@xata.io/client"; + "xataCustom.ts": "import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, @@ -181,24 +180,23 @@ describe('xata init', () => { const DatabaseClient = buildClient(); - const defaultOptions = { - databaseURL: "https://test-1234.us-east-1.xata.sh/db/db1", - }; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } - - let instance: XataClient | undefined = undefined; - - export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }; ", } `); @@ -242,8 +240,7 @@ describe('xata init', () => { } }", "readme.md": "", - "xataCustom.ts": "// Generated by Xata Codegen 0.29.4. Please do not edit. - import { buildClient } from "@xata.io/client"; + "xataCustom.ts": "import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, @@ -266,24 +263,23 @@ describe('xata init', () => { const DatabaseClient = buildClient(); - const defaultOptions = { - databaseURL: "https://test-1234.us-east-1.xata.sh/db/db1", - }; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } - - let instance: XataClient | undefined = undefined; - - export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }; ", } `); @@ -319,8 +315,10 @@ describe('xata init', () => { } }", "readme.md": "", - "xataCustom.ts": "// Generated by Xata Codegen 0.29.4. Please do not edit. - import { buildClient } from "npm:@xata.io/client@latest"; + "xataCustom.ts": "import { + buildClient, + getDeployPreviewBranch, + } from "npm:@xata.io/client@latest"; import type { BaseClientOptions, SchemaInference, @@ -343,24 +341,23 @@ describe('xata init', () => { const DatabaseClient = buildClient(); - const defaultOptions = { - databaseURL: "https://test-1234.us-east-1.xata.sh/db/db1", - }; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: Deno.env.get("XATA_API_KEY"), + databaseURL: Deno.env.get("XATA_DATABASE_URL"), + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(Deno.env.get) ?? + Deno.env.get("XATA_BRANCH") ?? + "main", + ...options, + }, + tables + ); } } - - let instance: XataClient | undefined = undefined; - - export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }; ", } `); @@ -400,8 +397,7 @@ describe('xata init', () => { "package.json": "{"name":"test","version":"1.0.0"}", "pnpm-lock.yaml": "lockfileVersion: '6.0'", "readme.md": "", - "xataCustom.ts": "// Generated by Xata Codegen 0.29.4. Please do not edit. - import { buildClient } from "@xata.io/client"; + "xataCustom.ts": "import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, @@ -424,24 +420,23 @@ describe('xata init', () => { const DatabaseClient = buildClient(); - const defaultOptions = { - databaseURL: "https://test-1234.us-east-1.xata.sh/db/db1", - }; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } - - let instance: XataClient | undefined = undefined; - - export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }; ", } `); diff --git a/cli/src/commands/shell/index.ts b/cli/src/commands/shell/index.ts index 8e6dd3a13..a8afd38e8 100644 --- a/cli/src/commands/shell/index.ts +++ b/cli/src/commands/shell/index.ts @@ -49,7 +49,7 @@ export default class Shell extends BaseCommand { const branchDetails = await getBranchDetailsWithPgRoll(xata, { workspace, region, database, branch }); const { schema } = branchDetails; - const { javascript } = await generate({ language: 'javascript', databaseURL, schema }); + const { javascript } = await generate({ language: 'javascript', schema }); await fs.writeFile(tempFile, javascript); } catch (err) { const message = err instanceof Error ? err.message : String(err); diff --git a/cli/src/config.ts b/cli/src/config.ts index d8e2faed1..365ceb6e7 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -5,7 +5,7 @@ export const projectConfigSchema = z.object({ databaseURL: z.string(), codegen: z.object({ output: z.string(), - moduleType: z.enum(['cjs', 'esm', 'deno']), + moduleType: z.enum(['cjs', 'esm', 'deno', 'vite']), declarations: z.boolean(), javascriptTarget: z.enum([ 'es5', @@ -21,7 +21,6 @@ export const projectConfigSchema = z.object({ ]) }), experimental: z.object({ - incrementalBuild: z.boolean(), workflow: z.boolean() }) }); diff --git a/cli/src/migrations/pgroll.ts b/cli/src/migrations/pgroll.ts index 933e074fd..26a0b79cd 100644 --- a/cli/src/migrations/pgroll.ts +++ b/cli/src/migrations/pgroll.ts @@ -1,13 +1,13 @@ import { Schemas, XataApiClient } from '@xata.io/client'; import { Column } from '@xata.io/codegen'; +import { OpRawSQL, OpRenameConstraint, PgRollOperation } from '@xata.io/pgroll'; import path from 'path'; import z from 'zod'; import { XataClient } from '../base.js'; +import { BranchSchemaFormatted } from '../commands/schema/types.js'; import { safeJSONParse, safeReadFile } from '../utils/files.js'; import { migrationsDir, readMigrationsDir } from './files.js'; import { MigrationFilePgroll, migrationFilePgroll } from './schema.js'; -import { OpRawSQL, OpRenameConstraint, PgRollOperation } from '@xata.io/pgroll'; -import { BranchSchemaFormatted } from '../commands/schema/types.js'; export const isBranchPgRollEnabled = (details: Schemas.DBBranch) => { // @ts-expect-error TODO: Fix this when api is finalized diff --git a/packages/client/src/api/client.ts b/packages/client/src/api/client.ts index 67eb2c989..079b73f31 100644 --- a/packages/client/src/api/client.ts +++ b/packages/client/src/api/client.ts @@ -1,5 +1,4 @@ import { defaultTrace, TraceFunction } from '../schema/tracing'; -import { getAPIKey } from '../util/environment'; import { FetchImpl, getFetchImplementation } from '../util/fetch'; import { RequiredKeys } from '../util/types'; import { generateUUID } from '../util/uuid'; @@ -40,7 +39,7 @@ const buildApiClient = () => class { constructor(options: XataApiClientOptions = {}) { const provider = options.host ?? 'production'; - const apiKey = options.apiKey ?? getAPIKey(); + const apiKey = options.apiKey; const trace = options.trace ?? defaultTrace; const clientID = generateUUID(); diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index aa365225e..48b88bc78 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -6,7 +6,6 @@ import { defaultTrace, TraceFunction } from './schema/tracing'; import { SearchPlugin, SearchPluginResult } from './search'; import { SQLPlugin, SQLPluginResult } from './sql'; import { TransactionPlugin, TransactionPluginResult } from './transaction'; -import { getAPIKey, getBranch, getDatabaseURL, getEnableBrowserVariable, getPreviewBranch } from './util/environment'; import { FetchImpl, getFetchImplementation } from './util/fetch'; import { AllRequired, StringKeys } from './util/types'; import { generateUUID } from './util/uuid'; @@ -83,7 +82,7 @@ export const buildClient = = {}>(plu #parseOptions(options?: BaseClientOptions): SafeOptions { // If is running from the browser and the user didn't pass `enableBrowser` we throw an error - const enableBrowser = options?.enableBrowser ?? getEnableBrowserVariable() ?? false; + const enableBrowser = options?.enableBrowser ?? false; // @ts-ignore Window, Deno are not globals const isBrowser = typeof window !== 'undefined' && typeof Deno === 'undefined'; if (isBrowser && !enableBrowser) { @@ -93,8 +92,9 @@ export const buildClient = = {}>(plu } const fetch = getFetchImplementation(options?.fetch); - const databaseURL = options?.databaseURL || getDatabaseURL(); - const apiKey = options?.apiKey || getAPIKey(); + const databaseURL = options?.databaseURL; + const apiKey = options?.apiKey; + const branch = options?.branch; const trace = options?.trace ?? defaultTrace; const clientName = options?.clientName; const host = options?.host ?? 'production'; @@ -108,25 +108,8 @@ export const buildClient = = {}>(plu throw new Error('Option databaseURL is required'); } - const envBranch = getBranch(); - const previewBranch = getPreviewBranch(); - const branch = options?.branch || previewBranch || envBranch || 'main'; - if (!!previewBranch && branch !== previewBranch) { - console.warn( - `Ignoring preview branch ${previewBranch} because branch option was passed to the client constructor with value ${branch}` - ); - } else if (!!envBranch && branch !== envBranch) { - console.warn( - `Ignoring branch ${envBranch} because branch option was passed to the client constructor with value ${branch}` - ); - } else if (!!previewBranch && !!envBranch && previewBranch !== envBranch) { - console.warn( - `Ignoring preview branch ${previewBranch} and branch ${envBranch} because branch option was passed to the client constructor with value ${branch}` - ); - } else if (!previewBranch && !envBranch && options?.branch === undefined) { - console.warn( - `No branch was passed to the client constructor. Using default branch ${branch}. You can set the branch with the environment variable XATA_BRANCH or by passing the branch option to the client constructor.` - ); + if (!branch) { + throw new Error('Option branch is required'); } return { diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 26dd96081..f949e2463 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -18,5 +18,5 @@ export * from './files'; export * from './transaction'; export { transformImage } from './files/transformations'; export type { ImageTransformations } from './files/transformations'; -export { getAPIKey, getBranch, getDatabaseURL, getPreviewBranch, buildPreviewBranchName } from './util/environment'; +export { buildPreviewBranchName, getDeployPreviewBranch } from './util/environment'; export { Buffer } from './util/buffer'; diff --git a/packages/client/src/types/global-deno.d.ts b/packages/client/src/types/global-deno.d.ts deleted file mode 100644 index e7ebf94f6..000000000 --- a/packages/client/src/types/global-deno.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare namespace Deno { - const env: { - get(name: string): string | undefined; - }; - - function run(options: { cmd: string[]; stdout?: string; stderr?: string }): { output(): Promise }; -} diff --git a/packages/client/src/types/global-node.d.ts b/packages/client/src/types/global-node.d.ts deleted file mode 100644 index bd2d275b5..000000000 --- a/packages/client/src/types/global-node.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare namespace process { - const env: Record; -} - -declare function require(module: string): any; diff --git a/packages/client/src/types/global-variables.d.ts b/packages/client/src/types/global-variables.d.ts deleted file mode 100644 index bad8d060e..000000000 --- a/packages/client/src/types/global-variables.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare const XATA_DATABASE_URL: string | undefined; -declare const XATA_API_KEY: string | undefined; -declare const XATA_BRANCH: string | undefined; -declare const XATA_ENABLE_BROWSER: string | boolean | undefined; diff --git a/packages/client/src/util/environment.ts b/packages/client/src/util/environment.ts index a47c831af..757aff50a 100644 --- a/packages/client/src/util/environment.ts +++ b/packages/client/src/util/environment.ts @@ -1,151 +1,36 @@ -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// +import { isObject } from './lang'; -import { isDefined, isObject } from './lang'; - -interface Environment { - apiKey: string | undefined; - databaseURL: string | undefined; - branch: string | undefined; - deployPreview: string | undefined; - deployPreviewBranch: string | undefined; - vercelGitCommitRef: string | undefined; - vercelGitRepoOwner: string | undefined; -} - -export function getEnvironment(): Environment { - // Node.js: process.env - try { - // Not using typeof process.env === 'object' because it's not working in some environments like Bun - if (isDefined(process) && isDefined(process.env)) { - return { - apiKey: process.env.XATA_API_KEY ?? getGlobalApiKey(), - databaseURL: process.env.XATA_DATABASE_URL ?? getGlobalDatabaseURL(), - branch: process.env.XATA_BRANCH ?? getGlobalBranch(), - deployPreview: process.env.XATA_PREVIEW, - deployPreviewBranch: process.env.XATA_PREVIEW_BRANCH, - vercelGitCommitRef: process.env.VERCEL_GIT_COMMIT_REF, - vercelGitRepoOwner: process.env.VERCEL_GIT_REPO_OWNER - }; - } - } catch (err) { - // Ignore: Should never happen - } - - // Deno: Deno.env.get - try { - if (isObject(Deno) && isObject(Deno.env)) { - return { - apiKey: Deno.env.get('XATA_API_KEY') ?? getGlobalApiKey(), - databaseURL: Deno.env.get('XATA_DATABASE_URL') ?? getGlobalDatabaseURL(), - branch: Deno.env.get('XATA_BRANCH') ?? getGlobalBranch(), - deployPreview: Deno.env.get('XATA_PREVIEW'), - deployPreviewBranch: Deno.env.get('XATA_PREVIEW_BRANCH'), - vercelGitCommitRef: Deno.env.get('VERCEL_GIT_COMMIT_REF'), - vercelGitRepoOwner: Deno.env.get('VERCEL_GIT_REPO_OWNER') - }; - } - } catch (err) { - // Ignore: Will fail if not using --allow-env - } - - return { - apiKey: getGlobalApiKey(), - databaseURL: getGlobalDatabaseURL(), - branch: getGlobalBranch(), - deployPreview: undefined, - deployPreviewBranch: undefined, - vercelGitCommitRef: undefined, - vercelGitRepoOwner: undefined - }; -} - -export function getEnableBrowserVariable() { +function parseEnvironment(environment: any): Record { try { - if (isObject(process) && isObject(process.env) && process.env.XATA_ENABLE_BROWSER !== undefined) { - return process.env.XATA_ENABLE_BROWSER === 'true'; + if (typeof environment === 'function') { + return new Proxy( + {}, + { + get(target) { + return environment(target); + } + } + ) as Record; } - } catch (err) { - // Ignore: Should never happen - } - try { - if (isObject(Deno) && isObject(Deno.env) && Deno.env.get('XATA_ENABLE_BROWSER') !== undefined) { - return Deno.env.get('XATA_ENABLE_BROWSER') === 'true'; + if (isObject(environment)) { + return environment as Record; } - } catch (err) { - // Ignore: Will fail if not using --allow-env - } - - try { - return XATA_ENABLE_BROWSER === true || XATA_ENABLE_BROWSER === 'true'; - } catch (err) { - return undefined; - } -} - -function getGlobalApiKey(): string | undefined { - try { - return XATA_API_KEY; - } catch (err) { - return undefined; - } -} - -function getGlobalDatabaseURL(): string | undefined { - try { - return XATA_DATABASE_URL; - } catch (err) { - return undefined; - } -} - -function getGlobalBranch(): string | undefined { - try { - return XATA_BRANCH; - } catch (err) { - return undefined; - } -} - -export function getDatabaseURL() { - try { - const { databaseURL } = getEnvironment(); - return databaseURL; - } catch (err) { - return undefined; + } catch (error) { + // noop } -} -export function getAPIKey() { - try { - const { apiKey } = getEnvironment(); - return apiKey; - } catch (err) { - return undefined; - } -} - -export function getBranch() { - try { - const { branch } = getEnvironment(); - return branch; - } catch (err) { - return undefined; - } + return {}; } export function buildPreviewBranchName({ org, branch }: { org: string; branch: string }) { return `preview-${org}-${branch}`; } -export function getPreviewBranch() { +export function getDeployPreviewBranch(environment: any) { try { - const { deployPreview, deployPreviewBranch, vercelGitCommitRef, vercelGitRepoOwner } = getEnvironment(); + const { deployPreview, deployPreviewBranch, vercelGitCommitRef, vercelGitRepoOwner } = + parseEnvironment(environment); if (deployPreviewBranch) return deployPreviewBranch; switch (deployPreview) { diff --git a/packages/codegen/example/build-example.mjs b/packages/codegen/example/build-example.mjs index cb8920493..9b96f3bc3 100644 --- a/packages/codegen/example/build-example.mjs +++ b/packages/codegen/example/build-example.mjs @@ -31,11 +31,11 @@ async function main() { } function replaceImport(source) { - return source.replaceAll('@xata.io/client', '../../client/src'); + return source?.replaceAll('@xata.io/client', '../../client/src'); } function undoReplaceImport(source) { - return source.replaceAll('../../client/src', '@xata.io/client'); + return source?.replaceAll('../../client/src', '@xata.io/client'); } main().catch(console.error); diff --git a/packages/codegen/example/types.d.ts b/packages/codegen/example/types.d.ts index 600b19945..d4277f692 100644 --- a/packages/codegen/example/types.d.ts +++ b/packages/codegen/example/types.d.ts @@ -249,5 +249,4 @@ declare const DatabaseClient: any; export declare class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions); } -export declare const getXataClient: () => XataClient; export {}; diff --git a/packages/codegen/example/xata.cjs b/packages/codegen/example/xata.cjs index e4556d88d..ed9b61386 100644 --- a/packages/codegen/example/xata.cjs +++ b/packages/codegen/example/xata.cjs @@ -1,7 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getXataClient = exports.XataClient = void 0; -// Generated by Xata Codegen 0.29.1. Please do not edit. +exports.XataClient = void 0; const client_1 = require("../../client/src"); /** @typedef { import('./types').SchemaTables } SchemaTables */ /** @type { SchemaTables } */ @@ -74,22 +73,23 @@ const tables = [ ]; /** @type { import('../../client/src').ClientConstructor<{}> } */ const DatabaseClient = (0, client_1.buildClient)(); -const defaultOptions = { - databaseURL: "https://test-r5vcv5.eu-west-1.xata.sh/db/test", -}; /** @typedef { import('./types').DatabaseSchema } DatabaseSchema */ /** @extends DatabaseClient */ class XataClient extends DatabaseClient { constructor(options) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + (0, client_1.getDeployPreviewBranch)(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } exports.XataClient = XataClient; -let instance = undefined; -/** @type { () => XataClient } */ -const getXataClient = () => { - if (instance) return instance; - instance = new XataClient(); - return instance; -}; -exports.getXataClient = getXataClient; diff --git a/packages/codegen/example/xata.js b/packages/codegen/example/xata.js index df321e500..734e27b58 100644 --- a/packages/codegen/example/xata.js +++ b/packages/codegen/example/xata.js @@ -1,5 +1,4 @@ -// Generated by Xata Codegen 0.29.1. Please do not edit. -import { buildClient } from '../../client/src'; +import { buildClient, getDeployPreviewBranch } from '../../client/src'; /** @typedef { import('./types').SchemaTables } SchemaTables */ /** @type { SchemaTables } */ const tables = [ @@ -71,20 +70,19 @@ const tables = [ ]; /** @type { import('../../client/src').ClientConstructor<{}> } */ const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: 'https://test-r5vcv5.eu-west-1.xata.sh/db/test' -}; /** @typedef { import('./types').DatabaseSchema } DatabaseSchema */ /** @extends DatabaseClient */ export class XataClient extends DatabaseClient { constructor(options) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: getDeployPreviewBranch(process.env) ?? process.env.XATA_BRANCH ?? 'main', + ...options + }, + tables + ); } } -let instance = undefined; -/** @type { () => XataClient } */ -export const getXataClient = () => { - if (instance) return instance; - instance = new XataClient(); - return instance; -}; diff --git a/packages/codegen/example/xata.ts b/packages/codegen/example/xata.ts index 68e6af4e0..cb2d107c5 100644 --- a/packages/codegen/example/xata.ts +++ b/packages/codegen/example/xata.ts @@ -1,7 +1,6 @@ -import { buildClient } from '../../client/src'; +import { buildClient, getDeployPreviewBranch } from '../../client/src'; import type { BaseClientOptions, SchemaInference, XataRecord } from '../../client/src'; -// This comment should be preserved by the codegen const tables = [ { name: 'teams', @@ -80,25 +79,21 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: 'https://test-r5vcv5.eu-west-1.xata.sh/db/test' -}; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: getDeployPreviewBranch(process.env) ?? process.env.XATA_BRANCH ?? 'main', + ...options + }, + tables + ); } } -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; - export type Teams = InferredTypes['teams']; export type TeamsRecord = Teams & XataRecord; diff --git a/packages/codegen/src/codegen.ts b/packages/codegen/src/codegen.ts index a7f991b83..8a56526c3 100644 --- a/packages/codegen/src/codegen.ts +++ b/packages/codegen/src/codegen.ts @@ -5,16 +5,12 @@ import * as parserTypeScript from 'prettier/parser-typescript.js'; import { Project, VariableDeclarationKind } from 'ts-morph'; import ts from 'typescript'; import { XataDatabaseSchema } from './schema'; -import { VERSION } from './version'; export type GenerateOptions = { schema: XataDatabaseSchema; - databaseURL: string; language: Language; moduleType?: ModuleType; javascriptTarget?: JavascriptTarget; - branch?: string; - workspace?: string; existingCode?: string; }; @@ -25,7 +21,7 @@ export type GenerateOutput = { }; export type Language = 'typescript' | 'javascript'; -export type ModuleType = 'esm' | 'cjs' | 'deno'; +export type ModuleType = 'esm' | 'cjs' | 'deno' | 'vite'; export type JavascriptTarget = keyof typeof ts.ScriptTarget | undefined; export function isValidJavascriptTarget(target?: string): target is JavascriptTarget { @@ -45,10 +41,8 @@ function getTypeName(tableName: string) { } export async function generate({ - databaseURL, - branch, language, - moduleType, + moduleType = 'esm', javascriptTarget, schema, existingCode @@ -65,7 +59,7 @@ export async function generate({ const sourceFile = project.createSourceFile('xata.ts', existingCode); const packageName = moduleType === 'deno' ? 'npm:@xata.io/client@latest' : '@xata.io/client'; - const packageImports = ['buildClient']; + const packageImports = ['buildClient', 'getDeployPreviewBranch']; const typeImports = ['BaseClientOptions', 'SchemaInference', 'XataRecord']; const importDeclarations = sourceFile @@ -93,11 +87,7 @@ export async function generate({ ); if (!sdkImport) { - sourceFile.addImportDeclaration({ - namedImports: packageImports, - moduleSpecifier: packageName, - leadingTrivia: existingCode ? undefined : `// Generated by Xata Codegen ${VERSION}. Please do not edit.\n` - }); + sourceFile.addImportDeclaration({ namedImports: packageImports, moduleSpecifier: packageName }); } else { const namedImports = new Set([...sdkImport.getNamedImports().map((i) => i.getName()), ...packageImports]); sdkImport.removeNamedImports(); @@ -224,25 +214,8 @@ export async function generate({ databaseClient.setInitializer(databaseClientContent); } - // Add default options - const defaultOptions = sourceFile.getVariableDeclaration('defaultOptions'); - const defaultOptionsContent = JSON.stringify({ databaseURL, branch }); - - if (!defaultOptions) { - sourceFile.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [{ name: 'defaultOptions', initializer: defaultOptionsContent }], - leadingTrivia: '\n' - }); - } else { - // TODO: merge with existing options - defaultOptions.setInitializer(defaultOptionsContent); - } - - // Add XataClient class - const xataClient = sourceFile.getClass('XataClient'); - - if (!xataClient) { + // Add XataClient class if doesn't exist already + if (!sourceFile.getClass('XataClient')) { sourceFile.addClass({ name: 'XataClient', extends: 'DatabaseClient', @@ -261,52 +234,19 @@ export async function generate({ hasQuestionToken: true } ], - statements: `super({ ...defaultOptions, ...options }, tables);` + statements: `super({ + apiKey: ${envVariable(moduleType, 'XATA_API_KEY')}, + databaseURL: ${envVariable(moduleType, 'XATA_DATABASE_URL')}, + // Use deploy preview branch if available, otherwise use branch from environment + branch: getDeployPreviewBranch(${envLoader(moduleType)}) ?? ${envVariable( + moduleType, + 'XATA_BRANCH' + )} ?? 'main', + ...options + }, tables);` } ] }); - } else { - // noop: we don't want to overwrite their constructor - } - - // Add XataClient instance - const xataClientInstance = sourceFile.getVariableDeclaration('instance'); - const getXataClient = sourceFile.getVariableDeclaration('getXataClient'); - - if (!getXataClient) { - if (!xataClientInstance) { - sourceFile.addVariableStatement({ - declarationKind: VariableDeclarationKind.Let, - declarations: [ - { - name: 'instance', - initializer: 'undefined', - type: 'XataClient | undefined' - } - ], - trailingTrivia: '\n' - }); - } - - sourceFile.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: 'getXataClient', - initializer: `() => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }` - } - ], - isExported: true, - leadingTrivia: language === 'javascript' ? `\n/** @type { () => XataClient } */\n` : '\n', - trailingTrivia: '\n' - }); - } else { - // noop: we don't want to overwrite their instance getter } sourceFile.saveSync(); @@ -361,3 +301,27 @@ function emitDeclarations(code: string) { return files.get('index.d.ts'); } + +function envLoader(module: ModuleType) { + switch (module) { + case 'cjs': + case 'esm': + return `process.env`; + case 'deno': + return `Deno.env.get`; + case 'vite': + return `import.meta.env`; + } +} + +function envVariable(module: ModuleType, variable: string) { + switch (module) { + case 'cjs': + case 'esm': + return `process.env.${variable}`; + case 'deno': + return `Deno.env.get("${variable}")`; + case 'vite': + return `import.meta.env.${variable}`; + } +} diff --git a/packages/importer/test/utils.ts b/packages/importer/test/utils.ts index d681172e0..a13e3c386 100644 --- a/packages/importer/test/utils.ts +++ b/packages/importer/test/utils.ts @@ -24,6 +24,7 @@ export const getXataClientWithPlugin = () => { return new XataClient({ apiKey: 'xau_test123', - databaseURL: 'https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb' + databaseURL: 'https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb', + branch: 'main' }); }; diff --git a/test/__snapshots__/codegen.test.ts.snap b/test/__snapshots__/codegen.test.ts.snap index f31939de2..a72d0afcd 100644 --- a/test/__snapshots__/codegen.test.ts.snap +++ b/test/__snapshots__/codegen.test.ts.snap @@ -3,40 +3,41 @@ exports[`generate > should generate CJS code 1`] = ` ""use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getXataClient = exports.XataClient = void 0; -// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. +exports.XataClient = void 0; const client_1 = require("@xata.io/client"); /** @typedef { import('./types').SchemaTables } SchemaTables */ /** @type { SchemaTables } */ const tables = [{ name: "users", columns: [{ name: "name", type: "string" }] }]; /** @type { import('@xata.io/client').ClientConstructor<{}> } */ const DatabaseClient = (0, client_1.buildClient)(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", - branch: "feature-branch", -}; /** @typedef { import('./types').DatabaseSchema } DatabaseSchema */ /** @extends DatabaseClient */ class XataClient extends DatabaseClient { constructor(options) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + (0, client_1.getDeployPreviewBranch)(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } exports.XataClient = XataClient; -let instance = undefined; -/** @type { () => XataClient } */ -const getXataClient = () => { - if (instance) return instance; - instance = new XataClient(); - return instance; -}; -exports.getXataClient = getXataClient; " `; exports[`generate > should generate Deno code 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "npm:@xata.io/client@latest"; +"import { + buildClient, + getDeployPreviewBranch, +} from "npm:@xata.io/client@latest"; import type { BaseClientOptions, SchemaInference, @@ -59,31 +60,28 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", - branch: "feature-branch", -}; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: Deno.env.get("XATA_API_KEY"), + databaseURL: Deno.env.get("XATA_DATABASE_URL"), + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(Deno.env.get) ?? + Deno.env.get("XATA_BRANCH") ?? + "main", + ...options, + }, + tables + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should ignore CJS for TS code 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, @@ -106,31 +104,28 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", - branch: "feature-branch", -}; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should inject branch if passed 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, @@ -153,31 +148,28 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", - branch: "feature-branch", -}; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should respect case naming 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, @@ -219,30 +211,28 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", -}; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should respect numbers in names 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, @@ -271,30 +261,28 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", -}; - export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should respect numbers in names 2`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; const tables = [ { name: "1teams-case", @@ -305,20 +293,23 @@ const tables = [ }, ]; const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", -}; export class XataClient extends DatabaseClient { constructor(options) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + tables + ); } } -let instance = undefined; -export const getXataClient = () => { - if (instance) return instance; - instance = new XataClient(); - return instance; -}; " `; @@ -354,7 +345,6 @@ declare const DatabaseClient: any; export declare class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions); } -export declare const getXataClient: () => XataClient; export {}; " `;