@sasjs/cli 6.4.2 → 6.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -52,7 +52,8 @@ function printHelpText() {
52
52
  description: [
53
53
  "Creates the 'sasjs' folder and doxy subfolder (with content files + configuration) for docs",
54
54
  "- Inserted in the current working directory.",
55
- "- If an existing NPM project, package.json is updated with the @sasjs/core dependency."
55
+ "- If an existing NPM project, package.json is updated with the @sasjs/core dependency.",
56
+ "- Aborts without changing anything if this folder already contains sasjs/sasjsconfig.json."
56
57
  ]
57
58
  },
58
59
  {
@@ -1,7 +1,22 @@
1
+ export declare class AlreadyInitialisedError extends Error {
2
+ constructor(message: string);
3
+ }
1
4
  export declare function init(): Promise<void>;
5
+ /**
6
+ * Creates the macro folder that the generated `macroFolders` points at.
7
+ * `sasjs compile` fails with ENOENT when that folder is absent, so an
8
+ * initialised project compiles before the user has written a macro.
9
+ * The folder carries a `.gitkeep` because git does not track empty directories -
10
+ * without it the folder disappears on clone and compilation breaks again.
11
+ * @param {string} parentFolderName- the name of the project folder.
12
+ */
13
+ export declare const createMacroFolder: (parentFolderName: string) => Promise<void>;
2
14
  /**
3
15
  * Creates a SASjs configuration file.
4
16
  * Its name will be of the form 'sasjsconfig.json'
17
+ * The target's `serverUrl` is empty rather than a placeholder URL: an empty
18
+ * value reads as "not configured yet", while any non-empty placeholder has to
19
+ * pass URL validation every time the config is read.
5
20
  * @param {string} parentFolderName- the name of the project folder.
6
21
  */
7
22
  export declare const createConfigFile: (parentFolderName: string) => Promise<void>;
@@ -1,4 +1,19 @@
1
1
  "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
2
17
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
18
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
19
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -39,49 +54,104 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
54
  return (mod && mod.__esModule) ? mod : { "default": mod };
40
55
  };
41
56
  Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.createConfigFile = void 0;
57
+ exports.createConfigFile = exports.createMacroFolder = exports.AlreadyInitialisedError = void 0;
43
58
  exports.init = init;
44
59
  var path_1 = __importDefault(require("path"));
45
60
  var utils_1 = require("../../utils/utils");
46
61
  var utils_2 = require("@sasjs/utils");
47
62
  var createLintConfigFile_1 = require("../shared/createLintConfigFile");
