diff --git a/CHANGELOG.md b/CHANGELOG.md index 06d221aa..2a675299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # CHANGELOG -The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release). -You can see it on the [releases page](../../releases). +The changelog is automatically updated using +[semantic-release](https://github.com/semantic-release/semantic-release). You +can see it on the [releases page](../../releases). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 68e33cb7..06e9bf8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,8 +2,8 @@ Thanks for being willing to contribute! -**Working on your first Pull Request?** You can learn how from this _free_ series -[How to Contribute to an Open Source Project on GitHub][egghead] +**Working on your first Pull Request?** You can learn how from this _free_ +series [How to Contribute to an Open Source Project on GitHub][egghead] ## Project setup @@ -21,24 +21,23 @@ Thanks for being willing to contribute! > git branch --set-upstream-to=upstream/master master > ``` > -> This will add the original repository as a "remote" called "upstream," -> Then fetch the git information from that remote, then set your local `master` -> branch to use the upstream master branch whenever you run `git pull`. -> Then you can make all of your pull request branches based on this `master` -> branch. Whenever you want to update your version of `master`, do a regular -> `git pull`. +> This will add the original repository as a "remote" called "upstream," Then +> fetch the git information from that remote, then set your local `master` +> branch to use the upstream master branch whenever you run `git pull`. Then you +> can make all of your pull request branches based on this `master` branch. +> Whenever you want to update your version of `master`, do a regular `git pull`. ## Committing and Pushing changes This project uses [`semantic-release`][semantic-release] to do automatic -releases and generate a changelog based on the commit history. So we follow -[a convention][convention] for commit messages. You don't have to follow this +releases and generate a changelog based on the commit history. So we follow [a +convention][convention] for commit messages. You don't have to follow this convention if you don't want to. Just know that when we merge your commit, we'll probably use "Squash and Merge" so we can change the commit message :) Please make sure to run the tests before you commit your changes. You can run -`npm run test:update` which will update any snapshots that need updating. -Make sure to include those changes (if they exist) in your commit. +`npm run test:update` which will update any snapshots that need updating. Make +sure to include those changes (if they exist) in your commit. ### opt in/out of git hooks @@ -53,10 +52,10 @@ pre-commit ``` One of the things that the git hooks does is automatically format the files you -change. It does this by reformating the entire file and running `git add` on -the file after. This breaks workflows where you're trying to commit portions of -the file only. You can always run your commit with `--no-verify`, but if this -is a bummer to your workflow, you can add an `.opt-out` file with the contents: +change. It does this by reformating the entire file and running `git add` on the +file after. This breaks workflows where you're trying to commit portions of the +file only. You can always run your commit with `--no-verify`, but if this is a +bummer to your workflow, you can add an `.opt-out` file with the contents: ``` autoformat @@ -69,7 +68,9 @@ Please checkout the [the open issues][issues] Also, please watch the repo and respond to questions/bug reports/feature requests! Thanks! -[egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github +[egghead]: + https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github [semantic-release]: https://npmjs.com/package/semantic-release -[convention]: https://github.com/conventional-changelog/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md +[convention]: + https://github.com/conventional-changelog/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md [issues]: https://github.com/hoverinc/hover-javascript/issues diff --git a/README.md b/README.md index 462200fb..203fbf7f 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Or, for `babel`, a `.babelrc` with: Or, for `jest`: ```javascript -const { jest: jestConfig } = require('@hover/javascript/jest'); +const {jest: jestConfig} = require('@hover/javascript/jest') module.exports = Object.assign(jestConfig, { // your overrides here @@ -93,7 +93,7 @@ module.exports = Object.assign(jestConfig, { transform: { '\\.(ts|tsx)$': '/node_modules/ts-jest/preprocessor.js', }, -}); +}) ``` > Note: `hover-scripts` intentionally does not merge things for you when you diff --git a/other/CODE_OF_CONDUCT.md b/other/CODE_OF_CONDUCT.md index 89dcd555..80a98f60 100644 --- a/other/CODE_OF_CONDUCT.md +++ b/other/CODE_OF_CONDUCT.md @@ -20,9 +20,9 @@ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual identity +and orientation. ## Our Standards @@ -52,11 +52,11 @@ Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. -Project maintainers 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, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +Project maintainers 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, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. ## Scope @@ -73,8 +73,9 @@ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kent+coc@doddsfamily.us. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +obligated to maintain confidentiality with regard to the reporter of an +incident. Further details of specific enforcement policies may be posted +separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other @@ -82,8 +83,8 @@ members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ diff --git a/other/MAINTAINING.md b/other/MAINTAINING.md index cf87aabe..28f3a502 100644 --- a/other/MAINTAINING.md +++ b/other/MAINTAINING.md @@ -18,60 +18,67 @@ This is documentation for maintainers of this project. ## Code of Conduct -Please review, understand, and be an example of it. Violations of the code of conduct are -taken seriously, even (especially) for maintainers. +Please review, understand, and be an example of it. Violations of the code of +conduct are taken seriously, even (especially) for maintainers. ## Issues -We want to support and build the community. We do that best by helping people learn to solve -their own problems. We have an issue template and hopefully most folks follow it. If it's -not clear what the issue is, invite them to create a minimal reproduction of what they're trying -to accomplish or the bug they think they've found. +We want to support and build the community. We do that best by helping people +learn to solve their own problems. We have an issue template and hopefully most +folks follow it. If it's not clear what the issue is, invite them to create a +minimal reproduction of what they're trying to accomplish or the bug they think +they've found. Once it's determined that a code change is necessary, point people to -[makeapullrequest.com](http://makeapullrequest.com) and invite them to make a pull request. -If they're the one who needs the feature, they're the one who can build it. If they need -some hand holding and you have time to lend a hand, please do so. It's an investment into -another human being, and an investment into a potential maintainer. +[makeapullrequest.com](http://makeapullrequest.com) and invite them to make a +pull request. If they're the one who needs the feature, they're the one who can +build it. If they need some hand holding and you have time to lend a hand, +please do so. It's an investment into another human being, and an investment +into a potential maintainer. -Remember that this is open source, so the code is not yours, it's ours. If someone needs a change -in the codebase, you don't have to make it happen yourself. Commit as much time to the project -as you want/need to. Nobody can ask any more of you than that. +Remember that this is open source, so the code is not yours, it's ours. If +someone needs a change in the codebase, you don't have to make it happen +yourself. Commit as much time to the project as you want/need to. Nobody can ask +any more of you than that. ## Pull Requests -As a maintainer, you're fine to make your branches on the main repo or on your own fork. Either -way is fine. +As a maintainer, you're fine to make your branches on the main repo or on your +own fork. Either way is fine. -When we receive a pull request, a travis build is kicked off automatically (see the `.travis.yml` -for what runs in the travis build). We avoid merging anything that breaks the travis build. +When we receive a pull request, a travis build is kicked off automatically (see +the `.travis.yml` for what runs in the travis build). We avoid merging anything +that breaks the travis build. -Please review PRs and focus on the code rather than the individual. You never know when this is -someone's first ever PR and we want their experience to be as positive as possible, so be -uplifting and constructive. +Please review PRs and focus on the code rather than the individual. You never +know when this is someone's first ever PR and we want their experience to be as +positive as possible, so be uplifting and constructive. When you merge the pull request, 99% of the time you should use the -[Squash and merge](https://help.github.com/articles/merging-a-pull-request/) feature. This keeps -our git history clean, but more importantly, this allows us to make any necessary changes to the -commit message so we release what we want to release. See the next section on Releases for more -about that. +[Squash and merge](https://help.github.com/articles/merging-a-pull-request/) +feature. This keeps our git history clean, but more importantly, this allows us +to make any necessary changes to the commit message so we release what we want +to release. See the next section on Releases for more about that. ## Release -Our releases are automatic. They happen whenever code lands into `master`. A travis build gets -kicked off and if it's successful, a tool called -[`semantic-release`](https://github.com/semantic-release/semantic-release) is used to -automatically publish a new release to npm as well as a changelog to GitHub. It is only able to -determine the version and whether a release is necessary by the git commit messages. With this -in mind, **please brush up on [the commit message convention][commit] which drives our releases.** +Our releases are automatic. They happen whenever code lands into `master`. A +travis build gets kicked off and if it's successful, a tool called +[`semantic-release`](https://github.com/semantic-release/semantic-release) is +used to automatically publish a new release to npm as well as a changelog to +GitHub. It is only able to determine the version and whether a release is +necessary by the git commit messages. With this in mind, **please brush up on +[the commit message convention][commit] which drives our releases.** -> One important note about this: Please make sure that commit messages do NOT contain the words -> "BREAKING CHANGE" in them unless we want to push a major version. I've been burned by this -> more than once where someone will include "BREAKING CHANGE: None" and it will end up releasing -> a new major version. Not a huge deal honestly, but kind of annoying... +> One important note about this: Please make sure that commit messages do NOT +> contain the words "BREAKING CHANGE" in them unless we want to push a major +> version. I've been burned by this more than once where someone will include +> "BREAKING CHANGE: None" and it will end up releasing a new major version. Not +> a huge deal honestly, but kind of annoying... ## Thanks! Thank you so much for helping to maintain this project! -[commit]: https://github.com/conventional-changelog-archived-repos/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md +[commit]: + https://github.com/conventional-changelog-archived-repos/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md diff --git a/other/manual-releases.md b/other/manual-releases.md index dadf7d38..14a145f2 100644 --- a/other/manual-releases.md +++ b/other/manual-releases.md @@ -6,10 +6,11 @@ -This project has an automated release set up. So things are only released when there are -useful changes in the code that justify a release. But sometimes things get messed up one way or another -and we need to trigger the release ourselves. When this happens, simply bump the number below and commit -that with the following commit message based on your needs: +This project has an automated release set up. So things are only released when +there are useful changes in the code that justify a release. But sometimes +things get messed up one way or another and we need to trigger the release +ourselves. When this happens, simply bump the number below and commit that with +the following commit message based on your needs: **Major** diff --git a/src/__tests__/index.js b/src/__tests__/index.js index c7249e0e..00dab083 100644 --- a/src/__tests__/index.js +++ b/src/__tests__/index.js @@ -1,58 +1,58 @@ -import path from 'path'; -import slash from 'slash'; -import cases from 'jest-in-case'; -import { unquoteSerializer } from '../scripts/__tests__/helpers/serializers'; +import path from 'path' +import slash from 'slash' +import cases from 'jest-in-case' +import {unquoteSerializer} from '../scripts/__tests__/helpers/serializers' -const projectRoot = path.join(__dirname, '../../'); +const projectRoot = path.join(__dirname, '../../') -expect.addSnapshotSerializer(unquoteSerializer); +expect.addSnapshotSerializer(unquoteSerializer) expect.addSnapshotSerializer({ print: val => slash(val.replace(projectRoot, '/')), test: val => typeof val === 'string' && val.includes(projectRoot), -}); +}) cases( 'format', - ({ snapshotLog = false, throws = false, signal = false, args = [] }) => { + ({snapshotLog = false, throws = false, signal = false, args = []}) => { // beforeEach - const { sync: crossSpawnSyncMock } = require('cross-spawn'); - const originalExit = process.exit; - const originalArgv = process.argv; - const originalLog = console.log; - process.exit = jest.fn(); - console.log = jest.fn(); + const {sync: crossSpawnSyncMock} = require('cross-spawn') + const originalExit = process.exit + const originalArgv = process.argv + const originalLog = console.log + process.exit = jest.fn() + console.log = jest.fn() try { // tests - process.argv = ['node', '../', ...args]; - crossSpawnSyncMock.mockClear(); + process.argv = ['node', '../', ...args] + crossSpawnSyncMock.mockClear() if (signal) { - crossSpawnSyncMock.mockReturnValueOnce({ result: 1, signal }); + crossSpawnSyncMock.mockReturnValueOnce({result: 1, signal}) } - require('../'); + require('../') if (snapshotLog) { - expect(console.log.mock.calls).toMatchSnapshot(); + expect(console.log.mock.calls).toMatchSnapshot() } else if (signal) { - expect(process.exit).toHaveBeenCalledTimes(1); - expect(process.exit).toHaveBeenCalledWith(1); - expect(console.log.mock.calls).toMatchSnapshot(); + expect(process.exit).toHaveBeenCalledTimes(1) + expect(process.exit).toHaveBeenCalledWith(1) + expect(console.log.mock.calls).toMatchSnapshot() } else { - expect(crossSpawnSyncMock).toHaveBeenCalledTimes(1); - const [firstCall] = crossSpawnSyncMock.mock.calls; - const [script, calledArgs] = firstCall; - expect([script, ...calledArgs].join(' ')).toMatchSnapshot(); + expect(crossSpawnSyncMock).toHaveBeenCalledTimes(1) + const [firstCall] = crossSpawnSyncMock.mock.calls + const [script, calledArgs] = firstCall + expect([script, ...calledArgs].join(' ')).toMatchSnapshot() } } catch (error) { if (throws) { - expect(error.message).toMatchSnapshot(); + expect(error.message).toMatchSnapshot() } else { - throw error; + throw error } } finally { // afterEach - process.exit = originalExit; - process.argv = originalArgv; - console.log = originalLog; - jest.resetModules(); + process.exit = originalExit + process.argv = originalArgv + console.log = originalLog + jest.resetModules() } }, { @@ -82,6 +82,6 @@ cases( signal: 'SIGBREAK', }, }, -); +) /* eslint complexity:0 */ diff --git a/src/__tests__/utils.js b/src/__tests__/utils.js index d7eb262a..60909155 100644 --- a/src/__tests__/utils.js +++ b/src/__tests__/utils.js @@ -1,79 +1,79 @@ jest.mock('read-pkg-up', () => ({ - sync: jest.fn(() => ({ packageJson: {}, path: '/blah/package.json' })), -})); -jest.mock('which', () => ({ sync: jest.fn(() => {}) })); + sync: jest.fn(() => ({packageJson: {}, path: '/blah/package.json'})), +})) +jest.mock('which', () => ({sync: jest.fn(() => {})})) jest.mock('cosmiconfig', () => { - const cosmiconfigExports = jest.requireActual('cosmiconfig'); - return { ...cosmiconfigExports, cosmiconfigSync: jest.fn() }; -}); + const cosmiconfigExports = jest.requireActual('cosmiconfig') + return {...cosmiconfigExports, cosmiconfigSync: jest.fn()} +}) -let whichSyncMock, readPkgUpSyncMock; +let whichSyncMock, readPkgUpSyncMock beforeEach(() => { - jest.resetModules(); - whichSyncMock = require('which').sync; - readPkgUpSyncMock = require('read-pkg-up').sync; -}); + jest.resetModules() + whichSyncMock = require('which').sync + readPkgUpSyncMock = require('read-pkg-up').sync +}) test('package is the package.json', () => { - const myPkg = { name: 'blah' }; - mockPkg({ package: myPkg }); - expect(require('../utils').pkg).toBe(myPkg); -}); + const myPkg = {name: 'blah'} + mockPkg({package: myPkg}) + expect(require('../utils').pkg).toBe(myPkg) +}) test('appDirectory is the dirname to the package.json', () => { - const pkgPath = '/some/path/to'; - mockPkg({ path: `${pkgPath}/package.json` }); - expect(require('../utils').appDirectory).toBe(pkgPath); -}); + const pkgPath = '/some/path/to' + mockPkg({path: `${pkgPath}/package.json`}) + expect(require('../utils').appDirectory).toBe(pkgPath) +}) test('resolveHoverScripts resolves to src/index.js when in the @hover/javascript package', () => { - mockPkg({ package: { name: '@hover/javascript' } }); + mockPkg({package: {name: '@hover/javascript'}}) expect(require('../utils').resolveHoverScripts()).toBe( require.resolve('../').replace(process.cwd(), '.'), - ); -}); + ) +}) test('resolveHoverScripts resolves to "hover-scripts" if not in the @hover/javascript package', () => { - mockPkg({ package: { name: 'not-@nover/javascript' } }); - whichSyncMock.mockImplementationOnce(() => require.resolve('../')); - expect(require('../utils').resolveHoverScripts()).toBe('hover-scripts'); -}); + mockPkg({package: {name: 'not-@nover/javascript'}}) + whichSyncMock.mockImplementationOnce(() => require.resolve('../')) + expect(require('../utils').resolveHoverScripts()).toBe('hover-scripts') +}) test(`resolveBin resolves to the full path when it's not in $PATH`, () => { expect(require('../utils').resolveBin('cross-env')).toBe( require.resolve('cross-env/src/bin/cross-env').replace(process.cwd(), '.'), - ); -}); + ) +}) test(`resolveBin resolves to the binary if it's in $PATH`, () => { whichSyncMock.mockImplementationOnce(() => require.resolve('cross-env/src/bin/cross-env').replace(process.cwd(), '.'), - ); - expect(require('../utils').resolveBin('cross-env')).toBe('cross-env'); - expect(whichSyncMock).toHaveBeenCalledTimes(1); - expect(whichSyncMock).toHaveBeenCalledWith('cross-env'); -}); + ) + expect(require('../utils').resolveBin('cross-env')).toBe('cross-env') + expect(whichSyncMock).toHaveBeenCalledTimes(1) + expect(whichSyncMock).toHaveBeenCalledWith('cross-env') +}) describe('for windows', () => { - let realpathSync; + let realpathSync beforeEach(() => { - jest.doMock('fs', () => ({ realpathSync: jest.fn() })); - realpathSync = require('fs').realpathSync; - }); + jest.doMock('fs', () => ({realpathSync: jest.fn()})) + realpathSync = require('fs').realpathSync + }) afterEach(() => { - jest.unmock('fs'); - }); + jest.unmock('fs') + }) test('resolveBin resolves to .bin path when which returns a windows-style cmd', () => { - const fullBinPath = '\\project\\node_modules\\.bin\\concurrently.CMD'; - realpathSync.mockImplementation(() => fullBinPath); - expect(require('../utils').resolveBin('concurrently')).toBe(fullBinPath); - expect(realpathSync).toHaveBeenCalledTimes(2); - }); -}); + const fullBinPath = '\\project\\node_modules\\.bin\\concurrently.CMD' + realpathSync.mockImplementation(() => fullBinPath) + expect(require('../utils').resolveBin('concurrently')).toBe(fullBinPath) + expect(realpathSync).toHaveBeenCalledTimes(2) + }) +}) test('getConcurrentlyArgs gives good args to pass to concurrently', () => { expect( @@ -93,79 +93,79 @@ test('getConcurrentlyArgs gives good args to pass to concurrently', () => { i: 'echo i', j: 'echo j', }), - ).toMatchSnapshot(); -}); + ).toMatchSnapshot() +}) test('parseEnv parses the existing environment variable', () => { - const globals = { react: 'React', 'prop-types': 'PropTypes' }; - process.env.BUILD_GLOBALS = JSON.stringify(globals); - expect(require('../utils').parseEnv('BUILD_GLOBALS')).toEqual(globals); - delete process.env.BUILD_GLOBALS; -}); + const globals = {react: 'React', 'prop-types': 'PropTypes'} + process.env.BUILD_GLOBALS = JSON.stringify(globals) + expect(require('../utils').parseEnv('BUILD_GLOBALS')).toEqual(globals) + delete process.env.BUILD_GLOBALS +}) test(`parseEnv returns the default if the environment variable doesn't exist`, () => { - const defaultVal = { hello: 'world' }; + const defaultVal = {hello: 'world'} expect(require('../utils').parseEnv('DOES_NOT_EXIST', defaultVal)).toBe( defaultVal, - ); -}); + ) +}) test('ifAnyDep returns the true argument if true and false argument if false', () => { - mockPkg({ package: { peerDependencies: { react: '*' } } }); - const t = { a: 'b' }; - const f = { c: 'd' }; - expect(require('../utils').ifAnyDep('react', t, f)).toBe(t); - expect(require('../utils').ifAnyDep('preact', t, f)).toBe(f); -}); + mockPkg({package: {peerDependencies: {react: '*'}}}) + const t = {a: 'b'} + const f = {c: 'd'} + expect(require('../utils').ifAnyDep('react', t, f)).toBe(t) + expect(require('../utils').ifAnyDep('preact', t, f)).toBe(f) +}) test('ifAnyDep works with arrays of dependencies', () => { - mockPkg({ package: { peerDependencies: { react: '*' } } }); - const t = { a: 'b' }; - const f = { c: 'd' }; - expect(require('../utils').ifAnyDep(['preact', 'react'], t, f)).toBe(t); - expect(require('../utils').ifAnyDep(['preact', 'webpack'], t, f)).toBe(f); -}); + mockPkg({package: {peerDependencies: {react: '*'}}}) + const t = {a: 'b'} + const f = {c: 'd'} + expect(require('../utils').ifAnyDep(['preact', 'react'], t, f)).toBe(t) + expect(require('../utils').ifAnyDep(['preact', 'webpack'], t, f)).toBe(f) +}) test('ifScript returns the true argument if true and the false argument if false', () => { - mockPkg({ package: { scripts: { build: 'echo build' } } }); - const t = { e: 'f' }; - const f = { g: 'h' }; - expect(require('../utils').ifScript('build', t, f)).toBe(t); - expect(require('../utils').ifScript('lint', t, f)).toBe(f); -}); + mockPkg({package: {scripts: {build: 'echo build'}}}) + const t = {e: 'f'} + const f = {g: 'h'} + expect(require('../utils').ifScript('build', t, f)).toBe(t) + expect(require('../utils').ifScript('lint', t, f)).toBe(f) +}) test('ifFile returns the true argument if true and the false argument if false', () => { - mockPkg({ path: require.resolve('../../package.json') }); - const t = { e: 'f' }; - const f = { g: 'h' }; - expect(require('../utils').ifFile('package.json', t, f)).toBe(t); - expect(require('../utils').ifFile('does-not-exist.blah', t, f)).toBe(f); -}); + mockPkg({path: require.resolve('../../package.json')}) + const t = {e: 'f'} + const f = {g: 'h'} + expect(require('../utils').ifFile('package.json', t, f)).toBe(t) + expect(require('../utils').ifFile('does-not-exist.blah', t, f)).toBe(f) +}) test('hasLocalConfiguration returns false if no local configuration found', () => { - mockCosmiconfig(); + mockCosmiconfig() - expect(require('../utils').hasLocalConfig('module')).toBe(false); -}); + expect(require('../utils').hasLocalConfig('module')).toBe(false) +}) test('hasLocalConfig returns true if a local configuration found', () => { - mockCosmiconfig({ config: {}, filepath: 'path/to/config' }); + mockCosmiconfig({config: {}, filepath: 'path/to/config'}) - expect(require('../utils').hasLocalConfig('module')).toBe(true); -}); + expect(require('../utils').hasLocalConfig('module')).toBe(true) +}) test('hasLocalConfiguration returns true if a local config found and it is empty', () => { - mockCosmiconfig({ isEmpty: true }); + mockCosmiconfig({isEmpty: true}) - expect(require('../utils').hasLocalConfig('module')).toBe(true); -}); + expect(require('../utils').hasLocalConfig('module')).toBe(true) +}) -function mockPkg({ package: pkg = {}, path = '/blah/package.json' }) { - readPkgUpSyncMock.mockImplementationOnce(() => ({ packageJson: pkg, path })); +function mockPkg({package: pkg = {}, path = '/blah/package.json'}) { + readPkgUpSyncMock.mockImplementationOnce(() => ({packageJson: pkg, path})) } function mockCosmiconfig(result = null) { - const { cosmiconfigSync } = require('cosmiconfig'); + const {cosmiconfigSync} = require('cosmiconfig') - cosmiconfigSync.mockImplementationOnce(() => ({ search: () => result })); + cosmiconfigSync.mockImplementationOnce(() => ({search: () => result})) } diff --git a/src/config/babelrc.js b/src/config/babelrc.js index ad4786ae..c2220294 100644 --- a/src/config/babelrc.js +++ b/src/config/babelrc.js @@ -1,28 +1,28 @@ -const browserslist = require('browserslist'); -const semver = require('semver'); +const browserslist = require('browserslist') +const semver = require('semver') -const { ifAnyDep, parseEnv, appDirectory, pkg } = require('../utils'); +const {ifAnyDep, parseEnv, appDirectory, pkg} = require('../utils') -const { BABEL_ENV, NODE_ENV, BUILD_FORMAT } = process.env; -const isTest = (BABEL_ENV || NODE_ENV) === 'test'; -const isPreact = parseEnv('BUILD_PREACT', false); -const isRollup = parseEnv('BUILD_ROLLUP', false); -const isUMD = BUILD_FORMAT === 'umd'; -const isCJS = BUILD_FORMAT === 'cjs'; -const isWebpack = parseEnv('BUILD_WEBPACK', false); -const treeshake = parseEnv('BUILD_TREESHAKE', isRollup || isWebpack); -const alias = parseEnv('BUILD_ALIAS', isPreact ? { react: 'preact' } : null); +const {BABEL_ENV, NODE_ENV, BUILD_FORMAT} = process.env +const isTest = (BABEL_ENV || NODE_ENV) === 'test' +const isPreact = parseEnv('BUILD_PREACT', false) +const isRollup = parseEnv('BUILD_ROLLUP', false) +const isUMD = BUILD_FORMAT === 'umd' +const isCJS = BUILD_FORMAT === 'cjs' +const isWebpack = parseEnv('BUILD_WEBPACK', false) +const treeshake = parseEnv('BUILD_TREESHAKE', isRollup || isWebpack) +const alias = parseEnv('BUILD_ALIAS', isPreact ? {react: 'preact'} : null) const hasBabelRuntimeDep = Boolean( pkg.dependencies && pkg.dependencies['@babel/runtime'], -); +) const RUNTIME_HELPERS_WARN = - 'You should add @babel/runtime as dependency to your package. It will allow reusing "babel helpers" from node_modules rather than bundling their copies into your files.'; + 'You should add @babel/runtime as dependency to your package. It will allow reusing "babel helpers" from node_modules rather than bundling their copies into your files.' if (!treeshake && !hasBabelRuntimeDep && !isTest) { - throw new Error(RUNTIME_HELPERS_WARN); + throw new Error(RUNTIME_HELPERS_WARN) } else if (treeshake && !isUMD && !hasBabelRuntimeDep) { - console.warn(RUNTIME_HELPERS_WARN); + console.warn(RUNTIME_HELPERS_WARN) } /** @@ -30,17 +30,17 @@ if (!treeshake && !hasBabelRuntimeDep && !isTest) { * fallback to the default if don't found custom configuration * @see https://github.com/browserslist/browserslist/blob/master/node.js#L139 */ -const browsersConfig = browserslist.loadConfig({ path: appDirectory }) || [ +const browsersConfig = browserslist.loadConfig({path: appDirectory}) || [ 'ie 10', 'ios 7', -]; +] const envTargets = isTest - ? { node: 'current' } + ? {node: 'current'} : isWebpack || isRollup - ? { browsers: browsersConfig } - : { node: getNodeVersion(pkg) }; -const envOptions = { modules: false, loose: true, targets: envTargets }; + ? {browsers: browsersConfig} + : {node: getNodeVersion(pkg)} +const envOptions = {modules: false, loose: true, targets: envTargets} module.exports = () => ({ presets: [ @@ -49,7 +49,7 @@ module.exports = () => ({ ['react', 'preact'], [ require.resolve('@babel/preset-react'), - { pragma: isPreact ? 'React.h' : undefined }, + {pragma: isPreact ? 'React.h' : undefined}, ], ), ifAnyDep(['flow-bin'], [require.resolve('@babel/preset-flow')]), @@ -57,44 +57,41 @@ module.exports = () => ({ plugins: [ [ require.resolve('@babel/plugin-transform-runtime'), - { useESModules: treeshake && !isCJS }, + {useESModules: treeshake && !isCJS}, ], require.resolve('babel-plugin-macros'), alias ? [ require.resolve('babel-plugin-module-resolver'), - { root: ['./src'], alias }, + {root: ['./src'], alias}, ] : null, [ require.resolve('babel-plugin-transform-react-remove-prop-types'), - isPreact ? { removeImport: true } : { mode: 'unsafe-wrap' }, + isPreact ? {removeImport: true} : {mode: 'unsafe-wrap'}, ], isUMD ? require.resolve('babel-plugin-transform-inline-environment-variables') : null, - [ - require.resolve('@babel/plugin-proposal-class-properties'), - { loose: true }, - ], + [require.resolve('@babel/plugin-proposal-class-properties'), {loose: true}], require.resolve('babel-plugin-minify-dead-code-elimination'), treeshake ? null : require.resolve('@babel/plugin-transform-modules-commonjs'), ].filter(Boolean), -}); +}) -function getNodeVersion({ engines: { node: nodeVersion = '10.13' } = {} }) { +function getNodeVersion({engines: {node: nodeVersion = '10.13'} = {}}) { const oldestVersion = semver .validRange(nodeVersion) .replace(/[>=<|]/g, ' ') .split(' ') .filter(Boolean) - .sort(semver.compare)[0]; + .sort(semver.compare)[0] if (!oldestVersion) { throw new Error( `Unable to determine the oldest version in the range in your package.json at engines.node: "${nodeVersion}". Please attempt to make it less ambiguous.`, - ); + ) } - return oldestVersion; + return oldestVersion } diff --git a/src/config/jest.config.js b/src/config/jest.config.js index ef0db616..237c19fb 100644 --- a/src/config/jest.config.js +++ b/src/config/jest.config.js @@ -1,15 +1,9 @@ -const path = require('path'); -const { - ifAnyDep, - hasAnyDep, - hasFile, - hasPkgProp, - fromRoot, -} = require('../utils'); +const path = require('path') +const {ifAnyDep, hasAnyDep, hasFile, hasPkgProp, fromRoot} = require('../utils') -const here = p => path.join(__dirname, p); +const here = p => path.join(__dirname, p) -const useBuiltInBabelConfig = !hasFile('.babelrc') && !hasPkgProp('babel'); +const useBuiltInBabelConfig = !hasFile('.babelrc') && !hasPkgProp('babel') const ignores = [ '/node_modules/', @@ -18,14 +12,14 @@ const ignores = [ '/__tests__/helpers/', '/__tests__/utils/', '__mocks__', -]; +] -const toGlob = extensions => `*.+(${extensions.join('|')})`; -const testMatchExtensions = ['js', 'jsx', 'ts', 'tsx']; -const testMatchGlob = toGlob(testMatchExtensions); +const toGlob = extensions => `*.+(${extensions.join('|')})` +const testMatchExtensions = ['js', 'jsx', 'ts', 'tsx'] +const testMatchGlob = toGlob(testMatchExtensions) const testMatchSuffixGlob = toGlob( testMatchExtensions.map(extension => 'test.'.concat(extension)), -); +) const jestConfig = { roots: [fromRoot('.')], @@ -55,25 +49,25 @@ const jestConfig = { ], globals: {}, transform: {}, -}; +} if (hasAnyDep('ts-jest')) { - jestConfig.preset = 'ts-jest'; + jestConfig.preset = 'ts-jest' jestConfig.globals['ts-jest'] = { diagnostics: { warnOnly: true, }, - }; + } } if (hasFile('tests/setup-env.js')) { - jestConfig.setupFilesAfterEnv = [fromRoot('tests/setup-env.js')]; + jestConfig.setupFilesAfterEnv = [fromRoot('tests/setup-env.js')] } if (useBuiltInBabelConfig) { Object.assign(jestConfig.transform, { '^.+\\.js$': here('./babel-transform'), - }); + }) } -module.exports = jestConfig; +module.exports = jestConfig diff --git a/src/config/lintstagedrc.js b/src/config/lintstagedrc.js index f10288e5..9e0558c3 100644 --- a/src/config/lintstagedrc.js +++ b/src/config/lintstagedrc.js @@ -1,7 +1,7 @@ -const { resolveHoverScripts, resolveBin } = require('../utils'); +const {resolveHoverScripts, resolveBin} = require('../utils') -const kcdScripts = resolveHoverScripts(); -const doctoc = resolveBin('doctoc'); +const kcdScripts = resolveHoverScripts() +const doctoc = resolveBin('doctoc') module.exports = { 'README.md': [`${doctoc} --maxlevel 3 --notitle`], @@ -10,4 +10,4 @@ module.exports = { `${kcdScripts} lint`, `${kcdScripts} test --findRelatedTests`, ], -}; +} diff --git a/src/config/prettierrc.js b/src/config/prettierrc.js index fd162d84..4cc05b02 100644 --- a/src/config/prettierrc.js +++ b/src/config/prettierrc.js @@ -15,4 +15,4 @@ module.exports = { trailingComma: 'all', useTabs: false, bracketSpacing: true, -}; +} diff --git a/src/config/rollup.config.js b/src/config/rollup.config.js index b2ed58cf..c293de6e 100644 --- a/src/config/rollup.config.js +++ b/src/config/rollup.config.js @@ -1,16 +1,16 @@ -const path = require('path'); -const commonjs = require('@rollup/plugin-commonjs'); -const json = require('@rollup/plugin-json'); -const nodeResolve = require('@rollup/plugin-node-resolve'); -const replace = require('@rollup/plugin-replace'); -const glob = require('glob'); -const camelcase = require('lodash.camelcase'); -const rollupBabel = require('rollup-plugin-babel'); -const { terser } = require('rollup-plugin-terser'); -const nodeBuiltIns = require('rollup-plugin-node-builtins'); -const nodeGlobals = require('rollup-plugin-node-globals'); -const { sizeSnapshot } = require('rollup-plugin-size-snapshot'); -const omit = require('lodash.omit'); +const path = require('path') +const commonjs = require('@rollup/plugin-commonjs') +const json = require('@rollup/plugin-json') +const nodeResolve = require('@rollup/plugin-node-resolve') +const replace = require('@rollup/plugin-replace') +const glob = require('glob') +const camelcase = require('lodash.camelcase') +const rollupBabel = require('rollup-plugin-babel') +const {terser} = require('rollup-plugin-terser') +const nodeBuiltIns = require('rollup-plugin-node-builtins') +const nodeGlobals = require('rollup-plugin-node-globals') +const {sizeSnapshot} = require('rollup-plugin-size-snapshot') +const omit = require('lodash.omit') const { pkg, hasFile, @@ -19,35 +19,35 @@ const { fromRoot, uniq, writeExtraEntry, -} = require('../utils'); +} = require('../utils') -const here = p => path.join(__dirname, p); -const capitalize = s => s[0].toUpperCase() + s.slice(1); +const here = p => path.join(__dirname, p) +const capitalize = s => s[0].toUpperCase() + s.slice(1) -const minify = parseEnv('BUILD_MINIFY', false); -const format = process.env.BUILD_FORMAT; -const isPreact = parseEnv('BUILD_PREACT', false); -const isNode = parseEnv('BUILD_NODE', false); -const name = process.env.BUILD_NAME || capitalize(camelcase(pkg.name)); -const useSizeSnapshot = parseEnv('BUILD_SIZE_SNAPSHOT', false); +const minify = parseEnv('BUILD_MINIFY', false) +const format = process.env.BUILD_FORMAT +const isPreact = parseEnv('BUILD_PREACT', false) +const isNode = parseEnv('BUILD_NODE', false) +const name = process.env.BUILD_NAME || capitalize(camelcase(pkg.name)) +const useSizeSnapshot = parseEnv('BUILD_SIZE_SNAPSHOT', false) -const esm = format === 'esm'; -const umd = format === 'umd'; +const esm = format === 'esm' +const umd = format === 'umd' const defaultGlobals = Object.keys(pkg.peerDependencies || {}).reduce( (deps, dep) => { - deps[dep] = capitalize(camelcase(dep)); - return deps; + deps[dep] = capitalize(camelcase(dep)) + return deps }, {}, -); +) -const deps = Object.keys(pkg.dependencies || {}); -const peerDeps = Object.keys(pkg.peerDependencies || {}); -const defaultExternal = umd ? peerDeps : deps.concat(peerDeps); +const deps = Object.keys(pkg.dependencies || {}) +const peerDeps = Object.keys(pkg.peerDependencies || {}) +const defaultExternal = umd ? peerDeps : deps.concat(peerDeps) -const input = glob.sync(fromRoot(process.env.BUILD_INPUT || 'src/index.js')); -const codeSplitting = input.length > 1; +const input = glob.sync(fromRoot(process.env.BUILD_INPUT || 'src/index.js')) +const codeSplitting = input.length > 1 if ( codeSplitting && @@ -56,42 +56,40 @@ if ( throw new Error( 'Filenames of code-splitted entries should be unique to get deterministic output filenames.' + `\nReceived those: ${input}.`, - ); + ) } -const filenameSuffix = process.env.BUILD_FILENAME_SUFFIX || ''; +const filenameSuffix = process.env.BUILD_FILENAME_SUFFIX || '' const filenamePrefix = - process.env.BUILD_FILENAME_PREFIX || (isPreact ? 'preact/' : ''); + process.env.BUILD_FILENAME_PREFIX || (isPreact ? 'preact/' : '') const globals = parseEnv( 'BUILD_GLOBALS', - isPreact - ? Object.assign(defaultGlobals, { preact: 'preact' }) - : defaultGlobals, -); + isPreact ? Object.assign(defaultGlobals, {preact: 'preact'}) : defaultGlobals, +) const external = parseEnv( 'BUILD_EXTERNAL', isPreact ? defaultExternal.concat(['preact', 'prop-types']) : defaultExternal, -).filter((e, i, arry) => arry.indexOf(e) === i); +).filter((e, i, arry) => arry.indexOf(e) === i) if (isPreact) { - delete globals.react; - delete globals['prop-types']; // TODO: is this necessary? - external.splice(external.indexOf('react'), 1); + delete globals.react + delete globals['prop-types'] // TODO: is this necessary? + external.splice(external.indexOf('react'), 1) } -const externalPattern = new RegExp(`^(${external.join('|')})($|/)`); +const externalPattern = new RegExp(`^(${external.join('|')})($|/)`) function externalPredicate(id) { - const isDep = external.length > 0 && externalPattern.test(id); + const isDep = external.length > 0 && externalPattern.test(id) if (umd) { // for UMD, we want to bundle all non-peer deps - return isDep; + return isDep } // for esm/cjs we want to make all node_modules external // TODO: support bundledDependencies if someone needs it ever... - const isNodeModule = id.includes('node_modules'); - const isRelative = id.startsWith('.'); - return isDep || (!isRelative && !path.isAbsolute(id)) || isNodeModule; + const isNodeModule = id.includes('node_modules') + const isRelative = id.startsWith('.') + return isDep || (!isRelative && !path.isAbsolute(id)) || isNodeModule } const filename = [ @@ -102,41 +100,41 @@ const filename = [ '.js', ] .filter(Boolean) - .join(''); + .join('') -const dirpath = path.join(...[filenamePrefix, 'dist'].filter(Boolean)); +const dirpath = path.join(...[filenamePrefix, 'dist'].filter(Boolean)) const output = [ { name, ...(codeSplitting - ? { dir: path.join(dirpath, format) } - : { file: path.join(dirpath, filename) }), + ? {dir: path.join(dirpath, format)} + : {file: path.join(dirpath, filename)}), format: esm ? 'es' : format, exports: esm ? 'named' : 'auto', globals, }, -]; +] const useBuiltinConfig = !hasFile('.babelrc') && !hasFile('.babelrc.js') && !hasFile('babel.config.js') && - !hasPkgProp('babel'); -const babelPresets = useBuiltinConfig ? [here('../config/babelrc.js')] : []; + !hasPkgProp('babel') +const babelPresets = useBuiltinConfig ? [here('../config/babelrc.js')] : [] const replacements = Object.entries( umd ? process.env : omit(process.env, ['NODE_ENV']), ).reduce((acc, [key, value]) => { - let val; + let val if (value === 'true' || value === 'false' || Number.isInteger(+value)) { - val = value; + val = value } else { - val = JSON.stringify(value); + val = JSON.stringify(value) } - acc[`process.env.${key}`] = val; - return acc; -}, {}); + acc[`process.env.${key}`] = val + return acc +}, {}) module.exports = { input: codeSplitting ? input : input[0], @@ -149,7 +147,7 @@ module.exports = { preferBuiltins: isNode, mainFields: ['module', 'main', 'jsnext', 'browser'], }), - commonjs({ include: 'node_modules/**' }), + commonjs({include: 'node_modules/**'}), json(), rollupBabel({ presets: babelPresets, @@ -157,26 +155,26 @@ module.exports = { runtimeHelpers: useBuiltinConfig, }), replace(replacements), - useSizeSnapshot ? sizeSnapshot({ printInfo: false }) : null, + useSizeSnapshot ? sizeSnapshot({printInfo: false}) : null, minify ? terser() : null, codeSplitting && ((writes = 0) => ({ onwrite() { if (++writes !== input.length) { - return; + return } input .filter(single => single.indexOf('index.js') === -1) .forEach(single => { - const chunk = path.basename(single); + const chunk = path.basename(single) writeExtraEntry(chunk.replace(/\..+$/, ''), { cjs: `${dirpath}/cjs/${chunk}`, esm: `${dirpath}/esm/${chunk}`, - }); - }); + }) + }) }, }))(), ].filter(Boolean), -}; +} diff --git a/src/index.js b/src/index.js index 6dbfbdb0..03c12104 100755 --- a/src/index.js +++ b/src/index.js @@ -1,14 +1,14 @@ #!/usr/bin/env node -let shouldThrow = false; +let shouldThrow = false try { const [major, minor] = process.version .slice(1) .split('.') - .map(Number); + .map(Number) shouldThrow = require(`${process.cwd()}/package.json`).name === '@hover/javascript' && - (major < 10 || (major === 10 && minor < 18)); + (major < 10 || (major === 10 && minor < 18)) } catch (error) { // ignore } @@ -16,7 +16,7 @@ try { if (shouldThrow) { throw new Error( 'You must use Node version 10.18 or greater to run the scripts within @hover/javascript, because we dogfood the untranspiled version of the scripts.', - ); + ) } -require('./run-script'); +require('./run-script') diff --git a/src/run-script.js b/src/run-script.js index 5fd86959..df25160c 100755 --- a/src/run-script.js +++ b/src/run-script.js @@ -1,14 +1,14 @@ -const path = require('path'); -const spawn = require('cross-spawn'); -const glob = require('glob'); +const path = require('path') +const spawn = require('cross-spawn') +const glob = require('glob') -const [executor, ignoredBin, script] = process.argv; +const [executor, ignoredBin, script] = process.argv if (script) { - spawnScript(); + spawnScript() } else { - const scriptsPath = path.join(__dirname, 'scripts/'); - const scriptsAvailable = glob.sync(path.join(__dirname, 'scripts', '*')); + const scriptsPath = path.join(__dirname, 'scripts/') + const scriptsAvailable = glob.sync(path.join(__dirname, 'scripts', '*')) // `glob.sync` returns paths with unix style path separators even on Windows. // So we normalize it before attempting to strip out the scripts path. const scriptsAvailableMessage = scriptsAvailable @@ -21,7 +21,7 @@ if (script) { ) .filter(Boolean) .join('\n ') - .trim(); + .trim() const fullMessage = ` Usage: ${ignoredBin} [script] [--flags] @@ -32,8 +32,8 @@ Options: All options depend on the script. Docs will be improved eventually, but for most scripts you can assume that the args you pass will be forwarded to the respective tool that's being run under the hood. May the force be with you. - `.trim(); - console.log(`\n${fullMessage}\n`); + `.trim() + console.log(`\n${fullMessage}\n`) } function getEnv() { @@ -43,18 +43,18 @@ function getEnv() { .filter(key => process.env[key] !== undefined) .reduce( (envCopy, key) => { - envCopy[key] = process.env[key]; - return envCopy; + envCopy[key] = process.env[key] + return envCopy }, { [`SCRIPTS_${script.toUpperCase()}`]: true, }, - ); + ) } function spawnScript() { // get all the arguments of the script and find the position of our script commands - const args = process.argv.slice(2); + const args = process.argv.slice(2) const scriptIndex = args.findIndex( x => x === 'format' || @@ -63,20 +63,20 @@ function spawnScript() { x === 'test' || x === 'validate' || x === 'build', - ); + ) // Extract the node arguments so we can pass them to node later on - const buildCommand = scriptIndex === -1 ? args[0] : args[scriptIndex]; - const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []; + const buildCommand = scriptIndex === -1 ? args[0] : args[scriptIndex] + const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [] if (!buildCommand) { - throw new Error(`Unknown script "${script}".`); + throw new Error(`Unknown script "${script}".`) } - const relativeScriptPath = path.join(__dirname, './scripts', buildCommand); - const scriptPath = attemptResolve(relativeScriptPath); + const relativeScriptPath = path.join(__dirname, './scripts', buildCommand) + const scriptPath = attemptResolve(relativeScriptPath) if (!scriptPath) { - throw new Error(`Unknown script "${script}".`); + throw new Error(`Unknown script "${script}".`) } // Attempt to strt the script with the passed node arguments @@ -87,12 +87,12 @@ function spawnScript() { stdio: 'inherit', env: getEnv(), }, - ); + ) if (result.signal) { - handleSignal(result); + handleSignal(result) } else { - process.exit(result.status); + process.exit(result.status) } } @@ -102,21 +102,21 @@ function handleSignal(result) { `The script "${script}" failed because the process exited too early. ` + 'This probably means the system ran out of memory or someone called ' + '`kill -9` on the process.', - ); + ) } else if (result.signal === 'SIGTERM') { console.log( `The script "${script}" failed because the process exited too early. ` + 'Someone might have called `kill` or `killall`, or the system could ' + 'be shutting down.', - ); + ) } - process.exit(1); + process.exit(1) } function attemptResolve(...resolveArgs) { try { - return require.resolve(...resolveArgs); + return require.resolve(...resolveArgs) } catch (error) { - return null; + return null } } diff --git a/src/scripts/__tests__/ci-after-success.js b/src/scripts/__tests__/ci-after-success.js index a8a3f8a2..f8c5ebe0 100644 --- a/src/scripts/__tests__/ci-after-success.js +++ b/src/scripts/__tests__/ci-after-success.js @@ -1,7 +1,7 @@ -import cases from 'jest-in-case'; -import { unquoteSerializer } from './helpers/serializers'; +import cases from 'jest-in-case' +import {unquoteSerializer} from './helpers/serializers' -expect.addSnapshotSerializer(unquoteSerializer); +expect.addSnapshotSerializer(unquoteSerializer) cases( 'ci-after-success', @@ -15,40 +15,40 @@ cases( }, }) => { // beforeEach - const { sync: crossSpawnSyncMock } = require('cross-spawn'); - const utils = require('../../utils'); - utils.resolveBin = (modName, { executable = modName } = {}) => executable; + const {sync: crossSpawnSyncMock} = require('cross-spawn') + const utils = require('../../utils') + utils.resolveBin = (modName, {executable = modName} = {}) => executable const originalEnvs = Object.keys(env).map(envKey => { - const orig = process.env[envKey]; - process.env[envKey] = env[envKey]; - return orig; - }); - const originalLog = console.log; - const originalExit = process.exit; - process.exit = jest.fn(); - console.log = jest.fn(); + const orig = process.env[envKey] + process.env[envKey] = env[envKey] + return orig + }) + const originalLog = console.log + const originalExit = process.exit + process.exit = jest.fn() + console.log = jest.fn() // tests if (version) { - utils.pkg.version = version; + utils.pkg.version = version } - utils.hasFile = () => hasCoverageDir; - process.env.SKIP_CODECOV = isOptedOutOfCoverage; - require('../ci-after-success'); + utils.hasFile = () => hasCoverageDir + process.env.SKIP_CODECOV = isOptedOutOfCoverage + require('../ci-after-success') - expect(console.log.mock.calls).toMatchSnapshot(); + expect(console.log.mock.calls).toMatchSnapshot() const commands = crossSpawnSyncMock.mock.calls.map( call => `${call[0]} ${call[1].join(' ')}`, - ); - expect(commands).toMatchSnapshot(); + ) + expect(commands).toMatchSnapshot() // afterEach - process.exit = originalExit; - console.log = originalLog; + process.exit = originalExit + console.log = originalLog Object.keys(originalEnvs).forEach(envKey => { - process.env[envKey] = env[envKey]; - }); - jest.resetModules(); + process.env[envKey] = env[envKey] + }) + jest.resetModules() }, { 'calls concurrently with both scripts when on ci': {}, @@ -87,4 +87,4 @@ cases( version: '1.2.3', }, }, -); +) diff --git a/src/scripts/__tests__/precommit.js b/src/scripts/__tests__/precommit.js index d9530a5e..12f653bc 100644 --- a/src/scripts/__tests__/precommit.js +++ b/src/scripts/__tests__/precommit.js @@ -1,8 +1,8 @@ -import cases from 'jest-in-case'; -import { unquoteSerializer, winPathSerializer } from './helpers/serializers'; +import cases from 'jest-in-case' +import {unquoteSerializer, winPathSerializer} from './helpers/serializers' -expect.addSnapshotSerializer(unquoteSerializer); -expect.addSnapshotSerializer(winPathSerializer); +expect.addSnapshotSerializer(unquoteSerializer) +expect.addSnapshotSerializer(winPathSerializer) cases( 'pre-commit', @@ -13,34 +13,34 @@ cases( hasFile = () => false, }) => { // beforeEach - const { sync: crossSpawnSyncMock } = require('cross-spawn'); - const originalArgv = process.argv; - const originalExit = process.exit; + const {sync: crossSpawnSyncMock} = require('cross-spawn') + const originalArgv = process.argv + const originalExit = process.exit Object.assign(utils, { hasPkgProp, hasFile, - resolveBin: (modName, { executable = modName } = {}) => executable, - }); - process.exit = jest.fn(); + resolveBin: (modName, {executable = modName} = {}) => executable, + }) + process.exit = jest.fn() - process.argv = ['node', '../pre-commit', ...args]; + process.argv = ['node', '../pre-commit', ...args] try { // tests - require('../pre-commit'); - expect(crossSpawnSyncMock).toHaveBeenCalledTimes(2); - const [firstCall, secondCall] = crossSpawnSyncMock.mock.calls; - const [scriptOne, calledArgsOne] = firstCall; - expect([scriptOne, ...calledArgsOne].join(' ')).toMatchSnapshot(); - const [scriptTwo, calledArgsTwo] = secondCall; - expect([scriptTwo, ...calledArgsTwo].join(' ')).toMatchSnapshot(); + require('../pre-commit') + expect(crossSpawnSyncMock).toHaveBeenCalledTimes(2) + const [firstCall, secondCall] = crossSpawnSyncMock.mock.calls + const [scriptOne, calledArgsOne] = firstCall + expect([scriptOne, ...calledArgsOne].join(' ')).toMatchSnapshot() + const [scriptTwo, calledArgsTwo] = secondCall + expect([scriptTwo, ...calledArgsTwo].join(' ')).toMatchSnapshot() } catch (error) { - throw error; + throw error } finally { // afterEach - process.exit = originalExit; - process.argv = originalArgv; - jest.resetModules(); + process.exit = originalExit + process.argv = originalArgv + jest.resetModules() } }, { @@ -61,4 +61,4 @@ cases( args: ['--verbose'], }, }, -); +) diff --git a/src/scripts/build/babel.js b/src/scripts/build/babel.js index 8a30d6cb..8b8630bf 100644 --- a/src/scripts/build/babel.js +++ b/src/scripts/build/babel.js @@ -1,58 +1,58 @@ -const path = require('path'); -const spawn = require('cross-spawn'); -const yargsParser = require('yargs-parser'); -const rimraf = require('rimraf'); -const glob = require('glob'); -const { hasPkgProp, fromRoot, resolveBin, hasFile } = require('../../utils'); +const path = require('path') +const spawn = require('cross-spawn') +const yargsParser = require('yargs-parser') +const rimraf = require('rimraf') +const glob = require('glob') +const {hasPkgProp, fromRoot, resolveBin, hasFile} = require('../../utils') -let args = process.argv.slice(2); -const here = p => path.join(__dirname, p); +let args = process.argv.slice(2) +const here = p => path.join(__dirname, p) -const parsedArgs = yargsParser(args); +const parsedArgs = yargsParser(args) const useBuiltinConfig = !args.includes('--presets') && !hasFile('.babelrc') && !hasFile('.babelrc.js') && !hasFile('babel.config.js') && - !hasPkgProp('babel'); + !hasPkgProp('babel') const config = useBuiltinConfig ? ['--presets', here('../../config/babelrc.js')] - : []; + : [] -const builtInIgnore = '**/__tests__/**,**/__mocks__/**'; +const builtInIgnore = '**/__tests__/**,**/__mocks__/**' -const ignore = args.includes('--ignore') ? [] : ['--ignore', builtInIgnore]; +const ignore = args.includes('--ignore') ? [] : ['--ignore', builtInIgnore] -const copyFiles = args.includes('--no-copy-files') ? [] : ['--copy-files']; +const copyFiles = args.includes('--no-copy-files') ? [] : ['--copy-files'] -const useSpecifiedOutDir = args.includes('--out-dir'); -const builtInOutDir = 'dist'; -const outDir = useSpecifiedOutDir ? [] : ['--out-dir', builtInOutDir]; +const useSpecifiedOutDir = args.includes('--out-dir') +const builtInOutDir = 'dist' +const outDir = useSpecifiedOutDir ? [] : ['--out-dir', builtInOutDir] if (!useSpecifiedOutDir && !args.includes('--no-clean')) { - rimraf.sync(fromRoot('dist')); + rimraf.sync(fromRoot('dist')) } else { - args = args.filter(a => a !== '--no-clean'); + args = args.filter(a => a !== '--no-clean') } const result = spawn.sync( - resolveBin('@babel/cli', { executable: 'babel' }), + resolveBin('@babel/cli', {executable: 'babel'}), [...outDir, ...copyFiles, ...ignore, ...config, 'src'].concat(args), - { stdio: 'inherit' }, -); + {stdio: 'inherit'}, +) // because babel will copy even ignored files, we need to remove the ignored files -const pathToOutDir = fromRoot(parsedArgs.outDir || builtInOutDir); +const pathToOutDir = fromRoot(parsedArgs.outDir || builtInOutDir) const ignoredPatterns = (parsedArgs.ignore || builtInIgnore) .split(',') - .map(pattern => path.join(pathToOutDir, pattern)); + .map(pattern => path.join(pathToOutDir, pattern)) const ignoredFiles = ignoredPatterns.reduce( (all, pattern) => [...all, ...glob.sync(pattern)], [], -); +) ignoredFiles.forEach(ignoredFile => { - rimraf.sync(ignoredFile); -}); + rimraf.sync(ignoredFile) +}) -process.exit(result.status); +process.exit(result.status) diff --git a/src/scripts/ci-after-success.js b/src/scripts/ci-after-success.js index 0d6f7b55..53788f20 100644 --- a/src/scripts/ci-after-success.js +++ b/src/scripts/ci-after-success.js @@ -1,6 +1,6 @@ -const spawn = require('cross-spawn'); +const spawn = require('cross-spawn') -const { TRAVIS_BRANCH, CF_BRANCH } = process.env; +const {TRAVIS_BRANCH, CF_BRANCH} = process.env const { resolveBin, @@ -8,24 +8,24 @@ const { hasFile, pkg, parseEnv, -} = require('../utils'); +} = require('../utils') -const releaseBranches = ['master', 'next', 'next-major', 'beta', 'alpha']; -const branch = CF_BRANCH || TRAVIS_BRANCH; -const isCI = parseEnv('TRAVIS', false) || parseEnv('CI', false); +const releaseBranches = ['master', 'next', 'next-major', 'beta', 'alpha'] +const branch = CF_BRANCH || TRAVIS_BRANCH +const isCI = parseEnv('TRAVIS', false) || parseEnv('CI', false) const autorelease = pkg.version === '0.0.0-semantically-released' && isCI && releaseBranches.includes(branch) && - !parseEnv('TRAVIS_PULL_REQUEST', false); + !parseEnv('TRAVIS_PULL_REQUEST', false) -const reportCoverage = hasFile('coverage') && !parseEnv('SKIP_CODECOV', false); +const reportCoverage = hasFile('coverage') && !parseEnv('SKIP_CODECOV', false) if (!autorelease && !reportCoverage) { console.log( 'No need to autorelease or report coverage. Skipping ci-after-success script...', - ); + ) } else { const result = spawn.sync( resolveBin('concurrently'), @@ -38,10 +38,10 @@ if (!autorelease && !reportCoverage) { ? `echo installing semantic-release && npx -p semantic-release@17 -c 'echo running semantic-release && semantic-release'` : null, }, - { killOthers: false }, + {killOthers: false}, ), - { stdio: 'inherit' }, - ); + {stdio: 'inherit'}, + ) - process.exit(result.status); + process.exit(result.status) } diff --git a/src/scripts/format.js b/src/scripts/format.js index b4823b76..32c8e461 100644 --- a/src/scripts/format.js +++ b/src/scripts/format.js @@ -1,41 +1,41 @@ -const path = require('path'); -const spawn = require('cross-spawn'); -const yargsParser = require('yargs-parser'); -const { resolveBin, hasFile, hasLocalConfig } = require('../utils'); +const path = require('path') +const spawn = require('cross-spawn') +const yargsParser = require('yargs-parser') +const {resolveBin, hasFile, hasLocalConfig} = require('../utils') -const args = process.argv.slice(2); -const parsedArgs = yargsParser(args); +const args = process.argv.slice(2) +const parsedArgs = yargsParser(args) -const here = p => path.join(__dirname, p); -const hereRelative = p => here(p).replace(process.cwd(), '.'); +const here = p => path.join(__dirname, p) +const hereRelative = p => here(p).replace(process.cwd(), '.') const useBuiltinConfig = - !args.includes('--config') && !hasLocalConfig('prettier'); + !args.includes('--config') && !hasLocalConfig('prettier') const config = useBuiltinConfig ? ['--config', hereRelative('../config/prettierrc.js')] - : []; + : [] const useBuiltinIgnore = - !args.includes('--ignore-path') && !hasFile('.prettierignore'); + !args.includes('--ignore-path') && !hasFile('.prettierignore') const ignore = useBuiltinIgnore ? ['--ignore-path', hereRelative('../config/prettierignore')] - : []; + : [] -const write = args.includes('--no-write') ? [] : ['--write']; +const write = args.includes('--no-write') ? [] : ['--write'] // this ensures that when running format as a pre-commit hook and we get // the full file path, we make that non-absolute so it is treated as a glob, // This way the prettierignore will be applied -const relativeArgs = args.map(a => a.replace(`${process.cwd()}/`, '')); +const relativeArgs = args.map(a => a.replace(`${process.cwd()}/`, '')) const filesToApply = parsedArgs._.length ? [] - : ['**/*.+(js|json|less|css|ts|tsx|md|mdx)']; + : ['**/*.+(js|json|less|css|ts|tsx|md|mdx)'] const result = spawn.sync( resolveBin('prettier'), [...config, ...ignore, ...write, ...filesToApply].concat(relativeArgs), - { stdio: 'inherit' }, -); + {stdio: 'inherit'}, +) -process.exit(result.status); +process.exit(result.status) diff --git a/src/scripts/pre-commit.js b/src/scripts/pre-commit.js index 32864171..17733caa 100644 --- a/src/scripts/pre-commit.js +++ b/src/scripts/pre-commit.js @@ -1,34 +1,34 @@ -const path = require('path'); -const spawn = require('cross-spawn'); -const { hasPkgProp, hasFile, resolveBin } = require('../utils'); +const path = require('path') +const spawn = require('cross-spawn') +const {hasPkgProp, hasFile, resolveBin} = require('../utils') -const here = p => path.join(__dirname, p); -const hereRelative = p => here(p).replace(process.cwd(), '.'); +const here = p => path.join(__dirname, p) +const hereRelative = p => here(p).replace(process.cwd(), '.') -const args = process.argv.slice(2); +const args = process.argv.slice(2) const useBuiltInConfig = !args.includes('--config') && !hasFile('.lintstagedrc') && !hasFile('lint-staged.config.js') && - !hasPkgProp('lint-staged'); + !hasPkgProp('lint-staged') const config = useBuiltInConfig ? ['--config', hereRelative('../config/lintstagedrc.js')] - : []; + : [] const lintStagedResult = spawn.sync( resolveBin('lint-staged'), [...config, ...args], - { stdio: 'inherit' }, -); + {stdio: 'inherit'}, +) if (lintStagedResult.status === 0) { const validateResult = spawn.sync('npm', ['run', 'validate'], { stdio: 'inherit', - }); + }) - process.exit(validateResult.status); + process.exit(validateResult.status) } else { - process.exit(lintStagedResult.status); + process.exit(lintStagedResult.status) } diff --git a/src/utils.js b/src/utils.js index 6d6087ce..cf2c3709 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,89 +1,86 @@ -const fs = require('fs'); -const path = require('path'); -const rimraf = require('rimraf'); -const mkdirp = require('mkdirp'); -const arrify = require('arrify'); -const has = require('lodash.has'); -const readPkgUp = require('read-pkg-up'); -const which = require('which'); -const { cosmiconfigSync } = require('cosmiconfig'); - -const { packageJson: pkg, path: pkgPath } = readPkgUp.sync({ +const fs = require('fs') +const path = require('path') +const rimraf = require('rimraf') +const mkdirp = require('mkdirp') +const arrify = require('arrify') +const has = require('lodash.has') +const readPkgUp = require('read-pkg-up') +const which = require('which') +const {cosmiconfigSync} = require('cosmiconfig') + +const {packageJson: pkg, path: pkgPath} = readPkgUp.sync({ cwd: fs.realpathSync(process.cwd()), -}); -const appDirectory = path.dirname(pkgPath); +}) +const appDirectory = path.dirname(pkgPath) function resolveHoverScripts() { if (pkg.name === '@hover/javascript') { - return require.resolve('./').replace(process.cwd(), '.'); + return require.resolve('./').replace(process.cwd(), '.') } - return resolveBin('hover-scripts'); + return resolveBin('hover-scripts') } // eslint-disable-next-line complexity -function resolveBin( - modName, - { executable = modName, cwd = process.cwd() } = {}, -) { - let pathFromWhich; +function resolveBin(modName, {executable = modName, cwd = process.cwd()} = {}) { + let pathFromWhich try { - pathFromWhich = fs.realpathSync(which.sync(executable)); - if (pathFromWhich && pathFromWhich.includes('.CMD')) return pathFromWhich; + pathFromWhich = fs.realpathSync(which.sync(executable)) + if (pathFromWhich && pathFromWhich.includes('.CMD')) return pathFromWhich } catch (_error) { // ignore _error } try { - const modPkgPath = require.resolve(`${modName}/package.json`); - const modPkgDir = path.dirname(modPkgPath); - const { bin } = require(modPkgPath); - const binPath = typeof bin === 'string' ? bin : bin[executable]; - const fullPathToBin = path.join(modPkgDir, binPath); + const modPkgPath = require.resolve(`${modName}/package.json`) + const modPkgDir = path.dirname(modPkgPath) + const {bin} = require(modPkgPath) + const binPath = typeof bin === 'string' ? bin : bin[executable] + const fullPathToBin = path.join(modPkgDir, binPath) if (fullPathToBin === pathFromWhich) { - return executable; + return executable } - return fullPathToBin.replace(cwd, '.'); + return fullPathToBin.replace(cwd, '.') } catch (error) { if (pathFromWhich) { - return executable; + return executable } - throw error; + throw error } } -const fromRoot = (...p) => path.join(appDirectory, ...p); -const hasFile = (...p) => fs.existsSync(fromRoot(...p)); +const fromRoot = (...p) => path.join(appDirectory, ...p) +const hasFile = (...p) => fs.existsSync(fromRoot(...p)) const ifFile = (files, t, f) => - arrify(files).some(file => hasFile(file)) ? t : f; + arrify(files).some(file => hasFile(file)) ? t : f -const hasPkgProp = props => arrify(props).some(prop => has(pkg, prop)); +const hasPkgProp = props => arrify(props).some(prop => has(pkg, prop)) const hasPkgSubProp = pkgProp => props => - hasPkgProp(arrify(props).map(p => `${pkgProp}.${p}`)); + hasPkgProp(arrify(props).map(p => `${pkgProp}.${p}`)) const ifPkgSubProp = pkgProp => (props, t, f) => - hasPkgSubProp(pkgProp)(props) ? t : f; + hasPkgSubProp(pkgProp)(props) ? t : f -const hasScript = hasPkgSubProp('scripts'); -const hasPeerDep = hasPkgSubProp('peerDependencies'); -const hasDep = hasPkgSubProp('dependencies'); -const hasDevDep = hasPkgSubProp('devDependencies'); -const hasAnyDep = args => [hasDep, hasDevDep, hasPeerDep].some(fn => fn(args)); +const hasScript = hasPkgSubProp('scripts') +const hasPeerDep = hasPkgSubProp('peerDependencies') +const hasDep = hasPkgSubProp('dependencies') +const hasDevDep = hasPkgSubProp('devDependencies') +const hasAnyDep = args => [hasDep, hasDevDep, hasPeerDep].some(fn => fn(args)) -const ifPeerDep = ifPkgSubProp('peerDependencies'); -const ifDep = ifPkgSubProp('dependencies'); -const ifDevDep = ifPkgSubProp('devDependencies'); -const ifAnyDep = (deps, t, f) => (hasAnyDep(arrify(deps)) ? t : f); -const ifScript = ifPkgSubProp('scripts'); +const ifPeerDep = ifPkgSubProp('peerDependencies') +const ifDep = ifPkgSubProp('dependencies') +const ifDevDep = ifPkgSubProp('devDependencies') +const ifAnyDep = (deps, t, f) => (hasAnyDep(arrify(deps)) ? t : f) +const ifScript = ifPkgSubProp('scripts') function parseEnv(name, def) { if (envIsSet(name)) { try { - return JSON.parse(process.env[name]); + return JSON.parse(process.env[name]) } catch (err) { - return process.env[name]; + return process.env[name] } } - return def; + return def } function envIsSet(name) { @@ -91,10 +88,10 @@ function envIsSet(name) { process.env.hasOwnProperty(name) && process.env[name] && process.env[name] !== 'undefined' - ); + ) } -function getConcurrentlyArgs(scripts, { killOthers = true } = {}) { +function getConcurrentlyArgs(scripts, {killOthers = true} = {}) { const colors = [ 'bgBlue', 'bgGreen', @@ -104,20 +101,20 @@ function getConcurrentlyArgs(scripts, { killOthers = true } = {}) { 'bgRed', 'bgBlack', 'bgYellow', - ]; + ] scripts = Object.entries(scripts).reduce((all, [name, script]) => { if (script) { - all[name] = script; + all[name] = script } - return all; - }, {}); + return all + }, {}) const prefixColors = Object.keys(scripts) .reduce( (pColors, _s, i) => pColors.concat([`${colors[i % colors.length]}.bold.reset`]), [], ) - .join(','); + .join(',') // prettier-ignore return [ @@ -130,17 +127,17 @@ function getConcurrentlyArgs(scripts, { killOthers = true } = {}) { } function uniq(arr) { - return Array.from(new Set(arr)); + return Array.from(new Set(arr)) } -function writeExtraEntry(name, { cjs, esm }, clean = true) { +function writeExtraEntry(name, {cjs, esm}, clean = true) { if (clean) { - rimraf.sync(fromRoot(name)); + rimraf.sync(fromRoot(name)) } - mkdirp.sync(fromRoot(name)); + mkdirp.sync(fromRoot(name)) - const pkgJson = fromRoot(`${name}/package.json`); - const entryDir = fromRoot(name); + const pkgJson = fromRoot(`${name}/package.json`) + const entryDir = fromRoot(name) fs.writeFileSync( pkgJson, @@ -153,14 +150,14 @@ function writeExtraEntry(name, { cjs, esm }, clean = true) { null, 2, ), - ); + ) } function hasLocalConfig(moduleName, searchOptions = {}) { - const explorerSync = cosmiconfigSync(moduleName, searchOptions); - const result = explorerSync.search(pkgPath); + const explorerSync = cosmiconfigSync(moduleName, searchOptions) + const result = explorerSync.search(pkgPath) - return result !== null; + return result !== null } module.exports = { @@ -184,4 +181,4 @@ module.exports = { resolveHoverScripts, uniq, writeExtraEntry, -}; +}