{"id":13672851,"url":"https://github.com/open-wc/es-dev-server","last_synced_at":"2025-04-09T21:20:36.357Z","repository":{"id":53846346,"uuid":"305439433","full_name":"open-wc/es-dev-server","owner":"open-wc","description":null,"archived":false,"fork":false,"pushed_at":"2021-01-18T21:36:47.000Z","size":770,"stargazers_count":54,"open_issues_count":6,"forks_count":6,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-29T13:07:37.620Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/open-wc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-19T16:03:26.000Z","updated_at":"2024-12-16T09:47:45.000Z","dependencies_parsed_at":"2022-08-24T15:51:31.507Z","dependency_job_id":null,"html_url":"https://github.com/open-wc/es-dev-server","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-wc%2Fes-dev-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-wc%2Fes-dev-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-wc%2Fes-dev-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-wc%2Fes-dev-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/open-wc","download_url":"https://codeload.github.com/open-wc/es-dev-server/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247994120,"owners_count":21030050,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T09:01:52.083Z","updated_at":"2025-04-09T21:20:36.336Z","avatar_url":"https://github.com/open-wc.png","language":"TypeScript","readme":"---\npermalink: 'developing/es-dev-server.html'\ntitle: ES dev server\nsection: guides\ntags:\n  - guides\n---\n\n\u003e **Notice**\n\u003e\n\u003e Development of es-dev-server continues under a new name: [web dev server](https://modern-web.dev/docs/dev-server/overview/). We recommend using it for new projects, and upgrading for existing projects.\n\u003e\n\u003e We will continue to support fixing bugs for es-dev-server.\n\n# es dev server\n\nA web server for development without bundling.\n\n```bash\nnpx es-dev-server --node-resolve --watch\n```\n\n**Quick overview**\n\n- efficient browser caching for fast reloads\n- [transform code on older browsers for compatibility](#compatibility-mode)\n- [resolve bare module imports for use in the browser](#node-resolve) (`--node-resolve`)\n- auto-reload on file changes with the (`--watch`)\n- history API fallback for SPA routing (`--app-index index.html`)\n- [plugin API for extensions](#plugins)\n\n[See all commands](#command-line-flags-and-configuration)\n\n## Getting started\n\nWe recommend [following this guide](https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5) for a step by step overview of different workflows with `es-dev-server`.\n\n## Setup\n\n```bash\nnpm i --save-dev es-dev-server\n```\n\nAdd scripts to your `package.json`, modify the flags as needed:\n\n```json\n{\n  \"scripts\": {\n    \"start\": \"es-dev-server --app-index index.html --node-resolve --watch --open\"\n  }\n}\n```\n\nRun the server:\n\n```bash\nnpm run start\n```\n\n## Node version\n\nes-dev-server requires node v10 or higher\n\n## Command-line flags and Configuration\n\n### Server configuration\n\n| name      | type           | description                                                             |\n| --------- | -------------- | ----------------------------------------------------------------------- |\n| port      | number         | The port to use, uses a random free port if not set.                    |\n| hostname  | string         | The hostname to use. Default: localhost                                 |\n| open      | boolean/string | Opens the browser on app-index, root dir or a custom path               |\n| app-index | string         | The app's index.html file, sets up history API fallback for SPA routing |\n| root-dir  | string         | The root directory to serve files from. Default: working directory      |\n| base-path | string         | Base path the app is served on. Example: /my-app                        |\n| config    | string         | The file to read configuration from (JS or JSON)                        |\n| cors      | boolean        | Enable CORS                                                             |\n| help      | none           | See all options                                                         |\n\n### Development help\n\n| name  | type    | description                                                         |\n| ----- | ------- | ------------------------------------------------------------------- |\n| watch | boolean | Reload the browser when files are edited                            |\n| http2 | boolean | Serve files over HTTP2. Sets up HTTPS with self-signed certificates |\n\n### Code transformation\n\n| name                 | type          | description                                                                                                                     |\n| -------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| compatibility        | string        | Compatibility mode for older browsers. Can be: `auto`, `always`, `min`, `max` or `none` Default `auto`                          |\n| node-resolve         | boolean       | Resolve bare import imports using node resolve                                                                                  |\n| dedupe               | boolean/array | Deduplicates all modules, or modules from specified packages if the value is an array                                           |\n| preserve-symlinks    | boolean       | Preserve symlinks when resolving modules. Set to true, if using tools that rely on symlinks, such as `npm link`. Default false. |\n| module-dirs          | string/array  | Directories to resolve modules from. Used by node-resolve                                                                       |\n| babel                | boolean       | Transform served code through babel. Requires .babelrc                                                                          |\n| file-extensions      | string/array  | Extra file extensions to use when transforming code.                                                                            |\n| babel-exclude        | number/array  | Patterns of files to exclude from babel compilation.                                                                            |\n| babel-modern-exclude | number/array  | Patterns of files to exclude from babel compilation on modern browsers.                                                         |\n| babel-module-exclude | number/array  | Patterns of files to exclude from babel compilation for modules only.                                                           |\n| event-stream         | boolean       | Whether to inject event stream script. Defaults to true.                                                                        |\n\nMost commands have an alias/shorthand. You can view them by using `--help`.\n\n### Configuration files\n\nWe 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.\n\nConfiguration options are the same as command line flags, using their camelCased names. Example:\n\n```javascript\nmodule.exports = {\n  port: 8080,\n  watch: true,\n  nodeResolve: true,\n  appIndex: 'demo/index.html',\n  plugins: [],\n  moduleDirs: ['node_modules', 'web_modules'],\n};\n```\n\nIn addition to the command-line flags, the configuration file accepts these additional options:\n\n| name            | type    | description                                               |\n| --------------- | ------- | --------------------------------------------------------- |\n| middlewares     | array   | Koa middlewares to add to the server. (read more below)   |\n| plugins         | array   | Plugins to add to the server. (read more below)           |\n| babelConfig     | object  | Babel config to run with the server.                      |\n| polyfillsLoader | object  | Configuration for the polyfills loader. (read more below) |\n| debug           | boolean | Whether to turn on debug mode on the server.              |\n\n## Serving files\n\nes-dev-server is a static web server. When a request is made from the browser for `/foo/bar.js` it will try and find this file from the root directory. It cannot serve any files outside of your root directory because the browser has no way to request them, and the path on the file system must always be reflected in the path of the browser.\n\n### index.html in the root\n\nThe simplest setup, making sure that all files are accessible, is to place your index.html at the root of your project\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nConsider this example directory structure:\n\n```\nnode_modules/...\nsrc/...\nindex.html\n```\n\nIf you run the `es-dev-server` command from the root of the project, you can access your app at `/` or `/index.html` in the browser.\n\n\u003c/details\u003e\n\n### index.html in a folder\n\nIf you move your `index.html` outside the root of your project, you have some different options.\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nUse the `--open` parameter for when you'd like to keep you index.html in a subfolder.\n\n```\nnode_modules/...\nsrc/...\nsrc/index.html\n```\n\nYou can access your app in the browser at `/src/` or `/src/index.html`. You can tell es-dev-server to explicitly open at this path:\n\n```bash\n# with app-index flag\nes-dev-server --app-index src/index.html --open\n# without app-index flag\nes-dev-server --open src/\n```\n\nYou can also change the root directory of the dev server:\n\n```bash\nes-dev-server --root-dir src --open\n```\n\nNow your `index.html` is accessible at `/` or `/index.html`. However, the dev server cannot serve any files outside of the root directory. So if your app uses any node modules, they will no longer because accessible.\n\nIf you want your index in a subfolder without this being visible in the browser URL, you can set up a file rewrite rule. [Read more here](#rewriting-request-urls)\n\n\u003c/details\u003e\n\n### Monorepos\n\nIf you are using es-dev-server in a monorepo, your node modules are in two different locations. In a package's folder and at the repository root. You need to make sure that es-dev-server can serve from both directories.\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nFor example, a typical monorepo setup looks like this:\n\n```\nnode_modules/...\npackages/my-package/node_modules/...\npackages/my-package/index.html\n```\n\nYou will need to make sure the root node_modules folder is accessible to the dev server.\n\nIf your working directory is `packages/my-package` you can use this command:\n\n```bash\n# with app-index (for SPA)\nes-dev-server --root-dir ../../ --app-index packages/my-package/index.html --open\n# without app-index\nes-dev-server --root-dir ../../ --open packages/my-package/index.html\n```\n\nIf your working directory is the root of the repository you can use this command:\n\n```bash\n# with app index (for SPA)\nes-dev-server --app-index packages/my-package/index.html --open\n# without app index\nes-dev-server --open packages/my-package/index.html\n```\n\nThis is the same approach as serving an index.html in a subdirectory, so the section above applies here as well.\n\n\u003c/details\u003e\n\n### Base Element\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nIf you are building a single page application with client-side routing, we recommend adding a base element to set the base URL of your document.\n\nThe base URL of the document can be accessed through `document.baseURI` and is used by the browser to resolve relative paths (anchors, images, links, scripts, etc.). By default, it is set to the browser's URL.\n\nYou can add `\u003cbase href=\"\"\u003e` element to modify how files are resolved relatively to your index.html. This can be very useful when your index.html is not at the root of your project.\n\n[Read more about this on MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)\n\n\u003c/details\u003e\n\n## Node resolve\n\n\"Bare imports\" are imports which don't specify a full path to a file:\n\n```js\nimport foo from 'bar';\n```\n\nThe browser doesn't know where to find this file called `bar`. The `--node-resolve` flag resolves this bare import to the actual file path before serving it to the browser:\n\n```js\nimport foo from './node_modules/bar/bar.js';\n```\n\nBecause we use [es-module-lexer](https://github.com/guybedford/es-module-lexer) for blazing fast analysis to find the imports in a file without booting up a full-blown parser like babel, we can do this without a noticeable impact on performance.\n\nFor the actual resolve logic, we internally use [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve) so that you can keep the resolve logic in sync between development and production. When using a config file, the `nodeResolve` can also be an object which accepts the same options as the rollup plugin. options.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample config\u003c/summary\u003e\n\nSee [the rollup docs](https://github.com/rollup/plugins/tree/master/packages/node-resolve) for all options and what they do.\n\nSome options like `dedupe`, `fileExtensions`, `preserveSymlinks` and `moduleDirs` are mapped to options for `nodeResolve` internally. You can overwrite them with your custom config.\n\n```js\nmodule.exports = {\n  nodeResolve: {\n    jsnext: true,\n    browser: true,\n    // set default to false because es-dev-server always\n    // runs in the browser\n    preferBuiltins: true,\n    // will overwrite es-dev-server's fileExtensions option\n    extensions: ['.mjs', '.js'],\n    // will overwrite es-dev-server's dedupe option\n    dedupe: ['lit-html'],\n    customResolveOptions: {\n      // will overwrite es-dev-server's moduleDirs option\n      moduleDirectory: ['node_modules'],\n      preserveSymlinks: true,\n    },\n  },\n};\n```\n\n\u003c/details\u003e\n\nIn the future, we are hoping that [import maps](https://github.com/WICG/import-maps) will make this step unnecessary.\n\n## Middleware\n\nYou can add your own middleware to es-dev-server using the `middlewares` property. The middleware should be a standard koa middleware. [Read more about koa here.](https://koajs.com/)\n\nYou can use middleware to modify respond to any request from the browser, for example to rewrite a URL or proxy to another server. For serving or manipulating files it's recommended to use plugins.\n\n### Proxying requests\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\n```javascript\nconst proxy = require('koa-proxies');\n\nmodule.exports = {\n  port: 9000,\n  middlewares: [\n    proxy('/api', {\n      target: 'http://localhost:9001',\n    }),\n  ],\n};\n```\n\n\u003c/details\u003e\n\n### Rewriting request urls\n\nYou can rewrite certain file requests using a simple middleware. This can be useful for example to serve your `index.html` from a different file location or to alias a module.\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nServe `/index.html` from `/src/index.html`:\n\n```javascript\nmodule.exports = {\n  middlewares: [\n    function rewriteIndex(context, next) {\n      if (context.url === '/' || context.url === '/index.html') {\n        context.url = '/src/index.html';\n      }\n\n      return next();\n    },\n  ],\n};\n```\n\n\u003c/details\u003e\n\n## Plugins\n\nPlugins are objects with lifecycle hooks called by es-dev-server as it serves files to the browser. They can be used to serve virtual files, transform files, or resolve module imports.\n\n### Adding plugins\n\nA plugin is just an object that you add to the `plugins` array in your configuration file. You can add an object directly, or create one from a function somewhere:\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\n```js\nconst awesomePlugin = require('awesome-plugin');\n\nmodule.exports = {\n  plugins: [\n    // use a plugin\n    awesomePlugin({ someOption: 'someProperty' }),\n    // create an inline plugin\n    {\n      transform(context) {\n        if (context.response.is('html')) {\n          return { body: context.body.replace(/\u003cbase href=\".*\"\u003e/, '\u003cbase href=\"/foo/\"\u003e') };\n        }\n      },\n    },\n  ],\n};\n```\n\n\u003c/details\u003e\n\nSee the full type interface for all options:\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\n```ts\nimport Koa, { Context } from 'koa';\nimport { FSWatcher } from 'chokidar';\nimport { Server } from 'net';\nimport { ParsedConfig } from './config';\n\ntype ServeResult = void | { body: string; type?: string; headers?: Record\u003cstring, string\u003e };\ntype TransformResult = void | { body?: string; headers?: Record\u003cstring, string\u003e };\ntype ResolveResult = void | string | Promise\u003cvoid\u003e | Promise\u003cstring\u003e;\n\ninterface ServerArgs {\n  config: ParsedConfig;\n  app: Koa;\n  server: Server;\n  fileWatcher: FSWatcher;\n}\n\nexport interface Plugin {\n  serverStart?(args: ServerArgs): void | Promise\u003cvoid\u003e;\n  serve?(context: Context): ServeResult | Promise\u003cServeResult\u003e;\n  transform?(context: Context): TransformResult | Promise\u003cTransformResult\u003e;\n  resolveImport?(args: { source: string; context: Context }): ResolveResult;\n  resolveMimeType?(context: Context): undefined | string | Promise\u003cundefined | string\u003e;\n}\n```\n\n\u003c/details\u003e\n\n### Hook: serve\n\nThe serve hook can be used to serve virtual files from the server. The first plugin to respond with a body is used. It can return a Promise.\n\n\u003cdetails\u003e\n\u003csummary\u003eRead more\u003c/summary\u003e\n\nServe an auto generated `index.html`:\n\n```js\nconst indexHTML = generateIndexHTML();\n\nmodule.exports = {\n  plugins: [\n    {\n      serve(context) {\n        if (context.path === '/index.html') {\n          return { body: indexHTML };\n        }\n      },\n    },\n  ],\n};\n```\n\nServe a virtual module:\n\n```js\nconst indexHTML = generateIndexHTML();\n\nmodule.exports = {\n  plugins: [\n    {\n      serve(context) {\n        if (context.path === '/messages.js') {\n          return { body: 'export default \"Hello world\";' };\n        }\n      },\n    },\n  ],\n};\n```\n\nThe file extension is used to infer the mime type to respond with. If you are using a non-standard file extension you can use the `type` property to set it explicitly:\n\n```js\nmodule.exports = {\n  plugins: [\n    {\n      serve(context) {\n        if (context.path === '/foo.xyz') {\n          return { body: 'console.log(\"foo bar\");', type: 'js' };\n        }\n      },\n    },\n  ],\n};\n```\n\n\u003c/details\u003e\n\n### Hook: resolveMimeType\n\nBrowsers don't use file extensions to know how to interpret files. Instead, they use [media or MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) which is set using the `content-type` header.\n\nes-dev-server guesses the MIME type based on the file extension. When serving virtual files with non-standard file extensions, you can set the MIME type in the returned result (see the examples above). If you are transforming code from one format to another, you need to use the `resolveMimeType` hook.\n\n\u003cdetails\u003e\n\u003csummary\u003eRead more\u003c/summary\u003e\n\nThe returned MIME type can be a file extension, this will be used to set the corresponding default MIME type. For example `js` resolves to `application/javascript` and `css` to `text/css`.\n\n```js\nmodule.exports = {\n  plugins: [\n    {\n      resolveMimeType(context) {\n        // change all MD files to HTML\n        if (context.response.is('md')) {\n          return 'html';\n        }\n      },\n    },\n    {\n      resolveMimeType(context) {\n        // change all CSS files to JS, except for a specific file\n        if (context.response.is('css') \u0026\u0026 context.path !== '/global.css') {\n          return 'js';\n        }\n      },\n    },\n  ],\n};\n```\n\nIt is also possible to set the full mime type directly:\n\n```js\nmodule.exports = {\n  plugins: [\n    {\n      resolveMimeType(context) {\n        if (context.response.is('md')) {\n          return 'text/html';\n        }\n      },\n    },\n  ],\n};\n```\n\n\u003c/details\u003e\n\n### Hook: transform\n\nThe transform hook is called for each file and can be used to transform a file. Multiple plugins can transform a single file. It can return a Promise.\n\nThis hook is useful for small modifications, such as injecting environment variables, or for compiling files to JS before serving them to the browser.\n\nIf you are transforming non-standard file types, you may also need to include a `resolveMimeType` hook.\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nRewrite the base path of your application for local development;\n\n```js\nmodule.exports = {\n  plugins: [\n    {\n      transform(context) {\n        if (context.path === '/index.html') {\n          const transformedBody = context.body.replace(/\u003cbase href=\".*\"\u003e/, '\u003cbase href=\"/foo/\"\u003e');\n          return { body: transformedBody };\n        }\n      },\n    },\n  ],\n};\n```\n\nInject a script to set global variables during local development:\n\n```js\nmodule.exports = {\n  plugins: [\n    {\n      transform(context) {\n        if (context.path === '/index.html') {\n          const transformedBody = context.body.replace(\n            '\u003c/head\u003e',\n            '\u003cscript\u003ewindow.process = { env: { NODE_ENV: \"development\" } }\u003c/script\u003e\u003c/head\u003e',\n          );\n          return { body: transformedBody };\n        }\n      },\n    },\n  ],\n};\n```\n\nInject environment variables into a JS module:\n\n```js\nconst packageJson = require('./package.json');\n\nmodule.exports = {\n  plugins: [\n    {\n      transform(context) {\n        if (context.path === '/src/environment.js') {\n          return { body: `export const version = '${packageJson.version}';` };\n        }\n      },\n    },\n  ],\n};\n```\n\nTransform markdown to HTML:\n\n```js\nconst markdownToHTML = require('markdown-to-html-library');\n\nmodule.exports = {\n  plugins: [\n    {\n      resolveMimeType(context) {\n        // this ensures the browser interprets .md files as .html\n        if (context.path.endsWith('.md')) {\n          return 'html';\n        }\n      },\n\n      async transform(context) {\n        // this will transform all MD files. if you only want to transform certain MD files\n        // you can check context.path\n        if (context.path.endsWith('.md')) {\n          const html = await markdownToHTML(body);\n\n          return { body: html };\n        }\n      },\n    },\n  ],\n};\n```\n\nPolyfill CSS modules in JS:\n\n```js\nmodule.exports = {\n  plugins: [\n    {\n      resolveMimeType(context) {\n        if (context.path.endsWith('.css')) {\n          return 'js';\n        }\n      },\n\n      async transform(context) {\n        if (context.path.endsWith('.css')) {\n          const stylesheet = `\n            const stylesheet = new CSSStyleSheet();\n            stylesheet.replaceSync(${JSON.stringify(body)});\n            export default stylesheet;\n          `;\n\n          return { body: stylesheet };\n        }\n      },\n    },\n  ],\n};\n```\n\n\u003c/details\u003e\n\n### Hook: resolveImport\n\nThe `resolveImport` hook is called for each module import. It can be used to resolve module imports before they reach the browser.\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nes-dev-server already resolves module imports when the `--node-resolve` flag is turned on. You can do the resolving yourself, or overwrite it for some files.\n\nThe hook receives the import string and should return the string to replace it with. This should be a browser-compatible path, not a file path.\n\n```js\nmodule.exports = {\n  plugins: [\n    {\n      async resolveImport({ source, context }) {\n        const resolvedImport = fancyResolveLibrary(source);\n        return resolvedImport;\n      },\n    },\n  ],\n};\n```\n\n\u003c/details\u003e\n\n### Hook: serverStart\n\nThe `serverStart` hook is called when the server starts. It is the ideal location to boot up other servers you will proxy to. It receives the server config, which you can use if plugins need access to general information such as the `rootDir` or `appIndex`. It also receives the HTTP server, Koa app, and `chokidar` file watcher instance. These can be used for more advanced plugins. This hook can be async, and it awaited before actually booting the server and opening the browser.\n\n\u003cdetails\u003e\n\u003csummary\u003eRead more\u003c/summary\u003e\n\nAccessing the serverStart parameters:\n\n```js\nfunction myFancyPlugin() {\n  let rootDir;\n\n  return {\n    serverStart({ config, app, server, fileWatcher }) {\n      // take the rootDir to access it later\n      rootDir = config.rootDir;\n\n      // register a koa middleware directly\n      app.use((context, next) =\u003e {\n        console.log(context.path);\n        return next();\n      });\n\n      // register a file to be watched\n      fileWatcher.add('/foo.md');\n    },\n  };\n}\n\nmodule.exports = {\n  plugins: [myFancyPlugin()],\n};\n```\n\nBoot up another server for proxying in serverStart:\n\n```js\nconst proxy = require('koa-proxies');\n\nmodule.exports = {\n  plugins: [\n    {\n      async serverStart({ app }) {\n        // set up a proxy for certain requests\n        app.use(\n          proxy('/api', {\n            target: 'http://localhost:9001',\n          }),\n        );\n\n        // boot up the other server, because it is awaited es-dev-server will also wait for it\n        await startOtherServer({ port: 9001 });\n      },\n    },\n  ],\n};\n```\n\n\u003c/details\u003e\n\n### Koa Context\n\nThe plugin hooks simply receive the Koa `Context` object. This contains information about the server's request and response. Check the [Koa documentation](https://koajs.com/) to learn more about this.\n\nTo transform specific kinds of files we don't recommend relying on file extensions. Other plugins may be using non-standard file extensions. Instead, you should use the server's MIME type or content-type header. You can easily check this using the `context.response.is()` function. This is used a lot in the examples above.\n\nBecause files can be requested with query parameters and hashes, we recommend using `context.path` for reading the path segment of the URL only. If you do need to access search parameters, we recommend using `context.URL.searchParams.get('my-parameter')`.\n\n## Order of execution\n\nThe order of execution for the es-dev-server when a file request is received:\n\n1. User middleware: before \"next\"\n2. Serve\n   - Plugins: serve\n   - es-dev-server: static file middleware (if no plugin match)\n3. Plugins: resolveMimeType\n4. Plugins: transform\n5. Resolve module imports\n   - Plugins: resolveModuleImport\n   - es-dev-server: node-resolve (if no plugin resolve)\n6. es-dev-server: babel + compatibility transforms\n7. es-dev-server: response cache (caches all JS files served, including plugin transforms)\n8. User middleware: after \"next\"\n\n## Typescript support\n\nBecause es-dev-server doesn't do any bundling, it's easy to integrate it with typescript and doesn't require any extra tooling or plugins. Just run `tsc` on your code, and serve the compiled output with es-dev-server. You can run both `tsc` and es-dev-server in watch mode, changes will be picked up automatically.\n\nMake sure to configure `tsc` to output real ES modules.\n\n## Compatibility mode\n\nCompatibility mode enables bundle-free development using modern browsers features on older browsers. Automatic compatibility mode is enabled by default.\n\n\u003cdetails\u003e\n\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nCompatibility mode can be configured using the `--compatibility` flag. The possible options are `auto`, `min`, `max`, and `none`. The default is mode is `auto`.\n\n**auto**\n`auto` compatibility looks at the current browser to determine the level of compatibility to enable. On the latest 2 versions of the major browsers, it doesn't do any work. This keeps the server as fast as possible in the general case.\n\nOn older browsers, the server uses the browser's user agent and [@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env) to do a targeted transformation for that specific browser and version. `@babel/preset-env` only works with stage 4 javascript features, they should become an official standard before they can be used.\n\nIf the browser does not support es module scripts, dynamic imports or `import.meta.url` es modules are transformed to [system-js](https://github.com/systemjs/systemjs).\n\nThis works down to at least IE11. Depending on what browser features you are using, it might work with an earlier version too but this is not tested.\n\n**always**\n`always` compatibility is the same as `auto`, except that it doesn't skip compiling on the latest 2 versions of the major browsers. This makes it a bit slower on modern browsers but allows you to use new features before they are implemented in the browser.\n\n**min**\n`min` compatibility forces the same level of compatibility on all browsers. It makes code compatible with the latest two versions of the major browsers and does not transform es modules.\n\n**max**\n`max` compatibility forces the same level of compatibility on all browsers. It compiles everything to es5 and [system-js](https://github.com/systemjs/systemjs).\n\n**none**\n`none` disables compatibility mode entirely.\n\n\u003c/details\u003e\n\n### Polyfills loader\n\nWhen compatibility mode is enabled, polyfills are loaded using [polyfills-loader](https://github.com/open-wc/open-wc/tree/master/packages/polyfills-loader).\n\n\u003cdetails\u003e\n  \u003csummary\u003eRead more\u003c/summary\u003e\n\nYou can customize the polyfill loader configuration from your configuration file. Check the docs for the [polyfills-loader](https://github.com/open-wc/open-wc/tree/master/packages/polyfills-loader) for all possible options.\n\n```js\nmodule.exports = {\n  polyfillsLoader: {\n    polyfills: {\n      fetch: false,\n      custom: [\n        {\n          name: 'my-feature-polyfill',\n          path: require.resolve('my-feature-polyfill'),\n          test: \"!('myFeature' in window)\",\n        },\n      ],\n    },\n  },\n};\n```\n\nBy default, es-dev-server wraps all scripts and are deferred until polyfills are loaded. Loading order of scripts are preserved, but this can create problems if you rely on a script being executed before HTML is parsed. You can configure `es-dev-server` to exclude certain types of scripts:\n\n```js\nmodule.exports = {\n  polyfillsLoader: {\n    exclude: {\n      jsModules: true,\n      inlineJsModules: true,\n      jsScripts: true,\n      inlineJsScripts: true,\n    },\n  },\n};\n```\n\n\u003c/details\u003e\n\n## Using es-dev-server programmatically\n\nYou can use different components from `es-dev-server` as a library and integrate it with other tools:\n\n\u003cdetails\u003e\n\n\u003csummary\u003eRead more\u003c/summary\u003e\n\n### createConfig\n\nWhen 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.\n\nBy default, all options besides static file serving are turned off, so it's easy to configure based on your requirements.\n\nThe config structure is the same as the configuration explained in the [configuration files section](#configuration-files)\n\n```javascript\nimport { createConfig } from 'es-dev-server';\n\nconst config = createConfig({\n  http2: true,\n  babel: true,\n  open: true,\n});\n```\n\n### createMiddlewares\n\n`createMiddlewares` creates the dev server's middlewares based on your configuration. You can use this to hook them up to your koa server.\n\nReturns an array of koa middleware functions.\n\n```javascript\nimport Koa from 'koa';\nimport { createConfig, createMiddlewares } from 'es-dev-server';\n\nconst config = createConfig({});\nconst middlewares = createMiddlewares(config);\n\nconst app = new Koa();\nmiddlewares.forEach(middleware =\u003e {\n  app.use(middleware);\n});\n```\n\n### createServer\n\n`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.\n\nReturns the koa app and a node http or http2 server.\n\n```javascript\nimport Koa from 'koa';\nimport { createConfig, createServer } from 'es-dev-server';\n\nconst config = createConfig({ ... });\nconst { app, server } = createServer(config);\nserver.listen(3000);\n```\n\n### watch mode\n\n`createMiddlewares` and `createServer` requires a chokidar fileWatcher if watch mode is enabled. You need to pass this separately because the watcher nees-dev-server to be killed explicitly when the server closes.\n\n```javascript\nimport Koa from 'koa';\nimport chokidar from 'chokidar';\nimport { createConfig, createMiddlewares, createServer } from 'es-dev-server';\n\nconst config = createConfig({ ... });\nconst fileWatcher = chokidar.watch([]);\n\n// if using createMiddlewares\ncreateMiddlewares(config, fileWatcher);\n// if using createServer\ncreateServer(config, fileWatcher);\n\n// close filewatcher when no longer necessary\nfileWatcher.close();\n```\n\n### startServer\n\n`startServer` asynchronously creates and starts the server, listening on the configured port. It opens the browser if configured and logs a startup message.\n\nReturns the koa app and a node http or http2 server.\n\n```javascript\nimport Koa from 'koa';\nimport { createConfig, startServer } from 'es-dev-server';\n\nasync function main() {\n  const config = createConfig({ ... });\n  const { app, server } = await startServer(config, fileWatcher);\n}\n\nmain();\n```\n\n\u003c/details\u003e\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-wc%2Fes-dev-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopen-wc%2Fes-dev-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-wc%2Fes-dev-server/lists"}