Adds publish/subscribe pattern to allow plugins and projects to hook into events (#55)

Also adds CLI to ease development.
This commit is contained in:
Sebastian Rindom
2020-05-07 13:47:27 +02:00
committed by GitHub
parent 6a78df1ecd
commit 4c2aec2838
39 changed files with 6974 additions and 444 deletions

View File

@@ -1,3 +1,4 @@
/node_modules
/dist
.env

View File

@@ -10,8 +10,7 @@
"author": "Sebastian Rindom",
"license": "GPL-3.0",
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/core": "^7.9.6",
"@babel/node": "^7.7.4",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-instanceof": "^7.8.3",
@@ -27,8 +26,12 @@
"supertest": "^4.0.2"
},
"dependencies": {
"@medusajs/medusa": "^0.1.27",
"bl": "^4.0.2",
"express": "^4.17.1",
"medusa-payment-stripe": "^0.1.27"
"medusa-interfaces": "^0.1.27",
"medusa-payment-stripe": "^0.1.27",
"mongoose": "^5.9.12"
},
"scripts": {
"start": "nodemon --watch plugins/ --watch src/ --exec babel-node node_modules/@medusajs/medusa/dist/app.js"

View File

@@ -0,0 +1,14 @@
class CartSubscriber {
constructor({ cartService, eventBusService }) {
this.eventBus_ = eventBusService
this.eventBus_.subscribe("cart.created", this.log)
this.eventBus_.subscribe("cart.updated", this.log)
}
async log(c) {
console.log(c)
}
}
export default CartSubscriber

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
{
"plugins": ["@babel/plugin-proposal-class-properties"],
"presets": ["@babel/preset-env"],
"env": {
"test": {
"plugins": ["@babel/plugin-transform-runtime"]
}
}
}

View File

@@ -0,0 +1,9 @@
{
"plugins": ["prettier"],
"extends": ["prettier"],
"rules": {
"prettier/prettier": "error",
"semi": "error",
"no-unused-expressions": "true"
}
}

2
packages/medusa-cli/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
dist/
node_modules/

View File

@@ -0,0 +1,7 @@
{
"endOfLine": "lf",
"semi": false,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5"
}

View File

@@ -0,0 +1,49 @@
{
"name": "medusa-cli",
"version": "0.1.0",
"description": "Command Line interface for Medusa Commerce",
"main": "dist/index.js",
"bin": {
"medusa": "dist/index.js"
},
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa",
"directory": "packages/medusa-cli"
},
"scripts": {
"build": "babel src --out-dir dist/ --ignore **/__tests__",
"prepare": "cross-env NODE_ENV=production npm run build",
"watch": "babel -w src --out-dir dist/ --ignore **/__tests__"
},
"author": "Sebastian Rindom",
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"prettier": "^1.19.1"
},
"dependencies": {
"@babel/polyfill": "^7.8.7",
"@babel/runtime": "^7.9.6",
"@hapi/joi": "^16.1.8",
"chalk": "^4.0.0",
"clipboardy": "^2.3.0",
"core-js": "^3.6.5",
"fs-exists-cached": "^1.0.0",
"joi-objectid": "^3.0.1",
"meant": "^1.0.1",
"medusa-core-utils": "^0.1.27",
"regenerator-runtime": "^0.13.5",
"resolve-cwd": "^3.0.0",
"yargs": "^15.3.1"
},
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
}

View File

@@ -0,0 +1,16 @@
const chalk = require(`chalk`)
const showSuccessMessage = () => {
console.log(chalk.green(`Success!\n`))
console.log(chalk.cyan(`Welcome to the Medusa CLI!`))
}
try {
// check if it's a global installation of medusa-cli
const npmArgs = JSON.parse(process.env[`npm_config_argv`])
if (npmArgs.cooked && npmArgs.cooked.includes(`--global`)) {
const createCli = require(`../dist/create-cli`)
showSuccessMessage()
createCli(`--help`)
}
} catch (e) {}

View File

