@sinemacula/coding-standards 1.12.2 → 1.13.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 CHANGED
@@ -98,10 +98,16 @@ After installing the npm package, extend the shared Biome config from your proje
98
98
  {
99
99
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
100
100
  "root": true,
101
- "extends": ["@sinemacula/coding-standards/js/biome.json"],
101
+ "extends": [
102
+ "@sinemacula/coding-standards/js/biome.json"
103
+ ],
102
104
  "files": {
103
105
  "ignoreUnknown": true,
104
- "includes": ["**", "!**/node_modules/**", "!**/vendor/**"]
106
+ "includes": [
107
+ "**",
108
+ "!**/node_modules/**",
109
+ "!**/vendor/**"
110
+ ]
105
111
  }
106
112
  }
107
113
  ```
@@ -189,12 +195,40 @@ package_filters = [
189
195
  ]
190
196
  ```
191
197
 
198
+ ### TypeScript (tsconfig)
199
+
200
+ The package ships a shared `tsconfig` base so every TypeScript repository checks its code to the same bar. Install the
201
+ package (as above) and extend the base from your `tsconfig.json`:
202
+
203
+ ```json
204
+ {
205
+ "extends": "@sinemacula/coding-standards/js/tsconfig.base.json",
206
+ "compilerOptions": {
207
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
208
+ "types": ["node"]
209
+ },
210
+ "include": ["src"]
211
+ }
212
+ ```
213
+
214
+ The base carries only the environment-independent options: the full strictness set and the module/resolution
215
+ discipline. Everything environment-specific stays in the consuming repo and layers on top - `lib` (DOM for the browser,
216
+ none for a Node service), `types`, Vue's `jsx`/`jsxImportSource`, `paths`, `noEmit`, and the `include`/`exclude` globs.
217
+ The `target` and `module` defaults suit bundler-built apps and libraries; a non-bundler project overrides them.
218
+
219
+ The base sets `noPropertyAccessFromIndexSignature`, so a property that comes from an index signature is accessed with
220
+ brackets (`config['key']`), not a dot. Biome cannot see types and so cannot tell that access apart from a normal one,
221
+ which is why its `useLiteralKeys` rule is off and the type-aware `@typescript-eslint/dot-notation` rule enforces dot
222
+ access for real properties instead. Load the type-checked ESLint layer to get it.
223
+
192
224
  ### Knip (JavaScript / TypeScript)
193
225
 
194
226
  ```json
195
227
  {
196
228
  "$schema": "https://unpkg.com/knip@6/schema.json",
197
- "extends": ["@sinemacula/coding-standards/js/knip.json"]
229
+ "extends": [
230
+ "@sinemacula/coding-standards/js/knip.json"
231
+ ]
198
232
  }
199
233
  ```
200
234
 
@@ -212,20 +246,20 @@ tag = "<version>"
212
246
 
213
247
  ## What's Included
214
248
 
