@sasjs/cli 4.18.2 → 4.18.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.
@@ -69,13 +69,17 @@ var setConstants = function (isLocal, target, configuration) {
69
69
  buildDestinationDocsFolder = path_1.default.join(buildDestinationFolder, 'docs');
70
70
  macroCorePath = (_a = process.env.macroCorePath) !== null && _a !== void 0 ? _a : '';
71
71
  if (!(macroCorePath === '')) return [3 /*break*/, 2];
72
- return [4 /*yield*/, (0, utils_1.getNodeModulePath)('@sasjs/core')];
72
+ return [4 /*yield*/, (0, utils_1.getNodeModulePath)('@sasjs/core', process.projectDir)];
73
73
  case 1:
74
- // If no environment variable is set/populated then check for an installed
75
- // @sasjs/core in locations known to node.
76
74
  macroCorePath = _b.sent();
77
75
  _b.label = 2;
78
76
  case 2:
77
+ if (!(macroCorePath === '')) return [3 /*break*/, 4];
78
+ return [4 /*yield*/, (0, utils_1.getNodeModulePath)('@sasjs/core')];
79
+ case 3:
80
+ macroCorePath = _b.sent();
81
+ _b.label = 4;
82
+ case 4:
79
83
  buildDestinationResultsFolder = (0, utils_2.getAbsolutePath)(buildResultsFolder, isLocal ? process.projectDir : homeDir);
80
84
  buildDestinationResultsLogsFolder = path_1.default.join(buildDestinationResultsFolder, 'logs');
81
85
  sas9CredentialsError = 'The following attributes were not found:' +
@@ -179,7 +179,7 @@ describe('setConstants', function () {
179
179
  }
180
180
  });
181
181
  }); });
182
- test('should call getNodeModulePath once when environment variable macroCorePath is undefined', function () { return __awaiter(void 0, void 0, void 0, function () {
182
+ test('should look up @sasjs/core scoped to process.projectDir, then fall back unscoped, when environment variable macroCorePath is undefined', function () { return __awaiter(void 0, void 0, void 0, function () {
183
183
  var getNodeModulePathSpy;
184
184
  return __generator(this, function (_a) {
185
185
  switch (_a.label) {
@@ -187,18 +187,20 @@ describe('setConstants', function () {
187
187
  process.env.macroCorePath = undefined;
188
188
  getNodeModulePathSpy = jest
189
189
  .spyOn(utils, 'getNodeModulePath')
190
- .mockImplementation(function (packageName) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
190
+ .mockImplementation(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
191
191
  return [2 /*return*/, Promise.resolve('')];
192
192
  }); }); });
193
193
  return [4 /*yield*/, (0, setConstants_1.setConstants)()];
194
194
  case 1:
195
195
  _a.sent();
196
- expect(getNodeModulePathSpy).toHaveBeenCalledOnceWith('@sasjs/core');
196
+ expect(getNodeModulePathSpy).toHaveBeenNthCalledWith(1, '@sasjs/core', process.projectDir);
197
+ expect(getNodeModulePathSpy).toHaveBeenNthCalledWith(2, '@sasjs/core');
198
+ expect(getNodeModulePathSpy).toHaveBeenCalledTimes(2);
197
199
  return [2 /*return*/];
198
200
  }
199
201
  });
200
202
  }); });
201
- test('should call getNodeModulePath once when environment variable macroCorePath is blank', function () { return __awaiter(void 0, void 0, void 0, function () {
203
+ test('should look up @sasjs/core scoped to process.projectDir, then fall back unscoped, when environment variable macroCorePath is blank', function () { return __awaiter(void 0, void 0, void 0, function () {
202
204
  var getNodeModulePathSpy;
203
205
  return __generator(this, function (_a) {
204
206
  switch (_a.label) {
@@ -206,13 +208,15 @@ describe('setConstants', function () {
206
208
  process.env.macroCorePath = '';
207
209
  getNodeModulePathSpy = jest
208
210
  .spyOn(utils, 'getNodeModulePath')
209
- .mockImplementation(function (packageName) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
211
+ .mockImplementation(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
210
212
  return [2 /*return*/, Promise.resolve('')];
211
213
  }); }); });
212
214
  return [4 /*yield*/, (0, setConstants_1.setConstants)()];
213
215
  case 1:
214
216
  _a.sent();
215
- expect(getNodeModulePathSpy).toHaveBeenCalledOnceWith('@sasjs/core');
217
+ expect(getNodeModulePathSpy).toHaveBeenNthCalledWith(1, '@sasjs/core', process.projectDir);
218
+ expect(getNodeModulePathSpy).toHaveBeenNthCalledWith(2, '@sasjs/core');
219
+ expect(getNodeModulePathSpy).toHaveBeenCalledTimes(2);
216
220
  return [2 /*return*/];
217
221
  }
218
222
  });
@@ -236,6 +240,54 @@ describe('setConstants', function () {
236
240
  }
237
241
  });
238
242
  }); });
243
+ test('should prefer @sasjs/core resolved relative to process.projectDir over the CLI-relative fallback', function () { return __awaiter(void 0, void 0, void 0, function () {
244
+ var projectCorePath, getNodeModulePathSpy;
245
+ return __generator(this, function (_a) {
246
+ switch (_a.label) {
247
+ case 0:
248
+ process.env.macroCorePath = undefined;
249
+ projectCorePath = path_1.default.join('some', 'project', 'node_modules', '@sasjs', 'core');
250
+ getNodeModulePathSpy = jest
251
+ .spyOn(utils, 'getNodeModulePath')
252
+ .mockImplementation(function (_packageName, fromDir) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
253
+ return [2 /*return*/, Promise.resolve(fromDir ? projectCorePath : 'cli-relative-core-path')];
254
+ }); }); });
255
+ return [4 /*yield*/, (0, setConstants_1.setConstants)()
256
+ // found on the first, project-scoped lookup - the unscoped fallback
257
+ // should never be reached
258
+ ];
259
+ case 1:
260
+ _a.sent();
261
+ // found on the first, project-scoped lookup - the unscoped fallback
262
+ // should never be reached
263
+ expect(getNodeModulePathSpy).toHaveBeenCalledTimes(1);
264
+ expect(process.sasjsConstants.macroCorePath).toEqual(projectCorePath);
265
+ return [2 /*return*/];
266
+ }
267
+ });
268
+ }); });
269
+ test('should fall back to the CLI-relative @sasjs/core when the project has none installed', function () { return __awaiter(void 0, void 0, void 0, function () {
270
+ var fallbackCorePath, getNodeModulePathSpy;
271
+ return __generator(this, function (_a) {
272
+ switch (_a.label) {
273
+ case 0:
274
+ process.env.macroCorePath = undefined;
275
+ fallbackCorePath = path_1.default.join('cli', 'node_modules', '@sasjs', 'core');
276
+ getNodeModulePathSpy = jest
277
+ .spyOn(utils, 'getNodeModulePath')
278
+ .mockImplementation(function (_packageName, fromDir) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
279
+ return [2 /*return*/, Promise.resolve(fromDir ? '' : fallbackCorePath)];
280
+ }); }); });
281
+ return [4 /*yield*/, (0, setConstants_1.setConstants)()];
282
+ case 1:
283
+ _a.sent();
284
+ expect(getNodeModulePathSpy).toHaveBeenNthCalledWith(1, '@sasjs/core', process.projectDir);
285
+ expect(getNodeModulePathSpy).toHaveBeenNthCalledWith(2, '@sasjs/core');
286
+ expect(process.sasjsConstants.macroCorePath).toEqual(fallbackCorePath);
287
+ return [2 /*return*/];
288
+ }
289
+ });
290
+ }); });
239
291
  });
