Skip to content

chore: add no-top-level-arrow-syntax rule to Deno Style Guide linter plugin #6558

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 15, 2025
24 changes: 24 additions & 0 deletions _tools/lint_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,30 @@
};
},
},
// https://docs.deno.com/runtime/contributing/style_guide/#top-level-functions-should-not-use-arrow-syntax
"no-top-level-arrow-syntax": {
create(context) {
return {
ArrowFunctionExpression(node) {
if (
node.parent.type === "VariableDeclarator" &&
node.parent.parent.type === "VariableDeclaration" &&
(node.parent.parent.parent.type === "Program" ||
node.parent.parent.parent.type === "ExportNamedDeclaration")
) {
// TODO(iuioiua): add fix

Check warning on line 70 in _tools/lint_plugin.ts

View check run for this annotation

Codecov / codecov/patch

_tools/lint_plugin.ts#L70

Added line #L70 was not covered by tests
context.report({
node,
range: node.range,
message: "Top-level functions should not use arrow syntax",
hint:
"Use function declaration instead of arrow function. E.g. Use `function foo() {}` instead of `const foo = () => {}`.",
});
}
},
};
},
},
// https://docs.deno.com/runtime/contributing/style_guide/#do-not-depend-on-external-code.
"no-external-code": {
create(context) {
Expand Down
41 changes: 41 additions & 0 deletions _tools/lint_plugin_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,47 @@ class MyClass {
);
});

Deno.test("deno-style-guide/no-top-level-arrow-syntax", {
ignore: !Deno.version.deno.startsWith("2"),
}, () => {
// Bad
assertLintPluginDiagnostics(
`
const foo = () => "bar";
export const bar = () => "foo";
`,
[
{
id: "deno-style-guide/no-top-level-arrow-syntax",
range: [13, 24],
fix: [],
message: "Top-level functions should not use arrow syntax",
hint:
"Use function declaration instead of arrow function. E.g. Use `function foo() {}` instead of `const foo = () => {}`.",
},
{
id: "deno-style-guide/no-top-level-arrow-syntax",
range: [45, 56],
fix: [],
message: "Top-level functions should not use arrow syntax",
hint:
"Use function declaration instead of arrow function. E.g. Use `function foo() {}` instead of `const foo = () => {}`.",
},
],
);

// Good
assertLintPluginDiagnostics(
`
function foo() {
const bar = () => "baz";

return "bar";
}`,
[],
);
});