215
- | Path | Tool | Description |
216
- |-------------------------------|--------------|--------------------------------------------------------|
217
- | `src/PhpCsFixerConfig.php` | PHP CS Fixer | Factory class for building PHP CS Fixer configurations |
218
- | `php/.php-cs-fixer.rules.php` | PHP CS Fixer | Shared rules array (PSR-12 base + org conventions) |
219
- | `SineMacula/ruleset.xml` | PHPCS | Auto-discovered coding standard (PSR-12 + exclusions) |
220
- | `php/phpstan-base.neon` | PHPStan | Base config (org-wide ignored errors + settings) |
221
- | `js/biome.json` | Biome | JavaScript / TypeScript formatter + linter rules |
222
- | `js/knip.json` | Knip | Unused-export detection rules |
223
- | `js/eslint/` | ESLint | Structural, type-aware + Vue rules; runs with Biome |
224
- | `markdown/.markdownlint.json` | markdownlint | Markdown linting rules |
225
- | `yaml/.yamllint.yaml` | yamllint | YAML linting rules |
226
- | `shell/.shellcheckrc` | ShellCheck | Shell script linting rules |
227
- | `security/.gitleaks.toml` | Gitleaks | Secret-detection ruleset |
228
- | `editorconfig/.editorconfig-checker.json` | editorconfig-checker | Disables only the max-line-length check |
249
+ | Path | Tool | Description |
250
+ |-------------------------------------------|----------------------|--------------------------------------------------------|
251
+ | `src/PhpCsFixerConfig.php` | PHP CS Fixer | Factory class for building PHP CS Fixer configurations |
252
+ | `php/.php-cs-fixer.rules.php` | PHP CS Fixer | Shared rules array (PSR-12 base + org conventions) |
253
+ | `SineMacula/ruleset.xml` | PHPCS | Auto-discovered coding standard (PSR-12 + exclusions) |
254
+ | `php/phpstan-base.neon` | PHPStan | Base config (org-wide ignored errors + settings) |
255
+ | `js/biome.json` | Biome | JavaScript / TypeScript formatter + linter rules |
256
+ | `js/knip.json` | Knip | Unused-export detection rules |
257
+ | `js/eslint/` | ESLint | Structural, type-aware + Vue rules; runs with Biome |
258
+ | `markdown/.markdownlint.json` | markdownlint | Markdown linting rules |
259
+ | `yaml/.yamllint.yaml` | yamllint | YAML linting rules |
260
+ | `shell/.shellcheckrc` | ShellCheck | Shell script linting rules |
261
+ | `security/.gitleaks.toml` | Gitleaks | Secret-detection ruleset |
262
+ | `editorconfig/.editorconfig-checker.json` | editorconfig-checker | Disables only the max-line-length check |
229
263
 
230
264
  ## Rules
231
265
 
