@timmo001/oxlint-rules 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: release-oxlint-rules
3
+ description: >-
4
+ Create and publish a major, minor, or patch release of
5
+ @timmo001/oxlint-rules. Use when asked to create, cut, prepare, or publish an
6
+ oxlint-rules release, including its version bump and npm and JSR publication.
7
+ ---
8
+
9
+ # Release Oxlint Rules
10
+
11
+ 1. Find a writable checkout whose Git remote is
12
+ `timmo001/oxlint-rules`. Read its `AGENTS.md`, package metadata, release
13
+ workflow, latest tags, and changes since the latest release. Stop if the
14
+ worktree contains unrelated changes or the local branch is behind its
15
+ remote.
16
+ 2. Confirm `package.json` and `jsr.json` have the same current version and that
17
+ its release exists. Resolve the next version from the requested major,
18
+ minor, patch, or exact version. Ask only when the release level is missing.
19
+ Do not add a `v` prefix when the repository's existing tags omit it.
20
+ 3. Update the version in `package.json` and `jsr.json`. Before committing, run
21
+ `mise run check`, `mise run build`, `npm pack --dry-run`, and
22
+ `bunx jsr@0.14.3 publish --dry-run --allow-dirty`. The dirty-tree flag is
23
+ required because the intended version bump is not committed yet.
24
+ 4. Treat a direct request to create or publish the release as authorisation for
25
+ its version commit, push, and GitHub release. Follow the active environment's
26
+ guarded commit and push workflow, committing only the release metadata with
27
+ `Release Oxlint rules <version>`. A request to prepare or plan a release does
28
+ not authorise publication.
29
+ 5. Resolve the target only after the push with `git rev-parse HEAD`. Pass the
30
+ resulting full 40-character SHA as the `--target` value when running
31
+ `gh release create`. Never pass an abbreviated SHA as `target_commitish`.
32
+ 6. Create the release before resolving workflow targets because publication is
33
+ triggered by the `release.published` event. Watch only the release commit's
34
+ exact workflow runs and require both `Publish to npm` and `Publish to JSR` to
35
+ succeed.
36
+ 7. Report the release URL, version commit, validation results, and npm and JSR
37
+ publication conclusions. Do not report the release as complete while either
38
+ publication job is pending or failed.
@@ -0,0 +1,12 @@
1
+ import { eslintCompatPlugin } from "@oxlint/plugins";
2
+
3
+ import { preferEventParameterTypeRule } from "./rules/prefer-event-parameter-type.ts";
4
+
5
+ const timmoPlugin = eslintCompatPlugin({
6
+ meta: { name: "timmo" },
7
+ rules: {
8
+ "prefer-event-parameter-type": preferEventParameterTypeRule,
9
+ },
10
+ });
11
+
12
+ export default timmoPlugin;
@@ -0,0 +1,83 @@
1
+ import { defineRule } from "@oxlint/plugins";
2
+
3
+ import type { ESTree } from "@oxlint/plugins";
4
+
5
+ type FunctionNode = ESTree.Function | ESTree.ArrowFunctionExpression;
6
+ type TypeAssertion = ESTree.TSAsExpression | ESTree.TSTypeAssertion;
7
+
8
+ function nearestEnclosingFunction(node: ESTree.Node): FunctionNode | null {
9
+ let current: ESTree.Node | null = node.parent;
10
+ while (current) {
11
+ if (
12
+ current.type === "FunctionDeclaration" ||
13
+ current.type === "FunctionExpression" ||
14
+ current.type === "ArrowFunctionExpression"
15
+ ) {
16
+ return current;
17
+ }
18
+ current = current.parent;
19
+ }
20
+ return null;
21
+ }
22
+
23
+ function assertedEventParameter(node: TypeAssertion): {
24
+ readonly parameter: string;
25
+ readonly property: "currentTarget" | "target";
26
+ } | null {
27
+ const expression = node.expression;
28
+ if (
29
+ expression.type !== "MemberExpression" ||
30
+ expression.computed ||
31
+ expression.object.type !== "Identifier" ||
32
+ expression.property.type !== "Identifier" ||
33
+ (expression.property.name !== "currentTarget" &&
34
+ expression.property.name !== "target")
35
+ ) {
36
+ return null;
37
+ }
38
+ const parameterName = expression.object.name;
39
+ const property = expression.property.name;
40
+ const owner = nearestEnclosingFunction(node);
41
+ if (
42
+ !owner?.params.some(
43
+ (parameter) =>
44
+ parameter.type === "Identifier" && parameter.name === parameterName,
45
+ )
46
+ ) {
47
+ return null;
48
+ }
49
+ return {
50
+ parameter: parameterName,
51
+ property,
52
+ };
53
+ }
54
+
55
+ /** Prefer expressing an event target type in its handler parameter signature. */
56
+ export const preferEventParameterTypeRule = defineRule({
57
+ meta: {
58
+ type: "suggestion",
59
+ docs: {
60
+ description:
61
+ "Prefer typing event target properties in the handler parameter instead of asserting them at use sites.",
62
+ },
63
+ messages: {
64
+ typeEventParameter:
65
+ "Type `{{parameter}}.{{property}}` in the function signature instead of asserting it at the use site.",
66
+ },
67
+ },
68
+ create(context) {
69
+ const checkAssertion = (node: TypeAssertion) => {
70
+ const eventParameter = assertedEventParameter(node);
71
+ if (!eventParameter) return;
72
+ context.report({
73
+ node,
74
+ messageId: "typeEventParameter",
75
+ data: eventParameter,
76
+ });
77
+ };
78
+ return {
79
+ TSAsExpression: checkAssertion,
80
+ TSTypeAssertion: checkAssertion,
81
+ };
82
+ },
83
+ });