PUG
PUG
PUG (ранее: Jade) - высокопроизводительный шаблонизатор HTML разметки.
Этот проект ранее был известен как "Jade". Однако разработчикам стало известно, что "Jade" является зарегистрированной торговой маркой; в результате потребовалось переименование. После некоторого обсуждения среди сопровождающих в качестве нового имени для этого проекта было выбрано "Pug" . Начиная с версии 2, "pug" является официальным именем пакета.
Основные ресурсы
Ссылки на библиотеки и фреймворки используемые в сборке.
Vite: https://vite.dev/
RollUpJs: https://rollupjs.org/
TypeScript: https://www.typescriptlang.org/
HTMX: https://htmx.org/
_hyperScript: https://hyperscript.org/
PUG JavaScript:
Основная документация JavaScript (может потребоваться VPN) : https://pugjs.org/api/getting-started.html
Основной репозиторий JavaScript кода: https://github.com/pugjs/pug
PUG PHP:
Основная документация для транспайлера PHP: https://www.phug-lang.com/
Основной репозиторий для PHP: https://github.com/pug-php/pug
Инсталляция
Разворачиваем каркас приложения
Для установки необходимо наличие служб Node.JS и NPM.
В примерах производится установка на версиях:
node - 21.7.3
npm - 10.5.0
Инициализируем установку шаблона приложение, с помощью движка Vite.
npm create vite@latest . -- --template vanilla-ts
Устанавливаем зависимости.
После установки зависимостей (пакеты библиотек) в проекте появится папка /node_modules
npm install
Делаем тестовый запуск.
npm run dev
Следующий шагом необходимо привести файл package.json
к следующему виду:
Добавляются следующие библиотеки и решения: веб-сервер express, htmx, pino логгер, типы для typescript, плагин pug, sass-препроцессор css, плагин для инъекций в приложение.
{
"name": "any-project-name",
"private": true,
"version": "0.0.0",
"type": "module",
"description": "",
"author": "",
"license": "ISC",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"dependencies": {
"express": "^4.18.2",
"htmx.org": "^2.0.1",
"pino": "^8.19.0",
"pino-colada": "^2.2.2",
"pino-pretty": "^10.3.1"
},
"devDependencies": {
"@antfu/eslint-config": "^2.6.4",
"@rollup/plugin-inject": "^5.0.5",
"@rushstack/eslint-patch": "^1.7.2",
"@types/glob": "^8.1.0",
"@types/node": "^20.11.13",
"@types/pino": "^7.0.5",
"@types/pug": "^2.0.10",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"eslint": "^8.56.0",
"glob": "^10.4.2",
"lint-staged": "^15.2.1",
"pug": "^3.0.2",
"sass": "^1.70.0",
"sass-loader": "^14.1.0",
"typescript": "~5.6.2",
"vite": "^5.4.10",
"vite-plugin-pug": "^0.3.2"
}
}
Повторно запустить процесс добавление зависимостей.
npm install
Удаляем все файлы из папки /src
Модифицируем файл tsconfig.json
следующими правками:
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"baseUrl": ".",
/* Bundler mode */
"moduleResolution": "Bundler",
"paths": {
"@/*": ["src/*"],
"@/assets": ["src/assets/*"],
"@/components": ["src/components/*"],
"@/models": ["src/models/*"],
"@/pages": ["src/pages/*"],
"@/plugins": ["src/plugins/*"],
"@/scripts": ["src/scripts/*"],
"@/libs": ["src/libs/*"]
},
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
/* Linting */
"strict": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmit": true,
},
"references": [{ "path": "./tsconfig.node.json" }],
"include": ["src", "src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"],
"types": ["vite/client"],
}
Добавляем файл tsconfig.node.json
в корень проекта, со следующим содержанием:
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"skipLibCheck": true
},
"include": ["vite.config.ts"]
}
Добавляем файл .prettierrc
в корень проекта, со следующим содержанием:
{
"semi": true,
"singleQuote": true,
"bracketSpacing": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 4,
"arrowParens": "always",
"endOfLine": "lf"
}
Добавляем файл eslint.config.js
в корень проекта, со следующим содержанием:
import antfu from '@antfu/eslint-config';
export default antfu({
rules: {
curly: 'off',
'antfu/consistent-list-newline': 'off',
'antfu/if-newline': 'off',
'import/newline-after-import': 0,
'no-console': 'off',
'style/arrow-parens': 'off',
'style/brace-style': 'off',
'style/indent': 'off',
'style/operator-linebreak': 'off',
'style/spaced-comment': 'off',
},
typescript: true,
yaml: false,
stylistic: {
semi: true,
indent: 4,
quotes: 'single',
},
ignores: ['public/', 'node_modules/', 'dist/', 'build/', 'bruno/'],
});
Добавляем файл vite.config.ts
в корень проекта, со следующим содержанием:
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { globSync } from 'glob';
import { defineConfig } from 'vite';
import pugPlugin from 'vite-plugin-pug';
/**
* ! не удалять и неизменять
* Добавляет на каждую страницу инициализацию для скриптов из ./src/common.ts, в head нужно для htmx и _hyperscript
*
* todo: приветствуюется лучшее решение для инициализации, или ждем пока разработчики запакуют исходники в модуль ES6
*
* @see https://vitejs.dev/guide/api-plugin#transformindexhtml
*/
function commonPlugin() {
return {
name: 'html-add-common',
transformIndexHtml() {
return [
{
tag: 'script',
attrs: {
src: 'https://unpkg.com/hyperscript.org@0.9.13',
},
injectTo: 'head',
},
{
tag: 'script',
attrs: {
src: 'https://unpkg.com/htmx.org@2.0.3',
integrity:
'sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq',
crossorigin: 'anonymous',
},
injectTo: 'head',
},
{
tag: 'title',
children: 'Умный Сервис',
injectTo: 'head',
},
{
tag: 'script',
attrs: {
type: 'module',
src: '/src/main.ts',
},
injectTo: 'body',
},
//{
// tag: 'script',
// attrs: { type: 'module', src: './src/q-core.ts' },
//},
//{
// tag: 'script',
// attrs: { type: 'module', src: './src/common.ts' },
//},
];
},
};
}
export default defineConfig({
build: {
cssMinify: false,
minify: false,
rollupOptions: {
/**
* @see https://vite-docs-ru.vercel.app/config/#build-sourcemap
* @see https://rollupjs.org/configuration-options/#core-functionality
*/
input: Object.fromEntries(
globSync(['./*.html', './src/html/**/*.html'])
.filter((file) => {
// Exclude debug.html in build stage
if (
process.env.NODE_ENV === 'production' &&
file === 'debug.html'
)
return false;
return true;
})
.map((file) => [
path.relative(
'',
file.slice(
0,
file.length - path.extname(file).length,
),
),
fileURLToPath(new URL(file, import.meta.url)),
]),
),
output: {
// inlineDynamicImports: true,
chunkFileNames: `assets/js/[name].[hash].js`,
entryFileNames: `assets/js/[name].[hash].js`,
assetFileNames: (assetInfo) => {
let extType = assetInfo.name.split('.').pop();
if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType))
extType = 'img';
if (/eot|ttf|woff2?/i.test(extType)) extType = 'font';
return `assets/${extType}/[name].[hash][extname]`;
},
},
},
},
css: {
preprocessorOptions: {
sass: {
//? https://sass-lang.com/documentation/breaking-changes/import/
silenceDeprecations: ['import', 'call-string'],
//additionalData: `@import './src/assets/sass/main.sass'`,
},
},
},
plugins: [
pugPlugin({ doctype: 'html' }),
/** @ts-expect-error some description generic problems*/
commonPlugin(),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@assets': path.resolve(__dirname, './src/assets'),
'@components': path.resolve(__dirname, './src/components'),
'@models': path.resolve(__dirname, './src/models'),
'@pages': path.resolve(__dirname, './src/pages'),
'@plugins': path.resolve(__dirname, './src/plugins'),
'@scripts': path.resolve(__dirname, './src/scripts'),
'@libs': path.resolve(__dirname, './src/libs'),
},
},
server: {
host: '0.0.0.0',
port: 8084,
},
});
В паке /src
создаем следующие папки:
/assets
- в этой папке создаем папки: /font
- исходники шрифтов, /sass
- файлы со стилями проекта для препроцессора sass/libs
- для библиотек js/ts/pages
- содержит страницы сущности в формате PUG/plugins
- кастомные плагины/scripts
- кастомные скрипты
/blocks
- файлы с блоками PUG (их можно воспринимать как классы в программировании)/components
- файлы с компонентами PUG (в основном это миксины)/partials
- файлы с частицами PUG (логически завершенные куски кода/разметки)
Файловая структура проекта
В этой секции изложены основные требования к содержанию и ведению файловой структуры любого проекта в котором используется этот шаблонизатор.
Блоки (Blocks)
Блок -
Частицы (Partials)
Частица -
Компоненты (Components)
Компонент -
Примеси (Mixins)
Примеси (Миксины) - скомпилированные функции позволяющие создавать повторнопереиспользуемые блоки с разметкой.
Миксины содержут неявную директиву
&attributes(attributes)
, позволяющую прокинуть извне атрибуты и их значения к привязонному к ней селектору.Аргументы функции миксина позволяют принимать значения по умолчанию.
mixin article(title='Default Title')
Миксин может получить неограниченное число аргументов. Используется структура аргумента
spread
Пример:mixin list(id, ...items)
Подробнее: https://pugjs.org/language/mixins.html
Стандартизация
В этом блоке описываются общие требования к синтаксическому оформлению pug файлов проекта.
- Название вызываемой функции миксина должно соответствовать названию файла с миксином.
- Функция миксина должна использовать не более трех аргументов, если аргументов больше, необходимо воспользоваться директивой
&attributes(attributes)
. - При подключении файла, первым идет указатель на папку:
./
- текущая директория,../
- родительская директория. - При подключении файла всегда указываем его расширение.
- Файл pug не должен содержать "магических" строк и чисел, все значения привязываем к переменным.
- Если набор данных возможно проитерировать, его необходимо вынести в json файл.
- Обязательно оставляем комментарии если файл разметки содержит опциональные варианты контента.
- Запрещено использовать литералы шаблона JS
`${ переменная }`
, не использовать.join('')
и иные функции объектов в буферизированном потоке. Исполняемый код необходимо размещать в переменных. - Не использовать фиксированные наименования у маршрутов к ресурсам проектов. Необходимо передавать полный uri путь к файлу.
- Использовать интерполяцию для закрывающихся и самозакрывающихся тегов.
FAQ
Основные ошибки и способы их устранения.
No Comments