Skip to content

Commit 6a7c0e6

Browse files
authored
(chore) Builds ES and CJS modules side by side with conditional exports (#3007)
- drops the legacy `lib/highlight.js` - build ES modules in `es` folder side-by-side with `lib` for CJS - adds `lib/common` - loader is no longer supported, languages must `export default` or equiv - add --no-esm build option (for speed) - add `.js.js` stubs for maximum compatible with requires using extensions
1 parent a8f4cf8 commit 6a7c0e6

File tree

5 files changed

+145
-34
lines changed

5 files changed

+145
-34
lines changed

README.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -255,11 +255,16 @@ const hljs = require('./highlight.js');
255255
const highlightedCode = hljs.highlightAuto('<span>Hello World!</span>').value
256256
```
257257

258-
For a smaller footprint, load only the languages you need.
258+
For a smaller footprint, load our common subset of languages (the same set used for our default web build).
259259

260260
```js
261-
const hljs = require('highlight.js/lib/core'); // require only the core library
262-
// separately require languages
261+
const hljs = require('highlight.js/lib/common');
262+
```
263+
264+
For the smallest footprint, load only the languages you need:
265+
266+
```js
267+
const hljs = require('highlight.js/lib/core');
263268
hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml'));
264269

265270
const highlightedCode = hljs.highlight('<span>Hello World!</span>', {language: 'xml'}).value

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"type": "git",
3333
"url": "git://github.com/highlightjs/highlight.js.git"
3434
},
35+
"type": "commonjs",
3536
"main": "./lib/index.js",
3637
"types": "./types/index.d.ts",
3738
"scripts": {

src/core.d.ts

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// commented out
2-
// <reference path="../types/index.d.ts" />
3-
41
import hljs from "highlight.js";
52
export default hljs;
63

tools/build.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const dir = {};
7171
commander
7272
.usage('[options] [<language>...]')
7373
.option('-n, --no-minify', 'Disable minification')
74+
.option('-ne, --no-esm', 'Disable building ESM')
7475
.option('-t, --target <name>',
7576
'Build for target ' +
7677
'[all, browser, cdn, node]',
@@ -86,7 +87,7 @@ async function doTarget(target, buildDir) {
8687
const build = require(`./build_${target}`);
8788
process.env.BUILD_DIR = buildDir;
8889
await clean(buildDir);
89-
await build.build({ languages: commander.args, minify: commander.opts().minify });
90+
await build.build({ languages: commander.args, minify: commander.opts().minify, esm: commander.opts().esm });
9091
}
9192

9293
async function doBuild() {

tools/build_node.js

+134-27
Original file line numberDiff line numberDiff line change
@@ -6,86 +6,193 @@ const { filter } = require("./lib/dependencies");
66
const { rollupWrite } = require("./lib/bundling.js");
77
const log = (...args) => console.log(...args);
88

9-
async function buildNodeIndex(languages) {
9+
const safeImportName = (s) => {
10+
s = s.replace(/-/g, "_");
11+
if (/^\d/.test(s)) s = `L_${s}`;
12+
return s;
13+
};
14+
15+
async function buildESMIndex(name, languages) {
16+
const header = `import hljs from './core.js';`;
17+
const footer = "export default hljs;";
18+
19+
20+
const registration = languages.map((lang) => {
21+
const importName = safeImportName(lang.name);
22+
return `import ${importName} from './languages/${lang.name}.js';\n` +
23+
`hljs.registerLanguage('${lang.name}', ${importName});`;
24+
});
25+
26+
const index = `${header}\n\n${registration.join("\n")}\n\n${footer}`;
27+
await fs.writeFile(`${process.env.BUILD_DIR}/es/${name}.js`, index);
28+
}
29+
30+
async function buildCJSIndex(name, languages) {
1031
const header = "var hljs = require('./core');";
1132
const footer = "module.exports = hljs;";
1233

1334
const registration = languages.map((lang) => {
14-
let require = `require('./languages/${lang.name}')`;
15-
if (lang.loader) {
16-
require = require += `.${lang.loader}`;
17-
}
35+
const require = `require('./languages/${lang.name}')`;
1836
return `hljs.registerLanguage('${lang.name}', ${require});`;
1937
});
2038

21-
// legacy
22-
await fs.writeFile(`${process.env.BUILD_DIR}/lib/highlight.js`,
23-
"// This file has been deprecated in favor of core.js\n" +
24-
"var hljs = require('./core');\n"
25-
);
26-
2739
const index = `${header}\n\n${registration.join("\n")}\n\n${footer}`;
28-
await fs.writeFile(`${process.env.BUILD_DIR}/lib/index.js`, index);
40+
await fs.writeFile(`${process.env.BUILD_DIR}/lib/${name}.js`, index);
2941
}
3042

31-
async function buildNodeLanguage(language) {
43+
async function buildNodeLanguage(language, options) {
44+
const EMIT = `function emitWarning() {
45+
if (!emitWarning.warned) {
46+
emitWarning.warned = true;
47+
process.emitWarning(
48+
'Using file extension in specifier is deprecated, use "highlight.js/lib/languages/%%%%" instead of "highlight.js/lib/languages/%%%%.js"',
49+
'DeprecationWarning'
50+
);
51+
}
52+
}
53+
emitWarning();`;
54+
const CJS_STUB = `${EMIT}
55+
module.exports = require('./%%%%.js');`;
56+
const ES_STUB = `${EMIT}
57+
import lang from './%%%%.js';
58+
export default lang;`;
3259
const input = { ...config.rollup.node.input, input: language.path };
3360
const output = { ...config.rollup.node.output, file: `${process.env.BUILD_DIR}/lib/languages/${language.name}.js` };
3461
await rollupWrite(input, output);
62+
await fs.writeFile(`${process.env.BUILD_DIR}/lib/languages/${language.name}.js.js`,
63+
CJS_STUB.replace(/%%%%/g, language.name));
64+
if (options.esm) {
65+
await fs.writeFile(`${process.env.BUILD_DIR}/es/languages/${language.name}.js.js`,
66+
ES_STUB.replace(/%%%%/g, language.name));
67+
await rollupWrite(input, {...output,
68+
format: "es",
69+
file: output.file.replace("/lib/", "/es/")
70+
});
71+
}
3572
}
3673

37-
async function buildNodeHighlightJS() {
74+
const EXCLUDE = ["join"];
75+
76+
async function buildESMUtils() {
77+
const input = { ...config.rollup.node.input, input: `src/lib/regex.js` };
78+
input.plugins = [...input.plugins, {
79+
transform: (code) => {
80+
EXCLUDE.forEach((fn) => {
81+
code = code.replace(`export function ${fn}(`, `function ${fn}(`);
82+
});
83+
return code;
84+
}
85+
}];
86+
await rollupWrite(input, {
87+
...config.rollup.node.output,
88+
format: "es",
89+
file: `${process.env.BUILD_DIR}/es/utils/regex.js`
90+
});
91+
}
92+
93+
async function buildNodeHighlightJS(options) {
3894
const input = { ...config.rollup.node.input, input: `src/highlight.js` };
3995
const output = { ...config.rollup.node.output, file: `${process.env.BUILD_DIR}/lib/core.js` };
4096
await rollupWrite(input, output);
97+
if (options.esm) {
98+
await rollupWrite(input, {
99+
...output,
100+
format: "es",
101+
file: `${process.env.BUILD_DIR}/es/core.js`
102+
});
103+
}
41104
}
42105

43-
async function buildPackageJSON() {
106+
function dual(file) {
107+
return {
108+
require: file,
109+
import: file.replace("/lib/", "/es/")
110+
};
111+
}
112+
113+
async function buildPackageJSON(options) {
44114
const packageJson = require("../package");
115+
116+
const exports = {
117+
".": dual("./lib/index.js"),
118+
"./package.json": "./package.json",
119+
"./lib/common": dual("./lib/common.js"),
120+
"./lib/core": dual("./lib/core.js"),
121+
"./lib/languages/*": dual("./lib/languages/*.js"),
122+
};
123+
if (options.esm) packageJson.exports = exports;
124+
45125
await fs.writeFile(`${process.env.BUILD_DIR}/package.json`, JSON.stringify(packageJson, null, 2));
46126
}
47127

48-
async function buildLanguages(languages) {
128+
async function buildLanguages(languages, options) {
49129
log("Writing languages.");
50130
await Promise.all(
51131
languages.map(async(lang) => {
52-
await buildNodeLanguage(lang);
132+
await buildNodeLanguage(lang, options);
53133
process.stdout.write(".");
54134
})
55135
);
56136
log("");
57137
}
58138

139+
const CORE_FILES = [
140+
"LICENSE",
141+
"README.md",
142+
"VERSION_10_UPGRADE.md",
143+
"VERSION_11_UPGRADE.md",
144+
"SUPPORTED_LANGUAGES.md",
145+
"SECURITY.md",
146+
"CHANGES.md",
147+
"types/index.d.ts"
148+
];
149+
59150
async function buildNode(options) {
60151
mkdir("lib/languages");
61152
mkdir("scss");
62153
mkdir("styles");
63154
mkdir("types");
64155

65-
install("./LICENSE", "LICENSE");
66-
install("./README.md", "README.md");
67-
install("./types/index.d.ts", "types/index.d.ts");
68-
install("./src/core.d.ts","lib/core.d.ts");
156+
157+
CORE_FILES.forEach(file => {
158+
install(`./${file}`, file);
159+
});
160+
install("./src/core.d.ts", "lib/core.d.ts");
161+
install("./src/core.d.ts", "lib/common.d.ts");
162+
163+
if (options.esm) {
164+
mkdir("es/languages");
165+
install("./src/core.d.ts", "es/core.d.ts");
166+
install("./src/core.d.ts", "es/common.d.ts");
167+
}
69168

70169
log("Writing styles.");
71170
const styles = await fs.readdir("./src/styles/");
72171
styles.forEach((file) => {
73172
install(`./src/styles/${file}`, `styles/${file}`);
74173
install(`./src/styles/${file}`, `scss/${file.replace(".css", ".scss")}`);
75174
});
76-
log("Writing package.json.");
77-
await buildPackageJSON();
78175

79176
let languages = await getLanguages();
80177
// filter languages for inclusion in the highlight.js bundle
81178
languages = filter(languages, options.languages);
179+
const common = languages.filter(l => l.categories.includes("common"));
82180

83-
await buildNodeIndex(languages);
84-
await buildLanguages(languages);
181+
log("Writing package.json.");
182+
await buildPackageJSON(options);
183+
184+
if (options.esm) {
185+
await fs.writeFile(`${process.env.BUILD_DIR}/es/package.json`, `{ "type": "module" }`);
186+
await buildESMIndex("index", languages);
187+
await buildESMIndex("common", common);
188+
await buildESMUtils();
189+
}
190+
await buildCJSIndex("index", languages);
191+
await buildCJSIndex("common", common);
192+
await buildLanguages(languages, options);
85193

86194
log("Writing highlight.js");
87-
await buildNodeHighlightJS();
88-
195+
await buildNodeHighlightJS(options);
89196
}
90197

91198
module.exports.build = buildNode;

0 commit comments

Comments
 (0)