exportc 0.0.3 → 0.0.4

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
@@ -20,11 +20,11 @@ npm run export
20
20
 
21
21
  ## Usage
22
22
 
23
- After initialization, import your server exports using the `export:/` prefix:
23
+ After initialization, import your server exports using the `export/` prefix:
24
24
 
25
25
  ```typescript
26
26
  // In your Vite app
27
- import { hello, Counter } from "export:/";
27
+ import { hello, Counter } from "export/";
28
28
 
29
29
  const message = await hello("World"); // "Hello, World!"
30
30
 
@@ -51,7 +51,7 @@ Deploy your exports to Cloudflare Workers.
51
51
 
52
52
  ## Vite Plugin
53
53
 
54
- The `exportPlugin` automatically starts Wrangler and transforms `export:/` imports:
54
+ The `exportPlugin` automatically starts Wrangler and transforms `export/` imports:
55
55
 
56
56
  ```typescript
57
57
  // vite.config.ts
@@ -70,12 +70,12 @@ export default defineConfig({
70
70
  **Development** (`npm run dev`):
71
71
  1. Automatically starts Wrangler dev server
72
72
  2. Waits for it to be ready
73
- 3. Transforms `export:/` imports to `http://localhost:8787`
73
+ 3. Transforms `export/` imports to `http://localhost:8787`
74
74
 
75
75
  **Production** (`npm run export`):
76
76
  1. Builds Vite app
77
77
  2. Deploys to Workers Sites (static assets + server exports)
78
- 3. `export:/` imports resolve to `https://{worker-name}.workers.dev`
78
+ 3. `export/` imports resolve to `https://{worker-name}.workers.dev`
79
79
 
80
80
  ## Project Structure
81
81
 
@@ -93,20 +93,26 @@ my-vite-app/
93
93
 
94
94
  ## TypeScript Support
95
95
 
96
- The `export-env.d.ts` file provides type declarations for `export:/` imports. Update it when you add new exports:
96
+ The `export-env.d.ts` file is **automatically generated** when you run `npm run dev`. The Vite plugin watches for changes to your export files and regenerates type declarations automatically.
97
97
 
98
98
  ```typescript
99
- // export-env.d.ts
100
- declare module "export:/" {
99
+ // export-env.d.ts (auto-generated)
100
+ declare module "export/" {
101
101
  export function hello(name: string): Promise<string>;
102
- export function myNewFunction(): Promise<void>;
102
+ export class Counter {
103
+ constructor(initial?: number);
104
+ increment(): Promise<number>;
105
+ [Symbol.dispose](): Promise<void>;
106
+ }
103
107
  }
104
108
 
105
- declare module "export:/utils" {
109
+ declare module "export/utils" {
106
110
  export function formatDate(date: Date): Promise<string>;
107
111
  }
108
112
  ```
109
113
 
114
+ Types are inferred from your actual export code, so you get accurate type information with zero manual maintenance.
115
+
110
116
  ## License
111
117
 
112
118
  MIT
package/commands/dev.js CHANGED
@@ -45,7 +45,7 @@ export async function dev(argv) {
45
45
  `Wrangler dev server is running at ${pc.cyan("http://localhost:8787")}
46
46
 
47
47
  ${pc.bold("In your Vite app:")}
48
- ${pc.cyan(`import { hello } from "export:/";`)}
48
+ ${pc.cyan(`import { hello } from "export/";`)}
49
49
  ${pc.cyan(`const message = await hello("World");`)}
50
50
 
51
51
  ${pc.dim("Press Ctrl+C to stop")}`,
package/commands/init.js CHANGED
@@ -76,7 +76,7 @@ export async function init(argv) {
76
76
  if (!fs.existsSync(indexPath)) {
77
77
  const template = isTypeScript
78
78
  ? `// Server-side exports - these will be available to your client code
79
- // Import from "export:/" in your client code
79
+ // Import from "export/" in your client code
80
80
 
81
81
  export async function hello(name: string): Promise<string> {
82
82
  return \`Hello, \${name}!\`;
@@ -103,7 +103,7 @@ export class Counter {
103
103
  }
104
104
  `
105
105
  : `// Server-side exports - these will be available to your client code
106
- // Import from "export:/" in your client code
106
+ // Import from "export/" in your client code
107
107
 
108
108
  export async function hello(name) {
109
109
  return \`Hello, \${name}!\`;
@@ -237,13 +237,13 @@ wrangler.toml
237
237
  if (isTypeScript) {
238
238
  const envDtsPath = path.join(cwd, "export-env.d.ts");
239
239
  if (!fs.existsSync(envDtsPath)) {
240
- const envDtsContent = `// Type declarations for export:/ imports
240
+ const envDtsContent = `// Type declarations for export/ imports
241
241
  // This file is auto-generated by exportc. You can modify it to add custom types.
242
242
 
243
243
  // Re-export types from your export directory
244
244
  // Update this when you add new exports
245
245
 
246
- declare module "export:/" {
246
+ declare module "export/" {
247
247
  export function hello(name: string): Promise<string>;
248
248
  export function add(a: number, b: number): Promise<number>;
249
249
  export class Counter {
@@ -255,7 +255,7 @@ declare module "export:/" {
255
255
  }
256
256
 
257
257
  // Add more module declarations for subpaths:
258
- // declare module "export:/utils" {
258
+ // declare module "export/utils" {
259
259
  // export function myUtil(): Promise<void>;
260
260
  // }
261
261
  `;
@@ -289,7 +289,7 @@ declare module "export:/" {
289
289
  ├── package.json ${pc.dim("# Worker configuration")}
290
290
  └── .gitignore
291
291
 
292
- ${pc.cyan("export-env.d.ts")} ${pc.dim("# Type declarations for export:/ imports")}`
292
+ ${pc.cyan("export-env.d.ts")} ${pc.dim("# Type declarations for export/ imports")}`
293
293
  : `${pc.cyan("export/")}
294
294
  ├── index.${ext} ${pc.dim("# Your server exports")}
295
295
  ├── package.json ${pc.dim("# Worker configuration")}
@@ -307,7 +307,7 @@ ${pc.bold("Next steps:")}
307
307
  ${pc.cyan("npm run dev")}
308
308
 
309
309
  3. Import in your client code:
310
- ${pc.cyan(`import { hello } from "export:/";`)}
310
+ ${pc.cyan(`import { hello } from "export/";`)}
311
311
  ${pc.cyan(`const message = await hello("World");`)}
312
312
 
313
313
  4. Deploy to Cloudflare Workers Sites:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exportc",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "CLI to add export to existing projects",
5
5
  "scripts": {
6
6
  "test": "node --test test/*.test.mjs"
package/vite-plugin.d.ts CHANGED
@@ -37,11 +37,11 @@ export interface ExportPluginOptions {
37
37
  * Vite plugin for export integration
38
38
  *
39
39
  * Automatically starts Wrangler when you run `npm run dev` and allows
40
- * importing server exports using the "export:/" prefix:
40
+ * importing server exports using the "export/" prefix:
41
41
  *
42
42
  * ```ts
43
- * import { hello } from "export:/";
44
- * import { utils } from "export:/utils";
43
+ * import { hello } from "export/";
44
+ * import { utils } from "export/utils";
45
45
  * ```
46
46
  *
47
47
  * @example
package/vite-plugin.js CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Vite plugin for export integration
3
3
  *
4
- * Allows importing server exports using the "export:/" prefix:
5
- * import { hello } from "export:/";
6
- * import { utils } from "export:/utils";
4
+ * Allows importing server exports using the "export/" prefix:
5
+ * import { hello } from "export/";
6
+ * import { utils } from "export/utils";
7
7
  *
8
8
  * In development:
9
9
  * - Automatically starts Wrangler dev server
@@ -14,10 +14,10 @@
14
14
  */
15
15
 
16
16
  import { spawn } from "node:child_process";
17
- import { existsSync, readFileSync } from "node:fs";
17
+ import { existsSync, readFileSync, writeFileSync, watchFile, unwatchFile } from "node:fs";
18
18
  import { resolve } from "node:path";
19
19
 
20
- const EXPORT_PREFIX = "export:";
20
+ const EXPORT_PREFIX = "export/";
21
21
  const DEFAULT_DEV_PORT = 8787;
22
22
 
23
23
  /**
@@ -55,6 +55,44 @@ export function exportPlugin(options = {}) {
55
55
  return null;
56
56
  };
57
57
 
58
+ // Generate export-env.d.ts from .export-types.js
59
+ const generateTypeDeclarations = (root) => {
60
+ try {
61
+ const typesFilePath = resolve(root, exportDir, ".export-types.js");
62
+ if (!existsSync(typesFilePath)) return;
63
+
64
+ const typesContent = readFileSync(typesFilePath, "utf8");
65
+ // Extract the first line which contains the default export
66
+ const firstLine = typesContent.split('\n')[0];
67
+ if (!firstLine.startsWith('export default ')) return;
68
+
69
+ // Remove "export default " prefix and trailing ";"
70
+ const jsonStr = firstLine.slice('export default '.length).replace(/;$/, '');
71
+
72
+ // Parse the types map
73
+ const typesMap = JSON.parse(jsonStr);
74
+
75
+ // Generate declare module statements
76
+ const declarations = ['// Auto-generated by exportc. Do not edit manually.', '// Re-run "npm run dev" to regenerate after changing export/ files.', ''];
77
+
78
+ for (const [route, types] of Object.entries(typesMap)) {
79
+ const modulePath = route === "" ? "export/" : `export/${route}`;
80
+ declarations.push(`declare module "${modulePath}" {`);
81
+ // Indent the type declarations
82
+ const indented = types.split('\n').map(line => line ? ` ${line}` : '').join('\n');
83
+ declarations.push(indented);
84
+ declarations.push('}');
85
+ declarations.push('');
86
+ }
87
+
88
+ const envDtsPath = resolve(root, "export-env.d.ts");
89
+ writeFileSync(envDtsPath, declarations.join('\n'));
90
+ console.log(`[exportc] Generated type declarations: export-env.d.ts`);
91
+ } catch (err) {
92
+ console.warn(`[exportc] Failed to generate type declarations:`, err.message);
93
+ }
94
+ };
95
+
58
96
  const startWrangler = (root) => {
59
97
  const exportPath = resolve(root, exportDir);
60
98
 
@@ -164,8 +202,27 @@ export function exportPlugin(options = {}) {
164
202
  const root = server.config.root || process.cwd();
165
203
  wranglerReadyPromise = startWrangler(root);
166
204
 
205
+ // Generate types after Wrangler is ready
206
+ wranglerReadyPromise.then(() => {
207
+ // Initial generation
208
+ setTimeout(() => generateTypeDeclarations(root), 1000);
209
+
210
+ // Watch for changes to .export-types.js
211
+ const typesFilePath = resolve(root, exportDir, ".export-types.js");
212
+ if (existsSync(typesFilePath)) {
213
+ watchFile(typesFilePath, { interval: 1000 }, () => {
214
+ generateTypeDeclarations(root);
215
+ });
216
+ }
217
+ });
218
+
167
219
  // Cleanup on server close
168
- server.httpServer?.on("close", stopWrangler);
220
+ server.httpServer?.on("close", () => {
221
+ stopWrangler();
222
+ // Stop watching
223
+ const typesFilePath = resolve(root, exportDir, ".export-types.js");
224
+ unwatchFile(typesFilePath);
225
+ });
169
226
  }
170
227
  },
171
228
 
@@ -233,7 +290,7 @@ export { default } from "${fullUrl}";
233
290
  return null;
234
291
  }
235
292
 
236
- if (code.includes('import("export:') || code.includes("import('export:")) {
293
+ if (code.includes('import("export/') || code.includes("import('export/")) {
237
294
  const baseUrl = isDev ? devUrl : prodUrl;
238
295
  if (!baseUrl && !isDev) {
239
296
  this.warn(`[exportc] Production URL not configured for dynamic imports in ${id}`);
@@ -241,7 +298,7 @@ export { default } from "${fullUrl}";
241
298
  }
242
299
 
243
300
  const transformed = code.replace(
244
- /import\((['"])export:([^'"]*)\1\)/g,
301
+ /import\((['"])export\/([^'"]*)\1\)/g,
245
302
  (match, quote, path) => {
246
303
  const fullUrl = new URL(path || "/", baseUrl).href;
247
304
  return `import(${quote}${fullUrl}${quote})`;