├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── esm-lint.yml ├── .gitignore ├── .npmrc ├── esm └── package.json ├── index.ts ├── license ├── package.json ├── readme.md ├── test.mjs └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | Lint: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version-file: package.json 15 | - run: npm install 16 | - name: XO 17 | run: npx xo 18 | 19 | Test: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions/setup-node@v4 24 | with: 25 | node-version-file: package.json 26 | - run: npm install 27 | - run: npm run build 28 | - run: node --test 29 | 30 | Build: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: actions/setup-node@v4 35 | with: 36 | node-version-file: package.json 37 | - run: npm install 38 | - run: npm run build 39 | 40 | Spelling: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v4 44 | - name: Avoid common misspelling 45 | run: "! git grep -E 'code-tags|code tag' -- :^.github" 46 | -------------------------------------------------------------------------------- /.github/workflows/esm-lint.yml: -------------------------------------------------------------------------------- 1 | env: 2 | IMPORT_TEXT: import {html, css, gql, md} from 3 | NPM_MODULE_NAME: code-tag 4 | NODE_VERSION: 18 5 | 6 | # FILE GENERATED WITH: npx ghat fregante/ghatemplates/esm-lint 7 | # SOURCE: https://github.com/fregante/ghatemplates 8 | 9 | name: ESM 10 | on: 11 | pull_request: 12 | branches: 13 | - '*' 14 | push: 15 | branches: 16 | - master 17 | - main 18 | jobs: 19 | Pack: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - run: npm install 24 | - run: npm run build --if-present 25 | - run: npm pack --dry-run 26 | - run: npm pack | tail -1 | xargs -n1 tar -xzf 27 | - uses: actions/upload-artifact@v3 28 | with: 29 | path: package 30 | Webpack: 31 | runs-on: ubuntu-latest 32 | needs: Pack 33 | steps: 34 | - uses: actions/download-artifact@v3 35 | - run: npm install ./artifact 36 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js 37 | - run: webpack --entry ./index.js 38 | - run: cat dist/main.js 39 | Parcel: 40 | runs-on: ubuntu-latest 41 | needs: Pack 42 | steps: 43 | - uses: actions/download-artifact@v3 44 | - run: npm install ./artifact 45 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js 46 | - run: npx parcel@2 build index.js 47 | - run: cat dist/index.js 48 | Rollup: 49 | runs-on: ubuntu-latest 50 | needs: Pack 51 | steps: 52 | - uses: actions/download-artifact@v3 53 | - run: npm install ./artifact rollup@2 @rollup/plugin-node-resolve 54 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js 55 | - run: npx rollup -p node-resolve index.js 56 | Vite: 57 | runs-on: ubuntu-latest 58 | needs: Pack 59 | steps: 60 | - uses: actions/download-artifact@v3 61 | - run: npm install ./artifact 62 | - run: >- 63 | echo '' > index.html 65 | - run: npx vite build 66 | - run: cat dist/assets/* 67 | esbuild: 68 | runs-on: ubuntu-latest 69 | needs: Pack 70 | steps: 71 | - uses: actions/download-artifact@v3 72 | - run: echo '{}' > package.json 73 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js 74 | - run: npm install ./artifact 75 | - run: npx esbuild --bundle index.js 76 | TypeScript: 77 | runs-on: ubuntu-latest 78 | needs: Pack 79 | steps: 80 | - uses: actions/download-artifact@v3 81 | - run: npm install ./artifact 82 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.ts 83 | - run: tsc index.ts 84 | - run: cat index.js 85 | Node: 86 | runs-on: ubuntu-latest 87 | needs: Pack 88 | steps: 89 | - uses: actions/download-artifact@v3 90 | - uses: actions/setup-node@v4 91 | with: 92 | node-version: ${{ env.NODE_VERSION }} 93 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.mjs 94 | - run: npm install ./artifact 95 | - run: node index.mjs 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | Desktop.ini 4 | ._* 5 | Thumbs.db 6 | *.tmp 7 | *.bak 8 | *.log 9 | logs 10 | *.map 11 | index.js 12 | index.d.ts 13 | !esm/package.json 14 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /esm/package.json: -------------------------------------------------------------------------------- 1 | {"type": "module"} 2 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | // Prettier supports these languages: 2 | // https://github.com/prettier/prettier/blob/e46aba0ab279c764dc26e0f41f15c55122440c51/src/language-js/embed.js#L13 3 | 4 | const concatenateTemplateLiteralTag = ( 5 | raw: TemplateStringsArray, 6 | ...keys: string[] 7 | ): string => keys.length === 0 ? raw[0]! : String.raw({raw}, ...keys); 8 | 9 | /** 10 | Enable highlighting/prettifying when used as html`
` or css`.a {}` 11 | https://prettier.io/docs/en/options.html#embedded-language-formatting 12 | */ 13 | const any = concatenateTemplateLiteralTag; 14 | 15 | export { 16 | any, 17 | any as html, 18 | any as css, 19 | any as gql, 20 | any as graphql, 21 | any as md, 22 | any as markdown, 23 | any as sql, 24 | }; 25 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Federico Brigante (https://fregante.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-tag", 3 | "version": "1.2.0", 4 | "description": "noop functions to help formatters and syntax highlighters recognize embedded code", 5 | "keywords": [ 6 | "prettier", 7 | "syntax", 8 | "highlight", 9 | "highlighting", 10 | "format", 11 | "formatting", 12 | "embedded language formatting", 13 | "markup", 14 | "html", 15 | "css", 16 | "graphql", 17 | "template literals", 18 | "tag" 19 | ], 20 | "repository": "fregante/code-tag", 21 | "funding": "https://github.com/sponsors/fregante", 22 | "license": "MIT", 23 | "author": "Federico Brigante (https://fregante.com)", 24 | "exports": { 25 | "require": "./cjs/index.js", 26 | "types": "./cjs/index.d.ts", 27 | "default": "./esm/index.js" 28 | }, 29 | "main": "cjs/index.js", 30 | "module": "esm/index.js", 31 | "types": "cjs/index.d.ts", 32 | "files": [ 33 | "cjs", 34 | "esm" 35 | ], 36 | "scripts": { 37 | "build": "tsc && tsc --outDir cjs --module commonjs --moduleResolution node", 38 | "prepack": "npm run build", 39 | "test": "xo && npm run build && node --test", 40 | "watch": "tsc --watch" 41 | }, 42 | "xo": { 43 | "rules": { 44 | "import/order": "off" 45 | } 46 | }, 47 | "devDependencies": { 48 | "@sindresorhus/tsconfig": "^5.0.0", 49 | "typescript": "^5.3.3", 50 | "xo": "^0.56.0" 51 | }, 52 | "engines": { 53 | "node": ">=18" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # code-tag [![npm version](https://img.shields.io/npm/v/code-tag.svg)][link-npm] [![(size)][badge-gzip]](#no-link) 2 | 3 | [badge-gzip]: https://img.shields.io/bundlephobia/minzip/code-tag.svg?label=gzipped 4 | [link-npm]: https://www.npmjs.com/package/code-tag 5 | 6 | > noop functions to help formatters and syntax highlighters recognize embedded code 7 | 8 | When embedding other languages in JavaScript, you can mark those strings with a [tag function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) to help JavaScript tools recognize the string as code: 9 | 10 | ```js 11 | document.body.innerHTML = html` 12 |

This is inline HTML

13 | 14 | 15 | `; 16 | ``` 17 | 18 | You can find such tag functions in: 19 | 20 | - **code-tag**: this package, it returns the string as is 21 | - [escape-goat](https://github.com/sindresorhus/escape-goat): it escapes the any replaced value in the string 22 | - [lit-html](https://lit.dev/docs/templates/overview/): it helps write Web Components 23 | - [Apollo](https://www.apollographql.com/docs/resources/graphql-glossary/#gql-function): it parses GraphQL strings 24 | - [Emotion](https://emotion.sh/docs/@emotion/css): it defines CSS-in-JS 25 | - etc… 26 | 27 | Here are some tools that support them natively: 28 | 29 | - [Prettier](https://prettier.io/docs/en/options.html#embedded-language-formatting): it formats the strings as real non-JavaScript code 30 | - GitHub: it highlights the syntax in the strings as code (as seen in the example above) 31 | 32 | ## Install 33 | 34 | ```sh 35 | npm install code-tag 36 | ``` 37 | 38 | ## Usage 39 | 40 | ```js 41 | import {html, css, gql, md, sql} from 'code-tag'; 42 | // Or: 43 | // const {html, css, gql, md, sql} = require('code-tag'); 44 | 45 | document.body.innerHTML = html` 46 |

