has error
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
97
README.md
Normal file
97
README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# MDX Dynamic Content Preview
|
||||
|
||||
一个基于 Vite + React 的 MDX 动态内容预览器,支持实时编译和热重载。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ **实时 MDX 编译** - 使用 @mdx-js/mdx 进行编译
|
||||
- ✅ **热重载** - MDX 文件修改后自动刷新预览
|
||||
- ✅ **TypeScript 支持** - 完整的类型安全
|
||||
- ✅ **组件自定义** - 支持自定义 React 组件样式
|
||||
- ✅ **开发体验** - Vite 提供的快速 HMR 和构建
|
||||
- ✅ **错误处理** - 友好的错误显示和加载状态
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### 启动开发服务器
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
访问 http://localhost:3000 查看预览效果。
|
||||
|
||||
### 构建生产版本
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
test-mdx/
|
||||
├── public/
|
||||
│ └── example.mdx # 示例 MDX 文件
|
||||
├── src/
|
||||
│ ├── app.tsx # 主应用组件
|
||||
│ └── main.tsx # 应用入口
|
||||
├── index.html # HTML 模板
|
||||
├── package.json # 项目配置
|
||||
├── tsconfig.json # TypeScript 配置
|
||||
├── tsconfig.node.json # Node.js TypeScript 配置
|
||||
└── vite.config.ts # Vite 构建配置
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 将你的 MDX 文件放在 `public/` 目录下
|
||||
2. 修改 `src/app.tsx` 中的文件路径:
|
||||
```typescript
|
||||
const response = await fetch('/your-file.mdx?t=' + Date.now());
|
||||
```
|
||||
3. 运行开发服务器即可看到预览效果
|
||||
|
||||
## 自定义组件样式
|
||||
|
||||
在 `src/app.tsx` 中的 `components` 对象中自定义各种 Markdown 元素的样式:
|
||||
|
||||
```typescript
|
||||
const components = {
|
||||
h1: (props) => <h1 style={{ color: 'blue' }} {...props} />,
|
||||
p: (props) => <p style={{ fontSize: '16px' }} {...props} />,
|
||||
// 添加更多组件...
|
||||
};
|
||||
```
|
||||
|
||||
## 热重载机制
|
||||
|
||||
- 开发模式下每 2 秒自动检查 MDX 文件变化
|
||||
- 使用时间戳参数避免缓存问题
|
||||
- 显示重载状态指示器
|
||||
- 修改后即时更新预览
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **Vite** - 构建工具和开发服务器
|
||||
- **React** - UI 框架
|
||||
- **TypeScript** - 类型安全
|
||||
- **@mdx-js/mdx** - MDX 编译器
|
||||
- **@mdx-js/react** - React 集成
|
||||
- **vite-plugin-static-copy** - 静态文件复制
|
||||
|
||||
## 开发脚本
|
||||
|
||||
- `pnpm dev` - 启动开发服务器
|
||||
- `pnpm build` - 构建生产版本
|
||||
- `pnpm preview` - 预览构建结果
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
24
index.html
Normal file
24
index.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MDX Dynamic Preview</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
32
package.json
Normal file
32
package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "test-mdx",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.14.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@mdx-js/mdx": "^3.1.1",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"react": "^19.2.1",
|
||||
"react-dom": "^19.2.1",
|
||||
"vite": "^7.2.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.2",
|
||||
"typescript": "^5.9.3",
|
||||
"vite-plugin-static-copy": "^3.1.4"
|
||||
}
|
||||
}
|
||||
2260
pnpm-lock.yaml
generated
Normal file
2260
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
99
public/example.mdx
Normal file
99
public/example.mdx
Normal file
@@ -0,0 +1,99 @@
|
||||
# 欢迎使用 MDX 预览器
|
||||
|
||||
这是一个示例的 MDX 文件,展示了 MDX 的各种功能。
|
||||
|
||||
## 🎉 更新时间:2025-12-09 22:05
|
||||
|
||||
如果你能看到这个更新的内容,说明热重载功能正在正常工作!
|
||||
|
||||
## 标题和文本
|
||||
|
||||
MDX 允许你在 Markdown 中直接使用 JSX 组件。例如,我们可以创建一个带有样式的文本:
|
||||
|
||||
<blockquote>
|
||||
这是一个引用块,展示了如何使用 MDX 组件系统。
|
||||
</blockquote>
|
||||
|
||||
## 代码示例
|
||||
|
||||
### 内联代码
|
||||
|
||||
这是一个 `内联代码` 的例子。
|
||||
|
||||
### 代码块
|
||||
|
||||
```javascript
|
||||
function greet(name) {
|
||||
return `Hello, ${name}!`;
|
||||
}
|
||||
|
||||
console.log(greet("MDX"));
|
||||
```
|
||||
|
||||
```typescript
|
||||
interface User {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
const user: User = {
|
||||
name: "Alice",
|
||||
age: 30
|
||||
};
|
||||
```
|
||||
|
||||
## 列表
|
||||
|
||||
### 无序列表
|
||||
|
||||
- 第一项
|
||||
- 第二项
|
||||
- 嵌套项目 1
|
||||
- 嵌套项目 2
|
||||
- 第三项
|
||||
|
||||
### 有序列表
|
||||
|
||||
1. 步骤一
|
||||
2. 步骤二
|
||||
3. 步骤三
|
||||
|
||||
## 交互式组件
|
||||
|
||||
这是一个自定义的计数器组件:
|
||||
|
||||
<div style={{
|
||||
padding: '16px',
|
||||
backgroundColor: '#f0f9ff',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #0ea5e9',
|
||||
margin: '16px 0'
|
||||
}}>
|
||||
<p style={{ marginBottom: '12px', fontWeight: 'bold' }}>
|
||||
🎯 互动示例
|
||||
</p>
|
||||
<p style={{ color: '#0284c7' }}>
|
||||
这是一个通过 MDX 直接嵌入的样式化 div,展示了如何在 Markdown 中使用自定义组件。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
## 表格
|
||||
|
||||
| 功能 | 描述 | 示例 |
|
||||
|------|------|------|
|
||||
| 标题 | 支持 h1-h6 | `# 标题` |
|
||||
| 代码 | 语法高亮 | `\`\`\`javascript` |
|
||||
| 组件 | JSX 支持 | `<Component />` |
|
||||
|
||||
## 总结
|
||||
|
||||
MDX 的强大之处在于:
|
||||
|
||||
1. **Markdown 的简洁性** - 保持写作的流畅性
|
||||
2. **React 组件的强大功能** - 可以使用任何 React 组件
|
||||
3. **类型安全** - TypeScript 支持
|
||||
4. **热重载** - 开发时实时预览
|
||||
|
||||
---
|
||||
|
||||
*这个文件展示了 MDX 的各种功能,包括文本格式化、代码高亮、组件嵌入等。*
|
||||
303
src/app.tsx
Normal file
303
src/app.tsx
Normal file
@@ -0,0 +1,303 @@
|
||||
import React, { useState, useEffect, createElement } from 'react';
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import { compile } from '@mdx-js/mdx';
|
||||
import * as runtime from 'react/jsx-runtime';
|
||||
import { ComponentProps } from 'react';
|
||||
|
||||
interface CodeProps extends ComponentProps<'code'> {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
interface BlockquoteProps extends ComponentProps<'blockquote'> { }
|
||||
|
||||
interface UlProps extends ComponentProps<'ul'> { }
|
||||
interface OlProps extends ComponentProps<'ol'> { }
|
||||
|
||||
interface ListItemProps extends ComponentProps<'li'> { }
|
||||
|
||||
const components = {
|
||||
h1: (props: ComponentProps<'h1'>) => <h1 style={{ color: '#2563eb' }} {...props} />,
|
||||
h2: (props: ComponentProps<'h2'>) => <h2 style={{ color: '#1e40af' }} {...props} />,
|
||||
p: (props: ComponentProps<'p'>) => <p style={{ fontSize: '16px', lineHeight: '1.6' }} {...props} />,
|
||||
code: ({ className, children, ...props }: CodeProps) => (
|
||||
<code
|
||||
style={{
|
||||
backgroundColor: '#f3f4f6',
|
||||
padding: '2px 6px',
|
||||
borderRadius: '4px',
|
||||
fontFamily: 'monospace'
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
),
|
||||
pre: (props: ComponentProps<'pre'>) => (
|
||||
<pre
|
||||
style={{
|
||||
backgroundColor: '#1f2937',
|
||||
color: '#f9fafb',
|
||||
padding: '16px',
|
||||
borderRadius: '8px',
|
||||
overflow: 'auto',
|
||||
fontFamily: 'monospace'
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
blockquote: (props: BlockquoteProps) => (
|
||||
<blockquote
|
||||
style={{
|
||||
borderLeft: '4px solid #e5e7eb',
|
||||
paddingLeft: '16px',
|
||||
margin: '16px 0',
|
||||
fontStyle: 'italic'
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
ul: (props: UlProps) => <ul style={{ paddingLeft: '20px' }} {...props} />,
|
||||
ol: (props: OlProps) => <ol style={{ paddingLeft: '20px' }} {...props} />,
|
||||
li: (props: ListItemProps) => <li style={{ marginBottom: '8px' }} {...props} />
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
const [Content, setContent] = useState<React.ComponentType | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [reloading, setReloading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadMDX = async () => {
|
||||
if (!loading) {
|
||||
setReloading(true);
|
||||
}
|
||||
try {
|
||||
const response = await fetch('/example.mdx?t=' + Date.now());
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const text = await response.text();
|
||||
|
||||
const compiledCode = await compile(text, {
|
||||
outputFormat: 'function-body',
|
||||
development: import.meta.env.DEV,
|
||||
});
|
||||
|
||||
const code = String(compiledCode);
|
||||
console.log('Raw compiled code:', code);
|
||||
|
||||
try {
|
||||
// Define basic HTML components for missing ones
|
||||
const htmlComponents = {
|
||||
h3: (props: any) => <h3 style={{ color: '#374151' }} {...props} />,
|
||||
h4: (props: any) => <h4 style={{ color: '#4b5563' }} {...props} />,
|
||||
h5: (props: any) => <h5 style={{ color: '#6b7280' }} {...props} />,
|
||||
h6: (props: any) => <h6 style={{ color: '#9ca3af' }} {...props} />,
|
||||
a: (props: any) => <a style={{ color: '#2563eb', textDecoration: 'underline' }} {...props} />,
|
||||
img: (props: any) => <img style={{ maxWidth: '100%', height: 'auto' }} {...props} />,
|
||||
hr: (props: any) => <hr style={{ border: '1px solid #e5e7eb', margin: '16px 0' }} {...props} />,
|
||||
br: (props: any) => <br {...props} />,
|
||||
em: (props: any) => <em style={{ fontStyle: 'italic' }} {...props} />,
|
||||
strong: (props: any) => <strong style={{ fontWeight: 'bold' }} {...props} />,
|
||||
table: (props: any) => <table style={{ width: '100%', borderCollapse: 'collapse', margin: '16px 0' }} {...props} />,
|
||||
thead: (props: any) => <thead style={{ backgroundColor: '#f9fafb' }} {...props} />,
|
||||
tbody: (props: any) => <tbody {...props} />,
|
||||
tr: (props: any) => <tr style={{ borderBottom: '1px solid #e5e7eb' }} {...props} />,
|
||||
th: (props: any) => <th style={{ padding: '12px', textAlign: 'left', fontWeight: 'bold' }} {...props} />,
|
||||
td: (props: any) => <td style={{ padding: '12px' }} {...props} />,
|
||||
div: (props: any) => <div {...props} />,
|
||||
span: (props: any) => <span {...props} />,
|
||||
};
|
||||
|
||||
// Merge all components
|
||||
const allComponents = { ...components, ...htmlComponents };
|
||||
|
||||
const scope = {
|
||||
React,
|
||||
components: allComponents,
|
||||
createElement,
|
||||
Fragment: React.Fragment,
|
||||
jsx: runtime.jsx,
|
||||
jsxs: runtime.jsxs,
|
||||
// Include all components directly in scope
|
||||
h1: allComponents.h1,
|
||||
h2: allComponents.h2,
|
||||
h3: allComponents.h3,
|
||||
h4: allComponents.h4,
|
||||
h5: allComponents.h5,
|
||||
h6: allComponents.h6,
|
||||
p: allComponents.p,
|
||||
a: allComponents.a,
|
||||
img: allComponents.img,
|
||||
ul: allComponents.ul,
|
||||
ol: allComponents.ol,
|
||||
li: allComponents.li,
|
||||
blockquote: allComponents.blockquote,
|
||||
hr: allComponents.hr,
|
||||
br: allComponents.br,
|
||||
em: allComponents.em,
|
||||
strong: allComponents.strong,
|
||||
code: allComponents.code,
|
||||
pre: allComponents.pre,
|
||||
table: allComponents.table,
|
||||
thead: allComponents.thead,
|
||||
tbody: allComponents.tbody,
|
||||
tr: allComponents.tr,
|
||||
th: allComponents.th,
|
||||
td: allComponents.td,
|
||||
div: allComponents.div,
|
||||
span: allComponents.span,
|
||||
};
|
||||
|
||||
const keys = Object.keys(scope);
|
||||
const values = Object.values(scope);
|
||||
|
||||
// Try to evaluate the code using a more robust approach
|
||||
try {
|
||||
// First, try to execute as function body
|
||||
const fn1 = new Function(
|
||||
...keys,
|
||||
`${code}; return MDXContent;`
|
||||
);
|
||||
const Component1 = fn1(...values);
|
||||
const MDXComponent1 = Component1.default || Component1;
|
||||
|
||||
if (MDXComponent1) {
|
||||
setContent(() => MDXComponent1);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
} catch (e1) {
|
||||
console.log('First evaluation method failed, trying second...');
|
||||
}
|
||||
|
||||
try {
|
||||
// Second, try to execute as function expression
|
||||
const fn2 = new Function(
|
||||
...keys,
|
||||
`return ${code}`
|
||||
);
|
||||
const Component2 = fn2(...values);
|
||||
const MDXComponent2 = Component2.default || Component2;
|
||||
|
||||
if (MDXComponent2) {
|
||||
setContent(() => MDXComponent2);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
} catch (e2) {
|
||||
console.log('Second evaluation method failed, trying third...');
|
||||
}
|
||||
|
||||
try {
|
||||
// Third, try to execute directly with eval-like approach
|
||||
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
||||
const fn3 = new AsyncFunction(
|
||||
...keys,
|
||||
code
|
||||
);
|
||||
const result = await fn3(...values);
|
||||
const MDXComponent3 = result.default || result;
|
||||
|
||||
if (MDXComponent3) {
|
||||
setContent(() => MDXComponent3);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
} catch (e3) {
|
||||
console.log('Third evaluation method failed');
|
||||
}
|
||||
|
||||
throw new Error('All evaluation methods failed');
|
||||
} catch (evalError) {
|
||||
console.error('Evaluation error:', evalError);
|
||||
console.error('Compiled code sample:', code.substring(0, 500));
|
||||
const errorMessage = evalError instanceof Error ? evalError.message : String(evalError);
|
||||
throw new Error(`MDX evaluation failed: ${errorMessage}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error loading MDX:', err);
|
||||
setError(err instanceof Error ? err.message : String(err));
|
||||
setLoading(false);
|
||||
} finally {
|
||||
setReloading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadMDX();
|
||||
|
||||
// Set up interval for auto-reloading during development
|
||||
let intervalId: NodeJS.Timeout;
|
||||
if (import.meta.env.DEV) {
|
||||
intervalId = setInterval(loadMDX, 2000); // Check every 2 seconds
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
};
|
||||
}, [loading]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div style={{ padding: '20px', textAlign: 'center' }}>
|
||||
<h1>MDX Viewer</h1>
|
||||
<p>Loading MDX content...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<h1>MDX Viewer</h1>
|
||||
<div style={{
|
||||
backgroundColor: '#fef2f2',
|
||||
border: '1px solid #fecaca',
|
||||
borderRadius: '8px',
|
||||
padding: '16px',
|
||||
color: '#dc2626'
|
||||
}}>
|
||||
<h3>Error loading MDX:</h3>
|
||||
<p>{error}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MDXProvider components={components}>
|
||||
<div style={{
|
||||
maxWidth: '800px',
|
||||
margin: '0 auto',
|
||||
padding: '20px',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
||||
}}>
|
||||
<div style={{ marginBottom: '32px', textAlign: 'center', position: 'relative' }}>
|
||||
<h1>MDX Viewer</h1>
|
||||
{reloading && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
right: '0',
|
||||
backgroundColor: '#22c55e',
|
||||
color: 'white',
|
||||
padding: '4px 8px',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
🔄 Reloading...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{Content && <Content />}
|
||||
</div>
|
||||
</MDXProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
9
src/main.tsx
Normal file
9
src/main.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './app.tsx'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
25
tsconfig.json
Normal file
25
tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
12
tsconfig.node.json
Normal file
12
tsconfig.node.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
25
vite.config.ts
Normal file
25
vite.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: 'public/*.mdx',
|
||||
dest: './'
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
server: {
|
||||
port: 3000,
|
||||
open: true
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: true
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user