@sasjs/cli 6.5.0 → 6.6.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.
@@ -46,6 +46,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
47
  }
48
48
  };
49
+ var __values = (this && this.__values) || function(o) {
50
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
51
+ if (m) return m.call(o);
52
+ if (o && typeof o.length === "number") return {
53
+ next: function () {
54
+ if (o && i >= o.length) o = void 0;
55
+ return { value: o && o[i++], done: !o };
56
+ }
57
+ };
58
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
59
+ };
49
60
  var __read = (this && this.__read) || function (o, n) {
50
61
  var m = typeof Symbol === "function" && o[Symbol.iterator];
51
62
  if (!m) return o;
@@ -71,17 +82,6 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
71
82
  }
72
83
  return to.concat(ar || Array.prototype.slice.call(from));
73
84
  };
74
- var __values = (this && this.__values) || function(o) {
75
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
76
- if (m) return m.call(o);
77
- if (o && typeof o.length === "number") return {
78
- next: function () {
79
- if (o && i >= o.length) o = void 0;
80
- return { value: o && o[i++], done: !o };
81
- }
82
- };
83
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
84
- };
85
85
  var __importDefault = (this && this.__importDefault) || function (mod) {
86
86
  return (mod && mod.__esModule) ? mod : { "default": mod };
87
87
  };
@@ -96,7 +96,6 @@ var types_1 = require("@sasjs/utils/types");
96
96
  var compressAndSave_1 = require("../../utils/compressAndSave");
97
97
  var config_1 = require("../../utils/config");
98
98
  var file_1 = require("../../utils/file");
99
- var compile_1 = require("../compile/compile");
100
99
  var internal_1 = require("../compile/internal");
101
100
  var config_2 = require("./internal/config");
102
101
  var getLaunchPageCode_1 = require("./internal/getLaunchPageCode");
@@ -124,28 +123,65 @@ function build(target) {
124
123
  _a = !(_d.sent());
125
124
  _d.label = 2;
126
125
  case 2:
127
- if (!_a) return [3 /*break*/, 4];
128
- return [4 /*yield*/, (0, compile_1.compile)(target)];
126
+ // build packages the build folder, it does not produce it: compiling here
127
+ // would make `build` and `bd` compile on a fresh clone, where the documented
128
+ // workflow is to run compile (or cbd) first and treat the build folder as the
129
+ // input - including when it has been adjusted by hand.
130
+ if (_a) {
131
+ throw new Error("The build folder ".concat(process.sasjsConstants.buildDestinationFolder, " does not exist.\n") +
132
+ "Run 'sasjs compile' (or 'sasjs cbd') first.");
133
+ }
134
+ return [4 /*yield*/, createFinalSasFiles(target)
135
+ // streamConfig.mockWeb opts the target into a static site that runs the same
136
+ // services in a browser. It is built from what compile just produced, so it
137
+ // is only meaningful after the tree above exists.
138
+ ];
129
139
  case 3:
130
140
  _d.sent();
131
- _d.label = 4;
132
- case 4: return [4 /*yield*/, createFinalSasFiles(target)
133
- // streamConfig.mockWeb opts the target into a static site that runs the same
134
- // services in a browser. It is built from what compile just produced, so it
135
- // is only meaningful after the tree above exists.
136
- ];
137
- case 5:
138
- _d.sent();
139
- if (!((_c = target.streamConfig) === null || _c === void 0 ? void 0 : _c.mockWeb)) return [3 /*break*/, 7];
141
+ if (!((_c = target.streamConfig) === null || _c === void 0 ? void 0 : _c.mockWeb)) return [3 /*break*/, 5];
140
142
  return [4 /*yield*/, (0, mockSite_1.buildMockSite)(target)];
141
- case 6:
143
+ case 4:
142
144
  _d.sent();
143
- _d.label = 7;
144
- case 7: return [2 /*return*/];
145
+ _d.label = 5;
146
+ case 5: return [2 /*return*/];
145
147
  }
146
148
  });
