Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions .eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const markdownIt = require('markdown-it')
const { parseHTML } = require('linkedom')
const { isProdEnv } = require('./config/env.js')
const { mainSections } = require('./config/constants.js')
const { cssTargets } = require('./config/css.js')
const initMarkdownLibrary = require('./src/markdown-it.js')
const demoLinkTransform = require('./src/transforms/demo-link-transform.js')
const answersLinkTransform = require('./src/transforms/answers-link-transform.js')
Expand All @@ -30,9 +31,6 @@ const { parseChangelog, enrichPost } = require('./src/libs/changelog-parser/chan
// имён, а сам плагин лежит в default.
const pluginRss = require('@11ty/eleventy-plugin-rss').default
const eleventyVitePlugin = require('@11ty/eleventy-plugin-vite').default
const postcssImport = require('postcss-import')
const postcssMediaMinmax = require('postcss-media-minmax')
const autoprefixer = require('autoprefixer')

function getAllDocs(collectionAPI) {
const dokas = collectionAPI.getFilteredByTag('doka')
Expand Down Expand Up @@ -420,15 +418,13 @@ module.exports = function (config) {

cacheDir: '.vite',

// Тот же движок и те же targets, что в прод-сборке (config/css.js),
// чтобы стили на дев-сервере и на проде не расходились. Минификация
// здесь не включена: на дев-сервере она ни к чему.
css: {
postcss: {
// csso здесь не нужен: на дев-сервере минификация не даёт ничего,
// зато ломает сборку. Он тянет css-tree 2.2.1, который не знает
// @container, @scope, вложенные слои и вложенность — а Vite гонит
// через эту цепочку и инлайновые стили демок, поэтому демка с
// @container отдавалась с 500. В прод-сборке csso остаётся: там
// postcss ходит только по src/styles, демки уезжают копией.
plugins: [postcssImport, postcssMediaMinmax, autoprefixer],
transformer: 'lightningcss',
lightningcss: {
targets: cssTargets,
},
},

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ npm run build && npm run lint:html # валидация разметки, н
| `.eleventy.js` | Главный конфиг: коллекции, фильтры, трансформации, плагины |
| `gulpfile.js` | Продакшн-пайплайн: бандл CSS/JS, кеш-хэши |
| `make-links.js` | Симлинки `src/{раздел}` → `content/{раздел}` |
| `config/` | Константы, окружение, цвета разделов |
| `config/` | Константы, окружение, цвета разделов, сборка стилей |
| `src/data/` | Глобальные данные 11ty |
| `src/layouts/`, `src/includes/` | Базовый шаблон и блоки |
| `src/views/` | Страницы: `*.njk` плюс `*.11tydata.js` с данными |
Expand Down
50 changes: 50 additions & 0 deletions config/__tests__/css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const fs = require('fs')
const os = require('os')
const path = require('path')

const { cssTargets, styleEntries, bundleStyle } = require('../css.js')

const bundleSource = (css) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'doka-css-'))
const filename = path.join(dir, 'index.css')
fs.writeFileSync(filename, css)

try {
return bundleStyle(filename).toString()
} finally {
fs.rmSync(dir, { recursive: true, force: true })
}
}

describe('сборка стилей', () => {
it('собирает @import в один файл', () => {
const result = bundleStyle(path.join('src/styles', styleEntries[0])).toString()

expect(result).not.toContain('@import')
expect(result.length).toBeGreaterThan(0)
})

// Ради этого и уезжали с csso: его css-tree не знал современного синтаксиса
// и ронял всю сборку, а Vite гонит через ту же цепочку стили демок.
it.each([
['@container', '@container (width > 400px) { .a { color: red } }'],
['@scope', '@scope (.a) to (.b) { .c { color: red } }'],
['вложенные слои', '@layer framework.base { .a { color: red } }'],
['вложенность', '.a { color: red; &.b { color: blue } }'],
])('не падает на %s', (_, css) => {
expect(() => bundleSource(css)).not.toThrow()
})

it('оставляет @container в выдаче', () => {
expect(bundleSource('@container (width > 400px) { .a { color: red } }')).toContain('@container')
})

// Префиксы ставит lightningcss по targets, отдельного autoprefixer больше нет.
it('ставит вендорные префиксы по targets', () => {
expect(bundleSource('.a { user-select: none }')).toContain('-webkit-user-select')
})

it('вычисляет targets из browserslist проекта', () => {
expect(Object.keys(cssTargets).length).toBeGreaterThan(0)
})
})
22 changes: 22 additions & 0 deletions config/css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const browserslist = require('browserslist')
const { bundle, browserslistToTargets } = require('lightningcss')

const { browserslist: browsers } = require('../package.json')

// Единственный источник правды по браузерам — поле browserslist в package.json.
// По нему lightningcss решает и какие вендорные префиксы ставить, и какой
// синтаксис компилировать: раньше первым занимался autoprefixer, вторым —
// postcss-media-minmax, и каждый жил по своим правилам.
const cssTargets = browserslistToTargets(browserslist(browsers))

// Точки входа: всё остальное подключается к ним через @import, их же
// lightningcss и собирает в один файл.
const styleEntries = ['index.css', 'index.sc.css', 'dark-theme.css']

const bundleStyle = (filename) => bundle({ filename, minify: true, targets: cssTargets }).code

module.exports = {
cssTargets,
styleEntries,
bundleStyle,
}
32 changes: 12 additions & 20 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
const path = require('path')
const fs = require('fs')
const { rm } = require('node:fs/promises')
const { mkdir, rm, writeFile } = require('node:fs/promises')

const gulp = require('gulp')
const git = require('gulp-git')
const shell = require('gulp-shell')
const postcss = require('gulp-postcss')
const csso = require('postcss-csso')
const pimport = require('postcss-import')
const minmax = require('postcss-media-minmax')
const autoprefixer = require('autoprefixer')
// С версии 0.15 gulp-esbuild экспортирует именованные функции вместо самого
// себя, а entryPoints и outfile стали обязательными: раньше точка входа
// бралась из потока gulp. Без них поток молча повисает, и gulp сообщает
Expand All @@ -21,6 +16,7 @@ const rev = require('gulp-rev').default
const revRewrite = require('gulp-rev-rewrite').default

const { contentRepGithub, contentRepFolders } = require(path.join(__dirname, 'config/constants'))
const { styleEntries, bundleStyle } = require(path.join(__dirname, 'config/css'))

// Раньше здесь был пакет del. С 7-й версии он ESM-only и больше не callable
// (экспортирует deleteAsync/deleteSync), а обе площадки вызова передавали
Expand All @@ -38,20 +34,16 @@ const makeLinks = shell.task(`node make-links.js --default`, {

// Styles

const styles = () => {
return gulp
.src('src/styles/{index.css,index.sc.css,dark-theme.css}')
.pipe(
postcss([
pimport,
minmax,
autoprefixer,
csso({
restructure: false,
}),
]),
)
.pipe(gulp.dest('dist/styles'))
// lightningcss сам собирает @import, ставит префиксы и минифицирует, поэтому
// gulp-поток здесь не нужен: точки входа известны, файлы он читает с диска.
const styles = async () => {
await mkdir('dist/styles', { recursive: true })

await Promise.all(
styleEntries.map((entry) =>
writeFile(path.join('dist/styles', entry), bundleStyle(path.join('src/styles', entry))),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А зачем тогда Vite?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Валидно, надо будет оставить только его и для локальной и для продакшен сборки. В следующих сериях =)

),
)
}

// Scripts
Expand Down
Loading
Loading