nitro-web 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.eslintrc.json +2 -5
  2. package/_example/{.env-example → .env} +3 -3
  3. package/_example/client/config.ts +1 -5
  4. package/_example/client/index.ts +1 -18
  5. package/_example/components/auth.api.js +1 -1
  6. package/_example/components/index.tsx +11 -3
  7. package/_example/components/settings.api.js +1 -1
  8. package/_example/package.json +109 -0
  9. package/_example/server/config.js +3 -16
  10. package/_example/server/index.js +4 -3
  11. package/_example/tailwind.config.js +12 -21
  12. package/_example/tsconfig.json +2 -2
  13. package/_example/types/index.d.ts +13 -0
  14. package/_example/types/twin.d.ts +19 -0
  15. package/_example/webpack.config.js +2 -3
  16. package/client/index.js +42 -0
  17. package/components/auth/signin.jsx +9 -1
  18. package/components/partials/element/modal.jsx +1 -1
  19. package/components/partials/form/drop-handler.jsx +1 -1
  20. package/components/partials/form/input-currency.jsx +5 -2
  21. package/components/partials/form/input-date.jsx +6 -6
  22. package/components/partials/form/input.jsx +9 -9
  23. package/components/partials/layout/layout2.jsx +1 -1
  24. package/components/partials/styleguide.jsx +6 -7
  25. package/components/settings/settings-account.jsx +1 -1
  26. package/components/settings/settings-business.jsx +1 -4
  27. package/components/settings/settings-team--member.jsx +1 -1
  28. package/package.json +28 -97
  29. package/readme.md +39 -9
  30. package/server/email/index.js +12 -10
  31. package/server/index.js +23 -0
  32. package/server/router.js +4 -8
  33. package/util.js +13 -0
  34. package/webpack.config.js +43 -42
  35. package/_example/types.d.ts +0 -7
  36. package/client.js +0 -42
  37. package/server.js +0 -20
@@ -17,7 +17,7 @@ export function Layout2({ Logo }) {
17
17
  <Outlet />
18
18
  </div>
19
19
 
20
- <div class="wrapper-2 w-full px-5 pb-4 flex items-center w-full text-sm text-[#1F1F1F]">
20
+ <div class="wrapper-2 w-full px-5 pb-4 flex items-center text-sm text-[#1F1F1F]">
21
21
  <ul class="flex-1 flex gap-4 list-style-none">
22
22
  <li><Link class="underline1" to="/">Home</Link></li>
23
23
  <li><Link class="underline1" to="/signin">About</Link></li>
@@ -1,4 +1,3 @@
1
- /*eslint-disable*/
2
1
  import { getCountryOptions, getCurrencyOptions, ucFirst } from '../../util.js'
3
2
  import { CheckIcon } from '@heroicons/react/20/solid'
4
3
  import { Input } from './form/input.jsx'
@@ -14,14 +13,13 @@ export function Styleguide({ config }) {
14
13
  address: '',
15
14
  country: 'us',
16
15
  currency: 'nzd', // can be commented too
17
- // amount: 100,
18
- // brandColor: '#F3CA5F',
16
+ amount: 100,
17
+ brandColor: '#F3CA5F',
19
18
  firstName: 'Tony',
20
19
  date: Date.now(),
21
20
  errors: [
22
21
  { title: 'address', detail: 'Address is required' },
23
22
  { title: 'currency', detail: 'Currency is required' },
24
- // { title: 'brandColor', detail: 'Color is required' },
25
23
  ],
26
24
  })
27
25
 
@@ -75,7 +73,8 @@ export function Styleguide({ config }) {
75
73
  </div>
76
74
  <div>
77
75
  <label for="input1">Label</label>
78
- <Checkbox name="input1" type="radio" text="Radio 1" subtext="some additional text here 1." id="input1-1" class="!mb-0" defaultChecked />
76
+ <Checkbox name="input1" type="radio" text="Radio 1" subtext="some additional text here 1." id="input1-1" class="!mb-0"
77
+ defaultChecked />
79
78
  <Checkbox name="input1" type="radio" text="Radio 2" subtext="some additional text here 2." id="input1-2" class="!mt-0" />
80
79
  </div>
81
80
  <div>
@@ -118,8 +117,8 @@ export function Styleguide({ config }) {
118
117
  <div><Button color="primary-md">*-md (default)</Button></div>
119
118
  <div><Button color="primary-lg">*-lg button</Button></div>
120
119
  <div><Button IconLeft={<CheckIcon class="size-5 -my-5 -mx-0.5" />}>IconLeft=Element</Button></div>
121
- <div><Button IconRight="v">IconRight="v"</Button></div>
122
- <div><Button IconRight2="v" className="w-[200px]">IconRight2="v"</Button></div>
120
+ <div><Button IconRight="v">IconRight=&quot;v&quot;</Button></div>
121
+ <div><Button IconRight2="v" className="w-[200px]">IconRight2=&quot;v&quot;</Button></div>
123
122
  <div><Button color="primary" isLoading>primary isLoading</Button></div>
124
123
  </div>
125
124
 
@@ -130,7 +130,7 @@ export function RemoveModal ({ show, setShow }) {
130
130
  )
131
131
  }
132
132
 
133
- import { css } from '@emotion/react'
133
+ import { css } from 'twin.macro'
134
134
  const style = (_theme) => css`
135
135
  /* input[type='file'] {
136
136
  padding: 8px 18px;
@@ -36,7 +36,7 @@ export function SettingsBusiness({ config }) {
36
36
  }
37
37
 
38
38
  return (
39
- <div css={style}>
39
+ <div>
40
40
  <Topbar
41
41
  title={<>Settings</>}
42
42
  submenu={
@@ -116,6 +116,3 @@ export function SettingsBusiness({ config }) {
116
116
  )
117
117
  }
118
118
 
119
- import { css } from '@emotion/react'
120
- const style = (_theme) => css`
121
- `
@@ -1,5 +1,5 @@
1
1
  // todo: finish tailwind conversion
2
- import { css } from '@emotion/react'
2
+ import { css } from 'twin.macro'
3
3
  import { Button } from '../partials/element/button.jsx'
4
4
  import { Modal } from '../partials/element/modal.jsx'
5
5
  import { FormError } from '../partials/form/form-error.jsx'
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "nitro-web",
3
3
  "description": "Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀",
4
4
  "repository": "github:boycce/nitro-web",
5
- "version": "0.0.1",
5
+ "version": "0.0.3",
6
6
  "main": "client.js",
7
7
  "type": "module",
8
8
  "keywords": [
@@ -14,41 +14,28 @@
14
14
  "tailwind",
15
15
  "webpack"
16
16
  ],
17
- "imports": {
18
- "#nitro-web/*": { "default": "./*" }
19
- },
20
- "babelMacros": {
21
- "twin": {
22
- "preset": "emotion",
23
- "config": "./_example/tailwind.config.js"
24
- }
17
+ "workspaces": [
18
+ "_example"
19
+ ],
20
+ "exports": {
21
+ ".": "./client/index.js",
22
+ "./server": "./server/index.js",
23
+ "./util": "./util.js",
24
+ "./.eslintrc.json": "./.eslintrc.json",
25
+ "./webpack.config.js": "./webpack.config.js"
25
26
  },
26
27
  "scripts": {
27
- "build": "NODE_ENV=production webpack --target=web",
28
- "dev": "clear && npm run dev:server:lint --silent & npm run dev:server --silent & npm run dev:client --silent",
29
- "dev:client": "NITRO=true webpack serve --progress --config ./_example/webpack.config.js",
30
- "dev:server": "NITRO=true nodemon ./_example/server -q -w ./_example/server/ -w ./_example/components/ -w ./server/ -w ./components/ -e js",
31
- "dev:server:lint": "eslint ./_example/components ./_example/server ./components/ ./server/",
28
+ "dev": "npm run dev -w example",
32
29
  "major": "standard-version --release-as major && npm publish",
33
30
  "minor": "standard-version --release-as minor && npm publish",
34
- "patch": "standard-version --release-as patch && npm publish",
35
- "start": "node ./_example/server",
36
- "stripe": "stripe listen --forward-to localhost:3001/api/stripe/webhook"
31
+ "patch": "standard-version --release-as patch && npm publish"
37
32
  },
38
33
  "dependencies": {
39
- "@emotion/react": "^11.11.4",
40
- "@headlessui/react": "^2.2.0",
41
- "@heroicons/react": "^2.2.0",
42
- "@hokify/axios": "^0.19.1",
43
- "@stripe/react-stripe-js": "^1.9.0",
44
- "@stripe/stripe-js": "^1.34.0",
45
34
  "@uiw/color-convert": "^2.3.0",
46
35
  "@uiw/react-color-hue": "^2.3.0",
47
36
  "@uiw/react-color-saturation": "^2.3.0",
48
- "axios-retry": "^3.3.1",
49
37
  "bcrypt": "^5.0.0",
50
38
  "body-parser": "^1.19.0",
51
- "chart.js": "^4.4.3",
52
39
  "compression": "^1.7.4",
53
40
  "connect-mongo": "^5.1.0",
54
41
  "date-fns": "^3.6.0",
@@ -58,98 +45,51 @@
58
45
  "express-fileupload": "^1.1.6",
59
46
  "express-session": "^1.17.0",
60
47
  "inline-css": "^4.0.2",
61
- "monastery": "~3.5.1",
62
48
  "nanoid": "^4.0.0",
63
49
  "nodemailer": "^6.5.0",
64
50
  "nodemailer-mailgun-transport": "^2.0.2",
65
51
  "nunjucks": "^3.2.2",
66
52
  "passport": "^0.4.1",
67
- "passport-jwt": "^4.0.0",
68
53
  "passport-local": "^1.0.0",
69
- "pdf-to-img": "^4.1.0",
70
- "pdfmake": "0.2.7",
71
- "react": "^18.3.1",
72
- "react-chartjs-2": "^5.2.0",
73
54
  "react-currency-input-field": "^3.8.0",
74
55
  "react-day-picker": "^8.10.1",
75
- "react-dom": "^18.3.1",
76
56
  "react-number-format": "^5.4.0",
77
57
  "react-router-dom": "6.24.1",
78
58
  "react-select": "^5.9.0",
79
59
  "react-tracked": "^1.3.0",
80
- "sort-route-addresses-nodeps": "0.0.4",
81
- "stripe": "^9.16.0"
60
+ "sort-route-addresses-nodeps": "0.0.4"
61
+ },
62
+ "peerDependencies": {
63
+ "@headlessui/react": "^2.2.0",
64
+ "@heroicons/react": "^2.2.0",
65
+ "@hokify/axios": "^0.19.1",
66
+ "@stripe/stripe-js": "^1.34.0",
67
+ "axios-retry": "^3.3.1",
68
+ "monastery": "^3.5.2",
69
+ "react": "^18.3.1",
70
+ "react-dom": "^18.3.1",
71
+ "stripe": "^9.16.0",
72
+ "twin.macro": "^3.4.1"
82
73
  },
83
74
  "devDependencies": {
84
- "@babel/core": "^7.8.0",
85
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
86
- "@babel/plugin-transform-runtime": "^7.17.0",
87
- "@babel/preset-env": "^7.8.0",
88
- "@babel/preset-react": "^7.9.4",
89
- "@babel/preset-typescript": "^7.24.7",
90
- "@emotion/babel-plugin": "^11.11.0",
91
75
  "@emotion/eslint-plugin": "^11.11.0",
92
- "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
93
- "@svgr/webpack": "^8.1.0",
76
+ "@emotion/react": "^11.11.4",
77
+ "@emotion/styled": "^11.11.4",
94
78
  "@types/react": "^19.0.2",
95
79
  "@types/react-dom": "^19.0.2",
96
80
  "@typescript-eslint/eslint-plugin": "^8.18.1",
97
81
  "@typescript-eslint/parser": "^8.18.1",
98
- "autoprefixer": "^9.8.8",
99
82
  "babel-eslint": "^10.0.3",
100
- "babel-loader": "^8.0.6",
101
- "babel-plugin-macros": "^3.1.0",
102
- "babel-plugin-react-html-attrs": "^2.1.0",
103
- "clean-terminal-webpack-plugin": "https://github.com/boycce/clean-terminal-webpack-plugin.git",
104
- "color": "^4.2.3",
105
- "copy-webpack-plugin": "^12.0.2",
106
- "core-js": "^3.12.1",
107
- "css-loader": "^3.6.0",
108
- "csv-loader": "^3.0.5",
109
83
  "eslint": "^8.57.1",
110
84
  "eslint-plugin-import": "^2.26.0",
111
- "eslint-plugin-jest": "^28.9.0",
112
85
  "eslint-plugin-react": "^7.19.0",
113
86
  "eslint-plugin-react-hooks": "^4.0.0",
114
87
  "eslint-webpack-plugin": "^2.7.0",
115
- "html-loader": "^1.3.2",
116
- "html-webpack-plugin": "^5.6.0",
117
- "jest": "^29.7.0",
118
- "migrate-mongo": "^10.0.0",
119
- "mini-css-extract-plugin": "^2.9.0",
120
- "nodemon": "^2.0.1",
121
- "postcss": "^8.4.49",
122
- "postcss-for": "^2.1.1",
123
- "postcss-import": "^16.1.0",
124
- "postcss-import-resolver": "^2.0.0",
125
- "postcss-loader": "^8.1.1",
126
- "postcss-nested": "^7.0.2",
127
- "react-refresh": "^0.14.2",
128
- "standard-version": "^9.3.2",
129
- "string-replace-loader": "^3.1.0",
130
- "super-nunjucks-loader": "^2.0.0",
131
- "tailwind-merge": "^2.6.0",
132
- "tailwindcss": "^3.4.17",
133
- "twin.macro": "^3.4.1",
134
- "typescript": "^5.5.2",
135
- "webpack": "^5.92.1",
136
- "webpack-cli": "^5.1.4",
137
- "webpack-dev-server": "^4.6.0",
138
- "webpack-node-externals": "^1.7.2"
88
+ "standard-version": "^9.3.2"
139
89
  },
140
90
  "engines": {
141
91
  "node": "^18"
142
92
  },
143
- "browserslist": [
144
- "> 1%",
145
- "last 2 versions",
146
- "not ie <= 10"
147
- ],
148
- "nodemonConfig": {
149
- "events": {
150
- "restart": "echo \"\\033[0;35mExpress restarting..\\033[0m\""
151
- }
152
- },
153
93
  "standard-version": {
154
94
  "releaseCommitMessageFormat": "{{currentTag}}",
155
95
  "sign": true,
@@ -158,15 +98,6 @@
158
98
  "tag": true
159
99
  }
160
100
  },
161
- "prettier": {
162
- "printWidth": 140,
163
- "quoteProps": "consistent",
164
- "semi": false,
165
- "singleQuote": true,
166
- "tabWidth": 2,
167
- "trailingComma": "es5",
168
- "useTabs": false
169
- },
170
101
  "jest": {
171
102
  "transform": {}
172
103
  },
package/readme.md CHANGED
@@ -1,22 +1,43 @@
1
1
  # Nitro
2
2
 
3
- [![NPM](https://img.shields.io/npm/v/nitro-web.svg)](https://www.npmjs.com/package/nitro-web) [![Build Status](https://travis-ci.com/boycce/nitro-web.svg?branch=master)](https://app.travis-ci.com/github/boycce/nitro-web)
3
+ [![NPM](https://img.shields.io/npm/v/nitro-web.svg)](https://www.npmjs.com/package/nitro-web)
4
4
 
5
5
  Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀
6
6
 
7
- ### Install
8
-
9
7
  ```bash
10
8
  npm i nitro-web
11
9
  ```
12
10
 
13
- ### Setup
11
+ ### Install
14
12
 
15
- 1. Copy the `./_example` folder to your project
16
- 2. Copy over `./package.json`
17
- 4. In `./package.json`, search and replace `/_example` with `./`
18
- 5. In `./package.json`, replace `{ "default": "./*" }` with `{ "default": "nitro-web/*" }`
19
- 6. `npm i`
13
+ 1. Copy ./_example into your project
14
+ 2. In package.json, replace `"nitro-web": "file:.."` with `"nitro-web": "^0.0.1"`
15
+ 3. In package.json, replace `"../.eslintrc.json"` with `"./node_modules/nitro-web/.eslintrc.json"`
16
+ 4. In tsconfig.json, remove
17
+ ```json
18
+ "paths": {
19
+ "nitro-web": [
20
+ "../client/index.js",
21
+ ],
22
+ },
23
+ ```
24
+ 5. Run `npm i`
25
+
26
+ ### Usage
27
+
28
+ On the client, you can import components and page-components as you would normally. See ./example for further info.
29
+
30
+ ```javascript
31
+ import { SigninPage, Toggle, util } from 'nitro-web'
32
+ ```
33
+
34
+ On the server, you can import the express router, default models, and controllers. See ./example for further info.
35
+
36
+ ```javascript
37
+ import { setupRouter, util } from 'nitro-web/server'
38
+ const server = await setupRouter(config)
39
+ server.listen(3001, '0.0.0.0')
40
+ ```
20
41
 
21
42
  ### Running in development
22
43
 
@@ -41,3 +62,12 @@ npm run start
41
62
  - React `^18.3`
42
63
  - Tailwind `^3.4`
43
64
  - Webpack `^5.92`
65
+
66
+ ### Common packages
67
+
68
+ - `pdf-to-img`
69
+ - `pdfmake`
70
+ - `react-chartjs-2`
71
+ - `jest: ^29.7.0`
72
+ - `migrate-mongo: ^10.0.0`
73
+ - `eslint-plugin-jest: ^28.9.0`
@@ -5,12 +5,13 @@ import nunjucks from 'nunjucks'
5
5
  import inlineCss from 'inline-css'
6
6
  import { dirname, join } from 'path'
7
7
  import { fileURLToPath } from 'url'
8
+ import path from 'path'
9
+ import { getDirectories } from '../../util.js'
8
10
 
9
11
  let templates = {}
10
12
  let nodemailerMailgun = undefined
11
13
  const _dirname = dirname(fileURLToPath(import.meta.url)) + '/'
12
14
 
13
-
14
15
  export async function sendEmail({ template, to, bcc, data={}, from, replyTo, recipientVariables, subject, test, skipCssInline, config }) {
15
16
  /**
16
17
  * Email recipient a predefined template with data and/or recipientVariables
@@ -72,7 +73,7 @@ export async function sendEmail({ template, to, bcc, data={}, from, replyTo, rec
72
73
  let settings = {
73
74
  bcc: bcc,
74
75
  from: from,
75
- isDev: config.clientUrl.match(/:/),
76
+ isDev: config.clientUrl.match(/:/), // possibly use config.env here
76
77
  recipientVariables: recipientVariables,
77
78
  replyTo: replyTo,
78
79
  skipCssInline: skipCssInline,
@@ -81,34 +82,35 @@ export async function sendEmail({ template, to, bcc, data={}, from, replyTo, rec
81
82
  test: config.emailTestMode || test,
82
83
  to: to,
83
84
  url: config.clientUrl,
85
+ emailTemplateDir: getDirectories(path, config.pwd).emailTemplateDir,
84
86
  }
85
87
 
86
88
  // Grab html and send
87
- let html = template.match('<') ? template : await getTemplate(settings, config)
89
+ let html = template.match('<') ? template : await getTemplate(settings)
88
90
  if (!html) throw new Error('Sendmail: No template returned from getTemplate(..)')
89
91
  return await sendWithMailgun(settings, html) // note, mailgun errors are resolved
90
92
  }
91
93
 
92
- async function getTemplate(settings, config) {
94
+ async function getTemplate(settings) {
93
95
  try {
94
96
  var templateName = settings.template
95
97
  if (!templates[templateName] || settings.isDev) {
96
- nunjucks.configure({ noCache: config.env === 'development' })
98
+ nunjucks.configure({ noCache: settings.isDev })
97
99
  // Setup the nunjucks environment
98
- let env = new nunjucks.Environment([
99
- new nunjucks.FileSystemLoader(`${config.emailTemplateDir}`), // user templates take precedence
100
+ let nunjucksEnv = new nunjucks.Environment([
101
+ new nunjucks.FileSystemLoader(`${settings.emailTemplateDir}`), // user templates take precedence
100
102
  new nunjucks.FileSystemLoader(`${_dirname}`), // then fallback to nitro default templates
101
103
  ])
102
104
  // Get the template
103
- let template = env.getTemplate(templateName + '.html', true)
105
+ let template = nunjucksEnv.getTemplate(templateName + '.html', true)
104
106
  // Render the template
105
107
  let html = template.render({})
106
108
  if (settings.skipCssInline && settings.test) {
107
109
  templates[templateName] = html
108
110
  } else {
109
111
  try {
110
- // First try to inline the CSS from the user templates directory (config.emailTemplateDir)
111
- templates[templateName] = await inlineCssForPath(html, config.emailTemplateDir)
112
+ // First try to inline the CSS from the user templates directory
113
+ templates[templateName] = await inlineCssForPath(html, settings.emailTemplateDir)
112
114
  } catch (e) {
113
115
  // If the CSS is not found, use default nitro CSS file
114
116
  if (templateName == 'reset-password' || templateName == 'welcome') {
@@ -0,0 +1,23 @@
1
+ export * from '../util.js'
2
+ export * as util from '../util.js'
3
+
4
+ // Export models
5
+ import userModel from './models/user.js'
6
+ import companyModel from './models/company.js'
7
+ async function setupDefaultModels(db) {
8
+ // Load default nitro models, if they don't exist already
9
+ if (!db.models.user) await db.model('user', userModel)
10
+ if (!db.models.company) await db.model('company', companyModel)
11
+ }
12
+ export { userModel, companyModel, setupDefaultModels }
13
+
14
+ // Export router
15
+ export { setupRouter } from './router.js'
16
+
17
+ // Export email utility
18
+ export { sendEmail } from './email/index.js'
19
+
20
+ // Export api default controllers
21
+ export { default as auth } from '../components/auth/auth.api.js'
22
+ export { default as settings } from '../components/settings/settings.api.js'
23
+ export { default as stripe } from '../components/billing/stripe.api.js'
package/server/router.js CHANGED
@@ -14,19 +14,16 @@ import * as util from '../util.js'
14
14
  const _dirname = dirname(fileURLToPath(import.meta.url)) + '/'
15
15
 
16
16
  export async function setupRouter (config) {
17
- const { componentsDir, distDir, emailTemplateDir, env, middleware, version } = config
17
+ const { env, middleware, version } = config
18
+ const { componentsDir, distDir, emailTemplateDir } = util.getDirectories(path, config.pwd)
18
19
  const expressApp = express()
19
20
  const server = http.createServer(expressApp)
20
21
  const apiRoutes = {}
21
22
  const controllers = {}
22
23
  const allMiddleware = { ...defaultMiddleware, ...(middleware || {}) }
23
24
 
24
- if (!componentsDir) {
25
- throw new Error('setupRouter: `config.componentsDir` missing')
26
- } else if (!env) {
25
+ if (!env) {
27
26
  throw new Error('setupRouter: `config.env` missing')
28
- } else if (!emailTemplateDir) {
29
- throw new Error('setupRouter: `config.emailTemplateDir` missing')
30
27
  }
31
28
 
32
29
  // Extend request/response with our custom error responses
@@ -95,8 +92,7 @@ export async function setupRouter (config) {
95
92
  expressApp.get('/email/partials/email.css', (req, res) => {
96
93
  // first check if there is a custom email.css in the emailTemplateDir
97
94
  // if not, return the default nitro email.css
98
- console.log(_dirname)
99
- if (fs.existsSync(emailTemplateDir + '/partials/email.css')) res.sendFile(emailTemplateDir + '/partials/email.css')
95
+ if (fs.existsSync(emailTemplateDir + 'partials/email.css')) res.sendFile(emailTemplateDir + 'partials/email.css')
100
96
  else res.sendFile(_dirname + 'email/partials/email.css')
101
97
  })
102
98
  expressApp.get('/email/:name', async (req, res) => {
package/util.js CHANGED
@@ -490,6 +490,19 @@ export function getCurrencyPrefixWidth (prefix, paddingRight=0) {
490
490
  return width
491
491
  }
492
492
 
493
+ export function getDirectories (path, pwd) {
494
+ const _pwd = pwd || process.env.PWD
495
+ return {
496
+ clientDir: path.join(_pwd, process.env.clientDir || 'client', '/'),
497
+ componentsDir: path.join(_pwd, process.env.componentsDir || 'components', '/'),
498
+ distDir: path.join(_pwd, process.env.distDir || ((process.env.clientDir || 'client') + '/dist'), '/'),
499
+ emailTemplateDir: path.join(_pwd, process.env.emailTemplateDir || 'server/email', '/'),
500
+ fontsDir: path.join(_pwd, (process.env.clientDir || 'client'), 'fonts', '/'),
501
+ imgsDir: path.join(_pwd, (process.env.clientDir || 'client'), 'imgs', '/'),
502
+ tmpDir: path.join(_pwd, process.env.tmpDir || 'tmp', '/'),
503
+ }
504
+ }
505
+
493
506
  export function getLink (obj, query) {
494
507
  /**
495
508
  * @param {object} obj - new query object