scripts/generate-plugin-route-hosts.ts
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bun
/**
* Generates the list of hostnames / zones the plugin worker is routed on from
* cloudflare_workers/plugin/wrangler.jsonc. The /updates edge cache purge uses
* it to purge only the zones that can hold plugin cache entries.
*
* Run `bun run generate:plugin-route-hosts` after changing plugin routes;
* tests/plugin-route-hosts.unit.test.ts fails while the file is stale.
*/
import { readFileSync, writeFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
export const WRANGLER_PATH = resolve(ROOT, 'cloudflare_workers/plugin/wrangler.jsonc')
export const GENERATED_PATH = resolve(ROOT, 'supabase/functions/_backend/plugin_runtime/utils/pluginRouteHosts.generated.ts')
/** Strips // and /* *\/ comments and trailing commas outside of strings. */
export function parseJsonc(text: string): unknown {
let out = ''
let inString = false
for (let i = 0; i < text.length; i++) {
const char = text[i]
if (inString) {
out += char
if (char === '\\') {
out += text[++i] ?? ''
continue
}
if (char === '"')
inString = false
continue
}
if (char === '"') {
inString = true
out += char
continue
}
if (char === '/' && text[i + 1] === '/') {
while (i < text.length && text[i] !== '\n')
i++
out += '\n'
continue
}
if (char === '/' && text[i + 1] === '*') {
i += 2
while (i < text.length && !(text[i] === '*' && text[i + 1] === '/'))
i++
i++
continue
}
out += char
}
return JSON.parse(stripTrailingCommas(out))
}
/** Drops commas before `}` / `]`, outside strings only (string values stay intact). */
function stripTrailingCommas(text: string) {
let out = ''
let inString = false
for (let i = 0; i < text.length; i++) {
const char = text[i]
if (inString) {
out += char
if (char === '\\') {
out += text[++i] ?? ''
continue
}
if (char === '"')
inString = false
continue
}
if (char === '"') {
inString = true
out += char
continue
}
if (char === ',') {
let next = i + 1
while (next < text.length && /\s/.test(text[next]))
next++
if (text[next] === '}' || text[next] === ']')
continue
}
out += char
}
return out
}
interface Route { pattern?: string, zone_name?: string }
export function collectPluginRouteHosts(config: unknown) {
const hosts = new Set<string>()
const zoneNames = new Set<string>()
const envs = Object.values((config as { env?: Record<string, { routes?: Route[] }> }).env ?? {})
for (const env of envs) {
for (const route of env.routes ?? []) {
if (route.pattern)
hosts.add(route.pattern.split('/')[0].toLowerCase())
if (route.zone_name)
zoneNames.add(route.zone_name.toLowerCase())
}
}
return { hosts: [...hosts].sort(), zoneNames: [...zoneNames].sort() }
}
function renderList(values: string[]) {
return `[\n${values.map(value => ` '${value}',\n`).join('')}]`
}
export function renderGenerated(config: unknown) {
const { hosts, zoneNames } = collectPluginRouteHosts(config)
return `// Generated by scripts/generate-plugin-route-hosts.ts from
// cloudflare_workers/plugin/wrangler.jsonc. Do not edit by hand.
/** Hostnames the plugin worker is routed on (custom domains and route patterns). */
export const PLUGIN_ROUTE_HOSTS: readonly string[] = ${renderList(hosts)}
/** Zones named explicitly by plugin routes (custom hostnames resolve through them). */
export const PLUGIN_ROUTE_ZONE_NAMES: readonly string[] = ${renderList(zoneNames)}
`
}
if (import.meta.main) {
writeFileSync(GENERATED_PATH, renderGenerated(parseJsonc(readFileSync(WRANGLER_PATH, 'utf8'))))
console.log(`wrote ${GENERATED_PATH}`)
}