HomeGuidesReferenceLearn

TypeScript functions

Learn how to create and use EAS Build functions in your custom build configurations.


This functionality is in preview, under active development, and likely to change.

EAS Build functions are a great way to extend the functionality of custom builds. You can use them to create reusable steps, and to write your logic in JavaScript, TypeScript, or Bash (more information in command in the config schema). This guide provides a walkthrough of creating a function in TypeScript.

1

Initialize an EAS Build function module

The easiest way to create an EAS Build function is to use the create-eas-build-function CLI tool. By running the following command from the same directory as your eas.json file, you can create a new custom TypeScript function:

Terminal
npx create-eas-build-function@latest ./.eas/build/myFunction

This creates a new module called myFunction in the .eas/build directory. The module will contain a pre-generated module configuration and the src directory with the index.ts file containing the default TypeScript function template.

.eas/build/myFunction/src/index.ts
// This file was autogenerated by `create-eas-build-function` command.
// Go to README.md to learn more about how to write your own custom build functions.

import { BuildStepContext } from '@expo/steps';

// interface FunctionInputs {
//   // specify the type of the inputs value and whether they are required here
//   // example: name: BuildStepInput<BuildStepInputValueTypeName.STRING, true>;
// }

// interface FunctionOutputs {
//   // specify the function outputs and whether they are required here
//   // example: name: BuildStepOutput<true>;
// }

async function myFunction(
  ctx: BuildStepContext
  // {
  //   inputs,
  //   outputs,
  //   env,
  // }: {
  //   inputs: FunctionInputs;
  //   outputs: FunctionOutputs;
  //   env: BuildStepEnv;
  // }
): Promise<void> {
  ctx.logger.info('Hello from my TypeScript function!');
}

export default myFunction;

2

Compiling the function and connect it to the config file

After generating the function, you will want to compile it and connect it to your custom build YAML configuration.

Compile the function

Functions must be compiled to a single JavaScript file that can be run without installing any Node Module dependencies. The default build script for generated functions uses the ncc to compile your function into a single file with all its dependencies. If you don't have the ncc installed globally on your machine, run npm install -g @vercel/ncc to install it. Next, run the build script in the .eas/build/myFunction directory:

Terminal
npm run build

This command triggers the build script placed in the package.json file of your custom function module.

package.json
{
  ...
  "scripts": {
    ...
    "build": "ncc build ./src/index.ts -o build/ --minify --no-cache --no-source-map-register"
    ...
  },
  ...
}

The build script generates build/index.js. This file must be uploaded to EAS Build as a part of your project archive, so that the builder can run your function. Ensure that the file is not excluded by a .gitignore file or .easignore file.

Connect the function to the custom build config

Let's assume that you have a config.yml file in the .eas/build directory. It contains the following content:

.eas/build/config.yml
build:
  name: My example config
  steps:
    - eas/checkout
    - run:
        name: Install dependencies
        command: npm install
    - run:
        name: Finished
        command: echo Finished

To add your function to the config, you need to add the following lines to the config.yml file:

.eas/build/config.yml
build:
  name: My example config
  steps:
    - eas/checkout
    - run:
        name: Install dependencies
        command: npm install
    - run:
        name: Finished
        command: echo Finished

functions:
  my_function:
    name: My function
    path: ./myFunction

The path property should be a relative path from the config file to your function directory. In this case, it's just ./myFunction.

Now, add a call to the my_function function in the config.yml file:

.eas/build/config.yml
build:
  name: My example config
  steps:
    - eas/checkout
    - run:
        name: Install dependencies
        command: npm install
    - my_function
    - run:
        name: Finished
        command: echo Finished

functions:
  my_function:
    name: My function
    path: ./myFunction

3

Working on the function

For a more advanced example, let's say you want to make a function calculate the sum of two numbers and print the result to the console, and then output that value from the function. To do this, modify the config.yml and index.ts files to make the function accept two inputs called num1 and num2 and return their sum as an output called sum.

.eas/build/config.yml
build:
  name: My example config
  steps:
    - eas/checkout
    - run:
        name: Install dependencies
        command: npm install
    - my_function:
        inputs:
          num1: 1
          num2: 2
        id: sum_function
    - run:
        name: Print the sum
        inputs:
          sum: ${ steps.sum_function.sum }
        command: echo ${ inputs.sum }
    - run:
        name: Finished
        command: echo Finished

functions:
  my_function:
    name: My function
    inputs:
      - name: num1
        type: number
      - name: num2
        type: number
    outputs:
      - name: sum
    path: ./myFunction
.eas/build/myFunction/src/index.ts
// This file was autogenerated by `create-eas-build-function` command.
// Go to README.md to learn more about how to write your own custom build functions.

import {
  BuildStepContext,
  BuildStepInput,
  BuildStepInputValueTypeName,
  BuildStepOutput,
} from '@expo/steps';

interface FunctionInputs {
  // first template argument is the type of the input value, second template argument is a boolean indicating if the input is required
  num1: BuildStepInput<BuildStepInputValueTypeName.NUMBER, true>;
  num2: BuildStepInput<BuildStepInputValueTypeName.NUMBER, true>;
}

interface FunctionOutputs {
  // template argument is a boolean indicating if the output is required
  sum: BuildStepOutput<true>;
}

async function myFunction(
  ctx: BuildStepContext,
  {
    inputs,
    outputs,
  }: // env,
  {
    inputs: FunctionInputs;
    outputs: FunctionOutputs;
    // env: BuildStepEnv;
  }
): Promise<void> {
  ctx.logger.info(`num1: ${inputs.num1.value}`);
  ctx.logger.info(`num2: ${inputs.num2.value}`);

  const sum = inputs.num1.value + inputs.num2.value;

  ctx.logger.info(`sum: ${sum}`);

  outputs.sum.set(sum.toString()); // Currently, outputs must be strings. This will improve in the future.
}

export default myFunction;
Remember to compile your function each time you make changes to it: npm run build.

Summary

  • Writing functions is a great way to extend the functionality of custom builds with your own logic.
  • EAS Build functions are reusable — you can use them in multiple custom build configurations.
  • Using EAS Build functions is a great option for more advanced use cases that are not easy to do by writing shell scripts.
  • Most of the built-in functions are open-source and can be forked or used as a reference for writing your own functions.

Check out the example repository for more detailed examples:

Custom build example repository

A custom EAS Build example that includes examples for workflows such as setting up functions, using environment variables, uploading artifacts, and more.