diff --git a/.changeset/wet-beds-attack.md b/.changeset/wet-beds-attack.md
new file mode 100644
index 000000000000..94fb53bbe8a9
--- /dev/null
+++ b/.changeset/wet-beds-attack.md
@@ -0,0 +1,5 @@
+---
+"cloudflare-workers-bindings-extension": patch
+---
+
+Add documentation links to binding created notification and bindings tree view
diff --git a/packages/cloudflare-workers-bindings-extension/package.json b/packages/cloudflare-workers-bindings-extension/package.json
index 2b128e6bb190..195967c03107 100644
--- a/packages/cloudflare-workers-bindings-extension/package.json
+++ b/packages/cloudflare-workers-bindings-extension/package.json
@@ -36,7 +36,13 @@
{
"command": "cloudflare-workers-bindings.addBinding",
"title": "Cloudflare Workers: Add binding",
- "icon": "$(add)"
+ "icon": "$(add)",
+ "enablement": "!ext.unsupportedWrangler"
+ },
+ {
+ "command": "cloudflare-workers-bindings.openDocs",
+ "title": "Cloudflare Workers: Open Documentation",
+ "icon": "$(book)"
}
],
"jsonValidation": [
@@ -57,6 +63,13 @@
"when": "view == cloudflare-workers-bindings",
"group": "navigation"
}
+ ],
+ "view/item/context": [
+ {
+ "command": "cloudflare-workers-bindings.openDocs",
+ "when": "view == cloudflare-workers-bindings && viewItem == binding",
+ "group": "inline"
+ }
]
},
"views": {
@@ -81,7 +94,13 @@
"viewsWelcome": [
{
"view": "cloudflare-workers-bindings",
- "contents": "Welcome to Cloudflare Workers! [Learn more](https://workers.cloudflare.com).\n[Add a binding](command:cloudflare-workers-bindings.addBinding)"
+ "contents": "Welcome to Cloudflare Workers! [Learn more](https://workers.cloudflare.com).\n[Add a binding](command:cloudflare-workers-bindings.addBinding)",
+ "when": "!ext.unsupportedWrangler"
+ },
+ {
+ "view": "cloudflare-workers-bindings",
+ "contents": "Please upgrade Wrangler to at least 3.99.0 in order to use the Cloudflare Workers Extension. You can install the latest Wrangler version by running `npm i wrangler@latest`",
+ "when": "ext.unsupportedWrangler"
}
]
},
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/ai.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/ai.svg
new file mode 100644
index 000000000000..72d8509c2d18
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/ai.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/analytics_engine_datasets.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/analytics_engine_datasets.svg
new file mode 100644
index 000000000000..0bba62fd3f91
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/analytics_engine_datasets.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/assets.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/assets.svg
new file mode 100644
index 000000000000..bab10670a729
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/assets.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/browser.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/browser.svg
new file mode 100644
index 000000000000..926b743027d4
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/browser.svg
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/d1.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/d1_databases.svg
similarity index 100%
rename from packages/cloudflare-workers-bindings-extension/resources/icons/d1.svg
rename to packages/cloudflare-workers-bindings-extension/resources/icons/d1_databases.svg
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/dispatch_namespaces.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/dispatch_namespaces.svg
new file mode 100644
index 000000000000..b80871b92122
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/dispatch_namespaces.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/durable_objects.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/durable_objects.svg
new file mode 100644
index 000000000000..0df07d2e33c5
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/durable_objects.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/hyperdrive.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/hyperdrive.svg
new file mode 100644
index 000000000000..1d3e70b71a1a
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/hyperdrive.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/kv.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/kv_namespaces.svg
similarity index 100%
rename from packages/cloudflare-workers-bindings-extension/resources/icons/kv.svg
rename to packages/cloudflare-workers-bindings-extension/resources/icons/kv_namespaces.svg
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/logfwdr.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/logfwdr.svg
new file mode 100644
index 000000000000..bad89037e9ad
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/logfwdr.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/mtls_certificates.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/mtls_certificates.svg
new file mode 100644
index 000000000000..b38e5af9c6bf
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/mtls_certificates.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/pipelines.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/pipelines.svg
new file mode 100644
index 000000000000..dc1c9f0f7c0f
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/pipelines.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/queues.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/queues.svg
new file mode 100644
index 000000000000..651f4745dcc8
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/queues.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/r2.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/r2_buckets.svg
similarity index 100%
rename from packages/cloudflare-workers-bindings-extension/resources/icons/r2.svg
rename to packages/cloudflare-workers-bindings-extension/resources/icons/r2_buckets.svg
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/send_email.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/send_email.svg
new file mode 100644
index 000000000000..5fd62ba6037d
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/send_email.svg
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/services.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/services.svg
new file mode 100644
index 000000000000..bab10670a729
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/services.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/tail_consumers.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/tail_consumers.svg
new file mode 100644
index 000000000000..bab10670a729
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/tail_consumers.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/unsafe.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/unsafe.svg
new file mode 100644
index 000000000000..58a678755095
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/unsafe.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/vars.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/vars.svg
new file mode 100644
index 000000000000..8c5e623511fd
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/vars.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/vectorize.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/vectorize.svg
new file mode 100644
index 000000000000..4393ddee1780
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/vectorize.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/version_metadata.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/version_metadata.svg
new file mode 100644
index 000000000000..9363094a5aa7
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/version_metadata.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/resources/icons/workflows.svg b/packages/cloudflare-workers-bindings-extension/resources/icons/workflows.svg
new file mode 100644
index 000000000000..b6a33b8248a2
--- /dev/null
+++ b/packages/cloudflare-workers-bindings-extension/resources/icons/workflows.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/cloudflare-workers-bindings-extension/src/add-binding.ts b/packages/cloudflare-workers-bindings-extension/src/add-binding.ts
index cbe4d3af2657..4e013fb9592d 100644
--- a/packages/cloudflare-workers-bindings-extension/src/add-binding.ts
+++ b/packages/cloudflare-workers-bindings-extension/src/add-binding.ts
@@ -1,3 +1,4 @@
+import assert from "node:assert";
import {
Disposable,
env,
@@ -18,9 +19,10 @@ import {
} from "./show-bindings";
import { importWrangler } from "./wrangler";
+type BindingLabel = "KV" | "R2" | "D1";
class BindingType implements QuickPickItem {
constructor(
- public label: string,
+ public label: BindingLabel,
public configKey?: string,
public detail?: string,
public iconPath?: Uri
@@ -33,63 +35,77 @@ export async function addBindingFlow(context: ExtensionContext) {
"KV",
"kv_namespaces",
"Global, low-latency, key-value data storage",
- Uri.file(context.asAbsolutePath("resources/icons/kv.svg"))
+ Uri.file(context.asAbsolutePath("resources/icons/kv_namespaces.svg"))
),
new BindingType(
"R2",
"r2_buckets",
"Object storage for all your data",
- Uri.file(context.asAbsolutePath("resources/icons/r2.svg"))
+ Uri.file(context.asAbsolutePath("resources/icons/r2_buckets.svg"))
),
new BindingType(
"D1",
"d1_databases",
"Serverless SQL databases",
- Uri.file(context.asAbsolutePath("resources/icons/d1.svg"))
+ Uri.file(context.asAbsolutePath("resources/icons/d1_databases.svg"))
),
];
interface State {
- title: string;
- step: number;
- totalSteps: number;
- bindingType: BindingType;
- name: string;
- runtime: QuickPickItem;
- id: string;
+ config?: Config;
+ configUri: Uri;
+ bindingType?: BindingType;
+ name?: string;
}
+ const title = "Add binding";
+
async function collectInputs() {
- const state = {} as Partial;
+ const configUri = await getConfigUri();
+ if (!configUri) {
+ const docs = await window.showErrorMessage(
+ "Unable to locate Wrangler configuration file — please open or create a project with a wrangler.json(c) or wrangler.toml file. You can run `npx create cloudflare@latest` to get started with a template.",
+ "Learn more"
+ );
+ if (docs) {
+ env.openExternal(
+ Uri.parse(
+ "https://developers.cloudflare.com/workers/wrangler/configuration/"
+ )
+ );
+ }
+ return;
+ }
+ let config: Config | undefined;
+ try {
+ config = await getWranglerConfig();
+ } catch {}
+ if (!config) {
+ window.showErrorMessage(
+ "Please update wrangler to at least 3.99.0 to use this extension."
+ );
+ return;
+ }
+
+ const state = { config, configUri };
await MultiStepInput.run((input) => pickBindingType(input, state));
- return state as State;
+ return state;
}
- const title = "Add binding";
-
- async function pickBindingType(input: MultiStepInput, state: Partial) {
+ async function pickBindingType(input: MultiStepInput, state: State) {
const pick = await input.showQuickPick({
title,
step: 1,
totalSteps: 2,
placeholder: "Choose a binding type",
items: bindingTypes,
- activeItem:
- typeof state.bindingType !== "string" ? state.bindingType : undefined,
});
state.bindingType = pick as BindingType;
return (input: MultiStepInput) => inputBindingName(input, state);
}
- async function inputBindingName(
- input: MultiStepInput,
- state: Partial
- ) {
- const config = await getWranglerConfig();
- if (!config) {
- throw new Error("No config found");
- }
- const allBindingNames = getAllBindingNames(config);
+ async function inputBindingName(input: MultiStepInput, state: State) {
+ const allBindingNames = getAllBindingNames(state.config ?? {});
let name = await input.showInputBox({
title,
@@ -107,17 +123,9 @@ export async function addBindingFlow(context: ExtensionContext) {
return () => addToConfig(state);
}
- async function addToConfig(state: Partial) {
- const configUri = await getConfigUri();
- if (!configUri) {
- // for some reason, if we just throw an error it doesn't surface properly when triggered by the button in the welcome view
- window.showErrorMessage(
- "Unable to locate Wrangler configuration file — have you opened a project with a wrangler.json(c) or wrangler.toml file?",
- {}
- );
- return null;
- }
- const workspaceFolder = workspace.getWorkspaceFolder(configUri);
+ async function addToConfig(state: State) {
+ assert(state.bindingType?.configKey && state.name);
+ const workspaceFolder = workspace.getWorkspaceFolder(state.configUri);
if (!workspaceFolder) {
return null;
@@ -125,25 +133,39 @@ export async function addBindingFlow(context: ExtensionContext) {
const wrangler = importWrangler(workspaceFolder.uri.fsPath);
- workspace.openTextDocument(configUri).then((doc) => {
- window.showTextDocument(doc);
+ const doc = await workspace.openTextDocument(state.configUri);
+ window.showTextDocument(doc);
+ let openDocs: string | undefined;
+ let useClipboard = false;
+ if (!wrangler) {
+ useClipboard = true;
+ } else {
try {
- wrangler.experimental_patchConfig(configUri.path, {
- [state.bindingType?.configKey!]: [{ binding: state.name! }],
+ const configFileName = state.configUri.path.split("/").at(-1);
+ wrangler.experimental_patchConfig(state.configUri.path, {
+ [state.bindingType.configKey]: [{ binding: state.name }],
});
- window.showInformationMessage(`Created binding '${state.name}'`);
- } catch {
- window.showErrorMessage(
- `Unable to directly add binding to config file. A snippet has been copied to clipboard - please paste this into your config file.`
+ openDocs = await window.showInformationMessage(
+ `🎉 The ${state.bindingType.label} binding '${state.name}' has been added to your ${configFileName}`,
+ `Open ${state.bindingType.label} documentation`
);
-
- const patch = `[[${state.bindingType?.configKey!}]]
-binding = "${state.name}"
-`;
-
- env.clipboard.writeText(patch);
+ } catch {
+ useClipboard = true;
}
- });
+ }
+ if (useClipboard) {
+ const patch = `[[${state.bindingType?.configKey!}]]
+ binding = "${state.name}"
+ `;
+ env.clipboard.writeText(patch);
+ openDocs = await window.showInformationMessage(
+ `✨ A snippet has been copied to clipboard - please paste this into your wrangler.toml`,
+ `Open ${state.bindingType.label} documentation`
+ );
+ }
+ if (openDocs) {
+ env.openExternal(Uri.parse(bindingDocs[state.bindingType.label]));
+ }
}
async function validateNameIsUnique(name: string, allBindingNames: string[]) {
@@ -170,7 +192,6 @@ interface QuickPickParameters {
step: number;
totalSteps: number;
items: T[];
- activeItem?: T;
ignoreFocusOut?: boolean;
placeholder: string;
buttons?: QuickInputButton[];
@@ -238,7 +259,6 @@ export class MultiStepInput {
step,
totalSteps,
items,
- activeItem,
ignoreFocusOut,
placeholder,
buttons,
@@ -255,9 +275,6 @@ export class MultiStepInput {
input.ignoreFocusOut = ignoreFocusOut ?? false;
input.placeholder = placeholder;
input.items = items;
- if (activeItem) {
- input.activeItems = [activeItem];
- }
input.buttons = [
...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
...(buttons || []),
@@ -408,3 +425,9 @@ const isRecord = (
value: unknown
): value is Record =>
typeof value === "object" && value !== null && !Array.isArray(value);
+
+const bindingDocs: Record = {
+ KV: "https://developers.cloudflare.com/kv/get-started/#5-access-your-kv-namespace-from-your-worker",
+ D1: "https://developers.cloudflare.com/d1/get-started/#write-queries-within-your-worker",
+ R2: "https://developers.cloudflare.com/r2/api/workers/workers-api-usage/#4-access-your-r2-bucket-from-your-worker",
+};
diff --git a/packages/cloudflare-workers-bindings-extension/src/extension.ts b/packages/cloudflare-workers-bindings-extension/src/extension.ts
index d10f1dfc6742..0a60de576c55 100644
--- a/packages/cloudflare-workers-bindings-extension/src/extension.ts
+++ b/packages/cloudflare-workers-bindings-extension/src/extension.ts
@@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { addBindingFlow } from "./add-binding";
-import { BindingsProvider } from "./show-bindings";
+import { BindingsProvider, Node } from "./show-bindings";
export type Result = {
bindingsProvider: BindingsProvider;
@@ -40,12 +40,28 @@ export async function activate(
await addBindingFlow(context);
}
);
- // Cleanup when the extension is deactivated
+
+ const openDocsCommand = vscode.commands.registerCommand(
+ "cloudflare-workers-bindings.openDocs",
+ async (node: Node) => {
+ const docs: Record = {
+ d1_databases: "https://developers.cloudflare.com/d1/",
+ r2_buckets: "https://developers.cloudflare.com/r2/",
+ kv_namespaces: "https://developers.cloudflare.com/kv/",
+ };
+ if (node.type === "binding") {
+ vscode.env.openExternal(vscode.Uri.parse(docs[node.name]));
+ }
+ }
+ );
+
+ // Cleanup when the extension is deactivated
context.subscriptions.push(
bindingsView,
watcher,
refreshCommand,
- addBindingCommand
+ addBindingCommand,
+ openDocsCommand
);
return {
diff --git a/packages/cloudflare-workers-bindings-extension/src/show-bindings.ts b/packages/cloudflare-workers-bindings-extension/src/show-bindings.ts
index a5e449539a9b..1dd73b1256b3 100644
--- a/packages/cloudflare-workers-bindings-extension/src/show-bindings.ts
+++ b/packages/cloudflare-workers-bindings-extension/src/show-bindings.ts
@@ -1,8 +1,9 @@
+import path from "path";
import * as vscode from "vscode";
import { importWrangler } from "./wrangler";
export type Config = ReturnType<
- ReturnType["experimental_readRawConfig"]
+ NonNullable>["experimental_readRawConfig"]
>["rawConfig"];
export type Environment = Required["env"][string];
@@ -39,7 +40,7 @@ type EnvNode = {
name: string | null;
};
-type BindingNode = Exclude<
+export type BindingNode = Exclude<
{
[Name in BindingType]: {
type: "binding";
@@ -56,7 +57,7 @@ type ResourceNode = {
description?: string;
};
-type Node = EnvNode | BindingNode | ResourceNode;
+export type Node = EnvNode | BindingNode | ResourceNode;
export class BindingsProvider implements vscode.TreeDataProvider {
// Event emitter for refreshing the tree
@@ -83,11 +84,24 @@ export class BindingsProvider implements vscode.TreeDataProvider {
return item;
}
+ // this is the header
case "binding": {
- return new vscode.TreeItem(
+ const item = new vscode.TreeItem(
friendlyBindingNames[node.name],
vscode.TreeItemCollapsibleState.Expanded
);
+ const enabledBindings = ["kv_namespaces", "r2_buckets", "d1_databases"];
+ if (enabledBindings.includes(node.name)) {
+ item.contextValue = "binding";
+ }
+ item.iconPath = path.join(
+ __dirname,
+ "../",
+ "resources",
+ "icons",
+ `${node.name}.svg`
+ );
+ return item;
}
case "resource": {
const item = new vscode.TreeItem(
@@ -600,26 +614,29 @@ function hasBinding | Array>(
}
// Finds the first wrangler config file in the workspace and parse it
-export async function getWranglerConfig(): Promise {
+export async function getWranglerConfig(): Promise {
const configUri = await getConfigUri();
if (!configUri) {
- return null;
+ return;
}
const workspaceFolder = vscode.workspace.getWorkspaceFolder(configUri);
if (!workspaceFolder) {
- return null;
+ return;
}
const wrangler = importWrangler(workspaceFolder.uri.fsPath);
- const { rawConfig } = wrangler.experimental_readRawConfig({
- config: configUri.fsPath,
- });
-
- return rawConfig;
+ if (!wrangler) {
+ return;
+ } else {
+ const { rawConfig } = wrangler.experimental_readRawConfig({
+ config: configUri.fsPath,
+ });
+ return rawConfig;
+ }
}
-export async function getConfigUri(): Promise {
+export async function getConfigUri(): Promise {
const [configUri] = await vscode.workspace.findFiles(
"wrangler.{toml,jsonc,json}",
null,
diff --git a/packages/cloudflare-workers-bindings-extension/src/test/suite/extension.test.ts b/packages/cloudflare-workers-bindings-extension/src/test/suite/extension.test.ts
index 0d3b4cc9437f..93c40f6c2570 100644
--- a/packages/cloudflare-workers-bindings-extension/src/test/suite/extension.test.ts
+++ b/packages/cloudflare-workers-bindings-extension/src/test/suite/extension.test.ts
@@ -18,6 +18,93 @@ describe("Extension Test Suite", () => {
});
describe("BindingsProvider", () => {
+ it("should not return wrangler if the version of wrangler is too old", async () => {
+ const pkgJsonPath = path
+ .resolve(__dirname, "..", "..", "..", "..", "wrangler", "package.json")
+ .toString();
+
+ const originalPkgJson = JSON.parse(
+ await fs.readFile(pkgJsonPath, "utf8")
+ );
+
+ const cleanup = await seed({
+ "wrangler.json": generateWranglerConfig({
+ r2_buckets: [
+ {
+ binding: "r2",
+ bucket_name: "something else",
+ },
+ ],
+ }),
+ });
+
+ try {
+ const extension = getExtension();
+ const { bindingsProvider } = await extension.activate();
+
+ await fs.writeFile(
+ pkgJsonPath,
+ JSON.stringify({ ...originalPkgJson, version: "3.98.0" })
+ );
+ // bindingsProvider uses wrangler to read config.
+ // If that doesn't work, but we have provided a wrangler.json,
+ // this means wrangler wasn't imported. Which should be
+ // because its an old version. (not the best test :/)
+ let children = await bindingsProvider.getChildren();
+ assert.deepEqual(children, []);
+
+ await fs.writeFile(
+ pkgJsonPath,
+ JSON.stringify({ ...originalPkgJson, version: "3.99.0" })
+ );
+ children = await bindingsProvider.getChildren();
+ assert.deepEqual(children, [
+ {
+ config: [
+ {
+ binding: "r2",
+ bucket_name: "something else",
+ },
+ ],
+ name: "r2_buckets",
+ type: "binding",
+ },
+ ]);
+
+ await fs.writeFile(
+ pkgJsonPath,
+ JSON.stringify({ ...originalPkgJson, version: "2.99.0" })
+ );
+ children = await bindingsProvider.getChildren();
+ assert.deepEqual(children, []);
+
+ // pre-releases should work
+ await fs.writeFile(
+ pkgJsonPath,
+ JSON.stringify({ ...originalPkgJson, version: "0.0.0-something" })
+ );
+ children = await bindingsProvider.getChildren();
+ assert.deepEqual(children, [
+ {
+ config: [
+ {
+ binding: "r2",
+ bucket_name: "something else",
+ },
+ ],
+ name: "r2_buckets",
+ type: "binding",
+ },
+ ]);
+ } finally {
+ await cleanup();
+ await fs.writeFile(
+ pkgJsonPath,
+ JSON.stringify(originalPkgJson, null, "\t") + "\n"
+ );
+ }
+ });
+
it("shows no bindings if there is no wrangler config", async () => {
const extension = getExtension();
const { bindingsProvider } = await extension.activate();
@@ -176,86 +263,173 @@ describe("Extension Test Suite", () => {
{
label: friendlyBindingNames.kv_namespaces,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ contextValue: "binding",
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/kv_namespaces.svg"
+ ),
},
{
label: friendlyBindingNames.r2_buckets,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ contextValue: "binding",
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/r2_buckets.svg"
+ ),
},
{
label: friendlyBindingNames.d1_databases,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ contextValue: "binding",
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/d1_databases.svg"
+ ),
},
{
label: friendlyBindingNames.durable_objects,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/durable_objects.svg"
+ ),
},
{
label: friendlyBindingNames.ai,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/ai.svg"
+ ),
},
{
label: friendlyBindingNames.analytics_engine_datasets,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/analytics_engine_datasets.svg"
+ ),
},
{
label: friendlyBindingNames.browser,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/browser.svg"
+ ),
},
{
label: friendlyBindingNames.hyperdrive,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/hyperdrive.svg"
+ ),
},
{
label: friendlyBindingNames.mtls_certificates,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/mtls_certificates.svg"
+ ),
},
{
label: friendlyBindingNames.services,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/services.svg"
+ ),
},
{
label: friendlyBindingNames.assets,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/assets.svg"
+ ),
},
{
label: friendlyBindingNames.vectorize,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/vectorize.svg"
+ ),
},
{
label: friendlyBindingNames.version_metadata,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/version_metadata.svg"
+ ),
},
{
label: friendlyBindingNames.dispatch_namespaces,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/dispatch_namespaces.svg"
+ ),
},
{
label: friendlyBindingNames.queues,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/queues.svg"
+ ),
},
{
label: friendlyBindingNames.workflows,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/workflows.svg"
+ ),
},
{
label: friendlyBindingNames.send_email,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/send_email.svg"
+ ),
},
{
label: friendlyBindingNames.logfwdr,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/logfwdr.svg"
+ ),
},
{
label: friendlyBindingNames.pipelines,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/pipelines.svg"
+ ),
},
{
label: friendlyBindingNames.vars,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/vars.svg"
+ ),
},
{
label: friendlyBindingNames.unsafe,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/unsafe.svg"
+ ),
},
]
);
@@ -528,6 +702,11 @@ describe("Extension Test Suite", () => {
{
label: friendlyBindingNames.d1_databases,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ contextValue: "binding",
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/d1_databases.svg"
+ ),
},
]
);
@@ -539,6 +718,11 @@ describe("Extension Test Suite", () => {
{
label: friendlyBindingNames.kv_namespaces,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ contextValue: "binding",
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/kv_namespaces.svg"
+ ),
},
]
);
@@ -553,6 +737,11 @@ describe("Extension Test Suite", () => {
{
label: friendlyBindingNames.r2_buckets,
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
+ contextValue: "binding",
+ iconPath: path.join(
+ __filename,
+ "../../../../resources/icons/r2_buckets.svg"
+ ),
},
]
);
@@ -703,6 +892,8 @@ async function symlinkWranglerNodeModule() {
"wrangler"
);
+ console.log("wranglerNodeModulePath", wranglerNodeModulePath);
+ console.log("nodeModulesPath", nodeModulesPath);
await fs.mkdir(nodeModulesPath, { recursive: true });
await fs.symlink(
wranglerNodeModulePath,
diff --git a/packages/cloudflare-workers-bindings-extension/src/wrangler.ts b/packages/cloudflare-workers-bindings-extension/src/wrangler.ts
index 2e35d08288e2..a4fbcc4e0146 100644
--- a/packages/cloudflare-workers-bindings-extension/src/wrangler.ts
+++ b/packages/cloudflare-workers-bindings-extension/src/wrangler.ts
@@ -1,8 +1,10 @@
+import { existsSync, readFileSync } from "fs";
import * as path from "path";
+import * as vscode from "vscode";
export function importWrangler(
workspaceRoot: string
-): typeof import("wrangler") {
+): typeof import("wrangler") | undefined {
const wrangler = path.join(
workspaceRoot,
"node_modules",
@@ -10,6 +12,38 @@ export function importWrangler(
"wrangler-dist",
"cli.js"
);
+ if (!existsSync(wrangler)) {
+ vscode.window.showErrorMessage(
+ "Cannot find Wrangler. Have you run `npm install` in your project directory?"
+ );
+ return;
+ }
+ const packageJsonPath = path.join(
+ workspaceRoot,
+ "node_modules",
+ "wrangler",
+ "package.json"
+ );
+ const wranglerVersion = JSON.parse(readFileSync(packageJsonPath, "utf8"))
+ .version as string;
+ const isPreRelease = wranglerVersion.startsWith("0.0.0");
+ const major = parseInt(wranglerVersion.split(".")[0]);
+ const minor = parseInt(wranglerVersion.split(".")[1]);
+ // min version is 3.99.0 (and there were no patches for 3.99.0)
+ // will probably need to update this to whatever one --x-provision is released on :')
+ if ((major < 3 && !isPreRelease) || (major === 3 && minor < 99)) {
+ vscode.commands.executeCommand(
+ "setContext",
+ "ext.unsupportedWrangler",
+ true
+ );
+ return;
+ }
+ vscode.commands.executeCommand(
+ "setContext",
+ "ext.unsupportedWrangler",
+ false
+ );
return require(wrangler);
}