Skip to content

Structure & cardinality

Beyond where a single file lives, an architecture often constrains the shape of a folder: how many files of a kind it holds, and whether its barrel re-exports every sibling. These two rule options close those gaps.

barrel

Re-export barrels (index.ts) are conventional but usually unchecked, so a new sibling is silently omitted from the barrel and never surfaces to importers. barrel asserts that an index re-exports every sibling matching a glob.

ts
{
  name: 'schema-barrel',
  files: 'models/**/index.ts',
  barrel: { mustReexport: '../*.model.ts' }, // every sibling model must be re-exported
}

The glob is resolved relative to the index file's own directory. Shapelint reads each matched index, collects its export … from '…' specifiers (extension- agnostic), and flags any sibling that isn't re-exported with ARCH_BARREL_INCOMPLETE, naming the missing module.

contains (cardinality)

requires asserts a companion file exists; it can't say a kind is unique. contains constrains how many files of a kind a folder may hold.

ts
{
  name: 'one-controller-per-feature',
  files: 'features/*',
  contains: {
    '*.controller.ts': 'exactly-one',
    '*.module.ts': 'exactly-one',
  },
}

Here files matches folders (features/*), not files. For each matched folder, every { glob: cardinality } entry counts the matching files:

CardinalityPasses when the count is
'exactly-one'exactly 1
'at-most-one'0 or 1
'at-least-one'1 or more

A feature folder with two *.controller.ts files fails ARCH_CARDINALITY; one with exactly one passes.

Released under the MIT License.