Repository: xataio/client-ts (ts)

Issue:
Codegen language selection is not working properly
From the README:

```
xata-codegen generate [path to xata directory] -o generated/client.ts
```

If you do that in `main`, the result is a `generated/client.ts.ts` file. Note the duplicated extension

If you do

```
xata-codegen generate [path to xata directory] -o generated/client.js
```

The result is a `generated/client.js.ts` extension.

Previously the language was deducted from the extension. But now there's an optional --lang parameter that if it's not used, the extension the `.ts` extension is always added regardless of the extension the user provides. I haven't checked what happens if you use the param and also put an extension in the `-o` option.

My suggestion is to remove the --lang option and deduct the language from the extension.

cc @TejasQ 

It's easy to reproduce the probem by runnning `npm run build-example`

<img width="1039" alt="image" src="https://user-images.githubusercontent.com/50486/158776486-819f449c-36e8-4025-82f3-88a5162643e7.png">


Pull request description:
Derive codegen language from output file path
## Summary

This PR fixes #51 by deriving the language from the output file path and removing the now unnecessary language parameter.

Patch:
diff --git a/codegen/src/codegen.ts b/codegen/src/codegen.ts
index dc33a256..8740dd4d 100644
--- a/codegen/src/codegen.ts
+++ b/codegen/src/codegen.ts
@@ -6,12 +6,13 @@ import { Column, fileSchema, Table } from './schema';
 import { join } from 'path';
 
 import prettier from 'prettier';
-import { getExtensionFromLanguage } from './getExtensionFromLanguage';
+import { getLanguageFromExtension } from './getLanguageFromExtension';
+import { isExtensionValid } from './isExtensionValid';
+import { errors } from './errors';
 
 type GenerateOptions = {
   xataDirectory: string;
   outputFilePath: string;
-  language?: Language;
   writeFile?: typeof fs.writeFile;
 };
 
@@ -99,15 +100,16 @@ function parseSchema(input: string) {
   }
 }
 
