@squeep/log-helper 1.0.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.
- package/.eslintrc.json +89 -0
- package/.nycrc.json +6 -0
- package/README.md +40 -0
- package/index.js +7 -0
- package/lib/file-scope.js +194 -0
- package/package.json +37 -0
- package/test/lib/file-scope.js +282 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"browser": false,
|
|
4
|
+
"es6": true,
|
|
5
|
+
"node": true
|
|
6
|
+
},
|
|
7
|
+
"extends": [
|
|
8
|
+
"eslint:recommended",
|
|
9
|
+
"plugin:node/recommended",
|
|
10
|
+
"plugin:security/recommended",
|
|
11
|
+
"plugin:sonarjs/recommended"
|
|
12
|
+
],
|
|
13
|
+
"parserOptions": {
|
|
14
|
+
"ecmaVersion": "latest"
|
|
15
|
+
},
|
|
16
|
+
"plugins": [
|
|
17
|
+
"node",
|
|
18
|
+
"security",
|
|
19
|
+
"sonarjs"
|
|
20
|
+
],
|
|
21
|
+
"rules": {
|
|
22
|
+
"array-element-newline": [
|
|
23
|
+
"error",
|
|
24
|
+
"consistent"
|
|
25
|
+
],
|
|
26
|
+
"arrow-parens": [
|
|
27
|
+
"error",
|
|
28
|
+
"always"
|
|
29
|
+
],
|
|
30
|
+
"arrow-spacing": [
|
|
31
|
+
"error",
|
|
32
|
+
{
|
|
33
|
+
"after": true,
|
|
34
|
+
"before": true
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"block-scoped-var": "error",
|
|
38
|
+
"block-spacing": "error",
|
|
39
|
+
"brace-style": "error",
|
|
40
|
+
"callback-return": "error",
|
|
41
|
+
"camelcase": "error",
|
|
42
|
+
"class-methods-use-this": "error",
|
|
43
|
+
"comma-dangle": [
|
|
44
|
+
"error",
|
|
45
|
+
"always-multiline"
|
|
46
|
+
],
|
|
47
|
+
"comma-spacing": [
|
|
48
|
+
"error",
|
|
49
|
+
{
|
|
50
|
+
"after": true,
|
|
51
|
+
"before": false
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"comma-style": [
|
|
55
|
+
"error",
|
|
56
|
+
"last"
|
|
57
|
+
],
|
|
58
|
+
"indent": [
|
|
59
|
+
"warn",
|
|
60
|
+
2,
|
|
61
|
+
{
|
|
62
|
+
"SwitchCase": 1
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"sonarjs/cognitive-complexity": "warn",
|
|
66
|
+
"sonarjs/no-duplicate-string": "warn",
|
|
67
|
+
"keyword-spacing": "error",
|
|
68
|
+
"linebreak-style": [
|
|
69
|
+
"error",
|
|
70
|
+
"unix"
|
|
71
|
+
],
|
|
72
|
+
"no-unused-vars": [
|
|
73
|
+
"error", {
|
|
74
|
+
"varsIgnorePattern": "^_"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"object-curly-spacing": [
|
|
78
|
+
"error",
|
|
79
|
+
"always"
|
|
80
|
+
],
|
|
81
|
+
"prefer-const": "error",
|
|
82
|
+
"quotes": [
|
|
83
|
+
"warn",
|
|
84
|
+
"single"
|
|
85
|
+
],
|
|
86
|
+
"strict": "error",
|
|
87
|
+
"vars-on-top": "error"
|
|
88
|
+
}
|
|
89
|
+
}
|
package/.nycrc.json
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @squeep/log-helper
|
|
2
|
+
|
|
3
|
+
Utilities for standardized logging.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
- `fileScope(filepath, options)`
|
|
8
|
+
Returns a function which will decorate function names with some combination of the source file path, package name, and package version. If the filepath is `index.js`, it will be replaced by its containing directory. Defaults for what is included are determined by Squeep opinions on package layouts.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
// Assuming this file is located at 'project/lib/subdir/code.js'
|
|
14
|
+
// Assuming project/package.json defines 'name' as 'project'
|
|
15
|
+
const { fileScope } = require('@squeep/log-helper');
|
|
16
|
+
const _fileScope = fileScope(__filename, { includeVersion: false });
|
|
17
|
+
|
|
18
|
+
function example() {
|
|
19
|
+
const _scope = _fileScope('example');
|
|
20
|
+
console.log(_scope); // Will show 'project:subdir/code:example'
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
- `includePackage` Package name from `package.json`.
|
|
26
|
+
- `includeVersion` Package version from `package.json`. If this is `true`, `includePackage` will also become `true`.
|
|
27
|
+
- `includePath` The path to the file, relative to the `package.json` file.
|
|
28
|
+
- `leftTrim` How much of the beginning of the path to elide. (e.g. 3 would render `foo/bar` as `/bar`)
|
|
29
|
+
- `delimiter` Placed between fields, defaults to `:`.
|
|
30
|
+
- `prefix` A field included before the package name.
|
|
31
|
+
|
|
32
|
+
Defaults, based on directory existing in project:
|
|
33
|
+
| | `src` | `lib` | other |
|
|
34
|
+
|----------------|-------|-------|-------|
|
|
35
|
+
| includePackage | false | true | false |
|
|
36
|
+
| includeVersion | false | true | false |
|
|
37
|
+
| includePath | true | true | false |
|
|
38
|
+
| leftTrim | 4 | 4 | 0 |
|
|
39
|
+
|
|
40
|
+
If any errors are encountered while trying to determine the path or package metadata, the scope will be prefixed with a '?'.
|
package/index.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An opinionated way to gather a source identifier to include in logged
|
|
5
|
+
* messages, using possibly too much information.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal exception
|
|
13
|
+
*/
|
|
14
|
+
class FileScopeError extends Error {
|
|
15
|
+
constructor(...args) {
|
|
16
|
+
super(...args);
|
|
17
|
+
Error.captureStackTrace(this, this.constructor);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get name() {
|
|
21
|
+
return this.constructor.name;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read and parse package.json from a path.
|
|
28
|
+
* @param {String} packagePath
|
|
29
|
+
* @returns {Object}
|
|
30
|
+
*/
|
|
31
|
+
function readPackageJSON(packagePath) {
|
|
32
|
+
try {
|
|
33
|
+
const content = fs.readFileSync(path.join(packagePath, 'package.json')); // eslint-disable-line security/detect-non-literal-fs-filename
|
|
34
|
+
return JSON.parse(content);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return {
|
|
37
|
+
name: '(unknown)',
|
|
38
|
+
version: '(unknown)',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns whether path p exists.
|
|
46
|
+
* @param {String} p
|
|
47
|
+
* @returns {Boolean}
|
|
48
|
+
*/
|
|
49
|
+
function pathExists(p) {
|
|
50
|
+
try {
|
|
51
|
+
fs.statSync(p); // eslint-disable-line security/detect-non-literal-fs-filename
|
|
52
|
+
return true;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
if (e.code !== 'ENOENT') {
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Walk up the path of provided filename until directory with
|
|
64
|
+
* package.json is located. We assume this is the package root.
|
|
65
|
+
* @param {String} filename
|
|
66
|
+
* @returns {String}
|
|
67
|
+
*/
|
|
68
|
+
function locatePackageBase(filename) {
|
|
69
|
+
let currentPath = filename;
|
|
70
|
+
do {
|
|
71
|
+
const d = path.dirname(currentPath);
|
|
72
|
+
try {
|
|
73
|
+
fs.statSync(path.join(d, 'package.json')); // eslint-disable-line security/detect-non-literal-fs-filename
|
|
74
|
+
return d + '/';
|
|
75
|
+
} catch (e) {
|
|
76
|
+
if (e.code !== 'ENOENT') {
|
|
77
|
+
throw e;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
currentPath = d;
|
|
81
|
+
} while (currentPath !== '/');
|
|
82
|
+
throw new FileScopeError('unable to find package root');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get default options based on package directory structure.
|
|
88
|
+
* @param {String} packageBase
|
|
89
|
+
* @returns {Object}
|
|
90
|
+
*/
|
|
91
|
+
function defaultOptions(packageBase) {
|
|
92
|
+
const options = {
|
|
93
|
+
includePath: false,
|
|
94
|
+
includePackage: false,
|
|
95
|
+
includeVersion: false,
|
|
96
|
+
leftTrim: 0,
|
|
97
|
+
_errorEncountered: false,
|
|
98
|
+
errorPrefix: '?',
|
|
99
|
+
delimiter: ':',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
if (packageBase) {
|
|
103
|
+
try {
|
|
104
|
+
options.includePath = true;
|
|
105
|
+
if (pathExists(path.join(packageBase, 'lib'))) {
|
|
106
|
+
options.leftTrim = 4;
|
|
107
|
+
options.includePackage = true;
|
|
108
|
+
options.includeVersion = true;
|
|
109
|
+
} else if (pathExists(path.join(packageBase, 'src'))) {
|
|
110
|
+
options.leftTrim = 4;
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {
|
|
113
|
+
options._errorEncountered = true;
|
|
114
|
+
options.includePath = false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return options;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Returns a function suitable for decorating a function name with
|
|
124
|
+
* package information and details of the source file.
|
|
125
|
+
* @param {String} filepath full path from __filename
|
|
126
|
+
* @param {Object=} options
|
|
127
|
+
* @param {Boolean=} options.includePackage
|
|
128
|
+
* @param {Boolean=} options.includeVersion
|
|
129
|
+
* @param {Boolean=} options.includePath
|
|
130
|
+
* @param {String=} options.prefix
|
|
131
|
+
* @param {Number=} options.leftTrim
|
|
132
|
+
* @param {String=} options.errorPrefix
|
|
133
|
+
* @param {String=} options.delimiter
|
|
134
|
+
* @returns {Function}
|
|
135
|
+
*/
|
|
136
|
+
function fileScope(filepath, options) {
|
|
137
|
+
let errorEncountered = false;
|
|
138
|
+
let packageBase = '';
|
|
139
|
+
try {
|
|
140
|
+
packageBase = locatePackageBase(filepath);
|
|
141
|
+
} catch (e) {
|
|
142
|
+
errorEncountered = true;
|
|
143
|
+
}
|
|
144
|
+
const defaults = defaultOptions(packageBase);
|
|
145
|
+
errorEncountered |= defaults._errorEncountered;
|
|
146
|
+
const {
|
|
147
|
+
includePackage,
|
|
148
|
+
includeVersion,
|
|
149
|
+
includePath,
|
|
150
|
+
prefix,
|
|
151
|
+
leftTrim,
|
|
152
|
+
errorPrefix,
|
|
153
|
+
delimiter,
|
|
154
|
+
} = {
|
|
155
|
+
...defaults,
|
|
156
|
+
...options,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
let packageIdentifier;
|
|
160
|
+
if (includePackage || includeVersion) {
|
|
161
|
+
const { name: packageName, version: packageVersion } = readPackageJSON(packageBase);
|
|
162
|
+
// including version implies including package
|
|
163
|
+
packageIdentifier = includeVersion ? `${packageName}@${packageVersion}` : packageName;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const { base, name, ext } = path.parse(filepath);
|
|
167
|
+
const isIndex = (name === 'index') && ['.js', '.ts', '.mjs', '.cjs'].includes(ext);
|
|
168
|
+
|
|
169
|
+
// If filepath is an index, trim off filename and last slash, otherwise just trim off extension
|
|
170
|
+
const rightTrim = 0 - (isIndex ? (base.length + 1) : ext.length);
|
|
171
|
+
// We can't trim both ends at once as dirname won't work when isIndex is true
|
|
172
|
+
const rightTrimmed = filepath.slice(0, rightTrim);
|
|
173
|
+
|
|
174
|
+
// Trim leading part of path
|
|
175
|
+
const trim = (includePath && packageBase) ? (leftTrim + packageBase.length) : (path.dirname(rightTrimmed).length + 1);
|
|
176
|
+
const trimmedFilename = rightTrimmed.slice(trim);
|
|
177
|
+
|
|
178
|
+
const components = [errorEncountered ? errorPrefix : '', prefix, packageIdentifier, trimmedFilename]
|
|
179
|
+
.filter((x) => x);
|
|
180
|
+
|
|
181
|
+
const scope = components.join(delimiter);
|
|
182
|
+
|
|
183
|
+
return (name) => `${scope}${delimiter}${name}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
module.exports = {
|
|
188
|
+
FileScopeError,
|
|
189
|
+
readPackageJSON,
|
|
190
|
+
pathExists,
|
|
191
|
+
locatePackageBase,
|
|
192
|
+
defaultOptions,
|
|
193
|
+
fileScope,
|
|
194
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@squeep/log-helper",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Simple helpers for standardized logging",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=14"
|
|
8
|
+
},
|
|
9
|
+
"directories": {
|
|
10
|
+
"lib": "lib",
|
|
11
|
+
"test": "test"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"coverage": "nyc npm test",
|
|
15
|
+
"coverage-check": "nyc check-coverage",
|
|
16
|
+
"eslint": "eslint *.js lib test",
|
|
17
|
+
"test": "mocha --recursive"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [],
|
|
20
|
+
"author": "Justin Wind <jwind-npm@squeep.com>",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"pre-commit": [
|
|
23
|
+
"eslint",
|
|
24
|
+
"coverage",
|
|
25
|
+
"coverage-check"
|
|
26
|
+
],
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"eslint": "^8.53.0",
|
|
29
|
+
"eslint-plugin-node": "^11.1.0",
|
|
30
|
+
"eslint-plugin-security": "^1.7.1",
|
|
31
|
+
"eslint-plugin-sonarjs": "^0.23.0",
|
|
32
|
+
"mocha": "^10.2.0",
|
|
33
|
+
"nyc": "^15.1.0",
|
|
34
|
+
"pre-commit": "^1.2.2",
|
|
35
|
+
"sinon": "^17.0.1"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const assert = require('assert');
|
|
5
|
+
const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const {
|
|
8
|
+
readPackageJSON,
|
|
9
|
+
fileScope,
|
|
10
|
+
locatePackageBase,
|
|
11
|
+
defaultOptions,
|
|
12
|
+
pathExists,
|
|
13
|
+
FileScopeError,
|
|
14
|
+
} = require('../../lib/file-scope');
|
|
15
|
+
|
|
16
|
+
describe('File Scope', function () {
|
|
17
|
+
let noentError;
|
|
18
|
+
|
|
19
|
+
before(function () {
|
|
20
|
+
noentError = new Error('ENOENT: no such file or directory');
|
|
21
|
+
Object.assign(noentError, {
|
|
22
|
+
errno: -2,
|
|
23
|
+
syscall: 'stat',
|
|
24
|
+
code: 'ENOENT',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(function () {
|
|
29
|
+
sinon.restore();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('FileScopeError', function () {
|
|
33
|
+
it('covers', function () {
|
|
34
|
+
const e = new FileScopeError('beep');
|
|
35
|
+
assert.strictEqual(e.name, 'FileScopeError');
|
|
36
|
+
});
|
|
37
|
+
}); // FileScopeError
|
|
38
|
+
|
|
39
|
+
describe('readPackageJSON', function () {
|
|
40
|
+
let filepath;
|
|
41
|
+
beforeEach(function () {
|
|
42
|
+
filepath = '/path/to/package.json';
|
|
43
|
+
sinon.stub(fs, 'readFileSync').returns(`{
|
|
44
|
+
"name": "@example/package",
|
|
45
|
+
"version": "3.1.4"
|
|
46
|
+
}`);
|
|
47
|
+
});
|
|
48
|
+
it('covers success', function () {
|
|
49
|
+
const result = readPackageJSON(filepath);
|
|
50
|
+
assert.strictEqual(result.name, '@example/package');
|
|
51
|
+
assert.strictEqual(result.version, '3.1.4');
|
|
52
|
+
});
|
|
53
|
+
it('covers failure', function () {
|
|
54
|
+
fs.readFileSync.throws();
|
|
55
|
+
const result = readPackageJSON(filepath);
|
|
56
|
+
assert.strictEqual(result.name, '(unknown)');
|
|
57
|
+
assert.strictEqual(result.version, '(unknown)');
|
|
58
|
+
});
|
|
59
|
+
}); // readJSONFile
|
|
60
|
+
|
|
61
|
+
describe('pathExists', function () {
|
|
62
|
+
let p;
|
|
63
|
+
beforeEach(function () {
|
|
64
|
+
p = '/path/to/package';
|
|
65
|
+
sinon.stub(fs, 'statSync').returns();
|
|
66
|
+
});
|
|
67
|
+
it('returns true when path exists', function () {
|
|
68
|
+
const result = pathExists(p);
|
|
69
|
+
assert.strictEqual(result, true);
|
|
70
|
+
});
|
|
71
|
+
it('returns false when path does not exist', function () {
|
|
72
|
+
fs.statSync.throws(noentError);
|
|
73
|
+
const result = pathExists(p);
|
|
74
|
+
assert.strictEqual(result, false);
|
|
75
|
+
});
|
|
76
|
+
it('raises other error', function () {
|
|
77
|
+
const expectedError = new Error('oh no');
|
|
78
|
+
fs.statSync.throws(expectedError);
|
|
79
|
+
assert.throws(() => pathExists(p), expectedError);
|
|
80
|
+
});
|
|
81
|
+
}); // pathExists
|
|
82
|
+
|
|
83
|
+
describe('locatePackageBase', function () {
|
|
84
|
+
let filepath;
|
|
85
|
+
|
|
86
|
+
beforeEach(function () {
|
|
87
|
+
filepath = '/path/to/package/lib/file.js';
|
|
88
|
+
sinon.stub(fs, 'statSync');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('covers unstubbed result', function () {
|
|
92
|
+
sinon.restore();
|
|
93
|
+
locatePackageBase(__filename);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('locates the package base', function () {
|
|
97
|
+
fs.statSync
|
|
98
|
+
.onCall(0).throws(noentError)
|
|
99
|
+
.onCall(1).returns()
|
|
100
|
+
;
|
|
101
|
+
const result = locatePackageBase(filepath);
|
|
102
|
+
assert.strictEqual(result, '/path/to/package/');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('cannot locate the package base', function () {
|
|
106
|
+
fs.statSync.throws(noentError);
|
|
107
|
+
assert.throws(() => locatePackageBase(filepath), FileScopeError);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('propagates unknown error', function () {
|
|
111
|
+
const expectedException = new Error('oh no');
|
|
112
|
+
fs.statSync
|
|
113
|
+
.onCall(0).throws(noentError)
|
|
114
|
+
.onCall(1).throws(expectedException)
|
|
115
|
+
;
|
|
116
|
+
assert.throws(() => locatePackageBase(filepath), expectedException);
|
|
117
|
+
});
|
|
118
|
+
}); // locatePackageBase
|
|
119
|
+
|
|
120
|
+
describe('defaultOptions', function () {
|
|
121
|
+
let packageBase, expected;
|
|
122
|
+
beforeEach(function () {
|
|
123
|
+
packageBase = '/path/to/package';
|
|
124
|
+
sinon.stub(fs, 'statSync').returns();
|
|
125
|
+
expected = {
|
|
126
|
+
includePath: false,
|
|
127
|
+
includePackage: false,
|
|
128
|
+
includeVersion: false,
|
|
129
|
+
leftTrim: 0,
|
|
130
|
+
_errorEncountered: false,
|
|
131
|
+
errorPrefix: '?',
|
|
132
|
+
delimiter: ':',
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
it('covers no path', function () {
|
|
136
|
+
const options = defaultOptions();
|
|
137
|
+
assert.deepStrictEqual(options, expected);
|
|
138
|
+
});
|
|
139
|
+
it('covers default', function () {
|
|
140
|
+
expected.includePath = true;
|
|
141
|
+
fs.statSync.throws(noentError);
|
|
142
|
+
const options = defaultOptions(packageBase);
|
|
143
|
+
assert.deepStrictEqual(options, expected);
|
|
144
|
+
});
|
|
145
|
+
it('covers lib package', function () {
|
|
146
|
+
expected.includePath = true;
|
|
147
|
+
expected.includePackage = true;
|
|
148
|
+
expected.includeVersion = true;
|
|
149
|
+
expected.leftTrim = 4;
|
|
150
|
+
const options = defaultOptions(packageBase);
|
|
151
|
+
assert.deepStrictEqual(options, expected);
|
|
152
|
+
});
|
|
153
|
+
it('covers src package', function () {
|
|
154
|
+
expected.includePath = true;
|
|
155
|
+
expected.leftTrim = 4;
|
|
156
|
+
fs.statSync.onCall(0).throws(noentError);
|
|
157
|
+
const options = defaultOptions(packageBase);
|
|
158
|
+
assert.deepStrictEqual(options, expected);
|
|
159
|
+
});
|
|
160
|
+
it('covers error', function () {
|
|
161
|
+
const expectedError = new Error('oh no');
|
|
162
|
+
fs.statSync.throws(expectedError);
|
|
163
|
+
expected._errorEncountered = true;
|
|
164
|
+
const options = defaultOptions(packageBase);
|
|
165
|
+
assert.deepStrictEqual(options, expected);
|
|
166
|
+
});
|
|
167
|
+
}); // defaultOptions
|
|
168
|
+
|
|
169
|
+
describe('fileScope', function () {
|
|
170
|
+
let filepath, options, method;
|
|
171
|
+
beforeEach(function () {
|
|
172
|
+
filepath = '/path/to/package/lib/deep/file.js';
|
|
173
|
+
sinon.stub(fs, 'statSync')
|
|
174
|
+
.onCall(0).throws(noentError) // deep
|
|
175
|
+
.onCall(1).throws(noentError) // lib
|
|
176
|
+
.onCall(2).returns() // packageBase
|
|
177
|
+
;
|
|
178
|
+
sinon.stub(fs, 'readFileSync').returns(`{
|
|
179
|
+
"name": "@example/package",
|
|
180
|
+
"version": "3.1.4"
|
|
181
|
+
}`);
|
|
182
|
+
options = {
|
|
183
|
+
includePath: true,
|
|
184
|
+
includePackage: false,
|
|
185
|
+
includeVersion: false,
|
|
186
|
+
};
|
|
187
|
+
method = 'method';
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('defaults', function () {
|
|
191
|
+
filepath = '/path/to/package/code/module/file.js';
|
|
192
|
+
fs.statSync
|
|
193
|
+
.onCall(3).throws(noentError) // no lib
|
|
194
|
+
.onCall(4).throws(noentError) // no src
|
|
195
|
+
;
|
|
196
|
+
const result = fileScope(filepath)(method);
|
|
197
|
+
assert.strictEqual(result, 'code/module/file:method');
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('everything', function () {
|
|
201
|
+
options.includePath = true;
|
|
202
|
+
options.includePackage = true;
|
|
203
|
+
options.includeVersion = true;
|
|
204
|
+
const result = fileScope(filepath, options)(method);
|
|
205
|
+
assert.strictEqual(result, '@example/package@3.1.4:deep/file:method');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('minimal', function () {
|
|
209
|
+
options.includePath = false;
|
|
210
|
+
options.includePackage = false;
|
|
211
|
+
options.includeVersion = false;
|
|
212
|
+
const result = fileScope(filepath, options)(method);
|
|
213
|
+
assert.strictEqual(result, 'file:method');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('package only', function () {
|
|
217
|
+
options.includePath = false;
|
|
218
|
+
options.includePackage = true;
|
|
219
|
+
options.includeVersion = false;
|
|
220
|
+
const result = fileScope(filepath, options)(method);
|
|
221
|
+
assert.strictEqual(result, '@example/package:file:method');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('covers no package root', function () {
|
|
225
|
+
options.includePackage = true;
|
|
226
|
+
fs.statSync.restore()
|
|
227
|
+
sinon.stub(fs, 'statSync').throws(noentError);
|
|
228
|
+
fs.readFileSync.throws(noentError);
|
|
229
|
+
const result = fileScope(filepath, options)(method);
|
|
230
|
+
assert.strictEqual(result, '?:(unknown):file:method');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('covers exception while finding package root', function () {
|
|
234
|
+
const expectedException = new Error('oh no');
|
|
235
|
+
options.includePackage = true;
|
|
236
|
+
fs.statSync.restore()
|
|
237
|
+
sinon.stub(fs, 'statSync').throws(expectedException);
|
|
238
|
+
fs.readFileSync.throws(noentError);
|
|
239
|
+
const result = fileScope(filepath, options)(method);
|
|
240
|
+
assert.strictEqual(result, '?:(unknown):file:method');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('handles index.js', function () {
|
|
244
|
+
filepath = '/path/to/package/src/deep/index.js';
|
|
245
|
+
fs.statSync
|
|
246
|
+
.onCall(3).throws(noentError) // no lib
|
|
247
|
+
.onCall(4).returns() // src
|
|
248
|
+
;
|
|
249
|
+
const result = fileScope(filepath)(method);
|
|
250
|
+
assert.strictEqual(result, 'deep:method');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('handles index.js when including path', function () {
|
|
254
|
+
filepath = '/path/to/package/code/folder/deep/index.js';
|
|
255
|
+
fs.statSync.restore();
|
|
256
|
+
sinon.stub(fs, 'statSync')
|
|
257
|
+
.throws(noentError)
|
|
258
|
+
.onCall(3).returns() // packageBase found
|
|
259
|
+
// no lib
|
|
260
|
+
// no src
|
|
261
|
+
;
|
|
262
|
+
const result = fileScope(filepath)(method);
|
|
263
|
+
assert.strictEqual(result, 'code/folder/deep:method');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('handles index.js when not including path', function () {
|
|
267
|
+
filepath = '/path/to/package/code/folder/deep/index.ts';
|
|
268
|
+
fs.statSync.restore();
|
|
269
|
+
sinon.stub(fs, 'statSync')
|
|
270
|
+
.throws(noentError)
|
|
271
|
+
.onCall(3).returns() // packageBase found
|
|
272
|
+
// no lib
|
|
273
|
+
// no src
|
|
274
|
+
;
|
|
275
|
+
const result = fileScope(filepath, { includePath: false })(method);
|
|
276
|
+
assert.strictEqual(result, 'deep:method');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
}); // fileScope
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
}); // File Scope
|