Skip to content

Commit 21a5fe4

Browse files
committed
automatic worker discovery for local dev env
1 parent dff05ac commit 21a5fe4

1 file changed

Lines changed: 76 additions & 21 deletions

File tree

index.js

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ const port = 3000;
1212

1313
const webflowDomain = process.argv[2] || 'test-e93bfd.webflow.io';
1414

15+
const workers = [];
1516
const importedWorkers = {};
1617

1718
/**
19+
* @param {string} workerName
1820
* @param {string} workerPath
1921
* @returns {Promise<Function>}
2022
*/
21-
async function importWorker(workerPath) {
23+
async function importWorker(workerName, workerPath) {
2224
const workerContent = (await fs.promises.readFile(workerPath, 'utf8'))
2325
.toString()
2426
.replaceAll('webflow.bitrise.io', webflowDomain)
@@ -28,14 +30,15 @@ async function importWorker(workerPath) {
2830
)
2931
.replaceAll(/\/\*(.|[\n\r])*?\*\//gm, '')
3032
.replaceAll(/\/\/.*$/g, '');
31-
const workerName = `${workerPath}-${crypto.createHash('md5').update(workerContent).digest('hex')}`;
32-
if (!importedWorkers[workerName]) {
33+
const workerNameWithHash = `${workerName}-${crypto.createHash('md5').update(workerContent).digest('hex')}`;
34+
if (!importedWorkers[workerNameWithHash]) {
3335
if (workerContent.match(/addEventListener\('fetch',/)) {
3436
// Service Worker Syntax
3537
eval(`(() => {
3638
function addEventListener(_, cb) {
37-
importedWorkers['${workerName}'] = { type: "Service" };
38-
importedWorkers['${workerName}'].handler = cb;
39+
importedWorkers['${workerNameWithHash}'] = { type: "Service" };
40+
importedWorkers['${workerNameWithHash}'].name = '${workerName}';
41+
importedWorkers['${workerNameWithHash}'].handler = cb;
3942
};
4043
${workerContent}
4144
})();`);
@@ -46,35 +49,82 @@ async function importWorker(workerPath) {
4649
${workerContent.replace(
4750
/export default {/,
4851
`
49-
importedWorkers['${workerName}'] = { type: "ES6 Module" };
50-
importedWorkers['${workerName}'].handler = {`,
52+
importedWorkers['${workerNameWithHash}'] = { type: "ES6 Module" };
53+
importedWorkers['${workerNameWithHash}'].name = '${workerName}';
54+
importedWorkers['${workerNameWithHash}'].handler = {`,
5155
)}
5256
})();`);
5357
}
54-
process.stdout.write(`[info] Registered new ${importedWorkers[workerName].type} Worker: ${workerName}\n`);
58+
process.stdout.write(`[info] Registered new Worker: ${workerName} (${importedWorkers[workerNameWithHash].type})\n`);
5559
}
56-
return importedWorkers[workerName];
60+
return importedWorkers[workerNameWithHash];
61+
}
62+
63+
/**
64+
*
65+
* @param {string}dir
66+
*/
67+
async function discoverWorkers(dir) {
68+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
69+
70+
await Promise.all(
71+
entries.map(async (entry) => {
72+
const fullPath = path.join(dir, entry.name);
73+
74+
if (entry.isDirectory()) {
75+
await discoverWorkers(fullPath);
76+
} else if (entry.name === 'wrangler.toml') {
77+
const tomlContent = await fs.promises.readFile(fullPath, 'utf8');
78+
// Basic TOML parsing - you may want to use a proper TOML parser library
79+
const config = {};
80+
const lines = tomlContent.split('\n');
81+
lines.forEach((line) => {
82+
const match = line.match(/^(\w+)\s*=\s*["']?([^"'\n]+)["']?/);
83+
if (match) {
84+
const [, key, value] = match;
85+
config[key] = value;
86+
}
87+
});
88+
89+
workers.push({
90+
name: path.basename(path.dirname(fullPath)),
91+
path: fullPath,
92+
config,
93+
});
94+
95+
process.stdout.write(`[info] Found worker config: ${fullPath}\n`);
96+
}
97+
}),
98+
);
5799
}
58100

59101
/**
60102
* @param {URL} urlObject
61103
* @returns {Promise<{ type: string; handler: Function; }>}
62104
*/
63105
async function getWorker(urlObject) {
64-
if (urlObject.pathname.match(/^\/integrations/)) {
65-
return importWorker('./src/js/integrations/worker.js');
66-
}
67-
if (urlObject.pathname.match(/^\/changelog/)) {
68-
return importWorker('./src/js/changelog/worker.js');
106+
if (workers.length === 0) {
107+
await discoverWorkers('./src/js');
69108
}
70-
if (urlObject.pathname.match(/^\/stacks/)) {
71-
return importWorker('./src/js/stacks/worker.js');
72-
}
73-
if (urlObject.pathname.match(/^\/careers\/maps\//)) {
74-
return importWorker('./src/js/career-maps/worker.js');
109+
110+
const matchedWorker = workers.filter((worker) => {
111+
let routePattern = worker.config.route.replace('bitrise.io', '^');
112+
if (routePattern.endsWith('*')) {
113+
routePattern = routePattern.slice(0, -1);
114+
} else {
115+
routePattern = `${routePattern}$`;
116+
}
117+
return urlObject.pathname.match(new RegExp(routePattern));
118+
});
119+
120+
if (matchedWorker.length > 0) {
121+
const worker = matchedWorker[0];
122+
return importWorker(worker.config.name, path.join(path.dirname(worker.path), worker.config.main));
75123
}
124+
76125
return {
77126
type: 'ES6 Module',
127+
name: 'express-proxy',
78128
handler: {
79129
async fetch(request) {
80130
const url = new URL(request.url);
@@ -118,7 +168,7 @@ app.get(/\/.*/, async (req, res) => {
118168
} catch (error) {
119169
const requestHandler = await getWorker(urlObject);
120170

121-
process.stdout.write(`[info] Using request handler ${requestHandler.type}\n`);
171+
process.stdout.write(`[info] Using request handler ${requestHandler.name} (${requestHandler.type})\n`);
122172

123173
const fetchEvent = {
124174
request: {
@@ -128,6 +178,11 @@ app.get(/\/.*/, async (req, res) => {
128178
const response = await buffer;
129179
res.statusCode = response.status;
130180

181+
const responseAccessControlAllowOrigin = response.headers.get('Access-Control-Allow-Origin');
182+
if (responseAccessControlAllowOrigin)
183+
res.setHeader('Access-Control-Allow-Origin', responseAccessControlAllowOrigin);
184+
const responseVary = response.headers.get('Vary');
185+
if (responseVary) res.setHeader('Vary', responseVary);
131186
const responseContentType = response.headers.get('Content-Type');
132187
if (responseContentType) res.setHeader('Content-Type', response.headers.get('Content-Type'));
133188
const responseLocation = response.headers.get('Location');
@@ -137,7 +192,7 @@ app.get(/\/.*/, async (req, res) => {
137192
responseLocation.replace(webflowDomain, `${hostname}:${port}`).replace('https', 'http'),
138193
);
139194

140-
process.stdout.write(`[info] Serving ${response.url} with status ${res.statusCode}\n`);
195+
process.stdout.write(`[info] Serving ${urlObject.href} with status ${res.statusCode}\n`);
141196

142197
let text = (await response.text())
143198
.replace("document.location.host === 'test-e93bfd.webflow.io'", 'true')

0 commit comments

Comments
 (0)