Skip to main content
@craft-ts
LearnLearn with EffectAI agentsGuideReference
@craft-ts/core@craft-ts/component@craft-ts/dev-tools@craft-ts/effect@craft-ts/style@craft-ts/style-testing@craft-ts/i18n@craft-ts/i18n-effect@craft-ts/mcp
ExamplesMigrationEffect compatibility & maturityAdopting CraftTS progressivelyPress kitRoadmapBacklog
Beta
  • Learn
  • Learn with Effect
  • AI agents
  • Guide
  • Reference
  • Packages
  • Resources
  • Guide overview
  • Create a project
i.Core concepts
  • The mental model
  • Which primitive should I use?
  • Anatomy of a primitive
  • Generators and yield*
  • Insertions
  • Typed insertion pipes
  • Exceptions as values
ii.Managing state
  • Local state
  • State machines
  • query
  • Mutations
  • queryParams
  • asyncProcess
  • Selecting
  • Reacting to mutations
  • Collections
  • Collection utilities
  • Persistence
  • GlobalPersisterHandler
  • Pagination placeholders
  • Schema validation
iii.Recommended approaches
  • Inject at the point of use
iv.Structuring the app
  • craftService
  • Service scopes
  • Shaping the public API
  • Abstract services
  • App start
  • Lazy services
  • Server functions
  • craftRegisterFor
  • Target wrapper
v.Routing & type-safe DI
  • Setup
  • CLI automation
  • ESLint rules
  • Route providers
  • Route guards
  • Route exception handling
  • Non-blocking navigation
  • Global error component
  • Route load errors
  • Scaling routes
vi.Components & templates
  • Components
  • Activating the style system
  • Typed styles
  • Defining a design system
  • Tokens and variables
  • Global foundation and fonts
  • Pseudo-elements and animations
  • Axes and the matrix
  • Context obligations
  • Testing visual states
  • Text contrast
  • Visual attestation
  • Template obligations
  • Typed CSS variables
  • Fine-grained reactivity
  • Progressive rendering
  • Directives and .pipe(...)
  • settledValue & pendingNode
  • Accessibility
  • Customization
  • Content projection
  • Styling a component
  • Template migrator
vii.Forms
  • Overview
  • Validators
  • Submitting
  • Nested forms
  • Exception handling
  • Complete examples
viii.Testing
  • Testing services
  • Testing components
  • Type-level tests
  • Browser boundaries
  • Page stability
  • Architecture rules
    • Overview
    • Declarative baseline
    • Unique identities
    • HTTP endpoint ownership
    • Computed purity
    • Dependency cycles
    • Mutation reactions
    • Route DI proofs
    • Route component files
    • Path boundaries
    • Exclusive links
    • Persisted identities
    • insertSelect keys
    • craftEffect and network
    • craftEffect and imperative sync
    • Interactive element names
    • Metric thresholds
    • Server-state loaders
    • Primitive loader requirements
  • Extensible architecture graph
  • Graph insights
  • Craft graph vs Nx
ix.Reactivity utilities
  • craftComputed
  • craftEffect
  • craftMethod
  • source$
  • on$
  • fromEventToSource$
  • sourceFromEvent
  • afterRecomputation
x.Internationalisation
  • Type-safe i18n
  • The catalogue
  • Tokens
  • The runtime
  • With Effect
xi.Deployment (experimental)
  • Overview
  • Manifest reference
  • Diagnostics
  • Providers
  • Alchemy provider
xii.Going further
  • SSR and hydration
  • Effect integration
  • Program operators
  • Pattern matching
  • Observability
  • AI agents
  • Temporal runtime

Folder layout organizer#

craft organize analyzes a fresh Craft dependency graph and proposes a folder layout. It does not move files. The default placement follows route ownership and application-shell reachability; project-specific rules can add known architectural constraints without changing those defaults for other projects.

Tune an organizer run#

The programmatic entry point is organizeProject, exported by @craft-ts/dev-tools and @craft-ts/dev-tools/folder-layout:

ts
import { organizeProject } from '@craft-ts/dev-tools/folder-layout';

organizeProject({
  project: 'apps/shop/tsconfig.graph.json',
  graph: 'craft-dependency-graph.json',
  out: 'apps/shop/folder-layout',
  weights: { calls: 4 },
  thresholds: { maxDepth: 2 },
  placementRules: [
    {
      id: 'app-start-services',
      when: {
        nodeKind: 'service',
        nodeDetails: { appStart: true },
      },
      scope: 'core',
      folder: 'core/app-start',
      reason: 'Runs during application bootstrap.',
    },
  ],
});

weights adjusts the relative influence of graph relations. thresholds controls the maximum proposed feature depth and the fan-in/fan-out values that mark hubs. Omitted values keep their defaults.

Keep project rules in JSON#

The CLI accepts the same weights, thresholds, and placementRules in a JSON file passed with --config:

json
{
  "placementRules": [
    {
      "id": "app-start-services",
      "when": {
        "nodeKind": "service",
        "nodeDetails": { "appStart": true }
      },
      "scope": "core",
      "folder": "core/app-start",
      "reason": "Runs during application bootstrap."
    }
  ]
}
bash
craft organize \
  --project apps/shop/tsconfig.graph.json \
  --graph craft-dependency-graph.json \
  --config apps/shop/organizer.config.json \
  --out apps/shop/folder-layout

The organizer evaluates rules in array order, before its default ownership classification. A rule matches a file when at least one Craft graph node in that file has the selected nodeKind and all selected nodeDetails values. The first matching rule sets the file's scope and destination. folder is relative to the organizer's target root, which defaults to the app's src/ folder when present.

Rules need a unique id, a nodeKind, optional nodeDetails, a scope (feature-local, parent-shared, global-shared, core, or unresolved), a safe relative folder, and a human-readable reason. In TypeScript, nodeKind narrows nodeDetails; for service, keys and values are checked against the service metadata (appStart and browserBoundary are booleans). The JSON CLI validates known service detail keys and value types at runtime. The reason appears with the file's proposal. Resolved rules are written to folder-layout-analysis.json and contribute to the proposal's configHash, so changing policy produces a distinct review artifact. A matching explicit rule has confidence 1; destination collisions still require review.

File-level behavior#

The organizer proposes moves for whole files. If one service in a file matches a rule, the entire file receives that placement. Keep declarations with different folder policies in separate files. If several rules match a file, the first rule wins; put narrower matchers first.

Demo policy: app-start services#

The dependency graph records appStart: true on Craft service nodes. The demo uses that fact in apps/demo/organizer.config.json to place matching source files under src/core/app-start/. This policy is passed only by the demo's attest:demo:folder-layout command, so it does not change the organizer's defaults for other applications.

To regenerate the proposal:

bash
npm run attest:demo:folder-layout

The command refreshes the read-only analysis and proposal artifacts. Apply a proposal separately with craft organize apply after reviewing it.

On this page

  • Tune an organizer run
  • Keep project rules in JSON
  • File-level behavior
  • Demo policy: app-start services

@craft-ts · MIT licensed

GitHubnpm

Search