edge-mock
Version:
types for testing an developer edge applications
282 lines • 10.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const express_1 = __importDefault(require("express"));
const webpack_1 = __importDefault(require("webpack"));
const livereload_1 = __importDefault(require("livereload"));
const index_1 = require("./index");
const live_fetch_1 = __importDefault(require("./live_fetch"));
const utils_1 = require("./utils");
const default_prepare_key = (f) => f.replace(/.*?dist\/assets\//, '');
function pathExists(path) {
return fs_1.default.promises
.access(path, fs_1.default.constants.F_OK)
.then(() => true)
.catch(() => false);
}
async function load_config() {
const cwd = process.cwd();
const dev_server_config = path_1.default.join(cwd, 'edge-mock-config.js');
let config = {};
if (await pathExists(dev_server_config)) {
try {
config = await Promise.resolve().then(() => __importStar(require(dev_server_config)));
console.log('edge-mock-config.js found, using it for config');
}
catch (e) {
console.error('error loading', dev_server_config, e);
}
}
else {
console.log('edge-mock-config.js not found, using default config');
}
config.webpack_config = config.webpack_config || path_1.default.join(cwd, 'webpack.config');
config.dist_path = config.dist_path || path_1.default.join(cwd, 'dist/worker');
config.dist_assets_path = config.dist_assets_path || path_1.default.join(cwd, 'dist/assets');
config.prepare_key = config.prepare_key || default_prepare_key;
config.port = config.port || 3000;
if (!('livereload' in config)) {
config.livereload = true;
}
config.livereload_port = config.livereload_port || 35729;
return config;
}
class WebpackState {
_error = null;
get error() {
return this._error;
}
clearError() {
this._error = null;
}
setError(err) {
this._error = err;
}
}
async function start_webpack(config) {
let static_content_kv;
const env = index_1.makeEdgeEnv({ fetch: live_fetch_1.default });
const webpack_state = new WebpackState();
async function on_webpack_success(stats) {
console.log(stats.toString('minimal'));
delete require.cache[require.resolve(config.dist_path)];
if (await pathExists(config.dist_assets_path)) {
if (!static_content_kv) {
static_content_kv = new index_1.EdgeKVNamespace();
global.__STATIC_CONTENT = static_content_kv;
console.log('adding KV store "__STATIC_CONTENT" to global namespace');
}
await static_content_kv._add_files(config.dist_assets_path, config.prepare_key);
global.__STATIC_CONTENT_MANIFEST = static_content_kv._manifestJson();
}
env.clearEventListener();
try {
await Promise.resolve().then(() => __importStar(require(config.dist_path)));
}
catch (err) {
webpack_state.setError(err);
return;
}
webpack_state.clearError();
}
const wp_config = await Promise.resolve().then(() => __importStar(require(config.webpack_config)));
webpack_1.default(wp_config.default).watch({}, (err, stats) => {
if (err) {
console.error(err);
webpack_state.setError(err);
}
else {
on_webpack_success(stats);
}
});
return [env, webpack_state];
}
function livereload_script(config) {
if (config.livereload) {
return `\n\n<script src="http://localhost:${config.livereload_port}/livereload.js?snipver=1"></script>\n`;
}
else {
return '';
}
}
class ErrorResponse {
response;
config;
html_template = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{status} Error</title>
<meta name="description" content="{message}" />
<style>
body {
display: flex;
color: #24292e;
justify-content: center;
margin: 40px 10px 60px;
font-size: 16px;
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
main {
width: min(calc(100vw - 20px), 902px);
box-sizing: border-box;
border: 1px solid #e1e4e8;
border-radius: 6px;
padding: 20px 15px 20px;
word-wrap: break-word;
min-height: 400px;
}
aside {
font-size: 0.9rem;
color: #666;
}
pre code {
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
background: #f6f8fa;
border-radius: 6px;
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
display: block;
}
</style>
</head>
<body>
<main>
<h1>Error</h1>
<p><b>{message}</b></p>
{detail}
<h2>Config:</h2>
<pre><code>{config}</code></pre>
<aside>
(This page is shown by
<a href="https://github.com/samuelcolvin/edge-mock#development-server" target="_blank">edge-mock-server</a>,
it's a summary of an error that occurred while trying to serve the above web-worker application.)
</aside>
</main>
</body>
</html>{livereload}
`;
constructor(response, config) {
this.response = response;
this.config = config;
}
onError(message, error, status = 502) {
this.response.status(status);
this.response.set({ 'content-type': 'text/html' });
const context = {
message: this.escape(message),
status: status.toString(),
detail: '',
livereload: livereload_script(this.config),
config: this.escape(JSON.stringify(this.config, null, 2)),
};
if (error) {
const stack = error.stack?.toString().replace(/\n.*(\/express\/lib\/|edge-mock\/server)(.|\n)*/, '');
context.detail = `<pre><code>${this.escape(error.message)}\n${this.escape(stack || '')}</code></pre>`;
console.error(`${message}\n${error.message}\n${stack || ''}`);
}
else {
console.error(message);
}
let html = this.html_template;
for (const [key, value] of Object.entries(context)) {
html = html.replace(new RegExp(`{${key}}`, 'g'), value);
}
this.response.send(html);
}
escape(s) {
const html_tags = { '&': '&', '<': '<', '>': '>' };
return s.replace(/[&<>]/g, letter => html_tags[letter] || letter);
}
}
function run_server(config, env, webpack_state) {
const app = express_1.default();
if (config.livereload) {
const reload_server = livereload_1.default.createServer({ delay: 300, port: config.livereload_port });
reload_server.watch(path_1.default.dirname(config.dist_path));
}
const reload_html = utils_1.encode(livereload_script(config));
app.all(/.*/, (req, res) => {
const error_handler = new ErrorResponse(res, config);
if (webpack_state.error) {
error_handler.onError('Failed to load worker code', webpack_state.error);
return;
}
let listener;
try {
listener = env.getListener();
}
catch (err) {
error_handler.onError(err.message);
return;
}
const { url, method, headers } = req;
const request = new Request(url, { method, headers: headers });
const event = new FetchEvent('fetch', { request });
event.respondWith = promise => {
Promise.resolve(promise)
.then(response => {
res.status(response.status);
res.set(Object.fromEntries(response.headers.entries()));
response.arrayBuffer().then(ab => {
let body = ab;
if (config.livereload && (response.headers.get('content-type') || '').includes('text/html')) {
body = utils_1.catArraysBufferViews([new Uint8Array(ab), reload_html]);
}
res.send(Buffer.from(body));
});
})
.catch(err => error_handler.onError('Internal Error awaiting response promise', err));
};
try {
Promise.resolve(listener(event)).catch(err => error_handler.onError('Internal Error running web-worker', err));
}
catch (err) {
error_handler.onError('Internal Error running web-worker', err);
}
});
app.listen(config.port, () => {
console.log(`dev app running at http://localhost:${config.port}, livereload: ${config.livereload}`);
});
}
async function main() {
const config = await load_config();
// console.log('starting webpack, config: %o', config)
const [env, wps] = await start_webpack(config);
await run_server(config, env, wps);
}
if (require.main === module) {
main().catch(e => {
console.error(e);
process.exit(1);
});
}
//# sourceMappingURL=server.js.map