@@ -0,0 +1,238 @@
const path = require(`path`)
const resolveCwd = require(`resolve-cwd`)
const yargs = require(`yargs`)
const { getLocalMedusaVersion } = require(`./util/version`)
const { didYouMean } = require(`./did-you-mean`)
const envinfo = require(`envinfo`)
const existsSync = require(`fs-exists-cached`).sync
const clipboardy = require(`clipboardy`)
const handlerP = fn => (...args) => {
Promise.resolve(fn(...args)).then(
() => process.exit(0),
err => console.log(err)
)
}
function buildLocalCommands(cli, isLocalProject) {
const defaultHost = `localhost`
const defaultPort = `9000`
const directory = path.resolve(`.`)
const projectInfo = { directory }
const useYarn = existsSync(path.join(directory, `yarn.lock`))
if (isLocalProject) {
const json = require(path.join(directory, `package.json`))
projectInfo.sitePackageJson = json
}
function getLocalMedusaMajorVersion() {
let version = getLocalMedusaVersion()
if (version) {
version = Number(version.split(`.`)[0])
}
return version
}
function resolveLocalCommand(command) {
if (!isLocalProject) {
cli.showHelp()
}
try {
const cmdPath = resolveCwd.silent(
`@medusajs/medusa/dist/commands/${command}`
)
return require(cmdPath).default
} catch (err) {
cli.showHelp()
}
}
function getCommandHandler(command, handler) {
return argv => {
const localCmd = resolveLocalCommand(command)
const args = { ...argv, ...projectInfo, useYarn }
// report.verbose(`running command: ${command}`)
return handler ? handler(args, localCmd) : localCmd(args)
}
}
cli
.command({
command: `develop`,
desc: `Start development server. Watches file and rebuilds when something changes`,
builder: _ =>
_.option(`H`, {
alias: `host`,
type: `string`,
default: defaultHost,
describe: `Set host. Defaults to ${defaultHost}`,
}).option(`p`, {
alias: `port`,
type: `string`,
default: process.env.PORT || defaultPort,
describe: process.env.PORT
? `Set port. Defaults to ${process.env.PORT} (set by env.PORT) (otherwise defaults ${defaultPort})`
: `Set port. Defaults to ${defaultPort}`,
}),
handler: handlerP(
getCommandHandler(`develop`, (args, cmd) => {
process.env.NODE_ENV = process.env.NODE_ENV || `development`
cmd(args)
// Return an empty promise to prevent handlerP from exiting early.
// The development server shouldn't ever exit until the user directly
// kills it so this is fine.
return new Promise(resolve => {})
})
),
})
.command({
command: `start`,
desc: `Start development server.`,
builder: _ =>
_.option(`H`, {
alias: `host`,
type: `string`,
default: defaultHost,
describe: `Set host. Defaults to ${defaultHost}`,
}).option(`p`, {
alias: `port`,
type: `string`,
default: process.env.PORT || defaultPort,
describe: process.env.PORT
? `Set port. Defaults to ${process.env.PORT} (set by env.PORT) (otherwise defaults ${defaultPort})`
: `Set port. Defaults to ${defaultPort}`,
}),
handler: handlerP(
getCommandHandler(`start`, (args, cmd) => {
process.env.NODE_ENV = process.env.NODE_ENV || `development`
cmd(args)
// Return an empty promise to prevent handlerP from exiting early.
// The development server shouldn't ever exit until the user directly
// kills it so this is fine.
return new Promise(resolve => {})
})
),
})
.command({
command: `user`,
desc: `Create a user`,
builder: _ =>
_.option(`e`, {
alias: `email`,
type: `string`,
describe: `User's email.`,
}).option(`p`, {
alias: `password`,
type: `string`,
describe: `User's password.`,
}),
handler: handlerP(
getCommandHandler(`user`, (args, cmd) => {
cmd(args)
// Return an empty promise to prevent handlerP from exiting early.
// The development server shouldn't ever exit until the user directly
// kills it so this is fine.
return new Promise(resolve => {})
})
),
})
}
function isLocalMedusaProject() {
let inMedusaProject = false
try {
const { dependencies, devDependencies } = require(path.resolve(
`./package.json`
))
inMedusaProject =
(dependencies && dependencies["@medusajs/medusa"]) ||
(devDependencies && devDependencies["@medusajs/medusa"])
} catch (err) {
/* ignore */
}
return !!inMedusaProject
}
function getVersionInfo() {
const { version } = require(`../package.json`)
const isMedusaProject = isLocalMedusaProject()
if (isMedusaProject) {
let medusaVersion = getLocalMedusaVersion()
if (!medusaVersion) {
medusaVersion = `unknown`
}
return `Medusa CLI version: ${version}
Medusa version: ${medusaVersion}
Note: this is the Medusa version for the site at: ${process.cwd()}`
} else {
return `Medusa CLI version: ${version}`
}
}
module.exports = argv => {
const cli = yargs()
const isLocalProject = isLocalMedusaProject()
cli
.scriptName(`medusa`)
.usage(`Usage: $0 <command> [options]`)
.alias(`h`, `help`)
.alias(`v`, `version`)
.option(`verbose`, {
default: false,
type: `boolean`,
describe: `Turn on verbose output`,
global: true,
})
.option(`no-color`, {
alias: `no-colors`,
default: false,
type: `boolean`,
describe: `Turn off the color in output`,
global: true,
})
.option(`json`, {
describe: `Turn on the JSON logger`,
default: false,
type: `boolean`,
global: true,
})
buildLocalCommands(cli, isLocalProject)
try {
cli.version(
`version`,
`Show the version of the Medusa CLI and the Medusa package in the current project`,
getVersionInfo()
)
} catch (e) {
// ignore
}
return cli
.wrap(cli.terminalWidth())
.demandCommand(1, `Pass --help to see all available commands and options.`)
.strict()
.fail((msg, err, yargs) => {
const availableCommands = yargs.getCommands().map(commandDescription => {
const [command] = commandDescription
return command.split(` `)[0]
})
const arg = argv.slice(2)[0]
const suggestion = arg ? didYouMean(arg, availableCommands) : ``
cli.showHelp()
// report.log(suggestion)
// report.log(msg)
})
.parse(argv.slice(2))
}

