4

What is the best way to extend an existing Angular Schematic? I'm looking specifically at adding some default code into a component's spec file at the moment, but a more generalized answer would be appreciated.

In the tutorials I've found, they show the externalSchematic function, which seems to be on the right track, but none of them show how to update/replace that schematic's template file. I've tried just copying the templates into my schematic and applying/merging them, but that seems a little overkill. Angular's documentation on this matter also seems scarce.

Is there a way to do extend the default schematics, or will I need to do everything from scratch?

Logan Waite
  • 423
  • 5
  • 12
  • I want to back up your question cause I've been through the same problem here. Right now, to me, the hacky solution is to copy source code from @schematics/angular and tinkering them to fit my need. It's quite a frustrating and not-any-better-than-scratch workaround, but If you really need I can post it in answers. – YGLin Aug 13 '19 at 09:39
  • 1
    I've moved on for the moment; I did subscribe to [this issue](https://github.com/angular/angular/issues/29241) to know when the api docs will show up for the next time, but I don't know when that will be. – Logan Waite Aug 13 '19 at 18:04

1 Answers1

3

I definitely agree that the documentation is sparse. The most helpful resource I found for extending existing schematics is this article: https://blog.angular.io/schematics-an-introduction-dc1dfbc2a2b2

Under the heading 'Calling Another Schematic', it has some example code on how to modify a newly create component, which seems like what you're looking for. At the end of the day, you should call the existing angular component schematic and then find the file(s) you want to modify (in your case, the .spec.ts file), and finally insert the new code you want:

import { Rule, SchematicContext, Tree, chain, externalSchematic } from '@angular-devkit/schematics';

const sText = "test to insert";

return chain([
    // Here you can modify options or do any preprocessing before calling the schematic (optional)
    (cTree: Tree, _context: SchematicContext) => {     
        return cTree;
    },

    // Call the external schematic
    externalSchematic('@schematics/angular', 'component', _options),

    // Do whatever downstream processing you need to 
    (cTree: Tree, _context: SchematicContext) => {

        // Find new component, which depends on where you put it in the tree
        // Some approaches prefer to scan the entire tree looking for the new file,
        // I prefer to narrow my search down a bit.
        cTree.getDir('.')
            .visit((sTempFilePath) => {

                if (!sTempFilePath.endsWith(dasherize(_options['name']) + '.component.spec.ts')) {
                    // Skip anything but the newly added typescript spec file
                    return;
                }

                // Now that we've found our new component file, read in the content
                const cContentBuffer = cTree.read(sTempFilePath);
                if (!cContentBuffer) {
                    return;
                }

                // Add text at beginning of file (can customize to add anywhere)
                cTree.overwrite(
                    sTempFilePath,
                    sText + cContentBuffer);

            });

        return cTree;       
    }

]);

One final note - sometimes I've found that I need to include a schema.json in my own schematic in order to utilize the angular schematics.

Hope that helps!

RocketMan
  • 441
  • 4
  • 15