@hs-web-team/eslint-config-node 4.0.2 → 4.2.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.
@@ -0,0 +1,28 @@
1
+ #!/bin/sh
2
+
3
+ commit_msg=$(head -1 "$1")
4
+
5
+ # Skip merge and revert commits (auto-generated messages)
6
+ case "$commit_msg" in
7
+ Merge\ *|Revert\ *) exit 0 ;;
8
+ esac
9
+
10
+ pattern="^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-zA-Z0-9_/-]+\))?!?: .+"
11
+
12
+ if ! echo "$commit_msg" | grep -qE "$pattern"; then
13
+ echo ""
14
+ echo "ERROR: Commit message does not follow Conventional Commits format."
15
+ echo ""
16
+ echo " Expected: type(optional-scope): description"
17
+ echo " Types: feat | fix | docs | style | refactor | test | chore | ci | perf | revert | build"
18
+ echo ""
19
+ echo " Examples:"
20
+ echo " feat: add new rule for import order"
21
+ echo " fix: correct comma-dangle config for browser"
22
+ echo " feat!: drop support for Node 18 (breaking change -> major bump)"
23
+ echo " feat(browser): add React 19 support"
24
+ echo ""
25
+ echo " See: https://www.conventionalcommits.org"
26
+ echo ""
27
+ exit 1
28
+ fi
@@ -0,0 +1,44 @@
1
+ name: Release Please
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: write
9
+ pull-requests: write
10
+ id-token: write # Required for OIDC npm publish
11
+
12
+ jobs:
13
+ release-please:
14
+ runs-on: ubuntu-latest
15
+ outputs:
16
+ release_created: ${{ steps.release.outputs.release_created }}
17
+ steps:
18
+ - uses: googleapis/release-please-action@v5
19
+ id: release
20
+ with:
21
+ token: ${{ secrets.GITHUB_TOKEN }}
22
+
23
+ publish:
24
+ needs: release-please
25
+ if: ${{ needs.release-please.outputs.release_created == 'true' }}
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v6
29
+ - uses: actions/setup-node@v6
30
+ with:
31
+ node-version: 24
32
+ registry-url: 'https://registry.npmjs.org'
33
+ - run: npm ci
34
+ - run: npm run build --if-present
35
+ - name: Publish to npm
36
+ run: |
37
+ VERSION=$(node -p "require('./package.json').version")
38
+ if [[ "$VERSION" =~ -next\. ]] || [[ "$VERSION" =~ -(alpha|beta|rc)\. ]]; then
39
+ echo "Publishing prerelease $VERSION with tag 'next'"
40
+ npm publish --tag next
41
+ else
42
+ echo "Publishing stable $VERSION"
43
+ npm publish
44
+ fi
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "4.2.1"
3
+ }
package/.stylelintrc.json CHANGED
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "extends": "stylelint-config-standard-scss",
3
+ "plugins": ["stylelint-declaration-strict-value"],
3
4
  "rules": {
5
+ "scale-unlimited/declaration-strict-value": [
6
+ ["/color/", "fill", "stroke", "font-size", "font-weight"],
7
+ {
8
+ "ignoreValues": ["inherit", "initial", "normal", "unset", "revert", "currentColor", "transparent"],
9
+ "message": "Expected a CSS custom property (var(--token)) or Sass variable ($var). For color opacity variants, use color-mix(in srgb, var(--token) 80%, transparent) instead of rgba().",
10
+ "severity": "warning"
11
+ }
12
+ ],
4
13
  "length-zero-no-unit": null,
5
14
  "declaration-empty-line-before": null,
6
15
  "no-empty-source": null,
@@ -11,7 +20,13 @@
11
20
  }
12
21
  ],
13
22
  "scss/dollar-variable-pattern": null,
14
- "selector-class-pattern": null,
23
+ "selector-class-pattern": [
24
+ "^-?[a-z0-9]([a-z0-9]|-(?!-))*$",
25
+ {
26
+ "message": "Class names should only contain lowercase alphanumeric characters and hyphens, with no consecutive hyphens",
27
+ "severity": "warning"
28
+ }
29
+ ],
15
30
  "declaration-block-no-redundant-longhand-properties": null,
16
31
  "alpha-value-notation": null,
