Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 188 additions & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
{
name: 'eslint overrides',
rules: {
curly: ['error', 'all'],
eqeqeq: ['error', 'always', { null: 'ignore' }],
'logical-assignment-operators': 'error',
'no-else-return': 'error',
Expand Down Expand Up @@ -265,6 +266,192 @@
'@typescript-eslint/no-throw-literal': 'off',
'unicorn/no-array-reduce': 'off',
},
}
},
//#endregion

{
name: "custom",

Check failure on line 273 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Replace `"custom"` with `'custom'`
plugins: {
custom: {
rules: {
"no-arrow-parameter-types": {

Check failure on line 277 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Replace `"no-arrow-parameter-types"` with `'no-arrow-parameter-types'`
meta: {
fixable: "code",

Check failure on line 279 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Replace `"code"` with `'code'`
hasSuggestions: true,
type: "suggestion",

Check failure on line 281 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Replace `"suggestion"` with `'suggestion'`
dialects: ["typescript"],

Check failure on line 282 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Replace `"typescript"` with `'typescript'`
schema: [
{
type: "object",

Check failure on line 285 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Replace `"object"` with `'object'`
properties: {
allowOptional: {
type: "boolean",

Check failure on line 288 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Replace `"boolean"` with `'boolean'`
default: false,
description:
"Allow type annotations when the parameter is optional. Sometimes useful for overloaded functions.",

Check failure on line 291 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Replace `"Allow·type·annotations·when·the·parameter·is·optional.·Sometimes·useful·for·overloaded·functions."` with `'Allow·type·annotations·when·the·parameter·is·optional.·Sometimes·useful·for·overloaded·functions.'`
},
},
},
],
defaultOptions: [
{
allowOptional: false,
},
],
},
create(context) {
const options = context.options[0] as { allowOptional: boolean };

return {
ArrowFunctionExpression(node) {
const paramsWithTypeAnnotation = node.params.filter(
(
// @ts-expect-error: will be inferred when moved into an official plugin
param,

Check failure on line 310 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Delete `,`
) => param.typeAnnotation !== undefined,

Check failure on line 311 in eslint.config.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Delete `,`
);

const isCatchClause =
node.parent.callee?.property?.name === "catch";

if (paramsWithTypeAnnotation.length > 0 && !isCatchClause) {
for (const param of paramsWithTypeAnnotation) {
if (param.optional && options.allowOptional) {
continue;
}

context.report({
node: param,
message:
"Arrow function parameters should not have type annotations. Instead the Object where the operation is used should be typed correctly.",
fix(fixer) {
if (param.optional) {
return null;
}

if (
node.parent.type === "VariableDeclarator" &&
!node.parent.id.typeAnnotation
) {
const variableDeclarationNode = node.parent;

const isAsyncFunction: boolean = node.async;

const isBodyBlockStatement =
node.body.type === "BlockStatement";

const isBodyJSXElement =
node.body.type === "JSXElement";

const hasReturnType = node.returnType !== undefined;

const lastParam = node.params.at(-1);

const paramIdDifferentLine =
lastParam.loc.start.line !==
variableDeclarationNode.id.loc.end.line;

const paramBlockDifferentLine =
lastParam.loc.end.line !==
node.body.loc.start.line;

const behindClosingParenthesis = hasReturnType
? (node.returnType.range[1] as number)
: (lastParam.range[1] as number) + ")".length;

const fixes = [
// Removes `=> `
fixer.replaceTextRange(
[
behindClosingParenthesis,
node.body.range[0] as number,
],
!hasReturnType &&
paramBlockDifferentLine &&
paramIdDifferentLine
? ")"
: "",
),
// Removes ` = ` or ` = async `
fixer.replaceTextRange(
[
variableDeclarationNode.id.range[1] as number,
(variableDeclarationNode.init
.range[0] as number) +
(isAsyncFunction ? "async ".length : 0),
],
"",
),
// Replaces `const ` with `function ` or `async function `
fixer.replaceTextRange(
[
variableDeclarationNode.parent
.range[0] as number,
variableDeclarationNode.range[0] as number,
],
isAsyncFunction
? "async function "
: "function ",
),
];

// If the body is not a BlockStatement, we need to wrap it in curly braces
if (!isBodyBlockStatement) {
fixes.push(
fixer.insertTextBefore(
node.body,
`{return ${isBodyJSXElement ? "(" : ""}`,
),
fixer.insertTextAfter(
node.body,
`${isBodyJSXElement ? ")" : ""}}`,
),
);

if (isBodyJSXElement) {
fixes.push(
fixer.removeRange([
node.body.range[1] as number,
node.range[1] as number,
]),
);
}
}

return fixes;
}

return fixer.removeRange(
param.typeAnnotation.range as [number, number],
);
},
suggest: [
{
desc: "Remove type annotation",
fix(fixer) {
if (param.optional) {
return fixer.removeRange([
(param.typeAnnotation.range[0] as number) - 1, // Remove the `?` before the type annotation
param.typeAnnotation.range[1] as number,
]);
}

return null;
},
},
],
});
}
}
},
};
},
},
},
},
},
rules: {
"custom/no-arrow-parameter-types": ["error", { allowOptional: true }],
},
},
);
15 changes: 9 additions & 6 deletions src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function db(
});

