@mikrojs/eslint-plugin 0.0.7

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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/dist/index.d.ts +37 -0
  4. package/dist/index.js +86 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/rules/no-dot-catch.d.ts +4 -0
  7. package/dist/rules/no-dot-catch.js +24 -0
  8. package/dist/rules/no-dot-catch.js.map +1 -0
  9. package/dist/rules/no-dot-catch.test.d.ts +1 -0
  10. package/dist/rules/no-dot-catch.test.js +32 -0
  11. package/dist/rules/no-dot-catch.test.js.map +1 -0
  12. package/dist/rules/no-eval.d.ts +4 -0
  13. package/dist/rules/no-eval.js +28 -0
  14. package/dist/rules/no-eval.js.map +1 -0
  15. package/dist/rules/no-eval.test.d.ts +1 -0
  16. package/dist/rules/no-eval.test.js +21 -0
  17. package/dist/rules/no-eval.test.js.map +1 -0
  18. package/dist/rules/no-intl.d.ts +4 -0
  19. package/dist/rules/no-intl.js +24 -0
  20. package/dist/rules/no-intl.js.map +1 -0
  21. package/dist/rules/no-intl.test.d.ts +1 -0
  22. package/dist/rules/no-intl.test.js +25 -0
  23. package/dist/rules/no-intl.test.js.map +1 -0
  24. package/dist/rules/no-promise-reject.d.ts +4 -0
  25. package/dist/rules/no-promise-reject.js +46 -0
  26. package/dist/rules/no-promise-reject.js.map +1 -0
  27. package/dist/rules/no-promise-reject.test.d.ts +1 -0
  28. package/dist/rules/no-promise-reject.test.js +36 -0
  29. package/dist/rules/no-promise-reject.test.js.map +1 -0
  30. package/dist/rules/no-sparse-arrays.d.ts +4 -0
  31. package/dist/rules/no-sparse-arrays.js +39 -0
  32. package/dist/rules/no-sparse-arrays.js.map +1 -0
  33. package/dist/rules/no-sparse-arrays.test.d.ts +1 -0
  34. package/dist/rules/no-sparse-arrays.test.js +40 -0
  35. package/dist/rules/no-sparse-arrays.test.js.map +1 -0
  36. package/dist/rules/no-temporal.d.ts +4 -0
  37. package/dist/rules/no-temporal.js +24 -0
  38. package/dist/rules/no-temporal.js.map +1 -0
  39. package/dist/rules/no-temporal.test.d.ts +1 -0
  40. package/dist/rules/no-temporal.test.js +25 -0
  41. package/dist/rules/no-temporal.test.js.map +1 -0
  42. package/dist/rules/no-throw.d.ts +4 -0
  43. package/dist/rules/no-throw.js +24 -0
  44. package/dist/rules/no-throw.js.map +1 -0
  45. package/dist/rules/no-throw.test.d.ts +1 -0
  46. package/dist/rules/no-throw.test.js +25 -0
  47. package/dist/rules/no-throw.test.js.map +1 -0
  48. package/dist/rules/no-try-catch.d.ts +4 -0
  49. package/dist/rules/no-try-catch.js +26 -0
  50. package/dist/rules/no-try-catch.js.map +1 -0
  51. package/dist/rules/no-try-catch.test.d.ts +1 -0
  52. package/dist/rules/no-try-catch.test.js +30 -0
  53. package/dist/rules/no-try-catch.test.js.map +1 -0
  54. package/dist/rules/no-unhandled-result.d.ts +4 -0
  55. package/dist/rules/no-unhandled-result.js +85 -0
  56. package/dist/rules/no-unhandled-result.js.map +1 -0
  57. package/dist/rules/no-unhandled-result.test.d.ts +1 -0
  58. package/dist/rules/no-unhandled-result.test.js +95 -0
  59. package/dist/rules/no-unhandled-result.test.js.map +1 -0
  60. package/package.json +47 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Bjørge Næss
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @mikrojs/eslint-plugin
2
+
3
+ ESLint plugin with rules for Mikro.js projects. Enforces patterns that reduce memory usage and code size on resource-constrained microcontrollers.
4
+
5
+ ## Rules
6
+
7
+ | Rule | Description |
8
+ | --------------------- | -------------------------------------------------------- |
9
+ | `no-dot-catch` | Require `.then(onFulfilled, onRejected)` over `.catch()` |
10
+ | `no-eval` | Disallow `eval()` |
11
+ | `no-intl` | Disallow `Intl` APIs (not available in QuickJS) |
12
+ | `no-promise-reject` | Require Result types instead of rejecting promises |
13
+ | `no-sparse-arrays` | Disallow sparse arrays |
14
+ | `no-throw` | Require Result types instead of throwing exceptions |
15
+ | `no-try-catch` | Require Result types instead of try/catch |
16
+ | `no-unhandled-result` | Require checking `.ok` on Result values |
17
+ | `no-temporal` | Disallow `Temporal` APIs (not available in QuickJS) |
18
+
19
+ ## Usage
20
+
21
+ ```js
22
+ // eslint.config.js
23
+ import mikrojs from '@mikrojs/eslint-plugin'
24
+
25
+ export default [mikrojs.configs.recommended]
26
+ ```
@@ -0,0 +1,37 @@
1
+ declare const plugin: {
2
+ meta: {
3
+ name: string;
4
+ version: string;
5
+ };
6
+ rules: {
7
+ 'no-unhandled-result': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unhandled", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
8
+ name: string;
9
+ };
10
+ 'no-throw': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noThrow", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
11
+ name: string;
12
+ };
13
+ 'no-try-catch': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noTryCatch", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
14
+ name: string;
15
+ };
16
+ 'no-promise-reject': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noPromiseReject" | "noRejectCall", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
17
+ name: string;
18
+ };
19
+ 'no-dot-catch': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noDotCatch", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
20
+ name: string;
21
+ };
22
+ 'no-eval': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noEval" | "noNewFunction", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
23
+ name: string;
24
+ };
25
+ 'no-intl': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noIntl", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
26
+ name: string;
27
+ };
28
+ 'no-temporal': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noTemporal", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
29
+ name: string;
30
+ };
31
+ 'no-sparse-arrays': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noSparse" | "noDeleteElement", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
32
+ name: string;
33
+ };
34
+ };
35
+ configs: Record<string, unknown[]>;
36
+ };
37
+ export default plugin;
package/dist/index.js ADDED
@@ -0,0 +1,86 @@
1
+ import tseslint from 'typescript-eslint';
2
+ import { noDotCatch } from './rules/no-dot-catch.js';
3
+ import { noEval } from './rules/no-eval.js';
4
+ import { noIntl } from './rules/no-intl.js';
5
+ import { noPromiseReject } from './rules/no-promise-reject.js';
6
+ import { noSparseArrays } from './rules/no-sparse-arrays.js';
7
+ import { noTemporal } from './rules/no-temporal.js';
8
+ import { noThrow } from './rules/no-throw.js';
9
+ import { noTryCatch } from './rules/no-try-catch.js';
10
+ import { noUnhandledResult } from './rules/no-unhandled-result.js';
11
+ const plugin = {
12
+ meta: {
13
+ name: '@mikrojs/eslint-plugin',
14
+ version: '0.0.0',
15
+ },
16
+ rules: {
17
+ 'no-unhandled-result': noUnhandledResult,
18
+ 'no-throw': noThrow,
19
+ 'no-try-catch': noTryCatch,
20
+ 'no-promise-reject': noPromiseReject,
21
+ 'no-dot-catch': noDotCatch,
22
+ 'no-eval': noEval,
23
+ 'no-intl': noIntl,
24
+ 'no-temporal': noTemporal,
25
+ 'no-sparse-arrays': noSparseArrays,
26
+ },
27
+ configs: {},
28
+ };
29
+ // Self-referencing: configs reference the plugin object
30
+ Object.assign(plugin.configs, {
31
+ recommended: tseslint.config({
32
+ files: ['**/*.ts', '**/*.tsx'],
33
+ extends: [tseslint.configs.recommended],
34
+ plugins: {
35
+ '@mikrojs': plugin,
36
+ },
37
+ rules: {
38
+ // Unused vars (allow underscore prefix)
39
+ 'no-unused-vars': 'off',
40
+ '@typescript-eslint/no-unused-vars': [
41
+ 'error',
42
+ { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
43
+ ],
44
+ // Type imports: use inline {type X} for mixed, top-level import type for all-type
45
+ '@typescript-eslint/consistent-type-imports': [
46
+ 'error',
47
+ {
48
+ prefer: 'type-imports',
49
+ fixStyle: 'inline-type-imports',
50
+ disallowTypeAnnotations: false,
51
+ },
52
+ ],
53
+ '@typescript-eslint/no-import-type-side-effects': 'error',
54
+ // Relax overly strict rules
55
+ '@typescript-eslint/no-explicit-any': 'off',
56
+ '@typescript-eslint/no-require-imports': 'off',
57
+ '@typescript-eslint/no-unused-expressions': 'off',
58
+ '@typescript-eslint/no-empty-object-type': 'off',
59
+ '@typescript-eslint/ban-ts-comment': 'off',
60
+ },
61
+ }, {
62
+ files: ['**/*.ts', '**/*.tsx'],
63
+ languageOptions: {
64
+ parserOptions: {
65
+ projectService: true,
66
+ },
67
+ },
68
+ rules: {
69
+ // Error handling
70
+ '@mikrojs/no-unhandled-result': 'error',
71
+ '@mikrojs/no-throw': 'error',
72
+ '@mikrojs/no-try-catch': 'error',
73
+ '@mikrojs/no-promise-reject': 'error',
74
+ '@mikrojs/no-dot-catch': 'error',
75
+ // Floating promises (type-aware)
76
+ '@typescript-eslint/no-floating-promises': 'error',
77
+ // Performance & correctness
78
+ '@mikrojs/no-eval': 'error',
79
+ '@mikrojs/no-intl': 'error',
80
+ '@mikrojs/no-temporal': 'error',
81
+ '@mikrojs/no-sparse-arrays': 'warn',
82
+ },
83
+ }),
84
+ });
85
+ export default plugin;
86
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,mBAAmB,CAAA;AAExC,OAAO,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAC,MAAM,EAAC,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAC,MAAM,EAAC,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAA;AAC5D,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAC,UAAU,EAAC,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAC,iBAAiB,EAAC,MAAM,gCAAgC,CAAA;AAEhE,MAAM,MAAM,GAAG;IACb,IAAI,EAAE;QACJ,IAAI,EAAE,wBAAwB;QAC9B,OAAO,EAAE,OAAO;KACjB;IACD,KAAK,EAAE;QACL,qBAAqB,EAAE,iBAAiB;QACxC,UAAU,EAAE,OAAO;QACnB,cAAc,EAAE,UAAU;QAC1B,mBAAmB,EAAE,eAAe;QACpC,cAAc,EAAE,UAAU;QAC1B,SAAS,EAAE,MAAM;QACjB,SAAS,EAAE,MAAM;QACjB,aAAa,EAAE,UAAU;QACzB,kBAAkB,EAAE,cAAc;KACnC;IACD,OAAO,EAAE,EAA+B;CACzC,CAAA;AAED,wDAAwD;AACxD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;IAC5B,WAAW,EAAE,QAAQ,CAAC,MAAM,CAC1B;QACE,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;QAC9B,OAAO,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC;QACvC,OAAO,EAAE;YACP,UAAU,EAAE,MAAM;SACnB;QACD,KAAK,EAAE;YACL,wCAAwC;YACxC,gBAAgB,EAAE,KAAK;YACvB,mCAAmC,EAAE;gBACnC,OAAO;gBACP,EAAC,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAC;aACnD;YACD,kFAAkF;YAClF,4CAA4C,EAAE;gBAC5C,OAAO;gBACP;oBACE,MAAM,EAAE,cAAc;oBACtB,QAAQ,EAAE,qBAAqB;oBAC/B,uBAAuB,EAAE,KAAK;iBAC/B;aACF;YACD,gDAAgD,EAAE,OAAO;YACzD,4BAA4B;YAC5B,oCAAoC,EAAE,KAAK;YAC3C,uCAAuC,EAAE,KAAK;YAC9C,0CAA0C,EAAE,KAAK;YACjD,yCAAyC,EAAE,KAAK;YAChD,mCAAmC,EAAE,KAAK;SAC3C;KACF,EACD;QACE,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;QAC9B,eAAe,EAAE;YACf,aAAa,EAAE;gBACb,cAAc,EAAE,IAAI;aACrB;SACF;QACD,KAAK,EAAE;YACL,iBAAiB;YACjB,8BAA8B,EAAE,OAAO;YACvC,mBAAmB,EAAE,OAAO;YAC5B,uBAAuB,EAAE,OAAO;YAChC,4BAA4B,EAAE,OAAO;YACrC,uBAAuB,EAAE,OAAO;YAChC,iCAAiC;YACjC,yCAAyC,EAAE,OAAO;YAClD,4BAA4B;YAC5B,kBAAkB,EAAE,OAAO;YAC3B,kBAAkB,EAAE,OAAO;YAC3B,sBAAsB,EAAE,OAAO;YAC/B,2BAA2B,EAAE,MAAM;SACpC;KACF,CACF;CACF,CAAC,CAAA;AAEF,eAAe,MAAM,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noDotCatch: ESLintUtils.RuleModule<"noDotCatch", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,24 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ export const noDotCatch = createRule({
4
+ name: 'no-dot-catch',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow .catch() on promises. Use Result types for async error handling.',
9
+ },
10
+ messages: {
11
+ noDotCatch: 'Do not use .catch(). Async errors should be returned as Result types.',
12
+ },
13
+ schema: [],
14
+ },
15
+ defaultOptions: [],
16
+ create(context) {
17
+ return {
18
+ 'CallExpression > MemberExpression[property.name="catch"]'(node) {
19
+ context.report({ node: node.parent, messageId: 'noDotCatch' });
20
+ },
21
+ };
22
+ },
23
+ });
24
+ //# sourceMappingURL=no-dot-catch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-dot-catch.js","sourceRoot":"","sources":["../../src/rules/no-dot-catch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAgB,MAAM,0BAA0B,CAAA;AAEnE,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,2DAA2D,IAAI,KAAK,CAC/E,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC;IACnC,IAAI,EAAE,cAAc;IACpB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,2EAA2E;SACzF;QACD,QAAQ,EAAE;YACR,UAAU,EAAE,uEAAuE;SACpF;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,0DAA0D,CAAC,IAA+B;gBACxF,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,IAAI,CAAC,MAAiC,EAAE,SAAS,EAAE,YAAY,EAAC,CAAC,CAAA;YACzF,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noDotCatch } from './no-dot-catch.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester();
8
+ ruleTester.run('no-dot-catch', noDotCatch, {
9
+ valid: [
10
+ // .then() is fine
11
+ 'promise.then(v => console.log(v))',
12
+ // .finally() is fine
13
+ 'promise.finally(() => cleanup())',
14
+ // Property named catch (not a call)
15
+ 'const c = obj.catch',
16
+ ],
17
+ invalid: [
18
+ {
19
+ code: 'promise.catch(e => console.error(e))',
20
+ errors: [{ messageId: 'noDotCatch' }],
21
+ },
22
+ {
23
+ code: 'fetch("url").catch(handleError)',
24
+ errors: [{ messageId: 'noDotCatch' }],
25
+ },
26
+ {
27
+ code: 'promise.then(ok).catch(fail)',
28
+ errors: [{ messageId: 'noDotCatch' }],
29
+ },
30
+ ],
31
+ });
32
+ //# sourceMappingURL=no-dot-catch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-dot-catch.test.js","sourceRoot":"","sources":["../../src/rules/no-dot-catch.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAA;AAE5C,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAA;AAEnC,UAAU,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,EAAE;IACzC,KAAK,EAAE;QACL,kBAAkB;QAClB,mCAAmC;QACnC,qBAAqB;QACrB,kCAAkC;QAClC,oCAAoC;QACpC,qBAAqB;KACtB;IACD,OAAO,EAAE;QACP;YACE,IAAI,EAAE,sCAAsC;YAC5C,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;QACD;YACE,IAAI,EAAE,iCAAiC;YACvC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;QACD;YACE,IAAI,EAAE,8BAA8B;YACpC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noEval: ESLintUtils.RuleModule<"noEval" | "noNewFunction", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,28 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ export const noEval = createRule({
4
+ name: 'no-eval',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow eval() and new Function(). Runtime compilation is expensive on constrained devices.',
9
+ },
10
+ messages: {
11
+ noEval: 'Do not use eval(). Runtime compilation is expensive on constrained devices — use pre-compiled modules instead.',
12
+ noNewFunction: 'Do not use new Function(). Runtime compilation is expensive on constrained devices — use pre-compiled modules instead.',
13
+ },
14
+ schema: [],
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ return {
19
+ 'CallExpression[callee.name="eval"]'(node) {
20
+ context.report({ node, messageId: 'noEval' });
21
+ },
22
+ 'NewExpression[callee.name="Function"]'(node) {
23
+ context.report({ node, messageId: 'noNewFunction' });
24
+ },
25
+ };
26
+ },
27
+ });
28
+ //# sourceMappingURL=no-eval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-eval.js","sourceRoot":"","sources":["../../src/rules/no-eval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAgB,MAAM,0BAA0B,CAAA;AAEnE,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,2DAA2D,IAAI,KAAK,CAC/E,CAAA;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAC;IAC/B,IAAI,EAAE,SAAS;IACf,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,8FAA8F;SACjG;QACD,QAAQ,EAAE;YACR,MAAM,EACJ,gHAAgH;YAClH,aAAa,EACX,wHAAwH;SAC3H;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,oCAAoC,CAAC,IAA6B;gBAChE,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAC,CAAC,CAAA;YAC7C,CAAC;YACD,uCAAuC,CAAC,IAA4B;gBAClE,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,eAAe,EAAC,CAAC,CAAA;YACpD,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noEval } from './no-eval.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester();
8
+ ruleTester.run('no-eval', noEval, {
9
+ valid: ['JSON.parse("{}") ', 'const fn = () => 42', 'function myEval(x: string) { return x }'],
10
+ invalid: [
11
+ {
12
+ code: 'eval("1 + 2")',
13
+ errors: [{ messageId: 'noEval' }],
14
+ },
15
+ {
16
+ code: 'const fn = new Function("a", "return a + 1")',
17
+ errors: [{ messageId: 'noNewFunction' }],
18
+ },
19
+ ],
20
+ });
21
+ //# sourceMappingURL=no-eval.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-eval.test.js","sourceRoot":"","sources":["../../src/rules/no-eval.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAA;AAEnC,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAA;AAEnC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE;IAChC,KAAK,EAAE,CAAC,mBAAmB,EAAE,qBAAqB,EAAE,yCAAyC,CAAC;IAC9F,OAAO,EAAE;QACP;YACE,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAC,CAAC;SAChC;QACD;YACE,IAAI,EAAE,8CAA8C;YACpD,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,eAAe,EAAC,CAAC;SACvC;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noIntl: ESLintUtils.RuleModule<"noIntl", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,24 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ export const noIntl = createRule({
4
+ name: 'no-intl',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow use of the Intl API. QuickJS-NG does not support Intl and will throw at runtime.',
9
+ },
10
+ messages: {
11
+ noIntl: 'The Intl API is not available in QuickJS-NG. Use manual formatting or a lightweight library instead.',
12
+ },
13
+ schema: [],
14
+ },
15
+ defaultOptions: [],
16
+ create(context) {
17
+ return {
18
+ 'MemberExpression[object.name="Intl"]'(node) {
19
+ context.report({ node, messageId: 'noIntl' });
20
+ },
21
+ };
22
+ },
23
+ });
24
+ //# sourceMappingURL=no-intl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-intl.js","sourceRoot":"","sources":["../../src/rules/no-intl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAgB,MAAM,0BAA0B,CAAA;AAEnE,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,2DAA2D,IAAI,KAAK,CAC/E,CAAA;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAC;IAC/B,IAAI,EAAE,SAAS;IACf,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,2FAA2F;SAC9F;QACD,QAAQ,EAAE;YACR,MAAM,EACJ,sGAAsG;SACzG;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,sCAAsC,CAAC,IAA+B;gBACpE,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAC,CAAC,CAAA;YAC7C,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noIntl } from './no-intl.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester();
8
+ ruleTester.run('no-intl', noIntl, {
9
+ valid: ['new Date().toISOString()', 'const s = String(42)', 'number.toFixed(2)'],
10
+ invalid: [
11
+ {
12
+ code: 'new Intl.DateTimeFormat("en-US")',
13
+ errors: [{ messageId: 'noIntl' }],
14
+ },
15
+ {
16
+ code: 'new Intl.NumberFormat("de-DE")',
17
+ errors: [{ messageId: 'noIntl' }],
18
+ },
19
+ {
20
+ code: 'Intl.Collator("en")',
21
+ errors: [{ messageId: 'noIntl' }],
22
+ },
23
+ ],
24
+ });
25
+ //# sourceMappingURL=no-intl.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-intl.test.js","sourceRoot":"","sources":["../../src/rules/no-intl.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAA;AAEnC,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAA;AAEnC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE;IAChC,KAAK,EAAE,CAAC,0BAA0B,EAAE,sBAAsB,EAAE,mBAAmB,CAAC;IAChF,OAAO,EAAE;QACP;YACE,IAAI,EAAE,kCAAkC;YACxC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAC,CAAC;SAChC;QACD;YACE,IAAI,EAAE,gCAAgC;YACtC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAC,CAAC;SAChC;QACD;YACE,IAAI,EAAE,qBAAqB;YAC3B,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAC,CAAC;SAChC;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noPromiseReject: ESLintUtils.RuleModule<"noPromiseReject" | "noRejectCall", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,46 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ export const noPromiseReject = createRule({
4
+ name: 'no-promise-reject',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow Promise.reject() and reject() calls. Use Result types for async errors.',
9
+ },
10
+ messages: {
11
+ noPromiseReject: 'Do not use Promise.reject(). Return err() from mikrojs/result instead.',
12
+ noRejectCall: 'Do not call reject(). Return err() from mikrojs/result instead.',
13
+ },
14
+ schema: [],
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ const rejectParams = new Set();
19
+ return {
20
+ // Track reject parameter names from new Promise((resolve, reject) => ...)
21
+ 'NewExpression[callee.name="Promise"] > ArrowFunctionExpression'(node) {
22
+ const param = node.params[1];
23
+ if (param?.type === 'Identifier') {
24
+ rejectParams.add(param.name);
25
+ }
26
+ },
27
+ 'NewExpression[callee.name="Promise"] > FunctionExpression'(node) {
28
+ const param = node.params[1];
29
+ if (param?.type === 'Identifier') {
30
+ rejectParams.add(param.name);
31
+ }
32
+ },
33
+ // Flag Promise.reject(...)
34
+ 'CallExpression > MemberExpression[object.name="Promise"][property.name="reject"]'(node) {
35
+ context.report({ node: node.parent, messageId: 'noPromiseReject' });
36
+ },
37
+ // Flag reject(...) calls where reject is a Promise constructor parameter
38
+ 'CallExpression[callee.type="Identifier"]'(node) {
39
+ if (node.callee.type === 'Identifier' && rejectParams.has(node.callee.name)) {
40
+ context.report({ node, messageId: 'noRejectCall' });
41
+ }
42
+ },
43
+ };
44
+ },
45
+ });
46
+ //# sourceMappingURL=no-promise-reject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-promise-reject.js","sourceRoot":"","sources":["../../src/rules/no-promise-reject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAgB,MAAM,0BAA0B,CAAA;AAEnE,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,2DAA2D,IAAI,KAAK,CAC/E,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;IACxC,IAAI,EAAE,mBAAmB;IACzB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,kFAAkF;SACrF;QACD,QAAQ,EAAE;YACR,eAAe,EAAE,wEAAwE;YACzF,YAAY,EAAE,iEAAiE;SAChF;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAA;QAEtC,OAAO;YACL,0EAA0E;YAC1E,gEAAgE,CAC9D,IAAsC;gBAEtC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;gBAC5B,IAAI,KAAK,EAAE,IAAI,KAAK,YAAY,EAAE,CAAC;oBACjC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBAC9B,CAAC;YACH,CAAC;YACD,2DAA2D,CACzD,IAAiC;gBAEjC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;gBAC5B,IAAI,KAAK,EAAE,IAAI,KAAK,YAAY,EAAE,CAAC;oBACjC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBAC9B,CAAC;YACH,CAAC;YAED,2BAA2B;YAC3B,kFAAkF,CAChF,IAA+B;gBAE/B,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,IAAI,CAAC,MAAiC,EAAE,SAAS,EAAE,iBAAiB,EAAC,CAAC,CAAA;YAC9F,CAAC;YAED,yEAAyE;YACzE,0CAA0C,CAAC,IAA6B;gBACtE,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC5E,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,cAAc,EAAC,CAAC,CAAA;gBACnD,CAAC;YACH,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noPromiseReject } from './no-promise-reject.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester();
8
+ ruleTester.run('no-promise-reject', noPromiseReject, {
9
+ valid: [
10
+ // Promise.resolve is fine
11
+ 'Promise.resolve(42)',
12
+ // new Promise with only resolve
13
+ 'new Promise((resolve) => resolve(42))',
14
+ // Regular function named reject (not a Promise callback)
15
+ 'function reject(x: string) { return x }; reject("hi")',
16
+ ],
17
+ invalid: [
18
+ {
19
+ code: 'Promise.reject(new Error("fail"))',
20
+ errors: [{ messageId: 'noPromiseReject' }],
21
+ },
22
+ {
23
+ code: 'Promise.reject("fail")',
24
+ errors: [{ messageId: 'noPromiseReject' }],
25
+ },
26
+ {
27
+ code: 'new Promise((resolve, reject) => { reject(new Error("fail")) })',
28
+ errors: [{ messageId: 'noRejectCall' }],
29
+ },
30
+ {
31
+ code: 'new Promise(function(resolve, reject) { reject("fail") })',
32
+ errors: [{ messageId: 'noRejectCall' }],
33
+ },
34
+ ],
35
+ });
36
+ //# sourceMappingURL=no-promise-reject.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-promise-reject.test.js","sourceRoot":"","sources":["../../src/rules/no-promise-reject.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,eAAe,EAAC,MAAM,wBAAwB,CAAA;AAEtD,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAA;AAEnC,UAAU,CAAC,GAAG,CAAC,mBAAmB,EAAE,eAAe,EAAE;IACnD,KAAK,EAAE;QACL,0BAA0B;QAC1B,qBAAqB;QACrB,gCAAgC;QAChC,uCAAuC;QACvC,yDAAyD;QACzD,uDAAuD;KACxD;IACD,OAAO,EAAE;QACP;YACE,IAAI,EAAE,mCAAmC;YACzC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,iBAAiB,EAAC,CAAC;SACzC;QACD;YACE,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,iBAAiB,EAAC,CAAC;SACzC;QACD;YACE,IAAI,EAAE,iEAAiE;YACvE,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,cAAc,EAAC,CAAC;SACtC;QACD;YACE,IAAI,EAAE,2DAA2D;YACjE,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,cAAc,EAAC,CAAC;SACtC;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noSparseArrays: ESLintUtils.RuleModule<"noSparse" | "noDeleteElement", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,39 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ export const noSparseArrays = createRule({
4
+ name: 'no-sparse-arrays',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow sparse arrays. QuickJS optimizes dense arrays — holes cause a fallback to a slower object-based representation.',
9
+ },
10
+ messages: {
11
+ noSparse: 'Avoid sparse arrays (holes). QuickJS optimizes dense arrays — use explicit undefined values or filter instead.',
12
+ noDeleteElement: 'Avoid delete on array elements — it creates holes. Use Array.prototype.splice() or filter instead.',
13
+ },
14
+ schema: [],
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ return {
19
+ // Catch [1,,3] — array literals with elisions
20
+ ArrayExpression(node) {
21
+ for (const element of node.elements) {
22
+ if (element === null) {
23
+ context.report({ node, messageId: 'noSparse' });
24
+ return;
25
+ }
26
+ }
27
+ },
28
+ // Catch delete arr[i]
29
+ UnaryExpression(node) {
30
+ if (node.operator === 'delete' &&
31
+ node.argument.type === 'MemberExpression' &&
32
+ node.argument.computed) {
33
+ context.report({ node, messageId: 'noDeleteElement' });
34
+ }
35
+ },
36
+ };
37
+ },
38
+ });
39
+ //# sourceMappingURL=no-sparse-arrays.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-sparse-arrays.js","sourceRoot":"","sources":["../../src/rules/no-sparse-arrays.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAgB,MAAM,0BAA0B,CAAA;AAEnE,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,2DAA2D,IAAI,KAAK,CAC/E,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;IACvC,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,0HAA0H;SAC7H;QACD,QAAQ,EAAE;YACR,QAAQ,EACN,gHAAgH;YAClH,eAAe,EACb,oGAAoG;SACvG;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,8CAA8C;YAC9C,eAAe,CAAC,IAA8B;gBAC5C,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACpC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;wBACrB,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAC,CAAC,CAAA;wBAC7C,OAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;YACD,sBAAsB;YACtB,eAAe,CAAC,IAA8B;gBAC5C,IACE,IAAI,CAAC,QAAQ,KAAK,QAAQ;oBAC1B,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,kBAAkB;oBACzC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EACtB,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,iBAAiB,EAAC,CAAC,CAAA;gBACtD,CAAC;YACH,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noSparseArrays } from './no-sparse-arrays.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester();
8
+ ruleTester.run('no-sparse-arrays', noSparseArrays, {
9
+ valid: [
10
+ // Dense array is fine
11
+ 'const arr = [1, 2, 3]',
12
+ // Explicit undefined is fine
13
+ 'const arr = [1, undefined, 3]',
14
+ // Empty array is fine
15
+ 'const arr: number[] = []',
16
+ // Delete on object property is fine
17
+ 'delete obj.prop',
18
+ // Splice is fine
19
+ 'arr.splice(1, 1)',
20
+ ],
21
+ invalid: [
22
+ {
23
+ code: 'const arr = [1,,3]',
24
+ errors: [{ messageId: 'noSparse' }],
25
+ },
26
+ {
27
+ code: 'const arr = [,,]',
28
+ errors: [{ messageId: 'noSparse' }],
29
+ },
30
+ {
31
+ code: 'delete arr[0]',
32
+ errors: [{ messageId: 'noDeleteElement' }],
33
+ },
34
+ {
35
+ code: 'delete arr[i]',
36
+ errors: [{ messageId: 'noDeleteElement' }],
37
+ },
38
+ ],
39
+ });
40
+ //# sourceMappingURL=no-sparse-arrays.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-sparse-arrays.test.js","sourceRoot":"","sources":["../../src/rules/no-sparse-arrays.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAA;AAEpD,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAA;AAEnC,UAAU,CAAC,GAAG,CAAC,kBAAkB,EAAE,cAAc,EAAE;IACjD,KAAK,EAAE;QACL,sBAAsB;QACtB,uBAAuB;QACvB,6BAA6B;QAC7B,+BAA+B;QAC/B,sBAAsB;QACtB,0BAA0B;QAC1B,oCAAoC;QACpC,iBAAiB;QACjB,iBAAiB;QACjB,kBAAkB;KACnB;IACD,OAAO,EAAE;QACP;YACE,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,UAAU,EAAC,CAAC;SAClC;QACD;YACE,IAAI,EAAE,kBAAkB;YACxB,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,UAAU,EAAC,CAAC;SAClC;QACD;YACE,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,iBAAiB,EAAC,CAAC;SACzC;QACD;YACE,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,iBAAiB,EAAC,CAAC;SACzC;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noTemporal: ESLintUtils.RuleModule<"noTemporal", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,24 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ export const noTemporal = createRule({
4
+ name: 'no-temporal',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow use of the Temporal API. QuickJS-NG does not support Temporal and will throw at runtime.',
9
+ },
10
+ messages: {
11
+ noTemporal: 'The Temporal API is not available in QuickJS-NG. Use Date or a lightweight library instead.',
12
+ },
13
+ schema: [],
14
+ },
15
+ defaultOptions: [],
16
+ create(context) {
17
+ return {
18
+ 'MemberExpression[object.name="Temporal"]'(node) {
19
+ context.report({ node, messageId: 'noTemporal' });
20
+ },
21
+ };
22
+ },
23
+ });
24
+ //# sourceMappingURL=no-temporal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-temporal.js","sourceRoot":"","sources":["../../src/rules/no-temporal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAgB,MAAM,0BAA0B,CAAA;AAEnE,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,2DAA2D,IAAI,KAAK,CAC/E,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC;IACnC,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,mGAAmG;SACtG;QACD,QAAQ,EAAE;YACR,UAAU,EACR,6FAA6F;SAChG;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,0CAA0C,CAAC,IAA+B;gBACxE,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAC,CAAC,CAAA;YACjD,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noTemporal } from './no-temporal.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester();
8
+ ruleTester.run('no-temporal', noTemporal, {
9
+ valid: ['new Date()', 'Date.now()', 'const d = new Date().toISOString()'],
10
+ invalid: [
11
+ {
12
+ code: 'Temporal.Now.instant()',
13
+ errors: [{ messageId: 'noTemporal' }],
14
+ },
15
+ {
16
+ code: 'Temporal.PlainDate.from("2025-01-01")',
17
+ errors: [{ messageId: 'noTemporal' }],
18
+ },
19
+ {
20
+ code: 'Temporal.Duration.from({hours: 1})',
21
+ errors: [{ messageId: 'noTemporal' }],
22
+ },
23
+ ],
24
+ });
25
+ //# sourceMappingURL=no-temporal.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-temporal.test.js","sourceRoot":"","sources":["../../src/rules/no-temporal.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAE3C,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAA;AAEnC,UAAU,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,EAAE;IACxC,KAAK,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,oCAAoC,CAAC;IACzE,OAAO,EAAE;QACP;YACE,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;QACD;YACE,IAAI,EAAE,uCAAuC;YAC7C,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;QACD;YACE,IAAI,EAAE,oCAAoC;YAC1C,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noThrow: ESLintUtils.RuleModule<"noThrow", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,24 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ export const noThrow = createRule({
4
+ name: 'no-throw',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow throw statements. Use Result types for expected errors and panic() for unrecoverable situations.',
9
+ },
10
+ messages: {
11
+ noThrow: 'Do not use throw. Use Result types for expected errors, or panic() from mikrojs/sys for unrecoverable situations.',
12
+ },
13
+ schema: [],
14
+ },
15
+ defaultOptions: [],
16
+ create(context) {
17
+ return {
18
+ ThrowStatement(node) {
19
+ context.report({ node, messageId: 'noThrow' });
20
+ },
21
+ };
22
+ },
23
+ });
24
+ //# sourceMappingURL=no-throw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-throw.js","sourceRoot":"","sources":["../../src/rules/no-throw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAA;AAEpD,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,2DAA2D,IAAI,KAAK,CAC/E,CAAA;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAAC;IAChC,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,2GAA2G;SAC9G;QACD,QAAQ,EAAE;YACR,OAAO,EACL,mHAAmH;SACtH;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,cAAc,CAAC,IAAI;gBACjB,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAC,CAAC,CAAA;YAC9C,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noThrow } from './no-throw.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester();
8
+ ruleTester.run('no-throw', noThrow, {
9
+ valid: ['console.error("something failed")', 'const err = new Error("test")'],
10
+ invalid: [
11
+ {
12
+ code: 'throw new Error("fail")',
13
+ errors: [{ messageId: 'noThrow' }],
14
+ },
15
+ {
16
+ code: 'throw "fail"',
17
+ errors: [{ messageId: 'noThrow' }],
18
+ },
19
+ {
20
+ code: 'function f() { throw new Error("fail") }',
21
+ errors: [{ messageId: 'noThrow' }],
22
+ },
23
+ ],
24
+ });
25
+ //# sourceMappingURL=no-throw.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-throw.test.js","sourceRoot":"","sources":["../../src/rules/no-throw.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,OAAO,EAAC,MAAM,eAAe,CAAA;AAErC,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAA;AAEnC,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE;IAClC,KAAK,EAAE,CAAC,mCAAmC,EAAE,+BAA+B,CAAC;IAC7E,OAAO,EAAE;QACP;YACE,IAAI,EAAE,yBAAyB;YAC/B,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,SAAS,EAAC,CAAC;SACjC;QACD;YACE,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,SAAS,EAAC,CAAC;SACjC;QACD;YACE,IAAI,EAAE,0CAA0C;YAChD,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,SAAS,EAAC,CAAC;SACjC;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noTryCatch: ESLintUtils.RuleModule<"noTryCatch", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,26 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ export const noTryCatch = createRule({
4
+ name: 'no-try-catch',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Disallow try/catch blocks. try/finally for cleanup is allowed.',
9
+ },
10
+ messages: {
11
+ noTryCatch: 'Do not use try/catch. Use Result types for error handling. try/finally for cleanup is allowed.',
12
+ },
13
+ schema: [],
14
+ },
15
+ defaultOptions: [],
16
+ create(context) {
17
+ return {
18
+ TryStatement(node) {
19
+ if (node.handler) {
20
+ context.report({ node: node.handler, messageId: 'noTryCatch' });
21
+ }
22
+ },
23
+ };
24
+ },
25
+ });
26
+ //# sourceMappingURL=no-try-catch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-try-catch.js","sourceRoot":"","sources":["../../src/rules/no-try-catch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAgB,MAAM,0BAA0B,CAAA;AAEnE,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,2DAA2D,IAAI,KAAK,CAC/E,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC;IACnC,IAAI,EAAE,cAAc;IACpB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,gEAAgE;SAC9E;QACD,QAAQ,EAAE;YACR,UAAU,EACR,gGAAgG;SACnG;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,YAAY,CAAC,IAA2B;gBACtC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACjB,OAAO,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAC,CAAC,CAAA;gBAC/D,CAAC;YACH,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noTryCatch } from './no-try-catch.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester();
8
+ ruleTester.run('no-try-catch', noTryCatch, {
9
+ valid: [
10
+ // try/finally is allowed (cleanup)
11
+ 'try { doStuff() } finally { cleanup() }',
12
+ // No try at all
13
+ 'doStuff()',
14
+ ],
15
+ invalid: [
16
+ {
17
+ code: 'try { doStuff() } catch (e) { console.error(e) }',
18
+ errors: [{ messageId: 'noTryCatch' }],
19
+ },
20
+ {
21
+ code: 'try { doStuff() } catch { console.error("failed") }',
22
+ errors: [{ messageId: 'noTryCatch' }],
23
+ },
24
+ {
25
+ code: 'try { doStuff() } catch (e) { handle(e) } finally { cleanup() }',
26
+ errors: [{ messageId: 'noTryCatch' }],
27
+ },
28
+ ],
29
+ });
30
+ //# sourceMappingURL=no-try-catch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-try-catch.test.js","sourceRoot":"","sources":["../../src/rules/no-try-catch.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAA;AAE5C,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAA;AAEnC,UAAU,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,EAAE;IACzC,KAAK,EAAE;QACL,mCAAmC;QACnC,yCAAyC;QACzC,gBAAgB;QAChB,WAAW;KACZ;IACD,OAAO,EAAE;QACP;YACE,IAAI,EAAE,kDAAkD;YACxD,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;QACD;YACE,IAAI,EAAE,qDAAqD;YAC3D,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;QACD;YACE,IAAI,EAAE,iEAAiE;YACvE,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,YAAY,EAAC,CAAC;SACpC;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const noUnhandledResult: ESLintUtils.RuleModule<"unhandled", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
@@ -0,0 +1,85 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/mikrojs/mikrojs/blob/main/docs/rules/${name}.md`);
3
+ function hasOkBooleanLiteral(type, checker) {
4
+ const okProp = type.getProperty('ok');
5
+ if (!okProp)
6
+ return false;
7
+ const decl = okProp.valueDeclaration ?? okProp.declarations?.[0];
8
+ if (!decl)
9
+ return false;
10
+ const okType = checker.getTypeOfSymbolAtLocation(okProp, decl);
11
+ const intrinsicName = okType.intrinsicName;
12
+ return intrinsicName === 'true' || intrinsicName === 'false';
13
+ }
14
+ function containsResultLikeType(type, checker) {
15
+ // Check if the type (or any member of a union) has an {ok: true} or {ok: false}
16
+ // property. This catches:
17
+ // - Result<T, E> → {ok: true} | {ok: false}
18
+ // - ErrResult<E> | void → {ok: false} | void — error still needs handling
19
+ // - Result<T, E> | undefined → same principle
20
+ const members = type.types;
21
+ if (members) {
22
+ return members.some((m) => hasOkBooleanLiteral(m, checker));
23
+ }
24
+ return hasOkBooleanLiteral(type, checker);
25
+ }
26
+ function unwrapPromise(type) {
27
+ const symbol = type.getSymbol();
28
+ if (symbol?.name === 'Promise') {
29
+ const typeArgs = type.typeArguments;
30
+ if (typeArgs && typeArgs.length >= 1)
31
+ return typeArgs[0];
32
+ }
33
+ return type;
34
+ }
35
+ function isResultOrPromiseResult(type, checker) {
36
+ if (containsResultLikeType(type, checker))
37
+ return true;
38
+ const unwrapped = unwrapPromise(type);
39
+ if (unwrapped !== type && containsResultLikeType(unwrapped, checker))
40
+ return true;
41
+ return false;
42
+ }
43
+ export const noUnhandledResult = createRule({
44
+ name: 'no-unhandled-result',
45
+ meta: {
46
+ type: 'problem',
47
+ docs: {
48
+ description: 'Require Result return values to be handled',
49
+ },
50
+ messages: {
51
+ unhandled: 'Result must be handled — errors will be silently ignored.',
52
+ },
53
+ schema: [],
54
+ },
55
+ defaultOptions: [],
56
+ create(context) {
57
+ return {
58
+ ExpressionStatement(node) {
59
+ const services = ESLintUtils.getParserServices(context);
60
+ const checker = services.program.getTypeChecker();
61
+ if (node.expression.type === 'CallExpression') {
62
+ const type = services.getTypeAtLocation(node.expression);
63
+ if (isResultOrPromiseResult(type, checker)) {
64
+ context.report({ node: node.expression, messageId: 'unhandled' });
65
+ }
66
+ }
67
+ else if (node.expression.type === 'AwaitExpression' &&
68
+ node.expression.argument.type === 'CallExpression') {
69
+ // Check the awaited type (the AwaitExpression's type, not the Promise)
70
+ const awaitedType = services.getTypeAtLocation(node.expression);
71
+ if (containsResultLikeType(awaitedType, checker)) {
72
+ context.report({ node: node.expression.argument, messageId: 'unhandled' });
73
+ return;
74
+ }
75
+ // Also check the call's return type (Promise<Result>)
76
+ const callType = services.getTypeAtLocation(node.expression.argument);
77
+ if (isResultOrPromiseResult(callType, checker)) {
78
+ context.report({ node: node.expression.argument, messageId: 'unhandled' });
79
+ }
80
+ }
81
+ },
82
+ };
83
+ },
84
+ });
85
+ //# sourceMappingURL=no-unhandled-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unhandled-result.js","sourceRoot":"","sources":["../../src/rules/no-unhandled-result.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,95 @@
1
+ import { RuleTester } from '@typescript-eslint/rule-tester';
2
+ import { afterAll, describe, it } from 'vitest';
3
+ import { noUnhandledResult } from './no-unhandled-result.js';
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const preamble = `
8
+ type OkResult<T> = { readonly ok: true; readonly value: T }
9
+ type ErrResult<E> = { readonly ok: false; readonly error: E }
10
+ type Result<T, E> = OkResult<T> | ErrResult<E>
11
+
12
+ type MyError = { name: 'Fail' }
13
+ declare function doThing(): Result<void, MyError>
14
+ declare function getValue(): Result<number, MyError>
15
+ declare function doAsync(): Promise<Result<void, MyError>>
16
+ declare function plain(): void
17
+ declare function getNumber(): number
18
+ declare function maybeResult(): Result<void, MyError> | void
19
+ declare function maybeUndefined(): Result<number, MyError> | undefined
20
+ declare function promiseMaybeResult(): Promise<Result<void, MyError> | void>
21
+ declare function errOnly(): ErrResult<MyError> | undefined
22
+ declare function asyncErrOnly(): Promise<ErrResult<MyError> | undefined>
23
+ `;
24
+ const ruleTester = new RuleTester({
25
+ languageOptions: {
26
+ parserOptions: {
27
+ projectService: {
28
+ allowDefaultProject: ['*.ts*'],
29
+ },
30
+ },
31
+ },
32
+ });
33
+ ruleTester.run('no-unhandled-result', noUnhandledResult, {
34
+ valid: [
35
+ // Assigned to variable
36
+ { code: `${preamble}\nconst r = doThing()` },
37
+ // Checked inline
38
+ { code: `${preamble}\nif (!doThing().ok) {}` },
39
+ // Passed as argument
40
+ { code: `${preamble}\nconsole.log(doThing())` },
41
+ // Used in match
42
+ { code: `${preamble}\nconst x = getValue().match({ok: v => v, err: () => 0})` },
43
+ // Non-Result void function
44
+ { code: `${preamble}\nplain()` },
45
+ // Non-Result number function
46
+ { code: `${preamble}\ngetNumber()` },
47
+ // Await assigned
48
+ { code: `${preamble}\nconst r = await doAsync()` },
49
+ // Chained property access
50
+ { code: `${preamble}\ndoThing().error` },
51
+ ],
52
+ invalid: [
53
+ // Bare call returning Result
54
+ {
55
+ code: `${preamble}\ndoThing()`,
56
+ errors: [{ messageId: 'unhandled' }],
57
+ },
58
+ // Bare call returning Result<number, E>
59
+ {
60
+ code: `${preamble}\ngetValue()`,
61
+ errors: [{ messageId: 'unhandled' }],
62
+ },
63
+ // Await unwrapping Promise<Result>
64
+ {
65
+ code: `${preamble}\nawait doAsync()`,
66
+ errors: [{ messageId: 'unhandled' }],
67
+ },
68
+ // Result | void — still contains a Result
69
+ {
70
+ code: `${preamble}\nmaybeResult()`,
71
+ errors: [{ messageId: 'unhandled' }],
72
+ },
73
+ // Result | undefined — still contains a Result
74
+ {
75
+ code: `${preamble}\nmaybeUndefined()`,
76
+ errors: [{ messageId: 'unhandled' }],
77
+ },
78
+ // Promise<Result | void> — still contains a Result
79
+ {
80
+ code: `${preamble}\nawait promiseMaybeResult()`,
81
+ errors: [{ messageId: 'unhandled' }],
82
+ },
83
+ // ErrResult | undefined — error can still be silently ignored
84
+ {
85
+ code: `${preamble}\nerrOnly()`,
86
+ errors: [{ messageId: 'unhandled' }],
87
+ },
88
+ // Promise<ErrResult | undefined> — same, awaited
89
+ {
90
+ code: `${preamble}\nawait asyncErrOnly()`,
91
+ errors: [{ messageId: 'unhandled' }],
92
+ },
93
+ ],
94
+ });
95
+ //# sourceMappingURL=no-unhandled-result.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unhandled-result.test.js","sourceRoot":"","sources":["../../src/rules/no-unhandled-result.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAA;AAE1D,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;CAgBhB,CAAA;AAED,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC;IAChC,eAAe,EAAE;QACf,aAAa,EAAE;YACb,cAAc,EAAE;gBACd,mBAAmB,EAAE,CAAC,OAAO,CAAC;aAC/B;SACF;KACF;CACF,CAAC,CAAA;AAEF,UAAU,CAAC,GAAG,CAAC,qBAAqB,EAAE,iBAAiB,EAAE;IACvD,KAAK,EAAE;QACL,uBAAuB;QACvB,EAAC,IAAI,EAAE,GAAG,QAAQ,uBAAuB,EAAC;QAC1C,iBAAiB;QACjB,EAAC,IAAI,EAAE,GAAG,QAAQ,yBAAyB,EAAC;QAC5C,qBAAqB;QACrB,EAAC,IAAI,EAAE,GAAG,QAAQ,0BAA0B,EAAC;QAC7C,gBAAgB;QAChB,EAAC,IAAI,EAAE,GAAG,QAAQ,0DAA0D,EAAC;QAC7E,2BAA2B;QAC3B,EAAC,IAAI,EAAE,GAAG,QAAQ,WAAW,EAAC;QAC9B,6BAA6B;QAC7B,EAAC,IAAI,EAAE,GAAG,QAAQ,eAAe,EAAC;QAClC,iBAAiB;QACjB,EAAC,IAAI,EAAE,GAAG,QAAQ,6BAA6B,EAAC;QAChD,0BAA0B;QAC1B,EAAC,IAAI,EAAE,GAAG,QAAQ,mBAAmB,EAAC;KACvC;IACD,OAAO,EAAE;QACP,6BAA6B;QAC7B;YACE,IAAI,EAAE,GAAG,QAAQ,aAAa;YAC9B,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,WAAW,EAAC,CAAC;SACnC;QACD,wCAAwC;QACxC;YACE,IAAI,EAAE,GAAG,QAAQ,cAAc;YAC/B,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,WAAW,EAAC,CAAC;SACnC;QACD,mCAAmC;QACnC;YACE,IAAI,EAAE,GAAG,QAAQ,mBAAmB;YACpC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,WAAW,EAAC,CAAC;SACnC;QACD,0CAA0C;QAC1C;YACE,IAAI,EAAE,GAAG,QAAQ,iBAAiB;YAClC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,WAAW,EAAC,CAAC;SACnC;QACD,+CAA+C;QAC/C;YACE,IAAI,EAAE,GAAG,QAAQ,oBAAoB;YACrC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,WAAW,EAAC,CAAC;SACnC;QACD,mDAAmD;QACnD;YACE,IAAI,EAAE,GAAG,QAAQ,8BAA8B;YAC/C,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,WAAW,EAAC,CAAC;SACnC;QACD,8DAA8D;QAC9D;YACE,IAAI,EAAE,GAAG,QAAQ,aAAa;YAC9B,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,WAAW,EAAC,CAAC;SACnC;QACD,iDAAiD;QACjD;YACE,IAAI,EAAE,GAAG,QAAQ,wBAAwB;YACzC,MAAM,EAAE,CAAC,EAAC,SAAS,EAAE,WAAW,EAAC,CAAC;SACnC;KACF;CACF,CAAC,CAAA"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@mikrojs/eslint-plugin",
3
+ "version": "0.0.7",
4
+ "description": "ESLint plugin for Mikro.js projects",
5
+ "keywords": [
6
+ "eslint",
7
+ "eslint-plugin",
8
+ "mikrojs"
9
+ ],
10
+ "homepage": "https://github.com/mikrojs/mikrojs#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/mikrojs/mikrojs/issues"
13
+ },
14
+ "license": "MIT",
15
+ "author": "Bjørge Næss <bjoerge@gmail.com>",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/mikrojs/mikrojs.git"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "exports": {
26
+ ".": "./dist/index.js"
27
+ },
28
+ "dependencies": {
29
+ "@typescript-eslint/utils": "^8.59.0",
30
+ "typescript-eslint": "8.58.0"
31
+ },
32
+ "devDependencies": {
33
+ "@typescript-eslint/rule-tester": "^8.59.0",
34
+ "eslint": "^10.2.1",
35
+ "typescript": "^6.0.3",
36
+ "vitest": "^4.1.5"
37
+ },
38
+ "peerDependencies": {
39
+ "eslint": "^10.0.0",
40
+ "typescript": "^6.0.0"
41
+ },
42
+ "scripts": {
43
+ "build:ts": "tsc -p tsconfig.build.json",
44
+ "test": "vitest run",
45
+ "typecheck": "tsc --noEmit"
46
+ }
47
+ }