|
| 1 | +const { build, context } = require("esbuild") |
| 2 | +const { copy } = require("esbuild-plugin-copy") |
| 3 | +const fs = require("fs") |
| 4 | +const path = require("path") |
| 5 | +const yaml = require("js-yaml") |
| 6 | + |
| 7 | +const entryPoints = [ |
| 8 | + "src/*.html", |
| 9 | + "src/*.tldrev", |
| 10 | + "src/*.md", |
| 11 | + "src/index.ts" |
| 12 | +] |
| 13 | + |
| 14 | +// Load substitutions from _config.yml |
| 15 | +function getSubstitutions() { |
| 16 | + const extConfigPath = path.resolve(__dirname, "../syllabus/_config_ext.yml") |
| 17 | + const baseConfigPath = path.resolve(__dirname, "../syllabus/_config.yml") |
| 18 | + let configPath |
| 19 | + if (fs.existsSync(extConfigPath)) { |
| 20 | + configPath = extConfigPath |
| 21 | + } else { |
| 22 | + configPath = baseConfigPath |
| 23 | + } |
| 24 | + const config = yaml.load(fs.readFileSync(configPath, "utf8")) |
| 25 | + return ( |
| 26 | + config?.sphinx?.config?.myst_substitutions |
| 27 | + || {} |
| 28 | + ) |
| 29 | +} |
| 30 | + |
| 31 | +// esbuild plugin for .md and .html substitution |
| 32 | +function mdHtmlSubstitutionPlugin() { |
| 33 | + return { |
| 34 | + name: "md-html-substitution", |
| 35 | + setup(build) { |
| 36 | + const substitutions = getSubstitutions() |
| 37 | + build.onLoad({ filter: /\.(md|html)$/ }, async (args) => { |
| 38 | + let content = await fs.promises.readFile(args.path, "utf8") |
| 39 | + const warnings = [] |
| 40 | + // Find all {{ key }} occurrences and substitute |
| 41 | + const regex = /{{\s*(\w+)\s*}}/g |
| 42 | + let match |
| 43 | + let newContent = "" |
| 44 | + let lastIndex = 0 |
| 45 | + while ((match = regex.exec(content)) !== null) { |
| 46 | + const [fullMatch, key] = match |
| 47 | + const start = match.index |
| 48 | + const end = regex.lastIndex |
| 49 | + // Add content before match |
| 50 | + newContent += content.slice(lastIndex, start) |
| 51 | + if (substitutions[key] === undefined) { |
| 52 | + // Find line/column for warning |
| 53 | + const before = content.slice(0, start) |
| 54 | + const lines = before.split("\n") |
| 55 | + const lineNum = lines.length |
| 56 | + const lineText = lines[lines.length - 1] + content.slice(start, end) |
| 57 | + const colNum = Buffer.byteLength(lines[lines.length - 1], "utf8") |
| 58 | + warnings.push({ |
| 59 | + text: `Substitution key '${key}' not found in myst_substitutions`, |
| 60 | + location: { |
| 61 | + file: args.path, |
| 62 | + namespace: "file", |
| 63 | + line: lineNum, |
| 64 | + column: colNum, |
| 65 | + length: Buffer.byteLength(fullMatch, "utf8"), |
| 66 | + lineText |
| 67 | + } |
| 68 | + }) |
| 69 | + newContent += fullMatch |
| 70 | + } else { |
| 71 | + newContent += substitutions[key] |
| 72 | + } |
| 73 | + lastIndex = end |
| 74 | + } |
| 75 | + newContent += content.slice(lastIndex) |
| 76 | + return { |
| 77 | + contents: newContent, |
| 78 | + loader: "copy", |
| 79 | + warnings |
| 80 | + } |
| 81 | + }) |
| 82 | + } |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +function config(isProduction) { |
| 87 | + return { |
| 88 | + entryPoints, |
| 89 | + outdir: "dist", |
| 90 | + bundle: true, |
| 91 | + sourcemap: true, |
| 92 | + loader: { |
| 93 | + ".woff": "file", |
| 94 | + ".eot": "file", |
| 95 | + ".ttf": "file", |
| 96 | + ".html": "copy", |
| 97 | + ".tldrev": "copy", |
| 98 | + ".md": "copy" |
| 99 | + }, |
| 100 | + plugins: [ |
| 101 | + mdHtmlSubstitutionPlugin(), |
| 102 | + copy({ |
| 103 | + assets: { |
| 104 | + from: [ "./src/assets/**/*" ], |
| 105 | + to: [ "./assets" ] |
| 106 | + } |
| 107 | + }) |
| 108 | + ], |
| 109 | + define: { |
| 110 | + "process.env.NODE_ENV": isProduction ? "\"production\"" : "\"dev\"", |
| 111 | + "process.env.IS_PREACT": "\"false\"" |
| 112 | + }, |
| 113 | + logLevel: "info" |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +switch (process.argv[2]) { |
| 118 | + case "serve": |
| 119 | + (async () => { |
| 120 | + const ctx = await context({ |
| 121 | + ...config(false), |
| 122 | + inject: [ "live-reload.js" ] |
| 123 | + }) |
| 124 | + await ctx.watch() |
| 125 | + await ctx.serve({ |
| 126 | + servedir: "dist" |
| 127 | + }) |
| 128 | + })() |
| 129 | + break; |
| 130 | + case "build": |
| 131 | + build({ |
| 132 | + ...config(true), |
| 133 | + minify: true |
| 134 | + }) |
| 135 | + break; |
| 136 | + default: |
| 137 | + console.warn("Specify either serve or build as an argument.") |
| 138 | + break; |
| 139 | +} |
0 commit comments