Skip to content

Update project files and add string normalization utility #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,10 @@
"node": true,
"jest/globals": true
},
"extends": [
"eslint:recommended",
"plugin:jest/recommended",
"prettier"
],
"extends": ["eslint:recommended", "plugin:jest/recommended", "prettier"],
"parserOptions": {
"ecmaVersion": "latest"
},
"plugins": [
"jest"
],
"plugins": ["jest"],
"rules": {}
}
}
32 changes: 16 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Node.js CI

on:
push:
branches: [ "main" ]
branches: ['main']
pull_request:
branches: [ "main" ]
branches: ['main']

jobs:
build:
Expand All @@ -15,17 +15,17 @@ jobs:
node-version: [18.x, 20.x]

steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint code
run: npm run lint
- name: Check formatting
run: npm run format
- name: Run tests
run: npm test
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint code
run: npm run lint
- name: Check formatting
run: npm run format
- name: Run tests
run: npm test
2 changes: 1 addition & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2
}
}
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ This template provides a solid foundation for any new JavaScript project, ensuri

## Features

- **Linting** with [ESLint](https://eslint.org/) to find and fix problems in your JavaScript code.
- **Formatting** with [Prettier](https://prettier.io/) for a consistent code style.
- **Testing** with [Jest](https://jestjs.io/) as the testing framework.
- **CI/CD** with [GitHub Actions](https://github.com/features/actions) to automate linting, formatting checks, and testing on every push and pull request.
- **Pre-commit Hooks** with [Husky](https://typicode.github.io/husky/) and [lint-staged](https://github.com/okonet/lint-staged) to lint and format your code before you even commit it.
- **Linting** with [ESLint](https://eslint.org/) to find and fix problems in your JavaScript code.
- **Formatting** with [Prettier](https://prettier.io/) for a consistent code style.
- **Testing** with [Jest](https://jestjs.io/) as the testing framework.
- **CI/CD** with [GitHub Actions](https://github.com/features/actions) to automate linting, formatting checks, and testing on every push and pull request.
- **Pre-commit Hooks** with [Husky](https://typicode.github.io/husky/) and [lint-staged](https://github.com/okonet/lint-staged) to lint and format your code before you even commit it.

## Getting Started

Expand All @@ -21,6 +21,7 @@ Click the "Use this template" button on the GitHub repository page to create a n
### Manual Setup

1. Clone the repository:

```bash
git clone https://github.com/your-username/js-quality-starter.git
cd js-quality-starter
Expand All @@ -35,11 +36,11 @@ Click the "Use this template" button on the GitHub repository page to create a n

In the project directory, you can run:

- `npm test`: Runs the tests using Jest.
- `npm run lint`: Lints all `.js` files in the project.
- `npm run lint:fix`: Lints and automatically fixes fixable issues.
- `npm run format`: Checks for formatting issues with Prettier.
- `npm run format:fix`: Formats all supported files with Prettier.
- `npm test`: Runs the tests using Jest.
- `npm run lint`: Lints all `.js` files in the project.
- `npm run lint:fix`: Lints and automatically fixes fixable issues.
- `npm run format`: Checks for formatting issues with Prettier.
- `npm run format:fix`: Formats all supported files with Prettier.

## How It Works

Expand All @@ -58,4 +59,4 @@ The `.github/workflows/ci.yml` file defines a GitHub Actions workflow that runs
3. Runs `npm run format` to check for formatting errors.
4. Runs `npm test` to execute the test suite.

This ensures that all code in the `main` branch is high quality and passes all checks.
This ensures that all code in the `main` branch is high quality and passes all checks.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ const config = {
coverageProvider: 'v8',
};

module.exports = config;
module.exports = config;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@
"prettier --write"
]
}
}
}
8 changes: 7 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/**
* Adds two numbers together.
* @param {number} a The first number.
* @param {number} b The second number.
* @returns {number} The sum of the two numbers.
*/
function add(a, b) {
return a + b;
}

module.exports = add;
module.exports = add;
2 changes: 1 addition & 1 deletion src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ describe('add function', () => {
test('should handle negative numbers', () => {
expect(add(-1, -1)).toBe(-2);
});
});
});
32 changes: 32 additions & 0 deletions src/utils/normalize-string.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Normalizes a string by trimming whitespace, converting to lowercase,
* replacing non-alphanumeric characters with dashes, collapsing multiple dashes,
* and removing leading/trailing dashes. This is commonly used to create URL-friendly "slugs".
*
* @param {string} str The input string to normalize.
* @returns {string} The normalized string.
* @throws {Error} If the input is null or undefined.
*/
function normalizeString(str) {
if (str === null || str === undefined) {
throw new Error('Input cannot be null or undefined.');
}

let s = String(str);

s = s.trim();
s = s.toLowerCase();

// Replace non-alphanumeric (excluding dash and underscore) with a dash
s = s.replace(/[^a-z0-9_-]/g, '-');

// Replace multiple dashes with a single dash
s = s.replace(/-+/g, '-');

// Remove leading/trailing dashes
s = s.replace(/^-+|-+$/g, '');

return s;
}

module.exports = normalizeString;
53 changes: 53 additions & 0 deletions src/utils/normalize-string.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const normalizeString = require('./normalize-string');

describe('normalizeString', () => {
test('should convert a basic string with spaces to kebab-case', () => {
expect(normalizeString('Hello World')).toBe('hello-world');
});

test('should trim leading and trailing whitespace', () => {
expect(normalizeString(' leading and trailing ')).toBe(
'leading-and-trailing'
);
});

test('should convert the entire string to lowercase', () => {
expect(normalizeString('UPPERCASE STRING')).toBe('uppercase-string');
});

test('should replace various special characters with a dash', () => {
expect(normalizeString('a!b@c#d$e%f^g&h*i(j)k')).toBe(
'a-b-c-d-e-f-g-h-i-j-k'
);
});

test('should collapse multiple dashes and spaces into a single dash', () => {
expect(normalizeString('multiple---dashes')).toBe('multiple-dashes');
expect(normalizeString('a b c')).toBe('a-b-c');
});

test('should remove leading and trailing dashes', () => {
expect(normalizeString('-leading-and-trailing-')).toBe(
'leading-and-trailing'
);
});

test('should handle a complex mix of operations correctly', () => {
expect(normalizeString(' --My Awesome Post #1! --')).toBe(
'my-awesome-post-1'
);
});

test('should return an empty string if the input is an empty string', () => {
expect(normalizeString('')).toBe('');
});

test('should throw an error for null or undefined input', () => {
expect(() => normalizeString(null)).toThrow(
'Input cannot be null or undefined.'
);
expect(() => normalizeString(undefined)).toThrow(
'Input cannot be null or undefined.'
);
});
});