/
language.js
123 lines (102 loc) · 3.35 KB
/
language.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
const fs = require("fs")
const fsProm = require("fs").promises
const Terser = require("terser");
const glob = require("glob")
const path = require("path")
const build_config = require("../build_config")
const REQUIRES_REGEX = /\/\*.*?Requires: (.*?)\r?\n/s
const CATEGORY_REGEX = /\/\*.*?Category: (.*?)\r?\n/s
const LANGUAGE_REGEX = /\/\*.*?Language: (.*?)\r?\n/s
const {rollupCode} = require("./bundling.js")
const { getThirdPartyPackages } = require("./external_language")
class Language {
constructor(name, path) {
this.name = name
this.prettyName = name
this.requires = []
this.categories = []
this.third_party = false
// compiled code
this.module = ""
this.minified = ""
this.path = path
this.data = fs.readFileSync(path, {encoding: "utf8"})
this.loadMetadata()
}
async compile(options) {
await compileLanguage(this,options);
return this;
}
get sample() {
if (this._sample) return this._sample;
this._sample = "";
if (fs.existsSync(this.samplePath))
this._sample = fs.readFileSync(this.samplePath, {encoding: "utf8"});
return this._sample;
}
get samplePath() {
if (this.moduleDir) {
// this is the 'extras' case.
return `${this.moduleDir}/test/detect/${this.name}/default.txt`;
}
else {
// this is the common/built-in case.
return `./test/detect/${this.name}/default.txt`;
}
}
loadMetadata() {
var requiresMatch = REQUIRES_REGEX.exec(this.data)
var categoryMatch = CATEGORY_REGEX.exec(this.data)
var languageMatch = LANGUAGE_REGEX.exec(this.data)
if (requiresMatch)
this.requires = requiresMatch[1].split(", ").map((n) => n.replace(".js",""))
if (categoryMatch)
this.categories = categoryMatch[1].split(/,\s?/)
if (languageMatch)
this.prettyName = languageMatch[1]
}
static fromFile(filename) {
if (path.isAbsolute(filename) || filename.startsWith("."))
{
var file = filename
} else {
var file = `./src/languages/${filename}`
}
return new Language(
path.basename(filename).replace(".js",""),
file
)
}
}
async function compileLanguage (language, options) {
const EXPORT_REGEX = /export default (.*);/;
// We can't use the source as extra language may be written in CJS
const input = { ...build_config.rollup.browser.input, input: language.path };
const output = { ...build_config.rollup.browser.output, file: "out.js" };
output.footer = null;
const esm = await rollupCode(input, output);
const iife = `hljs.registerLanguage('${language.name}', (function (){"use strict";` + esm.replace(EXPORT_REGEX, 'return $1') + '})())';
language.module = iife;
const miniESM = await Terser.minify(esm, options.terser);
const miniIIFE = await Terser.minify(iife, options.terser);
language.minified = miniIIFE.code;
language.minifiedESM = miniESM.code;
}
async function getLanguages() {
let languages = [];
glob.sync("./src/languages/*.js").forEach((file) => {
languages.push(Language.fromFile(file));
});
let extraPackages = await getThirdPartyPackages();
for (let ext of extraPackages) {
for (let file of ext.files) {
let l = Language.fromFile(file);
l.loader = ext.loader;
l.third_party = true;
l.moduleDir = ext.dir;
languages.push(l);
}
}
return languages;
}
module.exports = { Language, getLanguages };