Skip to content

Project

The Project abstraction lets us write code to understand and modify code, at a level above the filesystem. A Project represents the contents of a repository at a particular point in time. For most SDM operations, it is backed by a clone of the repository on the filesystem. For testing, you can use a project backed by a local directory or stored in memory.

See the API Doc for the full interface, and the methods in projectUtils for related helpers.

Obtaining a Project

The SDM has many APIs where you define a function that receives a Project as an argument. For instance, code transforms and code inspections receive already-cloned projects. A push test gets a Project to look at, which is cloned lazily if the push test uses it.

If you’re working in another goal, then you have access to a GoalInvocation, and you can get request a clone and then do something with it. See an example under running a command.

For Testing

To get a project for testing, use [InMemoryProject][apidoc-imp]. Its of factory method accepts any number of objects. Each specifies the path and contents of a file in the project.

For example:

const input = InMemoryProject.of({
            path: "README.me",
            content: `# Hello There

and some stuff
`,
}, { 
    path: "empty.md",
    content: "",
});

This example uses TypeScript’s multiline strings (delimited with backtick) to specify the file content.

Checking the content

There are many ways to look around the Project.

Does a particular file exist?

Call hasFile on the project.

const hasMyFile = await project.hasFile("path/to/file");

There is also hasDirectory to check for directory existence.

What is in a file?

Call getFile and then getContent.

const myFile = await project.getFile("path/to/file");
if (myFile) {
    const content: string = await myFile.getContent();
}

Does a file exist, for various conditions?

Use projectUtils.fileExists to look for files with a certain extension or property. The second argument is a glob, and the third is a function from File to boolean.

const result = await projectUtils.fileExists(project,
            "**/*",
            f => f.isExecutable()
        );

Extract information from files

Use projectUtils.gatherFromFiles to extract information from all the files with matching names. Pass the project, a glob, and a function from File to whatever it is you want to get back.

This example returns an array with the first line of every Java file:

const firstLines = await gatherFromFiles<string>(project, "**/*.java", async f => {
            const lines = await f.getContent().then(c => c.split("\n"));
            return lines.length > 0 ? lines[0] : "";
        })

File Globs

Serveral methods in [projectUtils][apidoc-projectUtils] accept a GlobOptions as a parameter. The GlobOptions is a string or an array of strings.

In a glob:

* `*` stands for any string or part of a string in a filename
* `**` stands for any number of directories, so matches recursively in a path
* Pass any number of pattern strings to include, followed by any number of negative patterns to exclude
* Start a pattern with `!` to make it a negation

For example, all TypeScript files that aren’t tests:

["**/*.ts", "!**/*.test.ts"]

Parse Code

Code is more than text. Express what you’re looking for in terms of your programming language using Atomist Path Expressions.

Modifying the content

Check the Project API for methods like:

  • addFile
  • deleteFile
  • deleteDirectory (and all its content)
  • makeExecutable (give a file execute permissions)
  • moveFile

Update Code

Code is more than text. Change code in place based on contextual understanding. Use Path Expressions.

Saving modifications

If you’re writing a code transform, your changes will be saved for you, by a commit in an autofix or in a pull request in a transform command.

If you’re writing a custom goal of some kind, and working with a clone that you requested, then check the GitProject interface for methods like:

  • commit
  • createBranch
  • push
  • raisePullRequest
  • checkout
  • hasBranch

Accessing the filesystem directly

If the Project is on the filesystem (usually a GitProject), then project.baseDir will give you its path on the filesystem.