Skip to content

Commit 28c7d41

Browse files
aduh95joshgoebel
andauthored
(build) include ESM distributables in CDN build (#3209)
Co-authored-by: Josh Goebel <[email protected]>
1 parent 50eca53 commit 28c7d41

File tree

8 files changed

+205
-164
lines changed

8 files changed

+205
-164
lines changed

.github/workflows/node.js.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ jobs:
3333
run: |
3434
npm run test-browser
3535
node test/builds/browser_build_as_commonjs.js
36+
# CDN build should be easily importable
37+
- if: contains(matrix.build-how, 'cdn')
38+
name: Test that we can import CDN esm build
39+
run: |
40+
node test/builds/cdn_build_as_esm.mjs
3641
3742
- if: contains(matrix.build-how, 'node')
3843
name: Test Node.js build

test/builds/cdn_build_as_esm.mjs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import hljs from "../../build/es/highlight.js";
2+
3+
const API = [
4+
"getLanguage",
5+
"registerLanguage",
6+
"highlight",
7+
"highlightAuto",
8+
"highlightAll",
9+
"highlightElement"
10+
];
11+
12+
const assert = (f,msg) => {
13+
if (!f()) {
14+
console.error(msg);
15+
process.exit(1);
16+
}
17+
};
18+
const keys = Object.keys(hljs);
19+
20+
API.forEach(n => {
21+
assert(_ => keys.includes(n), `API should include ${n}`);
22+
});
23+
24+
console.log("Pass: browser build works with Node.js just fine.")

tools/build_browser.js

Lines changed: 70 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ const log = (...args) => console.log(...args);
1414
const { rollupCode } = require("./lib/bundling.js");
1515
const bundling = require('./lib/bundling.js');
1616
const Table = require('cli-table');
17-
const { result } = require('lodash');
1817

19-
function buildHeader(args) {
18+
const getDefaultHeader = () => ({
19+
...require('../package.json'),
20+
git_sha : child_process
21+
.execSync("git rev-parse --short=10 HEAD")
22+
.toString().trim(),
23+
});
24+
function buildHeader(args = getDefaultHeader()) {
2025
return "/*!\n" +
2126
` Highlight.js v${args.version} (git: ${args.git_sha})\n` +
2227
` (c) ${config.copyrightYears} ${args.author.name} and other contributors\n` +
@@ -68,14 +73,12 @@ async function buildBrowser(options) {
6873

6974
detailedGrammarSizes(languages);
7075

71-
const size = await buildBrowserHighlightJS(languages, { minify: options.minify });
76+
const size = await buildCore("highlight", languages, { minify: options.minify, format: "cjs" });
7277

7378
log("-----");
74-
log("Core :", size.core, "bytes");
75-
if (options.minify) { log("Core (min) :", size.core_min, "bytes"); }
7679
log("Languages (raw) :",
7780
languages.map((el) => el.data.length).reduce((acc, curr) => acc + curr, 0), "bytes");
78-
log("highlight.js :", size.full, "bytes");
81+
log("highlight.js :", size.fullSize, "bytes");
7982
if (options.minify) {
8083
log("highlight.min.js :", size.minified, "bytes");
8184
log("highlight.min.js.gz :", zlib.gzipSync(size.minifiedSrc).length, "bytes");
@@ -175,93 +178,76 @@ function installDemoStyles() {
175178
});
176179
}
177180

178-
async function buildBrowserHighlightJS(languages, { minify }) {
179-
log("Building highlight.js.");
180-
181-
const git_sha = child_process
182-
.execSync("git rev-parse HEAD")
183-
.toString().trim()
184-
.slice(0, 10);
185-
const versionDetails = { ...require("../package"), git_sha };
186-
const header = buildHeader(versionDetails);
187-
188-
const outFile = `${process.env.BUILD_DIR}/highlight.js`;
189-
const minifiedFile = outFile.replace(/js$/, "min.js");
190-
191-
const built_in_langs = {
192-
name: "dynamicLanguages",
193-
resolveId: (source) => {
194-
if (source == "builtInLanguages") { return "builtInLanguages"}
195-
return null;
196-
},
197-
load: (id) => {
198-
if (id == "builtInLanguages") {
199-
const escape = (s) => "grmr_" + s.replace("-", "_");
200-
let src = "";
201-
src += languages.map((x) => `import ${escape(x.name)} from '${x.path}'`).join("\n");
202-
src += `\nexport {${languages.map((x) => escape(x.name)).join(",")}}`;
203-
return src;
204-
}
205-
return null;
181+
const builtInLanguagesPlugin = (languages) => ({
182+
name: "hljs-index",
183+
resolveId(source) {
184+
if (source === "builtInLanguages") {
185+
return source; // this signals that rollup should not ask other plugins or check the file system to find this id
206186
}
187+
return null; // other ids should be handled as usually
188+
},
189+
load(id) {
190+
const escape = (s) => "grmr_" + s.replace("-", "_");
191+
if (id === "builtInLanguages") {
192+
return languages.map((lang) =>
193+
`export { default as ${escape(lang.name)} } from ${JSON.stringify(lang.path)};`
194+
).join("\n");
195+
}
196+
return null;
207197
}
208-
209-
const plugins = [...config.rollup.browser_core.input.plugins, built_in_langs];
210-
211-
const input = { ...config.rollup.browser_core.input, input: `src/stub.js`, plugins };
212-
const output = { ...config.rollup.browser_core.output, file: outFile };
213-
let librarySrc = await rollupCode(input, output);
214-
215-
216-
// we don't use this, we just use it to get a size approximation for the build stats
217-
const coreSrc = await rollupCode({ ...config.rollup.browser_core.input, input: `src/highlight.js`, plugins }, output);
218-
const coreSize = coreSrc.length;
219-
220-
// strip off the original top comment
221-
librarySrc = librarySrc.replace(/\/\*.*?\*\//s, "");
222-
223-
const fullSrc = [
224-
header, librarySrc,
225-
// ...languages.map((lang) => lang.module)
226-
].join("\n");
227-
228-
const tasks = [];
229-
tasks.push(fs.writeFile(outFile, fullSrc, { encoding: "utf8" }));
230-
const shas = {
231-
"highlight.js": bundling.sha384(fullSrc)
198+
});
199+
200+
async function buildCore(name, languages, options) {
201+
const header = buildHeader();
202+
let relativePath = "";
203+
const input = {
204+
...config.rollup.core.input,
205+
input: `src/stub.js`
206+
};
207+
input.plugins = [
208+
...input.plugins,
209+
builtInLanguagesPlugin(languages)
210+
];
211+
const output = {
212+
...(options.format === "es" ? config.rollup.node.output : config.rollup.browser_iife.output),
213+
file: `${process.env.BUILD_DIR}/${name}.js`
232214
};
233215

234-
let core_min = [];
235-
let minifiedSrc = "";
236-
237-
if (minify) {
238-
const tersed = await Terser.minify(librarySrc, config.terser);
239-
const tersedCore = await Terser.minify(coreSrc, config.terser);
216+
// optimize for no languages by not including the language loading stub
217+
if (languages.length === 0) {
218+
input.input = "src/highlight.js";
219+
}
240220

241-
minifiedSrc = [
242-
header, tersed.code,
243-
// ...languages.map((lang) => lang.minified)
244-
].join("\n");
221+
if (options.format === "es") {
222+
output.format = "es";
223+
output.file = `${process.env.BUILD_DIR}/es/${name}.js`;
224+
relativePath = "es/";
225+
}
245226

246-
// get approximate core minified size
247-
core_min = [header, tersedCore.code].join().length;
227+
log(`Building ${relativePath}${name}.js.`);
248228

249-
tasks.push(fs.writeFile(minifiedFile, minifiedSrc, { encoding: "utf8" }));
250-
shas["highlight.min.js"] = bundling.sha384(minifiedSrc);
229+
const index = await rollupCode(input, output);
230+
const sizeInfo = { shas: [] };
231+
const writePromises = [];
232+
if (options.minify) {
233+
const { code } = await Terser.minify(index, {...config.terser, module: (options.format === "es") });
234+
const src = `${header}\n${code}`;
235+
writePromises.push(fs.writeFile(output.file.replace(/js$/, "min.js"), src));
236+
sizeInfo.minified = src.length;
237+
sizeInfo.minifiedSrc = src;
238+
sizeInfo.shas[`${relativePath}${name}.min.js`] = bundling.sha384(src)
251239
}
252-
253-
await Promise.all(tasks);
254-
return {
255-
core: coreSize,
256-
core_min: core_min,
257-
minified: Buffer.byteLength(minifiedSrc, 'utf8'),
258-
minifiedSrc,
259-
fullSrc,
260-
shas,
261-
full: Buffer.byteLength(fullSrc, 'utf8')
262-
};
240+
{
241+
const src = `${header}\n${index}`;
242+
writePromises.push(fs.writeFile(output.file, src));
243+
sizeInfo.fullSize = src.length;
244+
sizeInfo.fullSrc = src;
245+
sizeInfo.shas[`${relativePath}${name}.js`] = bundling.sha384(src)
246+
}
247+
await Promise.all(writePromises);
248+
return sizeInfo;
263249
}
264250

265251
// CDN build uses the exact same highlight.js distributable
266-
module.exports.buildBrowserHighlightJS = buildBrowserHighlightJS;
252+
module.exports.buildCore = buildCore;
267253
module.exports.build = buildBrowser;

0 commit comments

Comments
 (0)