Skip to content

Commit

Permalink
Improve webpack config
Browse files Browse the repository at this point in the history
  • Loading branch information
Wishez committed Jan 25, 2023
1 parent 082f392 commit f013f73
Show file tree
Hide file tree
Showing 22 changed files with 3,156 additions and 203 deletions.
9 changes: 2 additions & 7 deletions .github/workflows/bundle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,8 @@ jobs:
- name: Lint project
run: npm run lint-project

- name: Build bundle
run: npx webpack

- name: Generate example
run: |
npx antora antora-playbook-ru.yml
npx antora antora-playbook-en.yml
- name: Build bundle and generate docs
run: npm run build

- name: Upload bundle
if: inputs.upload
Expand Down
4 changes: 2 additions & 2 deletions antora-playbook-en.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
site:
title: Taymyr Antora UI
url: /
url: /en
start_page: ROOT::index.adoc
content:
sources:
Expand All @@ -15,7 +15,7 @@ ui:

output:
clean: false
dir: build/site
dir: build/site/en

antora:
extensions:
Expand Down
7 changes: 7 additions & 0 deletions config/constants/Env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const mode = process.env.NODE_ENV

export const Env = {
isDev: mode === 'development',
isProd: mode === 'production',
cwd: process.cwd(),
}
33 changes: 33 additions & 0 deletions config/helpers/htmlPlugin/createHtmlPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import HtmlWebpackPlugin from 'html-webpack-plugin'
import get from 'lodash/get'

interface IHtmlPluginCreatingOptions {
fileName: string
filterKey: string
filterValue: string
filterByTagNode?: (tagNode: Record<string, unknown>) => boolean
}

export const createHtmlPlugin = (config: IHtmlPluginCreatingOptions) => {
const { fileName, filterKey, filterValue, filterByTagNode = () => true } = config

const getTemplateContent: HtmlWebpackPlugin.Options['templateContent'] = htmlWebpackPlugin => {
return (
htmlWebpackPlugin.tags.headTags
.filter(
(tagNode: Record<string, unknown>) =>
get(tagNode, filterKey) === filterValue && filterByTagNode(tagNode),
)
.join('')
// It must be Handlebars template with root path as variable
.replaceAll('uiRootPath', '{{{uiRootPath}}}')
)
}

return new HtmlWebpackPlugin({
publicPath: 'uiRootPath',
templateContent: ({ htmlWebpackPlugin }) => `${getTemplateContent(htmlWebpackPlugin)}`,
filename: fileName,
inject: false,
})
}
1 change: 1 addition & 0 deletions config/helpers/htmlPlugin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { createHtmlPlugin } from './createHtmlPlugin'
6 changes: 6 additions & 0 deletions config/helpers/htmlPlugin/types/IHtmlPluginCreatingOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface IHtmlPluginCreatingOptions {
fileName: string
filterKey: string
filterValue: string
filterByTagNode?: (tagNode: Record<string, unknown>) => boolean
}
1 change: 1 addition & 0 deletions config/helpers/htmlPlugin/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { IHtmlPluginCreatingOptions } from './IHtmlPluginCreatingOptions'
1 change: 1 addition & 0 deletions config/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { spawnCommand } from './spawnCommand'
29 changes: 29 additions & 0 deletions config/helpers/spawnCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { spawn, SpawnOptions } from 'node:child_process'

export const spawnCommand = (commandStr: string, options?: SpawnOptions) => {
const [command, ...args] = commandStr.split(' ')

if (!command) return Promise.reject()

return new Promise((resolve, reject) => {
const spawnedProcess = spawn(command, args, {
shell: true,
stdio: 'inherit',
env: { FORCE_COLOR: 'true', ...process.env },
...options,
})

spawnedProcess.stdout?.on('data', data => {
console.log(data.toString())
})

spawnedProcess?.on('close', code => {
if (code === 1) {
//eslint-disable-next-line
reject()
}

resolve(null)
})
})
}
21 changes: 21 additions & 0 deletions config/plugins/BuildAntoraPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Compiler } from 'webpack'
import { spawnCommand } from '../helpers/spawnCommand'

