Skip to content

Commit affe6f0

Browse files
A few updates:
--- * Fixes #7 and adds support for SFRA * Adds new `--compile-only` flag to `sfcc watch` so you can use VS Code to handle uploads and just trigger the compiler on code changes * Adds better error handling for when uploads fail, and gives you possible reasons why they failed * Updated documentation
1 parent cca16e2 commit affe6f0

File tree

11 files changed

+1168
-1357
lines changed

11 files changed

+1168
-1357
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
node_modules
33
npm-debug.log
44
remote/ssl/sfcc-cli-localhost.*
5+
.vscode

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ Make developing for Salesforce Commerce Cloud work with any IDE on MacOS, Window
1515

1616
- [X] Easily Manage Multiple Clients & Instances
1717
- [X] Watch for code changes and upload in background ( without being prompted for passwords )
18+
- [X] Support for SFRA JS & CSS Compilers
1819
- [X] Support for Eclipse Build Processes
1920
- [X] Log Viewing with Advanced Search & Filter Capabilities
20-
- [X] SFCC CLI integration via [Browser Extension](https://github.com/redvanworkshop/sfcc-remote)
2121

2222

2323
Developer Overview

bin/cli.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ const argv = yargs
5757
describe: 'Only Show Notification for Errors',
5858
type: 'boolean',
5959
default: false
60+
},
61+
'compile-only': {
62+
describe: 'No Uploads, just Run Compilers',
63+
type: 'boolean',
64+
default: false
6065
}
6166
})
6267
.command('log [client] [instance]', 'Stream log files from an instance', {

commands/log.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,14 @@ module.exports = async options => {
3636
username: selected.u,
3737
password: selected.p
3838
}
39+
}).catch(error => {
40+
console.log(chalk.red.bold('\n✖') + ` ${error}\n`)
3941
})
4042

43+
if (!files) {
44+
return
45+
}
46+
4147
files = files.filter(({displayname}) => displayname.includes('.log'))
4248

4349
// Group Logs

commands/watch.js

