MishkaCmsCore.Runtime.Compilers.NpmPackages (Mishka CMS Core v0.0.1)

Copy Markdown View Source

The npm packages a JS hook imports, fetched once into the folder esbuild resolves bare imports from.

A kit hook carries its whole pinned tree in its npm field, resolved by the kit's exporter:

[%{"name" => "echarts", "version" => "6.1.0", "path" => "node_modules/echarts",
   "integrity" => "sha512-…"}, …]

Nothing is resolved here. Each entry is downloaded from the registry by its exact name and version, checked against its integrity hash and unpacked at its path under the root — with no node, npm or shell involved.

Once

A package already in place with the same integrity is not fetched again; the check is one small file read, so a compile with every package present makes no request. One process fetches a given package at a time. A folder this module did not write — a package the CMS provisions itself — is used as it is and never replaced.

Safely

  • name, version, path and integrity are checked before anything is requested or written;
  • the download URL is built from the configured registry, never read from a bundle;
  • the download is capped in size and must match its integrity hash before it is unpacked;
  • every file must land inside the package's own folder — an absolute path or a .. refuses the whole package;
  • the package is unpacked into a temporary folder and moved into place only once complete, so an interrupted fetch leaves nothing half-written for esbuild to read.

config :mishka_cms_core, :npm_registry, url names another registry; :npm_req_options passes options to every request — a test's plug:.

Summary

Functions

Puts every package of entries in place under root, in path order so a package is in place before any nested under it. {:error, reason} names the first that could not be.

entry with string keys. Ash's map type turns a key that already exists as an atom into one, so a changeset holds %{:name => …, "integrity" => …} where the database gives strings back.

Whether entry is a package this module can fetch: a valid npm name, an exact version, a path inside node_modules that ends in the name, and a sha512 integrity hash.

Functions

ensure(entries, root)

@spec ensure([map()] | nil, String.t()) :: :ok | {:error, String.t()}

Puts every package of entries in place under root, in path order so a package is in place before any nested under it. {:error, reason} names the first that could not be.

stringify(entry)

@spec stringify(term()) :: term()

entry with string keys. Ash's map type turns a key that already exists as an atom into one, so a changeset holds %{:name => …, "integrity" => …} where the database gives strings back.

valid_entry?(entry)

@spec valid_entry?(term()) :: boolean()

Whether entry is a package this module can fetch: a valid npm name, an exact version, a path inside node_modules that ends in the name, and a sha512 integrity hash.