147
149
  });
148
150
  }
151
+ /**
152
+ * Reports what the pack holds. A SASjs target can legitimately produce a pack
153
+ * with no SAS services at all - the services are stored programs for another
154
+ * runtime - and that is exactly the case where an unreported zero would look
155
+ * like a successful deploy.
156
+ */
157
+ var logPackedFileCounts = function (tree) {
158
+ var _a;
159
+ var counts = { services: 0, files: 0 };
160
+ var walk = function (folder) {
161
+ var e_1, _a;
162
+ var _b;
163
+ try {
164
+ for (var _c = __values((_b = folder.members) !== null && _b !== void 0 ? _b : []), _d = _c.next(); !_d.done; _d = _c.next()) {
165
+ var member = _d.value;
166
+ if (member.type === types_1.MemberType.service)
167
+ counts.services++;
168
+ else if (member.type === types_1.MemberType.file)
169
+ counts.files++;
170
+ else
171
+ walk(member);
172
+ }
173
+ }
174
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
175
+ finally {
176
+ try {
177
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
178
+ }
179
+ finally { if (e_1) throw e_1.error; }
180
+ }
181
+ };
182
+ walk(tree);
183
+ (_a = process.logger) === null || _a === void 0 ? void 0 : _a.info("Packing ".concat(counts.services, " SAS ").concat(counts.services === 1 ? 'service' : 'services', " and ").concat(counts.files, " ").concat(counts.files === 1 ? 'file' : 'files', "."));
184
+ };
149
185
  function createFinalSasFiles(target) {
150
186
  return __awaiter(this, void 0, void 0, function () {
151
187
  var streamConfig, streamWeb, buildConfig, serverType, name, macroFolders, buildOutputFileName, buildDestinationFolder, finalSasFileContent, finalFilePath, finalFilePathJSON, finalFilePathZipped, buildInfo, buildInit, buildTerm, macroCorePath, dependencyFilePaths, dependenciesContent, _a, folderContent, folderContentJSON, servicePackSASjs, servicePack;
@@ -189,6 +225,7 @@ function createFinalSasFiles(target) {
189
225
  return [4 /*yield*/, getFolderContent(serverType)];
190
226
  case 8:
191
227
  _a = _l.sent(), folderContent = _a.folderContent, folderContentJSON = _a.folderContentJSON;
228
+ logPackedFileCounts(folderContentJSON);
192
229
  servicePackSASjs = {
193
230
  appLoc: target.appLoc,
194
231
  fileTree: folderContentJSON
@@ -549,7 +586,7 @@ function getBuildVars(target) {
549
586
  });
550
587
  }
551
588
  var convertVarsToSasFormat = function (vars) {
552
- var e_1, _a;
589
+ var e_2, _a;
553
590
  var entries = Object.entries(vars);
554
591
  var varsContent = '\n';
555
592
  try {
@@ -560,12 +597,12 @@ var convertVarsToSasFormat = function (vars) {
560
597
  varsContent += "%let ".concat(name_1, "=").concat(chunkedString, ";\n");
561
598
  }
562
599
  }
563
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
600
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
564
601
  finally {
565
602
  try {
566
603
  if (entries_1_1 && !entries_1_1.done && (_a = entries_1.return)) _a.call(entries_1);
567
604
  }
568
- finally { if (e_1) throw e_1.error; }
605
+ finally { if (e_2) throw e_2.error; }
569
606
  }
570
607
  return varsContent;
571
608
  };
@@ -72,8 +72,11 @@ describe('sasjs build', function () {
72
72
  return [4 /*yield*/, (0, utils_2.findTargetInConfiguration)('viya')];
73
73
  case 2:
74
74
  target = (_a.sent()).target;
75
- return [4 /*yield*/, expect((0, build_1.build)(target)).toResolve()];
75
+ return [4 /*yield*/, (0, __1.compile)(target)];
76
76
  case 3:
77
+ _a.sent();
78
+ return [4 /*yield*/, expect((0, build_1.build)(target)).toResolve()];
79
+ case 4:
77
80
  _a.sent();
78
81
  return [2 /*return*/];
79
82
  }
@@ -101,6 +104,33 @@ describe('sasjs build', function () {
101
104
  }
102
105
  });
103
106
  }); }, 2 * 60 * 1000);
