@sasjs/cli 6.28.0 → 6.30.0

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.
@@ -68,10 +68,38 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
68
68
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
69
69
  }
70
70
  };
71
+ var __read = (this && this.__read) || function (o, n) {
72
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
73
+ if (!m) return o;
74
+ var i = m.call(o), r, ar = [], e;
75
+ try {
76
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
77
+ }
78
+ catch (error) { e = { error: error }; }
79
+ finally {
80
+ try {
81
+ if (r && !r.done && (m = i["return"])) m.call(i);
82
+ }
83
+ finally { if (e) throw e.error; }
84
+ }
85
+ return ar;
86
+ };
87
+ var __values = (this && this.__values) || function(o) {
88
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
89
+ if (m) return m.call(o);
90
+ if (o && typeof o.length === "number") return {
91
+ next: function () {
92
+ if (o && i >= o.length) o = void 0;
93
+ return { value: o && o[i++], done: !o };
94
+ }
95
+ };
96
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
97
+ };
71
98
  var __importDefault = (this && this.__importDefault) || function (mod) {
72
99
  return (mod && mod.__esModule) ? mod : { "default": mod };
73
100
  };
74
101
  Object.defineProperty(exports, "__esModule", { value: true });
102
+ var fs_1 = __importDefault(require("fs"));
75
103
  var path_1 = __importDefault(require("path"));
76
104
  var node_1 = require("@sasjs/adapter/node");
77
105
  var js_base64_1 = require("js-base64");
@@ -85,6 +113,13 @@ var compile_1 = require("../../compile/compile");
85
113
  var deploy_1 = require("../deploy");
86
114
  var utils_2 = require("./utils");
87
115
  var config_1 = require("../../../utils/config");
116
+ /** Every file under a folder, recursively, as absolute paths. */
117
+ var listFiles = function (folderPath) {
118
+ return fs_1.default.readdirSync(folderPath, { withFileTypes: true }).flatMap(function (entry) {
119
+ var entryPath = path_1.default.join(folderPath, entry.name);
120
+ return entry.isDirectory() ? listFiles(entryPath) : [entryPath];
121
+ });
122
+ };
88
123
  describe('sasjs cbd with server type SASJS', function () {
89
124
  var target;
90
125
  var appName;
@@ -115,9 +150,10 @@ describe('sasjs cbd with server type SASJS', function () {
115
150
  });
116
151
  }); });
