Skip to content
Draft
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
78 changes: 26 additions & 52 deletions .eleventy.js
Original file line number Diff line number Diff line change
@@ -1,95 +1,69 @@
// .eleventy.ts
import path from "path";
import { EleventyRenderPlugin } from "@11ty/eleventy";
import eleventyNavigationPlugin from "@11ty/eleventy-navigation";
import buildAssets from "./build-assets.js";

const now = String(Date.now());

// создаем коллекции на основе папок
// например newsByYear = { 2025: [{}], 2024: [{}], ... }
const makeCollection = (collection, folderName) => {
var now = String(Date.now());
var makeCollection = (collection, folderName) => {
const files = collection.getFilteredByGlob(`./pages/${folderName}/**/*.md`);
return files.reduce((years, post) => {
const year = path.dirname(post.inputPath).split("/").pop();
if (!years[year]) years[year] = [];

// добавляем в начало
// years[year].push(post);
if (!years[year]) {
years[year] = [];
}
years[year].unshift(post);
return years;
}, {});
};

export default function (eleventyConfig) {
// Enable quiet mode to reduce console noise
function eleventy_default(eleventyConfig) {
eleventyConfig.setQuietMode(true);

eleventyConfig.addPlugin(eleventyNavigationPlugin);

// https://www.11ty.dev/docs/plugins/render/#renderfile
eleventyConfig.addPlugin(EleventyRenderPlugin);

// Copy all images directly to dist.
eleventyConfig.addPassthroughCopy({ "src/assets/images": "/assets/images" });
// Copy robots.txt, etc to dist.
eleventyConfig.addPassthroughCopy({ "src/assets/static/*": "/" });

// папки для создания авто-коллекций
// @see makeCollection
const folders = ["news"];
folders.forEach((folderName) => {
eleventyConfig.addCollection(`${folderName}ByYear`, (collection) =>
makeCollection(collection, folderName)
eleventyConfig.addCollection(
`${folderName}ByYear`,
(collection) => makeCollection(collection, folderName)
);
});

// Отображаем дату в человеко-понятном виде, например 11 февраля
// @example {{ post.date | getHumanDate }}
eleventyConfig.addFilter("getHumanDate", function (dateObj) {
eleventyConfig.addFilter("getHumanDate", function(dateObj) {
const date = new Date(dateObj);
const options = {
// Type options
day: "2-digit",
month: "long",
locale: "ru-RU",
month: "long"
};
return date.toLocaleDateString("ru-RU", options);
});

// фильтр обрезает коллекцию
// @example {{ collection | limit(2) }}
eleventyConfig.addNunjucksFilter("limit", (array, limit) =>
array.slice(0, limit)
eleventyConfig.addNunjucksFilter(
"limit",
(array, limit) => (
// Type array and limit
array.slice(0, limit)
)
);

// TODO: фильтр для создания архива по годам
// eleventyConfig.addFilter("getYears", function (collection) {
// return Object.keys(collection);
// });

// текущий год доступен глобально
eleventyConfig.addGlobalData(
"getGlobalCurrentYear",
new Date().getFullYear().toString()
(/* @__PURE__ */ new Date()).getFullYear().toString()
);

// Add cache busting with {% version %} time string
eleventyConfig.addShortcode("version", function () {
eleventyConfig.addShortcode("version", function() {
return now;
});

// Build JS and CSS assets
eleventyConfig.on("beforeBuild", buildAssets);

eleventyConfig.addWatchTarget("./src/assets/");

return {
templateFormats: ["md", "njk", "html"],
dir: {
input: "pages",
output: "dist",
includes: "../src/_includes",
data: "../src/_data",
layouts: "../src/_includes/layouts",
},
layouts: "../src/_includes/layouts"
}
};
}
export {
eleventy_default as default
};
85 changes: 85 additions & 0 deletions .eleventy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import path from "path";
import { EleventyRenderPlugin } from "@11ty/eleventy";
import eleventyNavigationPlugin from "@11ty/eleventy-navigation";
import buildAssets from "./build-assets.js"; // Points to the compiled .ts output
import type { UserConfig, CollectionItem } from "@11ty/eleventy"; // Import UserConfig and CollectionItem

const now: string = String(Date.now());

// Define a type for the 'years' accumulator object in makeCollection
interface YearCollection {
[year: string]: CollectionItem[];
}

// Define a type for the collection API object passed to makeCollection
interface EleventyCollectionApi {
getFilteredByGlob(glob: string): CollectionItem[];
}

const makeCollection = (collection: EleventyCollectionApi, folderName: string): YearCollection => {
const files: CollectionItem[] = collection.getFilteredByGlob(`./pages/${folderName}/**/*.md`);
return files.reduce((years: YearCollection, post: CollectionItem) => {
const year = path.dirname(post.inputPath).split("/").pop() as string; // Ensure year is string
if (!years[year]) {
years[year] = [];
}
years[year].unshift(post);
return years;
}, {});
};

export default function (eleventyConfig: UserConfig): UserConfig { // Type eleventyConfig and return type
eleventyConfig.setQuietMode(true);
eleventyConfig.addPlugin(eleventyNavigationPlugin);
eleventyConfig.addPlugin(EleventyRenderPlugin);

eleventyConfig.addPassthroughCopy({ "src/assets/images": "/assets/images" });
eleventyConfig.addPassthroughCopy({ "src/assets/static/*": "/" });

const folders: string[] = ["news"];
folders.forEach((folderName: string) => {
eleventyConfig.addCollection(`${folderName}ByYear`, (collection: EleventyCollectionApi) =>
makeCollection(collection, folderName)
);
});

eleventyConfig.addFilter("getHumanDate", function (dateObj: string | Date): string { // Type dateObj
const date = new Date(dateObj);
const options: Intl.DateTimeFormatOptions = { // Type options
day: "2-digit",
month: "long",
};
return date.toLocaleDateString("ru-RU", options);
});

eleventyConfig.addNunjucksFilter("limit", (array: any[], limit: number): any[] => // Type array and limit
array.slice(0, limit)
);

eleventyConfig.addGlobalData(
"getGlobalCurrentYear",
new Date().getFullYear().toString()
);

eleventyConfig.addShortcode("version", function (): string {
return now;
});

// Type the buildAssets function if its signature is known, otherwise any
// Assuming buildAssets is async () => Promise<void> as per build-assets.ts
eleventyConfig.on("beforeBuild", buildAssets as () => Promise<void>);

eleventyConfig.addWatchTarget("./src/assets/");

// Ensure the return object matches Eleventy's expected config structure (implicitly typed by UserConfig return type)
return {
templateFormats: ["md", "njk", "html"],
dir: {
input: "pages",
output: "dist",
includes: "../src/_includes",
data: "../src/_data",
layouts: "../src/_includes/layouts",
},
};
}
106 changes: 48 additions & 58 deletions build-assets.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,54 @@
import { build } from 'esbuild';
import { YAMLPlugin } from 'esbuild-yaml';
import { exec } from 'child_process';

const input = 'src/assets';
const output = 'dist/assets';

/**
* Собираем JS файлы
*/
// build-assets.ts
import { build } from "esbuild";
import { YAMLPlugin } from "esbuild-yaml";
import { exec } from "child_process";
var input = "src/assets";
var output = "dist/assets";
async function buildJavaScript(isProduction) {
console.log('= 1 = Сборка JS файлов...');
await build({
entryPoints: [`${input}/js/index.js`],
bundle: true,
outdir: `${output}/js`,
minify: isProduction,
sourcemap: !isProduction,
target: 'es6',
plugins: [YAMLPlugin()],
});
console.log("= 1 = \u0421\u0431\u043E\u0440\u043A\u0430 JS \u0444\u0430\u0439\u043B\u043E\u0432...");
const options = {
entryPoints: [`${input}/js/index.js`],
bundle: true,
outdir: `${output}/js`,
minify: isProduction,
sourcemap: !isProduction,
target: "es6",
plugins: [YAMLPlugin()]
};
await build(options);
}

/**
* Собираем CSS файлы с помощью PostCSS
*/
async function buildStyles(isProduction) {
console.log('= 2 = Сборка CSS файлов...');

const postcssInputs = [
`${input}/styles/index.css`,
`${input}/styles/critical.css`
].join(' ');

const noMapOption = isProduction ? '' : '--no-map';
const postcssCommand = `postcss ${postcssInputs} --dir ${output}/styles --config postcss.config.cjs ${noMapOption}`;

return new Promise((resolve, reject) => {
exec(postcssCommand, (error, stdout, stderr) => {
if (error) {
console.error('PostCSS Error:', stderr);
console.error('PostCSS Stdout:', stdout); // Also log stdout for more context
reject(error);
} else {
resolve();
}
});
console.log("= 2 = \u0421\u0431\u043E\u0440\u043A\u0430 CSS \u0444\u0430\u0439\u043B\u043E\u0432...");
const postcssInputs = [
`${input}/styles/index.css`,
`${input}/styles/critical.css`
].join(" ");
const noMapOption = isProduction ? "" : "--no-map";
const postcssCommand = `postcss ${postcssInputs} --dir ${output}/styles --config postcss.config.js ${noMapOption}`;
return new Promise((resolve, reject) => {
exec(postcssCommand, (error, stdout, stderr) => {
if (error) {
console.error("PostCSS Error:", stderr);
console.error("PostCSS Stdout:", stdout);
reject(error);
} else {
resolve();
}
});
});
}
async function eleventyBuildAssets() {
const isProduction = process.env.NODE_ENV === "production";
console.log(`\u041F\u0440\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u043E \u0432 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0438: ${isProduction ? "production" : "development"}`);
try {
await buildJavaScript(isProduction);
await buildStyles(isProduction);
console.log("\u2705 CSS \u0438 JS \u0444\u0430\u0439\u043B\u044B \u0441\u043E\u0431\u0440\u0430\u043D\u044B \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E.");
} catch (e) {
console.error("Overall build failed:", e);
process.exit(1);
}
}

// Main build function exported for Eleventy
export default async () => {
const isProduction = process.env.NODE_ENV === 'production';
console.log(`Проложение запущено в окружении: ${isProduction ? 'production' : 'development'}`);

try {
await buildJavaScript(isProduction);
await buildStyles(isProduction);
console.log('✅ CSS и JS файлы собраны корректно.');
} catch (e) {
console.error('Overall build failed:', e);
process.exit(1); // Ensure process exits on failure
}
export {
eleventyBuildAssets as default
};
66 changes: 66 additions & 0 deletions build-assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { build, BuildOptions } from 'esbuild';
import { YAMLPlugin } from 'esbuild-yaml';
import { exec, ChildProcess } from 'child_process'; // Import ChildProcess for typing if needed, though exec types from @types/node are usually sufficient

const input: string = 'src/assets';
const output: string = 'dist/assets';

/**
* Собираем JS файлы
*/
async function buildJavaScript(isProduction: boolean): Promise<void> {
console.log('= 1 = Сборка JS файлов...');
const options: BuildOptions = {
entryPoints: [`${input}/js/index.js`],
bundle: true,
outdir: `${output}/js`,
minify: isProduction,
sourcemap: !isProduction,
target: 'es6',
plugins: [YAMLPlugin()],
};
await build(options);
}

/**
* Собираем CSS файлы с помощью PostCSS
*/
async function buildStyles(isProduction: boolean): Promise<void> {
console.log('= 2 = Сборка CSS файлов...');

const postcssInputs: string = [
`${input}/styles/index.css`,
`${input}/styles/critical.css`
].join(' ');

const noMapOption: string = isProduction ? '' : '--no-map';
// IMPORTANT: Update to use postcss.config.js
const postcssCommand: string = `postcss ${postcssInputs} --dir ${output}/styles --config postcss.config.js ${noMapOption}`;

return new Promise<void>((resolve, reject) => {
exec(postcssCommand, (error, stdout, stderr) => {
if (error) {
console.error('PostCSS Error:', stderr);
console.error('PostCSS Stdout:', stdout); // Also log stdout for more context
reject(error);
} else {
resolve();
}
});
});
}

// Main build function exported for Eleventy
export default async function eleventyBuildAssets(): Promise<void> {
const isProduction: boolean = process.env.NODE_ENV === 'production';
console.log(`Проложение запущено в окружении: ${isProduction ? 'production' : 'development'}`);

try {
await buildJavaScript(isProduction);
await buildStyles(isProduction);
console.log('✅ CSS и JS файлы собраны корректно.');
} catch (e: any) { // Or unknown, then check type
console.error('Overall build failed:', e);
process.exit(1); // Ensure process exits on failure
}
}
Loading