render-activity-stream-html.js (8253B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* eslint-disable no-console */ 6 const fs = require("fs"); 7 const { mkdir } = require("shelljs"); 8 const path = require("path"); 9 const { pathToFileURL } = require("url"); 10 const chalk = require("chalk"); 11 12 const DEFAULT_OPTIONS = { 13 // Glob leading from CWD to the parent of the intended prerendered directory. 14 // Starting in newtab/bin/ and we want to write to newtab/prerendered/ so we 15 // go up one level. 16 addonPath: "..", 17 // depends on the registration in browser/extensions/newtab/jar.mn 18 baseUrl: "resource://newtab/", 19 baseVendorUrl: "chrome://global/content/", 20 }; 21 22 /** 23 * templateHTML - Generates HTML for activity stream, given some options and 24 * prerendered HTML if necessary. 25 * 26 * @param {obj} options 27 * {str} options.baseUrl The base URL for all local assets 28 * * {str} options.baseVendorUrl The base URL for all vendor dependencies 29 * {bool} options.debug Should we use dev versions of JS libraries? 30 * {bool} options.noscripts Should we include scripts in the prerendered files? 31 * @return {str} An HTML document as a string 32 */ 33 function templateHTML(options) { 34 const debugString = options.debug ? "-dev" : ""; 35 // This list must match any similar ones in AboutNewTabChild.sys.mjs 36 const scripts = [ 37 "chrome://browser/content/contentTheme.js", 38 `${options.baseVendorUrl}vendor/react${debugString}.js`, 39 `${options.baseVendorUrl}vendor/react-dom${debugString}.js`, 40 `${options.baseVendorUrl}vendor/prop-types.js`, 41 `${options.baseVendorUrl}vendor/redux.js`, 42 `${options.baseVendorUrl}vendor/react-redux.js`, 43 `${options.baseVendorUrl}vendor/react-transition-group.js`, 44 `${options.baseUrl}data/content/activity-stream.bundle.js`, 45 `${options.baseUrl}data/content/newtab-render.js`, 46 ]; 47 48 // Add spacing and script tags 49 const scriptRender = `\n${scripts 50 .map(script => ` <script src="${script}"></script>`) 51 .join("\n")}`; 52 53 // The markup below needs to be formatted by Prettier. But any diff after 54 // running this script should be caught by try-runnner.js 55 return ` 56 <!-- This Source Code Form is subject to the terms of the Mozilla Public 57 - License, v. 2.0. If a copy of the MPL was not distributed with this file, 58 - You can obtain one at http://mozilla.org/MPL/2.0/. --> 59 60 <!-- Note: This page is pre-rendered by render-activity-stream-html.js --> 61 62 <!doctype html> 63 <html> 64 <head> 65 <meta charset="utf-8" /> 66 <meta 67 http-equiv="Content-Security-Policy" 68 content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob: chrome:; style-src 'unsafe-inline';" 69 /> 70 <meta name="color-scheme" content="light dark" /> 71 <meta name="viewport" content="width=device-width, initial-scale=1" /> 72 <title data-l10n-id="newtab-page-title"></title> 73 <link 74 rel="icon" 75 type="image/png" 76 href="chrome://branding/content/icon32.png" 77 /> 78 <link rel="localization" href="branding/brand.ftl" /> 79 <link rel="localization" href="toolkit/branding/brandings.ftl" /> 80 <link rel="localization" href="browser/newtab/newtab.ftl" /> 81 <link rel="localization" href="toolkit/global/mozMessageBar.ftl" /> 82 <link 83 rel="stylesheet" 84 href="chrome://global/skin/design-system/tokens-brand.css" 85 /> 86 <link 87 rel="stylesheet" 88 href="chrome://newtab/content/css/activity-stream.css" 89 /> 90 </head> 91 <body class="activity-stream"> 92 <div id="root"></div>${options.noscripts ? "" : scriptRender} 93 <script 94 async 95 type="module" 96 src="chrome://global/content/elements/moz-toggle.mjs" 97 ></script> 98 <script 99 async 100 type="module" 101 src="chrome://global/content/elements/moz-badge.mjs" 102 ></script> 103 <script 104 async 105 type="module" 106 src="chrome://global/content/elements/moz-button.mjs" 107 ></script> 108 <script 109 async 110 type="module" 111 src="chrome://global/content/elements/moz-button-group.mjs" 112 ></script> 113 <script 114 async 115 type="module" 116 src="chrome://global/content/elements/moz-box-button.mjs" 117 ></script> 118 <script 119 async 120 type="module" 121 src="chrome://global/content/elements/moz-message-bar.mjs" 122 ></script> 123 <script 124 async 125 type="module" 126 src="chrome://global/content/elements/moz-radio-group.mjs" 127 ></script> 128 <script 129 async 130 type="module" 131 src="chrome://global/content/elements/moz-select.mjs" 132 ></script> 133 <script 134 async 135 type="module" 136 src="chrome://global/content/elements/moz-reorderable-list.mjs" 137 ></script> 138 <script 139 async 140 type="module" 141 src="chrome://global/content/elements/panel-list.js" 142 ></script> 143 <script 144 async 145 type="module" 146 src="chrome://global/content/elements/moz-support-link.mjs" 147 ></script> 148 </body> 149 </html> 150 `.trimLeft(); 151 } 152 153 /** 154 * writeFiles - Writes to the desired files the result of a template given 155 * various prerendered data and options. 156 * 157 * @param {string} destPath Path to write the files to 158 * @param {Map} filesMap Mapping of a string file name to templater 159 * @param {object} options Various options for the templater 160 */ 161 function writeFiles(destPath, filesMap, options) { 162 for (const [file, templater] of filesMap) { 163 fs.writeFileSync(path.join(destPath, file), templater({ options })); 164 console.log(chalk.green(`✓ ${file}`)); 165 } 166 } 167 168 const STATIC_FILES = new Map([ 169 ["activity-stream.html", ({ options }) => templateHTML(options)], 170 [ 171 "activity-stream-debug.html", 172 ({ options }) => templateHTML(Object.assign({}, options, { debug: true })), 173 ], 174 [ 175 "activity-stream-noscripts.html", 176 ({ options }) => 177 templateHTML(Object.assign({}, options, { noscripts: true })), 178 ], 179 ]); 180 181 /** 182 * main - Parses command line arguments, generates html and js with templates, 183 * and writes files to their specified locations. 184 */ 185 async function main() { 186 const { default: meow } = await import("meow"); 187 const fileUrl = pathToFileURL(__filename); 188 const cli = meow( 189 ` 190 Usage 191 $ node ./bin/render-activity-stream-html.js [options] 192 193 Options 194 -a PATH, --addon-path PATH Path to the parent of the target directory. 195 default: "${DEFAULT_OPTIONS.addonPath}" 196 -b URL, --base-url URL Base URL for assets. 197 default: "${DEFAULT_OPTIONS.baseUrl}" 198 --help Show this help message. 199 `, 200 { 201 description: false, 202 // `pkg` is a tiny optimization. It prevents meow from looking for a package 203 // that doesn't technically exist. meow searches for a package and changes 204 // the process name to the package name. It resolves to the newtab 205 // package.json, which would give a confusing name and be wasteful. 206 pkg: { 207 name: "render-activity-stream-html", 208 version: "0.0.0", 209 }, 210 // `importMeta` is required by meow 10+. It was added to support ESM, but 211 // meow now requires it, and no longer supports CJS style imports. But it 212 // only uses import.meta.url, which can be polyfilled like this: 213 importMeta: { url: fileUrl }, 214 flags: { 215 addonPath: { 216 type: "string", 217 shortFlag: "a", 218 default: DEFAULT_OPTIONS.addonPath, 219 }, 220 baseUrl: { 221 type: "string", 222 shortFlag: "b", 223 default: DEFAULT_OPTIONS.baseUrl, 224 }, 225 baseVendorUrl: { 226 type: "string", 227 shortFlag: "v", 228 default: DEFAULT_OPTIONS.baseVendorUrl, 229 }, 230 }, 231 } 232 ); 233 234 const options = Object.assign({ debug: false }, cli.flags || {}); 235 const addonPath = path.resolve(__dirname, options.addonPath); 236 const prerenderedPath = path.join(addonPath, "prerendered"); 237 console.log(`Writing prerendered files to ${prerenderedPath}:`); 238 239 mkdir("-p", prerenderedPath); 240 writeFiles(prerenderedPath, STATIC_FILES, options); 241 } 242 243 main();