From 550b6dfdb02920218edc328fef3937cd8382f0a7 Mon Sep 17 00:00:00 2001 From: Jianhua Cheng Date: Sat, 12 Jan 2019 15:26:19 +0800 Subject: [PATCH] refactor: use `yargs` to organize the scripts into commands BREAKING CHANGE: the ways to run packages-scripts changed * replace `src`, `dist` with shared variables --- .github/PULL_REQUEST_TEMPLATE.md | 8 +- other/CODE_OF_CONDUCT.md | 32 +-- other/MAINTAINING.md | 10 +- package.json | 9 +- src/__tests__/__snapshots__/index.js.snap | 48 ---- src/__tests__/index.js | 84 ------- src/cmds/_internal/utils.js | 17 ++ src/cmds/bundle.js | 75 +++++++ src/cmds/compile.js | 62 ++++++ src/cmds/contributors.js | 20 ++ src/cmds/format.js | 46 ++++ src/cmds/lint.js | 26 +++ src/cmds/precommit.js | 22 ++ src/cmds/prepublish-only.js | 21 ++ src/cmds/test.js | 26 +++ src/cmds/travis-after-success.js | 7 + src/cmds/validate.js | 21 ++ src/config/babel-config.js | 4 +- src/config/babel-transform.js | 11 +- src/config/jest.config.js | 22 +- src/config/rollup.config.js | 20 +- src/index.js | 14 +- src/options.js | 7 + .../__tests__/__snapshots__/format.js.snap | 6 +- .../__tests__/__snapshots__/test.js.snap | 8 +- src/scripts/__tests__/contributors.js | 4 +- src/scripts/__tests__/format.js | 4 +- src/scripts/__tests__/lint.js | 9 +- src/scripts/__tests__/precommit.js | 4 +- src/scripts/__tests__/test.js | 2 +- src/scripts/__tests__/validate.js | 6 +- src/scripts/_internal/prepare-npm.js | 0 src/scripts/build/babel.js | 44 ++-- src/scripts/build/index.js | 9 - src/scripts/build/rollup.js | 209 +++++++++--------- src/scripts/contributors.js | 20 +- src/scripts/format.js | 89 ++++---- src/scripts/lint.js | 70 +++--- src/scripts/precommit.js | 55 ++--- src/scripts/prepublish-only.js | 51 +++++ src/scripts/test.js | 32 +-- src/scripts/validate.js | 44 ++-- src/utils/babel.js | 12 +- 43 files changed, 793 insertions(+), 497 deletions(-) delete mode 100644 src/__tests__/__snapshots__/index.js.snap delete mode 100644 src/__tests__/index.js create mode 100644 src/cmds/_internal/utils.js create mode 100644 src/cmds/bundle.js create mode 100644 src/cmds/compile.js create mode 100644 src/cmds/contributors.js create mode 100644 src/cmds/format.js create mode 100644 src/cmds/lint.js create mode 100644 src/cmds/precommit.js create mode 100644 src/cmds/prepublish-only.js create mode 100644 src/cmds/test.js create mode 100644 src/cmds/travis-after-success.js create mode 100644 src/cmds/validate.js create mode 100644 src/options.js create mode 100644 src/scripts/_internal/prepare-npm.js delete mode 100644 src/scripts/build/index.js create mode 100644 src/scripts/prepublish-only.js diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1e1f510..aa0dc2b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -34,9 +34,9 @@ merge of your pull request! -- [ ] Documentation -- [ ] Tests -- [ ] Ready to be merged -- [ ] Added myself to contributors table +* [ ] Documentation +* [ ] Tests +* [ ] Ready to be merged +* [ ] Added myself to contributors table diff --git a/other/CODE_OF_CONDUCT.md b/other/CODE_OF_CONDUCT.md index 89dcd55..a2e9bbc 100644 --- a/other/CODE_OF_CONDUCT.md +++ b/other/CODE_OF_CONDUCT.md @@ -6,12 +6,12 @@ **Table of Contents** -- [Our Pledge](#our-pledge) -- [Our Standards](#our-standards) -- [Our Responsibilities](#our-responsibilities) -- [Scope](#scope) -- [Enforcement](#enforcement) -- [Attribution](#attribution) +* [Our Pledge](#our-pledge) +* [Our Standards](#our-standards) +* [Our Responsibilities](#our-responsibilities) +* [Scope](#scope) +* [Enforcement](#enforcement) +* [Attribution](#attribution) @@ -29,21 +29,21 @@ orientation. Examples of behavior that contributes to creating a positive environment include: -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members Examples of unacceptable behavior by participants include: -- The use of sexualized language or imagery and unwelcome sexual attention or +* The use of sexualized language or imagery and unwelcome sexual attention or advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a +* Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities diff --git a/other/MAINTAINING.md b/other/MAINTAINING.md index cf87aab..b2b551e 100644 --- a/other/MAINTAINING.md +++ b/other/MAINTAINING.md @@ -6,11 +6,11 @@ **Table of Contents** -- [Code of Conduct](#code-of-conduct) -- [Issues](#issues) -- [Pull Requests](#pull-requests) -- [Release](#release) -- [Thanks!](#thanks) +* [Code of Conduct](#code-of-conduct) +* [Issues](#issues) +* [Pull Requests](#pull-requests) +* [Release](#release) +* [Thanks!](#thanks) diff --git a/package.json b/package.json index 7790c3a..f025fe5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "add-contributor": "node src contributors add", "test": "node src test", "test:update": "node src test --updateSnapshot", - "build": "node src build", + "build": "node src compile", "lint": "node src lint", "format": "node src format", "validate": "node src validate", @@ -28,7 +28,7 @@ "jest.js" ], "keywords": [], - "author": "Jianhua Cheng (http://chengjianhua.github.io/)", + "author": "Jianhua Cheng (https://chengjianhua.github.io/)", "license": "MIT", "dependencies": { "@babel/cli": "^7.1.5", @@ -49,6 +49,7 @@ "babel-plugin-transform-inline-environment-variables": "^0.4.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.13", "browserslist": "^4.0.0", + "chalk": "^2.4.2", "concurrently": "^3.5.1", "cosmiconfig": "^5.0.7", "cross-env": "^5.1.4", @@ -58,7 +59,8 @@ "eslint": "^5.0.0", "eslint-config-kentcdodds": "^14.0.0", "eslint-config-prettier": "^2.9.0", - "glob": "^7.1.2", + "fs-extra": "^7.0.1", + "glob": "^7.1.3", "husky": "^0.14.3", "is-ci": "^1.1.0", "jest": "^23.4.2", @@ -83,6 +85,7 @@ "rollup-plugin-terser": "^1.0.1", "semver": "^5.5.1", "which": "^1.3.0", + "yargs": "^12.0.5", "yargs-parser": "^10.0.0" }, "eslintConfig": { diff --git a/src/__tests__/__snapshots__/index.js.snap b/src/__tests__/__snapshots__/index.js.snap deleted file mode 100644 index 3922bbb..0000000 --- a/src/__tests__/__snapshots__/index.js.snap +++ /dev/null @@ -1,48 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`format calls node with the script path and args 1`] = `node /src/scripts/test.js --no-watch`; - -exports[`format does not log for other signals 1`] = `Array []`; - -exports[`format logs for SIGKILL signal 1`] = ` -Array [ - Array [ - The script "lint" failed because the process exited too early. This probably means the system ran out of memory or someone called \`kill -9\` on the process., - ], -] -`; - -exports[`format logs for SIGTERM signal 1`] = ` -Array [ - Array [ - The script "build" failed because the process exited too early. Someone might have called \`kill\` or \`killall\`, or the system could be shutting down., - ], -] -`; - -exports[`format logs help with no args 1`] = ` -Array [ - Array [ - -Usage: ../ [script] [--flags] - -Available Scripts: - build - contributors - format - lint - precommit - test - travis-after-success - validate - -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. -, - ], -] -`; - -exports[`format throws unknown script 1`] = `Unknown script "unknown-script".`; diff --git a/src/__tests__/index.js b/src/__tests__/index.js deleted file mode 100644 index c5af433..0000000 --- a/src/__tests__/index.js +++ /dev/null @@ -1,84 +0,0 @@ -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, '../../') - -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 = []}) => { - // 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() - try { - // tests - process.argv = ['node', '../', ...args] - crossSpawnSyncMock.mockClear() - if (signal) { - crossSpawnSyncMock.mockReturnValueOnce({result: 1, signal}) - } - require('../') - if (snapshotLog) { - 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() - } else { - 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() - } else { - throw error - } - } finally { - // afterEach - process.exit = originalExit - process.argv = originalArgv - console.log = originalLog - jest.resetModules() - } - }, - { - 'calls node with the script path and args': { - args: ['test', '--no-watch'], - }, - 'throws unknown script': { - args: ['unknown-script'], - throws: true, - }, - 'logs help with no args': { - snapshotLog: true, - }, - 'logs for SIGKILL signal': { - args: ['lint'], - signal: 'SIGKILL', - }, - 'logs for SIGTERM signal': { - args: ['build'], - signal: 'SIGTERM', - }, - 'does not log for other signals': { - args: ['test'], - signal: 'SIGBREAK', - }, - }, -) - -/* eslint complexity:0 */ diff --git a/src/cmds/_internal/utils.js b/src/cmds/_internal/utils.js new file mode 100644 index 0000000..59d8be7 --- /dev/null +++ b/src/cmds/_internal/utils.js @@ -0,0 +1,17 @@ +function cleanCommandArgs(commandName, rawArgv) { + const args = process.argv.slice(2) + const input = [...rawArgv._] + // if executing this script by another cli program, shift `precommit` itself + if (args[0] === commandName) { + args.shift() + input.shift() + } + + const normalizedArgs = {argv: {...rawArgv, _: input}, args} + + return normalizedArgs +} + +module.exports = { + cleanCommandArgv: cleanCommandArgs, +} diff --git a/src/cmds/bundle.js b/src/cmds/bundle.js new file mode 100644 index 0000000..b3b3eca --- /dev/null +++ b/src/cmds/bundle.js @@ -0,0 +1,75 @@ +exports.command = 'bundle' + +exports.describe = + 'Use rollup to compile and bundle source files through a entry file' + +exports.builder = yargs => { + const options = { + 'react-native': { + type: 'boolean', + describe: 'Target to React Native execution environment', + defaultDescription: 'false', + }, + node: { + type: 'boolean', + describe: 'Target to Node.js execution environment', + defaultDescription: 'false', + }, + config: { + type: 'string', + describe: 'Pick the rollup configuration file passed to rollup cli', + defaultDescription: 'rollup.config.js', + }, + 'size-snapshot': { + type: 'boolean', + describe: + 'Enable the output in terminal about the size snapshot of bundles', + default: true, + defaultDescription: 'true', + }, + watch: { + type: 'boolean', + describe: 'Enable watching changes to bundle', + defaultDescription: 'false', + }, + formats: { + type: 'string', + array: true, + defaultDescription: ['esm', 'cjs', 'umd', 'umd.min'], + default: ['esm', 'cjs', 'umd', 'umd.min'], + }, + environment: { + type: 'string', + describe: 'The `--environment` passed to rollup cli', + }, + preact: { + type: 'boolean', + describe: 'The project use preact instead of react', + }, + clean: { + type: 'boolean', + describe: `Don't clean output directory before bundling`, + default: true, + defaultDescription: 'true', + }, + 'add-preact-entry': { + type: 'boolean', + describe: `Write an extra entry \`preact\` into package.json`, + default: true, + defaultDescription: 'true', + }, + // sourcemap: { + // type: 'boolean', + // describe: `The \`--sourcemap\` passed to rollup cli`, + // defaultDescription: 'false', + // }, + } + + return yargs.options(options) +} + +exports.handler = argv => { + console.log('Bundling...') + + require('../scripts/build/rollup')(argv) +} diff --git a/src/cmds/compile.js b/src/cmds/compile.js new file mode 100644 index 0000000..c6b03aa --- /dev/null +++ b/src/cmds/compile.js @@ -0,0 +1,62 @@ +const chalk = require('chalk') +const debug = require('debug')('packages-scripts') + +const {cleanCommandArgv} = require('./_internal/utils') + +exports.command = `compile` + +exports.describe = + 'Only transpile the source files to another directory with the same structure' + +const COMMAND_NAME = 'compile' + +exports.builder = yargs => { + const options = { + 'out-dir': { + type: 'string', + alias: 'd', + requiresArg: true, + describe: `The \`--out-dir\` passed to @babel/cli, the output directory`, + default: 'dist', + defaultDescription: 'dist', + }, + 'delete-dir-on-start': { + type: 'boolean', + describe: `The \`--delete-dir-on-start\` passed to @babel/cli`, + default: true, + defaultDescription: 'true', + }, + // clean: { + // type: 'boolean', + // describe: `Don't clean the output directory before compiling`, + // default: true, + // defaultDescription: 'true', + // }, + // ignore: { + // type: 'string', + // default: '**/__tests__/**/*,**/__mocks__/**/*', + // defaultDescription: `The \`--ignore\` passed to @babel/cli`, + // }, + // 'copy-files': { + // type: 'boolean', + // default: true, + // defaultDescription: 'true', + // }, + } + + yargs.options(options) +} + +exports.handler = rawArgv => { + const {argv, args} = cleanCommandArgv(COMMAND_NAME, rawArgv) + const normalizedArgs = {...argv, input: argv._, args} + + debug('compile args %o', {argv, normalizedArgs}) + console.log( + chalk`Compiling {bold.white ${normalizedArgs.input.join(', ')}} ...`, + ) + + const result = require('../scripts/build/babel')(normalizedArgs) + + process.exit(result.status) +} diff --git a/src/cmds/contributors.js b/src/cmds/contributors.js new file mode 100644 index 0000000..94706d9 --- /dev/null +++ b/src/cmds/contributors.js @@ -0,0 +1,20 @@ +const debug = require('debug')('packages-scripts') + +const utils = require('./_internal/utils') + +exports.command = `contributors` + +const COMMAND_NAME = 'contributors' + +exports.describe = 'Operate contributors by `all-contributors-cli`' + +exports.builder = () => {} + +exports.handler = rawArgv => { + const {args} = utils.cleanCommandArgv(COMMAND_NAME, rawArgv) + + debug('contributors args %o', {argv: rawArgv, args}) + const result = require('../scripts/contributors')({args}) + + process.exit(result.status) +} diff --git a/src/cmds/format.js b/src/cmds/format.js new file mode 100644 index 0000000..1a41ce1 --- /dev/null +++ b/src/cmds/format.js @@ -0,0 +1,46 @@ +const debug = require('debug')('packages-scripts') + +const utils = require('./_internal/utils') + +const COMMAND_NAME = 'format' + +exports.command = `format` + +exports.describe = 'Auto format source files with prettier' + +exports.builder = yargs => { + const options = { + config: { + type: 'string', + requiresArg: true, + describe: `The \`--config\` passed to prettier, the prettier configuration to be used`, + defaultDescription: 'packages-scripts/prettier.js', + default: undefined, + }, + 'ignore-path': { + type: 'string', + describe: 'The path of .prettierignore file', + defaultDescription: 'packages-scripts/dist/configs/prettierignore', + requiresArg: true, + }, + write: { + type: 'boolean', + describe: `The \`--write\` passed to prettier, write changes to input files directly`, + default: true, + defaultDescription: 'true', + }, + } + + yargs.options(options) +} + +exports.handler = rawArgv => { + const {argv, args} = utils.cleanCommandArgv(COMMAND_NAME, rawArgv) + const normalizedArgs = {...argv, input: argv._, args} + + debug('precommit args %o', {argv, normalizedArgs}) + + const result = require('../scripts/format')(normalizedArgs) + + process.exit(result.status) +} diff --git a/src/cmds/lint.js b/src/cmds/lint.js new file mode 100644 index 0000000..b431988 --- /dev/null +++ b/src/cmds/lint.js @@ -0,0 +1,26 @@ +const debug = require('debug')('packages-scripts') + +exports.command = `lint` + +const COMMAND_NAME = 'lint' + +exports.describe = 'Lint source codes by `eslint`' + +exports.builder = () => {} + +exports.handler = ({_: restArgs, ...argv}) => { + const args = process.argv.slice(2) + const input = [...restArgs] + // if executing this script by another cli program, shift `compile` itself + if (args[0] === COMMAND_NAME) { + args.shift() + input.shift() + } + + const normalizedArgs = {...argv, input, args} + + debug('lint args %o', {argv, normalizedArgs}) + const result = require('../scripts/lint')(normalizedArgs) + + process.exit(result.status) +} diff --git a/src/cmds/precommit.js b/src/cmds/precommit.js new file mode 100644 index 0000000..ca59396 --- /dev/null +++ b/src/cmds/precommit.js @@ -0,0 +1,22 @@ +const debug = require('debug')('packages-scripts') + +const utils = require('./_internal/utils') + +exports.command = `precommit` + +const COMMAND_NAME = 'precommit' + +exports.describe = + 'Executing `lint-staged` or npm run validate before committing' + +exports.builder = () => {} + +exports.handler = rawArgv => { + const {argv, args} = utils.cleanCommandArgv(COMMAND_NAME, rawArgv) + const normalizedArgs = {...argv, input: argv._, args} + + debug('precommit args %o', {argv, normalizedArgs}) + const result = require('../scripts/precommit')(normalizedArgs) + + process.exit(result.status) +} diff --git a/src/cmds/prepublish-only.js b/src/cmds/prepublish-only.js new file mode 100644 index 0000000..a8e7828 --- /dev/null +++ b/src/cmds/prepublish-only.js @@ -0,0 +1,21 @@ +const debug = require('debug')('packages-scripts') + +const {cleanCommandArgv} = require('./_internal/utils') + +exports.command = `prepublish-only` + +const COMMAND_NAME = 'prepublish-only' + +exports.describe = + 'Prepare directory contents to publish. e.g.: copy pkg.files to build output directory' + +exports.builder = () => {} + +exports.handler = rawArgv => { + const {argv} = cleanCommandArgv(COMMAND_NAME, rawArgv) + + debug('prepublish-only args %o', {rawArgv, argv}) + require('../scripts/prepublish-only')().catch(error => { + console.error(error) + }) +} diff --git a/src/cmds/test.js b/src/cmds/test.js new file mode 100644 index 0000000..da2a285 --- /dev/null +++ b/src/cmds/test.js @@ -0,0 +1,26 @@ +const debug = require('debug')('packages-scripts:cmds:test') + +const utils = require('./_internal/utils') + +exports.command = `test` + +const COMMAND_NAME = 'test' + +exports.describe = 'Run tests by `jest`' + +// exports.builder = () => {} + +exports.handler = async rawArgv => { + const {args} = utils.cleanCommandArgv(COMMAND_NAME, rawArgv) + + debug('test args %o', {rawArgv, args}) + + try { + const result = await require('../scripts/test')({args}) + + debug('run tests result %o', result) + } catch (error) { + console.error(error) + process.exit(1) + } +} diff --git a/src/cmds/travis-after-success.js b/src/cmds/travis-after-success.js new file mode 100644 index 0000000..d9a5081 --- /dev/null +++ b/src/cmds/travis-after-success.js @@ -0,0 +1,7 @@ +exports.command = 'travis-after-success' + +exports.describe = 'Executing tasks after building in Travis CI successfully' + +exports.handler = () => { + require('../scripts/travis-after-success') +} diff --git a/src/cmds/validate.js b/src/cmds/validate.js new file mode 100644 index 0000000..9608624 --- /dev/null +++ b/src/cmds/validate.js @@ -0,0 +1,21 @@ +const debug = require('debug')('packages-scripts:cmd:validate') + +const utils = require('./_internal/utils') + +exports.command = `validate` + +const COMMAND_NAME = 'validate' + +exports.describe = 'Validate multiple scripts' + +exports.builder = () => {} + +exports.handler = rawArgv => { + const {argv} = utils.cleanCommandArgv(COMMAND_NAME, rawArgv) + + debug('validate args %o', {rawArgv, argv}) + + const result = require('../scripts/validate')(argv._) + + process.exit(result.status) +} diff --git a/src/config/babel-config.js b/src/config/babel-config.js index 3d4f41d..f89278b 100644 --- a/src/config/babel-config.js +++ b/src/config/babel-config.js @@ -33,7 +33,7 @@ if (!treeshake && !hasBabelRuntimeDep) { console.warn(RUNTIME_HELPERS_WARN) } -debug('loaded options for generating babel configuration %O', { +debug('loaded options for generating babel configuration %o', { BABEL_ENV, NODE_ENV, appDirectory, @@ -121,7 +121,7 @@ function getPresets() { : {node: getNodeVersion(pkg)} const envOptions = {modules: false, loose: true, targets: envTargets} - debug('set options for `@babel/preset-env`', envOptions) + debug('set options for `@babel/preset-env` %o', envOptions) return [ [require.resolve('@babel/preset-env'), envOptions], diff --git a/src/config/babel-transform.js b/src/config/babel-transform.js index 9b65c9e..d47bd6a 100644 --- a/src/config/babel-transform.js +++ b/src/config/babel-transform.js @@ -1,5 +1,14 @@ const babelJest = require('babel-jest') +const {loadBabelConfig} = require('../utils') + +const babelConfig = loadBabelConfig() +const useBuiltInBabelConfig = !babelConfig + module.exports = babelJest.createTransformer({ - presets: [require.resolve('./babel-config')], + presets: [ + useBuiltInBabelConfig + ? require.resolve('./babel-config') + : babelConfig.babelrc || babelConfig.config, + ], }) diff --git a/src/config/jest.config.js b/src/config/jest.config.js index 54520ec..5e52a18 100644 --- a/src/config/jest.config.js +++ b/src/config/jest.config.js @@ -1,9 +1,9 @@ const path = require('path') -const {ifAnyDep, hasFile, hasPkgProp, fromRoot} = require('../utils') -const here = p => path.join(__dirname, p) +const {ifAnyDep, fromRoot} = require('../utils') +const {defaultSourceRoot} = require('../options') -const useBuiltInBabelConfig = !hasFile('.babelrc') && !hasPkgProp('babel') +const here = p => path.join(__dirname, p) const ignores = [ '/node_modules/', @@ -13,14 +13,17 @@ const ignores = [ ] const jestConfig = { - roots: [fromRoot('src')], + roots: [fromRoot(defaultSourceRoot)], testEnvironment: ifAnyDep(['webpack', 'rollup', 'react'], 'jsdom', 'node'), testURL: 'http://localhost', moduleFileExtensions: ['js', 'jsx', 'json', 'ts', 'tsx'], - collectCoverageFrom: ['src/**/*.+(js|jsx|ts|tsx)'], + collectCoverageFrom: [`${defaultSourceRoot}/**/*.+(js|jsx|ts|tsx)`], testMatch: ['**/__tests__/**/*.+(js|jsx|ts|tsx)'], testPathIgnorePatterns: [...ignores], - coveragePathIgnorePatterns: [...ignores, 'src/(umd|cjs|esm)-entry.js$'], + coveragePathIgnorePatterns: [ + ...ignores, + `${defaultSourceRoot}/(umd|cjs|esm)-entry.js$`, + ], transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$'], coverageThreshold: { global: { @@ -30,10 +33,9 @@ const jestConfig = { statements: 100, }, }, -} - -if (useBuiltInBabelConfig) { - jestConfig.transform = {'^.+\\.js$': here('./babel-transform')} + transform: { + '^.+\\.(jsx?|tsx?)$': here('./babel-transform'), + }, } module.exports = jestConfig diff --git a/src/config/rollup.config.js b/src/config/rollup.config.js index 8fa9aed..032254a 100644 --- a/src/config/rollup.config.js +++ b/src/config/rollup.config.js @@ -11,6 +11,8 @@ 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 {defaultBuildRoot, defaultSourceRoot} = require('../options') // const debug = require('debug')('packages-scripts:config:rollup') const { @@ -54,9 +56,9 @@ const input = glob.sync( fromRoot( process.env.BUILD_INPUT || ifFile( - `src/${format}-entry.js`, - `src/${format}-entry.js`, - 'src/index.js', + `${defaultSourceRoot}/${format}-entry.js`, + `${defaultSourceRoot}/${format}-entry.js`, + `${defaultSourceRoot}/index.js`, ), ), ) @@ -95,7 +97,8 @@ const externalPredicate = external.length === 0 ? () => false : id => externalPattern.test(id) const filename = [ - pkg.name, + // remove scope name prefix `@xxx/` out from the filename + pkg.name.replace(/^@.*\/(.*)/, '$1'), filenameSuffix, `.${format}`, minify ? '.min' : null, @@ -104,7 +107,9 @@ const filename = [ .filter(Boolean) .join('') -const dirpath = path.join(...[filenamePrefix, 'dist'].filter(Boolean)) +// put all bundles in a directory, make cleaning the output directory less +// ambitiously before bundling multiple bundle files +const dirpath = path.join(...[defaultBuildRoot, filenamePrefix].filter(Boolean)) const output = [ { @@ -119,7 +124,6 @@ const output = [ ] const useBuiltinBabelConfig = isUsingBuiltInBabelConfig() -const babelPresets = useBuiltinBabelConfig ? getBuiltInBabelPreset() : [] const replacements = Object.entries( umd ? process.env : omit(process.env, ['NODE_ENV']), @@ -141,7 +145,9 @@ module.exports = { external: externalPredicate, plugins: [ rollupBabel({ - presets: babelPresets, + ...(useBuiltinBabelConfig && { + presets: [getBuiltInBabelPreset()], + }), babelrc: !useBuiltinBabelConfig, rootMode: 'upward', runtimeHelpers: isBabelPluginUsed('transform-runtime'), diff --git a/src/index.js b/src/index.js index 3b75b8c..cfa7932 100755 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,7 @@ #!/usr/bin/env node +const path = require('path') +const yargs = require('yargs') +const debug = require('debug')('packages-scripts') let shouldThrow try { @@ -16,4 +19,13 @@ if (shouldThrow) { ) } -require('./run-script') +// require('./run-script') + +// TODO: after development, put this to bin directory, not src/index.js +const argv = yargs + .scriptName('packages-scripts') + .commandDir(path.resolve(__dirname, './cmds')) + .help() + .demandCommand().argv + +debug('argv: %o', argv) diff --git a/src/options.js b/src/options.js new file mode 100644 index 0000000..5b9f287 --- /dev/null +++ b/src/options.js @@ -0,0 +1,7 @@ +const defaultSourceRoot = 'src' +const defaultBuildRoot = 'dist' + +module.exports = { + defaultSourceRoot, + defaultBuildRoot, +} diff --git a/src/scripts/__tests__/__snapshots__/format.js.snap b/src/scripts/__tests__/__snapshots__/format.js.snap index e24746e..832e1f8 100644 --- a/src/scripts/__tests__/__snapshots__/format.js.snap +++ b/src/scripts/__tests__/__snapshots__/format.js.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`format --config arg can be used for a custom config 1`] = `prettier --write **/*.+(js|json|less|css|ts|tsx|md) --config ./my-config.js`; +exports[`format --config arg can be used for a custom config 1`] = `prettier --config ./my-config.js --write **/*.+(js|json|less|css|ts|tsx|md)`; -exports[`format --ignore-path arg can be used for a custom ignore file 1`] = `prettier --write **/*.+(js|json|less|css|ts|tsx|md) --ignore-path ./.myignore`; +exports[`format --ignore-path arg can be used for a custom ignore file 1`] = `prettier --ignore-path ./.myignore --write **/*.+(js|json|less|css|ts|tsx|md)`; -exports[`format --no-write prevents --write argument from being added 1`] = `prettier **/*.+(js|json|less|css|ts|tsx|md) --no-write`; +exports[`format --no-write prevents --write argument from being added 1`] = `prettier **/*.+(js|json|less|css|ts|tsx|md)`; exports[`format calls prettier CLI with args 1`] = `prettier --write my-src/**/*.js`; diff --git a/src/scripts/__tests__/__snapshots__/test.js.snap b/src/scripts/__tests__/__snapshots__/test.js.snap index d6eecc4..3904482 100644 --- a/src/scripts/__tests__/__snapshots__/test.js.snap +++ b/src/scripts/__tests__/__snapshots__/test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`test calls jest.run with default args 1`] = `--config {"builtInConfig":true} --watch`; +exports[`test calls jest.run with default args 1`] = `--config {"builtInConfig":true}`; exports[`test does not watch --updateSnapshot 1`] = `--config {"builtInConfig":true} --updateSnapshot`; @@ -14,8 +14,8 @@ exports[`test does not watch with --no-watch 1`] = `--config {"builtInConfig":tr exports[`test forwards args 1`] = `--config {"builtInConfig":true} --coverage --watch`; -exports[`test uses custom config with --config 1`] = `--watch --config ./my-config.js`; +exports[`test uses custom config with --config 1`] = `--config ./my-config.js`; -exports[`test uses custom config with jest prop in pkg 1`] = `--watch`; +exports[`test uses custom config with jest prop in pkg 1`] = ``; -exports[`test uses custom config with jest.config.js file 1`] = `--watch`; +exports[`test uses custom config with jest.config.js file 1`] = ``; diff --git a/src/scripts/__tests__/contributors.js b/src/scripts/__tests__/contributors.js index 5759e08..85c5af7 100644 --- a/src/scripts/__tests__/contributors.js +++ b/src/scripts/__tests__/contributors.js @@ -14,8 +14,8 @@ afterEach(() => { }) test('calls all-contributors CLI with args', () => { - process.argv = ['node', '../contributors', 'add'] - require('../contributors') + process.argv = ['node', '../..', 'contributors', 'add'] + require('../..') expect(crossSpawnSyncMock).toHaveBeenCalledTimes(1) expect(crossSpawnSyncMock).toHaveBeenCalledWith( expect.stringMatching('all-contributors'), diff --git a/src/scripts/__tests__/format.js b/src/scripts/__tests__/format.js index d4f83f3..6f6f7af 100644 --- a/src/scripts/__tests__/format.js +++ b/src/scripts/__tests__/format.js @@ -16,9 +16,9 @@ cases( process.exit = jest.fn() // tests - process.argv = ['node', '../format', ...args] + process.argv = ['node', '../../index.js', 'format', ...args] crossSpawnSyncMock.mockClear() - require('../format') + require('../..') expect(crossSpawnSyncMock).toHaveBeenCalledTimes(1) const [firstCall] = crossSpawnSyncMock.mock.calls const [script, calledArgs] = firstCall diff --git a/src/scripts/__tests__/lint.js b/src/scripts/__tests__/lint.js index 1a9fdd6..a21bd98 100644 --- a/src/scripts/__tests__/lint.js +++ b/src/scripts/__tests__/lint.js @@ -11,6 +11,7 @@ cases( 'lint', ({ args = [], + input = [], utils = require('../../utils'), hasPkgProp = () => false, hasFile = () => false, @@ -33,7 +34,7 @@ cases( try { // tests - require('../lint') + require('../lint')({args, input}) expect(crossSpawnSyncMock).toHaveBeenCalledTimes(1) const [firstCall] = crossSpawnSyncMock.mock.calls const [script, calledArgs] = firstCall @@ -81,6 +82,12 @@ cases( './src/index.css', './src/component.js', ], + input: [ + './src/index.js', + './package.json', + './src/index.css', + './src/component.js', + ], }, }, ) diff --git a/src/scripts/__tests__/precommit.js b/src/scripts/__tests__/precommit.js index 6b0c33a..e350fd3 100644 --- a/src/scripts/__tests__/precommit.js +++ b/src/scripts/__tests__/precommit.js @@ -23,13 +23,13 @@ cases( }) process.exit = jest.fn() - process.argv = ['node', '../precommit', ...args] + process.argv = ['node', '../../index.js', 'precommit', ...args] utils.isOptedIn = optIn => optIn === 'pre-commit' crossSpawnSyncMock.mockClear() try { // tests - require('../precommit') + require('../../') expect(crossSpawnSyncMock).toHaveBeenCalledTimes(2) const [firstCall, secondCall] = crossSpawnSyncMock.mock.calls const [scriptOne, calledArgsOne] = firstCall diff --git a/src/scripts/__tests__/test.js b/src/scripts/__tests__/test.js index 152c27b..95fdc3c 100644 --- a/src/scripts/__tests__/test.js +++ b/src/scripts/__tests__/test.js @@ -39,7 +39,7 @@ cases( try { // tests - require('../test') + require('../test')({args}) expect(jestRunMock).toHaveBeenCalledTimes(1) const [firstCall] = jestRunMock.mock.calls const [jestArgs] = firstCall diff --git a/src/scripts/__tests__/validate.js b/src/scripts/__tests__/validate.js index 093c837..947f971 100644 --- a/src/scripts/__tests__/validate.js +++ b/src/scripts/__tests__/validate.js @@ -16,7 +16,7 @@ cases( try { // tests crossSpawnSyncMock.mockClear() - require('../validate') + require('../..') expect(crossSpawnSyncMock).toHaveBeenCalledTimes(1) const [firstCall] = crossSpawnSyncMock.mock.calls const [script, calledArgs] = firstCall @@ -48,7 +48,7 @@ cases( setup: withDefaultSetup(setupWithScripts(['test', 'build', 'lint'])), }, 'allows you to specify your own npm scripts': { - setup: setupWithArgs(['specialbuild,specialtest,speciallint']), + setup: setupWithArgs(['specialbuild', 'specialtest', 'speciallint']), }, [`doesn't use test or lint if it's in precommit`]: { setup: withDefaultSetup(() => { @@ -79,7 +79,7 @@ function setupWithArgs(args = []) { const originalResolveBin = utils.resolveBin utils.resolveBin = (modName, {executable = modName} = {}) => executable const originalArgv = process.argv - process.argv = ['node', '../format', ...args] + process.argv = ['node', '../..', 'validate', ...args] return function teardown() { process.argv = originalArgv utils.resolveBin = originalResolveBin diff --git a/src/scripts/_internal/prepare-npm.js b/src/scripts/_internal/prepare-npm.js new file mode 100644 index 0000000..e69de29 diff --git a/src/scripts/build/babel.js b/src/scripts/build/babel.js index 546c2e2..f45a95a 100644 --- a/src/scripts/build/babel.js +++ b/src/scripts/build/babel.js @@ -1,26 +1,29 @@ const spawn = require('cross-spawn') -const rimraf = require('rimraf') const debug = require('debug')('packages-scripts:scripts:build:babel') const { - fromRoot, resolveBin, getBuiltInBabelPreset, loadBabelConfig, } = require('../../utils') +const {defaultSourceRoot} = require('../../options') -function build() { - // TODO: is still valid while running this like `NODE_ENV=production node -r ./node_modules/.bin/ckh-scripts` ? - const args = process.argv.slice(2) +module.exports = function compile({outDir, deleteDirOnStart, input, args}) { + args = [...args] + input = [...input] + if (!input.length) { + input.unshift(defaultSourceRoot) + } const loadedBabelConfig = loadBabelConfig() const useBuiltinConfig = !loadedBabelConfig + // const fullOutputDir = path.join(defaultBuildRoot, outDir) + const fullOutputDir = outDir - const useSpecifiedOutDir = args.includes('--out-dir') || args.includes('-d') + args.splice(args.indexOf('--out-dir'), 2) - if (!useSpecifiedOutDir) { - args.unshift('--out-dir', 'dist') - } + // push to tail to override `--out-dir` in `args` array + args.push('--out-dir', fullOutputDir) if (!args.includes('--ignore')) { args.push('--ignore', '**/__tests__/**/*,**/__mocks__/**/*') @@ -30,10 +33,8 @@ function build() { args.push('--copy-files') } - if (!useSpecifiedOutDir && !args.includes('--no-clean')) { - // TODO: change to that if there is no `--no-clean` option specified, clean - // the out dir which user specified - rimraf.sync(fromRoot('dist')) + if (deleteDirOnStart) { + args.push('--delete-dir-on-start') } if (useBuiltinConfig) { @@ -45,19 +46,22 @@ function build() { ) } + input.forEach(filename => { + const foundIndex = args.indexOf(filename) + + if (foundIndex !== -1) { + args.splice(foundIndex, 1) + } + }) + const babelCmd = [ resolveBin('@babel/cli', {executable: 'babel'}), - ['src'].concat(args), + input.concat(args), ] - debug('starting to execute: %s %O', ...babelCmd) + debug('starting to execute %o ...', babelCmd) const result = spawn.sync(...babelCmd, {stdio: 'inherit'}) - // TODO: move the exit operation out of current file - process.exit(result.status) - return result } - -build() diff --git a/src/scripts/build/index.js b/src/scripts/build/index.js deleted file mode 100644 index 9b96498..0000000 --- a/src/scripts/build/index.js +++ /dev/null @@ -1,9 +0,0 @@ -if (process.argv.includes('--browser')) { - console.error('--browser has been deprecated, use --bundle instead') -} - -if (process.argv.includes('--bundle') || process.argv.includes('--browser')) { - require('./rollup') -} else { - require('./babel') -} diff --git a/src/scripts/build/rollup.js b/src/scripts/build/rollup.js index f7ae232..1f01025 100644 --- a/src/scripts/build/rollup.js +++ b/src/scripts/build/rollup.js @@ -2,126 +2,119 @@ const path = require('path') const spawn = require('cross-spawn') const glob = require('glob') const rimraf = require('rimraf') -const yargsParser = require('yargs-parser') + const { hasFile, resolveBin, fromRoot, getConcurrentlyArgs, writeExtraEntry, - parseEnv, } = require('../../utils') - -const buildNode = parseEnv('BUILD_NODE', false) -const buildReactNative = parseEnv('BUILD_REACT_NATIVE', false) - -const crossEnv = resolveBin('cross-env') -const rollup = resolveBin('rollup') -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 useBuiltinConfig = - !args.includes('--config') && !hasFile('rollup.config.js') -const config = useBuiltinConfig - ? `--config ${hereRelative('../../config/rollup.config.js')}` - : args.includes('--config') - ? '' - : '--config' // --config will pick up the rollup.config.js file - -const environment = parsedArgs.environment - ? `--environment ${parsedArgs.environment}` - : '' -const watch = parsedArgs.watch ? '--watch' : '' -const sizeSnapshot = parsedArgs['size-snapshot'] - -let formats = ['esm', 'cjs', 'umd', 'umd.min'] - -if (typeof parsedArgs.bundle === 'string') { - formats = parsedArgs.bundle.split(',') -} - -const buildPreact = args.includes('--p-react') -const scripts = buildPreact - ? getPReactScripts() - : getConcurrentlyArgs(getCommands()) - -const cleanBuildDirs = !args.includes('--no-clean') - -if (cleanBuildDirs) { - rimraf.sync(fromRoot('dist')) - - if (buildPreact) { - rimraf.sync(fromRoot('preact')) +const commonOpts = require('../../options') + +module.exports = ({ + reactNative: buildReactNative, + node: buildNode, + sizeSnapshot, + watch, + formats, + environment, + config, + preact: buildPreact, + clean, + addPreactEntry, +}) => { + const crossEnv = resolveBin('cross-env') + const rollup = resolveBin('rollup') + + const here = p => path.join(__dirname, p) + const hereRelative = p => here(p).replace(process.cwd(), '.') + + const useBuiltinConfig = !config && !hasFile('rollup.config.js') + + const scripts = buildPreact + ? getPReactScripts() + : getConcurrentlyArgs(getCommands()) + + if (clean) { + const buildToClean = fromRoot(commonOpts.defaultBuildRoot) + rimraf.sync(buildToClean) + + if (buildPreact) { + const preactBuildToClean = path.join(buildToClean, 'preact') + rimraf.sync(preactBuildToClean) + } } -} -const result = spawn.sync(resolveBin('concurrently'), scripts, { - stdio: 'inherit', -}) - -if (result.status === 0 && buildPreact && !args.includes('--no-package-json')) { - writeExtraEntry( - 'preact', - { - cjs: glob.sync(fromRoot('preact/**/*.cjs.js'))[0], - esm: glob.sync(fromRoot('preact/**/*.esm.js'))[0], - }, - false, - ) -} + const result = spawn.sync(resolveBin('concurrently'), scripts, { + stdio: 'inherit', + }) + + if (result.status === 0 && buildPreact && addPreactEntry) { + writeExtraEntry( + 'preact', + { + cjs: glob.sync(fromRoot('preact/**/*.cjs.js'))[0], + esm: glob.sync(fromRoot('preact/**/*.esm.js'))[0], + }, + false, + ) + } -function getPReactScripts() { - const reactCommands = prefixKeys('react.', getCommands()) - const preactCommands = prefixKeys('preact.', getCommands({preact: true})) - return getConcurrentlyArgs(Object.assign(reactCommands, preactCommands)) -} + function getPReactScripts() { + const reactCommands = prefixKeys('react.', getCommands()) + const preactCommands = prefixKeys('preact.', getCommands({preact: true})) + return getConcurrentlyArgs(Object.assign(reactCommands, preactCommands)) + } -function prefixKeys(prefix, object) { - return Object.entries(object).reduce((cmds, [key, value]) => { - cmds[`${prefix}${key}`] = value - return cmds - }, {}) -} + function prefixKeys(prefix, object) { + return Object.entries(object).reduce((cmds, [key, value]) => { + cmds[`${prefix}${key}`] = value + return cmds + }, {}) + } -function getCommands({preact = false} = {}) { - return formats.reduce((cmds, format) => { - const [formatName, minify = false] = format.split('.') - const nodeEnv = minify ? 'production' : 'development' - const sourceMap = formatName === 'umd' ? '--sourcemap' : '' - const buildMinify = Boolean(minify) + function getCommands({preact = false} = {}) { + return formats.reduce((cmds, format) => { + const [formatName, minify = false] = format.split('.') + const nodeEnv = minify ? 'production' : 'development' + const sourceMap = formatName === 'umd' ? '--sourcemap' : '' + const buildMinify = Boolean(minify) + + cmds[format] = getCommand( + [ + `BUILD_FORMAT=${formatName}`, + `BUILD_MINIFY=${buildMinify}`, + `NODE_ENV=${nodeEnv}`, + `BUILD_PREACT=${preact}`, + `BUILD_SIZE_SNAPSHOT=${sizeSnapshot}`, + `BUILD_NODE=${buildNode}`, + `BUILD_REACT_NATIVE=${buildReactNative}`, + ].join(' '), + sourceMap, + ) + return cmds + }, {}) + } - cmds[format] = getCommand( - [ - `BUILD_FORMAT=${formatName}`, - `BUILD_MINIFY=${buildMinify}`, - `NODE_ENV=${nodeEnv}`, - `BUILD_PREACT=${preact}`, - `BUILD_SIZE_SNAPSHOT=${sizeSnapshot}`, - `BUILD_NODE=${buildNode}`, - `BUILD_REACT_NATIVE=${buildReactNative}`, - ].join(' '), - sourceMap, - ) - return cmds - }, {}) -} + function getCommand(env, ...flags) { + return [ + crossEnv, + 'BUILD_ROLLUP=true', + env, + rollup, + useBuiltinConfig + ? `--config ${hereRelative('../../config/rollup.config.js')}` + : typeof config === 'undefined' + ? '--config' // --config will pick up the rollup.config.js file + : '', + environment ? `--environment ${environment}` : '', + watch ? '--watch' : '', + ...flags, + ] + .filter(Boolean) + .join(' ') + } -function getCommand(env, ...flags) { - return [ - crossEnv, - 'BUILD_ROLLUP=true', - env, - rollup, - config, - environment, - watch, - ...flags, - ] - .filter(Boolean) - .join(' ') + process.exit(result.status) } - -process.exit(result.status) diff --git a/src/scripts/contributors.js b/src/scripts/contributors.js index 62e052c..407e59b 100644 --- a/src/scripts/contributors.js +++ b/src/scripts/contributors.js @@ -1,14 +1,14 @@ const spawn = require('cross-spawn') const {resolveBin} = require('../utils') -const args = process.argv.slice(2) +module.exports = function contributors({args}) { + const result = spawn.sync( + resolveBin('all-contributors-cli', {executable: 'all-contributors'}), + args, + { + stdio: 'inherit', + }, + ) -const result = spawn.sync( - resolveBin('all-contributors-cli', {executable: 'all-contributors'}), - args, - { - stdio: 'inherit', - }, -) - -process.exit(result.status) + return result +} diff --git a/src/scripts/format.js b/src/scripts/format.js index 94b804a..4ed585a 100644 --- a/src/scripts/format.js +++ b/src/scripts/format.js @@ -1,44 +1,53 @@ const path = require('path') +const chalk = require('chalk') const spawn = require('cross-spawn') -const yargsParser = require('yargs-parser') + const {hasPkgProp, resolveBin, hasFile} = require('../utils') -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 useBuiltinConfig = - !args.includes('--config') && - !hasFile('.prettierrc') && - !hasFile('prettier.config.js') && - !hasPkgProp('prettierrc') -const config = useBuiltinConfig - ? ['--config', hereRelative('../config/prettierrc.js')] - : [] - -const useBuiltinIgnore = - !args.includes('--ignore-path') && !hasFile('.prettierignore') -const ignore = useBuiltinIgnore - ? ['--ignore-path', hereRelative('../config/prettierignore')] - : [] - -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 filesToApply = parsedArgs._.length - ? [] - : ['**/*.+(js|json|less|css|ts|tsx|md)'] - -const result = spawn.sync( - resolveBin('prettier'), - [...config, ...ignore, ...write, ...filesToApply].concat(relativeArgs), - {stdio: 'inherit'}, -) - -process.exit(result.status) +const DEFAULT_INPUT = '**/*.+(js|json|less|css|ts|tsx|md)' + +module.exports = function runPrettier(argv) { + const {config, ignorePath, write, input} = argv + + if (!input.length) { + input.unshift(DEFAULT_INPUT) + } + console.log(chalk`Formating {bold.white ${input.join(', ')}} ...`) + + const here = p => path.join(__dirname, p) + const hereRelative = p => here(p).replace(process.cwd(), '.') + + const useBuiltinConfig = + typeof config === 'undefined' && + !hasFile('.prettierrc') && + !hasFile('prettier.config.js') && + !hasPkgProp('prettierrc') + + const configOption = useBuiltinConfig + ? ['--config', hereRelative('../config/prettierrc.js')] + : config + ? ['--config', config] + : [] + + const useBuiltinIgnore = !ignorePath && !hasFile('.prettierignore') + const ignore = useBuiltinIgnore + ? ['--ignore-path', hereRelative('../config/prettierignore')] + : ignorePath + ? ['--ignore-path', ignorePath] + : [] + + const writeOption = 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 = input.map(a => a.replace(`${process.cwd()}/`, '')) + + const result = spawn.sync( + resolveBin('prettier'), + [...configOption, ...ignore, ...writeOption, ...relativeArgs], + {stdio: 'inherit'}, + ) + + return result +} diff --git a/src/scripts/lint.js b/src/scripts/lint.js index 7701ec7..87cb953 100644 --- a/src/scripts/lint.js +++ b/src/scripts/lint.js @@ -1,49 +1,49 @@ const path = require('path') const spawn = require('cross-spawn') -const yargsParser = require('yargs-parser') + const {hasPkgProp, resolveBin, hasFile} = require('../utils') -let args = process.argv.slice(2) -const here = p => path.join(__dirname, p) -const hereRelative = p => here(p).replace(process.cwd(), '.') -const parsedArgs = yargsParser(args) +module.exports = function lint({input, args}) { + const here = p => path.join(__dirname, p) + const hereRelative = p => here(p).replace(process.cwd(), '.') -const useBuiltinConfig = - !args.includes('--config') && - !hasFile('.eslintrc') && - !hasFile('.eslintrc.js') && - !hasPkgProp('eslintConfig') + const useBuiltinConfig = + !args.includes('--config') && + !hasFile('.eslintrc') && + !hasFile('.eslintrc.js') && + !hasPkgProp('eslintConfig') -const config = useBuiltinConfig - ? ['--config', hereRelative('../config/eslintrc.js')] - : [] + const config = useBuiltinConfig + ? ['--config', hereRelative('../config/eslintrc.js')] + : [] -const useBuiltinIgnore = - !args.includes('--ignore-path') && - !hasFile('.eslintignore') && - !hasPkgProp('eslintIgnore') + const useBuiltinIgnore = + !args.includes('--ignore-path') && + !hasFile('.eslintignore') && + !hasPkgProp('eslintIgnore') -const ignore = useBuiltinIgnore - ? ['--ignore-path', hereRelative('../config/eslintignore')] - : [] + const ignore = useBuiltinIgnore + ? ['--ignore-path', hereRelative('../config/eslintignore')] + : [] -const cache = args.includes('--no-cache') ? [] : ['--cache'] + const cache = args.includes('--no-cache') ? [] : ['--cache'] -const filesGiven = parsedArgs._.length > 0 + const filesGiven = input.length > 0 -const filesToApply = filesGiven ? [] : ['.'] + const filesToApply = filesGiven ? [] : ['.'] -if (filesGiven) { - // we need to take all the flag-less arguments (the files that should be linted) - // and filter out the ones that aren't js files. Otherwise json or css files - // may be passed through - args = args.filter(a => !parsedArgs._.includes(a) || a.endsWith('.js')) -} + if (filesGiven) { + // we need to take all the flag-less arguments (the files that should be linted) + // and filter out the ones that aren't js files. Otherwise json or css files + // may be passed through + args = args.filter(a => !input.includes(a) || a.endsWith('.js')) + } -const result = spawn.sync( - resolveBin('eslint'), - [...config, ...ignore, ...cache, ...args, ...filesToApply], - {stdio: 'inherit'}, -) + const result = spawn.sync( + resolveBin('eslint'), + [...config, ...ignore, ...cache, ...args, ...filesToApply], + {stdio: 'inherit'}, + ) -process.exit(result.status) + return result +} diff --git a/src/scripts/precommit.js b/src/scripts/precommit.js index 8ff31e2..1314669 100644 --- a/src/scripts/precommit.js +++ b/src/scripts/precommit.js @@ -8,40 +8,41 @@ const {isOptedIn, resolveBin, appDirectory} = require('../utils') const here = p => path.join(__dirname, p) const hereRelative = p => here(p).replace(process.cwd(), '.') -const args = process.argv.slice(2) +module.exports = function precommit({args}) { + const lintStagedConfigSearchResult = loadLintStagedConfig() + // eslint-disable-next-line no-unused-vars + let useBuiltInConfig, configFilePath -const lintStagedConfigSearchResult = loadLintStagedConfig() -// eslint-disable-next-line no-unused-vars -let useBuiltInConfig, configFilePath + if (lintStagedConfigSearchResult) { + configFilePath = lintStagedConfigSearchResult + ? lintStagedConfigSearchResult.filepath + : (useBuiltInConfig = false) + } else { + configFilePath = require.resolve('../config/lintstagedrc.js') + useBuiltInConfig = true + } -if (lintStagedConfigSearchResult) { - configFilePath = lintStagedConfigSearchResult - ? lintStagedConfigSearchResult.filepath - : (useBuiltInConfig = false) -} else { - configFilePath = require.resolve('../config/lintstagedrc.js') - useBuiltInConfig = true -} - -useBuiltInConfig = useBuiltInConfig && !args.includes('--config') -// lintStaged(console, configFilePath) + useBuiltInConfig = useBuiltInConfig && !args.includes('--config') + // lintStaged(console, configFilePath) -if (useBuiltInConfig) { - args.push('--config', hereRelative('../config/lintstagedrc.js')) -} + if (useBuiltInConfig) { + args.push('--config', hereRelative('../config/lintstagedrc.js')) + } -const lintStagedResult = spawn.sync(resolveBin('lint-staged'), args, { - stdio: 'inherit', -}) - -if (lintStagedResult.status !== 0 || !isOptedIn('pre-commit')) { - process.exit(lintStagedResult.status) -} else { - const validateResult = spawn.sync('npm', ['run', 'validate'], { + const lintStagedResult = spawn.sync(resolveBin('lint-staged'), args, { stdio: 'inherit', }) - process.exit(validateResult.status) + if (lintStagedResult.status !== 0 || !isOptedIn('pre-commit')) { + process.exit(lintStagedResult.status) + return lintStagedResult + } else { + const validateResult = spawn.sync('npm', ['run', 'validate'], { + stdio: 'inherit', + }) + + return validateResult + } } /** diff --git a/src/scripts/prepublish-only.js b/src/scripts/prepublish-only.js new file mode 100644 index 0000000..be92b94 --- /dev/null +++ b/src/scripts/prepublish-only.js @@ -0,0 +1,51 @@ +const path = require('path') +const fs = require('fs-extra') + +const {defaultBuildRoot, defaultSourceRoot} = require('../options') + +const {fromRoot, appDirectory} = require('../utils') + +// const MUST_INCLUDED_FILES = [ +// 'package.json', +// 'README(\\..*)$', +// 'CHANGES(\\..*)$', +// 'CHANGELOG(\\..*)$', +// 'HISTORY(\\..*)$', +// 'LICENSE(\\..*)$', +// 'LICENCE(\\..*)$', +// 'NOTICE(\\..*)$', +// ] + +module.exports = async function prePublishOnly() { + const buildDir = fromRoot(defaultBuildRoot) + // const mustIncludedFilesMap = MUST_INCLUDED_FILES.reduce((acc, reg) => { + // acc[reg] = new RegExp(reg) + // return acc + // }, {}) + const excludeRegExpressions = [ + defaultSourceRoot, + defaultBuildRoot, + 'node_modules', + ] + + const allFiles = await fs.readdir(appDirectory) + + const filesToCopy = allFiles.filter(file => { + console.log(file) + if (excludeRegExpressions.some(r => r === file)) return false + + // return Object.keys(mustIncludedFilesMap).some(k => + // mustIncludedFilesMap[k].test(file), + // ) + return true + }) + + return Promise.all( + filesToCopy.map(f => { + const absoluteSrc = fromRoot(f) + const absoluteDest = path.join(buildDir, f) + + return fs.copy(absoluteSrc, absoluteDest) + }), + ) +} diff --git a/src/scripts/test.js b/src/scripts/test.js index 82d24d4..acbeddc 100644 --- a/src/scripts/test.js +++ b/src/scripts/test.js @@ -1,26 +1,16 @@ process.env.BABEL_ENV = 'test' process.env.NODE_ENV = 'test' -const isCI = require('is-ci') -const {hasPkgProp, parseEnv, hasFile} = require('../utils') +const {hasPkgProp, hasFile} = require('../utils') -const args = process.argv.slice(2) +module.exports = function runTests({args}) { + const config = + !args.includes('--config') && + !hasFile('jest.config.js') && + !hasPkgProp('jest') + ? ['--config', JSON.stringify(require('../config/jest.config'))] + : [] -const watch = - !isCI && - !parseEnv('SCRIPTS_PRECOMMIT', false) && - !args.includes('--no-watch') && - !args.includes('--coverage') && - !args.includes('--updateSnapshot') - ? ['--watch'] - : [] - -const config = - !args.includes('--config') && - !hasFile('jest.config.js') && - !hasPkgProp('jest') - ? ['--config', JSON.stringify(require('../config/jest.config'))] - : [] - -// eslint-disable-next-line jest/no-jest-import -require('jest').run([...config, ...watch, ...args]) + // eslint-disable-next-line jest/no-jest-import + require('jest').run([...config, ...args]) +} diff --git a/src/scripts/validate.js b/src/scripts/validate.js index 6061c07..0bfb351 100644 --- a/src/scripts/validate.js +++ b/src/scripts/validate.js @@ -11,28 +11,28 @@ const { // this in the context of a precommit hook. const precommit = parseEnv('SCRIPTS_PRECOMMIT', false) -const validateScripts = process.argv[2] +module.exports = function runValidate(validateScripts) { + const useDefaultScripts = !validateScripts.length -const useDefaultScripts = typeof validateScripts !== 'string' + const scripts = useDefaultScripts + ? { + build: ifScript('build', 'npm run build --silent'), + lint: precommit ? null : ifScript('lint', 'npm run lint --silent'), + test: precommit + ? null + : ifScript('test', 'npm run test --silent -- --coverage'), + flow: ifScript('flow', 'npm run flow --silent'), + } + : validateScripts.reduce((scriptsToRun, name) => { + scriptsToRun[name] = `npm run ${name} --silent` + return scriptsToRun + }, {}) -const scripts = useDefaultScripts - ? { - build: ifScript('build', 'npm run build --silent'), - lint: precommit ? null : ifScript('lint', 'npm run lint --silent'), - test: precommit - ? null - : ifScript('test', 'npm run test --silent -- --coverage'), - flow: ifScript('flow', 'npm run flow --silent'), - } - : validateScripts.split(',').reduce((scriptsToRun, name) => { - scriptsToRun[name] = `npm run ${name} --silent` - return scriptsToRun - }, {}) + const result = spawn.sync( + resolveBin('concurrently'), + getConcurrentlyArgs(scripts), + {stdio: 'inherit'}, + ) -const result = spawn.sync( - resolveBin('concurrently'), - getConcurrentlyArgs(scripts), - {stdio: 'inherit'}, -) - -process.exit(result.status) + return result +} diff --git a/src/utils/babel.js b/src/utils/babel.js index f0c0ec3..c8ab9a7 100644 --- a/src/utils/babel.js +++ b/src/utils/babel.js @@ -2,13 +2,15 @@ const path = require('path') const babel = require('@babel/core') const debug = require('debug')('packages-scripts:utils:babel') +const {defaultSourceRoot} = require('../options') + const here = p => path.resolve(__dirname, p) function loadBabelOptions() { try { const loadedBabelOptions = babel.loadOptions(getLoadOptions()) - debug('loaded options with `babel.loadOptions()` %O', loadedBabelOptions) + debug('loaded options with `babel.loadOptions()` %o', loadedBabelOptions) return loadedBabelOptions } catch (error) { debug('loaded options with `babel.loadOptions()` failed') @@ -31,7 +33,7 @@ function loadBabelConfig() { } debug( - 'loaded babel configuration derived from user-defined babel configuration file %O', + 'loaded babel configuration derived from user-defined babel configuration file %o', loadedBabelConfig, ) @@ -69,7 +71,7 @@ function getLoadOptions() { return { rootMode: 'upward', // TODO: 如果不存在 src 的话应该如何处理 - filename: path.join(process.cwd(), 'src'), + filename: path.join(process.cwd(), defaultSourceRoot), } } @@ -78,9 +80,7 @@ function isUsingBuiltInBabelConfig() { } function getBuiltInBabelPreset() { - const babelPresets = [here('../config/babel-config.js')] - - return babelPresets + return here('../config/babel-config.js') } function getGeneralPluginsUsed() {