Transformer Plugin
@automapper/classes/transformer-plugin is part of the public API of @automapper/classes.
Problem
Decorating Classes' members with @AutoMap() is verbose, even when we're being meticulous about what members are being auto-configured vs custom-configured. In some other cases, the Classes themselves are being generated, and/or from external libraries that the consumers cannot touch.
@automapper/classes/transformer-plugin is to ease this pain point when using @automapper/classes
How it works
Let's look at the following classes
class Profile {
    bio!: string;
    age!: number;
}
class User {
    firstName!: string;
    lastName!: string;
    profile!: Profile;
}
Throughout the documentation, we all know that the above code will be compiled to
class Profile {}
class User {}
The requirement for @automapper/classes to work is to decorate all the members of both classes with @AutoMap in order for @automapper/classes to keep track of the metadata of each class.
class Profile {
    @AutoMap()
    bio!: string;
    @AutoMap()
    age!: number;
}
class User {
    @AutoMap()
    firstName!: string;
    @AutoMap()
    lastName!: string;
    @AutoMap(() => Profile)
    profile!: Profile;
}
This will get very verbose very soon.
@automapper/classes/transformer-plugin runs a before transformer that affects the AST directly before the Compilation step.
The transformer will
- Look at files that end with .entity.ts,.model.ts,.vm.ts, and.dto.ts(this can be changed via transformer plugin options)
- Iterate through all the members (PropertyDeclaration) of each class (ClassDeclaration) that it finds
- Store the members in a list that @automapper/classescan understand
- Add to each class a static methodand return the list.
Let's look at the above snippet again
// your code
class Profile {
    bio!: string;
    age!: number;
}
class User {
    firstName!: string;
    lastName!: string;
    profile!: Profile;
}
// after "before" transformer runs through your code
class Profile {
    bio!: string;
    age!: number;
    static __AUTOMAPPER_METADATA_FACTORY__() {
        return [
            ['bio', { type: () => String, depth: 1 }],
            ['age', { type: () => Number, depth: 1 }],
        ];
    }
}
class User {
    firstName!: string;
    lastName!: string;
    profile!: Profile;
    static __AUTOMAPPER_METADATA_FACTORY__() {
        return [
            ['firstName', { type: () => String, depth: 1 }],
            ['lastName', { type: () => String, depth: 1 }],
            ['profile', { type: () => Profile, depth: 1 }],
        ];
    }
}
After compilation, the members will be gone, but the static function will stay making it available to be called at runtime. Hence, the metadata will be available. @automapper/classes/transformer-plugin only adds the minimum amount of code needed to keep track of the metadata.
Limitations
Currently, @automapper/classes/transformer-plugin will handle most Nullable (type | null) and Maybe (propKey?: type) cases. However, for complex cases where you have unions with different types (string | number | boolean or ClassA | ClassB), please consider decorate the property (field) manually with @AutoMap() decorator.
Usage
This is utilizing an experimental feature of TypeScript. Hence, you need to modify the build step of your project to use @automapper/classes/transformer-plugin
Ignore a property
The plugin will automatically construct the metadata for all properties that aren't decorated with @AutoMap. However, there are also cases where you neither want @AutoMap nor having the plugin processing a property (eg: you want to take the control 100% with manual configuration). To ignore a property completely, you can use a JSDoc tag @autoMapIgnore on a property
class User {
    firstName!: string;
    lastName!: string;
    profile!: Profile;
    /**
     * @autoMapIgnore
     */
    ignoreMe!: string;
}
Options
export interface AutomapperTransformerPluginOptions {
    modelFileNameSuffix?: string[];
}
modelFileNameSuffix is default to ['.entity.ts', '.model.ts', '.vm.ts', '.dto.ts']
Webpack
If you use ts-loader or some fork of ts-loader, you can configure Webpack config to turn on Transformers
// snip
const automapperTransformerPlugin = require('@automapper/classes/transformer-plugin');
const pluginOptions = {
    modelFileNameSuffix: [
        /*...*/
    ],
};
module.exports = {
    // snip
    module: {
        rules: [
            // snip
            {
                test: /\.tsx?$/,
                loader: 'ts-loader',
                options: {
                    getCustomTransformers: (program) => ({
                        before: [
                            automapperTransformerPlugin(program, pluginOptions)
                                .before,
                        ],
                    }),
                },
            },
            // snip
        ],
    },
    // snip
};
Rollup
Use rollup-plugin-typescript2 as it has transformers capability
import automapperTransformerPlugin from '@automapper/classes/transformer-plugin';
import typescript from 'rollup-plugin-typescript2';
const pluginOptions = {
    modelFileNameSuffix: [
        /*...*/
    ],
};
export default {
    // snip
    preserveModules: true, // <-- turn on preserveModules
    plugins: [
        // snip
        typescript({
            transformers: [
                (service) => ({
                    before: [
                        automapperTransformerPlugin(
                            service.getProgram(),
                            pluginOptions
                        ).before,
                    ],
                }),
            ],
        }),
        // snip
    ],
};
ttypescript
ttypescript patches typescript in order to use transformers in tsconfig.json. See ttypescript's README for how to use this with module bundlers such as webpack or Rollup.
{
  "compilerOptions": {
    ...,
    "plugins": [
        {
            "transform": "@automapper/classes/transformer-plugin",
            "modelFileNameSuffix": [...]
        }
    ],
    ...
  }
}
NestJS CLI
nestjs/cli can enable Transformers by default. To use this plugin with nestjs/cli, modify your nest-cli.json
{
    "collection": "@nestjs/schematics",
    "sourceRoot": "src",
    "compilerOptions": {
        "plugins": ["@automapper/classes/transformer-plugin"]
    }
}
or with options
{
    "collection": "@nestjs/schematics",
    "sourceRoot": "src",
    "compilerOptions": {
        "plugins": [
            {
                "name": "@automapper/classes/transformer-plugin",
                "options": {
                    "modelFileNameSuffix": [".dto.ts", ".vm.ts"]
                }
            }
        ]
    }
}
NestJS with Nx
Nx v12.8 adds support for TypeScript Compiler plugins via an option called tsPlugins (now transformers) in their @nrwl/node:webpack executor, which is what @nrwl/nest is using for building the application.
Read more about the usage here: Nx 12.8 Blog post
Pre 12.8
NestJS in Nx workspace utilizes nrwl/node:build executor (formerly, builder) which allows you to pass in a custom Webpack config. However, to turn on Transformer, there's still this open issue in which you can find multiple solutions/workarounds as of the moment.
Angular
Angular CLI has sophisticated build process so please take a look at this article and related articles mentioned to come up with your approach of turning on Transformers for Angular projects