export class BuildAntoraPlugin {
apply(compiler: Compiler) {
let shouldUpdateAntora = true
compiler.hooks.done.tap('Build antora', async stats => {
if (shouldUpdateAntora) {
await spawnCommand('npm run build-antora')
shouldUpdateAntora = false
setTimeout(() => {
compiler.hooks.done.callAsync(stats, () => {
return
})
}, 500)
} else {
shouldUpdateAntora = true
}
})
}
}
73 changes: 73 additions & 0 deletions config/webpack.common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// eslint-disable-next-line import/default
import CopyPlugin from 'copy-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import path from 'path'
import type { Configuration } from 'webpack'
// Compiler's webpack annotations are incompatible in types package:(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import ZipPlugin from 'zip-webpack-plugin'
import { Env } from './constants/Env'
import { createHtmlPlugin } from './helpers/htmlPlugin'
import { BuildAntoraPlugin } from './plugins/BuildAntoraPlugin'

const bundleName = 'ui-bundle'
const HbsPartialsPath = {
HEAD_ICONS: 'partials/head-icons.hbs',
HEAD_STYLES: 'partials/head-styles.hbs',
BODY_ASSETS: 'partials/body-assets.hbs',
}

const config: Configuration = {
entry: path.resolve(Env.cwd, './src/index.ts'),
module: {
rules: [
{
test: /\.(sc|sa|c)ss$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: './css/[name].[contenthash].css',
}),
createHtmlPlugin({
fileName: HbsPartialsPath.HEAD_ICONS,
filterKey: 'meta.plugin',
filterValue: 'favicons-webpack-plugin',
}),
createHtmlPlugin({
fileName: HbsPartialsPath.HEAD_STYLES,
filterKey: 'attributes.rel',
filterValue: 'stylesheet',
}),
createHtmlPlugin({
fileName: HbsPartialsPath.BODY_ASSETS,
filterKey: 'tagName',
filterValue: 'script',
}),
new CopyPlugin({
patterns: [
{ from: 'src/layouts', to: 'layouts' },
{
from: 'src/partials',
to: 'partials',
filter: filepath =>
!Object.values(HbsPartialsPath).some(hbsPartialPath =>
filepath.endsWith(hbsPartialPath),
),
},
],
}),
new ZipPlugin({ filename: bundleName }),
new BuildAntoraPlugin(),
],
performance: {
assetFilter: (filename: string) => {
return !filename.startsWith(bundleName)
},
},
}

export default config
36 changes: 36 additions & 0 deletions config/webpack.dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import path from 'path'
import type { Configuration, WebpackOptionsNormalized } from 'webpack'
import { merge } from 'webpack-merge'
import WatchExternalFilesPlugin from 'webpack-watch-files-plugin'
import { Env } from './constants/Env'
import commonConfig from './webpack.common'

const watchedFiles = [path.resolve(Env.cwd, 'docs/**/*'), path.resolve(Env.cwd, 'src/**/*')]

const config = merge<WebpackOptionsNormalized | Configuration>(commonConfig, {
mode: 'development',
target: 'web',
output: {
filename: 'js/[name].js',
},
devServer: {
port: 4224,
static: {
directory: path.resolve(Env.cwd, 'build'),
},
open: ['/site/en'],
compress: true,
hot: true,
devMiddleware: {
writeToDisk: true,
},
},
devtool: 'source-map',
plugins: [
new WatchExternalFilesPlugin({
files: watchedFiles,
}),
],
})

export default config
22 changes: 22 additions & 0 deletions config/webpack.prod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import FaviconsPlugin from 'favicons-webpack-plugin'
import path from 'path'
import type { Configuration } from 'webpack'
import { merge } from 'webpack-merge'
import commonConfig from './webpack.common'

const config = merge<Configuration>(commonConfig, {
mode: 'production',
output: {
filename: 'js/[name].js?v=[hash]',
},
plugins: [
new FaviconsPlugin({
logo: 'logo.svg',
favicons: { appName: 'Taymyr' },
prefix: 'assets/[contenthash]/',
inject: htmlPlugin => path.basename(htmlPlugin.options.filename).endsWith('head-icons.hbs'),
}),
],
})

export default config
Loading

0 comments on commit f013f73

Please sign in to comment.