exportc 0.0.1 → 0.0.2
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 +14 -8
- package/commands/init.js +16 -12
- package/package.json +1 -1
- package/vite-plugin.d.ts +22 -1
- package/vite-plugin.js +144 -47
package/README.md
CHANGED
|
@@ -8,12 +8,11 @@ Add [export](https://github.com/ihasq/export) to existing Vite projects.
|
|
|
8
8
|
# In your existing Vite project
|
|
9
9
|
npx exportc init
|
|
10
10
|
|
|
11
|
-
# Install dependencies
|
|
12
|
-
cd export && npm install
|
|
11
|
+
# Install export dependencies
|
|
12
|
+
cd export && npm install && cd ..
|
|
13
13
|
|
|
14
|
-
# Start development (
|
|
15
|
-
npm run
|
|
16
|
-
npm run dev # Terminal 2: Vite dev server
|
|
14
|
+
# Start development (Wrangler starts automatically!)
|
|
15
|
+
npm run dev
|
|
17
16
|
```
|
|
18
17
|
|
|
19
18
|
## Usage
|
|
@@ -49,7 +48,7 @@ Deploy your exports to Cloudflare Workers.
|
|
|
49
48
|
|
|
50
49
|
## Vite Plugin
|
|
51
50
|
|
|
52
|
-
The `exportPlugin` transforms `export:/` imports:
|
|
51
|
+
The `exportPlugin` automatically starts Wrangler and transforms `export:/` imports:
|
|
53
52
|
|
|
54
53
|
```typescript
|
|
55
54
|
// vite.config.ts
|
|
@@ -59,15 +58,22 @@ import { exportPlugin } from "exportc/vite";
|
|
|
59
58
|
export default defineConfig({
|
|
60
59
|
plugins: [
|
|
61
60
|
exportPlugin({
|
|
62
|
-
// Development server (default: http://localhost:8787)
|
|
63
|
-
dev: "http://localhost:8787",
|
|
64
61
|
// Production Worker URL (required for production builds)
|
|
65
62
|
production: "https://my-api.workers.dev",
|
|
63
|
+
|
|
64
|
+
// Optional: customize dev server
|
|
65
|
+
// port: 8787,
|
|
66
|
+
// autoStart: true, // Auto-start Wrangler (default: true)
|
|
66
67
|
}),
|
|
67
68
|
],
|
|
68
69
|
});
|
|
69
70
|
```
|
|
70
71
|
|
|
72
|
+
When you run `npm run dev`, the plugin:
|
|
73
|
+
1. Automatically starts Wrangler dev server
|
|
74
|
+
2. Waits for it to be ready
|
|
75
|
+
3. Transforms `export:/` imports to point to the local server
|
|
76
|
+
|
|
71
77
|
## Project Structure
|
|
72
78
|
|
|
73
79
|
After running `exportc init`:
|
package/commands/init.js
CHANGED
|
@@ -157,13 +157,15 @@ export class Counter {
|
|
|
157
157
|
|
|
158
158
|
fs.writeFileSync(exportPkgPath, JSON.stringify(exportPkg, null, 2) + "\n");
|
|
159
159
|
|
|
160
|
-
// Update main package.json
|
|
160
|
+
// Update main package.json
|
|
161
161
|
pkg.scripts = pkg.scripts || {};
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
|
|
163
|
+
// Add deploy script for export
|
|
164
|
+
if (!pkg.scripts["deploy"]) {
|
|
165
|
+
pkg.scripts["deploy"] = "cd export && npm run deploy";
|
|
166
|
+
} else if (!pkg.scripts["deploy"].includes("export")) {
|
|
167
|
+
// Add export deploy to existing deploy script
|
|
168
|
+
pkg.scripts["deploy:export"] = "cd export && npm run deploy";
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
// Add exportc to devDependencies
|
|
@@ -293,16 +295,18 @@ ${pc.cyan("export-env.d.ts")} ${pc.dim("# Type declarations for export:/ imp
|
|
|
293
295
|
|
|
294
296
|
${pc.bold("Next steps:")}
|
|
295
297
|
|
|
296
|
-
1. Install dependencies:
|
|
297
|
-
${pc.cyan("cd export && npm install")}
|
|
298
|
+
1. Install export dependencies:
|
|
299
|
+
${pc.cyan("cd export && npm install && cd ..")}
|
|
298
300
|
|
|
299
|
-
2. Start development:
|
|
300
|
-
${pc.cyan("npm run
|
|
301
|
-
${pc.cyan("npm run dev")} ${pc.dim("# In another terminal")}
|
|
301
|
+
2. Start development (Vite + Wrangler auto-start):
|
|
302
|
+
${pc.cyan("npm run dev")}
|
|
302
303
|
|
|
303
304
|
3. Import in your client code:
|
|
304
305
|
${pc.cyan(`import { hello } from "export:/";`)}
|
|
305
|
-
${pc.cyan(`const message = await hello("World");`)}
|
|
306
|
+
${pc.cyan(`const message = await hello("World");`)}
|
|
307
|
+
|
|
308
|
+
4. Deploy to Cloudflare:
|
|
309
|
+
${pc.cyan("npm run deploy")}`,
|
|
306
310
|
"Created"
|
|
307
311
|
);
|
|
308
312
|
|
package/package.json
CHANGED
package/vite-plugin.d.ts
CHANGED
|
@@ -7,18 +7,38 @@ export interface ExportPluginOptions {
|
|
|
7
7
|
*/
|
|
8
8
|
dev?: string;
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Development server port
|
|
12
|
+
* @default 8787
|
|
13
|
+
*/
|
|
14
|
+
port?: number;
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* Production Worker URL
|
|
12
18
|
* Required for production builds
|
|
13
19
|
* @example "https://my-api.workers.dev"
|
|
14
20
|
*/
|
|
15
21
|
production?: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Export directory path (relative to project root)
|
|
25
|
+
* @default "./export"
|
|
26
|
+
*/
|
|
27
|
+
exportDir?: string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Auto-start Wrangler dev server when running Vite in dev mode
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
autoStart?: boolean;
|
|
16
34
|
}
|
|
17
35
|
|
|
18
36
|
/**
|
|
19
37
|
* Vite plugin for export integration
|
|
20
38
|
*
|
|
21
|
-
*
|
|
39
|
+
* Automatically starts Wrangler when you run `npm run dev` and allows
|
|
40
|
+
* importing server exports using the "export:/" prefix:
|
|
41
|
+
*
|
|
22
42
|
* ```ts
|
|
23
43
|
* import { hello } from "export:/";
|
|
24
44
|
* import { utils } from "export:/utils";
|
|
@@ -31,6 +51,7 @@ export interface ExportPluginOptions {
|
|
|
31
51
|
* export default defineConfig({
|
|
32
52
|
* plugins: [
|
|
33
53
|
* exportPlugin({
|
|
54
|
+
* // Required for production builds
|
|
34
55
|
* production: "https://my-api.workers.dev"
|
|
35
56
|
* })
|
|
36
57
|
* ]
|
package/vite-plugin.js
CHANGED
|
@@ -5,51 +5,175 @@
|
|
|
5
5
|
* import { hello } from "export:/";
|
|
6
6
|
* import { utils } from "export:/utils";
|
|
7
7
|
*
|
|
8
|
-
* In development
|
|
9
|
-
*
|
|
8
|
+
* In development:
|
|
9
|
+
* - Automatically starts Wrangler dev server
|
|
10
|
+
* - Proxies to local Wrangler (localhost:8787)
|
|
11
|
+
*
|
|
12
|
+
* In production:
|
|
13
|
+
* - Resolves to the deployed Worker URL
|
|
10
14
|
*/
|
|
11
15
|
|
|
16
|
+
import { spawn } from "node:child_process";
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
import { resolve } from "node:path";
|
|
19
|
+
|
|
12
20
|
const EXPORT_PREFIX = "export:";
|
|
13
|
-
const
|
|
21
|
+
const DEFAULT_DEV_PORT = 8787;
|
|
14
22
|
|
|
15
23
|
/**
|
|
16
24
|
* @param {Object} options
|
|
17
25
|
* @param {string} [options.dev] - Development server URL (default: http://localhost:8787)
|
|
18
26
|
* @param {string} [options.production] - Production Worker URL (required for production builds)
|
|
27
|
+
* @param {string} [options.exportDir] - Export directory (default: ./export)
|
|
28
|
+
* @param {boolean} [options.autoStart] - Auto-start Wrangler in dev mode (default: true)
|
|
19
29
|
* @returns {import('vite').Plugin}
|
|
20
30
|
*/
|
|
21
31
|
export function exportPlugin(options = {}) {
|
|
22
|
-
const
|
|
32
|
+
const devPort = options.port || DEFAULT_DEV_PORT;
|
|
33
|
+
const devUrl = options.dev || `http://localhost:${devPort}`;
|
|
23
34
|
const prodUrl = options.production;
|
|
35
|
+
const exportDir = options.exportDir || "./export";
|
|
36
|
+
const autoStart = options.autoStart !== false;
|
|
24
37
|
|
|
25
38
|
let isDev = true;
|
|
39
|
+
let wranglerProcess = null;
|
|
40
|
+
let wranglerReady = false;
|
|
41
|
+
let wranglerReadyPromise = null;
|
|
42
|
+
|
|
43
|
+
const startWrangler = (root) => {
|
|
44
|
+
const exportPath = resolve(root, exportDir);
|
|
45
|
+
|
|
46
|
+
if (!existsSync(exportPath)) {
|
|
47
|
+
console.warn(`[exportc] Export directory not found: ${exportPath}`);
|
|
48
|
+
console.warn(`[exportc] Run 'npx exportc init' to initialize.`);
|
|
49
|
+
return Promise.resolve();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const nodeModulesPath = resolve(exportPath, "node_modules");
|
|
53
|
+
if (!existsSync(nodeModulesPath)) {
|
|
54
|
+
console.warn(`[exportc] Dependencies not installed in ${exportPath}`);
|
|
55
|
+
console.warn(`[exportc] Run 'cd ${exportDir} && npm install' first.`);
|
|
56
|
+
return Promise.resolve();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return new Promise((resolveReady) => {
|
|
60
|
+
console.log(`[exportc] Starting Wrangler dev server...`);
|
|
61
|
+
|
|
62
|
+
// Generate types first, then start wrangler
|
|
63
|
+
const generateTypes = spawn("npm", ["run", "dev"], {
|
|
64
|
+
cwd: exportPath,
|
|
65
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
+
shell: true,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
wranglerProcess = generateTypes;
|
|
70
|
+
|
|
71
|
+
generateTypes.stdout.on("data", (data) => {
|
|
72
|
+
const output = data.toString();
|
|
73
|
+
process.stdout.write(`[export] ${output}`);
|
|
74
|
+
|
|
75
|
+
// Detect when wrangler is ready
|
|
76
|
+
if (output.includes("Ready on") || output.includes("Listening on") || output.includes("localhost")) {
|
|
77
|
+
if (!wranglerReady) {
|
|
78
|
+
wranglerReady = true;
|
|
79
|
+
console.log(`[exportc] Wrangler ready at ${devUrl}`);
|
|
80
|
+
resolveReady();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
generateTypes.stderr.on("data", (data) => {
|
|
86
|
+
const output = data.toString();
|
|
87
|
+
// Filter out noisy warnings
|
|
88
|
+
if (!output.includes("ExperimentalWarning")) {
|
|
89
|
+
process.stderr.write(`[export] ${output}`);
|
|
90
|
+
}
|
|
91
|
+
// Also check stderr for ready message
|
|
92
|
+
if (output.includes("Ready on") || output.includes("Listening on") || output.includes("localhost:8787")) {
|
|
93
|
+
if (!wranglerReady) {
|
|
94
|
+
wranglerReady = true;
|
|
95
|
+
console.log(`[exportc] Wrangler ready at ${devUrl}`);
|
|
96
|
+
resolveReady();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
generateTypes.on("error", (err) => {
|
|
102
|
+
console.error(`[exportc] Failed to start Wrangler:`, err.message);
|
|
103
|
+
resolveReady();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
generateTypes.on("close", (code) => {
|
|
107
|
+
if (code !== 0 && code !== null) {
|
|
108
|
+
console.error(`[exportc] Wrangler exited with code ${code}`);
|
|
109
|
+
}
|
|
110
|
+
wranglerProcess = null;
|
|
111
|
+
resolveReady();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Timeout after 30 seconds
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
if (!wranglerReady) {
|
|
117
|
+
console.warn(`[exportc] Wrangler startup timeout, continuing anyway...`);
|
|
118
|
+
wranglerReady = true;
|
|
119
|
+
resolveReady();
|
|
120
|
+
}
|
|
121
|
+
}, 30000);
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const stopWrangler = () => {
|
|
126
|
+
if (wranglerProcess) {
|
|
127
|
+
console.log(`[exportc] Stopping Wrangler...`);
|
|
128
|
+
wranglerProcess.kill("SIGTERM");
|
|
129
|
+
wranglerProcess = null;
|
|
130
|
+
wranglerReady = false;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
26
133
|
|
|
27
134
|
return {
|
|
28
135
|
name: "vite-plugin-export",
|
|
29
136
|
|
|
30
137
|
config(config, { command }) {
|
|
31
138
|
isDev = command === "serve";
|
|
139
|
+
return {};
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
configureServer(server) {
|
|
143
|
+
if (isDev && autoStart) {
|
|
144
|
+
const root = server.config.root || process.cwd();
|
|
145
|
+
wranglerReadyPromise = startWrangler(root);
|
|
146
|
+
|
|
147
|
+
// Cleanup on server close
|
|
148
|
+
server.httpServer?.on("close", stopWrangler);
|
|
149
|
+
}
|
|
150
|
+
},
|
|
32
151
|
|
|
33
|
-
|
|
152
|
+
async buildStart() {
|
|
34
153
|
if (isDev) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
};
|
|
154
|
+
if (autoStart && wranglerReadyPromise) {
|
|
155
|
+
await wranglerReadyPromise;
|
|
156
|
+
}
|
|
157
|
+
this.info(`Development mode - using ${devUrl}`);
|
|
158
|
+
} else if (prodUrl) {
|
|
159
|
+
this.info(`Production mode - using ${prodUrl}`);
|
|
160
|
+
} else {
|
|
161
|
+
this.warn(`Production URL not configured. Add 'production' option to exportPlugin().`);
|
|
47
162
|
}
|
|
48
163
|
},
|
|
49
164
|
|
|
165
|
+
buildEnd() {
|
|
166
|
+
if (!isDev) {
|
|
167
|
+
stopWrangler();
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
closeBundle() {
|
|
172
|
+
stopWrangler();
|
|
173
|
+
},
|
|
174
|
+
|
|
50
175
|
resolveId(source) {
|
|
51
176
|
if (source.startsWith(EXPORT_PREFIX)) {
|
|
52
|
-
// Mark as external - we'll handle it in load
|
|
53
177
|
return { id: source, external: false };
|
|
54
178
|
}
|
|
55
179
|
return null;
|
|
@@ -60,28 +184,20 @@ export function exportPlugin(options = {}) {
|
|
|
60
184
|
return null;
|
|
61
185
|
}
|
|
62
186
|
|
|
63
|
-
// Extract the path after "export:"
|
|
64
187
|
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
188
|
const baseUrl = isDev ? devUrl : prodUrl;
|
|
69
189
|
|
|
70
190
|
if (!baseUrl) {
|
|
71
191
|
if (!isDev) {
|
|
72
192
|
this.error(
|
|
73
|
-
`[exportc] Production URL not configured. Add { production: "https://your-worker.workers.dev" } to exportPlugin()
|
|
193
|
+
`[exportc] Production URL not configured. Add { production: "https://your-worker.workers.dev" } to exportPlugin().`
|
|
74
194
|
);
|
|
75
195
|
}
|
|
76
196
|
this.error(`[exportc] Could not resolve base URL for export imports.`);
|
|
77
197
|
}
|
|
78
198
|
|
|
79
|
-
// Build the full URL
|
|
80
199
|
const fullUrl = new URL(exportPath, baseUrl).href;
|
|
81
200
|
|
|
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
201
|
const code = `
|
|
86
202
|
// Auto-generated by exportc/vite
|
|
87
203
|
// Importing from: ${fullUrl}
|
|
@@ -92,24 +208,18 @@ export { default } from "${fullUrl}";
|
|
|
92
208
|
return code;
|
|
93
209
|
},
|
|
94
210
|
|
|
95
|
-
// Transform import statements in the final bundle for production
|
|
96
211
|
transform(code, id) {
|
|
97
|
-
// Skip node_modules and non-JS files
|
|
98
212
|
if (id.includes("node_modules") || !/\.(js|ts|jsx|tsx|vue|svelte)$/.test(id)) {
|
|
99
213
|
return null;
|
|
100
214
|
}
|
|
101
215
|
|
|
102
|
-
// Replace dynamic imports of export: URLs
|
|
103
216
|
if (code.includes('import("export:') || code.includes("import('export:")) {
|
|
104
217
|
const baseUrl = isDev ? devUrl : prodUrl;
|
|
105
218
|
if (!baseUrl && !isDev) {
|
|
106
|
-
this.warn(
|
|
107
|
-
`[exportc] Production URL not configured for dynamic imports in ${id}`
|
|
108
|
-
);
|
|
219
|
+
this.warn(`[exportc] Production URL not configured for dynamic imports in ${id}`);
|
|
109
220
|
return null;
|
|
110
221
|
}
|
|
111
222
|
|
|
112
|
-
// Replace export:/ with the actual URL
|
|
113
223
|
const transformed = code.replace(
|
|
114
224
|
/import\((['"])export:([^'"]*)\1\)/g,
|
|
115
225
|
(match, quote, path) => {
|
|
@@ -125,19 +235,6 @@ export { default } from "${fullUrl}";
|
|
|
125
235
|
|
|
126
236
|
return null;
|
|
127
237
|
},
|
|
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
238
|
};
|
|
142
239
|
}
|
|
143
240
|
|