@firtoz/worker-helper 1.1.0 → 1.3.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/CHANGELOG.md +17 -0
- package/package.json +6 -3
- package/src/cf-typegen.ts +69 -80
- package/src/utils/prepare-env.ts +58 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @firtoz/worker-helper
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`ef2b36e`](https://github.com/firtoz/fullstack-toolkit/commit/ef2b36e4be4fda049f02f1d000649e4c75ff08ec) Thanks [@firtoz](https://github.com/firtoz)! - Export `cf-typegen` as a CLI binary. Users can now run `cf-typegen $(pwd)` directly after installing the package as a dev dependency.
|
|
8
|
+
|
|
9
|
+
## 1.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`2725815`](https://github.com/firtoz/fullstack-toolkit/commit/27258158dd318b34b44ed77b88b2ac9b2b4b6a3d) Thanks [@firtoz](https://github.com/firtoz)! - Improved workspace-wide type generation and environment setup
|
|
14
|
+
|
|
15
|
+
- Refactored `cf-typegen.ts` to automatically discover all wrangler configs using `git ls-files`
|
|
16
|
+
- Uses git for workspace discovery - fast, respects .gitignore, and finds all tracked configs
|
|
17
|
+
- Added `prepareEnvFiles` utility to handle .env file creation from .env.example templates
|
|
18
|
+
- Type generation now includes bindings from all workspace projects for better DX
|
|
19
|
+
|
|
3
20
|
## 1.1.0
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firtoz/worker-helper",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Type-safe Web Worker helper with Zod validation and Cloudflare Workers utilities (cf-typegen)",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"module": "./src/index.ts",
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
"require": "./src/*.ts"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"cf-typegen": "./src/cf-typegen.ts"
|
|
22
|
+
},
|
|
20
23
|
"files": [
|
|
21
24
|
"src/**/*.ts",
|
|
22
25
|
"README.md",
|
|
@@ -61,10 +64,10 @@
|
|
|
61
64
|
"access": "public"
|
|
62
65
|
},
|
|
63
66
|
"dependencies": {
|
|
64
|
-
"zod": "^4.3.
|
|
67
|
+
"zod": "^4.3.6"
|
|
65
68
|
},
|
|
66
69
|
"devDependencies": {
|
|
67
|
-
"@types/node": "^25.0.
|
|
70
|
+
"@types/node": "^25.0.10",
|
|
68
71
|
"@firtoz/maybe-error": "^1.5.2",
|
|
69
72
|
"bun-types": "^1.3.6"
|
|
70
73
|
}
|
package/src/cf-typegen.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import { execSync } from "node:child_process";
|
|
2
3
|
import * as fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import process from "node:process";
|
|
6
|
+
import { prepareEnvFiles } from "./utils/prepare-env";
|
|
5
7
|
|
|
6
8
|
// Use the current working directory
|
|
7
9
|
const cwd = process.argv[2];
|
|
@@ -15,112 +17,100 @@ if (!cwd || !fs.existsSync(cwd)) {
|
|
|
15
17
|
console.log(`Running CF typegen for: ${cwd}`);
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
|
-
*
|
|
20
|
+
* Find the git root directory using git command
|
|
19
21
|
*/
|
|
20
|
-
function
|
|
22
|
+
function findGitRoot(startPath: string): string | null {
|
|
21
23
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const trimmed = line.trim();
|
|
32
|
-
// Skip comments and empty lines
|
|
33
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
24
|
+
const output = execSync("git rev-parse --show-toplevel", {
|
|
25
|
+
cwd: startPath,
|
|
26
|
+
encoding: "utf8",
|
|
27
|
+
});
|
|
28
|
+
return output.trim();
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Find all wrangler config files using git ls-files
|
|
36
|
+
* This is more efficient and respects .gitignore
|
|
37
|
+
*/
|
|
38
|
+
function findAllWranglerConfigs(gitRoot: string): string[] {
|
|
39
|
+
try {
|
|
40
|
+
// Use git ls-files to find all tracked wrangler configs
|
|
41
|
+
const output = execSync('git ls-files "**/wrangler.json*"', {
|
|
42
|
+
cwd: gitRoot,
|
|
43
|
+
encoding: "utf8",
|
|
44
|
+
});
|
|
40
45
|
|
|
41
|
-
return
|
|
46
|
+
return output
|
|
47
|
+
.trim()
|
|
48
|
+
.split("\n")
|
|
49
|
+
.filter((line) => line.length > 0)
|
|
50
|
+
.map((relativePath) => path.join(gitRoot, relativePath));
|
|
42
51
|
} catch (err) {
|
|
43
|
-
console.warn(
|
|
52
|
+
console.warn("⚠ Failed to run git ls-files:", err);
|
|
44
53
|
return [];
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*/
|
|
51
|
-
function prepareEnvLocal(): { created: boolean; added: string[] } {
|
|
52
|
-
const envPath = path.join(cwd, ".env.local");
|
|
53
|
-
const examplePath = path.join(cwd, ".env.local.example");
|
|
57
|
+
function runWranglerTypes() {
|
|
58
|
+
const envFiles = prepareEnvFiles(cwd);
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
console.log("No .env.local.example found, skipping env preparation");
|
|
57
|
-
return { created: false, added: [] };
|
|
58
|
-
}
|
|
60
|
+
console.log("Running wrangler types...");
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
// Find git root to discover all wrangler configs
|
|
63
|
+
const gitRoot = findGitRoot(cwd);
|
|
64
|
+
if (!gitRoot) {
|
|
65
|
+
console.warn(
|
|
66
|
+
"⚠ Could not find git root, skipping workspace config discovery",
|
|
67
|
+
);
|
|
68
|
+
console.log(" Generating types for current directory only");
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const added: string[] = [];
|
|
71
|
+
// Find all wrangler configs in the workspace
|
|
72
|
+
const allConfigs = gitRoot ? findAllWranglerConfigs(gitRoot) : [];
|
|
69
73
|
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
} else {
|
|
73
|
-
created = true;
|
|
74
|
+
if (allConfigs.length > 0) {
|
|
75
|
+
console.log(` Found ${allConfigs.length} wrangler config(s) in workspace`);
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
// Build the command with multiple -c flags
|
|
79
|
+
// The first config should be the current directory's wrangler.jsonc
|
|
80
|
+
const configFlags = ["-c wrangler.jsonc"];
|
|
81
|
+
|
|
82
|
+
// Add other configs (relative to cwd for better readability)
|
|
83
|
+
const currentWranglerJsonc = path.join(cwd, "wrangler.jsonc");
|
|
84
|
+
const currentWranglerJson = path.join(cwd, "wrangler.json");
|
|
85
|
+
|
|
86
|
+
for (const configPath of allConfigs) {
|
|
87
|
+
const resolvedPath = path.resolve(configPath);
|
|
88
|
+
// Skip if it's the current directory's config
|
|
89
|
+
if (
|
|
90
|
+
resolvedPath === currentWranglerJsonc ||
|
|
91
|
+
resolvedPath === currentWranglerJson
|
|
92
|
+
) {
|
|
93
|
+
continue;
|
|
85
94
|
}
|
|
95
|
+
// Make path relative to cwd
|
|
96
|
+
const relativePath = path.relative(cwd, configPath);
|
|
97
|
+
configFlags.push(`-c ${relativePath}`);
|
|
86
98
|
}
|
|
87
99
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
fs.writeFileSync(envPath, content);
|
|
100
|
+
for (const envFile of envFiles) {
|
|
101
|
+
configFlags.push(`--env-file ${envFile}`);
|
|
91
102
|
}
|
|
92
103
|
|
|
93
|
-
|
|
94
|
-
}
|
|
104
|
+
const command = `wrangler types ${configFlags.join(" ")}`;
|
|
95
105
|
|
|
96
|
-
|
|
97
|
-
function prepareEnv() {
|
|
98
|
-
try {
|
|
99
|
-
const updates = prepareEnvLocal();
|
|
100
|
-
if (updates.created) {
|
|
101
|
-
console.log("✓ Created .env.local");
|
|
102
|
-
}
|
|
103
|
-
if (updates.added.length > 0) {
|
|
104
|
-
console.log(`✓ Added missing env vars: ${updates.added.join(", ")}`);
|
|
105
|
-
}
|
|
106
|
-
if (!updates.created && updates.added.length === 0) {
|
|
107
|
-
console.log("✓ .env.local file already has all required vars");
|
|
108
|
-
}
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.error(String(error));
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
106
|
+
console.log(` Command: ${command}`);
|
|
114
107
|
|
|
115
|
-
// Step 2: Run wrangler types
|
|
116
|
-
function runWranglerTypes() {
|
|
117
|
-
console.log("Running wrangler types...");
|
|
118
108
|
try {
|
|
119
|
-
execSync(
|
|
109
|
+
execSync(command, {
|
|
120
110
|
cwd,
|
|
121
111
|
stdio: "inherit",
|
|
122
112
|
});
|
|
123
|
-
console.log("✓ Wrangler types generated");
|
|
113
|
+
console.log("✓ Wrangler types generated with all workspace bindings");
|
|
124
114
|
} catch {
|
|
125
115
|
console.error("Failed to run wrangler types");
|
|
126
116
|
process.exit(1);
|
|
@@ -129,7 +119,6 @@ function runWranglerTypes() {
|
|
|
129
119
|
|
|
130
120
|
// Run all steps
|
|
131
121
|
try {
|
|
132
|
-
prepareEnv();
|
|
133
122
|
runWranglerTypes();
|
|
134
123
|
console.log("\n✓ CF typegen completed successfully");
|
|
135
124
|
} catch (error: unknown) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ensures a .env and .env.local file exists in the target directory.
|
|
6
|
+
* If it doesn't exist, copies from .env.example and .env.local.example.
|
|
7
|
+
*
|
|
8
|
+
* @param targetDir - The directory where the .env and .env.local files should exist
|
|
9
|
+
* @returns An array of the files that were created or already existed
|
|
10
|
+
*/
|
|
11
|
+
export function prepareEnvFiles(targetDir: string): string[] {
|
|
12
|
+
const exampleEnvPath = path.join(targetDir, ".env.example");
|
|
13
|
+
const exampleEnvLocalPath = path.join(targetDir, ".env.local.example");
|
|
14
|
+
|
|
15
|
+
const exampleEnvExists = fs.existsSync(exampleEnvPath);
|
|
16
|
+
const exampleLocalEnvExists = fs.existsSync(exampleEnvLocalPath);
|
|
17
|
+
|
|
18
|
+
const envPath = path.join(targetDir, ".env");
|
|
19
|
+
const envLocalPath = path.join(targetDir, ".env.local");
|
|
20
|
+
|
|
21
|
+
let envExists = fs.existsSync(envPath);
|
|
22
|
+
let envLocalExists = fs.existsSync(envLocalPath);
|
|
23
|
+
|
|
24
|
+
if (exampleEnvExists) {
|
|
25
|
+
if (!envExists) {
|
|
26
|
+
fs.cpSync(exampleEnvPath, envPath);
|
|
27
|
+
|
|
28
|
+
console.log("✓ Created .env from .env.example");
|
|
29
|
+
envExists = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (exampleLocalEnvExists) {
|
|
34
|
+
if (!envLocalExists) {
|
|
35
|
+
fs.cpSync(exampleEnvLocalPath, envLocalPath);
|
|
36
|
+
|
|
37
|
+
console.log("✓ Created .env.local from .env.local.example");
|
|
38
|
+
envLocalExists = true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result: string[] = [];
|
|
43
|
+
// The order matters, because latter files' values will override former files' values
|
|
44
|
+
if (exampleEnvExists) {
|
|
45
|
+
result.push(".env.example");
|
|
46
|
+
}
|
|
47
|
+
if (exampleLocalEnvExists) {
|
|
48
|
+
result.push(".env.local.example");
|
|
49
|
+
}
|
|
50
|
+
if (envExists) {
|
|
51
|
+
result.push(".env");
|
|
52
|
+
}
|
|
53
|
+
if (envLocalExists) {
|
|
54
|
+
result.push(".env.local");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result;
|
|
58
|
+
}
|