Can Yeoman generators update existing files?
Asked Answered
S

5

32

So just to give you some context, I'm trying to create a generator that will create some files (based on user input of course) as well as update some existing files in the project (like adding a new route for example).

Creating the files using this.template is no problem... the question is: is there any way to do this with Yeoman without having to read the file using Node and doing some fanciful find and replace?

Sotted answered 4/10, 2013 at 10:4 Comment(0)
S
26

Ok, so I found the answer to my question.

Addy Osmani showed me where to look in this thread on twitter, and then I later found this link which shows exactly what I need.

The gist of it boils down to two functions : readFileAsString and write. Usage is as follows:

var path = "/path/to/file.html",
    file = this.readFileAsString(path);

/* make modifications to the file string here */

this.write(path, file);

Edit: I've also blogged about this on my blog.

 EDIT 1

As mentionned in comments by Toilal :

The write method doesn't exists anymore, and must be replaced by writeFileFromString (arguments are also reversed) – Toilal

EDIT 2

And then, as mentionned in comments by ivoba:

this.writeFileFromString & this.readFileAsString are deprecated, github.com/yeoman/html-wiring should be used by now, things change :) – ivoba

Sotted answered 4/10, 2013 at 16:45 Comment(4)
The write method doesn't exists anymore, and must be replaced by writeFileFromString (arguments are also reversed)Kantianism
this.writeFileFromString & this.readFileAsString are deprecated, github.com/yeoman/html-wiring should be used by now, things change :)Leasia
Is there a way to perform this twice in one generator? Even if I put two of these using different hooks it only applies the second modification.Fishnet
html-wiring is deprecated too.Tiercel
Z
24

Yeoman also provides a more elegant way of fs operations using mem-fs-editor.

You can use this.fs.copy, passing a process function as an option to do any modifications to the content:

this.fs.copy(path, newPath, {
    process: function(content) {

        /* Any modification goes here. Note that contents is a Buffer object */

        var regEx = new RegExp('old string', 'g');
        var newContent = content.toString().replace(regEx, 'new string');
        return newContent;
    }
});

This way you can also take advantage of mem-fs-editor features.

Zorine answered 10/3, 2015 at 22:20 Comment(2)
it is dangerous to use regEx to update a js file, Yeoman documentation discourages this, as stated for@WtowerBroadloom
I agree. though it hasn't been mentioned in the question that the source file is a js file.Zorine
R
5

In combination with @sepans' excellent answer, instead of using regular expressions, one can use some parser.

From Yeoman documentation:

Tip: Update existing file's content

Updating a pre-existing file is not always a simple task. The most reliable way to do so is to parse the file AST and edit it. The main issue with this solution is that editing an AST can be verbose and a bit hard to grasp.

Some popular AST parsers are:

  • Cheerio for parsing HTML.
  • Esprima for parsing JavaScript - you might be interested in AST-Query which provide a lower level API to edit Esprima syntax tree.
  • For JSON files, you can use the native JSON object methods.

Parsing a code file with RegEx is perilous path, and before doing so, you should read this CS anthropological answers and grasp the flaws of RegEx parsing. If you do choose to edit existing files using RegEx rather than AST tree, please be careful and provide complete unit tests. - Please please, don't break your users' code.

More specifically, when using esprima you will most probably require also some generator such as escodegen to generate js back.

var templatePath = this.destinationPath(...);
this.fs.copy(templatePath, templatePath, {
  process: function (content) {
    // here use the parser
    return ...
  }
});

Note that you can use the same path in from, to arguments in order to replace the existing file.

On the other hand, the downside of such parsers is that in some cases it may alter the original file way too much, and although safe, this is more intrusive.

Ravid answered 31/8, 2016 at 7:48 Comment(0)
G
1

Use var text = this.fs.read(filePath) to read from a file and this.fs.write(filePath, content) to write to a file at location.

Guttersnipe answered 8/4, 2019 at 20:32 Comment(0)
P
1

You can use the mem-fs-editor, and call the fs.append(filepath, contents, [options]) method.

e.g.

this.fs.append(this.contextRoot + "/index.html", "  <p>Appended text</p>");
Papillary answered 12/4, 2020 at 17:29 Comment(1)
It is too complicate to edit html and it is not possible to use append to achieve valid result. I would suggest more simple file type for example here. For example .gitignoreCoherent

© 2022 - 2024 — McMap. All rights reserved.