240
292
  var verifySasjsConstants = function (appFolder, hasSasjsCore, isLocal) {
241
293
  if (hasSasjsCore === void 0) { hasSasjsCore = false; }
@@ -493,6 +493,48 @@ describe('utils', function () {
493
493
  }
494
494
  });
495
495
  }); });
496
+ it('should prefer a module installed under the given fromDir over default resolution', function () { return __awaiter(void 0, void 0, void 0, function () {
497
+ var fakeModuleName, fromDir, fakeModuleDir, resolvedPath;
498
+ return __generator(this, function (_a) {
499
+ switch (_a.label) {
500
+ case 0:
501
+ fakeModuleName = "sasjs-test-fake-module-".concat((0, utils_2.generateTimestamp)());
502
+ fromDir = path_1.default.join(require('os').tmpdir(), "getNodeModulePath-".concat((0, utils_2.generateTimestamp)()));
503
+ fakeModuleDir = path_1.default.join(fromDir, 'node_modules', fakeModuleName);
504
+ return [4 /*yield*/, (0, utils_2.createFolder)(fakeModuleDir)];
505
+ case 1:
506
+ _a.sent();
507
+ return [4 /*yield*/, (0, utils_2.createFile)(path_1.default.join(fakeModuleDir, 'package.json'), JSON.stringify({ name: fakeModuleName, version: '1.0.0' }))
508
+ // not resolvable at all without fromDir - it only exists in fromDir's
509
+ // own node_modules, nowhere on the default resolution path
510
+ ];
511
+ case 2:
512
+ _a.sent();
513
+ // not resolvable at all without fromDir - it only exists in fromDir's
514
+ // own node_modules, nowhere on the default resolution path
515
+ return [4 /*yield*/, expect((0, utils_1.getNodeModulePath)(fakeModuleName)).resolves.toEqual('')];
516
+ case 3:
517
+ // not resolvable at all without fromDir - it only exists in fromDir's
518
+ // own node_modules, nowhere on the default resolution path
519
+ _a.sent();
520
+ return [4 /*yield*/, (0, utils_1.getNodeModulePath)(fakeModuleName, fromDir)
521
+ // compare tails rather than exact paths: require.resolve returns the
522
+ // real (symlink-resolved) path, which can differ from os.tmpdir()'s
523
+ // raw value (e.g. /var vs /private/var on macOS)
524
+ ];
525
+ case 4:
526
+ resolvedPath = _a.sent();
527
+ // compare tails rather than exact paths: require.resolve returns the
528
+ // real (symlink-resolved) path, which can differ from os.tmpdir()'s
529
+ // raw value (e.g. /var vs /private/var on macOS)
530
+ expect(resolvedPath.endsWith(path_1.default.join('node_modules', fakeModuleName))).toEqual(true);
531
+ return [4 /*yield*/, (0, utils_2.deleteFolder)(fromDir)];
532
+ case 5:
533
+ _a.sent();
534
+ return [2 /*return*/];
535
+ }
536
+ });
537
+ }); });
496
538
  });
