├── cartridges
└── sfcc_dev_tools
│ ├── package.json
│ ├── cartridge
│ ├── sfcc_dev_tools.properties
│ ├── scripts
│ │ ├── hooks.json
│ │ ├── hooks
│ │ │ ├── benchmark.js
│ │ │ └── devtools.js
│ │ └── util
│ │ │ └── serialize.js
│ ├── static
│ │ └── default
│ │ │ ├── js
│ │ │ └── dev_tools.js.LICENSE.txt
│ │ │ └── css
│ │ │ └── dev_tools.css
│ ├── templates
│ │ └── default
│ │ │ └── sfcc
│ │ │ └── devtools.isml
│ └── controllers
│ │ └── DevTools.js
│ ├── caches.json
│ ├── .project
│ └── README.md
├── LICENSE
└── README.md
/cartridges/sfcc_dev_tools/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "hooks": "./cartridge/scripts/hooks.json",
3 | "caches": "./caches.json"
4 | }
5 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/cartridge/sfcc_dev_tools.properties:
--------------------------------------------------------------------------------
1 | ## cartridge.properties for cartridge sfcc_dev_tools
2 | #Mon Oct 26 21:28:54 CDT 2020
3 | demandware.cartridges.sfcc_dev_tools.multipleLanguageStorefront=true
4 | demandware.cartridges.sfcc_dev_tools.id=sfcc_dev_tools
5 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/caches.json:
--------------------------------------------------------------------------------
1 | {
2 | "caches": [
3 | {
4 | "id": "DevToolsCache",
5 | "expireAfterSeconds": 300
6 | },
7 | {
8 | "id": "DevToolsBenchmarkCache",
9 | "expireAfterSeconds": 300
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/cartridge/scripts/hooks.json:
--------------------------------------------------------------------------------
1 | {
2 | "hooks": [
3 | {
4 | "name": "sfcc.util.devtools",
5 | "script": "./hooks/devtools"
6 | },
7 | {
8 | "name": "app.template.afterFooter",
9 | "script": "./hooks/devtools"
10 | },
11 | {
12 | "name": "app.server.registerRoute",
13 | "script": "./hooks/benchmark"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | sfcc_dev_tools
4 |
5 |
6 |
7 |
8 |
9 | com.demandware.studio.core.beehiveElementBuilder
10 |
11 |
12 |
13 |
14 |
15 | com.demandware.studio.core.beehiveNature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 SFCC DevOps
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/cartridge/static/default/js/dev_tools.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*!
2 | * Vue.js v2.7.14
3 | * (c) 2014-2022 Evan You
4 | * Released under the MIT License.
5 | */
6 |
7 | /**!
8 | * @fileOverview Kickass library to create and place poppers near their reference elements.
9 | * @version 1.16.1
10 | * @license
11 | * Copyright (c) 2016 Federico Zivolo and contributors
12 | *
13 | * Permission is hereby granted, free of charge, to any person obtaining a copy
14 | * of this software and associated documentation files (the "Software"), to deal
15 | * in the Software without restriction, including without limitation the rights
16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 | * copies of the Software, and to permit persons to whom the Software is
18 | * furnished to do so, subject to the following conditions:
19 | *
20 | * The above copyright notice and this permission notice shall be included in all
21 | * copies or substantial portions of the Software.
22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 | * SOFTWARE.
30 | */
31 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/cartridge/scripts/hooks/benchmark.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | /**
4 | * Hook executed for SFRA-only
5 | * Registers SFRA route
6 | * @param route
7 | */
8 | function registerRoute(route) {
9 | route.on('route:Start', function onRouteStartHandler(req, res) {
10 | var path = req.path.split('/');
11 | var controller = path.pop();
12 | var type = req.includeRequest ? 'remote-include' : 'route';
13 | var name = (res.viewData) ? res.viewData.action + '|' + res.viewData.queryString : controller;
14 |
15 | if (dw.system.HookMgr.hasHook('sfcc.util.devtools')) {
16 | dw.system.HookMgr.callHook('sfcc.util.devtools', 'benchmark', 'start', {
17 | name: name.replace(/\|$/, ''),
18 | type: type,
19 | start: new Date().getTime()
20 | });
21 | }
22 | });
23 |
24 | route.on('route:Redirect', function onRouteRedirectHandler() {
25 | // Redirect logic here
26 | });
27 |
28 | route.on('route:Step', function onRouteStepHandler() {
29 | // Step logic here
30 | });
31 |
32 | route.on('route:Complete', function onRouteCompleteHandler(req, res) {
33 | var path = req.path.split('/');
34 | var controller = path.pop();
35 | var name = (res.viewData) ? res.viewData.action + '|' + res.viewData.queryString : controller;
36 |
37 | if (dw.system.HookMgr.hasHook('sfcc.util.devtools')) {
38 | dw.system.HookMgr.callHook('sfcc.util.devtools', 'benchmark', 'stop', name.replace(/\|$/, ''));
39 | }
40 | });
41 |
42 | route.on('route:BeforeComplete', function onRouteBeforeCompleteHandler(req, res) {
43 | var path = req.path.split('/');
44 | var controller = path.pop();
45 | var name = (res.viewData) ? res.viewData.action + '|' + res.viewData.queryString : controller;
46 |
47 | if (dw.system.HookMgr.hasHook('sfcc.util.devtools')) {
48 | dw.system.HookMgr.callHook('sfcc.util.devtools', 'benchmark', 'stop', name.replace(/\|$/, ''));
49 | }
50 | });
51 | }
52 |
53 | /* Module Exports */
54 | exports.registerRoute = registerRoute;
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | SFCC Developers Core - SFCC Cartridge
4 | ===
5 |
6 | > A Salesforce Commerce Cloud (Demandware) Cartridge for Developers.
7 |
8 | ## Cartridges
9 |
10 | - [X] **[Dev Tools](./cartridges/sfcc_dev_tools/)** - Developer Tools for Debugging your Storefront
11 |
12 | > **:warning: NOTICE:** The author of the **Dev Console** has requested their cartridge be relocated to their personal GitHub account for future development. You can locate this cartridge at its **[New Home](https://github.com/z1haze/sfcc_dev_console)**.
13 |
14 | Installation
15 | ---
16 |
17 | [](https://github.com/opensfcc/sfcc_developers_core/releases/latest)
18 |
19 | 1. Unzip and Rename the folder to `sfcc_developers_core`
20 | 2. Move `sfcc_developers_core` into the root of your SFCC Project
21 | 3. Add `sfcc_developers_core` to project or global `.gitignore`
22 | 4. Add `sfcc_dev_tools` to Business Manager Storefront `Cartridges` Path
23 | 5. Review Usage Instructions in each Cartridges README.md
24 |
25 | Contributing
26 | ---
27 |
28 | > Interested in making this tool better? Fork this Repository and we'll gladly accept Pull Requests.
29 |
30 | #### Developer Setup:
31 |
32 | ```bash
33 | git clone https://github.com/opensfcc/sfcc_developers_core.git
34 | cd sfcc_developers_core
35 | npm install
36 | npm run dev # one time build for development
37 | npm run watch # watch for changes and build for development
38 | npm run build # one time build for production
39 | ```
40 |
41 | Once you have something you would like to share, check out our Contribution Guide.
42 |
43 | [](https://github.com/opensfcc/sfcc_developers_core/blob/develop/.github/CONTRIBUTING.md)
44 |
45 | Disclaimer
46 | ---
47 |
48 | > The trademarks and product names of Salesforce®, including the mark Salesforce®, are the property of Salesforce.com. OpenSFCC is not affiliated with Salesforce.com, nor does Salesforce.com sponsor or endorse the OpenSFCC products or website. The use of the Salesforce® trademark on this project does not indicate an endorsement, recommendation, or business relationship between Salesforce.com and OpenSFCC.
49 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/cartridge/templates/default/sfcc/devtools.isml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/cartridge/controllers/DevTools.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Controller to handle AJAX Calls from Dev Tools Drawer
5 | *
6 | * @module controllers/DevTools
7 | */
8 |
9 | var serialize = require('../scripts/util/serialize');
10 |
11 | /**
12 | * Insert Dev Tools Drawer on Site
13 | */
14 | function AfterFooter() {
15 | const System = require('dw/system/System');
16 | const Response = require('dw/system/Response');
17 |
18 | if (request.httpMethod !== 'GET' || System.getInstanceType() === System.PRODUCTION_SYSTEM) {
19 | return;
20 | }
21 |
22 | response.setHttpHeader(Response.CONTENT_SECURITY_POLICY, 'frame-ancestors \'self\'');
23 | response.setHttpHeader(Response.X_CONTENT_TYPE_OPTIONS, 'nosniff');
24 |
25 | var ISML = require('dw/template/ISML');
26 | ISML.renderTemplate('sfcc/devtools');
27 | }
28 |
29 | /**
30 | * Fetch Server Data for Dev Drawer
31 | */
32 | function GetData() {
33 | const System = require('dw/system/System');
34 | const Response = require('dw/system/Response');
35 |
36 | if (System.getInstanceType() === System.PRODUCTION_SYSTEM) {
37 | sendJSON({
38 | error: true,
39 | message: 'Not available on production instance!'
40 | }, 403);
41 |
42 | return;
43 | }
44 |
45 | if (request.httpMethod !== 'GET') {
46 | return sendJSON({
47 | error: true,
48 | message: 'Method Not Allowed'
49 | }, 405);
50 | }
51 |
52 | response.setHttpHeader(Response.CONTENT_SECURITY_POLICY, 'frame-ancestors \'self\'');
53 | response.setHttpHeader(Response.X_CONTENT_TYPE_OPTIONS, 'nosniff');
54 |
55 | const location = request.getGeolocation();
56 |
57 | // Get Basket Info
58 | var BasketMgr = require('dw/order/BasketMgr');
59 | var basket = BasketMgr.getCurrentBasket();
60 |
61 | // Get Preferences
62 | var Site = require('dw/system/Site');
63 | var currentSite = Site.getCurrent();
64 | var preferences = Site.getCurrent().getPreferences();
65 |
66 | // Get Dev Tools Cache
67 | var devToolsCache = dw.system.CacheMgr.getCache('DevToolsCache');
68 | var benchmarkCache = dw.system.CacheMgr.getCache('DevToolsBenchmarkCache');
69 |
70 | // Send Content then Clear Logs
71 | sendJSON({
72 | basket: serialize(basket),
73 | geolocation: serialize(location),
74 | benchmarks: benchmarkCache ? serialize(benchmarkCache.get('benchmarks')) : null,
75 | messages: {
76 | debug: devToolsCache ? devToolsCache.get('debug') : null,
77 | error: devToolsCache ? devToolsCache.get('error') : null,
78 | fatal: devToolsCache ? devToolsCache.get('fatal') : null,
79 | info: devToolsCache ? devToolsCache.get('info') : null,
80 | log: devToolsCache ? devToolsCache.get('log') : null,
81 | warn: devToolsCache ? devToolsCache.get('warn') : null
82 | },
83 | preferences: serialize(preferences),
84 | session: serialize(session),
85 | site: serialize(currentSite)
86 | })
87 | }
88 |
89 | /**
90 | * Helper to send a json response
91 | *
92 | * @param content
93 | * @param status
94 | */
95 | function sendJSON(content, status) {
96 | response.setStatus(status || 200);
97 | response.setContentType('application/json');
98 | response.getWriter().print(JSON.stringify(content));
99 | }
100 |
101 | module.exports.AfterFooter = AfterFooter;
102 | module.exports.AfterFooter.public = true;
103 |
104 | module.exports.GetData = GetData;
105 | module.exports.GetData.public = true;
106 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/cartridge/scripts/util/serialize.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const storedUUIDs = new dw.util.HashSet();
4 |
5 | /**
6 | * Main function that should be able to serialize any type of sfcc object
7 | *
8 | * @param original
9 | * @param maxDepth
10 | * @param depth
11 | * @param pojo
12 | * @returns {string|string|[]|*}
13 | */
14 | function serialize (original, maxDepth, depth, pojo) {
15 | // Set Max Depth if not defined
16 | if (!maxDepth) {
17 | maxDepth = 3;
18 | }
19 |
20 | // Set Current depth if not defined
21 | if (!depth) {
22 | depth = 0;
23 | }
24 |
25 | // return if primitive
26 | if (isPrimitive(original)) {
27 | return original;
28 | }
29 |
30 | // Prevent the cyclic loop caused by e.g. a customer has a profile, and a profile has a customer - around and around we go
31 | if ('UUID' in original) {
32 | if (storedUUIDs.contains(original.UUID)) {
33 | // return '{already returned}';
34 | }
35 |
36 | storedUUIDs.add(original.UUID, pojo || {});
37 | }
38 |
39 | if (depth > maxDepth) {
40 | if ('toString' in original) {
41 | return original.toString();
42 | }
43 |
44 | return '{max depth reached}';
45 | }
46 |
47 | // convert and return a collection
48 | if (original instanceof dw.util.Collection) {
49 | return serializeCollection(original, maxDepth, depth + 1);
50 | }
51 |
52 | // convert and return a map
53 | if (original instanceof dw.util.Map) {
54 | return serializeMap(original, depth + 1, maxDepth);
55 | }
56 |
57 | // return the toString for date
58 | if (original instanceof Date || original instanceof dw.util.Decimal) {
59 | return original.toString();
60 | }
61 |
62 | if (original instanceof XML) {
63 | return original.toXMLString();
64 | }
65 |
66 | // what we have left is an object (hopefully)
67 | // convert and return the object
68 | return serializeObject(original, maxDepth, depth, pojo || {});
69 | }
70 |
71 | /**
72 | * Serializes a native SFCC object to a POJO
73 | *
74 | * @param object
75 | * @param maxDepth
76 | * @param depth
77 | * @param pojo
78 | * @returns {*}
79 | */
80 | function serializeObject (object, maxDepth, depth, pojo) {
81 | for (let prop in object) {
82 | // have to do this because dw has some invalid properties on objects - very weird
83 | let k = null;
84 |
85 | try {
86 | k = object[prop];
87 | } catch (e) {
88 | continue;
89 | }
90 |
91 | if (typeof k === 'function') {
92 | continue;
93 | }
94 |
95 | /**
96 | * For some reason SFCC applies the custom properties on certain object types that
97 | * it really shouldn't, and when trying to process those custom fields, it blows up.
98 | * This helps is to get around that by skipping them under certain scenarios.
99 | */
100 | if (object instanceof dw.order.PaymentProcessor && prop === 'custom') {
101 | continue;
102 | }
103 |
104 | if (object instanceof dw.catalog.ProductActiveData && prop === 'custom') {
105 | continue;
106 | }
107 |
108 | pojo[prop] = serialize(k, maxDepth, depth + 1, {});
109 | }
110 |
111 | return pojo;
112 | }
113 |
114 | /**
115 | * Serializes a native SFCC collection into an array
116 | * @param collection
117 | * @param maxDepth
118 | * @param depth
119 | * @returns {[]}
120 | */
121 | function serializeCollection (collection, maxDepth, depth) {
122 | const iterator = collection.iterator();
123 | const items = [];
124 |
125 | while (iterator.hasNext()) {
126 | let item = iterator.next();
127 | items.push(serialize(item, maxDepth, depth, {}));
128 | }
129 |
130 | return items;
131 | }
132 |
133 | /**
134 | * Serializes a native SFCC map into a pojo
135 | * @param map
136 | * @param depth
137 | * @param maxDepth
138 | * @returns {string|*[]|*}
139 | */
140 | function serializeMap (map, depth, maxDepth) {
141 | const obj = {};
142 |
143 | map.keySet().toArray().forEach(function (item) {
144 | obj[item] = map.get(item);
145 | });
146 |
147 | return serialize(obj, maxDepth, depth, {});
148 | }
149 |
150 | function isPrimitive (v) {
151 | return typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null || typeof v === 'undefined';
152 | }
153 |
154 | module.exports = serialize;
155 |
--------------------------------------------------------------------------------
/cartridges/sfcc_dev_tools/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | SFCC Dev Tools Cartridge
4 | ===
5 |
6 | > A Salesforce Commerce Cloud (Demandware) Cartridge for Developers.
7 |
8 | 
9 |
10 | ## Features
11 |
12 | - [X] Server Side Debugging in ISML, JS & DS Files
13 | - [X] Client Side Rendering of Debug Output in Dev Tools Console
14 | - [X] Interactive Drawer with Console for Debugging and Testing Live Code
15 | - [X] Safety Measures to prevent running in Production Environments
16 | - [X] No Site Preferences or Misc Imports Required
17 |
18 | **NOTE:** The Debugging Console is not built for Native SFCC Messages, but specific to custom debuggers you add in your own code as outlined below.
19 |
20 | Installation
21 | ---
22 |
23 | 1. [Install Cartridge](../../README.md#installation)
24 | 2. Add `sfcc_dev_tools` to Storefront `Cartridges` Path
25 | 3. Add the Dev Tools to your Storefront using either SFRA or Site Genesis instructions below
26 |
27 | #### SFRA
28 |
29 | > You do not need to do any extra setup for SFRA. Our cartridge takes advantage of the `app.template.afterFooter` template hook baked into SFRA.
30 |
31 | #### Site Genesis
32 |
33 | > To render the Dev Tools on Site Genesis, stick this code snippet in an ISML template near the footer of your website as close to the closing `