117
152
  it("should deploy compile and build to sasjs/server", function () { return __awaiter(void 0, void 0, void 0, function () {
118
- var deployMock, _a, customTarget, macroCorePath, mf_getuser, _b, mp_jsonout, ms_webout, webout, mocksPath, appinitJSPath, getdataJSPath, appinitJSContents, getdataJSContents, depsInserts, appinit, getdata, getCode;
119
- return __generator(this, function (_c) {
120
- switch (_c.label) {
153
+ var deployMock, _a, customTarget, macroCorePath, mf_getuser, _b, mp_jsonout, ms_webout, webout, depsInserts, getCode, _c, payload, deployed, indexMembers, projectRoot, mocksRoot, mockFiles, mockFiles_1, mockFiles_1_1, filePath, relative, _d, _e, _f, e_1_1, serviceFiles, serviceFiles_1, serviceFiles_1_1, filePath, name_1, contents, member, e_2_1;
154
+ var e_1, _g, _h, e_2, _j;
155
+ return __generator(this, function (_k) {
156
+ switch (_k.label) {
121
157
  case 0:
122
158
  jest.spyOn(fileModule, 'fileExists').mockImplementation(function (filePath) {
123
159
  if (filePath.endsWith('.zip'))
@@ -141,7 +177,7 @@ describe('sasjs cbd with server type SASJS', function () {
141
177
  }
142
178
  })];
143
179
  case 1:
144
- _c.sent();
180
+ _k.sent();
145
181
  return [4 /*yield*/, (0, utils_2.updateLocalTarget)(target.name, {
146
182
  jobConfig: undefined,
147
183
  serviceConfig: undefined,
@@ -151,91 +187,139 @@ describe('sasjs cbd with server type SASJS', function () {
151
187
  }
152
188
  })];
153
189
  case 2:
154
- _c.sent();
190
+ _k.sent();
155
191
  _a = process;
156
192
  return [4 /*yield*/, (0, config_1.getLocalConfig)()];
157
193
  case 3:
158
- _a.sasjsConfig = _c.sent();
194
+ _a.sasjsConfig = _k.sent();
159
195
  return [4 /*yield*/, (0, config_1.findTargetInConfiguration)(target.name, types_1.TargetScope.Local)];
160
196
  case 4:
161
- customTarget = (_c.sent()).target;
197
+ customTarget = (_k.sent()).target;
162
198
  return [4 /*yield*/, (0, compile_1.compile)(customTarget)];
163
199
  case 5:
164
- _c.sent();
200
+ _k.sent();
165
201
  return [4 /*yield*/, expect((0, build_1.build)(customTarget)).toResolve()];
166
202
  case 6:
167
- _c.sent();
203
+ _k.sent();
168
204
  return [4 /*yield*/, (0, deploy_1.deploy)(customTarget, true)];
169
205
  case 7:
170
- _c.sent();
206
+ _k.sent();
171
207
  macroCorePath = process.sasjsConstants.macroCorePath;
172
208
  _b = utils_1.removeHeader;
173
209
  return [4 /*yield*/, (0, utils_1.readFile)("".concat(macroCorePath, "/base/mf_getuser.sas"))];
174
210
  case 8:
175
- mf_getuser = _b.apply(void 0, [_c.sent()]);
211
+ mf_getuser = _b.apply(void 0, [_k.sent()]);
176
212
  return [4 /*yield*/, (0, utils_1.readFile)("".concat(macroCorePath, "/base/mp_jsonout.sas"))];
177
213
  case 9:
178
- mp_jsonout = _c.sent();
214
+ mp_jsonout = _k.sent();
179
215
  return [4 /*yield*/, (0, utils_1.readFile)("".concat(macroCorePath, "/server/ms_webout.sas"))];
180
216
  case 10:
181
- ms_webout = _c.sent();
217
+ ms_webout = _k.sent();
182
218
  webout = "%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO,maxobs=MAX);\n %ms_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt\n ,missing=&missing\n ,showmeta=&showmeta\n ,maxobs=&maxobs\n )\n%mend;\n";
183
- mocksPath = path_1.default.join(__dirname, appName, 'sasjs', 'mocks');
184
- appinitJSPath = path_1.default.join(mocksPath, 'services', 'common', 'appinit.js');
185
- getdataJSPath = path_1.default.join(mocksPath, 'services', 'common', 'getdata.js');
186
- return [4 /*yield*/, (0, utils_1.readFile)(appinitJSPath)];
187
- case 11:
188
- appinitJSContents = _c.sent();
189
- return [4 /*yield*/, (0, utils_1.readFile)(getdataJSPath)];
190
- case 12:
191
- getdataJSContents = _c.sent();
192
219
  depsInserts = "\n\n* Service Variables start;\n\n\n* Service Variables end;\n* SAS Macros start;\n\n\n\n* SAS Macros end;\n* SAS Includes start;\n\n* SAS Includes end;\n* Binary Files start;\n\n* Binary Files end;\n\n\n* Service start;\n";
193
- appinit = "/**\n @file appinit.sas\n @brief Initialisation service - runs on app startup\n @details This is always the first service called when the app is opened.\n\n <h4> SAS Macros </h4>\n\n**/\n\nproc sql;\ncreate table areas as select distinct area\n from mydb.springs;\n\n%webout(OPEN)\n%webout(OBJ,areas)\n%webout(CLOSE)\n\n";
194
- getdata = "/**\n @file getdata.sas\n @brief Get Data service - runs on app startup\n @details This is always the first service called when the app is opened.\n\n <h4> SAS Macros </h4>\n\n**/\n\n%webout(FETCH)\n\nproc sql;\ncreate table springs as select * from mydb.springs\n where area in (select area from work.areas);\n\n%webout(OPEN)\n%webout(OBJ,springs)\n%webout(CLOSE)\n\n";
195
220
  getCode = function (file) {
196
221
  return "".concat(mf_getuser).concat(mp_jsonout).concat(ms_webout).concat(webout, "/* provide additional debug info */\n%global _program;\n%put &=syscc;\n%put user=%mf_getuser();\n%put pgm=&_program;\n%put timestamp=%sysfunc(datetime(),datetime19.);").concat(depsInserts).concat(file, "* Service end;");
197
222
  };
198
- expect(deployMock).toHaveBeenCalledWith({
199
- appLoc: target.appLoc,
200
- fileTree: {
201
- members: [
202
- {
203
- name: 'services',
204
- type: 'folder',
205
- members: [
206
- {
207
- name: 'common',
208
- type: 'folder',
209
- members: [
210
- {
211
- name: 'appinit.js',
212
- type: 'file',
213
- encoding: 'base64',
214
- code: (0, js_base64_1.encode)(appinitJSContents)
215
- },
216
- {
217
- name: 'appinit',
218
- type: 'service',
219
- code: getCode((0, utils_1.removeHeader)(appinit))
220
- },
221
- {
222
- name: 'getdata.js',
223
- type: 'file',
224
- encoding: 'base64',
225
- code: (0, js_base64_1.encode)(getdataJSContents)
226
- },
227
- {
228
- name: 'getdata',
229
- type: 'service',
230
- code: getCode((0, utils_1.removeHeader)(getdata))
231
- }
232
- ]
233
- }
234
- ]
235
- }
236
- ]
237
- }
238
- }, target.appLoc, undefined);
223
+ // The payload is derived from the project the command created rather than
224
+ // listed by hand. A hand-kept list turned this spec red when the seed app
225
+ // gained a data file beside its services - a file the CLI deploys correctly
226
+ // - so the reference here is the project itself. What stays explicit is the
227
+ // CLI's own contribution: the wrapper, built by `getCode` above.
228
+ expect(deployMock).toHaveBeenCalledTimes(1);
229
+ _c = __read(deployMock.mock.calls[0], 1), payload = _c[0];
230
+ expect(payload.appLoc).toEqual(target.appLoc);
231
+ deployed = new Map();
232
+ indexMembers = function (members, prefix) {
233
+ if (members === void 0) { members = []; }
234
+ if (prefix === void 0) { prefix = ''; }
235
+ return members.forEach(function (member) {
236
+ var memberPath = prefix ? "".concat(prefix, "/").concat(member.name) : member.name;
237
+ if (member.type === 'folder')
238
+ indexMembers(member.members, memberPath);
239
+ else
240
+ deployed.set(memberPath, member);
241
+ });
242
+ };
243
+ indexMembers(payload.fileTree.members);
244
+ projectRoot = path_1.default.join(__dirname, appName);
245
+ mocksRoot = path_1.default.join(projectRoot, 'sasjs', 'mocks');
246
+ mockFiles = listFiles(mocksRoot);
247
+ _k.label = 11;
248
+ case 11:
249
+ _k.trys.push([11, 16, 17, 18]);
250
+ mockFiles_1 = __values(mockFiles), mockFiles_1_1 = mockFiles_1.next();
251
+ _k.label = 12;
252
+ case 12:
253
+ if (!!mockFiles_1_1.done) return [3 /*break*/, 15];
254
+ filePath = mockFiles_1_1.value;
255
+ relative = path_1.default
256
+ .relative(mocksRoot, filePath)
257
+ .split(path_1.default.sep)
258
+ .join('/');
259
+ _e = (_d = expect(deployed.get(relative))).toEqual;
260
+ _h = {
261
+ name: path_1.default.basename(filePath),
262
+ type: 'file',
263
+ encoding: 'base64'
264
+ };
265
+ _f = js_base64_1.encode;
266
+ return [4 /*yield*/, (0, utils_1.readFile)(filePath)];
267
+ case 13:
268
+ _e.apply(_d, [(_h.code = _f.apply(void 0, [_k.sent()]),
269
+ _h)]);
270
+ _k.label = 14;
271
+ case 14:
272
+ mockFiles_1_1 = mockFiles_1.next();
273
+ return [3 /*break*/, 12];
274
+ case 15: return [3 /*break*/, 18];
275
+ case 16:
276
+ e_1_1 = _k.sent();
277
+ e_1 = { error: e_1_1 };
278
+ return [3 /*break*/, 18];
279
+ case 17:
280
+ try {
281
+ if (mockFiles_1_1 && !mockFiles_1_1.done && (_g = mockFiles_1.return)) _g.call(mockFiles_1);
282
+ }
283
+ finally { if (e_1) throw e_1.error; }
284
+ return [7 /*endfinally*/];
285
+ case 18:
286
+ serviceFiles = listFiles(path_1.default.join(projectRoot, 'sasjs', 'services', 'common')).filter(function (filePath) { return filePath.endsWith('.sas'); });
287
+ _k.label = 19;
288
+ case 19:
289
+ _k.trys.push([19, 24, 25, 26]);
290
+ serviceFiles_1 = __values(serviceFiles), serviceFiles_1_1 = serviceFiles_1.next();
291
+ _k.label = 20;
292
+ case 20:
293
+ if (!!serviceFiles_1_1.done) return [3 /*break*/, 23];
294
+ filePath = serviceFiles_1_1.value;
295
+ name_1 = path_1.default.basename(filePath, '.sas');
296
+ return [4 /*yield*/, (0, utils_1.readFile)(filePath)];
297
+ case 21:
298
+ contents = _k.sent();
299
+ member = deployed.get("services/common/".concat(name_1));
300
+ expect(member).toMatchObject({ name: name_1, type: 'service' });
301
+ // the compiled service ends its text with a blank line before the end
302
+ // marker, whatever trailing whitespace the source file carries
303
+ expect(member.code).toEqual(getCode((0, utils_1.removeHeader)(contents).trimEnd() + '\n\n'));
304
+ _k.label = 22;
305
+ case 22:
306
+ serviceFiles_1_1 = serviceFiles_1.next();
307
+ return [3 /*break*/, 20];
308
+ case 23: return [3 /*break*/, 26];
309
+ case 24:
310
+ e_2_1 = _k.sent();
311
+ e_2 = { error: e_2_1 };
312
+ return [3 /*break*/, 26];
313
+ case 25:
314
+ try {
315
+ if (serviceFiles_1_1 && !serviceFiles_1_1.done && (_j = serviceFiles_1.return)) _j.call(serviceFiles_1);
316
+ }
317
+ finally { if (e_2) throw e_2.error; }
318
+ return [7 /*endfinally*/];
319
+ case 26:
320
+ // and nothing else: a member the CLI gains or loses shows as a difference
321
+ // in the count, rather than silently passing inside a hand-written tree
322
+ expect(deployed.size).toEqual(mockFiles.length + serviceFiles.length);
239
323
  return [2 /*return*/];
240
324
  }
241
325
  });
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
14
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
15
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -43,18 +54,29 @@ exports.createLintConfigFile = void 0;
43
54
  var path_1 = __importDefault(require("path"));
44
55
  var lint_1 = require("@sasjs/lint");
45
56
  var utils_1 = require("@sasjs/utils");
57
+ /**
58
+ * Where the settings of a `.sasjslint` are documented.
59
+ *
60
+ * The file is read as strict JSON, so it cannot carry comments - the same
61
+ * problem `sasjsconfig.json` answers with `$schema`, and the same address
62
+ * pattern (`sasjs init` writes https://cli.sasjs.io/sasjsconfig-schema.json).
63
+ * An editor that understands JSON schemas then completes the settings and
64
+ * shows each one's description as it is typed.
65
+ */
66
+ var lintSchemaUrl = 'https://cli.sasjs.io/sasjslint-schema.json';
46
67
  /**
47
68
  * Creates a SASjs Lint configuration file.
48
69
  * Its name will be of the form '.sasjslint'
49
70
  * @param {string} parentFolderName- the name of the project folder.
50
71
  */
51
72
  var createLintConfigFile = function (parentFolderName) { return __awaiter(void 0, void 0, void 0, function () {
52
- var configDestinationPath;
73
+ var configDestinationPath, config;
53
74
  return __generator(this, function (_a) {
54
75
  switch (_a.label) {
55
76
  case 0:
56
77
  configDestinationPath = path_1.default.join(process.projectDir, parentFolderName, '.sasjslint');
57
- return [4 /*yield*/, (0, utils_1.createFile)(configDestinationPath, JSON.stringify(lint_1.DefaultLintConfiguration, null, 2))];
78
+ config = __assign({ $schema: lintSchemaUrl }, lint_1.DefaultLintConfiguration);
79
+ return [4 /*yield*/, (0, utils_1.createFile)(configDestinationPath, JSON.stringify(config, null, 2))];
58
80
  case 1:
59
81
  _a.sent();
60
82
  return [2 /*return*/];
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __generator = (this && this.__generator) || function (thisArg, body) {
45
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
46
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
47
+ function verb(n) { return function (v) { return step([n, v]); }; }
48
+ function step(op) {
49
+ if (f) throw new TypeError("Generator is already executing.");
50
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
51
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
52
+ if (y = 0, t) op = [op[0] & 2, t.value];
53
+ switch (op[0]) {
54
+ case 0: case 1: t = op; break;
55
+ case 4: _.label++; return { value: op[1], done: false };
56
+ case 5: _.label++; y = op[1]; op = [0]; continue;
57
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
58
+ default:
59
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
60
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
61
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
62
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
63
+ if (t[2]) _.ops.pop();
64
+ _.trys.pop(); continue;
65
+ }
66
+ op = body.call(thisArg, _);
67
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
68
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
69
+ }
70
+ };
71
+ var __rest = (this && this.__rest) || function (s, e) {
72
+ var t = {};
73
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
74
+ t[p] = s[p];
75
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
76
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
77
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
78
+ t[p[i]] = s[p[i]];
79
+ }
80
+ return t;
81
+ };
82
+ Object.defineProperty(exports, "__esModule", { value: true });
83
+ var promises_1 = require("fs/promises");
84
+ var os = __importStar(require("os"));
85
+ var path = __importStar(require("path"));
86
+ var lint_1 = require("@sasjs/lint");
87
+ var utils_1 = require("@sasjs/utils");
88
+ var createLintConfigFile_1 = require("../createLintConfigFile");
89
+ /**
90
+ * The file this writes is the first thing a new project has to edit, and the
91
+ * only place its settings are described: it is read as strict JSON, so it cannot
92
+ * carry comments, and `$schema` is what gives an editor the description of each
93
+ * setting where it is written.
94
+ */
95
+ describe('createLintConfigFile', function () {
96
+ var originalProjectDir = process.projectDir;
97
+ var projectDir;
98
+ beforeEach(function () { return __awaiter(void 0, void 0, void 0, function () {
99
+ return __generator(this, function (_a) {
100
+ switch (_a.label) {
101
+ case 0:
102
+ projectDir = path.join(os.tmpdir(), "sasjs-lint-config-".concat(Date.now(), "-").concat(Math.random().toString(36).slice(2)));
103
+ return [4 /*yield*/, (0, utils_1.createFolder)(projectDir)];
104
+ case 1:
105
+ _a.sent();
106
+ process.projectDir = projectDir;
107
+ return [2 /*return*/];
108
+ }
109
+ });
110
+ }); });
111
+ afterEach(function () {
112
+ process.projectDir = originalProjectDir;
113
+ });
114
+ it('writes every default setting, with the schema', function () { return __awaiter(void 0, void 0, void 0, function () {
115
+ var written, _a, _b, _schema, settings;
116
+ return __generator(this, function (_c) {
117
+ switch (_c.label) {
118
+ case 0: return [4 /*yield*/, (0, createLintConfigFile_1.createLintConfigFile)('.')];
119
+ case 1:
120
+ _c.sent();
121
+ _b = (_a = JSON).parse;
122
+ return [4 /*yield*/, (0, promises_1.readFile)(path.join(projectDir, '.sasjslint'), 'utf-8')];
123
+ case 2:
124
+ written = _b.apply(_a, [_c.sent()]);
125
+ expect(written.$schema).toEqual('https://cli.sasjs.io/sasjslint-schema.json');
126
+ _schema = written.$schema, settings = __rest(written, ["$schema"]);
127
+ expect(settings).toEqual(lint_1.DefaultLintConfiguration);
128
+ return [2 /*return*/];
129
+ }
130
+ });
131
+ }); });
132
+ });
@@ -50,6 +50,18 @@ export declare const generateTestTarget: (targetName: string, appLoc: string, se
50
50
  export declare const createTestGlobalTarget: (targetName: string, appLoc: string, serviceConfig?: ServiceConfig, serverType?: ServerType) => Promise<Target>;
51
51
  export declare const verifyStep: (step?: "db" | "compile" | "build", buildFileName?: string, customFiles?: "custom" | "no") => Promise<void>;
52
52
  export declare const mockProcessExit: () => jest.SpyInstance<never, [code?: string | number | null | undefined], any>;
53
+ /**
54
+ * The file tree of a seed app, read from the archive `sasjs create` downloads.
55
+ *
56
+ * A created project used to be compared against a hand-kept list of the files
57
+ * each template ships, so every seed release that added or removed one turned
58
+ * this suite red with no change to the CLI - twice in one afternoon, for
59
+ * `springs.json` and for the angular template's component specs. The template
60
+ * is what the command copies, so it is what the test compares against: the same
61
+ * repository, fetched the same way, with the copy's own normalisations applied
62
+ * where the command makes them.
63
+ */
64
+ export declare const getTemplateFileTree: (repoUrl: string) => Promise<Folder>;
53
65
  export declare const verifyFolder: (folder: Folder, parentFolderName?: string) => Promise<boolean>;
54
66
  export declare const verifyPackageJsonContent: (parentFolderName?: string) => Promise<void>;
55
67
  export declare const removeAllTargetsFromConfigs: () => Promise<void>;
@@ -108,8 +108,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
108
108
  return (mod && mod.__esModule) ? mod : { "default": mod };
109
109
  };
110
110
  Object.defineProperty(exports, "__esModule", { value: true });
111
- exports.verifyGitNotPresent = exports.verifyCompiledJob = exports.verifyCompiledService = exports.verifyDotFilesNotGenerated = exports.verifyDotFiles = exports.verifyDocs = exports.updateConfig = exports.updateTarget = exports.removeAllTargetsFromConfigs = exports.verifyPackageJsonContent = exports.verifyFolder = exports.mockProcessExit = exports.verifyStep = exports.createTestGlobalTarget = exports.generateTestTarget = exports.removeTestServerFolder = exports.removeTestApp = exports.createTestMinimalApp = exports.createTestJobsApp = exports.createTestApp = exports.stubNpm = exports.npmCommands = exports.generateUniqueTimestamp = void 0;
111
+ exports.verifyGitNotPresent = exports.verifyCompiledJob = exports.verifyCompiledService = exports.verifyDotFilesNotGenerated = exports.verifyDotFiles = exports.verifyDocs = exports.updateConfig = exports.updateTarget = exports.removeAllTargetsFromConfigs = exports.verifyPackageJsonContent = exports.verifyFolder = exports.getTemplateFileTree = exports.mockProcessExit = exports.verifyStep = exports.createTestGlobalTarget = exports.generateTestTarget = exports.removeTestServerFolder = exports.removeTestApp = exports.createTestMinimalApp = exports.createTestJobsApp = exports.createTestApp = exports.stubNpm = exports.npmCommands = exports.generateUniqueTimestamp = void 0;
112
112
  var fs_1 = __importDefault(require("fs"));
113
+ var os_1 = __importDefault(require("os"));
113
114
  var path_1 = __importDefault(require("path"));
114
115
  var dotenv_1 = __importDefault(require("dotenv"));
115
116
  var utils_1 = require("@sasjs/utils");
@@ -122,6 +123,8 @@ var compiledFilesCustom1_1 = require("./fileStructures/compiledFilesCustom1");
122
123
  var builtFiles_1 = require("./fileStructures/builtFiles");
123
124
  var builtFilesCustom1_1 = require("./fileStructures/builtFilesCustom1");
124
125
  var create_1 = require("../commands/create/create");
126
+ var utils_2 = require("./utils");
127
+ var zip_1 = require("./zip");
125
128
  var setConstants_1 = require("./setConstants");
126
129
  var shell = __importStar(require("./shell"));
127
130
  /**
@@ -524,6 +527,68 @@ var verifyBuildJson = function (folders, buildJson, buildSas) {
524
527
  });
525
528
  return true;
526
529
  };
530
+ /**
531
+ * The file tree of a seed app, read from the archive `sasjs create` downloads.
532
+ *
533
+ * A created project used to be compared against a hand-kept list of the files
534
+ * each template ships, so every seed release that added or removed one turned
535
+ * this suite red with no change to the CLI - twice in one afternoon, for
536
+ * `springs.json` and for the angular template's component specs. The template
537
+ * is what the command copies, so it is what the test compares against: the same
538
+ * repository, fetched the same way, with the copy's own normalisations applied
539
+ * where the command makes them.
540
+ */
541
+ var getTemplateFileTree = function (repoUrl) { return __awaiter(void 0, void 0, void 0, function () {
542
+ var workspace, archive, main_1, root;
543
+ return __generator(this, function (_a) {
544
+ switch (_a.label) {
545
+ case 0:
546
+ if (!repoUrl) {
547
+ throw new Error("No seed app URL for that template - add it to seedAppUrls in src/utils/utils.ts");
548
+ }
549
+ workspace = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'sasjs-template-'));
550
+ _a.label = 1;
551
+ case 1:
552
+ _a.trys.push([1, , 3, 4]);
553
+ archive = path_1.default.join(workspace, 'main.zip');
554
+ main_1 = (0, utils_2.downloadFile)("".concat(repoUrl, "/archive/refs/heads/main.zip"), archive);
555
+ // the seed repos are on `main`, but the command falls back to `master` and
556
+ // the reference has to be taken from whatever it would have downloaded
557
+ if (main_1.code || main_1.stderr.includes('404')) {
558
+ (0, utils_2.downloadFile)("".concat(repoUrl, "/archive/refs/heads/master.zip"), archive);
559
+ }
560
+ return [4 /*yield*/, (0, zip_1.extractZip)(archive, workspace, true)];
561
+ case 2:
562
+ _a.sent();
563
+ root = fs_1.default
564
+ .readdirSync(workspace, { withFileTypes: true })
565
+ .find(function (entry) { return entry.isDirectory(); });
566
+ if (!root) {
567
+ throw new Error("Could not find the unzipped folder for ".concat(repoUrl));
568
+ }
569
+ return [2 /*return*/, toFileTree(path_1.default.join(workspace, root.name), root.name)];
570
+ case 3:
571
+ fs_1.default.rmSync(workspace, { recursive: true, force: true });
572
+ return [7 /*endfinally*/];
573
+ case 4: return [2 /*return*/];
574
+ }
575
+ });
576
+ }); };
577
+ exports.getTemplateFileTree = getTemplateFileTree;
578
+ var toFileTree = function (folderPath, folderName) {
579
+ var entries = fs_1.default.readdirSync(folderPath, { withFileTypes: true });
580
+ return {
581
+ folderName: folderName,
582
+ // `create` removes this once the docs have been fetched into the folder, so
583
+ // it is the one file the project is not expected to carry
584
+ files: entries
585
+ .filter(function (entry) { return entry.isFile() && entry.name !== '.gitmodules'; })
586
+ .map(function (entry) { return ({ fileName: entry.name }); }),
587
+ subFolders: entries
588
+ .filter(function (entry) { return entry.isDirectory(); })
589
+ .map(function (entry) { return toFileTree(path_1.default.join(folderPath, entry.name), entry.name); })
590
+ };
591
+ };
527
592
  var verifyFolder = function (folder_1) {
528
593
  var args_1 = [];
529
594
  for (var _i = 1; _i < arguments.length; _i++) {
@@ -585,9 +650,11 @@ var verifyPackageJsonContent = function () {
585
650
  case 2:
586
651
  packageJsonContent = _a.sent();
587
652
  packageJson = JSON.parse(packageJsonContent);
588
- expect(parentFolderName.includes('minimal')
589
- ? packageJson.devDependencies
590
- : packageJson.dependencies).toEqual(expect.objectContaining({ '@sasjs/core': expect.anything() }));
653
+ // Which map the seed app declares its SASjs dependency in is the seed's
654
+ // business - a web app carries @sasjs/core as a devDependency (it compiles
655
+ // the macros in) while the jobs template installs it as a dependency. What
656
+ // this asserts is that the template brings the macros with it at all.
657
+ expect(__assign(__assign({}, packageJson.devDependencies), packageJson.dependencies)).toEqual(expect.objectContaining({ '@sasjs/core': expect.anything() }));
591
658
  return [2 /*return*/];
592
659
  }
593
660
  });
@@ -4,6 +4,14 @@ import SASjs from '@sasjs/adapter/node';
4
4
  import { LogJson } from '../types';
5
5
  export declare function inExistingProject(folderPath: string): Promise<boolean>;
6
6
  export declare function diff(a: any[], b: any[]): any[];
7
+ /**
8
+ * The seed apps `sasjs create` downloads, by template name.
9
+ *
10
+ * One map for the commands that fetch them and the specs that compare their
11
+ * output, so a template added here is covered by both rather than downloaded
12
+ * by one and unknown to the other.
13
+ */
14
+ export declare const seedAppUrls: Record<string, string>;
7
15
  export declare function createReactApp(folderPath: string): Promise<void>;
8
16
  export declare function createAngularApp(folderPath: string): Promise<void>;
9
17
  export declare function createMinimalApp(folderPath: string): Promise<void>;