@enhance-eng/archangel 0.1.0 → 0.1.1

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.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/index.d.ts +64 -20
  3. package/package.json +11 -9
package/README.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  **Make architecture verifiable.**
4
4
 
5
- Archangel checks the architecture rules of a TypeScript codebase. You write down how your code is
5
+ ![Seconds to check the same 400 architecture rules on a 9,800-file TypeScript monorepo: archangel 1.9 s; dependency-cruiser, ESLint and Steiger together 67.7 s](https://raw.githubusercontent.com/enhance-labs-builds/archangel/main/docs/assets/speed-light.svg)
6
+
7
+ Architecture tests for TypeScript, checked in milliseconds. You write down how your code is
6
8
  organized. Archangel tells you when the code breaks those rules, and what to do about it. It checks
7
9
  a large repository in about a second, so it can run in your editor as you type.
8
10
 
package/index.d.ts CHANGED
@@ -116,6 +116,21 @@ export interface RuleOptions {
116
116
  };
117
117
  }
118
118
 
119
+ /**
120
+ * A rule name `N` that the config writes itself. A slash separates a preset's name from the names
121
+ * it declares, so the config's own name has one only after the name of a preset in `X`, the
122
+ * presets the config extends, and then replaces that preset's rule of the name. Any other name
123
+ * with a slash becomes the text of the type error.
124
+ */
125
+ export type OwnRuleName<N extends string, X extends string> = N extends `${infer Prefix}/${string}`
126
+ ? Prefix extends X
127
+ ? N
128
+ : "a rule's own name has no slash, except after the name of a preset the config extends, to replace its rule"
129
+ : N;
130
+
131
+ /** A rule's `name`, checked by {@link OwnRuleName}. */
132
+ type Named<N extends string, X extends string> = { readonly name?: OwnRuleName<N, X> };
133
+
119
134
  /** What limits an import condition to some imports. */
