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 🙏

© 2025 – Pkg Stats / Ryan Hefner

content-tag-utils

v0.6.0

Published

utilities for working with content-tag: transforming, working with line/column information, etc

Readme

content-tag-utils

Utilities for writing tools that work with content-tag and converting bytes-indexes to character-indexes.

Aimed at sharing logic between:

[!NOTE]
This utility is meant for local tooling and not the browser, or transforming runtime code. No sourcemaps are involved. (Sourcemaps should be used when transforming code meant for runtime).

Install

npm add content-tag-utils

Using from source / github, via package.json:

{
    "dependencies": {
        "content-tag-utils": "github:NullVoxPopuli/content-tag-utils"
    }
}

Usage

import {
  transform,
  transformSync,
  coordinatesOf,
  Transformer,
} from "content-tag-utils";

and

import { unprocess } from 'content-tag-utils/unprocess';

Transformer

A general utility for working with content-tag, keeping tracked of each template as you apply transformations. Transformations are recorded and then applied later when calling .toString().

For example:

import { Transformer } from "content-tag-utils";

let file = `
export const Foo = <template>
    Hello there
</template>
`;

let t = new Transformer(file);

// apply some transformations, with their coordinates
await t.asyncMap((contents, coordinates => {
    /* ... */
    return 'new content';
});
t.map((contents, coordinates) => {
    /* ... */
    return 'new content 2';
});

// iterate over the templates, with their coordinates
await t.asyncEach((contents, coordinates => {
    /* ... */
});
t.each((contents, coordinates) => {
    /* ... */
});

// get the output
t.toString();

// can also do more transformations and get the output again later
await t.transform(/* ... */ )
t.toString();

Properties / Methods:

  • t.toString() returns a string of the original file with all applied transforms
    • t.toString({ placeholders: true }) returns a string of original file, but as valid JS with placeholder markers - also applies transforms if anyone were done.
  • t.parseResults output from content-tag , but frozen / read-only - these are used as keys for other methods
  • t.map()
  • t.each()
  • t.asyncMap()
  • t.asyncEach()
  • t.transformOneSync()
  • t.transformOne()
  • t.reverseInnerCoordinatesOf() Given in-template coordinates, returns the coordinates in the context of the file
  • t.stringUtils Collection of utilities for working with parseResults
  • t.stringUtils.contentBefore(parseResult) return the string contents before the passed parse result, before the opening <template>
  • t.stringUtils.originalContentOf(parseResult) returns the original content of the parseResult, prior to any transformations
  • t.stringUtils.openingTag(parseResult) returns the opening <template> including any attributes are key-value pairs it may have on it
  • t.stringUtils.closingTag(parseResult) returns the clasing </template> which is expected to always be === '</template'

transform + transformSync

Transforms each template within a gjs or gts file in one go.

These are convenience functions that wraps the Transformer.

The first argument to the callback will be the previous template-contents, and the second argument will be the coordinates of that template.

import { transform, transformSync } from "content-tag-utils";

let file = `
export const Foo = <template>
    Hello there
</template>
`;

let result = await transform(file, (contents, coordinates) => `${contents}!`);
let result2 = transformSync(file, (contents, coordinates) => `${contents}!`);

result / result 2 ( a ! character is added right before the closing ):

export const Foo = <template>
    Hello there
!</template>

coordinatesOf

For a given source document (gjs or gts), and a single parseResult (one of the entries from the array returned from content-tag's parse), what is the line/column number of the first character for that parseResult, and the columnOffset (useful for extracting templates to do work on and then put back, or giving pointers to errors present in the template).

import { coordinatesOf } from "content-tag-utils";
import { Preprocessor } from "content-tag";

let p = new Preprocessor();

let file = `
export const Foo = <template>
    Hello there
</template>
`;

let parsed = p.parse(file);

let result = coordinatesOf(file, parsed[0]);

result (all values are character-indexes):

{
    line: 2,
    column: 29,
    columnOffset: 0,

    start: 30,
    end: 47,
}

reverseInnerCoordinates

Given inner coordinates scoped to a template, this function returns the coordinates in the overall source file.

import { reverseInnerCoordinates } from 'content-tag-utils';

let file = `
export const Foo = <template>
    Hello there
</template>
`;
// e.g.: a lint result
let innerCoordinates = {
    line: 2,
    column: 4,
    endColumn: 5,
    endLine: 2,
    // extraneous, but may be present in your tool
    error: 'no capital letters!',
};

const templateInfos = extractTemplates(file);
const result = reverseInnerCoordinates(templateInfos[0]!, innerCoordinates);

result:

{
    column: 4,
    endColumn: 5,
    endLine: 3,
    line: 3,
}

unprocess

Converts a js or ts file that used to be gjs or gts back to gjs or gts.

import { unprocess } from 'content-tag-utils/unprocess';

let file = `
import { template as template_fd9b2463e5f141cfb5666b64daa1f11a } from "@ember/template-compiler";
import type { TOC } from '@ember/component/template-only';
export default template_fd9b2463e5f141cfb5666b64daa1f11a(\`hi there\`, {
        eval () {
            return eval(arguments[0]);
        }
    }) satisfies TOC<{
    }>;
`;

let result = unprocess(file);

result:

export default <template>hi there</template>;