diff --git a/src/package-lock.json b/src/package-lock.json index 7d470c6998e..2d7b06c0a20 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -788,6 +788,15 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, + "rainbow-code": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/rainbow-code/-/rainbow-code-2.1.7.tgz", + "integrity": "sha512-MuA4/PlfE0uJwxG3/MhylsdnhLDKZcFJfsDrVQX6LrFH2qfaLNB0TdbSupohrPRWgdIS3uBn1gCp05EkRRBtgw==", + "dev": true, + "requires": { + "web-worker": "1.0.0" + } + }, "recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -1094,6 +1103,12 @@ "integrity": "sha512-io/H/D18edTL1D2lcaUTLNLFEVZIPhNd4IdXDB9bEb+uDv2m/6NfyHiXKLFjbmI1ubeYpoQpR1gl9nlcWdI0vA==", "dev": true }, + "web-worker": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.0.0.tgz", + "integrity": "sha512-BzuMqeKVkKKwHV6tJuwePFcxYMxvC97D448mXTgh/CxXAB4sRtoV26gRPN+JDxsXRR7QZyioMV9O6NzQaASf7Q==", + "dev": true + }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/src/package.json b/src/package.json index d63099ecebd..88f22d6de38 100644 --- a/src/package.json +++ b/src/package.json @@ -43,6 +43,7 @@ "node-fetch": "2.6.1", "xml-js": "1.6.11", "run-script-os": "1.1.3", - "node-watch": "0.7.0" + "node-watch": "0.7.0", + "rainbow-code": "2.1.7" } } diff --git a/src/static/css/2019.css b/src/static/css/2019.css index 7bd853d7251..9e1b376a1c4 100644 --- a/src/static/css/2019.css +++ b/src/static/css/2019.css @@ -749,10 +749,6 @@ li { line-height: 1.5em; } -pre { - margin: 0; -} - hr { opacity: 0.2; } @@ -1449,3 +1445,91 @@ p.copyright a { transition-delay: 0s !important; } } + +/* Syntax Highlighting */ +pre { + margin: 0; + word-wrap: break-word; + padding: 6px 10px; + line-height: 19px; + margin-bottom: 20px; +} + +pre code { + border: 0; + padding: 0; + margin: 0; + border-radius: 0; +} + +code { + font-size: 1rem; +} + +pre .comment { + color: #006400; +} + +pre .support { + color: #0086b3; +} + +pre .tag, +pre .tag-name { + color: #4a3244; +} + +pre .keyword, +pre .css-property, +pre .vendor-fix, +pre .sass, +pre .class, +pre .id, +pre .css-value, +pre .entity.function, +pre .storage.function { + font-weight: bold; + color: #4a3244; +} + +pre .css-property, +pre .css-value, +pre .vendor-fix, +pre .support.namespace { + color: #333; +} + +pre .function.call { + color: navy; +} + +pre .constant.numeric, +pre .keyword.unit, +pre .hex-color { + font-weight: normal; + color: #00f; +} + +pre .entity.class { + color: #458; +} + +pre .entity.id, +pre .entity.function { + color: #900; +} + +pre .attribute, +pre .variable { + color: #000; +} + +pre .string, +pre .support.value { + font-weight: normal; + color: #b31107; +} + +pre .regexp { + color: #009926; +} diff --git a/src/tools/generate/generate_chapters.js b/src/tools/generate/generate_chapters.js index 6266ada4209..cc50b12f55e 100644 --- a/src/tools/generate/generate_chapters.js +++ b/src/tools/generate/generate_chapters.js @@ -14,6 +14,7 @@ const { wrap_tables } = require('./wrap_tables'); const { remove_unnecessary_markup } = require('./remove_unnecessary_markup'); const { generate_ebooks } = require('./generate_ebooks'); const { generate_js } = require('./generate_js'); +const { generate_syntax_highlighting } = require('./generate_syntax_highlighting'); const converter = new showdown.Converter({ tables: true, metadata: true }); converter.setFlavor('github'); @@ -31,9 +32,9 @@ const generate_chapters = async (chapter_match) => { let chapter_config = {}; let featured_quotes = {}; let re; - + configs = await get_yearly_configs(); - for (const year in configs) { + for (const year in configs) { sitemap_languages[year] = configs[year].settings[0].supported_languages; for (const part in configs[year].outline) { for (const chapter in configs[year].outline[part].chapters) { @@ -46,7 +47,7 @@ const generate_chapters = async (chapter_match) => { } } } - + if (chapter_match) { // Remove any trailing .md and replace all paths with brackets to capture components // en/2019/javascript.md -> (en)/(2019)/(javascript).md @@ -121,6 +122,7 @@ const parse_file = async (markdown,chapter) => { let body = html; const m = converter.getMetadata(); + body = generate_syntax_highlighting(body); body = generate_header_links(body); body = generate_figure_ids(body); body = wrap_tables(body); diff --git a/src/tools/generate/generate_syntax_highlighting.js b/src/tools/generate/generate_syntax_highlighting.js new file mode 100644 index 00000000000..dde9bd42c1e --- /dev/null +++ b/src/tools/generate/generate_syntax_highlighting.js @@ -0,0 +1,43 @@ +const { JSDOM } = require('jsdom'); +const rainbow = require('rainbow-code'); + + +/** + * Generate Syntax highlighting for a particular Languaage (ex: html, css, javascript etc..) + * + * @param {object} dom Object of class JSDOM (Parsed HTML). + * @param {String} body HTML body parsed from convertor + * @param {String} language language which rainbow syntax highlighting to use + * @param {String} [alias] an optional alias for a language (e.g. js for javascript) + * @returns body with highlighting syntax snippet of a particular language. + */ +const generate_syntax_highlighting_for_language = (dom, body, language, alias="") => { + const query_selector = alias ? `code.language-${language}, code.language-${alias}` : `code.language-${language}`; + const code_snippets = dom.window.document.querySelectorAll(query_selector); + code_snippets.forEach(element => { + const snippet_clean = element.innerHTML.replace(/</g, '<').replace(/>/g, '>'); + const snippet_converted = rainbow.colorSync(snippet_clean, language); + body = body.replace(element.innerHTML, snippet_converted); + }); + return body; +}; + + +/** + * Generate Syntax highlighting for a HTML, CSS, JavaScript and SQL + * + * @param {String} body HTML body which was parsed from markdown. + * @returns body with highlighting syntax snippet. + */ +const generate_syntax_highlighting = (body) => { + const dom = new JSDOM(body); + body = generate_syntax_highlighting_for_language(dom, body, "html"); + body = generate_syntax_highlighting_for_language(dom, body, "css" ); + body = generate_syntax_highlighting_for_language(dom, body, "javascript", "js"); + body = generate_syntax_highlighting_for_language(dom, body, "sql"); + return body; +}; + +module.exports = { + generate_syntax_highlighting +};