Add Babel as a transpiling option for JS bundles (Fixes #10467)

This commit is contained in:
Alex Gibson 2021-09-17 14:03:25 +01:00
Родитель 89fd76c83b
Коммит 6f93237cdb
9 изменённых файлов: 1414 добавлений и 300 удалений

Просмотреть файл

@ -4,7 +4,6 @@ module.exports = {
'commonjs': true
},
extends: [
'@mozilla-protocol/eslint-config',
'plugin:json/recommended'
],
/**
@ -12,18 +11,27 @@ module.exports = {
* */
overrides: [
{
// JS files transpiled by Babel
files: [
'media/js/firefox/welcome/**/*.js',
'media/js/firefox/whatsnew/**/*.js'
'media/js/**/*.es6.js',
],
env: {
'es6': true
},
parserOptions: {
ecmaVersion: 8
'es2017': true
}
},
{
// JS files served only to Firefox browsers.
files: [
'media/js/firefox/welcome/**/*.js',
'media/js/firefox/whatsnew/**/*.js',
'media/js/firefox/firstrun/**/*.js',
],
env: {
'es2017': true
}
},
{
// JS build files for local dev.
files: [
'webpack.config.js',
'webpack.static.config.js',
@ -31,10 +39,7 @@ module.exports = {
],
env: {
'node': true,
'es6': true
},
parserOptions: {
ecmaVersion: 8
'es2017': true
},
rules: {
'strict': ['error', 'global'],
@ -43,6 +48,7 @@ module.exports = {
],
globals: {
'Mozilla': 'writable',
'Mzp': 'writable',
'site': 'writable'
}
};

Просмотреть файл

@ -101,6 +101,42 @@ Once you define a bundle in ``static-bundles.json``, the ``webpack.config.js``
file will use these as entrypoints for compiling JS and CSS and watching for
changes.
Writing JavaScript
------------------
Bedrock's Webpack configuration supports some different options for writing
JavaScript:
Default configuration
~~~~~~~~~~~~~~~~~~~~~
Write `example-script.js` using ES5 syntax and features. Webpack will bundle
the JS as-is, without any additional pre-processing.
Babel configuration
~~~~~~~~~~~~~~~~~~~
Write `example-script.es6.js` using ES2015+ syntax. Webpack will transpile
the code to ES5 using `Babel <https://babeljs.io/>`_. This is useful when
you want to write modern syntax but still support older browsers.
.. important::
Whilst Babel will transpile most modern JS syntax to ES5 when suitable
fallbacks exist, it won't automatically include custom polyfills for
everything since these can start to greatly increase bundle size. If you
want to use ``Promise`` or ``async/await`` functions for example, then
you will need to load polyfills for those. This can be done either at
the page level, or globally in ``lib.js`` if it's something that multiple
pages would benefit from. But please pick and choose wisely, and be
concious of performance.
For pages that are served to Firefox browsers only, such as ``/whatsnew``, it is
also possible to write native ES2015+ syntax and serve that directly in production.
Here there is no need to include the `.es6.js` file extension. Instead, you can
simply use `.js` instead. The rules that which files you can do this in are defined
in our `ESLint config <https://github.com/mozilla/bedrock/blob/master/.eslintrc.js>`_.
Writing URL Patterns
--------------------
@ -526,7 +562,7 @@ Picto
- title
String indicating heading text (usually a translation id wrapped in ftl function)
Default: None
Default: None
Example: ``title=ftl('misinformation-why-trust-firefox')``
@ -565,14 +601,14 @@ Picto
Example: ``class='trust'``
- image_width
- image_width
Number indicating width of image.
Default: 64
Example: ``image_width='153px'``
- include_highres_image
- include_highres_image
Boolean that determines whether the image can also appear in high res.
Default: False
@ -609,30 +645,30 @@ Call out
- desc
String indicating paragraph text (usually a translation id wrapped in ftl function).
Default: None
Default: None
Example: ``desc=ftl('firefox-channel-test-beta-versions-of-firefox-ios')``
- class
String adding class(es) to the section tag.
Default: None
Default: None
Example: ``class='mzp-t-firefox ' + product_class``
Example: ``class='mzp-t-firefox ' + product_class``
- include_cta
- include_cta
Boolean indicating whether or not to include the body of the macro call (usually a mix of text and html).
Default: None
Default: None
Example: ``include_cta=True``
Example: ``include_cta=True``
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 2
Example: ``heading_level=1``
Example: ``heading_level=1``
Split
@ -641,7 +677,7 @@ Split
- block_id
String providing id to the section tag (usually if it needs to be used as an in-page link).
Default: None
Default: None
Example: ``id='nextgen``
@ -655,35 +691,35 @@ Split
- block_class
String providing class(es) to the section tag.
Default: None
Default: None
Example: ``block_class='mzp-l-split-reversed mzp-l-split-center-on-sm-md``
- theme_class
String providing theme class(es) to a container div tag inside the section.
Default: None
Default: None
Example: ``theme_class='mzp-t-dark'``
Example: ``theme_class='mzp-t-dark'``
- body_class
String providing class(es) to the body (text content) div inside the section.
Default: None
Default: None
Example: ``Not currently in use``
Example: ``Not currently in use``
- image_url
Path to image location.
Default: None
Default: None
Example: ``image_url=img/firefox/accounts/trailhead/value-respect.jpg``
Example: ``image_url=img/firefox/accounts/trailhead/value-respect.jpg``
- media_class
String providing class(es) to the media div inside the section.
Default: None
Default: None
Example: ``media_class='mzp-l-split-h-center'``
@ -692,35 +728,35 @@ Split
Default: False
Example: ``include_highres_image=True``
Example: ``include_highres_image=True``
- l10n_image
- l10n_image
Boolean to indicate if image has translatable text.
Default: False
Example: ``l10n_image=True``
Example: ``l10n_image=True``
- image_alt
String providing alt text to the image.
Default: ''
Default: ''
Example: ``Not currently in use``
Example: ``Not currently in use``
- media_after
- media_after
Boolean to determine if image appears before or after text when stacked on mobile size screens.
Default: False
Example: ``media_after=True``
Example: ``media_after=True``
- media_include
Path to video media.
Default: None
Default: None
Example: ``media_include='firefox/facebookcontainer/includes/video.html'``
Example: ``media_include='firefox/facebookcontainer/includes/video.html'``
Billboard
@ -731,63 +767,63 @@ Billboard
Default: N/A
Example: ``title=ftl('about-the-mozilla-manifesto')``
Example: ``title=ftl('about-the-mozilla-manifesto')``
- ga_title
**Required**. String providing value for data-link-name attribute on cta.
Default: N/A
Example: ``ga_title='The Mozilla Manifesto'``
Example: ``ga_title='The Mozilla Manifesto'``
- desc
- desc
**Required**.String indicating paragraph text (usually a translation id wrapped in ftl function).
Default: N/A
Example: ``desc=ftl('about-the-principles-we-wrote-in')``
Example: ``desc=ftl('about-the-principles-we-wrote-in')``
- link_cta
**Required**. String indicating link text (usually a translation id wrapped in an ftl function).
**Required**. String indicating link text (usually a translation id wrapped in an ftl function).
Default: N/A
Example: ``link_cta=ftl('about-read-the-manifesto')``
- link_url
**Required**. String or url helper function provides href value for cta link.
**Required**. String or url helper function provides href value for cta link.
Default: N/A
Example: ``link_url=url('mozorg.about.manifesto')``
Example: ``link_url=url('mozorg.about.manifesto')``
- image_url
- image_url
**Required**. Path to image location.
Default: N/A
Example: ``image_url='img/home/2018/billboard-healthy-internet.png'``
Example: ``image_url='img/home/2018/billboard-healthy-internet.png'``
- include_highres_image
- include_highres_image
Boolean that determines whether the image can also appear in high res.
Default: False
Example: ``include_highres_image=True``
Example: ``include_highres_image=True``
- reverse
Uses default layout: mzp-l-billboard-rightReverse will switch to billboard (text) left.
- reverse
Uses default layout: mzp-l-billboard-rightReverse will switch to billboard (text) left.
Default: False
Example: ``reverse=True``
Example: ``reverse=True``
- heading_level
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 2
Default: 2
Example: ``heading_level=1``
Example: ``heading_level=1``
Feature Card
@ -800,7 +836,7 @@ Feature Card
Example: ``title=ftl('firefox-home-firefox-browser')``
- ga_title
- ga_title
String used as an identifying name on a link for google analytics. Only used if link_url and link_cta are provided as well.
Default: None
@ -812,21 +848,21 @@ Feature Card
Default: N/A
Example: ``image_url=img/firefox/accounts/trailhead/value-respect.jpg``
Example: ``image_url=img/firefox/accounts/trailhead/value-respect.jpg``
- class
String adding class(es) to the section tag.
Default: None
Example: ``class=mzp-l-card-feature-left-half t-mozvpn``
Example: ``class=mzp-l-card-feature-left-half t-mozvpn``
- link_url
String or url helper function provides href value for cta link. Only used if link_cta is provided as well.
Default: None
Example: ``link_url=url('firefox.privacy.index')``
Example: ``link_url=url('firefox.privacy.index')``
- link_cta
String indicating link text (usually a translation id wrapped in an ftl function). Only used if link_url is provided as well.
@ -840,35 +876,35 @@ Feature Card
Default: False
Example: ``include_highres_image=True``
Example: ``include_highres_image=True``
- l10n_image
- l10n_image
Boolean to indicate if image has translatable text.
Default: False
Example: ``l10n_image=True``
Example: ``l10n_image=True``
- aspect_ratio
aspect_ratio String with an mzp class name indicating desired aspect ratio (adds class to section tag).
Default: False
Example: ``aspect_ratio='mzp-has-aspect-3-2'``
Example: ``aspect_ratio='mzp-has-aspect-3-2'``
- heading_level
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 2
Example: ``heading_level=3``
Example: ``heading_level=3``
- media_after
- media_after
Boolean to determine if image appears before or after text when stacked on mobile size screens.
Default: False
Example: ``media_after=True``
Example: ``media_after=True``
Card
@ -888,7 +924,7 @@ Card
Example: ``title=ftl('about-the-mozilla-manifesto')``
- ga_title
- ga_title
**Required**. String providing value for data-link-name attribute on cta.
Default: N/A
@ -900,23 +936,23 @@ Card
Default: N/A
Example: ``desc=ftl('about-the-principles-we-wrote-in')``
Example: ``desc=ftl('about-the-principles-we-wrote-in')``
- aspect_ratio
String indicating size/aspect ratio of the card (make sure to have it even if its in a defined Card Layout.
String indicating size/aspect ratio of the card (make sure to have it even if its in a defined Card Layout.
Default: N/A
Example: ``aspect_ratio=mzp-has-aspect-16-9``
Example: ``aspect_ratio=mzp-has-aspect-16-9``
- link_url
**Required**. String or url helper function provides href value for cta link.
Default: N/A
Example: ``link_url=url('mozorg.about.manifesto')``
Example: ``link_url=url('mozorg.about.manifesto')``
- image_url
- image_url
**Required**. Path to image location.
Default: N/A
@ -928,18 +964,18 @@ Card
Default: N/A
Example: ``include_highres_image=True``
Example: ``include_highres_image=True``
- l10n_image
- l10n_image
Boolean to indicate if image has translatable text.
Default: False
Example: ``l10n_image=True``
Example: ``l10n_image=True``
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 3
Example: ``heading_level=2``
Example: ``heading_level=2``

Просмотреть файл

@ -0,0 +1,192 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
// Lazyload images
window.Mozilla.LazyLoad.init();
const modalContainers = document.getElementsByClassName('has-modal');
const content = document.querySelector('.mzp-u-modal-content');
const articleArray = document.querySelectorAll('[data-modal-id]');
const modalNextButtonFragment =
`<div class="c-modal-next">
<button type="button" class="c-modal-button-next" title="Next">
Next
</button>
</div>`;
const modalPrevButtonFragment =
`<div class="c-modal-prev">
<button type="button" class="c-modal-button-prev" title="Previous">
Previous
</button>
</div>`;
// Set to true when a listener it set, as to not set it multiple times.
// Resets when the modal is closed.
let kbInit = null;
function keyboardListenerInit() {
if (kbInit) {
return;
// Turn Off
}
document.addEventListener('keyup', keyboardNextPrev, false);
kbInit = true;
}
function keyboardNextPrev(event) {
if (!kbInit) {
return;
}
switch (event.keyCode) {
case 37: // Left arrow
prevModalArticle();
break;
case 38: // Up arrow
prevModalArticle();
break;
case 39: // Right arrow
nextModalArticle();
break;
case 40: // Down arrow
nextModalArticle();
break;
}
}
function modalInit() {
// Lazy load images in the modal
const modalImage = document.querySelector('.mzp-c-modal-overlay-contents .mzp-c-card-image');
if (modalImage) {
const srcset = modalImage.getAttribute('data-srcset');
if (srcset) {
modalImage.srcset = srcset;
}
modalImage.src = modalImage.getAttribute('data-src');
modalImage.removeAttribute('data-src');
modalImage.removeAttribute('data-srcset');
}
const modalNextButton = document.querySelector('.c-modal-next');
const modalPrevButton = document.querySelector('.c-modal-prev');
modalNextButton.removeEventListener('click', nextModalArticle, false);
modalPrevButton.removeEventListener('click', prevModalArticle, false);
modalNextButton.addEventListener('click', nextModalArticle, false);
modalPrevButton.addEventListener('click', prevModalArticle, false);
keyboardListenerInit();
}
function getCurrentModalIndex() {
const modalContent = document.querySelector('.mzp-u-modal-content.mzp-c-modal-overlay-contents');
const newArticleIndex = parseInt(modalContent.getAttribute('data-current-index'), 10);
return newArticleIndex;
}
function updateModalArticle(index) {
const modalContent = document.querySelector('.mzp-u-modal-content.mzp-c-modal-overlay-contents');
let newArticleId = articleArray[index].getAttribute('data-modal-id');
const newModalContent = document.querySelector(`[data-modal-parent="${newArticleId}"]`).cloneNode(true);
const currentModalContent = modalContent.firstElementChild;
window.location.hash = newArticleId;
modalContent.replaceChild(newModalContent, currentModalContent);
modalContent.setAttribute('data-current-index', index);
modalInit();
}
function nextModalArticle() {
let newArticleIndex = getCurrentModalIndex();
newArticleIndex++;
// If at the end of the gallery, start over
if (newArticleIndex === (articleArray.length)) {
newArticleIndex = 0;
}
updateModalArticle(newArticleIndex);
}
function prevModalArticle() {
let newArticleIndex = getCurrentModalIndex();
newArticleIndex--;
// If at the beginning of the gallery, start over
if (newArticleIndex < 0) {
newArticleIndex = articleArray.length - 1;
}
updateModalArticle(newArticleIndex);
}
for (let i = 0; i < modalContainers.length; i++) {
const modalContainer = modalContainers[i];
modalContainer.setAttribute('aria-role', 'button');
modalContainer.setAttribute('data-current-index', i);
modalContainer.addEventListener('click', function(e) {
e.preventDefault();
const modalId = this.getAttribute('data-modal-id');
const currentIndex = parseInt(this.getAttribute('data-current-index'), 10);
const modalContent = document.querySelector(`[data-modal-parent="${modalId}"]`).cloneNode(true);
window.location.hash = modalId;
modalContent.removeAttribute('id');
modalContent.setAttribute('aria-role', 'article');
window.Mzp.Modal.createModal(this, content, {
allowScroll: false,
closeText: window.Mozilla.Utils.trans('global-close'),
onCreate: () => {
content.appendChild(modalContent);
content.setAttribute('data-current-index', currentIndex);
const modalCloseButton = document.querySelector('.mzp-c-modal-close');
modalCloseButton.insertAdjacentHTML('beforebegin', modalNextButtonFragment);
modalCloseButton.insertAdjacentHTML('beforebegin', modalPrevButtonFragment);
// Lazy load images in the modal and set next/prev listeners
modalInit();
},
onDestroy: () => {
if (window.history) {
window.history.replaceState('', '', window.location.pathname);
}
kbInit = false;
// Recache the current modal content which may have changed via next/prev buttons
const modalParent = document.querySelector('.mzp-u-modal-content.mzp-c-modal-overlay-contents');
const currentModalContent = modalParent.firstElementChild;
modalParent.removeChild(currentModalContent);
}
});
});
}
// trigger modal on page load if hash is present and matches a person with a bio
if (window.location.hash) {
const target = document.getElementById(window.location.hash.substr(1));
if (target && target.classList.contains('has-modal')) {
target.click();
}
}

Просмотреть файл

@ -1,206 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// create namespace
if (typeof window.Mozilla === 'undefined') {
window.Mozilla = {};
}
(function() {
'use strict';
// Lazyload images
Mozilla.LazyLoad.init();
var modalContainers = document.getElementsByClassName('has-modal');
var content = document.querySelector('.mzp-u-modal-content');
var modalNextButtonFragment =
'<div class="c-modal-next">' +
' <button type="button" class="c-modal-button-next" title="Next">' +
' Next' +
' </button>' +
'</div>';
var modalPrevButtonFragment =
'<div class="c-modal-prev">' +
' <button type="button" class="c-modal-button-prev" title="Previous">' +
' Previous' +
' </button>' +
'</div>';
var articleArray = document.querySelectorAll('[data-modal-id]');
// Set to true when a listener it set, as to not set it multiple times.
// Resets when the modal is closed.
var kbInit = null;
function keyboardListenerInit() {
if (kbInit) {
return;
// Turn Off
}
document.addEventListener('keyup', keyboardNextPrev, false);
kbInit = true;
}
function keyboardNextPrev(event) {
if (!kbInit) {
return;
}
switch (event.keyCode) {
case 37: // Left arrow
prevModalArticle();
break;
case 38: // Up arrow
prevModalArticle();
break;
case 39: // Right arrow
nextModalArticle();
break;
case 40: // Down arrow
nextModalArticle();
break;
}
}
function modalInit() {
// Lazy load images in the modal
var modalImage = document.querySelector('.mzp-c-modal-overlay-contents .mzp-c-card-image');
if (modalImage) {
var srcset = modalImage.getAttribute('data-srcset');
if (srcset) {
modalImage.srcset = srcset;
}
modalImage.src = modalImage.getAttribute('data-src');
modalImage.removeAttribute('data-src');
modalImage.removeAttribute('data-srcset');
}
var modalNextButton = document.querySelector('.c-modal-next');
var modalPrevButton = document.querySelector('.c-modal-prev');
modalNextButton.removeEventListener('click', nextModalArticle, false);
modalPrevButton.removeEventListener('click', prevModalArticle, false);
modalNextButton.addEventListener('click', nextModalArticle, false);
modalPrevButton.addEventListener('click', prevModalArticle, false);
keyboardListenerInit();
}
function getCurrentModalIndex(){
var modalContent = document.querySelector('.mzp-u-modal-content.mzp-c-modal-overlay-contents');
var newArticleIndex = parseInt(modalContent.dataset.currentIndex, 10);
return newArticleIndex;
}
function updateModalArticle(index) {
var modalContent = document.querySelector('.mzp-u-modal-content.mzp-c-modal-overlay-contents');
var newArticleId = newArticleId = articleArray[index].dataset.modalId;
var newModalContent = document.querySelector('[data-modal-parent="' + newArticleId + '"]').cloneNode(true);
var currentModalContent = modalContent.firstElementChild;
window.location.hash = newArticleId;
modalContent.replaceChild(newModalContent, currentModalContent);
modalContent.dataset.currentIndex = index;
modalInit();
}
function nextModalArticle() {
var newArticleIndex = getCurrentModalIndex();
newArticleIndex++;
// If at the end of the gallery, start over
if (newArticleIndex === (articleArray.length)) {
newArticleIndex = 0;
}
updateModalArticle(newArticleIndex);
}
function prevModalArticle() {
var newArticleIndex = getCurrentModalIndex();
newArticleIndex--;
// If at the beginning of the gallery, start over
if (newArticleIndex < 0) {
newArticleIndex = articleArray.length - 1;
}
updateModalArticle(newArticleIndex);
}
for (var i = 0; i < modalContainers.length; i++) {
var modalContainer = modalContainers[i];
modalContainer.setAttribute('aria-role', 'button');
modalContainer.dataset.currentIndex = i;
modalContainer.addEventListener('click', function(e) {
e.preventDefault();
var modalId = this.dataset.modalId;
var currentIndex = parseInt(this.dataset.currentIndex, 10);
var modalContent = document.querySelector('[data-modal-parent="' + modalId + '"]').cloneNode(true);
window.location.hash = modalId;
modalContent.removeAttribute('id');
modalContent.setAttribute('aria-role', 'article');
Mzp.Modal.createModal(this, content, {
allowScroll: false,
closeText: window.Mozilla.Utils.trans('global-close'),
onCreate: function() {
content.appendChild(modalContent);
content.dataset.currentIndex = currentIndex;
var modalCloseButton = document.querySelector('.mzp-c-modal-close');
modalCloseButton.insertAdjacentHTML('beforebegin', modalNextButtonFragment);
modalCloseButton.insertAdjacentHTML('beforebegin', modalPrevButtonFragment);
// Lazy load images in the modal and set next/prev listeners
modalInit();
},
onDestroy: function() {
if (window.history) {
window.history.replaceState('', '', window.location.pathname);
}
kbInit = false;
// Recache the current modal content which may have changed via next/prev buttons
var modalParent = document.querySelector('.mzp-u-modal-content.mzp-c-modal-overlay-contents');
var currentModalContent = modalParent.firstElementChild;
modalParent.removeChild(currentModalContent);
}
});
});
}
// trigger modal on page load if hash is present and matches a person with a bio
if (window.location.hash) {
var target = document.getElementById(window.location.hash.substr(1));
if (target && target.classList.contains('has-modal')) {
target.click();
}
}
})(window.Mozilla);

Просмотреть файл

@ -1555,7 +1555,7 @@
"files": [
"protocol/js/protocol-modal.js",
"js/base/mozilla-lazy-load.js",
"js/foundation/annual-report-2019.js"
"js/foundation/annual-report-2019.es6.js"
],
"name": "annual_report_2019"
},

Просмотреть файл

@ -4,9 +4,11 @@
"description": "Making mozilla.org awesome, one pebble at a time",
"private": true,
"dependencies": {
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"@mozilla-protocol/core": "14.0.3",
"@mozilla-protocol/eslint-config": "^1.1.0",
"@sentry/browser": "^6.12.0",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "9.0.1",
"css-loader": "6.2.0",

Просмотреть файл

@ -1,7 +1,5 @@
'use strict';
var webpackConfig = require('../../webpack.config');
module.exports = function(config) {
config.set({
// Karma configuration
@ -82,7 +80,29 @@ module.exports = function(config) {
},
webpack: {
devtool: 'inline-source-map'
devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.es6\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env', {
targets: {
ie: '10'
}
}
]
]
}
}
}
]
}
},
proxies: {

Просмотреть файл

@ -51,6 +51,28 @@ const jsConfig = {
path: path.resolve(__dirname, 'assets/'),
publicPath: '/media/',
},
module: {
rules: [
{
test: /\.es6\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env', {
targets: {
ie: '10'
}
}
]
]
}
}
}
]
},
watchOptions: {
aggregateTimeout: 600,
ignored: './node_modules/'

1064
yarn.lock

Разница между файлами не показана из-за своего большого размера Загрузить разницу