Skip to content

Commit 89c3d4d

Browse files
LarsDenBakkerdaKmoR
authored andcommitted
feat(es-dev-server): allow using the server as a library
1 parent a9fcc31 commit 89c3d4d

23 files changed

+794
-522
lines changed

packages/es-dev-server/README.md

Lines changed: 114 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Click read more to view different strategies for setting up your project's folde
113113
If you run `es-dev-server` regularly from the root of this project, you can access your app at `/` or `/index.html` in the browser.
114114

115115
### index.html in a subfoolder
116-
If you move your `index.html` in inside a subfolder:
116+
If you move your `index.html` inside a subfolder:
117117
```
118118
node_modules/...
119119
src/...
@@ -173,7 +173,7 @@ Click read more to view different strategies for setting up your project's folde
173173

174174
</details>
175175

176-
## Command line flags overview
176+
## Command line flags and Configuration
177177
### Server configuration
178178
| name | type | description |
179179
| -------------------- | -------------- | ------------------------------------------------------------------------ |
@@ -194,8 +194,8 @@ Click read more to view different strategies for setting up your project's folde
194194
### Code transformation
195195
| name | type | description |
196196
| -------------------- | -------------- | ------------------------------------------------------------------------ |
197-
| node-resolve | number | Resolve bare import imports using node resolve |
198197
| compatibility | string | Compatibility mode for older browsers. Can be: `esm`, `modern` or `all` |
198+
| node-resolve | number | Resolve bare import imports using node resolve |
199199
| module-dirs | string/array | Directories to resolve modules from. Used by node-resolve |
200200
| babel | number | Transform served code through babel. Requires .babelrc |
201201
| file-extensions | number/array | Extra file extentions to use when transforming code. |
@@ -204,26 +204,32 @@ Click read more to view different strategies for setting up your project's folde
204204

205205
Most commands have an alias/shorthand. You can view them by using `--help`.
206206

207-
## Advanced usage
208-
209207
### Configuration files
210208
We pick up an `es-dev-server.config.js` file automatically if it is present in the current working directory. You can specify a custom config path using the `config` flag.
211-
<details>
212-
<summary>Read more</summary>
213209

214-
The configuration file allows the same command line flags configured, using their camelCased names. Example:
215-
```javascript
216-
module.exports = {
217-
port: 8080,
218-
appIndex: 'demo/index.html',
219-
moduleDirs: ['node_modules', 'custom-modules']
220-
}
210+
Configuration options are the same as command line flags, using their camelCased names. Example:
211+
```javascript
212+
module.exports = {
213+
port: 8080,
214+
watch: true,
215+
nodeResolve: true,
216+
appIndex: 'demo/index.html',
217+
moduleDirs: ['node_modules', 'custom-modules']
218+
}
221219
```
222-
</details>
220+
221+
In addition to the command line flags, the configuration file accepts these additional options:
222+
223+
| name | type | description |
224+
| -------------------- | -------------- | ------------------------------------------------------------------------ |
225+
| middlewares | array | Koa middlewares to add to the server, read more below. |
226+
| babelConfig | object | Babel config to run with the server |
227+
228+
## Advanced usage
223229

224230
### Custom middlewares / proxy
225231

226-
You can install custom middlewares, using the `customMiddlewares` property.
232+
You can install custom middlewares, using the `middlewares` property.
227233

228234
<details>
229235
<summary>Read more</summary>
@@ -236,7 +242,7 @@ You can install custom middlewares, using the `customMiddlewares` property.
236242

