Skip to content

Instantly share code, notes, and snippets.

@swissmanu
Created March 21, 2019 07:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save swissmanu/1c82b173da8559cdc6334c147953c47c to your computer and use it in GitHub Desktop.
Save swissmanu/1c82b173da8559cdc6334c147953c47c to your computer and use it in GitHub Desktop.
Apply Prettier Schematic
import { Rule, Tree } from '@angular-devkit/schematics';
import * as prettier from 'prettier';
import { Observable } from 'rxjs';
export default function(): Rule {
return (tree: Tree) =>
new Observable<Tree>(o => {
prettier.resolveConfig(process.cwd()).then(prettierConfig => {
if (!prettierConfig) {
o.error(new Error('Could not resolve prettier configuration'));
return;
}
tree.visit(path => {
if (path.endsWith('.tsx') || path.endsWith('.ts') || path.endsWith('.jsx') || path.endsWith('.js')) {
const content = tree.read(path);
if (content) {
const formatted = prettier.format(content.toString(), prettierConfig);
tree.overwrite(path, formatted);
}
}
return false;
});
o.next(tree);
o.complete();
});
});
}
{
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"apply-prettier": {
"factory": "./apply-prettier/index",
"description": "Applies prettier to a tree of files",
"private": true
}
}
}
// Simple Usage Example
import { strings } from '@angular-devkit/core';
import {
apply,
chain,
filter,
mergeWith,
noop,
Rule,
schematic,
SchematicContext,
template,
Tree,
url
} from '@angular-devkit/schematics';
import { Schema as Options } from './schema';
export default function(options: Options): Rule {
return chain([
(_tree: Tree, _context: SchematicContext) => {
const templateSource = apply(url('./files'), [
template({
...strings,
...options
}),
schematic('apply-prettier', {}) // <- ❤️
]);
return mergeWith(templateSource);
}
]);
}
@keawade
Copy link

keawade commented Nov 11, 2021

First, thanks so much for publishing this snippet. It was really helpful to see an example of how I might apply arbitrary formatters to the tree before writing to disk.

I ran into a minor issue with current (2.4.1) Prettier:

No parser and no filepath given, using 'babel' the parser now but this will throw an error in the future. Please specify a parser or a filepath so one can be inferred.

I also wanted prettier to format more than just my JS and TS files and Prettier was having trouble because the default parser of babel would not work for some of my files. I was able to resolve this by adding a filepath to the format call options. doing so allowed Prettier to infer which formatter to use for the current file from the extension.

Here is my modified implementation:

import { extname } from 'path';
import { Rule, Tree } from '@angular-devkit/schematics';
import * as prettier from 'prettier';
import { Observable } from 'rxjs';

const FORMATTED_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js', '.json', '.yml', '.yaml', '.html'];

export default (): Rule =>
  (tree: Tree): Observable<Tree> => {
    return new Observable<Tree>((subscriber) => {
      prettier.resolveConfig(process.cwd()).then((prettierConfig) => {
        if (!prettierConfig) {
          subscriber.error(new Error('Could not resolve prettier configuration'));
          return;
        }

        tree.visit((path) => {
          if (FORMATTED_EXTENSIONS.includes(extname(path))) {
            const content = tree.read(path);
            if (content) {
              const formatted = prettier.format(content.toString(), {
                ...prettierConfig,
                filepath: path,
              });
              tree.overwrite(path, formatted);
            }
          }

          return false;
        });
        subscriber.next(tree);
        subscriber.complete();
      });
    });
  };

@swissmanu
Copy link
Author

@keawade nice! thanks for you amendment :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment