@stdex/create 0.0.0-stage → 1.2.0

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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -2
  3. package/README_EN.md +67 -0
  4. package/package.json +40 -3
  5. package/src/eslint.config.js +2 -0
  6. package/src/index.js +233 -0
  7. package/templates/base/eslint.config.js +2 -0
  8. package/templates/base/index.js +3 -0
  9. package/templates/base/package.json +16 -0
  10. package/templates/miniprogram/README.md +27 -0
  11. package/templates/miniprogram/_gitignore +3 -0
  12. package/templates/miniprogram/eslint.config.cjs +25 -0
  13. package/templates/miniprogram/gulpfile.js +81 -0
  14. package/templates/miniprogram/package.json +22 -0
  15. package/templates/miniprogram/project.config.json +31 -0
  16. package/templates/miniprogram/project.private.config.json +8 -0
  17. package/templates/miniprogram/src/app.json +16 -0
  18. package/templates/miniprogram/src/app.scss +0 -0
  19. package/templates/miniprogram/src/app.ts +6 -0
  20. package/templates/miniprogram/src/lib/app-data.ts +46 -0
  21. package/templates/miniprogram/src/pages/index/index.json +3 -0
  22. package/templates/miniprogram/src/pages/index/index.scss +11 -0
  23. package/templates/miniprogram/src/pages/index/index.ts +13 -0
  24. package/templates/miniprogram/src/pages/index/index.wxml +1 -0
  25. package/templates/miniprogram/src/sitemap.json +9 -0
  26. package/templates/miniprogram/tsconfig.json +11 -0
  27. package/templates/miniprogram/typings/index.d.ts +5 -0
  28. package/templates/miniprogram/typings/types/index.d.ts +1 -0
  29. package/templates/miniprogram/typings/types/wx/index.d.ts +162 -0
  30. package/templates/miniprogram/typings/types/wx/lib.wx.api.d.ts +32387 -0
  31. package/templates/miniprogram/typings/types/wx/lib.wx.app.d.ts +400 -0
  32. package/templates/miniprogram/typings/types/wx/lib.wx.behavior.d.ts +68 -0
  33. package/templates/miniprogram/typings/types/wx/lib.wx.canvas.d.ts +2623 -0
  34. package/templates/miniprogram/typings/types/wx/lib.wx.cloud.d.ts +1016 -0
  35. package/templates/miniprogram/typings/types/wx/lib.wx.component.d.ts +700 -0
  36. package/templates/miniprogram/typings/types/wx/lib.wx.event.d.ts +1436 -0
  37. package/templates/miniprogram/typings/types/wx/lib.wx.page.d.ts +272 -0
  38. package/templates/miniprogram/typings/types/wx/lib.wx.phys3D.d.ts +409 -0
  39. package/templates/miniprogram/typings/types/wx/lib.wx.wasm.d.ts +152 -0
  40. package/templates/miniprogram/typings/types/wx/lib.wx.xr-frame.d.ts +15631 -0
  41. package/templates/miniprogram/variables.json +11 -0
  42. package/templates/service/README.md +64 -0
  43. package/templates/service/_env +1 -0
  44. package/templates/service/_gitignore +5 -0
  45. package/templates/service/eslint.config.js +2 -0
  46. package/templates/service/package.json +48 -0
  47. package/templates/service/prisma/schema.prisma +40 -0
  48. package/templates/service/prisma/sql/demoTypedSql.sql +1 -0
  49. package/templates/service/prisma.config.ts +12 -0
  50. package/templates/service/setup.js +4 -0
  51. package/templates/service/src/cache.ts +28 -0
  52. package/templates/service/src/config.ts +49 -0
  53. package/templates/service/src/controllers/common.ts +43 -0
  54. package/templates/service/src/controllers/demo-school.ts +7 -0
  55. package/templates/service/src/controllers/demo-student.ts +7 -0
  56. package/templates/service/src/controllers/index.ts +15 -0
  57. package/templates/service/src/database.ts +52 -0
  58. package/templates/service/src/index.ts +15 -0
  59. package/templates/service/src/logger.ts +10 -0
  60. package/templates/service/src/routes/demo-school.ts +9 -0
  61. package/templates/service/src/routes/index.ts +3 -0
  62. package/templates/service/src/routes/router.ts +42 -0
  63. package/templates/service/src/tasks/common.ts +25 -0
  64. package/templates/service/src/tasks/demo-school.ts +10 -0
  65. package/templates/service/src/tasks/index.ts +1 -0
  66. package/templates/service/tsconfig.build.json +9 -0
  67. package/templates/service/tsconfig.json +10 -0
  68. package/templates/site/_gitignore +3 -0
  69. package/templates/site/_pnpm-workspace.yaml +4 -0
  70. package/templates/site/eslint.config.js +19 -0
  71. package/templates/site/package.json +41 -0
  72. package/templates/site/public/favicon.png +0 -0
  73. package/templates/site/src/app/header.tsx +31 -0
  74. package/templates/site/src/app/index.tsx +38 -0
  75. package/templates/site/src/app/layout.tsx +63 -0
  76. package/templates/site/src/app/menu.tsx +115 -0
  77. package/templates/site/src/app/router.tsx +50 -0
  78. package/templates/site/src/app/style.css +9 -0
  79. package/templates/site/src/assets/logo.png +0 -0
  80. package/templates/site/src/components/Breadcrumb.tsx +47 -0
  81. package/templates/site/src/components/DevStatus.tsx +14 -0
  82. package/templates/site/src/index.html +13 -0
  83. package/templates/site/src/index.tsx +5 -0
  84. package/templates/site/src/lib/config.ts +6 -0
  85. package/templates/site/src/lib/init-dayjs.ts +8 -0
  86. package/templates/site/src/lib/logging.ts +5 -0
  87. package/templates/site/src/views/dashboard.tsx +7 -0
  88. package/templates/site/src/views/school-detail.tsx +13 -0
  89. package/templates/site/src/views/school-ranking.tsx +7 -0
  90. package/templates/site/src/views/schools.tsx +43 -0
  91. package/templates/site/src/views/setting.tsx +7 -0
  92. package/templates/site/src/views/student-score.tsx +7 -0
  93. package/templates/site/src/views/students.tsx +7 -0
  94. package/templates/site/tsconfig.json +10 -0
  95. package/templates/site/vite.config.js +60 -0
@@ -0,0 +1,31 @@
1
+ import ImageLogo from '@/assets/logo.png'
2
+ import { DevStatus } from '@/components/DevStatus'
3
+
4
+ export function AppHeader() {
5
+ return <div css={s.wrap}>
6
+ <img css={s.logo} src={ImageLogo} />
7
+ <div css={s.siteName}>演示项目</div>
8
+ <DevStatus />
9
+ </div>
10
+ }
11
+
12
+ const s = {
13
+ wrap: css`
14
+ align-items: center;
15
+ display: flex;
16
+ padding: 8px 24px;
17
+ `,
18
+
19
+ logo: css`
20
+ height: 42px;
21
+ width: 42px;
22
+ margin-right: 20px;
23
+ `,
24
+
25
+ siteName: css`
26
+ flex: 1;
27
+ font-size: 16px;
28
+ letter-spacing: 1px;
29
+ white-space: nowrap;
30
+ `,
31
+ }
@@ -0,0 +1,38 @@
1
+ import { StyleProvider } from '@ant-design/cssinjs'
2
+ import { EmotionCacheProvider } from '@stdex/utils-frontend/react/emotion.js'
3
+ import { ConfigProvider, App as AntdApp } from 'antd'
4
+ import zhCn from 'antd/locale/zh_CN'
5
+ import { createRoot } from 'react-dom/client'
6
+ import { RouterProvider } from 'react-router/dom'
7
+ import { initDayJs } from '@/lib/init-dayjs.js'
8
+ import { router } from './router'
9
+
10
+ import './style.css'
11
+
12
+ initDayJs()
13
+
14
+ export function renderApp() {
15
+ function AntdWrapper() {
16
+ return <EmotionCacheProvider>
17
+ <StyleProvider layer>
18
+ <ConfigProvider
19
+ locale={zhCn}
20
+ theme={{
21
+ token: {},
22
+ components: {},
23
+ }}
24
+ >
25
+ <AntdApp>
26
+ <RouterProvider router={router} />
27
+ </AntdApp>
28
+ </ConfigProvider>
29
+ </StyleProvider>
30
+ </EmotionCacheProvider>
31
+ }
32
+
33
+ const $root = document.createElement('div')
34
+ $root.id = 'root'
35
+ document.body.appendChild($root)
36
+ const root = createRoot($root)
37
+ root.render(<AntdWrapper />)
38
+ }
@@ -0,0 +1,63 @@
1
+ import { Layout } from 'antd'
2
+ import { Outlet } from 'react-router'
3
+ import { AppHeader } from './header'
4
+ import { AppMenu } from './menu'
5
+
6
+ export function AppLayout() {
7
+ const [siderCollapsed, setSiderCollapsed] = useState(false)
8
+
9
+ return <Layout css={s.root}>
10
+ <Layout.Header css={s.header}>
11
+ <AppHeader />
12
+ </Layout.Header>
13
+
14
+ <Layout>
15
+ <Layout.Sider
16
+ css={s.sider}
17
+ theme="light"
18
+ collapsible
19
+ collapsed={siderCollapsed}
20
+ onCollapse={setSiderCollapsed}
21
+ >
22
+ <AppMenu />
23
+ </Layout.Sider>
24
+
25
+ <Layout css={s.main}>
26
+ <Outlet />
27
+ </Layout>
28
+ </Layout>
29
+ </Layout>
30
+ }
31
+
32
+ const s = {
33
+ root: css`
34
+ height: 100vh;
35
+ overflow: hidden;
36
+ `,
37
+
38
+ header: css`
39
+ background-color: #fff;
40
+ border-bottom: 1px solid #f5f5f5;
41
+ padding: 0;
42
+ height: auto;
43
+ line-height: unset;
44
+ `,
45
+
46
+ sider: css`
47
+ padding-top: 4px;
48
+ .ant-layout-sider-children {
49
+ overflow-y: auto;
50
+ }
51
+ `,
52
+
53
+ main: css`
54
+ & > .page {
55
+ background-color: #fff;
56
+ border-radius: 3px;
57
+ box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.05);
58
+ flex: 1;
59
+ margin: 16px;
60
+ padding: 16px;
61
+ }
62
+ `,
63
+ }
@@ -0,0 +1,115 @@
1
+ import {
2
+ DashboardOutlined,
3
+ ApartmentOutlined,
4
+ UserOutlined,
5
+ SettingOutlined,
6
+ } from '@ant-design/icons'
7
+ import { Menu, type MenuProps } from 'antd'
8
+ import { useNavigate, useMatches } from 'react-router'
9
+
10
+ type MenuInfo = Parameters<Required<MenuProps>['onClick']>[0]
11
+ type MenuItem = Required<MenuProps>['items'][number]
12
+
13
+ /**
14
+ * 菜单对应的页面路由,不在此表的菜单则不跳转
15
+ */
16
+ const routes = [
17
+ { key: 'dashboard', path: '/' },
18
+ { key: 'schools', path: '/schools' },
19
+ { key: 'school-ranking', path: '/schools/ranking' },
20
+ { key: 'students', path: '/students' },
21
+ { key: 'student-score', path: '/students/score' },
22
+ { key: 'setting', path: '/setting' },
23
+ ]
24
+
25
+ const menuItems: MenuItem[] = [
26
+ {
27
+ key: 'dashboard',
28
+ label: '信息概览',
29
+ icon: <DashboardOutlined />,
30
+ },
31
+
32
+ {
33
+ key: 'school-manage',
34
+ label: '学校管理',
35
+ icon: <ApartmentOutlined />,
36
+ children: [
37
+ {
38
+ key: 'schools',
39
+ label: '学校列表',
40
+ },
41
+ {
42
+ key: 'school-ranking',
43
+ label: '学校排名',
44
+ },
45
+ ],
46
+ },
47
+
48
+ {
49
+ key: 'student-manage',
50
+ label: '学生管理',
51
+ icon: <UserOutlined />,
52
+ children: [
53
+ {
54
+ key: 'students',
55
+ label: '学生列表',
56
+ },
57
+ {
58
+ key: 'student-score',
59
+ label: '成绩管理',
60
+ },
61
+ ],
62
+ },
63
+
64
+ {
65
+ key: 'setting',
66
+ label: '系统设置',
67
+ icon: <SettingOutlined />,
68
+ },
69
+ ]
70
+
71
+ /**
72
+ * 返回指定 key 的菜单项的完整 keyPath
73
+ */
74
+ function getKeyPath(key: string, items?: MenuItem[]): string[] {
75
+ items ??= menuItems
76
+ for (const item of items) {
77
+ if (item?.key === undefined) continue
78
+ if (item.key === key) return [key]
79
+ if ('children' in item && item.children) {
80
+ const subKeyPath = getKeyPath(key, item.children)
81
+ if (subKeyPath.length) return [String(item.key), ...subKeyPath]
82
+ }
83
+ }
84
+ return []
85
+ }
86
+
87
+ export function AppMenu() {
88
+ const navigate = useNavigate()
89
+ const matches = useMatches()
90
+
91
+ const { defaultSelectedKeys, defaultOpenKeys } = useMemo(() => {
92
+ const currentPath = matches[matches.length - 1]?.pathname ?? null
93
+ const currentRoute = routes.find(v => v.path === currentPath)
94
+ if (currentRoute) {
95
+ return {
96
+ defaultSelectedKeys: [currentRoute.key],
97
+ defaultOpenKeys: getKeyPath(currentRoute.key).filter(key => key !== currentRoute.key),
98
+ }
99
+ }
100
+ return { defaultSelectedKeys: [], defaultOpenKeys: [] }
101
+ }, [matches])
102
+
103
+ function onClick(info: MenuInfo) {
104
+ const route = routes.find(v => v.key === info.key)
105
+ if (route) void navigate(route.path)
106
+ }
107
+
108
+ return <Menu
109
+ mode="inline"
110
+ items={menuItems}
111
+ defaultSelectedKeys={defaultSelectedKeys}
112
+ defaultOpenKeys={defaultOpenKeys}
113
+ onClick={onClick}
114
+ />
115
+ }
@@ -0,0 +1,50 @@
1
+ import { createBrowserRouter, Navigate } from 'react-router'
2
+ import { Dashboard } from '@/views/dashboard'
3
+ import { SchoolDetail } from '@/views/school-detail'
4
+ import { SchoolRanking } from '@/views/school-ranking'
5
+ import { Schools } from '@/views/schools'
6
+ import { Setting } from '@/views/setting'
7
+ import { StudentScore } from '@/views/student-score'
8
+ import { Students } from '@/views/students'
9
+ import { AppLayout } from './layout'
10
+
11
+ export const router = createBrowserRouter([
12
+ {
13
+ path: '/',
14
+ Component: AppLayout,
15
+ children: [
16
+ {
17
+ index: true,
18
+ Component: Dashboard,
19
+ },
20
+ {
21
+ path: '/schools',
22
+ Component: Schools,
23
+ },
24
+ {
25
+ path: '/schools/ranking',
26
+ Component: SchoolRanking,
27
+ },
28
+ {
29
+ path: '/schools/:id',
30
+ Component: SchoolDetail,
31
+ },
32
+ {
33
+ path: '/students',
34
+ Component: Students,
35
+ },
36
+ {
37
+ path: '/students/score',
38
+ Component: StudentScore,
39
+ },
40
+ {
41
+ path: '/setting',
42
+ Component: Setting,
43
+ },
44
+ {
45
+ path: '*',
46
+ element: <Navigate to="/" />,
47
+ },
48
+ ],
49
+ },
50
+ ])
@@ -0,0 +1,9 @@
1
+ html,
2
+ body {
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+ html, body, #root, .ant-app {
7
+ height: 100%;
8
+ overflow: hidden;
9
+ }
@@ -0,0 +1,47 @@
1
+ import { Breadcrumb as AntdBreadcrumb, type BreadcrumbProps } from 'antd'
2
+ import { Link } from 'react-router'
3
+
4
+ // 字符串是纯文本节点;对象是可点击节点
5
+ type Item =
6
+ | string
7
+ | {
8
+ path: string
9
+ title: string
10
+ }
11
+
12
+ type ItemRender = Required<BreadcrumbProps>['itemRender']
13
+
14
+ export function Breadcrumb({ items }: { items: (string | Item)[] }) {
15
+ const antdItems = useMemo(
16
+ () =>
17
+ items.map(item =>
18
+ typeof item === 'string' ? { title: item } : { href: item.path, title: item.title },
19
+ ),
20
+ [items],
21
+ )
22
+
23
+ const itemRender: ItemRender = item => {
24
+ return item.href ? (
25
+ <Link to={item.href} css={s.link}>
26
+ {item.title}
27
+ </Link>
28
+ ) : (
29
+ <span>{item.title}</span>
30
+ )
31
+ }
32
+
33
+ return <AntdBreadcrumb
34
+ css={{ margin: '16px 16px 0' }}
35
+ itemRender={itemRender}
36
+ items={antdItems}
37
+ />
38
+ }
39
+
40
+ const s = {
41
+ link: css`
42
+ &:hover {
43
+ background-color: transparent;
44
+ color: var(--ant-color-link);
45
+ }
46
+ `,
47
+ }
@@ -0,0 +1,14 @@
1
+ import { Tag } from 'antd'
2
+ import { config } from '@/lib/config'
3
+
4
+ export function DevStatus() {
5
+ return config.inDev ? (
6
+ <Tag color="green" variant="solid">
7
+ 开发模式
8
+ </Tag>
9
+ ) : (
10
+ <Tag variant="solid" color="#999">
11
+ 非开发模式
12
+ </Tag>
13
+ )
14
+ }
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="shortcut icon" href="./favicon.png" />
7
+ <title>演示项目</title>
8
+ </head>
9
+
10
+ <body></body>
11
+
12
+ <script type="module" src="./index.tsx"></script>
13
+ </html>
@@ -0,0 +1,5 @@
1
+ import '@stdex/utils-frontend/react/register-globals.js'
2
+ import '@stdex/utils-frontend/react/emotion-register-globals.js'
3
+ import { renderApp } from '@/app/index'
4
+
5
+ renderApp()
@@ -0,0 +1,6 @@
1
+ interface InjectVars {
2
+ APP_CONFIG: {
3
+ inDev: boolean
4
+ }
5
+ }
6
+ export const config = (window as unknown as InjectVars).APP_CONFIG
@@ -0,0 +1,8 @@
1
+ import dayjs from 'dayjs'
2
+ import objectSupport from 'dayjs/plugin/objectSupport.js'
3
+ import 'dayjs/locale/zh-cn.js'
4
+
5
+ export function initDayJs() {
6
+ dayjs.extend(objectSupport)
7
+ dayjs.locale('zh-cn')
8
+ }
@@ -0,0 +1,5 @@
1
+ import { logger, getLogger, LogLevel } from '@stdex/logging'
2
+ import { config } from '@/lib/config.js'
3
+
4
+ logger.setLevel(config.inDev ? LogLevel.Debug : LogLevel.Info)
5
+ export { logger, getLogger }
@@ -0,0 +1,7 @@
1
+ import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
2
+
3
+ export function Dashboard() {
4
+ usePageTitle('信息概览')
5
+
6
+ return <div className="page">信息概览</div>
7
+ }
@@ -0,0 +1,13 @@
1
+ import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
2
+ import { useParams } from 'react-router'
3
+ import { Breadcrumb } from '@/components/Breadcrumb'
4
+
5
+ export function SchoolDetail() {
6
+ usePageTitle('学校详情')
7
+ const params = useParams<{ id: string }>()
8
+
9
+ return <>
10
+ <Breadcrumb items={[{ path: '/schools', title: '学校列表' }, '学校详情']} />
11
+ <div className="page">学校详情 - {params.id}</div>
12
+ </>
13
+ }
@@ -0,0 +1,7 @@
1
+ import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
2
+
3
+ export function SchoolRanking() {
4
+ usePageTitle('学校排名')
5
+
6
+ return <div className="page">学校排名</div>
7
+ }
@@ -0,0 +1,43 @@
1
+ import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
2
+ import { useNavigate } from 'react-router'
3
+
4
+ const schools = [
5
+ { id: 1, name: '学校-1' },
6
+ { id: 2, name: '学校-2' },
7
+ { id: 3, name: '学校-3' },
8
+ ]
9
+
10
+ export function Schools() {
11
+ usePageTitle('学校列表')
12
+ const navigate = useNavigate()
13
+
14
+ function goDetail(id: number) {
15
+ void navigate('/schools/' + id.toString())
16
+ }
17
+
18
+ return <div className="page">
19
+ <div css={s.title}>学校列表</div>
20
+ <div css={s.list}>
21
+ {schools.map(school => <div key={school.id} className="item">
22
+ <div className="name">{school.name}</div>
23
+ <a onClick={() => goDetail(school.id)}>详情</a>
24
+ </div>)}
25
+ </div>
26
+ </div>
27
+ }
28
+
29
+ const s = {
30
+ title: css`
31
+ margin-bottom: 20px;
32
+ `,
33
+
34
+ list: css`
35
+ width: 12em;
36
+ .item {
37
+ display: flex;
38
+ .name {
39
+ flex: 1;
40
+ }
41
+ }
42
+ `,
43
+ }
@@ -0,0 +1,7 @@
1
+ import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
2
+
3
+ export function Setting() {
4
+ usePageTitle('系统设置')
5
+
6
+ return <div className="page">系统设置</div>
7
+ }
@@ -0,0 +1,7 @@
1
+ import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
2
+
3
+ export function StudentScore() {
4
+ usePageTitle('成绩管理')
5
+
6
+ return <div className="page">成绩管理</div>
7
+ }
@@ -0,0 +1,7 @@
1
+ import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
2
+
3
+ export function Students() {
4
+ usePageTitle('学生列表')
5
+
6
+ return <div className="page">学生列表</div>
7
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./node_modules/@stdex/presets-typescript/tsconfig-vite",
3
+ "compilerOptions": {
4
+ "jsxImportSource": "@emotion/react",
5
+ "paths": {
6
+ "@/*": ["./src/*"]
7
+ }
8
+ },
9
+ "include": ["./src/"]
10
+ }
@@ -0,0 +1,60 @@
1
+ import path from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import react from '@vitejs/plugin-react-swc'
4
+ import { defineConfig } from 'vite'
5
+ import svgr from 'vite-plugin-svgr'
6
+
7
+ const base = path.dirname(fileURLToPath(new URL(import.meta.url)))
8
+ const src = path.join(base, 'src')
9
+
10
+ export default defineConfig(({ mode }) => {
11
+ const inDev = mode === 'development'
12
+ const appConfig = {
13
+ inDev,
14
+ }
15
+
16
+ return {
17
+ mode,
18
+ root: src,
19
+ base: '',
20
+ publicDir: path.join(base, 'public'),
21
+
22
+ define: {
23
+ 'window.APP_CONFIG': JSON.stringify(appConfig),
24
+ },
25
+
26
+ resolve: {
27
+ alias: {
28
+ '@': src,
29
+ },
30
+ },
31
+
32
+ // 让 vite 对 node_modules 里的 jsx 文件也进行依赖解析
33
+ // 不然 @stdex/utils 里的 jsx 文件会无法正常引用类库
34
+ optimizeDeps: {
35
+ extensions: ['.jsx'],
36
+ },
37
+
38
+ plugins: [
39
+ react({
40
+ jsxImportSource: '@emotion/react',
41
+ plugins: [['@swc/plugin-emotion', {}]],
42
+ }),
43
+ svgr(),
44
+ ],
45
+
46
+ server: {
47
+ host: true,
48
+ port: 3000,
49
+ strictPort: true,
50
+ },
51
+
52
+ build: {
53
+ outDir: path.join(base, 'dist'),
54
+ emptyOutDir: true,
55
+ rollupOptions: {
56
+ input: path.join(src, 'index.html'),
57
+ },
58
+ },
59
+ }
60
+ })