UNPKG

@eik/node-client

Version:

Utilities for working with assets and import maps on an Eik server

219 lines (218 loc) 6.77 kB
/** * @typedef {object} Options * @property {string} [base=null] * @property {boolean} [development=false] * @property {boolean} [loadMaps=false] * @property {string} [path=process.cwd()] */ /** * @typedef {object} ImportMap * @property {Record<string, string>} imports */ /** * An Eik utility for servers running on Node. With it you can: * - generate different URLs to assets on an Eik server depending on environment (development vs production). * - get the import maps you have configured in `eik.json` from the Eik server, should you want to use them in the HTML response. * * @example * ```js * // Create an instance, then load information from `eik.json` and the Eik server * import Eik from "@eik/node-client"; * * const eik = new Eik(); * await eik.load(); * ``` * @example * ```js * // Serve a local version of a file from `./public` * // in development and from Eik in production * import path from "node:path"; * import Eik from "@eik/node-client"; * import fastifyStatic from "@fastify/static"; * import fastify from "fastify"; * * const app = fastify(); * app.register(fastifyStatic, { * root: path.join(process.cwd(), "public"), * prefix: "/public/", * }); * * const eik = new Eik({ * development: process.env.NODE_ENV === "development", * base: "/public", * }); * * // load information from `eik.json` and the Eik server * await eik.load(); * * // when development is true script.value will be /public/script.js. * // when development is false script.value will be * // https://{server}/pkg/{name}/{version}/script.js * // where {server}, {name} and {version} are read from eik.json * const script = eik.file("/script.js"); * * app.get("/", (req, reply) => { * reply.type("text/html; charset=utf-8"); * reply.send(`<html><body> * <script * src="${script.value}" * ${script.integrity ? `integrity="${script.integrity}"` : ""} * type="module"></script> * </body></html>`); * }); * * app.listen({ * port: 3000, * }); * * console.log("Listening on http://localhost:3000"); * ``` */ export default class Eik { /** * @param {Options} options */ constructor({ development, loadMaps, base, path, }?: Options); /** * Reads the Eik config from disk into the object instance, used for building {@link file} links in production. * * If {@link Options.loadMaps} is `true` the import maps * defined in the Eik config will be fetched from the Eik server for * use in {@link maps}. */ load(): Promise<void>; /** * The `"name"` field from the Eik config * @throws if read before calling {@link load} */ get name(): any; /** * The `"version"` field from the Eik config * @throws if read before calling {@link load} */ get version(): any; /** * The `"type"` field from the Eik config mapped to its URL equivalent (eg. "package" is "pkg"). * @throws if read before calling {@link load} */ get type(): any; /** * The `"server"` field from the Eik config * @throws if read before calling {@link load} */ get server(): any; /** * The pathname to the base on Eik (ex. /pkg/my-app/1.0.0/) * @throws if read before calling {@link load} */ get pathname(): string; /** * Similar to {@link file}, this method returns a path to the base on Eik * (ex. https://eik.store.com/pkg/my-app/1.0.0), or {@link Options.base} * if {@link Options.development} is true. * * You can use this instead of `file` if you have a directory full of files * and you don't need {@link Asset.integrity}. * * @returns {string} The base path for assets published on Eik * @throws when {@link Options.development} is false if called before calling {@link load} */ base(): string; /** * Get a link to a file that is published on Eik when running in production. * When {@link Options.development} is `true` the pathname is prefixed * with the {@link Options.base} option instead of pointing to Eik. * * @param {string} pathname pathname to the file relative to the base on Eik (ex: /path/to/script.js for a prod URL https://eik.store.com/pkg/my-app/1.0.0/path/to/script.js) * @returns {import('./asset.js').Asset} * @throws when {@link Options.development} is false if called before calling {@link load} * * @example * ```js * // in production * const eik = new Eik({ * development: false, * }); * await eik.load(); * * const file = eik.file("/path/to/script.js"); * // { * // value: https://eik.store.com/pkg/my-app/1.0.0/path/to/script.js * // integrity: sha512-zHQjnD-etc. * // } * // where the server URL, app name and version are read from eik.json * // { * // "name": "my-app", * // "version": "1.0.0", * // "server": "https://eik.store.com", * // } * ``` * @example * ```js * // in development * const eik = new Eik({ * development: true, * base: "/public", * }); * await eik.load(); * * const file = eik.file("/path/to/script.js"); * // { * // value: /public/path/to/script.js * // integrity: undefined * // } * ``` */ file(pathname?: string): import("./asset.js").Asset; /** * When {@link Options.loadMaps} is `true` and you call {@link load}, the client * fetches the configured import maps from the Eik server. * * This method returns the import maps that were fetched during `load`. * * @returns {ImportMap[]} * @throws if {@link Options.loadMaps} is not `true` or called before calling {@link load} * * @example * ```js * const client = new Eik({ * loadMaps: true, * }); * await client.load(); * * const maps = client.maps(); * ``` */ maps(): ImportMap[]; /** * Function that generates and returns an import map script tag for use in an document head. * * Only a single import map is allowed per HTML document. * A key (ex. `react`) can only be defined once. * If multiple import maps defined in `eik.json` use the same key, the last key wins. * * @example * ``` * const importMap = eik.toHTML(); * * <head> * ... * ${importMap} * ... * </head> * ``` * * @returns {string} */ toHTML(): string; #private; } export type Options = { base?: string; development?: boolean; loadMaps?: boolean; path?: string; }; export type ImportMap = { imports: Record<string, string>; };