@noctcore/lint-meta-rules 0.1.0 → 0.3.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.
- package/README.md +19 -1
- package/dist/chunk-Z7TXSZR4.js +63 -0
- package/dist/i18n.cjs +317 -0
- package/dist/i18n.d.cts +60 -0
- package/dist/i18n.d.ts +60 -0
- package/dist/i18n.js +266 -0
- package/dist/index.cjs +439 -0
- package/dist/index.d.cts +127 -1
- package/dist/index.d.ts +127 -1
- package/dist/index.js +417 -19
- package/docs/rules/dockerfile-base-image-digest-pin.md +43 -0
- package/docs/rules/github-actions-runner-pinned.md +42 -0
- package/docs/rules/github-actions-sha-pinned.md +46 -0
- package/docs/rules/security-scanner-version-parity.md +61 -0
- package/docs/rules/service-image-digest-pin.md +52 -0
- package/docs/rules/translation-dead-keys.md +98 -0
- package/package.json +24 -3
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# `dockerfile-base-image-digest-pin`
|
|
2
|
+
|
|
3
|
+
> Dockerfile `FROM` base images are pinned by `@sha256:` digest.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
`FROM node:22-slim` names whatever the registry serves at build time, so two builds of one commit can
|
|
8
|
+
differ and a moved tag ships a change nobody reviewed. `tag@sha256:<digest>` keeps the tag for readers
|
|
9
|
+
and fixes the bytes.
|
|
10
|
+
|
|
11
|
+
## What it flags
|
|
12
|
+
|
|
13
|
+
Every `FROM` whose image has no `@sha256:` digest. Exempt: `FROM scratch` and `FROM <earlier stage>`.
|
|
14
|
+
An image chosen through a build arg (`FROM ${BASE}`) is resolved from its `ARG BASE=<default>` before
|
|
15
|
+
the first `FROM`; one that cannot be resolved is reported, since the text does not show what is
|
|
16
|
+
pulled. Each violation carries the 1-indexed line.
|
|
17
|
+
|
|
18
|
+
```dockerfile
|
|
19
|
+
# Bad
|
|
20
|
+
FROM node:22-slim AS deps
|
|
21
|
+
|
|
22
|
+
# Good
|
|
23
|
+
FROM node:22-slim@sha256:<digest> AS deps
|
|
24
|
+
FROM deps AS build
|
|
25
|
+
FROM scratch
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Factory
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
createDockerfileBaseImageDigestPinRule(options?: DockerfileBaseImageDigestPinOptions): IMetaRule
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Option | Type | Default | Meaning |
|
|
35
|
+
| --- | --- | --- | --- |
|
|
36
|
+
| `dockerfileGlobs` | `string[]` | `Dockerfile`, `Dockerfile.*`, `*.Dockerfile` at any depth, dot-directories included | Dockerfiles to scan. |
|
|
37
|
+
| `skipDirs` | `string[]` | `['node_modules', '.git', 'dist', '.turbo', 'coverage']` | A path with any of these segments is skipped. |
|
|
38
|
+
| `ciCritical` | `boolean` | `true` | Whether a violation fails CI. |
|
|
39
|
+
|
|
40
|
+
## Limits
|
|
41
|
+
|
|
42
|
+
A `FROM` split with a line continuation (`\`) is not read. Add a `Containerfile` glob if you use
|
|
43
|
+
Podman naming.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# `github-actions-runner-pinned`
|
|
2
|
+
|
|
3
|
+
> Workflow jobs run on a named runner image, never a `*-latest` label.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
GitHub repoints `ubuntu-latest` (and `macos-latest`, `windows-latest`) to a new OS image on its own
|
|
8
|
+
schedule, so a green workflow can turn red, or quietly change what it tests, with no commit in your
|
|
9
|
+
repo. Pinning a named image makes the move a reviewed diff.
|
|
10
|
+
|
|
11
|
+
## What it flags
|
|
12
|
+
|
|
13
|
+
Every floating label in a `runs-on:` value, read as a scalar, a flow list (`[a, b]`), a block list, or
|
|
14
|
+
a `group:` / `labels:` mapping. An expression (`${{ matrix.os }}`) cannot be judged from the text and
|
|
15
|
+
is left alone; a commented-out `runs-on:` is ignored.
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
# Bad
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
runs-on: [self-hosted, ubuntu-latest]
|
|
21
|
+
|
|
22
|
+
# Good
|
|
23
|
+
runs-on: ubuntu-24.04
|
|
24
|
+
runs-on: ${{ matrix.os }}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Factory
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
createGithubActionsRunnerPinnedRule(options?: GithubActionsRunnerPinnedOptions): IMetaRule
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Option | Type | Default | Meaning |
|
|
34
|
+
| --- | --- | --- | --- |
|
|
35
|
+
| `workflowGlobs` | `string[]` | `['.github/workflows/*.yml', '.github/workflows/*.yaml']` | Workflow files to scan. |
|
|
36
|
+
| `floatingLabel` | `RegExp` | `/^[\w.-]+-latest$/u` | A label matching this is floating. Do not pass a `g`-flagged regex. |
|
|
37
|
+
| `ciCritical` | `boolean` | `true` | Whether a violation fails CI. |
|
|
38
|
+
|
|
39
|
+
## Limits
|
|
40
|
+
|
|
41
|
+
Line-based text, not a YAML parse. A matrix value such as `os: [ubuntu-latest]` that feeds
|
|
42
|
+
`runs-on: ${{ matrix.os }}` is not checked.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# `github-actions-sha-pinned`
|
|
2
|
+
|
|
3
|
+
> GitHub Actions `uses:` refs are pinned to a 40-character commit SHA with a `# vN` comment.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
A tag or branch ref is a moving target: whoever controls the action's repository can repoint it, and
|
|
8
|
+
the new code runs with your workflow's token. A full commit SHA cannot move. The trailing `# vN`
|
|
9
|
+
comment keeps the pin readable and is what Dependabot rewrites on a bump, so a bare SHA is rejected
|
|
10
|
+
too.
|
|
11
|
+
|
|
12
|
+
## What it flags
|
|
13
|
+
|
|
14
|
+
Every `uses:` line (step-level and job-level reusable workflow calls) in the scanned workflow files:
|
|
15
|
+
|
|
16
|
+
- a ref whose pin is not a 40-character hex SHA (tags, branches, short SHAs, no `@` at all),
|
|
17
|
+
- a SHA-pinned ref with no `# vN` comment,
|
|
18
|
+
- a `docker://` ref with no `@sha256:<digest>`.
|
|
19
|
+
|
|
20
|
+
Local actions (`uses: ./path`) are exempt. Each violation carries the 1-indexed line.
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
# Bad
|
|
24
|
+
- uses: actions/checkout@v6
|
|
25
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
|
|
26
|
+
|
|
27
|
+
# Good
|
|
28
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
29
|
+
- uses: ./.github/actions/setup
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Factory
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
createGithubActionsShaPinnedRule(options?: GithubActionsShaPinnedOptions): IMetaRule
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
| Option | Type | Default | Meaning |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `workflowGlobs` | `string[]` | `['.github/workflows/*.yml', '.github/workflows/*.yaml']` | Workflow files to scan. |
|
|
41
|
+
| `ciCritical` | `boolean` | `true` | Whether a violation fails CI. |
|
|
42
|
+
|
|
43
|
+
## Limits
|
|
44
|
+
|
|
45
|
+
The check is line-based text, not a YAML parse. A `uses:` value split across lines, or built from an
|
|
46
|
+
expression, is not seen.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# `security-scanner-version-parity`
|
|
2
|
+
|
|
3
|
+
> CI and the local pre-push hook pin the same secret-scanner version, and the hook checks it at run
|
|
4
|
+
> time.
|
|
5
|
+
|
|
6
|
+
## Why
|
|
7
|
+
|
|
8
|
+
Different scanner versions ship different rulesets, so a push can pass the local hook and fail CI (or
|
|
9
|
+
the reverse) with no code difference. Pinning one version on both sides, and having the hook refuse a
|
|
10
|
+
native binary of another version, keeps the two scans the same scan.
|
|
11
|
+
|
|
12
|
+
## What it flags
|
|
13
|
+
|
|
14
|
+
With the defaults (gitleaks):
|
|
15
|
+
|
|
16
|
+
- workflows that pin different `GITLEAKS_VERSION` values,
|
|
17
|
+
- a hook that runs gitleaks when no workflow pins `GITLEAKS_VERSION`, or workflows that pin it when the
|
|
18
|
+
hook never runs gitleaks,
|
|
19
|
+
- a hook with no `GITLEAKS_VERSION="x.y.z"` declaration, or one that differs from CI,
|
|
20
|
+
- a hook `GITLEAKS_IMAGE="...gitleaks:vX.Y.Z"` tag that differs from CI,
|
|
21
|
+
- a hook that never runs `gitleaks version`.
|
|
22
|
+
|
|
23
|
+
Dormant when neither the workflows nor the hook mention the scanner.
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
# .github/workflows/security.yml
|
|
27
|
+
env:
|
|
28
|
+
GITLEAKS_VERSION: '8.30.1'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Good: scripts/ci/pre-push.sh
|
|
33
|
+
GITLEAKS_VERSION="8.30.1"
|
|
34
|
+
GITLEAKS_IMAGE="ghcr.io/gitleaks/gitleaks:v${GITLEAKS_VERSION}"
|
|
35
|
+
[ "$(gitleaks version)" = "$GITLEAKS_VERSION" ] || exit 1
|
|
36
|
+
|
|
37
|
+
# Bad: a drifted pin and no run-time check
|
|
38
|
+
GITLEAKS_VERSION="8.29.0"
|
|
39
|
+
gitleaks git .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Factory
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
createSecurityScannerVersionParityRule(options?: SecurityScannerVersionParityOptions): IMetaRule
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Option | Type | Default | Meaning |
|
|
49
|
+
| --- | --- | --- | --- |
|
|
50
|
+
| `scanner` | `string` | `'gitleaks'` | The scanner's binary name, as it appears in the hook and its image name. |
|
|
51
|
+
| `versionVariable` | `string` | `'GITLEAKS_VERSION'` | The variable both sides pin the version in (`KEY: x.y.z` in workflows, `KEY=x.y.z` in the hook). |
|
|
52
|
+
| `imageVariable` | `string` | `'GITLEAKS_IMAGE'` | The hook's optional image variable, whose `<scanner>:vX.Y.Z` tag must agree. |
|
|
53
|
+
| `hookFile` | `string` | `'scripts/ci/pre-push.sh'` | The local hook script, repo-relative. |
|
|
54
|
+
| `workflowGlobs` | `string[]` | `['.github/workflows/*.yml', '.github/workflows/*.yaml']` | Workflow files to scan. |
|
|
55
|
+
| `ciCritical` | `boolean` | `true` | Whether a violation fails CI. |
|
|
56
|
+
|
|
57
|
+
## Limits
|
|
58
|
+
|
|
59
|
+
It checks that the hook's text runs `<scanner> version`, not that the comparison is correct, and it
|
|
60
|
+
reads only `x.y.z` versions. One hook file is checked; a repo with several hooks needs one rule
|
|
61
|
+
instance per hook.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# `service-image-digest-pin`
|
|
2
|
+
|
|
3
|
+
> Workflow service/container images and docker-compose images are pinned by `@sha256:` digest.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
A tag (`postgres:17-alpine`) or `latest` names whatever the registry serves today, so a green build
|
|
8
|
+
can change under an unchanged commit, and a local run cannot promise the bytes CI ran.
|
|
9
|
+
`tag@sha256:<digest>` keeps the readable tag and fixes the content.
|
|
10
|
+
|
|
11
|
+
## What it flags
|
|
12
|
+
|
|
13
|
+
- In workflow files: `image:` under a job's `services:`, and a job's `container:` in scalar or mapping
|
|
14
|
+
form. An action step's `with: image:` input is not an image pull and is ignored.
|
|
15
|
+
- In compose files: every service's `image:`, except a service that also has `build:` (there `image:`
|
|
16
|
+
tags an image built locally, which has no upstream digest).
|
|
17
|
+
|
|
18
|
+
Each violation carries the 1-indexed line.
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
# Bad
|
|
22
|
+
services:
|
|
23
|
+
db:
|
|
24
|
+
image: postgres:17-alpine
|
|
25
|
+
|
|
26
|
+
# Good
|
|
27
|
+
services:
|
|
28
|
+
db:
|
|
29
|
+
image: postgres:17-alpine@sha256:<digest>
|
|
30
|
+
api:
|
|
31
|
+
build: .
|
|
32
|
+
image: app-api:local
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Factory
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
createServiceImageDigestPinRule(options?: ServiceImageDigestPinOptions): IMetaRule
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| Option | Type | Default | Meaning |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| `workflowGlobs` | `string[]` | `['.github/workflows/*.yml', '.github/workflows/*.yaml']` | Workflow files to scan. |
|
|
44
|
+
| `composeGlobs` | `string[]` | `compose` / `docker-compose` with optional `.x` / `-x` infix, `.yml` / `.yaml`, at any depth including dot-directories | Compose files to scan. |
|
|
45
|
+
| `skipDirs` | `string[]` | `['node_modules', '.git', 'dist', '.turbo', 'coverage']` | A compose path with any of these segments is skipped. |
|
|
46
|
+
| `allowUnpinned` | `string[]` | `[]` | Exact image refs allowed unpinned, for a ref the registry cannot serve a digest for. Only that exact ref passes; a bumped tag is checked again. |
|
|
47
|
+
| `ciCritical` | `boolean` | `true` | Whether a violation fails CI. |
|
|
48
|
+
|
|
49
|
+
## Limits
|
|
50
|
+
|
|
51
|
+
Line-based text, not a YAML parse. An image behind an env default (`${REDIS_IMAGE:-redis:7}`) is
|
|
52
|
+
reported as written, since the text does not show a digest.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# `translation-dead-keys`
|
|
2
|
+
|
|
3
|
+
> Every translation catalog key is reachable from the source: named by a translation call, or spelled
|
|
4
|
+
> by some string in the code.
|
|
5
|
+
|
|
6
|
+
Import it from the `i18n` entry point, which (unlike the main one) loads ESLint:
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { createTranslationDeadKeysRule } from '@noctcore/lint-meta-rules/i18n';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
A catalog key nothing uses is still translated, reviewed and shipped in every language. Finding them is
|
|
15
|
+
a whole-program question, so it is not an ESLint rule: a per-file rule never sees every call site
|
|
16
|
+
(editor runs, `--cache`, lint-staged, sharded workers), and keys flow as data (key tables, key-building
|
|
17
|
+
helpers, server-sent codes) that no call-site analysis follows.
|
|
18
|
+
|
|
19
|
+
This rule resolves call sites with the same visitor and catalog loader as
|
|
20
|
+
`noctcore-contracts/translation-key-exists` (exported by `@noctcore/eslint-plugin-contracts`), so the
|
|
21
|
+
two checks never disagree on which key a call means. It then adds the data routes a per-file rule
|
|
22
|
+
cannot see.
|
|
23
|
+
|
|
24
|
+
## What counts as reached
|
|
25
|
+
|
|
26
|
+
A key is reached, and never reported, when ANY of these holds:
|
|
27
|
+
|
|
28
|
+
- a translation call resolves to it: `t('key')`, `t('ns:key')`, `t('key', { ns })`,
|
|
29
|
+
`useTranslation('ns', { keyPrefix })`, `<Trans i18nKey>`, a `TFunction<'ns'>` parameter, the
|
|
30
|
+
`fallbackNamespaces`;
|
|
31
|
+
- a template key's static head is a prefix of it: `` t(`status.${s}`) `` reaches every `status.*`;
|
|
32
|
+
- a `returnObjects` call names one of its ancestors;
|
|
33
|
+
- ANY string literal in the scanned source equals the key, `ns:key`, or its plural/context base
|
|
34
|
+
(`key` reaches `key_one`, `key_ordinal_few`, `key_male`), in any namespace;
|
|
35
|
+
- ANY template literal or `+` chain in the scanned source can produce it:
|
|
36
|
+
`` `nav.${id}.label` `` and `'errors.' + code` are patterns, not just call arguments;
|
|
37
|
+
- it matches an `allow` pattern.
|
|
38
|
+
|
|
39
|
+
And it reports **no dead key at all** when a scanned file cannot be analysed (a parse error), or when
|
|
40
|
+
`sourceGlobs` match nothing: in both cases the rule would otherwise call reached keys dead.
|
|
41
|
+
|
|
42
|
+
What is left was named by no call and spelled by no string. On a production app with 1,824 keys this
|
|
43
|
+
reported 55, and every one of them had zero references outside the catalogs.
|
|
44
|
+
|
|
45
|
+
## Blind spots
|
|
46
|
+
|
|
47
|
+
It is conservative, not complete. It reports a reached key as dead when the key arrives by a route it
|
|
48
|
+
cannot see:
|
|
49
|
+
|
|
50
|
+
- **Keys that never appear in the scanned source**: server-sent codes, a CMS, another app, JSON
|
|
51
|
+
config. List them in `allow`, or add the files that spell them to `sourceGlobs`.
|
|
52
|
+
- **A variable key under a `keyPrefix` binding**: `useTranslation('ns', { keyPrefix: 'form' })` then
|
|
53
|
+
`t(field)` with `field = 'name'` reaches `form.name`, but no string spells `form.name`. (A static key
|
|
54
|
+
under a `keyPrefix` is resolved correctly.)
|
|
55
|
+
- **Keys built by anything other than `+` or a template literal**: `[a, b].join('.')`,
|
|
56
|
+
`` `${a}` `` split across variables, `String.prototype.concat`.
|
|
57
|
+
|
|
58
|
+
It also misses some dead keys, by design: any string that happens to equal a key (in any namespace)
|
|
59
|
+
keeps it alive, and a broad pattern such as `` `${x}.title` `` keeps every `*.title` alive.
|
|
60
|
+
|
|
61
|
+
## Factory
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
createTranslationDeadKeysRule(options?: TranslationDeadKeysOptions): IMetaRule
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Inert until both `catalogs` and `sourceGlobs` are set; there are no built-in paths.
|
|
68
|
+
|
|
69
|
+
| Option | Type | Default | Meaning |
|
|
70
|
+
| --- | --- | --- | --- |
|
|
71
|
+
| `catalogs` | `CatalogSource[]` | `[]` | The catalogs to check, in `translation-key-exists`' shape (`{ file, namespace?, keyPath? }`, `{ns}` placeholders allowed). List ONE language: a key is dead or alive regardless of how many languages translate it. |
|
|
72
|
+
| `sourceGlobs` | `string[]` | `[]` | Every file that can reach a key: call sites AND key tables. Include tests if a key used only by a test should count as alive. |
|
|
73
|
+
| `namespaces` | `string[]` | derived | The namespaces to check. Derived from `catalogs`: a `{ns}` file segment is globbed, a trailing `{ns}` keyPath segment lists the object's keys. Required when a `{ns}` sits anywhere else. |
|
|
74
|
+
| `allow` | `string[]` | `[]` | `ns:key` patterns reached from outside the scanned source; `*` matches any run of characters. |
|
|
75
|
+
| `skipDirs` | `string[]` | `['node_modules', '.git', 'dist', '.turbo', 'coverage']` | Source paths with any of these segments are skipped. |
|
|
76
|
+
| `id` | `string` | `'translation-dead-keys'` | Rule id, for running more than one instance. |
|
|
77
|
+
| `ciCritical` | `boolean` | `true` | Whether a dead key fails CI. |
|
|
78
|
+
|
|
79
|
+
Every resolution option of `translation-key-exists` is accepted and means the same thing:
|
|
80
|
+
`defaultNamespace`, `fallbackNamespaces`, `hooks`, `instances`, `functions`, `typeNames`,
|
|
81
|
+
`transComponents`, `namespaceIdentifiers`, `nsSeparator`, `keySeparator`. Pass them the same values.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
createTranslationDeadKeysRule({
|
|
85
|
+
catalogs: [
|
|
86
|
+
{ file: 'apps/web/src/lib/i18n/locales/pl.json', keyPath: '{ns}' },
|
|
87
|
+
{ file: 'apps/web/src/features/{ns}/locales/pl.json' },
|
|
88
|
+
],
|
|
89
|
+
defaultNamespace: 'common',
|
|
90
|
+
namespaceIdentifiers: { HELP_NS: 'help' },
|
|
91
|
+
sourceGlobs: ['apps/web/src/**/*.ts', 'apps/web/src/**/*.tsx'],
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Requirements
|
|
96
|
+
|
|
97
|
+
The `i18n` entry needs the optional peers `eslint` (>= 9) and `@typescript-eslint/parser`. Scanned
|
|
98
|
+
files are parsed as TypeScript with JSX enabled, without type information.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noctcore/lint-meta-rules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Portable, parameterized lint-meta rules — whole-repo / cross-file invariants ESLint cannot reach — for the @noctcore/harness lint-meta runner.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
"import": "./dist/index.js",
|
|
14
14
|
"require": "./dist/index.cjs"
|
|
15
15
|
},
|
|
16
|
+
"./i18n": {
|
|
17
|
+
"types": "./dist/i18n.d.ts",
|
|
18
|
+
"import": "./dist/i18n.js",
|
|
19
|
+
"require": "./dist/i18n.cjs"
|
|
20
|
+
},
|
|
16
21
|
"./package.json": "./package.json"
|
|
17
22
|
},
|
|
18
23
|
"files": [
|
|
@@ -41,15 +46,31 @@
|
|
|
41
46
|
"homepage": "https://github.com/noctcore/eslint-plugins/tree/main/packages/lint-meta-rules",
|
|
42
47
|
"bugs": "https://github.com/noctcore/eslint-plugins/issues",
|
|
43
48
|
"scripts": {
|
|
44
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
49
|
+
"build": "tsup src/index.ts src/i18n.ts --format esm,cjs --dts --clean",
|
|
45
50
|
"typecheck": "tsc --noEmit",
|
|
46
51
|
"test": "bun test"
|
|
47
52
|
},
|
|
48
53
|
"dependencies": {
|
|
49
|
-
"@noctcore/
|
|
54
|
+
"@noctcore/eslint-plugin-contracts": "^0.5.0",
|
|
55
|
+
"@noctcore/harness": "^0.3.0",
|
|
56
|
+
"@typescript-eslint/utils": "^8.61.1"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
60
|
+
"eslint": ">=9.0.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"@typescript-eslint/parser": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"eslint": {
|
|
67
|
+
"optional": true
|
|
68
|
+
}
|
|
50
69
|
},
|
|
51
70
|
"devDependencies": {
|
|
52
71
|
"@types/node": "^22.0.0",
|
|
72
|
+
"@typescript-eslint/parser": "^8.61.1",
|
|
73
|
+
"eslint": "^10.7.0",
|
|
53
74
|
"tsup": "^8.5.1",
|
|
54
75
|
"typescript": "^5.6.0"
|
|
55
76
|
}
|