@canonical/code-standards 0.1.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/.mcp.json +8 -0
- package/README.md +297 -0
- package/data/code.ttl +437 -0
- package/data/css.ttl +265 -0
- package/data/icons.ttl +359 -0
- package/data/packaging.ttl +464 -0
- package/data/react.ttl +752 -0
- package/data/rust.ttl +1806 -0
- package/data/storybook.ttl +403 -0
- package/data/styling.ttl +165 -0
- package/data/tsdoc.ttl +216 -0
- package/data/turtle.ttl +179 -0
- package/definitions/CodeStandard.ttl +80 -0
- package/docs/code.md +720 -0
- package/docs/css.md +275 -0
- package/docs/icons.md +367 -0
- package/docs/index.md +15 -0
- package/docs/react.md +766 -0
- package/docs/rust.md +1784 -0
- package/docs/storybook.md +413 -0
- package/docs/styling.md +163 -0
- package/docs/tsdoc.md +213 -0
- package/docs/turtle.md +179 -0
- package/package.json +9 -0
- package/skills/add-standard/SKILL.md +288 -0
- package/src/scripts/generate-docs.ts +131 -0
- package/src/scripts/index.ts +19 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
@prefix cs: <http://pragma.canonical.com/codestandards#> .
|
|
2
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
3
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
4
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
5
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
6
|
+
|
|
7
|
+
# Packaging Category
|
|
8
|
+
cs:PackagingCategory a cs:Category ;
|
|
9
|
+
rdfs:label "Packaging"@en ;
|
|
10
|
+
rdfs:comment "Standards for structuring TypeScript/JavaScript packages — archetype classification, folder layout, export shape, barrel files, and file naming"@en ;
|
|
11
|
+
cs:slug "packaging" .
|
|
12
|
+
|
|
13
|
+
# Application Package Structure
|
|
14
|
+
cs:ApplicationPackage a cs:CodeStandard ;
|
|
15
|
+
cs:name "packaging/application/structure" ;
|
|
16
|
+
cs:hasCategory cs:PackagingCategory ;
|
|
17
|
+
cs:description "Application packages (CLIs, servers, MCP tools, build scripts, workers) execute behavior through entry points. They are not imported as dependencies by other packages. Their internal code must be organized around execution domains (commands, routes, tools, operations) rather than library conventions like `src/lib/` or barrel re-exports. Each domain folder must have its own barrel file (`index.ts`) that defines the domain's internal API — sibling domains import through the barrel, never from individual files." ;
|
|
18
|
+
cs:dos """
|
|
19
|
+
(Do) Recognise an application package by its `package.json` signals — `bin` field, `private: true`, no `main`/`exports`:
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"name": "@scope/my-cli",
|
|
23
|
+
"private": true,
|
|
24
|
+
"bin": { "my-cli": "./dist/index.js" }
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
(Do) Organize code around execution domains. Each domain folder gets its own barrel (`index.ts`) that exposes the domain's internal API to sibling domains:
|
|
29
|
+
```
|
|
30
|
+
packages/my-cli/
|
|
31
|
+
├── src/
|
|
32
|
+
│ ├── commands/ # CLI command handlers
|
|
33
|
+
│ │ ├── build.ts
|
|
34
|
+
│ │ ├── deploy.ts
|
|
35
|
+
│ │ └── index.ts # Barrel: exports all commands
|
|
36
|
+
│ ├── operations/ # Shared internal logic across commands
|
|
37
|
+
│ │ ├── resolveConfig.ts
|
|
38
|
+
│ │ ├── runValidation.ts
|
|
39
|
+
│ │ └── index.ts # Barrel: exports shared operations
|
|
40
|
+
│ ├── utils/ # Internal helpers
|
|
41
|
+
│ │ ├── formatOutput.ts
|
|
42
|
+
│ │ └── index.ts # Barrel: exports utilities
|
|
43
|
+
│ └── index.ts # Entry point (bootstraps CLI)
|
|
44
|
+
└── package.json
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
(Do) Import from sibling domains through their barrel, not from individual files:
|
|
48
|
+
```typescript
|
|
49
|
+
// src/commands/deploy.ts
|
|
50
|
+
import { resolveConfig, runValidation } from "../operations/index.js";
|
|
51
|
+
import { formatOutput } from "../utils/index.js";
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
(Do) Use domain-appropriate folder names that reflect what the code does — `commands/`, `tools/`, `routes/`, `operations/`, `handlers/`:
|
|
55
|
+
```
|
|
56
|
+
packages/my-mcp/
|
|
57
|
+
├── src/
|
|
58
|
+
│ ├── tools/ # MCP tool handlers
|
|
59
|
+
│ │ ├── query.ts
|
|
60
|
+
│ │ ├── mutate.ts
|
|
61
|
+
│ │ └── index.ts # Barrel: exports tool handlers
|
|
62
|
+
│ ├── operations/ # Shared internal logic
|
|
63
|
+
│ │ ├── executeQuery.ts
|
|
64
|
+
│ │ └── index.ts # Barrel: exports shared operations
|
|
65
|
+
│ └── index.ts # MCP server entry point
|
|
66
|
+
└── package.json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
(Do) For **hybrid** packages (primarily application but exporting a small reusable surface), isolate the exported surface in `src/lib/` following library rules while keeping the rest as application structure:
|
|
70
|
+
```
|
|
71
|
+
packages/my-mcp/
|
|
72
|
+
├── src/
|
|
73
|
+
│ ├── lib/ # Only the reusable exported surface
|
|
74
|
+
│ │ ├── types.ts
|
|
75
|
+
│ │ └── index.ts # Barrel: public API for external consumers
|
|
76
|
+
│ ├── tools/ # MCP tool handlers (not exported)
|
|
77
|
+
│ │ ├── query.ts
|
|
78
|
+
│ │ ├── mutate.ts
|
|
79
|
+
│ │ └── index.ts # Barrel: exports tool handlers
|
|
80
|
+
│ ├── operations/ # Shared internal logic
|
|
81
|
+
│ │ ├── executeQuery.ts
|
|
82
|
+
│ │ └── index.ts # Barrel: exports shared operations
|
|
83
|
+
│ └── index.ts # MCP server entry point
|
|
84
|
+
└── package.json
|
|
85
|
+
```
|
|
86
|
+
""" ;
|
|
87
|
+
cs:donts """
|
|
88
|
+
(Don't) Apply library structure (`src/lib/`, barrel re-exports) to application packages:
|
|
89
|
+
```
|
|
90
|
+
// Bad: CLI tool forced into library layout
|
|
91
|
+
packages/my-cli/
|
|
92
|
+
├── src/
|
|
93
|
+
│ ├── lib/ # Wrong: this is a CLI, not a library
|
|
94
|
+
│ │ ├── commands/
|
|
95
|
+
│ │ └── index.ts # Barrel re-export — nobody imports this
|
|
96
|
+
│ └── index.ts
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
(Don't) Create a `src/lib/` folder for internal shared operations. Use domain-appropriate names instead:
|
|
100
|
+
```
|
|
101
|
+
// Bad: "lib" implies external consumption
|
|
102
|
+
packages/my-cli/
|
|
103
|
+
├── src/
|
|
104
|
+
│ ├── lib/
|
|
105
|
+
│ │ └── resolveConfig.ts # Only used internally
|
|
106
|
+
|
|
107
|
+
// Good: "operations" or "shared" makes intent clear
|
|
108
|
+
packages/my-cli/
|
|
109
|
+
├── src/
|
|
110
|
+
│ ├── operations/
|
|
111
|
+
│ │ └── resolveConfig.ts # Internal shared logic
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
(Don't) Import directly from files inside a sibling domain — always go through the domain barrel:
|
|
115
|
+
```typescript
|
|
116
|
+
// Bad: reaching into a sibling domain's internals
|
|
117
|
+
import { resolveConfig } from "../operations/resolveConfig.js";
|
|
118
|
+
import { formatOutput } from "../utils/formatOutput.js";
|
|
119
|
+
|
|
120
|
+
// Good: import through the domain barrel
|
|
121
|
+
import { resolveConfig } from "../operations/index.js";
|
|
122
|
+
import { formatOutput } from "../utils/index.js";
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
(Don't) Omit barrels from domain folders:
|
|
126
|
+
```
|
|
127
|
+
// Bad: no barrels — every consumer imports individual files
|
|
128
|
+
packages/my-cli/
|
|
129
|
+
├── src/
|
|
130
|
+
│ ├── commands/
|
|
131
|
+
│ │ ├── build.ts
|
|
132
|
+
│ │ └── deploy.ts # No index.ts — siblings must know file names
|
|
133
|
+
│ ├── operations/
|
|
134
|
+
│ │ ├── resolveConfig.ts
|
|
135
|
+
│ │ └── runValidation.ts # No index.ts — fragile cross-domain imports
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
(Don't) Default to library structure just because the package lives in a monorepo:
|
|
139
|
+
```
|
|
140
|
+
// Bad: reflexively adding lib/ to a build script package
|
|
141
|
+
packages/build-tools/
|
|
142
|
+
├── src/
|
|
143
|
+
│ ├── lib/ # This is a build script, not a library
|
|
144
|
+
│ │ └── runBuild.ts
|
|
145
|
+
│ └── index.ts
|
|
146
|
+
```
|
|
147
|
+
""" .
|
|
148
|
+
|
|
149
|
+
# Library Package Structure
|
|
150
|
+
cs:LibraryPackage a cs:CodeStandard ;
|
|
151
|
+
cs:name "packaging/library/structure" ;
|
|
152
|
+
cs:hasCategory cs:PackagingCategory ;
|
|
153
|
+
cs:description "Library packages export reusable code (components, utilities, hooks, types) for consumption by other packages. They must organize their exportable code in a `src/lib/` folder and re-export through a root barrel. This convention ensures consistency across the monorepo and clearly separates public API code from non-exported concerns like storybook configuration or test utilities. Each domain folder within `lib/` must have its own barrel (`index.ts`)." ;
|
|
154
|
+
cs:dos """
|
|
155
|
+
(Do) Recognise a library package by its `package.json` signals — `main`/`exports` field, publishable, no `bin`:
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"name": "@scope/design-system",
|
|
159
|
+
"main": "./dist/index.js",
|
|
160
|
+
"exports": { ".": "./dist/index.js" }
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
(Do) Place all reusable/exportable code in `src/lib/`, with a barrel in each domain folder:
|
|
165
|
+
```
|
|
166
|
+
packages/my-package/
|
|
167
|
+
├── src/
|
|
168
|
+
│ ├── lib/
|
|
169
|
+
│ │ ├── Button/
|
|
170
|
+
│ │ │ ├── Button.tsx
|
|
171
|
+
│ │ │ ├── Button.tests.tsx
|
|
172
|
+
│ │ │ ├── types.ts
|
|
173
|
+
│ │ │ └── index.ts # Barrel: exports Button public API
|
|
174
|
+
│ │ ├── hooks/
|
|
175
|
+
│ │ │ ├── useToggle.ts
|
|
176
|
+
│ │ │ └── index.ts # Barrel: exports hooks
|
|
177
|
+
│ │ ├── types/
|
|
178
|
+
│ │ │ └── index.ts # Barrel: exports shared types
|
|
179
|
+
│ │ └── index.ts # Barrel: lib public API
|
|
180
|
+
│ ├── storybook/ # Storybook-specific files (not exported)
|
|
181
|
+
│ └── index.ts # Re-exports from lib
|
|
182
|
+
└── package.json
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
(Do) Re-export from the lib folder in the package entry point:
|
|
186
|
+
```typescript
|
|
187
|
+
// src/index.ts
|
|
188
|
+
export * from "./lib/index.js";
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
(Do) Use the lib barrel to compose the package's public API from domain barrels:
|
|
192
|
+
```typescript
|
|
193
|
+
// src/lib/index.ts
|
|
194
|
+
export * from "./Button/index.js";
|
|
195
|
+
export * from "./hooks/index.js";
|
|
196
|
+
export type * from "./types/index.js";
|
|
197
|
+
```
|
|
198
|
+
""" ;
|
|
199
|
+
cs:donts """
|
|
200
|
+
(Don't) Use alternative folder names like `ui`, `components`, or `utils` for exportable code at the package level:
|
|
201
|
+
```
|
|
202
|
+
// Bad: Using 'ui' instead of 'lib'
|
|
203
|
+
packages/my-package/
|
|
204
|
+
├── src/
|
|
205
|
+
│ ├── ui/ # Wrong: should be 'lib'
|
|
206
|
+
│ │ └── Button/
|
|
207
|
+
│ └── index.ts
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
(Don't) Mix exportable and non-exportable code at the same level:
|
|
211
|
+
```
|
|
212
|
+
// Bad: Mixing concerns
|
|
213
|
+
packages/my-package/
|
|
214
|
+
├── src/
|
|
215
|
+
│ ├── Button/ # Component mixed with...
|
|
216
|
+
│ ├── storybook/ # ...non-exportable storybook config
|
|
217
|
+
│ └── test-utils/ # ...and test utilities
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
(Don't) Create deeply nested lib-like structures; keep lib at the top level of src:
|
|
221
|
+
```
|
|
222
|
+
// Bad: Nested lib folders
|
|
223
|
+
packages/my-package/
|
|
224
|
+
├── src/
|
|
225
|
+
│ ├── features/
|
|
226
|
+
│ │ └── lib/ # Wrong: lib should be at src level
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
(Don't) Omit barrels from domain folders within lib:
|
|
230
|
+
```
|
|
231
|
+
// Bad: no barrels — consumers must know individual file paths
|
|
232
|
+
packages/my-package/
|
|
233
|
+
├── src/
|
|
234
|
+
│ ├── lib/
|
|
235
|
+
│ │ ├── Button/
|
|
236
|
+
│ │ │ ├── Button.tsx
|
|
237
|
+
│ │ │ └── types.ts # No index.ts — fragile imports
|
|
238
|
+
│ │ ├── hooks/
|
|
239
|
+
│ │ │ └── useToggle.ts # No index.ts
|
|
240
|
+
│ │ └── index.ts
|
|
241
|
+
```
|
|
242
|
+
""" .
|
|
243
|
+
|
|
244
|
+
# Export Shape Standard
|
|
245
|
+
cs:ExportShape a cs:CodeStandard ;
|
|
246
|
+
cs:name "packaging/export/shape" ;
|
|
247
|
+
cs:hasCategory cs:PackagingCategory ;
|
|
248
|
+
cs:description "Files must use either a single default export or multiple named exports. When using multiple named exports, all exports must have the same type or shape." ;
|
|
249
|
+
cs:dos """
|
|
250
|
+
(Do) Use a single default export for files implementing a single component or function:
|
|
251
|
+
```typescript
|
|
252
|
+
// ComponentName.tsx
|
|
253
|
+
export default ComponentName;
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
(Do) Name atomic function files after the function they export:
|
|
257
|
+
```typescript
|
|
258
|
+
// assignElement.ts
|
|
259
|
+
export default function assignElement(target: Element, source: Partial<Element>) {
|
|
260
|
+
// ...
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// formatCurrency.ts
|
|
264
|
+
export default function formatCurrency(value: number) {
|
|
265
|
+
// ...
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
(Do) Use multiple named exports for files providing a public API or a collection of related items, and ensure all exports have the same type:
|
|
270
|
+
```typescript
|
|
271
|
+
// index.ts
|
|
272
|
+
export { ComponentA, ComponentB };
|
|
273
|
+
|
|
274
|
+
// types.ts
|
|
275
|
+
export type TypeA = { ... };
|
|
276
|
+
export type TypeB = { ... };
|
|
277
|
+
|
|
278
|
+
// Consistent export shape:
|
|
279
|
+
export const myFuncA = (value: string) => {};
|
|
280
|
+
export const myFuncB = (value: string) => {};
|
|
281
|
+
export const myFuncC = (value: string) => {};
|
|
282
|
+
```
|
|
283
|
+
""" ;
|
|
284
|
+
cs:donts """
|
|
285
|
+
(Don't) Mix default and unrelated named exports in a way that confuses the file's purpose:
|
|
286
|
+
```typescript
|
|
287
|
+
export default ComponentName;
|
|
288
|
+
export const helper = () => {};
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
(Don't) Name an atomic function file with a name that doesn't match its exported function:
|
|
292
|
+
```typescript
|
|
293
|
+
// helpers.ts — wrong: generic name instead of function name
|
|
294
|
+
export default function assignElement(target: Element, source: Partial<Element>) {
|
|
295
|
+
// ...
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// utils.ts — wrong: generic name instead of function name
|
|
299
|
+
export default function formatCurrency(value: number) {
|
|
300
|
+
// ...
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
(Don't) Provide multiple unrelated exports from a file meant for a single domain:
|
|
305
|
+
```typescript
|
|
306
|
+
export default debounce;
|
|
307
|
+
export const throttle = () => {};
|
|
308
|
+
export const logger = () => {};
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
(Don't) Export objects of different types or shapes from the same file:
|
|
312
|
+
```typescript
|
|
313
|
+
export const transformer = (value: string) => {};
|
|
314
|
+
export const reducer = (map: string[]) => {};
|
|
315
|
+
class ABC {}
|
|
316
|
+
export { ABC };
|
|
317
|
+
```
|
|
318
|
+
""" .
|
|
319
|
+
|
|
320
|
+
# Barrel File Standard
|
|
321
|
+
cs:BarrelPublicApi a cs:CodeStandard ;
|
|
322
|
+
cs:name "packaging/export/barrel" ;
|
|
323
|
+
cs:hasCategory cs:PackagingCategory ;
|
|
324
|
+
cs:description "Every domain folder must have a barrel file (`index.ts`) that defines its API surface. In library packages, barrels define the public API for external consumers. In application packages, barrels define the internal API between sibling domains. Within a domain, implementation files import from siblings directly; cross-domain imports always go through the barrel." ;
|
|
325
|
+
cs:dos """
|
|
326
|
+
(Do) Use a barrel at the package entry point to define the public API (library packages):
|
|
327
|
+
```typescript
|
|
328
|
+
// src/index.ts
|
|
329
|
+
export { Button } from "./components/Button.js";
|
|
330
|
+
export type { ButtonProps } from "./components/Button.types.js";
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
(Do) Use a barrel at each domain folder to define the domain's API (application packages):
|
|
334
|
+
```typescript
|
|
335
|
+
// src/operations/index.ts
|
|
336
|
+
export { resolveConfig } from "./resolveConfig.js";
|
|
337
|
+
export { runValidation } from "./runValidation.js";
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
(Do) Import internal implementation code within a domain from the owning file directly:
|
|
341
|
+
```typescript
|
|
342
|
+
// src/build/buildTheme.ts — same domain, import the file
|
|
343
|
+
import { computeDeltas } from "./computeDeltas.js";
|
|
344
|
+
import { recoverPrimitiveRef } from "./recoverPrimitiveRef.js";
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
(Do) Import cross-domain code through the sibling domain's barrel:
|
|
348
|
+
```typescript
|
|
349
|
+
// src/commands/deploy.ts — different domain, import through barrel
|
|
350
|
+
import { resolveConfig } from "../operations/index.js";
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
(Do) Keep compatibility barrels thin and documented as public facades:
|
|
354
|
+
```typescript
|
|
355
|
+
/** Public compatibility surface. */
|
|
356
|
+
export { computeDeltas } from "./computeDeltas.js";
|
|
357
|
+
export { formatDelta } from "./formatDelta.js";
|
|
358
|
+
```
|
|
359
|
+
""" ;
|
|
360
|
+
cs:donts """
|
|
361
|
+
(Don't) Route internal same-domain code through its own barrel:
|
|
362
|
+
```typescript
|
|
363
|
+
// Bad: internal implementation depends on its own barrel
|
|
364
|
+
// src/build/applyTheme.ts
|
|
365
|
+
import { computeDeltas, recoverPrimitiveRef } from "./index.js";
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
(Don't) Import cross-domain code by reaching into individual files:
|
|
369
|
+
```typescript
|
|
370
|
+
// Bad: bypassing the domain barrel
|
|
371
|
+
import { resolveConfig } from "../operations/resolveConfig.js";
|
|
372
|
+
|
|
373
|
+
// Good: through the barrel
|
|
374
|
+
import { resolveConfig } from "../operations/index.js";
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
(Don't) Hide domain ownership behind convenience barrels:
|
|
378
|
+
```typescript
|
|
379
|
+
// Bad: types appear to belong to context.ts instead of their owning domains
|
|
380
|
+
import type { Artifact, CSSNode, OverlayToken } from "./context.js";
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
(Don't) Omit the barrel from a domain folder:
|
|
384
|
+
```
|
|
385
|
+
// Bad: no index.ts — consumers must know internal file names
|
|
386
|
+
src/operations/
|
|
387
|
+
├── resolveConfig.ts
|
|
388
|
+
└── runValidation.ts
|
|
389
|
+
```
|
|
390
|
+
""" .
|
|
391
|
+
|
|
392
|
+
# Single Export File Naming Standard
|
|
393
|
+
cs:SingleExportFileNaming a cs:CodeStandard ;
|
|
394
|
+
cs:name "packaging/naming/single-export-file" ;
|
|
395
|
+
cs:hasCategory cs:PackagingCategory ;
|
|
396
|
+
cs:description "Files that contain a single export must be named after that export. This applies to functions, classes, types, constants, and any other single-export module. The file name must match the exported identifier exactly, preserving its casing (camelCase for functions/variables, PascalCase for classes/types/components)." ;
|
|
397
|
+
cs:dos """
|
|
398
|
+
(Do) Name files after the single function they export:
|
|
399
|
+
```typescript
|
|
400
|
+
// isAccepted.ts
|
|
401
|
+
export const isAccepted = (status: string): boolean => {
|
|
402
|
+
return status === "accepted";
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// formatCurrency.ts
|
|
406
|
+
export default function formatCurrency(value: number): string {
|
|
407
|
+
return `$${value.toFixed(2)}`;
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
(Do) Name files after the single type or interface they export:
|
|
412
|
+
```typescript
|
|
413
|
+
// ConnectionConfig.ts
|
|
414
|
+
export interface ConnectionConfig {
|
|
415
|
+
host: string;
|
|
416
|
+
port: number;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// UserRole.ts
|
|
420
|
+
export type UserRole = "admin" | "editor" | "viewer";
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
(Do) Name files after the single class they export:
|
|
424
|
+
```typescript
|
|
425
|
+
// EventEmitter.ts
|
|
426
|
+
export class EventEmitter {
|
|
427
|
+
// ...
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
(Do) Name files after the single constant they export:
|
|
432
|
+
```typescript
|
|
433
|
+
// DEFAULT_TIMEOUT.ts
|
|
434
|
+
export const DEFAULT_TIMEOUT = 5000;
|
|
435
|
+
```
|
|
436
|
+
""" ;
|
|
437
|
+
cs:donts """
|
|
438
|
+
(Don't) Use generic names like `helpers.ts`, `utils.ts`, or `types.ts` for files with a single export:
|
|
439
|
+
```typescript
|
|
440
|
+
// helpers.ts — wrong: should be isAccepted.ts
|
|
441
|
+
export const isAccepted = (status: string): boolean => {
|
|
442
|
+
return status === "accepted";
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// utils.ts — wrong: should be formatCurrency.ts
|
|
446
|
+
export default function formatCurrency(value: number): string {
|
|
447
|
+
return `$${value.toFixed(2)}`;
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
(Don't) Use names that describe the domain instead of the export:
|
|
452
|
+
```typescript
|
|
453
|
+
// validation.ts — wrong: should be isAccepted.ts
|
|
454
|
+
export const isAccepted = (status: string): boolean => {
|
|
455
|
+
return status === "accepted";
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// network.ts — wrong: should be ConnectionConfig.ts
|
|
459
|
+
export interface ConnectionConfig {
|
|
460
|
+
host: string;
|
|
461
|
+
port: number;
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
""" .
|