|
1 | 1 | const path = require('node:path') |
2 | 2 | const process = require('node:process') |
3 | | -const child_process = require('node:child_process') |
| 3 | +const cp = require('node:child_process') |
4 | 4 | const esbuild = require('esbuild') |
5 | | -const util = require('./utilities') |
6 | | - |
7 | | -const watchMode = Boolean(process.env.npm_config_watch) |
8 | | - |
9 | | - |
10 | | -/** |
11 | | - * Configuration variables |
12 | | - */ |
13 | | - |
14 | | -// Basic build configuration and values |
15 | | -const commonOptions = { |
16 | | - entryNames: '[name]-[hash]', |
17 | | - bundle: true, |
18 | | - minify: true, |
19 | | - logLevel: watchMode ? 'warning' : 'info', |
20 | | -} |
21 | | -const epubOutDir = path.resolve('../formatters/epub/dist') |
22 | | -const htmlOutDir = path.resolve('../formatters/html/dist') |
23 | | - |
24 | | -// Handlebars template paths |
25 | | -const templates = { |
26 | | - sourceDir: path.resolve('js/handlebars/templates'), |
27 | | - compiledDir: path.resolve('../tmp/handlebars'), |
28 | | - filename: 'handlebars.templates.js', |
29 | | -} |
30 | | -templates.compiledPath = path.join(templates.compiledDir, templates.filename) |
31 | | - |
32 | | - |
33 | | -/** |
34 | | - * Build: Plugins |
35 | | - */ |
36 | | - |
37 | | -// Empty outdir directories before both normal and watch-mode builds |
38 | | -const epubOnStartPlugin = { |
39 | | - name: 'epubOnStart', |
40 | | - setup(build) { build.onStart(() => util.ensureEmptyDirsExistSync([epubOutDir])) }, |
41 | | -} |
42 | | -const htmlOnStartPlugin = { |
43 | | - name: 'htmlOnStart', |
44 | | - setup(build) { build.onStart(() => util.ensureEmptyDirsExistSync([htmlOutDir])) }, |
45 | | -} |
| 5 | +const fsExtra = require('fs-extra') |
| 6 | +const fs = require('node:fs/promises') |
| 7 | +const handlebars = require('handlebars') |
| 8 | +const util = require('node:util') |
46 | 9 |
|
| 10 | +const exec = util.promisify(cp.exec) |
47 | 11 |
|
48 | | -/** |
49 | | - * Build |
50 | | - */ |
51 | | - |
52 | | -// ePub: esbuild options |
53 | | -const epubBuildOptions = { |
54 | | - ...commonOptions, |
55 | | - outdir: epubOutDir, |
56 | | - plugins: [epubOnStartPlugin], |
57 | | - entryPoints: [ |
58 | | - 'js/entry/epub.js', |
59 | | - 'css/entry/epub-elixir.css', |
60 | | - 'css/entry/epub-erlang.css', |
61 | | - ], |
62 | | -} |
63 | | - |
64 | | -// ePub: esbuild (conditionally configuring watch mode and rebuilding of docs) |
65 | | -if (!watchMode) { |
66 | | - esbuild.build(epubBuildOptions).catch(() => process.exit(1)) |
67 | | -} else { |
68 | | - esbuild.build({ |
69 | | - ...epubBuildOptions, |
70 | | - watch: { |
71 | | - onRebuild(error, result) { |
72 | | - if (error) { |
73 | | - console.error('[watch] epub build failed:', error) |
74 | | - } else { |
75 | | - console.log('[watch] epub assets rebuilt') |
76 | | - if (result.errors.length > 0) console.log('[watch] epub build errors:', result.errors) |
77 | | - if (result.warnings.length > 0) console.log('[watch] epub build warnings:', result.warnings) |
78 | | - generateDocs("epub") |
79 | | - } |
80 | | - }, |
81 | | - }, |
82 | | - }).then(() => generateDocs("epub")).catch(() => process.exit(1)) |
83 | | -} |
84 | | - |
85 | | -// HTML: Precompile Handlebars templates |
86 | | -util.runShellCmdSync(`npx handlebars ${templates.sourceDir} --output ${templates.compiledPath}`) |
| 12 | +const watchMode = Boolean(process.env.npm_config_watch) |
87 | 13 |
|
88 | | -// HTML: esbuild options |
89 | | -const htmlBuildOptions = { |
90 | | - ...commonOptions, |
91 | | - outdir: htmlOutDir, |
92 | | - plugins: [htmlOnStartPlugin], |
93 | | - entryPoints: [ |
94 | | - templates.compiledPath, |
95 | | - 'js/entry/html.js', |
96 | | - 'css/entry/html-elixir.css', |
97 | | - 'css/entry/html-erlang.css', |
98 | | - ], |
99 | | - loader: { |
100 | | - '.woff2': 'file', |
101 | | - // TODO: Remove when @fontsource/* removes legacy .woff |
102 | | - '.woff': 'file', |
| 14 | +/** @type {import('esbuild').BuildOptions[]} */ |
| 15 | +const formatters = [ |
| 16 | + { |
| 17 | + formatter: 'epub', |
| 18 | + outdir: path.resolve('../formatters/epub/dist'), |
| 19 | + entryPoints: [ |
| 20 | + 'js/entry/epub.js', |
| 21 | + 'css/entry/epub-elixir.css', |
| 22 | + 'css/entry/epub-erlang.css' |
| 23 | + ] |
103 | 24 | }, |
104 | | -} |
105 | | - |
106 | | -// HTML: esbuild (conditionally configuring watch mode and rebuilding of docs) |
107 | | -if (!watchMode) { |
108 | | - esbuild.build(htmlBuildOptions).then(() => buildTemplatesRuntime()).catch(() => process.exit(1)) |
109 | | -} else { |
110 | | - esbuild.build({ |
111 | | - ...htmlBuildOptions, |
112 | | - watch: { |
113 | | - onRebuild(error, result) { |
114 | | - if (error) { |
115 | | - console.error('[watch] html build failed:', error) |
116 | | - } else { |
117 | | - console.log('[watch] html assets rebuilt') |
118 | | - if (result.errors.length > 0) console.log('[watch] html build errors:', result.errors) |
119 | | - if (result.warnings.length > 0) console.log('[watch] html build warnings:', result.warnings) |
120 | | - buildTemplatesRuntime() |
121 | | - generateDocs("html") |
| 25 | + { |
| 26 | + formatter: 'html', |
| 27 | + outdir: path.resolve('../formatters/html/dist'), |
| 28 | + entryPoints: [ |
| 29 | + 'js/entry/html.js', |
| 30 | + 'css/entry/html-elixir.css', |
| 31 | + 'css/entry/html-erlang.css' |
| 32 | + ], |
| 33 | + loader: { |
| 34 | + '.woff2': 'file', |
| 35 | + // TODO: Remove when @fontsource/* removes legacy .woff |
| 36 | + '.woff': 'file' |
| 37 | + } |
| 38 | + } |
| 39 | +] |
| 40 | + |
| 41 | +Promise.all(formatters.map(async ({formatter, ...options}) => { |
| 42 | + // Clean outdir. |
| 43 | + await fsExtra.emptyDir(options.outdir) |
| 44 | + |
| 45 | + await esbuild.build({ |
| 46 | + entryNames: watchMode ? '[name]-dev' : '[name]-[hash]', |
| 47 | + bundle: true, |
| 48 | + minify: !watchMode, |
| 49 | + logLevel: watchMode ? 'warning' : 'info', |
| 50 | + watch: watchMode, |
| 51 | + ...options, |
| 52 | + plugins: [{ |
| 53 | + name: 'ex_doc', |
| 54 | + setup (build) { |
| 55 | + // Pre-compile handlebars templates. |
| 56 | + build.onLoad({ |
| 57 | + filter: /\.handlebars$/ |
| 58 | + }, async ({ path: filename }) => { |
| 59 | + try { |
| 60 | + const source = await fs.readFile(filename, 'utf-8') |
| 61 | + const template = handlebars.precompile(source) |
| 62 | + const contents = [ |
| 63 | + "import * as Handlebars from 'handlebars/runtime'", |
| 64 | + "import '../helpers'", |
| 65 | + `export default Handlebars.template(${template})` |
| 66 | + ].join('\n') |
| 67 | + return { contents } |
| 68 | + } catch (error) { |
| 69 | + return { errors: [{ text: error.message }] } |
| 70 | + } |
| 71 | + }) |
| 72 | + |
| 73 | + // Generate docs with new assets (watch mode only). |
| 74 | + if (watchMode) { |
| 75 | + build.onEnd(async result => { |
| 76 | + if (result.errors.length) return |
| 77 | + console.log(`${formatter} assets built`) |
| 78 | + await exec('mix compile --force', {cwd: '../'}) |
| 79 | + await exec(`mix docs --formatter ${formatter}`, {cwd: '../'}) |
| 80 | + console.log(`${formatter} docs built`) |
| 81 | + }) |
122 | 82 | } |
123 | | - }, |
124 | | - }, |
125 | | - }).then(() => { |
126 | | - buildTemplatesRuntime() |
127 | | - generateDocs("html") |
128 | | - }).catch(() => process.exit(1)) |
129 | | -} |
130 | | - |
131 | | -/** |
132 | | - * Functions |
133 | | - */ |
134 | | - |
135 | | -// HTML: Handlebars runtime |
136 | | -// The Handlebars runtime from the local module dist directory is used to ensure |
137 | | -// the version matches that which was used to compile the templates. |
138 | | -// 'bundle' must be false in order for 'Handlebar' to be available at runtime. |
139 | | -function buildTemplatesRuntime() { |
140 | | - esbuild.build({ |
141 | | - ...commonOptions, |
142 | | - outdir: htmlOutDir, |
143 | | - entryPoints: ['node_modules/handlebars/dist/handlebars.runtime.js'], |
144 | | - bundle: false, |
145 | | - }).catch(() => process.exit(1)) |
146 | | -} |
147 | | - |
148 | | -// Docs generation (used in watch mode only) |
149 | | -function generateDocs(formatter) { |
150 | | - console.log(`Building ${formatter} docs`) |
151 | | - process.chdir('../') |
152 | | - child_process.execSync('mix compile --force') |
153 | | - child_process.execSync(`mix docs --formatter ${formatter}`) |
154 | | - process.chdir('./assets/') |
155 | | -} |
| 83 | + } |
| 84 | + }] |
| 85 | + }) |
| 86 | +})).catch((error) => { |
| 87 | + console.error(error) |
| 88 | + process.exit(1) |
| 89 | +}) |
0 commit comments