How to export from Ulysses to Markdown
Tags: ulysses, markdown, automation
Published: 2021-09-26 03:37:20

---
title: "How to export from Ulysses to Markdown"
published: true
tags: [ulysses, markdown, automation]
canonical_url: https://www.emgoto.com/ulysses-to-markdown/
cover_image: https://www.emgoto.com/ulysses-to-markdown-dev.png
---
I use the [Ulysses](https://ulysses.app/) text editor to write all my posts. Converting them into Markdown files for my blog isn’t as easy as copy-pasting, especially when things like [frontmatter](https://jekyllrb.com/docs/front-matter/) and images are involved. 

So here's the script I use to convert Ulysses files to Markdown.

1. Right click a sheet in Ulysses and choose the **Quick Export** option. Choose the Textbundle format.
2. Save the Textbundle folder to your desired location. I save it inside the `scripts` folder of my repository.
3. Then run the following script to convert the Ulysses file to Markdown.

```js
#!/usr/bin/env node

const { renameSync, unlink, rmdir } = require('fs');
const { join } = require('path');
const glob = require('glob');
const replace = require('replace-in-file');

const toFrontMatter = (title) =>
    `---
title: "${title}"
date: ${new Date().toISOString().substring(0, 10)}
tags: []
---`;

const ulysses = async () => {
    const file = glob.sync(
        join(process.cwd(), 'scripts', '**', '*.textbundle'),
    )[0];

    // Moves the .textbundle file to the /posts directory
    const slug = file.match(/\/([^\/]+).textbundle/)[1];
    const postsDirectory = join(process.cwd(), 'posts');
    let newFolder = `${postsDirectory}/${slug}`;
    renameSync(file, newFolder);

    // Move images out of assets folder
    const images = glob.sync(join(newFolder, 'assets', '*'));
    images.forEach((image) => {
        let imageName = image.match(/assets\/([^\/]+)/)[1];
        const newFolder = `${postsDirectory}/${slug}`;

        renameSync(image, `${newFolder}/${imageName}`);
    });

    // Remove redundant folder and files
    rmdir(`${newFolder}/assets`, () => {});
    unlink(`${newFolder}/info.json`, () => {});

    // Formats into frontmatter structure
    const headerRegex = /^# .*/g;
    const markdownPngRegex = /!\[.*\]\(assets.*png\)/g;

    const headerFn = (title) =>
        toFrontMatter(title.replace('# ', '').trim());
    const markdownPngFn = (line) => line.replace(/\(assets/, '(.');

    const options = {
        files: `${newFolder}/index.mdx`,
        from: [headerRegex, markdownPngRegex],
        to: [headerFn, markdownPngFn],
    };

    return replace(options);
};

ulysess();
```

You can run the script directly with:

```sh
node ./scripts/ulysses.js
```

Or you can add the script to your `package.json` file:

```json
"scripts": {
    "uly": "./scripts/ulysses.js",
},
```

And then run it with `yarn uly`.

## How the Ulysses to Markdown script works
Exporting a Ulysses file as a Textbundle saves it with the following file structure:

```
scripts
	ulysses-to-markdown.textbundle
	    info.json
	    text.md
	    assets
	        image.png
```

The script will rearrange it to look like this:
```
posts
    ulysses-to-markdown
        text.md
        image.png
```

The original Markdown file, directly from Ulysses, will look like this:

```md
# How to export from Ulysses to Markdown

Here's the content of the post. 

![](assets/image.png)
```

And the script will convert it into this format:
```md
---
title: "How to export from Ulysses to Markdown"
date: 2021-09-25
tags: []
---
Here's the content of the post. 

![](./image.png)
```