View File

@@ -0,0 +1,18 @@
import meant from "meant"
export function didYouMean(scmd, commands) {
const bestSimilarity = meant(scmd, commands).map(str => {
return ` ${str}`
})
if (bestSimilarity.length === 0) return ``
if (bestSimilarity.length === 1) {
return `\nDid you mean this?\n ${bestSimilarity[0]}\n`
} else {
return (
[`\nDid you mean one of these?`]
.concat(bestSimilarity.slice(0, 3))
.join(`\n`) + `\n`
)
}
}

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env node
import "core-js/stable"
import "regenerator-runtime/runtime"
import os from "os"
import semver from "semver"
import util from "util"
import createCli from "./create-cli"
// import report from "./reporter"
import pkg from "../package.json"
// import updateNotifier from "update-notifier"
// import { ensureWindowsDriveLetterIsUppercase } from "./util/ensure-windows-drive-letter-is-uppercase"
const useJsonLogger = process.argv.slice(2).some(arg => arg.includes(`json`))
if (useJsonLogger) {
process.env.GATSBY_LOGGER = `json`
}
// Ensure stable runs on Windows when started from different shells (i.e. c:\dir vs C:\dir)
if (os.platform() === `win32`) {
// ensureWindowsDriveLetterIsUppercase()
}
// Check if update is available
// updateNotifier({ pkg }).notify({ isGlobal: true })
const MIN_NODE_VERSION = `10.13.0`
// const NEXT_MIN_NODE_VERSION = `10.13.0`
if (!semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`)) {
//report.panic(
// report.stripIndent(`
// Gatsby requires Node.js ${MIN_NODE_VERSION} or higher (you have ${process.version}).
// Upgrade Node to the latest stable release: https://gatsby.dev/upgrading-node-js
// `)
//)
}
// if (!semver.satisfies(process.version, `>=${NEXT_MIN_NODE_VERSION}`)) {
// report.warn(
// report.stripIndent(`
// Node.js ${process.version} has reached End of Life status on 31 December, 2019.
// Gatsby will only actively support ${NEXT_MIN_NODE_VERSION} or higher and drop support for Node 8 soon.
// Please upgrade Node.js to a currently active LTS release: https://gatsby.dev/upgrading-node-js
// `)
// )
// }
process.on(`unhandledRejection`, reason => {
// This will exit the process in newer Node anyway so lets be consistent
// across versions and crash
// reason can be anything, it can be a message, an object, ANYTHING!
// we convert it to an error object so we don't crash on structured error validation
if (!(reason instanceof Error)) {
reason = new Error(util.format(reason))
}
console.log(reason)
// report.panic(`UNHANDLED REJECTION`, reason as Error)
})
process.on(`uncaughtException`, error => {
console.log(error)
// report.panic(`UNHANDLED EXCEPTION`, error)
})
createCli(process.argv)

View File

@@ -0,0 +1,6 @@
import { getMedusaVersion } from "medusa-core-utils"
export const getLocalMedusaVersion = () => {
const version = getMedusaVersion()
return version
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
import path from "path"
export const getMedusaVersion = () => {
try {
return require(path.join(
process.cwd(),
`node_modules`,
`@medusajs/medusa`,
`package.json`
)).version
} catch (e) {
return ``
}
}

View File

@@ -846,6 +846,13 @@
levenary "^1.1.0"
semver "^5.5.0"
"@babel/runtime@^7.9.6":
version "7.9.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f"
integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.6":
version "7.8.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
@@ -3996,6 +4003,11 @@ regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
regenerator-runtime@^0.13.4:
version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
regenerator-transform@^0.14.0:
version "0.14.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb"

View File

@@ -19,8 +19,8 @@
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
@@ -30,9 +30,11 @@
"jest": "^25.5.2",
"prettier": "^1.19.1"
},
"peerDependencies": {
"mongoose": "5.x"
},
"dependencies": {
"medusa-core-utils": "^0.3.0",
"mongoose": "^5.8.0"
"medusa-core-utils": "^0.3.0"
},
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
}

View File

