@sinemacula/coding-standards 1.17.0 → 1.19.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.
Files changed (2) hide show
  1. package/README.md +57 -44
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [![Latest Stable Version](https://img.shields.io/packagist/v/sinemacula/coding-standards.svg)](https://packagist.org/packages/sinemacula/coding-standards)
4
4
  [![npm Version](https://img.shields.io/npm/v/@sinemacula/coding-standards.svg)](https://www.npmjs.com/package/@sinemacula/coding-standards)
5
+ [![Build Status](https://github.com/sinemacula/coding-standards/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/sinemacula/coding-standards/actions/workflows/tests.yml)
6
+ [![Quality Gates](https://github.com/sinemacula/coding-standards/actions/workflows/quality-gates.yml/badge.svg?branch=master)](https://github.com/sinemacula/coding-standards/actions/workflows/quality-gates.yml)
5
7
  [![Maintainability](https://qlty.sh/gh/sinemacula/projects/coding-standards/maintainability.svg)](https://qlty.sh/gh/sinemacula/projects/coding-standards)
6
8
  [![Code Coverage](https://qlty.sh/gh/sinemacula/projects/coding-standards/coverage.svg)](https://qlty.sh/gh/sinemacula/projects/coding-standards)
7
9
  [![Total Downloads](https://img.shields.io/packagist/dt/sinemacula/coding-standards.svg)](https://packagist.org/packages/sinemacula/coding-standards)
@@ -86,8 +88,8 @@ parameters:
86
88
 
87
89
  For Laravel projects, also install
88
90
  [`sinemacula/coding-standards-laravel`](https://github.com/sinemacula/coding-standards-laravel) and reference its
89
- `SineMaculaLaravel` PHPCS standard (which includes this one) in place of `SineMacula`. It adds the
90
- Laravel-specific sniffs and PHPStan rules; see that package's README for setup.
91
+ `SineMaculaLaravel` PHPCS standard (which includes this one) in place of `SineMacula`. It adds the Laravel-specific
92
+ sniffs and PHPStan rules; see that package's README for setup.
91
93
 
92
94
  ### Biome (JavaScript / TypeScript)
93
95
 
@@ -112,9 +114,8 @@ After installing the npm package, extend the shared Biome config from your proje
112
114
  }
113
115
  ```
114
116
 
115
- `extends` paths are resolved through normal Node module lookup, so the package only needs to be installed (no path
116
- math against `node_modules/` required). Project-specific `files.includes` and `files.excludes` stay in the consumer
117
- config.
117
+ `extends` paths are resolved through normal Node module lookup, so the package only needs to be installed (no path math
118
+ against `node_modules/` required). Project-specific `files.includes` and `files.excludes` stay in the consumer config.
118
119
 
119
120
  ### ESLint (JavaScript / TypeScript)
120
121
 
@@ -134,9 +135,9 @@ The package exposes three flat-config entry points:
134
135
  - `@sinemacula/coding-standards/js/eslint/type-checked` - the opt-in type-aware layer. It includes the base layer and
135
136
  adds the cross-file / type-driven rules, so it needs a consumer `tsconfig`; use it in place of the base layer where
136
137
  one exists.
137
- - `@sinemacula/coding-standards/js/eslint/vue` - the opt-in Vue layer for single-file components. Unlike the
138
- type-aware layer it carries no base rules of its own, so spread it *alongside* whichever layer the repository
139
- already uses rather than in place of one.
138
+ - `@sinemacula/coding-standards/js/eslint/vue` - the opt-in Vue layer for single-file components. Unlike the type-aware
139
+ layer it carries no base rules of its own, so spread it *alongside* whichever layer the repository already uses rather
140
+ than in place of one.
140
141
 
141
142
  Create an `eslint.config.js` (or `.qlty/configs/eslint.config.js` when wired through Qlty) that spreads the layer you
142
143
  want. Without a `tsconfig`, use the base layer:
@@ -169,23 +170,23 @@ export default [...typeChecked, ...vue];
169
170
  ```
170
171
 
171
172
  The Vue layer registers the single-file-component parser (without it `.vue` files are not linted at all), resolves
172
- `<script lang="ts">` blocks through the TypeScript parser, and holds component filenames to kebab-case. It also
173
- carries the template layout rules, which is the one place ESLint takes on formatting: Biome does not understand
174
- single-file components, so `.vue` markup would otherwise go unformatted entirely. Those rules are aligned to the
175
- shared four-space indent.
173
+ `<script lang="ts">` blocks through the TypeScript parser, and holds component filenames to kebab-case. It also carries
174
+ the template layout rules, which is the one place ESLint takes on formatting: Biome does not understand single-file
175
+ components, so `.vue` markup would otherwise go unformatted entirely. Those rules are aligned to the shared four-space
176
+ indent.
176
177
 
177
178
  When wiring ESLint through Qlty, the shared eslint plugin sandbox installs only `eslint`, `jest`, and `prettier` by
178
- default, so the flat config's imports of this package and `typescript-eslint` fail to resolve. Widen the install
179
- filter in your `.qlty/qlty.toml` so the sandbox carries them (this repository's `source.toml` exports the same
180
- override, but source-exported plugin definitions do not reliably propagate, so mirror it consumer-side):
179
+ default, so the flat config's imports of this package and `typescript-eslint` fail to resolve. Widen the install filter
180
+ in your `.qlty/qlty.toml` so the sandbox carries them (this repository's `source.toml` exports the same override, but
181
+ source-exported plugin definitions do not reliably propagate, so mirror it consumer-side):
181
182
 
182
183
  ```toml
183
184
  [plugins.definitions.eslint]
184
185
  package_filters = ["@sinemacula/coding-standards", "typescript-eslint", "@typescript-eslint", "eslint-plugin-jsdoc"]
185
186
  ```
186
187
 
187
- Repositories enabling the Vue layer widen the same filter further, since its plugins have to resolve inside that
188
- sandbox too:
188
+ Repositories enabling the Vue layer widen the same filter further, since its plugins have to resolve inside that sandbox
189
+ too:
189
190
 
190
191
  ```toml
191
192
  [plugins.definitions.eslint]
@@ -204,17 +205,25 @@ package (as above) and extend the base from your `tsconfig.json`:
204
205
  {
205
206
  "extends": "@sinemacula/coding-standards/js/tsconfig.base.json",
206
207
  "compilerOptions": {
207
- "lib": ["ES2023", "DOM", "DOM.Iterable"],
208
- "types": ["node"]
208
+ "lib": [
209
+ "ES2023",
210
+ "DOM",
211
+ "DOM.Iterable"
212
+ ],
213
+ "types": [
214
+ "node"
215
+ ]
209
216
  },
210
- "include": ["src"]
217
+ "include": [
218
+ "src"
219
+ ]
211
220
  }
212
221
  ```
213
222
 
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.
223
+ The base carries only the environment-independent options: the full strictness set and the module/resolution discipline.
224
+ Everything environment-specific stays in the consuming repo and layers on top - `lib` (DOM for the browser, none for a
225
+ Node service), `types`, Vue's `jsx`/`jsxImportSource`, `paths`, `noEmit`, and the `include`/`exclude` globs. The`target`
226
+ and `module` defaults suit bundler-built apps and libraries; a non-bundler project overrides them.
218
227
 
219
228
  The base sets `noPropertyAccessFromIndexSignature`, so a property that comes from an index signature is accessed with
220
229
  brackets (`config['key']`), not a dot. Biome cannot see types and so cannot tell that access apart from a normal one,
@@ -273,6 +282,7 @@ native directive - `// phpcs:ignore <code>` for a sniff, `@phpstan-ignore <ident
273
282
  |------------------------------------------------------------|-----------------------------------------------------------------------------|
274
283
  | `SineMacula.Attributes.DisallowToolingAttribute` | No IDE/tooling attributes (e.g. `JetBrains\PhpStorm`). |
275
284
  | `SineMacula.Classes.RequireFinalClass` | Concrete classes must be `final` or `abstract` (`@inheritable` opts out). |
285
+ | `SineMacula.Classes.RequireReadonlyClass` | A class with only `readonly` properties (no statics) must be `readonly`. |
276
286
  | `SineMacula.Classes.RequireReadonlyPublicProperty` | Public properties (declared or promoted) must be `readonly`. |
277
287
  | `SineMacula.Commenting.CommentLineLength` | Standalone comment prose wrapped to 80 chars; premature wraps also fixed. |
278
288
  | `SineMacula.Commenting.ConsistentEnumCaseComments` | Enum case docs are all-or-nothing within an enum. |
@@ -294,6 +304,7 @@ native directive - `// phpcs:ignore <code>` for a sniff, `@phpstan-ignore <ident
294
304
  | `SineMacula.NamingConventions.DisallowInterfacePrefix` | Interface names must not use the Hungarian `I` prefix. |
295
305
  | `SineMacula.NamingConventions.ValidEnumCaseName` | Enum cases must be `SCREAMING_SNAKE_CASE`. |
296
306
  | `SineMacula.NamingConventions.ValidGlobalFunctionName` | Global functions must be declared in `snake_case`. |
307
+ | `SineMacula.TypeHints.DisallowFullyQualifiedConstantType` | Constant types must be imported, not inline fully-qualified names. |
297
308
  | `SineMacula.TypeHints.RequireConstantType` | Class/interface/enum/trait constants must declare a native type. |
298
309
  | `SineMacula.WhiteSpace.PromotedConstructorSpacing` | Blank line above each promoted-constructor parameter. |
299
310
 
@@ -325,28 +336,28 @@ type-checked layer.
325
336
 
326
337
  `boolean-method-name` takes `additionalPrefixes`, `additionalPredicates` and `additionalCommandVerbs` (string arrays)
327
338
  to widen the accepted vocabulary from a consumer config. `max-methods-per-class` takes `max`, `no-base-error` takes
328
- `allow`, and `require-copyright` takes `tags` to adjust their defaults. `align-doc-tags` takes `tags` and `column`,
329
- the column counting from the `@`, so the default of 14 gives `@author` six spaces and `@copyright` three.
330
- Together `single-line-property-doc` and `multiline-function-doc` set a member's comment shape by its kind: data
331
- members (interface property signatures, enum members and data class fields) take one line, while methods, interface
332
- method signatures and class fields holding a function take several. A data comment is never required, only held to
333
- one line where present; a free function keeps the freedom of either shape.
339
+ `allow`, and `require-copyright` takes `tags` to adjust their defaults. `align-doc-tags` takes `tags` and `column`, the
340
+ column counting from the `@`, so the default of 14 gives `@author` six spaces and `@copyright` three. Together
341
+ `single-line-property-doc` and `multiline-function-doc` set a member's comment shape by its kind: data members
342
+ (interface property signatures, enum members and data class fields) take one line, while methods, interface method
343
+ signatures and class fields holding a function take several. A data comment is never required, only held to one line
344
+ where present; a free function keeps the freedom of either shape.
334
345
 
335
346
  `comment-line-wrap` takes `maxLength` (default 80) and is the syntax-only counterpart of the PHP
336
347
  `SineMacula.Commenting.CommentLineLength` sniff. It fills standalone `//` runs and multi-line docblock prose greedily,
337
348
  reporting an overflowing line and a prematurely wrapped line on their own footings and autofixing both. Markdown
338
349
  headings, docblock tag lines, machine-parsed tool directives (`eslint`, `biome-ignore`, `@ts-`, `Stryker`, `c8`/`v8`/
339
- `istanbul ignore`, `@vite-ignore` and the like), fenced code, an indented code or command block, a doc-tag whose
340
- value opens a multi-line bracketed type (an `array{...}` shape, a `<...>` generic or a `\Closure(...)` signature),
341
- tables, separators, a line whose overflow is a single unbreakable token such as a long name or URL, trailing comments
342
- after code and compact single-line docblocks are left untouched.
350
+ `istanbul ignore`, `@vite-ignore` and the like), fenced code, an indented code or command block, a doc-tag whose value
351
+ opens a multi-line bracketed type (an `array{...}` shape, a `<...>` generic or a `\Closure(...)` signature), tables,
352
+ separators, a line whose overflow is a single unbreakable token such as a long name or URL, trailing comments after code
353
+ and compact single-line docblocks are left untouched.
343
354
 
344
355
  The base layer also switches on a set of built-in rules: `@typescript-eslint/no-explicit-any`, `curly` (a brace on every
345
356
  control statement, as PSR-12 already requires on the PHP side), `max-lines-per-function` (50 lines, test code exempt)
346
- and `max-depth` (4), plus `eslint-plugin-jsdoc` rules that require a documentation comment
347
- on every declared function, method, class, interface member and class field, forbid types in `@param`/`@returns` (the
348
- tags themselves are welcome, types belong in the signature) and keep a blank line above every documentation block,
349
- single-line blocks included. The type-checked layer adds `@typescript-eslint/explicit-module-boundary-types` and
357
+ and `max-depth` (4), plus `eslint-plugin-jsdoc` rules that require a documentation comment on every declared function,
358
+ method, class, interface member and class field, forbid types in `@param`/`@returns` (the tags themselves are welcome,
359
+ types belong in the signature) and keep a blank line above every documentation block, single-line blocks included. The
360
+ type-checked layer adds `@typescript-eslint/explicit-module-boundary-types` and
350
361
  `@typescript-eslint/only-throw-error`.
351
362
 
352
363
  ## Requirements
@@ -357,12 +368,14 @@ single-line blocks included. The type-checked layer adds `@typescript-eslint/exp
357
368
  ## Testing
358
369
 
359
370
  ```bash
360
- composer test # PHPUnit suite for the custom sniffs and PHPStan rule
361
- composer test:coverage # suite with Clover coverage output
362
- composer analyse # PHPStan static analysis
363
- composer check # static analysis and lint via qlty
364
- composer format # format via qlty
365
- composer smells # duplication / complexity smells via qlty
371
+ composer test # PHPUnit suite for the custom sniffs and PHPStan rule
372
+ composer test:coverage # suite with Clover coverage output
373
+ composer test:mutation # Infection mutation gate (min MSI 90)
374
+ composer test:mutation:full # full mutation suite without thresholds
375
+ composer analyse # PHPStan static analysis
376
+ composer check # static analysis and lint via qlty
377
+ composer format # format via qlty
378
+ composer smells # duplication / complexity smells via qlty
366
379
  ```
367
380
 
368
381
  ## Changelog
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sinemacula/coding-standards",
3
- "version": "1.17.0",
3
+ "version": "1.19.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>",