Implement a Basic Vue Dev Server
This challenge focuses on building a fundamental development server for Vue.js applications using TypeScript. A dev server is crucial for modern web development workflows, providing features like hot module replacement (HMR), live reloading, and efficient serving of development builds, significantly speeding up the development feedback loop.
Problem Description
Your task is to create a basic in-memory development server for a Vue application. This server should:
- Serve HTML: When a request is made to the root path (
/), it should serve anindex.htmlfile that bootstraps a Vue application. - Serve Vue Components: It should be able to serve
.vuefiles. These files will contain both the template and script sections. - Compile SFCs: The server needs to compile Single File Components (
.vuefiles) into JavaScript that the browser can understand. This involves extracting the template and script, and ideally, handling scoped CSS. - Bundle JavaScript: The server should bundle the application's JavaScript, including dependencies and your Vue components, into a single manageable chunk.
- Live Reloading (Simulated): While a full HMR implementation is complex, for this challenge, you should simulate a live reload mechanism. When a
.vuefile changes, the server should inject a script into theindex.htmlthat prompts the browser to refresh the page.
Examples
Example 1: Basic Vue App
Input:
A directory structure containing:
index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue App</title> </head> <body> <div id="app"></div> <script type="module" src="/src/main.ts"></script> </body> </html>src/main.tsimport { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#app');src/App.vue<template> <div> <h1>Hello, {{ name }}!</h1> <button @click="changeName">Change Name</button> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ data() { return { name: 'Vue Dev Server' }; }, methods: { changeName() { this.name = 'Updated!'; } } }); </script> <style scoped> h1 { color: blue; } </style>
Expected Behavior (when accessing / in a browser):
The browser should display: "Hello, Vue Dev Server!" with a button below it. Clicking the button changes the text to "Hello, Updated!".
Explanation:
The dev server should:
- Intercept the request for
/and serve theindex.html. - When
index.htmlrequests/src/main.ts, it should serve the compiled JavaScript formain.ts. - The compilation of
main.tswill reveal it depends onApp.vue. - The server will then compile
App.vue, extracting the template, script, and style. - The compiled script will be bundled with
main.tsand served to the browser. - The browser executes the JavaScript, rendering the Vue component.
Example 2: File Change and Reload
Input:
The same files as Example 1.
Scenario:
The user modifies src/App.vue to change the <h1> tag to <h2> and the color to green.
Expected Behavior:
The browser should automatically refresh, and the content should now be displayed as a green <h2> tag.
Explanation:
The dev server should detect the file change in src/App.vue. It then injects a script tag into the served index.html (or communicates with the browser via WebSockets if a more advanced setup were implemented) that triggers a page reload.
Constraints
- Language: TypeScript for the dev server implementation.
- Vue Version: Assume Vue 3.
- Compilation: You can leverage existing Vite plugins or libraries for Vue SFC compilation (e.g.,
@vitejs/plugin-vueconceptually, though you'll be implementing the server logic around it). You do not need to write a Vue SFC compiler from scratch. - Bundling: Similar to compilation, you can abstract away the complex bundling process and focus on how the dev server orchestrates the serving of modules. Consider how to handle module resolution and dependencies.
- File System: For simplicity, assume all source files are in memory or a simulated file system. No actual file watching or disk I/O is required for the core challenge if using an in-memory approach.
- Dependencies: You are allowed to use a Node.js HTTP server library (e.g.,
http,express) and a Vue SFC compiler. - HMR: A full HMR implementation is out of scope. Simulated live reload by injecting a refresh script is sufficient.
Notes
- Focus on Orchestration: The core of this challenge is understanding how a dev server manages requests, resolves module dependencies, triggers compilation, and signals for reloads.
- Module Resolution: Think about how a browser will request individual modules (e.g.,
import App from './App.vue') and how your server will respond with the correct compiled JavaScript. - Hot Module Replacement (HMR) vs. Live Reload: Understand the difference. Live reload refreshes the entire page. HMR updates only the changed modules without losing application state. For this challenge, live reload is the goal.
- Simplification: You don't need to implement a full bundler like Webpack or Rollup. The goal is to simulate the dev server experience. Consider how you might serve modules on demand.
- Inspiration: Look at how tools like Vite or Vue CLI's dev server function conceptually.