flarelabs-net / vite-plugin-cloudflare

0 stars 0 forks source link

API brainstorming with Cina #3

Open IgorMinar opened 3 days ago

IgorMinar commented 3 days ago

scribbling just a few thoughts from a brainstorming session with @1000hz in Austin.

what if instead of:

export default defineConfig({
    plugins: [
        cloudflare({
            environments: {
                worker_a: {
                    main: './worker-a/index.ts',
                    wranglerConfig: './worker-a/wrangler.toml',
                },
                worker_b: {
                    main: './worker-b/index.ts',
                    wranglerConfig: './worker-b/wrangler.toml',
                },
            },
            entrypoint: 'worker_a',
        }),
    ],
});

we did something like:

export default defineConfig({
    plugins: [
        cloudflare(['./worker-a/wrangler.toml', './worker-b/wrangler.toml'])
    ],
});

where the first config describes the entry-point worker.

some observations:

alternatively the API could also be:

export default defineConfig({
    plugins: [
        cloudflare({
            workers: [
                {
                    main: './worker-a/index.ts',
                    wranglerConfig: './worker-a/wrangler.toml',
                },
                {
                    main: './worker-b/index.ts',
                    wranglerConfig: './worker-b/wrangler.toml',
                },
            ]
        }),
    ],
});

yet another possibility is to move away from the wrangler.toml completely:

export default defineConfig({
    plugins: [
        cloudflare({
            workers: [
                {
                    src: './worker-a/'
                                          // where `ls -l ./worker-a/` is:
                                          // index.ts <- main
                                          // config.json <- new config file in json format that specifies bindings
                },
                {
                    src: './worker-b/'
                },
            ]
        }),
    ],
});

yet another version is an alternative on the previous where the config is embedded in index.ts as some kind of statically extractable metadata, e.g. export const config = { ... }. In this case we should make it possible to override the config via some external metadata in case one wants to deploy the worker to an environment with different bindings config.

jamesopstad commented 2 days ago

Yes, I've been thinking about this too. The problem with taking the main path from the wrangler config is that in Vite we want it to point to the dev entry whereas in wrangler we want it to point to the build output (resulting from running vite build). We could have main fields in the wrangler config that are scoped to development and production but I don't think this would be very intuitive.

For me, the best approach would be if there is no wrangler.toml and the wrangler config becomes a build time only construct. What I mean by this is that all the configuration is done in Vite and then when you run vite build it outputs a wrangler.json file alongside the build output for each environment. The configuration would now be defined in JS/TS and has all the advantages of being code:

We would also be able to provide the JS configuration directly to Miniflare without having to read and interpret files from the file system. At build time it would generate the wrangler.json files with themain field pointing to the build entrypoint.

This might look a bit like this (the defaults could be overriden by the environment):

export default defineConfig({
    plugins: [
        cloudflare({
            defaults: {
                compatibilityDate: '2024-09-09',
            },
            environments: {
                worker_a: {
                    main: './worker-a/index.ts',
                    options: {
                        serviceBindings: { WORKER_B: 'worker_b' },
                    },
                },
                worker_b: {
                    main: './worker-b/index.ts',
                    options: {
                        kvNamespaces: { MY_KV: 'xxxxx' },
                    },
                },
            },
            entrypoint: 'worker_a',
        }),
    ],
});

We could export types so that users can extract the config into smaller components. We would also have the option of exposing helper functions or a builder API for constructing the config if that was needed.

IgorMinar commented 2 days ago

how about this version:

vite.conf.js:

export default defineConfig({
    plugins: [
        cloudflare(['./worker-a/index.ts', './worker-b/index.ts'])
    ],
});

./worker-a/index.ts

import serviceB from '../worker-b/';
import kv from 'cloudflare:kv';

export const config {
  compatDate: '2024-09-09',
  compatFlags: ['nodejs_compat'];
  bindings: {
    foo: 'BAR',
    myKv: kv('someNamespaceId'),
    serviceB
  }
}

export default {
  fetch() { ... }
}

./worker-b/index.ts

import kv from 'cloudflare:kv';

export const config {
  compatDate: '2024-09-09',
  compatFlags: ['nodejs_compat'];
  bindings: {
    myKv: kv('someNamespaceId2'),
  }
}

export default {
  fetch() { ... }
} satisfies Worker<config>; // <<< gives you type safety for Env object