diff --git a/package-lock.json b/package-lock.json index cacf8d9f..9ad01c1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1795,6 +1795,24 @@ "chalk": "^3.0.0" } }, + "@kentcdodds/react-workshop-app": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@kentcdodds/react-workshop-app/-/react-workshop-app-1.0.1.tgz", + "integrity": "sha512-KJvTUNo3Ulu1yai7kMWXqtg2o8uMDfXWrr+xv68YwD+J2vG24qcX1iVOPs+LJRWz8AJbWHuG4fz9w7p4tE0N2Q==", + "requires": { + "@babel/runtime": "^7.8.4", + "@testing-library/jest-dom": "^5.1.1", + "@testing-library/react": "^9.4.1", + "chalk": "^3.0.0", + "glob": "^7.1.6", + "history": "^4.10.1", + "normalize.css": "^8.0.1", + "react": "^16.12.0", + "react-dom": "^16.12.0", + "react-router-dom": "^5.1.2", + "stop-runaway-react-effects": "^1.2.1" + } + }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", diff --git a/package.json b/package.json index 74c3f3d0..cd0cb5a4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "npm": ">=6" }, "dependencies": { + "@kentcdodds/react-workshop-app": "^1.0.1", "@testing-library/jest-dom": "^5.1.1", "@testing-library/react": "^9.4.1", "chalk": "^3.0.0", @@ -44,7 +45,7 @@ "test": "react-scripts test --env=jsdom", "test:coverage": "npm run test -- --watchAll=false --coverage", "test:exercises": "npm run test -- testing.*exercises\\/ --onlyChanged", - "setup": "node ./scripts/setup && npm run validate && node ./scripts/autofill-feedback-email.js", + "setup": "node ./scripts/setup", "lint": "eslint .", "validate": "npm-run-all --parallel build test:coverage lint" }, diff --git a/scripts/autofill-feedback-email.js b/scripts/autofill-feedback-email.js deleted file mode 100644 index 3bc761e4..00000000 --- a/scripts/autofill-feedback-email.js +++ /dev/null @@ -1,61 +0,0 @@ -/* eslint no-console:0 */ -const path = require('path') -const inquirer = require('inquirer') -const replace = require('replace-in-file') -const isCI = require('is-ci') -const spawn = require('cross-spawn') - -if (isCI) { - console.log(`Not running autofill feedback as we're on CI`) -} else { - const prompt = inquirer.prompt([ - { - name: 'email', - message: `what's your email address?`, - validate(val) { - if (!val) { - // they don't want to do this... - return true - } else if (!val.includes('@')) { - return 'email requires an @ sign...' - } - return true - }, - }, - ]) - const timeoutId = setTimeout(() => { - console.log( - `\n\nprompt timed out. No worries. Run \`node ./scripts/autofill-feedback-email.js\` if you'd like to try again`, - ) - prompt.ui.close() - }, 15000) - - prompt.then(({email} = {}) => { - clearTimeout(timeoutId) - if (!email) { - console.log(`Not autofilling email because none was provided`) - return - } - const options = { - files: [path.join(__dirname, '..', 'src/**/*.js')], - from: /&em=\r?\n/, - to: `&em=${email}\n`, - } - - replace(options).then( - changedFiles => { - console.log(`Updated ${changedFiles.length} with the email ${email}`) - console.log( - 'committing changes for you so your jest watch mode works nicely', - ) - spawn.sync('git', ['commit', '-am', 'email autofill', '--no-verify'], { - stdio: 'inherit', - }) - }, - error => { - console.error('Failed to update files') - console.error(error.stack) - }, - ) - }) -} diff --git a/scripts/setup.js b/scripts/setup.js index 7a924f8c..056580c9 100644 --- a/scripts/setup.js +++ b/scripts/setup.js @@ -1,19 +1,22 @@ -var path = require('path') -var pkg = require(path.join(process.cwd(), 'package.json')) +var spawnSync = require('child_process').spawnSync -// if you install it then this should be require('workshop-setup') -// but that... doesn't really make sense. -require('./workshop-setup') - .setup(pkg.engines) - .then( - () => { - console.log(`💯 You're all set up! 👏`) - }, - error => { - console.error(`🚨 There was a problem:`) - console.error(error) - console.error( - `\nIf you would like to just ignore this error, then feel free to do so and install dependencies as you normally would in "${process.cwd()}". Just know that things may not work properly if you do...`, - ) - }, +console.log('▶️ Starting workshop setup...') + +var error = spawnSync('npx --version', {shell: true}) + .stderr.toString() + .trim() +if (error) { + console.error( + '🚨 npx is not available on this computer. Please install npm@5.2.0 or greater', ) + throw error +} + +var result = spawnSync( + 'npx "https://gist.github.com/kentcdodds/bb452ffe53a5caa3600197e1d8005733" -q', + {stdio: 'inherit', shell: true}, +) + +if (result.status === 0) { + console.log('✅ Workshop setup complete...') +} diff --git a/scripts/workshop-setup.js b/scripts/workshop-setup.js deleted file mode 100644 index f1a93eb4..00000000 --- a/scripts/workshop-setup.js +++ /dev/null @@ -1,1969 +0,0 @@ -module.exports = -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./src/index.js"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./node_modules/indent-string/index.js": -/*!*********************************************!*\ - !*** ./node_modules/indent-string/index.js ***! - \*********************************************/ -/*! no static exports found */ -/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -module.exports = (str, count, opts) => { - // Support older versions: use the third parameter as options.indent - // TODO: Remove the workaround in the next major version - const options = typeof opts === 'object' ? Object.assign({indent: ' '}, opts) : {indent: opts || ' '}; - count = count === undefined ? 1 : count; - - if (typeof str !== 'string') { - throw new TypeError(`Expected \`input\` to be a \`string\`, got \`${typeof str}\``); - } - - if (typeof count !== 'number') { - throw new TypeError(`Expected \`count\` to be a \`number\`, got \`${typeof count}\``); - } - - if (typeof options.indent !== 'string') { - throw new TypeError(`Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\``); - } - - if (count === 0) { - return str; - } - - const regex = options.includeEmptyLines ? /^/mg : /^(?!\s*$)/mg; - return str.replace(regex, options.indent.repeat(count)); -} -; - - -/***/ }), - -/***/ "./node_modules/semver/semver.js": -/*!***************************************!*\ - !*** ./node_modules/semver/semver.js ***! - \***************************************/ -/*! no static exports found */ -/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ -/***/ (function(module, exports) { - -exports = module.exports = SemVer - -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) - } -} else { - debug = function () {} -} - -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' - -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 - -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 - -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var R = 0 - -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. - -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. - -var NUMERICIDENTIFIER = R++ -src[NUMERICIDENTIFIER] = '0|[1-9]\\d*' -var NUMERICIDENTIFIERLOOSE = R++ -src[NUMERICIDENTIFIERLOOSE] = '[0-9]+' - -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. - -var NONNUMERICIDENTIFIER = R++ -src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' - -// ## Main Version -// Three dot-separated numeric identifiers. - -var MAINVERSION = R++ -src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')' - -var MAINVERSIONLOOSE = R++ -src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')' - -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. - -var PRERELEASEIDENTIFIER = R++ -src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + - '|' + src[NONNUMERICIDENTIFIER] + ')' - -var PRERELEASEIDENTIFIERLOOSE = R++ -src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + - '|' + src[NONNUMERICIDENTIFIER] + ')' - -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. - -var PRERELEASE = R++ -src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + - '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))' - -var PRERELEASELOOSE = R++ -src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))' - -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. - -var BUILDIDENTIFIER = R++ -src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+' - -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. - -var BUILD = R++ -src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + - '(?:\\.' + src[BUILDIDENTIFIER] + ')*))' - -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. - -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. - -var FULL = R++ -var FULLPLAIN = 'v?' + src[MAINVERSION] + - src[PRERELEASE] + '?' + - src[BUILD] + '?' - -src[FULL] = '^' + FULLPLAIN + '$' - -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + - src[PRERELEASELOOSE] + '?' + - src[BUILD] + '?' - -var LOOSE = R++ -src[LOOSE] = '^' + LOOSEPLAIN + '$' - -var GTLT = R++ -src[GTLT] = '((?:<|>)?=?)' - -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -var XRANGEIDENTIFIERLOOSE = R++ -src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -var XRANGEIDENTIFIER = R++ -src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*' - -var XRANGEPLAIN = R++ -src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:' + src[PRERELEASE] + ')?' + - src[BUILD] + '?' + - ')?)?' - -var XRANGEPLAINLOOSE = R++ -src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[PRERELEASELOOSE] + ')?' + - src[BUILD] + '?' + - ')?)?' - -var XRANGE = R++ -src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$' -var XRANGELOOSE = R++ -src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$' - -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -var COERCE = R++ -src[COERCE] = '(?:^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' - -// Tilde ranges. -// Meaning is "reasonably at or greater than" -var LONETILDE = R++ -src[LONETILDE] = '(?:~>?)' - -var TILDETRIM = R++ -src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+' -re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g') -var tildeTrimReplace = '$1~' - -var TILDE = R++ -src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$' -var TILDELOOSE = R++ -src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$' - -// Caret ranges. -// Meaning is "at least and backwards compatible with" -var LONECARET = R++ -src[LONECARET] = '(?:\\^)' - -var CARETTRIM = R++ -src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+' -re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g') -var caretTrimReplace = '$1^' - -var CARET = R++ -src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$' -var CARETLOOSE = R++ -src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$' - -// A simple gt/lt/eq thing, or just "" to indicate "any version" -var COMPARATORLOOSE = R++ -src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$' -var COMPARATOR = R++ -src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$' - -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -var COMPARATORTRIM = R++ -src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + - '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')' - -// this one has to use the /g flag -re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' - -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -var HYPHENRANGE = R++ -src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAIN] + ')' + - '\\s*$' - -var HYPHENRANGELOOSE = R++ -src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s*$' - -// Star ranges basically just allow anything at all. -var STAR = R++ -src[STAR] = '(<|>)?=?\\s*\\*' - -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) - } -} - -exports.parse = parse -function parse (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (version instanceof SemVer) { - return version - } - - if (typeof version !== 'string') { - return null - } - - if (version.length > MAX_LENGTH) { - return null - } - - var r = options.loose ? re[LOOSE] : re[FULL] - if (!r.test(version)) { - return null - } - - try { - return new SemVer(version, options) - } catch (er) { - return null - } -} - -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} - -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null -} - -exports.SemVer = SemVer - -function SemVer (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version - } else { - version = version.version - } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) - } - - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') - } - - if (!(this instanceof SemVer)) { - return new SemVer(version, options) - } - - debug('SemVer', version, options) - this.options = options - this.loose = !!options.loose - - var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL]) - - if (!m) { - throw new TypeError('Invalid Version: ' + version) - } - - this.raw = version - - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] - - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } - - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } - - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } - - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num - } - } - return id - }) - } - - this.build = m[5] ? m[5].split('.') : [] - this.format() -} - -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') - } - return this.version -} - -SemVer.prototype.toString = function () { - return this.version -} - -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } - - return this.compareMain(other) || this.comparePre(other) -} - -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } - - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} - -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } - - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 - } - - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) -} - -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break - - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] - } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } - } - break - - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} - -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } - - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null - } -} - -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } - } - return defaultResult // may be undefined - } -} - -exports.compareIdentifiers = compareIdentifiers - -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) - - if (anum && bnum) { - a = +a - b = +b - } - - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 -} - -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) -} - -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major -} - -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor -} - -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch -} - -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} - -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) -} - -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} - -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compare(a, b, loose) - }) -} - -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.rcompare(a, b, loose) - }) -} - -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 -} - -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} - -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} - -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} - -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} - -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 -} - -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b - - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b - - case '': - case '=': - case '==': - return eq(a, b, loose) - - case '!=': - return neq(a, b, loose) - - case '>': - return gt(a, b, loose) - - case '>=': - return gte(a, b, loose) - - case '<': - return lt(a, b, loose) - - case '<=': - return lte(a, b, loose) - - default: - throw new TypeError('Invalid operator: ' + op) - } -} - -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value - } - } - - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) - } - - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) - - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version - } - - debug('comp', this) -} - -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR] - var m = comp.match(r) - - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) - } - - this.operator = m[1] - if (this.operator === '=') { - this.operator = '' - } - - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) - } -} - -Comparator.prototype.toString = function () { - return this.value -} - -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) - - if (this.semver === ANY) { - return true - } - - if (typeof version === 'string') { - version = new SemVer(version, this.options) - } - - return cmp(version, this.operator, this.semver, this.options) -} - -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } - - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - var rangeTmp - - if (this.operator === '') { - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } - - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) - - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} - -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) - } - } - - if (range instanceof Comparator) { - return new Range(range.value, options) - } - - if (!(this instanceof Range)) { - return new Range(range, options) - } - - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease - - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) - - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } - - this.format() -} - -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} - -Range.prototype.toString = function () { - return this.range -} - -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[COMPARATORTRIM]) - - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[TILDETRIM], tildeTrimReplace) - - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[CARETTRIM], caretTrimReplace) - - // normalize spaces - range = range.split(/\s+/).join(' ') - - // At this point, the range is completely trimmed and - // ready to be split into comparators. - - var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) - - return set -} - -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } - - return this.set.some(function (thisComparators) { - return ( - isSatisfiable(thisComparators, options) && - range.set.some(function (rangeComparators) { - return ( - isSatisfiable(rangeComparators, options) && - thisComparators.every(function (thisComparator) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - ) - }) - ) - }) -} - -// take a set of comparators and determine whether there -// exists a version which can satisfy it -function isSatisfiable (comparators, options) { - var result = true - var remainingComparators = comparators.slice() - var testComparator = remainingComparators.pop() - - while (result && remainingComparators.length) { - result = remainingComparators.every(function (otherComparator) { - return testComparator.intersects(otherComparator, options) - }) - - testComparator = remainingComparators.pop() - } - - return result -} - -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} - -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} - -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} - -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} - -function replaceTilde (comp, options) { - var r = options.loose ? re[TILDELOOSE] : re[TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - - debug('tilde return', ret) - return ret - }) -} - -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} - -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[CARETLOOSE] : re[CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' - } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' - } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' - } - } - - debug('caret return', ret) - return ret - }) -} - -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') -} - -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[XRANGELOOSE] : re[XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp - - if (gtlt === '=' && anyX) { - gtlt = '' - } - - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 - - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 - } - } - - ret = gtlt + M + '.' + m + '.' + p - } else if (xm) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (xp) { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } - - debug('xRange return', ret) - - return ret - }) -} - -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[STAR], '') -} - -// This function is passed to string.replace(re[HYPHENRANGE]) -// M, m, patch, prerelease, build -// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 -// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do -// 1.2 - 3.4 => >=1.2.0 <3.5.0 -function hyphenReplace ($0, - from, fM, fm, fp, fpr, fb, - to, tM, tm, tp, tpr, tb) { - if (isX(fM)) { - from = '' - } else if (isX(fm)) { - from = '>=' + fM + '.0.0' - } else if (isX(fp)) { - from = '>=' + fM + '.' + fm + '.0' - } else { - from = '>=' + from - } - - if (isX(tM)) { - to = '' - } else if (isX(tm)) { - to = '<' + (+tM + 1) + '.0.0' - } else if (isX(tp)) { - to = '<' + tM + '.' + (+tm + 1) + '.0' - } else if (tpr) { - to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr - } else { - to = '<=' + to - } - - return (from + ' ' + to).trim() -} - -// if ANY of the sets match ALL of its comparators, then pass -Range.prototype.test = function (version) { - if (!version) { - return false - } - - if (typeof version === 'string') { - version = new SemVer(version, this.options) - } - - for (var i = 0; i < this.set.length; i++) { - if (testSet(this.set[i], version, this.options)) { - return true - } - } - return false -} - -function testSet (set, version, options) { - for (var i = 0; i < set.length; i++) { - if (!set[i].test(version)) { - return false - } - } - - if (version.prerelease.length && !options.includePrerelease) { - // Find the set of versions that are allowed to have prereleases - // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 - // That should allow `1.2.3-pr.2` to pass. - // However, `1.2.4-alpha.notready` should NOT be allowed, - // even though it's within the range set by the comparators. - for (i = 0; i < set.length; i++) { - debug(set[i].semver) - if (set[i].semver === ANY) { - continue - } - - if (set[i].semver.prerelease.length > 0) { - var allowed = set[i].semver - if (allowed.major === version.major && - allowed.minor === version.minor && - allowed.patch === version.patch) { - return true - } - } - } - - // Version has a -pre, but it's not one of the ones we like. - return false - } - - return true -} - -exports.satisfies = satisfies -function satisfies (version, range, options) { - try { - range = new Range(range, options) - } catch (er) { - return false - } - return range.test(version) -} - -exports.maxSatisfying = maxSatisfying -function maxSatisfying (versions, range, options) { - var max = null - var maxSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!max || maxSV.compare(v) === -1) { - // compare(max, v, true) - max = v - maxSV = new SemVer(max, options) - } - } - }) - return max -} - -exports.minSatisfying = minSatisfying -function minSatisfying (versions, range, options) { - var min = null - var minSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!min || minSV.compare(v) === 1) { - // compare(min, v, true) - min = v - minSV = new SemVer(min, options) - } - } - }) - return min -} - -exports.minVersion = minVersion -function minVersion (range, loose) { - range = new Range(range, loose) - - var minver = new SemVer('0.0.0') - if (range.test(minver)) { - return minver - } - - minver = new SemVer('0.0.0-0') - if (range.test(minver)) { - return minver - } - - minver = null - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] - - comparators.forEach(function (comparator) { - // Clone to avoid manipulating the comparator's semver object. - var compver = new SemVer(comparator.semver.version) - switch (comparator.operator) { - case '>': - if (compver.prerelease.length === 0) { - compver.patch++ - } else { - compver.prerelease.push(0) - } - compver.raw = compver.format() - /* fallthrough */ - case '': - case '>=': - if (!minver || gt(minver, compver)) { - minver = compver - } - break - case '<': - case '<=': - /* Ignore maximum versions */ - break - /* istanbul ignore next */ - default: - throw new Error('Unexpected operation: ' + comparator.operator) - } - }) - } - - if (minver && range.test(minver)) { - return minver - } - - return null -} - -exports.validRange = validRange -function validRange (range, options) { - try { - // Return '*' instead of '' so that truthiness works. - // This will throw if it's invalid anyway - return new Range(range, options).range || '*' - } catch (er) { - return null - } -} - -// Determine if version is less than all the versions possible in the range -exports.ltr = ltr -function ltr (version, range, options) { - return outside(version, range, '<', options) -} - -// Determine if version is greater than all the versions possible in the range. -exports.gtr = gtr -function gtr (version, range, options) { - return outside(version, range, '>', options) -} - -exports.outside = outside -function outside (version, range, hilo, options) { - version = new SemVer(version, options) - range = new Range(range, options) - - var gtfn, ltefn, ltfn, comp, ecomp - switch (hilo) { - case '>': - gtfn = gt - ltefn = lte - ltfn = lt - comp = '>' - ecomp = '>=' - break - case '<': - gtfn = lt - ltefn = gte - ltfn = gt - comp = '<' - ecomp = '<=' - break - default: - throw new TypeError('Must provide a hilo val of "<" or ">"') - } - - // If it satisifes the range it is not outside - if (satisfies(version, range, options)) { - return false - } - - // From now on, variable terms are as if we're in "gtr" mode. - // but note that everything is flipped for the "ltr" function. - - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] - - var high = null - var low = null - - comparators.forEach(function (comparator) { - if (comparator.semver === ANY) { - comparator = new Comparator('>=0.0.0') - } - high = high || comparator - low = low || comparator - if (gtfn(comparator.semver, high.semver, options)) { - high = comparator - } else if (ltfn(comparator.semver, low.semver, options)) { - low = comparator - } - }) - - // If the edge version comparator has a operator then our version - // isn't outside it - if (high.operator === comp || high.operator === ecomp) { - return false - } - - // If the lowest version comparator has an operator and our version - // is less than it then it isn't higher than the range - if ((!low.operator || low.operator === comp) && - ltefn(version, low.semver)) { - return false - } else if (low.operator === ecomp && ltfn(version, low.semver)) { - return false - } - } - return true -} - -exports.prerelease = prerelease -function prerelease (version, options) { - var parsed = parse(version, options) - return (parsed && parsed.prerelease.length) ? parsed.prerelease : null -} - -exports.intersects = intersects -function intersects (r1, r2, options) { - r1 = new Range(r1, options) - r2 = new Range(r2, options) - return r1.intersects(r2) -} - -exports.coerce = coerce -function coerce (version) { - if (version instanceof SemVer) { - return version - } - - if (typeof version !== 'string') { - return null - } - - var match = version.match(re[COERCE]) - - if (match == null) { - return null - } - - return parse(match[1] + - '.' + (match[2] || '0') + - '.' + (match[3] || '0')) -} - - -/***/ }), - -/***/ "./src/index.js": -/*!**********************************!*\ - !*** ./src/index.js + 8 modules ***! - \**********************************/ -/*! exports provided: verifySystem, installDeps, setup */ -/*! ModuleConcatenation bailout: Cannot concat with ./node_modules/indent-string/index.js (<- Module is not an ECMAScript module) */ -/*! ModuleConcatenation bailout: Cannot concat with ./node_modules/semver/semver.js (<- Module is not an ECMAScript module) */ -/*! ModuleConcatenation bailout: Cannot concat with external "child_process" (<- Module is not an ECMAScript module) */ -/*! ModuleConcatenation bailout: Cannot concat with external "fs" (<- Module is not an ECMAScript module) */ -/*! ModuleConcatenation bailout: Cannot concat with external "path" (<- Module is not an ECMAScript module) */ -/*! ModuleConcatenation bailout: Cannot concat with external "process" (<- Module is not an ECMAScript module) */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); - -// EXTERNAL MODULE: ./node_modules/indent-string/index.js -var indent_string = __webpack_require__("./node_modules/indent-string/index.js"); -var indent_string_default = /*#__PURE__*/__webpack_require__.n(indent_string); - -// EXTERNAL MODULE: ./node_modules/semver/semver.js -var semver = __webpack_require__("./node_modules/semver/semver.js"); -var semver_default = /*#__PURE__*/__webpack_require__.n(semver); - -// CONCATENATED MODULE: ./src/utils/one-line.js -function oneLine(strings, ...interpolations) { - const string = strings.map((s, i) => `${s}${interpolations[i] === undefined ? '' : interpolations[i]}`).join('').split('\n').join(' ').split(' ').filter(Boolean).join(' '); - return string; -} - -/* harmony default export */ var one_line = (oneLine); -// EXTERNAL MODULE: external "process" -var external_process_ = __webpack_require__("process"); -var external_process_default = /*#__PURE__*/__webpack_require__.n(external_process_); - -// CONCATENATED MODULE: ./src/verify-system/node.js - - - -/* harmony default export */ var verify_system_node = (getNodeValidator); - -function getNodeValidator(desired) { - return validateNode; - - function validateNode() { - const actual = external_process_default.a.versions.node; - - if (!semver_default.a.satisfies(actual, desired)) { - return one_line` - The installed version of node (${actual}) does not satisfy - the desired range of ${desired}. - Please install a version within the range. You can use - http://git.io/nvm or https://github.com/coreybutler/nvm-windows - to make changing The installed version of node easier. - `; - } - - return null; - } -} -// EXTERNAL MODULE: external "child_process" -var external_child_process_ = __webpack_require__("child_process"); -var external_child_process_default = /*#__PURE__*/__webpack_require__.n(external_child_process_); - -// CONCATENATED MODULE: ./src/verify-system/exec-validator.js - - - -/* harmony default export */ var exec_validator = (execValidator); - -function execValidator(desired, command, message) { - let actual = '0.0.0'; - - try { - actual = Object(external_child_process_["execSync"])(command).toString().trim(); - } catch (error) { - return one_line` - There was an error running the command \`${command}\`: - ${error.message} - `; - } - - return semver_default.a.satisfies(actual, desired) ? null : message(actual, desired); -} -// CONCATENATED MODULE: ./src/verify-system/npm.js - - -/* harmony default export */ var verify_system_npm = (getNPMValidator); - -function getNPMValidator(desired) { - return validateNpm; - - function validateNpm() { - return exec_validator(desired, 'npm --version', actual => { - return one_line` - The installed version of npm (${actual}) does not satisfy - the desired range of ${desired}. - Please at least have a version within the specified version range. - You can install the latest version by running - \`npm install --global npm@${desired}\`. - `; - }); - } -} -// CONCATENATED MODULE: ./src/verify-system/yarn.js - - -/* harmony default export */ var verify_system_yarn = (getYarnValidator); - -function getYarnValidator(desired) { - return validateYarn; - - function validateYarn() { - return exec_validator(desired, 'yarn --version', actual => { - return one_line` - The installed version of yarn (${actual}) does not satisfy - the desired range of ${desired}. - Please at least have a version within the specified version range. - Updating to the latest version of yarn depends on how you installed it - in the first place. Please see more information here: - https://yarnpkg.com/en/docs/install - `; - }); - } -} -// CONCATENATED MODULE: ./src/verify-system/index.js - - - - - - - -/* harmony default export */ var verify_system = (validateAll); -Object.assign(validateAll, { - utils: { - execValidator: exec_validator, - oneLine: one_line, - semver: semver_default.a - }, - validators: { - node: verify_system_node, - npm: verify_system_npm, - yarn: verify_system_yarn - } -}); - -function validateAll(validators = []) { - const promises = validators.filter(Boolean).map(v => Promise.resolve(v())); - return Promise.all(promises).then(results => { - const errors = results.filter(Boolean); - const errorCount = errors.length; - - if (errorCount) { - const errorMessages = errors.map(error => `- ${error}`).join('\n'); - const one = errorCount === 1; - return Promise.reject([one_line` - There ${one ? 'is an issue' : 'are some issues'} with your system. - `, indent_string_default()(errorMessages, 2)].join('\n')); - } else { - return null; - } - }); -} -// EXTERNAL MODULE: external "path" -var external_path_ = __webpack_require__("path"); -var external_path_default = /*#__PURE__*/__webpack_require__.n(external_path_); - -// EXTERNAL MODULE: external "fs" -var external_fs_ = __webpack_require__("fs"); -var external_fs_default = /*#__PURE__*/__webpack_require__.n(external_fs_); - -// CONCATENATED MODULE: ./src/install-deps/index.js - - - -/* harmony default export */ var install_deps = (installDeps); - -function installDeps(directories = [process.cwd()], { - yarnOk -} = {}) { - if (!Array.isArray(directories)) { - directories = [directories]; - } - - let promise = Promise.resolve(); - directories.forEach(dir => { - promise = promise.then(() => spawnInstall(dir)); - }); - return promise; - - function spawnInstall(cwd) { - return new Promise((resolve, reject) => { - const hasPkgLock = external_fs_default.a.existsSync(external_path_default.a.join(process.cwd(), 'package-lock.json')); - const hasYarnLock = external_fs_default.a.existsSync(external_path_default.a.join(process.cwd(), 'yarn.lock')); - const useYarn = yarnOk && (hasYarnLock || !hasPkgLock); - let installer = 'npm'; - let installerArgs = [hasPkgLock ? 'ci' : 'install', hasPkgLock ? null : '--no-package-lock'].filter(Boolean); - - if (useYarn) { - installer = 'yarn'; - installerArgs = [hasYarnLock ? null : '--no-lockfile'].filter(Boolean); - } else if (!yarnOk && hasYarnLock && !hasPkgLock) { - console.warn(`⚠️ "${cwd}" has a yarn.lock file, but this system does not have the right version of yarn installed.`, `We'll install using npm instead, but you may experience issues. Install the correct version of yarn to get rid of this warning.`); - } - - const command = [installer, ...installerArgs].join(' '); - console.log(`📦 starting \`${command}\` in "${cwd}"`); - const child = external_child_process_default.a.spawn(installer, installerArgs, { - stdio: 'inherit', - shell: true, - cwd - }); - child.on('exit', onExit); - - function onExit(code) { - if (code === 0) { - console.log(`🎉 finished installing dependencies in "${cwd}"`); - resolve(); - } else { - console.error(`💀 error installing dependencies in "${cwd}"`); - reject(cwd); - } - } - }); - } -} -// CONCATENATED MODULE: ./src/setup.js - - -/* harmony default export */ var setup = (setup_setup); - -async function setup_setup({ - directories, - node, - yarn, - npm -}) { - const nodeError = await verify_system.validators.node(node)(); - - if (nodeError) { - return Promise.reject(nodeError); - } - - const npmError = await verify_system.validators.npm(npm)(); - const yarnError = await verify_system.validators.yarn(yarn)(); - - if (yarnError && npmError) { - const errorMessage = [verify_system.utils.oneLine` - We tried to validate The installed version of npm and yarn and both failed. - We recommend you fix yarn, but fixing either will resolve this issue. - Here are the validation error messages for each: - `, '', yarnError, '', npmError].join('\n'); - return Promise.reject(errorMessage); - } - - return install_deps(directories, { - yarnOk: !yarnError - }); -} -// CONCATENATED MODULE: ./src/index.js -/* concated harmony reexport verifySystem */__webpack_require__.d(__webpack_exports__, "verifySystem", function() { return verify_system; }); -/* concated harmony reexport installDeps */__webpack_require__.d(__webpack_exports__, "installDeps", function() { return install_deps; }); -/* concated harmony reexport setup */__webpack_require__.d(__webpack_exports__, "setup", function() { return setup; }); - - - - - -/***/ }), - -/***/ "child_process": -/*!********************************!*\ - !*** external "child_process" ***! - \********************************/ -/*! no static exports found */ -/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ -/***/ (function(module, exports) { - -module.exports = require("child_process"); - -/***/ }), - -/***/ "fs": -/*!*********************!*\ - !*** external "fs" ***! - \*********************/ -/*! no static exports found */ -/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ -/***/ (function(module, exports) { - -module.exports = require("fs"); - -/***/ }), - -/***/ "path": -/*!***********************!*\ - !*** external "path" ***! - \***********************/ -/*! no static exports found */ -/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ -/***/ (function(module, exports) { - -module.exports = require("path"); - -/***/ }), - -/***/ "process": -/*!**************************!*\ - !*** external "process" ***! - \**************************/ -/*! no static exports found */ -/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ -/***/ (function(module, exports) { - -module.exports = require("process"); - -/***/ }) - -/******/ }); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/src/create-workshop-app/get-project-title.js b/src/create-workshop-app/get-project-title.js deleted file mode 100644 index 860a73e7..00000000 --- a/src/create-workshop-app/get-project-title.js +++ /dev/null @@ -1,5 +0,0 @@ -const path = require('path') - -const pkg = require(path.join(process.cwd(), 'package.json')) - -module.exports = pkg.title diff --git a/src/create-workshop-app/hack-fetch.js b/src/create-workshop-app/hack-fetch.js deleted file mode 100644 index 84a81c7d..00000000 --- a/src/create-workshop-app/hack-fetch.js +++ /dev/null @@ -1,64 +0,0 @@ -// Please don't actually do this in a real app -// this is here to make it easy for us to simulate making HTTP calls in this -// little app that doesn't actually have any server element. -const originalFetch = window.fetch - -// Allows us to restore the original fetch -originalFetch.restoreOriginalFetch = () => (window.fetch = originalFetch) -originalFetch.overrideFetch = () => (window.fetch = hackFetch) - -async function hackFetch(...args) { - const fakeResponses = - hackFetch.fakeResponses || window.fetch.fakeResponses || [] - const fakeResponse = fakeResponses.find(({test}) => { - try { - return test(...args) - } catch (error) { - // ignore the error and hope everything's ok... - return false - } - }) - if (!fakeResponse) { - return originalFetch(...args) - } - const groupTitle = `%c ${args[1].method} -> ${args[0]}` - try { - const response = await fakeResponse.handler(...args) - console.groupCollapsed(groupTitle, 'color: #0f9d58') - let parsedBody - try { - parsedBody = JSON.parse(args[1].body) - } catch (error) { - // ignore - } - console.info('REQUEST:', { - url: args[0], - ...args[1], - ...(parsedBody ? {parsedBody} : null), - }) - console.info('RESPONSE:', { - ...response, - ...(response.json ? {json: await response.json()} : {}), - }) - console.groupEnd() - return response - } catch (error) { - let rejection = error - if (error instanceof Error) { - rejection = { - status: 500, - message: error.message, - } - } - console.groupCollapsed(groupTitle, 'color: #ef5350') - console.info('REQUEST:', {url: args[0], ...args[1]}) - console.info('REJECTION:', rejection) - console.groupEnd() - return Promise.reject(rejection) - } -} -hackFetch.isHacked = true -Object.assign(hackFetch, window.fetch) - -// alright. Let's hack fetch! -window.fetch.overrideFetch() diff --git a/src/create-workshop-app/index.js b/src/create-workshop-app/index.js deleted file mode 100644 index b865d2fc..00000000 --- a/src/create-workshop-app/index.js +++ /dev/null @@ -1,381 +0,0 @@ -import './hack-fetch' -import 'normalize.css/normalize.css' -import './workshop-app-styles.css' - -import preval from 'preval.macro' -import React from 'react' -import { - BrowserRouter as Router, - Switch, - Route, - Link, - useParams, -} from 'react-router-dom' -import {createBrowserHistory} from 'history' -import {hijackEffects} from 'stop-runaway-react-effects' - -function createWorkshopApp({ - getExerciseImport, - getFinalImport, - getExampleImport, - fakeFetchResponses = [], - stopRunawayEffects = true, -}) { - window.fetch.fakeResponses = fakeFetchResponses - if (process.env.NODE_ENV !== 'production' && stopRunawayEffects) { - hijackEffects() - } - - const exerciseInfo = preval`module.exports = require('./load-exercises')` - const projectTitle = preval`module.exports = require('./get-project-title')` - - if (!projectTitle) { - throw new Error('The package.json must have a title!') - } - - for (const id in exerciseInfo) { - const info = exerciseInfo[id] - info.exercise.Component = React.lazy(getExerciseImport(id)) - info.final.Component = React.lazy(getFinalImport(id)) - } - - const history = createBrowserHistory() - - function handleAnchorClick(event) { - if (event.metaKey || event.shiftKey) { - return - } - event.preventDefault() - history.push(event.target.closest('a').getAttribute('href')) - } - - function ComponentContainer({label, ...props}) { - return ( -
-

{label}

-
-
- ) - } - - function ExtraCreditLinks({exerciseId}) { - const {extraCreditTitles} = exerciseInfo[exerciseId] - if (!extraCreditTitles) { - return null - } - - return ( -
- {`💯 Extra Credits: `} - {Object.entries(extraCreditTitles).map(([id, title], index, array) => ( - - - {title} - - {array.length - 1 === index ? null : ' | '} - - ))} -
- ) - } - - function ExerciseContainer() { - let {exerciseId} = useParams() - const { - title, - exercise, - final, - exercise: {Component: Exercise}, - final: {Component: Final}, - } = exerciseInfo[exerciseId] - return ( -
-

{title}

- - Exercise - - } - > - - - - Final - - } - > - - - - -
- ) - } - - function NavigationFooter({exerciseId, type}) { - const current = exerciseInfo[exerciseId] - let suffix = '' - let info = current.final - if (type === 'exercise') { - suffix = '/exercise' - info = current.exercise - } else if (type === 'final') { - suffix = '/final' - } - return ( -
-
- {info.previous ? ( - - {exerciseInfo[info.previous].title}{' '} - - 👈 - - - ) : null} -
-
- Home -
-
- {info.next ? ( - - - 👉 - {' '} - {exerciseInfo[info.next].title} - - ) : null} -
-
- ) - } - - function Home() { - return ( -
-

{projectTitle}

- - - 💪 - - Exercise - - {' – '} - - - 🏁 - - Final Version - -
- {Object.entries(exerciseInfo).map( - ([filename, {title, final, exercise}]) => { - return ( -
- {filename} - {'. '} - {title}{' '} - - - 💪 - - - {' – '} - - - 🏁 - - -
- ) - }, - )} -
-
- ) - } - - function NotFound() { - return ( -
-
- Sorry... nothing here. To open one of the exercises, go to{' '} - {`/exerciseId`}, for example:{' '} - - {`/01`} - -
-
- ) - } - - function Routes() { - return ( - - - - - - - - - - - - - - ) - } - - // cache - const lazyComps = {final: {}, exercise: {}, examples: {}} - - function useIsolatedComponent({pathname}) { - const isIsolated = pathname.startsWith('/isolated') - const isFinal = pathname.includes('/exercises-final/') - const isExercise = pathname.includes('/exercises/') - const isExample = pathname.includes('/examples/') - const moduleName = isIsolated - ? pathname.split(/\/isolated\/.*?\//).slice(-1)[0] - : null - const IsolatedComponent = React.useMemo(() => { - if (!moduleName) { - return null - } - if (isFinal) { - return (lazyComps.final[moduleName] = - lazyComps.final[moduleName] || React.lazy(getFinalImport(moduleName))) - } else if (isExercise) { - return (lazyComps.exercise[moduleName] = - lazyComps.exercise[moduleName] || - React.lazy(getExerciseImport(moduleName))) - } else if (getExampleImport && isExample) { - return (lazyComps.examples[moduleName] = - lazyComps.examples[moduleName] || - React.lazy(getExampleImport(moduleName))) - } - }, [isExample, isExercise, isFinal, moduleName]) - return moduleName ? IsolatedComponent : null - } - - function useExerciseTitle({pathname}) { - const isIsolated = pathname.startsWith('/isolated') - const isFinal = pathname.includes('/exercises-final/') - const isExercise = pathname.includes('/exercises/') - const exerciseName = isIsolated - ? pathname.split(/\/isolated\/.*?\//).slice(-1)[0] - : pathname.split('/').slice(-1)[0] - - React.useEffect(() => { - document.title = [ - projectTitle, - exerciseName, - isExercise ? 'Exercise' : null, - isFinal ? 'Final' : null, - ] - .filter(Boolean) - .join(' | ') - }, [exerciseName, isExercise, isFinal]) - } - - function useLocationBodyClassName({pathname}) { - const className = pathname.replace(/\//g, '_') - React.useEffect(() => { - document.body.classList.add(className) - return () => document.body.classList.remove(className) - }, [className]) - } - - // The reason we don't put the Isolated components as regular routes - // and do all this complex stuff instead is so the React DevTools component - // tree is as small as possible to make it easier for people to figure - // out what is relevant to the example. - function WorkshopApp() { - const [location, setLocation] = React.useState(history.location) - React.useEffect(() => history.listen(l => setLocation(l)), []) - useExerciseTitle(location) - useLocationBodyClassName(location) - - const IsolatedComponent = useIsolatedComponent(location) - - return ( - - Loading... -
- } - > - {IsolatedComponent ? ( -
-
- -
-
- ) : ( - - )} - - ) - } - - return WorkshopApp -} - -export default createWorkshopApp diff --git a/src/create-workshop-app/jest-expect-message.js b/src/create-workshop-app/jest-expect-message.js deleted file mode 100644 index 792d8b42..00000000 --- a/src/create-workshop-app/jest-expect-message.js +++ /dev/null @@ -1,71 +0,0 @@ -// Huge thank you to https://github.com/mattphillips/jest-expect-message/blob/95e716c22348c9c30d932984cef36632b69b5d16/src/withMessage.js -import chalk from 'chalk' - -// just made a few modifications for the workshop use case -const originalExpect = global.expect - -class JestAssertionError extends Error { - constructor(result, callsite) { - super(result.message()) - this.matcherResult = result - - if (Error.captureStackTrace) { - Error.captureStackTrace(this, callsite) - } - } -} - -const wrapMatcher = (matcher, customMessage) => { - const newMatcher = (...args) => { - try { - return matcher(...args) - } catch (error) { - if (!error.matcherResult) { - throw error - } - const {matcherResult} = error - - if (typeof customMessage !== 'string' || customMessage.length < 1) { - throw new JestAssertionError(matcherResult, newMatcher) - } - - const message = () => - '🚨 ' + chalk.red(customMessage) + '\n\n' + matcherResult.message() - - throw new JestAssertionError({...matcherResult, message}, newMatcher) - } - } - return newMatcher -} - -const wrapMatchers = (matchers, customMessage) => { - return Object.keys(matchers).reduce((acc, name) => { - const matcher = matchers[name] - - if (typeof matcher === 'function') { - return { - ...acc, - [name]: wrapMatcher(matcher, customMessage), - } - } - - return { - ...acc, - [name]: wrapMatchers(matcher, customMessage), // recurse on .not/.resolves/.rejects - } - }, {}) -} - -// proxy the expect function -let expectProxy = Object.assign( - (actual, customMessage) => - wrapMatchers(originalExpect(actual), customMessage), // partially apply expect to get all matchers and chain them - originalExpect, // clone additional properties on expect -) - -expectProxy.extend = o => { - originalExpect.extend(o) // add new matchers to expect - expectProxy = Object.assign(expectProxy, originalExpect) // clone new asymmetric matchers -} - -global.expect = expectProxy diff --git a/src/create-workshop-app/load-exercises.js b/src/create-workshop-app/load-exercises.js deleted file mode 100644 index 19bf4bd0..00000000 --- a/src/create-workshop-app/load-exercises.js +++ /dev/null @@ -1,75 +0,0 @@ -const path = require('path') -const fs = require('fs') -const glob = require('glob') - -const exerciseInfo = glob - .sync('./src/exercises*/[0-9][0-9]*.js', {cwd: process.cwd()}) - .reduce((acc, filePath) => { - const contents = String(fs.readFileSync(filePath)) - const {dir, name} = path.parse(filePath) - const parentDir = path.basename(dir) - const id = name.split('-')[0] - const number = Number(id) - const previous = String(number - 1).padStart(2, '0') - const next = String(number + 1).padStart(2, '0') - const isExercise = parentDir === 'exercises' - const isExtraCredit = name.includes('-extra') - const isFinal = !isExercise && !isExtraCredit - const [firstLine, secondLine] = contents.split('\n') - const title = firstLine.replace(/\/\//, '').trim() - const extraCreditTitle = secondLine.replace(/\/\/ 💯/, '').trim() - const extraCreditNumber = (name.match(/-extra.(\d+)/) || [])[1] - - acc[id] = acc[id] || {} - - Object.assign( - acc[id], - isExercise - ? { - title, - exercise: { - previous, - next, - isolatedPath: `/isolated/exercises/${name}`, - }, - } - : null, - isFinal - ? { - final: { - previous, - next, - isolatedPath: `/isolated/exercises-final/${name}`, - }, - } - : null, - isExtraCredit - ? { - extraCreditTitles: { - ...acc[id].extraCreditTitles, - [extraCreditNumber]: extraCreditTitle, - }, - } - : null, - ) - return acc - }, {}) - -// get rid of next and previous which don't exist -for (const infoKey in exerciseInfo) { - const info = exerciseInfo[infoKey] - if (!exerciseInfo[info.exercise.previous]) { - delete info.exercise.previous - } - if (!exerciseInfo[info.exercise.next]) { - delete info.exercise.next - } - if (!exerciseInfo[info.final.previous]) { - delete info.final.previous - } - if (!exerciseInfo[info.final.next]) { - delete info.final.next - } -} - -module.exports = exerciseInfo diff --git a/src/create-workshop-app/setup-tests.js b/src/create-workshop-app/setup-tests.js deleted file mode 100644 index 970a9862..00000000 --- a/src/create-workshop-app/setup-tests.js +++ /dev/null @@ -1,21 +0,0 @@ -import './jest-expect-message' -import '@testing-library/jest-dom/extend-expect' - -beforeAll(() => { - jest.spyOn(console, 'info') - console.info.mockImplementation(() => {}) -}) -afterAll(() => console.info.mockRestore()) -beforeEach(() => console.info.mockClear()) - -// none of these tests should actually invoke fetch -beforeEach(() => { - jest.spyOn(window, 'fetch').mockImplementation((...args) => { - console.warn('window.fetch is not mocked for this call', ...args) - return Promise.reject(new Error('This must be mocked!')) - }) -}) - -afterEach(() => { - window.fetch.mockRestore() -}) diff --git a/src/create-workshop-app/workshop-app-styles.css b/src/create-workshop-app/workshop-app-styles.css deleted file mode 100644 index 7cf989db..00000000 --- a/src/create-workshop-app/workshop-app-styles.css +++ /dev/null @@ -1,30 +0,0 @@ -html, -body { - height: 100vh; -} -body { - margin: 0; -} -hr { - width: 100%; -} - -#⚛ { - min-height: 100%; -} - -.isolated-top-container { - padding: 30px; - height: 100vh; - display: grid; - align-items: center; - justify-content: center; -} - -.totally-centered { - width: 100%; - height: 100%; - display: flex; - justify-content: center; - align-items: center; -} diff --git a/src/index.js b/src/index.js index 42f148e0..8a20231c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,17 @@ import React from 'react' import ReactDOM from 'react-dom' -import createWorkshopApp from './create-workshop-app' +import preval from 'preval.macro' +import createWorkshopApp from '@kentcdodds/react-workshop-app' +import pkg from '../package.json' + +const exerciseInfo = preval`module.exports = require('@kentcdodds/react-workshop-app/load-exercises')` const WorkshopApp = createWorkshopApp({ getExerciseImport: id => () => import(`./exercises/${id}.js`), getFinalImport: id => () => import(`./exercises-final/${id}.js`), getExampleImport: id => () => import(`./examples/${id}.js`), + exerciseInfo, + projectTitle: pkg.title, }) const rootEl = document.getElementById('⚛') diff --git a/src/setupTests.js b/src/setupTests.js index c37f2905..3350e6eb 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -1,4 +1,4 @@ -import './create-workshop-app/setup-tests' +import '@kentcdodds/react-workshop-app/setup-tests' import {extensions} from '../test/extensions' diff --git a/test/utils.js b/test/utils.js index e084806a..a8a8ea70 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,5 +1,4 @@ import {render, fireEvent, wait} from '@testing-library/react' -import chalk from 'chalk' import React from 'react' import { findAllInRenderedTree, @@ -13,26 +12,18 @@ const findSwitchInstances = rootInstance => ) function validateSwitchInstance(switchInstance) { - if (!switchInstance) { - throw new Error( - chalk.red( - `🚨 Unable to find the Switch component. Make sure you're rendering that!`, - ), - ) - } - try { - expect(switchInstance.props).toMatchObject({ - on: expect.any(Boolean), - onClick: expect.any(Function), - // it can also have aria-pressed... - }) - } catch (error) { - const helpfulMessage = chalk.red( - '🚨 The Switch component is not being passed the right props.', - ) - error.message = `${helpfulMessage}\n\n${error.message}` - throw error - } + expect( + switchInstance, + `Unable to find the Switch component. Make sure you're rendering that!`, + ).toBeDefined() + expect( + switchInstance.props, + 'The Switch component is not being passed the right props.', + ).toMatchObject({ + on: expect.any(Boolean), + onClick: expect.any(Function), + // it can also have aria-pressed... + }) } // this only exists so we can search for an instance of the Switch