@cloudwerk/vite-plugin 0.5.0 → 0.6.1
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/dist/index.d.ts +11 -2
- package/dist/index.js +382 -6
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { CloudwerkConfig, RouteManifest, ScanResult } from '@cloudwerk/core/build';
|
|
2
|
+
import { CloudwerkConfig, RouteManifest, ScanResult, QueueManifest, ServiceManifest } from '@cloudwerk/core/build';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @cloudwerk/vite-plugin - Types
|
|
@@ -150,6 +150,15 @@ declare function cloudwerkPlugin(options?: CloudwerkVitePluginOptions): Plugin;
|
|
|
150
150
|
* a Hono app with all routes registered from the file-based routing manifest.
|
|
151
151
|
*/
|
|
152
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Options for generating server entry.
|
|
155
|
+
*/
|
|
156
|
+
interface GenerateServerEntryOptions {
|
|
157
|
+
/** Queue manifest if queues are configured */
|
|
158
|
+
queueManifest?: QueueManifest | null;
|
|
159
|
+
/** Service manifest if services are configured */
|
|
160
|
+
serviceManifest?: ServiceManifest | null;
|
|
161
|
+
}
|
|
153
162
|
/**
|
|
154
163
|
* Generate the server entry module code.
|
|
155
164
|
*
|
|
@@ -165,7 +174,7 @@ declare function cloudwerkPlugin(options?: CloudwerkVitePluginOptions): Plugin;
|
|
|
165
174
|
* @param options - Resolved plugin options
|
|
166
175
|
* @returns Generated TypeScript/JavaScript code
|
|
167
176
|
*/
|
|
168
|
-
declare function generateServerEntry(manifest: RouteManifest, scanResult: ScanResult, options: ResolvedCloudwerkOptions): string;
|
|
177
|
+
declare function generateServerEntry(manifest: RouteManifest, scanResult: ScanResult, options: ResolvedCloudwerkOptions, entryOptions?: GenerateServerEntryOptions): string;
|
|
169
178
|
|
|
170
179
|
/**
|
|
171
180
|
* Client Entry Virtual Module Generator
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,14 @@ import {
|
|
|
12
12
|
resolveRoutesPath,
|
|
13
13
|
hasUseClientDirective,
|
|
14
14
|
generateComponentId,
|
|
15
|
-
ROUTE_FILE_NAMES
|
|
15
|
+
ROUTE_FILE_NAMES,
|
|
16
|
+
scanQueues,
|
|
17
|
+
buildQueueManifest,
|
|
18
|
+
QUEUES_DIR,
|
|
19
|
+
scanServices,
|
|
20
|
+
buildServiceManifest,
|
|
21
|
+
SERVICES_DIR,
|
|
22
|
+
SERVICE_FILE_NAME
|
|
16
23
|
} from "@cloudwerk/core/build";
|
|
17
24
|
|
|
18
25
|
// src/types.ts
|
|
@@ -29,7 +36,9 @@ var RESOLVED_VIRTUAL_IDS = {
|
|
|
29
36
|
|
|
30
37
|
// src/virtual-modules/server-entry.ts
|
|
31
38
|
import * as path from "path";
|
|
32
|
-
function generateServerEntry(manifest, scanResult, options) {
|
|
39
|
+
function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
40
|
+
const queueManifest = entryOptions?.queueManifest;
|
|
41
|
+
const serviceManifest = entryOptions?.serviceManifest;
|
|
33
42
|
const imports = [];
|
|
34
43
|
const pageRegistrations = [];
|
|
35
44
|
const routeRegistrations = [];
|
|
@@ -48,6 +57,7 @@ function generateServerEntry(manifest, scanResult, options) {
|
|
|
48
57
|
let middlewareIndex = 0;
|
|
49
58
|
let errorIndex = 0;
|
|
50
59
|
let notFoundIndex = 0;
|
|
60
|
+
const ssgPageInfo = [];
|
|
51
61
|
for (const err of scanResult.errors) {
|
|
52
62
|
if (!importedModules.has(err.absolutePath)) {
|
|
53
63
|
const varName = `error_${errorIndex++}`;
|
|
@@ -100,6 +110,10 @@ function generateServerEntry(manifest, scanResult, options) {
|
|
|
100
110
|
if (route.fileType === "page") {
|
|
101
111
|
const varName = `page_${pageIndex++}`;
|
|
102
112
|
imports.push(`import * as ${varName} from '${route.absolutePath}'`);
|
|
113
|
+
const hasDynamicSegments = route.segments.some(
|
|
114
|
+
(s) => s.type === "dynamic" || s.type === "catchAll" || s.type === "optionalCatchAll"
|
|
115
|
+
);
|
|
116
|
+
ssgPageInfo.push({ varName, urlPattern: route.urlPattern, hasDynamicSegments });
|
|
103
117
|
const layoutChain = route.layouts.map((p) => layoutModules.get(p)).join(", ");
|
|
104
118
|
const middlewareChain = route.middleware.map((p) => middlewareModules.get(p)).join(", ");
|
|
105
119
|
const errorModule = route.errorBoundary ? errorModules.get(route.errorBoundary) : null;
|
|
@@ -131,6 +145,7 @@ function generateServerEntry(manifest, scanResult, options) {
|
|
|
131
145
|
*/
|
|
132
146
|
|
|
133
147
|
import { Hono } from 'hono'
|
|
148
|
+
import { ssgParams } from 'hono/ssg'
|
|
134
149
|
import { contextMiddleware, createHandlerAdapter, createMiddlewareAdapter, setRouteConfig, NotFoundError, RedirectError } from '@cloudwerk/core/runtime'
|
|
135
150
|
import { setActiveRenderer } from '@cloudwerk/ui'
|
|
136
151
|
|
|
@@ -301,6 +316,11 @@ function registerPage(app, pattern, pageModule, layoutModules, middlewareModules
|
|
|
301
316
|
})
|
|
302
317
|
}
|
|
303
318
|
|
|
319
|
+
// Apply SSG params middleware if page has generateStaticParams (for static generation)
|
|
320
|
+
if (typeof pageModule.generateStaticParams === 'function') {
|
|
321
|
+
app.use(pattern, ssgParams(pageModule.generateStaticParams))
|
|
322
|
+
}
|
|
323
|
+
|
|
304
324
|
// Register GET handler for page
|
|
305
325
|
app.get(pattern, async (c) => {
|
|
306
326
|
const params = c.req.param()
|
|
@@ -465,6 +485,13 @@ app.use('*', async (c, next) => {
|
|
|
465
485
|
${pageRegistrations.join("\n")}
|
|
466
486
|
${routeRegistrations.join("\n")}
|
|
467
487
|
|
|
488
|
+
// SSG routes endpoint - returns all static routes for build-time generation
|
|
489
|
+
app.get('/__ssg/routes', async (c) => {
|
|
490
|
+
const routes = []
|
|
491
|
+
${generateSSGRouteChecks(ssgPageInfo)}
|
|
492
|
+
return c.json({ routes })
|
|
493
|
+
})
|
|
494
|
+
|
|
468
495
|
// 404 handler
|
|
469
496
|
app.notFound(async (c) => {
|
|
470
497
|
const path = c.req.path
|
|
@@ -532,8 +559,167 @@ app.onError(async (err, c) => {
|
|
|
532
559
|
// ============================================================================
|
|
533
560
|
|
|
534
561
|
export default app
|
|
562
|
+
${generateQueueExports(queueManifest)}
|
|
563
|
+
${generateServiceRegistration(serviceManifest)}
|
|
535
564
|
`;
|
|
536
565
|
}
|
|
566
|
+
function generateSSGRouteChecks(pages) {
|
|
567
|
+
const lines = [];
|
|
568
|
+
for (const page of pages) {
|
|
569
|
+
if (page.hasDynamicSegments) {
|
|
570
|
+
lines.push(` // ${page.urlPattern}`);
|
|
571
|
+
lines.push(` if (typeof ${page.varName}.generateStaticParams === 'function') {`);
|
|
572
|
+
lines.push(` try {`);
|
|
573
|
+
lines.push(` const params = await ${page.varName}.generateStaticParams()`);
|
|
574
|
+
lines.push(` if (Array.isArray(params)) {`);
|
|
575
|
+
lines.push(` for (const p of params) {`);
|
|
576
|
+
lines.push(` let url = '${page.urlPattern}'`);
|
|
577
|
+
lines.push(` for (const [key, value] of Object.entries(p)) {`);
|
|
578
|
+
lines.push(` url = url.replace(':' + key, String(value))`);
|
|
579
|
+
lines.push(` }`);
|
|
580
|
+
lines.push(` routes.push(url)`);
|
|
581
|
+
lines.push(` }`);
|
|
582
|
+
lines.push(` }`);
|
|
583
|
+
lines.push(` } catch (e) {`);
|
|
584
|
+
lines.push(` console.error('SSG: Failed to get params for ${page.urlPattern}:', e)`);
|
|
585
|
+
lines.push(` }`);
|
|
586
|
+
lines.push(` }`);
|
|
587
|
+
} else {
|
|
588
|
+
lines.push(` // ${page.urlPattern}`);
|
|
589
|
+
lines.push(` if ('config' in ${page.varName} && ${page.varName}.config?.rendering === 'static') {`);
|
|
590
|
+
lines.push(` routes.push('${page.urlPattern}')`);
|
|
591
|
+
lines.push(` }`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return lines.join("\n");
|
|
595
|
+
}
|
|
596
|
+
function generateQueueExports(queueManifest) {
|
|
597
|
+
if (!queueManifest || queueManifest.queues.length === 0) {
|
|
598
|
+
return "";
|
|
599
|
+
}
|
|
600
|
+
const lines = [];
|
|
601
|
+
const imports = [];
|
|
602
|
+
const queueHandlers = [];
|
|
603
|
+
lines.push("");
|
|
604
|
+
lines.push("// ============================================================================");
|
|
605
|
+
lines.push("// Queue Consumer Handlers");
|
|
606
|
+
lines.push("// ============================================================================");
|
|
607
|
+
lines.push("");
|
|
608
|
+
for (let i = 0; i < queueManifest.queues.length; i++) {
|
|
609
|
+
const queue = queueManifest.queues[i];
|
|
610
|
+
const varName = `queueDef_${i}`;
|
|
611
|
+
imports.push(`import ${varName} from '${queue.absolutePath}'`);
|
|
612
|
+
queueHandlers.push(`
|
|
613
|
+
/**
|
|
614
|
+
* Queue consumer handler for '${queue.name}'
|
|
615
|
+
*/
|
|
616
|
+
async function handle_${queue.name}_queue(batch, env, ctx) {
|
|
617
|
+
const definition = ${varName}
|
|
618
|
+
|
|
619
|
+
// Create message wrappers
|
|
620
|
+
const messages = batch.messages.map((msg) => ({
|
|
621
|
+
id: msg.id,
|
|
622
|
+
body: msg.body,
|
|
623
|
+
timestamp: new Date(msg.timestamp),
|
|
624
|
+
attempts: msg.attempts,
|
|
625
|
+
ack: () => msg.ack(),
|
|
626
|
+
retry: (options) => msg.retry(options),
|
|
627
|
+
deadLetter: (reason) => {
|
|
628
|
+
// Mark for DLQ if configured
|
|
629
|
+
if (definition.config?.deadLetterQueue) {
|
|
630
|
+
msg.retry({ delaySeconds: 0 })
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
}))
|
|
634
|
+
|
|
635
|
+
// Validate messages if schema is defined
|
|
636
|
+
if (definition.schema) {
|
|
637
|
+
for (const message of messages) {
|
|
638
|
+
const result = definition.schema.safeParse(message.body)
|
|
639
|
+
if (!result.success) {
|
|
640
|
+
console.error('Queue message validation failed:', result.error)
|
|
641
|
+
message.retry({ delaySeconds: 60 })
|
|
642
|
+
return
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
try {
|
|
648
|
+
// Use batch processor if available
|
|
649
|
+
if (definition.processBatch) {
|
|
650
|
+
await definition.processBatch(messages)
|
|
651
|
+
} else if (definition.process) {
|
|
652
|
+
// Process messages individually
|
|
653
|
+
for (const message of messages) {
|
|
654
|
+
try {
|
|
655
|
+
await definition.process(message)
|
|
656
|
+
} catch (error) {
|
|
657
|
+
if (definition.onError) {
|
|
658
|
+
await definition.onError(error, message)
|
|
659
|
+
} else {
|
|
660
|
+
throw error
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} catch (error) {
|
|
666
|
+
console.error('Queue processing error:', error)
|
|
667
|
+
// Retry all messages
|
|
668
|
+
batch.retryAll()
|
|
669
|
+
}
|
|
670
|
+
}`);
|
|
671
|
+
}
|
|
672
|
+
lines.push(imports.join("\n"));
|
|
673
|
+
lines.push(queueHandlers.join("\n"));
|
|
674
|
+
lines.push("");
|
|
675
|
+
lines.push("/**");
|
|
676
|
+
lines.push(" * Main queue handler that routes to specific queue handlers.");
|
|
677
|
+
lines.push(" * Export this as the `queue` handler in your worker.");
|
|
678
|
+
lines.push(" */");
|
|
679
|
+
lines.push("export async function queue(batch, env, ctx) {");
|
|
680
|
+
lines.push(" const queueName = batch.queue");
|
|
681
|
+
lines.push("");
|
|
682
|
+
for (const queue of queueManifest.queues) {
|
|
683
|
+
lines.push(` if (queueName === '${queue.queueName}') {`);
|
|
684
|
+
lines.push(` return handle_${queue.name}_queue(batch, env, ctx)`);
|
|
685
|
+
lines.push(" }");
|
|
686
|
+
lines.push("");
|
|
687
|
+
}
|
|
688
|
+
lines.push(" console.warn(\\`Unknown queue: \\${queueName}\\`)");
|
|
689
|
+
lines.push("}");
|
|
690
|
+
return lines.join("\n");
|
|
691
|
+
}
|
|
692
|
+
function generateServiceRegistration(serviceManifest) {
|
|
693
|
+
if (!serviceManifest || serviceManifest.services.length === 0) {
|
|
694
|
+
return "";
|
|
695
|
+
}
|
|
696
|
+
const lines = [];
|
|
697
|
+
const imports = [];
|
|
698
|
+
const registrations = [];
|
|
699
|
+
lines.push("");
|
|
700
|
+
lines.push("// ============================================================================");
|
|
701
|
+
lines.push("// Service Registration");
|
|
702
|
+
lines.push("// ============================================================================");
|
|
703
|
+
lines.push("");
|
|
704
|
+
imports.push("import { registerLocalService } from '@cloudwerk/core/bindings'");
|
|
705
|
+
for (let i = 0; i < serviceManifest.services.length; i++) {
|
|
706
|
+
const service = serviceManifest.services[i];
|
|
707
|
+
const varName = `serviceDef_${i}`;
|
|
708
|
+
imports.push(`import ${varName} from '${service.absolutePath}'`);
|
|
709
|
+
if (service.mode === "local") {
|
|
710
|
+
registrations.push(`registerLocalService('${service.name}', ${varName})`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
lines.push(imports.join("\n"));
|
|
714
|
+
lines.push("");
|
|
715
|
+
if (registrations.length > 0) {
|
|
716
|
+
lines.push("// Register local services");
|
|
717
|
+
for (const reg of registrations) {
|
|
718
|
+
lines.push(reg);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return lines.join("\n");
|
|
722
|
+
}
|
|
537
723
|
|
|
538
724
|
// src/virtual-modules/client-entry.ts
|
|
539
725
|
function generateClientEntry(clientComponents, options) {
|
|
@@ -1437,6 +1623,55 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1437
1623
|
console.log(`[cloudwerk] Found ${state.manifest.routes.length} routes`);
|
|
1438
1624
|
}
|
|
1439
1625
|
}
|
|
1626
|
+
async function buildQueueManifestIfExists(root) {
|
|
1627
|
+
if (!state) {
|
|
1628
|
+
throw new Error("Plugin state not initialized");
|
|
1629
|
+
}
|
|
1630
|
+
const queuesPath = path3.resolve(root, state.options.appDir, QUEUES_DIR);
|
|
1631
|
+
try {
|
|
1632
|
+
await fs2.promises.access(queuesPath);
|
|
1633
|
+
} catch {
|
|
1634
|
+
state.queueScanResult = null;
|
|
1635
|
+
state.queueManifest = null;
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
state.queueScanResult = await scanQueues(
|
|
1639
|
+
path3.resolve(root, state.options.appDir),
|
|
1640
|
+
{ extensions: state.options.config.extensions }
|
|
1641
|
+
);
|
|
1642
|
+
state.queueManifest = buildQueueManifest(
|
|
1643
|
+
state.queueScanResult,
|
|
1644
|
+
root,
|
|
1645
|
+
{ appName: "cloudwerk" }
|
|
1646
|
+
);
|
|
1647
|
+
if (state.options.verbose && state.queueManifest.queues.length > 0) {
|
|
1648
|
+
console.log(`[cloudwerk] Found ${state.queueManifest.queues.length} queue(s)`);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
async function buildServiceManifestIfExists(root) {
|
|
1652
|
+
if (!state) {
|
|
1653
|
+
throw new Error("Plugin state not initialized");
|
|
1654
|
+
}
|
|
1655
|
+
const servicesPath = path3.resolve(root, state.options.appDir, SERVICES_DIR);
|
|
1656
|
+
try {
|
|
1657
|
+
await fs2.promises.access(servicesPath);
|
|
1658
|
+
} catch {
|
|
1659
|
+
state.serviceScanResult = null;
|
|
1660
|
+
state.serviceManifest = null;
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
state.serviceScanResult = await scanServices(
|
|
1664
|
+
path3.resolve(root, state.options.appDir),
|
|
1665
|
+
{ extensions: state.options.config.extensions }
|
|
1666
|
+
);
|
|
1667
|
+
state.serviceManifest = buildServiceManifest(
|
|
1668
|
+
state.serviceScanResult,
|
|
1669
|
+
root
|
|
1670
|
+
);
|
|
1671
|
+
if (state.options.verbose && state.serviceManifest.services.length > 0) {
|
|
1672
|
+
console.log(`[cloudwerk] Found ${state.serviceManifest.services.length} service(s)`);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1440
1675
|
function isRouteFile(filePath) {
|
|
1441
1676
|
if (!state) return false;
|
|
1442
1677
|
const appDir = path3.resolve(state.options.root, state.options.appDir);
|
|
@@ -1445,6 +1680,25 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1445
1680
|
const nameWithoutExt = basename2.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
1446
1681
|
return ROUTE_FILE_NAMES.includes(nameWithoutExt);
|
|
1447
1682
|
}
|
|
1683
|
+
function isQueueFile(filePath) {
|
|
1684
|
+
if (!state) return false;
|
|
1685
|
+
const queuesDir = path3.resolve(state.options.root, state.options.appDir, QUEUES_DIR);
|
|
1686
|
+
if (!filePath.startsWith(queuesDir)) return false;
|
|
1687
|
+
const relativePath = path3.relative(queuesDir, filePath);
|
|
1688
|
+
if (relativePath.includes(path3.sep)) return false;
|
|
1689
|
+
const ext = path3.extname(filePath);
|
|
1690
|
+
return state.options.config.extensions.includes(ext);
|
|
1691
|
+
}
|
|
1692
|
+
function isServiceFile(filePath) {
|
|
1693
|
+
if (!state) return false;
|
|
1694
|
+
const servicesDir = path3.resolve(state.options.root, state.options.appDir, SERVICES_DIR);
|
|
1695
|
+
if (!filePath.startsWith(servicesDir)) return false;
|
|
1696
|
+
const basename2 = path3.basename(filePath);
|
|
1697
|
+
const ext = path3.extname(filePath);
|
|
1698
|
+
const nameWithoutExt = basename2.replace(ext, "");
|
|
1699
|
+
if (nameWithoutExt !== SERVICE_FILE_NAME) return false;
|
|
1700
|
+
return state.options.config.extensions.includes(ext);
|
|
1701
|
+
}
|
|
1448
1702
|
function invalidateVirtualModules() {
|
|
1449
1703
|
if (!server) return;
|
|
1450
1704
|
const idsToInvalidate = [
|
|
@@ -1535,11 +1789,17 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1535
1789
|
errors: [],
|
|
1536
1790
|
notFound: []
|
|
1537
1791
|
},
|
|
1792
|
+
queueManifest: null,
|
|
1793
|
+
queueScanResult: null,
|
|
1794
|
+
serviceManifest: null,
|
|
1795
|
+
serviceScanResult: null,
|
|
1538
1796
|
clientComponents: /* @__PURE__ */ new Map(),
|
|
1539
1797
|
serverEntryCache: null,
|
|
1540
1798
|
clientEntryCache: null
|
|
1541
1799
|
};
|
|
1542
1800
|
await buildManifest(root);
|
|
1801
|
+
await buildQueueManifestIfExists(root);
|
|
1802
|
+
await buildServiceManifestIfExists(root);
|
|
1543
1803
|
await scanClientComponents(root, state);
|
|
1544
1804
|
},
|
|
1545
1805
|
/**
|
|
@@ -1559,6 +1819,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1559
1819
|
await buildManifest(state.options.root);
|
|
1560
1820
|
invalidateVirtualModules();
|
|
1561
1821
|
}
|
|
1822
|
+
if (isQueueFile(filePath)) {
|
|
1823
|
+
const queuesDir = path3.resolve(root, state.options.appDir, QUEUES_DIR);
|
|
1824
|
+
if (state?.options.verbose) {
|
|
1825
|
+
console.log(`[cloudwerk] Queue added: ${path3.relative(queuesDir, filePath)}`);
|
|
1826
|
+
}
|
|
1827
|
+
await buildQueueManifestIfExists(state.options.root);
|
|
1828
|
+
invalidateVirtualModules();
|
|
1829
|
+
}
|
|
1830
|
+
if (isServiceFile(filePath)) {
|
|
1831
|
+
const servicesDir = path3.resolve(root, state.options.appDir, SERVICES_DIR);
|
|
1832
|
+
if (state?.options.verbose) {
|
|
1833
|
+
console.log(`[cloudwerk] Service added: ${path3.relative(servicesDir, filePath)}`);
|
|
1834
|
+
}
|
|
1835
|
+
await buildServiceManifestIfExists(state.options.root);
|
|
1836
|
+
invalidateVirtualModules();
|
|
1837
|
+
}
|
|
1562
1838
|
});
|
|
1563
1839
|
devServer.watcher.on("unlink", async (filePath) => {
|
|
1564
1840
|
if (isRouteFile(filePath)) {
|
|
@@ -1568,6 +1844,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1568
1844
|
await buildManifest(state.options.root);
|
|
1569
1845
|
invalidateVirtualModules();
|
|
1570
1846
|
}
|
|
1847
|
+
if (isQueueFile(filePath)) {
|
|
1848
|
+
const queuesDir = path3.resolve(root, state.options.appDir, QUEUES_DIR);
|
|
1849
|
+
if (state?.options.verbose) {
|
|
1850
|
+
console.log(`[cloudwerk] Queue removed: ${path3.relative(queuesDir, filePath)}`);
|
|
1851
|
+
}
|
|
1852
|
+
await buildQueueManifestIfExists(state.options.root);
|
|
1853
|
+
invalidateVirtualModules();
|
|
1854
|
+
}
|
|
1855
|
+
if (isServiceFile(filePath)) {
|
|
1856
|
+
const servicesDir = path3.resolve(root, state.options.appDir, SERVICES_DIR);
|
|
1857
|
+
if (state?.options.verbose) {
|
|
1858
|
+
console.log(`[cloudwerk] Service removed: ${path3.relative(servicesDir, filePath)}`);
|
|
1859
|
+
}
|
|
1860
|
+
await buildServiceManifestIfExists(state.options.root);
|
|
1861
|
+
invalidateVirtualModules();
|
|
1862
|
+
}
|
|
1571
1863
|
});
|
|
1572
1864
|
devServer.watcher.on("change", async (filePath) => {
|
|
1573
1865
|
if (isRouteFile(filePath)) {
|
|
@@ -1577,6 +1869,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1577
1869
|
await buildManifest(state.options.root);
|
|
1578
1870
|
invalidateVirtualModules();
|
|
1579
1871
|
}
|
|
1872
|
+
if (isQueueFile(filePath)) {
|
|
1873
|
+
const queuesDir = path3.resolve(root, state.options.appDir, QUEUES_DIR);
|
|
1874
|
+
if (state?.options.verbose) {
|
|
1875
|
+
console.log(`[cloudwerk] Queue changed: ${path3.relative(queuesDir, filePath)}`);
|
|
1876
|
+
}
|
|
1877
|
+
await buildQueueManifestIfExists(state.options.root);
|
|
1878
|
+
invalidateVirtualModules();
|
|
1879
|
+
}
|
|
1880
|
+
if (isServiceFile(filePath)) {
|
|
1881
|
+
const servicesDir = path3.resolve(root, state.options.appDir, SERVICES_DIR);
|
|
1882
|
+
if (state?.options.verbose) {
|
|
1883
|
+
console.log(`[cloudwerk] Service changed: ${path3.relative(servicesDir, filePath)}`);
|
|
1884
|
+
}
|
|
1885
|
+
await buildServiceManifestIfExists(state.options.root);
|
|
1886
|
+
invalidateVirtualModules();
|
|
1887
|
+
}
|
|
1580
1888
|
const wranglerPath2 = findWranglerTomlPath(root);
|
|
1581
1889
|
if (wranglerPath2 && filePath === wranglerPath2) {
|
|
1582
1890
|
if (verbose) {
|
|
@@ -1621,7 +1929,11 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1621
1929
|
state.serverEntryCache = generateServerEntry(
|
|
1622
1930
|
state.manifest,
|
|
1623
1931
|
state.scanResult,
|
|
1624
|
-
state.options
|
|
1932
|
+
state.options,
|
|
1933
|
+
{
|
|
1934
|
+
queueManifest: state.queueManifest,
|
|
1935
|
+
serviceManifest: state.serviceManifest
|
|
1936
|
+
}
|
|
1625
1937
|
);
|
|
1626
1938
|
}
|
|
1627
1939
|
return state.serverEntryCache;
|
|
@@ -1655,13 +1967,71 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1655
1967
|
return null;
|
|
1656
1968
|
},
|
|
1657
1969
|
/**
|
|
1658
|
-
* Transform hook to detect and wrap client components
|
|
1970
|
+
* Transform hook to detect and wrap client components,
|
|
1971
|
+
* and rewrite binding imports to use the bindings proxy.
|
|
1659
1972
|
*/
|
|
1660
1973
|
transform(code, id) {
|
|
1661
1974
|
if (!state) return null;
|
|
1662
1975
|
if (id.includes("node_modules")) return null;
|
|
1663
1976
|
if (!id.endsWith(".tsx") && !id.endsWith(".ts")) return null;
|
|
1664
|
-
|
|
1977
|
+
let transformedCode = code;
|
|
1978
|
+
let wasTransformed = false;
|
|
1979
|
+
const bindingsImportRegex = /import\s*\{([^}]+)\}\s*from\s*['"]@cloudwerk\/core\/bindings['"]/g;
|
|
1980
|
+
if (bindingsImportRegex.test(code)) {
|
|
1981
|
+
bindingsImportRegex.lastIndex = 0;
|
|
1982
|
+
transformedCode = code.replace(bindingsImportRegex, (match, imports) => {
|
|
1983
|
+
const importNames = imports.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1984
|
+
const knownExports = [
|
|
1985
|
+
"bindings",
|
|
1986
|
+
"getBinding",
|
|
1987
|
+
"hasBinding",
|
|
1988
|
+
"getBindingNames",
|
|
1989
|
+
"queues",
|
|
1990
|
+
"getQueue",
|
|
1991
|
+
"hasQueue",
|
|
1992
|
+
"getQueueNames",
|
|
1993
|
+
"services",
|
|
1994
|
+
"getService",
|
|
1995
|
+
"hasService",
|
|
1996
|
+
"getServiceNames",
|
|
1997
|
+
"registerLocalService",
|
|
1998
|
+
"unregisterLocalService",
|
|
1999
|
+
"clearLocalServices",
|
|
2000
|
+
"durableObjects",
|
|
2001
|
+
"getDurableObject",
|
|
2002
|
+
"hasDurableObject",
|
|
2003
|
+
"getDurableObjectNames",
|
|
2004
|
+
"createLazyBinding"
|
|
2005
|
+
];
|
|
2006
|
+
const runtimeImports = [];
|
|
2007
|
+
const bindingImports = [];
|
|
2008
|
+
for (const name of importNames) {
|
|
2009
|
+
const [originalName] = name.split(/\s+as\s+/);
|
|
2010
|
+
if (knownExports.includes(originalName.trim())) {
|
|
2011
|
+
runtimeImports.push(name);
|
|
2012
|
+
} else {
|
|
2013
|
+
bindingImports.push(name);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (bindingImports.length === 0) {
|
|
2017
|
+
return match;
|
|
2018
|
+
}
|
|
2019
|
+
wasTransformed = true;
|
|
2020
|
+
const parts = [];
|
|
2021
|
+
const helperImports = runtimeImports.includes("createLazyBinding") ? runtimeImports : ["createLazyBinding", ...runtimeImports];
|
|
2022
|
+
parts.push(`import { ${helperImports.join(", ")} } from '@cloudwerk/core/bindings'`);
|
|
2023
|
+
for (const name of bindingImports) {
|
|
2024
|
+
const aliasMatch = name.match(/^(\w+)\s+as\s+(\w+)$/);
|
|
2025
|
+
if (aliasMatch) {
|
|
2026
|
+
parts.push(`const ${aliasMatch[2]} = createLazyBinding('${aliasMatch[1]}')`);
|
|
2027
|
+
} else {
|
|
2028
|
+
parts.push(`const ${name.trim()} = createLazyBinding('${name.trim()}')`);
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
return parts.join("\n");
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
if (hasUseClientDirective(transformedCode)) {
|
|
1665
2035
|
const componentId = generateComponentId(id, state.options.root);
|
|
1666
2036
|
const bundlePath = `${state.options.hydrationEndpoint}/${componentId}.js`;
|
|
1667
2037
|
const info = {
|
|
@@ -1674,7 +2044,7 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1674
2044
|
if (state.options.verbose) {
|
|
1675
2045
|
console.log(`[cloudwerk] Detected client component: ${componentId}`);
|
|
1676
2046
|
}
|
|
1677
|
-
const result = transformClientComponent(
|
|
2047
|
+
const result = transformClientComponent(transformedCode, {
|
|
1678
2048
|
componentId,
|
|
1679
2049
|
bundlePath: id
|
|
1680
2050
|
// Use file path for Vite to resolve in dev mode
|
|
@@ -1687,6 +2057,12 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1687
2057
|
map: null
|
|
1688
2058
|
};
|
|
1689
2059
|
}
|
|
2060
|
+
if (wasTransformed) {
|
|
2061
|
+
return {
|
|
2062
|
+
code: transformedCode,
|
|
2063
|
+
map: null
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
1690
2066
|
return null;
|
|
1691
2067
|
}
|
|
1692
2068
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudwerk/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Vite plugin for Cloudwerk file-based routing with virtual entry generation",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@swc/core": "^1.3.100",
|
|
22
|
-
"@cloudwerk/core": "^0.
|
|
23
|
-
"@cloudwerk/ui": "^0.
|
|
22
|
+
"@cloudwerk/core": "^0.14.0",
|
|
23
|
+
"@cloudwerk/ui": "^0.14.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"vite": "^5.0.0 || ^6.0.0",
|