const path = require('path'); const fs = require('fs'); const marked = require('marked').marked; const CopyPlugin = require('copy-webpack-plugin'); const renderer = new marked.Renderer() renderer.link = (href, title, text) => { if(href === null) { return text } let out = '' return out } renderer.image = (href, title, text) => { if (href === null) { return text; } let out = '' + text + ''; return out; } marked.setOptions({ renderer: renderer }) class BlogListingPlugin { static default_options = { output_file: 'blog_list.json', } constructor(options = {}) { this.options = {...BlogListingPlugin.default_options, ...options} } apply(compiler) { const plugin_name = BlogListingPlugin.name const { webpack } = compiler compiler.hooks.thisCompilation.tap( plugin_name, (compilation) => { compilation.hooks.processAssets.tapAsync( { name: plugin_name, stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE }, (compilationAssets, callback) => { let content = [] Object.keys(compilationAssets).sort().reverse().forEach((file_path) => { if(file_path.startsWith('blog') && file_path.endsWith('.md') && !file_path.endsWith('_draft.md')) { const file_name = path.basename(file_path).slice(0, -3) const blog_info = path.basename(path.dirname(file_path)).split('_') const blog_date = blog_info[0] const blog_entry = blog_info.slice(1).join('_') compilation.emitAsset( 'blog/' + blog_date + '_' + blog_entry + '/' + file_name + '.html', new webpack.sources.RawSource( marked( new TextDecoder('utf-8').decode(compilationAssets[file_path].source())))) content.push({ name: file_name, date: blog_date }) } }) content.sort((x1, x2) => { x1.create_date < x2.create_date }) compilation.emitAsset( this.options.output_file, new webpack.sources.RawSource(JSON.stringify(content))) return callback(); } ) } ) } } module.exports = { mode: "none", entry: './src/index.jsx', module: { rules: [ { test: /\.jsx$/, exclude: /node_modules/, use: [{ loader: "babel-loader", options: { presets: ["@babel/preset-env"], plugins: [ ["babel-plugin-inferno", { "imports": true }] ] } }] }, { test: /src.*\.s[ac]ss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }, { test: /src.*\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.svg$/, type: "asset/inline" }, { test: /assets\/.*\.(png|jpg|jpeg|gif|ttf|otf)$/, type: "asset/resource" } ] }, plugins: [ new BlogListingPlugin(), new CopyPlugin({ patterns: [ {from: 'index.html'}, {from: 'robot.txt'}, {from: 'src/lang', to: 'lang'}, {from: 'assets', to: 'assets', globOptions: {ignore: '**/blog/**'}}, {from: 'assets/blog', to: 'blog', globOptions: {ignore: '**/blog/draft_*/**'}} ]}) ], output: { filename: 'bundle.js', path: path.resolve(__dirname, 'public'), clean: true } };