@@ -235,38 +269,38 @@ native directive - `// phpcs:ignore <code>` for a sniff, `@phpstan-ignore <ident
235
269
 
236
270
  ### PHPCS sniffs
237
271
 
238
- | Sniff | Enforces |
239
- |-------|----------|
240
- | `SineMacula.Attributes.DisallowToolingAttribute` | No IDE/tooling attributes (e.g. `JetBrains\PhpStorm`). |
241
- | `SineMacula.Classes.RequireFinalClass` | Concrete classes must be `final` or `abstract` (`@inheritable` opts out). |
242
- | `SineMacula.Classes.RequireReadonlyPublicProperty` | Public properties (declared or promoted) must be `readonly`. |
243
- | `SineMacula.Commenting.CommentLineLength` | Standalone comment lines must not exceed 80 chars (FQCN/URL exempt). |
244
- | `SineMacula.Commenting.ConsistentEnumCaseComments` | Enum case docs are all-or-nothing within an enum. |
245
- | `SineMacula.Commenting.MultilineMethodComment` | A method's doc comment must span multiple lines. |
246
- | `SineMacula.Commenting.RequireConstantComment` | Every class/interface/enum/trait constant needs a doc comment. |
247
- | `SineMacula.Commenting.RequireCopyrightTag` | Class/interface/enum/trait docblocks must carry an `@copyright` tag. |
248
- | `SineMacula.Commenting.RequireNonPromotedParameterComment` | Plain params mixed with promoted ones need a comment. |
249
- | `SineMacula.Commenting.RequirePromotedPropertyComment` | Every constructor-promoted property needs a doc comment. |
250
- | `SineMacula.Commenting.SingleLineMemberComment` | A property, constant or enum-case doc comment sits on one line. |
251
- | `SineMacula.Exceptions.DisallowBaseException` | No throwing the base `\Exception`; throw a domain exception. |
252
- | `SineMacula.Exceptions.RequireEmptyCatchComment` | An empty catch block must comment its intentional swallow. |
253
- | `SineMacula.Functions.RequireSensitiveParameter` | Secret-named params need `#[\SensitiveParameter]`. |
254
- | `SineMacula.Metrics.MaxMethodCount` | A class/interface/trait/enum may declare at most 20 methods (tests exempt). |
255
- | `SineMacula.Metrics.MethodLength` | A method body may have at most 50 significant lines (tests exempt). |
256
- | `SineMacula.Namespaces.RequireConcernsNamespace` | Traits must live under a `Concerns` namespace segment. |
257
- | `SineMacula.Namespaces.RequireContractsNamespace` | Interfaces must live under a `Contracts` namespace segment. |
258
- | `SineMacula.Namespaces.RequireEnumsNamespace` | Enums must live under an `Enums` namespace segment. |
259
- | `SineMacula.NamingConventions.BooleanMethodName` | `bool` methods are predicates; command verbs/@imperative exempt. |
260
- | `SineMacula.NamingConventions.DisallowInterfacePrefix` | Interface names must not use the Hungarian `I` prefix. |
261
- | `SineMacula.NamingConventions.ValidEnumCaseName` | Enum cases must be `SCREAMING_SNAKE_CASE`. |
262
- | `SineMacula.NamingConventions.ValidGlobalFunctionName` | Global functions must be declared in `snake_case`. |
263
- | `SineMacula.TypeHints.RequireConstantType` | Class/interface/enum/trait constants must declare a native type. |
264
- | `SineMacula.WhiteSpace.PromotedConstructorSpacing` | Blank line above each promoted-constructor parameter. |
272
+ | Sniff | Enforces |
273
+ |------------------------------------------------------------|-----------------------------------------------------------------------------|
274
+ | `SineMacula.Attributes.DisallowToolingAttribute` | No IDE/tooling attributes (e.g. `JetBrains\PhpStorm`). |
275
+ | `SineMacula.Classes.RequireFinalClass` | Concrete classes must be `final` or `abstract` (`@inheritable` opts out). |
276
+ | `SineMacula.Classes.RequireReadonlyPublicProperty` | Public properties (declared or promoted) must be `readonly`. |
277
+ | `SineMacula.Commenting.CommentLineLength` | Standalone comment lines must not exceed 80 chars (FQCN/URL exempt). |
278
+ | `SineMacula.Commenting.ConsistentEnumCaseComments` | Enum case docs are all-or-nothing within an enum. |
279
+ | `SineMacula.Commenting.MultilineMethodComment` | A method's doc comment must span multiple lines. |
280
+ | `SineMacula.Commenting.RequireConstantComment` | Every class/interface/enum/trait constant needs a doc comment. |
281
+ | `SineMacula.Commenting.RequireCopyrightTag` | Class/interface/enum/trait docblocks must carry an `@copyright` tag. |
282
+ | `SineMacula.Commenting.RequireNonPromotedParameterComment` | Plain params mixed with promoted ones need a comment. |
283
+ | `SineMacula.Commenting.RequirePromotedPropertyComment` | Every constructor-promoted property needs a doc comment. |
284
+ | `SineMacula.Commenting.SingleLineMemberComment` | A property, constant or enum-case doc comment sits on one line. |
285
+ | `SineMacula.Exceptions.DisallowBaseException` | No throwing the base `\Exception`; throw a domain exception. |
286
+ | `SineMacula.Exceptions.RequireEmptyCatchComment` | An empty catch block must comment its intentional swallow. |
287
+ | `SineMacula.Functions.RequireSensitiveParameter` | Secret-named params need `#[\SensitiveParameter]`. |
288
+ | `SineMacula.Metrics.MaxMethodCount` | A class/interface/trait/enum may declare at most 20 methods (tests exempt). |
289
+ | `SineMacula.Metrics.MethodLength` | A method body may have at most 50 significant lines (tests exempt). |
290
+ | `SineMacula.Namespaces.RequireConcernsNamespace` | Traits must live under a `Concerns` namespace segment. |
291
+ | `SineMacula.Namespaces.RequireContractsNamespace` | Interfaces must live under a `Contracts` namespace segment. |
292
+ | `SineMacula.Namespaces.RequireEnumsNamespace` | Enums must live under an `Enums` namespace segment. |
293
+ | `SineMacula.NamingConventions.BooleanMethodName` | `bool` methods are predicates; command verbs/@imperative exempt. |
294
+ | `SineMacula.NamingConventions.DisallowInterfacePrefix` | Interface names must not use the Hungarian `I` prefix. |
295
+ | `SineMacula.NamingConventions.ValidEnumCaseName` | Enum cases must be `SCREAMING_SNAKE_CASE`. |
296
+ | `SineMacula.NamingConventions.ValidGlobalFunctionName` | Global functions must be declared in `snake_case`. |
297
+ | `SineMacula.TypeHints.RequireConstantType` | Class/interface/enum/trait constants must declare a native type. |
298
+ | `SineMacula.WhiteSpace.PromotedConstructorSpacing` | Blank line above each promoted-constructor parameter. |
265
299
 
