@sasjs/cli 6.4.8 → 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.
Files changed (33) hide show
  1. package/README.md +1 -0
  2. package/build/commands/build/build.js +66 -29
  3. package/build/commands/build/buildDeployCommand.d.ts +6 -0
  4. package/build/commands/build/buildDeployCommand.js +120 -0
  5. package/build/commands/build/spec/build.spec.js +31 -1
  6. package/build/commands/build/spec/buildDeployCommand.spec.d.ts +1 -0
  7. package/build/commands/build/spec/buildDeployCommand.spec.js +234 -0
  8. package/build/commands/compile/compile.js +168 -33
  9. package/build/commands/compile/compileCommand.js +1 -1
  10. package/build/commands/compile/internal/checkCompileStatus.js +30 -11
  11. package/build/commands/compile/internal/compileFile.js +16 -7
  12. package/build/commands/compile/internal/compileManifest.d.ts +36 -0
  13. package/build/commands/compile/internal/compileManifest.js +610 -0
  14. package/build/commands/compile/internal/index.d.ts +2 -0
  15. package/build/commands/compile/internal/index.js +2 -0
  16. package/build/commands/compile/internal/passThrough.d.ts +12 -0
  17. package/build/commands/compile/internal/passThrough.js +17 -0
  18. package/build/commands/compile/internal/spec/compileTestFile.spec.js +9 -6
  19. package/build/commands/compile/spec/checkCompileStatus.spec.d.ts +1 -0
  20. package/build/commands/compile/spec/checkCompileStatus.spec.js +221 -0
  21. package/build/commands/compile/spec/compileCommand.spec.js +9 -2
  22. package/build/commands/compile/spec/compileManifest.spec.d.ts +1 -0
  23. package/build/commands/compile/spec/compileManifest.spec.js +401 -0
  24. package/build/commands/compile/spec/passThrough.spec.d.ts +1 -0
  25. package/build/commands/compile/spec/passThrough.spec.js +160 -0
  26. package/build/commands/deploy/spec/cbd.spec.js +11 -7
  27. package/build/commands/help/help.js +11 -0
  28. package/build/commands/testing/spec/testCommand.spec.js +12 -0
  29. package/build/commands/testing/spec/testing.spec.js +8 -2
  30. package/build/commands/testing/testCommand.js +3 -1
  31. package/build/types/command/commandAliases.js +2 -0
  32. package/build/types/command/commandFactory.js +2 -0
  33. package/package.json +1 -1
@@ -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[]>;