Skip to content
Zach Collins edited this page Jul 14, 2015 · 2 revisions

Anytime you make changes to your modules or their providers, you'll need to run the pitcher code generator over your module code.

Instead of having a magic-heavy intermediate library to inject dependencies and instantiate your singletons, pitcher provides a very light runtime library and relies instead on code generation to produce the intermediate logic for each of your modules.

The benefits of this approach are generally the following 3 points:

  1. Code generation can leverage static analysis to check for common errors, such as provider type mismatch, missing or circular dependencies, and duplicate definitions.
  2. Stack traces of code generation can be thinner and more clear than runtime libraries, requiring less intermediate invocations and surfacing more of the app's own code.
  3. Explicit code tends to be faster, requiring few method calls and garbage collection. You can produce several small object graphs and throw them away very quickly.

However, to be frank, this does come with some costs that should be considered before leveraging pitcher.

  1. Bigger build pipeline and tooling. You'll need to manage running the code generation tool until typescript itself is improved to have a seamless plugin experience.
  2. Potential bugs in code generator could block a build.

Configuring the code generator tool

Before invoking the code generator, you'll want to separate out your pitcher modules into a separate folder. For demonstrative purposes, we'll suppose that all modules exist in a /modules directory.

Next, we'll need to add an entry into the tsconfig.json file of our project.

{
 ...
 "pitcher": {
   "moduleGlob": "modules/*.ts",
   "moduleOutputDir": "generated-modules",
   "moduleSrcDir": "modules"
 }
}

After rewriting your modules, pitcher will then output the results into generated-modules/<filename>.ts. It is from this directory that you should include your rewritten modules and construct a graph:

import { AppModule } from "generated-modules/app.ts";

var graph = pitcher.forEntry(new AppModule()).build();

If you don't care to distinguish the original source modules and the rewritten ones, you can leave moduleOutputDir empty, and pitcher will simply overwrite your source files. However, keeping the generated modules separate from the source modules keeps your editing files smaller and easier to read.

There are also more advanced configuration options here. However, the above approach is the recommended setup.

Running the code generator

After setting your pitcher config in tsconfig.json, you can finally invoke the generator:

▶ node_modules/.bin/pitcher -h

  Usage: pitcher [options]

  Options:

    -h, --help           output usage information
    -V, --version        output the version number
    -p, --project <dir>  specify the directory from which to search for tsconfig.json file to load pitcher configuration.  defaults to the working directory
    -w, --watch          will continue to watch for file changes and rerun the process when detected
    -b, --batch <ms>     when watch is provided, after detecting a file change, the watch will wait the given ms before executing another run.  defaults to 500

Running pitcher with -w is useful during development to keep your code generated files up to date with changes automatically. However, you may need to pipe the stderr to notice any errors that can occur.

Advanced configuration

The pitcher config json can include the following options:

export interface GeneratorConfig {
    /**
      when provided, module files are determined by the given glob string.  this may be specified with moduleFiles, in which case their results are concatted.
    */
    moduleGlob?: string;
    /**
      when provided, module files are determined by the given paths.  this may be specified with moduleGlob, in which case their results are
      concatted.
    */
    moduleFiles?: string[];
    /**
      specifies the output directory for the generator.  the path is taken by prepending this path to the relative path from moduleSrcDir to each file.
      Defaults to moduleSrcDir.
    */
    moduleOutputDir?: string;
    /**
      specifies the relative src directory for files resolved by moduleFiles and moduleGlob.  The relative path between each file and this directory is appended to the moduleOutputDir when determining where to write the output.
      Defaults to the working directory.
    */
    moduleSrcDir?: string;
    /**
      when true, pitcher will fail when any of the original source files contain errors according to the typescript compiler.  Not needed in most cases, but can be useful in certain build setups where earlier failures are useful, or to save resources when watching file changes. NOTE: When using this mode, make sure your output directory is different from your source directory.  If you provide strict mode when writing modules to the same source, you may get 'stuck' when changing a provider name breaks the generated code.  In general, don't use this unless you know what you are doing.
    */
    strictMode?: boolean;
}

Running the Generator programmatically

If you wish to integrate pitcher more deeply into your toolchain, you can invoke it from your code.

Note that by default, the pitcher/index.d.ts does not include definitions to compile typescript against the generator. You'll need to manually add a reference to the generator.d.ts in your tsconfig or as an ambient module include.

You will also need to reference typescript.d.ts, which you can obtain in the bin directory of typescript itself.

An example usage of invoking the generator can be found by studying pitcher's own code.