chore(): start moving some packages to the core directory (#7215)
This commit is contained in:
1 parent
fdee748eed
commit
bbccd6481d
1436 files changed
+275
-756
No files matched your search
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
/dist
|
||||
yarn.lock
|
||||
my-medusa-store
|
||||
@@ -0,0 +1,188 @@
|
||||
# Change Log
|
||||
|
||||
## 1.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#6027](https://github.com/medusajs/medusa/pull/6027) [`fe1d3a4a78`](https://github.com/medusajs/medusa/commit/fe1d3a4a78385bb356539d6f9cba5ce2012f5da8) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): Add a `--verbose` option.
|
||||
|
||||
## 1.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#6729](https://github.com/medusajs/medusa/pull/6729) [`fbc369705d`](https://github.com/medusajs/medusa/commit/fbc369705d4feae21d77ea2fc59173ac9519cee6) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): Add `--v2` option
|
||||
|
||||
## 1.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5983](https://github.com/medusajs/medusa/pull/5983) [`f86877586`](https://github.com/medusajs/medusa/commit/f86877586147ecedbf7f56a1c57f37ef0c33286c) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(create-medusa-app,medusa-core-utils): Use NodeJS.Timeout instead of NodeJS.Timer as the latter was deprecated in v14.
|
||||
chore(icons): Update icons to latest version.
|
||||
|
||||
## 1.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5547](https://github.com/medusajs/medusa/pull/5547) [`09ab1d1be`](https://github.com/medusajs/medusa/commit/09ab1d1be6ec0c50e3fcc828c740265debd2222b) Thanks [@egormkn](https://github.com/egormkn)! - feat(create-medusa-app): print error message for failed database connection
|
||||
|
||||
## 1.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5474](https://github.com/medusajs/medusa/pull/5474) [`03959c3e3`](https://github.com/medusajs/medusa/commit/03959c3e3a38c155607647e70668ee250b76fda9) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): improve spinner style
|
||||
|
||||
## 1.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5404](https://github.com/medusajs/medusa/pull/5404) [`a1807aea8`](https://github.com/medusajs/medusa/commit/a1807aea83a5bf82c4221839a1fa57b41d2cc6ac) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): add tracking for selected options
|
||||
|
||||
## 1.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5189](https://github.com/medusajs/medusa/pull/5189) [`18a05dee8`](https://github.com/medusajs/medusa/commit/18a05dee86f55366c92a2669eacbccf526373ff5) Thanks [@shahednasser](https://github.com/shahednasser)! - fix(create-medusa-app): fix inconsistency in checking errors in migrations
|
||||
|
||||
## 1.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#5061](https://github.com/medusajs/medusa/pull/5061) [`5b09f816c`](https://github.com/medusajs/medusa/commit/5b09f816cbb44d457c8cbdee803d9f84bd3fed0d) Thanks [@shahednasser](https://github.com/shahednasser)! - fix(create-medusa-app): fix command for windows OS
|
||||
|
||||
## 1.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#4968](https://github.com/medusajs/medusa/pull/4968) [`240b03800`](https://github.com/medusajs/medusa/commit/240b038006924d4872de068c98e2d6862145cb52) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): add install Next.js storefront option
|
||||
|
||||
## 1.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`c58588904`](https://github.com/medusajs/medusa/commit/c58588904c5631111603b15afacf7cdc4c738cc4)]:
|
||||
- medusa-telemetry@0.0.17
|
||||
|
||||
## 1.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#4794](https://github.com/medusajs/medusa/pull/4794) [`c684d16ec`](https://github.com/medusajs/medusa/commit/c684d16ec012a547a9981480db8b4b96f5f22904) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): remove `--stable` option and change to clone default branch
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#4733](https://github.com/medusajs/medusa/pull/4733) [`30ce35b16`](https://github.com/medusajs/medusa/commit/30ce35b163afa25f4e1d8d1bd392f401a3b413df) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app, utils, medusa-cli): add database options + remove util from `@medusajs/utils`
|
||||
|
||||
## 1.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#4567](https://github.com/medusajs/medusa/pull/4567) [`15e87a810`](https://github.com/medusajs/medusa/commit/15e87a8100c9fe66f6d120423ef0351f4c657e7e) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): add `stable` option + add URI encoding to database string
|
||||
|
||||
- Updated dependencies [[`131477faf`](https://github.com/medusajs/medusa/commit/131477faf0409c49d4aacf26ea591e33b2fa22fd)]:
|
||||
- @medusajs/utils@1.9.3
|
||||
|
||||
## 1.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#4543](https://github.com/medusajs/medusa/pull/4543) [`f32588122`](https://github.com/medusajs/medusa/commit/f32588122733a4dca54d06a53d18e14ad8ed021b) Thanks [@shahednasser](https://github.com/shahednasser)! - fix(create-medusa-app): improved error messages
|
||||
|
||||
## 1.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#4493](https://github.com/medusajs/medusa/pull/4493) [`4b4296dc1`](https://github.com/medusajs/medusa/commit/4b4296dc1651d325bd7e64eb0fdb0584989828f7) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): remove .git directory in the project
|
||||
|
||||
- [#4369](https://github.com/medusajs/medusa/pull/4369) [`d363da2b7`](https://github.com/medusajs/medusa/commit/d363da2b72820defe90b12b0dc741087f3b21090) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(create-medusa-app): Cleanup the main script for readability and maintanability
|
||||
|
||||
- Updated dependencies [[`499c3478c`](https://github.com/medusajs/medusa/commit/499c3478c910c8b922a15cc6f4d9fbad122a347f), [`9dcdc0041`](https://github.com/medusajs/medusa/commit/9dcdc0041a2b08cc0723343dd8d9127d9977b086), [`9760d4a96`](https://github.com/medusajs/medusa/commit/9760d4a96c27f6f89a8c3f3b6e73b17547f97f2a)]:
|
||||
- @medusajs/utils@1.9.2
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#4339](https://github.com/medusajs/medusa/pull/4339) [`6d5da9166`](https://github.com/medusajs/medusa/commit/6d5da9166f609017f92c8c4c34c8eeca53699e3d) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): support admin onboarding experience
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- [`b779040ac`](https://github.com/medusajs/medusa/commit/b779040acbaded8596db697a9f8dbe7451cfb9ca) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Bump create-medusa-app to major
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#4215](https://github.com/medusajs/medusa/pull/4215) [`c04d93cd0`](https://github.com/medusajs/medusa/commit/c04d93cd041a0080af46c6068d6cf4724600af02) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app): update command for a better onboarding experience
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#4273](https://github.com/medusajs/medusa/pull/4273) [`f98ba5bde`](https://github.com/medusajs/medusa/commit/f98ba5bde83ba785eead31b0c9eb9f135d664178) Thanks [@shahednasser](https://github.com/shahednasser)! - feat(create-medusa-app,medusa-cli): Allow clearing project
|
||||
|
||||
- [#4321](https://github.com/medusajs/medusa/pull/4321) [`5ad734740`](https://github.com/medusajs/medusa/commit/5ad7347408746fd717354c99a530aaab5e86ffa2) Thanks [@shahednasser](https://github.com/shahednasser)! - fix(create-medusa-app): disable opening browser
|
||||
|
||||
- [#4328](https://github.com/medusajs/medusa/pull/4328) [`e1f43fd20`](https://github.com/medusajs/medusa/commit/e1f43fd207621deec885c06f704c2e4c5d7c559b) Thanks [@shahednasser](https://github.com/shahednasser)! - chore(create-medusa-app): change boxen version
|
||||
|
||||
- [#4247](https://github.com/medusajs/medusa/pull/4247) [`4b5b7b514`](https://github.com/medusajs/medusa/commit/4b5b7b51483bae8996235a37e75c0671f9e2994f) Thanks [@shahednasser](https://github.com/shahednasser)! - fix(create-medusa-app): remove seed command from create-medusa-app and improve success message
|
||||
|
||||
- Updated dependencies [[`f98ba5bde`](https://github.com/medusajs/medusa/commit/f98ba5bde83ba785eead31b0c9eb9f135d664178), [`14c0f62f8`](https://github.com/medusajs/medusa/commit/14c0f62f84704a4c87beff3daaff60a52f5c88b8)]:
|
||||
- @medusajs/utils@1.9.1
|
||||
|
||||
## 0.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#3376](https://github.com/medusajs/medusa/pull/3376) [`88392e017`](https://github.com/medusajs/medusa/commit/88392e017653b53069793bd2ee451c2e6b7bcf17) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore(create-medusa-app): Remove admin + Gatsby starter from npx
|
||||
|
||||
## 0.0.10-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#3376](https://github.com/medusajs/medusa/pull/3376) [`88392e017`](https://github.com/medusajs/medusa/commit/88392e017653b53069793bd2ee451c2e6b7bcf17) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore(create-medusa-app): Remove admin + Gatsby starter from npx
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#3217](https://github.com/medusajs/medusa/pull/3217) [`8c5219a31`](https://github.com/medusajs/medusa/commit/8c5219a31ef76ee571fbce84d7d57a63abe56eb0) Thanks [@adrien2p](https://github.com/adrien2p)! - chore: Fix npm packages files included
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#3185](https://github.com/medusajs/medusa/pull/3185) [`08324355a`](https://github.com/medusajs/medusa/commit/08324355a4466b017a0bc7ab1d333ee3cd27b8c4) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Patches all dependencies + minor bumps `winston` to include a [fix for a significant memory leak](https://github.com/winstonjs/winston/pull/2057)
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#2069](https://github.com/medusajs/medusa/pull/2069) [`ad717b953`](https://github.com/medusajs/medusa/commit/ad717b9533a0500e20c4e312d1ee48b35ea9d5e1) Thanks [@olivermrbl](https://github.com/olivermrbl)! - Remove deprecated dependency `@hapi/joi`
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.0.6](https://github.com/medusajs/medusa/compare/create-medusa-app@0.0.5...create-medusa-app@0.0.6) (2022-01-11)
|
||||
|
||||
### Features
|
||||
|
||||
- **create-medusa-app:** add medusa.express ([#981](https://github.com/medusajs/medusa/issues/981)) ([bbc16d6](https://github.com/medusajs/medusa/commit/bbc16d6b115fb389ee0fe58d909e74a162686163))
|
||||
|
||||
## [0.0.5](https://github.com/medusajs/medusa/compare/create-medusa-app@0.0.3...create-medusa-app@0.0.5) (2021-10-18)
|
||||
|
||||
**Note:** Version bump only for package create-medusa-app
|
||||
|
||||
## [0.0.4](https://github.com/medusajs/medusa/compare/create-medusa-app@0.0.3...create-medusa-app@0.0.4) (2021-10-18)
|
||||
|
||||
**Note:** Version bump only for package create-medusa-app
|
||||
|
||||
## [0.0.3](https://github.com/medusajs/medusa/compare/create-medusa-app@0.0.1...create-medusa-app@0.0.3) (2021-09-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- flip seed flag to default to true ([#398](https://github.com/medusajs/medusa/issues/398)) ([dd1025f](https://github.com/medusajs/medusa/commit/dd1025fd5369eb264d7d5f5d6db41c888259d786))
|
||||
- versioning ([3b8901e](https://github.com/medusajs/medusa/commit/3b8901ebc2fb41dc8d5372a808c5eaafd7d32646))
|
||||
|
||||
## 0.0.1 (2021-09-14)
|
||||
|
||||
### Features
|
||||
|
||||
- adds create-medusa-app ([#377](https://github.com/medusajs/medusa/issues/377)) ([ec6d16e](https://github.com/medusajs/medusa/commit/ec6d16e945f4b8a99e9dcc8ae2e92a2318fbc709))
|
||||
@@ -0,0 +1,58 @@
|
||||
<p align="center">
|
||||
<a href="https://www.medusajs.com">
|
||||
<img alt="Medusa" src="https://user-images.githubusercontent.com/7554214/153162406-bf8fd16f-aa98-4604-b87b-e13ab4baf604.png" width="100" />
|
||||
</a>
|
||||
</p>
|
||||
<h1 align="center">
|
||||
create-medusa-app
|
||||
</h1>
|
||||
|
||||
<h4 align="center">
|
||||
<a href="https://docs.medusajs.com">Documentation</a> |
|
||||
<a href="https://www.medusajs.com">Website</a>
|
||||
</h4>
|
||||
|
||||
<p align="center">
|
||||
An open source composable commerce engine built for developers.
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/medusajs/medusa/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Medusa is released under the MIT license." />
|
||||
</a>
|
||||
<a href="https://circleci.com/gh/medusajs/medusa">
|
||||
<img src="https://circleci.com/gh/medusajs/medusa.svg?style=shield" alt="Current CircleCI build status." />
|
||||
</a>
|
||||
<a href="https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md">
|
||||
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
|
||||
</a>
|
||||
<a href="https://www.producthunt.com/posts/medusa"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Day-%23DA552E" alt="Product Hunt"></a>
|
||||
<a href="https://discord.gg/xpCwq3Kfn8">
|
||||
<img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
|
||||
</a>
|
||||
<a href="https://twitter.com/intent/follow?screen_name=medusajs">
|
||||
<img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Overview
|
||||
|
||||
Using this NPX command, you can setup a Medusa backend and admin along with a PostgreSQL database in simple steps.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
Run the following command in your terminal:
|
||||
|
||||
```bash
|
||||
npx create-medusa-app@latest
|
||||
```
|
||||
|
||||
Then, answer the prompted questions to setup your PostgreSQL database and Medusa project. Once the setup is done, the Medusa admin dashboard will open in your default browser.
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description | Default value |
|
||||
|--------------------|-------------------------------------------------------|------------------------------------------------------|
|
||||
| `--repo-url <url>` | Create Medusa project from a different repository URL | `https://github.com/medusajs/medusa-starter-default` |
|
||||
| `--seed` | Using this option seeds the database with demo data | false |
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "create-medusa-app",
|
||||
"version": "1.2.8",
|
||||
"description": "Create a Medusa project using a single command.",
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
"bin": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "ts-node --esm src/index.ts",
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch",
|
||||
"prepublishOnly": "cross-env NODE_ENV=production tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"boxen": "^5",
|
||||
"chalk": "^5.2.0",
|
||||
"commander": "^10.0.1",
|
||||
"glob": "^7.1.6",
|
||||
"inquirer": "^9.2.2",
|
||||
"medusa-telemetry": "^0.0.17",
|
||||
"nanoid": "^4.0.2",
|
||||
"node-emoji": "^2.0.2",
|
||||
"node-fetch": "^3.3.1",
|
||||
"open": "^9.1.0",
|
||||
"ora": "^6.3.0",
|
||||
"pg": "^8.10.0",
|
||||
"slugify": "^1.6.6",
|
||||
"uuid": "^9.0.0",
|
||||
"validator": "^13.9.0",
|
||||
"wait-on": "^7.0.1",
|
||||
"winston": "^3.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chalk": "^2.2.0",
|
||||
"@types/commander": "^2.12.2",
|
||||
"@types/configstore": "^6.0.0",
|
||||
"@types/inquirer": "^9.0.3",
|
||||
"@types/node-emoji": "^1.8.2",
|
||||
"@types/pg": "^8.6.6",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@types/validator": "^13.7.17",
|
||||
"@types/wait-on": "^5.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
||||
"@typescript-eslint/parser": "^6.19.0",
|
||||
"configstore": "^6.0.0",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"prettier": "^2.8.8",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/medusajs/medusa.git",
|
||||
"directory": "packages/create-medusa-app"
|
||||
},
|
||||
"author": "Medusa",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "41a5425405aea5045a26def95c0dc00cf4a5a44d"
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
import inquirer from "inquirer"
|
||||
import slugifyType from "slugify"
|
||||
import chalk from "chalk"
|
||||
import { getDbClientAndCredentials, runCreateDb } from "../utils/create-db.js"
|
||||
import prepareProject from "../utils/prepare-project.js"
|
||||
import startMedusa from "../utils/start-medusa.js"
|
||||
import open from "open"
|
||||
import waitOn from "wait-on"
|
||||
import ora, { Ora } from "ora"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import isEmailImported from "validator/lib/isEmail.js"
|
||||
import logMessage from "../utils/log-message.js"
|
||||
import createAbortController, {
|
||||
isAbortError,
|
||||
} from "../utils/create-abort-controller.js"
|
||||
import { track } from "medusa-telemetry"
|
||||
import boxen from "boxen"
|
||||
import { emojify } from "node-emoji"
|
||||
import ProcessManager from "../utils/process-manager.js"
|
||||
import { nanoid } from "nanoid"
|
||||
import { displayFactBox, FactBoxOptions } from "../utils/facts.js"
|
||||
import { EOL } from "os"
|
||||
import { runCloneRepo } from "../utils/clone-repo.js"
|
||||
import {
|
||||
askForNextjsStarter,
|
||||
installNextjsStarter,
|
||||
startNextjsStarter,
|
||||
} from "../utils/nextjs-utils.js"
|
||||
|
||||
const slugify = slugifyType.default
|
||||
const isEmail = isEmailImported.default
|
||||
|
||||
export type CreateOptions = {
|
||||
repoUrl?: string
|
||||
seed?: boolean
|
||||
// commander passed --no-boilerplate as boilerplate
|
||||
boilerplate?: boolean
|
||||
skipDb?: boolean
|
||||
dbUrl?: string
|
||||
browser?: boolean
|
||||
migrations?: boolean
|
||||
directoryPath?: string
|
||||
withNextjsStarter?: boolean
|
||||
verbose?: boolean
|
||||
v2?: boolean
|
||||
}
|
||||
|
||||
export default async ({
|
||||
repoUrl = "",
|
||||
seed,
|
||||
boilerplate,
|
||||
skipDb,
|
||||
dbUrl,
|
||||
browser,
|
||||
migrations,
|
||||
directoryPath,
|
||||
withNextjsStarter = false,
|
||||
verbose = false,
|
||||
v2 = false,
|
||||
}: CreateOptions) => {
|
||||
track("CREATE_CLI_CMA")
|
||||
|
||||
const spinner: Ora = ora()
|
||||
const processManager = new ProcessManager()
|
||||
const abortController = createAbortController(processManager)
|
||||
const factBoxOptions: FactBoxOptions = {
|
||||
interval: null,
|
||||
spinner,
|
||||
processManager,
|
||||
message: "",
|
||||
title: "",
|
||||
verbose,
|
||||
}
|
||||
const dbName = !skipDb && !dbUrl ? `medusa-${nanoid(4)}` : ""
|
||||
let isProjectCreated = false
|
||||
let isDbInitialized = false
|
||||
let printedMessage = false
|
||||
let nextjsDirectory = ""
|
||||
|
||||
processManager.onTerminated(async () => {
|
||||
spinner.stop()
|
||||
// prevent an error from occurring if
|
||||
// client hasn't been declared yet
|
||||
if (isDbInitialized && client) {
|
||||
await client.end()
|
||||
}
|
||||
|
||||
// the SIGINT event is triggered twice once the backend runs
|
||||
// this ensures that the message isn't printed twice to the user
|
||||
if (!printedMessage && isProjectCreated) {
|
||||
printedMessage = true
|
||||
showSuccessMessage(projectName, undefined, nextjsDirectory)
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
const projectName = await askForProjectName(directoryPath)
|
||||
const projectPath = getProjectPath(projectName, directoryPath)
|
||||
const adminEmail =
|
||||
!skipDb && migrations ? await askForAdminEmail(seed, boilerplate) : ""
|
||||
const installNextjs = withNextjsStarter || (await askForNextjsStarter())
|
||||
|
||||
let { client, dbConnectionString } = !skipDb
|
||||
? await getDbClientAndCredentials({
|
||||
dbName,
|
||||
dbUrl,
|
||||
verbose,
|
||||
})
|
||||
: { client: null, dbConnectionString: "" }
|
||||
isDbInitialized = true
|
||||
|
||||
track("CMA_OPTIONS", {
|
||||
repoUrl,
|
||||
seed,
|
||||
boilerplate,
|
||||
skipDb,
|
||||
browser,
|
||||
migrations,
|
||||
installNextjs,
|
||||
verbose,
|
||||
})
|
||||
|
||||
logMessage({
|
||||
message: `${emojify(
|
||||
":rocket:"
|
||||
)} Starting project setup, this may take a few minutes.`,
|
||||
})
|
||||
|
||||
spinner.start()
|
||||
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Setting up project...",
|
||||
})
|
||||
|
||||
try {
|
||||
await runCloneRepo({
|
||||
projectName: projectPath,
|
||||
repoUrl,
|
||||
abortController,
|
||||
spinner,
|
||||
verbose,
|
||||
v2,
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Created project directory",
|
||||
})
|
||||
|
||||
nextjsDirectory = installNextjs
|
||||
? await installNextjsStarter({
|
||||
directoryName: projectPath,
|
||||
abortController,
|
||||
factBoxOptions,
|
||||
verbose,
|
||||
})
|
||||
: ""
|
||||
|
||||
if (client && !dbUrl) {
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Creating database...",
|
||||
})
|
||||
client = await runCreateDb({ client, dbName, spinner })
|
||||
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: `Database ${dbName} created`,
|
||||
})
|
||||
}
|
||||
|
||||
// prepare project
|
||||
let inviteToken: string | undefined = undefined
|
||||
try {
|
||||
inviteToken = await prepareProject({
|
||||
directory: projectPath,
|
||||
dbConnectionString,
|
||||
admin: {
|
||||
email: adminEmail,
|
||||
},
|
||||
seed,
|
||||
boilerplate,
|
||||
spinner,
|
||||
processManager,
|
||||
abortController,
|
||||
skipDb,
|
||||
migrations,
|
||||
onboardingType: installNextjs ? "nextjs" : "default",
|
||||
nextjsDirectory,
|
||||
client,
|
||||
verbose,
|
||||
v2,
|
||||
})
|
||||
} catch (e: any) {
|
||||
if (isAbortError(e)) {
|
||||
process.exit()
|
||||
}
|
||||
|
||||
spinner.stop()
|
||||
logMessage({
|
||||
message: `An error occurred while preparing project: ${e}`,
|
||||
type: "error",
|
||||
})
|
||||
|
||||
return
|
||||
} finally {
|
||||
// close db connection
|
||||
await client?.end()
|
||||
}
|
||||
|
||||
spinner.succeed(chalk.green("Project Prepared"))
|
||||
|
||||
if (skipDb || !browser) {
|
||||
showSuccessMessage(projectPath, inviteToken, nextjsDirectory)
|
||||
process.exit()
|
||||
}
|
||||
|
||||
// start backend
|
||||
logMessage({
|
||||
message: "Starting Medusa...",
|
||||
})
|
||||
|
||||
try {
|
||||
startMedusa({
|
||||
directory: projectPath,
|
||||
abortController,
|
||||
})
|
||||
|
||||
if (installNextjs && nextjsDirectory) {
|
||||
startNextjsStarter({
|
||||
directory: nextjsDirectory,
|
||||
abortController,
|
||||
verbose,
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
if (isAbortError(e)) {
|
||||
process.exit()
|
||||
}
|
||||
|
||||
logMessage({
|
||||
message: `An error occurred while starting Medusa`,
|
||||
type: "error",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
isProjectCreated = true
|
||||
|
||||
await waitOn({
|
||||
resources: ["http://localhost:9000/health"],
|
||||
}).then(async () =>
|
||||
open(
|
||||
inviteToken
|
||||
? `http://localhost:7001/invite?token=${inviteToken}&first_run=true`
|
||||
: "http://localhost:7001"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
async function askForProjectName(directoryPath?: string): Promise<string> {
|
||||
const { projectName } = await inquirer.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "projectName",
|
||||
message: "What's the name of your project?",
|
||||
default: "my-medusa-store",
|
||||
filter: (input) => {
|
||||
return slugify(input).toLowerCase()
|
||||
},
|
||||
validate: (input) => {
|
||||
if (!input.length) {
|
||||
return "Please enter a project name"
|
||||
}
|
||||
const projectPath = getProjectPath(input, directoryPath)
|
||||
return fs.existsSync(projectPath) &&
|
||||
fs.lstatSync(projectPath).isDirectory()
|
||||
? "A directory already exists with the same name. Please enter a different project name."
|
||||
: true
|
||||
},
|
||||
},
|
||||
])
|
||||
return projectName
|
||||
}
|
||||
|
||||
async function askForAdminEmail(
|
||||
seed?: boolean,
|
||||
boilerplate?: boolean
|
||||
): Promise<string> {
|
||||
const { adminEmail } = await inquirer.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "adminEmail",
|
||||
message: "Enter an email for your admin dashboard user",
|
||||
default: !seed && boilerplate ? "admin@medusa-test.com" : undefined,
|
||||
validate: (input) => {
|
||||
return typeof input === "string" && input.length > 0 && isEmail(input)
|
||||
? true
|
||||
: "Please enter a valid email"
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
return adminEmail
|
||||
}
|
||||
|
||||
function showSuccessMessage(
|
||||
projectName: string,
|
||||
inviteToken?: string,
|
||||
nextjsDirectory?: string
|
||||
) {
|
||||
logMessage({
|
||||
message: boxen(
|
||||
chalk.green(
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
`Change to the \`${projectName}\` directory to explore your Medusa project.${EOL}${EOL}Start your Medusa app again with the following command:${EOL}${EOL}npx @medusajs/medusa-cli develop${EOL}${EOL}${inviteToken ? `After you start the Medusa app, you can set a password for your admin user with the URL ${getInviteUrl(inviteToken)}${EOL}${EOL}` : ""}${nextjsDirectory?.length ? `The Next.js Starter storefront was installed in the \`${nextjsDirectory}\` directory. Change to that directory and start it with the following command:${EOL}${EOL}npm run dev${EOL}${EOL}` : ""}Check out the Medusa documentation to start your development:${EOL}${EOL}https://docs.medusajs.com/${EOL}${EOL}Star us on GitHub if you like what we're building:${EOL}${EOL}https://github.com/medusajs/medusa/stargazers`
|
||||
),
|
||||
{
|
||||
titleAlignment: "center",
|
||||
textAlignment: "center",
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
float: "center",
|
||||
}
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
function getProjectPath(projectName: string, directoryPath?: string) {
|
||||
return path.join(directoryPath || "", projectName)
|
||||
}
|
||||
|
||||
function getInviteUrl(inviteToken: string) {
|
||||
return `http://localhost:7001/invite?token=${inviteToken}&first_run=true`
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env node
|
||||
import { program } from "commander"
|
||||
import create from "./commands/create.js"
|
||||
|
||||
program
|
||||
.description("Create a new Medusa project")
|
||||
.option("--repo-url <url>", "URL of repository to use to setup project.")
|
||||
.option("--seed", "Seed the created database with demo data.")
|
||||
.option(
|
||||
"--no-boilerplate",
|
||||
"Install a Medusa project without the boilerplate and demo files."
|
||||
)
|
||||
.option(
|
||||
"--skip-db",
|
||||
"Skips creating the database, running migrations, and seeding, and subsequently skips opening the browser.",
|
||||
false
|
||||
)
|
||||
.option(
|
||||
"--db-url <url>",
|
||||
"Skips database creation and sets the database URL to the provided URL. Throws an error if can't connect to the database. Will still run migrations and open the admin after project creation."
|
||||
)
|
||||
.option(
|
||||
"--no-migrations",
|
||||
"Skips running migrations, creating admin user, and seeding. If used, it's expected that you pass the --db-url option with a url of a database that has all necessary migrations. Otherwise, unexpected errors will occur.",
|
||||
true
|
||||
)
|
||||
.option(
|
||||
"--no-browser",
|
||||
"Disables opening the browser at the end of the project creation and only shows success message.",
|
||||
true
|
||||
)
|
||||
.option(
|
||||
"--directory-path <path>",
|
||||
"Specify the directory path to install the project in."
|
||||
)
|
||||
.option(
|
||||
"--with-nextjs-starter",
|
||||
"Install the Next.js starter along with the Medusa backend",
|
||||
false
|
||||
)
|
||||
.option(
|
||||
"--verbose",
|
||||
"Show all logs of underlying commands. Useful for debugging.",
|
||||
false
|
||||
)
|
||||
.option(
|
||||
"--v2",
|
||||
"Install Medusa with the V2 feature flag enabled. WARNING: Medusa V2 is still in development and shouldn't be used in production.",
|
||||
false
|
||||
)
|
||||
.parse()
|
||||
|
||||
void create(program.opts())
|
||||
@@ -0,0 +1 @@
|
||||
declare module "medusa-telemetry"
|
||||
@@ -0,0 +1,25 @@
|
||||
import fs from "fs"
|
||||
import glob from "glob"
|
||||
import path from "path"
|
||||
|
||||
export function clearProject(directory: string) {
|
||||
const adminFiles = glob.sync(path.join(directory, `src`, `admin/**/*`))
|
||||
const onboardingFiles = glob.sync(
|
||||
path.join(directory, `src`, `**/onboarding/`)
|
||||
)
|
||||
const typeFiles = glob.sync(path.join(directory, `src`, `types`))
|
||||
const srcFiles = glob.sync(
|
||||
path.join(directory, `src`, `**/*.{ts,tsx,js,jsx}`)
|
||||
)
|
||||
|
||||
const files = [...adminFiles, ...onboardingFiles, ...typeFiles, ...srcFiles]
|
||||
|
||||
files.forEach((file) =>
|
||||
fs.rmSync(file, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
})
|
||||
)
|
||||
// add empty typescript file to avoid build errors
|
||||
fs.openSync(path.join(directory, "src", "index.ts"), "w")
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import execute from "./execute.js"
|
||||
import { Ora } from "ora"
|
||||
import { isAbortError } from "./create-abort-controller.js"
|
||||
import logMessage from "./log-message.js"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
type CloneRepoOptions = {
|
||||
directoryName?: string
|
||||
repoUrl?: string
|
||||
abortController?: AbortController
|
||||
verbose?: boolean
|
||||
v2?: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_REPO = "https://github.com/medusajs/medusa-starter-default"
|
||||
const V2_BRANCH = "feat/v2"
|
||||
|
||||
export default async function cloneRepo({
|
||||
directoryName = "",
|
||||
repoUrl,
|
||||
abortController,
|
||||
verbose = false,
|
||||
v2 = false,
|
||||
}: CloneRepoOptions) {
|
||||
await execute(
|
||||
[
|
||||
`git clone ${repoUrl || DEFAULT_REPO}${
|
||||
v2 ? ` -b ${V2_BRANCH}` : ""
|
||||
} ${directoryName}`,
|
||||
{
|
||||
signal: abortController?.signal,
|
||||
},
|
||||
],
|
||||
{ verbose }
|
||||
)
|
||||
}
|
||||
|
||||
export async function runCloneRepo({
|
||||
projectName,
|
||||
repoUrl,
|
||||
abortController,
|
||||
spinner,
|
||||
verbose = false,
|
||||
v2 = false,
|
||||
}: {
|
||||
projectName: string
|
||||
repoUrl: string
|
||||
abortController: AbortController
|
||||
spinner: Ora
|
||||
verbose?: boolean
|
||||
v2?: boolean
|
||||
}) {
|
||||
try {
|
||||
await cloneRepo({
|
||||
directoryName: projectName,
|
||||
repoUrl,
|
||||
abortController,
|
||||
verbose,
|
||||
v2,
|
||||
})
|
||||
|
||||
deleteGitDirectory(projectName)
|
||||
} catch (e) {
|
||||
if (isAbortError(e)) {
|
||||
process.exit()
|
||||
}
|
||||
|
||||
spinner.stop()
|
||||
logMessage({
|
||||
message: `An error occurred while setting up your project: ${e}`,
|
||||
type: "error",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function deleteGitDirectory(projectDirectory: string) {
|
||||
fs.rmSync(path.join(projectDirectory, ".git"), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import ProcessManager from "./process-manager.js"
|
||||
|
||||
export default (processManager: ProcessManager) => {
|
||||
const abortController = new AbortController()
|
||||
processManager.onTerminated(() => abortController.abort())
|
||||
return abortController
|
||||
}
|
||||
|
||||
export const isAbortError = (e: any) =>
|
||||
e !== null && "code" in e && e.code === "ABORT_ERR"
|
||||
|
||||
export const getAbortError = () => {
|
||||
return {
|
||||
code: "ABORT_ERR",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
import { EOL } from "os"
|
||||
import pg from "pg"
|
||||
import postgresClient from "./postgres-client.js"
|
||||
import inquirer from "inquirer"
|
||||
import logMessage from "./log-message.js"
|
||||
import formatConnectionString from "./format-connection-string.js"
|
||||
import { Ora } from "ora"
|
||||
import { getCurrentOs } from "./get-current-os.js"
|
||||
|
||||
type CreateDbOptions = {
|
||||
client: pg.Client
|
||||
db: string
|
||||
}
|
||||
|
||||
export default async function createDb({ client, db }: CreateDbOptions) {
|
||||
await client.query(`CREATE DATABASE "${db}"`)
|
||||
}
|
||||
|
||||
export async function runCreateDb({
|
||||
client,
|
||||
dbName,
|
||||
spinner,
|
||||
}: {
|
||||
client: pg.Client
|
||||
dbName: string
|
||||
spinner: Ora
|
||||
}): Promise<pg.Client> {
|
||||
let newClient = client
|
||||
|
||||
try {
|
||||
// create postgres database
|
||||
await createDb({
|
||||
client,
|
||||
db: dbName,
|
||||
})
|
||||
|
||||
// create a new connection with database selected
|
||||
await client.end()
|
||||
newClient = await postgresClient({
|
||||
user: client.user,
|
||||
password: client.password,
|
||||
database: dbName,
|
||||
})
|
||||
} catch (e) {
|
||||
spinner.stop()
|
||||
logMessage({
|
||||
message: `An error occurred while trying to create your database: ${e}`,
|
||||
type: "error",
|
||||
})
|
||||
}
|
||||
|
||||
return newClient
|
||||
}
|
||||
|
||||
async function getForDbName({
|
||||
dbName,
|
||||
verbose = false,
|
||||
}: {
|
||||
dbName: string
|
||||
verbose?: boolean
|
||||
}): Promise<{
|
||||
client: pg.Client
|
||||
dbConnectionString: string
|
||||
}> {
|
||||
let client!: pg.Client
|
||||
let postgresUsername = "postgres"
|
||||
let postgresPassword = ""
|
||||
|
||||
try {
|
||||
client = await postgresClient({
|
||||
user: postgresUsername,
|
||||
password: postgresPassword,
|
||||
})
|
||||
} catch (e) {
|
||||
if (verbose) {
|
||||
logMessage({
|
||||
message: `The following error occured when connecting to the database: ${e}`,
|
||||
type: "verbose",
|
||||
})
|
||||
}
|
||||
// ask for the user's postgres credentials
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "postgresUsername",
|
||||
message: "Enter your Postgres username",
|
||||
default: "postgres",
|
||||
validate: (input) => {
|
||||
return typeof input === "string" && input.length > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "password",
|
||||
name: "postgresPassword",
|
||||
message: "Enter your Postgres password",
|
||||
},
|
||||
])
|
||||
|
||||
postgresUsername = answers.postgresUsername
|
||||
postgresPassword = answers.postgresPassword
|
||||
|
||||
try {
|
||||
client = await postgresClient({
|
||||
user: postgresUsername,
|
||||
password: postgresPassword,
|
||||
})
|
||||
} catch (e) {
|
||||
logMessage({
|
||||
message: `Couldn't connect to PostgreSQL because of the following error: ${e}.${EOL}${EOL}Make sure you have PostgreSQL installed and the credentials you provided are correct.${EOL}${EOL}You can learn how to install PostgreSQL here: https://docs.medusajs.com/development/backend/prepare-environment?os=${getCurrentOs()}#postgresql${EOL}${EOL}If you keep running into this issue despite having PostgreSQL installed, please check out our troubleshooting guidelines: https://docs.medusajs.com/troubleshooting/database-error`,
|
||||
type: "error",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// format connection string
|
||||
const dbConnectionString = formatConnectionString({
|
||||
user: postgresUsername,
|
||||
password: postgresPassword,
|
||||
host: client!.host,
|
||||
db: dbName,
|
||||
})
|
||||
|
||||
return {
|
||||
client,
|
||||
dbConnectionString,
|
||||
}
|
||||
}
|
||||
|
||||
async function getForDbUrl({
|
||||
dbUrl,
|
||||
verbose = false,
|
||||
}: {
|
||||
dbUrl: string
|
||||
verbose?: boolean
|
||||
}): Promise<{
|
||||
client: pg.Client
|
||||
dbConnectionString: string
|
||||
}> {
|
||||
let client!: pg.Client
|
||||
|
||||
try {
|
||||
client = await postgresClient({
|
||||
connectionString: dbUrl,
|
||||
})
|
||||
} catch (e) {
|
||||
if (verbose) {
|
||||
logMessage({
|
||||
message: `The following error occured when connecting to the database: ${e}`,
|
||||
type: "verbose",
|
||||
})
|
||||
}
|
||||
logMessage({
|
||||
message: `Couldn't connect to PostgreSQL using the database URL you passed. Make sure it's correct and try again.`,
|
||||
type: "error",
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
client,
|
||||
dbConnectionString: dbUrl,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDbClientAndCredentials({
|
||||
dbName = "",
|
||||
dbUrl = "",
|
||||
verbose = false,
|
||||
}): Promise<{
|
||||
client: pg.Client
|
||||
dbConnectionString: string
|
||||
verbose?: boolean
|
||||
}> {
|
||||
if (dbName) {
|
||||
return await getForDbName({
|
||||
dbName,
|
||||
verbose,
|
||||
})
|
||||
} else {
|
||||
return await getForDbUrl({
|
||||
dbUrl,
|
||||
verbose,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { exec, spawnSync, SpawnSyncOptions } from "child_process"
|
||||
import util from "util"
|
||||
import { getAbortError } from "./create-abort-controller.js"
|
||||
|
||||
const promiseExec = util.promisify(exec)
|
||||
|
||||
type ExecuteOptions = {
|
||||
stdout?: string
|
||||
stderr?: string
|
||||
}
|
||||
|
||||
type VerboseOptions = {
|
||||
verbose?: boolean
|
||||
// Since spawn doesn't allow us to both retrieve the
|
||||
// output and output it live without using events,
|
||||
// enabling this option, which is only useful if `verbose` is `true`,
|
||||
// defers the output of the process until after the process is executed
|
||||
// instead of outputting the log in realtime, which is the default.
|
||||
// it prioritizes retrieving the output over outputting it in real-time.
|
||||
needOutput?: boolean
|
||||
}
|
||||
|
||||
type PromiseExecParams = Parameters<typeof promiseExec>
|
||||
type SpawnParams = [string, SpawnSyncOptions]
|
||||
|
||||
const execute = async (
|
||||
command: SpawnParams | PromiseExecParams,
|
||||
{ verbose = false, needOutput = false }: VerboseOptions
|
||||
): Promise<ExecuteOptions> => {
|
||||
if (verbose) {
|
||||
const [commandStr, options] = command as SpawnParams
|
||||
const childProcess = spawnSync(commandStr, {
|
||||
...options,
|
||||
shell: true,
|
||||
stdio: needOutput
|
||||
? "pipe"
|
||||
: [process.stdin, process.stdout, process.stderr],
|
||||
})
|
||||
|
||||
if (childProcess.error) {
|
||||
throw childProcess.error
|
||||
}
|
||||
|
||||
if (
|
||||
childProcess.signal &&
|
||||
["SIGINT", "SIGTERM"].includes(childProcess.signal)
|
||||
) {
|
||||
console.log("abortingggg")
|
||||
throw getAbortError()
|
||||
}
|
||||
|
||||
if (needOutput) {
|
||||
console.log(
|
||||
childProcess.stdout?.toString() || childProcess.stderr?.toString()
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
stdout: childProcess.stdout?.toString() || "",
|
||||
stderr: childProcess.stderr?.toString() || "",
|
||||
}
|
||||
} else {
|
||||
const childProcess = await promiseExec(...(command as PromiseExecParams))
|
||||
|
||||
return {
|
||||
stdout: childProcess.stdout as string,
|
||||
stderr: childProcess.stderr as string,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default execute
|
||||
@@ -0,0 +1,136 @@
|
||||
import boxen from "boxen"
|
||||
import chalk from "chalk"
|
||||
import { emojify } from "node-emoji"
|
||||
import { Ora } from "ora"
|
||||
import ProcessManager from "./process-manager.js"
|
||||
|
||||
export type FactBoxOptions = {
|
||||
interval: NodeJS.Timeout | null
|
||||
spinner: Ora
|
||||
processManager: ProcessManager
|
||||
message?: string
|
||||
title?: string
|
||||
verbose?: boolean
|
||||
}
|
||||
|
||||
const facts = [
|
||||
"Plugins allow you to integrate third-party services for payment, fulfillment, notifications, and more.",
|
||||
"You can specify a product's availability in one or more sales channels.",
|
||||
"Payment and shipping options and providers can be configured per region.",
|
||||
"Tax-inclusive pricing allows you to set prices for products, shipping options, and more without having to worry about calculating taxes.",
|
||||
"Medusa provides multi-currency and region support, with full control over prices for each currency and region.",
|
||||
"You can organize customers by customer groups and set special prices for them.",
|
||||
"You can specify the inventory of products per location and sales channel.",
|
||||
"Publishable-API Keys allow you to send requests to the backend within a scoped resource.",
|
||||
"You can create custom endpoints by creating a TypeScript file under the src/api directory.",
|
||||
"You can listen to events to perform asynchronous actions using Subscribers.",
|
||||
"An entity represents a table in the database. You can create a table by creating a custom entity and migration.",
|
||||
"Medusa's store endpoint paths are prefixed by /store. The admin endpoints are prefixed by /admin.",
|
||||
"Medusa provides a JavaScript client and a React library that you can use to build a storefront or a custom admin.",
|
||||
"Services are classes with methods related to an entity or functionality. You can create a custom service in a TypeScript file under src/services.",
|
||||
"Modules allow you to replace an entire functionality with your custom logic.",
|
||||
"The event bus module is responsible for triggering events and relaying them to subscribers.",
|
||||
"The cache module is responsible for caching data that requires heavy computation.",
|
||||
]
|
||||
|
||||
export const getFact = () => {
|
||||
const randIndex = Math.floor(Math.random() * facts.length)
|
||||
|
||||
return facts[randIndex]
|
||||
}
|
||||
|
||||
export const showFact = ({
|
||||
spinner,
|
||||
title,
|
||||
verbose,
|
||||
}: Pick<FactBoxOptions, "spinner" | "verbose"> & {
|
||||
title: string
|
||||
}) => {
|
||||
const fact = getFact()
|
||||
if (verbose) {
|
||||
spinner.stopAndPersist({
|
||||
symbol: chalk.cyan("⠋"),
|
||||
text: title,
|
||||
})
|
||||
} else {
|
||||
spinner.text = `${title}\n${boxen(`${fact}`, {
|
||||
title: chalk.cyan(`${emojify(":bulb:")} Medusa Tips`),
|
||||
titleAlignment: "center",
|
||||
textAlignment: "center",
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
})}`
|
||||
}
|
||||
}
|
||||
|
||||
export const createFactBox = ({
|
||||
spinner,
|
||||
title,
|
||||
processManager,
|
||||
verbose,
|
||||
}: Pick<FactBoxOptions, "spinner" | "processManager" | "verbose"> & {
|
||||
title: string
|
||||
}): NodeJS.Timeout => {
|
||||
showFact({ spinner, title, verbose })
|
||||
const interval = setInterval(() => {
|
||||
showFact({ spinner, title, verbose })
|
||||
}, 10000)
|
||||
|
||||
processManager.addInterval(interval)
|
||||
|
||||
return interval
|
||||
}
|
||||
|
||||
export const resetFactBox = ({
|
||||
interval,
|
||||
spinner,
|
||||
successMessage,
|
||||
processManager,
|
||||
newTitle,
|
||||
verbose,
|
||||
}: Pick<
|
||||
FactBoxOptions,
|
||||
"interval" | "spinner" | "processManager" | "verbose"
|
||||
> & {
|
||||
successMessage: string
|
||||
newTitle?: string
|
||||
}): NodeJS.Timeout | null => {
|
||||
if (interval) {
|
||||
clearInterval(interval)
|
||||
}
|
||||
|
||||
spinner.succeed(chalk.green(successMessage)).start()
|
||||
let newInterval = null
|
||||
if (newTitle) {
|
||||
newInterval = createFactBox({
|
||||
spinner,
|
||||
title: newTitle,
|
||||
processManager,
|
||||
verbose,
|
||||
})
|
||||
}
|
||||
|
||||
return newInterval
|
||||
}
|
||||
|
||||
export function displayFactBox({
|
||||
interval,
|
||||
spinner,
|
||||
processManager,
|
||||
title = "",
|
||||
message = "",
|
||||
verbose = false,
|
||||
}: FactBoxOptions): NodeJS.Timeout | null {
|
||||
if (!message) {
|
||||
return createFactBox({ spinner, title, processManager, verbose })
|
||||
}
|
||||
|
||||
return resetFactBox({
|
||||
interval,
|
||||
spinner,
|
||||
successMessage: message,
|
||||
processManager,
|
||||
newTitle: title,
|
||||
verbose,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
type ConnectionStringOptions = {
|
||||
user?: string
|
||||
password?: string
|
||||
host?: string
|
||||
db: string
|
||||
}
|
||||
|
||||
export function encodeDbValue(value: string): string {
|
||||
return encodeURIComponent(value)
|
||||
}
|
||||
|
||||
export default ({ user, password, host, db }: ConnectionStringOptions) => {
|
||||
let connection = `postgres://`
|
||||
if (user) {
|
||||
connection += encodeDbValue(user)
|
||||
}
|
||||
|
||||
if (password) {
|
||||
connection += `:${encodeDbValue(password)}`
|
||||
}
|
||||
|
||||
if (user || password) {
|
||||
connection += "@"
|
||||
}
|
||||
|
||||
connection += `${host}/${db}`
|
||||
|
||||
return connection
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import Configstore from "configstore"
|
||||
|
||||
let config: Configstore
|
||||
|
||||
export const getConfigStore = (): Configstore => {
|
||||
if (!config) {
|
||||
config = new Configstore(
|
||||
`medusa`,
|
||||
{},
|
||||
{
|
||||
globalConfigPath: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export const getCurrentOs = (): string => {
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
return "macos"
|
||||
case "linux":
|
||||
return "linux"
|
||||
default:
|
||||
return "windows"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import chalk from "chalk"
|
||||
import { program } from "commander"
|
||||
import { logger } from "./logger.js"
|
||||
|
||||
type LogOptions = {
|
||||
message: string
|
||||
type?: "error" | "success" | "info" | "warning" | "verbose"
|
||||
}
|
||||
|
||||
export default ({ message, type = "info" }: LogOptions) => {
|
||||
switch (type) {
|
||||
case "info":
|
||||
logger.info(chalk.white(message))
|
||||
break
|
||||
case "success":
|
||||
logger.info(chalk.green(message))
|
||||
break
|
||||
case "warning":
|
||||
logger.warning(chalk.yellow(message))
|
||||
break
|
||||
case "verbose":
|
||||
logger.info(`${chalk.bgYellowBright("VERBOSE LOG:")} ${message}`)
|
||||
break
|
||||
case "error":
|
||||
program.error(chalk.bold.red(message))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import winston from "winston"
|
||||
|
||||
const consoleTransport = new winston.transports.Console({
|
||||
format: winston.format.printf((log) => log.message),
|
||||
})
|
||||
const options = {
|
||||
transports: [consoleTransport],
|
||||
}
|
||||
|
||||
export const logger = winston.createLogger(options)
|
||||
@@ -0,0 +1,116 @@
|
||||
import inquirer from "inquirer"
|
||||
import { exec } from "child_process"
|
||||
import execute from "./execute.js"
|
||||
import { FactBoxOptions, displayFactBox } from "./facts.js"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import { customAlphabet } from "nanoid"
|
||||
import { isAbortError } from "./create-abort-controller.js"
|
||||
import logMessage from "./log-message.js"
|
||||
|
||||
const NEXTJS_REPO = "https://github.com/medusajs/nextjs-starter-medusa"
|
||||
|
||||
export async function askForNextjsStarter(): Promise<boolean> {
|
||||
const { installNextjs } = await inquirer.prompt([
|
||||
{
|
||||
type: "confirm",
|
||||
name: "installNextjs",
|
||||
message: `Would you like to create the Next.js storefront? You can also create it later`,
|
||||
default: false,
|
||||
},
|
||||
])
|
||||
|
||||
return installNextjs
|
||||
}
|
||||
|
||||
type InstallOptions = {
|
||||
directoryName: string
|
||||
abortController?: AbortController
|
||||
factBoxOptions: FactBoxOptions
|
||||
verbose?: boolean
|
||||
}
|
||||
|
||||
export async function installNextjsStarter({
|
||||
directoryName,
|
||||
abortController,
|
||||
factBoxOptions,
|
||||
verbose = false,
|
||||
}: InstallOptions): Promise<string> {
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Installing Next.js Storefront...",
|
||||
})
|
||||
|
||||
let nextjsDirectory = `${directoryName}-storefront`
|
||||
|
||||
if (
|
||||
fs.existsSync(nextjsDirectory) &&
|
||||
fs.lstatSync(nextjsDirectory).isDirectory()
|
||||
) {
|
||||
// append a random number to the directory name
|
||||
nextjsDirectory += `-${customAlphabet(
|
||||
// npm throws an error if the directory name has an uppercase letter
|
||||
"123456789abcdefghijklmnopqrstuvwxyz",
|
||||
4
|
||||
)()}`
|
||||
}
|
||||
|
||||
try {
|
||||
await execute(
|
||||
[
|
||||
`npx create-next-app -e ${NEXTJS_REPO} ${nextjsDirectory}`,
|
||||
{
|
||||
signal: abortController?.signal,
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_yes: "yes",
|
||||
},
|
||||
},
|
||||
],
|
||||
{ verbose }
|
||||
)
|
||||
} catch (e) {
|
||||
if (isAbortError(e)) {
|
||||
process.exit()
|
||||
}
|
||||
|
||||
logMessage({
|
||||
message: `An error occurred while installing Next.js storefront: ${e}`,
|
||||
type: "error",
|
||||
})
|
||||
}
|
||||
|
||||
fs.renameSync(
|
||||
path.join(nextjsDirectory, ".env.template"),
|
||||
path.join(nextjsDirectory, ".env.local")
|
||||
)
|
||||
|
||||
displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: `Installed Next.js Starter successfully in the ${nextjsDirectory} directory.`,
|
||||
})
|
||||
|
||||
return nextjsDirectory
|
||||
}
|
||||
|
||||
type StartOptions = {
|
||||
directory: string
|
||||
abortController?: AbortController
|
||||
verbose?: boolean
|
||||
}
|
||||
|
||||
export function startNextjsStarter({
|
||||
directory,
|
||||
abortController,
|
||||
verbose = false,
|
||||
}: StartOptions) {
|
||||
const childProcess = exec(`npm run dev`, {
|
||||
cwd: directory,
|
||||
signal: abortController?.signal,
|
||||
})
|
||||
|
||||
if (verbose) {
|
||||
childProcess.stdout?.pipe(process.stdout)
|
||||
childProcess.stderr?.pipe(process.stderr)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import pg from "pg"
|
||||
const { Client } = pg
|
||||
|
||||
type PostgresConnection = {
|
||||
user?: string
|
||||
password?: string
|
||||
connectionString?: string
|
||||
database?: string
|
||||
}
|
||||
|
||||
export default async (connect: PostgresConnection) => {
|
||||
const client = new Client(connect)
|
||||
|
||||
await client.connect()
|
||||
|
||||
return client
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
import chalk from "chalk"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import { Ora } from "ora"
|
||||
import execute from "./execute.js"
|
||||
import { EOL } from "os"
|
||||
import { displayFactBox, FactBoxOptions } from "./facts.js"
|
||||
import ProcessManager from "./process-manager.js"
|
||||
import { clearProject } from "./clear-project.js"
|
||||
import type { Client } from "pg"
|
||||
|
||||
type PrepareOptions = {
|
||||
directory: string
|
||||
dbConnectionString: string
|
||||
admin?: {
|
||||
email: string
|
||||
}
|
||||
seed?: boolean
|
||||
boilerplate?: boolean
|
||||
spinner: Ora
|
||||
processManager: ProcessManager
|
||||
abortController?: AbortController
|
||||
skipDb?: boolean
|
||||
migrations?: boolean
|
||||
onboardingType?: "default" | "nextjs"
|
||||
nextjsDirectory?: string
|
||||
client: Client | null
|
||||
verbose?: boolean
|
||||
v2?: boolean
|
||||
}
|
||||
|
||||
export default async ({
|
||||
directory,
|
||||
dbConnectionString,
|
||||
admin,
|
||||
seed,
|
||||
boilerplate,
|
||||
spinner,
|
||||
processManager,
|
||||
abortController,
|
||||
skipDb,
|
||||
migrations,
|
||||
onboardingType = "default",
|
||||
nextjsDirectory = "",
|
||||
client,
|
||||
verbose = false,
|
||||
v2 = false,
|
||||
}: PrepareOptions) => {
|
||||
// initialize execution options
|
||||
const execOptions = {
|
||||
cwd: directory,
|
||||
signal: abortController?.signal,
|
||||
}
|
||||
|
||||
const npxOptions = {
|
||||
...execOptions,
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_yes: "yes",
|
||||
},
|
||||
}
|
||||
|
||||
const factBoxOptions: FactBoxOptions = {
|
||||
interval: null,
|
||||
spinner,
|
||||
processManager,
|
||||
message: "",
|
||||
title: "",
|
||||
verbose,
|
||||
}
|
||||
|
||||
// initialize the invite token to return
|
||||
let inviteToken: string | undefined = undefined
|
||||
|
||||
if (!skipDb) {
|
||||
let env = `DATABASE_TYPE=postgres${EOL}DATABASE_URL=${dbConnectionString}${EOL}MEDUSA_ADMIN_ONBOARDING_TYPE=${onboardingType}${EOL}STORE_CORS=http://localhost:8000,http://localhost:7001`
|
||||
if (v2) {
|
||||
env += `${EOL}POSTGRES_URL=${dbConnectionString}`
|
||||
}
|
||||
if (nextjsDirectory) {
|
||||
env += `${EOL}MEDUSA_ADMIN_ONBOARDING_NEXTJS_DIRECTORY=${nextjsDirectory}`
|
||||
}
|
||||
// add connection string to project
|
||||
fs.appendFileSync(path.join(directory, `.env`), env)
|
||||
}
|
||||
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
spinner,
|
||||
title: "Installing dependencies...",
|
||||
processManager,
|
||||
})
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
try {
|
||||
await execute([`yarn`, execOptions], { verbose })
|
||||
} catch (e) {
|
||||
// yarn isn't available
|
||||
// use npm
|
||||
await execute([`npm install --legacy-peer-deps`, execOptions], {
|
||||
verbose,
|
||||
})
|
||||
}
|
||||
},
|
||||
ignoreERESOLVE: true,
|
||||
})
|
||||
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Installed Dependencies",
|
||||
})
|
||||
|
||||
if (!boilerplate) {
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Preparing Project Directory...",
|
||||
})
|
||||
// delete files and directories related to onboarding
|
||||
clearProject(directory)
|
||||
displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Prepared Project Directory",
|
||||
})
|
||||
}
|
||||
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Building Project...",
|
||||
})
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
try {
|
||||
await execute([`yarn build`, execOptions], { verbose })
|
||||
} catch (e) {
|
||||
// yarn isn't available
|
||||
// use npm
|
||||
await execute([`npm run build`, execOptions], { verbose })
|
||||
}
|
||||
},
|
||||
ignoreERESOLVE: true,
|
||||
})
|
||||
|
||||
displayFactBox({ ...factBoxOptions, message: "Project Built" })
|
||||
|
||||
if (!skipDb && migrations) {
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Running Migrations...",
|
||||
})
|
||||
|
||||
// run migrations
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
const proc = await execute(
|
||||
["npx @medusajs/medusa-cli@latest migrations run", npxOptions],
|
||||
{ verbose, needOutput: true }
|
||||
)
|
||||
|
||||
if (client) {
|
||||
// check the migrations table is in the database
|
||||
// to ensure that migrations ran
|
||||
let errorOccurred = false
|
||||
try {
|
||||
const migrations = await client.query(
|
||||
`SELECT * FROM "${v2 ? "mikro_orm_migrations" : "migrations"}"`
|
||||
)
|
||||
errorOccurred = migrations.rowCount == 0
|
||||
} catch (e) {
|
||||
// avoid error thrown if the migrations table
|
||||
// doesn't exist
|
||||
errorOccurred = true
|
||||
}
|
||||
|
||||
// ensure that migrations actually ran in case of an uncaught error
|
||||
if (errorOccurred && (proc.stderr || proc.stdout)) {
|
||||
throw new Error(
|
||||
`An error occurred while running migrations: ${
|
||||
proc.stderr || proc.stdout
|
||||
}`
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Ran Migrations",
|
||||
})
|
||||
}
|
||||
|
||||
if (admin && !skipDb && migrations && !v2) {
|
||||
// create admin user
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Creating an admin user...",
|
||||
})
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
const proc = await execute(
|
||||
[
|
||||
`npx @medusajs/medusa-cli@latest user -e ${admin.email} --invite`,
|
||||
npxOptions,
|
||||
],
|
||||
{ verbose, needOutput: true }
|
||||
)
|
||||
|
||||
// get invite token from stdout
|
||||
const match = (proc.stdout as string).match(
|
||||
/Invite token: (?<token>.+)/
|
||||
)
|
||||
inviteToken = match?.groups?.token
|
||||
},
|
||||
})
|
||||
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Created admin user",
|
||||
})
|
||||
}
|
||||
|
||||
if (!skipDb && migrations) {
|
||||
if (seed || !boilerplate) {
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Seeding database...",
|
||||
})
|
||||
|
||||
// check if a seed file exists in the project
|
||||
if (!fs.existsSync(path.join(directory, "data", "seed.json"))) {
|
||||
spinner
|
||||
?.warn(
|
||||
chalk.yellow(
|
||||
"Seed file was not found in the project. Skipping seeding..."
|
||||
)
|
||||
)
|
||||
.start()
|
||||
return inviteToken
|
||||
}
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
await execute(
|
||||
[
|
||||
`npx @medusajs/medusa-cli@latest seed --seed-file=${path.join(
|
||||
"data",
|
||||
"seed.json"
|
||||
)}`,
|
||||
npxOptions,
|
||||
],
|
||||
{ verbose }
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Seeded database with demo data",
|
||||
})
|
||||
} else if (
|
||||
fs.existsSync(path.join(directory, "data", "seed-onboarding.json"))
|
||||
) {
|
||||
// seed the database with onboarding seed
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Finish preparation...",
|
||||
})
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
await execute(
|
||||
[
|
||||
`npx @medusajs/medusa-cli@latest seed --seed-file=${path.join(
|
||||
"data",
|
||||
"seed-onboarding.json"
|
||||
)}`,
|
||||
npxOptions,
|
||||
],
|
||||
{ verbose }
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
displayFactBox({ ...factBoxOptions, message: "Finished Preparation" })
|
||||
}
|
||||
|
||||
return inviteToken
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
type ProcessOptions = {
|
||||
process: Function
|
||||
ignoreERESOLVE?: boolean
|
||||
}
|
||||
|
||||
export default class ProcessManager {
|
||||
intervals: NodeJS.Timeout[] = []
|
||||
static MAX_RETRIES = 3
|
||||
|
||||
constructor() {
|
||||
this.onTerminated(() => {
|
||||
this.intervals.forEach((interval) => {
|
||||
clearInterval(interval)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onTerminated(fn: () => Promise<void> | void) {
|
||||
process.on("SIGTERM", () => fn())
|
||||
process.on("SIGINT", () => fn())
|
||||
}
|
||||
|
||||
addInterval(interval: NodeJS.Timeout) {
|
||||
this.intervals.push(interval)
|
||||
}
|
||||
|
||||
// when running commands with npx or npm sometimes they
|
||||
// terminate with EAGAIN error unexpectedly
|
||||
// this utility function allows retrying the process if
|
||||
// EAGAIN occurs, or otherwise throw the error that occurs
|
||||
async runProcess({ process, ignoreERESOLVE }: ProcessOptions) {
|
||||
let processError = false
|
||||
let retries = 0
|
||||
do {
|
||||
++retries
|
||||
try {
|
||||
await process()
|
||||
} catch (error) {
|
||||
if (
|
||||
typeof error === "object" &&
|
||||
error !== null &&
|
||||
"code" in error &&
|
||||
error?.code === "EAGAIN"
|
||||
) {
|
||||
processError = true
|
||||
} else if (
|
||||
ignoreERESOLVE &&
|
||||
typeof error === "object" &&
|
||||
error !== null &&
|
||||
"code" in error &&
|
||||
error?.code === "ERESOLVE"
|
||||
) {
|
||||
// ignore error
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
} while (processError && retries <= ProcessManager.MAX_RETRIES)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { exec } from "child_process"
|
||||
|
||||
type StartOptions = {
|
||||
directory: string
|
||||
abortController?: AbortController
|
||||
}
|
||||
|
||||
export default ({ directory, abortController }: StartOptions) => {
|
||||
const childProcess = exec(`npx @medusajs/medusa-cli@latest develop`, {
|
||||
cwd: directory,
|
||||
signal: abortController?.signal,
|
||||
env: {
|
||||
...process.env,
|
||||
OPEN_BROWSER: "false",
|
||||
npm_config_yes: "yes",
|
||||
},
|
||||
})
|
||||
|
||||
childProcess.stdout?.pipe(process.stdout)
|
||||
childProcess.stderr?.pipe(process.stderr)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "Node16",
|
||||
"moduleResolution": "node16",
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
},
|
||||
"include": ["src"],
|
||||
"ts-node": {
|
||||
"esm": true,
|
||||
"experimentalSpecifierResolution": "node",
|
||||
"transpileOnly": true
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user