@recranet/astro-workers-for-platforms-adapter 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1 +1,43 @@
1
1
  # astro-workers-for-platforms-adapter
2
+
3
+ Custom Astro adapter for Cloudflare Workers for Platforms.
4
+
5
+ ## How it works
6
+
7
+ ```
8
+ Request → Dispatch Worker → User Worker (worker-entry.mjs) → env.ASSETS.fetch()
9
+ ```
10
+
11
+ This adapter ensures Astro always emits a worker entry, even for fully static sites. While static assets could be uploaded to R2 and served by the dispatch worker.
12
+ Workers for Platforms supports a native Static Assets binding (`env.ASSETS`) that serves files from the edge with per-tenant isolation — no R2 needed.
13
+
14
+ Running `astro build` produces two outputs: a bundled worker at `dist/server/entry.mjs` and static assets in `dist/client/`. The adapter sets `ssr.noExternal: true` so Vite inlines all dependencies into a single file with no external imports.
15
+
16
+ Deployment uses the Workers for Platforms asset upload API in three steps: create session, upload asset buckets, then deploy the worker with a completion token.
17
+
18
+ ## Why not `@astrojs/cloudflare`?
19
+
20
+ The official `@astrojs/cloudflare` adapter cannot be used here because:
21
+
22
+ - It requires wrangler as a peer dependency (we deploy programmatically via the API, not with wrangler)
23
+ - As of Astro 6, it deletes `_worker.js` for fully static sites — Workers for Platforms always needs a worker entry
24
+
25
+ ## Getting started
26
+
27
+ ```bash
28
+ npm install @recranet/astro-workers-for-platforms-adapter
29
+ ```
30
+
31
+ ```js
32
+ // astro.config.mjs
33
+ import { defineConfig } from 'astro/config';
34
+ import astroWorkersForPlatformsAdapter from '@recranet/astro-workers-for-platforms-adapter';
35
+
36
+ export default defineConfig({
37
+ adapter: astroWorkersForPlatformsAdapter(),
38
+ });
39
+ ```
40
+
41
+ ## References
42
+
43
+ - [Cloudflare VibeSDK](https://github.com/cloudflare/vibesdk/blob/27b7a9bf9e8fcced21e28f7c97d9134c120614f3/worker/agents/core/codingAgent.ts#L729)
package/adapter.mjs CHANGED
@@ -1,29 +1,5 @@
1
- // Custom Astro adapter for Cloudflare Workers for Platforms.
2
- //
3
- // Architecture:
4
- // Request → Dispatch Worker → User Worker (worker-entry.mjs) → env.ASSETS.fetch()
5
- //
6
- // This adapter ensures Astro always emits a worker entry, even for fully static sites.
7
- // While static assets could be uploaded to R2 and served by the dispatch worker,
8
- // Workers for Platforms supports a native Static Assets binding (env.ASSETS) that
9
- // serves files from the edge with per-tenant isolation — no R2 needed.
10
- //
11
- // The official @astrojs/cloudflare adapter cannot be used here because:
12
- // - It requires wrangler as a peer dependency (we deploy via the CF API directly)
13
- // - As of Astro 6 (PR #15478), it deletes the _worker.js output for fully static
14
- // sites, which is the opposite of what Workers for Platforms needs
15
- // - Its worker entrypoint is designed for Pages/Workers, not the dispatch namespace
16
- // API with env.ASSETS binding and run_worker_first config
17
- //
18
- // Build flow:
19
- // `astro build` triggers this adapter, which sets `ssr.noExternal: true` so Vite
20
- // bundles all dependencies (including hono/utils/mime used in worker-entry.mjs)
21
- // into a single dist/server/entry.mjs with no external imports. Static assets
22
- // are prerendered into dist/client/.
23
- //
24
- // The build output (entry.mjs + static assets) is deployed using the Workers for
25
- // Platforms 3-step asset upload API (create session → upload buckets → deploy
26
- // worker with completion token).
1
+ import purgeAssets from './vite-plugin-purge-assets.mjs';
2
+
27
3
  export default function astroWorkersForPlatformsAdapter() {
28
4
  return {
29
5
  name: '@recranet/astro-workers-for-platforms-adapter',
@@ -31,6 +7,7 @@ export default function astroWorkersForPlatformsAdapter() {
31
7
  'astro:config:setup': ({ updateConfig }) => {
32
8
  updateConfig({
33
9
  vite: {
10
+ plugins: [purgeAssets()],
34
11
  ssr: {
35
12
  // Bundle all dependencies into the server entry (no external imports)
36
13
  noExternal: true,
@@ -53,6 +30,9 @@ export default function astroWorkersForPlatformsAdapter() {
53
30
  },
54
31
  });
55
32
  },
33
+ 'astro:build:done': ({ logger }) => {
34
+ logger.info('Build complete');
35
+ },
56
36
  },
57
37
  };
58
38
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recranet/astro-workers-for-platforms-adapter",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -13,15 +13,13 @@
13
13
  "adapter.d.mts",
14
14
  "worker-entry.mjs",
15
15
  "preview-entry.mjs",
16
- "vite-plugin-astro-preview.mjs"
16
+ "vite-plugin-astro-preview.mjs",
17
+ "vite-plugin-purge-assets.mjs"
17
18
  ],
18
19
  "peerDependencies": {
19
20
  "astro": "^6.0.0"
20
21
  },
21
22
  "dependencies": {
22
23
  "hono": "^4.12.7"
23
- },
24
- "scripts": {
25
- "publish": "npm publish . --access public"
26
24
  }
27
25
  }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Vite plugin that removes unreferenced assets from the bundle.
3
+ *
4
+ * Uses Rollup's `generateBundle` hook to inspect all emitted chunks and assets.
5
+ * Any asset not referenced by a chunk or another asset is deleted from the
6
+ * bundle before it is written to disk.
7
+ */
8
+ export default function purgeAssets() {
9
+ return {
10
+ name: 'purge-assets',
11
+ apply: 'build',
12
+ enforce: 'post',
13
+ generateBundle(_, bundle) {
14
+ // Collect all asset filenames
15
+ const assetFiles = new Set();
16
+ for (const [fileName, item] of Object.entries(bundle)) {
17
+ if (item.type === 'asset') {
18
+ assetFiles.add(fileName);
19
+ }
20
+ }
21
+
22
+ // Collect references from chunks (JS/CSS) and HTML assets
23
+ const referenced = new Set();
24
+ for (const item of Object.values(bundle)) {
25
+ let source;
26
+ if (item.type === 'chunk') {
27
+ source = item.code;
28
+ } else if (
29
+ item.type === 'asset' &&
30
+ typeof item.source === 'string'
31
+ ) {
32
+ source = item.source;
33
+ }
34
+ if (!source) continue;
35
+
36
+ for (const assetFile of assetFiles) {
37
+ if (referenced.has(assetFile)) continue;
38
+ // Match the asset's basename — handles both relative and absolute references
39
+ const baseName = assetFile.split('/').pop();
40
+ if (source.includes(baseName)) {
41
+ referenced.add(assetFile);
42
+ }
43
+ }
44
+ }
45
+
46
+ // Delete unreferenced assets
47
+ let purged = 0;
48
+ for (const assetFile of assetFiles) {
49
+ if (!referenced.has(assetFile)) {
50
+ delete bundle[assetFile];
51
+ purged++;
52
+ }
53
+ }
54
+
55
+ if (purged > 0) {
56
+ this.info(`Purged ${purged} unreferenced asset(s)`);
57
+ }
58
+ },
59
+ };
60
+ }