Skip to content

Commit

Permalink
Allow running in same Node vm
Browse files Browse the repository at this point in the history
  • Loading branch information
codetheweb committed Jan 23, 2024
1 parent bd5f733 commit a9078aa
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist
node_modules
bundled.*
.edgespec
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"dependencies": {
"@edge-runtime/node-utils": "^2.2.2",
"@edge-runtime/primitives": "^4.0.5",
"chalk": "^5.3.0",
"clipanion": "^4.0.0-rc.3",
"esbuild": "^0.19.11",
"human-readable": "^0.2.1",
Expand Down
3 changes: 3 additions & 0 deletions src/bundle/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ const constructEntrypoint = async (options: BundleOptions) => {
`
}

// todo: combine with bundle.ts
export const watchAndBundle = async (options: BundleOptions) => {
// todo: this should watch all relevant paths from a tsconfig.json, not just the ./api directory
const watcher = new Watcher(options.directoryPath, {
recursive: true,
ignoreInitial: true,
Expand All @@ -80,6 +82,7 @@ export const watchAndBundle = async (options: BundleOptions) => {
bundle: true,
format: "esm",
write: false,
sourcemap: "inline",
...options.esbuild,
})
await ctx.rebuild()
Expand Down
66 changes: 56 additions & 10 deletions src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { EdgeRuntime } from "edge-runtime"
import { watchAndBundle } from "src/bundle/watch"
import { durationFormatter } from "human-readable"
import ora from "ora"
import { handleRequestWithEdgeSpec } from "src"
import path from "node:path"
import chalk from "chalk"

export class DevCommand extends Command {
static paths = [[`dev`]]
Expand All @@ -21,6 +24,12 @@ export class DevCommand extends Command {
description: "The directory of your app",
})

// todo: better syntax for this flag, seems to need --no-emulate-wintercg
emulateWinterCG = Option.Boolean("--emulate-wintercg", true, {
description:
"Emulate the WinterCG runtime. When true, native APIs are unavailable.",
})

async execute() {
const listenSpinner = ora({
text: "Starting server...",
Expand All @@ -29,14 +38,37 @@ export class DevCommand extends Command {
}).start()

let runtime: EdgeRuntime
let nonWinterCGHandler: ReturnType<typeof handleRequestWithEdgeSpec>

const server = createServer(
transformToNodeBuilder({
defaultOrigin: `http://localhost:${this.port}`,
})(async (req) => {
const response = await runtime.dispatchFetch(req.url, req)
await response.waitUntil()
return response
if (this.emulateWinterCG) {
const response = await runtime.dispatchFetch(req.url, req)
await response.waitUntil()
return response
}

try {
return await nonWinterCGHandler(req)
} catch (error) {
if (error instanceof Error) {
this.context.stderr.write(
chalk.bgRed("\nUnhandled exception:\n") +
(error.stack ?? error.message) +
"\n"
)
} else {
this.context.stderr.write(
"Unhandled exception:\n" + JSON.stringify(error) + "\n"
)
}

return new Response("Internal server error", {
status: 500,
})
}
})
)

Expand All @@ -47,11 +79,14 @@ export class DevCommand extends Command {
})
})

const command = this
await watchAndBundle({
directoryPath: this.appDirectory,
// todo: allow running in the same Node.js process/context to access system APIs
bundledAdapter: "wintercg-minimal",
bundledAdapter: this.emulateWinterCG ? "wintercg-minimal" : undefined,
esbuild: {
platform: this.emulateWinterCG ? "browser" : "node",
outfile: this.emulateWinterCG ? undefined : ".edgespec/dev-bundle.js",
write: this.emulateWinterCG ? false : true,
plugins: [
{
name: "watch",
Expand All @@ -71,15 +106,26 @@ export class DevCommand extends Command {
allowMultiples: ["m", "s", "ms"],
})

build.onEnd((result) => {
build.onEnd(async (result) => {
const durationMs = performance.now() - buildStartedAt
spinner.succeed(`Built in ${timeFormatter(durationMs)}`)

if (!result.outputFiles) throw new Error("no output files")
if (command.emulateWinterCG) {
if (!result.outputFiles) throw new Error("no output files")

runtime = new EdgeRuntime({
initialCode: result.outputFiles[0].text,
})
runtime = new EdgeRuntime({
initialCode: result.outputFiles[0].text,
})
} else {
const edgeSpecModule = await import(
`file:${path.resolve(
".edgespec/dev-bundle.js"
)}#${Date.now()}`
)
nonWinterCGHandler = handleRequestWithEdgeSpec(
edgeSpecModule.default
)
}
})
},
},
Expand Down

0 comments on commit a9078aa

Please sign in to comment.