@sinemacula/coding-standards 1.8.3 → 1.9.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,46 @@
1
+ import { createRule } from './lib.js';
2
+
3
+ /** The pattern a valid enum member name must match. */
4
+ const NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
5
+
6
+ /**
7
+ * Enum member naming rule.
8
+ *
9
+ * Ensures every enum member is declared in SCREAMING_SNAKE_CASE, matching the
10
+ * Sine Macula house convention for enum members. Only real `enum` declarations
11
+ * are checked; object-literal "as const" pseudo-enums are out of scope.
12
+ *
13
+ * @author Ben Carey <bdmc@sinemacula.co.uk>
14
+ * @copyright 2026 Sine Macula Limited
15
+ */
16
+ export default createRule({
17
+ name: 'valid-enum-member-name',
18
+ meta: {
19
+ type: 'suggestion',
20
+ docs: {
21
+ description: 'Require enum members to be declared in SCREAMING_SNAKE_CASE.',
22
+ },
23
+ schema: [],
24
+ messages: {
25
+ notUpperSnakeCase: 'Enum member "{{ name }}" must be declared in SCREAMING_SNAKE_CASE.',
26
+ },
27
+ },
28
+ defaultOptions: [],
29
+ create(context) {
30
+ // Only enum members are visited; a switch `case` is a SwitchCase node
31
+ // and never reaches this visitor.
32
+ return {
33
+ TSEnumMember(node) {
34
+ const id = node.id;
35
+ // A member name is either a bare identifier or a string literal;
36
+ // a string literal is checked by its resolved value, not the raw
37
+ // source text.
38
+ const name = id.type === 'Identifier' ? id.name : String(id.value);
39
+
40
+ if (!NAME_PATTERN.test(name)) {
41
+ context.report({ node: id, messageId: 'notUpperSnakeCase', data: { name } });
42
+ }
43
+ },
44
+ };
45
+ },
46
+ });
@@ -0,0 +1,41 @@
1
+ import tseslint from 'typescript-eslint';
2
+ import base from './index.js';
3
+ import plugin from './plugin.js';
4
+
5
+ /**
6
+ * Opt-in type-aware layer: the cross-file and type-driven rules. Spreads the
7
+ * base config, then turns on type information via the project service so the
8
+ * type-aware custom rules and the curated typescript-eslint rules can resolve.
9
+ * Layered on only where a consumer tsconfig exists, keeping the base config the
10
+ * cheap fast path.
11
+ *
12
+ * @author Ben Carey <bdmc@sinemacula.co.uk>
13
+ * @copyright 2026 Sine Macula Limited
14
+ */
15
+ export default [
16
+ ...base,
17
+ {
18
+ files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'],
19
+ plugins: {
20
+ '@sinemacula': plugin,
21
+ '@typescript-eslint': tseslint.plugin,
22
+ },
23
+ languageOptions: {
24
+ parser: tseslint.parser,
25
+ parserOptions: {
26
+ projectService: true,
27
+ },
28
+ },
29
+ rules: {
30
+ '@sinemacula/boolean-method-name': 'error',
31
+
32
+ '@typescript-eslint/await-thenable': 'error',
33
+ '@typescript-eslint/consistent-type-imports': 'error',
34
+ '@typescript-eslint/explicit-module-boundary-types': 'error',
35
+ '@typescript-eslint/no-floating-promises': 'error',
36
+ '@typescript-eslint/no-misused-promises': 'error',
37
+ '@typescript-eslint/only-throw-error': 'error',
38
+ // '@typescript-eslint/strict-boolean-expressions': 'error',
39
+ },
40
+ },
41
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sinemacula/coding-standards",
3
- "version": "1.8.3",
3
+ "version": "1.9.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>",
@@ -15,10 +15,12 @@
15
15
  "keywords": [
16
16
  "biome",
17
17
  "knip",
18
+ "eslint",
18
19
  "coding-standards",
19
20
  "linting",
20
21
  "config"
21
22
  ],
23
+ "type": "module",
22
24
  "files": [
23
25
  "js/",
24
26
  "markdown/",
@@ -27,8 +29,47 @@
27
29
  "security/",
28
30
  "README.md",
29
31
  "LICENSE",
30
- "NOTICE"
32
+ "NOTICE",
33
+ "!js/eslint/rules/__tests__"
31
34
  ],
35
+ "exports": {
36
+ "./js/eslint": "./js/eslint/index.js",
37
+ "./js/eslint/type-checked": "./js/eslint/type-checked.js",
38
+ "./*": "./*"
39
+ },
40
+ "scripts": {
41
+ "test:js": "vitest run",
42
+ "lint:js": "eslint js/eslint"
43
+ },
44
+ "devDependencies": {
45
+ "@typescript-eslint/rule-tester": "^8.0.0",
46
+ "@typescript-eslint/utils": "^8.0.0",
47
+ "eslint": "^9.0.0",
48
+ "eslint-plugin-jsdoc": "^63.0.13",
49
+ "typescript": "^5.0.0",
50
+ "typescript-eslint": "^8.0.0",
51
+ "vitest": "^3.0.0"
52
+ },
53
+ "peerDependencies": {
54
+ "eslint": ">=9",
55
+ "eslint-plugin-jsdoc": ">=48",
56
+ "typescript": ">=4.8.4",
57
+ "typescript-eslint": "^8"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "eslint": {
61
+ "optional": true
62
+ },
63
+ "eslint-plugin-jsdoc": {
64
+ "optional": true
65
+ },
66
+ "typescript": {
67
+ "optional": true
68
+ },
69
+ "typescript-eslint": {
70
+ "optional": true
71
+ }
72
+ },
32
73
  "publishConfig": {
33
74
  "access": "public",
34
75
  "registry": "https://registry.npmjs.org/"