From 73a90fff0e3aba910c7e7f9076827638b129bea9 Mon Sep 17 00:00:00 2001 From: Jared Wray Date: Tue, 3 Sep 2024 08:48:27 -0700 Subject: [PATCH] adding in tests and github actions --- .github/ISSUE_TEMPLATE/bug_report.md | 14 +++ .github/ISSUE_TEMPLATE/feature_request.md | 14 +++ .github/PULL_REQUEST_TEMPLATE.md | 6 + .github/workflows/code-coverage.yaml | 39 +++++++ .github/workflows/codeql.yaml | 72 ++++++++++++ .github/workflows/deploy-site.yaml | 41 +++++++ .github/workflows/release.yaml | 39 +++++++ .github/workflows/tests.yaml | 34 ++++++ CODE_OF_CONDUCT.md | 128 ++++++++++++++++++++++ CONTRIBUTING.md | 27 +++++ LICENSE | 2 +- README.md | 20 ++-- package.json | 28 ++++- site/docula.config.cjs | 8 +- site/favicon.ico | Bin 0 -> 410598 bytes site/logo.svg | 8 +- src/index.ts | 20 ++-- test/index.test.ts | 83 ++++++++++++++ vitest.config.ts | 12 +- 19 files changed, 564 insertions(+), 31 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/code-coverage.yaml create mode 100644 .github/workflows/codeql.yaml create mode 100644 .github/workflows/deploy-site.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/tests.yaml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 site/favicon.ico create mode 100644 test/index.test.ts diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0e4c9d8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,14 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**How To Reproduce (best to provide workable code or tests!)** +Please provide code or unit test example that will reproduce the error. If you can't provide code, please provide a detailed description of how to reproduce the error. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..c444027 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..77f07ea --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,6 @@ +**Please check if the PR fulfills these requirements** +- [ ] Followed the [Contributing](https://github.com/jaredwray/hookified/blob/main/CONTRIBUTING.md) guidelines. +- [ ] Tests for the changes have been added (for bug fixes/features) with 100% code coverage. +- [ ] Docs have been added / updated (for bug fixes / features) + +**What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) diff --git a/.github/workflows/code-coverage.yaml b/.github/workflows/code-coverage.yaml new file mode 100644 index 0000000..b33d907 --- /dev/null +++ b/.github/workflows/code-coverage.yaml @@ -0,0 +1,39 @@ +name: code-coverage + +on: + workflow_dispatch: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: ['20'] + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Testing + run: npm run test + + - name: Code Coverage + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 0000000..55da301 --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,72 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "codeql" + +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy-site.yaml b/.github/workflows/deploy-site.yaml new file mode 100644 index 0000000..4a7d68d --- /dev/null +++ b/.github/workflows/deploy-site.yaml @@ -0,0 +1,41 @@ +name: deploy-site + +on: + workflow_dispatch: + release: + types: [released] + +jobs: + setup-build-deploy: + name: Deploy Website + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: ['20'] + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Build Website + run: npm run website:build + + - name: Publish to Cloudflare Pages + uses: cloudflare/pages-action@1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: b09b24c345713c704e71dea8bd81f713 + projectName: hookified + directory: site/dist + branch: main + gitHubToken: ${{ secrets.GH_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..a3a719f --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,39 @@ +name: release + +on: + workflow_dispatch: + release: + types: [released] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: ['20'] + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Testing + run: npm run test + + - name: Publish + run: | + npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} + npm publish --ignore-scripts + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..9d413e2 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,34 @@ +name: tests + +on: + workflow_dispatch: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: ['20', '22'] + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Testing + run: npm run test + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..c23fb4c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +me@jaredwray.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..095d142 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,27 @@ +# Contributing +When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. + +Please note we have a [Code of Conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. + +We release new versions of this project (maintenance/features) on a monthly cadence so please be aware that some items will not get released right away. + +# Pull Request Process +You can contribute changes to this repo by opening a pull request: + +1) After forking this repository to your Git account, make the proposed changes on your forked branch. +2) Run tests and linting locally. + - Run `npm install`. + - Run `npm test`. +3) Commit your changes and push them to your forked repository. +4) Navigate to the main `Hookified` repository and select the *Pull Requests* tab. +5) Click the *New pull request* button, then select the option "Compare across forks" +6) Leave the base branch set to main. Set the compare branch to your forked branch, and open the pull request. +7) Once your pull request is created, ensure that all checks have passed and that your branch has no conflicts with the base branch. If there are any issues, resolve these changes in your local repository, and then commit and push them to git. +8) Similarly, respond to any reviewer comments or requests for changes by making edits to your local repository and pushing them to Git. +9) Once the pull request has been reviewed, those with write access to the branch will be able to merge your changes into the `Hookified` repository. + +If you need more information on the steps to create a pull request, you can find a detailed walkthrough in the [Github documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) + + +# Code of Conduct +Please refer to our [Code of Conduct](https://github.com/jaredwray/hookified/blob/main/CODE_OF_CONDUCT.md) readme for how to contribute to this open source project and work within the community. diff --git a/LICENSE b/LICENSE index ca087f5..528d47b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Jared Wray +Copyright (c) Jared Wray Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 4b5783d..acf167f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,18 @@ -![Hookified](site/logo.svg) +Hookified -# Hookified -Event and Middleware Hooks +# Event and Middleware Hooks for Your Libraries + +[![tests](https://github.com/jaredwray/hookified/actions/workflows/tests.yaml/badge.svg)](https://github.com/jaredwray/hookified/actions/workflows/tests.yaml) +[![GitHub license](https://img.shields.io/github/license/jaredwray/hookified)](https://github.com/jaredwray/hookified/blob/master/LICENSE) +[![codecov](https://codecov.io/gh/jaredwray/hookified/graph/badge.svg?token=nKkVklTFdA)](https://codecov.io/gh/jaredwray/hookified) +[![npm](https://img.shields.io/npm/dm/hookified)](https://npmjs.com/package/hookified) +[![npm](https://img.shields.io/npm/v/hookified)](https://npmjs.com/package/hookified) ## Features - Emit Events via [Emittery](https://npmjs.com/package/emittery) -- Middleware Hooks - Easily add middleware to your library for additional functionality -- TypeScript, ESM, and Nodejs 20+ +- Middleware Hooks with data passing +- ESM and Nodejs 20+ +- Maintained on a regular basis! ## Installation ```bash @@ -33,9 +39,9 @@ class MyClass extends Hookified { async myMethod2() Promise { let data = { some: 'data' }; // do something - await this.exec('before:myMethod2', data); + await this.hook('before:myMethod2', data); // do something - await this.exec('after:myMethod2', data); + await this.hook('after:myMethod2', data); return data; } } diff --git a/package.json b/package.json index 1350c68..9ab1a02 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,42 @@ { "name": "hookified", - "version": "1.0.0", + "version": "0.5.0", "description": "Event and Middleware Hooks", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "test": "xo --fix && vitest --coverage", + "test": "xo --fix && vitest run --coverage", + "test:ci": "xo && vitest run --coverage", "clean": "rimraf ./dist ./coverage ./site/dist", "build": "rimraf ./dist && tsc", "website:build": "docula build", "website:serve": "docula serve", "prepare": "npm run build" }, + "keywords": [ + "hooks", + "emittery", + "eventemitter", + "eventemitter3", + "middleware", + "events", + "hooks", + "event-hooks", + "hook-system", + "event-driven", + "event-emitter", + "hook-handlers", + "async-hooks", + "middleware", + "emit", + "event-management", + "before-after-hooks", + "event-listeners", + "custom-hooks", + "hook-utilities", + "typescript-hooks" + ], "repository": { "type": "git", "url": "git+https://github.com/jaredwray/hookified.git" diff --git a/site/docula.config.cjs b/site/docula.config.cjs index bb764f0..9565f3d 100644 --- a/site/docula.config.cjs +++ b/site/docula.config.cjs @@ -10,11 +10,11 @@ module.exports.options = { siteUrl: 'https://hookified.org', }; -module.exports.onPrepare = async (config) => { +module.exports.onPrepare = async config => { const readmePath = path.join(process.cwd(), './README.md'); const readmeSitePath = path.join(config.sitePath, 'README.md'); const readme = await fs.promises.readFile(readmePath, 'utf8'); - const updatedReadme = readme.replace('![Hookified](site/logo.svg)\n\n', ''); - console.log('writing updated readme to ', readmeSitePath); + const updatedReadme = readme.replace('Hookified\n\n', ''); + console.log('writing updated readme to', readmeSitePath); await fs.promises.writeFile(readmeSitePath, updatedReadme); -} \ No newline at end of file +}; diff --git a/site/favicon.ico b/site/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..64e5816ae8b0e892fe4c0a521683cc4bd2ed395a GIT binary patch literal 410598 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 - +