-export type Language = 'typescript' | 'javascript' | 'js' | 'ts';
+export type Language = 'typescript' | 'javascript';
+
+export async function generate({ outputFilePath: output, xataDirectory, writeFile = fs.writeFile }: GenerateOptions) {
+  const fullOutputPath = path.resolve(process.cwd(), output);
+  const [extension] = fullOutputPath.split('.').slice(-1);
+
+  if (!isExtensionValid(extension)) {
+    throw new Error(errors.invalidCodegenOutputExtension);
+  }
 
-export async function generate({
-  outputFilePath: output,
-  xataDirectory,
-  language = 'ts',
-  writeFile = fs.writeFile
-}: GenerateOptions) {
-  const fullOutputPath = path.resolve(process.cwd(), `${output}${getExtensionFromLanguage(language)}`);
   const schemaFile = join(xataDirectory, 'schema.json');
   const input = await readSchema(schemaFile);
   const schema = parseSchema(input);
@@ -123,7 +125,7 @@ export async function generate({
     }
   }
 
-  if (['typescript', 'ts'].includes(language)) {
+  if (getLanguageFromExtension(extension) === 'typescript') {
     const code = `
     import {
       BaseClient,
diff --git a/codegen/src/errors.ts b/codegen/src/errors.ts
new file mode 100644
index 00000000..c8c6bbeb
--- /dev/null
+++ b/codegen/src/errors.ts
@@ -0,0 +1,6 @@
+export const errors = {
+  invalidCodegenOutputExtension: `You've specified an invalid extension in your output parameter. We can generate code for files in JavaScript (ending with .js), or TypeScript (ending with .ts). Please adjust your output argument.
+  
+  More in the docs:
+  ` /** @todo add a link after docs exist */
+};
diff --git a/codegen/src/generateWithOutput.ts b/codegen/src/generateWithOutput.ts
index 3f033fc7..a5641512 100644
--- a/codegen/src/generateWithOutput.ts
+++ b/codegen/src/generateWithOutput.ts
@@ -1,10 +1,8 @@
 import { Ora } from 'ora';
 import { relative } from 'path';
-import { generate, Language } from './codegen';
-import { getExtensionFromLanguage } from './getExtensionFromLanguage';
+import { generate } from './codegen';
 
 export const generateWithOutput = async ({
-  lang,
   out,
   xataDirectory,
   spinner
@@ -12,13 +10,10 @@ export const generateWithOutput = async ({
   spinner: Ora;
   xataDirectory: string;
   out: string;
-  lang: Language;
 }) => {
   spinner.text = 'Found schema, generating...';
 
-  await generate({ xataDirectory, outputFilePath: out, language: lang });
+  await generate({ xataDirectory, outputFilePath: out });
 
-  spinner.succeed(
-    `Your XataClient is generated at ./${relative(process.cwd(), `${out}${getExtensionFromLanguage(lang)}`)}.`
-  );
+  spinner.succeed(`Your XataClient is generated at ./${relative(process.cwd(), out)}.`);
 };
diff --git a/codegen/src/getExtensionFromLanguage.ts b/codegen/src/getExtensionFromLanguage.ts
deleted file mode 100644
index f3032489..00000000
--- a/codegen/src/getExtensionFromLanguage.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import type { Language } from './codegen';
-
-export const getExtensionFromLanguage = (language?: Language) => {
-  switch (language) {
-    case 'javascript':
-    case 'js':
-      return '.js';
-    case 'typescript':
-    case 'ts':
-    case undefined:
-      return '.ts';
-    default:
-      throw new Error('Invalid language specified.');
-  }
-};
diff --git a/codegen/src/getLanguageFromExtension.ts b/codegen/src/getLanguageFromExtension.ts
new file mode 100644
index 00000000..b6d0ee3d
--- /dev/null
+++ b/codegen/src/getLanguageFromExtension.ts
@@ -0,0 +1,14 @@
+import type { Language } from './codegen';
+import { errors } from './errors';
+
+export const getLanguageFromExtension = (extension?: 'ts' | 'js'): Language => {
+  switch (extension) {
+    case 'js':
+      return 'javascript';
+    case 'ts':
+    case undefined:
+      return 'typescript';
+    default:
+      throw new Error(errors.invalidCodegenOutputExtension);
+  }
+};
diff --git a/codegen/src/index.ts b/codegen/src/index.ts
index f8cbd65f..1366263b 100644
--- a/codegen/src/index.ts
+++ b/codegen/src/index.ts
@@ -15,7 +15,6 @@ import { cliPath } from './cliPath';
 
 const defaultXataDirectory = join(process.cwd(), 'xata');
 const defaultOutputFile = join(process.cwd(), 'XataClient');
-const defaultLanguage = 'ts';
 
 program
   .name('xata-codegen')
@@ -33,11 +32,6 @@ program
     defaultXataDirectory
   )
   .option('-o, --out <path>', 'A path to store your generated API client.', defaultOutputFile)
-  .option(
-    '-l, --lang [js|ts]',
-    "An option to choose the type of code you'd like us to output: TypeScript (ts, preferred) or JavaScript (js)",
-    defaultLanguage
-  )
   .action(async (xataDirectory, { out, lang }) => {
     const spinner = ora();
     const schema = join(xataDirectory, 'schema.json');
@@ -45,7 +39,7 @@ program
 
     try {
       await access(schema); // Make sure the schema file exists
-      await generateWithOutput({ xataDirectory, out, lang, spinner });
+      await generateWithOutput({ xataDirectory, out, spinner });
     } catch (e: any) {
       if (!e.message.includes('ENOENT')) {
         spinner.fail(e.message);
@@ -74,7 +68,6 @@ program
         await generateWithOutput({
           xataDirectory,
           out: defaultOutputFile,
-          lang: defaultLanguage,
           spinner
         });
       } catch (e: any) {
diff --git a/codegen/src/isExtensionValid.ts b/codegen/src/isExtensionValid.ts
new file mode 100644
index 00000000..a20fcd5e
--- /dev/null
+++ b/codegen/src/isExtensionValid.ts
@@ -0,0 +1,1 @@
+export const isExtensionValid = (extension: string): extension is 'js' | 'ts' => ['ts', 'js'].includes(extension);
diff --git a/package.json b/package.json
index 42498b21..fa4935dc 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
     "lint": "eslint",
     "lint:fix": "eslint --fix",
     "prepare": "husky install",
-    "build-example": "cd codegen && npm run example-ts && npm run example-js"
+    "build-example": "cd codegen && npm run example-ts && npm run example-js && cd .."
   },
   "repository": {
     "type": "git",
@@ -43,6 +43,10 @@
     "typescript": "^4.5.5"
   },
   "lint-staged": {
+    "codegen/src/*.{js,ts,tsx}": [
+      "npm run build-example",
+      "git add codegen/example"
+    ],
     "*.{js,ts,tsx}": [
       "eslint --cache --fix",
       "prettier --write"


Test patch:
diff --git a/test/codegen.test.ts b/test/codegen.test.ts
index 90d10d12..6bc1098c 100644
--- a/test/codegen.test.ts
+++ b/test/codegen.test.ts
@@ -4,7 +4,7 @@ import { generate } from '../codegen/src/codegen';
 describe('codegen', () => {
   it('should generate correct TypeScript', async () => {
     const xataDirectory = join(__dirname, 'mocks');
-    const outputFilePath = 'hahaha';
+    const outputFilePath = 'hahaha.ts';
 
     const writeFile = jest.fn();
     await generate({ xataDirectory, outputFilePath, writeFile });
@@ -16,7 +16,7 @@ describe('codegen', () => {
 
   it('should generate correct JavaScript', async () => {
     const xataDirectory = join(__dirname, 'mocks');
-    const outputFilePath = 'hahaha';
+    const outputFilePath = 'hahaha.js';
 
     const writeFile = jest.fn();
     await generate({ xataDirectory, outputFilePath, writeFile: writeFile, language: 'javascript' });