@@ -8,7 +8,7 @@ import mongoose from "mongoose"
class BaseModel {
constructor() {
/** @const the underlying mongoose model used for queries */
this.mongooseModel_ = this.createMongooseModel_(this.schema)
this.mongooseModel_ = this.createMongooseModel_()
}
/**
@@ -72,8 +72,9 @@ class BaseModel {
* @param options {?object=} mongoose options
* @return {object} mongoose result
*/
updateOne(query, update, options) {
return this.mongooseModel_.updateOne(query, update, options)
updateOne(query, update, options = {}) {
options.new = true
return this.mongooseModel_.findOneAndUpdate(query, update, options)
}
/**

View File

@@ -0,0 +1,9 @@
{
"plugins": ["prettier"],
"extends": ["prettier"],
"rules": {
"prettier/prettier": "error",
"semi": "error",
"no-unused-expressions": "true"
}
}

View File

@@ -0,0 +1,7 @@
{
"endOfLine": "lf",
"semi": false,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5"
}

View File

@@ -15,6 +15,8 @@
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"client-sessions": "^0.8.0",
@@ -28,9 +30,6 @@
"watch": "babel -w src --out-dir . --ignore **/__tests__"
},
"dependencies": {
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/runtime": "^7.7.6",
"express": "^4.17.1",
"medusa-core-utils": "^0.3.0",
"medusa-interfaces": "^0.3.0"

View File

@@ -0,0 +1,12 @@
class CartSubscriber {
constructor({ cartService, eventBusService }) {
this.cartService_ = cartService
this.eventBus_ = eventBusService
this.eventBus_.subscribe("cart.created", (data) => {
console.log(data)
})
}
}
export default CartSubscriber

View File

@@ -861,6 +861,13 @@
levenary "^1.1.0"
semver "^5.5.0"
"@babel/runtime@^7.9.6":
version "7.9.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f"
integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.6":
version "7.8.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
@@ -4064,6 +4071,11 @@ regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
regenerator-runtime@^0.13.4:
version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
regenerator-transform@^0.14.0:
version "0.14.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb"

View File

@@ -38,6 +38,12 @@
"serve": "node dist/app.js",
"test": "jest"
},
"peerDependencies": {
"medusa-interfaces": "^0.3.0"
},
"peerDependencies": {
"mongoose": "5.x"
},
"dependencies": {
"@babel/plugin-transform-classes": "^7.9.5",
"@hapi/joi": "^16.1.8",
@@ -47,7 +53,6 @@
"bull": "^3.12.1",
"client-sessions": "^0.8.0",
"cookie-parser": "^1.4.4",
"core-js": "^3.4.8",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
@@ -57,15 +62,13 @@
"joi-objectid": "^3.0.1",
"jsonwebtoken": "^8.5.1",
"medusa-core-utils": "^0.3.0",
"medusa-interfaces": "^0.3.0",
"medusa-interfaces": "^0.1.27",
"medusa-test-utils": "^0.3.0",
"mongoose": "^5.8.0",
"morgan": "^1.9.1",
"passport": "^0.4.0",
"passport-http-bearer": "^1.0.1",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"regenerator-runtime": "^0.13.3",
"winston": "^3.2.1"
},
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"

View File

@@ -0,0 +1,45 @@
import { spawn, execSync } from "child_process"
import mongoose from "mongoose"
import chokidar from "chokidar"
import express from "express"
import path from "path"
import loaders from "../loaders"
import Logger from "../loaders/logger"
export default async function({ port, directory }) {
const args = process.argv
args.shift()
args.shift()
args.shift()
execSync("babel src -d dist", {
cwd: directory,
stdio: ["ignore", process.stdout, process.stderr],
})
let child = spawn("medusa", [`start`, ...args], {
cwd: directory,
env: process.env,
stdio: ["pipe", process.stdout, process.stderr],
})
chokidar.watch(`${directory}/src`).on("change", file => {
const f = file.split("src")[1]
Logger.info(`${f} changed: restarting...`)
child.kill("SIGINT")
execSync(`babel src -d dist`, {
cwd: directory,
stdio: ["pipe", process.stdout, process.stderr],
})
Logger.info("Rebuilt")
child = spawn("medusa", [`start`, ...args], {
cwd: directory,
env: process.env,
stdio: ["pipe", process.stdout, process.stderr],
})
})
}

View File

@@ -0,0 +1,28 @@
import "core-js/stable"
import "regenerator-runtime/runtime"
import mongoose from "mongoose"
import chokidar from "chokidar"
import express from "express"
import cwdResolve from "resolve-cwd"
import loaders from "../loaders"
import Logger from "../loaders/logger"
export default async function({ port, directory }) {
async function start() {
const app = express()
const { dbConnection } = await loaders({ directory, expressApp: app })
const server = app.listen(port, err => {
if (err) {
return
}
Logger.info(`Server is ready on port: ${port}!`)
})
return { dbConnection, server }
}
let { dbConnection, server } = await start()
}

View File

@@ -0,0 +1,23 @@
import "core-js/stable"
import "regenerator-runtime/runtime"
import mongoose from "mongoose"
import chokidar from "chokidar"
import express from "express"
import cwdResolve from "resolve-cwd"
import loaders from "../loaders"
import Logger from "../loaders/logger"
export default async function({ directory, email, password }) {
const app = express()
const { container, dbConnection } = await loaders({
directory,
expressApp: app,
})
const userService = container.resolve("userService")
const user = await userService.create({ email }, password)
process.exit()
}

View File

@@ -8,7 +8,7 @@ import passportLoader from "./passport"
import pluginsLoader from "./plugins"
import Logger from "./logger"
export default async ({ expressApp }) => {
export default async ({ directory: rootDirectory, expressApp }) => {
const container = createContainer()
container.registerAdd = function(name, registration) {
let storeKey = name + "_STORE"
@@ -36,7 +36,7 @@ export default async ({ expressApp }) => {
await servicesLoader({ container })
Logger.info("Services initialized")
await mongooseLoader({ container })
const dbConnection = await mongooseLoader({ container })
Logger.info("MongoDB Intialized")
await expressLoader({ app: expressApp })
@@ -51,11 +51,13 @@ export default async ({ expressApp }) => {
next()
})
await pluginsLoader({ container, app: expressApp })
await pluginsLoader({ container, rootDirectory, app: expressApp })
Logger.info("Plugins Intialized")
await apiLoader({ container, app: expressApp })
Logger.info("API initialized")
return { container, dbConnection, app: expressApp }
}
function asArray(resolvers) {

View File

@@ -3,17 +3,19 @@ import config from "../config"
export default async ({ container }) => {
const logger = container.resolve("logger")
await mongoose
.connect(config.databaseURL, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
})
.catch(err => {
logger.error(err)
})
mongoose.connection.on("error", err => {
logger.error(err)
})
return mongoose
.connect(config.databaseURL, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false,
})
.catch(err => {
logger.error(err)
})
}

View File

@@ -15,9 +15,9 @@ import { sync as existsSync } from "fs-exists-cached"
/**
* Registers all services in the services directory
*/
export default ({ container, app }) => {
export default ({ rootDirectory, container, app }) => {
const { configModule, configFilePath } = getConfigFile(
process.cwd(),
rootDirectory,
`medusa-config`
)
@@ -39,7 +39,7 @@ export default ({ container, app }) => {
})
resolved.push({
resolve: process.cwd(),
resolve: `${rootDirectory}/dist`,
name: `project-plugin`,
id: createPluginId(`project-plugin`),
options: {},
@@ -51,6 +51,7 @@ export default ({ container, app }) => {
registerServices(pluginDetails, container)
registerMedusaApi(pluginDetails, container)
registerApi(pluginDetails, app)
registerSubscribers(pluginDetails, container)
})
}
@@ -156,6 +157,31 @@ function registerServices(pluginDetails, container) {
})
}
/**
* Registers a plugin's models at the right location in our container. Models
* must inherit from BaseModel. Models are registered directly in the container.
* Names are camelCase formatted and namespaced by the folder i.e:
* models/example-person -> examplePersonModel
* @param {object} pluginDetails - the plugin details including plugin options,
* version, id, resolved path, etc. See resolvePlugin
* @param {object} container - the container where the services will be
* registered
* @return {void}
*/
function registerSubscribers(pluginDetails, container) {
const files = glob.sync(`${pluginDetails.resolve}/subscribers/*.js`, {})
files.forEach(fn => {
const loaded = require(fn).default
const name = formatRegistrationName(fn)
container.build(
asFunction(
cradle => new loaded(cradle, pluginDetails.options)
).singleton()
)
})
}
/**
* Registers a plugin's models at the right location in our container. Models
* must inherit from BaseModel. Models are registered directly in the container.

View File

@@ -312,9 +312,9 @@ export const carts = {
}
export const CartModelMock = {
create: jest.fn().mockReturnValue(Promise.resolve()),
create: jest.fn().mockImplementation(data => Promise.resolve(data)),
updateOne: jest.fn().mockImplementation((query, update) => {
return Promise.resolve()
return Promise.resolve(update)
}),
deleteOne: jest.fn().mockReturnValue(Promise.resolve()),
findOne: jest.fn().mockImplementation(query => {

View File

@@ -15,15 +15,15 @@ class CartModel extends BaseModel {
static schema = {
email: { type: String },
billing_address: { type: AddressSchema, default: {} },
shipping_address: { type: AddressSchema, default: {} },
billing_address: { type: AddressSchema },
shipping_address: { type: AddressSchema },
items: { type: [LineItemSchema], default: [] },
region_id: { type: String, required: true },
discounts: { type: [DiscountModel.schema], default: [] },
customer_id: { type: String, default: "" },
payment_sessions: { type: [PaymentMethodSchema], default: [] },
shipping_options: { type: [ShippingMethodSchema], default: [] },
payment_method: { type: PaymentMethodSchema, default: {} },
payment_method: { type: PaymentMethodSchema },
shipping_methods: { type: [ShippingMethodSchema], default: [] },
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
}

View File

@@ -0,0 +1,10 @@
export const EventBusServiceMock = {
emit: jest.fn(),
subscribe: jest.fn(),
}
const mock = jest.fn().mockImplementation(() => {
return EventBusServiceMock
})
export default mock

View File

@@ -7,6 +7,7 @@ import {
} from "../__mocks__/payment-provider"
import { ProductVariantServiceMock } from "../__mocks__/product-variant"
import { RegionServiceMock } from "../__mocks__/region"
import { EventBusServiceMock } from "../__mocks__/event-bus"
import { ShippingOptionServiceMock } from "../__mocks__/shipping-option"
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
import { CartModelMock, carts } from "../../models/__mocks__/cart"
@@ -40,6 +41,7 @@ describe("CartService", () => {
describe("setMetadata", () => {
const cartService = new CartService({
cartModel: CartModelMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -50,6 +52,12 @@ describe("CartService", () => {
const id = mongoose.Types.ObjectId()
await cartService.setMetadata(`${id}`, "metadata", "testMetadata")
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(CartModelMock.updateOne).toBeCalledTimes(1)
expect(CartModelMock.updateOne).toBeCalledWith(
{ _id: `${id}` },
@@ -74,6 +82,7 @@ describe("CartService", () => {
const cartService = new CartService({
cartModel: CartModelMock,
regionService: RegionServiceMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -85,6 +94,12 @@ describe("CartService", () => {
region_id: IdMap.getId("testRegion"),
})
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.created",
expect.any(Object)
)
expect(CartModelMock.create).toHaveBeenCalledTimes(1)
expect(CartModelMock.create).toHaveBeenCalledWith({
region_id: IdMap.getId("testRegion"),
@@ -97,6 +112,7 @@ describe("CartService", () => {
cartModel: CartModelMock,
productVariantService: ProductVariantServiceMock,
lineItemService: LineItemServiceMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -123,6 +139,12 @@ describe("CartService", () => {
await cartService.addLineItem(IdMap.getId("emptyCart"), lineItem)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
{
@@ -280,6 +302,7 @@ describe("CartService", () => {
cartModel: CartModelMock,
productVariantService: ProductVariantServiceMock,
lineItemService: LineItemServiceMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -310,6 +333,12 @@ describe("CartService", () => {
lineItem
)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
{
@@ -357,6 +386,7 @@ describe("CartService", () => {
describe("updateEmail", () => {
const cartService = new CartService({
cartModel: CartModelMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -369,6 +399,12 @@ describe("CartService", () => {
"test@testdom.com"
)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
{
@@ -394,6 +430,7 @@ describe("CartService", () => {
describe("updateBillingAddress", () => {
const cartService = new CartService({
cartModel: CartModelMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -413,6 +450,12 @@ describe("CartService", () => {
await cartService.updateBillingAddress(IdMap.getId("emptyCart"), address)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
{
@@ -450,6 +493,7 @@ describe("CartService", () => {
describe("updateShippingAddress", () => {
const cartService = new CartService({
cartModel: CartModelMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -469,6 +513,12 @@ describe("CartService", () => {
await cartService.updateShippingAddress(IdMap.getId("emptyCart"), address)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
{
@@ -508,6 +558,7 @@ describe("CartService", () => {
cartModel: CartModelMock,
regionService: RegionServiceMock,
productVariantService: ProductVariantServiceMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -520,6 +571,12 @@ describe("CartService", () => {
IdMap.getId("region-us")
)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
{
@@ -645,6 +702,7 @@ describe("CartService", () => {
cartModel: CartModelMock,
regionService: RegionServiceMock,
paymentProviderService: PaymentProviderServiceMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -664,6 +722,12 @@ describe("CartService", () => {
paymentMethod
)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(RegionServiceMock.retrieve).toHaveBeenCalledTimes(1)
expect(RegionServiceMock.retrieve).toHaveBeenCalledWith(
IdMap.getId("testRegion")
@@ -787,6 +851,7 @@ describe("CartService", () => {
cartModel: CartModelMock,
regionService: RegionServiceMock,
paymentProviderService: PaymentProviderServiceMock,
eventBusService: EventBusServiceMock,
})
beforeEach(() => {
@@ -796,6 +861,12 @@ describe("CartService", () => {
it("initializes payment sessions for each of the providers", async () => {
await cartService.setPaymentSessions(IdMap.getId("cartWithLine"))
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(2)
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledWith(
"default_provider",
@@ -938,6 +1009,7 @@ describe("CartService", () => {
describe("retrievePaymentSession", () => {
const cartService = new CartService({
cartModel: CartModelMock,
eventBusService: EventBusServiceMock,
})
let res
@@ -999,6 +1071,7 @@ describe("CartService", () => {
cartModel: CartModelMock,
shippingProfileService: ShippingProfileServiceMock,
shippingOptionService: ShippingOptionServiceMock,
eventBusService: EventBusServiceMock,
})
describe("successfully adds the shipping method", () => {
@@ -1043,6 +1116,12 @@ describe("CartService", () => {
})
it("updates cart", () => {
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
{
@@ -1171,6 +1250,7 @@ describe("CartService", () => {
const cartService = new CartService({
cartModel: CartModelMock,
discountService: DiscountServiceMock,
eventBusService: EventBusServiceMock,
})
beforeEach(async () => {
jest.clearAllMocks()
@@ -1183,6 +1263,12 @@ describe("CartService", () => {
_id: IdMap.getId("fr-cart"),
})
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
"cart.updated",
expect.any(Object)
)
expect(DiscountServiceMock.retrieveByCode).toHaveBeenCalledTimes(1)
expect(DiscountServiceMock.retrieveByCode).toHaveBeenCalledWith("10%OFF")

View File

@@ -7,6 +7,11 @@ import { BaseService } from "medusa-interfaces"
* @implements BaseService
*/
class CartService extends BaseService {
static Events = {
CREATED: "cart.created",
UPDATED: "cart.updated",
}
constructor({
cartModel,
eventBusService,
@@ -199,6 +204,10 @@ class CartService extends BaseService {
.create({
region_id: region._id,
})
.then(result => {
this.eventBus_.emit(CartService.Events.CREATED, result)
return result
})
.catch(err => {
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
})
@@ -246,17 +255,23 @@ class CartService extends BaseService {
)
}
return this.cartModel_.updateOne(
{
_id: cartId,
"items._id": currentItem._id,
},
{
$set: {
"items.$.quantity": newQuantity,
return this.cartModel_
.updateOne(
{
_id: cartId,
"items._id": currentItem._id,
},
}
)
{
$set: {
"items.$.quantity": newQuantity,
},
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
// Confirm inventory
@@ -273,14 +288,20 @@ class CartService extends BaseService {
}
// The line we are adding doesn't already exist so it is safe to push
return this.cartModel_.updateOne(
{
_id: cartId,
},
{
$push: { items: validatedLineItem },
}
)
return this.cartModel_
.updateOne(
{
_id: cartId,
},
{
$push: { items: validatedLineItem },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
@@ -325,17 +346,23 @@ class CartService extends BaseService {
}
// Update the line item
return this.cartModel_.updateOne(
{
_id: cartId,
"items._id": lineItemId,
},
{
$set: {
"items.$": validatedLineItem,
return this.cartModel_
.updateOne(
{
_id: cartId,
"items._id": lineItemId,
},
}
)
{
$set: {
"items.$": validatedLineItem,
},
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
@@ -357,14 +384,20 @@ class CartService extends BaseService {
)
}
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$set: { email: value },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$set: { email: value },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
@@ -383,14 +416,20 @@ class CartService extends BaseService {
)
}
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$set: { billing_address: value },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$set: { billing_address: value },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
@@ -409,14 +448,20 @@ class CartService extends BaseService {
)
}
return this.cartModel_.updateOne(
{
_id: cartId,
},
{
$set: { shipping_address: value },
}
)
return this.cartModel_
.updateOne(
{
_id: cartId,
},
{
$set: { shipping_address: value },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
* Updates the cart's discounts.
@@ -455,49 +500,73 @@ class CartService extends BaseService {
shippingDisc.length === 0 &&
discount.discount_rule.type === "free_shipping"
) {
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$push: { discounts: discount },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$push: { discounts: discount },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
} else if (
shippingDisc.length > 0 &&
discount.discount_rule.type === "free_shipping"
) {
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$pull: { discounts: { _id: shippingDisc[0]._id } },
$push: { discounts: discount },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$pull: { discounts: { _id: shippingDisc[0]._id } },
$push: { discounts: discount },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
// replace the current discount if there, else add the new one
if (otherDisc.length === 0) {
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$push: { discounts: discount },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$push: { discounts: discount },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
} else {
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$pull: { discounts: { _id: otherDisc[0]._id } },
$push: { discounts: discount },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$pull: { discounts: { _id: otherDisc[0]._id } },
$push: { discounts: discount },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
}
@@ -571,14 +640,20 @@ class CartService extends BaseService {
}
// At this point we can register the payment method.
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$set: { payment_method: paymentMethod },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$set: { payment_method: paymentMethod },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
@@ -627,14 +702,20 @@ class CartService extends BaseService {
// Update the payment sessions with the concatenated array of updated and
// newly created payment sessions
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$set: { payment_sessions: sessions.concat(newSessions) },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$set: { payment_sessions: sessions.concat(newSessions) },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
@@ -693,14 +774,20 @@ class CartService extends BaseService {
newMethods.push(option)
}
return this.cartModel_.updateOne(
{
_id: cart._id,
},
{
$set: { shipping_methods: newMethods },
}
)
return this.cartModel_
.updateOne(
{
_id: cart._id,
},
{
$set: { shipping_methods: newMethods },
}
)
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
@@ -763,7 +850,13 @@ class CartService extends BaseService {
update.payment_method = undefined
}
return this.cartModel_.updateOne({ _id: cart._id }, { $set: update })
return this.cartModel_
.updateOne({ _id: cart._id }, { $set: update })
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
}
/**
@@ -788,6 +881,11 @@ class CartService extends BaseService {
const keyPath = `metadata.${key}`
return this.cartModel_
.updateOne({ _id: validatedId }, { $set: { [keyPath]: value } })
.then(result => {
// Notify subscribers
this.eventBus_.emit(CartService.Events.UPDATED, result)
return result
})
.catch(err => {
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
})

View File

@@ -59,7 +59,8 @@ class EventBusService {
*
* @returns {Promise} resolves to the results of the subscriber calls.
*/
async worker_({ eventName, data }) {
worker_ = job => {
const { eventName, data } = job.data
const observers = this.observers_[eventName] || []
this.logger_.info(
`Processing ${eventName} which has ${observers.length} subscribers`

View File

@@ -2156,7 +2156,7 @@ core-js-compat@^3.4.7:
browserslist "^4.8.0"
semver "^6.3.0"
core-js@^3.2.1, core-js@^3.4.8:
core-js@^3.2.1:
version "3.4.8"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.4.8.tgz#e0fc0c61f2ef90cbc10c531dbffaa46dfb7152dd"
integrity sha512-b+BBmCZmVgho8KnBUOXpvlqEMguko+0P+kXCwD4vIprsXC6ht1qgPxtb1OK6XgSlrySF71wkwBQ0Hv695bk9gQ==
@@ -4522,6 +4522,13 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
medusa-interfaces@^0.1.27:
version "0.1.27"
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.27.tgz#e77f9a9f82a7118eac8b35c1498ef8a5cec78898"
integrity sha512-FVRyD85aBCPon+itf0cgTc1/lQXxHcMXGWGF8fIMzcoVj8xqSjculwZmY4GH4EtKhv3X3+DGe0BsIMbiW31unA==
dependencies:
mongoose "^5.8.0"
memory-pager@^1.0.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"

184
yarn.lock
View File

@@ -2,157 +2,6 @@
# yarn lockfile v1
"@babel/code-frame@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==
dependencies:
"@babel/highlight" "^7.8.3"
"@babel/generator@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9"
integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==
dependencies:
"@babel/types" "^7.9.5"
jsesc "^2.5.1"
lodash "^4.17.13"
source-map "^0.5.0"
"@babel/helper-annotate-as-pure@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee"
integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-define-map@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15"
integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==
dependencies:
"@babel/helper-function-name" "^7.8.3"
"@babel/types" "^7.8.3"
lodash "^4.17.13"
"@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c"
integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==
dependencies:
"@babel/helper-get-function-arity" "^7.8.3"
"@babel/template" "^7.8.3"
"@babel/types" "^7.9.5"
"@babel/helper-get-function-arity@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5"
integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-member-expression-to-functions@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c"
integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-optimise-call-expression@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9"
integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-plugin-utils@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==
"@babel/helper-replace-supers@^7.8.6":
version "7.8.6"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8"
integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==
dependencies:
"@babel/helper-member-expression-to-functions" "^7.8.3"
"@babel/helper-optimise-call-expression" "^7.8.3"
"@babel/traverse" "^7.8.6"
"@babel/types" "^7.8.6"
"@babel/helper-split-export-declaration@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==
"@babel/highlight@^7.8.3":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==
dependencies:
"@babel/helper-validator-identifier" "^7.9.0"
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/parser@^7.8.6", "@babel/parser@^7.9.0":
version "7.9.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8"
integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==
"@babel/plugin-transform-classes@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c"
integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.8.3"
"@babel/helper-define-map" "^7.8.3"
"@babel/helper-function-name" "^7.9.5"
"@babel/helper-optimise-call-expression" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/helper-replace-supers" "^7.8.6"
"@babel/helper-split-export-declaration" "^7.8.3"
globals "^11.1.0"
"@babel/template@^7.8.3":
version "7.8.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/parser" "^7.8.6"
"@babel/types" "^7.8.6"
"@babel/traverse@^7.8.6":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2"
integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/generator" "^7.9.5"
"@babel/helper-function-name" "^7.9.5"
"@babel/helper-split-export-declaration" "^7.8.3"
"@babel/parser" "^7.9.0"
"@babel/types" "^7.9.5"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.13"
"@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444"
integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==
dependencies:
"@babel/helper-validator-identifier" "^7.9.5"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@evocateur/libnpmaccess@^3.1.2":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845"
@@ -1401,7 +1250,7 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
chalk@^2.0.0, chalk@^2.3.1, chalk@^2.4.2:
chalk@^2.3.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -1744,13 +1593,6 @@ debug@^3.1.0:
dependencies:
ms "^2.1.1"
debug@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
@@ -2349,11 +2191,6 @@ glob@^7.1.1, glob@^7.1.3, glob@^7.1.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globby@^9.2.0:
version "9.2.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d"
@@ -2854,11 +2691,6 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^3.13.1:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
@@ -2872,11 +2704,6 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -3065,7 +2892,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.2.1:
lodash@^4.17.12, lodash@^4.17.15, lodash@^4.2.1:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -4368,7 +4195,7 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
source-map@^0.5.0, source-map@^0.5.6:
source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@@ -4671,11 +4498,6 @@ tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
to-object-path@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"