Зачем использовать EJS с Vite?

Давайте рассмотрим пример сценария: вы создаете веб-приложение, которое будет работать в пограничных местоположениях CDN с использованием Cloudflare Workers. В этом случае у вас могут быть следующие требования:

  • Вам необходимо настроить обратный прокси-сервер для некоторых сторонних веб-сайтов, таких как Frame, Intercom Helpdesk и т. д.
  • Вы должны иметь возможность вставлять пользовательские фрагменты кода HTML/JS на страницы этих веб-сайтов.
  • Фрагменты кода должны корректно работать в различных средах, таких как рабочая среда и тест/контроль качества.
  • Чтобы оптимизировать комплект приложений, необходимо предварительно скомпилировать эти шаблоны, а не включать библиотеку шаблонов.

В таких случаях использование EJS в сочетании с Vite может быть выгодным выбором.

Как это выглядит?

Фрагмент HTML удобно вынесен в отдельный файл с подсветкой синтаксиса HTML/JS и автодополнением кода (views/analytics.ejs):

<script async src="https://www.googletagmanager.com/gtag/js?id=<%- env.GA_MEASUREMENT_ID %>"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag() {
    dataLayer.push(arguments);
  }
  gtag("js", new Date());
  gtag("config", "<%- env.GA_MEASUREMENT_ID %>");
</script>

В то время как скрипт Cloudflare Worker внедряет его в целевую страницу (HTML), загруженную из Framer:

import { Hono } from "hono";
import analytics from "../views/analytics.ejs";

export const app = new Hono<Env>();

// Serve landing pages, inject Google Analytics
app.use("*", async ({ req, env }, next) => {
  const url = new URL(req.url);

  // Skip non-landing pages
  if (!["/", "/about", "/home"].includes(url.pathname)) {
    return next();
  }

  const res = await fetch("https://example.framer.app/", req.raw);

  return new HTMLRewriter()
    .on("body", {
      element(el) {
        el.onEndTag((tag) => {
          try {
            tag.before(analytics(env), { html: true });
          } catch (err) {
            console.error(err);
          }
        });
      },
    })
    .transform(res.clone());
});

Как предварительно скомпилировать шаблоны EJS с помощью Vite?

Установите модули NPM ejs и @types/ejs в качестве зависимостей разработки (yarn add ejs @types/ejs -D).

Добавьте следующий плагин в ваш файл vite.config.ts:

import { compile } from "ejs";
import { readFile } from "node:fs/promises";
import { relative, resolve } from "node:path";
import { defineConfig } from "vite";

export default defineConfig({
  ...
  plugins: [
    {
      name: "ejs",
      async transform(_, id) {
        if (id.endsWith(".ejs")) {
          const src = await readFile(id, "utf-8");
          const code = compile(src, {
            client: true,
            strict: true,
            localsName: "env",
            views: [resolve(__dirname, "views")],
            filename: relative(__dirname, id),
          }).toString();
          return `export default ${code}`;
        }
      },
    },
  ],
});

Как заставить импорт .ejs работать с TypeScript?

  1. Добавьте **/*.ejs в список включенных файлов в файле tsconfig.json.
  2. Добавьте следующее объявление типа в ваш global.d.ts файл:
declare module "*.ejs" {
  /**
   * Generates HTML markup from an EJS template.
   *
   * @param locals an object of data to be passed into the template.
   * @param escape callback used to escape variables
   * @param include callback used to include files at runtime with `include()`
   * @param rethrow callback used to handle and rethrow errors
   *
   * @return Return type depends on `Options.async`.
   */
  const fn: (
    locals?: Data,
    escape?: EscapeCallback,
    include?: IncludeCallback,
    rethrow?: RethrowCallback,
  ) => string;
  export default fn;
}

kriasoft/relay-starter-kit — это всеобъемлющий шаблон проекта веб-приложения с полным стеком, который предварительно настроен со всеми упомянутыми функциями (находится в папке /edge).

Если вам нужна помощь с веб-инфраструктурой и DevOps, не стесняйтесь обращаться ко мне в Codementor или Discord. Я здесь, чтобы помочь! Удачного кодирования!

Рекомендации