x-fidelity 1.8.0 → 1.9.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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [1.9.0](https://github.com/zotoio/x-fidelity/compare/v1.8.0...v1.9.0) (2024-08-06)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **config:** local paths and config option resolutions ([52a8144](https://github.com/zotoio/x-fidelity/commit/52a81448eab3376f063f867b0ab476fbfc0e210f))
7
+
8
+
9
+ ### Features
10
+
11
+ * add support for relative and absolute paths in directory and local-config options ([c67f0fe](https://github.com/zotoio/x-fidelity/commit/c67f0fe802139d9b4422cfb77bf0ed46e820f73a))
12
+
1
13
  # [1.8.0](https://github.com/zotoio/x-fidelity/compare/v1.7.0...v1.8.0) (2024-08-01)
2
14
 
3
15
 
package/dist/core/cli.js CHANGED
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.options = void 0;
4
7
  const logger_1 = require("../utils/logger");
5
8
  const commander_1 = require("commander");
9
+ const path_1 = __importDefault(require("path"));
6
10
  // Ensure logger is initialized
7
11
  if (!logger_1.logger || typeof logger_1.logger.info !== 'function') {
8
12
  console.error('Logger is not properly initialized');
@@ -16,17 +20,23 @@ if (!logger_1.logger || typeof logger_1.logger.info !== 'function') {
16
20
  global.logger = fallbackLogger;
17
21
  }
18
22
  commander_1.program
19
- .option("-d, --dir <directory>", "The checkout directory to analyze (default: current directory)", ".")
20
- .option("-a, --archetype <archetype>", "The archetype to use for analysis (default: node-fullstack)", "node-fullstack")
23
+ .option("-d, --dir <directory>", "The checkout directory to analyze", ".")
24
+ .option("-a, --archetype <archetype>", "The archetype to use for analysis", "node-fullstack")
21
25
  .option("-c, --configServer <configServer>", "The config server URL for fetching remote archetype configurations and rules")
22
- .option("-o, --openaiEnabled <boolean>", "Enable OpenAI analysis (default: false)", false)
26
+ .option("-o, --openaiEnabled <boolean>", "Enable OpenAI analysis", false)
23
27
  .option("-t, --telemetryCollector <telemetryCollector>", "The URL telemetry data will be sent to for usage analysis")
24
- .option("-m, --mode <mode>", "Run mode: 'client' or 'server' (default: client)", "client")
25
- .option("-p, --port <port>", "The port to run the server on (default: 8888)", "8888")
28
+ .option("-m, --mode <mode>", "Run mode: 'client' or 'server'", "client")
29
+ .option("-p, --port <port>", "The port to run the server on", "8888")
26
30
  .option("-l, --localConfig <path>", "Path to local archetype config and rules");
27
31
  commander_1.program.parse();
28
32
  const options = commander_1.program.opts();
29
33
  exports.options = options;
34
+ // Resolve paths
35
+ const resolvePath = (inputPath) => path_1.default.resolve(process.cwd(), inputPath);
36
+ options.dir = resolvePath(options.dir);
37
+ if (options.localConfig) {
38
+ options.localConfig = resolvePath(options.localConfig);
39
+ }
30
40
  const banner = (`
31
41
  =====================================
32
42
  __ __ ________ ______
@@ -38,14 +48,14 @@ const banner = (`
38
48
  | ## | ## | ## | ## \\
39
49
  \\## \\## \\## \\######
40
50
 
41
- --------------------
51
+ -------------------------------------
42
52
  ${new Date().toString().slice(0, 24)}
43
53
  archetype: ${options.archetype}
44
- directory: ${process.env.PWD}/${options.dir}
54
+ directory: ${options.dir}
45
55
  configServer: ${options.configServer ? options.configServer : 'none'}
46
56
  mode: ${options.mode}
47
57
  port: ${options.mode === 'server' ? options.port : 'n/a'}
48
- local-config: ${options.localConfig ? options.localConfig : 'none'}
58
+ localConfig: ${options.localConfig ? options.localConfig : 'none'}
49
59
  for available options run: xfidelity --help
50
60
  =====================================`);
51
61
  logger_1.logger.info(banner);
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ try {
25
25
  }
26
26
  else {
27
27
  (() => __awaiter(void 0, void 0, void 0, function* () {
28
- const results = yield (0, engine_1.analyzeCodebase)(`${process.env.PWD}/${cli_1.options.dir}`, cli_1.options.archetype, cli_1.options.configServer);
28
+ const results = yield (0, engine_1.analyzeCodebase)(cli_1.options.dir, cli_1.options.archetype, cli_1.options.configServer, cli_1.options.localConfig);
29
29
  // if results are found, there were warning level issues found in the codebase
30
30
  if (results.length > 0) {
31
31
  logger_1.logger.warn('WARNING: lo-fi attributes detected in codebase!');
@@ -86,12 +86,12 @@ function loadLocalRule(ruleName) {
86
86
  const filePath = path.join(__dirname, fileName);
87
87
  if (!fileName.startsWith('openai') || ((0, openaiUtils_1.isOpenAIEnabled)() && fileName.startsWith('openai'))) {
88
88
  try {
89
- logger_1.logger.debug(`Loading local rule file: ${filePath}`);
89
+ logger_1.logger.info(`Loading default rule file: ${filePath}`);
90
90
  const fileContent = yield fs.promises.readFile(filePath, 'utf8');
91
91
  return JSON.parse(fileContent);
92
92
  }
93
93
  catch (error) {
94
- logger_1.logger.error(`FATAL: Error loading local rule file: ${fileName}`);
94
+ logger_1.logger.error(`FATAL: Error loading default rule file: ${fileName}`);
95
95
  logger_1.logger.error(error);
96
96
  return null;
97
97
  }
@@ -103,6 +103,7 @@ function loadLocalConfigRule(ruleName, localConfigPath) {
103
103
  return __awaiter(this, void 0, void 0, function* () {
104
104
  const fileName = `${ruleName}-rule.json`;
105
105
  const filePath = path.join(localConfigPath, 'rules', fileName);
106
+ logger_1.logger.info(`Loading local config rule file: ${filePath}`);
106
107
  if (!fileName.startsWith('openai') || (process.env.OPENAI_API_KEY && fileName.startsWith('openai'))) {
107
108
  try {
108
109
  logger_1.logger.debug(`Loading local config rule file: ${filePath}`);
@@ -33,7 +33,7 @@ app.get('/archetypes/:archetype', (req, res) => __awaiter(void 0, void 0, void 0
33
33
  const archetype = req.params.archetype;
34
34
  if (validInput(archetype)) {
35
35
  const configManager = config_1.ConfigManager.getInstance();
36
- yield configManager.initialize(archetype);
36
+ yield configManager.initialize(archetype, cli_1.options.configServer, cli_1.options.localConfig);
37
37
  const archetypeConfig = configManager.getConfig();
38
38
  logger_1.logger.debug(`Found archetype ${archetype} config: ${JSON.stringify(archetypeConfig)}`);
39
39
  res.json(archetypeConfig);
@@ -53,10 +53,10 @@ app.get('/archetypes/:archetype/rules', (req, res) => __awaiter(void 0, void 0,
53
53
  const archetype = req.params.archetype;
54
54
  if (validInput(archetype)) {
55
55
  const configManager = config_1.ConfigManager.getInstance();
56
- yield configManager.initialize(archetype);
56
+ yield configManager.initialize(archetype, cli_1.options.configServer, cli_1.options.localConfig);
57
57
  const archetypeConfig = configManager.getConfig();
58
58
  if (archetypeConfig && archetypeConfig.rules) {
59
- const rules = yield (0, rules_1.loadRules)(archetype, archetypeConfig.rules);
59
+ const rules = yield (0, rules_1.loadRules)(archetype, archetypeConfig.rules, cli_1.options.configServer, '', cli_1.options.localConfig);
60
60
  res.json(rules);
61
61
  }
62
62
  else {
@@ -73,10 +73,10 @@ app.get('/archetypes/:archetype/rules/:rule', (req, res) => __awaiter(void 0, vo
73
73
  const rule = req.params.rule;
74
74
  if (validInput(archetype) && validInput(rule)) {
75
75
  const configManager = config_1.ConfigManager.getInstance();
76
- yield configManager.initialize(archetype);
76
+ yield configManager.initialize(archetype, cli_1.options.configServer, cli_1.options.localConfig);
77
77
  const archetypeConfig = configManager.getConfig();
78
78
  if (archetypeConfig && archetypeConfig.rules && archetypeConfig.rules.includes(rule)) {
79
- const rules = yield (0, rules_1.loadRules)(archetype, archetypeConfig.rules);
79
+ const rules = yield (0, rules_1.loadRules)(archetype, archetypeConfig.rules, cli_1.options.configServer, '', cli_1.options.localConfig);
80
80
  const ruleJson = rules.find((r) => r.name === rule);
81
81
  res.json(ruleJson);
82
82
  }
@@ -69,17 +69,17 @@ class ConfigManager {
69
69
  if (this.configServer) {
70
70
  try {
71
71
  const configUrl = `${this.configServer}/archetypes/${archetype}`;
72
- logger_1.logger.debug(`Fetching remote config from: ${configUrl}`);
72
+ logger_1.logger.debug(`Fetching remote archetype config from: ${configUrl}`);
73
73
  const response = yield axios_1.default.get(configUrl);
74
74
  this.config = Object.assign(Object.assign({}, this.config), response.data);
75
- logger_1.logger.debug(`Remote config fetched successfully ${JSON.stringify(this.config)}`);
75
+ logger_1.logger.debug(`Remote archetype config fetched successfully ${JSON.stringify(this.config)}`);
76
76
  }
77
77
  catch (error) {
78
78
  if (error instanceof Error) {
79
- logger_1.logger.error(`Error fetching remote config: ${error.message}`);
79
+ logger_1.logger.error(`Error fetching remote archetype config: ${error.message}`);
80
80
  }
81
81
  else {
82
- logger_1.logger.error('Error fetching remote config: Unknown error');
82
+ logger_1.logger.error('Error fetching remote archetype config: Unknown error');
83
83
  }
84
84
  // If remote fetch fails, fall back to local config
85
85
  if (this.localConfigPath) {
@@ -96,16 +96,17 @@ class ConfigManager {
96
96
  return __awaiter(this, void 0, void 0, function* () {
97
97
  try {
98
98
  const configPath = path.join(this.localConfigPath, `${archetype}.json`);
99
+ logger_1.logger.info(`Loading local archetype config from: ${configPath}`);
99
100
  const configContent = yield fs.promises.readFile(configPath, 'utf8');
100
101
  const localConfig = JSON.parse(configContent);
101
102
  return Object.assign(Object.assign({}, archetypes_1.archetypes[archetype]), localConfig);
102
103
  }
103
104
  catch (error) {
104
105
  if (error instanceof Error) {
105
- logger_1.logger.error(`Error loading local config: ${error.message}`);
106
+ logger_1.logger.error(`Error loading local archetype config: ${error.message}`);
106
107
  }
107
108
  else {
108
- logger_1.logger.error('Error loading local config: Unknown error');
109
+ logger_1.logger.error('Error loading local archetype config: Unknown error');
109
110
  }
110
111
  return archetypes_1.archetypes[archetype];
111
112
  }
package/dist/xfidelity CHANGED
@@ -25,7 +25,7 @@ try {
25
25
  }
26
26
  else {
27
27
  (() => __awaiter(void 0, void 0, void 0, function* () {
28
- const results = yield (0, engine_1.analyzeCodebase)(`${process.env.PWD}/${cli_1.options.dir}`, cli_1.options.archetype, cli_1.options.configServer);
28
+ const results = yield (0, engine_1.analyzeCodebase)(cli_1.options.dir, cli_1.options.archetype, cli_1.options.configServer, cli_1.options.localConfig);
29
29
  // if results are found, there were warning level issues found in the codebase
30
30
  if (results.length > 0) {
31
31
  logger_1.logger.warn('WARNING: lo-fi attributes detected in codebase!');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x-fidelity",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "cli for opinionated framework adherence checks",
5
5
  "main": "dist/xfidelity",
6
6
  "bin": {
package/src/core/cli.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { logger } from '../utils/logger';
2
2
  import { program } from "commander";
3
+ import path from "path";
3
4
 
4
5
  // Ensure logger is initialized
5
6
  if (!logger || typeof logger.info !== 'function') {
@@ -15,19 +16,26 @@ if (!logger || typeof logger.info !== 'function') {
15
16
  }
16
17
 
17
18
  program
18
- .option("-d, --dir <directory>", "The checkout directory to analyze (default: current directory)", ".")
19
- .option("-a, --archetype <archetype>", "The archetype to use for analysis (default: node-fullstack)", "node-fullstack")
19
+ .option("-d, --dir <directory>", "The checkout directory to analyze", ".")
20
+ .option("-a, --archetype <archetype>", "The archetype to use for analysis", "node-fullstack")
20
21
  .option("-c, --configServer <configServer>", "The config server URL for fetching remote archetype configurations and rules")
21
- .option("-o, --openaiEnabled <boolean>", "Enable OpenAI analysis (default: false)", false)
22
+ .option("-o, --openaiEnabled <boolean>", "Enable OpenAI analysis", false)
22
23
  .option("-t, --telemetryCollector <telemetryCollector>", "The URL telemetry data will be sent to for usage analysis")
23
- .option("-m, --mode <mode>", "Run mode: 'client' or 'server' (default: client)", "client")
24
- .option("-p, --port <port>", "The port to run the server on (default: 8888)", "8888")
24
+ .option("-m, --mode <mode>", "Run mode: 'client' or 'server'", "client")
25
+ .option("-p, --port <port>", "The port to run the server on", "8888")
25
26
  .option("-l, --localConfig <path>", "Path to local archetype config and rules");
26
27
 
27
28
  program.parse();
28
29
 
29
30
  const options = program.opts();
30
31
 
32
+ // Resolve paths
33
+ const resolvePath = (inputPath: string) => path.resolve(process.cwd(), inputPath);
34
+ options.dir = resolvePath(options.dir);
35
+ if (options.localConfig) {
36
+ options.localConfig = resolvePath(options.localConfig);
37
+ }
38
+
31
39
  const banner = (`
32
40
  =====================================
33
41
  __ __ ________ ______
@@ -39,14 +47,14 @@ const banner = (`
39
47
  | ## | ## | ## | ## \\
40
48
  \\## \\## \\## \\######
41
49
 
42
- --------------------
50
+ -------------------------------------
43
51
  ${new Date().toString().slice(0, 24)}
44
52
  archetype: ${options.archetype}
45
- directory: ${process.env.PWD}/${options.dir}
53
+ directory: ${options.dir}
46
54
  configServer: ${options.configServer ? options.configServer : 'none'}
47
55
  mode: ${options.mode}
48
56
  port: ${options.mode === 'server' ? options.port : 'n/a'}
49
- local-config: ${options.localConfig ? options.localConfig : 'none'}
57
+ localConfig: ${options.localConfig ? options.localConfig : 'none'}
50
58
  for available options run: xfidelity --help
51
59
  =====================================`);
52
60
 
package/src/index.ts CHANGED
@@ -12,7 +12,7 @@ try {
12
12
  startServer(options.port);
13
13
  } else {
14
14
  (async () => {
15
- const results = await analyzeCodebase(`${process.env.PWD}/${options.dir}`, options.archetype, options.configServer);
15
+ const results = await analyzeCodebase(options.dir, options.archetype, options.configServer, options.localConfig);
16
16
 
17
17
  // if results are found, there were warning level issues found in the codebase
18
18
  if (results.length > 0) {
@@ -52,11 +52,11 @@ async function loadLocalRule(ruleName: string): Promise<RuleProperties | null> {
52
52
 
53
53
  if (!fileName.startsWith('openai') || (isOpenAIEnabled() && fileName.startsWith('openai'))) {
54
54
  try {
55
- logger.debug(`Loading local rule file: ${filePath}`);
55
+ logger.info(`Loading default rule file: ${filePath}`);
56
56
  const fileContent = await fs.promises.readFile(filePath, 'utf8');
57
57
  return JSON.parse(fileContent);
58
58
  } catch (error) {
59
- logger.error(`FATAL: Error loading local rule file: ${fileName}`);
59
+ logger.error(`FATAL: Error loading default rule file: ${fileName}`);
60
60
  logger.error(error);
61
61
  return null;
62
62
  }
@@ -68,6 +68,7 @@ export { loadRules };
68
68
  async function loadLocalConfigRule(ruleName: string, localConfigPath: string): Promise<RuleProperties | null> {
69
69
  const fileName = `${ruleName}-rule.json`;
70
70
  const filePath = path.join(localConfigPath, 'rules', fileName);
71
+ logger.info(`Loading local config rule file: ${filePath}`);
71
72
 
72
73
  if (!fileName.startsWith('openai') || (process.env.OPENAI_API_KEY && fileName.startsWith('openai'))) {
73
74
  try {
@@ -22,7 +22,7 @@ app.get('/archetypes/:archetype', async (req, res) => {
22
22
  const archetype = req.params.archetype;
23
23
  if (validInput(archetype)) {
24
24
  const configManager = ConfigManager.getInstance();
25
- await configManager.initialize(archetype);
25
+ await configManager.initialize(archetype, options.configServer, options.localConfig);
26
26
  const archetypeConfig = configManager.getConfig();
27
27
  logger.debug(`Found archetype ${archetype} config: ${JSON.stringify(archetypeConfig)}`);
28
28
  res.json(archetypeConfig);
@@ -43,10 +43,10 @@ app.get('/archetypes/:archetype/rules', async (req, res) => {
43
43
  const archetype = req.params.archetype;
44
44
  if (validInput(archetype)) {
45
45
  const configManager = ConfigManager.getInstance();
46
- await configManager.initialize(archetype);
46
+ await configManager.initialize(archetype, options.configServer, options.localConfig);
47
47
  const archetypeConfig = configManager.getConfig();
48
48
  if (archetypeConfig && archetypeConfig.rules) {
49
- const rules = await loadRules(archetype, archetypeConfig.rules);
49
+ const rules = await loadRules(archetype, archetypeConfig.rules, options.configServer, '', options.localConfig);
50
50
  res.json(rules);
51
51
  } else {
52
52
  res.status(404).json({ error: 'archetype not found or has no rules' });
@@ -62,10 +62,10 @@ app.get('/archetypes/:archetype/rules/:rule', async (req, res) => {
62
62
  const rule = req.params.rule;
63
63
  if (validInput(archetype) && validInput(rule)) {
64
64
  const configManager = ConfigManager.getInstance();
65
- await configManager.initialize(archetype);
65
+ await configManager.initialize(archetype, options.configServer, options.localConfig);
66
66
  const archetypeConfig = configManager.getConfig();
67
67
  if (archetypeConfig && archetypeConfig.rules && archetypeConfig.rules.includes(rule)) {
68
- const rules = await loadRules(archetype, archetypeConfig.rules);
68
+ const rules = await loadRules(archetype, archetypeConfig.rules, options.configServer, '', options.localConfig);
69
69
  const ruleJson = rules.find((r) => r.name === rule);
70
70
  res.json(ruleJson);
71
71
  } else {
@@ -41,18 +41,18 @@ export class ConfigManager {
41
41
  if (this.configServer) {
42
42
  try {
43
43
  const configUrl = `${this.configServer}/archetypes/${archetype}`;
44
- logger.debug(`Fetching remote config from: ${configUrl}`);
44
+ logger.debug(`Fetching remote archetype config from: ${configUrl}`);
45
45
  const response = await axios.get(configUrl);
46
46
  this.config = {
47
47
  ...this.config,
48
48
  ...response.data
49
49
  };
50
- logger.debug(`Remote config fetched successfully ${JSON.stringify(this.config)}`);
50
+ logger.debug(`Remote archetype config fetched successfully ${JSON.stringify(this.config)}`);
51
51
  } catch (error) {
52
52
  if (error instanceof Error) {
53
- logger.error(`Error fetching remote config: ${error.message}`);
53
+ logger.error(`Error fetching remote archetype config: ${error.message}`);
54
54
  } else {
55
- logger.error('Error fetching remote config: Unknown error');
55
+ logger.error('Error fetching remote archetype config: Unknown error');
56
56
  }
57
57
  // If remote fetch fails, fall back to local config
58
58
  if (this.localConfigPath) {
@@ -68,6 +68,7 @@ export class ConfigManager {
68
68
 
69
69
  try {
70
70
  const configPath = path.join(this.localConfigPath, `${archetype}.json`);
71
+ logger.info(`Loading local archetype config from: ${configPath}`);
71
72
  const configContent = await fs.promises.readFile(configPath, 'utf8');
72
73
  const localConfig = JSON.parse(configContent);
73
74
  return {
@@ -76,9 +77,9 @@ export class ConfigManager {
76
77
  };
77
78
  } catch (error) {
78
79
  if (error instanceof Error) {
79
- logger.error(`Error loading local config: ${error.message}`);
80
+ logger.error(`Error loading local archetype config: ${error.message}`);
80
81
  } else {
81
- logger.error('Error loading local config: Unknown error');
82
+ logger.error('Error loading local archetype config: Unknown error');
82
83
  }
83
84
  return archetypes[archetype];
84
85
  }