untodo 0.0.1-alpha.0

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,122 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+
3
+ const createRule = ESLintUtils.RuleCreator(
4
+ (name) => `https://github.com/ysknsid25/untodo/blob/main/docs/rules/${name}.md`
5
+ );
6
+
7
+ const noTodo = createRule({
8
+ name: "no-todo",
9
+ meta: {
10
+ type: "problem",
11
+ docs: {
12
+ description: "Disallow leftover `TODO()` calls."
13
+ },
14
+ schema: [],
15
+ messages: {
16
+ noTodo: "Unresolved TODO: {{reason}}"
17
+ }
18
+ },
19
+ defaultOptions: [],
20
+ create(context) {
21
+ return {
22
+ CallExpression(node) {
23
+ if (node.callee.type !== "Identifier" || node.callee.name !== "TODO") {
24
+ return;
25
+ }
26
+ const reason = extractReason(node.arguments[0]);
27
+ context.report({
28
+ node,
29
+ messageId: "noTodo",
30
+ data: { reason: reason ?? "(no reason)" }
31
+ });
32
+ }
33
+ };
34
+ }
35
+ });
36
+ function extractReason(arg) {
37
+ if (typeof arg !== "object" || arg === null || arg.type !== "ObjectExpression") {
38
+ return void 0;
39
+ }
40
+ const props = arg.properties;
41
+ for (const prop of props) {
42
+ const p = prop;
43
+ if (p.type !== "Property")
44
+ continue;
45
+ const keyName = p.key?.type === "Identifier" ? p.key.name : p.key?.type === "Literal" ? String(p.key.value) : void 0;
46
+ if (keyName === "reason" && p.value?.type === "Literal") {
47
+ return String(p.value.value);
48
+ }
49
+ }
50
+ return void 0;
51
+ }
52
+
53
+ const noFixme = createRule({
54
+ name: "no-fixme",
55
+ meta: {
56
+ type: "problem",
57
+ docs: {
58
+ description: "Disallow leftover `FIXME()` calls."
59
+ },
60
+ schema: [],
61
+ messages: {
62
+ noFixme: "Unresolved FIXME call."
63
+ }
64
+ },
65
+ defaultOptions: [],
66
+ create(context) {
67
+ return {
68
+ CallExpression(node) {
69
+ if (node.callee.type === "Identifier" && node.callee.name === "FIXME") {
70
+ context.report({ node, messageId: "noFixme" });
71
+ }
72
+ }
73
+ };
74
+ }
75
+ });
76
+
77
+ const noHack = createRule({
78
+ name: "no-hack",
79
+ meta: {
80
+ type: "suggestion",
81
+ docs: {
82
+ description: "Warn on `HACK()` calls."
83
+ },
84
+ schema: [],
85
+ messages: {
86
+ noHack: "HACK call detected \u2014 please justify or remove."
87
+ }
88
+ },
89
+ defaultOptions: [],
90
+ create(context) {
91
+ return {
92
+ CallExpression(node) {
93
+ if (node.callee.type === "Identifier" && node.callee.name === "HACK") {
94
+ context.report({ node, messageId: "noHack" });
95
+ }
96
+ }
97
+ };
98
+ }
99
+ });
100
+
101
+ const rules = {
102
+ "no-todo": noTodo,
103
+ "no-fixme": noFixme,
104
+ "no-hack": noHack
105
+ };
106
+ const configs = {
107
+ recommended: [
108
+ {
109
+ rules: {
110
+ "untodo/no-todo": "warn",
111
+ "untodo/no-fixme": "warn",
112
+ "untodo/no-hack": "warn"
113
+ }
114
+ }
115
+ ]
116
+ };
117
+ const untodoPlugin = {
118
+ rules,
119
+ configs
120
+ };
121
+
122
+ export { untodoPlugin as default };
package/dist/index.cjs ADDED
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ class UntodoError extends Error {
4
+ /**
5
+ * @param message Human-readable error message.
6
+ */
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "UntodoError";
10
+ }
11
+ }
12
+ class NotImplementedError extends UntodoError {
13
+ /**
14
+ * @param message Human-readable error message.
15
+ */
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "NotImplementedError";
19
+ }
20
+ }
21
+ class FixmeError extends UntodoError {
22
+ /**
23
+ * @param message Human-readable error message.
24
+ */
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "FixmeError";
28
+ }
29
+ }
30
+ class HackError extends UntodoError {
31
+ /**
32
+ * @param message Human-readable error message.
33
+ */
34
+ constructor(message) {
35
+ super(message);
36
+ this.name = "HackError";
37
+ }
38
+ }
39
+
40
+ let currentConfig = {};
41
+ function defineConfig(config) {
42
+ currentConfig = config;
43
+ return config;
44
+ }
45
+ function getConfig() {
46
+ return currentConfig;
47
+ }
48
+ function resetConfig() {
49
+ currentConfig = {};
50
+ }
51
+
52
+ function TODO(meta, cb) {
53
+ const handler = cb ?? getConfig().onTodo;
54
+ handler?.(meta);
55
+ return void 0;
56
+ }
57
+ function FIXME(meta, cb) {
58
+ const handler = cb ?? getConfig().onFixme;
59
+ handler?.(meta);
60
+ return void 0;
61
+ }
62
+ function HACK(meta, cb) {
63
+ const handler = cb ?? getConfig().onHack;
64
+ handler?.(meta);
65
+ return void 0;
66
+ }
67
+
68
+ exports.FIXME = FIXME;
69
+ exports.FixmeError = FixmeError;
70
+ exports.HACK = HACK;
71
+ exports.HackError = HackError;
72
+ exports.NotImplementedError = NotImplementedError;
73
+ exports.TODO = TODO;
74
+ exports.UntodoError = UntodoError;
75
+ exports.defineConfig = defineConfig;
76
+ exports.getConfig = getConfig;
77
+ exports.resetConfig = resetConfig;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Base meta passed to {@link TODO}.
3
+ *
4
+ * Users can extend it via Declaration Merging — additional fields are additive,
5
+ * so `reason` is always required:
6
+ *
7
+ * ```ts
8
+ * declare module 'untodo' {
9
+ * interface TodoMeta {
10
+ * issue?: number | string;
11
+ * assignee?: string;
12
+ * }
13
+ * }
14
+ * ```
15
+ */
16
+ interface TodoMeta {
17
+ /** Human-readable reason this code is unimplemented. Required. */
18
+ reason: string;
19
+ }
20
+ /**
21
+ * Base meta passed to {@link FIXME}.
22
+ *
23
+ * Extendable via Declaration Merging in the same way as {@link TodoMeta}.
24
+ */
25
+ interface FixmeMeta {
26
+ /** Human-readable description of what is broken. Required. */
27
+ reason: string;
28
+ }
29
+ /**
30
+ * Base meta passed to {@link HACK}.
31
+ *
32
+ * Extendable via Declaration Merging in the same way as {@link TodoMeta}.
33
+ */
34
+ interface HackMeta {
35
+ /** Human-readable description of the workaround. Required. */
36
+ reason: string;
37
+ }
38
+ /**
39
+ * Union of all meta shapes accepted by the untodo function family.
40
+ */
41
+ type AnyUntodoMeta = TodoMeta | FixmeMeta | HackMeta;
42
+
43
+ /**
44
+ * Base class for all errors thrown by user-supplied untodo callbacks.
45
+ *
46
+ * The library itself never throws; this hierarchy exists so that consumers
47
+ * who *want* to throw inside `onTodo` / `onFixme` / `onHack` can use a
48
+ * consistent type for catch blocks.
49
+ */
50
+ declare class UntodoError extends Error {
51
+ /**
52
+ * @param message Human-readable error message.
53
+ */
54
+ constructor(message: string);
55
+ }
56
+ /**
57
+ * Error intended to be thrown from a {@link TODO} callback when the missing
58
+ * implementation must surface as a runtime failure.
59
+ */
60
+ declare class NotImplementedError extends UntodoError {
61
+ /**
62
+ * @param message Human-readable error message.
63
+ */
64
+ constructor(message: string);
65
+ }
66
+ /**
67
+ * Error intended to be thrown from a {@link FIXME} callback.
68
+ */
69
+ declare class FixmeError extends UntodoError {
70
+ /**
71
+ * @param message Human-readable error message.
72
+ */
73
+ constructor(message: string);
74
+ }
75
+ /**
76
+ * Error intended to be thrown from a {@link HACK} callback.
77
+ */
78
+ declare class HackError extends UntodoError {
79
+ /**
80
+ * @param message Human-readable error message.
81
+ */
82
+ constructor(message: string);
83
+ }
84
+
85
+ /**
86
+ * Project-wide configuration consumed by {@link TODO}, {@link FIXME}
87
+ * and {@link HACK} when no per-call callback is supplied.
88
+ *
89
+ * Set this once from a setup file (e.g. `untodo.config.ts`) using
90
+ * {@link defineConfig}.
91
+ */
92
+ interface UntodoConfig {
93
+ /**
94
+ * Repository in `org/repo` form. Used by tooling to expand the `issue`
95
+ * meta field into a full URL (e.g. `https://github.com/org/repo/issues/123`).
96
+ */
97
+ repo?: string;
98
+ /** Default handler invoked for {@link TODO} calls without a callback. */
99
+ onTodo?: (meta: TodoMeta) => void;
100
+ /** Default handler invoked for {@link FIXME} calls without a callback. */
101
+ onFixme?: (meta: FixmeMeta) => void;
102
+ /** Default handler invoked for {@link HACK} calls without a callback. */
103
+ onHack?: (meta: HackMeta) => void;
104
+ }
105
+ /**
106
+ * Register the project-wide untodo configuration and return it unchanged.
107
+ *
108
+ * Mirrors the `defineConfig` ergonomics used by Vite / Vitest. Per-call
109
+ * callbacks passed to {@link TODO}, {@link FIXME} or {@link HACK} take
110
+ * precedence over the handlers registered here.
111
+ *
112
+ * @param config The configuration object to register.
113
+ * @returns The same `config` object, for chaining or assignment.
114
+ */
115
+ declare function defineConfig(config: UntodoConfig): UntodoConfig;
116
+ /**
117
+ * Read the currently-registered {@link UntodoConfig}.
118
+ *
119
+ * Returns an empty object if {@link defineConfig} has not been called
120
+ * (or after {@link resetConfig}).
121
+ */
122
+ declare function getConfig(): UntodoConfig;
123
+ /**
124
+ * Clear the registered configuration. Primarily intended for tests.
125
+ */
126
+ declare function resetConfig(): void;
127
+
128
+ /**
129
+ * Marks a code path as not yet implemented.
130
+ *
131
+ * Returns `never`, so callers continue to type-check as if the value flowed
132
+ * through. Does **not** throw — runtime behaviour is delegated to the
133
+ * supplied callback (or the global `onTodo` from {@link defineConfig}).
134
+ *
135
+ * Lint enforcement is the responsibility of the `untodo/no-todo` ESLint /
136
+ * oxlint rule shipped from `untodo/eslint`.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * function fetchUser(): User {
141
+ * return TODO({ reason: 'wire up the user repository' });
142
+ * }
143
+ * ```
144
+ *
145
+ * @param meta Structured description of the unimplemented work.
146
+ * @param cb Optional per-call handler; overrides the global `onTodo`.
147
+ * @returns Never — the function returns `undefined` typed as `never`.
148
+ */
149
+ declare function TODO(meta: TodoMeta, cb?: (meta: TodoMeta) => void): never;
150
+ /**
151
+ * Marks code that is known to be broken and must be repaired.
152
+ *
153
+ * Same semantics as {@link TODO}: returns `never`, never throws, and
154
+ * runtime behaviour is delegated to the supplied callback (or the global
155
+ * `onFixme` from {@link defineConfig}).
156
+ *
157
+ * @param meta Structured description of the defect.
158
+ * @param cb Optional per-call handler; overrides the global `onFixme`.
159
+ * @returns Never — the function returns `undefined` typed as `never`.
160
+ */
161
+ declare function FIXME(meta: FixmeMeta, cb?: (meta: FixmeMeta) => void): never;
162
+ /**
163
+ * Marks a deliberate workaround that should be revisited.
164
+ *
165
+ * Same semantics as {@link TODO}: returns `never`, never throws, and
166
+ * runtime behaviour is delegated to the supplied callback (or the global
167
+ * `onHack` from {@link defineConfig}).
168
+ *
169
+ * @param meta Structured description of the workaround.
170
+ * @param cb Optional per-call handler; overrides the global `onHack`.
171
+ * @returns Never — the function returns `undefined` typed as `never`.
172
+ */
173
+ declare function HACK(meta: HackMeta, cb?: (meta: HackMeta) => void): never;
174
+
175
+ export { FIXME, FixmeError, HACK, HackError, NotImplementedError, TODO, UntodoError, defineConfig, getConfig, resetConfig };
176
+ export type { AnyUntodoMeta, FixmeMeta, HackMeta, TodoMeta, UntodoConfig };
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Base meta passed to {@link TODO}.
3
+ *
4
+ * Users can extend it via Declaration Merging — additional fields are additive,
5
+ * so `reason` is always required:
6
+ *
7
+ * ```ts
8
+ * declare module 'untodo' {
9
+ * interface TodoMeta {
10
+ * issue?: number | string;
11
+ * assignee?: string;
12
+ * }
13
+ * }
14
+ * ```
15
+ */
16
+ interface TodoMeta {
17
+ /** Human-readable reason this code is unimplemented. Required. */
18
+ reason: string;
19
+ }
20
+ /**
21
+ * Base meta passed to {@link FIXME}.
22
+ *
23
+ * Extendable via Declaration Merging in the same way as {@link TodoMeta}.
24
+ */
25
+ interface FixmeMeta {
26
+ /** Human-readable description of what is broken. Required. */
27
+ reason: string;
28
+ }
29
+ /**
30
+ * Base meta passed to {@link HACK}.
31
+ *
32
+ * Extendable via Declaration Merging in the same way as {@link TodoMeta}.
33
+ */
34
+ interface HackMeta {
35
+ /** Human-readable description of the workaround. Required. */
36
+ reason: string;
37
+ }
38
+ /**
39
+ * Union of all meta shapes accepted by the untodo function family.
40
+ */
41
+ type AnyUntodoMeta = TodoMeta | FixmeMeta | HackMeta;
42
+
43
+ /**
44
+ * Base class for all errors thrown by user-supplied untodo callbacks.
45
+ *
46
+ * The library itself never throws; this hierarchy exists so that consumers
47
+ * who *want* to throw inside `onTodo` / `onFixme` / `onHack` can use a
48
+ * consistent type for catch blocks.
49
+ */
50
+ declare class UntodoError extends Error {
51
+ /**
52
+ * @param message Human-readable error message.
53
+ */
54
+ constructor(message: string);
55
+ }
56
+ /**
57
+ * Error intended to be thrown from a {@link TODO} callback when the missing
58
+ * implementation must surface as a runtime failure.
59
+ */
60
+ declare class NotImplementedError extends UntodoError {
61
+ /**
62
+ * @param message Human-readable error message.
63
+ */
64
+ constructor(message: string);
65
+ }
66
+ /**
67
+ * Error intended to be thrown from a {@link FIXME} callback.
68
+ */
69
+ declare class FixmeError extends UntodoError {
70
+ /**
71
+ * @param message Human-readable error message.
72
+ */
73
+ constructor(message: string);
74
+ }
75
+ /**
76
+ * Error intended to be thrown from a {@link HACK} callback.
77
+ */
78
+ declare class HackError extends UntodoError {
79
+ /**
80
+ * @param message Human-readable error message.
81
+ */
82
+ constructor(message: string);
83
+ }
84
+
85
+ /**
86
+ * Project-wide configuration consumed by {@link TODO}, {@link FIXME}
87
+ * and {@link HACK} when no per-call callback is supplied.
88
+ *
89
+ * Set this once from a setup file (e.g. `untodo.config.ts`) using
90
+ * {@link defineConfig}.
91
+ */
92
+ interface UntodoConfig {
93
+ /**
94
+ * Repository in `org/repo` form. Used by tooling to expand the `issue`
95
+ * meta field into a full URL (e.g. `https://github.com/org/repo/issues/123`).
96
+ */
97
+ repo?: string;
98
+ /** Default handler invoked for {@link TODO} calls without a callback. */
99
+ onTodo?: (meta: TodoMeta) => void;
100
+ /** Default handler invoked for {@link FIXME} calls without a callback. */
101
+ onFixme?: (meta: FixmeMeta) => void;
102
+ /** Default handler invoked for {@link HACK} calls without a callback. */
103
+ onHack?: (meta: HackMeta) => void;
104
+ }
105
+ /**
106
+ * Register the project-wide untodo configuration and return it unchanged.
107
+ *
108
+ * Mirrors the `defineConfig` ergonomics used by Vite / Vitest. Per-call
109
+ * callbacks passed to {@link TODO}, {@link FIXME} or {@link HACK} take
110
+ * precedence over the handlers registered here.
111
+ *
112
+ * @param config The configuration object to register.
113
+ * @returns The same `config` object, for chaining or assignment.
114
+ */
115
+ declare function defineConfig(config: UntodoConfig): UntodoConfig;
116
+ /**
117
+ * Read the currently-registered {@link UntodoConfig}.
118
+ *
119
+ * Returns an empty object if {@link defineConfig} has not been called
120
+ * (or after {@link resetConfig}).
121
+ */
122
+ declare function getConfig(): UntodoConfig;
123
+ /**
124
+ * Clear the registered configuration. Primarily intended for tests.
125
+ */
126
+ declare function resetConfig(): void;
127
+
128
+ /**
129
+ * Marks a code path as not yet implemented.
130
+ *
131
+ * Returns `never`, so callers continue to type-check as if the value flowed
132
+ * through. Does **not** throw — runtime behaviour is delegated to the
133
+ * supplied callback (or the global `onTodo` from {@link defineConfig}).
134
+ *
135
+ * Lint enforcement is the responsibility of the `untodo/no-todo` ESLint /
136
+ * oxlint rule shipped from `untodo/eslint`.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * function fetchUser(): User {
141
+ * return TODO({ reason: 'wire up the user repository' });
142
+ * }
143
+ * ```
144
+ *
145
+ * @param meta Structured description of the unimplemented work.
146
+ * @param cb Optional per-call handler; overrides the global `onTodo`.
147
+ * @returns Never — the function returns `undefined` typed as `never`.
148
+ */
149
+ declare function TODO(meta: TodoMeta, cb?: (meta: TodoMeta) => void): never;
150
+ /**
151
+ * Marks code that is known to be broken and must be repaired.
152
+ *
153
+ * Same semantics as {@link TODO}: returns `never`, never throws, and
154
+ * runtime behaviour is delegated to the supplied callback (or the global
155
+ * `onFixme` from {@link defineConfig}).
156
+ *
157
+ * @param meta Structured description of the defect.
158
+ * @param cb Optional per-call handler; overrides the global `onFixme`.
159
+ * @returns Never — the function returns `undefined` typed as `never`.
160
+ */
161
+ declare function FIXME(meta: FixmeMeta, cb?: (meta: FixmeMeta) => void): never;
162
+ /**
163
+ * Marks a deliberate workaround that should be revisited.
164
+ *
165
+ * Same semantics as {@link TODO}: returns `never`, never throws, and
166
+ * runtime behaviour is delegated to the supplied callback (or the global
167
+ * `onHack` from {@link defineConfig}).
168
+ *
169
+ * @param meta Structured description of the workaround.
170
+ * @param cb Optional per-call handler; overrides the global `onHack`.
171
+ * @returns Never — the function returns `undefined` typed as `never`.
172
+ */
173
+ declare function HACK(meta: HackMeta, cb?: (meta: HackMeta) => void): never;
174
+
175
+ export { FIXME, FixmeError, HACK, HackError, NotImplementedError, TODO, UntodoError, defineConfig, getConfig, resetConfig };
176
+ export type { AnyUntodoMeta, FixmeMeta, HackMeta, TodoMeta, UntodoConfig };