diff --git a/.eslintrc b/.eslintrc index 7cec6b7..6865141 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,8 +1,5 @@ { "root": true, "plugins": ["@homer0"], - "extends": [ - "plugin:@homer0/node-with-prettier", - "plugin:@homer0/jsdoc" - ] + "extends": ["plugin:@homer0/node-with-prettier", "plugin:@homer0/jsdoc"] } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1ab600e..3bfcb95 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [ '16', '18' ] + node: ['16', '18'] name: Run jest and ESLint (Node ${{ matrix.node }}) steps: - uses: actions/checkout@v3 diff --git a/.prettierignore b/.prettierignore index 333ac01..c0a0dac 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,3 @@ **/tests/**/fixtures +CHANGELOG.md +README.md diff --git a/.prettierrc.js b/.prettierrc.js index 3101e7e..a2a5061 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -2,5 +2,5 @@ const base = require('@homer0/prettier-config'); module.exports = { ...base, - plugins: ['./src'], + plugins: ['./src/index.js'], }; diff --git a/README.md b/README.md index 4a4a40a..53a2ff9 100644 --- a/README.md +++ b/README.md @@ -1004,15 +1004,40 @@ The way you can solve this is by adding a period at the end of the line, which w ### 🤘 Development -As this project is part of the `packages` monorepo, some of the tooling, like ESLint and Husky, are installed on the root's `package.json`. - #### Tasks -| Task | Description | -| ----------- | -------------------------- | -| `test:unit` | Runs the unit tests. | -| `test:e2e` | Runs the functional tests. | -| `test` | Runs all tests. | +| Task | Description | +| ----------- | ------------------------------ | +| `test:unit` | Runs the unit tests. | +| `test:e2e` | Runs the functional tests. | +| `test` | Runs all tests. | +| `lint` | Lint the modified files. | +| `lint:all` | Lint the entire project code. | +| `todo` | List all the pending to-do's. | + +### Repository hooks + +I use [`husky`](https://www.npmjs.com/package/husky) to automatically install the repository hooks so... + +1. The code will be formatted and linted before any commit. +2. The dependencies will be updated after every merge. +3. The tests will run before pushing. + +> ⚠️ When the linter and formatter runs for staged files, if the file is importing Prettier, it may fail due to Prettier being ESM. This is temporary, and the workaround for now is to run `npm run lint:all` and commit with `-n`. + +#### Commits convention + +I use [conventional commits](https://www.conventionalcommits.org) with [`commitlint`](https://commitlint.js.org) in order to support semantic releases. The one that sets it up is actually husky, that installs a script that runs `commitlint` on the `git commit` command. + +The configuration is on the `commitlint` property of the `package.json`. + +### Releases + +I use [`semantic-release`](https://www.npmjs.com/package/semantic-release) and a GitHub action to automatically release on NPM everything that gets merged to main. + +The configuration for `semantic-release` is on `./releaserc` and the workflow for the release is on `./.github/workflow/release.yml`. + +> ⚠️ `semantic-release` requires Node 18 to be installed, so I temporarily removed it form the `package.json` and it's only installed in the GitHub action, before being used. #### Testing @@ -1022,9 +1047,18 @@ The configurations files are `.jestrc-e2e` and `.jestrc-unit`, and the test file In the case of the functional tests, there's a special environment on `./tests/utils` that loads and parses a list of fixture files in order to save them on the global object. In reality, there's only one test file for the functional tests, the one that reads the global object and dynamically generates the `it(...)`: `index.e2e.js`. -### 🐞 Validating bugs -> Yes, since this is in a monorepo (for now), I can't put this on the issue template. +### Linting && Formatting + +I use [ESlint](https://eslint.org) with [my own custom configuration](https://www.npmjs.com/package/@homer0/eslint-plugin) to validate all the JS code. The configuration file for the project code is on `./.eslintrc` and the one for the tests is on `./tests/.eslintrc`. There's also an `./.eslintignore` to exclude some files on the process. The script that runs it is on `./utils/scripts/lint-all`. + +For formatting I use [Prettier](https://prettier.io) with [my custom configuration](https://www.npmjs.com/package/@homer0/prettier-config) and this same plugin. The configuration file for the project code is on `./.prettierrc`. + +### To-Dos + +I use `@todo` comments to write all the pending improvements and fixes, and [Leasot](https://www.npmjs.com/package/leasot) to generate a report. The script that runs it is on `./utils/scripts/todo`. + +### 🐞 Validating bugs You can use the functional tests to validate a scenario in which the plugin is not behaving as you would expect. @@ -1050,7 +1084,9 @@ module.exports = { only: true, jsdocPrintWidth: 70 }; - `only: true` is not a plugin option, but will make the test runner ignore all the other tests, and only run the one you specify. - Below `//# input` you can put any number of comment blocks, in the state you would expect the plugin to pick them. - Below `//# output` you have to put the expected output after formatting the input with the plugin. -- The "input" and "output" are handled as if they were different files, so you can even put functions and real code, it won't be executed though, just formatted. +- The "input" and "output" are handled as if they were different files, so you can even put functions and real code, they won't be executed though, just formatted. + +Then, you can just run run the test for the fixture with `npm run test:e2e`. ## Motivation diff --git a/src/fns/constants.js b/src/fns/constants.js index 225fe14..45c1442 100644 --- a/src/fns/constants.js +++ b/src/fns/constants.js @@ -57,7 +57,8 @@ const getTagsWithDescriptionAsName = () => [ 'remarks', 'summary', 'throws', - 'todo', + 'returns', + 'return', ]; /** * Gets a list of tags that need to be in column format. @@ -75,13 +76,14 @@ const getTagsThatRequireColumns = () => ['template']; * * @returns {string[]} */ -const getTagsWithNameAsDescription = () => [ - 'see', - 'borrows', - 'yields', - 'returns', - 'return', -]; +const getTagsWithNameAsDescription = () => ['see', 'borrows', 'yields', 'todo', 'since']; +/** + * There are certain tags which description cannot be converted into sentences (upper case + * and period at the end). + * + * @returns {string[]} + */ +const getTagsWithDescriptionThatCannotBeSentences = () => ['since']; /** * Gets the list of languages the plugin supports. * @@ -237,6 +239,8 @@ const getSupportedLanguages = () => [ module.exports.getTagsSynonyms = getTagsSynonyms; module.exports.getTagsWithDescriptionAsName = getTagsWithDescriptionAsName; module.exports.getTagsThatRequireColumns = getTagsThatRequireColumns; +module.exports.getTagsWithDescriptionThatCannotBeSentences = + getTagsWithDescriptionThatCannotBeSentences; module.exports.getTagsWithNameAsDescription = getTagsWithNameAsDescription; module.exports.getSupportedLanguages = getSupportedLanguages; module.exports.provider = provider('constants', module.exports); diff --git a/src/fns/formatDescription.js b/src/fns/formatDescription.js index 8660af6..23ffe05 100644 --- a/src/fns/formatDescription.js +++ b/src/fns/formatDescription.js @@ -61,11 +61,11 @@ const findTag = R.curry((targetTag, matchHandlerFn, unmatchHandlerFn, step) => { /** * Generates a reducer handler for specific tags. * - * @param {string | string[]} descriptionProperty The property or properties that make - * the description of a tag. - * @param {boolean} saveIndex Whether or not the tag index should be - * saved as the accumulator `tagIndex` - * property. + * @param {string | string[]} descriptionProperty + * The property or properties that make the description of a tag. + * @param {boolean} saveIndex + * Whether or not the tag index should be saved as the accumulator `tagIndex` + * property. * @returns {FindTagHandlerFn} */ const processTag = (descriptionProperty, saveIndex = false) => { diff --git a/src/fns/prepareTagDescription.js b/src/fns/prepareTagDescription.js index 433264c..d637484 100644 --- a/src/fns/prepareTagDescription.js +++ b/src/fns/prepareTagDescription.js @@ -1,6 +1,9 @@ const R = require('ramda'); const { ensureSentence, hasValidProperty, isTag } = require('./utils'); -const { getTagsWithNameAsDescription } = require('./constants'); +const { + getTagsWithNameAsDescription, + getTagsWithDescriptionThatCannotBeSentences, +} = require('./constants'); const { get, provider } = require('./app'); /** @@ -33,23 +36,26 @@ const prepareTagDescription = (tag) => { const useHasValidProperty = get(hasValidProperty); const useIsStag = get(isTag); const useMakePropertyInstoSentence = get(makePropertyIntoSentence); - return R.when( - R.complement(useIsStag(['example', 'examples'])), - R.compose( - R.when( - useHasValidProperty('description'), - useMakePropertyInstoSentence('description'), - ), - R.when( - R.allPass([ - useIsStag(get(getTagsWithNameAsDescription)()), - useHasValidProperty('name'), - ]), - useMakePropertyInstoSentence('name'), + const useCannotBeSentence = get(getTagsWithDescriptionThatCannotBeSentences)(); + return R.unless( + useIsStag(useCannotBeSentence), + R.when( + R.complement(useIsStag(['example', 'examples'])), + R.compose( + R.when( + useHasValidProperty('description'), + useMakePropertyInstoSentence('description'), + ), + R.when( + R.allPass([ + useIsStag(get(getTagsWithNameAsDescription)()), + useHasValidProperty('name'), + ]), + useMakePropertyInstoSentence('name'), + ), ), ), - tag, - ); + )(tag); }; module.exports.prepareTagDescription = prepareTagDescription; diff --git a/src/fns/render.js b/src/fns/render.js index a7add44..4ff2891 100644 --- a/src/fns/render.js +++ b/src/fns/render.js @@ -127,7 +127,8 @@ const renderTagsInColumns = (columnsWidth, fullWidth, options, tags) => * The options sent to the plugin. * @param {CommentTag[]} tags * The list of tags to render. - * @returns {string[]} The list of lines. + * @returns {string[]} + * The list of lines. */ const tryToRenderTagsInColums = (tagsData, width, options, tags) => R.compose( diff --git a/tests/.eslintrc b/tests/.eslintrc index 59321cc..1aabab3 100644 --- a/tests/.eslintrc +++ b/tests/.eslintrc @@ -1,9 +1,7 @@ { "root": true, "plugins": ["@homer0"], - "extends": [ - "plugin:@homer0/jest-with-prettier" - ], + "extends": ["plugin:@homer0/jest-with-prettier"], "globals": { "global": true, "__dirname": true diff --git a/tests/e2e/fixtures/columns-02.fixture.js b/tests/e2e/fixtures/columns-02.fixture.js index b3b35b2..451befd 100644 --- a/tests/e2e/fixtures/columns-02.fixture.js +++ b/tests/e2e/fixtures/columns-02.fixture.js @@ -12,9 +12,10 @@ module.exports = { /** * @callback IsAvailable * @param {"blue"|"red"|"purple"|"orange"} color Something. - * @returns {boolean} + * @returns {boolean} Some flag property. * @throws {Error} If something goes wrong * @see Something. + * @since 1.0.0 RC1 */ /** @@ -28,8 +29,9 @@ module.exports = { /** * @callback IsAvailable * @param {'blue' | 'red' | 'purple' | 'orange'} color Something. - * @returns {boolean} + * @returns {boolean} Some flag property. * @throws {Error} If something goes wrong. + * @since 1.0.0 RC1 * @see Something. */ diff --git a/tests/e2e/fixtures/inline-01.fixture.js b/tests/e2e/fixtures/inline-01.fixture.js index c9e3025..5f712e5 100644 --- a/tests/e2e/fixtures/inline-01.fixture.js +++ b/tests/e2e/fixtures/inline-01.fixture.js @@ -19,6 +19,7 @@ module.exports = { * @see Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla facilisis enim sed mattis vulputate. Fusce velit dui, commodo eget ex sed, rutrum finibus ipsum. Vivamus dapibus sollicitudin lobortis. * @throws {Error} If something goes wrong * @yields {Object} Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla facilisis enim sed mattis vulputate. Fusce velit dui, commodo eget ex sed, rutrum finibus ipsum. Vivamus dapibus sollicitudin lobortis. + * @todo Refactor how the multiline names are handled. And some extra text to make the line a bit more longer. */ //# output @@ -44,4 +45,6 @@ module.exports = { * @see Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla facilisis enim sed mattis * vulputate. Fusce velit dui, commodo eget ex sed, rutrum finibus ipsum. Vivamus dapibus * sollicitudin lobortis. + * @todo Refactor how the multiline names are handled. And some extra text to make the line a bit + * more longer. */ diff --git a/tests/e2e/fixtures/issue-03.fixture.js b/tests/e2e/fixtures/issue-03.fixture.js new file mode 100644 index 0000000..f4f707a --- /dev/null +++ b/tests/e2e/fixtures/issue-03.fixture.js @@ -0,0 +1,25 @@ +module.exports = { + jsdocPrintWidth: 95, + jsdocUseColumns: false, +}; + +//# input + +/** + * @param {foo} bar baz + * @returns {number} Returns how the node is positioned relatively to the reference node + * according to the bitmask. 0 if reference node and given node are the same. + * @todo Refactor how the multiline names are handled. And some extra text to make the line longer. + */ + +//# output + +/** + * @param {foo} bar + * Baz. + * @returns {number} + * Returns how the node is positioned relatively to the reference node according to the + * bitmask. 0 if reference node and given node are the same. + * @todo Refactor how the multiline names are handled. And some extra text to make the line + * longer. + */ diff --git a/tests/e2e/fixtures/random-01.fixture.js b/tests/e2e/fixtures/random-01.fixture.js index fb120b6..f2d8507 100644 --- a/tests/e2e/fixtures/random-01.fixture.js +++ b/tests/e2e/fixtures/random-01.fixture.js @@ -112,7 +112,8 @@ export function log(name = 'batman', logger) {} * }; * }} newUser * Something else. - * @returns {User} Some description for the return value. + * @returns {User} + * Some description for the return value. * @summary * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec malesuada fermentum nibh, sed * aliquet ante porta a. Nullam blandit posuere fringilla. Nullam vel risus vitae lectus luctus @@ -136,9 +137,9 @@ export function log(name = 'batman', logger) {} /** * @returns A function that is used to analyze the value and the index and determine whether or - * not to increment the count. Return `true` to increment the count, and return `false` - * to keep the count the same. If the predicate is not provided, every value will be - * counted. + * not to increment the count. Return `true` to increment the count, and return + * `false` to keep the count the same. If the predicate is not provided, every value + * will be counted. */ export function foo(predicate) { console.log(predicate); diff --git a/tests/unit/fns/prepareTagDescription.test.js b/tests/unit/fns/prepareTagDescription.test.js index e91b9a4..c97807f 100644 --- a/tests/unit/fns/prepareTagDescription.test.js +++ b/tests/unit/fns/prepareTagDescription.test.js @@ -13,6 +13,17 @@ describe('prepareTagDescription', () => { name: 'something', }, }, + { + it: 'should ignore a tag that cannot be a sentence', + input: { + tag: 'since', + name: 'something', + }, + output: { + tag: 'since', + name: 'something', + }, + }, { it: 'should transform a tag description', input: { diff --git a/utils/scripts/lint-all b/utils/scripts/lint-all index 81e490e..ea6ec3d 100755 --- a/utils/scripts/lint-all +++ b/utils/scripts/lint-all @@ -1,2 +1,3 @@ #!/bin/bash -e -eslint ./ --ext .js +eslint ./ --ext .js && \ +prettier --write . diff --git a/utils/scripts/test-unit b/utils/scripts/test-unit index 23c8367..ba22c8d 100755 --- a/utils/scripts/test-unit +++ b/utils/scripts/test-unit @@ -1,2 +1,2 @@ #!/bin/bash -e -NODE_OPTIONS=--experimental-vm-modules jest -c ./.jestrc-unit.js +NODE_OPTIONS=--experimental-vm-modules jest -c ./.jestrc-unit.js $@