Skip to content

Commit 9fca76e

Browse files
committed
docs: document _getDependencyData() directory parameter semantics
Add a Provider Implementation section to CONVENTIONS.md explaining that _createSbom passes both manifestDir (project directory) and workspaceDir (lock file directory) to _getDependencyData. Documents when each should be used and provides guidance for new provider implementations. Implements TC-4102 Assisted-by: Claude Code
1 parent 7a2cffa commit 9fca76e

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

CONVENTIONS.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Coding Conventions
2+
3+
<!-- This file documents project-specific coding standards for exhort-javascript-api. -->
4+
5+
## Language and Framework
6+
7+
- **Primary Language**: JavaScript (ES modules, `"type": "module"` in package.json)
8+
- **TypeScript**: Configuration present but code is primarily JavaScript with JSDoc
9+
- **Node.js**: Requires Node >= 20.0.0, npm >= 11.5.1
10+
- **CLI**: `yargs` for command-line argument parsing
11+
- **Parsing Libraries**: `fast-xml-parser`, `fast-toml`, `smol-toml`, `tree-sitter-requirements`
12+
13+
## Code Style
14+
15+
- **Linter**: ESLint with recommended config + editorconfig + import plugins
16+
- **Indentation**: Tabs (4 spaces for YAML/Markdown)
17+
- **Line endings**: LF
18+
- **Max line length**: 100 (120 for Markdown)
19+
- **Charset**: UTF-8, final newline, trim trailing whitespace
20+
- **Import ordering** (ESLint enforced): builtin, external, internal, parent, sibling, index — alphabetical within groups
21+
- **Strict equality**: `eqeqeq: ["warn", "always", {"null": "never"}]`
22+
- **Curly braces**: Required (`curly: "warn"`)
23+
- **No throw literals**: `no-throw-literal: "warn"`
24+
- **No Prettier** — ESLint + EditorConfig handle formatting
25+
26+
## Naming Conventions
27+
28+
- **Classes**: PascalCase with underscore-separated language names (`Java_maven`, `Base_java`, `Javascript_npm`)
29+
- **Files**: snake_case for providers (`base_java.js`, `javascript_npm.js`, `python_pip.js`)
30+
- **Test files**: `*.test.js` suffix (`analysis.test.js`, `provider.test.js`)
31+
- **Functions/Methods**: camelCase (`provideComponent()`, `provideStack()`, `validateLockFile()`)
32+
- **Variables**: camelCase (`manifestPath`, `backendUrl`)
33+
- **Constants**: UPPER_SNAKE_CASE (`ecosystem_maven`, `DEFAULT_WORKSPACE_DISCOVERY_IGNORE`)
34+
- **Private class fields**: `#` prefix (`#manifest`, `#cmd`, `#ecosystem`)
35+
- **Protected methods**: `_` prefix (`_lockFileName()`, `_cmdName()`, `_listCmdArgs()`)
36+
37+
## File Organization
38+
39+
```
40+
src/
41+
├── index.js # Main export
42+
├── cli.js # CLI entry point
43+
├── analysis.js # API request handling
44+
├── provider.js # Provider matching logic
45+
├── workspace.js # Workspace discovery
46+
├── tools.js # Utilities
47+
├── sbom.js # SBOM handling
48+
├── cyclone_dx_sbom.js # CycloneDX SBOM generation
49+
├── providers/ # Ecosystem providers
50+
│ ├── base_java.js
51+
│ ├── base_javascript.js
52+
│ ├── java_maven.js
53+
│ ├── javascript_npm.js
54+
│ ├── python_pip.js
55+
│ ├── rust_cargo.js
56+
│ └── processors/ # Specialized processors
57+
├── license/ # License detection
58+
└── oci_image/ # OCI image analysis
59+
60+
test/
61+
├── analysis.test.js
62+
├── provider.test.js
63+
├── tools.test.js
64+
└── providers/ # Provider-specific tests
65+
```
66+
67+
## Provider Implementation
68+
69+
### `_getDependencyData()` directory parameters
70+
71+
`Base_pyproject._createSbom()` resolves two directories before calling `_getDependencyData()`:
72+
73+
- **`manifestDir`** — the directory containing the `pyproject.toml` being analyzed (`path.dirname(manifest)`). This is always the project directory.
74+
- **`workspaceDir`** — the directory containing the lock file, found by `_findLockFileDir()` walking up from `manifestDir`. Falls back to `manifestDir` when no lock file is found.
75+
76+
The base class calls `_getDependencyData(manifestDir, workspaceDir, parsed, opts)` with both directories. Subclasses must implement this method with the same 4-parameter signature.
77+
78+
**Lock-file-based providers** (uv, poetry) use `workspaceDir` for lock-file-related operations (parsing, resolving workspace dependencies). They may also use `manifestDir` for running package manager commands that need to execute in the project directory.
79+
80+
**Non-lock-file providers** (pip) must use `manifestDir` for command execution because tools like `pip install .` run relative to the project directory where `pyproject.toml` lives. Since these providers have no lock file, `_findLockFileDir()` returns `null` and `workspaceDir` equals `manifestDir` — but providers must still accept the `workspaceDir` parameter to match the base class signature.
81+
82+
**Guidance for new providers:**
83+
84+
- Always declare all four parameters: `(manifestDir, workspaceDir, parsed, opts)`
85+
- Use `manifestDir` when running package manager commands that operate on the project (e.g., `pip install .`, `poetry show`)
86+
- Use `workspaceDir` when reading or resolving lock file contents
87+
- If your provider does not use a lock file, prefix the unused parameter with `_` (e.g., `_workspaceDir`) to signal that it is intentionally ignored
88+
89+
## Error Handling
90+
91+
- **Throw Error objects**: `throw new Error("message")`, `throw new TypeError("message")`
92+
- **No custom error classes** — uses built-in `Error` and `TypeError`
93+
- **HTTP errors**: Check `resp.status`, throw with status code and response text
94+
- **Async errors**: Bubble up naturally via async/await (no blanket try-catch)
95+
- **Validation errors**: Thrown early with descriptive context (manifest type, lock file)
96+
97+
## Testing Conventions
98+
99+
- **Framework**: Mocha with TDD UI (`suite()` / `test()`)
100+
- **Assertions**: Chai with `expect()` syntax
101+
- **Mocking**: Sinon for stubs; MSW (Mock Service Worker) for HTTP mocking
102+
- **Module mocking**: `esmock` with experimental loader
103+
- **Coverage**: C8 with 82% line coverage requirement
104+
- **Test patterns**: `expect(res).to.deep.equal(...)`, `expect(() => ...).to.throw('message')`
105+
- **Higher-order setup**: Functions like `interceptAndRun()` for test setup/teardown
106+
107+
## Commit Messages
108+
109+
- Likely Conventional Commits format
110+
- DCO (Developer Certificate of Origin) required
111+
- Semantic versioning (`0.3.0` in package.json)
112+
113+
## Dependencies
114+
115+
- **Package manager**: npm with `package-lock.json`
116+
- **Module system**: ES modules with explicit `.js` extensions in relative imports
117+
- **Import convention**: `import fs from 'node:fs'` (node: protocol for built-ins)
118+
- **Environment variables**: Prefixed with `TRUSTIFY_DA_` (e.g., `TRUSTIFY_DA_MVN_PATH`, `TRUSTIFY_DA_TOKEN`, `TRUSTIFY_DA_DEBUG`)
119+
- **Multi-ecosystem support**: npm, pnpm, yarn, Maven, Gradle, pip, cargo, Go modules, Docker/Podman

0 commit comments

Comments
 (0)