exportc 0.0.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/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # exportc
2
+
3
+ Add [export](https://github.com/ihasq/export) to existing Vite projects.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # In your existing Vite project
9
+ npx exportc init
10
+
11
+ # Install dependencies
12
+ cd export && npm install
13
+
14
+ # Start development (two terminals)
15
+ npm run export:dev # Terminal 1: Wrangler dev server
16
+ npm run dev # Terminal 2: Vite dev server
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ After initialization, import your server exports using the `export:/` prefix:
22
+
23
+ ```typescript
24
+ // In your Vite app
25
+ import { hello, Counter } from "export:/";
26
+
27
+ const message = await hello("World"); // "Hello, World!"
28
+
29
+ const counter = await new Counter(0);
30
+ await counter.increment(); // 1
31
+ ```
32
+
33
+ ## Commands
34
+
35
+ ### `exportc init`
36
+
37
+ Initialize export in your Vite project:
38
+ - Creates `export/` directory with example server code
39
+ - Updates `vite.config.ts` with the export plugin
40
+ - Adds npm scripts for development and deployment
41
+
42
+ ### `exportc dev`
43
+
44
+ Start the Wrangler development server for your exports.
45
+
46
+ ### `exportc deploy`
47
+
48
+ Deploy your exports to Cloudflare Workers.
49
+
50
+ ## Vite Plugin
51
+
52
+ The `exportPlugin` transforms `export:/` imports:
53
+
54
+ ```typescript
55
+ // vite.config.ts
56
+ import { defineConfig } from "vite";
57
+ import { exportPlugin } from "exportc/vite";
58
+
59
+ export default defineConfig({
60
+ plugins: [
61
+ exportPlugin({
62
+ // Development server (default: http://localhost:8787)
63
+ dev: "http://localhost:8787",
64
+ // Production Worker URL (required for production builds)
65
+ production: "https://my-api.workers.dev",
66
+ }),
67
+ ],
68
+ });
69
+ ```
70
+
71
+ ## Project Structure
72
+
73
+ After running `exportc init`:
74
+
75
+ ```
76
+ my-vite-app/
77
+ ├── src/ # Your Vite app
78
+ ├── export/ # Server exports (Cloudflare Worker)
79
+ │ ├── index.ts # Your server code
80
+ │ └── package.json # Worker configuration
81
+ ├── export-env.d.ts # TypeScript declarations
82
+ └── vite.config.ts # Updated with exportPlugin
83
+ ```
84
+
85
+ ## TypeScript Support
86
+
87
+ The `export-env.d.ts` file provides type declarations for `export:/` imports. Update it when you add new exports:
88
+
89
+ ```typescript
90
+ // export-env.d.ts
91
+ declare module "export:/" {
92
+ export function hello(name: string): Promise<string>;
93
+ export function myNewFunction(): Promise<void>;
94
+ }
95
+
96
+ declare module "export:/utils" {
97
+ export function formatDate(date: Date): Promise<string>;
98
+ }
99
+ ```
100
+
101
+ ## License
102
+
103
+ MIT
@@ -0,0 +1,64 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { spawn } from "node:child_process";
6
+
7
+ export async function deploy(argv) {
8
+ const cwd = process.cwd();
9
+ const exportDir = path.join(cwd, "export");
10
+
11
+ // Check if export directory exists
12
+ if (!fs.existsSync(exportDir)) {
13
+ p.cancel(`No export/ directory found. Run ${pc.cyan("exportc init")} first.`);
14
+ process.exit(1);
15
+ }
16
+
17
+ // Check if dependencies are installed
18
+ const nodeModules = path.join(exportDir, "node_modules");
19
+ if (!fs.existsSync(nodeModules)) {
20
+ p.cancel(`Dependencies not installed. Run ${pc.cyan("cd export && npm install")} first.`);
21
+ process.exit(1);
22
+ }
23
+
24
+ p.intro(pc.bgCyan(pc.black(" exportc deploy ")));
25
+
26
+ const s = p.spinner();
27
+ s.start("Deploying to Cloudflare Workers...");
28
+
29
+ // Run deploy
30
+ const deployProcess = spawn("npm", ["run", "deploy"], {
31
+ cwd: exportDir,
32
+ stdio: "inherit",
33
+ shell: true,
34
+ });
35
+
36
+ const exitCode = await new Promise((resolve) => {
37
+ deployProcess.on("close", resolve);
38
+ deployProcess.on("error", () => resolve(1));
39
+ });
40
+
41
+ if (exitCode !== 0) {
42
+ s.stop("Deployment failed");
43
+ process.exit(1);
44
+ }
45
+
46
+ s.stop("Deployed successfully!");
47
+
48
+ // Read worker name from export/package.json
49
+ const exportPkgPath = path.join(exportDir, "package.json");
50
+ const exportPkg = JSON.parse(fs.readFileSync(exportPkgPath, "utf8"));
51
+ const workerName = exportPkg.name;
52
+
53
+ p.note(
54
+ `Your exports are now live at:
55
+ ${pc.cyan(`https://${workerName}.workers.dev/`)}
56
+
57
+ ${pc.bold("Update your Vite config for production:")}
58
+ ${pc.dim("// vite.config.ts")}
59
+ ${pc.cyan(`exportPlugin({ production: "https://${workerName}.workers.dev" })`)}`,
60
+ "Deployed"
61
+ );
62
+
63
+ p.outro("Done!");
64
+ }
@@ -0,0 +1,57 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { spawn } from "node:child_process";
6
+
7
+ export async function dev(argv) {
8
+ const cwd = process.cwd();
9
+ const exportDir = path.join(cwd, "export");
10
+
11
+ // Check if export directory exists
12
+ if (!fs.existsSync(exportDir)) {
13
+ p.cancel(`No export/ directory found. Run ${pc.cyan("exportc init")} first.`);
14
+ process.exit(1);
15
+ }
16
+
17
+ // Check if dependencies are installed
18
+ const nodeModules = path.join(exportDir, "node_modules");
19
+ if (!fs.existsSync(nodeModules)) {
20
+ p.cancel(`Dependencies not installed. Run ${pc.cyan("cd export && npm install")} first.`);
21
+ process.exit(1);
22
+ }
23
+
24
+ p.intro(pc.bgCyan(pc.black(" exportc dev ")));
25
+
26
+ const s = p.spinner();
27
+ s.start("Starting export dev server...");
28
+
29
+ // Generate types first
30
+ const generateTypes = spawn("npm", ["run", "dev"], {
31
+ cwd: exportDir,
32
+ stdio: "inherit",
33
+ shell: true,
34
+ });
35
+
36
+ generateTypes.on("error", (err) => {
37
+ s.stop("Failed to start dev server");
38
+ console.error(pc.red(err.message));
39
+ process.exit(1);
40
+ });
41
+
42
+ s.stop("Export dev server started!");
43
+
44
+ p.note(
45
+ `Wrangler dev server is running at ${pc.cyan("http://localhost:8787")}
46
+
47
+ ${pc.bold("In your Vite app:")}
48
+ ${pc.cyan(`import { hello } from "export:/";`)}
49
+ ${pc.cyan(`const message = await hello("World");`)}
50
+
51
+ ${pc.dim("Press Ctrl+C to stop")}`,
52
+ "Running"
53
+ );
54
+
55
+ // Keep the process running
56
+ await new Promise(() => {});
57
+ }
@@ -0,0 +1,310 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ export async function init(argv) {
10
+ const cwd = process.cwd();
11
+
12
+ p.intro(pc.bgCyan(pc.black(" exportc init ")));
13
+
14
+ // Check for Vite project
15
+ const viteConfigFiles = ["vite.config.ts", "vite.config.js", "vite.config.mts", "vite.config.mjs"];
16
+ const viteConfig = viteConfigFiles.find((f) => fs.existsSync(path.join(cwd, f)));
17
+
18
+ if (!viteConfig) {
19
+ p.cancel("No Vite config found. exportc currently only supports Vite projects.");
20
+ process.exit(1);
21
+ }
22
+
23
+ // Check for package.json
24
+ const pkgPath = path.join(cwd, "package.json");
25
+ if (!fs.existsSync(pkgPath)) {
26
+ p.cancel("No package.json found.");
27
+ process.exit(1);
28
+ }
29
+
30
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
31
+
32
+ // Check if already initialized
33
+ const exportDir = path.join(cwd, "export");
34
+ if (fs.existsSync(exportDir)) {
35
+ const overwrite = await p.confirm({
36
+ message: "export/ directory already exists. Continue and overwrite?",
37
+ initialValue: false,
38
+ });
39
+ if (p.isCancel(overwrite) || !overwrite) {
40
+ p.cancel("Operation cancelled.");
41
+ process.exit(0);
42
+ }
43
+ }
44
+
45
+ // Get worker name
46
+ const workerName = await p.text({
47
+ message: "Worker name:",
48
+ placeholder: pkg.name ? `${pkg.name}-api` : "my-api",
49
+ defaultValue: pkg.name ? `${pkg.name}-api` : "my-api",
50
+ validate: (v) => {
51
+ if (!v) return "Worker name is required";
52
+ if (!/^[a-z0-9-]+$/.test(v)) return "Use lowercase letters, numbers, and hyphens only";
53
+ },
54
+ });
55
+
56
+ if (p.isCancel(workerName)) {
57
+ p.cancel("Operation cancelled.");
58
+ process.exit(0);
59
+ }
60
+
61
+ // Check for TypeScript
62
+ const isTypeScript = viteConfig.endsWith(".ts") || viteConfig.endsWith(".mts") ||
63
+ fs.existsSync(path.join(cwd, "tsconfig.json"));
64
+
65
+ const s = p.spinner();
66
+ s.start("Initializing export...");
67
+
68
+ // Create export directory
69
+ if (!fs.existsSync(exportDir)) {
70
+ fs.mkdirSync(exportDir, { recursive: true });
71
+ }
72
+
73
+ // Create export/index.ts or index.js
74
+ const ext = isTypeScript ? "ts" : "js";
75
+ const indexPath = path.join(exportDir, `index.${ext}`);
76
+ if (!fs.existsSync(indexPath)) {
77
+ const template = isTypeScript
78
+ ? `// Server-side exports - these will be available to your client code
79
+ // Import from "export:/" in your client code
80
+
81
+ export async function hello(name: string): Promise<string> {
82
+ return \`Hello, \${name}!\`;
83
+ }
84
+
85
+ export function add(a: number, b: number): number {
86
+ return a + b;
87
+ }
88
+
89
+ export class Counter {
90
+ private count: number;
91
+
92
+ constructor(initial: number = 0) {
93
+ this.count = initial;
94
+ }
95
+
96
+ increment(): number {
97
+ return ++this.count;
98
+ }
99
+
100
+ getCount(): number {
101
+ return this.count;
102
+ }
103
+ }
104
+ `
105
+ : `// Server-side exports - these will be available to your client code
106
+ // Import from "export:/" in your client code
107
+
108
+ export async function hello(name) {
109
+ return \`Hello, \${name}!\`;
110
+ }
111
+
112
+ export function add(a, b) {
113
+ return a + b;
114
+ }
115
+
116
+ export class Counter {
117
+ #count;
118
+
119
+ constructor(initial = 0) {
120
+ this.#count = initial;
121
+ }
122
+
123
+ increment() {
124
+ return ++this.#count;
125
+ }
126
+
127
+ getCount() {
128
+ return this.#count;
129
+ }
130
+ }
131
+ `;
132
+ fs.writeFileSync(indexPath, template);
133
+ }
134
+
135
+ // Create export/package.json
136
+ const exportPkgPath = path.join(exportDir, "package.json");
137
+ const exportPkg = {
138
+ name: workerName,
139
+ private: true,
140
+ type: "module",
141
+ exports: "./",
142
+ scripts: {
143
+ dev: "generate-export-types && wrangler dev",
144
+ deploy: "generate-export-types && wrangler deploy",
145
+ },
146
+ dependencies: {
147
+ "export-runtime": "^0.0.14",
148
+ },
149
+ devDependencies: {
150
+ wrangler: "^4.0.0",
151
+ },
152
+ };
153
+
154
+ if (isTypeScript) {
155
+ exportPkg.devDependencies["@cloudflare/workers-types"] = "^4.20241127.0";
156
+ }
157
+
158
+ fs.writeFileSync(exportPkgPath, JSON.stringify(exportPkg, null, 2) + "\n");
159
+
160
+ // Update main package.json scripts
161
+ pkg.scripts = pkg.scripts || {};
162
+ if (!pkg.scripts["export:dev"]) {
163
+ pkg.scripts["export:dev"] = "cd export && npm run dev";
164
+ }
165
+ if (!pkg.scripts["export:deploy"]) {
166
+ pkg.scripts["export:deploy"] = "cd export && npm run deploy";
167
+ }
168
+
169
+ // Add exportc to devDependencies
170
+ pkg.devDependencies = pkg.devDependencies || {};
171
+ if (!pkg.devDependencies.exportc) {
172
+ pkg.devDependencies.exportc = "^0.0.1";
173
+ }
174
+
175
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
176
+
177
+ // Update vite.config
178
+ const viteConfigPath = path.join(cwd, viteConfig);
179
+ let viteConfigContent = fs.readFileSync(viteConfigPath, "utf8");
180
+
181
+ // Check if export plugin is already added
182
+ if (!viteConfigContent.includes("exportc/vite")) {
183
+ // Add import at the top
184
+ const importStatement = `import { exportPlugin } from "exportc/vite";\n`;
185
+
186
+ // Find the first import or the start of the file
187
+ const importMatch = viteConfigContent.match(/^(import\s+.+\s+from\s+['"].+['"];?\s*\n?)+/m);
188
+ if (importMatch) {
189
+ const insertPos = importMatch.index + importMatch[0].length;
190
+ viteConfigContent =
191
+ viteConfigContent.slice(0, insertPos) +
192
+ importStatement +
193
+ viteConfigContent.slice(insertPos);
194
+ } else {
195
+ viteConfigContent = importStatement + viteConfigContent;
196
+ }
197
+
198
+ // Add plugin to defineConfig
199
+ // Look for plugins array
200
+ if (viteConfigContent.includes("plugins:")) {
201
+ // Add to existing plugins array
202
+ viteConfigContent = viteConfigContent.replace(
203
+ /plugins:\s*\[/,
204
+ "plugins: [exportPlugin(), "
205
+ );
206
+ } else {
207
+ // Add plugins array to defineConfig
208
+ viteConfigContent = viteConfigContent.replace(
209
+ /defineConfig\(\s*\{/,
210
+ "defineConfig({\n plugins: [exportPlugin()],"
211
+ );
212
+ }
213
+
214
+ fs.writeFileSync(viteConfigPath, viteConfigContent);
215
+ }
216
+
217
+ // Create .gitignore entries for export directory
218
+ const exportGitignorePath = path.join(exportDir, ".gitignore");
219
+ const gitignoreContent = `# Generated files
220
+ .export-*.js
221
+ .export-*.d.ts
222
+ export.d.ts
223
+ wrangler.toml
224
+ .wrangler/
225
+ .dev.vars
226
+ `;
227
+ fs.writeFileSync(exportGitignorePath, gitignoreContent);
228
+
229
+ // Create export-env.d.ts in main project for TypeScript support
230
+ if (isTypeScript) {
231
+ const envDtsPath = path.join(cwd, "export-env.d.ts");
232
+ if (!fs.existsSync(envDtsPath)) {
233
+ const envDtsContent = `// Type declarations for export:/ imports
234
+ // This file is auto-generated by exportc. You can modify it to add custom types.
235
+
236
+ // Re-export types from your export directory
237
+ // Update this when you add new exports
238
+
239
+ declare module "export:/" {
240
+ export function hello(name: string): Promise<string>;
241
+ export function add(a: number, b: number): Promise<number>;
242
+ export class Counter {
243
+ constructor(initial?: number);
244
+ increment(): Promise<number>;
245
+ getCount(): Promise<number>;
246
+ [Symbol.dispose](): Promise<void>;
247
+ }
248
+ }
249
+
250
+ // Add more module declarations for subpaths:
251
+ // declare module "export:/utils" {
252
+ // export function myUtil(): Promise<void>;
253
+ // }
254
+ `;
255
+ fs.writeFileSync(envDtsPath, envDtsContent);
256
+ }
257
+
258
+ // Update tsconfig.json to include the type declarations
259
+ const tsconfigPath = path.join(cwd, "tsconfig.json");
260
+ if (fs.existsSync(tsconfigPath)) {
261
+ try {
262
+ const tsconfigContent = fs.readFileSync(tsconfigPath, "utf8");
263
+ const tsconfig = JSON.parse(tsconfigContent);
264
+
265
+ // Add export-env.d.ts to include if not already present
266
+ tsconfig.include = tsconfig.include || [];
267
+ if (!tsconfig.include.includes("export-env.d.ts")) {
268
+ tsconfig.include.push("export-env.d.ts");
269
+ fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n");
270
+ }
271
+ } catch {
272
+ // Ignore JSON parse errors (might have comments)
273
+ }
274
+ }
275
+ }
276
+
277
+ s.stop("Export initialized!");
278
+
279
+ const filesCreated = isTypeScript
280
+ ? `${pc.cyan("export/")}
281
+ ├── index.${ext} ${pc.dim("# Your server exports")}
282
+ ├── package.json ${pc.dim("# Worker configuration")}
283
+ └── .gitignore
284
+
285
+ ${pc.cyan("export-env.d.ts")} ${pc.dim("# Type declarations for export:/ imports")}`
286
+ : `${pc.cyan("export/")}
287
+ ├── index.${ext} ${pc.dim("# Your server exports")}
288
+ ├── package.json ${pc.dim("# Worker configuration")}
289
+ └── .gitignore`;
290
+
291
+ p.note(
292
+ `${filesCreated}
293
+
294
+ ${pc.bold("Next steps:")}
295
+
296
+ 1. Install dependencies:
297
+ ${pc.cyan("cd export && npm install")}
298
+
299
+ 2. Start development:
300
+ ${pc.cyan("npm run export:dev")} ${pc.dim("# In one terminal")}
301
+ ${pc.cyan("npm run dev")} ${pc.dim("# In another terminal")}
302
+
303
+ 3. Import in your client code:
304
+ ${pc.cyan(`import { hello } from "export:/";`)}
305
+ ${pc.cyan(`const message = await hello("World");`)}`,
306
+ "Created"
307
+ );
308
+
309
+ p.outro(`Run ${pc.cyan("cd export && npm install")} to get started!`);
310
+ }
package/index.js ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+
3
+ import mri from "mri";
4
+ import * as p from "@clack/prompts";
5
+ import pc from "picocolors";
6
+ import { init } from "./commands/init.js";
7
+ import { dev } from "./commands/dev.js";
8
+ import { deploy } from "./commands/deploy.js";
9
+
10
+ const argv = mri(process.argv.slice(2), {
11
+ alias: {
12
+ h: "help",
13
+ v: "version",
14
+ },
15
+ boolean: ["help", "version"],
16
+ });
17
+
18
+ const command = argv._[0];
19
+
20
+ const HELP = `
21
+ ${pc.bold("exportc")} - Add export to existing projects
22
+
23
+ ${pc.bold("Usage:")}
24
+ exportc <command> [options]
25
+
26
+ ${pc.bold("Commands:")}
27
+ init Initialize export in an existing Vite project
28
+ dev Start development server (Vite + Wrangler)
29
+ deploy Deploy to Cloudflare Workers
30
+
31
+ ${pc.bold("Options:")}
32
+ -h, --help Show this help message
33
+ -v, --version Show version number
34
+
35
+ ${pc.bold("Examples:")}
36
+ ${pc.dim("# Add export to your Vite project")}
37
+ npx exportc init
38
+
39
+ ${pc.dim("# Start development")}
40
+ npx exportc dev
41
+
42
+ ${pc.dim("# Deploy to Cloudflare")}
43
+ npx exportc deploy
44
+ `;
45
+
46
+ if (argv.help || !command) {
47
+ console.log(HELP);
48
+ process.exit(0);
49
+ }
50
+
51
+ if (argv.version) {
52
+ const pkg = await import("./package.json", { with: { type: "json" } });
53
+ console.log(pkg.default.version);
54
+ process.exit(0);
55
+ }
56
+
57
+ const commands = {
58
+ init,
59
+ dev,
60
+ deploy,
61
+ };
62
+
63
+ const handler = commands[command];
64
+ if (!handler) {
65
+ console.error(pc.red(`Unknown command: ${command}`));
66
+ console.log(HELP);
67
+ process.exit(1);
68
+ }
69
+
70
+ try {
71
+ await handler(argv);
72
+ } catch (err) {
73
+ p.cancel(err.message);
74
+ process.exit(1);
75
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "exportc",
3
+ "version": "0.0.1",
4
+ "description": "CLI to add export to existing projects",
5
+ "scripts": {
6
+ "test": "node --test test/*.test.mjs"
7
+ },
8
+ "keywords": [
9
+ "cloudflare",
10
+ "workers",
11
+ "vite",
12
+ "esm",
13
+ "rpc"
14
+ ],
15
+ "homepage": "https://github.com/ihasq/export#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/ihasq/export/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/ihasq/export.git",
22
+ "directory": "packages/exportc"
23
+ },
24
+ "license": "MIT",
25
+ "author": "ihasq",
26
+ "type": "module",
27
+ "bin": {
28
+ "exportc": "./index.js"
29
+ },
30
+ "exports": {
31
+ ".": "./index.js",
32
+ "./vite": "./vite-plugin.js"
33
+ },
34
+ "files": [
35
+ "index.js",
36
+ "vite-plugin.js",
37
+ "vite-plugin.d.ts",
38
+ "commands"
39
+ ],
40
+ "dependencies": {
41
+ "@clack/prompts": "^0.8.2",
42
+ "mri": "^1.2.0",
43
+ "picocolors": "^1.1.1"
44
+ },
45
+ "peerDependencies": {
46
+ "vite": "^5.0.0 || ^6.0.0"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "vite": {
50
+ "optional": true
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,41 @@
1
+ import type { Plugin } from "vite";
2
+
3
+ export interface ExportPluginOptions {
4
+ /**
5
+ * Development server URL
6
+ * @default "http://localhost:8787"
7
+ */
8
+ dev?: string;
9
+
10
+ /**
11
+ * Production Worker URL
12
+ * Required for production builds
13
+ * @example "https://my-api.workers.dev"
14
+ */
15
+ production?: string;
16
+ }
17
+
18
+ /**
19
+ * Vite plugin for export integration
20
+ *
21
+ * Allows importing server exports using the "export:/" prefix:
22
+ * ```ts
23
+ * import { hello } from "export:/";
24
+ * import { utils } from "export:/utils";
25
+ * ```
26
+ *
27
+ * @example
28
+ * // vite.config.ts
29
+ * import { exportPlugin } from "exportc/vite";
30
+ *
31
+ * export default defineConfig({
32
+ * plugins: [
33
+ * exportPlugin({
34
+ * production: "https://my-api.workers.dev"
35
+ * })
36
+ * ]
37
+ * });
38
+ */
39
+ export function exportPlugin(options?: ExportPluginOptions): Plugin;
40
+
41
+ export default exportPlugin;
package/vite-plugin.js ADDED
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Vite plugin for export integration
3
+ *
4
+ * Allows importing server exports using the "export:/" prefix:
5
+ * import { hello } from "export:/";
6
+ * import { utils } from "export:/utils";
7
+ *
8
+ * In development, proxies to local Wrangler dev server (localhost:8787)
9
+ * In production, resolves to the deployed Worker URL
10
+ */
11
+
12
+ const EXPORT_PREFIX = "export:";
13
+ const DEFAULT_DEV_URL = "http://localhost:8787";
14
+
15
+ /**
16
+ * @param {Object} options
17
+ * @param {string} [options.dev] - Development server URL (default: http://localhost:8787)
18
+ * @param {string} [options.production] - Production Worker URL (required for production builds)
19
+ * @returns {import('vite').Plugin}
20
+ */
21
+ export function exportPlugin(options = {}) {
22
+ const devUrl = options.dev || DEFAULT_DEV_URL;
23
+ const prodUrl = options.production;
24
+
25
+ let isDev = true;
26
+
27
+ return {
28
+ name: "vite-plugin-export",
29
+
30
+ config(config, { command }) {
31
+ isDev = command === "serve";
32
+
33
+ // Add proxy for development
34
+ if (isDev) {
35
+ return {
36
+ server: {
37
+ proxy: {
38
+ // Proxy WebSocket connections
39
+ "^/__export_ws__": {
40
+ target: devUrl.replace(/^http/, "ws"),
41
+ ws: true,
42
+ rewrite: (path) => path.replace("/__export_ws__", ""),
43
+ },
44
+ },
45
+ },
46
+ };
47
+ }
48
+ },
49
+
50
+ resolveId(source) {
51
+ if (source.startsWith(EXPORT_PREFIX)) {
52
+ // Mark as external - we'll handle it in load
53
+ return { id: source, external: false };
54
+ }
55
+ return null;
56
+ },
57
+
58
+ load(id) {
59
+ if (!id.startsWith(EXPORT_PREFIX)) {
60
+ return null;
61
+ }
62
+
63
+ // Extract the path after "export:"
64
+ const exportPath = id.slice(EXPORT_PREFIX.length) || "/";
65
+
66
+ // In development, use the local dev server
67
+ // In production, use the configured production URL
68
+ const baseUrl = isDev ? devUrl : prodUrl;
69
+
70
+ if (!baseUrl) {
71
+ if (!isDev) {
72
+ this.error(
73
+ `[exportc] Production URL not configured. Add { production: "https://your-worker.workers.dev" } to exportPlugin() in vite.config.`
74
+ );
75
+ }
76
+ this.error(`[exportc] Could not resolve base URL for export imports.`);
77
+ }
78
+
79
+ // Build the full URL
80
+ const fullUrl = new URL(exportPath, baseUrl).href;
81
+
82
+ // Generate a module that re-exports from the Worker URL
83
+ // For development, we dynamically import from the Worker
84
+ // For production, we use the deployed URL
85
+ const code = `
86
+ // Auto-generated by exportc/vite
87
+ // Importing from: ${fullUrl}
88
+ export * from "${fullUrl}";
89
+ export { default } from "${fullUrl}";
90
+ `;
91
+
92
+ return code;
93
+ },
94
+
95
+ // Transform import statements in the final bundle for production
96
+ transform(code, id) {
97
+ // Skip node_modules and non-JS files
98
+ if (id.includes("node_modules") || !/\.(js|ts|jsx|tsx|vue|svelte)$/.test(id)) {
99
+ return null;
100
+ }
101
+
102
+ // Replace dynamic imports of export: URLs
103
+ if (code.includes('import("export:') || code.includes("import('export:")) {
104
+ const baseUrl = isDev ? devUrl : prodUrl;
105
+ if (!baseUrl && !isDev) {
106
+ this.warn(
107
+ `[exportc] Production URL not configured for dynamic imports in ${id}`
108
+ );
109
+ return null;
110
+ }
111
+
112
+ // Replace export:/ with the actual URL
113
+ const transformed = code.replace(
114
+ /import\((['"])export:([^'"]*)\1\)/g,
115
+ (match, quote, path) => {
116
+ const fullUrl = new URL(path || "/", baseUrl).href;
117
+ return `import(${quote}${fullUrl}${quote})`;
118
+ }
119
+ );
120
+
121
+ if (transformed !== code) {
122
+ return { code: transformed, map: null };
123
+ }
124
+ }
125
+
126
+ return null;
127
+ },
128
+
129
+ // Generate TypeScript declarations hint
130
+ buildStart() {
131
+ if (isDev) {
132
+ this.info(
133
+ `[exportc] Development mode - proxying to ${devUrl}`
134
+ );
135
+ } else if (prodUrl) {
136
+ this.info(
137
+ `[exportc] Production mode - using ${prodUrl}`
138
+ );
139
+ }
140
+ },
141
+ };
142
+ }
143
+
144
+ export default exportPlugin;