@synapsestudios/lintofu 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,46 +3,68 @@
3
3
  An ESLint plugin for linting [OpenTofu](https://opentofu.org) configurations.
4
4
 
5
5
  > [!WARNING]
6
- > The parser depends on Go's [`syscall/js`](https://pkg.go.dev/syscall/js), which the Go team has declared experimental and exempt from the Go compatibility promise. A future Go release can break us. `lintofu` will stay on `0.x` until `syscall/js` stabilizes or we replace it. See [ADR 0001](./docs/adr/0001-syscall-js-experimental.md).
6
+ > The parser depends on Go's [`syscall/js`](https://pkg.go.dev/syscall/js), which the Go team has declared experimental and exempt from the Go compatibility promise. A future Go release _may_ break the functionality of this plugin. `lintofu` will stay on `0.x` until `syscall/js` stabilizes or we replace it. See [ADR 0001](./docs/adr/0001-syscall-js-experimental.md).
7
7
 
8
- Pre-1.0. API and rule set may change. Published to GitHub Packages under the `@synapsestudios` scope while it's being tested internally; a move to the public npm registry will follow.
8
+ This is a pre-release and experimental ESLint plugin.
9
9
 
10
- **Documentation:** [synapsestudios.github.io/lintofu](https://synapsestudios.github.io/lintofu/) covers the user guide, the developer guide, and the full rule index. The same pages are in [`docs/`](./docs), which is what resolves when reading this file from the npm tarball.
10
+ Published to npm as [`@synapsestudios/lintofu`](https://www.npmjs.com/package/@synapsestudios/lintofu).
11
+
12
+ ## Head striaght to the docs?
13
+
14
+ The lintofu docs are located here: [synapsestudios.github.io/lintofu](https://synapsestudios.github.io/lintofu/) the frontend that represents the documentation located in [`docs/`](./docs).
11
15
 
12
16
  ## What it is
13
17
 
14
- `lintofu` is an **ESLint language plugin** (the same mechanism that powers [`@eslint/json`](https://github.com/eslint/json) and [`@eslint/css`](https://github.com/eslint/css)). It parses `.tf` and `.tofu` files using HashiCorp's own `hcl/v2` parser (compiled to WebAssembly) and exposes the AST to ESLint rules so you can enforce OpenTofu coding conventions alongside the rest of your ESLint rules.
18
+ `lintofu` is an **ESLint language plugin** (the same mechanism that powers [`@eslint/json`](https://github.com/eslint/json) and [`@eslint/css`](https://github.com/eslint/css)).
19
+
20
+ It parses `.tf` and `.tofu` files using HashiCorp's own `hcl/v2` parser (compiled to WebAssembly) and exposes the AST to ESLint rules so you can enforce OpenTofu or Terraform coding conventions alongside the rest of your ESLint rules.
21
+
22
+ ### What it (is not)
23
+
24
+ We (Synapse Studios) have been using this internally for a few months. This statement is to provide clarity on the kind of expectations you should have when engaging with it, we will accept contributions from the community, however, we can make no promises as to what kind of attention we will be able to give it when it comes to approving/reviewing/or otherwise engaging with community feedback.
25
+
26
+ This will be (for the foreseeable future) _best effort_ and sometimes we will be too busy to triage incoming pull requests or bug reports.
27
+
28
+ ### AI Disclosure
29
+
30
+ To be clear, AI _has_ been used to help build this tool, but the specifications, features, rules, the way in which the plugin handles rule precedence, processing, and the like have _all_ been defined by humans clearly, with insight and scrutiny from a team of experienced DevOps engineers and Software Engineers in order to build something that isn't complete slop. We're tired of slop.
15
31
 
16
32
  ## Scope
17
33
 
18
- `lintofu` checks syntactic and stylistic conventions: documentation, pinning, and the structural patterns listed in the [rule index](./docs/rules/index.md). Its boundaries:
34
+ `lintofu` asserts against syntactic problems, stylistic conventions, and some provider specific code smells. We provide a list of somewhat generic (but other times opinionated) set of core rules, listed in our [rules index](https://synapsestudios.github.io/lintofu/rules/), that you can take advantage of.
35
+
36
+ ### Our intentions for `lintofu`:
19
37
 
20
- - Semantic validation belongs to `tofu validate`. `lintofu` leaves that to OpenTofu.
21
- - Semantic and evaluated rules (type-checking, symbol resolution) belong to the future built-in linter described in the [OpenTofu linting RFC](https://github.com/opentofu/opentofu/pull/3999), which needs the full language runtime. `lintofu` stays on the syntactic and stylistic side, where ESLint's ecosystem already fits.
22
- - `lintofu` is a fresh ESLint-native tool. It shares no code with TFLint, which lacks OpenTofu-specific feature support.
38
+ We built lintofu as a stop-gap until feature rich fully fledged linting is available. You can read more about that from the [OpenTofu linting RFC](https://github.com/opentofu/opentofu/pull/3999). This isn't meant to be a complete replacement for what will soon be the linter implementation in OpenTofu, however, it _is_ meant to provide all of the necessary facilities and interfaces to build a comprehensive suite of assertions and rules at the same level.
23
39
 
24
40
  ## Install
25
41
 
42
+ ### Pre-requisites
43
+
44
+ Required versions:
45
+ - Node >= `22.13`
46
+ - ESLint >= `9.x`
47
+
48
+ ### Setup
49
+
26
50
  ```sh
27
51
  npm install --save-dev @synapsestudios/lintofu
28
52
  ```
29
53
 
30
- The package lives in GitHub Packages, so consumers need an `.npmrc` that maps the `@synapsestudios` scope to the registry and supplies a token with `read:packages`:
54
+ If you installed it from GitHub Packages before, delete these lines from your `.npmrc`. While they're there, npm resolves every `@synapsestudios` package from GitHub Packages, which no longer gets new releases:
31
55
 
32
56
  ```
33
57
  @synapsestudios:registry=https://npm.pkg.github.com
34
58
  //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
35
59
  ```
36
60
 
37
- Requires Node ^20.19, ^22.13, or >=24 and ESLint ^9 || ^10.
38
-
39
61
  ## Supported versions
40
62
 
41
- Providers: latest major plus one prior (AWS provider v6 and v5 today); older majors are best effort. Rules that only apply from a given provider major declare it in `meta.docs.appliesTo`. Full policy in [ADR 0005](./docs/adr/0005-version-support-policy.md).
63
+ We would like to have as broad of compatibility as possible, so our aim is to have full compatibility with the major cloud providers in which our rules are tested against, from the _last_ major version to the latest major version.
42
64
 
43
- The parser wraps HashiCorp's `hcl/v2`, which parses HCL2, the syntax that shipped with Terraform 0.12. That sets a hard floor: Terraform 0.12 and newer parse, Terraform 0.11 and older (HCL1) do not. OpenTofu forked from Terraform 1.5, so every OpenTofu release is above the floor.
65
+ As this plugin ages, we expect to have to provide _some_ backwards compatibility with our rules that may target older providers' code smells, but our focus will be on moving the tool forwards with the rest of the ecosystem. Our full decision making record and rationale on this is defined in [ADR 0005](./docs/adr/0005-version-support-policy.md).
44
66
 
45
- Above the floor, the same ADR defines the per-generation status:
67
+ The parser wraps HashiCorp's `hcl/v2`, which parses HCL2, the syntax that shipped with Terraform 0.12. We _will not be supporting `hcl/v1` or Terraform versions that pre-date `0.12`.
46
68
 
47
69
  <!-- lintofu:support-matrix:start -->
48
70
 
@@ -55,20 +77,18 @@ Above the floor, the same ADR defines the per-generation status:
55
77
 
56
78
  <!-- lintofu:support-matrix:end -->
57
79
 
58
- - **supported**: generations the rules are written for; advice is kept valid for them as part of normal maintenance.
59
- - **best effort**: configurations parse and lint. Many rules exist specifically to flag upgrade hazards in these generations (legacy `list(...)`/`map(...)`, quoted type constraints, and friends), but we fix issues as they're reported rather than checking proactively.
60
- - **unsupported**: HCL1 syntax. The parser rejects these files.
61
-
62
- These statuses are policy commitments, not CI results: no workflow runs real OpenTofu or Terraform binaries yet. This table is maintained by hand and will be replaced by generated output once the version test harness exists.
80
+ - **supported**: We focus on building rules that apply to these versions.
81
+ - **best effort**: Some rules may target these specific versions and the gotchas associated with upgrading _from_ them, but we won't be writing rules that target specific syntax that is only applicable to these versions.
82
+ - **unsupported**: The parser will reject these.
63
83
 
64
84
  <!-- lintofu:tested-versions:start -->
65
85
 
66
86
  ## Tested provider versions
67
87
 
68
- Provider-specific rules are validated against these versions. [`provider-versions.json`](./provider-versions.json) is the source of truth; edit it and run `npm run docs:versions` to update this section. CI fails if the two drift.
88
+ Provider-specific rules are validated against these versions. This is a list located here: [`provider-versions.json`](./provider-versions.json) which defines which provider versions we test against in CI.
69
89
 
70
- - aws provider v6.42.0
71
- - cloudflare provider v5.24.0
90
+ - aws provider `v6.42.0`
91
+ - cloudflare provider `v5.24.0`
72
92
 
73
93
  <!-- lintofu:tested-versions:end -->
74
94
 
@@ -81,7 +101,7 @@ import lintofu from "@synapsestudios/lintofu";
81
101
  export default [lintofu.configs.recommended];
82
102
  ```
83
103
 
84
- Or wire up manually for full control:
104
+ Or opt into specific rules and error statuses.
85
105
 
86
106
  ```js
87
107
  // eslint.config.js
@@ -102,21 +122,11 @@ export default [
102
122
 
103
123
  ## Rules
104
124
 
105
- Every rule's description, severity, category, recommended flag, and autofix
106
- support lives in the generated rule index:
107
-
108
- - **[Rule index](https://synapsestudios.github.io/lintofu/rules/)** on the
109
- documentation site
110
- - [`docs/rules/index.md`](./docs/rules/index.md) in this repository, the same
111
- content, which resolves here today while the site is still private
112
-
113
- The index is generated from each rule's `meta.docs` by `npm run docs:rules`, and
114
- CI fails on a rule with no docs page or a stale index, so it cannot drift from
115
- the rules that actually ship.
125
+ Every rule's description, severity, category, recommended flag, and autofix support lives in the generated **[Rule index](https://synapsestudios.github.io/lintofu/rules/)** on the documentation site
116
126
 
117
127
  ## Requesting a rule
118
128
 
119
- To suggest a new rule, open a [rule request](https://github.com/synapsestudios/lintofu/issues/new?template=rule-request.yml). The form asks for a snippet the rule should flag, the compliant form, a category, a suggested severity, whether an autofix seems possible, and any references. A maintainer turns the request into a rule issue from there.
129
+ To suggest a new rule, open a [rule request](https://github.com/synapsestudios/lintofu/issues/new?template=rule-request.yml). The form asks for a snippet the rule should flag, the compliant form, a category, a suggested severity, whether an autofix seems possible, and any references. One of our maintainers will pick up your issue and approve it or reject it with a reason.
120
130
 
121
131
  ## Parser services
122
132
 
@@ -129,24 +139,14 @@ in this repository.
129
139
 
130
140
  ## Writing your own rule
131
141
 
132
- `lintofu` re-exports the rule-authoring types it uses internally, so a custom
133
- rule type-checks without reaching into private modules. The worked example is in
134
- the developer guide:
135
- [on the site](https://synapsestudios.github.io/lintofu/developer-guide/writing-a-rule)
136
- or [`docs/developer-guide/writing-a-rule.md`](./docs/developer-guide/writing-a-rule.md)
137
- in this repository.
142
+ `lintofu` re-exports the rule-authoring types it uses internally, so a custom rule type-checks without reaching into private modules. We have an example of this [on the site](https://synapsestudios.github.io/lintofu/developer-guide/writing-a-rule) or [`docs/developer-guide/writing-a-rule.md`](./docs/developer-guide/writing-a-rule.md) in this repository.
138
143
 
139
144
  ## Architecture
140
145
 
141
146
  The parser is Go: `github.com/hashicorp/hcl/v2/hclsyntax` compiled to
142
- WebAssembly. The full picture is in the developer guide:
143
- [on the site](https://synapsestudios.github.io/lintofu/developer-guide/architecture)
144
- or [`docs/developer-guide/architecture.md`](./docs/developer-guide/architecture.md)
145
- in this repository.
147
+ WebAssembly. We have a simple ASCII representation of the architecture here [on the site](https://synapsestudios.github.io/lintofu/developer-guide/architecture) or [`docs/developer-guide/architecture.md`](./docs/developer-guide/architecture.md) in this repository.
146
148
 
147
149
  ## Known limitations
148
150
 
149
- - **Experimental dependency on `syscall/js`.** See [ADR 0001](./docs/adr/0001-syscall-js-experimental.md). The project inherits its experimental status from Go's WASM JS interop package. Pinning a Go version in CI is the mitigation.
150
151
  - **`.tf.json` / `.tofu.json` not supported.** The JSON form of HCL is not 1:1 with native syntax; see [hashicorp/hcl#627 for the rationale](https://github.com/hashicorp/hcl/issues/627).
151
- - **No TFLint custom-rule compatibility.** An opinionated migration example may land later; a general compatibility layer is not planned for early releases.
152
- - **Performance is unprofiled.** Go+WASM boot costs ~100ms per ESLint process; per-file parse is sub-millisecond after boot.
152
+ - **No TFLint custom-rule compatibility.** We are _considering_ adding `tflint` support so that users can import rules, but we haven't made a decision on whether or not it should happen.
Binary file
@@ -2,7 +2,7 @@ const rule = {
2
2
  meta: {
3
3
  type: 'problem',
4
4
  docs: {
5
- description: 'Flag legacy `aws_security_group_rule` resources and inline `ingress`/`egress` blocks; prefer `aws_vpc_security_group_{ingress,egress}_rule`.',
5
+ description: 'Flag legacy `aws_security_group_rule` resources and inline `ingress`/`egress` blocks; prefer `aws_vpc_security_group_ingress_rule` and `aws_vpc_security_group_egress_rule`.',
6
6
  recommended: true,
7
7
  severity: 'error',
8
8
  category: 'aws',
@@ -1 +1 @@
1
- {"version":3,"file":"no-legacy-security-group-rules.js","sourceRoot":"","sources":["../../../src/rules/aws/no-legacy-security-group-rules.ts"],"names":[],"mappings":"AAEA,MAAM,IAAI,GAEL;IACH,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,8IAA8I;YAChJ,WAAW,EAAE,IAAI;YACjB,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,KAAK;SAChB;QACD,QAAQ,EAAE;YACR,uBAAuB,EACrB,qPAAqP;SACxP;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,KAAK,CAAC,IAAI;gBACR,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;oBAAE,OAAO;gBACrC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,yBAAyB,EAAE,CAAC;oBACjD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,yBAAyB,EAAE,CAAC,CAAC;oBAC/D,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,oBAAoB,EAAE,CAAC;oBAC5C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;wBACrC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;4BACxD,OAAO,CAAC,MAAM,CAAC;gCACb,IAAI,EAAE,KAAK;gCACX,SAAS,EAAE,yBAAyB;6BACrC,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"no-legacy-security-group-rules.js","sourceRoot":"","sources":["../../../src/rules/aws/no-legacy-security-group-rules.ts"],"names":[],"mappings":"AAEA,MAAM,IAAI,GAEL;IACH,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,8KAA8K;YAChL,WAAW,EAAE,IAAI;YACjB,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,KAAK;SAChB;QACD,QAAQ,EAAE;YACR,uBAAuB,EACrB,qPAAqP;SACxP;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,KAAK,CAAC,IAAI;gBACR,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;oBAAE,OAAO;gBACrC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,yBAAyB,EAAE,CAAC;oBACjD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,yBAAyB,EAAE,CAAC,CAAC;oBAC/D,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,oBAAoB,EAAE,CAAC;oBAC5C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;wBACrC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;4BACxD,OAAO,CAAC,MAAM,CAAC;gCACb,IAAI,EAAE,KAAK;gCACX,SAAS,EAAE,yBAAyB;6BACrC,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synapsestudios/lintofu",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "ESLint plugin for linting OpenTofu configurations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,23 +60,23 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "@eslint/js": "^10.0.1",
63
- "@types/node": "^26.6.3",
64
- "eslint": "^10.11.0",
63
+ "@types/node": "^26.6.6",
64
+ "eslint": "^10.14.0",
65
65
  "eslint-config-prettier": "^10.1.8",
66
66
  "eslint-plugin-eslint-plugin": "^7.6.2",
67
- "eslint-plugin-jsdoc": "^64.5.4",
68
- "eslint-plugin-prettier": "^5.5.6",
69
- "eslint-plugin-security": "^4.0.1",
70
- "eslint-plugin-tsdoc": "^0.5.3",
71
- "globals": "^17.12.0",
72
- "lefthook": "^2.1.14",
73
- "prettier": "^3.9.9",
67
+ "eslint-plugin-jsdoc": "^65.4.2",
68
+ "eslint-plugin-prettier": "^5.5.7",
69
+ "eslint-plugin-security": "^4.2.1",
70
+ "eslint-plugin-tsdoc": "^0.5.4",
71
+ "globals": "^17.13.0",
72
+ "lefthook": "^2.2.2",
73
+ "prettier": "^3.9.10",
74
74
  "prettier-plugin-organize-imports": "^4.3.0",
75
75
  "tsx": "^4.23.15",
76
76
  "typescript": "^6.0.3",
77
- "typescript-eslint": "^8.70.1",
77
+ "typescript-eslint": "^8.72.1",
78
78
  "vitepress": "^1.6.4",
79
- "vitest": "^5.0.2"
79
+ "vitest": "^5.0.5"
80
80
  },
81
81
  "keywords": [
82
82
  "eslint",