63
+ var AlreadyInitialisedError = /** @class */ (function (_super) {
64
+ __extends(AlreadyInitialisedError, _super);
65
+ function AlreadyInitialisedError(message) {
66
+ var _this = _super.call(this, message) || this;
67
+ _this.name = 'AlreadyInitialisedError';
68
+ // The build targets ES5, where extending a built-in does not repair the
69
+ // prototype chain - without this, `instanceof` is false at runtime.
70
+ Object.setPrototypeOf(_this, AlreadyInitialisedError.prototype);
71
+ return _this;
72
+ }
73
+ return AlreadyInitialisedError;
74
+ }(Error));
75
+ exports.AlreadyInitialisedError = AlreadyInitialisedError;
48
76
  function init() {
49
77
  return __awaiter(this, void 0, void 0, function () {
50
- var parentFolderName, lintConfigPath;
78
+ var parentFolderName, existingConfigPath, lintConfigPath;
51
79
  var _a;
52
80
  return __generator(this, function (_b) {
53
81
  switch (_b.label) {
54
82
  case 0:
55
83
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.info('Initialising SASjs...');
56
84
  parentFolderName = '.';
57
- return [4 /*yield*/, (0, utils_1.setupNpmProject)(parentFolderName)];
85
+ existingConfigPath = path_1.default.join(process.projectDir, parentFolderName, 'sasjs', 'sasjsconfig.json');
86
+ return [4 /*yield*/, (0, utils_2.fileExists)(existingConfigPath)];
58
87
  case 1:
88
+ if (_b.sent()) {
89
+ throw new AlreadyInitialisedError('This folder is already a SASjs project (sasjs/sasjsconfig.json exists). Aborting without making changes.');
90
+ }
91
+ return [4 /*yield*/, (0, utils_1.setupNpmProject)(parentFolderName)];
92
+ case 2:
59
93
  _b.sent();
60
94
  return [4 /*yield*/, (0, utils_1.setupGitIgnore)(parentFolderName)];
61
- case 2:
95
+ case 3:
62
96
  _b.sent();
63
97
  return [4 /*yield*/, (0, utils_1.setupDoxygen)(parentFolderName)];
64
- case 3:
98
+ case 4:
65
99
  _b.sent();
66
100
  return [4 /*yield*/, (0, exports.createConfigFile)(parentFolderName)];
67
- case 4:
101
+ case 5:
102
+ _b.sent();
103
+ return [4 /*yield*/, (0, exports.createMacroFolder)(parentFolderName)];
104
+ case 6:
68
105
  _b.sent();
69
106
  lintConfigPath = path_1.default.join(process.projectDir, parentFolderName, '.sasjslint');
70
107
  return [4 /*yield*/, (0, utils_2.fileExists)(lintConfigPath)];
71
- case 5:
72
- if (!!(_b.sent())) return [3 /*break*/, 7];
108
+ case 7:
109
+ if (!!(_b.sent())) return [3 /*break*/, 9];
73
110
  return [4 /*yield*/, (0, createLintConfigFile_1.createLintConfigFile)(parentFolderName)];
74
- case 6:
111
+ case 8:
75
112
  _b.sent();
76
- _b.label = 7;
77
- case 7: return [2 /*return*/];
113
+ _b.label = 9;
114
+ case 9: return [2 /*return*/];
78
115
  }
79
116
  });
80
117
  });
81
118
  }
119
+ /**
120
+ * Creates the macro folder that the generated `macroFolders` points at.
121
+ * `sasjs compile` fails with ENOENT when that folder is absent, so an
122
+ * initialised project compiles before the user has written a macro.
123
+ * The folder carries a `.gitkeep` because git does not track empty directories -
124
+ * without it the folder disappears on clone and compilation breaks again.
125
+ * @param {string} parentFolderName- the name of the project folder.
126
+ */
127
+ var createMacroFolder = function (parentFolderName) { return __awaiter(void 0, void 0, void 0, function () {
128
+ var macroFolderPath, gitKeepPath;
129
+ return __generator(this, function (_a) {
130
+ switch (_a.label) {
131
+ case 0:
132
+ macroFolderPath = path_1.default.join(process.projectDir, parentFolderName, 'sasjs', 'macros');
133
+ return [4 /*yield*/, (0, utils_2.createFolder)(macroFolderPath)];
134
+ case 1:
135
+ _a.sent();
136
+ gitKeepPath = path_1.default.join(macroFolderPath, '.gitkeep');
137
+ return [4 /*yield*/, (0, utils_2.fileExists)(gitKeepPath)];
138
+ case 2:
139
+ if (!!(_a.sent())) return [3 /*break*/, 4];
140
+ return [4 /*yield*/, (0, utils_2.createFile)(gitKeepPath, '')];
141
+ case 3:
142
+ _a.sent();
143
+ _a.label = 4;
144
+ case 4: return [2 /*return*/];
145
+ }
146
+ });
147
+ }); };
148
+ exports.createMacroFolder = createMacroFolder;
82
149
  /**
83
150
  * Creates a SASjs configuration file.
84
151
  * Its name will be of the form 'sasjsconfig.json'
152
+ * The target's `serverUrl` is empty rather than a placeholder URL: an empty
153
+ * value reads as "not configured yet", while any non-empty placeholder has to
154
+ * pass URL validation every time the config is read.
85
155
  * @param {string} parentFolderName- the name of the project folder.
86
156
  */