This is HTML in JS

47 | `; 48 | 49 | document.querySelector('style').textContent = css` 50 | .this.is { 51 | css: 'in JS'; 52 | } 53 | `; 54 | 55 | await githubQuery(gql` 56 | query { 57 | repository(owner: "fregante", name: "template-tags") { 58 | nameWithOwner 59 | } 60 | } 61 | `); 62 | 63 | yourMarkdownConverter(md` 64 | # Markdown 65 | 66 | Is _highlighted_ [as well](https://www.youtube.com/watch?v=dQw4w9WgXcQ) 67 | `); 68 | 69 | await sqlQuery(sql`select * from users`); 70 | ``` 71 | 72 | There's also an `any` export that you can rename as you please: 73 | 74 | ```js 75 | import {any as mdx} from 'code-tag'; 76 | 77 | mdx` 78 | Some other Language 79 | `; 80 | ``` 81 | 82 | ## License 83 | 84 | MIT © [Federico Brigante](https://fregante.com) 85 | -------------------------------------------------------------------------------- /test.mjs: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert/strict'; 2 | import {createRequire} from 'node:module'; 3 | import {describe, it} from 'node:test'; 4 | 5 | // The tests specifically import the built files to ensure that they're generated correctly 6 | import * as esm from './esm/index.js'; 7 | 8 | const require = createRequire(import.meta.url); 9 | const cjs = require('./cjs/index.js'); 10 | 11 | testContext(cjs, 'cjs'); 12 | testContext(esm, 'esm'); 13 | 14 | function testContext({any, html, css, gql, md, sql}, name) { 15 | describe(name + ' imports', () => { 16 | it('exports', () => { 17 | assert.equal(any, html); 18 | assert.equal(any, css); 19 | assert.equal(any, gql); 20 | assert.equal(any, md); 21 | assert.equal(any, sql); 22 | }); 23 | 24 | it('code-tag', () => { 25 | assert.equal(any`a`, 'a'); 26 | assert.equal(any` a `, ' a ', 'Preserve boundary whitespace'); 27 | assert.equal(any`a${'b'}c${1}`, 'abc1', 'Interpolate with strings and numbers'); 28 | assert.equal(any`\\\na\\\na`, '\\\na\\\na', 'Preserve escape sequences'); 29 | assert.equal(any`\\\na${'\\\na'}`, '\\\na\\\na', 'Preserve escape sequences in interpolation'); 30 | assert.equal(any`🇪🇺 🇺🇳`, '🇪🇺 🇺🇳', 'Preserve combined emojis'); 31 | assert.equal(any`🇪🇺 ${'🇺🇳'}`, '🇪🇺 🇺🇳', 'Preserve combined emojis in interpolation'); 32 | }); 33 | 34 | it('stringifiable objects', () => { 35 | const stringifiableObject = new Date(); 36 | const stringifiedObject = String(stringifiableObject); 37 | 38 | assert.equal(any`${stringifiableObject}`, stringifiedObject, 'Interpolate objects with toString'); 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sindresorhus/tsconfig", 3 | "compilerOptions": { 4 | "module": "es2020", 5 | "moduleResolution": "Bundler", 6 | "outDir": "esm" 7 | }, 8 | "files": [ 9 | "index.ts" 10 | ] 11 | } 12 | --------------------------------------------------------------------------------