237243
module.exports = {
238244
port: 9000,
239-
customMiddlewares: [
245+
middlewares: [
240246
proxy('/api', {
241247
target: 'http://localhost:9001',
242248
})
@@ -256,7 +262,7 @@ You can rewrite certain file requests using a simple custom middleware. This can
256262

257263
```javascript
258264
module.exports = {
259-
customMiddlewares: [
265+
middlewares: [
260266
function rewriteIndex(context, next) {
261267
if (context.url === '/' || context.url === '/index.html') {
262268
context.url = '/src/index.html';
@@ -374,6 +380,96 @@ Compatibility mode enables bundle-free development with features such as es modu
374380

375381
</details>
376382

383+
## Using es-dev-server programmatically
384+
You can use different components from `es-dev-server` as a library and integrate it with other tools:
385+
386+
<details>
387+
388+
<summary>Read more</summary>
389+
390+
### createConfig
391+
When using the server from javascript you are going to need a config object to tell the server what options to turn on and off. It's best to use `createConfig` for this as this converts the public API to an internal config structure and sets up default values.
392+
393+
By default all options besides static file serving is turned off, so it's easy to configure based on your own requirements.
394+
395+
The config structure is the same as the configuration explained in the [configuration files section](#configuration-files)
396+
397+
```javascript
398+
import { createConfig } from 'es-dev-server';
399+
400+
const config = createConfig({
401+
http2: true,
402+
babel: true,
403+
open: true,
404+
});
405+
```
406+
407+
### createMiddlewares
408+
`createMiddlewares` creates the dev server's middlewares based on your configuration. You can use this to hook them up to your own koa server.
409+
410+
Returns an array of koa middleware functions.
411+
412+
```javascript
413+
import Koa from 'koa';
414+
import { createConfig, createMiddlewares } from 'es-dev-server';
415+
416+
const config = createConfig({ });
417+
const middlewares = createMiddlewares(config);
418+
419+
const app = new Koa();
420+
middlewares.forEach(middleware => {
421+
app.use(middleware);
422+
});
423+
```
424+
425+
### createServer
426+
`createServer` creates an instance of the dev server including all middlewares, but without starting the server. This is useful if you want to be in control of starting the server yourself.
427+
428+
Returns the koa app and a node http or http2 server.
429+
430+
```javascript
431+
import Koa from 'koa';
432+
import { createConfig, createServer } from 'es-dev-server';
433+
434+
const config = createConfig({ ... });
435+
const { app, server } = createServer(config);
436+
server.listen(3000);
437+
```
438+
439+
### watch mode
440+
`createMiddlewares` and `createServer` requires a chokidar fileWatcher if watch mode is enabled. You need to pass this separately because the watcher needs to be killed explicitly when the server closes.
441+
442+
```javascript
443+
import Koa from 'koa';
444+
import chokidar from 'chokidar';
445+
import { createConfig, createMiddlewares, createServer } from 'es-dev-server';
446+
447+
const config = createConfig({ ... });
448+
const fileWatcher = chokidar.watch([]);
449+
450+
// if using createMiddlewares
451+
createMiddlewares(config, fileWatcher);
452+
// if using createServer
453+
createServer(config, fileWatcher);
454+
455+
// close filewatcher when no longer necessary
456+
fileWatcher.close();
457+
```
458+
459+
### startServer
460+
`startServer` creates and starts the server, listening on the configured port. It opens the browser if configured and logs a startup message.
461+
462+
Returns the koa app and a node http or http2 server.
463+
464+
```javascript
465+
import Koa from 'koa';
466+
import { createConfig, startServer } from 'es-dev-server';
467+
468+
const config = createConfig({ ... });
469+
const { app, server } = startServer(config, fileWatcher);
470+
```
471+
472+
</details>
377473

378474
<script>
379475
export default {

packages/es-dev-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "es-dev-server",
3-
"main": "./dist/cli.js",
3+
"main": "./dist/es-dev-server.js",
44
"version": "1.7.2",
55
"description": "Development server for modern web apps",
66
"author": "open-wc",

packages/es-dev-server/src/cli.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#!/usr/bin/env node
2-
import { startServer } from './server.js';
3-
import readCommandLineArgs from './command-line-args.js';
4-
5-
const config = readCommandLineArgs();
2+
import { startServer, createConfig } from './es-dev-server.js';
3+
import { readCommandLineArgs } from './command-line-args.js';
64

5+
const config = createConfig(readCommandLineArgs());
76
startServer(config);

packages/es-dev-server/src/command-line-args.js

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ import camelcase from 'camelcase';
66
import fs from 'fs';
77
import deepmerge from 'deepmerge';
88

9-
export default function readCommandLineArgs(argv = process.argv) {
9+
/**
10+
* Reads command line args from arguments array. Defaults to `process.argv`.
11+
*
12+
* @param {string[]} [argv]
13+
* @returns {import('./config.js').Config}
14+
*/
15+
export function readCommandLineArgs(argv = process.argv) {
1016
const optionDefinitions = [
1117
{
1218
name: 'config',
@@ -110,6 +116,7 @@ export default function readCommandLineArgs(argv = process.argv) {
110116
];
111117

112118
const dashesArgs = commandLineArgs(optionDefinitions, { argv, partial: true });
119+
const openInCommandLineArgs = 'open' in dashesArgs;
113120

114121
// convert kebab-case to camelCase
115122
/** @type {object} */
@@ -125,7 +132,7 @@ export default function readCommandLineArgs(argv = process.argv) {
125132
{
126133
header: 'es-dev-server',
127134
content: `
128-
Simply server with node resolution for bare modules.
135+
A dev server for modern web development workflows.
129136
130137
Usage: \`es-dev-server [options...]\`
131138
`.trim(),
@@ -162,42 +169,18 @@ export default function readCommandLineArgs(argv = process.argv) {
162169
}
163170
}
164171

165-
// open if the open option is given, even when there were no arguments
166-
const openBrowser = 'open' in options;
167-
let openPath;
168-
if (typeof options.open === 'string' && options.open !== '') {
169-
// user-provided open path
170-
openPath = path.normalize(options.open);
171-
} else if (options.appIndex) {
172-
// if an appIndex was provided, use it's directory as open path
173-
openPath = `${path.parse(options.appIndex).dir}/`;
174-
} else {
175-
// default to working directory root
176-
openPath = '/';
177-
}
172+
let { open } = options;
178173

179-
// make sure path properly starts a /
180-
if (!openPath.startsWith('/')) {
181-
openPath = `/${openPath}`;
174+
// open can be a boolean nor a string. if it's a boolean from command line args,
175+
// it's passed as null. so if it's not a string or boolean type, we check for the
176+
// existence of open in the args object, and treat that as true
177+
if (typeof open !== 'string' && typeof open !== 'boolean' && openInCommandLineArgs) {
178+
open = true;
182179
}
183180

184181
return {
185-
openBrowser,
186-
openPath,
187-
port: options.port,
188-
hostname: options.hostname,
189-
watch: options.watch,
190-
rootDir: options.rootDir,
191-
appIndex: options.appIndex,
192-
nodeResolve: options.nodeResolve,
193-
http2: options.http2,
194-
compatibilityMode: options.compatibility,
195-
readUserBabelConfig: options.babel,
196-
customMiddlewares: options.customMiddlewares,
197-
extraFileExtensions: options.fileExtensions,
198-
moduleDirectories: options.moduleDirs,
199-
babelExclude: options.babelExclude,
200-
babelModernExclude: options.babelModernExclude,
182+
...options,
183+
open,
201184
logStartup: true,
202185
};
203186
}

0 commit comments

Comments
 (0)