const query: DBConnection['query'] = async (
queryTextOrConfig: string | QueryConfig | QueryArrayConfig,
queryTextOrConfig,
values?: any[]
): Promise<QueryArrayResult | QueryResult> => {
await createConnection();
Expand Down Expand Up @@ -147,20 +147,23 @@ ${error}
};

const select: DBConnection['select'] = async (
queryTextOrConfig: string | QueryConfig | QueryArrayConfig,
queryTextOrConfig,
values?: any[]
) => {
const { rows } = await query(queryTextOrConfig, values);
return rows;
};

const column: DBConnection['column'] = async (
columnName: string,
queryTextOrConfig: string | QueryConfig | QueryArrayConfig,
columnName,
queryTextOrConfig,
values?: any[]
) => {
const rows = await select(queryTextOrConfig, values);
return rows.map((r: { [key: string]: any }) => r[columnName]);
const rows: Array<{ [key: string]: any }> = await select(
queryTextOrConfig,
values
);
return rows.map((r) => r[columnName]);
};

return {
Expand Down
16 changes: 10 additions & 6 deletions src/migrationBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -825,9 +825,11 @@ export class MigrationBuilder {
// This function wraps each operation within a function that either calls
// the operation or its reverse, and appends the result
// (array of sql statements) to the steps array
const wrap =
<TOperation extends Operation>(operation: TOperation) =>
(...args: Parameters<TOperation>) => {
const wrap: <TOperation extends Operation>(
operation: TOperation
) => (...args: Parameters<TOperation>) => void =
(operation) =>
(...args) => {
if (this._REVERSE_MODE) {
if (typeof operation.reverse !== 'function') {
const name = `pgm.${operation.name}()`;
Expand Down Expand Up @@ -970,9 +972,11 @@ export class MigrationBuilder {

// Expose DB so we can access database within transaction
/* eslint-disable @typescript-eslint/no-explicit-any */
const wrapDB =
<T extends any[], TResult>(operation: (...args: T) => TResult) =>
(...args: T) => {
const wrapDB: <T extends any[], TResult>(
operation: (...args: T) => TResult
) => (...args: T) => TResult =
(operation) =>
(...args) => {
if (this._REVERSE_MODE) {
throw new Error('Impossible to automatically infer down migration');
}
Expand Down
4 changes: 2 additions & 2 deletions src/operations/operators/addToOperatorFamily.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export type AddToOperatorFamilyFn = (

export type AddToOperatorFamily = Reversible<AddToOperatorFamilyFn>;

export const addToOperatorFamily = (
export const addToOperatorFamily: (
mOptions: MigrationOptions
): AddToOperatorFamily => {
) => AddToOperatorFamily = (mOptions) => {
const method: AddToOperatorFamily = (
operatorFamilyName,
indexMethod,
Expand Down
4 changes: 2 additions & 2 deletions src/operations/operators/removeFromOperatorFamily.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export type RemoveFromOperatorFamily = (
operatorList: OperatorListDefinition[]
) => string;

export const removeFromOperatorFamily = (
export const removeFromOperatorFamily: (
mOptions: MigrationOptions
): RemoveFromOperatorFamily => {
) => RemoveFromOperatorFamily = (mOptions) => {
const method: RemoveFromOperatorFamily = (
operatorFamilyName,
indexMethod,
Expand Down
2 changes: 1 addition & 1 deletion src/operations/schemas/createSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type CreateSchemaFn = (
export type CreateSchema = Reversible<CreateSchemaFn>;

export function createSchema(mOptions: MigrationOptions): CreateSchema {
const _create: CreateSchema = (schemaName: string, options = {}) => {
const _create: CreateSchema = (schemaName, options = {}) => {
const { ifNotExists = false, authorization } = options;

const ifNotExistsStr = ifNotExists ? ' IF NOT EXISTS' : '';
Expand Down
4 changes: 2 additions & 2 deletions src/operations/tables/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,10 +439,10 @@ export function parseLike(
like: Name | { table: Name; options?: LikeOptions },
literal: Literal
): string {
const formatOptions = (
const formatOptions: (
name: 'INCLUDING' | 'EXCLUDING',
options?: Like | Like[]
) =>
) => string = (name, options) =>
toArray(options)
.filter((option): option is Like => option !== undefined)
.map((option) => ` ${name} ${option}`)
Expand Down
2 changes: 2 additions & 0 deletions src/operations/triggers/createTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ export type CreateTrigger = Reversible<CreateTriggerFn>;

export function createTrigger(mOptions: MigrationOptions): CreateTrigger {
const _create: CreateTrigger = (
/* eslint-disable custom/no-arrow-parameter-types */
tableName: Name,
triggerName: string,
triggerOptions:
| (TriggerOptions & DropOptions)
| (TriggerOptions & FunctionOptions & DropOptions),
definition?: Value
/* eslint-enable custom/no-arrow-parameter-types */
) => {
const {
constraint = false,
Expand Down
2 changes: 1 addition & 1 deletion test/ts/customRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Options =
| ({ databaseUrl: string } & TestOptions)
| ({ dbClient: Client } & TestOptions);

export const run = async (options: Options): Promise<boolean> => {
export const run: (options: Options) => Promise<boolean> = async (options) => {
const opts: Omit<RunnerOption, 'direction'> & Options = {
migrationsTable: 'migrations',
dir: resolve(import.meta.dirname, 'migrations'),
Expand Down
Loading