comment-variables 0.4.1 → 0.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.
- package/README.md +4 -4
- package/comments.config.js +32 -20
- package/library/_commons/constants/bases.js +22 -8
- package/library/_commons/constants/regexes.js +13 -0
- package/library/_commons/rules/compress.js +2 -2
- package/library/_commons/rules/resolve.js +2 -4
- package/library/_commons/schemas/config.js +2 -2
- package/library/_commons/utilities/find-all-imports.js +102 -59
- package/library/_commons/utilities/flatten-config-data.js +72 -34
- package/library/_commons/utilities/helpers.js +19 -1
- package/library/_commons/utilities/resolve-config.js +63 -20
- package/library/index.js +32 -16
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -18,15 +18,15 @@ Interacts with your `comments.config.js` default exported object to print all th
|
|
|
18
18
|
comment-variables compress
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Scans your line and block comments for values defined in your `comments.config.js` (like `"This is a comment"`) to turn them into their corresponding `$COMMENT#*` tokens defined in your `comments.config.js`. (`
|
|
21
|
+
Scans your line and block comments for string values defined in your `comments.config.js` (like `"This is a comment"`) to turn them into their corresponding `$COMMENT#*` tokens defined in your `comments.config.js`. (`This is a comment.` => `$COMMENT#COMMENT`)
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
comment-variables resolve
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Scans your line and block comments for `$COMMENT#*` tokens (like
|
|
27
|
+
Scans your line and block comments for `$COMMENT#*` tokens (like `$COMMENT#COMMENT`) to turn them into their corresponding string values defined in your `comments.config.js`. (`$COMMENT#COMMENT` => `This is a comment.`)
|
|
28
28
|
|
|
29
|
-
_The `compress` and `resolve` commands
|
|
29
|
+
_The `compress` and `resolve` commands make each other entirely reversible._
|
|
30
30
|
|
|
31
31
|
## Flags
|
|
32
32
|
|
|
@@ -48,7 +48,7 @@ By default, `comment-variables` excludes your config file and all the (JavaScrip
|
|
|
48
48
|
comment-variables --my-ignores-only
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
By default, `comment-variables` includes a preset list of ignored folders ("node_modules"
|
|
51
|
+
By default, `comment-variables` includes a preset list of ignored folders (`"node_modules"`, `".next"`, `".react-router"`...). This flag cancels this mechanism so that you can have full control over your ignored files and folders.
|
|
52
52
|
|
|
53
53
|
_All three flags can be composed together, and with any of the three commands:_
|
|
54
54
|
|
package/comments.config.js
CHANGED
|
@@ -31,11 +31,14 @@ const data = {
|
|
|
31
31
|
"The flow that compresses actual comments into $COMMENT#* placeholders.", // $COMMENT#JSDOC#DEFINITIONS#COMPRESSCOMMENTSFLOW
|
|
32
32
|
findAllImports:
|
|
33
33
|
"Finds all import paths recursively related to a given file path.", // $COMMENT#JSDOC#DEFINITIONS#FINDALLIMPORTS
|
|
34
|
-
processImport:
|
|
34
|
+
processImport:
|
|
35
|
+
"Processes recursively and resolves a single import path. (Unlike `findAllImports`, here `currentDir`, `cwd`, `visitedSet`, `depth`, and `maxDepth` aren't options because they are mandatory and not pre-parameterized.)", // $COMMENT#JSDOC#DEFINITIONS#PROCESSIMPORT
|
|
35
36
|
flattenConfigData:
|
|
36
37
|
"Flattens the config's data property into a one-dimensional object of $COMMENT-*-like keys and string values.", // $COMMENT#JSDOC#DEFINITIONS#FLATTENCONFIGDATA
|
|
37
38
|
resolveConfig:
|
|
38
39
|
"Verifies, validates and resolves the config path to retrieve the config's data and ignores.", // $COMMENT#JSDOC#DEFINITIONS#RESOLVECONFIG
|
|
40
|
+
logError:
|
|
41
|
+
'Logs an error to the console depending on its type. (`"error"` or `"warning"`.)', // $COMMENT#JSDOC#DEFINITIONS#LOGERROR
|
|
39
42
|
}),
|
|
40
43
|
params: Object.freeze({
|
|
41
44
|
string: "The string.", // $COMMENT#JSDOC#PARAMS#STRING
|
|
@@ -51,24 +54,33 @@ const data = {
|
|
|
51
54
|
"Either the flattened config data or the reversed flattened config data, since they share the same structure.", // $COMMENT#JSDOC#PARAMS#EITHERFLATTENEDCONFIGDATA
|
|
52
55
|
filePath:
|
|
53
56
|
"The absolute path of the file whose imports are being recursively found, such as that of a project's `comments.config.js` file.", // $COMMENT#JSDOC#PARAMS#FILEPATH
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
cwdOption:
|
|
58
|
+
"The current working directory, set as `process.cwd()` by default.", // $COMMENT#JSDOC#PARAMS#CWDOPTION
|
|
59
|
+
visitedSetOption:
|
|
60
|
+
"The set of strings tracking the import paths that have already been visited, instantiated as a `new Set()` by default.", // $COMMENT#JSDOC#PARAMS#VISITEDSETOPTION
|
|
61
|
+
depthOption:
|
|
62
|
+
"The current depth of the recursion, instantiated at `0` by default.", // $COMMENT#JSDOC#PARAMS#DEPTHOPTION
|
|
63
|
+
maxDepthOption:
|
|
64
|
+
"The maximum depth allowed for the recursion, instantiated at `100` by default.", // $COMMENT#JSDOC#PARAMS#MAXDEPTHOPTION
|
|
61
65
|
importPath: "The import path currently being addressed.", // $COMMENT#JSDOC#PARAMS#IMPORTPATH
|
|
62
|
-
|
|
63
|
-
"The directory containing the import path currently being addressed.", // $COMMENT#JSDOC#PARAMS#
|
|
66
|
+
currentDirSetting:
|
|
67
|
+
"The directory containing the import path currently being addressed.", // $COMMENT#JSDOC#PARAMS#CURRENTDIRSETTING
|
|
68
|
+
cwdSetting: "The current working directory.", // $COMMENT#JSDOC#PARAMS#CWDSETTING
|
|
69
|
+
visitedSetSetting:
|
|
70
|
+
"The set of strings tracking the import paths that have already been visited.", // $COMMENT#JSDOC#PARAMS#VISITEDSETSETTING
|
|
71
|
+
depthSetting: "The current depth of the recursion.", // $COMMENT#JSDOC#PARAMS#DEPTHSETTING
|
|
72
|
+
maxDepthSetting: "The maximum depth allowed for the recursion.", // $COMMENT#JSDOC#PARAMS#MAXDEPTHSETTING
|
|
64
73
|
configData:
|
|
65
|
-
"The config's data property. (Values are typed `
|
|
66
|
-
|
|
67
|
-
"The map housing the flattened keys with their values and sources through recursion, instantiated as a `new Map()`.", // $COMMENT#JSDOC#PARAMS#
|
|
68
|
-
|
|
69
|
-
"The list of keys that are parent to the key at hand given the recursive nature of the config's data's data structure, instantiated as an empty array of strings.", // $COMMENT#JSDOC#PARAMS#
|
|
74
|
+
"The config's data property. (Values are typed `unknown` given the limitations in typing recursive values in JSDoc.)", // $COMMENT#JSDOC#PARAMS#CONFIGDATA
|
|
75
|
+
configDataMapOption:
|
|
76
|
+
"The map housing the flattened keys with their values and sources through recursion, instantiated as a `new Map()`.", // $COMMENT#JSDOC#PARAMS#CONFIGDATAMAPOPTION
|
|
77
|
+
parentKeysOption:
|
|
78
|
+
"The list of keys that are parent to the key at hand given the recursive nature of the config's data's data structure, instantiated as an empty array of strings (`[]`).", // $COMMENT#JSDOC#PARAMS#PARENTKEYSOPTION
|
|
70
79
|
configPath:
|
|
71
|
-
|
|
80
|
+
'The path of the config from `comments.config.js`, or from a config passed via the `--config` flag in the CLI, or from one passed via `"commentVariables.config": true` in `.vscode/settings.json` for the VS Code Extension.', // $COMMENT#JSDOC#PARAMS#CONFIGPATH
|
|
81
|
+
options: "The additional options as follows:", // $COMMENT#JSDOC#PARAMS#OPTIONS
|
|
82
|
+
settings: "The required settings as follows:", // $COMMENT#JSDOC#PARAMS#SETTINGS
|
|
83
|
+
error: "The error object being handle for the logging.", // $COMMENT#JSDOC#PARAMS#ERROR
|
|
72
84
|
}),
|
|
73
85
|
returns: Object.freeze({
|
|
74
86
|
exitDueToFailure:
|
|
@@ -78,13 +90,13 @@ const data = {
|
|
|
78
90
|
makeRuleCompress:
|
|
79
91
|
"The compress rule based on the reversed flattened config data.", // $COMMENT#JSDOC#RETURNS#MAKERULECOMPRESS
|
|
80
92
|
findAllImports:
|
|
81
|
-
"The complete set of strings of import paths recursively related to the given file path
|
|
93
|
+
"The complete set of strings of import paths recursively related to the given file path in a success object (`success: true`). Errors are bubbled up during failures in a failure object (`success: false`).", // $COMMENT#JSDOC#RETURNS#FINDALLIMPORTS
|
|
82
94
|
processImport:
|
|
83
|
-
"`true` to
|
|
95
|
+
"`true` to continue to the next operation, `false` to stop the whole `findAllImports` process.", // $COMMENT#JSDOC#RETURNS#PROCESSIMPORT
|
|
84
96
|
flattenConfigData:
|
|
85
|
-
"Both the flattened config data and its reversed version to ensure the strict reversibility of the `resolve` and `compress` commands.", // $COMMENT#JSDOC#RETURNS#FLATTENCONFIGDATA
|
|
97
|
+
"Both the flattened config data and its reversed version to ensure the strict reversibility of the `resolve` and `compress` commands in a success object (`success: true`). Errors are bubbled up during failures so they can be reused differently on the CLI and the VS Code Extension in a failure object (`success: false`).", // $COMMENT#JSDOC#RETURNS#FLATTENCONFIGDATA
|
|
86
98
|
resolveConfig:
|
|
87
|
-
"The flattened config data, the reverse flattened config data, the verified config path, the raw passed ignores, and the original config.", // $COMMENT#JSDOC#RETURNS#RESOLVECONFIG
|
|
99
|
+
"The flattened config data, the reverse flattened config data, the verified config path, the raw passed ignores, and the original config. Errors are returned during failures so they can be reused differently on the CLI and the VS Code Extension.", // $COMMENT#JSDOC#RETURNS#RESOLVECONFIG
|
|
88
100
|
}),
|
|
89
101
|
}),
|
|
90
102
|
};
|
|
@@ -18,10 +18,10 @@ export const hasPackageJson = fs.existsSync(path.join(cwd, "package.json"));
|
|
|
18
18
|
// to prevent irreversible changes
|
|
19
19
|
export const hasGitFolder = fs.existsSync(path.join(cwd, ".git"));
|
|
20
20
|
|
|
21
|
-
// comments.config.js
|
|
21
|
+
// comments.config.js // comment-variables-resolve-config
|
|
22
22
|
export const defaultConfigFileName = "comments.config.js";
|
|
23
23
|
|
|
24
|
-
// flags
|
|
24
|
+
// flags // comment-variables-resolve-config
|
|
25
25
|
export const configFlag = "--config";
|
|
26
26
|
export const lintConfigImportsFlag = "--lint-config-imports";
|
|
27
27
|
export const myIgnoresOnlyFlag = "--my-ignores-only";
|
|
@@ -29,11 +29,12 @@ export const myIgnoresOnlyFlag = "--my-ignores-only";
|
|
|
29
29
|
// ESLint ignores
|
|
30
30
|
export const knownIgnores = [
|
|
31
31
|
"node_modules",
|
|
32
|
+
"dist",
|
|
33
|
+
"out",
|
|
32
34
|
".next",
|
|
33
35
|
".react-router",
|
|
34
36
|
".parcel-cache",
|
|
35
37
|
".react-router-parcel",
|
|
36
|
-
"dist",
|
|
37
38
|
];
|
|
38
39
|
|
|
39
40
|
// ESLint file globs
|
|
@@ -70,8 +71,21 @@ export const typeScriptAndJSXCompatible = {
|
|
|
70
71
|
// messageId
|
|
71
72
|
export const placeholderMessageId = "placeholderMessageId";
|
|
72
73
|
|
|
73
|
-
//
|
|
74
|
-
export const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
// placeholder prefix // comment-variables-resolve-config
|
|
75
|
+
export const $COMMENT = "$COMMENT";
|
|
76
|
+
|
|
77
|
+
// success objects // comment-variables-resolve-config
|
|
78
|
+
export const successFalse = Object.freeze({
|
|
79
|
+
success: false,
|
|
80
|
+
});
|
|
81
|
+
export const successTrue = Object.freeze({
|
|
82
|
+
success: true,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// error objects // comment-variables-resolve-config
|
|
86
|
+
export const typeError = Object.freeze({
|
|
87
|
+
type: "error",
|
|
88
|
+
});
|
|
89
|
+
export const typeWarning = Object.freeze({
|
|
90
|
+
type: "warning",
|
|
91
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { $COMMENT } from "./bases.js";
|
|
2
|
+
|
|
3
|
+
import { escapeRegex } from "../utilities/helpers.js";
|
|
4
|
+
|
|
5
|
+
// comment-variables-resolve-config
|
|
6
|
+
export const configKeyRegex = /^[\p{Ll}\p{Lu}\p{Lo}\p{Pd}\p{Pc}\p{N}\s]+$/u;
|
|
7
|
+
// comment-variables-resolve-config
|
|
8
|
+
export const flattenedConfigKeyRegex = /^[\p{Lu}\p{Lo}\p{Pd}\p{Pc}\p{N}#]+$/u; // same as configKeyRegex but without lowercase letters (\p{Ll}), without whitespaces (\s which are replaced by underscores) and with the '#' character (that links each subkey together)
|
|
9
|
+
// comment-variables-resolve-config
|
|
10
|
+
export const flattenedConfigPlaceholderRegex = new RegExp(
|
|
11
|
+
`${escapeRegex($COMMENT)}#([\\p{Lu}\\p{Lo}\\p{Pd}\\p{Pc}\\p{N}#_]+)`,
|
|
12
|
+
"gu"
|
|
13
|
+
); // same as flattenedConfigKeyRegex but taking the prefix $COMMENT and its # into consideration, removing ^ and $ in the capture group, and using _ as replacement for whitesplaces, globally
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { placeholderMessageId } from "../constants/bases.js";
|
|
1
|
+
import { placeholderMessageId, $COMMENT } from "../constants/bases.js";
|
|
2
2
|
|
|
3
3
|
import { escapeRegex } from "..//utilities/helpers.js";
|
|
4
4
|
|
|
@@ -49,7 +49,7 @@ const makeRule = (reversedFlattenedConfigData) => {
|
|
|
49
49
|
|
|
50
50
|
fixedText = fixedText.replace(pattern, () => {
|
|
51
51
|
modified = true;
|
|
52
|
-
return `$COMMENT#${commentKey}`;
|
|
52
|
+
return `${$COMMENT}#${commentKey}`;
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
flattenedConfigPlaceholderRegex,
|
|
4
|
-
} from "../constants/bases.js";
|
|
1
|
+
import { placeholderMessageId } from "../constants/bases.js";
|
|
2
|
+
import { flattenedConfigPlaceholderRegex } from "../constants/regexes.js";
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* The utility that creates the resolve rule based on the flattened config data, used to transform $COMMENT#* placeholders into actual comments.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
import { configKeyRegex } from "../constants/
|
|
3
|
+
import { configKeyRegex } from "../constants/regexes.js";
|
|
4
4
|
|
|
5
5
|
export const ConfigDataSchema = z
|
|
6
6
|
.lazy(() =>
|
|
7
7
|
z.record(
|
|
8
|
-
z.
|
|
8
|
+
z.unknown().superRefine((val, ctx) => {
|
|
9
9
|
if (typeof val === "string") {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
@@ -4,93 +4,141 @@ import path from "path";
|
|
|
4
4
|
import { resolveImportingPath } from "resolve-importing-path";
|
|
5
5
|
import { getSourceCodeFromFilePath } from "get-sourcecode-from-file-path";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { successFalse, successTrue, typeWarning } from "../constants/bases.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* @typedef {{
|
|
11
|
+
* success: false;
|
|
12
|
+
* errors: Array<{ message: string; type: "warning";}>;
|
|
13
|
+
* } | {
|
|
14
|
+
* success: true;
|
|
15
|
+
* visitedSet: Set<string>;
|
|
16
|
+
* }} FindAllImportsResults
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// IMPORTANT. findAllImports needs to be able to take a callback function that it can play at every recursion to find the corresponding value for go-to-definitions. But that's on the roadmap, not in the first release. The first implementation of this pinpoint go-to-definition mechanism will be made by analyzing each path obtained rather than by doing so as the paths are being obtained.
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Processes recursively and resolves a single import path. (Unlike `findAllImports`, here `currentDir`, `cwd`, `visitedSet`, `depth`, and `maxDepth` aren't options because they are mandatory and not pre-parameterized.)
|
|
11
23
|
* @param {string} importPath The import path currently being addressed.
|
|
12
|
-
* @param {
|
|
13
|
-
* @param {string}
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
16
|
-
* @param {number}
|
|
17
|
-
* @
|
|
24
|
+
* @param {Object} settings The required settings as follows:
|
|
25
|
+
* @param {string} settings.currentDir The directory containing the import path currently being addressed.
|
|
26
|
+
* @param {string} settings.cwd The current working directory.
|
|
27
|
+
* @param {Set<string>} settings.visitedSet The set of strings tracking the import paths that have already been visited.
|
|
28
|
+
* @param {number} settings.depth The current depth of the recursion.
|
|
29
|
+
* @param {number} settings.maxDepth The maximum depth allowed for the recursion.
|
|
30
|
+
* @returns `true` to continue to the next operation, `false` to stop the whole `findAllImports` process. //
|
|
18
31
|
*/
|
|
19
32
|
const processImport = (
|
|
20
33
|
importPath,
|
|
21
|
-
currentDir,
|
|
22
|
-
cwd,
|
|
23
|
-
visitedSet,
|
|
24
|
-
depth,
|
|
25
|
-
maxDepth
|
|
34
|
+
{ currentDir, cwd, visitedSet, depth, maxDepth }
|
|
26
35
|
) => {
|
|
36
|
+
// Resolves the provided import path.
|
|
27
37
|
const resolvedPath = resolveImportingPath(currentDir, importPath, cwd);
|
|
28
|
-
|
|
38
|
+
// Returns true early to skip processing on unresolved paths.
|
|
39
|
+
if (!resolvedPath) return { ...successTrue, visitedSet };
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
// Establishes the options for the next round of findAllImports.
|
|
42
|
+
const findAllImportsOptions = {
|
|
32
43
|
cwd,
|
|
33
44
|
visitedSet,
|
|
34
|
-
depth + 1,
|
|
35
|
-
maxDepth
|
|
45
|
+
depth: depth + 1,
|
|
46
|
+
maxDepth,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Runs findAllImports on the imported path resolved, thus recursively.
|
|
50
|
+
const findAllImportsResults = /** @type {FindAllImportsResults} */ (
|
|
51
|
+
findAllImports(resolvedPath, findAllImportsOptions)
|
|
36
52
|
);
|
|
37
|
-
|
|
53
|
+
// Returns true if the round of findAllImports succeeded, false if it failed.
|
|
54
|
+
return findAllImportsResults;
|
|
38
55
|
};
|
|
39
56
|
|
|
40
57
|
/**
|
|
41
58
|
* Finds all import paths recursively related to a given file path.
|
|
42
59
|
* @param {string} filePath The absolute path of the file whose imports are being recursively found, such as that of a project's `comments.config.js` file.
|
|
43
|
-
* @param {
|
|
44
|
-
* @param {
|
|
45
|
-
* @param {
|
|
46
|
-
* @param {number}
|
|
47
|
-
* @
|
|
60
|
+
* @param {Object} options The additional options as follows:
|
|
61
|
+
* @param {string} [options.cwd] The current working directory, set as `process.cwd()` by default.
|
|
62
|
+
* @param {Set<string>} [options.visitedSet] The current working directory, set as `process.cwd()` by default.
|
|
63
|
+
* @param {number} [options.depth] The current depth of the recursion, instantiated at `0` by default.
|
|
64
|
+
* @param {number} [options.maxDepth] The maximum depth allowed for the recursion, instantiated at `100` by default.
|
|
65
|
+
* @returns The complete set of strings of import paths recursively related to the given file path in a success object (`success: true`). Errors are bubbled up during failures in a failure object (`success: false`).
|
|
48
66
|
*/
|
|
49
67
|
export const findAllImports = (
|
|
50
68
|
filePath,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
{
|
|
70
|
+
cwd = process.cwd(),
|
|
71
|
+
visitedSet = new Set(),
|
|
72
|
+
depth = 0,
|
|
73
|
+
maxDepth = 100,
|
|
74
|
+
} = {}
|
|
55
75
|
) => {
|
|
56
76
|
// Fails early if max depth is recursively reached.
|
|
57
77
|
if (depth > maxDepth) {
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
return {
|
|
79
|
+
...successFalse,
|
|
80
|
+
errors: [
|
|
81
|
+
{
|
|
82
|
+
...typeWarning,
|
|
83
|
+
message: `WARNING. Max depth ${maxDepth} reached at ${filePath}.`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
60
87
|
}
|
|
61
88
|
// Fails early if no file is found.
|
|
62
89
|
if (!fs.existsSync(filePath)) {
|
|
63
|
-
|
|
64
|
-
|
|
90
|
+
return {
|
|
91
|
+
...successFalse,
|
|
92
|
+
errors: [
|
|
93
|
+
{
|
|
94
|
+
...typeWarning,
|
|
95
|
+
message: `WARNING. File not found at ${filePath}.`,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Returns the existing set directly if a path has already been visited.
|
|
101
|
+
if (visitedSet.has(filePath)) {
|
|
102
|
+
return { ...successTrue, visitedSet };
|
|
65
103
|
}
|
|
66
104
|
|
|
67
105
|
// Updates the visited set.
|
|
68
|
-
if (visitedSet.has(filePath)) return visitedSet;
|
|
69
106
|
visitedSet.add(filePath);
|
|
70
107
|
|
|
71
108
|
// Parses the file's source code AST.
|
|
72
109
|
const sourceCode = getSourceCodeFromFilePath(filePath);
|
|
110
|
+
// Fails early there is no AST.
|
|
73
111
|
if (!sourceCode?.ast) {
|
|
74
|
-
|
|
75
|
-
|
|
112
|
+
return {
|
|
113
|
+
...successFalse,
|
|
114
|
+
errors: [
|
|
115
|
+
{
|
|
116
|
+
...typeWarning,
|
|
117
|
+
message: `WARNING. Failed to parse AST for ${filePath}.`,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
};
|
|
76
121
|
}
|
|
77
122
|
|
|
123
|
+
// Makes the joint settings for the conditional calls of processImport.
|
|
124
|
+
const processImportSettings = {
|
|
125
|
+
currentDir: path.dirname(filePath),
|
|
126
|
+
cwd,
|
|
127
|
+
visitedSet,
|
|
128
|
+
depth,
|
|
129
|
+
maxDepth,
|
|
130
|
+
};
|
|
131
|
+
|
|
78
132
|
// Processes all imports.
|
|
79
|
-
const currentDir = path.dirname(filePath);
|
|
80
133
|
for (const node of sourceCode.ast.body) {
|
|
81
134
|
// ES Modules (import x from 'y')
|
|
82
135
|
if (node.type === "ImportDeclaration") {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
depth,
|
|
90
|
-
maxDepth
|
|
91
|
-
)
|
|
92
|
-
) {
|
|
93
|
-
return null;
|
|
136
|
+
const processImportResults = processImport(
|
|
137
|
+
node.source.value,
|
|
138
|
+
processImportSettings
|
|
139
|
+
);
|
|
140
|
+
if (!processImportResults) {
|
|
141
|
+
return processImportResults;
|
|
94
142
|
}
|
|
95
143
|
}
|
|
96
144
|
|
|
@@ -101,20 +149,15 @@ export const findAllImports = (
|
|
|
101
149
|
node.expression.callee.name === "require" &&
|
|
102
150
|
node.expression.arguments[0]?.type === "Literal"
|
|
103
151
|
) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
depth,
|
|
111
|
-
maxDepth
|
|
112
|
-
)
|
|
113
|
-
) {
|
|
114
|
-
return null;
|
|
152
|
+
const processImportResults = processImport(
|
|
153
|
+
node.expression.arguments[0].value,
|
|
154
|
+
processImportSettings
|
|
155
|
+
);
|
|
156
|
+
if (!processImportResults) {
|
|
157
|
+
return processImportResults;
|
|
115
158
|
}
|
|
116
159
|
}
|
|
117
160
|
}
|
|
118
161
|
|
|
119
|
-
return visitedSet;
|
|
162
|
+
return { ...successTrue, visitedSet };
|
|
120
163
|
};
|
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { successFalse, successTrue, typeError } from "../constants/bases.js";
|
|
2
|
+
import { flattenedConfigKeyRegex } from "../constants/regexes.js";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Record<string, unknown>} ConfigData
|
|
6
|
+
*
|
|
7
|
+
* @typedef {{
|
|
8
|
+
* success: false;
|
|
9
|
+
* errors: Array<{ type: "error" | "warning"; message: string }>;
|
|
10
|
+
* } | {
|
|
11
|
+
* success: true;
|
|
12
|
+
* flattenedConfigData: Record<string, string>;
|
|
13
|
+
* reversedFlattenedConfigData: Record<string, string>;
|
|
14
|
+
* }} FlattenConfigDataResults
|
|
15
|
+
*/
|
|
4
16
|
|
|
5
17
|
/**
|
|
6
18
|
* Flattens the config's data property into a one-dimensional object of $COMMENT-*-like keys and string values.
|
|
7
|
-
* @param {
|
|
8
|
-
* @param {
|
|
9
|
-
* @param {string
|
|
10
|
-
* @
|
|
19
|
+
* @param {ConfigData} configData The config's data property. (Values are typed `unknown` given the limitations in typing recursive values in JSDoc.)
|
|
20
|
+
* @param {Object} [options] The additional options as follows:
|
|
21
|
+
* @param {Map<string, {value: string; source: string}>} [options.configDataMap] The map housing the flattened keys with their values and sources through recursion, instantiated as a `new Map()`.
|
|
22
|
+
* @param {string[]} [options.parentKeys] The list of keys that are parent to the key at hand given the recursive nature of the config's data's data structure, instantiated as an empty array of strings (`[]`).
|
|
23
|
+
* @returns Both the flattened config data and its reversed version to ensure the strict reversibility of the `resolve` and `compress` commands in a success object (`success: true`). Errors are bubbled up during failures so they can be reused differently on the CLI and the VS Code Extension in a failure object (`success: false`).
|
|
11
24
|
*/
|
|
12
25
|
export const flattenConfigData = (
|
|
13
26
|
configData,
|
|
14
|
-
configDataMap = new Map(),
|
|
15
|
-
parentKeys = []
|
|
27
|
+
{ configDataMap = new Map(), parentKeys = [] } = {}
|
|
16
28
|
) => {
|
|
17
29
|
for (const [key, value] of Object.entries(configData)) {
|
|
18
30
|
const newKeys = [...parentKeys, key];
|
|
@@ -24,12 +36,18 @@ export const flattenConfigData = (
|
|
|
24
36
|
|
|
25
37
|
if (typeof value === "string") {
|
|
26
38
|
if (configDataMap.has(normalizedKey)) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
// checks the uniqueness of each normalized key
|
|
40
|
+
return {
|
|
41
|
+
...successFalse,
|
|
42
|
+
errors: [
|
|
43
|
+
{
|
|
44
|
+
...typeError,
|
|
45
|
+
message: `ERROR. The normalized key "${normalizedKey}" has already been assigned. Check between the two following key paths: \n"${
|
|
46
|
+
configDataMap.get(normalizedKey).source
|
|
47
|
+
}" \n"${source}"`,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
33
51
|
}
|
|
34
52
|
|
|
35
53
|
configDataMap.set(normalizedKey, {
|
|
@@ -37,10 +55,13 @@ export const flattenConfigData = (
|
|
|
37
55
|
source,
|
|
38
56
|
});
|
|
39
57
|
} else if (typeof value === "object" && value && !Array.isArray(value)) {
|
|
40
|
-
/** @type {
|
|
41
|
-
const
|
|
58
|
+
const subConfigData = /** @type {ConfigData} */ (value);
|
|
59
|
+
const flattenConfigDataOptions = { configDataMap, parentKeys: newKeys };
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
const flattenConfigDataResults = /** @type {FlattenConfigDataResults} */ (
|
|
62
|
+
flattenConfigData(subConfigData, flattenConfigDataOptions)
|
|
63
|
+
);
|
|
64
|
+
if (!flattenConfigDataResults.success) return flattenConfigDataResults;
|
|
44
65
|
}
|
|
45
66
|
}
|
|
46
67
|
|
|
@@ -63,36 +84,52 @@ export const flattenConfigData = (
|
|
|
63
84
|
const flattenedConfigDataValuesArray = Object.values(flattenedConfigData);
|
|
64
85
|
const flattenedConfigDataValuesSet = new Set(flattenedConfigDataValuesArray);
|
|
65
86
|
|
|
66
|
-
|
|
67
|
-
// checks the reversability of flattenedConfigData
|
|
87
|
+
for (const key of flattenedConfigDataKeysSet) {
|
|
68
88
|
if (flattenedConfigDataValuesSet.has(key)) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
89
|
+
// checks the reversability of flattenedConfigData
|
|
90
|
+
return {
|
|
91
|
+
...successFalse,
|
|
92
|
+
errors: [
|
|
93
|
+
{
|
|
94
|
+
...typeError,
|
|
95
|
+
message: `ERROR. The key "${key}" is and shouldn't be among the values of flattenedConfigData.`,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
73
99
|
}
|
|
74
100
|
if (!flattenedConfigKeyRegex.test(key)) {
|
|
75
101
|
// checks if each key for flattenedConfigData passes the flattenedConfigKeyRegex test
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
return {
|
|
103
|
+
...successFalse,
|
|
104
|
+
errors: [
|
|
105
|
+
{
|
|
106
|
+
...typeError,
|
|
107
|
+
message: `ERROR. Somehow the key "${key}" is not properly formatted. (This is mostly an internal mistake.)`,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
80
111
|
}
|
|
81
|
-
}
|
|
112
|
+
}
|
|
82
113
|
|
|
83
114
|
/** @type {Set<string>} */
|
|
84
115
|
const set = new Set();
|
|
85
116
|
|
|
86
|
-
|
|
117
|
+
for (const value of flattenedConfigDataValuesArray) {
|
|
87
118
|
if (set.has(value)) {
|
|
119
|
+
console.log("errors, duplicate value");
|
|
88
120
|
// checks that no two values are duplicate
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
121
|
+
return {
|
|
122
|
+
...successFalse,
|
|
123
|
+
errors: [
|
|
124
|
+
{
|
|
125
|
+
...typeError,
|
|
126
|
+
message: `ERROR. The value "${value}" is already assigned to an existing key.`,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
};
|
|
93
130
|
}
|
|
94
131
|
set.add(value);
|
|
95
|
-
}
|
|
132
|
+
}
|
|
96
133
|
|
|
97
134
|
// Also including the reversed flattened config data.
|
|
98
135
|
|
|
@@ -101,6 +138,7 @@ export const flattenConfigData = (
|
|
|
101
138
|
);
|
|
102
139
|
|
|
103
140
|
return {
|
|
141
|
+
...successTrue,
|
|
104
142
|
flattenedConfigData,
|
|
105
143
|
reversedFlattenedConfigData,
|
|
106
144
|
};
|
|
@@ -6,7 +6,25 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const exitDueToFailure = () => process.exit(1);
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Logs an error to the console depending on its type. (`"error"` or `"warning"`.)
|
|
11
|
+
* @param {{type: "error" | "warning"; message: string}} error The error object being handle for the logging.
|
|
12
|
+
*/
|
|
13
|
+
export const logError = (error) => {
|
|
14
|
+
switch (error.type) {
|
|
15
|
+
case "error":
|
|
16
|
+
console.error(error.message);
|
|
17
|
+
break;
|
|
18
|
+
case "warning":
|
|
19
|
+
console.warn(error.message);
|
|
20
|
+
break;
|
|
21
|
+
default:
|
|
22
|
+
console.error("ERROR. Error type unrecognized.");
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/* escapeRegex */ // comment-variables-resolve-config
|
|
10
28
|
|
|
11
29
|
/**
|
|
12
30
|
* Escapes all regex characters with a `"\"` in a string to prepare it for use in a regex.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import url from "url";
|
|
3
|
+
|
|
4
|
+
import { successFalse, typeError } from "../constants/bases.js";
|
|
3
5
|
|
|
4
6
|
import { flattenConfigData } from "./flatten-config-data.js";
|
|
5
7
|
|
|
@@ -7,39 +9,64 @@ import { ConfigDataSchema, ConfigIgnoresSchema } from "../schemas/config.js";
|
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Verifies, validates and resolves the config path to retrieve the config's data and ignores.
|
|
10
|
-
* @param {string} configPath The path of the config
|
|
11
|
-
* @returns The flattened config data, the reverse flattened config data, the verified config path, the raw passed ignores, and the original config.
|
|
12
|
+
* @param {string} configPath The path of the config from `comments.config.js`, or from a config passed via the `--config` flag in the CLI, or from one passed via `"commentVariables.config": true` in `.vscode/settings.json` for the VS Code Extension.
|
|
13
|
+
* @returns The flattened config data, the reverse flattened config data, the verified config path, the raw passed ignores, and the original config. Errors are returned during failures so they can be reused differently on the CLI and the VS Code Extension.
|
|
12
14
|
*/
|
|
13
15
|
export async function resolveConfig(configPath) {
|
|
14
16
|
// Step 1: Checks if config file exists
|
|
15
17
|
|
|
16
|
-
if (!existsSync(configPath)) {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
if (!fs.existsSync(configPath)) {
|
|
19
|
+
return {
|
|
20
|
+
...successFalse,
|
|
21
|
+
errors: [
|
|
22
|
+
{
|
|
23
|
+
...typeError,
|
|
24
|
+
message: "ERROR. No config file found.",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
// Step 2: Imports the config dynamically
|
|
22
31
|
|
|
23
|
-
const configModule =
|
|
24
|
-
|
|
32
|
+
const configModule = /** @type {unknown} */ (
|
|
33
|
+
await import(url.pathToFileURL(configPath))
|
|
34
|
+
);
|
|
35
|
+
const config = /** @type {unknown} */ (configModule.default);
|
|
25
36
|
|
|
26
37
|
// Step 3: Validates config object
|
|
27
38
|
|
|
28
39
|
// validates config
|
|
29
40
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
return {
|
|
42
|
+
...successFalse,
|
|
43
|
+
errors: [
|
|
44
|
+
{
|
|
45
|
+
...typeError,
|
|
46
|
+
message:
|
|
47
|
+
"ERROR. Invalid config format. The config should be an object.",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
// validates config.data
|
|
37
54
|
const configDataResult = ConfigDataSchema.safeParse(config.data);
|
|
38
55
|
|
|
39
56
|
if (!configDataResult.success) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
return {
|
|
58
|
+
...successFalse,
|
|
59
|
+
errors: [
|
|
60
|
+
{
|
|
61
|
+
...typeError,
|
|
62
|
+
message: "ERROR. Config data could not pass validation from zod.",
|
|
63
|
+
},
|
|
64
|
+
...configDataResult.error.errors.map((e) => ({
|
|
65
|
+
...typeError,
|
|
66
|
+
message: e.message,
|
|
67
|
+
})),
|
|
68
|
+
],
|
|
69
|
+
};
|
|
43
70
|
}
|
|
44
71
|
|
|
45
72
|
// validates config.ignores
|
|
@@ -48,9 +75,25 @@ export async function resolveConfig(configPath) {
|
|
|
48
75
|
);
|
|
49
76
|
|
|
50
77
|
if (!configIgnoresSchemaResult.success) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
78
|
+
return {
|
|
79
|
+
...successFalse,
|
|
80
|
+
errors: [
|
|
81
|
+
{
|
|
82
|
+
...typeError,
|
|
83
|
+
message: "ERROR. Config ignores could not pass validation from zod.",
|
|
84
|
+
},
|
|
85
|
+
...configIgnoresSchemaResult.error.errors.map((e) => ({
|
|
86
|
+
...typeError,
|
|
87
|
+
message: e.message,
|
|
88
|
+
})),
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const flattenedConfigDataResults = flattenConfigData(configDataResult.data);
|
|
94
|
+
|
|
95
|
+
if (!flattenedConfigDataResults.success) {
|
|
96
|
+
return flattenedConfigDataResults;
|
|
54
97
|
}
|
|
55
98
|
|
|
56
99
|
// sends back:
|
|
@@ -59,7 +102,7 @@ export async function resolveConfig(configPath) {
|
|
|
59
102
|
// - the verified config path
|
|
60
103
|
// - and the raw passed ignores
|
|
61
104
|
return {
|
|
62
|
-
...
|
|
105
|
+
...flattenedConfigDataResults, // finalized
|
|
63
106
|
configPath, // finalized
|
|
64
107
|
passedIgnores: configIgnoresSchemaResult.data, // addressed with --lint-config-imports and --my-ignores-only to be finalized
|
|
65
108
|
config, // and the config itself too
|
package/library/index.js
CHANGED
|
@@ -7,18 +7,18 @@ import {
|
|
|
7
7
|
cwd,
|
|
8
8
|
hasPackageJson,
|
|
9
9
|
hasGitFolder,
|
|
10
|
-
defaultConfigFileName,
|
|
11
|
-
configFlag,
|
|
12
|
-
lintConfigImportsFlag,
|
|
13
|
-
myIgnoresOnlyFlag,
|
|
14
|
-
knownIgnores,
|
|
10
|
+
defaultConfigFileName, // shared
|
|
11
|
+
configFlag, // shared
|
|
12
|
+
lintConfigImportsFlag, // shared
|
|
13
|
+
myIgnoresOnlyFlag, // shared
|
|
14
|
+
knownIgnores, // shared
|
|
15
15
|
resolveRuleName,
|
|
16
16
|
compressRuleName,
|
|
17
17
|
} from "./_commons/constants/bases.js";
|
|
18
18
|
|
|
19
|
-
import { exitDueToFailure } from "./_commons/utilities/helpers.js";
|
|
20
|
-
import { resolveConfig } from "./_commons/utilities/resolve-config.js";
|
|
21
|
-
import { findAllImports } from "./_commons/utilities/find-all-imports.js";
|
|
19
|
+
import { exitDueToFailure, logError } from "./_commons/utilities/helpers.js";
|
|
20
|
+
import { resolveConfig } from "./_commons/utilities/resolve-config.js"; // shared
|
|
21
|
+
import { findAllImports } from "./_commons/utilities/find-all-imports.js"; // own package
|
|
22
22
|
import {
|
|
23
23
|
resolveCommentsFlow,
|
|
24
24
|
compressCommentsFlow,
|
|
@@ -61,8 +61,9 @@ const passedConfigPath =
|
|
|
61
61
|
// defaults to comments.config.js if no --config flag is set
|
|
62
62
|
const rawConfigPath = passedConfigPath ?? path.join(cwd, defaultConfigFileName);
|
|
63
63
|
|
|
64
|
-
const
|
|
65
|
-
if (!
|
|
64
|
+
const resolveConfigResults = await resolveConfig(rawConfigPath);
|
|
65
|
+
if (!resolveConfigResults.success) {
|
|
66
|
+
resolveConfigResults.errors.forEach((e) => logError(e));
|
|
66
67
|
exitDueToFailure();
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -72,21 +73,36 @@ const {
|
|
|
72
73
|
reversedFlattenedConfigData,
|
|
73
74
|
configPath,
|
|
74
75
|
passedIgnores,
|
|
75
|
-
} =
|
|
76
|
+
} = resolveConfigResults;
|
|
76
77
|
|
|
77
78
|
skipDetails || console.log("Running with config:", config);
|
|
78
|
-
skipDetails || console.log("Flattened config is:", flattenedConfigData);
|
|
79
|
+
skipDetails || console.log("Flattened config data is:", flattenedConfigData);
|
|
79
80
|
skipDetails ||
|
|
80
|
-
console.log(
|
|
81
|
+
console.log(
|
|
82
|
+
"Reversed flattened config data is:",
|
|
83
|
+
reversedFlattenedConfigData
|
|
84
|
+
);
|
|
81
85
|
skipDetails || console.log("Config path is:", configPath);
|
|
82
86
|
skipDetails || console.log("Passed ignores are:", passedIgnores);
|
|
83
87
|
|
|
84
88
|
// ADDRESSES THE --lint-config-imports FLAG, GIVEN THAT THE FILES IMPORTED BY THE CONFIG ARE IGNORED BY DEFAULT.
|
|
85
89
|
|
|
86
90
|
const lintConfigImports = commands.indexOf(lintConfigImportsFlag) >= 2;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
let rawConfigPathIgnores = [configPath];
|
|
92
|
+
|
|
93
|
+
if (!lintConfigImports) {
|
|
94
|
+
const findAllImportsResults = findAllImports(configPath);
|
|
95
|
+
if (!findAllImportsResults.success) {
|
|
96
|
+
findAllImportsResults.errors.forEach((e) => logError(e));
|
|
97
|
+
console.warn(
|
|
98
|
+
"Defaulting to --lint-config-imports flag behavior, not ignoring config path imports, only the config path itself."
|
|
99
|
+
);
|
|
100
|
+
} else {
|
|
101
|
+
rawConfigPathIgnores = [...findAllImportsResults.visitedSet] ?? [
|
|
102
|
+
configPath,
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
90
106
|
|
|
91
107
|
// the ignore paths must be relative
|
|
92
108
|
const configPathIgnores = rawConfigPathIgnores.map((e) =>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comment-variables",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "A CLI tool for configuring, managing and maintaining JavaScript comments as JavaScript variables.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"jscomments": "./library/index.js",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@eslint/markdown": "^6.5.0",
|
|
29
29
|
"eslint": "^9.29.0",
|
|
30
|
-
"get-sourcecode-from-file-path": "^1.
|
|
31
|
-
"resolve-importing-path": "^1.0.
|
|
30
|
+
"get-sourcecode-from-file-path": "^1.1.2",
|
|
31
|
+
"resolve-importing-path": "^1.0.3",
|
|
32
32
|
"tsconfig-paths": "^4.2.0",
|
|
33
33
|
"typescript-eslint": "^8.34.1",
|
|
34
34
|
"zod": "^3.25.67"
|