@hs-web-team/eslint-config-node 3.1.2 → 3.2.0-next.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,40 @@
1
+ {
2
+ "extends": "stylelint-config-standard-scss",
3
+ "rules": {
4
+ "length-zero-no-unit": null,
5
+ "declaration-empty-line-before": null,
6
+ "no-empty-source": null,
7
+ "at-rule-no-unknown": [
8
+ true,
9
+ {
10
+ "ignoreAtRules": ["function", "if", "else", "for", "each", "include", "mixin", "extend"]
11
+ }
12
+ ],
13
+ "scss/dollar-variable-pattern": null,
14
+ "scss/at-import-no-partial-leading-underscore": null,
15
+ "selector-class-pattern": null,
16
+ "declaration-block-no-redundant-longhand-properties": null,
17
+ "scss/at-import-partial-extension": null,
18
+ "alpha-value-notation": null,
19
+ "scss/percent-placeholder-pattern": null,
20
+ "scss/at-mixin-argumentless-call-parentheses": null,
21
+ "color-function-notation": null,
22
+ "scss/dollar-variable-empty-line-before": null,
23
+ "shorthand-property-no-redundant-values": null,
24
+ "no-duplicate-selectors": null,
25
+ "scss/double-slash-comment-whitespace-inside": null,
26
+ "font-family-name-quotes": null,
27
+ "media-feature-range-notation": [
28
+ "context",
29
+ {
30
+ "severity": "warning"
31
+ }
32
+ ],
33
+ "value-keyword-case": [
34
+ "lower",
35
+ {
36
+ "camelCaseSvgKeywords": true
37
+ }
38
+ ]
39
+ }
40
+ }
package/CLAUDE.md CHANGED
@@ -4,7 +4,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Overview
6
6
 
7
- This is an ESLint configuration package (`@hs-web-team/eslint-config-node`) for HubSpot Marketing WebTeam projects. It provides shared ESLint rules and Prettier configuration for both backend Node.js projects and browser/React applications.
7
+ This is a configuration package (`@hs-web-team/eslint-config-node`) for HubSpot Marketing WebTeam projects. It provides shared configurations for:
8
+ - **ESLint** rules for Node.js and browser/React projects
9
+ - **Prettier** formatting configuration
10
+ - **Stylelint** rules for SCSS/CSS linting
11
+ - **Cypress** E2E testing configuration
8
12
 
9
13
  ## Key Commands
10
14
 
@@ -66,12 +70,48 @@ When testing changes to this package in downstream projects, you'll typically:
66
70
  - Arrow parens: avoid, LF line endings, single attribute per line
67
71
  - Override for YAML files: uses double quotes instead of single
68
72
 
73
+ ### Stylelint Configuration
74
+ - **`.stylelintrc.json`**: Shared Stylelint configuration for SCSS/CSS linting
75
+ - Extends `stylelint-config-standard-scss` for standard SCSS linting rules
76
+ - Customized rules for HubSpot projects:
77
+ - Disables strict class name patterns and variable naming patterns
78
+ - Allows zero units (e.g., `0px`), redundant longhand properties, and duplicate selectors
79
+ - Configures SCSS-specific rules to ignore common directives (`@function`, `@if`, `@mixin`, etc.)
80
+ - Enforces lowercase keywords with camelCase for SVG keywords
81
+ - Sets media feature range notation to "context" with warning severity
82
+ - Consuming projects extend this via `.stylelintrc.json`: `{ "extends": "@hs-web-team/eslint-config-node/.stylelintrc.json" }`
83
+ - Documentation: `examples/stylelint-usage.md`
84
+
85
+ ### Cypress Configuration
86
+ - **`cypress.config.js`**: Shared Cypress configuration for E2E testing
87
+ - Uses CommonJS (`require`) syntax
88
+ - Core settings:
89
+ - Disables Chrome web security for cross-origin testing
90
+ - Timeouts: 20s default command, page load, and response timeouts
91
+ - Viewport: 1920x1080
92
+ - Retries: 1 retry in run mode, 0 in open mode
93
+ - Screenshots on failure enabled, video disabled by default
94
+ - Uses **esbuild** preprocessor for fast bundling (much faster than webpack)
95
+ - Includes Cucumber preprocessor setup with native esbuild support
96
+ - Native TypeScript support via esbuild (no additional loader needed)
97
+ - Spec pattern: `cypress/e2e/*.cy.js`
98
+ - Exported utilities:
99
+ - `config`: Main Cypress configuration object
100
+ - `envs`: Environment constants (DEV, QA, PROD)
101
+ - `getDevBaseUrl()`: Reads baseUrl from `hubspot.config.yml` DEV portal
102
+ - `getBaseUrls()`: Reads URLs from `.ci/config.yml` for multiple environments
103
+ - HubSpot-specific: Designed to work with HubSpot CMS projects using `hubspot.config.yml`
104
+ - Documentation: `examples/cypress-usage.md`
105
+
69
106
  ### Package Details