87
157
  var createConfigFile = function (parentFolderName) { return __awaiter(void 0, void 0, void 0, function () {
@@ -97,7 +167,7 @@ var createConfigFile = function (parentFolderName) { return __awaiter(void 0, vo
97
167
  {
98
168
  name: 'mytarget',
99
169
  serverType: 'SASJS',
100
- serverUrl: ' ',
170
+ serverUrl: '',
101
171
  appLoc: '/Public/apps/myapp'
102
172
  }
103
173
  ]
@@ -85,8 +85,15 @@ var InitCommand = /** @class */ (function (_super) {
85
85
  return command_1.ReturnCode.Success;
86
86
  })
87
87
  .catch(function (err) {
88
- var _a;
89
- (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('Error initialising SASjs: ', err);
88
+ var _a, _b;
89
+ // Refusing an already-initialised folder is an expected outcome, not a
90
+ // crash: report it on one line, and exit with a code a script can tell
91
+ // apart from a genuine failure.
92
+ if (err instanceof init_1.AlreadyInitialisedError) {
93
+ (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error(err.message);
94
+ return command_1.ReturnCode.AlreadyInitialised;
95
+ }
96
+ (_b = process.logger) === null || _b === void 0 ? void 0 : _b.error('Error initialising SASjs: ', err);
90
97
  return command_1.ReturnCode.InternalError;
91
98
  })];
92
99
  case 1: return [2 /*return*/, _a.sent()];
@@ -44,9 +44,11 @@ var path_1 = __importDefault(require("path"));
44
44
  var test_1 = require("../../../utils/test");
45
45
  var utils_1 = require("@sasjs/utils");
46
46
  var initFiles_1 = require("./initFiles");
47
- var create_1 = require("../../create/create");
48
47
  var init_1 = require("../init");
49
48
  var utils_2 = require("../../../utils");
49
+ var config_1 = require("../../../utils/config");
50
+ var compile_1 = require("../../compile/compile");
51
+ var create_1 = require("../../create/create");
50
52
  describe('sasjs init', function () {
51
53
  beforeAll(function () {
52
54
  dotenv_1.default.config();
@@ -56,103 +58,290 @@ describe('sasjs init', function () {
56
58
  switch (_a.label) {
57
59
  case 0:
58
60
  if (!process.projectDir) return [3 /*break*/, 2];
59
- return [4 /*yield*/, (0, utils_1.deleteFolder)(process.projectDir)];
61
+ return [4 /*yield*/, (0, utils_1.deleteFolder)(process.projectDir)
62
+ // The compile spec sets these; leaving them set would hand later specs a
63
+ // configuration and build folders belonging to a deleted project.
64
+ ];
60
65
  case 1:
61
66
  _a.sent();
62
67
  _a.label = 2;
63
- case 2: return [2 /*return*/];
68
+ case 2:
69
+ // The compile spec sets these; leaving them set would hand later specs a
70
+ // configuration and build folders belonging to a deleted project.
71
+ process.currentDir = '';
72
+ process.sasjsConfig = undefined;
73
+ return [2 /*return*/];
64
74
  }
65
75
  });
66
76
  }); });
67
- it('should initialise with default app in the current folder', function () { return __awaiter(void 0, void 0, void 0, function () {
68
- var appName;
77
+ it('should initialise in an empty current folder', function () { return __awaiter(void 0, void 0, void 0, function () {
69
78
  return __generator(this, function (_a) {
70
79
  switch (_a.label) {
71
- case 0:
72
- appName = "test-app-init-".concat((0, test_1.generateUniqueTimestamp)());
73
- process.projectDir = path_1.default.join(__dirname, appName);
74
- return [4 /*yield*/, (0, utils_2.setConstants)()];
80
+ case 0: return [4 /*yield*/, setupProjectFolder('test-app-init-')];
75
81
  case 1:
76
82
  _a.sent();
77
- return [4 /*yield*/, (0, utils_1.createFolder)(process.projectDir)];
83
+ return [4 /*yield*/, expect((0, init_1.init)()).toResolve()];
78
84
  case 2:
79
85
  _a.sent();
80
- return [4 /*yield*/, expect((0, create_1.create)('.', '')).toResolve()];
86
+ return [4 /*yield*/, verifyInit()];
81
87
  case 3:
82
88
  _a.sent();
89
+ return [2 /*return*/];
90
+ }
91
+ });
92
+ }); });
93
+ it('should add SASjs to an existing npm project', function () { return __awaiter(void 0, void 0, void 0, function () {
94
+ var packageJsonPath, existingPackageJson, packageJson, _a, _b;
95
+ return __generator(this, function (_c) {
96
+ switch (_c.label) {
97
+ case 0: return [4 /*yield*/, setupProjectFolder('test-app-init-npm-')];
98
+ case 1:
99
+ _c.sent();
100
+ packageJsonPath = path_1.default.join(process.projectDir, 'package.json');
101
+ existingPackageJson = {
102
+ name: 'my-existing-project',
103
+ version: '1.0.0',
104
+ scripts: { build: 'exit 0' }
105
+ };
106
+ return [4 /*yield*/, (0, utils_1.createFile)(packageJsonPath, JSON.stringify(existingPackageJson, null, 2))];
107
+ case 2:
108
+ _c.sent();
83
109
  return [4 /*yield*/, expect((0, init_1.init)()).toResolve()];
84
- case 4:
85
- _a.sent();
110
+ case 3:
111
+ _c.sent();
86
112
  return [4 /*yield*/, verifyInit()];
113
+ case 4:
114
+ _c.sent();
115
+ _b = (_a = JSON).parse;
116
+ return [4 /*yield*/, (0, utils_1.readFile)(packageJsonPath)];
87
117
  case 5:
88
- _a.sent();
118
+ packageJson = _b.apply(_a, [_c.sent()]);
119
+ expect(packageJson.name).toEqual('my-existing-project');
120
+ expect(packageJson.scripts.build).toEqual('exit 0');
89
121
  return [2 /*return*/];
90
122
  }
91
123
  });
92
124
  }); });
93
- it("should initialise with 'sasonly' app in the current folder", function () { return __awaiter(void 0, void 0, void 0, function () {
94
- var appName;
95
- return __generator(this, function (_a) {
96
- switch (_a.label) {
97
- case 0:
98
- appName = "test-app-init-sasonly-".concat((0, test_1.generateUniqueTimestamp)());
99
- process.projectDir = path_1.default.join(__dirname, appName);
100
- return [4 /*yield*/, (0, utils_2.setConstants)()];
125
+ it('should produce a project that compiles straight away', function () { return __awaiter(void 0, void 0, void 0, function () {
126
+ var _a, target;
127
+ return __generator(this, function (_b) {
128
+ switch (_b.label) {
129
+ case 0: return [4 /*yield*/, setupProjectFolder('test-app-init-compile-')];
101
130
  case 1:
102
- _a.sent();
103
- return [4 /*yield*/, (0, utils_1.createFolder)(process.projectDir)];
131
+ _b.sent();
132
+ return [4 /*yield*/, expect((0, init_1.init)()).toResolve()
133
+ // Reading the target validates its serverUrl, and compile() reads every
134
+ // folder the config names - the two things that stopped a freshly
135
+ // initialised project from compiling.
136
+ // compile() reads process.sasjsConfig and the build folders, which the CLI
137
+ // sets up from its target command - do the same from a test.
138
+ ];
104
139
  case 2:
105
- _a.sent();
140
+ _b.sent();
141
+ // Reading the target validates its serverUrl, and compile() reads every
142
+ // folder the config names - the two things that stopped a freshly
143
+ // initialised project from compiling.
144
+ // compile() reads process.sasjsConfig and the build folders, which the CLI
145
+ // sets up from its target command - do the same from a test.
146
+ process.currentDir = process.projectDir;
147
+ _a = process;
148
+ return [4 /*yield*/, (0, config_1.getLocalConfig)()];
149
+ case 3:
150
+ _a.sasjsConfig = _b.sent();
151
+ return [4 /*yield*/, (0, utils_2.setConstants)()];
152
+ case 4:
153
+ _b.sent();
154
+ return [4 /*yield*/, (0, config_1.findTargetInConfiguration)('mytarget')];
155
+ case 5:
156
+ target = (_b.sent()).target;
157
+ return [4 /*yield*/, expect((0, compile_1.compile)(target)).toResolve()];
158
+ case 6:
159
+ _b.sent();
160
+ return [4 /*yield*/, expect((0, utils_1.fileExists)(path_1.default.join(process.projectDir, 'sasjsbuild'))).resolves.toEqual(true)];
161
+ case 7:
162
+ _b.sent();
163
+ return [2 /*return*/];
164
+ }
165
+ });
166
+ }); });
167
+ it('should abort over a template scaffold that ships its own config', function () { return __awaiter(void 0, void 0, void 0, function () {
168
+ var configPath, templateConfig, _a, _b, error, _c, _d, _e;
169
+ return __generator(this, function (_f) {
170
+ switch (_f.label) {
171
+ case 0: return [4 /*yield*/, setupProjectFolder('test-app-init-sasonly-')
172
+ // The templates carry their own sasjs/sasjsconfig.json, so they reach the
173
+ // guard by a different route than createFileStructure does.
174
+ ];
175
+ case 1:
176
+ _f.sent();
177
+ // The templates carry their own sasjs/sasjsconfig.json, so they reach the
178
+ // guard by a different route than createFileStructure does.
106
179
  return [4 /*yield*/, expect((0, create_1.create)('.', 'sasonly')).toResolve()];
180
+ case 2:
181
+ // The templates carry their own sasjs/sasjsconfig.json, so they reach the
182
+ // guard by a different route than createFileStructure does.
183
+ _f.sent();
184
+ configPath = path_1.default.join(process.projectDir, 'sasjs', 'sasjsconfig.json');
185
+ _b = (_a = JSON).parse;
186
+ return [4 /*yield*/, (0, utils_1.readFile)(configPath)];
107
187
  case 3:
108
- _a.sent();
109
- return [4 /*yield*/, expect((0, init_1.init)()).toResolve()];
188
+ templateConfig = _b.apply(_a, [_f.sent()]);
189
+ return [4 /*yield*/, (0, init_1.init)().catch(function (err) { return err; })];
110
190
  case 4:
111
- _a.sent();
112
- return [4 /*yield*/, verifyInit()];
191
+ error = _f.sent();
192
+ expect(error).toBeInstanceOf(init_1.AlreadyInitialisedError);
193
+ _c = expect;
194
+ _e = (_d = JSON).parse;
195
+ return [4 /*yield*/, (0, utils_1.readFile)(configPath)];
113
196
  case 5:
114
- _a.sent();
197
+ _c.apply(void 0, [_e.apply(_d, [_f.sent()])]).toEqual(templateConfig);
115
198
  return [2 /*return*/];
116
199
  }
117
200
  });
118
201
  }); });
119
- it('should initialise in an empty current folder', function () { return __awaiter(void 0, void 0, void 0, function () {
120
- var appName;
121
- return __generator(this, function (_a) {
122
- switch (_a.label) {
123
- case 0:
124
- appName = "test-app-init-.-".concat((0, test_1.generateUniqueTimestamp)());
125
- process.projectDir = path_1.default.join(__dirname, appName);
126
- return [4 /*yield*/, (0, utils_2.setConstants)()];
202
+ it('should abort over a project sasjs create has just scaffolded', function () { return __awaiter(void 0, void 0, void 0, function () {
203
+ var configPath, createdConfig, _a, _b, error, _c, _d, _e;
204
+ return __generator(this, function (_f) {
205
+ switch (_f.label) {
206
+ case 0: return [4 /*yield*/, setupProjectFolder('test-app-init-created-')];
127
207
  case 1:
128
- _a.sent();
129
- return [4 /*yield*/, (0, utils_1.createFolder)(process.projectDir)];
208
+ _f.sent();
209
+ return [4 /*yield*/, expect((0, create_1.create)('.', '')).toResolve()
210
+ // create() writes a fuller config than init does, so running init over it
211
+ // must not replace that configuration.
212
+ ];
130
213
  case 2:
131
- _a.sent();
132
- return [4 /*yield*/, expect((0, init_1.init)()).toResolve()];
214
+ _f.sent();
215
+ configPath = path_1.default.join(process.projectDir, 'sasjs', 'sasjsconfig.json');
216
+ _b = (_a = JSON).parse;
217
+ return [4 /*yield*/, (0, utils_1.readFile)(configPath)];
133
218
  case 3:
134
- _a.sent();
135
- return [4 /*yield*/, verifyInit()];
219
+ createdConfig = _b.apply(_a, [_f.sent()]);
220
+ expect(createdConfig.defaultTarget).not.toEqual('mytarget');
221
+ return [4 /*yield*/, (0, init_1.init)().catch(function (err) { return err; })];
136
222
  case 4:
137
- _a.sent();
223
+ error = _f.sent();
224
+ expect(error).toBeInstanceOf(init_1.AlreadyInitialisedError);
225
+ _c = expect;
226
+ _e = (_d = JSON).parse;
227
+ return [4 /*yield*/, (0, utils_1.readFile)(configPath)];
228
+ case 5:
229
+ _c.apply(void 0, [_e.apply(_d, [_f.sent()])]).toEqual(createdConfig);
230
+ return [2 /*return*/];
231
+ }
232
+ });
233
+ }); });
234
+ it('should abort without changes when the folder is already a SASjs project', function () { return __awaiter(void 0, void 0, void 0, function () {
235
+ var sasjsFolderPath, configPath, existingConfig, error, _a, _b, _c;
236
+ return __generator(this, function (_d) {
237
+ switch (_d.label) {
238
+ case 0: return [4 /*yield*/, setupProjectFolder('test-app-init-existing-')];
239
+ case 1:
240
+ _d.sent();
241
+ sasjsFolderPath = path_1.default.join(process.projectDir, 'sasjs');
242
+ configPath = path_1.default.join(sasjsFolderPath, 'sasjsconfig.json');
243
+ existingConfig = {
244
+ defaultTarget: 'myTarget',
245
+ targets: [
246
+ {
247
+ name: 'myTarget',
248
+ serverType: 'SAS9',
249
+ serverUrl: 'https://my.server.com',
250
+ appLoc: '/User Folders/my app'
251
+ }
252
+ ]
253
+ };
254
+ return [4 /*yield*/, (0, utils_1.createFolder)(sasjsFolderPath)];
255
+ case 2:
256
+ _d.sent();
257
+ return [4 /*yield*/, (0, utils_1.createFile)(configPath, JSON.stringify(existingConfig, null, 2))];
258
+ case 3:
259
+ _d.sent();
260
+ return [4 /*yield*/, (0, init_1.init)().catch(function (err) { return err; })];
261
+ case 4:
262
+ error = _d.sent();
263
+ expect(error).toBeInstanceOf(init_1.AlreadyInitialisedError);
264
+ expect(error.message).toMatch(/already a SASjs project \(sasjs\/sasjsconfig\.json exists\)/);
265
+ // Nothing is written before the abort, so the existing project is untouched.
266
+ _a = expect;
267
+ _c = (_b = JSON).parse;
268
+ return [4 /*yield*/, (0, utils_1.readFile)(configPath)];
269
+ case 5:
270
+ // Nothing is written before the abort, so the existing project is untouched.
271
+ _a.apply(void 0, [_c.apply(_b, [_d.sent()])]).toEqual(existingConfig);
272
+ return [4 /*yield*/, expect((0, utils_1.fileExists)(path_1.default.join(process.projectDir, 'package.json'))).resolves.toEqual(false)];
273
+ case 6:
274
+ _d.sent();
275
+ return [4 /*yield*/, expect((0, utils_1.fileExists)(path_1.default.join(process.projectDir, '.gitignore'))).resolves.toEqual(false)];
276
+ case 7:
277
+ _d.sent();
278
+ return [4 /*yield*/, expect((0, utils_1.fileExists)(path_1.default.join(sasjsFolderPath, 'doxy'))).resolves.toEqual(false)];
279
+ case 8:
280
+ _d.sent();
138
281
  return [2 /*return*/];
139
282
  }
140
283
  });
141
284
  }); });
142
285
  });
143
- var verifyInit = function () { return __awaiter(void 0, void 0, void 0, function () {
144
- var gitIgnoreFilePath, gitIgnoreContent;
286
+ var setupProjectFolder = function (prefix) { return __awaiter(void 0, void 0, void 0, function () {
145
287
  return __generator(this, function (_a) {
146
288
  switch (_a.label) {
147
- case 0: return [4 /*yield*/, (0, test_1.verifyFolder)(initFiles_1.initFiles)];
289
+ case 0:
290
+ process.projectDir = path_1.default.join(__dirname, "".concat(prefix).concat((0, test_1.generateUniqueTimestamp)()));
291
+ return [4 /*yield*/, (0, utils_2.setConstants)()];
148
292
  case 1:
149
293
  _a.sent();
294
+ return [4 /*yield*/, (0, utils_1.createFolder)(process.projectDir)];
295
+ case 2:
296
+ _a.sent();
297
+ return [2 /*return*/];
298
+ }
299
+ });
300
+ }); };
301
+ var verifyInit = function () { return __awaiter(void 0, void 0, void 0, function () {
302
+ var gitIgnoreFilePath, gitIgnoreContent, configPath, config, _a, _b, macroFolderPath, lintConfigPath, packageJson, _c, _d;
303
+ return __generator(this, function (_e) {
304
+ switch (_e.label) {
305
+ case 0: return [4 /*yield*/, (0, test_1.verifyFolder)(initFiles_1.initFiles)];
306
+ case 1:
307
+ _e.sent();
150
308
  gitIgnoreFilePath = path_1.default.join(process.projectDir, '.gitignore');
151
309
  return [4 /*yield*/, (0, utils_1.readFile)(gitIgnoreFilePath)];
152
310
  case 2:
153
- gitIgnoreContent = _a.sent();
311
+ gitIgnoreContent = _e.sent();
154
312
  expect(gitIgnoreContent.match(/^sasjsbuild/gm).length).toEqual(1);
155
313
  expect(gitIgnoreContent.match(/^node_modules/gm).length).toEqual(1);
314
+ configPath = path_1.default.join(process.projectDir, 'sasjs', 'sasjsconfig.json');
315
+ _b = (_a = JSON).parse;
316
+ return [4 /*yield*/, (0, utils_1.readFile)(configPath)];
317
+ case 3:
318
+ config = _b.apply(_a, [_e.sent()]);
319
+ expect(config.$schema).toEqual('https://cli.sasjs.io/sasjsconfig-schema.json');
320
+ expect(config.macroFolders).toEqual(['sasjs/macros']);
321
+ expect(config.defaultTarget).toEqual('mytarget');
322
+ expect(config.targets.length).toEqual(1);
323
+ expect(config.targets[0]).toEqual({
324
+ name: 'mytarget',
325
+ serverType: 'SASJS',
326
+ serverUrl: '',
327
+ appLoc: '/Public/apps/myapp'
328
+ });
329
+ macroFolderPath = path_1.default.join(process.projectDir, 'sasjs', 'macros');
330
+ return [4 /*yield*/, expect((0, utils_1.fileExists)(macroFolderPath)).resolves.toEqual(true)];
331
+ case 4:
332
+ _e.sent();
333
+ return [4 /*yield*/, expect((0, utils_1.fileExists)(path_1.default.join(macroFolderPath, '.gitkeep'))).resolves.toEqual(true)];
334
+ case 5:
335
+ _e.sent();
336
+ lintConfigPath = path_1.default.join(process.projectDir, '.sasjslint');
337
+ return [4 /*yield*/, expect((0, utils_1.fileExists)(lintConfigPath)).resolves.toEqual(true)];
338
+ case 6:
339
+ _e.sent();
340
+ _d = (_c = JSON).parse;
341
+ return [4 /*yield*/, (0, utils_1.readFile)(path_1.default.join(process.projectDir, 'package.json'))];
342
+ case 7:
343
+ packageJson = _d.apply(_c, [_e.sent()]);
344
+ expect(packageJson.dependencies['@sasjs/core']).toBeDefined();
156
345
  return [2 /*return*/];
157
346
  }
158
347
  });
@@ -146,6 +146,28 @@ describe('InitCommand', function () {
146
146
  }
147
147
  });
148
148
  }); });
149
+ it('should report an already-initialised folder on one line', function () { return __awaiter(void 0, void 0, void 0, function () {
150
+ var message, args, command, returnCode;
151
+ return __generator(this, function (_a) {
152
+ switch (_a.label) {
153
+ case 0:
154
+ message = 'This folder is already a SASjs project (sasjs/sasjsconfig.json exists). Aborting without making changes.';
155
+ jest
156
+ .spyOn(initModule, 'init')
157
+ .mockImplementation(function () {
158
+ return Promise.reject(new initModule.AlreadyInitialisedError(message));
159
+ });
160
+ args = __spreadArray(__spreadArray([], __read(defaultArgs), false), ['init'], false);
161
+ command = new initCommand_1.InitCommand(args);
162
+ return [4 /*yield*/, command.execute()];
163
+ case 1:
164
+ returnCode = _a.sent();
165
+ expect(returnCode).toEqual(command_1.ReturnCode.AlreadyInitialised);
166
+ expect(process.logger.error).toHaveBeenCalledWith(message);
167
+ return [2 /*return*/];
168
+ }
169
+ });
170
+ }); });
149
171
  });
150
172
  var setupMocks = function () {
151
173
  jest.resetAllMocks();
@@ -13,6 +13,11 @@ exports.initFiles = {
13
13
  folderName: 'sasjs',
14
14
  files: [{ fileName: 'sasjsconfig.json' }],
15
15
  subFolders: [
16
+ {
17
+ folderName: 'macros',
18
+ files: [{ fileName: '.gitkeep' }],
19
+ subFolders: []
20
+ },
16
21
  {
17
22
  folderName: 'doxy',
18
23
  files: [
@@ -2,5 +2,6 @@ export declare enum ReturnCode {
2
2
  Success = 0,
3
3
  InvalidCommand = 1,
4
4
  InternalError = 2,
5
- LintError = 3
5
+ LintError = 3,
6
+ AlreadyInitialised = 4
6
7
  }
@@ -7,4 +7,8 @@ var ReturnCode;
7
7
  ReturnCode[ReturnCode["InvalidCommand"] = 1] = "InvalidCommand";
8
8
  ReturnCode[ReturnCode["InternalError"] = 2] = "InternalError";
9
9
  ReturnCode[ReturnCode["LintError"] = 3] = "LintError";
10
+ // `init` refusing a folder that is already a SASjs project. Distinct from
11
+ // InvalidCommand, which also covers unparseable arguments, so a script can
12
+ // tell a refusal from a bad invocation.
13
+ ReturnCode[ReturnCode["AlreadyInitialised"] = 4] = "AlreadyInitialised";
10
14
  })(ReturnCode || (exports.ReturnCode = ReturnCode = {}));
package/package.json CHANGED
@@ -123,5 +123,5 @@
123
123
  "ws": "8.21.0",
124
124
  "nanoid": "3.3.18"
125
125
  },
126
- "version": "6.4.2"
126
+ "version": "6.4.3"
127
127
  }