{"id":19252720,"url":"https://github.com/morevm/v-bem-transformer","last_synced_at":"2025-10-12T17:26:56.960Z","repository":{"id":37035351,"uuid":"456200132","full_name":"MorevM/v-bem-transformer","owner":"MorevM","description":"Intuitive and performant BEM in Vue files via pseudo-directive","archived":false,"fork":false,"pushed_at":"2024-06-08T10:03:04.000Z","size":2047,"stargazers_count":0,"open_issues_count":10,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-10T01:31:56.197Z","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/MorevM.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE.md","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":"2022-02-06T16:07:06.000Z","updated_at":"2024-03-11T16:30:39.000Z","dependencies_parsed_at":"2024-04-27T11:40:38.187Z","dependency_job_id":null,"html_url":"https://github.com/MorevM/v-bem-transformer","commit_stats":{"total_commits":111,"total_committers":3,"mean_commits":37.0,"dds":"0.49549549549549554","last_synced_commit":"6bbc7dd04ceaa0fdf60dc9222ddc8f9e6eb7e5b2"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MorevM%2Fv-bem-transformer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MorevM%2Fv-bem-transformer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MorevM%2Fv-bem-transformer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MorevM%2Fv-bem-transformer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MorevM","download_url":"https://codeload.github.com/MorevM/v-bem-transformer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246726338,"owners_count":20823785,"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":"2024-11-09T18:28:11.537Z","updated_at":"2025-10-12T17:26:51.906Z","avatar_url":"https://github.com/MorevM.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Stability of \"master\" branch](https://img.shields.io/github/actions/workflow/status/MorevM/v-bem-transformer/build.yaml?branch=master)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n![Last commit](https://img.shields.io/github/last-commit/morevm/v-bem-transformer)\n![Release version](https://img.shields.io/github/v/release/morevm/v-bem-transformer?include_prereleases)\n![GitHub Release Date](https://img.shields.io/github/release-date/morevm/v-bem-transformer)\n![Keywords](https://img.shields.io/github/package-json/keywords/morevm/v-bem-transformer)\n\n# @morev/v-bem-transformer\n\nIntuitive and performant BEM in Vue files via directive syntax 🛠\n\n✔️ Supports `Vue 2 / 3` both; \\\n✔️ Supports `Nuxt 2 / 3` both; \\\n✔️ Provides a composable to use with Composition API; \\\n✔️ Best BEM practices for single-file-components; \\\n✔️ Small footprint (1kb gzipped).\n\n## Table of contents\n\n* [What's the point, what does it do?](#whats-the-point-what-does-it-do)\n* [Installation](#installation)\n* [Usage](#usage)\n* [Usage with Nuxt](#usage-with-nuxt)\n* [Configuration](#configuration)\n* [How does it work](#how-does-it-work)\n* [Recipes](#recipes)\n* [Known limitations](#known-limitations)\n\n## What's the point, what does it do?\n\nThe package helps level out one of [BEM](https://en.bem.info/)'s biggest problems - its verbosity. \\\nIt also allows you to have more confidence that there are no errors in the block name,\nand that nothing unnecessary has been added to the component's classes that relate to other components.\n\nImplementation details are described in the [\"How does it work\"](#how-does-it-work) section,\nmore examples and recipes are shown in the [\"Recipes\"](#recipes) section,\nhere is just a self-explanatory code example:\n\nUsing the package, if you provide the code like that...\n\n```vue\n\u003ctemplate\u003e\n  \u003cdiv v-bem\u003e\n    \u003cdiv v-bem:header\u003e\n      \u003cdiv v-bem:title=\"{ size: 'large', wide: true }\"\u003e\n        Some title\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript setup\u003e\n  defineOptions({ name: 'the-block' });\n\u003c/script\u003e\n```\n\n...it will be rendered into the following:\n\n```html\n\u003cdiv class=\"the-block\"\u003e\n  \u003cdiv class=\"the-block__header\"\u003e\n    \u003cdiv class=\"\n      the-block__title \n      the-block__title--size-large \n      the-block__title--wide\n    \"\u003e\n      Some title\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\nYou can also use variables for modifiers or elements, making it easier than ever to handle states.\n\n## Installation\n\n\u003e [!CAUTION]\n\u003e Requirements:\n\u003e\n\u003e * Node version: `\u003e= 18.0.0`;\n\u003e * Nuxt version (if used): `\u003e= 2.17.0 || \u003e= 3.5.0`;\n\u003e * Any bundler is required: `vite`, `esbuild`, `webpack`, `rollup` are supported via [unplugin](https://github.com/unjs/unplugin).\n\u003e\n\u003e **The plugin will not work if you are using a Node or Nuxt version less than the specified ones.**\n\n---\n\n### Using `yarn`\n\n```bash\nyarn add @morev/v-bem-transformer\n```\n\n---\n\n### Using `npm`\n\n```bash\nnpm install @morev/v-bem-transformer\n```\n\n---\n\n### Using `pnpm`\n\n```bash\npnpm add @morev/v-bem-transformer\n```\n\n---\n\n### Using `bun`\n\n```bash\nbun add @morev/v-bem-transformer\n```\n\n## Usage\n\n\u003e [!Note]\n\u003e You may skip this section if you are going to use the module with Nuxt. \\\n\u003e [Go to \"Usage with Nuxt\" section](#usage-with-nuxt).\n\n\n### Step 1: Bundler plugin\n\nFirst, you need to attach the plugin to your builder (`vite` is used here in the example):\n\n\u003e [!IMPORTANT]\n\u003e The plugin SHOULD be the first in the chain to work correctly.\n\n```ts\nimport { defineConfig } from 'vite';\nimport pluginVue from '@vitejs/plugin-vue';\nimport { vitePlugin as pluginVBem } from '@morev/v-bem-transformer';\n\nexport default defineConfig({\n  plugins: [\n    pluginVBem({\n      // custom options described below (and also fully typed via TS right here)\n    }),\n    pluginVue(),\n  ],\n});\n```\n\nThe packages provides plugins for `vite`, `rollup`, `webpack` and `esbuild`.\n\n### Step 2: Vue plugin\n\n\u003e [!NOTE]\n\u003e This guide illustrates how to use it with `Vue 3`. \\\n\u003e Connecting to `Vue 2` follows the same algorithm, except for the specifics of installing plugins -\n\u003e you need to use `Vue.use()` instead of `app.use()`.\n\n```ts\nimport { createApp } from 'vue';\nimport App from './App.vue';\n\nimport { vuePlugin as pluginVBem } from '@morev/v-bem-transformer/vue';\n\nconst app = createApp(App)\napp.use(pluginVBem({\n  // custom options described below (and also typed with TS right here)\n}));\n\napp.mount('#app');\n```\n\n### Step 3: Types for `b()` method (if needed)\n\nIf you are going to use the function generating BEM classes directly (quite rarely used to be honest) and you need a type inside a component,\nadd the following to your `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"types\": [\n      \"@morev/v-bem-transformer/types/vue-globals.d.ts\"\n    ]\n  }\n}\n```\n\n\u003e [!WARNING]\n\u003e `vue-globals.d.ts` registers a property named `b`. \\\n\u003e If you are going to use a different property name - you must provide the appropriate types yourself.\n\n### Step 4: Composable (if needed)\n\nIf you need to access BEM generator function within `\u003cscript setup\u003e`, you can create you own `useBem` composable this way:\n\n```ts\n// ~/composables/use-bem.ts\nimport { useBemFactory } from '@morev/v-bem-transformer/use-bem-factory';\n\nexport const useBem = useBemFactory({\n  // custom options described below\n});\n```\n\n\u003e [!TIP]\n\u003e You can find this composable template with typings [here](https://github.com/MorevM/v-bem-transformer/blob/master/src/nuxt-templates/use-bem.ts).\n\n\n## Usage with Nuxt\n\nThe package supports both `Nuxt 2` and `Nuxt 3`. \\\nNuxt 2 support without Bridge is slightly limited - the module will not automatically register the `useBem` composable\n(but you still can do it yourself, for example if you are using [`@nuxtjs/composition-api`](https://github.com/nuxt-community/composition-api)).\n\n[Install the package](#installation), next add `@morev/v-bem-transformer/nuxt` to the `modules` section of your `nuxt.config`:\n\n```ts\nexport default defineNuxtConfig({\n  modules: [\n    '@morev/v-bem-transformer/nuxt',\n  ],\n  vBemTransformer: {\n    // Optional configuration options described below.\n  }\n});\n\n// ...or using the tuple syntax:\nexport default defineNuxtConfig({\n  modules: [\n    ['@morev/v-bem-transformer/nuxt', {\n      // Optional configuration options described below.\n    }],\n  ],\n});\n```\n\nUsing Nuxt 3, no additional steps are required, just start using the `v-bem` directive and composable `useBem` within your components. \\\nIt will be fully typed by default.\n\n\n## Configuration\n\nAll methods have built-in documentation via TS,\nsource types are available [here](https://github.com/MorevM/v-bem-transformer/blob/master/src/types.ts).\n\n## How does it work\n\nThe package works in two steps:\n\n1. Registers a global mixin that provides a [BEM class name generator](https://github.com/MorevM/bem-classnames) bound to each component. \\\n  You can [check it out here](https://github.com/MorevM/v-bem-transformer/blob/master/src/vue.ts).\n1. At build time, using regular expressions, replaces `v-bem` directives with class declarations (preserving existing ones, if any)\nthat call the method added using the mixin in the previous step:\n\n```html\n\u003c!-- Before the transformation --\u003e\n\u003cdiv v-bem\u003e\n  \u003cdiv v-bem:element=\"{ modifier: true }\"\u003e\n    \u003cdiv v-bem:inner :class=\"dynamicClass\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\n\u003c!-- After the transformation --\u003e\n\u003cdiv :class=\"b(null)\"\u003e\n  \u003cdiv :class=\"b('element', { modifier: true })\"\u003e\n    \u003cdiv :class=\"[dynamicClass, b('inner')]\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\n### Why not just use directives?\n\nA directive is a separate entity with its own lifecycle, so using it actually for each DOM element is overkill. \\\nIf we use transformation, we just get class bindings.\n\nAlso directives require separate processing at the SSR level, which adds complexity and points of failure (especially in Vue 2).\n\n## Recipes\n\n\u003cdetails\u003e\n  \u003csummary\u003eHow to use with static classnames?\u003c/summary\u003e\n  \u003cbr /\u003e\n  There are two use cases: writing static classes via the directive modifier syntax, or using the native `class` attribute.\n\n  **Input**:\n\n  ```vue\n  \u003ctemplate\u003e\n    \u003cdiv v-bem.static.another-static\u003e\n      \u003cdiv v-bem:element class=\"is-active\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/template\u003e\n  \n  \u003cscript lang=\"ts\" setup\u003e\n    defineOptions({ name: 'the-block' });\n  \u003c/script\u003e\n  ```\n\n  **Output**:\n\n  ```html\n  \u003cdiv class=\"the-block static another-static\"\u003e\n    \u003cdiv class=\"the-block__element is-active\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  ```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eHow to use with dynamic static classnames?\u003c/summary\u003e\n  \u003cbr /\u003e\n  As well as in the previous case, you have two options: writing via the dynamic directive modifier syntax, or using the native `class` attribute.\n\n  **Input**:\n\n  ```vue\n  \u003ctemplate\u003e\n    \u003cdiv v-bem.[dynamicClassBinding]\u003e\n      \u003cdiv v-bem:element :class=\"dynamicClassBinding\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/template\u003e\n  \n  \u003cscript lang=\"ts\" setup\u003e\n    defineOptions({ name: 'the-block' });\n    \n    const dynamicClassBinding = ref('is-active');\n  \u003c/script\u003e\n  ```\n\n  **Output**:\n\n  ```html\n  \u003cdiv class=\"the-block static another-static\"\u003e\n    \u003cdiv class=\"the-block__element is-active\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  ```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eHandling states with dynamic modifier bindings\u003c/summary\u003e\n\n  **Input**:\n\n  ```vue\n  \u003ctemplate\u003e\n    \u003cdiv v-bem\u003e\n      \u003cdiv v-bem:element=\"{ active: isActive }\"\u003e\u003c/div\u003e\n      \u003cbutton type=\"button\" @click=\"isActive = !isActive\"\u003eToggle\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/template\u003e\n  \n  \u003cscript lang=\"ts\" setup\u003e\n    defineOptions({ name: 'the-block' });\n    \n    const isActive = ref(false);\n  \u003c/script\u003e\n  ```\n\n  **Initial output**:\n\n  ```html\n  \u003cdiv class=\"the-block\"\u003e\n    \u003cdiv class=\"the-block__element\"\u003e\u003c/div\u003e\n    \u003cbutton type=\"button\"\u003eToggle\u003c/button\u003e\n  \u003c/div\u003e\n  ```\n\n  **Output after click on the button**:\n\n  ```html\n  \u003cdiv class=\"the-block\"\u003e\n    \u003cdiv class=\"the-block__element the-block__element--active\"\u003e\u003c/div\u003e\n    \u003cbutton type=\"button\"\u003eToggle\u003c/button\u003e\n  \u003c/div\u003e\n  ```\n\n  All variables within `v-bem` directive are fully reactive.\n\u003c/details\u003e\n\n## Known limitations\n\nAs the module manipulates the source code via a trivial regular expression, there is no support for JSX/TSX and programmatically created elements\n(using Vue's `h()` method for example).\n\nYou still can use `useBem()` composable using Composition API and `this.b()` to access `bemFunction`,\nbut transforming as a directive will only work in Vue files that do not use a custom syntax like `pug`.\n\n\u003cdetails\u003e\n  \u003csummary\u003eHow to deal with JSX/TSX using Composition API?\u003c/summary\u003e\n  \u003cbr /\u003e\n\n  ```vue\n  \u003cscript lang=\"ts\" setup\u003e\n    defineOptions({ name: 'the-block' });\n    \n    const $b = useBem();\n    \n    const render = () =\u003e (\n      \u003cdiv class={$b()}\u003e\n        \u003cdiv class={$b('inner-element')}\u003e\u003c/div\u003e\n      \u003c/div\u003e\n    );\n  \u003c/script\u003e\n  ```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eHow to deal with render function using Options API?\u003c/summary\u003e\n  \u003cbr /\u003e\n\n  ```vue\n  \u003cscript lang=\"ts\"\u003e\n    import { h } from 'vue';\n    \n    export default {\n      name: 'the-block',\n      render() {\n        return h('div', { class: this.b() }, [\n          h('div', { class: this.b('element') }, 'Some content')\n        ])\n      }\n    }\n  \u003c/script\u003e\n  ```\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorevm%2Fv-bem-transformer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmorevm%2Fv-bem-transformer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorevm%2Fv-bem-transformer/lists"}