1 | # f2e-server
|
2 | f2e-server 2
|
3 |
|
4 | ## Install
|
5 | `npm i -g f2e-server`
|
6 |
|
7 | ## Options
|
8 | - `f2e -h`
|
9 | - `f2e conf` 生成 .f2econfig.js 配置文件 [.f2econfig.js](.f2econfig.js) 的一个clone版本,需要自行修改
|
10 | - `f2e build` 构建到 output 目录 (需要在配置文件中配置 output 路径)
|
11 | - `f2e build -w true` 开启构建并监听文件变化输出结果
|
12 | - `f2e start` 启动开发服务器
|
13 | - `f2e start -h`
|
14 | - `f2e start` 从 2850 开始自增检测可以使用的PORT并启动
|
15 | - `f2e start -c .f2econfig.js` 指定配置文件
|
16 | - `f2e start -p 8080` 指定端口启动
|
17 | - `sudo f2e start -p 443` 开启HTTPS支持
|
18 | - `sudo f2e start -H mysite.local` 设置本地域名并从80端口启动
|
19 | - `sudo f2e start -H mysite.local -p 8080` 设置本地域名并从指定端口启动
|
20 |
|
21 | ## Config
|
22 | `f2e conf` 生成 [.f2econfig.js](.f2econfig.js) 配置文件
|
23 |
|
24 | ### 基本配置
|
25 |
|
26 | ``` javascript
|
27 |
|
28 | const path = require('path')
|
29 | const request = require('request')
|
30 |
|
31 | module.exports = {
|
32 | // host: 'f2e.local.cn',
|
33 | /**
|
34 | * 不启用 host 识别,只根据端口处理
|
35 | */
|
36 | no_host: false,
|
37 | // port: 2850,
|
38 | /**
|
39 | * 是否开启自动刷新
|
40 | * @type {Boolean}
|
41 | */
|
42 | livereload: true,
|
43 | /**
|
44 | * 使用 less 编译为css, 使用 less 配置
|
45 | * @type {Object}
|
46 | */
|
47 | useLess: {
|
48 | compress: false
|
49 | },
|
50 | /**
|
51 | * 支持babel编译 js/es/jsx, 支持 `.babelrc` 配置,
|
52 | * @type {Object}
|
53 | */
|
54 | useBabel: {
|
55 | getModuleId: pathname => pathname.replace(/\\+/g, '/'),
|
56 | /**
|
57 | * 支持多组babel-option 配置通过 only 参数匹配,匹配到一个,则停止
|
58 | */
|
59 | _rules: [
|
60 | {
|
61 | only: ['number.js'],
|
62 | getModuleId: pathname => 'number',
|
63 | }
|
64 | ]
|
65 | },
|
66 | /**
|
67 | * 是否支持 gzip
|
68 | * @type {Boolean}
|
69 | */
|
70 | gzip: true,
|
71 | /**
|
72 | * Range 默认大小
|
73 | * @type {Number}
|
74 | */
|
75 | range_size: 1024 * 1024,
|
76 | /**
|
77 | * 支持中间件列表, 默认添加的系统中间件后面, build之前
|
78 | *
|
79 | * ☆ 重要的
|
80 | * 1. 自定义中间件中所有事件 onXxx 也支持在外面定义, 在组件内更显条理, 而且也方便自己封装插件多处引入
|
81 | * 2. 系统中间件顺序 include(0) -> less(1) -> babel(2) ---> build(last)
|
82 | * 3. 顶层定义的事件顺序在系统中间件之前
|
83 | * @type {Array<Function>}
|
84 | */
|
85 | middlewares: [
|
86 | // marked 编译
|
87 | (conf) => {
|
88 | // conf 为当前配置
|
89 | return {
|
90 | /**
|
91 | *
|
92 | * @param {string} pathname 当前资源路径
|
93 | * @param {Request} req 原生request对象
|
94 | * @param {Response} resp 原生response对象
|
95 | * @param {Object} memory 所有目录对应内存结构, get/set等方法调用会被 onSet/onGet 等拦截
|
96 | */
|
97 | onRoute (pathname, req, resp, memory) {
|
98 | // 搞一个代理试试
|
99 | if (pathname.match(/^es6/)) {
|
100 | request(pathname.replace('es6', 'http://es6.ruanyifeng.com')).pipe(resp)
|
101 | return false
|
102 | }
|
103 | },
|
104 | /**
|
105 | *
|
106 | * @param {string} eventType 事件类型 change/add/etc.
|
107 | * @param {string} pathname 当前修改文件的路径
|
108 | * @param {boolean} build 是否开启了build配置, build模式下可能同时需要触发其他资源修改等
|
109 | */
|
110 | buildWatcher (eventType, pathname, build) {
|
111 | console.log(new Date().toLocaleString(), eventType, pathname)
|
112 | },
|
113 | /**
|
114 | * onSet 设置资源内容时候触发
|
115 | * @param {string} pathname 当前资源路径
|
116 | * @param {string/Buffer} data 上一个流结束时候的数据
|
117 | * @param {object} store 数据仓库 {_get, _set}
|
118 | * @return {string/Buffer} 将要设置的内容
|
119 | */
|
120 | onSet(pathname, data, store) {
|
121 | if (pathname.match(/\.md$/)) {
|
122 | let res = require('marked')(data.toString())
|
123 | // 在数据仓库中设置一个新的资源 .html
|
124 | store._set(pathname.replace(/\.md$/, '.html'), res)
|
125 | }
|
126 | },
|
127 | /**
|
128 | * 跟onSet类似, 开发环境下面,每次请求都会执行, 缩短server启动时间可以把onSet的逻辑扔这里
|
129 | */
|
130 | onGet(pathname, data, store) {},
|
131 | /**
|
132 | * 不希望影响构建的操作, 仅在server中触发, 不希望影响构建的操作(例: 自动更新脚本插入)
|
133 | */
|
134 | onText(pathname, data, req, resp, memory) {},
|
135 | buildFilter(pathname, data) {},
|
136 | outputFilter (pathname, data) {
|
137 | // .md 资源server环境可见, 但是不输出
|
138 | return !/\.md$/.test(pathname)
|
139 | }
|
140 | }
|
141 | },
|
142 | // lodash 模板引擎
|
143 | () => {
|
144 | const _ = require('lodash')
|
145 | return {
|
146 | // 中间件置顶位置 include 之后
|
147 | setBefore: 1,
|
148 | onSet (pathname, data) {
|
149 | // data 目录下面的文本资源需要经过模板引擎编译
|
150 | if (pathname.match(/^test\/.*.html/)) {
|
151 | let str = data.toString()
|
152 | try {
|
153 | str = _.template(str)({__dirname, require})
|
154 | } catch (e) {
|
155 | console.log(pathname, e)
|
156 | }
|
157 | return str
|
158 | }
|
159 | }
|
160 | }
|
161 | }
|
162 | ],
|
163 | /**
|
164 | * 只构建指定条件的资源
|
165 | * @param {string} pathname 资源路径名
|
166 | * @param {Buffer/string} data 资源内容
|
167 | * @return {Boolean}
|
168 | */
|
169 | buildFilter: (pathname, data) => {
|
170 | // 路径过滤
|
171 | let nameFilter = !pathname || /lib|test|index|README/.test(pathname)
|
172 | // 资源大小过滤
|
173 | let sizeFilter = !data || data.toString().length < 1024 * 1024
|
174 | return nameFilter && sizeFilter
|
175 | },
|
176 | /**
|
177 | * build 阶段是否使用 uglify/cleanCSS 进行 minify 操作
|
178 | * @param {string} pathname 资源路径名
|
179 | * @param {Buffer/string} data 资源内容
|
180 | * @return {Boolean}
|
181 | */
|
182 | shouldUseMinify: (pathname, data) => {
|
183 | let ok = data.toString().length < 1024 * 1024
|
184 | !ok && console.log('shouldNotUseMinify: ' + pathname)
|
185 | return ok
|
186 | },
|
187 | /**
|
188 | * 简单资源打包方案
|
189 | */
|
190 | bundles: [
|
191 | {
|
192 | /**
|
193 | * 满足当前正则匹配,则附加到 `pathname.replace(test, dist)` 资源
|
194 | * 1. dist路径必须能够匹配资源否则无效
|
195 | * 2. test匹配到的资源(除dist外), 不再输出
|
196 | */
|
197 | test: /bundle[\\/].*/,
|
198 | dist: 'test.js'
|
199 | }
|
200 | ],
|
201 | /**
|
202 | * app 默认时候 f2e 构建系统, 支持 'static' 纯静态服务器
|
203 | * 如果 app 自定义, 相当于只是使用 f2e 的端口开启和域名解析功能, 其他所有配置失效
|
204 | */
|
205 | // app: 'static',
|
206 | /**
|
207 | * 资源数据目录, 未设置的时候 build 中间件不开启
|
208 | * @type {local-url}
|
209 | */
|
210 | output: path.resolve(__dirname, '../output'),
|
211 | /**
|
212 | * after server create
|
213 | * you can render websocket server via this
|
214 | */
|
215 | onServerCreate: (server) => {
|
216 | const { Server } = require('ws')
|
217 | const wss = new Server({server});
|
218 | wss.on('connection', (socket) => {
|
219 | socket.send('init')
|
220 | })
|
221 | }
|
222 | }
|
223 |
|
224 | ```
|
225 |
|
226 | ### 中间件
|
227 | 参考 [f2e-middleware](https://github.com/shy2850/f2e-middleware)
|
228 |
|
229 | 1. lodash 模板引擎
|
230 | 2. markdown 编译
|
231 | 3. proxy 请求代理配置
|
232 | 4. dest 构建资源输出重命名
|
233 | 5. qrcode 简单二维码生成器
|
234 |
|
235 | ### app接入
|
236 | 支持接入 [Koa](http://koajs.com/) 以及 [express](https://expressjs.com/)
|
237 |
|
238 | ``` javascript
|
239 | const Koa = require('koa')
|
240 | const app = new Koa()
|
241 |
|
242 | app.use(ctx => {
|
243 | ctx.body = __dirname
|
244 | })
|
245 |
|
246 |
|
247 | const express = require('express')
|
248 | const app1 = express()
|
249 |
|
250 | app1.get('/', function (req, res) {
|
251 | res.send(__dirname)
|
252 | })
|
253 |
|
254 | app1.use(express.static('lib'))
|
255 |
|
256 | module.exports = {
|
257 | // app: app.callback(),
|
258 | // app: 'static', // 纯静态资源服务器
|
259 | app: app1
|
260 | }
|
261 | ```
|