├── .editorconfig ├── .eslintrc.json ├── .github ├── funding.yml └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .vscode └── settings.json ├── changelog.md ├── fakeTag.mjs ├── jsconfig.json ├── license.md ├── package.json ├── readme.md └── test.mjs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | max_line_length = 80 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["eslint:recommended"], 3 | "env": { 4 | "es2022": true, 5 | "node": true, 6 | "browser": true 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": "latest" 10 | }, 11 | "plugins": ["simple-import-sort"], 12 | "rules": { 13 | "simple-import-sort/imports": "error", 14 | "simple-import-sort/exports": "error" 15 | }, 16 | "overrides": [ 17 | { 18 | "files": ["*.mjs"], 19 | "parserOptions": { 20 | "sourceType": "module" 21 | }, 22 | "globals": { 23 | "__dirname": "off", 24 | "__filename": "off", 25 | "exports": "off", 26 | "module": "off", 27 | "require": "off" 28 | } 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: jaydenseric 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | name: Test with Node.js v${{ matrix.node }} and ${{ matrix.os }} 6 | runs-on: ${{ matrix.os }} 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, macos-latest] 10 | node: ["14", "16", "18"] 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Setup Node.js v${{ matrix.node }} 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: ${{ matrix.node }} 17 | - name: npm install and test 18 | run: npm install-test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "proseWrap": "never" 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.disableAutomaticTypeAcquisition": true, 3 | "typescript.enablePromptUseWorkspaceTsdk": true, 4 | "typescript.tsdk": "node_modules/typescript/lib" 5 | } 6 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # fake-tag changelog 2 | 3 | ## 5.0.0 4 | 5 | ### Major 6 | 7 | - Updated Node.js support to `^14.17.0 || ^16.0.0 || >= 18.0.0`. 8 | - Updated dev dependencies, some of which require newer Node.js versions than previously supported. 9 | - Removed the package `main` field. 10 | - Use the `node:` URL scheme for Node.js builtin module imports in tests. 11 | 12 | ### Patch 13 | 14 | - Updated `jsconfig.json`: 15 | - Set `compilerOptions.maxNodeModuleJsDepth` to `10`. 16 | - Set `compilerOptions.module` to `nodenext`. 17 | - Updated ESLint config. 18 | - Updated GitHub Actions CI config: 19 | - Run tests with Node.js v14, v16, v18. 20 | - Updated `actions/checkout` to v3. 21 | - Updated `actions/setup-node` to v3. 22 | - Removed the now redundant `not IE > 0` from the Browserslist query. 23 | - Revamped the readme: 24 | - Removed the badges. 25 | - Added a “Requirements” section. 26 | - Added information about Deno, import maps, TypeScript config and [optimal JavaScript module design](https://jaydenseric.com/blog/optimal-javascript-module-design). 27 | 28 | ## 4.0.0 29 | 30 | ### Major 31 | 32 | - Updated Node.js support to `^12.22.0 || ^14.17.0 || >= 16.0.0`. 33 | - Updated dev dependencies, some of which require newer Node.js versions than previously supported. 34 | - Removed `./package` from the package `exports` field; the full `package.json` filename must be used in a `require` path. 35 | - The API is now ESM in `.mjs` files instead of CJS in `.js` files, [accessible via `import` but not `require`](https://nodejs.org/dist/latest/docs/api/esm.html#require). 36 | - Implemented TypeScript types via JSDoc comments. 37 | 38 | ### Minor 39 | 40 | - Added the main `./fakeTag.mjs` module to the package `exports` field to allow it to be deep imported. 41 | 42 | ### Patch 43 | 44 | - Also run GitHub Actions CI with Node.js v17. 45 | - Simplified package scripts. 46 | - Simplified dev dependencies and config for ESLint. 47 | - Check TypeScript types via a new package `types` script. 48 | - Removed the [`jsdoc-md`](https://npm.im/jsdoc-md) dev dependency and the related package scripts, replacing the readme “API” section with manually written “Examples” and “Exports” sections. 49 | - Added `esm` and `mjs` keywords to the package `keywords` field. 50 | - Improved variable and test names within tests. 51 | - Configured Prettier option `singleQuote` to the default, `false`. 52 | - Added a `license.md` MIT License file. 53 | - Improved documentation. 54 | 55 | ## 3.0.0 56 | 57 | ### Major 58 | 59 | - Updated Node.js support to `^12.20 || >= 14.13`. 60 | - Updated browser support to the Browserslist query `> 0.5%, not OperaMini all, not IE > 0, not dead`. 61 | - Updated dev dependencies, some of which require newer Node.js versions than were previously supported. 62 | - Added a package `exports` field. 63 | - Refactored the published module to use more modern ES language features. 64 | - The tests are now ESM in an `.mjs` file instead of CJS in a `.js` file. 65 | - Use JSDoc comments and [`jsdoc-md`](https://npm.im/jsdoc-md) in package scripts to generate a new “API” readme section. The published module now contains JSDoc comments, which might affect TypeScript projects. 66 | 67 | ### Patch 68 | 69 | - Updated dev dependencies. 70 | - Add Node.js to the package `browserslist` field query. 71 | - Updated GitHub Actions CI config: 72 | - Also run on pull request. 73 | - Run tests with Node.js v12, v14, v16. 74 | - Updated `actions/checkout` to v2. 75 | - Updated `actions/setup-node` to v2. 76 | - Use the simpler [`npm install-test`](https://docs.npmjs.com/cli/v7/commands/npm-install-test) command. 77 | - Don’t specify the `CI` environment variable as it’s set by default. 78 | - Added a package `sideEffects` field. 79 | - More specific package `main` path. 80 | - Removed `npm-debug.log` from the `.gitignore` file as npm [v4.2.0](https://github.com/npm/npm/releases/tag/v4.2.0)+ doesn’t create it in the current working directory. 81 | - Improved the package `test:prettier` script. 82 | - Configured Prettier option `semi` to the default, `true`. 83 | - The exported fake tag function now has the name `fakeTag` instead of being anonymous. 84 | - Updated the EditorConfig. 85 | - Readme tweaks. 86 | - The file `changelog.md` is no longer published. 87 | 88 | ## 2.0.0 89 | 90 | ### Major 91 | 92 | - Updated Node.js support from v8.10+ to v10+. This is mostly to do with dev dependencies and shouldn’t be a breaking change for the published package. 93 | - Updated dev dependencies, some of which only support Node.js v10+. 94 | - Use [`coverage-node`](https://npm.im/coverage-node) for test code coverage. 95 | 96 | ### Minor 97 | 98 | - Setup [GitHub Sponsors funding](https://github.com/sponsors/jaydenseric): 99 | - Added `.github/funding.yml` to display a sponsor button in GitHub. 100 | - Added a `package.json` `funding` field to enable npm CLI funding features. 101 | 102 | ### Patch 103 | 104 | - Removed the now redundant [`eslint-plugin-import-order-alphabetical`](https://npm.im/eslint-plugin-import-order-alphabetical) dev dependency. 105 | - Use strict mode for scripts. 106 | - In CI, additionally test macOS as well as Node.js v10 and v13. 107 | 108 | ## 1.0.1 109 | 110 | ### Patch 111 | 112 | - Updated dev dependencies. 113 | - Removed [`husky`](https://npm.im/husky) and [`lint-staged`](https://npm.im/lint-staged). 114 | - Replaced old ESLint config with [`eslint-config-env`](https://npm.im/eslint-config-env). 115 | - Use [`test-director`](https://npm.im/test-director) instead of [`ava`](https://npm.im/ava) for tests. 116 | - Updated the package description. 117 | - Simplified the package `repository` field. 118 | - Added a package `main` field. 119 | - Added a package `engines` field declaring support for Node.js >= v8.10, as that is what ESLint supports. This only limits the dev environment; the published code is very simple and should be able to run almost anywhere. 120 | - Added a package `browserslist` field, for linting. 121 | - Moved dev tool config from `package.json` to separate files to reduce the published package size. 122 | - Updated package scripts. 123 | - Ensure Prettier also lints `.yml` files. 124 | - Replaced Travis with GitHub Actions for CI. 125 | - Removed `package-lock.json` from the `.gitignore` file, as it has been disabled anyway. 126 | - Removed some readme badges, and used [badgen.net](https://badgen.net) instead of [shields.io](https://shields.io) for the npm version badge. 127 | - Updated the readme with more details, links, and a typo fix. 128 | - Corrected the first version number in the changelog from `0.1.0` to `1.0.0`. 129 | 130 | ## 1.0.0 131 | 132 | - Initial release. 133 | -------------------------------------------------------------------------------- /fakeTag.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** 4 | * A fake template literal tag that doesn’t do anything except return the tagged 5 | * template string. Import and use the fake tag with the required name, e.g. 6 | * `gql`. 7 | * @param {TemplateStringsArray} literals The template literal’s text segments. 8 | * @param {...unknown} expressions The template literal’s embedded expressions. 9 | * @returns {string} The tagged template string. 10 | * @example 11 | * Tagging a [GraphQL](https://graphql.org) SDL string with `gql`: 12 | * 13 | * ```js 14 | * import gql from "fake-tag"; 15 | * 16 | * const typeDefs = gql` 17 | * "A foo." 18 | * type Foo { 19 | * "The \`Foo\` ID." 20 | * id: ID! 21 | * } 22 | * `; 23 | * ``` 24 | */ 25 | export default function fakeTag(literals, ...expressions) { 26 | let string = ""; 27 | 28 | for (const [index, literal] of literals.entries()) { 29 | string += literal; 30 | 31 | if (index in expressions) string += expressions[index]; 32 | } 33 | 34 | return string; 35 | } 36 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "maxNodeModuleJsDepth": 10, 4 | "module": "nodenext", 5 | "noEmit": true, 6 | "strict": true 7 | }, 8 | "typeAcquisition": { 9 | "enable": false 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright Jayden Seric 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": "fake-tag", 3 | "version": "5.0.0", 4 | "description": "A fake template literal tag to trick syntax highlighters, linters and formatters into action.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Jayden Seric", 8 | "email": "me@jaydenseric.com", 9 | "url": "https://jaydenseric.com" 10 | }, 11 | "repository": "github:jaydenseric/fake-tag", 12 | "homepage": "https://github.com/jaydenseric/fake-tag#readme", 13 | "bugs": "https://github.com/jaydenseric/fake-tag/issues", 14 | "funding": "https://github.com/sponsors/jaydenseric", 15 | "keywords": [ 16 | "es6", 17 | "dummy", 18 | "fake", 19 | "template", 20 | "literal", 21 | "string", 22 | "tag", 23 | "esm", 24 | "mjs" 25 | ], 26 | "files": [ 27 | "fakeTag.mjs" 28 | ], 29 | "sideEffects": false, 30 | "exports": { 31 | ".": "./fakeTag.mjs", 32 | "./fakeTag.mjs": "./fakeTag.mjs", 33 | "./package.json": "./package.json" 34 | }, 35 | "engines": { 36 | "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" 37 | }, 38 | "browserslist": "Node 14.17 - 15 and Node < 15, Node 16 - 17 and Node < 17, Node >= 18, > 0.5%, not OperaMini all, not dead", 39 | "devDependencies": { 40 | "@types/node": "^18.7.8", 41 | "coverage-node": "^8.0.0", 42 | "eslint": "^8.22.0", 43 | "eslint-plugin-simple-import-sort": "^7.0.0", 44 | "prettier": "^2.7.1", 45 | "test-director": "^10.0.0", 46 | "typescript": "^4.7.4" 47 | }, 48 | "scripts": { 49 | "eslint": "eslint .", 50 | "prettier": "prettier -c .", 51 | "types": "tsc -p jsconfig.json", 52 | "tests": "coverage-node test.mjs", 53 | "test": "npm run eslint && npm run prettier && npm run types && npm run tests", 54 | "prepublishOnly": "npm test" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # fake-tag 2 | 3 | A fake template literal tag to trick syntax highlighters, linters and formatters into action. Interpolations and escapes are tested. 4 | 5 | ## Installation 6 | 7 | For [Node.js](https://nodejs.org), to install [`fake-tag`](https://npm.im/fake-tag) with [npm](https://npmjs.com/get-npm), run: 8 | 9 | ```sh 10 | npm install fake-tag 11 | ``` 12 | 13 | For [Deno](https://deno.land) and browsers, an example import map: 14 | 15 | ```json 16 | { 17 | "imports": { 18 | "fake-tag": "https://unpkg.com/fake-tag@5.0.0/fakeTag.mjs" 19 | } 20 | } 21 | ``` 22 | 23 | Then, import and use the template literal tag [`fakeTag`](./fakeTag.mjs). 24 | 25 | ## Examples 26 | 27 | Tagging a [GraphQL](https://graphql.org) SDL string with `gql`: 28 | 29 | ```js 30 | import gql from "fake-tag"; 31 | 32 | const typeDefs = gql` 33 | "A foo." 34 | type Foo { 35 | "The \`Foo\` ID." 36 | id: ID! 37 | } 38 | `; 39 | ``` 40 | 41 | ## Requirements 42 | 43 | Supported runtime environments: 44 | 45 | - [Node.js](https://nodejs.org) versions `^14.17.0 || ^16.0.0 || >= 18.0.0`. 46 | - [Deno](https://deno.land). 47 | - Browsers matching the [Browserslist](https://browsersl.ist) query [`> 0.5%, not OperaMini all, not dead`](https://browsersl.ist/?q=%3E+0.5%25%2C+not+OperaMini+all%2C+not+dead). 48 | 49 | Non [Deno](https://deno.land) projects must configure [TypeScript](https://typescriptlang.org) to use types from the ECMAScript modules that have a `// @ts-check` comment: 50 | 51 | - [`compilerOptions.allowJs`](https://typescriptlang.org/tsconfig#allowJs) should be `true`. 52 | - [`compilerOptions.maxNodeModuleJsDepth`](https://typescriptlang.org/tsconfig#maxNodeModuleJsDepth) should be reasonably large, e.g. `10`. 53 | - [`compilerOptions.module`](https://typescriptlang.org/tsconfig#module) should be `"node16"` or `"nodenext"`. 54 | 55 | ## Exports 56 | 57 | The [npm](https://npmjs.com) package [`fake-tag`](https://npm.im/fake-tag) features [optimal JavaScript module design](https://jaydenseric.com/blog/optimal-javascript-module-design). These ECMAScript modules are exported via the [`package.json`](./package.json) field [`exports`](https://nodejs.org/api/packages.html#exports): 58 | 59 | - [`fakeTag.mjs`](./fakeTag.mjs) 60 | 61 | ## FAQ 62 | 63 | ### Why not comment tags? 64 | 65 | A comment tag looks like this: 66 | 67 | ```js 68 | const QUERY = /* GraphQL */ ` 69 | { 70 | foo 71 | } 72 | `; 73 | ``` 74 | 75 | They are far superior to a fake tag: 76 | 77 | - No dependency to manage. 78 | - No inconvenient imports. 79 | - No bundle size bloat. 80 | - No runtime overhead. 81 | 82 | Unfortunately not all tools support them yet. [`prettier` has since v1.13.0](https://github.com/prettier/prettier/issues/4360#issuecomment-392391729), but [`eslint-plugin-graphql` at v3.1.0 still doesn’t](https://github.com/apollographql/eslint-plugin-graphql/issues/224). 83 | 84 | ### Why not `String.raw`? 85 | 86 | This may be temptingly simple: 87 | 88 | ```js 89 | const gql = String.raw; 90 | const QUERY = gql` 91 | { 92 | foo 93 | } 94 | `; 95 | ``` 96 | 97 | However, it doesn’t unescape characters. For the usage example, if you `console.log(typeDefs)` before and after replacing the import with `const gql = String.raw` you will see the difference in the type description markdown: 98 | 99 | ```diff 100 | "A foo." 101 | type Foo { 102 | - "The `Foo` ID." 103 | + "The \`Foo\` ID." 104 | id: ID! 105 | } 106 | ``` 107 | -------------------------------------------------------------------------------- /test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import { strictEqual } from "node:assert"; 4 | import TestDirector from "test-director"; 5 | 6 | import fakeTag from "./fakeTag.mjs"; 7 | 8 | const tests = new TestDirector(); 9 | 10 | tests.add("`fakeTag` with an empty template literal.", () => { 11 | strictEqual(fakeTag``, ""); 12 | }); 13 | 14 | tests.add("`fakeTag` with literal escapes.", () => { 15 | strictEqual(fakeTag`\``, "`"); 16 | }); 17 | 18 | tests.add("`fakeTag` with only a literal.", () => { 19 | strictEqual(fakeTag`1`, "1"); 20 | }); 21 | 22 | tests.add("`fakeTag` with only embedded expressions.", () => { 23 | strictEqual(fakeTag`${1}${2}${3}`, "123"); 24 | }); 25 | 26 | tests.add("`fakeTag` with embedded expressions surrounding a literal.", () => { 27 | strictEqual(fakeTag`${1}2${3}`, "123"); 28 | }); 29 | 30 | tests.add("`fakeTag` with literals surrounding embedded expressions.", () => { 31 | strictEqual(fakeTag`1${2}${3}4`, "1234"); 32 | }); 33 | 34 | tests.run(); 35 | --------------------------------------------------------------------------------