Lines changed: 128 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ module.exports = options => {
1717
let instance = argv['_'][2] || null
1818
let selected = null
1919
let errorMessage
20-
let recentFiles = []
2120

2221
const useLog = options.log
2322
const errorsOnly = options.errorsOnly
23+
const compileOnly = options.compileOnly
2424

2525
// Get Client & Instance, or check for Default
2626
if (client && instance) {
@@ -46,6 +46,10 @@ module.exports = options => {
4646
spinner.start()
4747
}
4848

49+
// intentional empty
50+
console.log('')
51+
spinner.start()
52+
4953
const watcher = chokidar.watch(selected.d, {
5054
ignored: [/[/\\]\./, '**/node_modules/**', '**/bundle-analyzer.*'],
5155
ignoreInitial: true,
@@ -81,96 +85,146 @@ module.exports = options => {
8185
if (ext === 'js') {
8286
output(() =>
8387
console.log(
84-
`\n${chalk.bgGreen.white.bold(' SFRA ')} ${chalk.cyan.bold('Compiling')} ${chalk.magenta.bold(
88+
`${chalk.bgGreen.white.bold(' SFRA ')} ${chalk.cyan.bold('Compiling')} ${chalk.magenta.bold(
8589
'JavaScript'
8690
)} ...\n`
8791
)
8892
)
8993

94+
if (!errorsOnly) {
95+
notify({
96+
title: `${client} ${instance}`,
97+
icon: path.join(__dirname, '../icons/', 'sfcc-cli.png'),
98+
subtitle: 'COMPILING JAVASCRIPT ...',
99+
message: `${path.basename(dir)}`
100+
})
101+
}
102+
90103
exec(jsCompile, (err, data, stderr) => {
91104
if (err || stderr) {
92105
output(() => console.log(chalk.red.bold(`✖ Build Error: ${err} ${stderr}`)))
106+
107+
notify({
108+
title: `${client} ${instance}`,
109+
icon: path.join(__dirname, '../icons/', 'sfcc-error.png'),
110+
subtitle: 'COMPILING JAVASCRIPT FAILED',
111+
message: `${err} ${stderr}`,
112+
sound: true,
113+
wait: true
114+
})
115+
} else {
116+
output(() => console.log(`${chalk.green.bold('COMPLETE')}\n`))
117+
output(() => console.log(data))
118+
output(() => console.log('\n'))
119+
120+
if (!errorsOnly) {
121+
notify({
122+
title: `${client} ${instance}`,
123+
icon: path.join(__dirname, '../icons/', 'sfcc-success.png'),
124+
subtitle: 'COMPILE COMPLETE',
125+
message: `${path.basename(dir)}`
126+
})
127+
}
93128
}
94129
})
95130
} else if (ext === 'css' || ext === 'scss') {
96131
output(() =>
97132
console.log(
98-
`\n${chalk.bgGreen.white.bold(' SFRA ')} ${chalk.cyan.bold('Compiling')} ${chalk.magenta.bold('CSS')} ...\n`
133+
`${chalk.bgGreen.white.bold(' SFRA ')} ${chalk.cyan.bold('Compiling')} ${chalk.magenta.bold('CSS')} ...\n`
99134
)
100135
)
101136

137+
if (!errorsOnly) {
138+
notify({
139+
title: `${client} ${instance}`,
140+
icon: path.join(__dirname, '../icons/', 'sfcc-cli.png'),
141+
subtitle: 'COMPILING CSS ...',
142+
message: `${path.basename(dir)}`
143+
})
144+
}
145+
102146
exec(cssCompile, (err, data, stderr) => {
103147
if (err || stderr) {
104148
output(() => console.log(chalk.red.bold(`✖ SFRA Compile Error: ${err} ${stderr}`)))
149+
150+
notify({
151+
title: `${client} ${instance}`,
152+
icon: path.join(__dirname, '../icons/', 'sfcc-error.png'),
153+
subtitle: 'COMPILING CSS FAILED',
154+
message: `${err} ${stderr}`,
155+
sound: true,
156+
wait: true
157+
})
158+
} else {
159+
output(() => console.log(`${chalk.green.bold('COMPLETE')}\n`))
160+
output(() => console.log(data))
161+
output(() => console.log('\n'))
162+
163+
if (!errorsOnly) {
164+
notify({
165+
title: `${client} ${instance}`,
166+
icon: path.join(__dirname, '../icons/', 'sfcc-success.png'),
167+
subtitle: 'COMPILE COMPLETE',
168+
message: `${path.basename(dir)}`
169+
})
170+
}
105171
}
106172
})
107173
}
108174
}
109175

110176
const buildCheck = file => {
111-
if (recentFiles.indexOf(file) === -1) {
112-
recentFiles.push(file)
113-
114-
setTimeout(() => {
115-
let idx = recentFiles.indexOf(file)
116-
recentFiles.splice(idx, 1)
117-
}, 10000)
118-
119-
if (Object.keys(selected.b).length > 0) {
120-
const checkPath = path.dirname(file).replace(path.normalize(selected.d), '')
121-
Object.keys(selected.b).map(build => {
122-
const builder = selected.b[build]
123-
if (
124-
builder.enabled &&
125-
new RegExp(builder.watch.join('|')).test(checkPath) &&
126-
typeof builder.cmd.exec !== 'undefined' &&
127-
builder.cmd.exec.length > 0
128-
) {
129-
const cmd = builder.cmd.exec
130-
const building = build.split('_')
131-
132-
output(() =>
133-
console.log(
134-
`\n${chalk.bgGreen.white.bold(' BUILDING ')} ${chalk.cyan.bold(
135-
building[1]
136-
)} for cartridge ${chalk.magenta.bold(building[0])} ...\n\n`
137-
)
138-
)
139-
exec(cmd, (err, data, stderr) => {
140-
if (err || stderr) {
141-
output(() => console.log(chalk.red.bold(`✖ Build Error: ${err} ${stderr}`)))
142-
}
143-
})
144-
}
145-
})
146-
} else {
147-
const filePath = path.dirname(file)
148-
const ext = file.split('.').pop()
149-
const dirs = filePath.split('/')
150-
const length = path.normalize(selected.d).split('/').length
151-
152-
// Ignore file changes that are likely results from builds
153-
const ignoredPath = ['/css/', '/js/']
154-
155-
// Check current directory for WebPack
177+
if (Object.keys(selected.b).length > 0) {
178+
const checkPath = path.dirname(file).replace(path.normalize(selected.d), '')
179+
Object.keys(selected.b).map(build => {
180+
const builder = selected.b[build]
156181
if (
157-
fs.existsSync(path.join(filePath, 'webpack.config.js')) &&
158-
!new RegExp(ignoredPath.join('|')).test(file)
182+
builder.enabled &&
183+
new RegExp(builder.watch.join('|')).test(checkPath) &&
184+
typeof builder.cmd.exec !== 'undefined' &&
185+
builder.cmd.exec.length > 0
159186
) {
160-
compile(ext, filePath)
161-
} else {
162-
// Work our way backwards to look for WebPack until we get to project root
163-
for (var i = dirs.length; i >= length; i--) {
164-
dirs.pop()
165-
let curPath = dirs.join('/')
166-
167-
if (
168-
fs.existsSync(path.join(curPath, 'webpack.config.js')) &&
169-
!new RegExp(ignoredPath.join('|')).test(file)
170-
) {
171-
compile(ext, curPath)
172-
break
187+
const cmd = builder.cmd.exec
188+
const building = build.split('_')
189+
190+
output(() =>
191+
console.log(
192+
`\n${chalk.bgGreen.white.bold(' BUILDING ')} ${chalk.cyan.bold(
193+
building[1]
194+
)} for cartridge ${chalk.magenta.bold(building[0])} ...\n\n`
195+
)
196+
)
197+
exec(cmd, (err, data, stderr) => {
198+
if (err || stderr) {
199+
output(() => console.log(chalk.red.bold(`✖ Build Error: ${err} ${stderr}`)))
173200
}
201+
})
202+
}
203+
})
204+
} else {
205+
const filePath = path.dirname(file)
206+
const ext = file.split('.').pop()
207+
const dirs = filePath.split('/')
208+
const length = path.normalize(selected.d).split('/').length
209+
210+
// Ignore file changes that are likely results from builds
211+
const ignoredPath = ['/css/', '/static/']
212+
213+
// Check current directory for WebPack
214+
if (fs.existsSync(path.join(filePath, 'webpack.config.js')) && !new RegExp(ignoredPath.join('|')).test(file)) {
215+
compile(ext, filePath)
216+
} else {
217+
// Work our way backwards to look for WebPack until we get to project root
218+
for (var i = dirs.length; i >= length; i--) {
219+
dirs.pop()
220+
let curPath = dirs.join('/')
221+
222+
if (
223+
fs.existsSync(path.join(curPath, 'webpack.config.js')) &&
224+
!new RegExp(ignoredPath.join('|')).test(file)
225+
) {
226+
compile(ext, curPath)
227+
break
174228
}
175229
}
176230
}
@@ -185,18 +239,26 @@ module.exports = options => {
185239

186240
// Watch for File Changes
187241
watcher.on('change', file => {
188-
upload({file, spinner, selected, client, instance, options, callback})
242+
if (!compileOnly) {
243+
upload({file, spinner, selected, client, instance, options, callback})
244+
}
245+
189246
buildCheck(file)
190247
})
191248

192249
watcher.on('add', file => {
193-
upload({file, spinner, selected, client, instance, options, callback})
250+
if (!compileOnly) {
251+
upload({file, spinner, selected, client, instance, options, callback})
252+
}
253+
194254
buildCheck(file)
195255
})
196256

197257
// @TODO: Watch for Removing Files
198258
watcher.on('unlink', file => {
199-
output(() => console.log(`${chalk.red('✗ REMOVING')} ${file.replace(selected.d, '.')}`))
259+
if (!compileOnly) {
260+
output(() => console.log(`${chalk.red('✖ REMOVING')} ${file.replace(selected.d, '.')}`))
261+
}
200262
})
201263

202264
// Watch for Errors

docs/cmd-watch.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ If you don't want to get notified of every upload, but would rather only get not
2929
sfcc watch <client> <instance> --errors-only
3030
```
3131

32+
If you just want to trigger the compiler on file changes, and let your IDE handle uploads, you can pass `--compile-only`:
33+
34+
```bash
35+
sfcc watch <client> <instance> --compile-only
36+
```
37+
3238
If you would like to run the watcher as a background process, but capture the log output, you can pass `--log`, and a log will be created at `~/.sfcc-cli.log` ( log is truncated to last 500 lines each time you start a new `watch` ):
3339

3440
```bash

lib/find.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ module.exports = async (file, options) => {
3333
})
3434
})
3535
} catch (err) {
36-
console.log(err)
37-
return new Error(err)
36+
return await new Promise((resolve, reject) => {
37+
let errorMessage = 'Unable to connect to WebDAV. Check credentials in ~/.sfcc-cli'
38+
if (err && err.message === 'Request failed with status code 401') {
39+
errorMessage = 'Invalid Username or Password. Run `sfcc setup` or edit ~/.sfcc-cli'
40+
}
41+
42+
reject(errorMessage)
43+
})
3844
}
3945
}

lib/read.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ const path = require('path')
22
const axios = require('axios')
33

44
module.exports = async (file, options) => {
5-
const {data} = await axios(
6-
Object.assign({}, options, {
7-
url: path.isAbsolute(file) ? file : `/${file}`,
8-
method: 'GET'
9-
})
10-
)
5+
try {
6+
const {data} = await axios(
7+
Object.assign({}, options, {
8+
url: path.isAbsolute(file) ? file : `/${file}`,
9+
method: 'GET'
10+
})
11+
)
1112

12-
return data
13+
return data
14+
} catch (err) {}
1315
}

lib/upload.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,18 +86,23 @@ module.exports = async ({file, spinner, selected, client, instance, options, cal
8686
}, 3000)
8787
}
8888
} catch (err) {
89+
let errorMessage = `Unable to Upload ${path.basename(src)}`
90+
if (typeof err.retriesLeft !== 'undefined' && err.retriesLeft === 0) {
91+
errorMessage = 'Invalid Username or Password. Run `sfcc setup` or edit ~/.sfcc-cli'
92+
}
93+
8994
if (useLog) {
90-
logger.log('✖ ' + err.message)
95+
logger.log('✖ ' + errorMessage)
9196
} else {
92-
spinner.text = err.message
97+
spinner.text = errorMessage
9398
spinner.fail()
9499
}
95100

96101
notify({
97102
title: `${client} ${instance}`,
98103
icon: path.join(__dirname, '../icons/', 'sfcc-error.png'),
99104
subtitle: 'UPLOAD FAILED',
100-
message: err.message,
105+
message: errorMessage,
101106
sound: true,
102107
wait: true
103108
})

0 commit comments

Comments
 (0)