From e8a3ea043f0b06b7cd6325387ca1df94e44389d1 Mon Sep 17 00:00:00 2001 From: Valentin Silyutin Date: Wed, 21 Jan 2026 14:56:24 +0400 Subject: [PATCH] @advdominion/babel-plugin-nunjucks --- packages/babel-plugin-nunjucks/README.md | 70 +++++++++++ packages/babel-plugin-nunjucks/index.js | 129 ++++++++++++++++++++ packages/babel-plugin-nunjucks/package.json | 20 +++ yarn.lock | 49 ++++++++ 4 files changed, 268 insertions(+) create mode 100644 packages/babel-plugin-nunjucks/README.md create mode 100644 packages/babel-plugin-nunjucks/index.js create mode 100644 packages/babel-plugin-nunjucks/package.json diff --git a/packages/babel-plugin-nunjucks/README.md b/packages/babel-plugin-nunjucks/README.md new file mode 100644 index 0000000..89dc630 --- /dev/null +++ b/packages/babel-plugin-nunjucks/README.md @@ -0,0 +1,70 @@ +# babel-plugin-nunjucks + +Плагин для [babel-loader](https://github.com/babel/babel-loader), позволяющий использовать шаблонизатор [Nunjucks](https://mozilla.github.io/nunjucks/) внутри JS-файлов. + +## Установка + +```bash +yarn add -D @advdominion/babel-plugin-nunjucks +``` + +## Использование + +Используются [Tagged templates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) с тегом `njk`: + +```js +njk` + {% from "./item.njk" import item as item %} + +
+ {% for n in range(0, 10) %} + {{item()}} + {% endfor %} +
+`; +``` + +Пример конфигурации Webpack: + +```js +[ + { + test: /\.js$/, + exclude: [/mocks\.js$/], + use: [ + { + loader: 'babel-loader', + options: { + cacheDirectory: true, + }, + }, + ], + }, + { + test: /mocks\.js$/, + use: [ + { + loader: 'babel-loader', + options: { + compact: false, + plugins: [ + [ + '@advdominion/babel-plugin-nunjucks', + { + templatesFolder: 'src/templates/', + }, + ], + ], + }, + }, + ], + }, +]; +``` + +## Опции + +- `templatesFolder` - строка, обязательный параметр. Путь до папки с файлами шаблонов +- `globals` - массив, необязательный параметр. Глобальные переменные для Nunjucks (например, `[{name: "message", value: "Hello, world!"}]`) + +**Важно:** Параметр `cacheDirectory` в опциях babel-loader должен быть отключён. diff --git a/packages/babel-plugin-nunjucks/index.js b/packages/babel-plugin-nunjucks/index.js new file mode 100644 index 0000000..72dd2ff --- /dev/null +++ b/packages/babel-plugin-nunjucks/index.js @@ -0,0 +1,129 @@ +/* eslint-disable unicorn/no-useless-spread */ + +import js from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import simpleImportSort from 'eslint-plugin-simple-import-sort'; +import eslintPluginUnicorn from 'eslint-plugin-unicorn'; +import pluginVue from 'eslint-plugin-vue'; +import globals from 'globals'; + +const config = [ + { + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.nodeBuiltin, + }, + }, + }, + js.configs.recommended, + { + rules: { + ...{ + 'array-callback-return': 'error', + 'no-await-in-loop': 'warn', + 'no-duplicate-imports': 'warn', + 'no-promise-executor-return': 'error', + 'no-self-compare': 'error', + 'no-template-curly-in-string': 'warn', + 'no-unassigned-vars': 'warn', + 'no-unmodified-loop-condition': 'error', + 'no-unreachable-loop': 'error', + 'no-use-before-define': 'error', + 'no-useless-assignment': 'warn', + }, + ...{ + 'dot-notation': 'warn', + 'eqeqeq': 'error', + 'no-implicit-coercion': 'warn', + 'no-negated-condition': 'warn', + 'no-useless-concat': 'warn', + 'object-shorthand': 'warn', + 'func-style': 'error', + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'no-param-reassign': 'error', + 'no-var': 'error', + 'one-var': ['warn', 'never'], + 'prefer-const': 'warn', + 'prefer-template': 'warn', + }, + }, + }, + { + plugins: { + 'simple-import-sort': simpleImportSort, + }, + rules: { + 'simple-import-sort/imports': 'warn', + }, + }, + { + plugins: { + unicorn: eslintPluginUnicorn, + }, + rules: { + ...Object.fromEntries( + Object.entries(eslintPluginUnicorn.configs.recommended.rules).map(([rule, level]) => [ + rule, + level === 'off' ? 'off' : 'warn', + ]), + ), + 'unicorn/better-regex': 'warn', + 'unicorn/filename-case': ['warn', { cases: { kebabCase: true, pascalCase: true } }], + 'unicorn/prefer-global-this': 'off', + 'unicorn/prefer-import-meta-properties': 'warn', + 'unicorn/prefer-top-level-await': 'off', + 'unicorn/prevent-abbreviations': 'off', + }, + }, + ...pluginVue.configs['flat/recommended'], + { + rules: { + ...{ + 'vue/no-v-html': 'off', + 'vue/one-component-per-file': 'off', + }, + ...{ + 'vue/component-name-in-template-casing': 'warn', + 'vue/component-options-name-casing': 'warn', + 'vue/custom-event-name-casing': 'warn', + 'vue/match-component-file-name': ['warn', { extensions: ['vue'], shouldMatchCase: true }], + 'vue/match-component-import-name': 'warn', + 'vue/no-boolean-default': 'warn', + 'vue/no-potential-component-option-typo': 'warn', + 'vue/no-useless-mustaches': 'warn', + 'vue/no-useless-v-bind': 'warn', + 'vue/require-direct-export': 'warn', + 'vue/require-name-property': 'warn', + 'vue/v-for-delimiter-style': 'warn', + 'vue/v-on-handler-style': ['warn', 'inline'], + }, + ...{ + 'vue/dot-notation': 'warn', + 'vue/eqeqeq': 'error', + 'vue/no-console': ['warn', { allow: ['warn', 'error'] }], + 'vue/no-constant-condition': 'error', + 'vue/no-empty-pattern': 'error', + 'vue/no-implicit-coercion': 'warn', + 'vue/no-irregular-whitespace': 'error', + 'vue/no-loss-of-precision': 'error', + 'vue/no-negated-condition': 'warn', + 'vue/no-sparse-arrays': 'error', + 'vue/no-useless-concat': 'warn', + 'vue/object-shorthand': 'warn', + 'vue/prefer-template': 'warn', + }, + }, + }, + { + files: ['**/*.vue'], + rules: { + 'no-useless-assignment': 'off', // ESLint не умеет работать с этим правилом внутри