Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support keys with non-standard characters #1

Closed
threepointone opened this issue Dec 19, 2024 · 1 comment
Closed

support keys with non-standard characters #1

threepointone opened this issue Dec 19, 2024 · 1 comment

Comments

@threepointone
Copy link
Owner

This should work

SOME-TEST=123

The replacement should be process.env["SOME-TEST"]

I tried, and it failed with esbuild errors like:

The define key "process.env[YET-ANOTHER-TEST]" contains invalid identifier "env[YET-ANOTHER-TEST]"

attempt:

diff --git a/package.json b/package.json
index 35027cd..b646707 100644
--- a/package.json
+++ b/package.json
@@ -4,11 +4,11 @@
   "description": "Wrangler wrapper with .env file support",
   "main": "dist/index.js",
   "bin": {
-    "wangler": "./dist/index.js"
+    "wangler": "dist/index.js"
   },
   "repository": {
     "type": "git",
-    "url": "https://github.com/threepointone/wangler.git"
+    "url": "git+https://github.com/threepointone/wangler.git"
   },
   "scripts": {
     "build": "tsc",
@@ -40,5 +40,9 @@
     "dotenv"
   ],
   "author": "Sunil Pai <spai@cloudflare.com>",
-  "license": "MIT"
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/threepointone/wangler/issues"
+  },
+  "homepage": "https://github.com/threepointone/wangler#readme"
 }
diff --git a/src/index.test.ts b/src/index.test.ts
index c9e8d47..eb1e572 100644
--- a/src/index.test.ts
+++ b/src/index.test.ts
@@ -1,4 +1,4 @@
-import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
+import { describe, test, it, expect, beforeEach, afterEach, vi } from "vitest";
 
 // Mock dotenv
 vi.mock("dotenv", () => ({
@@ -248,3 +248,88 @@ describe("Command passthrough", () => {
     expect(fullCommand).toContain("--define process.env"); // Should include env processing
   });
 });
+
+function extractDefineArgs(envVars: Record<string, string>): string {
+  function isValidJSIdentifier(str: string): boolean {
+    return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str);
+  }
+
+  return Object.entries(envVars)
+    .map(([key, value]) => {
+      const processEnvAccess = isValidJSIdentifier(key)
+        ? `process.env.${key}`
+        : `process.env["${key}"]`;
+      const importMetaAccess = isValidJSIdentifier(key)
+        ? `import.meta.env.${key}`
+        : `import.meta.env["${key}"]`;
+      return `--define ${processEnvAccess}:\'${JSON.stringify(
+        value
+      )}\' --define ${importMetaAccess}:\'${JSON.stringify(
+        value
+      )}\' --var ${key}:${JSON.stringify(value)}`;
+    })
+    .join(" ");
+}
+
+describe("Environment Variable Handling", () => {
+  it("should handle standard environment variable names correctly", () => {
+    const envVars = {
+      NORMAL_VAR: "value1",
+      ANOTHER_VAR: "value2",
+    };
+
+    const defineArgs = extractDefineArgs(envVars);
+
+    expect(defineArgs).toContain("process.env.NORMAL_VAR");
+    expect(defineArgs).toContain("import.meta.env.NORMAL_VAR");
+    expect(defineArgs).toContain("process.env.ANOTHER_VAR");
+    expect(defineArgs).toContain("import.meta.env.ANOTHER_VAR");
+    expect(defineArgs).not.toContain('process.env["NORMAL_VAR"]');
+  });
+
+  it("should handle environment variables with special characters correctly", () => {
+    const envVars = {
+      "my-special-var": "value1",
+      "another-var": "value2",
+      "123invalid": "value3",
+    };
+
+    const defineArgs = extractDefineArgs(envVars);
+
+    expect(defineArgs).toContain('process.env["my-special-var"]');
+    expect(defineArgs).toContain('import.meta.env["my-special-var"]');
+    expect(defineArgs).toContain('process.env["another-var"]');
+    expect(defineArgs).toContain('process.env["123invalid"]');
+    expect(defineArgs).not.toContain("process.env.my-special-var");
+  });
+
+  it("should handle mixed standard and special character variables", () => {
+    const envVars = {
+      NORMAL_VAR: "value1",
+      "special-var": "value2",
+      _VALID_VAR: "value3",
+      $valid: "value4",
+    };
+
+    const defineArgs = extractDefineArgs(envVars);
+
+    expect(defineArgs).toContain("process.env.NORMAL_VAR");
+    expect(defineArgs).toContain('process.env["special-var"]');
+    expect(defineArgs).toContain("process.env._VALID_VAR");
+    expect(defineArgs).toContain("process.env.$valid");
+    expect(defineArgs).not.toContain('process.env["NORMAL_VAR"]');
+    expect(defineArgs).not.toContain("process.env.special-var");
+  });
+
+  it("should properly escape values in JSON stringification", () => {
+    const envVars = {
+      NORMAL_VAR: 'value with "quotes"',
+      "special-var": "value with 'single' quotes",
+    };
+
+    const defineArgs = extractDefineArgs(envVars);
+
+    expect(defineArgs).toContain('"value with \\"quotes\\""');
+    expect(defineArgs).toContain("\"value with 'single' quotes\"");
+  });
+});
diff --git a/src/index.ts b/src/index.ts
index 652d1ef..122483f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -149,24 +149,30 @@ if (
     ...penvVars,
   };
 
+  // Function to check if a string is a valid JavaScript identifier
+  function isValidJSIdentifier(str: string): boolean {
+    // First character must be a letter, underscore, or dollar sign
+    // Subsequent characters can also include numbers
+    return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str);
+  }
+
   // Convert environment variables to --define arguments
   const defineArgs = Object.entries(allEnvVars)
-    .map(
-      ([key, value]) =>
-        `--define process.env.${key}:\'${JSON.stringify(
-          value
-        )}\' --define import.meta.env.${key}:\'${JSON.stringify(
-          value
-        )}\' --var ${key}:${JSON.stringify(value)}`
-    )
+    .map(([key, value]) => {
+      const processEnvAccess = isValidJSIdentifier(key)
+        ? `process.env.${key}`
+        : `process.env[${JSON.stringify(key)}]`;
+      const importMetaAccess = isValidJSIdentifier(key)
+        ? `import.meta.env.${key}`
+        : `import.meta.env[\\\"${key}\\\"]`;
+      return `--define ${processEnvAccess}:\'${JSON.stringify(
+        value
+      )}\' --define ${importMetaAccess}:\'${JSON.stringify(
+        value
+      )}\' --var ${key}:${JSON.stringify(value)}`;
+    })
     .join(" ");
 
-  // Find the actual wrangler binary
-  // const wranglerPath = path.resolve(
-  //   require.resolve("wrangler/package.json"),
-  //   "../bin/wrangler.js"
-  // );
-
   // Construct the full command with remaining args
   fullCommand = `node ${wranglerPath} ${command} ${defineArgs} ${remainingArgs.join(
     " "
@threepointone
Copy link
Owner Author

evanw/esbuild#4008

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant