Skip to content

Commit 6b26514

Browse files
Merge remote-tracking branch 'origin/develop'
2 parents 9cca700 + d887fb1 commit 6b26514

1,595 files changed

Lines changed: 29995 additions & 16686 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintrc.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
{
22
"root": true,
33
"ignorePatterns": ["**/*"],
4-
"plugins": ["@nx", "perfectionist", "simple-import-sort", "unused-imports"],
4+
"plugins": [
5+
"@nx",
6+
"perfectionist",
7+
"simple-import-sort",
8+
"unused-imports",
9+
"prettier"
10+
],
511
"overrides": [
612
{
713
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
@@ -40,7 +46,8 @@
4046
}
4147
}
4248
],
43-
"unused-imports/no-unused-imports": "error"
49+
"unused-imports/no-unused-imports": "error",
50+
"prettier/prettier": "error"
4451
}
4552
},
4653
{
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
---
2+
applyTo: "**/*.ts"
3+
description: "TypeScript coding conventions for the vscode-idl codebase — naming, imports, types, null handling, functions, classes, error handling, JSDoc, formatting, and testing patterns"
4+
---
5+
6+
# TypeScript Coding Conventions
7+
8+
## Naming Conventions
9+
10+
**Functions (exported and internal file-private helpers):** PascalCase
11+
```ts
12+
export function CleanPath(uri: string): string { ... }
13+
function BuildIdMap(nodes: ENVIModelerNode[]): Map<string, string> { ... }
14+
export async function GetAutoComplete(...): Promise<GetAutoCompleteResponse> { ... }
15+
```
16+
17+
**Classes:** PascalCase
18+
```ts
19+
export class GlobalIndex { ... }
20+
export class CancellationToken { ... }
21+
```
22+
23+
**Interfaces:** PascalCase with `I` prefix
24+
```ts
25+
export interface IFindTokensOptions { ... }
26+
export interface IParserOptions { ... }
27+
export interface IMessageToWorker<_Message extends string> { ... }
28+
```
29+
30+
**Type aliases:** PascalCase, no `I` prefix
31+
```ts
32+
export type ParsedType = 'def' | 'notebook' | 'pro';
33+
export type LogInterceptor = (options: ILogOptions) => void;
34+
export type NameCounters = Record<string, number>;
35+
```
36+
37+
**Module-level constants:** SCREAMING_SNAKE_CASE
38+
```ts
39+
export const LAYOUT_BASE_X = 1200;
40+
export const TOKEN_TYPES: ITokenTypes = { BASIC: 0, START: 1, END: 2 };
41+
```
42+
43+
**Exported mutable module-level state:** SCREAMING_SNAKE_CASE with `export let`
44+
```ts
45+
export let EXTENSION_FOLDER = '';
46+
export let LANGUAGE_SERVER_CLIENT: LanguageClient;
47+
```
48+
49+
**Local variables:** camelCase
50+
```ts
51+
const foundDefs: ITokenDef<TokenName>[] = [];
52+
let hasLineContinuation = false;
53+
```
54+
55+
**Generic type parameters:** PascalCase with
56+
```ts
57+
export class WorkerIO<Message extends string> { ... }
58+
export interface IMessageToWorker<Message extends string> { ... }
59+
```
60+
61+
**File naming:** kebab-case with dot-separated category suffix
62+
- `name.class.ts` — class definitions
63+
- `name.interface.ts` — interfaces, types, and constants
64+
- `name.class.interface.ts` — interface for a specific class
65+
- `name.spec.ts` — test files
66+
- `verb-noun.ts` — standalone utility functions (e.g., `clean-path.ts`, `get-sort-idx.ts`)
67+
- `index.ts` — barrel re-exports only
68+
69+
---
70+
71+
## Imports
72+
73+
**Always use named imports.** Default imports are not used.
74+
```ts
75+
import { CancellationToken } from '@idl/cancellation-tokens';
76+
import { existsSync, readFileSync } from 'fs';
77+
import { basename, dirname, join } from 'path';
78+
```
79+
80+
**Star (`* as`) imports only** for modules without good typings or where the whole namespace is needed:
81+
```ts
82+
import * as vscode from 'vscode';
83+
import * as chalk from 'chalk';
84+
```
85+
86+
**CJS interop** uses `= require()` syntax:
87+
```ts
88+
import GlobToRegExp = require('glob-to-regexp');
89+
```
90+
91+
**Use `@idl/...` path aliases** for all cross-library imports — never use deep relative paths across library boundaries:
92+
```ts
93+
import { IDL_TRANSLATION } from '@idl/translation';
94+
import { CleanPath } from '@idl/shared/extension';
95+
```
96+
97+
**Barrel `index.ts` files** contain only `export * from '...'` lines — no logic, no default exports, no selective re-exports:
98+
```ts
99+
export * from './lib/clean-path';
100+
export * from './lib/simple-promise-queue.class';
101+
```
102+
103+
---
104+
105+
## Type Annotations
106+
107+
**Use `undefined`, not `null`.** Never use `null` to represent the absence of a value — use `undefined` or optional (`?`) members instead. This keeps absence checks consistent and avoids the two-value problem (`null` vs `undefined`) throughout the codebase.
108+
109+
```ts
110+
// DO — optional parameter (implicitly `string | undefined`)
111+
export function MyFunc(optParam?: string): void { ... }
112+
113+
// DO — optional property in an interface
114+
export interface IMyOptions {
115+
folder?: string; // string | undefined, not string | null
116+
closer?: ITokenDef;
117+
}
118+
119+
// DO — explicit undefined union when a variable may not be set yet
120+
let current: string | undefined;
121+
122+
// DO — check with !== undefined (not truthiness) when falsy values are valid
123+
if (current !== undefined) { ... }
124+
125+
// DON'T — never use null
126+
let current: string | null = null; // avoid
127+
export function MyFunc(optParam: string | null): void { ... } // avoid
128+
```
129+
130+
**Explicit parameter and return types on all exported functions:**
131+
```ts
132+
export function NextName(counters: NameCounters, prefix: string): string { ... }
133+
export function ComputeLayout(nodes: ENVIModelerNode[]): Map<string, [number, number]> { ... }
134+
```
135+
136+
Return type may be omitted when trivially inferred from a simple expression.
137+
138+
**`any` only when truly necessary** — e.g., cross-boundary serialization or genuinely unknown shapes. Do not use it to avoid typing work.
139+
140+
**Generics** are used extensively for typed message passing, event systems, and indexed lookups:
141+
```ts
142+
export class WorkerIO<Message extends string> { ... }
143+
export type ExportedGlobalTokensByType = { [key in GlobalTokenType]: IGlobalIndexedToken<key>[] };
144+
```
145+
146+
**Prefer `{ [key: string]: T }` index types over `Map`** for plain data objects that are serialized or iterated by key:
147+
```ts
148+
changedFiles: { [key: string]: boolean } = {};
149+
globalTokensByFile: { [key: string]: GlobalTokens } = {};
150+
```
151+
152+
**`Partial<Record<K, V>>`** and mapped types for optional lookup tables:
153+
```ts
154+
export const FIXED_DISPLAY_NAMES: Partial<Record<ENVIModelerNode['type'], string>> = { ... };
155+
```
156+
157+
**`interface` vs `type`:**
158+
- `interface` with `I` prefix → object shapes that describe a data structure or contract
159+
- `type` → unions, mapped types, function signatures, string literal sets, and type composition
160+
161+
---
162+
163+
## Null and Undefined Handling
164+
165+
**Prefer `??` over `||`** for falsy-safe defaults (avoids incorrectly overriding `0`, `''`, `false`):
166+
```ts
167+
counters[prefix] = (counters[prefix] ?? 0) + 1;
168+
base['revision'] = node.revision ?? '1.0.0';
169+
```
170+
171+
**Explicit `!== undefined`** for values that could be legitimately falsy:
172+
```ts
173+
if (buff !== undefined) { ... }
174+
if (problem !== undefined) { ... }
175+
```
176+
177+
**Optional chaining (`?.`)** in usage:
178+
```ts
179+
if (first?.name in NAME_TOKENS) { ... }
180+
```
181+
182+
**No non-null assertion (`!`).** Handle `undefined` explicitly rather than asserting it away.
183+
184+
---
185+
186+
## Function Style
187+
188+
**Top-level exported functions and internal file helpers:** regular `function` declarations (not arrow functions assigned to `const`)
189+
```ts
190+
export function CleanPath(uri: string): string { ... }
191+
function BuildIdMap(nodes: ENVIModelerNode[]): Map<string, string> { ... }
192+
```
193+
194+
**Callbacks and inline functions:** arrow functions
195+
```ts
196+
vscode.workspace.onDidChangeConfiguration((ev) => { ... });
197+
this.listeners[fileType].forEach((listener) => { listener(payload); });
198+
```
199+
200+
**Async:** always `async/await`. No raw `.then()` / `.catch()` chains.
201+
```ts
202+
export async function GetAutoComplete(...): Promise<GetAutoCompleteResponse> {
203+
const recipes = await GetCompletionRecipes(index, file, code, position);
204+
return BuildCompletionItems(index, recipes, config, formatting);
205+
}
206+
```
207+
208+
**`switch (true)` pattern** as an alternative to long `if/else if` chains:
209+
```ts
210+
switch (true) {
211+
case kids.length === 0: { ... } break;
212+
case kids.length > 1: { ... } break;
213+
default: break;
214+
}
215+
```
216+
217+
---
218+
219+
## Classes vs Standalone Functions
220+
221+
**Use classes** for stateful, long-lived objects with clear lifecycle (construct → use → destroy):
222+
- e.g., `IDLIndex`, `LogManager`, `WorkerIO`, `CancellationToken`
223+
224+
**Static methods on classes** for related utility logic that belongs conceptually to the class:
225+
```ts
226+
export class IDLFileHelper {
227+
static isConfigFile(file: string): boolean { ... }
228+
}
229+
```
230+
231+
**Standalone exported functions** for stateless logic with no shared state:
232+
```ts
233+
export function CleanPath(uri: string): string { ... }
234+
export function GetSortIndexForStrings(arr: string[]): number[] { ... }
235+
```
236+
237+
**Module-level `export let` singletons** for shared global state — initialized by a separate `Initialize...()` function, not a singleton class:
238+
```ts
239+
export let IDL_EXTENSION_CONFIG: IIDLWorkspaceConfig;
240+
export let LANGUAGE_SERVER_CLIENT: LanguageClient;
241+
```
242+
243+
---
244+
245+
## Error Handling
246+
247+
**Minimal try/catch.** Let errors propagate unless there is a specific, documented reason to catch them. Do not wrap large blocks defensively.
248+
249+
**When intentionally skipping a try/catch, leave the commented-out block** with an explanation:
250+
```ts
251+
// Don't catch errors at this level — failures should propagate so they
252+
// get caught and reported at the caller.
253+
// try {
254+
Object.assign(tokenized, Tokenizer(code, cancel, tokenOptions));
255+
// } catch (err) { ... }
256+
```
257+
258+
**Route errors via callbacks** passed into constructors rather than throwing from within class internals:
259+
```ts
260+
constructor(options: ILogManagerOptions) {
261+
this.alert = options.alert; // caller decides how to surface errors
262+
}
263+
```
264+
265+
**`CancellationToken` uses throw for control flow** — this is the one intentional throw-for-flow pattern:
266+
```ts
267+
throwIfCancelled() {
268+
if (this.cancelRequested()) throw new Error(CANCELLATION_MESSAGE);
269+
}
270+
```
271+
272+
---
273+
274+
## Comments and JSDoc
275+
276+
**`/** ... */` JSDoc on all exported functions, classes, interfaces, and class members:**
277+
```ts
278+
/**
279+
* Converts a file-system path to a normalized URI string.
280+
*/
281+
export function CleanPath(uri: string): string { ... }
282+
```
283+
284+
**Single-line `/** */` for interface and class properties:**
285+
```ts
286+
/** Cancellation token */
287+
cancel: CancellationToken;
288+
289+
/** Worker threads by worker ID */
290+
workers: { [key: string]: Worker } = {};
291+
```
292+
293+
**`/** */` for significant local variable declarations** inside functions:
294+
```ts
295+
/** Current position of the iteration cursor */
296+
const current = iterator.current;
297+
```
298+
299+
**No `@param` or `@returns` tags.** Write the full description in the summary line of the JSDoc block.
300+
301+
**Preserve commented-out code** with an explanatory comment — do not silently delete it.
302+
303+
---
304+
305+
## Formatting
306+
307+
- **Single quotes** for all string literals: `'pro'`, `'my_file.pro'`
308+
- **Semicolons** always
309+
- **2-space indentation**
310+
- **Trailing commas** in multiline function call arguments and array/object literals
311+
- **Template literals** for string interpolation: `` `${prefix}_${count}` ``
312+
- Target **~80–100 character line length**; break long import lists and argument lists across lines
313+

0 commit comments

Comments
 (0)