npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pretextbook/import

v0.14.0

Published

Shared import helpers for converting LaTeX/Markdown/PreTeXt into PreTeXt for VS Code and web apps

Readme

@pretextbook/import

Shared helpers for importing existing source content into PreTeXt.

Two use cases:

  • VS Code extension logic: format detection, cleaning, and conversion utilities.
  • Web apps: React components for paste-or-upload import UX, plus a non-React library entry point suitable for server-side or extension-host use.

The LaTeX cleaning pipeline is a TypeScript port of davidfarmer/PreprocessLaTeX (src/main.js's describeFiles flow): drop comments, normalize whitespace, expand \input/\include, rewrite plain-TeX font directives, scrub presentation macros, and convert via @pretextbook/latex-pretext.

Install

npm install @pretextbook/import

Utilities

import {
  detectSourceFormat,
  convertSourceToPretext,
  importProjectFromFiles,
} from "@pretextbook/import";

const format = detectSourceFormat(rawInput);
const result = convertSourceToPretext(rawInput, format);

if ("pretextError" in result) {
  console.error(result.pretextError);
} else {
  console.log(result.pretextSource);
  console.log(result.warnings); // cleaning diagnostics
}

// Multi-file project import (file paths -> source text):
const project = importProjectFromFiles({
  "main.tex": "\\documentclass{article}\\begin{document}Hi.\\end{document}",
});
if ("outputFiles" in project) {
  // Standard PreTeXt project layout, keyed by path:
  console.log(project.outputFiles["source/main.ptx"]);
  console.log(project.outputFiles["project.ptx"]);
  console.log(project.outputFiles["publication/publication.ptx"]);
}

Division splitting

The converted document is split into one file per division, with source/main.ptx rebuilt around xi:include references. How deep to split is splitLevel:

importProjectFromFiles(files, {
  documentKind: "book", // override auto-detect
  splitLevel: 2, // 0 = one file; 1 = chapters; 2 = + their sections
});

splitLevel defaults to 1 for a book and 0 for an article. Every PreTeXt division splits, not just chapters and sections — <frontmatter>, <part>, <appendix>, <worksheet> and friends all get their own file. Files use the usual prefixes (ch-intro.ptx, sec-limits.ptx), divisions a document has one of are named for themselves (frontmatter.ptx), and a division's children live in a directory named after it (source/ch-intro/sec-first.ptx).

The older splitChapters / splitSections booleans still work.

Importing an existing PreTeXt project

If an upload contains a project.ptx, it is imported as a project rather than as loose source: the first non-standalone target's source is resolved through its xi:includes, re-split by division, and written back at its original path. The publication file, images, custom XSL, and everything else are carried over untouched at their original paths, so <image source="…"/> and <directories external="…"/> keep resolving. project.ptx is regenerated, preserving every original target, and stale output/ directories are dropped.

const result = importProjectFromFiles(files, { assets });
result.analysis.manifest?.targets; // what the project declared
result.projectLayout; // { mainSourcePath, publicationPath, preserved: true }

Pass preserveProjectLayout: false to import it as loose source into the standard layout instead.

Choosing among several sources

analyzeImportSources surveys an uploaded file set without converting anything — use it to drive a format dropdown or a main-file picker, then pass the answers back:

import {
  extractUpload,
  analyzeImportSources,
  importProjectFromFiles,
} from "@pretextbook/import";

const { files, assets } = await extractUpload(file);
const analysis = analyzeImportSources(files);

analysis.formats; // e.g. ["latex", "markdown"]
analysis.candidates; // every file that could be the document root
analysis.primary; // the one that will be used by default
analysis.extraRoots; // other standalone documents in the upload

const result = importProjectFromFiles(files, {
  assets,
  sourceFormat: "latex", // force a format
  mainFile: "book/main.tex", // force the root
  attachRoots: [
    // fold the others in as divisions
    { path: "appendix.tex", level: "chapter" },
    { path: "notes.tex", include: false },
  ],
});

When several files are standalone documents (a \documentclass per chapter is a common LaTeX habit), the extras are attached to the main document as chapters or sections by default — their bodies are spliced in before conversion, with their own \inputs expanded and their preamble macros hoisted. Pass attachRoots: false to import the main file alone.

Upload pipeline

import { handleImportUploadFile } from "@pretextbook/import";

const result = await handleImportUploadFile(file);
if ("pretextError" in result) {
  console.error(result.pretextError);
} else {
  // Text files (the converted PreTeXt project + routed .bib auxiliaries)
  for (const [path, content] of Object.entries(result.outputFiles)) {
    saveOrUpload(path, content);
  }
  // Binary assets (images, PDFs, EPS) routed to source/assets/
  for (const [path, bytes] of Object.entries(result.outputAssets)) {
    saveBinary(path, bytes);
  }
  // Optional cleaned native source (LaTeX/Markdown), if the input was that format
  if (result.nativeOutputFiles) {
    for (const [path, content] of Object.entries(result.nativeOutputFiles)) {
      saveOrUpload(path, content);
    }
  }
}

Supports .tex, .md, .ptx, .xml, .zip, and .tar.gz uploads. For zip and tar.gz archives, the extractor identifies the main source file, expands LaTeX \input/\include or PreTeXt xi:include references, and routes binaries (.png, .jpg, .pdf, .eps, ...) into source/assets/ and .bib files into source/. An archive that contains a project.ptx keeps its own layout instead — see above.

React Components

import { ImportSourceForm, ImportUploadPanel } from "@pretextbook/import/react";

function ImportPanel() {
  return (
    <>
      <ImportSourceForm onImport={(r) => console.log(r)} />
      <ImportUploadPanel onImport={(r) => console.log(r)} />
    </>
  );
}

ImportUploadPanel exposes UI controls for the documentKind override and splitSections toggle. Pass importOptions={...} to suppress those controls and use a fixed configuration.

ImportWizard is the full multi-step flow (upload → sources → review → confirm). Its source-selection step — format dropdown, main-document picker, and per-file attach controls — appears whenever the upload leaves a real choice open, and is always reachable from the review step.

Choosing the default import style

A LaTeX or Markdown upload can be imported two ways: "converted" (the generated PreTeXt) or "native" (the cleaned original source, unconverted). Each host picks which one the review step starts on — the VS Code extension reads the pretext-tools.import.defaultMode setting; another host might read an account preference:

<ImportWizard
  defaultImportMode="native" // starts on "Keep as LaTeX"; user can still switch
  onImportModeChange={(mode) => remember(mode)}
  onConfirm={handleConfirm}
/>

Pass lockImportMode alongside it to hide the chooser entirely and import in that one style — for hosts that only support one.

A PreTeXt upload has no native alternative, so the chooser is hidden and the mode handed to onConfirm is always "converted", whatever the default said. Outside the wizard the same rules are available directly:

import {
  DEFAULT_IMPORT_MODE, // "converted"
  hasNativeImportMode, // does this result carry a native alternative?
  resolveImportMode, // preference + result → the mode that really applies
  filesForImportMode,
  assetsForImportMode,
  projectForImportMode,
} from "@pretextbook/import";

const mode = resolveImportMode(result, preferredMode);
const files = filesForImportMode(result, mode);