120
135
  export interface ImportLimits {
121
136
  /** Only imports of these names. */
@@ -292,21 +307,36 @@ export type Rule<E extends Elements, S extends Selectors = {}, P extends Plugins
292
307
  | SeveralSentences<E, S>
293
308
  | PluginRule<E, S, P>;
294
309
 
295
- /** The rule builders that a `rules` function receives. */
296
- export interface RuleBuilders<E extends Elements, S extends Selectors = {}, P extends Plugins = {}> {
310
+ /**
311
+ * The rule builders that a `rules` function receives, for a config that extends the presets
312
+ * named in `X`.
313
+ */
314
+ export interface RuleBuilders<
315
+ E extends Elements,
316
+ S extends Selectors = {},
317
+ P extends Plugins = {},
318
+ X extends string = string,
319
+ > {
297
320
  /** Each listed element may import only itself and later listed elements. */
298
- layers(order: readonly NoInfer<ElementName<E>>[], options?: RuleOptions): Rule<E, S, P>;
321
+ layers<const N extends string = string>(
322
+ order: readonly NoInfer<ElementName<E>>[],
323
+ options?: RuleOptions & Named<N, X>,
324
+ ): Rule<E, S, P>;
299
325
  /** The instances of the element may not import each other. */
300
- isolate(element: NoInfer<CapturedElement<E>>, options?: RuleOptions): Rule<E, S, P>;
326
+ isolate<const N extends string = string>(
327
+ element: NoInfer<CapturedElement<E>>,
328
+ options?: RuleOptions & Named<N, X>,
329
+ ): Rule<E, S, P>;
301
330
  /** Code outside an instance of the element may import it only through these files. */
302
- entry(
331
+ entry<const N extends string = string>(
303
332
  element: NoInfer<ElementName<E>>,
304
333
  files: string | readonly string[],
305
- options?: RuleOptions,
334
+ options?: RuleOptions & Named<N, X>,
306
335
  ): Rule<E, S, P>;
307
336
  /** A rule as a sentence: `files`, or `folders`, and exactly one condition or plugin condition. */
308
- rule(
309
- sentence: Sentence<E, S> | FolderSentence | SeveralSentences<E, S> | PluginRule<E, S, P>,
337
+ rule<const N extends string = string>(
338
+ sentence: (Sentence<E, S> | FolderSentence | SeveralSentences<E, S> | PluginRule<E, S, P>) &
339
+ Named<N, X>,
310
340
  ): Rule<E, S, P>;
311
341
  }
312
342
 
@@ -314,9 +344,9 @@ export interface RuleBuilders<E extends Elements, S extends Selectors = {}, P ex
314
344
  * What a preset returns: part of a config, which `extends` puts under the config. Archangel names
315
345
  * each element, selector and rule `<name>/<its name>`, and reads paths relative to `root`.
316
346
  */
317
- export interface Preset {
347
+ export interface Preset<X extends string = string> {
318
348
  /** The prefix of everything the preset declares, such as `fsd`. */
319
- readonly name: string;
349
+ readonly name: X;
320
350
  /** The package the preset comes from, which a problem its user cannot fix names. */
321
351
  readonly package?: string;
322
352
  /** The folder the preset applies to, relative to the config folder. */
@@ -329,23 +359,35 @@ export interface Preset {
329
359
  }
330
360
 
331
361
  /** The options of the Feature-Sliced Design preset. */
332
- export interface FsdOptions {
362
+ export interface FsdOptions<X extends string = string> {
333
363
  /** The folder that holds the layers, such as `src`. */
334
364
  readonly root: string;
335
365
  /** Turn on Steiger's four opt-in rules too. */
336
366
  readonly strict?: boolean;
337
367
  /** The prefix, for a second use in one repository; `fsd` by default. */
338
- readonly name?: string;
368
+ readonly name?: X;
339
369
  }
340
370
 
341
371
  /** The Feature-Sliced Design preset: Steiger's rules for the layers under `root`. */
342
- export declare function fsd(options: FsdOptions): Preset;
372
+ export declare function fsd<const X extends string = 'fsd'>(options: FsdOptions<X>): Preset<X>;
343
373
 
344
374
  /** The Next.js preset: code a client component reaches stays out of server-only code. */
345
- export declare function nextjs(options?: { readonly name?: string; readonly root?: string }): Preset;
375
+ export declare function nextjs<const X extends string = 'nextjs'>(options?: {
376
+ readonly name?: X;
377
+ readonly root?: string;
378
+ }): Preset<X>;
346
379
 
347
- /** An archangel config. */
348
- export interface Config<E extends Elements, S extends Selectors = {}, P extends Plugins = {}> {
380
+ /**
381
+ * An archangel config, which extends the presets named in `X` and names its own rules with the
382
+ * names in `N`.
383
+ */
384
+ export interface Config<
385
+ E extends Elements,
386
+ S extends Selectors = {},
387
+ P extends Plugins = {},
388
+ X extends string = string,
389
+ N extends string = string,
390
+ > {
349
391
  /** The elements: parts of the code by file pattern, or packages; a config that extends a preset may have none. */
350
392
  readonly elements?: E;
351
393
  /** Named sets of files, which rules and other selectors use by name. */
@@ -357,13 +399,13 @@ export interface Config<E extends Elements, S extends Selectors = {}, P extends
357
399
  /** The plugins, by the name the rules use them by, as `plugin: '<name>/<condition>'`. */
358
400
  readonly plugins?: P;
359
401
  /** The presets the config extends; the config's own rules replace preset rules of one name. */
360
- readonly extends?: readonly Preset[];
402
+ readonly extends?: readonly Preset<X>[];
361
403
  /** Rule names to turn off after the presets are merged, such as `fsd/insignificant-slice`. */
362
404
  readonly off?: readonly string[];
363
405
  /** The rules, or a function that builds them. */
364
406
  readonly rules:
365
- | readonly Rule<E, S, P>[]
366
- | ((builders: RuleBuilders<E, S, P>) => readonly Rule<E, S, P>[]);
407
+ | readonly (Rule<E, S, P> & Named<N, X>)[]
408
+ | ((builders: RuleBuilders<E, S, P, X>) => readonly (Rule<E, S, P> & Named<N, X>)[]);
367
409
  }
368
410
 
369
411
  /** Declares an archangel config, with types that check its names as it is written. */
@@ -371,4 +413,6 @@ export declare function defineConfig<
371
413
  const E extends Elements,
372
414
  const S extends Selectors = {},
373
415
  const P extends Plugins = {},
374
- >(config: Config<E, S, P>): Config<E, S, P>;
416
+ const X extends string = never,
417
+ const N extends string = string,
418
+ >(config: Config<E, S, P, X, N>): Config<E, S, P>;
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@enhance-eng/archangel",
3
- "version": "0.1.0",
4
- "description": "Make architecture verifiable. Architecture rules for TypeScript, checked in milliseconds: imports, cycles, names, folder structure and exports.",
3
+ "version": "0.1.1",
4
+ "description": "Make architecture verifiable. Architecture tests for TypeScript, checked in milliseconds: imports, cycles, names, folder structure and exports.",
5
5
  "keywords": [
6
6
  "architecture",
7
+ "archunit",
8
+ "architecture-tests",
7
9
  "lint",
8
10
  "typescript",
9
11
  "imports",
@@ -51,12 +53,12 @@
51
53
  "pluralize": "8.0.0"
52
54
  },
53
55
  "optionalDependencies": {
54
- "@enhance-eng/archangel-darwin-arm64": "0.1.0",
55
- "@enhance-eng/archangel-darwin-x64": "0.1.0",
56
- "@enhance-eng/archangel-linux-arm64-gnu": "0.1.0",
57
- "@enhance-eng/archangel-linux-arm64-musl": "0.1.0",
58
- "@enhance-eng/archangel-linux-x64-gnu": "0.1.0",
59
- "@enhance-eng/archangel-linux-x64-musl": "0.1.0",
60
- "@enhance-eng/archangel-win32-x64": "0.1.0"
56
+ "@enhance-eng/archangel-darwin-arm64": "0.1.1",
57
+ "@enhance-eng/archangel-darwin-x64": "0.1.1",
58
+ "@enhance-eng/archangel-linux-arm64-gnu": "0.1.1",
59
+ "@enhance-eng/archangel-linux-arm64-musl": "0.1.1",
60
+ "@enhance-eng/archangel-linux-x64-gnu": "0.1.1",
61
+ "@enhance-eng/archangel-linux-x64-musl": "0.1.1",
62
+ "@enhance-eng/archangel-win32-x64": "0.1.1"
61
63
  }
62
64
  }