107
+ it('reports a missing build folder instead of compiling one', function () { return __awaiter(void 0, void 0, void 0, function () {
108
+ var target, buildDestinationFolder;
109
+ return __generator(this, function (_a) {
110
+ switch (_a.label) {
111
+ case 0:
112
+ appName = "test-app-build-".concat((0, test_1.generateUniqueTimestamp)());
113
+ return [4 /*yield*/, (0, test_1.createTestApp)(__dirname, appName)];
114
+ case 1:
115
+ _a.sent();
116
+ return [4 /*yield*/, (0, utils_2.findTargetInConfiguration)('viya')];
117
+ case 2:
118
+ target = (_a.sent()).target;
119
+ buildDestinationFolder = process.sasjsConstants.buildDestinationFolder;
120
+ return [4 /*yield*/, (0, utils_1.folderExists)(buildDestinationFolder)];
121
+ case 3:
122
+ if (!_a.sent()) return [3 /*break*/, 5];
123
+ return [4 /*yield*/, (0, utils_1.deleteFolder)(buildDestinationFolder)];
124
+ case 4:
125
+ _a.sent();
126
+ _a.label = 5;
127
+ case 5: return [4 /*yield*/, expect((0, build_1.build)(target)).rejects.toThrow(/Run 'sasjs compile' \(or 'sasjs cbd'\) first/)];
128
+ case 6:
129
+ _a.sent();
130
+ return [2 /*return*/];
131
+ }
132
+ });
133
+ }); }, 2 * 60 * 1000);
104
134
  it("should compile and build remaining multiline comments", function () { return __awaiter(void 0, void 0, void 0, function () {
105
135
  var beforeComment, afterComment, commentLine1, commentLine2, testContent, macroPath, _a, _b, target, finalSasFilePath, finalSasFile;
106
136
  return __generator(this, function (_c) {
@@ -79,6 +79,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
79
79
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
80
80
  }
81
81
  };
82
+ var __values = (this && this.__values) || function(o) {
83
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
84
+ if (m) return m.call(o);
85
+ if (o && typeof o.length === "number") return {
86
+ next: function () {
87
+ if (o && i >= o.length) o = void 0;
88
+ return { value: o && o[i++], done: !o };
89
+ }
90
+ };
91
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
92
+ };
82
93
  var __importDefault = (this && this.__importDefault) || function (mod) {
83
94
  return (mod && mod.__esModule) ? mod : { "default": mod };
84
95
  };
@@ -97,57 +108,67 @@ var compileModule = __importStar(require("./compile"));
97
108
  var internal_1 = require("./internal");
98
109
  function compile(target_1) {
99
110
  return __awaiter(this, arguments, void 0, function (target, forceCompile) {
100
- var result, compileTree, macroFolders, programFolders_1, buildMacroTestFolder_1, macroTestFiles;
111
+ var result, compileTree, _a, _b, macroFolders, programFolders_1, buildMacroTestFolder_1, macroTestFiles;
101
112
  var _this = this;
102
- var _a, _b;
113
+ var _c, _d, _e;
103
114
  if (forceCompile === void 0) { forceCompile = false; }
104
- return __generator(this, function (_c) {
105
- switch (_c.label) {
115
+ return __generator(this, function (_f) {
116
+ switch (_f.label) {
106
117
  case 0: return [4 /*yield*/, (0, internal_1.checkCompileStatus)(target, ['tests'])
107
118
  // no need to compile again
108
119
  ];
109
120
  case 1:
110
- result = _c.sent();
121
+ result = _f.sent();
111
122
  // no need to compile again
112
123
  if (result.compiled && !forceCompile) {
113
- (_a = process.logger) === null || _a === void 0 ? void 0 : _a.info('Skipping compilation.');
114
- (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info(result.message);
124
+ (_c = process.logger) === null || _c === void 0 ? void 0 : _c.info('Skipping compilation.');
125
+ (_d = process.logger) === null || _d === void 0 ? void 0 : _d.info(result.message);
115
126
  return [2 /*return*/];
116
127
  }
128
+ if (!result.compiled)
129
+ (_e = process.logger) === null || _e === void 0 ? void 0 : _e.info("Compiling: ".concat(result.message));
117
130
  return [4 /*yield*/, compileModule.copyFilesToBuildFolder(target).catch(function (error) {
118
131
  var _a;
119
132
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('Project compilation has failed.');
120
133
  throw error;
121
134
  })];
122
135
  case 2:
123
- _c.sent();
136
+ _f.sent();
124
137
  compileTree = (0, internal_1.getCompileTree)(target);
125
138
  return [4 /*yield*/, compileModule.compileJobsServicesTests(target, compileTree)];
126
139
  case 3:
127
- _c.sent();
128
- return [4 /*yield*/, (0, config_1.getMacroFolders)(target)];
140
+ _f.sent();
141
+ _a = internal_1.writeCompileManifest;
142
+ return [4 /*yield*/, (0, internal_1.getManifestFolders)(target)];
129
143
  case 4:
130
- macroFolders = _c.sent();
131
- if (!macroFolders.length) return [3 /*break*/, 11];
144
+ _b = [_f.sent()];
145
+ return [4 /*yield*/, (0, internal_1.getManifestStamps)(target)];
146
+ case 5: return [4 /*yield*/, _a.apply(void 0, _b.concat([_f.sent()]))];
147
+ case 6:
148
+ _f.sent();
149
+ return [4 /*yield*/, (0, config_1.getMacroFolders)(target)];
150
+ case 7:
151
+ macroFolders = _f.sent();
152
+ if (!macroFolders.length) return [3 /*break*/, 14];
132
153
  return [4 /*yield*/, (0, config_1.getProgramFolders)(target)];
133
- case 5:
134
- programFolders_1 = _c.sent();
154
+ case 8:
155
+ programFolders_1 = _f.sent();
135
156
  return [4 /*yield*/, (0, utils_1.asyncForEach)(macroFolders, function (macroFolder) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
136
157
  switch (_a.label) {
137
158
  case 0: return [4 /*yield*/, (0, internal_1.copyTestMacroFiles)(macroFolder)];
138
159
  case 1: return [2 /*return*/, _a.sent()];
139
160
  }
140
161
  }); }); })];
141
- case 6:
142
- _c.sent();
162
+ case 9:
163
+ _f.sent();
143
164
  buildMacroTestFolder_1 = path_1.default.join(process.sasjsConstants.buildDestinationTestFolder, 'macros');
144
165
  return [4 /*yield*/, (0, utils_1.folderExists)(buildMacroTestFolder_1)];
145
- case 7:
146
- if (!_c.sent()) return [3 /*break*/, 11];
166
+ case 10:
167
+ if (!_f.sent()) return [3 /*break*/, 14];
147
168
  return [4 /*yield*/, (0, utils_1.listFilesAndSubFoldersInFolder)(buildMacroTestFolder_1)];
148
- case 8: return [4 /*yield*/, (_c.sent()).filter(file_1.isSasFile)];
149
- case 9:
150
- macroTestFiles = _c.sent();
169
+ case 11: return [4 /*yield*/, (_f.sent()).filter(file_1.isSasFile)];
170
+ case 12:
171
+ macroTestFiles = _f.sent();
151
172
  return [4 /*yield*/, (0, utils_1.asyncForEach)(macroTestFiles, function (macroTestFile) { return __awaiter(_this, void 0, void 0, function () {
152
173
  return __generator(this, function (_a) {
153
174
  switch (_a.label) {
@@ -156,21 +177,24 @@ function compile(target_1) {
156
177
  }
157
178
  });
158
179
  }); })];
159
- case 10:
160
- _c.sent();
161
- _c.label = 11;
162
- case 11: return [4 /*yield*/, compileTree.saveTree()];
163
- case 12:
164
- _c.sent();
165
- return [4 /*yield*/, (0, internal_1.compileTestFlow)(target).catch(function (err) { var _a; return (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('Test flow compilation has failed.'); })];
166
180
  case 13:
167
- _c.sent();
181
+ _f.sent();
182
+ _f.label = 14;
183
+ case 14: return [4 /*yield*/, compileTree.saveTree()];
184
+ case 15:
185
+ _f.sent();
186
+ return [4 /*yield*/, (0, internal_1.compileTestFlow)(target).catch(function (err) { var _a; return (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('Test flow compilation has failed.'); })];
187
+ case 16:
188
+ _f.sent();
168
189
  return [4 /*yield*/, compileWeb(target)];
169
- case 14:
170
- _c.sent();
190
+ case 17:
191
+ _f.sent();
171
192
  return [4 /*yield*/, syncFolder(target)];
172
- case 15:
173
- _c.sent();
193
+ case 18:
194
+ _f.sent();
195
+ return [4 /*yield*/, logCopiedFileCounts()];
196
+ case 19:
197
+ _f.sent();
174
198
  return [2 /*return*/];
175
199
  }
176
200
  });
@@ -316,6 +340,117 @@ function compileJobsServicesTests(target, compileTree) {
316
340
  });
317
341
  });
318
342
  }
343
+ /**
344
+ * Reports what the build folder holds, SAS and otherwise.
345
+ *
346
+ * A JS-only services folder is a legitimate shape for a SASjs server, and it is
347
+ * also the shape where a silent "compiled" would hide an empty build - the
348
+ * counts make the difference between "0 and 45" and "nothing happened" visible.
349
+ */
350
+ var logCopiedFileCounts = function () { return __awaiter(void 0, void 0, void 0, function () {
351
+ var _a, buildDestinationServicesFolder, buildDestinationJobsFolder, countFiles, services, jobs, sas, runtime, runtimeNote;
352
+ var _b;
353
+ return __generator(this, function (_c) {
354
+ switch (_c.label) {
355
+ case 0:
356
+ _a = process.sasjsConstants, buildDestinationServicesFolder = _a.buildDestinationServicesFolder, buildDestinationJobsFolder = _a.buildDestinationJobsFolder;
357
+ countFiles = function (folder) { return __awaiter(void 0, void 0, void 0, function () {
358
+ var counts, walk;
359
+ return __generator(this, function (_a) {
360
+ switch (_a.label) {
361
+ case 0:
362
+ counts = { sas: 0, runtime: 0 };
363
+ return [4 /*yield*/, (0, utils_1.folderExists)(folder)];
364
+ case 1:
365
+ if (!(_a.sent()))
366
+ return [2 /*return*/, counts];
367
+ walk = function (dir) { return __awaiter(void 0, void 0, void 0, function () {
368
+ var _a, _b, fileName, e_1_1, _c, _d, subFolder, e_2_1;
369
+ var e_1, _e, e_2, _f;
370
+ return __generator(this, function (_g) {
371
+ switch (_g.label) {
372
+ case 0:
373
+ _g.trys.push([0, 5, 6, 7]);
374
+ return [4 /*yield*/, (0, utils_1.listFilesInFolder)(dir)];
375
+ case 1:
376
+ _a = __values.apply(void 0, [_g.sent()]), _b = _a.next();
377
+ _g.label = 2;
378
+ case 2:
379
+ if (!!_b.done) return [3 /*break*/, 4];
380
+ fileName = _b.value;
381
+ if ((0, file_1.isSasFile)(fileName))
382
+ counts.sas++;
383
+ else
384
+ counts.runtime++;
385
+ _g.label = 3;
386
+ case 3:
387
+ _b = _a.next();
388
+ return [3 /*break*/, 2];
389
+ case 4: return [3 /*break*/, 7];
390
+ case 5:
391
+ e_1_1 = _g.sent();
392
+ e_1 = { error: e_1_1 };
393
+ return [3 /*break*/, 7];
394
+ case 6:
395
+ try {
396
+ if (_b && !_b.done && (_e = _a.return)) _e.call(_a);
397
+ }
398
+ finally { if (e_1) throw e_1.error; }
399
+ return [7 /*endfinally*/];
400
+ case 7:
401
+ _g.trys.push([7, 13, 14, 15]);
402
+ return [4 /*yield*/, (0, utils_1.listSubFoldersInFolder)(dir)];
403
+ case 8:
404
+ _c = __values.apply(void 0, [_g.sent()]), _d = _c.next();
405
+ _g.label = 9;
406
+ case 9:
407
+ if (!!_d.done) return [3 /*break*/, 12];
408
+ subFolder = _d.value;
409
+ return [4 /*yield*/, walk(path_1.default.join(dir, subFolder))];
410
+ case 10:
411
+ _g.sent();
412
+ _g.label = 11;
413
+ case 11:
414
+ _d = _c.next();
415
+ return [3 /*break*/, 9];
416
+ case 12: return [3 /*break*/, 15];
417
+ case 13:
418
+ e_2_1 = _g.sent();
419
+ e_2 = { error: e_2_1 };
420
+ return [3 /*break*/, 15];
421
+ case 14:
422
+ try {
423
+ if (_d && !_d.done && (_f = _c.return)) _f.call(_c);
424
+ }
425
+ finally { if (e_2) throw e_2.error; }
426
+ return [7 /*endfinally*/];
427
+ case 15: return [2 /*return*/];
428
+ }
429
+ });
430
+ }); };
431
+ return [4 /*yield*/, walk(folder)];
432
+ case 2:
433
+ _a.sent();
434
+ return [2 /*return*/, counts];
435
+ }
436
+ });
437
+ }); };
438
+ return [4 /*yield*/, countFiles(buildDestinationServicesFolder)];
439
+ case 1:
440
+ services = _c.sent();
441
+ return [4 /*yield*/, countFiles(buildDestinationJobsFolder)];
442
+ case 2:
443
+ jobs = _c.sent();
444
+ sas = services.sas + jobs.sas;
445
+ runtime = services.runtime + jobs.runtime;
446
+ runtimeNote = runtime === 0
447
+ ? ''
448
+ : "; ".concat(runtime, " ").concat(runtime === 1 ? 'file' : 'files', " copied through unchanged (not SAS programs)");
449
+ (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info("Compiled ".concat(sas, " SAS ").concat(sas === 1 ? 'file' : 'files').concat(runtimeNote, "."));
450
+ return [2 /*return*/];
451
+ }
452
+ });
453
+ }); };
319
454
  function recreateBuildFolder() {
320
455
  return __awaiter(this, void 0, void 0, function () {
321
456
  var buildDestinationFolder, pathExists;
@@ -152,7 +152,7 @@ var CompileCommand = /** @class */ (function (_super) {
152
152
  case 1:
153
153
  target = (_a.sent()).target;
154
154
  buildDestinationFolder = process.sasjsConstants.buildDestinationFolder;
155
- return [4 /*yield*/, (0, compile_1.compile)(target, true)
155
+ return [4 /*yield*/, (0, compile_1.compile)(target)
156
156
  .then(function () {
157
157
  var _a;
158
158
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.success("The project was successfully compiled for ".concat(target.serverType, " using target '").concat(target.name, "'\nThe compile output is located in the ").concat(buildDestinationFolder, " directory."));
@@ -41,14 +41,14 @@ var utils_1 = require("@sasjs/utils");
41
41
  var _1 = require("./");
42
42
  function checkCompileStatus(target, exceptions) {
43
43
  return __awaiter(this, void 0, void 0, function () {
44
- var buildDestinationFolder, pathExists, _a, areServiceFoldersMatching, serviceReasons, _b, areJobFoldersMatching, jobReasons, compiled;
45
- return __generator(this, function (_c) {
46
- switch (_c.label) {
44
+ var buildDestinationFolder, pathExists, _a, areServiceFoldersMatching, serviceReasons, _b, areJobFoldersMatching, jobReasons, reasons, changedFiles, _c, _d;
45
+ return __generator(this, function (_e) {
46
+ switch (_e.label) {
47
47
  case 0:
48
48
  buildDestinationFolder = process.sasjsConstants.buildDestinationFolder;
49
49
  return [4 /*yield*/, (0, utils_1.folderExists)(buildDestinationFolder)];
50
50
  case 1:
51
- pathExists = _c.sent();
51
+ pathExists = _e.sent();
52
52
  if (!pathExists) {
53
53
  return [2 /*return*/, {
54
54
  compiled: false,
@@ -57,16 +57,35 @@ function checkCompileStatus(target, exceptions) {
57
57
  }
58
58
  return [4 /*yield*/, checkServiceFolders(target, exceptions)];
59
59
  case 2:
60
- _a = _c.sent(), areServiceFoldersMatching = _a.areServiceFoldersMatching, serviceReasons = _a.reasons;
60
+ _a = _e.sent(), areServiceFoldersMatching = _a.areServiceFoldersMatching, serviceReasons = _a.reasons;
61
61
  return [4 /*yield*/, checkJobFolders(target, exceptions)];
62
62
  case 3:
63
- _b = _c.sent(), areJobFoldersMatching = _b.areJobFoldersMatching, jobReasons = _b.reasons;
64
- compiled = areServiceFoldersMatching && areJobFoldersMatching;
63
+ _b = _e.sent(), areJobFoldersMatching = _b.areJobFoldersMatching, jobReasons = _b.reasons;
64
+ if (!areServiceFoldersMatching || !areJobFoldersMatching) {
65
+ reasons = [];
66
+ if (!areServiceFoldersMatching)
67
+ reasons.push("Missing Services: ".concat(serviceReasons.join(', ')));
68
+ if (!areJobFoldersMatching)
69
+ reasons.push("Missing jobs: ".concat(jobReasons.join(', ')));
70
+ return [2 /*return*/, { compiled: false, message: reasons.join('\n') }];
71
+ }
72
+ _c = _1.getChangedFiles;
73
+ return [4 /*yield*/, (0, _1.getManifestFolders)(target)];
74
+ case 4:
75
+ _d = [_e.sent()];
76
+ return [4 /*yield*/, (0, _1.getManifestStamps)(target)];
77
+ case 5: return [4 /*yield*/, _c.apply(void 0, _d.concat([_e.sent()]))];
78
+ case 6:
79
+ changedFiles = _e.sent();
80
+ if (changedFiles.length) {
81
+ return [2 /*return*/, {
82
+ compiled: false,
83
+ message: "Changed since the last compile: ".concat(changedFiles.join(', '))
84
+ }];
85
+ }
65
86
  return [2 /*return*/, {
66
- compiled: compiled,
67
- message: compiled
68
- ? 'All services and jobs are already present.'
69
- : "Missing Services: ".concat(serviceReasons.join(', '), "\nMissing jobs: ").concat(jobReasons.join(', '))
87
+ compiled: true,
88
+ message: 'All services and jobs are already present and up to date.'
70
89
  }];
71
90
  }
72
91
  });
@@ -58,14 +58,23 @@ var path_1 = __importDefault(require("path"));
58
58
  function compileFile(target_1, filePath_1, macroFolders_1, programFolders_1) {
59
59
  return __awaiter(this, arguments, void 0, function (target, filePath, macroFolders, programFolders, programVar, compileTree, fileType, sourceFolder) {
60
60
  var dependencies;
61
+ var _a;
61
62
  if (programVar === void 0) { programVar = ''; }
62
- return __generator(this, function (_a) {
63
- switch (_a.label) {
64
- case 0: return [4 /*yield*/, (0, _1.loadDependencies)(target, sourceFolder
65
- ? path_1.default.join(sourceFolder, filePath.split(path_1.default.sep).pop())
66
- : filePath, macroFolders, programFolders, (0, utils_1.isTestFile)(filePath) ? types_1.SASJsFileType.test : fileType, compileTree)];
63
+ return __generator(this, function (_b) {
64
+ switch (_b.label) {
65
+ case 0:
66
+ // Only SAS programs are compiled. Anything else - a `.js` stored program, a
67
+ // web asset, a file a deploy script reads - is already in the build folder
68
+ // from the folder copy, and parsing it as SAS would corrupt it.
69
+ if ((0, _1.shouldPassThrough)(path_1.default.basename(filePath))) {
70
+ (_a = process.logger) === null || _a === void 0 ? void 0 : _a.info("Copying ".concat(path_1.default.basename(filePath), " to the build folder unchanged - it is not a SAS program."));
71
+ return [2 /*return*/];
72
+ }
73
+ return [4 /*yield*/, (0, _1.loadDependencies)(target, sourceFolder
74
+ ? path_1.default.join(sourceFolder, filePath.split(path_1.default.sep).pop())
75
+ : filePath, macroFolders, programFolders, (0, utils_1.isTestFile)(filePath) ? types_1.SASJsFileType.test : fileType, compileTree)];
67
76
  case 1:
68
- dependencies = _a.sent();
77
+ dependencies = _b.sent();
69
78
  if (fileType === types_1.SASJsFileType.service) {
70
79
  dependencies = "".concat(programVar, "\n").concat(dependencies);
71
80
  }
@@ -74,7 +83,7 @@ function compileFile(target_1, filePath_1, macroFolders_1, programFolders_1) {
74
83
  }
75
84
  return [4 /*yield*/, (0, utils_1.createFile)(filePath, dependencies)];
76
85
  case 2:
77
- _a.sent();
86
+ _b.sent();
78
87
  return [2 /*return*/];
79
88
  }
80
89
  });
@@ -0,0 +1,36 @@
1
+ import { Target } from '@sasjs/utils';
2
+ /**
3
+ * Hashes of the source files the last compile read, keyed by absolute path.
4
+ *
5
+ * The build folder cannot be used as the record of what was compiled: `compile`
6
+ * transforms the files it writes (macros and includes are inlined), so a
7
+ * compiled service never matches its source byte for byte. Comparing content
8
+ * hashes against a manifest written at the end of a successful compile is what
9
+ * makes "the source changed" detectable without the reader passing a force flag.
10
+ */
11
+ export type CompileManifest = Record<string, string>;
12
+ export declare const getManifestPath: () => string;
13
+ export declare const writeCompileManifest: (folders: string[], stampFiles?: string[]) => Promise<void>;
14
+ export declare const readCompileManifest: () => Promise<CompileManifest | null>;
15
+ /**
16
+ * Names the source files that have changed since the last successful compile:
17
+ * edited content, new files, and files the manifest has never seen.
18
+ */
19
+ export declare const getChangedFiles: (folders: string[], stampFiles?: string[]) => Promise<string[]>;
20
+ /**
21
+ * Every folder whose contents feed the build: the services and jobs, plus the
22
+ * web app and the sync folder when the target uses them. A skip is only safe if
23
+ * it accounts for all of them - a frontend change with an unchanged services
24
+ * folder must still rebuild.
25
+ */
26
+ export declare const getManifestFolders: (target: Target) => Promise<string[]>;
27
+ /**
28
+ * Individual files whose change invalidates the whole build.
29
+ *
30
+ * The test programs are not inside a service folder, and the dependency
31
+ * manifests are the record of which @sasjs/core a build used: a core bump
32
+ * arrives as an install, so the version in `node_modules/@sasjs/core` and the
33
+ * lockfile are the signals - walking every macro of the library would cost
34
+ * more than it catches.
35
+ */
36
+ export declare const getManifestStamps: (target: Target) => Promise<string[]>;