Skip to content

Commit

Permalink
Stabilize the next-dev API (#612)
Browse files Browse the repository at this point in the history
* downgrade and pin miniflare version to avoid a regression bug

* stabilize the next-dev options API

---------

Co-authored-by: Pete Bacon Darwin <[email protected]>
  • Loading branch information
dario-piotrowicz and petebacondarwin authored Jan 8, 2024
1 parent f0df948 commit f07ea9b
Show file tree
Hide file tree
Showing 7 changed files with 482 additions and 115 deletions.
5 changes: 5 additions & 0 deletions .changeset/silver-kings-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cloudflare/next-on-pages': patch
---

Update the `next-dev`'s `setupDevBindings` API and remove the module's `__experimental__` prefix
5 changes: 1 addition & 4 deletions internal-packages/next-dev/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# Next-on-pages Next-Dev

> **Warning**
> The submodule API needs to be finalized, in the meantime the submodule is being exported as `__experimental__next-dev`, the `__experimental__` prefix will be removed in a future release
The `next-dev` submodule of the `@cloudflare/next-on-pages` package implements a utility that allows you to run the [standard Next.js development server](https://nextjs.org/docs/app/api-reference/next-cli#development) (with hot-code reloading, error reporting, HMR and everything it has to offer) with also adding local Cloudflare bindings simulations (implemented via [Miniflare](https://github.com/cloudflare/miniflare)).

IMPORTANT: As mentioned above the module allows you to run the standard Next.js dev server as is and it only makes sure that Cloudflare bindings are accessible, it does not generate a worker nor faithfully represent the final application that will be deployed to Cloudflare Pages, so please use this only as a development tool and make sure to properly test your application with `wrangler pages dev` before actually deploying it to.
IMPORTANT: As mentioned above the module allows you to run the standard Next.js dev server as is and it only makes sure that Cloudflare bindings are accessible, it does not generate a worker nor faithfully represent the final application that will be deployed to Cloudflare Pages, so please use this only as a development tool and make sure to properly test your application with `wrangler pages dev` before actually deploying it to Cloudflare.

## How to use the module

Expand Down
2 changes: 1 addition & 1 deletion internal-packages/next-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"devBindingsOptions.ts"
],
"dependencies": {
"miniflare": "^3.20231030.2",
"miniflare": "3.20231002.0",
"node-fetch": "^3.3.2"
},
"devDependencies": {
Expand Down
78 changes: 36 additions & 42 deletions internal-packages/next-dev/src/durableObjects.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DevBindingsOptions } from './index';
import type { Binding } from './index';
import type { WorkerOptions } from 'miniflare';
import type { WorkerDefinition, WorkerRegistry } from './wrangler';
import {
Expand All @@ -9,26 +9,31 @@ import {
} from './wrangler';
import { warnAboutExternalBindingsNotFound } from './utils';

export type DevBindingsDurableObjectOptions = Record<
string,
Extract<Binding, { type: 'durable-object' }>
>;

/**
* Gets information regarding DurableObject bindings that can be passed to miniflare to access external (locally exposed in the local registry) Durable Object bindings.
*
* @param durableObjects
* @returns the durableObjects and WorkersOptions objects to use or undefined if connecting to the registry and/or creating the options has failed
*/
export async function getDOBindingInfo(
durableObjects: DevBindingsOptions['durableObjects'],
durableObjects: DevBindingsDurableObjectOptions,
): Promise<
| {
workerOptions: WorkerOptions;
durableObjects: DevBindingsOptions['durableObjects'];
durableObjects: WorkerOptions['durableObjects'];
}
| undefined
> {
const requestedDurableObjectsNames = new Set(
Object.keys(durableObjects ?? {}),
const requestedDurableObjectBindings = new Map(
Object.entries(durableObjects ?? {}),
);

if (requestedDurableObjectsNames.size === 0) {
if (requestedDurableObjectBindings.size === 0) {
return;
}

Expand All @@ -41,36 +46,30 @@ export async function getDOBindingInfo(
}

if (!registeredWorkers) {
warnAboutLocalDurableObjectsNotFound(requestedDurableObjectsNames);
warnAboutLocalDurableObjectsNotFound(
new Set(requestedDurableObjectBindings.keys()),
);
return;
}

const registeredWorkersWithDOs: RegisteredWorkersWithDOs =
getRegisteredWorkersWithDOs(
registeredWorkers,
requestedDurableObjectsNames,
);

const [foundDurableObjects, missingDurableObjects] = [
...requestedDurableObjectsNames.keys(),
].reduce(
([foundDOs, missingDOs], durableObjectName) => {
let found = false;
for (const [, worker] of registeredWorkersWithDOs.entries()) {
found = !!worker.durableObjects.find(
durableObject => durableObject.name === durableObjectName,
);
if (found) break;
}
if (found) {
foundDOs.add(durableObjectName);
} else {
missingDOs.add(durableObjectName);
}
return [foundDOs, missingDOs];
},
[new Set(), new Set()] as [Set<string>, Set<string>],
);
getRegisteredWorkersWithDOs(registeredWorkers);

const foundDurableObjects = new Set<string>();
const missingDurableObjects = new Set<string>();

for (const [bindingName, binding] of requestedDurableObjectBindings) {
const worker = registeredWorkersWithDOs.get(binding.service.name);
if (
worker?.durableObjects.some(
durableObject => durableObject.className === binding.className,
)
) {
foundDurableObjects.add(bindingName);
} else {
missingDurableObjects.add(bindingName);
}
}

if (missingDurableObjects.size) {
warnAboutLocalDurableObjectsNotFound(missingDurableObjects);
Expand Down Expand Up @@ -103,7 +102,7 @@ export async function getDOBindingInfo(
[externalDO.durableObjectName]: externalDO,
};
},
{} as DevBindingsOptions['durableObjects'],
{} as WorkerOptions['durableObjects'],
);

return {
Expand All @@ -112,20 +111,15 @@ export async function getDOBindingInfo(
};
}

function getRegisteredWorkersWithDOs(
registeredWorkers: WorkerRegistry,
durableObjectsNames: Set<string>,
) {
function getRegisteredWorkersWithDOs(registeredWorkers: WorkerRegistry) {
const registeredWorkersWithDOs: Map<string, WorkerRegistry[string]> =
new Map();

for (const workerName of Object.keys(registeredWorkers ?? {})) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const worker = registeredWorkers![workerName]!;
const containsRequestedDO = !!worker.durableObjects.find(({ name }) =>
durableObjectsNames.has(name),
);
if (containsRequestedDO) {
const worker = registeredWorkers[workerName]!;
const containsDOs = !!worker.durableObjects.length;
if (containsDOs) {
registeredWorkersWithDOs.set(workerName, worker);
}
}
Expand Down
Loading

0 comments on commit f07ea9b

Please sign in to comment.