JasonShin / sqlx-ts

node.js compile-time SQL validations & type generations
https://jasonshin.github.io/sqlx-ts/
MIT License
170 stars 6 forks source link

Programmatic JS API #124

Open JulianCataldo opened 2 months ago

JulianCataldo commented 2 months ago

Hello!

Calling sqlx-ts from a Node script could be very handy, so we can integrate it in broader codegen workflow. It could just take an input string and output a string, so we (users) could handle watching and writing files ourselves.

I'm not sure if it's feasible due to the JS/Rust bindings stuff, etc.

What do you think?

Thank you

JasonShin commented 2 months ago

Hello, this is interesting. Could you explain a bit more about the codegen use case (and being able to call native sqlx-ts from Node?)? I've been using sqlx-ts CLI primarily for local DX and CI/CD integration.

If watching for file changes is needed, I recommend calling sqlx-ts binary using nodemon (or anything that you prefer).

JulianCataldo commented 2 months ago

Hey!

This is the script I'm using right now, the goal is to have a live feedback loop during dev.

import { exec as _exec } from 'node:child_process';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { dirname, join, normalize } from 'node:path';
import { promisify } from 'node:util';

import { watch } from 'chokidar';

const exec = promisify(_exec);

const TMP = './.tmp';

await mkdir(TMP).catch(() => null);

watch(['./src/database/*/**/*.ts', '!**/*.queries.ts']).on(
  'all',
  async (event, file) => {
    const dest = join(TMP, file);
    await mkdir(dirname(dest), { recursive: true });
    const fileContent = await readFile(file, 'utf8');

    // NOTE: We need to remove template expression (`${foo}`).
    // They're not supported by `sqlx-ts`
    const fileWithoutTemplateExpressions = fileContent.replaceAll(
      /\${(.*?)}/g,
      '?',
    );

    await writeFile(dest, fileWithoutTemplateExpressions);

    const resultSqlTs = await exec(
      `pnpm sqlx-ts '${TMP}' --config .sqlxrc.json`,
    );
    console.log(resultSqlTs);
    const resultPrettier = await exec(`pnpm prettier --write ${TMP}/**/*.ts`);
    console.log(resultPrettier);

    const sourceQueries = dest.replace(/\.ts$/, '.queries.ts');
    const destQueries = sourceQueries.replace(normalize(TMP), '.');
    console.log({ sourceQueries, destQueries });

    const queriesContent = await readFile(sourceQueries, 'utf8');
    await writeFile(
      destQueries,
      `/* /!\\ AUTO-GENERATED /!\\ */\n\n${queriesContent}`,
    );
  },
);

(Unrelated but the Prettier part can be handled with a JS API, too, I was just too lazy to set it up, and it's just the finishing touch).

I'm doing several stuff here, and if I want to alter the input/output, I have to use the filesystem as a medium, which is sub-optimal.

With a JS API, I could just call something like (skipping the bike-shedding):

import { generateTypesForFile } from 'sqlx-ts';

// ...

const { result, errors } = await generateTypesForFile({ input: '...a string from the TS/whatever file…' });

// Do something with the string result
// ...

I hope this use case is more clear for you :)

Thank you