forked from modernweb-dev/web
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(dev-server): first implementation
- Loading branch information
1 parent
749fa47
commit 0cc6a82
Showing
88 changed files
with
1,470 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@web/dev-server-core': patch | ||
--- | ||
|
||
expose ErrorWithLocation class |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@web/dev-server': patch | ||
'@web/dev-server-cli': patch | ||
--- | ||
|
||
first implementation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@web/test-runner-cli': patch | ||
--- | ||
|
||
add test options to startTestRunner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@web/test-runner': patch | ||
--- | ||
|
||
expose a startTestRunner function |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './dist/index.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// this file is autogenerated with the update-esm-entrypoints script | ||
import cjsEntrypoint from './dist/index.js'; | ||
|
||
const { startDevServer, readConfig, validateCoreConfig, readCliArgsConfig } = cjsEntrypoint; | ||
|
||
export { startDevServer, readConfig, validateCoreConfig, readCliArgsConfig }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
{ | ||
"name": "@web/dev-server-cli", | ||
"version": "0.0.0", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"description": "Dev server for web applications", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/modernweb-dev/web.git", | ||
"directory": "packages/dev-server-cli" | ||
}, | ||
"author": "modern-web", | ||
"homepage": "https://github.com/modernweb-dev/web/tree/master/packages/dev-server-cli", | ||
"main": "dist/index.js", | ||
"engines": { | ||
"node": ">=10.0.0" | ||
}, | ||
"scripts": { | ||
"build": "tsc", | ||
"test": "mocha \"test/**/*.test.ts\" --require ts-node/register --reporter dot", | ||
"test:watch": "mocha \"test/**/*.test.ts\" --require ts-node/register --watch --watch-files src,test --reporter dot" | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"keywords": [ | ||
"web", | ||
"dev", | ||
"server", | ||
"implementation", | ||
"cli" | ||
], | ||
"dependencies": { | ||
"@babel/code-frame": "^7.10.4", | ||
"@types/command-line-args": "^5.0.0", | ||
"@web/config-loader": "^0.1.1", | ||
"@web/dev-server-core": "^0.2.2", | ||
"camelcase": "^6.0.0", | ||
"chalk": "^4.1.0", | ||
"command-line-args": "^5.1.1", | ||
"command-line-usage": "^6.1.0", | ||
"ip": "^1.1.5", | ||
"open": "^7.1.0", | ||
"portfinder": "^1.0.27" | ||
}, | ||
"devDependencies": { | ||
"node-fetch": "3.0.0-beta.7" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { DevServerCoreConfig } from '@web/dev-server-core'; | ||
|
||
export interface DevServerCliConfig extends DevServerCoreConfig { | ||
open?: 'string' | boolean; | ||
appIndex?: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import commandLineArgs from 'command-line-args'; | ||
import commandLineUsage, { OptionDefinition } from 'command-line-usage'; | ||
import camelCase from 'camelcase'; | ||
|
||
const defaultOptions: (OptionDefinition & { description: string })[] = [ | ||
{ | ||
name: 'config', | ||
alias: 'c', | ||
type: String, | ||
description: 'The file to read configuration from. Config entries are camelCases flags.', | ||
}, | ||
{ | ||
name: 'root-dir', | ||
alias: 'r', | ||
type: String, | ||
description: | ||
'The root directory to serve files from. Defaults to the current working directory.', | ||
}, | ||
{ | ||
name: 'open', | ||
alias: 'o', | ||
type: String, | ||
description: 'Opens the browser on app-index, root dir or a custom path.', | ||
}, | ||
{ | ||
name: 'app-index', | ||
alias: 'a', | ||
type: String, | ||
description: | ||
"The app's index.html file. When set, serves the index.html for non-file requests. Use this to enable SPA routing.", | ||
}, | ||
{ | ||
name: 'help', | ||
type: Boolean, | ||
description: 'Print help commands', | ||
}, | ||
]; | ||
|
||
export function readCliArgsConfig<T>( | ||
extraOptions: OptionDefinition[] = [], | ||
argv = process.argv, | ||
): Partial<T> { | ||
const options = [...defaultOptions, ...extraOptions]; | ||
const cliArgs = commandLineArgs(options, { argv }); | ||
// when the open flag is used without arguments, it defaults to null. treat this as "true" | ||
if ('open' in cliArgs && typeof cliArgs.open !== 'string') { | ||
cliArgs.open = true; | ||
} | ||
|
||
if ('help' in cliArgs) { | ||
/* eslint-disable-next-line no-console */ | ||
console.log( | ||
commandLineUsage([ | ||
{ | ||
header: 'Web Dev Server', | ||
content: 'Dev Server for web development.', | ||
}, | ||
{ | ||
header: 'Usage', | ||
content: 'web-dev-server [options...]' + '\nwds [options...]', | ||
}, | ||
{ header: 'Options', optionList: options }, | ||
]), | ||
); | ||
process.exit(); | ||
} | ||
|
||
const cliArgsConfig: Partial<T> = {}; | ||
|
||
for (const [key, value] of Object.entries(cliArgs)) { | ||
cliArgsConfig[camelCase(key) as keyof T] = value; | ||
} | ||
|
||
return cliArgsConfig; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { getPortPromise } from 'portfinder'; | ||
import { readConfig as readFileConfig, ConfigLoaderError } from '@web/config-loader'; | ||
import chalk from 'chalk'; | ||
import path from 'path'; | ||
import { DevServerCliConfig } from './DevServerCliConfig'; | ||
|
||
const defaultBaseConfig: Partial<DevServerCliConfig> = { | ||
rootDir: process.cwd(), | ||
hostname: 'localhost', | ||
middleware: [], | ||
plugins: [], | ||
}; | ||
|
||
export function validateCoreConfig<T extends DevServerCliConfig>(config: Partial<T>): T { | ||
if (typeof config.hostname !== 'string') { | ||
throw new Error('No hostname specified.'); | ||
} | ||
if (typeof config.port !== 'number') { | ||
throw new Error('No port specified.'); | ||
} | ||
if (typeof config.rootDir !== 'string') { | ||
throw new Error('No rootDir specified.'); | ||
} | ||
if ( | ||
config.open != null && | ||
!(typeof config.open === 'string' || typeof config.open === 'boolean') | ||
) { | ||
throw new Error('The open option should be a boolean or string.'); | ||
} | ||
|
||
return config as T; | ||
} | ||
|
||
export async function readConfig<T extends DevServerCliConfig & { config?: string }>( | ||
cliArgsConfig: Partial<T> = {}, | ||
): Promise<Partial<T>> { | ||
try { | ||
const fileConfig = await readFileConfig( | ||
'web-dev-server.config', | ||
typeof cliArgsConfig.config === 'string' ? cliArgsConfig.config : undefined, | ||
); | ||
const config: Partial<T> = { | ||
...defaultBaseConfig, | ||
...fileConfig, | ||
...cliArgsConfig, | ||
}; | ||
|
||
if (typeof config.rootDir === 'string') { | ||
config.rootDir = path.resolve(config.rootDir); | ||
} | ||
|
||
if (typeof config.port !== 'number') { | ||
const port = 9000 + Math.floor(Math.random() * 1000); | ||
config.port = await getPortPromise({ port }); | ||
} | ||
|
||
return config; | ||
} catch (error) { | ||
if (error instanceof ConfigLoaderError) { | ||
console.error(chalk.red(`\n${error.message}\n`)); | ||
process.exit(1); | ||
} | ||
throw error; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export { startDevServer } from './startDevServer'; | ||
export { readConfig, validateCoreConfig } from './config/readConfig'; | ||
export { readCliArgsConfig } from './config/readCliArgsConfig'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { DevServerCliConfig } from './config/DevServerCliConfig'; | ||
import { DevServerLogger } from './logger/DevServerLogger'; | ||
import ip from 'ip'; | ||
import chalk from 'chalk'; | ||
|
||
const createAddress = (config: DevServerCliConfig, host: string, path: string) => | ||
`http${config.http2 ? 's' : ''}://${host}:${config.port}${path}`; | ||
|
||
function logNetworkAddress(config: DevServerCliConfig, logger: DevServerLogger, openPath: string) { | ||
try { | ||
const address = ip.address(); | ||
if (typeof address === 'string') { | ||
logger.log( | ||
`${chalk.white('Network:')} ${chalk.cyanBright(createAddress(config, address, openPath))}`, | ||
); | ||
} | ||
} catch { | ||
// | ||
} | ||
} | ||
|
||
export function logStartMessage(config: DevServerCliConfig, logger: DevServerLogger) { | ||
const prettyHost = config.hostname ?? 'localhost'; | ||
let openPath = typeof config.open === 'string' ? config.open : '/'; | ||
if (!openPath.startsWith('/')) { | ||
openPath = `/${openPath}`; | ||
} | ||
|
||
logger.log(''); | ||
logger.log(chalk.bold('Web Dev Server started...')); | ||
logger.log(''); | ||
|
||
logger.group(); | ||
logger.log(`${chalk.white('Root dir:')} ${chalk.cyanBright(config.rootDir)}`); | ||
logger.log( | ||
`${chalk.white('Local:')} ${chalk.cyanBright(createAddress(config, prettyHost, openPath))}`, | ||
); | ||
logNetworkAddress(config, logger, openPath); | ||
logger.groupEnd(); | ||
logger.log(''); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { Logger, PluginSyntaxError } from '@web/dev-server-core'; | ||
import { codeFrameColumns } from '@babel/code-frame'; | ||
import path from 'path'; | ||
import chalk from 'chalk'; | ||
|
||
export class DevServerLogger implements Logger { | ||
constructor(private debugLogging: boolean = false) {} | ||
|
||
log(...messages: unknown[]) { | ||
console.log(...messages); | ||
} | ||
|
||
debug(...messages: unknown[]) { | ||
if (this.debugLogging) { | ||
console.debug(...messages); | ||
} | ||
} | ||
|
||
error(...messages: unknown[]) { | ||
console.error(...messages); | ||
} | ||
|
||
warn(...messages: unknown[]) { | ||
console.warn(...messages); | ||
} | ||
|
||
group() { | ||
console.group(); | ||
} | ||
|
||
groupEnd() { | ||
console.groupEnd(); | ||
} | ||
|
||
logSyntaxError(error: PluginSyntaxError) { | ||
const { message, code, filePath, column, line } = error; | ||
const result = codeFrameColumns(code, { start: { line, column } }, { highlightCode: true }); | ||
const relativePath = path.relative(process.cwd(), filePath); | ||
console.error( | ||
chalk.red(`Error while transforming ${chalk.cyanBright(relativePath)}: ${message}`), | ||
); | ||
console.error(result); | ||
console.error(''); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import openBrowserWindow from 'open'; | ||
import path from 'path'; | ||
import { DevServerCliConfig } from './config/DevServerCliConfig'; | ||
|
||
function isValidURL(str: string) { | ||
try { | ||
return !!new URL(str); | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
export async function openBrowser(config: DevServerCliConfig) { | ||
let openPath: string; | ||
if (typeof config.open === 'string') { | ||
// user-provided open path | ||
openPath = (config.open as string) === '' ? '/' : config.open; | ||
} else if (config.appIndex) { | ||
// if an appIndex was provided, use it's directory as open path | ||
openPath = `${config.basePath ?? ''}${path.dirname(config.appIndex)}/`; | ||
} else { | ||
openPath = config.basePath ? `${config.basePath}/` : '/'; | ||
} | ||
|
||
if (!isValidURL(openPath)) { | ||
// construct a full URL to open if the user didn't provide a full URL | ||
openPath = new URL( | ||
openPath, | ||
`http${config.http2 ? 's' : ''}://${config.hostname}:${config.port}`, | ||
).href; | ||
} | ||
|
||
await openBrowserWindow(openPath); | ||
} |
Oops, something went wrong.