266
300
  ### PHPStan rules
267
301
 
268
- | Identifier | Enforces |
269
- |------------|----------|
302
+ | Identifier | Enforces |
303
+ |------------------------------------|-------------------------------------------------------------------|
270
304
  | `sineMacula.mutableStaticProperty` | Static properties written at runtime; `@managed-static` opts out. |
271
305
 
272
306
  ### ESLint rules
@@ -274,19 +308,19 @@ native directive - `// phpcs:ignore <code>` for a sniff, `@phpstan-ignore <ident
274
308
  All rules run in the base layer except `boolean-method-name`, which resolves return types and so requires the opt-in
275
309
  type-checked layer.
276
310
 
277
- | Rule | Enforces |
278
- |------|----------|
279
- | `@sinemacula/no-interface-prefix` | Interface and type-alias names must not use the Hungarian `I` prefix. |
280
- | `@sinemacula/require-readonly-public-property` | Public class properties (declared or promoted) must be `readonly`. |
281
- | `@sinemacula/valid-enum-member-name` | Enum members must be declared in `SCREAMING_SNAKE_CASE`. |
282
- | `@sinemacula/boolean-method-name` | Boolean-returning methods need an is/has/can prefix; `@imperative` exempt. |
283
- | `@sinemacula/no-mutable-static` | No mutable exported bindings or mutable `static` class fields; test code exempt. |
284
- | `@sinemacula/max-methods-per-class` | A single class may declare at most 20 methods; test code exempt. |
285
- | `@sinemacula/no-base-error` | Throw a domain-specific `Error` subclass, never the base `Error`; test code exempt. |
286
- | `@sinemacula/require-copyright` | Every file must carry a documentation comment with `@copyright` and `@author`. |
287
- | `@sinemacula/align-doc-tags` | `@author` and `@copyright` values line up at a single column; autofixable. |
288
- | `@sinemacula/single-line-property-doc` | A data member's documentation comment sits on one line; autofixable. |
289
- | `@sinemacula/multiline-function-doc` | A method's documentation comment spans multiple lines; autofixable. |
311
+ | Rule | Enforces |
312
+ |------------------------------------------------|-------------------------------------------------------------------------------------|
313
+ | `@sinemacula/no-interface-prefix` | Interface and type-alias names must not use the Hungarian `I` prefix. |
314
+ | `@sinemacula/require-readonly-public-property` | Public class properties (declared or promoted) must be `readonly`. |
315
+ | `@sinemacula/valid-enum-member-name` | Enum members must be declared in `SCREAMING_SNAKE_CASE`. |
316
+ | `@sinemacula/boolean-method-name` | Boolean-returning methods need an is/has/can prefix; `@imperative` exempt. |
317
+ | `@sinemacula/no-mutable-static` | No mutable exported bindings or mutable `static` class fields; test code exempt. |
318
+ | `@sinemacula/max-methods-per-class` | A single class may declare at most 20 methods; test code exempt. |
319
+ | `@sinemacula/no-base-error` | Throw a domain-specific `Error` subclass, never the base `Error`; test code exempt. |
320
+ | `@sinemacula/require-copyright` | Every file must carry a documentation comment with `@copyright` and `@author`. |
321
+ | `@sinemacula/align-doc-tags` | `@author` and `@copyright` values line up at a single column; autofixable. |
322
+ | `@sinemacula/single-line-property-doc` | A data member's documentation comment sits on one line; autofixable. |
323
+ | `@sinemacula/multiline-function-doc` | A method's documentation comment spans multiple lines; autofixable. |
290
324
 
291
325
  `boolean-method-name` takes `additionalPrefixes`, `additionalPredicates` and `additionalCommandVerbs` (string arrays)
292
326
  to widen the accepted vocabulary from a consumer config. `max-methods-per-class` takes `max`, `no-base-error` takes
package/js/biome.json CHANGED
@@ -38,7 +38,7 @@
38
38
  "noUselessTernary": "warn",
39
39
  "noUselessUndefinedInitialization": "warn",
40
40
  "useArrowFunction": "warn",
41
- "useLiteralKeys": "warn",
41
+ "useLiteralKeys": "off",
42
42
  "useNumericLiterals": "warn"
43
43
  },
44
44
  "correctness": {
@@ -31,6 +31,9 @@ export default [
31
31
 
32
32
  '@typescript-eslint/await-thenable': 'error',
33
33
  '@typescript-eslint/consistent-type-imports': 'error',
34
+ // Type-aware replacement for Biome's useLiteralKeys: dot access for
35
+ // real properties, bracket access allowed on index signatures.
36
+ '@typescript-eslint/dot-notation': ['error', { allowIndexSignaturePropertyAccess: true }],
34
37
  '@typescript-eslint/explicit-module-boundary-types': 'error',
35
38
  '@typescript-eslint/no-floating-promises': 'error',
36
39
  '@typescript-eslint/no-misused-promises': 'error',
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "@sinemacula/coding-standards base",
4
+ "compilerOptions": {
5
+ "target": "ES2023",
6
+ "module": "Preserve",
7
+ "moduleResolution": "Bundler",
8
+ "moduleDetection": "force",
9
+ "verbatimModuleSyntax": true,
10
+ "isolatedModules": true,
11
+ "resolveJsonModule": true,
12
+ "esModuleInterop": true,
13
+ "strict": true,
14
+ "noUncheckedIndexedAccess": true,
15
+ "noPropertyAccessFromIndexSignature": true,
16
+ "exactOptionalPropertyTypes": true,
17
+ "noImplicitOverride": true,
18
+ "noFallthroughCasesInSwitch": true,
19
+ "noImplicitReturns": true,
20
+ "allowUnreachableCode": false,
21
+ "noUnusedLocals": true,
22
+ "noUnusedParameters": true,
23
+ "noUncheckedSideEffectImports": true,
24
+ "forceConsistentCasingInFileNames": true,
25
+ "skipLibCheck": true
26
+ }
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sinemacula/coding-standards",
3
- "version": "1.12.2",
3
+ "version": "1.13.0",
4
4
  "description": "Centralized coding standards, static analysis configurations, and code quality tooling for all Sine Macula repositories.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Ben Carey <bdmc@sinemacula.co.uk>",
@@ -16,6 +16,7 @@
16
16
  "biome",
17
17
  "knip",
18
18
  "eslint",
19
+ "typescript",
19
20
  "coding-standards",
20
21
  "linting",
21
22
  "config"
@@ -36,6 +37,7 @@
36
37
  "./js/eslint": "./js/eslint/index.js",
37
38
  "./js/eslint/type-checked": "./js/eslint/type-checked.js",
38
39
  "./js/eslint/vue": "./js/eslint/vue.js",
40
+ "./js/tsconfig.base.json": "./js/tsconfig.base.json",
39
41
  "./*": "./*"
40
42
  },
41
43
  "scripts": {