# Unified File Handling

## Introduction <a href="#introduction" id="introduction"></a>

The crown jewel of EJS is its unique approach to file handling. Enabling the simple syntax of synchronous imports while leveraging the performance benefits of asynchronous batch loading, developers truly can 'have their cake and eat it, too.'

## File Loader <a href="#file-loader" id="file-loader"></a>

The **file loader** is the very first part of an EJS application that is [booted](https://docs.elentra.org/ejs/1.3/guide/environments/bootstrapping). It is responsible for loading all subsequent classes, [components](https://docs.elentra.org/ejs/1.3/guide/modules/components), and data files.

File handlers are registered with the file loader during the boot process before handing the loader to the application. This ensures consistent behaviour for each file type, regardless of when, where, or how it is loaded.

## File Handlers

A **file handler** is a class that is responsible for processing a given file type after it is loaded but before it is returned to the application.

Each file handler is registered with the file loader and associated with a file extension. The file extension is how the loader knows which handler to run.

EJS includes several file handlers by default, but developers are free to create their own or override these:

* **Class Handler**
  * Extension: None
  * Description: Any file loaded without an extension is considered a JavaScript class file: its dependencies will be matched recursively and the class constructor returned. Additionally, all classes have the `__dirname` and `__filename` properties added to their prototype.
* **JSON Handler**
  * Extension: .json
  * Description: Any file loaded with a JSON extension will be parsed into an object.
* **Vue Handler**
  * Extension: .vue
  * Description: Any file loaded with a .vue extension is considered a single-file VueJS component definition. These files will be split into template, script, and style sections. The script section is parsed as a class to recursively match dependencies, before being combined with the template into a VueJS component constructor. The style portion is appended to the `<head>`directly.

File handlers can be any object with a `handle` method which accepts the filename and file contents and returns a Promise. If the Promise resolves, it must return the processed file.

```javascript
class MyHandler
{
    handle(string filename, string file) : Promise
}
```

{% hint style="info" %}
**Tip:** If a file handler does not include any asynchronous processing, you still must return a Promise, but you can resolve it immediately.

For example, the JSON handler works like this:
{% endhint %}

```javascript
class JsonHandler
{
    handle(filename, file) {
        return Promise.resolve(JSON.parse(file));
    }
}
```

## Path Resolution <a href="#path-resolution" id="path-resolution"></a>

By default, the file loader will load files using the exact path provided when calling `load()`. However, you may pass a script path to the constructor and all filenames will then resolve relative to this path.

In cases where you would like to compute a filename relative to another, the file loader provides a `resolveFilename(filename, contextFilename)` method. This method will replace a leading `./` in `filename` with the pathname of `contextFilename`.

```javascript
let resolvedFilename = loader.resolveFilename('./MyFile.js', 'Path/To/File.js');
// Path/To/MyFile.js
```

It's important to note that the `load()` method will not resolve relative filenames on its own; it will only prepend the base path. However, [use statements](https://docs.elentra.org/ejs/1.3/guide/file-handling/use-statement) are automatically resolved relative to the file in which they are called.

{% code title="/src/Path/To/MyFile.js" %}

```javascript
let loader = new Loader('/src');

loader.load('./MyOtherFile.js'); // error, resolves to /src./MyOtherFile.js
loader.load('/Path/To/MyOtherFile.js'); // success, resolves to /src/Path/To/MyOtherFile.js

use('./MyOtherFile.js'); // success, resolves to /src/Path/To/MyOtherFile.js
use('/Path/To/MyOtherFile.js'); // success, resolves to /src/Path/To/MyOtherFile.js
```

{% endcode %}
