@ztd-me/eslint 0.0.0-stage → 0.1.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,120 @@
1
+ export const sharedRules = {
2
+ 'complexity': [
3
+ 'error',
4
+ {
5
+ max: 10,
6
+ variant: 'classic',
7
+ },
8
+ ],
9
+ 'sonarjs/cognitive-complexity': ['error', 15],
10
+ 'max-depth': [
11
+ 'error',
12
+ {
13
+ max: 4,
14
+ },
15
+ ],
16
+ 'max-lines-per-function': [
17
+ 'error',
18
+ {
19
+ max: 60,
20
+ skipBlankLines: true,
21
+ skipComments: true,
22
+ IIFEs: true,
23
+ },
24
+ ],
25
+ 'max-lines': [
26
+ 'error',
27
+ {
28
+ max: 300,
29
+ skipBlankLines: true,
30
+ skipComments: true,
31
+ },
32
+ ],
33
+ 'max-params': [
34
+ 'error',
35
+ {
36
+ max: 4,
37
+ },
38
+ ],
39
+ 'style/max-len': [
40
+ 'error',
41
+ {
42
+ code: 120,
43
+ comments: 120,
44
+ tabWidth: 2,
45
+ ignoreUrls: true,
46
+ ignoreStrings: false,
47
+ ignoreTemplateLiterals: false,
48
+ ignoreRegExpLiterals: false,
49
+ ignoreComments: false,
50
+ ignoreTrailingComments: false,
51
+ },
52
+ ],
53
+ 'eqeqeq': ['error', 'always'],
54
+ 'curly': ['error', 'all'],
55
+ 'array-callback-return': [
56
+ 'error',
57
+ {
58
+ allowImplicit: false,
59
+ checkForEach: true,
60
+ allowVoid: false,
61
+ },
62
+ ],
63
+ 'no-empty': [
64
+ 'error',
65
+ {
66
+ allowEmptyCatch: false,
67
+ },
68
+ ],
69
+ 'no-shadow': [
70
+ 'error',
71
+ {
72
+ builtinGlobals: false,
73
+ hoist: 'all',
74
+ ignoreOnInitialization: false,
75
+ },
76
+ ],
77
+ 'no-nested-ternary': 'error',
78
+ 'no-unused-expressions': [
79
+ 'error',
80
+ {
81
+ allowShortCircuit: false,
82
+ allowTernary: false,
83
+ allowTaggedTemplates: true,
84
+ enforceForJSX: true,
85
+ },
86
+ ],
87
+ 'eslint-comments/require-description': [
88
+ 'error',
89
+ {
90
+ ignore: ['eslint-enable'],
91
+ },
92
+ ],
93
+ 'unused-imports/no-unused-vars': [
94
+ 'error',
95
+ {
96
+ args: 'all',
97
+ argsIgnorePattern: '^_',
98
+ caughtErrors: 'all',
99
+ caughtErrorsIgnorePattern: '^_',
100
+ vars: 'all',
101
+ varsIgnorePattern: '^_',
102
+ ignoreRestSiblings: true,
103
+ reportUsedIgnorePattern: true,
104
+ },
105
+ ],
106
+ 'no-constant-binary-expression': 'error',
107
+ 'no-unsafe-optional-chaining': [
108
+ 'error',
109
+ {
110
+ disallowArithmeticOperators: true,
111
+ },
112
+ ],
113
+ 'no-promise-executor-return': [
114
+ 'error',
115
+ {
116
+ allowVoid: false,
117
+ },
118
+ ],
119
+ 'no-setter-return': 'error',
120
+ };
@@ -0,0 +1,2 @@
1
+ import type { Linter } from 'eslint';
2
+ export declare const testsRules: Linter.RulesRecord;
@@ -0,0 +1,31 @@
1
+ export const testsRules = {
2
+ 'test/valid-expect': [
3
+ 'error',
4
+ {
5
+ alwaysAwait: true,
6
+ minArgs: 1,
7
+ maxArgs: 1,
8
+ },
9
+ ],
10
+ 'test/no-conditional-expect': [
11
+ 'error',
12
+ {
13
+ expectAssertions: false,
14
+ },
15
+ ],
16
+ 'test/no-conditional-tests': 'error',
17
+ 'test/no-disabled-tests': 'error',
18
+ 'test/expect-expect': [
19
+ 'error',
20
+ {
21
+ assertFunctionNames: ['expect', 'assert'],
22
+ additionalTestBlockFunctions: [],
23
+ },
24
+ ],
25
+ 'test/unbound-method': [
26
+ 'error',
27
+ {
28
+ ignoreStatic: false,
29
+ },
30
+ ],
31
+ };
@@ -0,0 +1,2 @@
1
+ import type { Linter } from 'eslint';
2
+ export declare const tsSyntaxRules: Linter.RulesRecord;
@@ -0,0 +1,88 @@
1
+ export const tsSyntaxRules = {
2
+ 'ts/no-explicit-any': [
3
+ 'error',
4
+ {
5
+ fixToUnknown: false,
6
+ ignoreRestArgs: false,
7
+ },
8
+ ],
9
+ 'ts/no-non-null-assertion': 'error',
10
+ 'ts/no-empty-object-type': [
11
+ 'error',
12
+ {
13
+ allowInterfaces: 'never',
14
+ allowObjectTypes: 'never',
15
+ },
16
+ ],
17
+ 'ts/ban-ts-comment': [
18
+ 'error',
19
+ {
20
+ 'ts-ignore': true,
21
+ 'ts-nocheck': true,
22
+ 'ts-check': false,
23
+ 'ts-expect-error': 'allow-with-description',
24
+ 'minimumDescriptionLength': 10,
25
+ },
26
+ ],
27
+ 'ts/consistent-type-assertions': [
28
+ 'error',
29
+ {
30
+ assertionStyle: 'as',
31
+ objectLiteralTypeAssertions: 'never',
32
+ arrayLiteralTypeAssertions: 'never',
33
+ },
34
+ ],
35
+ 'ts/explicit-module-boundary-types': [
36
+ 'error',
37
+ {
38
+ allowArgumentsExplicitlyTypedAsAny: false,
39
+ allowDirectConstAssertionInArrowFunctions: false,
40
+ allowHigherOrderFunctions: false,
41
+ allowOverloadFunctions: false,
42
+ allowTypedFunctionExpressions: true,
43
+ },
44
+ ],
45
+ 'ts/no-useless-constructor': 'error',
46
+ 'ts/no-invalid-void-type': [
47
+ 'error',
48
+ {
49
+ allowAsThisParameter: true,
50
+ allowInGenericTypeArguments: true,
51
+ },
52
+ ],
53
+ 'ts/no-extraneous-class': [
54
+ 'error',
55
+ {
56
+ allowConstructorOnly: false,
57
+ allowEmpty: false,
58
+ allowStaticOnly: false,
59
+ allowWithDecorator: true,
60
+ },
61
+ ],
62
+ 'ts/max-params': [
63
+ 'error',
64
+ {
65
+ max: 4,
66
+ countVoidThis: false,
67
+ },
68
+ ],
69
+ 'ts/no-shadow': [
70
+ 'error',
71
+ {
72
+ builtinGlobals: false,
73
+ hoist: 'functions-and-types',
74
+ ignoreOnInitialization: false,
75
+ ignoreTypeValueShadow: true,
76
+ ignoreFunctionTypeParameterNameValueShadow: true,
77
+ },
78
+ ],
79
+ 'ts/no-unused-expressions': [
80
+ 'error',
81
+ {
82
+ allowShortCircuit: false,
83
+ allowTernary: false,
84
+ allowTaggedTemplates: true,
85
+ enforceForJSX: true,
86
+ },
87
+ ],
88
+ };
@@ -0,0 +1,2 @@
1
+ import type { Linter } from 'eslint';
2
+ export declare const typedRules: Linter.RulesRecord;
@@ -0,0 +1,170 @@
1
+ export const typedRules = {
2
+ 'ts/strict-boolean-expressions': [
3
+ 'error',
4
+ {
5
+ allowAny: false,
6
+ allowNumber: false,
7
+ allowString: false,
8
+ allowNullableBoolean: false,
9
+ allowNullableEnum: false,
10
+ allowNullableNumber: false,
11
+ allowNullableObject: false,
12
+ allowNullableString: false,
13
+ },
14
+ ],
15
+ 'ts/no-floating-promises': [
16
+ 'error',
17
+ {
18
+ checkThenables: true,
19
+ ignoreVoid: false,
20
+ ignoreIIFE: false,
21
+ allowForKnownSafeCalls: [],
22
+ allowForKnownSafePromises: [],
23
+ },
24
+ ],
25
+ 'ts/no-misused-promises': [
26
+ 'error',
27
+ {
28
+ checksConditionals: {
29
+ flagUnions: 'all',
30
+ },
31
+ checksSpreads: true,
32
+ checksVoidReturn: true,
33
+ },
34
+ ],
35
+ 'ts/restrict-plus-operands': [
36
+ 'error',
37
+ {
38
+ allowAny: false,
39
+ allowBoolean: false,
40
+ allowNullish: false,
41
+ allowNumberAndString: false,
42
+ allowRegExp: false,
43
+ skipCompoundAssignments: false,
44
+ },
45
+ ],
46
+ 'ts/restrict-template-expressions': [
47
+ 'error',
48
+ {
49
+ allow: [],
50
+ allowAny: false,
51
+ allowArray: false,
52
+ allowBoolean: false,
53
+ allowNullish: false,
54
+ allowNumber: true,
55
+ allowRegExp: false,
56
+ allowNever: false,
57
+ },
58
+ ],
59
+ 'ts/return-await': ['error', 'always'],
60
+ 'ts/switch-exhaustiveness-check': [
61
+ 'error',
62
+ {
63
+ allowDefaultCaseForExhaustiveSwitch: true,
64
+ considerDefaultExhaustiveForUnions: false,
65
+ requireDefaultForNonUnion: true,
66
+ },
67
+ ],
68
+ 'ts/only-throw-error': [
69
+ 'error',
70
+ {
71
+ allow: [],
72
+ allowRethrowing: true,
73
+ allowThrowingAny: false,
74
+ allowThrowingUnknown: false,
75
+ },
76
+ ],
77
+ 'ts/no-unsafe-type-assertion': 'error',
78
+ 'ts/use-unknown-in-catch-callback-variable': 'error',
79
+ 'ts/no-unnecessary-condition': [
80
+ 'error',
81
+ {
82
+ allowConstantLoopConditions: 'only-allowed-literals',
83
+ checkTypePredicates: true,
84
+ },
85
+ ],
86
+ 'ts/no-base-to-string': [
87
+ 'error',
88
+ {
89
+ checkUnknown: true,
90
+ ignoredTypeNames: [
91
+ 'Error',
92
+ 'RegExp',
93
+ 'URL',
94
+ 'URLSearchParams',
95
+ ],
96
+ },
97
+ ],
98
+ 'ts/no-misused-spread': [
99
+ 'error',
100
+ {
101
+ allow: [],
102
+ },
103
+ ],
104
+ 'ts/no-unsafe-enum-comparison': 'error',
105
+ 'ts/no-deprecated': [
106
+ 'error',
107
+ {
108
+ allow: [],
109
+ },
110
+ ],
111
+ 'ts/consistent-type-exports': [
112
+ 'error',
113
+ {
114
+ fixMixedExportsWithInlineTypeSpecifier: false,
115
+ },
116
+ ],
117
+ 'ts/prefer-readonly': [
118
+ 'error',
119
+ {
120
+ onlyInlineLambdas: false,
121
+ },
122
+ ],
123
+ 'ts/require-array-sort-compare': [
124
+ 'error',
125
+ {
126
+ ignoreStringArrays: true,
127
+ },
128
+ ],
129
+ 'ts/require-await': 'error',
130
+ 'ts/prefer-nullish-coalescing': [
131
+ 'error',
132
+ {
133
+ ignoreConditionalTests: true,
134
+ ignoreMixedLogicalExpressions: false,
135
+ ignorePrimitives: {
136
+ bigint: false,
137
+ boolean: false,
138
+ number: false,
139
+ string: false,
140
+ },
141
+ },
142
+ ],
143
+ 'ts/prefer-optional-chain': [
144
+ 'error',
145
+ {
146
+ requireNullish: true,
147
+ allowPotentiallyUnsafeFixesThatModifyTheReturnTypeIKnowWhatImDoing: false,
148
+ },
149
+ ],
150
+ 'ts/no-array-delete': 'error',
151
+ 'ts/no-unsafe-unary-minus': 'error',
152
+ 'ts/prefer-promise-reject-errors': [
153
+ 'error',
154
+ {
155
+ allowEmptyReject: false,
156
+ allowThrowingAny: false,
157
+ allowThrowingUnknown: false,
158
+ },
159
+ ],
160
+ 'ts/await-thenable': 'error',
161
+ 'ts/no-for-in-array': 'error',
162
+ 'ts/no-implied-eval': 'error',
163
+ 'ts/no-unnecessary-type-assertion': 'error',
164
+ 'ts/no-unsafe-argument': 'error',
165
+ 'ts/no-unsafe-assignment': 'error',
166
+ 'ts/no-unsafe-call': 'error',
167
+ 'ts/no-unsafe-member-access': 'error',
168
+ 'ts/no-unsafe-return': 'error',
169
+ 'ts/unbound-method': 'error',
170
+ };
@@ -0,0 +1,2 @@
1
+ import type { Linter } from 'eslint';
2
+ export declare const vueA11yRules: Linter.RulesRecord;
@@ -0,0 +1,32 @@
1
+ export const vueA11yRules = {
2
+ 'vue-a11y/alt-text': 'error',
3
+ 'vue-a11y/anchor-has-content': 'error',
4
+ 'vue-a11y/aria-props': 'error',
5
+ 'vue-a11y/aria-role': 'error',
6
+ 'vue-a11y/aria-unsupported-elements': 'error',
7
+ 'vue-a11y/click-events-have-key-events': 'error',
8
+ 'vue-a11y/form-control-has-label': 'error',
9
+ 'vue-a11y/heading-has-content': 'error',
10
+ 'vue-a11y/iframe-has-title': 'error',
11
+ 'vue-a11y/interactive-supports-focus': 'error',
12
+ 'vue-a11y/label-has-for': [
13
+ 'error',
14
+ {
15
+ required: {
16
+ some: ['nesting', 'id'],
17
+ },
18
+ allowChildren: false,
19
+ },
20
+ ],
21
+ 'vue-a11y/media-has-caption': 'error',
22
+ 'vue-a11y/mouse-events-have-key-events': 'error',
23
+ 'vue-a11y/no-access-key': 'error',
24
+ 'vue-a11y/no-aria-hidden-on-focusable': 'error',
25
+ 'vue-a11y/no-autofocus': 'error',
26
+ 'vue-a11y/no-distracting-elements': 'error',
27
+ 'vue-a11y/no-redundant-roles': 'error',
28
+ 'vue-a11y/no-role-presentation-on-focusable': 'error',
29
+ 'vue-a11y/no-static-element-interactions': 'error',
30
+ 'vue-a11y/role-has-required-aria-props': 'error',
31
+ 'vue-a11y/tabindex-no-positive': 'error',
32
+ };
@@ -0,0 +1,2 @@
1
+ import type { Linter } from 'eslint';
2
+ export declare const vueRules: Linter.RulesRecord;
@@ -0,0 +1,90 @@
1
+ export const vueRules = {
2
+ 'vue/no-dupe-keys': 'error',
3
+ 'vue/no-v-html': 'error',
4
+ 'vue/no-setup-props-reactivity-loss': 'error',
5
+ 'vue/no-ref-object-reactivity-loss': 'error',
6
+ 'vue/no-mutating-props': [
7
+ 'error',
8
+ {
9
+ shallowOnly: false,
10
+ },
11
+ ],
12
+ 'vue/require-explicit-emits': [
13
+ 'error',
14
+ {
15
+ allowProps: false,
16
+ },
17
+ ],
18
+ 'vue/require-explicit-slots': 'error',
19
+ 'vue/require-prop-types': 'error',
20
+ 'vue/no-undef-components': [
21
+ 'error',
22
+ {
23
+ ignorePatterns: [],
24
+ },
25
+ ],
26
+ 'vue/no-undef-directives': [
27
+ 'error',
28
+ {
29
+ ignore: [],
30
+ },
31
+ ],
32
+ 'vue/no-unused-emit-declarations': 'error',
33
+ 'vue/html-button-has-type': [
34
+ 'error',
35
+ {
36
+ button: true,
37
+ submit: true,
38
+ reset: true,
39
+ },
40
+ ],
41
+ 'vue/no-template-target-blank': [
42
+ 'error',
43
+ {
44
+ allowReferrer: false,
45
+ enforceDynamicLinks: 'always',
46
+ },
47
+ ],
48
+ 'vue/eqeqeq': ['error', 'always'],
49
+ 'vue/max-lines-per-block': [
50
+ 'error',
51
+ {
52
+ script: 200,
53
+ template: 200,
54
+ style: 150,
55
+ skipBlankLines: true,
56
+ },
57
+ ],
58
+ 'vue/max-template-depth': [
59
+ 'error',
60
+ {
61
+ maxDepth: 5,
62
+ },
63
+ ],
64
+ 'vue/max-len': [
65
+ 'error',
66
+ {
67
+ code: 120,
68
+ template: 120,
69
+ comments: 120,
70
+ tabWidth: 2,
71
+ ignoreUrls: true,
72
+ ignoreStrings: false,
73
+ ignoreTemplateLiterals: false,
74
+ ignoreRegExpLiterals: false,
75
+ ignoreHTMLAttributeValues: false,
76
+ ignoreHTMLTextContents: false,
77
+ },
78
+ ],
79
+ 'vue/no-template-shadow': 'error',
80
+ 'vue/one-component-per-file': 'error',
81
+ 'vue/no-required-prop-with-default': 'error',
82
+ 'vue/html-end-tags': 'error',
83
+ 'vue/no-async-in-computed-properties': 'error',
84
+ 'vue/no-side-effects-in-computed-properties': 'error',
85
+ 'vue/no-use-v-if-with-v-for': 'error',
86
+ 'vue/require-v-for-key': 'error',
87
+ 'vue/no-watch-after-await': 'error',
88
+ 'vue/no-lifecycle-after-await': 'error',
89
+ 'vue/no-expose-after-await': 'error',
90
+ };
@@ -0,0 +1,9 @@
1
+ import type { TypeScriptOptions } from './options.js';
2
+ export declare function resolveTypeScript(options: boolean | TypeScriptOptions): {
3
+ tsconfigPath: string;
4
+ parserOptions: {
5
+ tsconfigRootDir: string;
6
+ projectService: false;
7
+ project: string;
8
+ };
9
+ };
@@ -0,0 +1,20 @@
1
+ import { dirname, resolve } from 'node:path';
2
+ import process from 'node:process';
3
+ import ts from 'typescript';
4
+ export function resolveTypeScript(options) {
5
+ const settings = typeof options === 'object' ? options : {};
6
+ const root = settings.tsconfigRootDir ?? process.cwd();
7
+ const tsconfigPath = resolve(root, settings.tsconfigPath ?? 'tsconfig.json');
8
+ const read = ts.readConfigFile(tsconfigPath, path => ts.sys.readFile(path));
9
+ if (read.error !== undefined) {
10
+ throw new Error(`@ztd-me/eslint: cannot read ${tsconfigPath}. `
11
+ + 'Supply typescript.tsconfigPath or use typescript:false for JavaScript.');
12
+ }
13
+ const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, dirname(tsconfigPath));
14
+ if (parsed.options.strict !== true || parsed.options.noUncheckedIndexedAccess !== true
15
+ || parsed.options.strictNullChecks === false) {
16
+ throw new Error('@ztd-me/eslint: tsconfig must enable strict (including strictNullChecks) '
17
+ + 'and noUncheckedIndexedAccess');
18
+ }
19
+ return { tsconfigPath, parserOptions: { tsconfigRootDir: root, projectService: false, project: tsconfigPath } };
20
+ }
@@ -0,0 +1,39 @@
1
+ # Stage-only publication
2
+
3
+ `.github/workflows/publish-eslint.yml` verifies and stages `@ztd-me/eslint`. Every upload uses `pnpm stage publish`; the workflow never directly publishes or approves a stage. `NPM_TOKEN` has Read and write (stage only) rights, with Bypass 2FA disabled. The token is bound to `https://registry.npmjs.org` in the stage step only, kept in memory, and never printed. Installation/build/test/pack steps have no token.
4
+
5
+ The bootstrap trigger is a push to an explicit `release/eslint-stage-*` branch. Once the workflow exists on the default branch, `workflow_dispatch` supports either `token` or `oidc` authentication. Neither trigger merges source. A separate draft PR carries repository review.
6
+
7
+ The verification jobs use a frozen pnpm lockfile on Node 22 and 24. The Node 24 job packs the tested package. Stage jobs consume that exact artifact and save the npm stage response as a workflow artifact. The Actions run, source SHA, tarball checksum and stage ID are review evidence. Do not resubmit the same stage after an ambiguous timeout; inspect the existing stage before retrying.
8
+
9
+ ## Bootstrap
10
+
11
+ The first upload uses the user-provided repository `NPM_TOKEN` secret. The package did not exist on npm at implementation start. Staging does not make it installable, and creating a repository secret does not establish an npm Trusted Publisher.
12
+
13
+ After the stage succeeds, the package owner must review and promote the stage using npm's website and their 2FA, or run an authenticated `pnpm stage approve <stage-id>` and complete the required proof of presence. Keep the OTP and credential out of chat. This user-controlled promotion is required before stage 1 can be called complete.
14
+
15
+ ## Trusted Publisher settings
16
+
17
+ The package owner configures the trust grant in npm's package settings after npm permits access to those settings. Exact GitHub values:
18
+
19
+ | Setting | Value |
20
+ | --- | --- |
21
+ | Organization or user | `zeithrold` |
22
+ | Repository | `tools` |
23
+ | Workflow filename | `publish-eslint.yml` |
24
+ | Environment name | Leave empty; this workflow uses no GitHub environment |
25
+ | Allowed actions | Stage publishing only; do not enable direct publish or dist-tag mutation for this workflow |
26
+
27
+ The `stage-oidc` job runs on GitHub-hosted `ubuntu-latest`, with job-scoped `id-token: write` and `contents: read`. It is selected only by an explicit dispatch with authentication `oidc`. No npm token is passed to that job. The token bootstrap job has only `contents: read`. The assistant does not create the trust grant or change package security settings.
28
+
29
+ References: [pnpm stage](https://pnpm.io/cli/stage), [pnpm token authentication](https://pnpm.io/blog/releases/11.10), [npm staged publishing](https://docs.npmjs.com/staged-publishing/), [npm Trusted Publishers](https://docs.npmjs.com/trusted-publishers/).
30
+
31
+ ## Public-release verification
32
+
33
+ After promotion, inspect the exact public registry version, then run:
34
+
35
+ ```sh
36
+ node scripts/registry-smoke.mjs 0.1.0
37
+ ```
38
+
39
+ This creates a fresh consumer, installs the exact registry version using pnpm, checks default/named ESM exports and declarations, and runs JS/TS/framework smoke checks. It keeps pnpm supply-chain protection active. If the freshly published version is blocked by pnpm's minimum release age or another policy, wait for policy eligibility and retry; do not set exceptions or lower the policy. A stage response, registry metadata alone, or a local tarball install is insufficient proof of public installation.