How To Develop

Custom Vercel Runtime

 

01.10.2020

@xf3l1x
f3l1x.io

How to start

How to start

site
└── index.html
$ vercel
$ vercel
Vercel CLI 20.1.1
? Set up and deploy “~/2020-10-01-vercel-meetup/01-zero-config”? [Y/n] y
? Which scope do you want to deploy to? xorg
? Link to existing project? [y/N] n
? What’s your project’s name? 01-zero-config
? In which directory is your code located? ./
No framework detected. Default Project Settings:
- Build Command: `npm run vercel-build` or `npm run build`
- Output Directory: `public` if it exists, or `.`
- Development Command: None
? Want to override the settings? [y/N] n
🔗  Linked to xorg/01-zero-config (created .vercel and added it to .gitignore)
🔍  Inspect: https://vercel.com/xorg/01-zero-config/lvnj9dd0a [1s]
✅  Production: https://01-zero-config.vercel.app [copied to clipboard] [8s]
📝  Deployed to production. Run `vercel --prod` to overwrite later (https://vercel.link/2F).
💡  To change the domain or build command, go to https://vercel.com/xorg/01-zero-config/settings

How to start

site
└── index.html
$ vercel
$ vercel
Vercel CLI 20.1.1
? Set up and deploy “~/2020-10-01-vercel-meetup/01-zero-config”? [Y/n] y
? Which scope do you want to deploy to? xorg
? Link to existing project? [y/N] n
? What’s your project’s name? 01-zero-config
? In which directory is your code located? ./
No framework detected. Default Project Settings:
- Build Command: `npm run vercel-build` or `npm run build`
- Output Directory: `public` if it exists, or `.`
- Development Command: None
? Want to override the settings? [y/N] n
🔗  Linked to xorg/01-zero-config (created .vercel and added it to .gitignore)
🔍  Inspect: https://vercel.com/xorg/01-zero-config/lvnj9dd0a [1s]
✅  Production: https://01-zero-config.vercel.app [copied to clipboard] [8s]
📝  Deployed to production. Run `vercel --prod` to overwrite later (https://vercel.link/2F).
💡  To change the domain or build command, go to https://vercel.com/xorg/01-zero-config/settings

How to start

site
└── index.html
$ vercel
$ vercel
Vercel CLI 20.1.1
? Set up and deploy “~/2020-10-01-vercel-meetup/01-zero-config”? [Y/n] y
? Which scope do you want to deploy to? xorg
? Link to existing project? [y/N] n
? What’s your project’s name? 01-zero-config
? In which directory is your code located? ./
No framework detected. Default Project Settings:
- Build Command: `npm run vercel-build` or `npm run build`
- Output Directory: `public` if it exists, or `.`
- Development Command: None
? Want to override the settings? [y/N] n
🔗  Linked to xorg/01-zero-config (created .vercel and added it to .gitignore)
🔍  Inspect: https://vercel.com/xorg/01-zero-config/lvnj9dd0a [1s]
✅  Production: https://01-zero-config.vercel.app [copied to clipboard] [8s]
📝  Deployed to production. Run `vercel --prod` to overwrite later (https://vercel.link/2F).
💡  To change the domain or build command, go to https://vercel.com/xorg/01-zero-config/settings

How to start

site
└── index.html
$ vercel
$ vercel
Vercel CLI 20.1.1
? Set up and deploy “~/2020-10-01-vercel-meetup/01-zero-config”? [Y/n] y
? Which scope do you want to deploy to? xorg
? Link to existing project? [y/N] n
? What’s your project’s name? 01-zero-config
? In which directory is your code located? ./
No framework detected. Default Project Settings:
- Build Command: `npm run vercel-build` or `npm run build`
- Output Directory: `public` if it exists, or `.`
- Development Command: None
? Want to override the settings? [y/N] n
🔗  Linked to xorg/01-zero-config (created .vercel and added it to .gitignore)
🔍  Inspect: https://vercel.com/xorg/01-zero-config/lvnj9dd0a [1s]
✅  Production: https://01-zero-config.vercel.app [copied to clipboard] [8s]
📝  Deployed to production. Run `vercel --prod` to overwrite later (https://vercel.link/2F).
💡  To change the domain or build command, go to https://vercel.com/xorg/01-zero-config/settings

But! How to start?

site
├── index.html
├── index.php
├── package.json
└── vercel.json
$ vercel
{
    "builds": [
        { "src": "index.html", "use": "@vercel/static" },
        { "src": "package.json", "use": "@vercel/static-build" },
        { "src": "index.php", "use": "vercel-php" }
    ]
}

{ CODE }

{ CODE }

RUNTIME

BUILD PHASE

site
├── index.html
├── index.php
├── package.json
└── vercel.json
{
    "builds": [{
        "src": "index.html",
        "use": "@vercel/static"
    }]
}
$ vercel
builder1.build(ctx)
index.html
dist/main.css
dist/app.js
/api/users
/api/comments

STATIC FILES

LAMBDAS

builderN.build(ctx)

Runtimes

Builders

@vercel/static

@vercel/static-build

@vercel/node

@vercel/go

@vercel/python

@vercel/ruby

officials
community

vercel-bash

vercel-deno

vercel-php

now-rust

vercel-sapper

@nuxtjs/vercel-builder

Runtimes

Builders

@vercel/static

@vercel/static-build

@vercel/node

@vercel/go

@vercel/python

@vercel/ruby

officials
community

vercel-bash

vercel-deno

vercel-php

now-rust

vercel-sapper

@nuxtjs/vercel-builder

Markdown Runtime

MD → HTML

Why

  • new stuff

  • challenge

  • learn sth

  • have fun

Research

Research

Runtime API

interface Runtime {
  version: number;
  
  build: (options: BuildOptions) => Promise<BuildResult>;
  
  analyze?: (options: AnalyzeOptions) => Promise<string>;
  
  prepareCache?: (options: PrepareCacheOptions) => Promise<CacheOutputs>;
  
  shouldServe?: (options: ShouldServeOptions) => Promise<boolean>;
  
  startDevServer?: (
    options: StartDevServerOptions
  ) => Promise<StartDevServerResult>;
}

Runtime API

interface Runtime {
  
  build: (options: BuildOptions) => Promise<BuildResult>;
  
}

Runtime

[file structure]
runtime
├── src
│   └── index.ts
├── package.json
├── tsconfig.json
└── README.md

Runtime

[package.json]
{
  "name": "vercel-md",
  "description": "Markdown Runtime for Vercel platform",
  "version": "0.0.1",
  "license": "MIT",
  "main": "./dist/index.js",
  "files": [
    "dist"
  ],
  "dependencies": {
    "markdown-it": "^11.0.1"
  },
  "devDependencies": {
    "@types/node": "^14.0.14",
    "@vercel/build-utils": "^2.4.0",
    "typescript": "^4.0.2"
  }
}

Runtime

[src/index.ts]
import { BuildOptions } from "@vercel/build-utils";

export async function build(options: BuildOptions): Promise<any> {
  
  console.log(options);

};
site
├── index.md
└── vercel.json

Runtime

[src/index.ts]
import { FileBlob, BuildOptions } from "@vercel/build-utils";
import markdownit from 'markdown-it';

export async function build({ files, entrypoint }: BuildOptions): Promise<any> {
  const file = await FileBlob.fromStream({
    stream: files[entrypoint].toStream(),
  });

  const md = new markdownit();
  const result = new FileBlob({
    data: md.render(file.data.toString())
  });

  const replacedEntrypoint = entrypoint.replace(/\.[^.]+$/, '.html');

  return { [replacedEntrypoint]: result };
};

Runtime

[src/index.ts]
import { FileBlob, BuildOptions } from "@vercel/build-utils";
import markdownit from 'markdown-it';

export async function build({ files, entrypoint }: BuildOptions): Promise<any> {
  const file = await FileBlob.fromStream({
    stream: files[entrypoint].toStream(),
  });

  const md = new markdownit();
  const result = new FileBlob({
    data: md.render(file.data.toString())
  });

  const replacedEntrypoint = entrypoint.replace(/\.[^.]+$/, '.html');

  return { [replacedEntrypoint]: result };
};

Runtime

[src/index.ts]
import { FileBlob, BuildOptions } from "@vercel/build-utils";
import markdownit from 'markdown-it';

export async function build({ files, entrypoint }: BuildOptions): Promise<any> {
  const file = await FileBlob.fromStream({
    stream: files[entrypoint].toStream(),
  });

  const md = new markdownit();
  const result = new FileBlob({
    data: md.render(file.data.toString())
  });

  const replacedEntrypoint = entrypoint.replace(/\.[^.]+$/, '.html');

  return { [replacedEntrypoint]: result };
};
import { FileBlob, BuildOptions } from "@vercel/build-utils";
import markdownit from 'markdown-it';

export async function build({ files, entrypoint }: BuildOptions): Promise<any> {
  const file = await FileBlob.fromStream({
    stream: files[entrypoint].toStream(),
  });

  const md = new markdownit();
  const result = new FileBlob({
    data: md.render(file.data.toString())
  });

  const replacedEntrypoint = entrypoint.replace(/\.[^.]+$/, '.html');

  return { [replacedEntrypoint]: result };
};
index.md → index.html

Runtime

[src/index.ts]

Runtime

[src/index.ts]
import { FileBlob, BuildOptions } from "@vercel/build-utils";
import markdownit from 'markdown-it';

export async function build({ files, entrypoint }: BuildOptions): Promise<any> {
  const file = await FileBlob.fromStream({
    stream: files[entrypoint].toStream(),
  });

  const md = new markdownit();
  const result = new FileBlob({
    data: md.render(file.data.toString())
  });

  const replacedEntrypoint = entrypoint.replace(/\.[^.]+$/, '.html');

  return { [replacedEntrypoint]: result };
};

Runtime

[publishing]
npm publish
{
    "builds": [
        { "src": "*.md", "use": "vercel-md" }
    ]
}

Conclusion

  • speedup workflow

  • take challenges

  • explore & conquer

  • have fun

Juicy Functions

Juicy Functions

Time to selfie 📷

Thank you!

@xf3l1x
f3l1x.io