@fyuld/snitch 2.3.3 → 2.3.5

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.
@@ -0,0 +1,125 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "title": "Policy Configuration",
5
+ "description": "Policy definition with violation rules and provider pipelines",
6
+ "properties": {
7
+ "title": {
8
+ "type": "string",
9
+ "description": "Human-readable policy title"
10
+ },
11
+ "violations": {
12
+ "type": "array",
13
+ "description": "Violation rules for this policy",
14
+ "items": {
15
+ "$ref": "#/definitions/ViolationRule"
16
+ }
17
+ }
18
+ },
19
+ "required": ["title", "violations"],
20
+ "additionalProperties": false,
21
+ "definitions": {
22
+ "ViolationRule": {
23
+ "type": "object",
24
+ "properties": {
25
+ "message": {
26
+ "type": "string",
27
+ "description": "Violation message template with placeholders"
28
+ },
29
+ "cause": {
30
+ "type": "string",
31
+ "description": "Cause description template with placeholders"
32
+ },
33
+ "providers": {
34
+ "type": "array",
35
+ "description": "Provider pipeline for detecting violations",
36
+ "items": {
37
+ "$ref": "#/definitions/ProviderConfig"
38
+ }
39
+ }
40
+ },
41
+ "required": ["message", "cause", "providers"],
42
+ "additionalProperties": false
43
+ },
44
+ "ProviderConfig": {
45
+ "oneOf": [
46
+ { "$ref": "#/definitions/IdentityProvider" },
47
+ { "$ref": "#/definitions/FilterByFileProvider" },
48
+ { "$ref": "#/definitions/FilterExcludeFunctionNamesProvider" },
49
+ { "$ref": "#/definitions/FilterHasLiteralArgumentsProvider" },
50
+ { "$ref": "#/definitions/FilterByDeclarationTypeProvider" }
51
+ ]
52
+ },
53
+ "IdentityProvider": {
54
+ "type": "object",
55
+ "properties": {
56
+ "type": { "const": "identity" },
57
+ "source": {
58
+ "enum": [
59
+ "functionInvocations",
60
+ "literals",
61
+ "declarations",
62
+ "typeDeclarations",
63
+ "topLevelFunctions",
64
+ "exports",
65
+ "sequentialGroups"
66
+ ]
67
+ }
68
+ },
69
+ "required": ["type", "source"],
70
+ "additionalProperties": false
71
+ },
72
+ "FilterByFileProvider": {
73
+ "type": "object",
74
+ "properties": {
75
+ "type": { "const": "filter-by-file" },
76
+ "patterns": {
77
+ "type": "array",
78
+ "items": { "type": "string" }
79
+ }
80
+ },
81
+ "required": ["type", "patterns"],
82
+ "additionalProperties": false
83
+ },
84
+ "FilterExcludeFunctionNamesProvider": {
85
+ "type": "object",
86
+ "properties": {
87
+ "type": { "const": "filter-exclude-function-names" },
88
+ "names": {
89
+ "type": "array",
90
+ "items": { "type": "string" }
91
+ }
92
+ },
93
+ "required": ["type", "names"],
94
+ "additionalProperties": false
95
+ },
96
+ "FilterHasLiteralArgumentsProvider": {
97
+ "type": "object",
98
+ "properties": {
99
+ "type": { "const": "filter-has-literal-arguments" },
100
+ "literalKinds": {
101
+ "type": "array",
102
+ "items": {
103
+ "enum": ["string", "number", "boolean", "null", "object", "array"]
104
+ }
105
+ }
106
+ },
107
+ "required": ["type", "literalKinds"],
108
+ "additionalProperties": false
109
+ },
110
+ "FilterByDeclarationTypeProvider": {
111
+ "type": "object",
112
+ "properties": {
113
+ "type": { "const": "filter-by-declaration-type" },
114
+ "declarationTypes": {
115
+ "type": "array",
116
+ "items": {
117
+ "enum": ["interface", "type", "enum", "class", "function", "const", "let", "var"]
118
+ }
119
+ }
120
+ },
121
+ "required": ["type", "declarationTypes"],
122
+ "additionalProperties": false
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Snitch Configuration",
4
+ "description": "Configuration file for Snitch code quality policies",
5
+ "type": "object",
6
+ "properties": {
7
+ "extends": {
8
+ "type": "string",
9
+ "description": "Base configuration to extend from"
10
+ },
11
+ "sourceDirectory": {
12
+ "type": "string",
13
+ "description": "Directory containing source code to analyze"
14
+ },
15
+ "testsDirectory": {
16
+ "type": "string",
17
+ "description": "Directory containing test files"
18
+ },
19
+ "policies": {
20
+ "type": "object",
21
+ "description": "Policy definitions with provider pipelines",
22
+ "additionalProperties": {
23
+ "$ref": "./policy.schema.json"
24
+ }
25
+ },
26
+ "thirdPartyTools": {
27
+ "type": "array",
28
+ "description": "Third party tools (formatters, test runners, etc.) to install and configure",
29
+ "items": {
30
+ "$ref": "./third-party-tool.schema.json"
31
+ }
32
+ }
33
+ },
34
+ "required": ["sourceDirectory", "testsDirectory", "policies"],
35
+ "additionalProperties": false
36
+ }
@@ -0,0 +1,77 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "title": "Third Party Tool Configuration",
5
+ "description": "Configuration for third party tools (formatters, test runners, build tools, etc.)",
6
+ "properties": {
7
+ "title": {
8
+ "type": "string",
9
+ "description": "Human-readable tool name"
10
+ },
11
+ "description": {
12
+ "type": "string",
13
+ "description": "Description of what the tool does"
14
+ },
15
+ "id": {
16
+ "type": "string",
17
+ "description": "Unique identifier for the tool"
18
+ },
19
+ "purpose": {
20
+ "type": "array",
21
+ "description": "List of purposes this tool serves",
22
+ "items": {
23
+ "type": "string"
24
+ }
25
+ },
26
+ "configFilename": {
27
+ "type": "string",
28
+ "description": "The filename to create for this tool's configuration"
29
+ },
30
+ "software": {
31
+ "type": "object",
32
+ "description": "Software package information",
33
+ "properties": {
34
+ "pm": {
35
+ "type": "string",
36
+ "enum": ["npm"],
37
+ "description": "Package manager to use"
38
+ },
39
+ "name": {
40
+ "type": "string",
41
+ "description": "Package name"
42
+ },
43
+ "version": {
44
+ "type": "string",
45
+ "description": "Package version (optional)"
46
+ }
47
+ },
48
+ "required": ["pm", "name"],
49
+ "additionalProperties": false
50
+ },
51
+ "configuration": {
52
+ "oneOf": [
53
+ {
54
+ "type": "string",
55
+ "description": "URI to configuration file (URL or file path)"
56
+ },
57
+ {
58
+ "type": "object",
59
+ "description": "Inline configuration object"
60
+ }
61
+ ]
62
+ },
63
+ "sideEffects": {
64
+ "type": "object",
65
+ "description": "Side effects to apply to project files",
66
+ "properties": {
67
+ "package.json": {
68
+ "type": "object",
69
+ "description": "Modifications to merge into package.json"
70
+ }
71
+ },
72
+ "additionalProperties": false
73
+ }
74
+ },
75
+ "required": ["title", "description", "id", "purpose", "configFilename", "software", "configuration"],
76
+ "additionalProperties": false
77
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"enforcement-software.d.ts","sourceRoot":"","sources":["../../src/utils/enforcement-software.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AAQlE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BjF"}
1
+ {"version":3,"file":"enforcement-software.d.ts","sourceRoot":"","sources":["../../src/utils/enforcement-software.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AAQlE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgCjF"}
@@ -44,7 +44,8 @@ async function setupThirdPartyTools(tools) {
44
44
  const allSideEffects = [];
45
45
  for (const tool of tools) {
46
46
  const { configFilename, configuration, sideEffects } = tool;
47
- const configContent = await resolveConfiguration(configuration);
47
+ const configObject = await resolveConfiguration(configuration);
48
+ const configContent = formatConfigContent(configObject, configFilename);
48
49
  if (createdFiles.has(configFilename)) {
49
50
  throw new Error(`Duplicate config filename detected: ${configFilename}. Multiple tools are trying to create the same file.`);
50
51
  }
@@ -64,7 +65,7 @@ async function setupThirdPartyTools(tools) {
64
65
  }
65
66
  async function resolveConfiguration(source) {
66
67
  if (typeof source === 'object') {
67
- return JSON.stringify(source, null, 2);
68
+ return source;
68
69
  }
69
70
  return new Promise((resolve, reject) => {
70
71
  https
@@ -78,7 +79,7 @@ async function resolveConfiguration(source) {
78
79
  data += chunk;
79
80
  });
80
81
  res.on('end', () => {
81
- resolve(data);
82
+ resolve(JSON.parse(data));
82
83
  });
83
84
  })
84
85
  .on('error', (err) => {
@@ -86,6 +87,16 @@ async function resolveConfiguration(source) {
86
87
  });
87
88
  });
88
89
  }
90
+ function formatConfigContent(content, filename) {
91
+ const ext = path.extname(filename);
92
+ if (ext === '.js') {
93
+ return `module.exports = ${JSON.stringify(content, null, 2)}\n`;
94
+ }
95
+ if (ext === '.json') {
96
+ return JSON.stringify(content, null, 2);
97
+ }
98
+ throw new Error(`Unsupported config file extension: ${ext}. Only .js and .json are supported.`);
99
+ }
89
100
  function applySideEffects(sideEffects) {
90
101
  if (sideEffects['package.json']) {
91
102
  const packageJsonPath = path.resolve('package.json');
@@ -1 +1 @@
1
- {"version":3,"file":"enforcement-software.js","sourceRoot":"","sources":["../../src/utils/enforcement-software.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"enforcement-software.js","sourceRoot":"","sources":["../../src/utils/enforcement-software.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fyuld/snitch",
3
- "version": "2.3.3",
3
+ "version": "2.3.5",
4
4
  "description": "Snitch",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -40,8 +40,7 @@
40
40
  },
41
41
  "files": [
42
42
  "dist",
43
- ".snitchconfig.json",
44
- "snitchconfig.schema.json"
43
+ "assets"
45
44
  ],
46
45
  "dependencies": {
47
46
  "@fyuld/errors": "^1.1.0",
@@ -1,34 +0,0 @@
1
- {
2
- "sourceDirectory": "src",
3
- "testsDirectory": "test",
4
- "policies": {
5
- "keep-tests-clean": {
6
- "title": "Keep Tests Clean",
7
- "violations": [
8
- {
9
- "message": "Found literal in function call: {functionName}",
10
- "cause": "{literalValue}",
11
- "providers": [
12
- { "type": "identity", "source": "functionInvocations" },
13
- { "type": "filter-by-file", "patterns": ["test/**/*.ts", "**/*.test.ts", "**/*.spec.ts"] },
14
- { "type": "filter-exclude-function-names", "names": ["test", "given", "when", "then", "describe", "it"] },
15
- { "type": "filter-has-literal-arguments", "literalKinds": ["string", "number"] }
16
- ]
17
- }
18
- ]
19
- },
20
- "no-vars": {
21
- "title": "No Var Declarations",
22
- "violations": [
23
- {
24
- "message": "Found var declaration: {declarationName}",
25
- "cause": "{definition}",
26
- "providers": [
27
- { "type": "identity", "source": "declarations" },
28
- { "type": "filter-by-declaration-type", "declarationTypes": ["var"] }
29
- ]
30
- }
31
- ]
32
- }
33
- }
34
- }