sfcc-dev-mcp
Version:
MCP server for Salesforce B2C Commerce Cloud development assistance including logs, debugging, and development tools
398 lines (311 loc) • 12.2 kB
Markdown
# Instructions for Creating a Salesforce B2C Commerce (SFRA) Cartridge
This document provides instructions to create, configure, and deploy a new custom cartridge for Salesforce B2C Commerce using the Storefront Reference Architecture (SFRA).
**NOTE**: When doing this, also request best practices for controller creation from this MCP server. Additionally, consult the **Performance and Stability Best Practices** guide from this MCP server to ensure your cartridge follows performance optimization strategies and coding standards.
## 1. Core Principles
**Cartridge:** A cartridge is a self-contained module for code and data. It is the fundamental unit for extending functionality.
**Cartridge Path:** A colon-separated list of cartridge names that dictates the order of code execution. The path is searched from left to right, and the first resource found is used.
**Override Mechanism:** To customize functionality, create a new cartridge (e.g., app_custom_mybrand) and place it at the beginning of the cartridge path. This allows your custom files to override the base functionality without modifying the core app_storefront_base cartridge.
**Example Path:** `app_custom_mybrand:app_storefront_base`
## 2. Prerequisites
- **Git Client:** Installed and configured.
- **Node.js:** Version 18 is recommended for compatibility.
- **SFCC Sandbox:** Access to a sandbox instance, including Business Manager credentials.
- **GitHub Access:** Ability to clone SalesforceCommerceCloud repositories.
## 3. Environment Setup
Create a parent project directory.
Clone the following repositories as siblings inside the parent directory:
Note: This step is not necessary unless specifically asked for. You should only clone these if the user requests it, for most
projects, there is only a need for the new cartridge that is being built - but not the entire storefront reference architecture.
```bash
# Contains the base cartridge (app_storefront_base)
git clone git@github.com:SalesforceCommerceCloud/storefront-reference-architecture.git
# Contains build and deployment scripts
git clone git@github.com:SalesforceCommerceCloud/sgmf-scripts.git
```
Install dependencies:
```bash
cd storefront-reference-architecture
npm install
```
## 4. Cartridge File Structure
A new cartridge should be created using the provided scaffolding tool. The core structure is as follows:
```
package.json
.eslintrc.json
.eslintignore
.stylelintrc.json
.gitignore
README.md
dw.json
webpack.config.js
cartridges/
└── plugin_my_custom_cartridge/
└── cartridge/
├── client/
│ └── default/
│ ├── js/
│ └── scss/
├── controllers/
├── models/
├── scripts/
└── templates/
└── default/
```
Optional but common directories:
- `cartridge/forms/default/`: XML form definitions.
- `cartridge/services/`: Definitions for external web service integrations.
- `cartridge/scripts/jobs/`: Scripts for automated tasks scheduled in Business Manager.
- `cartridge/properties/`: Localization string files (.properties).
## 5. Creating a New Cartridge
### Step 1: Generate the Cartridge Structure
!IMPORTANT!: Always do this step, don't attempt to create the cartridge structure manually. The scaffolding tool ensures all necessary files and configurations are created correctly.
The `sgmf-scripts --createCartridge` command creates a complete project structure including all necessary configuration files. Run the following commands:
```bash
npm install sgmf-scripts -g
sgmf-scripts --createCartridge plugin_my_custom_cartridge
```
**Important**: This command creates a subdirectory named `plugin_my_custom_cartridge` containing the entire project structure, including:
- `.eslintrc.json`
- `.stylelintrc.json`
- `dw.json`
- `package.json`
- `webpack.config.js`
- `cartridges/` directory with the cartridge structure
- Other configuration files
### Step 2: Reorganize Project Structure (Optional)
If you want the project files at the root level instead of in a subdirectory, you can move all files and folders up one level:
```bash
# Navigate to the created directory
cd plugin_my_custom_cartridge
# Move all files and folders up one level
mv * ../
mv .* ../ 2>/dev/null || true # Move hidden files, ignore errors for . and ..
# Navigate back to parent and remove empty directory
cd ..
rmdir plugin_my_custom_cartridge
```
### Step 3: Complete the Setup with manual file creation
#### .eslintignore (ESLint Ignore Configuration)
```plaintext
node_modules/
cartridges/**/cartridge/static/
coverage/
doc/
bin/
codecept.conf.js
```
#### gitignore (Git Ignore Configuration)
```plaintext
node_modules/
cartridges/*/cartridge/static/
coverage/
npm-debug.log
cartridges.zip
.idea/
dw.json
sitegenesisdata/
mobilefirstdata/
storefrontdata/
demo_data_sfra/
demo_data_sfra.zip
.DS_Store
test/appium/webdriver/config.json
.vscode
.history
*.iml
.idea
test/acceptance/report
test/integration/config.json
```
**Note**: Only do this reorganization if you prefer to have the project files at the root level of your workspace. The generated structure works perfectly as-is within the subdirectory.
## 6. Configuration Files (Reference)
The following configuration files are automatically generated by the scaffolding tool. You can reference these examples if you need to understand or modify the generated configurations:
**Note:** The scaffolding tool creates these files automatically. Only modify them if you have specific customization needs.
### .eslintrc.json (ESLint Configuration)
```json
{
"root": true,
"extends": [
"airbnb-base/legacy",
"prettier"
],
"rules": {
"import/no-unresolved": "off",
"indent": ["error", 4, { "SwitchCase": 1, "VariableDeclarator": 1 }],
"func-names": "off",
"require-jsdoc": "error",
"valid-jsdoc": ["error", { "preferType": { "Boolean": "boolean", "Number": "number", "object": "Object", "String": "string" }, "requireReturn": false}],
"vars-on-top": "off",
"global-require": "off",
"no-shadow": ["error", { "allow": ["err", "callback"]}],
"max-len": "off",
"no-plusplus": "off"
}
}
```
### package.json (Main Configuration)
```json
{
"name": "plugin_my_custom_cartridge",
"version": "1.0.0",
"description": "A custom cartridge, for your SFRA project",
"main": "index.js",
"scripts": {
"test": "sgmf-scripts --test test/unit/**/*.js",
"lint": "sgmf-scripts --lint js",
"upload": "sgmf-scripts --upload -- ",
"uploadCartridge": "sgmf-scripts --uploadCartridge plugin_dynamicurl && sgmf-scripts --uploadCartridge bm_dynamicurl_sitemap",
"lint:isml": "./node_modules/.bin/isml-linter",
"build:isml": "./node_modules/.bin/isml-linter --build",
"fix:isml": "./node_modules/.bin/isml-linter --autofix",
"watch": "sgmf-scripts --watch",
"prepare": "husky install"
},
"devDependencies": {
"app-module-path": "2.2.0",
"chai": "4.3.6",
"chai-subset": "1.6.0",
"dw-api-mock": "git+https://bitbucket.org/theunth/dw-api-mock.git",
"eslint": "8.56.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-sitegenesis": "1.0.0",
"husky": "8.0.1",
"isml-linter": "5.40.3",
"lodash": "4.17.21",
"mocha": "10.1.0",
"proxyquire": "2.1.3",
"sgmf-scripts": "3.0.0",
"sinon": "17.0.1"
},
"browserslist": [
"last 2 versions",
"ie >= 10"
],
"paths": {
"base": "../storefront-reference-architecture/cartridges/app_storefront_base/"
}
}
```
### dw.json (Deployment Credentials)
This file contains sandbox credentials. It must be added to .gitignore. This file lives in the root of the project.
```json
{
"hostname": "your-sandbox-id.dx.commercecloud.salesforce.com",
"username": "your.username@example.com",
"password": "your-business-manager-password",
"code-version": "sfra_dev_v1"
}
```
### webpack.config.js (Webpack Configuration)
```javascript
'use strict';
var path = require('path');
var ExtractTextPlugin = require('sgmf-scripts')['extract-text-webpack-plugin'];
var sgmfScripts = require('sgmf-scripts');
module.exports = [{
mode: 'production',
name: 'js',
entry: sgmfScripts.createJsPath(),
output: {
path: path.resolve('./cartridges/plugin_my_custom_cartridge/cartridge/static'),
filename: '[name].js'
}
}, {
mode: 'none',
name: 'scss',
entry: sgmfScripts.createScssPath(),
output: {
path: path.resolve('./cartridges/plugin_my_custom_cartridge/cartridge/static'),
filename: '[name].css'
},
module: {
rules: [{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
use: [{
loader: 'css-loader',
options: {
url: false,
minimize: true
}
}, {
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')()
]
}
}, {
loader: 'sass-loader',
options: {
includePaths: [
path.resolve(process.cwd(), '../storefront-reference-architecture/node_modules/'),
path.resolve(process.cwd(), '../storefront-reference-architecture/node_modules/flag-icon-css/sass')
]
}
}]
})
}]
},
plugins: [
new ExtractTextPlugin({ filename: '[name].css' })
]
}];
```
## 7. "Hello, World" Example
### Step 1: Navigate to Your Project
After creating the cartridge structure, navigate to your project directory:
```bash
# If you kept the subdirectory structure:
cd plugin_my_custom_cartridge
# If you moved files to root level, you're already in the right place
```
### Step 2: Create the Controller
**File:** `cartridges/plugin_my_custom_cartridge/cartridge/controllers/Hello.js`
```javascript
'use strict';
var server = require('server');
// URL: /Hello-Show
server.get('Show', function (req, res, next) {
res.render('hello/helloTemplate', {
message: 'Hello from a custom cartridge!'
});
next();
});
module.exports = server.exports();
```
### Step 3: Create the ISML Template
**File:** `cartridges/plugin_my_custom_cartridge/cartridge/templates/default/hello/helloTemplate.isml`
```html
<iscontent type="text/html" charset="UTF-8" compact="true"/>
<isdecorate template="common/layout/page">
<isreplace name="main">
<div class="container">
<h1>Custom Page</h1>
<p>${pdict.message}</p>
</div>
</isreplace>
</isdecorate>
```
### Step 4: Update Deployment Configuration
Ensure your `dw.json` file has the correct sandbox credentials (this file is automatically generated but needs your specific sandbox details).
## 8. Deployment and Registration
### Step 1: Upload the Cartridge
From the project root, run:
```bash
npm run uploadCartridge plugin_my_custom_cartridge
```
For continuous development, use `npm run watch`.
## 9. Naming Conventions & Best Practices
**Cartridge Naming:** Use standard prefixes.
- `app_custom_*`: Site-specific customizations.
- `int_*`: Third-party integrations.
- `bm_*`: Business Manager extensions.
- `plugin_*`: Reusable SFRA feature extensions.
**File Naming:** Controllers use PascalCase.js. Other JS files use camelCase.js.
**NEVER modify app_storefront_base or other base/plugin cartridges directly.**
**Extend, Don't Replace:** Use server.append() or server.prepend() to extend controllers. Avoid server.replace().
**Logic-less Templates:** Keep business logic out of ISML files. Use models to prepare data.
**Security:** Protect all state-changing POST requests with CSRF tokens. Properly encode all user-provided output to prevent XSS.
**Localization:** Use Resource.msg() in templates to fetch text from .properties files.