70
107
  - **Type**: ESM module (`"type": "module"`)
71
108
  - **Node requirement**: >= 22
72
109
  - **Exports**:
73
- - Main export (`.`): `index.js` - Node.js configuration
74
- - Browser export (`./browser`): `browser.js` - Browser/React configuration
110
+ - Main export (`.`): `index.js` - Node.js ESLint configuration
111
+ - Browser export (`./browser`): `browser.js` - Browser/React ESLint configuration
112
+ - Prettier export (`./.prettierrc.json`): `.prettierrc.json` - Prettier configuration
113
+ - Stylelint export (`./.stylelintrc.json`): `.stylelintrc.json` - Stylelint configuration
114
+ - Cypress export (`./cypress.config`): `cypress.config.js` - Cypress configuration
75
115
  - **Binary command**: `add-prettier` maps to `bin/add-prettier-scripts.js`
76
116
 
77
117
  ### Migration Context
@@ -81,12 +121,24 @@ This package is currently on v3, which uses ESLint 9's flat config format. The p
81
121
 
82
122
  ## Important Notes
83
123
 
84
- - This package supports both **backend Node.js projects** (default export) and **browser/React projects** (`/browser` export)
85
- - The configuration uses ESLint 9's flat config format (not the legacy `.eslintrc` format)
86
- - Downstream projects extend this config in their `eslint.config.js` by spreading the imported config:
87
- - Node.js: `...wtConfig`
88
- - Browser: `...wtBrowserConfig`
89
- - Mixed module systems: `bin/add-prettier-scripts.js` uses CommonJS (`require`) while the main package is ESM
124
+ - This package supports multiple use cases:
125
+ - **ESLint**: Backend Node.js projects (default export) and browser/React projects (`/browser` export)
126
+ - **Prettier**: Code formatting (`./.prettierrc.json` export)
127
+ - **Stylelint**: SCSS/CSS linting (`./.stylelintrc.json` export)
128
+ - **Cypress**: E2E testing (`./cypress.config` export)
129
+ - ESLint configuration uses ESLint 9's flat config format (not the legacy `.eslintrc` format)
130
+ - Downstream projects extend configs:
131
+ - ESLint (Node.js): `...wtConfig` in `eslint.config.js`
132
+ - ESLint (Browser): `...wtBrowserConfig` in `eslint.config.js`
133
+ - Prettier: Import via `.prettierrc.js` using `require('@hs-web-team/eslint-config-node/.prettierrc.json')`
134
+ - Stylelint: Extend in `.stylelintrc.json` with `"extends": "@hs-web-team/eslint-config-node/.stylelintrc.json"`
135
+ - Cypress: Import and spread in `cypress.config.js`
136
+ - Mixed module systems:
137
+ - Main package is ESM (`index.js`, `browser.js`)
138
+ - Utility scripts use CommonJS (`bin/add-prettier-scripts.js`, `cypress.config.js`)
90
139
  - CI runs on Node 22 and 24 (see `.github/workflows/pr.yml`)
91
140
  - No automated tests currently (`npm test` will fail with "Error: no test specified")
92
- - Detailed browser configuration documentation available in `examples/browser-usage.md`
141
+ - Detailed documentation available in `examples/`:
142
+ - `browser-usage.md` - Browser/React ESLint configuration
143
+ - `stylelint-usage.md` - Stylelint configuration
144
+ - `cypress-usage.md` - Cypress E2E testing configuration
package/README.md CHANGED
@@ -8,6 +8,8 @@ This package provides ESLint rules and configurations for **Hubspot Marketing We
8
8
 
9
9
  - [Node.js Setup](#nodejs-setup)
10
10
  - [Browser/React Setup](#browserreact-setup)
11
+ - [Stylelint Setup](#stylelint-setup)
12
+ - [Cypress Setup](#cypress-setup)
11
13
  - [Where to use it](#where-to-use-it)
12
14
  - [Using the Prettier Scripts](#using-the-prettier-scripts)
13
15
  <!-- index-end -->
@@ -78,14 +80,90 @@ This package provides ESLint rules and configurations for **Hubspot Marketing We
78
80
 
79
81
  For detailed browser configuration documentation and migration guides, see [examples/browser-usage.md](./examples/browser-usage.md).
80
82
 
83
+ ## Stylelint Setup
84
+
85
+ This package provides shared Stylelint configuration for SCSS/CSS linting.
86
+
87
+ 1. Install dependencies
88
+
89
+ ```sh
90
+ npm i -D stylelint stylelint-config-standard-scss
91
+ ```
92
+
93
+ 2. Create `.stylelintrc.json` in project root
94
+
95
+ ```json
96
+ {
97
+ "extends": "@hs-web-team/eslint-config-node/.stylelintrc.json"
98
+ }
99
+ ```
100
+
101
+ 3. Add scripts to package.json
102
+
103
+ ```json
104
+ {
105
+ "scripts": {
106
+ "stylelint:check": "stylelint '**/*.{css,scss}'",
107
+ "stylelint:fix": "stylelint '**/*.{css,scss}' --fix"
108
+ }
109
+ }
110
+ ```
111
+
112
+ For detailed Stylelint configuration documentation, see [examples/stylelint-usage.md](./examples/stylelint-usage.md).
113
+
114
+ ## Cypress Setup
115
+
116
+ This package provides shared Cypress configuration for E2E testing.
117
+
118
+ 1. Install dependencies
119
+
120
+ ```sh
121
+ npm i -D cypress @badeball/cypress-cucumber-preprocessor esbuild js-yaml
122
+ ```
123
+
124
+ 2. Create `cypress.config.js` in project root
125
+
126
+ ```javascript
127
+ const { defineConfig } = require('cypress');
128
+ const { config, envs, getBaseUrls } = require('@hs-web-team/eslint-config-node/cypress.config');
129
+
130
+ module.exports = defineConfig({
131
+ ...config,
132
+ e2e: {
133
+ ...config.e2e,
134
+ baseUrl: 'http://localhost:3000',
135
+ },
136
+ env: {
137
+ ...envs,
138
+ },
139
+ });
140
+ ```
141
+
142
+ 3. Add scripts to package.json
143
+
144
+ ```json
145
+ {
146
+ "scripts": {
147
+ "cypress:open": "cypress open",
148
+ "cypress:run": "cypress run",
149
+ "test:e2e": "cypress run"
150
+ }
151
+ }
152
+ ```
153
+
154
+ For detailed Cypress configuration documentation including HubSpot-specific features, see [examples/cypress-usage.md](./examples/cypress-usage.md).
155
+
81
156
  ## Where to use it
82
157
 
83
- This package provides two configurations:
158
+ This package provides multiple configurations:
84
159
 
85
160
  - **Node.js configuration** (default export): For backend Node.js projects
86
161
  - **Browser configuration** (`/browser` export): For browser-based projects including React applications
162
+ - **Stylelint configuration** (`.stylelintrc.json` export): For SCSS/CSS linting
163
+ - **Cypress configuration** (`cypress.config` export): For E2E testing with Cypress
164
+ - **Prettier configuration** (`.prettierrc.json` export): For code formatting
87
165
 
88
- Choose the appropriate configuration based on your project type.
166
+ Choose the appropriate configurations based on your project needs.
89
167
 
90
168
  ## Using the Prettier Scripts
91
169
 
@@ -115,4 +193,4 @@ See [MIGRATION-V2.md](./docs/MIGRATION-V2.md)
115
193
 
116
194
  ## Migration from v2 to v3
117
195
 
118
- See [MIGRATION-V3.md](./docs/MIGRATION-V3.md)
196
+ See [MIGRATION-V3.md](./docs/MIGRATION-V3.md)
@@ -0,0 +1,129 @@
1
+ const yaml = require('js-yaml');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { addCucumberPreprocessorPlugin } = require('@badeball/cypress-cucumber-preprocessor');
5
+ const { createEsbuildPlugin } = require('@badeball/cypress-cucumber-preprocessor/esbuild');
6
+
7
+ const DEV = 'DEV';
8
+ const QA = 'qa';
9
+ const PROD = 'prod';
10
+ const currentEnv = QA;
11
+
12
+ const envs = {
13
+ currentEnv,
14
+ DEV,
15
+ QA,
16
+ PROD,
17
+ };
18
+
19
+ /**
20
+ * @description Recursively climbs up the filepath, until it finds what directory
21
+ * the directory where the hubspot.config.yml file is located.
22
+ *
23
+ * @param {string} currDir - the current working directory path to search from
24
+ * @returns {string} The absolute path of the project's root directory
25
+ */
26
+ const getRootDir = currDir => {
27
+ if (fs.existsSync(path.join(currDir, 'hubspot.config.yml'))) return currDir;
28
+ const parentDir = path.dirname(currDir);
29
+ if (parentDir === currDir) {
30
+ throw new Error('Error: Could not find the hubspot.config.yml file within the projects directories.');
31
+ }
32
+ return getRootDir(parentDir);
33
+ };
34
+
35
+ /**
36
+ * @returns {string|null} The `baseUrl` set for the `DEV` portal in `hubspot.config.yml`
37
+ * or `null` if this is not the dev environment or no such property exists.
38
+ */
39
+ const getDevBaseUrl = () => {
40
+ try {
41
+ global.console.log(
42
+ 'To test a dev URL, add the `baseUrl` property to your `DEV` portal configuration in `hubspot.config.yml`',
43
+ );
44
+
45
+ const root = getRootDir(__dirname);
46
+ const configPath = path.resolve(root, 'hubspot.config.yml');
47
+ const config = fs.readFileSync(configPath, 'utf8');
48
+ const { portals } = yaml.load(config);
49
+ const devPortal = portals.find(portal => portal.name === 'DEV');
50
+ const devBaseUrl = devPortal.baseUrl;
51
+ return devBaseUrl || null;
52
+ } catch (error) {
53
+ global.console.error(error);
54
+ return null;
55
+ }
56
+ };
57
+
58
+ /**
59
+ * @description Get the baseUrls for different environments from the ci config file for local test execution.
60
+ * @returns {object} baseUrls - The base urls object
61
+ */
62
+ const getBaseUrls = () => {
63
+ let fileContents = '';
64
+ let ciConfig = {};
65
+ const baseUrls = {};
66
+ baseUrls[envs.DEV] = getDevBaseUrl();
67
+
68
+ const fileExist = fs.existsSync('.ci/config.yml');
69
+ if (fileExist) {
70
+ fileContents = fs.readFileSync('.ci/config.yml', 'utf8');
71
+ ciConfig = yaml.load(fileContents);
72
+ if (ciConfig.regression.e2eTestEnvironment && ciConfig.regression.e2eTestEnvironment.length > 0) {
73
+ try {
74
+ ciConfig.regression.e2eTestEnvironment.forEach(item => {
75
+ const envName = item.name;
76
+ baseUrls[envName] = item.url;
77
+ });
78
+ } catch (error) {
79
+ console.error('Error reading the base urls from the ci config file:', error);
80
+ return null;
81
+ }
82
+ }
83
+ }
84
+ return baseUrls || null;
85
+ };
86
+
87
+ async function setupNodeEvents(on, config) {
88
+ // This is required for the preprocessor to be able to generate JSON reports after each run
89
+ await addCucumberPreprocessorPlugin(on, config);
90
+
91
+ // Use esbuild for fast TypeScript and feature file processing
92
+ on(
93
+ 'file:preprocessor',
94
+ createEsbuildPlugin(config),
95
+ );
96
+
97
+ return config;
98
+ }
99
+
100
+ const e2e = {
101
+ specPattern: 'cypress/e2e/*.cy.js',
102
+ setupNodeEvents,
103
+ };
104
+
105
+ const config = {
106
+ chromeWebSecurity: false,
107
+ defaultCommandTimeout: 20000,
108
+ e2e,
109
+ numTestsKeptInMemory: 0,
110
+ pageLoadTimeout: 20000,
111
+ port: 3500,
112
+ responseTimeout: 20000,
113
+ retries: {
114
+ runMode: 1,
115
+ openMode: 0,
116
+ },
117
+ screenshotOnRunFailure: true,
118
+ trashAssetsBeforeRuns: true,
119
+ video: false,
120
+ viewportHeight: 1080,
121
+ viewportWidth: 1920,
122
+ };
123
+
124
+ module.exports = {
125
+ config,
126
+ envs,
127
+ getDevBaseUrl,
128
+ getBaseUrls,
129
+ };
@@ -0,0 +1,377 @@
1
+ # Cypress Configuration Usage
2
+
3
+ This package provides a shared Cypress configuration for E2E testing in HubSpot Marketing WebTeam projects.
4
+
5
+ ## Installation
6
+
7
+ 1. Install the package and Cypress dependencies:
8
+
9
+ ```bash
10
+ npm install --save-dev @hs-web-team/eslint-config-node cypress @badeball/cypress-cucumber-preprocessor esbuild js-yaml
11
+ ```
12
+
13
+ 2. If using TypeScript:
14
+
15
+ ```bash
16
+ npm install --save-dev typescript @types/node
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Basic Setup
22
+
23
+ Create a `cypress.config.js` file in your project root:
24
+
25
+ ```javascript
26
+ const { defineConfig } = require('cypress');
27
+ const { config, envs, getBaseUrls } = require('@hs-web-team/eslint-config-node/cypress.config');
28
+
29
+ module.exports = defineConfig({
30
+ ...config,
31
+ e2e: {
32
+ ...config.e2e,
33
+ baseUrl: 'http://localhost:3000', // Your app's base URL
34
+ // Add custom e2e options here
35
+ },
36
+ env: {
37
+ ...envs,
38
+ // Add custom environment variables
39
+ },
40
+ });
41
+ ```
42
+
43
+ ### With HubSpot Projects
44
+
45
+ For HubSpot CMS projects that use `hubspot.config.yml`:
46
+
47
+ ```javascript
48
+ const { defineConfig } = require('cypress');
49
+ const { config, envs, getBaseUrls } = require('@hs-web-team/eslint-config-node/cypress.config');
50
+
51
+ const baseUrls = getBaseUrls();
52
+
53
+ module.exports = defineConfig({
54
+ ...config,
55
+ e2e: {
56
+ ...config.e2e,
57
+ baseUrl: baseUrls.qa || baseUrls.DEV, // Use QA or DEV environment
58
+ },
59
+ env: {
60
+ ...envs,
61
+ baseUrls,
62
+ },
63
+ });
64
+ ```
65
+
66
+ ## What's Included
67
+
68
+ ### Core Configuration
69
+
70
+ The shared configuration includes:
71
+
72
+ - **Chrome Web Security**: Disabled (`chromeWebSecurity: false`) for cross-origin testing
73
+ - **Timeouts**:
74
+ - Default command timeout: 20 seconds
75
+ - Page load timeout: 20 seconds
76
+ - Response timeout: 20 seconds
77
+ - **Test Settings**:
78
+ - Port: 3500
79
+ - Viewport: 1920x1080
80
+ - Tests kept in memory: 0 (prevents memory issues)
81
+ - **Retries**:
82
+ - Run mode: 1 retry
83
+ - Open mode: 0 retries
84
+ - **Screenshots**: Enabled on failure
85
+ - **Video**: Disabled by default
86
+ - **Trash assets before runs**: Enabled
87
+
88
+ ### E2E Configuration
89
+
90
+ - **Spec Pattern**: `cypress/e2e/*.cy.js`
91
+ - **Cucumber Support**: Includes Cucumber preprocessor setup with esbuild
92
+ - **TypeScript Support**: Native TypeScript support via esbuild (no additional loader needed)
93
+ - **esbuild Preprocessor**: Fast bundling for `.feature` files, TypeScript, and JavaScript
94
+
95
+ ### Environment Utilities
96
+
97
+ The configuration exports utility functions:
98
+
99
+ #### `envs`
100
+ Pre-configured environment constants:
101
+ ```javascript
102
+ const envs = {
103
+ currentEnv: 'qa', // Default environment
104
+ DEV: 'DEV',
105
+ QA: 'qa',
106
+ PROD: 'prod',
107
+ };
108
+ ```
109
+
110
+ #### `getDevBaseUrl()`
111
+ Retrieves the `baseUrl` from the `DEV` portal in `hubspot.config.yml`:
112
+
113
+ ```javascript
114
+ const devUrl = getDevBaseUrl(); // Returns baseUrl or null
115
+ ```
116
+
117
+ **Requirements**:
118
+ - Project must have a `hubspot.config.yml` file
119
+ - DEV portal must have a `baseUrl` property configured
120
+
121
+ #### `getBaseUrls()`
122
+ Retrieves base URLs for all environments from `.ci/config.yml`:
123
+
124
+ ```javascript
125
+ const urls = getBaseUrls();
126
+ // Returns: { DEV: '...', qa: '...', prod: '...' }
127
+ ```
128
+
129
+ **Requirements**:
130
+ - `.ci/config.yml` file with `regression.e2eTestEnvironment` configuration
131
+ - Format:
132
+ ```yaml
133
+ regression:
134
+ e2eTestEnvironment:
135
+ - name: qa
136
+ url: https://qa.example.com
137
+ - name: prod
138
+ url: https://prod.example.com
139
+ ```
140
+
141
+ ## Project Structure
142
+
143
+ ### Recommended Folder Structure
144
+
145
+ ```
146
+ project-root/
147
+ ├── cypress/
148
+ │ ├── e2e/
149
+ │ │ ├── login.cy.js
150
+ │ │ └── homepage.cy.js
151
+ │ ├── fixtures/
152
+ │ │ └── users.json
153
+ │ ├── support/
154
+ │ │ ├── commands.js
155
+ │ │ └── e2e.js
156
+ │ └── downloads/
157
+ ├── cypress.config.js
158
+ └── package.json
159
+ ```
160
+
161
+ ### For Cucumber/BDD Testing
162
+
163
+ ```
164
+ project-root/
165
+ ├── cypress/
166
+ │ ├── e2e/
167
+ │ │ └── features/
168
+ │ │ ├── login.feature
169
+ │ │ └── homepage.feature
170
+ │ ├── support/
171
+ │ │ ├── step_definitions/
172
+ │ │ │ ├── login.ts
173
+ │ │ │ └── homepage.ts
174
+ │ │ └── e2e.ts
175
+ └── cypress.config.js
176
+ ```
177
+
178
+ ## Add Scripts to package.json
179
+
180
+ Add Cypress scripts to your `package.json`:
181
+
182
+ ```json
183
+ {
184
+ "scripts": {
185
+ "cypress:open": "cypress open",
186
+ "cypress:run": "cypress run",
187
+ "cypress:run:chrome": "cypress run --browser chrome",
188
+ "cypress:run:headed": "cypress run --headed",
189
+ "test:e2e": "cypress run"
190
+ }
191
+ }
192
+ ```
193
+
194
+ Then run:
195
+ - `npm run cypress:open` - Open Cypress Test Runner (interactive)
196
+ - `npm run cypress:run` - Run all tests headlessly
197
+ - `npm run test:e2e` - Run E2E tests (alias for cypress:run)
198
+
199
+ ## Customizing Configuration
200
+
201
+ ### Override Default Values
202
+
203
+ ```javascript
204
+ const { defineConfig } = require('cypress');
205
+ const { config } = require('@hs-web-team/eslint-config-node/cypress.config');
206
+
207
+ module.exports = defineConfig({
208
+ ...config,
209
+ // Override specific values
210
+ defaultCommandTimeout: 30000,
211
+ viewportWidth: 1280,
212
+ viewportHeight: 720,
213
+ video: true, // Enable video recording
214
+ e2e: {
215
+ ...config.e2e,
216
+ baseUrl: 'http://localhost:8080',
217
+ specPattern: 'cypress/integration/**/*.spec.js',
218
+ },
219
+ });
220
+ ```
221
+
222
+ ### Add Custom setupNodeEvents
223
+
224
+ ```javascript
225
+ const { defineConfig } = require('cypress');
226
+ const { config } = require('@hs-web-team/eslint-config-node/cypress.config');
227
+
228
+ module.exports = defineConfig({
229
+ ...config,
230
+ e2e: {
231
+ ...config.e2e,
232
+ async setupNodeEvents(on, cypressConfig) {
233
+ // Call shared setup first (includes esbuild preprocessor and Cucumber setup)
234
+ const updatedConfig = await config.e2e.setupNodeEvents(on, cypressConfig);
235
+
236
+ // Add your custom tasks/events
237
+ on('task', {
238
+ log(message) {
239
+ console.log(message);
240
+ return null;
241
+ },
242
+ });
243
+
244
+ return updatedConfig;
245
+ },
246
+ },
247
+ });
248
+ ```
249
+
250
+ ## Integration with CI/CD
251
+
252
+ ### GitHub Actions Example
253
+
254
+ ```yaml
255
+ name: E2E Tests
256
+ on: [push, pull_request]
257
+
258
+ jobs:
259
+ cypress-run:
260
+ runs-on: ubuntu-latest
261
+ steps:
262
+ - name: Checkout
263
+ uses: actions/checkout@v4
264
+
265
+ - name: Setup Node
266
+ uses: actions/setup-node@v4
267
+ with:
268
+ node-version: 22
269
+
270
+ - name: Install dependencies
271
+ run: npm ci
272
+
273
+ - name: Run Cypress tests
274
+ uses: cypress-io/github-action@v6
275
+ with:
276
+ start: npm start
277
+ wait-on: 'http://localhost:3000'
278
+ ```
279
+
280
+ ## TypeScript Support
281
+
282
+ The configuration includes TypeScript support. Create a `tsconfig.json` for Cypress:
283
+
284
+ ```json
285
+ {
286
+ "compilerOptions": {
287
+ "target": "ES2021",
288
+ "lib": ["ES2021", "DOM"],
289
+ "types": ["cypress", "node"],
290
+ "moduleResolution": "node",
291
+ "esModuleInterop": true,
292
+ "skipLibCheck": true,
293
+ "strict": true
294
+ },
295
+ "include": ["cypress/**/*.ts"],
296
+ "exclude": ["node_modules"]
297
+ }
298
+ ```
299
+
300
+ ## Cucumber/BDD Support
301
+
302
+ The configuration includes Cucumber preprocessor setup. To use it:
303
+
304
+ 1. Install dependencies:
305
+ ```bash
306
+ npm install --save-dev @badeball/cypress-cucumber-preprocessor
307
+ ```
308
+
309
+ 2. Create `.cypress-cucumber-preprocessorrc.json`:
310
+ ```json
311
+ {
312
+ "stepDefinitions": "cypress/support/step_definitions/**/*.{js,ts}",
313
+ "json": {
314
+ "enabled": true,
315
+ "output": "cypress/results/cucumber-report.json"
316
+ }
317
+ }
318
+ ```
319
+
320
+ 3. Write feature files in `cypress/e2e/features/`:
321
+ ```gherkin
322
+ Feature: Login
323
+
324
+ Scenario: User can login successfully
325
+ Given I visit the login page
326
+ When I enter valid credentials
327
+ Then I should see the dashboard
328
+ ```
329
+
330
+ ## Cypress Version Requirements
331
+
332
+ This configuration requires **Cypress 14.0.0 or higher**. Cypress 14+ includes:
333
+
334
+ - Improved performance for component test runs
335
+ - Support for latest React, Angular, Next.js, Svelte, and Vite versions
336
+ - Seamless handling of Chrome's document.domain deprecation
337
+ - Upgraded Electron with Chromium 130+ for enhanced stability
338
+ - Better `cy.origin()` support for cross-subdomain navigation
339
+
340
+ Current latest version: Cypress 15.9.0 (January 2026)
341
+
342
+ ## Why esbuild Instead of Webpack?
343
+
344
+ The configuration uses **esbuild** instead of webpack for several reasons:
345
+
346
+ - ⚡ **Much faster** - esbuild is 10-100x faster than webpack
347
+ - 🎯 **Native TypeScript support** - No need for ts-loader or additional configuration
348
+ - 📦 **Smaller footprint** - Fewer dependencies to install and maintain
349
+ - 🔧 **Simpler setup** - Works out of the box with the Cucumber preprocessor
350
+
351
+ ## Troubleshooting
352
+
353
+ ### Module Not Found Errors
354
+
355
+ Ensure all required dependencies are installed:
356
+ ```bash
357
+ npm install --save-dev cypress @badeball/cypress-cucumber-preprocessor esbuild js-yaml
358
+ ```
359
+
360
+ ### Compilation Errors
361
+
362
+ If you see compilation errors with TypeScript files, ensure esbuild is installed:
363
+ ```bash
364
+ npm install --save-dev esbuild
365
+ ```
366
+
367
+ ### Environment Configuration Not Loading
368
+
369
+ Ensure your project has the required configuration files:
370
+ - `hubspot.config.yml` for HubSpot projects
371
+ - `.ci/config.yml` for multi-environment setups
372
+
373
+ ### Memory Issues
374
+
375
+ The configuration sets `numTestsKeptInMemory: 0` to prevent memory issues. If you still experience problems, consider:
376
+ - Running tests in smaller batches
377
+ - Increasing Node.js memory: `NODE_OPTIONS=--max_old_space_size=4096 cypress run`
@@ -0,0 +1,144 @@
1
+ # Stylelint Configuration Usage
2
+
3
+ This package provides a shared Stylelint configuration for SCSS/CSS linting in HubSpot Marketing WebTeam projects.
4
+
5
+ ## Installation
6
+
7
+ 1. Install the package and stylelint dependencies:
8
+
9
+ ```bash
10
+ npm install --save-dev @hs-web-team/eslint-config-node stylelint stylelint-config-standard-scss
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Create a `.stylelintrc.json` file in your project root:
16
+
17
+ ```json
18
+ {
19
+ "extends": "@hs-web-team/eslint-config-node/.stylelintrc.json"
20
+ }
21
+ ```
22
+
23
+ Alternatively, create a `.stylelintrc.js` file:
24
+
25
+ ```javascript
26
+ module.exports = {
27
+ extends: '@hs-web-team/eslint-config-node/.stylelintrc.json',
28
+ // Add your custom rules here
29
+ rules: {
30
+ // Custom overrides
31
+ },
32
+ };
33
+ ```
34
+
35
+ ## Add Scripts to package.json
36
+
37
+ Add the following scripts to your `package.json`:
38
+
39
+ ```json
40
+ {
41
+ "scripts": {
42
+ "stylelint:check": "stylelint '**/*.{css,scss}'",
43
+ "stylelint:fix": "stylelint '**/*.{css,scss}' --fix"
44
+ }
45
+ }
46
+ ```
47
+
48
+ Then run:
49
+
50
+ - `npm run stylelint:check` - Check for style issues
51
+ - `npm run stylelint:fix` - Automatically fix style issues
52
+
53
+ ## What's Included
54
+
55
+ The configuration extends `stylelint-config-standard-scss` with custom rules for HubSpot projects:
56
+
57
+ ### Base Configuration
58
+ - **Extends**: `stylelint-config-standard-scss` (Standard SCSS linting rules)
59
+
60
+ ### Disabled Rules
61
+ The following rules are disabled to allow more flexibility in HubSpot projects:
62
+
63
+ - **length-zero-no-unit**: Allows units on zero values (e.g., `0px`)
64
+ - **declaration-empty-line-before**: No empty line requirement before declarations
65
+ - **no-empty-source**: Allows empty source files
66
+ - **selector-class-pattern**: No pattern enforcement for class names
67
+ - **declaration-block-no-redundant-longhand-properties**: Allows longhand properties
68
+ - **shorthand-property-no-redundant-values**: Allows redundant shorthand values
69
+ - **no-duplicate-selectors**: Allows duplicate selectors
70
+ - **font-family-name-quotes**: No quote requirement for font family names
71
+
72
+ ### SCSS-Specific Rules
73
+ The following SCSS rules are customized:
74
+
75
+ - **at-rule-no-unknown**: Configured to ignore SCSS directives (`@function`, `@if`, `@else`, `@for`, `@each`, `@include`, `@mixin`, `@extend`)
76
+ - **scss/dollar-variable-pattern**: No pattern enforcement for variable names
77
+ - **scss/at-import-no-partial-leading-underscore**: Allows leading underscores in imports
78
+ - **scss/at-import-partial-extension**: Allows file extensions in imports
79
+ - **scss/percent-placeholder-pattern**: No pattern enforcement for placeholders
80
+ - **scss/at-mixin-argumentless-call-parentheses**: Flexible parentheses for mixins
81
+ - **scss/dollar-variable-empty-line-before**: No empty line requirement before variables
82
+ - **scss/double-slash-comment-whitespace-inside**: Flexible comment whitespace
83
+
84
+ ### Custom Rules
85
+
86
+ - **alpha-value-notation**: No specific alpha value notation required
87
+ - **color-function-notation**: Flexible color function notation
88
+ - **media-feature-range-notation**: Set to `"context"` with warning severity
89
+ - **value-keyword-case**: Enforces lowercase keywords with camelCase for SVG keywords
90
+
91
+ ## File Patterns
92
+
93
+ By default, Stylelint will process:
94
+ - `**/*.css`
95
+ - `**/*.scss`
96
+
97
+ ## Ignore Files
98
+
99
+ Create a `.stylelintignore` file to exclude files from linting:
100
+
101
+ ```
102
+ node_modules/
103
+ dist/
104
+ build/
105
+ *.min.css
106
+ ```
107
+
108
+ ## Integration with CI/CD
109
+
110
+ Add stylelint checks to your CI pipeline:
111
+
112
+ ```yaml
113
+ # Example GitHub Actions
114
+ - name: Run Stylelint
115
+ run: npm run stylelint:check
116
+ ```
117
+
118
+ ## Customizing Rules
119
+
120
+ To override rules for your project, add them to your `.stylelintrc.json`:
121
+
122
+ ```json
123
+ {
124
+ "extends": "@hs-web-team/eslint-config-node/.stylelintrc.json",
125
+ "rules": {
126
+ "selector-class-pattern": "^[a-z][a-zA-Z0-9]+$",
127
+ "max-nesting-depth": 3
128
+ }
129
+ }
130
+ ```
131
+
132
+ ## Troubleshooting
133
+
134
+ ### Module Not Found Error
135
+
136
+ If you get a module not found error, ensure you have installed all dependencies:
137
+
138
+ ```bash
139
+ npm install --save-dev stylelint stylelint-config-standard-scss
140
+ ```
141
+
142
+ ### Conflicts with Prettier
143
+
144
+ If using Prettier for CSS/SCSS formatting, ensure Stylelint focuses on code quality rules rather than formatting. The shared configuration already disables many formatting-related rules.
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@hs-web-team/eslint-config-node",
3
- "version": "3.1.2",
4
- "description": "HubSpot Marketing WebTeam ESLint rules for Node.js",
3
+ "version": "3.2.0-next.0",
4
+ "description": "HubSpot Marketing WebTeam shared configurations for ESLint, Prettier, Stylelint, and Cypress",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "exports": {
8
8
  ".": "./index.js",
9
- "./browser": "./browser.js"
9
+ "./browser": "./browser.js",
10
+ "./.prettierrc.json": "./.prettierrc.json",
11
+ "./.stylelintrc.json": "./.stylelintrc.json",
12
+ "./cypress.config": "./cypress.config.js"
10
13
  },
11
14
  "scripts": {
12
15
  "lint": "npx eslint -c ./index.js *.js --fix",
@@ -23,7 +26,12 @@
23
26
  "url": "git+https://github.com/HubSpotWebTeam/wt-eslint-node.git"
24
27
  },
25
28
  "keywords": [
26
- "eslint"
29
+ "eslint",
30
+ "prettier",
31
+ "stylelint",
32
+ "cypress",
33
+ "hubspot",
34
+ "config"
27
35
  ],
28
36
  "author": "Alessio Michelini & David Mooney",
29
37
  "license": "ISC",
@@ -41,9 +49,41 @@
41
49
  "eslint-plugin-jsx-a11y": "^6.10.2",
42
50
  "eslint-plugin-react": "^7.37.5",
43
51
  "eslint-plugin-react-hooks": "^7.0.1",
44
- "globals": "^17.0.0",
52
+ "globals": "^17.2.0",
45
53
  "jiti": "^2.6.1",
46
54
  "prettier": "^3.8.1",
47
- "typescript-eslint": "^8.53.1"
55
+ "typescript-eslint": "^8.54.0"
56
+ },
57
+ "peerDependencies": {
58
+ "@badeball/cypress-cucumber-preprocessor": "^20.0.0",
59
+ "cypress": "^14.5.2",
60
+ "esbuild": "^0.27.2",
61
+ "js-yaml": "^4.1.1",
62
+ "stylelint": "^16.22.0",
63
+ "stylelint-config-standard": "^36.0.1",
64
+ "stylelint-config-standard-scss": "^13.1.0"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "stylelint": {
68
+ "optional": true
69
+ },
70
+ "stylelint-config-standard": {
71
+ "optional": true
72
+ },
73
+ "stylelint-config-standard-scss": {
74
+ "optional": true
75
+ },
76
+ "cypress": {
77
+ "optional": true
78
+ },
79
+ "@badeball/cypress-cucumber-preprocessor": {
80
+ "optional": true
81
+ },
82
+ "esbuild": {
83
+ "optional": true
84
+ },
85
+ "js-yaml": {
86
+ "optional": true
87
+ }
48
88
  }
49
89
  }