| const path = require("path"); |
| const { CleanWebpackPlugin } = require("clean-webpack-plugin"); |
| const CopyPlugin = require("copy-webpack-plugin"); |
| const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; |
| const Handlebars = require("handlebars"); |
| const fs = require("fs"); |
|
|
| |
| const appConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname, "config/app.json"), "utf8")); |
| const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); |
| const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin"); |
|
|
| const FRAGMENTS_PATH = "src/fragments"; |
|
|
| |
| const loadFragmentsMap = (() => { |
| let cachedFragments = null; |
| return async () => { |
| if (cachedFragments === null) { |
| cachedFragments = {}; |
| const walkDir = async (dir, basePath = '') => { |
| const files = fs.readdirSync(dir); |
| await Promise.all(files.map(async file => { |
| const filePath = path.join(dir, file); |
| const relativePath = path.join(basePath, file); |
| if (fs.statSync(filePath).isDirectory()) { |
| await walkDir(filePath, relativePath); |
| } else { |
| const nameWithoutExt = relativePath.replace(/\.html$/, ''); |
| const dottedPath = 'fragment-' + nameWithoutExt.replace(/\\/g, '-').replace(/\//g, '-').replace(/\./g, '-'); |
| const content = fs.readFileSync(filePath, "utf8"); |
| let minifiedContent; |
|
|
| if (content.trim().startsWith('<!DOCTYPE') || content.trim().startsWith('<html')) { |
| minifiedContent = content; |
| } else { |
| try { |
| const minifiedRes = await HtmlMinimizerPlugin.swcMinifyFragment({"tmp.html": content}) |
| if (minifiedRes.errors) { |
| minifiedContent = content; |
| } else { |
| minifiedContent = minifiedRes.code; |
| } |
| } catch (error) { |
| minifiedContent = content; |
| } |
| } |
| cachedFragments[dottedPath] = minifiedContent; |
| } |
| })); |
| }; |
| await walkDir(FRAGMENTS_PATH); |
| } |
| return cachedFragments; |
| }; |
| })(); |
|
|
| const transformMarkdownWithFragments = async (data, filepath) => { |
| const fragments = await loadFragmentsMap(); |
| console.log(`Available fragments: ${Object.keys(fragments).join(', ')}`); |
| |
| |
| const markdown = require('markdown-it')({ |
| html: true, |
| linkify: true, |
| typographer: true |
| }); |
| |
| const markdownContent = data.toString('utf8'); |
| const htmlContent = markdown.render(markdownContent); |
| |
| |
| const template = Handlebars.compile(htmlContent); |
| return template(fragments); |
| }; |
|
|
| module.exports = { |
| entry: { |
| distill: "./src/distill.js", |
| main: "./src/index.js", |
| }, |
| output: { |
| filename: "[name].bundle.js", |
| path: path.resolve(__dirname, "dist"), |
| }, |
| module: { |
| rules: [ |
| { test: /\.css$/, use: ["style-loader", "css-loader"] }, |
| { |
| test: /\.(js|mjs)$/, |
| exclude: /node_modules/, |
| use: { |
| loader: "babel-loader", |
| options: { |
| presets: ["@babel/preset-env"], |
| }, |
| }, |
| } |
| ], |
| }, |
| plugins: [ |
| new CleanWebpackPlugin(), |
| new CopyPlugin({ |
| patterns: [ |
| { from: "src/fragments/*", to: "fragments/[name].html" }, |
| { from: "src/style.css", to: "style.css" }, |
| { from: "src/transformers-custom.css", to: "transformers-custom.css" }, |
| { from: "content/*.png", to: "static/[name][ext]" }, |
| { from: "content/*.svg", to: "static/[name][ext]" }, |
| { from: "content/*.html", to: "static/[name][ext]" }, |
| { from: "content/hf-logo.svg", to: "hf-logo.svg" }, |
| { |
| from: "content/article.md", |
| to: "index.html", |
| transform: async (content, path) => { |
| const fragments = await loadFragmentsMap(); |
| |
| |
| const markdown = require('markdown-it')({ |
| html: true, |
| linkify: true, |
| typographer: true |
| }); |
| |
| const markdownContent = content.toString('utf8'); |
| const htmlContent = markdown.render(markdownContent); |
| |
| |
| const tocScript = ` |
| <script> |
| function initializeTOC() { |
| const article = document.querySelector('d-article'); |
| const toc = document.querySelector('d-contents'); |
| if (toc) { |
| const headings = [...article.querySelectorAll('h1, h2, h3, h4')].filter(h => !h.hasAttribute('data-no-toc')); |
| let ToC = '<nav role="navigation" class="l-text figcaption">'; |
| ToC += '<div class="toc-header"><span class="toc-title">Table of Contents</span></div>'; |
| ToC += '<div class="toc-content">'; |
| |
| headings.forEach((heading, index) => { |
| const id = heading.id || 'heading-' + index; |
| if (!heading.id) heading.id = id; |
| const level = parseInt(heading.tagName.charAt(1)); |
| const indent = level === 1 ? '' : 'style="margin-left: ' + ((level - 1) * 1.2) + 'em;"'; |
| ToC += '<div ' + indent + '><a href="#' + id + '">' + heading.textContent + '</a></div>'; |
| }); |
| |
| ToC += '</div></nav>'; |
| toc.innerHTML = ToC; |
| toc.setAttribute('prerendered', 'true'); |
| |
| // Extract tenet text for tooltips |
| const tenetTooltips = { |
| 'source-of-truth': 'We aim be a source of truth for all model definitions. Model implementations should be reliable, reproducible, and faithful to the original performances.', |
| 'one-model-one-file': 'All inference and training core logic has to be visible, top‑to‑bottom, to maximize each model\\'s hackability.', |
| 'code-is-product': 'Optimize for reading, diffing, and tweaking, our users are power users. Variables can be explicit, full words, even several words, readability is primordial.', |
| 'standardize-dont-abstract': 'If it\\'s model behavior, keep it in the file; abstractions only for generic infra.', |
| 'do-repeat-yourself': 'Copy when it helps users; keep successors in sync without centralizing behavior.', |
| 'minimal-user-api': 'Config, model, preprocessing; from_pretrained, save_pretrained, push_to_hub. We want the least amount of codepaths.', |
| 'backwards-compatibility': 'Evolve by additive standardization, never break public APIs.', |
| 'consistent-public-surface': 'Same argument names, same outputs, hidden states and attentions exposed, enforced by tests.', |
| }; |
| |
| // Add smooth scrolling and custom tooltips to all tenet links (TOC and article) |
| const tocLinks = document.querySelectorAll('d-contents a'); |
| tocLinks.forEach(link => { |
| const href = link.getAttribute('href'); |
| const anchor = href ? href.substring(1) : ''; |
| |
| if (tenetTooltips[anchor]) { |
| link.setAttribute('data-tooltip', tenetTooltips[anchor]); |
| link.style.position = 'relative'; |
| } |
| |
| link.addEventListener('click', function(e) { |
| e.preventDefault(); |
| const target = document.querySelector(this.getAttribute('href')); |
| if (target) { |
| target.scrollIntoView({ behavior: 'smooth' }); |
| } |
| }); |
| }); |
| |
| // Add custom tooltips to tenet links in article content |
| const articleLinks = document.querySelectorAll('d-article a[href^="#"]'); |
| articleLinks.forEach(link => { |
| const href = link.getAttribute('href'); |
| const anchor = href ? href.substring(1) : ''; |
| if (tenetTooltips[anchor]) { |
| link.setAttribute('data-tooltip', tenetTooltips[anchor]); |
| } |
| }); |
| |
| // Update active state on scroll |
| window.addEventListener('scroll', function() { |
| const scrollPos = window.scrollY + 100; |
| headings.forEach((heading) => { |
| const link = document.querySelector('d-contents a[href="#' + heading.id + '"]'); |
| if (link) { |
| if (heading.offsetTop <= scrollPos && |
| heading.offsetTop + heading.offsetHeight > scrollPos) { |
| link.classList.add('active'); |
| } else { |
| link.classList.remove('active'); |
| } |
| } |
| }); |
| }); |
| } |
| } |
| |
| // Initialize Prism syntax highlighting |
| function initializeSyntaxHighlighting() { |
| if (typeof Prism !== 'undefined') { |
| Prism.highlightAll(); |
| } |
| } |
| |
| // Try multiple times to ensure it runs after distill.js |
| document.addEventListener('DOMContentLoaded', function() { |
| initializeTOC(); |
| initializeSyntaxHighlighting(); |
| }); |
| setTimeout(function() { |
| initializeTOC(); |
| initializeSyntaxHighlighting(); |
| }, 100); |
| setTimeout(function() { |
| initializeTOC(); |
| initializeSyntaxHighlighting(); |
| }, 500); |
| setTimeout(function() { |
| initializeTOC(); |
| initializeSyntaxHighlighting(); |
| }, 1000); |
| </script>`; |
|
|
| |
| const template = `<!DOCTYPE html> |
| <html> |
| <head> |
| <script src="distill.bundle.js" type="module" fetchpriority="high" blocking></script> |
| <script src="main.bundle.js" type="module" fetchpriority="low" defer></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script> |
| <script src="https://d3js.org/d3.v7.min.js"></script> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <meta charset="utf8"> |
| <title>${appConfig.fullTitle}</title> |
| <link rel="stylesheet" href="style.css"> |
| <link rel="stylesheet" href="transformers-custom.css"> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css"> |
| </head> |
| <body> |
| <d-front-matter> |
| <script id='distill-front-matter' type="text/json">{ |
| "title": "${appConfig.fullTitle}", |
| "description": "${appConfig.description}", |
| "published": "Aug 21, 2025", |
| "authors": [{"author": "Pablo Montalvo", "authorURL": "https://huggingface.co/Molbap"}, |
| {"author": "Lysandre Debut", "authorURL": "https://huggingface.co/lysandre"}, |
| {"author": "Pedro Cuenca", "authorURL": "https://huggingface.co/pcuenq"} |
| {"author": "Yoni Gozlan", "authorURL": "https://huggingface.co/yonigozlan"}] |
| }</script> |
| </d-front-matter> |
| <d-title> |
| <h1>${appConfig.fullTitle}</h1> |
| <p>${appConfig.description}</p> |
| </d-title> |
| <d-byline></d-byline> |
| <d-article> |
| <d-contents> |
| <nav role="navigation" class="l-text figcaption"> |
| <div class="toc-header"><span class="toc-title">Table of Contents</span></div> |
| <div class="toc-content"> |
| <div><a href="#introduction">Introduction</a></div> |
| <div style="margin-left: 1.2em;"><a href="#what-you-will-learn">What you will learn</a></div> |
| <div><a href="#source-of-truth">0. Source of truth</a></div> |
| <div><a href="#one-model-one-file">1. One model, one file</a></div> |
| <div><a href="#code-is-product">2. Code is product</a></div> |
| <div><a href="#standardize-dont-abstract">3. Standardize, don't abstract</a></div> |
| <div><a href="#do-repeat-yourself">4. DRY* (DO Repeat Yourself)</a></div> |
| <div><a href="#minimal-user-api">5. Minimal user API</a></div> |
| <div><a href="#backwards-compatibility">6. Backwards compatibility</a></div> |
| <div><a href="#consistent-public-surface">7. Consistent public surface</a></div> |
| <div><a href="#modular">Going modular</a></div> |
| <div><a href="#attention-classes">External Attention classes</a></div> |
| <div><a href="#encoders-ftw">Encoders win!</a></div> |
| </div> |
| </nav> |
| </d-contents> |
| ${htmlContent} |
| </d-article> |
| ${tocScript} |
| </body> |
| </html>`; |
| |
| |
| const handlebars = Handlebars.compile(template); |
| return handlebars(fragments); |
| } |
| }, |
| ], |
| }), |
| ], |
| devtool: process.env.NODE_ENV === 'production' ? 'source-map' : 'eval-source-map', |
| devServer: { |
| static: { |
| directory: path.join(__dirname, 'dist'), |
| }, |
| hot: true, |
| watchFiles: ['src/**/*'], |
| client: { |
| overlay: true, |
| }, |
| }, |
| mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', |
| optimization: { |
| minimizer: [ |
| new ImageMinimizerPlugin({ |
| minimizer: [{ |
| implementation: ImageMinimizerPlugin.sharpMinify, |
| options: { |
| encodeOptions: { |
| jpeg: { |
| quality: 80 |
| }, |
| png: { |
| quality: 80 |
| }, |
| webp: { |
| quality: 80 |
| } |
| } |
| } |
| }, |
| { |
| implementation: ImageMinimizerPlugin.svgoMinify, |
| options: { |
| encodeOptions: { |
| multipass: true, |
| plugins: [ |
| 'preset-default', |
| ] |
| } |
| } |
| } |
| ] |
| }), |
| ] |
| }, |
| }; |
|
|
| console.log(process.env.NODE_ENV) |
|
|