feat: CLI + local linking (#313)

* fix: allow local cmd without exiting process

* fix: improves cli experience

* fix: allow running link with --develop

* test: adds snapshot testing of error logs

* chore: cleanup

* feat(medusa-cli): new command (#320)

* adds: new command

* fix: creates  command for easy project setup

* chore: deps

* chore: deps

* fix: loggin

* fix: logging

* fix: adds cli as dependency in core

* fix: consolidates CLI in medusa

* fix: use project deps medusa bin

* fix: use project deps medusa bin

* fix: use cli path

* fix: new command setup db + env vars

* fix: new command with db seed

* fix: cleanup
This commit is contained in:
Sebastian Rindom
2021-07-26 10:09:55 +02:00
committed by GitHub
parent 804a2f6ed9
commit f4a7138a58
22 changed files with 2284 additions and 159 deletions
@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Reporter handles "Error" signature correctly 1`] = `
Object {
"level": "error",
"message": "Error",
"stack": Any<Array>,
}
`;
exports[`Reporter handles "String" signature correctly 1`] = `
Object {
"level": "error",
"message": "Test log",
}
`;
exports[`Reporter handles "String, Error" signature correctly 1`] = `
Object {
"level": "error",
"message": "Test log",
"stack": Any<Array>,
}
`;
@@ -0,0 +1,49 @@
import logger, { Reporter } from "../"
describe(`Reporter`, () => {
const winstonMock = {
log: jest.fn(),
}
const reporter = new Reporter({
logger: winstonMock,
activityLogger: {},
})
const getErrorMessages = fn =>
fn.mock.calls
.map(([firstArg]) => firstArg)
.filter(structuredMessage => structuredMessage.level === `error`)
beforeEach(() => {
winstonMock.log.mockClear()
})
it(`handles "String" signature correctly`, () => {
reporter.error("Test log")
const generated = getErrorMessages(winstonMock.log)[0]
expect(generated).toMatchSnapshot()
})
it(`handles "String, Error" signature correctly`, () => {
reporter.error("Test log", new Error("String Error"))
const generated = getErrorMessages(winstonMock.log)[0]
expect(generated).toMatchSnapshot({
stack: expect.any(Array),
})
})
it(`handles "Error" signature correctly`, () => {
reporter.error(new Error("Error"))
const generated = getErrorMessages(winstonMock.log)[0]
expect(generated).toMatchSnapshot({
stack: expect.any(Array),
})
})
})
+254
View File
@@ -0,0 +1,254 @@
import stackTrace from "stack-trace"
import { ulid } from "ulid"
import winston from "winston"
import ora from "ora"
const LOG_LEVEL = process.env.LOG_LEVEL || "silly"
const NODE_ENV = process.env.NODE_ENV || "development"
const transports = []
if (process.env.NODE_ENV && process.env.NODE_ENV !== "development") {
transports.push(new winston.transports.Console())
} else {
transports.push(
new winston.transports.Console({
format: winston.format.combine(
winston.format.cli(),
winston.format.splat()
),
})
)
}
const loggerInstance = winston.createLogger({
level: LOG_LEVEL,
levels: winston.config.npm.levels,
format: winston.format.combine(
winston.format.timestamp({
format: "YYYY-MM-DD HH:mm:ss",
}),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json()
),
transports,
})
export class Reporter {
constructor({ logger, activityLogger }) {
this.activities_ = []
this.loggerInstance_ = logger
this.ora_ = activityLogger
}
panic = error => {
this.loggerInstance_.log({
level: "error",
details: error,
})
process.exit(1)
}
/**
* Determines if the logger should log at a given level.
* @param {string} level - the level to check if logger is configured for
* @return {boolean} whether we should log
*/
shouldLog = level => {
level = this.loggerInstance_.levels[level]
const logLevel = this.loggerInstance_.levels[this.loggerInstance_.level]
return level <= logLevel
}
/**
* Sets the log level of the logger.
* @param {string} level - the level to set the logger to
*/
setLogLevel = level => {
this.loggerInstance_.level = level
}
/**
* Resets the logger to the value specified by the LOG_LEVEL env var. If no
* LOG_LEVEL is set it defaults to "silly".
*/
unsetLogLevel = () => {
this.loggerInstance_.level = LOG_LEVEL
}
/**
* Begin an activity. In development an activity is displayed as a spinner;
* in other environments it will log the activity at the info level.
* @param {string} message - the message to log the activity under
* @returns {string} the id of the activity; this should be passed to do
* further operations on the activity such as success, failure, progress.
*/
activity = message => {
const id = ulid()
if (NODE_ENV === "development" && this.shouldLog("info")) {
const activity = this.ora_(message).start()
this.activities_[id] = {
activity,
start: Date.now(),
}
return id
} else {
this.activities_[id] = {
start: Date.now(),
}
this.loggerInstance_.log({
activity_id: id,
level: "info",
message,
})
return id
}
}
/**
* Reports progress on an activity. In development this will update the
* activity log message, in other environments a log message will be issued
* at the info level. Logging will include the activityId.
* @param {string} activityId - the id of the activity as returned by activity
* @param {string} message - the message to log
*/
progress = (activityId, message) => {
const toLog = {
level: "info",
message,
}
if (typeof activityId === "string" && this.activities_[activityId]) {
const activity = this.activities_[activityId]
if (activity.activity) {
activity.text = message
} else {
toLog.activity_id = activityId
this.loggerInstance_.log(toLog)
}
} else {
this.loggerInstance_.log(toLog)
}
}
/**
* Logs an error. If an error object is provided the stack trace for the error
* will also be logged.
* @param {String | Error} messageOrError - can either be a string with a
* message to log the error under; or an error object.
* @param {Error?} error - an error object to log message with
*/
error = (messageOrError, error) => {
let message = messageOrError
if (typeof messageOrError === "object") {
message = messageOrError.message
error = messageOrError
}
const toLog = {
level: "error",
message,
}
if (error) {
toLog.stack = stackTrace.parse(error)
}
this.loggerInstance_.log(toLog)
}
/**
* Reports failure of an activity. In development the activity will be udpated
* with the failure message in other environments the failure will be logged
* at the error level.
* @param {string} activityId - the id of the activity as returned by activity
* @param {string} message - the message to log
*/
failure = (activityId, message) => {
const toLog = {
level: "error",
message,
}
if (typeof activityId === "string" && this.activities_[activityId]) {
const time = Date.now()
const activity = this.activities_[activityId]
if (activity.activity) {
activity.activity.fail(`${message} ${time - activity.start}`)
} else {
toLog.duration = time - activity.start
toLog.activity_id = activityId
this.loggerInstance_.log(toLog)
}
} else {
this.loggerInstance_.log(toLog)
}
}
/**
* Reports success of an activity. In development the activity will be udpated
* with the failure message in other environments the failure will be logged
* at the info level.
* @param {string} activityId - the id of the activity as returned by activity
* @param {string} message - the message to log
*/
success = (activityId, message) => {
const toLog = {
level: "info",
message,
}
if (typeof activityId === "string" && this.activities_[activityId]) {
const time = Date.now()
const activity = this.activities_[activityId]
if (activity.activity) {
activity.activity.succeed(`${message} ${time - activity.start}ms`)
} else {
toLog.duration = time - activity.start
toLog.activity_id = activityId
this.loggerInstance_.log(toLog)
}
} else {
this.loggerInstance_.log(toLog)
}
}
/**
* Logs a message at the info level.
* @param {string} message - the message to log
*/
info = message => {
this.loggerInstance_.log({
level: "info",
message,
})
}
/**
* Logs a message at the warn level.
* @param {string} message - the message to log
*/
warn = message => {
this.loggerInstance_.warn({
level: "warn",
message,
})
}
/**
* A wrapper around winston's log method.
*/
log = (...args) => {
this.loggerInstance_.log(...args)
}
}
const logger = new Reporter({
logger: loggerInstance,
activityLogger: ora,
})
export default logger