17
32
  "scss/percent-placeholder-pattern": null,
@@ -33,6 +48,13 @@
33
48
  {
34
49
  "camelCaseSvgKeywords": true
35
50
  }
51
+ ],
52
+ "selector-disallowed-list": [
53
+ ["/(?:^|[\\s>+~])\\s*\\.-[a-z]/i"],
54
+ {
55
+ "message": "Modifier classes (starting with '-') must not be standalone selectors; always combine with a base class (e.g., .prefix-element.-modifier or &.-modifier in nested rules)",
56
+ "severity": "error"
57
+ }
36
58
  ]
37
59
  }
38
60
  }
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## [4.2.1](https://github.com/HubSpotWebTeam/wt-eslint-node/compare/v4.2.0...v4.2.1) (2026-06-05)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * re-trigger publish after release-please CI fix ([45fe949](https://github.com/HubSpotWebTeam/wt-eslint-node/commit/45fe94930c06d61e6f8f3015bc4e6518e187aaa1))
9
+
10
+ ## [4.2.0](https://github.com/HubSpotWebTeam/wt-eslint-node/compare/v4.1.0...v4.2.0) (2026-06-05)
11
+
12
+
13
+ ### Features
14
+
15
+ * warn on hardcoded color, font-size, and font-weight values ([#37](https://github.com/HubSpotWebTeam/wt-eslint-node/issues/37)) ([783a320](https://github.com/HubSpotWebTeam/wt-eslint-node/commit/783a320a3d0e858cd370670be452aaa847ba9c39))
package/README.md CHANGED
@@ -13,6 +13,7 @@ This package provides ESLint rules and configurations for **Hubspot Marketing We
13
13
  - [Accessibility Testing](#accessibility-testing-optional)
14
14
  - [Where to use it](#where-to-use-it)
15
15
  - [Using the Prettier Scripts](#using-the-prettier-scripts)
16
+ - [Contributing](#contributing)
16
17
  <!-- index-end -->
17
18
 
18
19
  ## Node.js Setup
@@ -199,3 +200,57 @@ See [MIGRATION-V2.md](./docs/MIGRATION-V2.md)
199
200
  ## Migration from v2 to v3
200
201
 
201
202
  See [MIGRATION-V3.md](./docs/MIGRATION-V3.md)
203
+
204
+ ## Contributing
205
+
206
+ ### Conventional Commits
207
+
208
+ This repo uses [Conventional Commits](https://www.conventionalcommits.org) for all commit messages. A `commit-msg` git hook validates the format automatically — run `npm install` once to activate it.
209
+
210
+ **Format:** `type(optional-scope): description`
211
+
212
+ | Type | Use for | Version bump |
213
+ |------|---------|-------------|
214
+ | `feat` | New feature or config rule | Minor (`4.0.0` → `4.1.0`) |
215
+ | `fix` | Bug fix or rule correction | Patch (`4.0.0` → `4.0.1`) |
216
+ | `feat!` or `BREAKING CHANGE:` footer | Breaking API/config change | Major (`4.0.0` → `5.0.0`) |
217
+ | `chore` | Dependency updates, tooling | No release |
218
+ | `docs` | Documentation only | No release |
219
+ | `refactor` | Internal restructuring | No release |
220
+ | `ci` | CI/CD changes | No release |
221
+ | `test` | Test changes | No release |
222
+
223
+ **Examples:**
224
+
225
+ ```
226
+ feat: add import/order rule to Node config
227
+ fix: align browser comma-dangle with prettier defaults
228
+ feat(browser): add React 19 peer dep support
229
+ feat!: drop Node 18 support — requires Node 24+
230
+ chore: bump typescript-eslint to v8.60
231
+ docs: update Cypress usage examples
232
+ ```
233
+
234
+ A breaking change can also be indicated by adding a `BREAKING CHANGE:` footer to any commit type:
235
+
236
+ ```
237
+ feat: remove deprecated stylelint rules
238
+
239
+ BREAKING CHANGE: Removed rules that were dropped in stylelint-scss@7.
240
+ Projects relying on those rules must update their configs before upgrading.
241
+ ```
242
+
243
+ ### Release Process
244
+
245
+ Releases are automated via [release-please](https://github.com/googleapis/release-please). No manual version bumping or GitHub release creation is needed.
246
+
247
+ **How it works:**
248
+
249
+ 1. Merge PRs to `main` using conventional commit messages (see above).
250
+ 2. After each merge, release-please opens or updates a **Release PR** (titled `chore(main): release X.Y.Z`) that accumulates all unreleased changes, bumps `package.json`, and generates a `CHANGELOG.md` entry.
251
+ 3. When you are ready to publish, **merge the Release PR**.
252
+ 4. release-please creates a GitHub release and git tag automatically.
253
+ 5. The `release.yml` workflow fires on the new release and publishes to npm.
254
+
255
+ **To release a prerelease (`-next.N`):**
256
+ Update the version in `package.json` manually on a branch (e.g. `4.1.0-next.0`) and create a GitHub release by hand — the existing `release.yml` workflow will publish it with the `next` tag on npm. Prereleases are not yet managed by release-please in this repo.
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const projectRoot = process.env.INIT_CWD || process.env.npm_config_local_prefix;
7
+
8
+ // Skip if we can't determine the consuming project's root, or if postinstall
9
+ // is running inside this package's own development environment.
10
+ if (!projectRoot || projectRoot === path.resolve(__dirname, '..')) {
11
+ process.exit(0);
12
+ }
13
+
14
+ const ownPkg = require('../package.json');
15
+
16
+ const stylelintPeers = Object.entries(ownPkg.peerDependencies || {}).filter(([name]) =>
17
+ name.startsWith('stylelint')
18
+ );
19
+
20
+ // Extracts the minimum required major version from a semver range like ^17.0.0 or >=17.1.1.
21
+ const minMajor = (range) => {
22
+ const match = range.match(/(\d+)/);
23
+ return match ? parseInt(match[1], 10) : 0;
24
+ };
25
+
26
+ const isInstalled = (pkg) =>
27
+ fs.existsSync(path.join(projectRoot, 'node_modules', pkg, 'package.json'));
28
+
29
+ // Only warn about missing stylelint plugins if stylelint itself is installed —
30
+ // consumers who don't use stylelint at all don't need any of these.
31
+ const stylelintInUse = isInstalled('stylelint');
32
+
33
+ const outdated = [];
34
+ const missing = [];
35
+
36
+ for (const [pkg, requiredRange] of stylelintPeers) {
37
+ const pkgJsonPath = path.join(projectRoot, 'node_modules', pkg, 'package.json');
38
+
39
+ if (!fs.existsSync(pkgJsonPath)) {
40
+ if (stylelintInUse && pkg !== 'stylelint') {
41
+ missing.push({ pkg, requiredRange });
42
+ }
43
+ continue;
44
+ }
45
+
46
+ const { version } = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
47
+ const installedMajor = parseInt(version.split('.')[0], 10);
48
+
49
+ if (installedMajor < minMajor(requiredRange)) {
50
+ outdated.push({ pkg, version, requiredRange });
51
+ }
52
+ }
53
+
54
+ if (outdated.length === 0 && missing.length === 0) process.exit(0);
55
+
56
+ const pkgName = ownPkg.name;
57
+
58
+ if (missing.length > 0) {
59
+ console.warn(`\n⚠️ ${pkgName}: missing stylelint peer dependencies.\n`);
60
+ console.warn(' These packages are required for stylelint to work correctly:');
61
+ for (const { pkg, requiredRange } of missing) {
62
+ console.warn(` • ${pkg} (required: ${requiredRange})`);
63
+ }
64
+ const installCmd = missing.map(({ pkg, requiredRange }) => `${pkg}@"${requiredRange}"`).join(' ');
65
+ console.warn(`\n Run: npm install --save-dev ${installCmd}`);
66
+ console.warn(' Linting will fail without these packages.\n');
67
+ }
68
+
69
+ if (outdated.length > 0) {
70
+ console.warn(`\n⚠️ ${pkgName}: outdated stylelint peer dependencies detected.\n`);
71
+ console.warn(' These packages are installed at versions that are no longer compatible:');
72
+ for (const { pkg, version, requiredRange } of outdated) {
73
+ console.warn(` • ${pkg}@${version} → required: ${requiredRange}`);
74
+ }
75
+ const upgradeCmd = outdated.map(({ pkg, requiredRange }) => `${pkg}@"${requiredRange}"`).join(' ');
76
+ console.warn(`\n Run: npm install ${upgradeCmd}`);
77
+ console.warn(' Failure to upgrade may cause unexpected lint errors.\n');
78
+ }
@@ -7,7 +7,7 @@ This package provides a shared Stylelint configuration for SCSS/CSS linting in H
7
7
  1. Install the package and stylelint dependencies:
8
8
 
9
9
  ```bash
10
- npm install --save-dev @hs-web-team/eslint-config-node stylelint stylelint-config-standard-scss
10
+ npm install --save-dev @hs-web-team/eslint-config-node stylelint stylelint-config-standard-scss stylelint-declaration-strict-value
11
11
  ```
12
12
 
13
13
  ## Usage
@@ -136,7 +136,7 @@ To override rules for your project, add them to your `.stylelintrc.json`:
136
136
  If you get a module not found error, ensure you have installed all dependencies:
137
137
 
138
138
  ```bash
139
- npm install --save-dev stylelint stylelint-config-standard-scss
139
+ npm install --save-dev stylelint stylelint-config-standard-scss stylelint-declaration-strict-value
140
140
  ```
141
141
 
142
142
  ### Conflicts with Prettier
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hs-web-team/eslint-config-node",
3
- "version": "4.0.2",
3
+ "version": "4.2.1",
4
4
  "description": "HubSpot Marketing WebTeam shared configurations for ESLint, Prettier, Stylelint, and Cypress",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -29,8 +29,10 @@
29
29
  }
30
30
  },
31
31
  "scripts": {
32
+ "postinstall": "node bin/check-peer-deps.cjs",
32
33
  "lint": "npx eslint -c ./index.js *.js --fix",
33
- "test": "echo \"Error: no test specified\" && exit 1"
34
+ "test": "echo \"Error: no test specified\" && exit 1",
35
+ "prepare": "git rev-parse --git-dir > /dev/null 2>&1 && git config core.hooksPath .githooks || true"
34
36
  },
35
37
  "engines": {
36
38
  "node": ">=24"
@@ -82,7 +84,8 @@
82
84
  "cypress": "^15.16.0",
83
85
  "stylelint": "^17.1.1",
84
86
  "stylelint-config-standard": "^40.0.0",
85
- "stylelint-config-standard-scss": "^17.0.0"
87
+ "stylelint-config-standard-scss": "^17.0.0",
88
+ "stylelint-declaration-strict-value": "^1.11.1"
86
89
  },
87
90
  "peerDependenciesMeta": {
88
91
  "stylelint": {
@@ -94,6 +97,9 @@
94
97
  "stylelint-config-standard-scss": {
95
98
  "optional": true
96
99
  },
100
+ "stylelint-declaration-strict-value": {
101
+ "optional": true
102
+ },
97
103
  "cypress": {
98
104
  "optional": true
99
105
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
3
+ "release-type": "node",
4
+ "include-component-in-tag": false,
5
+ "packages": {
6
+ ".": {}
7
+ }
8
+ }
@@ -1,34 +0,0 @@
1
- name: Publish Package to npmjs
2
- on:
3
- release:
4
- types: [created]
5
-
6
- permissions:
7
- id-token: write # Required for OIDC
8
- contents: read
9
-
10
- jobs:
11
- build:
12
- runs-on: ubuntu-latest
13
- steps:
14
- # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
15
- - uses: actions/checkout@v6
16
- - uses: actions/setup-node@v6
17
- with:
18
- node-version: 24
19
- registry-url: 'https://registry.npmjs.org'
20
- - run: npm ci
21
- - run: npm run build --if-present
22
- - name: Publish to npm
23
- run: |
24
- VERSION=$(node -p "require('./package.json').version")
25
- if [[ "$VERSION" =~ -next\. ]]; then
26
- echo "Publishing prerelease version $VERSION with tag 'next'"
27
- npm publish --tag next
28
- elif [[ "$VERSION" =~ -(alpha|beta|rc)\. ]]; then
29
- echo "Publishing prerelease version $VERSION with tag 'next'"
30
- npm publish --tag next
31
- else
32
- echo "Publishing stable version $VERSION"
33
- npm publish
34
- fi