A Neovim plugin for Salesforce Commerce Cloud (SFCC) development.
- DEBUGGER: nvim-dap adapter that runs the B2C CLI debug adapter (
b2c debug) for live script debugging against a sandbox - LSP SUPPORT: exposes the bundled
@salesforce/b2c-script-typesTypeScript Server plugin (shipped with the B2C CLI) so cartridge JavaScript getsdw/*IntelliSense plus resolution of~/cartridge/...,*/cartridge/..., and<cartridgeName>/cartridge/...imports- See LSP Configuration for recommended project configuration
- SYNTAX: ISML support layered on the standard
htmlfiletype- all
<is*>tags highlighted distinctly from regular HTML tags -
<isscript>body injected as JavaScript (tree-sitter) -
<iscomment>body highlighted as a comment -
${ ... }ISML expressions highlighted in attribute values and content - indentation for
<isif>/<iselseif>/<iselse>branches
- all
- code/cartridge upload, deploy, watching
- use the B2C CLI (
b2c code deploy,b2c code watch,b2c code download)
- use the B2C CLI (
- log viewing/tailing
- use the B2C CLI (
b2c log tail)
- use the B2C CLI (
- Neovim
- @salesforce/b2c-cli on your
$PATH(npm i -g @salesforce/b2c-cli) — provides the debug adapter and the bundled TS Server plugin - nvim-dap
Optional:
- nvim-dap-ui — the UI shown in the screenshot above
- nvim-lspconfig — used for the LSP integration
Install with your favorite plugin manager. Example with lazy.nvim:
{
"clavery/nvim-sfcc",
dependencies = { "mfussenegger/nvim-dap", "neovim/nvim-lspconfig" },
---@module "sfcc"
---@type sfcc.setup.opts
opts = {
-- (optional) absolute path to the b2c CLI binary; defaults to "b2c" on $PATH
-- b2c_path = "/path/to/b2c",
-- (optional) add a default `b2c` launch entry to dap.configurations.javascript
-- include_configs = true,
},
}Make sure you have a dw.json (or dw.js) in your project root. The B2C CLI reads it, plus environment variables, .env, and active-instance selection — see b2c setup inspect to verify resolved config.
A typical dw.json:
{
"name": "default",
"active": true,
"hostname": "abcd-001.dx.commercecloud.salesforce.com",
"username": "...",
"password": "...",
"code-version": "version1",
"configs": [
{
"name": "sandbox002",
"active": false,
"hostname": "abcd-002.dx.commercecloud.salesforce.com",
"username": "...",
"password": "...",
"code-version": "version1"
}
]
}Add a launch entry. The DAP adapter is registered as b2c; pass extra b2c debug flags via the args field if you need to override the active instance or cartridge path:
{
"configurations": [
{
"type": "b2c",
"request": "launch",
"name": "Attach to active sandbox"
},
{
"type": "b2c",
"request": "launch",
"name": "Attach to sandbox002",
"args": ["--instance", "sandbox002"]
}
]
}Then start a session with the normal nvim-dap commands (see nvim-dap for keybindings):
:lua require('dap').continue()Requires: nvim-lspconfig (or any TS LSP client) and the B2C CLI on your $PATH.
The plugin resolves the bundled @salesforce/b2c-script-types TypeScript Server plugin via b2c setup ide tsserver-plugin --json and feeds it to your LSP server's init_options. Confirm the CLI works first:
b2c setup ide tsserver-pluginFor nvim-lspconfig:
local lspconfig = require('lspconfig')
lspconfig.ts_ls.setup({
init_options = {
plugins = {
require('sfcc').b2c_lsp_plugin(),
},
},
})Or with newer (>=0.11) neovim:
vim.lsp.config("ts_ls", {
init_options = {
plugins = {
require("sfcc").b2c_lsp_plugin(),
},
},
})b2c_lsp_plugin(config) accepts an optional config table forwarded to the TS Server plugin (e.g. { cartridges = { ... }, enabled = true }). When omitted, the plugin auto-discovers cartridges and reads dw.json for ordering.
If b2c is not on $PATH, point at it explicitly:
require('sfcc').setup({
b2c_path = "/absolute/path/to/b2c",
})You generally do not need a tsconfig.json or jsconfig.json — the plugin resolves dw/* and cartridge-relative requires internally. If you keep one for unrelated paths, avoid mapping dw/* or ~/cartridge/* yourself; let the plugin handle those.
Files with the .isml extension get filetype html.isml. With nvim-treesitter and the html parser installed you'll get the richest experience: every <is*> tag name is highlighted as a directive, the body of <isscript> is injected as JavaScript, and <iscomment> bodies render as comments. Without tree-sitter, the bundled syntax/isml.vim overlay layers the same rules on top of the stock html syntax. ISML expressions (${ ... }) are highlighted via an extmark provider that runs in either mode — no extra setup required.
No custom tree-sitter parser is shipped: the queries extend the upstream tree-sitter-html parser via after/queries/html/*.
- TypeScript < v5.0.0 is not supported —
typescript-language-serveruses your project's TypeScript when present, and the bundled plugin requires TS 5+
Open a github issue for any issues.
This project should not be treated as a Salesforce Product. Customers and partners use this at-will with no expectation of roadmap, technical support, defect resolution, or production-style SLAs.
This project is maintained by the Salesforce Community. Salesforce Commerce Cloud or Salesforce Platform Technical Support do not support this project or its setup.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
