Skip to content

Commit ac75743

Browse files
Bobby EarlBlackbaud-MikitaYankouski
Bobby Earl
authored andcommitted
Checking if files exists before writing in generate. Allowing --force/-f to bypass. (blackbaud#500)
* Checking if files exists before writing in generate. Allowing force to bypass. * Allowing shortcut force. * Fixed linting error. * Fixed linting suggestions.
1 parent 44655f3 commit ac75743

File tree

4 files changed

+102
-35
lines changed

4 files changed

+102
-35
lines changed

cli/generate.js

+40-33
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,24 @@
33

44
const fs = require('fs-extra');
55
const path = require('path');
6+
const logger = require('@blackbaud/skyux-logger');
67

7-
function resolveFilePath(pathParts, fileName) {
8-
fs.ensureDirSync(path.resolve('src', 'app', ...pathParts));
8+
function safeFileWrite(pathParts, fileName, fileContent, force) {
9+
const resolvedFileName = path.resolve('src', 'app', ...pathParts, fileName);
10+
const resolvedFileExists = fs.existsSync(resolvedFileName);
11+
12+
if (resolvedFileExists && !force) {
13+
logger.warn(`${resolvedFileName} already exists. Use --force to overwrite.`);
14+
return;
15+
}
16+
17+
if (resolvedFileExists) {
18+
logger.warn(`${resolvedFileName} already exists. Forcefully overwriting.`);
19+
}
920

10-
return path.resolve('src', 'app', ...pathParts, fileName);
21+
fs.ensureDirSync(path.resolve('src', 'app', ...pathParts));
22+
fs.writeFileSync(resolvedFileName, fileContent);
23+
logger.info(`Successfully created ${resolvedFileName}.`);
1124
}
1225

1326
function properCase(name) {
@@ -45,9 +58,8 @@ function snakeCase(name) {
4558
return nameSnake;
4659
}
4760

48-
function generateComponentTs(pathParts, fileName, name, nameSnakeCase) {
49-
fs.writeFileSync(
50-
resolveFilePath(pathParts, fileName + '.ts'),
61+
function generateComponentTs(pathParts, fileName, name, nameSnakeCase, force) {
62+
const fileContent =
5163
`import {
5264
Component
5365
} from '@angular/core';
@@ -60,15 +72,14 @@ function generateComponentTs(pathParts, fileName, name, nameSnakeCase) {
6072
export class ${name} {
6173
6274
}
63-
`
64-
);
65-
}
75+
`;
6676

67-
function generateComponentSpec(pathParts, fileName, name, nameSnakeCase) {
68-
let nameWithSpaces = properCase(nameSnakeCase.replace(/\-/g, ' '));
77+
safeFileWrite(pathParts, `${fileName}.ts`, fileContent, force);
78+
}
6979

70-
fs.writeFileSync(
71-
resolveFilePath(pathParts, fileName + '.spec.ts'),
80+
function generateComponentSpec(pathParts, fileName, name, nameSnakeCase, force) {
81+
const nameWithSpaces = properCase(nameSnakeCase.replace(/\-/g, ' '));
82+
const fileContent =
7283
`import {
7384
TestBed
7485
} from '@angular/core/testing';
@@ -105,29 +116,24 @@ describe('${nameWithSpaces} component', () => {
105116
});
106117
107118
});
108-
`
109-
);
119+
`;
120+
121+
safeFileWrite(pathParts, `${fileName}.spec.ts`, fileContent, force);
110122
}
111123

112-
function generateComponentHtml(pathParts, fileName) {
113-
fs.writeFileSync(
114-
resolveFilePath(pathParts, fileName + '.html'),
115-
''
116-
);
124+
function generateComponentHtml(pathParts, fileName, force) {
125+
safeFileWrite(pathParts, `${fileName}.html`, '', force);
117126
}
118127

119-
function generateComponentScss(pathParts, fileName) {
120-
fs.writeFileSync(
121-
resolveFilePath(pathParts, fileName + '.scss'),
122-
''
123-
);
128+
function generateComponentScss(pathParts, fileName, force) {
129+
safeFileWrite(pathParts, `${fileName}.scss`, '', force);
124130
}
125131

126132
function getPathParts(name) {
127133
return name.replace(/\\/g, '/').split('/');
128134
}
129135

130-
function generateComponent(name) {
136+
function generateComponent(name, force) {
131137
const pathParts = getPathParts(name);
132138

133139
const classNameWithoutComponent = properCase(pathParts.pop());
@@ -138,21 +144,22 @@ function generateComponent(name) {
138144

139145
const fileName = `${nameSnakeCase}.component`;
140146

141-
generateComponentTs(pathParts, fileName, className, nameSnakeCase);
142-
generateComponentSpec(pathParts, fileName, className, nameSnakeCase);
143-
generateComponentHtml(pathParts, fileName);
144-
generateComponentScss(pathParts, fileName);
147+
generateComponentTs(pathParts, fileName, className, nameSnakeCase, force);
148+
generateComponentSpec(pathParts, fileName, className, nameSnakeCase, force);
149+
generateComponentHtml(pathParts, fileName, force);
150+
generateComponentScss(pathParts, fileName, force);
145151
}
146152

147153
function generate(argv) {
148154
try {
149-
let type = argv._[1];
150-
let name = argv._[2];
155+
const type = argv._[1];
156+
const name = argv._[2];
157+
const force = argv.force;
151158

152159
switch (type) {
153160
case 'component':
154161
case 'c':
155-
generateComponent(name);
162+
generateComponent(name, force);
156163
break;
157164
}
158165
} catch (err) {

index.js

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function getConfig(command) {
1313
module.exports = {
1414
runCommand: (command, argv) => {
1515
const shorthand = {
16+
f: 'force',
1617
l: 'launch',
1718
b: 'browser',
1819
s: 'serve'
@@ -52,6 +53,7 @@ module.exports = {
5253
require('./cli/version')();
5354
break;
5455
case 'generate':
56+
case 'g':
5557
require('./cli/generate')(argv);
5658
break;
5759
default:

test/cli-generate.spec.js

+53-1
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,12 @@ describe('cli generate', () => {
5656

5757
fsMock = {
5858
ensureDirSync: jasmine.createSpy('ensureDirSync'),
59-
writeFileSync: jasmine.createSpy('writeFileSync')
59+
writeFileSync: jasmine.createSpy('writeFileSync'),
60+
existsSync: jasmine.createSpy('existsSync').and.returnValue(false)
6061
};
6162

6263
mock('fs-extra', fsMock);
64+
mock('@blackbaud/skyux-logger', jasmine.createSpyObj('logger', ['info', 'warn']));
6365

6466
spyOn(process, 'exit').and.returnValue();
6567

@@ -155,4 +157,54 @@ export class ${expectedClassName}`
155157
1
156158
);
157159
});
160+
161+
function generateSafe(force) {
162+
const name = 'my-component';
163+
const pathMock = jasmine.createSpyObj('path', ['resolve']);
164+
const fsMock = jasmine.createSpyObj('fs', [
165+
'ensureDirSync',
166+
'existsSync',
167+
'writeFileSync'
168+
]);
169+
const loggerMock = jasmine.createSpyObj('logger', ['info', 'warn']);
170+
171+
pathMock.resolve.and.returnValue('resolved-' + name);
172+
fsMock.existsSync.and.returnValue(true);
173+
174+
mock('path', pathMock);
175+
mock('fs-extra', fsMock);
176+
mock('@blackbaud/skyux-logger', loggerMock);
177+
178+
const generate = mock.reRequire('../cli/generate');
179+
generate({
180+
_: [
181+
'generate',
182+
'component',
183+
name
184+
],
185+
force: force
186+
});
187+
188+
if (force) {
189+
expect(fsMock.writeFileSync).toHaveBeenCalled();
190+
expect(loggerMock.warn.calls.argsFor(0)).toEqual([
191+
`resolved-${name} already exists. Forcefully overwriting.`
192+
]);
193+
} else {
194+
expect(fsMock.writeFileSync).not.toHaveBeenCalled();
195+
expect(loggerMock.warn).toHaveBeenCalledWith(
196+
`resolved-${name} already exists. Use --force to overwrite.`
197+
);
198+
}
199+
200+
mock.stopAll();
201+
}
202+
203+
it('should log a warning and exit if a file already exists', () => {
204+
generateSafe(false);
205+
});
206+
207+
it('should log a warning and continue writing if force is supplied', () => {
208+
generateSafe(true);
209+
});
158210
});

test/index.spec.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ describe('@blackbaud/skyux-builder', () => {
5454
'generate': {
5555
cmd: 'generate',
5656
lib: 'generate'
57+
},
58+
'g': {
59+
cmd: 'generate',
60+
lib: 'generate'
5761
}
5862
};
5963

@@ -91,11 +95,13 @@ describe('@blackbaud/skyux-builder', () => {
9195
it('should process shorthand tags', (done) => {
9296
const argv = {
9397
l: 'showForLaunch',
94-
b: 'showForBrowser'
98+
b: 'showForBrowser',
99+
f: 'showForForce'
95100
};
96101
mock('../cli/test', (c, a) => {
97102
expect(a.launch).toEqual(argv.l);
98103
expect(a.browser).toEqual(argv.b);
104+
expect(a.force).toEqual(argv.f);
99105
done();
100106
});
101107
const lib = require('../index');

0 commit comments

Comments
 (0)