{"id":29193130,"url":"https://github.com/ember-tooling/content-tag-utils","last_synced_at":"2026-01-20T16:34:55.513Z","repository":{"id":273765332,"uuid":"920675157","full_name":"ember-tooling/content-tag-utils","owner":"ember-tooling","description":"Utils for tools to transform, and get line/column information from content-tag, which uses byte-wise character positions","archived":false,"fork":false,"pushed_at":"2025-05-01T09:41:52.000Z","size":157,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-06-30T08:52:34.555Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ember-tooling.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-22T15:22:56.000Z","updated_at":"2025-05-01T09:41:28.000Z","dependencies_parsed_at":"2025-04-03T02:38:30.814Z","dependency_job_id":"be783193-0240-42ab-b017-d678f083824a","html_url":"https://github.com/ember-tooling/content-tag-utils","commit_stats":null,"previous_names":["nullvoxpopuli/content-tag-utils","ember-tooling/content-tag-utils"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/ember-tooling/content-tag-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ember-tooling%2Fcontent-tag-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ember-tooling%2Fcontent-tag-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ember-tooling%2Fcontent-tag-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ember-tooling%2Fcontent-tag-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ember-tooling","download_url":"https://codeload.github.com/ember-tooling/content-tag-utils/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ember-tooling%2Fcontent-tag-utils/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263061405,"owners_count":23407606,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-02T02:07:41.054Z","updated_at":"2026-01-20T16:34:55.507Z","avatar_url":"https://github.com/ember-tooling.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# content-tag-utils\n\nUtilities for writing tools that work with [content-tag](https://github.com/embroider-build/content-tag) and converting bytes-indexes to character-indexes.\n\nAimed at sharing logic between:\n\n- [eslint](https://eslint.org/) w/ [ember-eslint-parser](https://github.com/ember-tooling/ember-eslint-parser)\n- [prettier](https://github.com/prettier/prettier) w/ [prettier-plugin-ember-template-tag](https://github.com/ember-tooling/prettier-plugin-ember-template-tag)\n- [ember-template-lint](https://github.com/ember-template-lint/ember-template-lint)\n\n\u003e [!NOTE]  \n\u003e 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).\n\n## Install\n\n```bash\nnpm add content-tag-utils\n```\n\nUsing from source / github, via package.json:\n\n```js\n{\n    \"dependencies\": {\n        \"content-tag-utils\": \"github:NullVoxPopuli/content-tag-utils\"\n    }\n}\n```\n\n## Usage\n\n```js\nimport {\n  transform,\n  transformSync,\n  coordinatesOf,\n  Transformer,\n} from \"content-tag-utils\";\n```\n\nand\n\n```js\nimport { unprocess } from 'content-tag-utils/unprocess';\n```\n\n### Transformer\n\nA general utility for working with content-tag, keeping tracked of each template as you apply transformations.\nTransformations are recorded and then applied later when calling `.toString()`.\n\nFor example:\n\n```js\nimport { Transformer } from \"content-tag-utils\";\n\nlet file = `\nexport const Foo = \u003ctemplate\u003e\n    Hello there\n\u003c/template\u003e\n`;\n\nlet t = new Transformer(file);\n\n// apply some transformations, with their coordinates\nawait t.asyncMap((contents, coordinates =\u003e {\n    /* ... */\n    return 'new content';\n});\nt.map((contents, coordinates) =\u003e {\n    /* ... */\n    return 'new content 2';\n});\n\n// iterate over the templates, with their coordinates\nawait t.asyncEach((contents, coordinates =\u003e {\n    /* ... */\n});\nt.each((contents, coordinates) =\u003e {\n    /* ... */\n});\n\n// get the output\nt.toString();\n\n// can also do more transformations and get the output again later\nawait t.transform(/* ... */ )\nt.toString();\n```\n\nProperties / Methods:\n\n- `t.toString()` returns a string of the original file with all applied transforms\n  - `t.toString({ placeholders: true })` returns a string of original file, but as valid JS with placeholder markers - also applies transforms if anyone were done. \n- `t.parseResults` output from `content-tag` , but frozen / read-only - these are used as keys for other methods\n- `t.map()`\n- `t.each()`\n- `t.asyncMap()`\n- `t.asyncEach()`\n- `t.transformOneSync()`\n- `t.transformOne()`\n- `t.reverseInnerCoordinatesOf()` Given in-template coordinates, returns the coordinates in the context of the file\n- `t.stringUtils` Collection of utilities for working with parseResults \n- `t.stringUtils.contentBefore(parseResult)` return the string contents before the passed parse result, before the opening `\u003ctemplate\u003e`\n- `t.stringUtils.originalContentOf(parseResult)` returns the original content of the parseResult, prior to any transformations  \n- `t.stringUtils.openingTag(parseResult)` returns the opening `\u003ctemplate\u003e` including any attributes are key-value pairs it may have on it \n- `t.stringUtils.closingTag(parseResult)` returns the clasing `\u003c/template\u003e` which is expected to always be `=== '\u003c/template'`  \n\n### transform + transformSync\n\nTransforms each template within a gjs or gts file in one go.\n\nThese are convenience functions that wraps the `Transformer`.\n\nThe first argument to the callback will be the previous template-contents, and the second argument will be the coordinates of that template.\n\n```js\nimport { transform, transformSync } from \"content-tag-utils\";\n\nlet file = `\nexport const Foo = \u003ctemplate\u003e\n    Hello there\n\u003c/template\u003e\n`;\n\nlet result = await transform(file, (contents, coordinates) =\u003e `${contents}!`);\nlet result2 = transformSync(file, (contents, coordinates) =\u003e `${contents}!`);\n```\n\nresult / result 2 ( a ! character is added right before the closing \u003c/template\u003e):\n\n```gjs\nexport const Foo = \u003ctemplate\u003e\n    Hello there\n!\u003c/template\u003e\n```\n\n### coordinatesOf\n\nFor 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).\n\n```js\nimport { coordinatesOf } from \"content-tag-utils\";\nimport { Preprocessor } from \"content-tag\";\n\nlet p = new Preprocessor();\n\nlet file = `\nexport const Foo = \u003ctemplate\u003e\n    Hello there\n\u003c/template\u003e\n`;\n\nlet parsed = p.parse(file);\n\nlet result = coordinatesOf(file, parsed[0]);\n```\n\nresult (all values are character-indexes):\n\n```gjs\n{\n    line: 2,\n    column: 29,\n    columnOffset: 0,\n\n    start: 30,\n    end: 47,\n}\n```\n\n### reverseInnerCoordinates\n\nGiven inner coordinates scoped to a template, this function returns the coordinates in the overall source file.\n\n```js\nimport { reverseInnerCoordinates } from 'content-tag-utils';\n\nlet file = `\nexport const Foo = \u003ctemplate\u003e\n    Hello there\n\u003c/template\u003e\n`;\n// e.g.: a lint result\nlet innerCoordinates = {\n    line: 2,\n    column: 4,\n    endColumn: 5,\n    endLine: 2,\n    // extraneous, but may be present in your tool\n    error: 'no capital letters!',\n};\n\nconst templateInfos = extractTemplates(file);\nconst result = reverseInnerCoordinates(templateInfos[0]!, innerCoordinates);\n```\n\nresult:\n\n```gjs\n{\n    column: 4,\n    endColumn: 5,\n    endLine: 3,\n    line: 3,\n}\n```\n\n### unprocess \n\nConverts a js or ts file that used to be gjs or gts back to gjs or gts.\n\n\n```js\nimport { unprocess } from 'content-tag-utils/unprocess';\n\nlet file = `\nimport { template as template_fd9b2463e5f141cfb5666b64daa1f11a } from \"@ember/template-compiler\";\nimport type { TOC } from '@ember/component/template-only';\nexport default template_fd9b2463e5f141cfb5666b64daa1f11a(\\`hi there\\`, {\n        eval () {\n            return eval(arguments[0]);\n        }\n    }) satisfies TOC\u003c{\n    }\u003e;\n`;\n\nlet result = unprocess(file);\n```\n\nresult:\n\n```gjs\nexport default \u003ctemplate\u003ehi there\u003c/template\u003e;\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fember-tooling%2Fcontent-tag-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fember-tooling%2Fcontent-tag-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fember-tooling%2Fcontent-tag-utils/lists"}