diff --git a/package-lock.json b/package-lock.json index 84506081..726ecee9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2287,6 +2287,20 @@ "rescript": "^11.1.0-rc.7" } }, + "node_modules/@rescript/tools": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@rescript/tools/-/tools-0.6.2.tgz", + "integrity": "sha512-v3ZCMXy5zbgBdKM+cM2aEWT9e+zZIo8PERoTcK4XzmQ2yI5cBEOflkRpKpaP7UbIYfEW6rdckA+C0mAdEzDUYw==", + "dependencies": { + "rescript": "^11.0.0-rc.7" + }, + "bin": { + "rescript-tools": "npm/cli.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/@scarf/scarf": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.1.1.tgz", @@ -10238,9 +10252,9 @@ } }, "node_modules/rescript-embed-lang": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/rescript-embed-lang/-/rescript-embed-lang-0.5.0.tgz", - "integrity": "sha512-VEKFMnyPrq2TtjNVp7mM2nthamY5bQVI7bxk2GVcMEcdTGxJuEytvNRVU4rMOgxeEGdJI71Df/p7KJRaGsGAfw==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/rescript-embed-lang/-/rescript-embed-lang-0.5.1.tgz", + "integrity": "sha512-67X3lqYN3Ppbc56L1rtWoysv/BXp55VRUUVIrVneCh9AGlkHWce6zRs8B4PbOg60bGM5Wp0Z8Tek+djpONMX+A==", "hasInstallScript": true }, "node_modules/resolve": { @@ -12306,6 +12320,7 @@ "dependencies": { "@pgtyped/parser": "^2.1.0", "@pgtyped/wire": "^2.2.0", + "@rescript/tools": "^0.6.2", "camel-case": "^4.1.1", "chalk": "^4.0.0", "chokidar": "^3.3.1", @@ -12354,7 +12369,7 @@ "pgtyped-rescript": "^2.4.0", "pgtyped-rescript-query": "^2.3.0", "rescript": "11.1.0", - "rescript-embed-lang": "^0.5.0", + "rescript-embed-lang": "^0.5.1", "typescript": "4.9.4" }, "devDependencies": { @@ -14009,7 +14024,7 @@ "pgtyped-rescript": "^2.4.0", "pgtyped-rescript-query": "^2.3.0", "rescript": "11.1.0", - "rescript-embed-lang": "^0.5.0", + "rescript-embed-lang": "^0.5.1", "ts-node": "10.9.1", "typescript": "4.9.4" }, @@ -14060,6 +14075,14 @@ "integrity": "sha512-wNZOZ63sYcaIYZCmTZeIPCeLa3HCGgPbIOR8zjyNkoBYUlxNV8Nb2ZyqlXR5Mb9ttvv8fTV56JbKhyVEZEYo8g==", "requires": {} }, + "@rescript/tools": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@rescript/tools/-/tools-0.6.2.tgz", + "integrity": "sha512-v3ZCMXy5zbgBdKM+cM2aEWT9e+zZIo8PERoTcK4XzmQ2yI5cBEOflkRpKpaP7UbIYfEW6rdckA+C0mAdEzDUYw==", + "requires": { + "rescript": "^11.0.0-rc.7" + } + }, "@scarf/scarf": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.1.1.tgz", @@ -19656,6 +19679,7 @@ "@pgtyped/parser": "^2.1.0", "@pgtyped/wire": "^2.2.0", "@rescript/core": "1.3.0", + "@rescript/tools": "^0.6.2", "@types/debug": "4.1.8", "@types/fs-extra": "11.0.1", "@types/glob": "8.1.0", @@ -20186,9 +20210,9 @@ "integrity": "sha512-9la2Dv+ACylQ77I8s4spPu1JnLZXbH5WgxcLHLLUBWgFFSiv0wXqgzWztrBIZqwFgVX5BYcwldUqUVcEzdCyHg==" }, "rescript-embed-lang": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/rescript-embed-lang/-/rescript-embed-lang-0.5.0.tgz", - "integrity": "sha512-VEKFMnyPrq2TtjNVp7mM2nthamY5bQVI7bxk2GVcMEcdTGxJuEytvNRVU4rMOgxeEGdJI71Df/p7KJRaGsGAfw==" + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/rescript-embed-lang/-/rescript-embed-lang-0.5.1.tgz", + "integrity": "sha512-67X3lqYN3Ppbc56L1rtWoysv/BXp55VRUUVIrVneCh9AGlkHWce6zRs8B4PbOg60bGM5Wp0Z8Tek+djpONMX+A==" }, "resolve": { "version": "1.22.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 075b42f4..ff8cd3e8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -38,6 +38,7 @@ "dependencies": { "@pgtyped/parser": "^2.1.0", "@pgtyped/wire": "^2.2.0", + "@rescript/tools": "^0.6.2", "camel-case": "^4.1.1", "chalk": "^4.0.0", "chokidar": "^3.3.1", diff --git a/packages/cli/src/generator.ts b/packages/cli/src/generator.ts index 2af4fa58..d77342c4 100644 --- a/packages/cli/src/generator.ts +++ b/packages/cli/src/generator.ts @@ -289,7 +289,9 @@ async function generateTypedecsFromFile( const typeSource: TypeSource = (query) => getTypes(query, connection); const { queries, events } = - mode === 'res' ? parseRescriptFile(contents) : parseSQLFile(contents); + mode === 'res' + ? parseRescriptFile(contents, fileName) + : parseSQLFile(contents); if (events.length > 0) { prettyPrintEvents(contents, events); if (events.find((e) => 'critical' in e)) { diff --git a/packages/cli/src/parseRescript.ts b/packages/cli/src/parseRescript.ts index 54d4612f..f57a3083 100644 --- a/packages/cli/src/parseRescript.ts +++ b/packages/cli/src/parseRescript.ts @@ -1,7 +1,13 @@ import { parseSQLFile } from '@pgtyped/parser'; import { SQLParseResult } from '@pgtyped/parser/lib/loader/sql'; +import cp from 'child_process'; +// @ts-ignore +import { getBinaryPath } from '@rescript/tools/npm/getBinaryPath.js'; -export function parseCode(fileContent: string): SQLParseResult { +export function parseCode( + fileContent: string, + fileName: string, +): SQLParseResult { if (!fileContent.includes('%sql')) { return { queries: [], @@ -10,12 +16,22 @@ export function parseCode(fileContent: string): SQLParseResult { } // Replace with more robust @rescript/tools CLI usage when that package ships linuxarm64 binary. - const regex = /%sql(?:\.\w+)?\(`([^`]*)`\)/g; - let match; - const queries = []; + const content: Array<{ contents: string }> = JSON.parse( + cp + .execFileSync(getBinaryPath(), [ + 'extract-embedded', + ['sql', 'sql.one', 'sql.expectOne', 'sql.many', 'sql.execute'].join( + ',', + ), + fileName, + ]) + .toString(), + ); - while ((match = regex.exec(fileContent)) !== null) { - let query = match[1].trim(); + const queries: Array = []; + + content.forEach((v) => { + let query = v.contents.trim(); if (!query.endsWith(';')) { query += ';'; } @@ -40,7 +56,7 @@ export function parseCode(fileContent: string): SQLParseResult { } queries.push(query); - } + }); const asSql = queries.join('\n\n'); diff --git a/packages/example/package.json b/packages/example/package.json index 15d2da68..3c2f36f6 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -28,7 +28,7 @@ "pgtyped-rescript": "^2.4.0", "pgtyped-rescript-query": "^2.3.0", "rescript": "11.1.0", - "rescript-embed-lang": "^0.5.0", + "rescript-embed-lang": "^0.5.1", "typescript": "4.9.4" }, "devDependencies": { diff --git a/packages/example/src/books/BookService__sql.gen.tsx b/packages/example/src/books/BookService__sql.gen.tsx index 7204fbef..b1028c18 100644 --- a/packages/example/src/books/BookService__sql.gen.tsx +++ b/packages/example/src/books/BookService__sql.gen.tsx @@ -15,11 +15,11 @@ export type arrayJSON_t = JSON_t[]; export type categoryArray = category[]; -/** 'FindBookById' parameters type */ -export type findBookByIdParams = { readonly id?: number }; +/** 'BooksByAuthor' parameters type */ +export type booksByAuthorParams = { readonly authorName: string }; -/** 'FindBookById' return type */ -export type findBookByIdResult = { +/** 'BooksByAuthor' return type */ +export type booksByAuthorResult = { readonly author_id: (undefined | number); readonly big_int: (undefined | bigint); readonly categories: (undefined | categoryArray); @@ -29,14 +29,14 @@ export type findBookByIdResult = { readonly rank: (undefined | number) }; -/** 'FindBookById' query type */ -export type findBookByIdQuery = { readonly params: findBookByIdParams; readonly result: findBookByIdResult }; +/** 'BooksByAuthor' query type */ +export type booksByAuthorQuery = { readonly params: booksByAuthorParams; readonly result: booksByAuthorResult }; -/** 'BooksByAuthor' parameters type */ -export type booksByAuthorParams = { readonly authorName: string }; +/** 'FindBookById' parameters type */ +export type findBookByIdParams = { readonly id?: number }; -/** 'BooksByAuthor' return type */ -export type booksByAuthorResult = { +/** 'FindBookById' return type */ +export type findBookByIdResult = { readonly author_id: (undefined | number); readonly big_int: (undefined | bigint); readonly categories: (undefined | categoryArray); @@ -46,40 +46,40 @@ export type booksByAuthorResult = { readonly rank: (undefined | number) }; -/** 'BooksByAuthor' query type */ -export type booksByAuthorQuery = { readonly params: booksByAuthorParams; readonly result: booksByAuthorResult }; +/** 'FindBookById' query type */ +export type findBookByIdQuery = { readonly params: findBookByIdParams; readonly result: findBookByIdResult }; /** Returns an array of all matched results. */ -export const FindBookById_many: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise = BookService__sqlJS.FindBookById.many as any; +export const BooksByAuthor_many: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise = BookService__sqlJS.BooksByAuthor.many as any; /** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ -export const FindBookById_one: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise<(undefined | findBookByIdResult)> = BookService__sqlJS.FindBookById.one as any; +export const BooksByAuthor_one: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise<(undefined | booksByAuthorResult)> = BookService__sqlJS.BooksByAuthor.one as any; /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ -export const FindBookById_expectOne: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams, errorMessage:(undefined | string)) => Promise< - { TAG: "Ok"; _0: findBookByIdResult } - | { TAG: "Error"; _0: string }> = BookService__sqlJS.FindBookById.expectOne as any; +export const BooksByAuthor_expectOne: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams, errorMessage:(undefined | string)) => Promise< + { TAG: "Ok"; _0: booksByAuthorResult } + | { TAG: "Error"; _0: string }> = BookService__sqlJS.BooksByAuthor.expectOne as any; /** Executes the query, but ignores whatever is returned by it. */ -export const FindBookById_execute: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise = BookService__sqlJS.FindBookById.execute as any; +export const BooksByAuthor_execute: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise = BookService__sqlJS.BooksByAuthor.execute as any; -export const findBookById: (params:findBookByIdParams, client:PgTyped_Pg_Client_t) => Promise = BookService__sqlJS.findBookById as any; +export const booksByAuthor: (params:booksByAuthorParams, client:PgTyped_Pg_Client_t) => Promise = BookService__sqlJS.booksByAuthor as any; /** Returns an array of all matched results. */ -export const BooksByAuthor_many: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise = BookService__sqlJS.BooksByAuthor.many as any; +export const FindBookById_many: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise = BookService__sqlJS.FindBookById.many as any; /** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ -export const BooksByAuthor_one: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise<(undefined | booksByAuthorResult)> = BookService__sqlJS.BooksByAuthor.one as any; +export const FindBookById_one: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise<(undefined | findBookByIdResult)> = BookService__sqlJS.FindBookById.one as any; /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ -export const BooksByAuthor_expectOne: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams, errorMessage:(undefined | string)) => Promise< - { TAG: "Ok"; _0: booksByAuthorResult } - | { TAG: "Error"; _0: string }> = BookService__sqlJS.BooksByAuthor.expectOne as any; +export const FindBookById_expectOne: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams, errorMessage:(undefined | string)) => Promise< + { TAG: "Ok"; _0: findBookByIdResult } + | { TAG: "Error"; _0: string }> = BookService__sqlJS.FindBookById.expectOne as any; /** Executes the query, but ignores whatever is returned by it. */ -export const BooksByAuthor_execute: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise = BookService__sqlJS.BooksByAuthor.execute as any; +export const FindBookById_execute: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise = BookService__sqlJS.FindBookById.execute as any; -export const booksByAuthor: (params:booksByAuthorParams, client:PgTyped_Pg_Client_t) => Promise = BookService__sqlJS.booksByAuthor as any; +export const findBookById: (params:findBookByIdParams, client:PgTyped_Pg_Client_t) => Promise = BookService__sqlJS.findBookById as any; export const FindBookById: { /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ diff --git a/packages/example/src/books/BookService__sql.res b/packages/example/src/books/BookService__sql.res index 1469903f..2a4b178d 100644 --- a/packages/example/src/books/BookService__sql.res +++ b/packages/example/src/books/BookService__sql.res @@ -11,15 +11,15 @@ type arrayJSON_t = array @gentype type categoryArray = array -/** 'FindBookById' parameters type */ +/** 'Query1' parameters type */ @gentype -type findBookByIdParams = { - id?: int, +type query1Params = { + authorName: string, } -/** 'FindBookById' return type */ +/** 'Query1' return type */ @gentype -type findBookByIdResult = { +type query1Result = { author_id: option, big_int: option, categories: option, @@ -29,45 +29,47 @@ type findBookByIdResult = { rank: option, } -/** 'FindBookById' query type */ +/** 'Query1' query type */ @gentype -type findBookByIdQuery = { - params: findBookByIdParams, - result: findBookByIdResult, +type query1Query = { + params: query1Params, + result: query1Result, } -%%private(let findBookByIdIR: IR.t = %raw(`{"usedParamSet":{"id":true},"params":[{"name":"id","required":false,"transform":{"type":"scalar"},"locs":[{"a":31,"b":33}]}],"statement":"SELECT * FROM books WHERE id = :id"}`)) +%%private(let query1IR: IR.t = %raw(`{"usedParamSet":{"authorName":true},"params":[{"name":"authorName","required":true,"transform":{"type":"scalar"},"locs":[{"a":118,"b":129}]}],"statement":"SELECT b.* FROM books b\n INNER JOIN authors a ON a.id = b.author_id\n WHERE a.first_name || ' ' || a.last_name = :authorName!"}`)) /** Runnable query: ```sql -SELECT * FROM books WHERE id = $1 +SELECT b.* FROM books b + INNER JOIN authors a ON a.id = b.author_id + WHERE a.first_name || ' ' || a.last_name = $1 ``` */ @gentype -module FindBookById: { +module Query1: { /** Returns an array of all matched results. */ @gentype - let many: (PgTyped.Pg.Client.t, findBookByIdParams) => promise> + let many: (PgTyped.Pg.Client.t, query1Params) => promise> /** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ @gentype - let one: (PgTyped.Pg.Client.t, findBookByIdParams) => promise> + let one: (PgTyped.Pg.Client.t, query1Params) => promise> /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ @gentype let expectOne: ( PgTyped.Pg.Client.t, - findBookByIdParams, + query1Params, ~errorMessage: string=? - ) => promise> + ) => promise> /** Executes the query, but ignores whatever is returned by it. */ @gentype - let execute: (PgTyped.Pg.Client.t, findBookByIdParams) => promise + let execute: (PgTyped.Pg.Client.t, query1Params) => promise } = { - @module("pgtyped-rescript-runtime") @new external findBookById: IR.t => PreparedStatement.t = "PreparedQuery"; - let query = findBookById(findBookByIdIR) + @module("pgtyped-rescript-runtime") @new external query1: IR.t => PreparedStatement.t = "PreparedQuery"; + let query = query1(query1IR) let query = (params, ~client) => query->PreparedStatement.run(params, ~client) @gentype @@ -92,19 +94,19 @@ module FindBookById: { } @gentype -@deprecated("Use 'FindBookById.many' directly instead") -let findBookById = (params, ~client) => FindBookById.many(client, params) +@deprecated("Use 'Query1.many' directly instead") +let query1 = (params, ~client) => Query1.many(client, params) -/** 'BooksByAuthor' parameters type */ +/** 'FindBookById' parameters type */ @gentype -type booksByAuthorParams = { - authorName: string, +type findBookByIdParams = { + id?: int, } -/** 'BooksByAuthor' return type */ +/** 'FindBookById' return type */ @gentype -type booksByAuthorResult = { +type findBookByIdResult = { author_id: option, big_int: option, categories: option, @@ -114,47 +116,45 @@ type booksByAuthorResult = { rank: option, } -/** 'BooksByAuthor' query type */ +/** 'FindBookById' query type */ @gentype -type booksByAuthorQuery = { - params: booksByAuthorParams, - result: booksByAuthorResult, +type findBookByIdQuery = { + params: findBookByIdParams, + result: findBookByIdResult, } -%%private(let booksByAuthorIR: IR.t = %raw(`{"usedParamSet":{"authorName":true},"params":[{"name":"authorName","required":true,"transform":{"type":"scalar"},"locs":[{"a":118,"b":129}]}],"statement":"SELECT b.* FROM books b\n INNER JOIN authors a ON a.id = b.author_id\n WHERE a.first_name || ' ' || a.last_name = :authorName!"}`)) +%%private(let findBookByIdIR: IR.t = %raw(`{"usedParamSet":{"id":true},"params":[{"name":"id","required":false,"transform":{"type":"scalar"},"locs":[{"a":31,"b":33}]}],"statement":"SELECT * FROM books WHERE id = :id"}`)) /** Runnable query: ```sql -SELECT b.* FROM books b - INNER JOIN authors a ON a.id = b.author_id - WHERE a.first_name || ' ' || a.last_name = $1 +SELECT * FROM books WHERE id = $1 ``` */ @gentype -module BooksByAuthor: { +module FindBookById: { /** Returns an array of all matched results. */ @gentype - let many: (PgTyped.Pg.Client.t, booksByAuthorParams) => promise> + let many: (PgTyped.Pg.Client.t, findBookByIdParams) => promise> /** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ @gentype - let one: (PgTyped.Pg.Client.t, booksByAuthorParams) => promise> + let one: (PgTyped.Pg.Client.t, findBookByIdParams) => promise> /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ @gentype let expectOne: ( PgTyped.Pg.Client.t, - booksByAuthorParams, + findBookByIdParams, ~errorMessage: string=? - ) => promise> + ) => promise> /** Executes the query, but ignores whatever is returned by it. */ @gentype - let execute: (PgTyped.Pg.Client.t, booksByAuthorParams) => promise + let execute: (PgTyped.Pg.Client.t, findBookByIdParams) => promise } = { - @module("pgtyped-rescript-runtime") @new external booksByAuthor: IR.t => PreparedStatement.t = "PreparedQuery"; - let query = booksByAuthor(booksByAuthorIR) + @module("pgtyped-rescript-runtime") @new external findBookById: IR.t => PreparedStatement.t = "PreparedQuery"; + let query = findBookById(findBookByIdIR) let query = (params, ~client) => query->PreparedStatement.run(params, ~client) @gentype @@ -179,7 +179,7 @@ module BooksByAuthor: { } @gentype -@deprecated("Use 'BooksByAuthor.many' directly instead") -let booksByAuthor = (params, ~client) => BooksByAuthor.many(client, params) +@deprecated("Use 'FindBookById.many' directly instead") +let findBookById = (params, ~client) => FindBookById.many(client, params)