497
539
  describe('getUniqServicesObj', function () {
498
540
  it('should return an empty object when services is falsy', function () {
@@ -42,5 +42,23 @@ export declare const terminateProcess: (status: number) => never;
42
42
  * @returns boolean
43
43
  */
44
44
  export declare const isSASjsProject: () => Promise<boolean>;
45
- export declare const getNodeModulePath: (module: string) => Promise<string>;
45
+ /**
46
+ * Locates an installed npm module and returns its root folder.
47
+ *
48
+ * Without `fromDir`, `require.resolve` searches relative to *this file's own*
49
+ * location on disk - i.e. wherever @sasjs/cli itself is installed - not the
50
+ * user's project. That's fine for CLI-internal dependencies, but wrong for
51
+ * anything the user is expected to manage in their own project's
52
+ * node_modules (e.g. @sasjs/core): since @sasjs/cli also depends on that
53
+ * same package, default resolution finds the CLI's own bundled copy first
54
+ * and never reaches the project's. Passing `fromDir` anchors the search at
55
+ * that directory instead (Node's `paths` option replaces, rather than
56
+ * extends, the default resolution paths), so callers that need
57
+ * project-first resolution should pass `process.projectDir` and fall back to
58
+ * an unscoped call if that returns nothing.
59
+ * @param {string} module - the name of the npm module to locate.
60
+ * @param {string} fromDir - optional directory to resolve `module` from,
61
+ * instead of this file's own location.
62
+ */
63
+ export declare const getNodeModulePath: (module: string, fromDir?: string) => Promise<string>;
46
64
  export declare const isSasJsServerInServerMode: (target: Target) => Promise<boolean>;
@@ -699,12 +699,30 @@ var isSASjsProject = function () { return __awaiter(void 0, void 0, void 0, func
699
699
  });
700
700
  }); };
701
701
  exports.isSASjsProject = isSASjsProject;
702
- var getNodeModulePath = function (module) { return __awaiter(void 0, void 0, void 0, function () {
702
+ /**
703
+ * Locates an installed npm module and returns its root folder.
704
+ *
705
+ * Without `fromDir`, `require.resolve` searches relative to *this file's own*
706
+ * location on disk - i.e. wherever @sasjs/cli itself is installed - not the
707
+ * user's project. That's fine for CLI-internal dependencies, but wrong for
708
+ * anything the user is expected to manage in their own project's
709
+ * node_modules (e.g. @sasjs/core): since @sasjs/cli also depends on that
710
+ * same package, default resolution finds the CLI's own bundled copy first
711
+ * and never reaches the project's. Passing `fromDir` anchors the search at
712
+ * that directory instead (Node's `paths` option replaces, rather than
713
+ * extends, the default resolution paths), so callers that need
714
+ * project-first resolution should pass `process.projectDir` and fall back to
715
+ * an unscoped call if that returns nothing.
716
+ * @param {string} module - the name of the npm module to locate.
717
+ * @param {string} fromDir - optional directory to resolve `module` from,
718
+ * instead of this file's own location.
719
+ */
720
+ var getNodeModulePath = function (module, fromDir) { return __awaiter(void 0, void 0, void 0, function () {
703
721
  var nodePackagePath;
704
722
  return __generator(this, function (_a) {
705
723
  // Look for ${module}/package.json, then return only the path
706
724
  try {
707
- nodePackagePath = path_1.default.dirname(require.resolve(path_1.default.join(module, 'package.json')));
725
+ nodePackagePath = path_1.default.dirname(require.resolve(path_1.default.join(module, 'package.json'), fromDir ? { paths: [fromDir] } : undefined));
708
726
  if (nodePackagePath)
709
727
  return [2 /*return*/, nodePackagePath];
710
728
  }
package/package.json CHANGED
@@ -122,5 +122,5 @@
122
122
  "overrides": {
123
123
  "ws": "8.21.0"
124
124
  },
125
- "version": "4.18.2"
125
+ "version": "4.18.3"
126
126
  }