Deno.test("deno-style-guide/no-external-code", {
ignore: !Deno.version.deno.startsWith("2"),
}, () => {
Expand Down
34 changes: 19 additions & 15 deletions assert/equals_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,26 @@ import {
yellow,
} from "@std/internal/styles";

const createHeader = (): string[] => [
"",
"",
` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${
green(
bold("Expected"),
)
}`,
"",
"",
];
function createHeader(): string[] {
return [
"",
"",
` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${
green(
bold("Expected"),
)
}`,
"",
"",
];
}

const added: (s: string) => string = (s: string): string =>
green(bold(stripAnsiCode(s)));
const removed: (s: string) => string = (s: string): string =>
red(bold(stripAnsiCode(s)));
function added(s: string): string {
return green(bold(stripAnsiCode(s)));
}
function removed(s: string): string {
return red(bold(stripAnsiCode(s)));
}

Deno.test({
name: "assertEquals() matches when values are equal",
Expand Down
34 changes: 19 additions & 15 deletions expect/_assert_equals_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,26 @@ import { AssertionError, assertThrows } from "@std/assert";
import { bold, gray, green, red, stripAnsiCode, yellow } from "@std/fmt/colors";
import { assertEquals } from "./_assert_equals.ts";

const createHeader = (): string[] => [
"",
"",
` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${
green(
bold("Expected"),
)
}`,
"",
"",
];
function createHeader(): string[] {
return [
"",
"",
` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${
green(
bold("Expected"),
)
}`,
"",
"",
];
}

const added: (s: string) => string = (s: string): string =>
green(bold(stripAnsiCode(s)));
const removed: (s: string) => string = (s: string): string =>
red(bold(stripAnsiCode(s)));
function added(s: string): string {
return green(bold(stripAnsiCode(s)));
}
function removed(s: string): string {
return red(bold(stripAnsiCode(s)));
}

Deno.test({
name: "assertEquals() matches when values are equal",
Expand Down
4 changes: 2 additions & 2 deletions expect/_extend_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Book {
}
}

const areAuthorsEqual: Tester = (a: unknown, b: unknown) => {
function areAuthorsEqual(a: unknown, b: unknown) {
const isAAuthor = a instanceof Author;
const isBAuthor = b instanceof Author;

Expand All @@ -39,7 +39,7 @@ const areAuthorsEqual: Tester = (a: unknown, b: unknown) => {
} else {
return false;
}
};
}

const areBooksEqual: Tester = function (
this: MatcherContext,
Expand Down
34 changes: 19 additions & 15 deletions expect/_to_equal_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,26 @@ import { bold, gray, green, red, stripAnsiCode, yellow } from "@std/fmt/colors";
import { AssertionError, assertThrows } from "@std/assert";
import { expect } from "./expect.ts";

const createHeader = (): string[] => [
"",
"",
` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${
green(
bold("Expected"),
)
}`,
"",
"",
];
function createHeader(): string[] {
return [
"",
"",
` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${
green(
bold("Expected"),
)
}`,
"",
"",
];
}

const added: (s: string) => string = (s: string): string =>
green(bold(stripAnsiCode(s)));
const removed: (s: string) => string = (s: string): string =>
red(bold(stripAnsiCode(s)));
function added(s: string): string {
return green(bold(stripAnsiCode(s)));
}
function removed(s: string): string {
return red(bold(stripAnsiCode(s)));
}

Deno.test({
name: "expect().toEqual() matches when values are equal",
Expand Down
14 changes: 8 additions & 6 deletions fs/_map_error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@

type ClassOrT<T> = T extends Class<infer U> ? U : T;

const mapper = (Ctor: typeof errors[keyof typeof errors]) => (err: Error) =>
Object.assign(new Ctor(err.message), {
stack: err.stack,
}) as unknown as ClassOrT<typeof Ctor>;
function mapper(Ctor: typeof errors[keyof typeof errors]) {
return (err: Error) =>
Object.assign(new Ctor(err.message), {
stack: err.stack,
}) as unknown as ClassOrT<typeof Ctor>;

Check warning on line 13 in fs/_map_error.ts

View check run for this annotation

Codecov / codecov/patch

fs/_map_error.ts#L10-L13

Added lines #L10 - L13 were not covered by tests
}

const map: Record<string, ReturnType<typeof mapper>> = {
EEXIST: mapper(errors.AlreadyExists),
ENOENT: mapper(errors.NotFound),
EBADF: mapper(errors.BadResource),
};

const isNodeErr = (e: unknown): e is Error & { code: string } => {
function isNodeErr(e: unknown): e is Error & { code: string } {

Check warning on line 22 in fs/_map_error.ts

View check run for this annotation

Codecov / codecov/patch

fs/_map_error.ts#L22

Added line #L22 was not covered by tests
return e instanceof Error && "code" in e;
};
}

Check warning on line 24 in fs/_map_error.ts

View check run for this annotation

Codecov / codecov/patch

fs/_map_error.ts#L24

Added line #L24 was not covered by tests

export function mapError<E>(e: E) {
if (!isNodeErr(e)) return e;
Expand Down
4 changes: 2 additions & 2 deletions ini/stringify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ function isPlainObject(object: unknown): object is object {
return Object.prototype.toString.call(object) === "[object Object]";
}

const sort = ([_a, valA]: [string, unknown], [_b, valB]: [string, unknown]) => {
function sort([_a, valA]: [string, unknown], [_b, valB]: [string, unknown]) {
if (isPlainObject(valA)) return 1;
if (isPlainObject(valB)) return -1;
return 0;
};
}

function defaultReplacer(_key: string, value: unknown, _section?: string) {
return `${value}`;
Expand Down
6 changes: 4 additions & 2 deletions log/base_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ export type { LevelName, LogLevel, LogRecord };
* @returns A string representation of the log record.
*/
export type FormatterFunction = (logRecord: LogRecord) => string;
const DEFAULT_FORMATTER: FormatterFunction = ({ levelName, msg }) =>
`${levelName} ${msg}`;
// deno-lint-ignore deno-style-guide/naming-convention
function DEFAULT_FORMATTER(logRecord: LogRecord): string {
return `${logRecord.levelName} ${logRecord.msg}`;
}

/** Options for {@linkcode BaseHandler}. */
export interface BaseHandlerOptions {
Expand Down
5 changes: 3 additions & 2 deletions log/formatters_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { FakeTime } from "@std/testing/time";
import { jsonFormatter } from "./formatters.ts";
import { LogRecord } from "./logger.ts";

const log = (msg: string, args: unknown[] = []) =>
new LogRecord({
function log(msg: string, args: unknown[] = []) {
return new LogRecord({
msg,
args,
level: 20,
loggerName: "user-logger",
});
}

Deno.test("jsonFormatter() handles messages without arguments", function () {
using _time = new FakeTime(1);
Expand Down
Loading