From ee6360543bf7d19c399fb4c04db117b7a3a2cfcb Mon Sep 17 00:00:00 2001
From: Calen Pennington <cale@edx.org>
Date: Thu, 22 Mar 2018 16:14:51 -0400
Subject: [PATCH] Convert webpackified factories to es6 modules with global
 exports, and remove the invoke_page_factory pattern

---
 cms/static/cms/js/build.js                    |   2 -
 cms/static/js/factories/base.js               |   2 +
 cms/static/js/factories/container.js          |  43 ++++----
 cms/static/js/factories/context_course.js     |   3 +
 cms/static/js/factories/library.js            |  47 ++++----
 cms/static/js/factories/login.js              | 103 +++++++++---------
 cms/static/js/factories/textbooks.js          |  41 +++----
 cms/static/js/factories/xblock_validation.js  |  35 +++---
 cms/static/js/pages/container.js              |   8 --
 cms/static/js/pages/course.js                 |   8 --
 cms/static/js/pages/library.js                |   8 --
 cms/static/js/pages/login.js                  |   8 --
 cms/static/js/pages/textbooks.js              |   7 --
 cms/static/js/pages/xblock_validation.js      |   8 --
 cms/static/js/sock.js                         |  68 ++++++------
 cms/templates/base.html                       |  18 +--
 cms/templates/login.html                      |   6 +-
 cms/templates/studio_xblock_wrapper.html      |  14 ++-
 cms/templates/textbooks.html                  |   6 +-
 .../templates/static_content.html             |  25 -----
 common/djangoapps/terrain/ui_helpers.py       |   6 +-
 common/static/common/js/utils/page_factory.js |  27 -----
 common/static/common/js/xblock/runtime.v1.js  |   2 +-
 .../test/test-theme/cms/templates/login.html  |   6 +-
 package-lock.json                             |   2 +-
 scripts/xsslint/xsslint/linters.py            |   2 -
 themes/red-theme/cms/templates/login.html     |   6 +-
 webpack.common.config.js                      |  11 +-
 28 files changed, 219 insertions(+), 303 deletions(-)
 create mode 100644 cms/static/js/factories/context_course.js
 delete mode 100644 cms/static/js/pages/container.js
 delete mode 100644 cms/static/js/pages/course.js
 delete mode 100644 cms/static/js/pages/library.js
 delete mode 100644 cms/static/js/pages/login.js
 delete mode 100644 cms/static/js/pages/textbooks.js
 delete mode 100644 cms/static/js/pages/xblock_validation.js
 delete mode 100644 common/static/common/js/utils/page_factory.js

diff --git a/cms/static/cms/js/build.js b/cms/static/cms/js/build.js
index 2112606a1c3..70690676af8 100644
--- a/cms/static/cms/js/build.js
+++ b/cms/static/cms/js/build.js
@@ -26,14 +26,12 @@
             'js/factories/group_configurations',
             'js/certificates/factories/certificates_page_factory',
             'js/factories/index',
-            'js/factories/library',
             'js/factories/manage_users',
             'js/factories/outline',
             'js/factories/register',
             'js/factories/settings',
             'js/factories/settings_advanced',
             'js/factories/settings_graders',
-            'js/factories/textbooks',
             'js/factories/videos_index',
         ]),
         /**
diff --git a/cms/static/js/factories/base.js b/cms/static/js/factories/base.js
index 7f61b473c9e..a7143935730 100644
--- a/cms/static/js/factories/base.js
+++ b/cms/static/js/factories/base.js
@@ -1,3 +1,5 @@
+// We can't convert this to an es6 module until all factories that use it have been converted out
+// of RequireJS
 define(['js/base', 'cms/js/main', 'js/src/logger', 'datepair', 'accessibility',
     'ieshim', 'tooltip_manager', 'lang_edx', 'js/models/course'],
     function() {
diff --git a/cms/static/js/factories/container.js b/cms/static/js/factories/container.js
index 8861d6f1249..cfca2988518 100644
--- a/cms/static/js/factories/container.js
+++ b/cms/static/js/factories/container.js
@@ -1,21 +1,26 @@
-define([
-    'jquery', 'underscore', 'js/models/xblock_container_info', 'js/views/pages/container',
-    'js/collections/component_template', 'xmodule', 'cms/js/main',
-    'xblock/cms.runtime.v1'
-],
-function($, _, XBlockContainerInfo, ContainerPage, ComponentTemplates, xmoduleLoader) {
-    'use strict';
-    return function(componentTemplates, XBlockInfoJson, action, options) {
-        var main_options = {
-            el: $('#content'),
-            model: new XBlockContainerInfo(XBlockInfoJson, {parse: true}),
-            action: action,
-            templates: new ComponentTemplates(componentTemplates, {parse: true})
-        };
+import * as $ from 'jquery';
+import * as _ from 'underscore';
+import * as XBlockContainerInfo from 'js/models/xblock_container_info';
+import * as ContainerPage from 'js/views/pages/container';
+import * as ComponentTemplates from 'js/collections/component_template';
+import * as xmoduleLoader from 'xmodule';
+import './base';
+import 'cms/js/main';
+import 'xblock/cms.runtime.v1';
 
-        xmoduleLoader.done(function() {
-            var view = new ContainerPage(_.extend(main_options, options));
-            view.render();
-        });
+'use strict';
+export default function ContainerFactory(componentTemplates, XBlockInfoJson, action, options) {
+    var main_options = {
+        el: $('#content'),
+        model: new XBlockContainerInfo(XBlockInfoJson, {parse: true}),
+        action: action,
+        templates: new ComponentTemplates(componentTemplates, {parse: true})
     };
-});
+
+    xmoduleLoader.done(function() {
+        var view = new ContainerPage(_.extend(main_options, options));
+        view.render();
+    });
+};
+
+export {ContainerFactory}
diff --git a/cms/static/js/factories/context_course.js b/cms/static/js/factories/context_course.js
new file mode 100644
index 00000000000..475e5a6282c
--- /dev/null
+++ b/cms/static/js/factories/context_course.js
@@ -0,0 +1,3 @@
+import * as ContextCourse from 'js/models/course';
+
+export {ContextCourse}
diff --git a/cms/static/js/factories/library.js b/cms/static/js/factories/library.js
index e6eb9290693..4cde6873f93 100644
--- a/cms/static/js/factories/library.js
+++ b/cms/static/js/factories/library.js
@@ -1,23 +1,28 @@
-define([
-    'jquery', 'underscore', 'js/models/xblock_info', 'js/views/pages/paged_container',
-    'js/views/library_container', 'js/collections/component_template', 'xmodule', 'cms/js/main',
-    'xblock/cms.runtime.v1'
-],
-function($, _, XBlockInfo, PagedContainerPage, LibraryContainerView, ComponentTemplates, xmoduleLoader) {
-    'use strict';
-    return function(componentTemplates, XBlockInfoJson, options) {
-        var main_options = {
-            el: $('#content'),
-            model: new XBlockInfo(XBlockInfoJson, {parse: true}),
-            templates: new ComponentTemplates(componentTemplates, {parse: true}),
-            action: 'view',
-            viewClass: LibraryContainerView,
-            canEdit: true
-        };
+import * as $ from 'jquery';
+import * as _ from 'underscore';
+import * as XBlockInfo from 'js/models/xblock_info';
+import * as PagedContainerPage from 'js/views/pages/paged_container';
+import * as LibraryContainerView from 'js/views/library_container';
+import * as ComponentTemplates from 'js/collections/component_template';
+import * as xmoduleLoader from 'xmodule';
+import 'cms/js/main';
+import 'xblock/cms.runtime.v1';
 
-        xmoduleLoader.done(function() {
-            var view = new PagedContainerPage(_.extend(main_options, options));
-            view.render();
-        });
+'use strict';
+export default function LibraryFactory(componentTemplates, XBlockInfoJson, options) {
+    var main_options = {
+        el: $('#content'),
+        model: new XBlockInfo(XBlockInfoJson, {parse: true}),
+        templates: new ComponentTemplates(componentTemplates, {parse: true}),
+        action: 'view',
+        viewClass: LibraryContainerView,
+        canEdit: true
     };
-});
+
+    xmoduleLoader.done(function() {
+        var view = new PagedContainerPage(_.extend(main_options, options));
+        view.render();
+    });
+};
+
+export {LibraryFactory}
diff --git a/cms/static/js/factories/login.js b/cms/static/js/factories/login.js
index fdbcef31e8e..fa8bb454a9a 100644
--- a/cms/static/js/factories/login.js
+++ b/cms/static/js/factories/login.js
@@ -1,57 +1,62 @@
-define(['jquery.cookie', 'utility', 'common/js/components/utils/view_utils'], function(cookie, utility, ViewUtils) {
-    'use strict';
-    return function LoginFactory(homepageURL) {
-        function postJSON(url, data, callback) {
-            $.ajax({
-                type: 'POST',
-                url: url,
-                dataType: 'json',
-                data: data,
-                success: callback
-            });
-        }
+import * as cookie from 'jquery.cookie';
+import * as utility from 'utility';
+import * as ViewUtils from 'common/js/components/utils/view_utils';
 
-        // Clear the login error message when credentials are edited
-        $('input#email').on('input', function() {
-            $('#login_error').removeClass('is-shown');
-        });
+'use strict';
 
-        $('input#password').on('input', function() {
-            $('#login_error').removeClass('is-shown');
+export default function LoginFactory(homepageURL) {
+    function postJSON(url, data, callback) {
+        $.ajax({
+            type: 'POST',
+            url: url,
+            dataType: 'json',
+            data: data,
+            success: callback
         });
+    }
+
+    // Clear the login error message when credentials are edited
+    $('input#email').on('input', function() {
+        $('#login_error').removeClass('is-shown');
+    });
 
-        $('form#login_form').submit(function(event) {
-            event.preventDefault();
-            var $submitButton = $('#submit'),
-                deferred = new $.Deferred(),
-                promise = deferred.promise();
-            ViewUtils.disableElementWhileRunning($submitButton, function() { return promise; });
-            var submit_data = $('#login_form').serialize();
+    $('input#password').on('input', function() {
+        $('#login_error').removeClass('is-shown');
+    });
 
-            postJSON('/login_post', submit_data, function(json) {
-                if (json.success) {
-                    var next = /next=([^&]*)/g.exec(decodeURIComponent(window.location.search));
-                    if (next && next.length > 1 && !isExternal(next[1])) {
-                        ViewUtils.redirect(next[1]);
-                    } else {
-                        ViewUtils.redirect(homepageURL);
-                    }
-                } else if ($('#login_error').length === 0) {
-                    $('#login_form').prepend(
-                        '<div id="login_error" class="message message-status error">' +
-                        json.value +
-                        '</span></div>'
-                    );
-                    $('#login_error').addClass('is-shown');
-                    deferred.resolve();
+    $('form#login_form').submit(function(event) {
+        event.preventDefault();
+        var $submitButton = $('#submit'),
+            deferred = new $.Deferred(),
+            promise = deferred.promise();
+        ViewUtils.disableElementWhileRunning($submitButton, function() { return promise; });
+        var submit_data = $('#login_form').serialize();
+
+        postJSON('/login_post', submit_data, function(json) {
+            if (json.success) {
+                var next = /next=([^&]*)/g.exec(decodeURIComponent(window.location.search));
+                if (next && next.length > 1 && !isExternal(next[1])) {
+                    ViewUtils.redirect(next[1]);
                 } else {
-                    $('#login_error')
-                        .stop()
-                        .addClass('is-shown')
-                        .html(json.value);
-                    deferred.resolve();
+                    ViewUtils.redirect(homepageURL);
                 }
-            });
+            } else if ($('#login_error').length === 0) {
+                $('#login_form').prepend(
+                    '<div id="login_error" class="message message-status error">' +
+                    json.value +
+                    '</span></div>'
+                );
+                $('#login_error').addClass('is-shown');
+                deferred.resolve();
+            } else {
+                $('#login_error')
+                    .stop()
+                    .addClass('is-shown')
+                    .html(json.value);
+                deferred.resolve();
+            }
         });
-    };
-});
+    });
+};
+
+export {LoginFactory}
diff --git a/cms/static/js/factories/textbooks.js b/cms/static/js/factories/textbooks.js
index 0641e025d36..2fd11e407f1 100644
--- a/cms/static/js/factories/textbooks.js
+++ b/cms/static/js/factories/textbooks.js
@@ -1,20 +1,23 @@
-define([
-    'gettext', 'js/models/section', 'js/collections/textbook', 'js/views/list_textbooks'
-], function(gettext, Section, TextbookCollection, ListTextbooksView) {
-    'use strict';
-    return function(textbooksJson) {
-        var textbooks = new TextbookCollection(textbooksJson, {parse: true}),
-            tbView = new ListTextbooksView({collection: textbooks});
+import * as gettext from 'gettext';
+import * as Section from 'js/models/section';
+import * as TextbookCollection from 'js/collections/textbook';
+import * as ListTextbooksView from 'js/views/list_textbooks';
 
-        $('.content-primary').append(tbView.render().el);
-        $('.nav-actions .new-button').click(function(event) {
-            tbView.addOne(event);
-        });
-        $(window).on('beforeunload', function() {
-            var dirty = textbooks.find(function(textbook) { return textbook.isDirty(); });
-            if (dirty) {
-                return gettext('You have unsaved changes. Do you really want to leave this page?');
-            }
-        });
-    };
-});
+'use strict';
+export default function TextbooksFactory(textbooksJson) {
+    var textbooks = new TextbookCollection(textbooksJson, {parse: true}),
+        tbView = new ListTextbooksView({collection: textbooks});
+
+    $('.content-primary').append(tbView.render().el);
+    $('.nav-actions .new-button').click(function(event) {
+        tbView.addOne(event);
+    });
+    $(window).on('beforeunload', function() {
+        var dirty = textbooks.find(function(textbook) { return textbook.isDirty(); });
+        if (dirty) {
+            return gettext('You have unsaved changes. Do you really want to leave this page?');
+        }
+    });
+};
+
+export {TextbooksFactory}
diff --git a/cms/static/js/factories/xblock_validation.js b/cms/static/js/factories/xblock_validation.js
index 4bcfc3b3391..56786c89d9c 100644
--- a/cms/static/js/factories/xblock_validation.js
+++ b/cms/static/js/factories/xblock_validation.js
@@ -1,19 +1,22 @@
-define(['js/views/xblock_validation', 'js/models/xblock_validation'],
-function(XBlockValidationView, XBlockValidationModel) {
-    'use strict';
-    return function(validationMessages, hasEditingUrl, isRoot, isUnit, validationEle) {
-        var model, response;
 
-        if (hasEditingUrl && !isRoot) {
-            validationMessages.showSummaryOnly = true;
-        }
-        response = validationMessages;
-        response.isUnit = isUnit;
+import * as XBlockValidationView from 'js/views/xblock_validation';
+import * as XBlockValidationModel from 'js/models/xblock_validation';
 
-        model = new XBlockValidationModel(response, {parse: true});
+'use strict';
+export default function XBlockValidationFactory(validationMessages, hasEditingUrl, isRoot, isUnit, validationEle) {
+    var model, response;
 
-        if (!model.get('empty')) {
-            new XBlockValidationView({el: validationEle, model: model, root: isRoot}).render();
-        }
-    };
-});
+    if (hasEditingUrl && !isRoot) {
+        validationMessages.showSummaryOnly = true;
+    }
+    response = validationMessages;
+    response.isUnit = isUnit;
+
+    model = new XBlockValidationModel(response, {parse: true});
+
+    if (!model.get('empty')) {
+        new XBlockValidationView({el: validationEle, model: model, root: isRoot}).render();
+    }
+};
+
+export {XBlockValidationFactory}
diff --git a/cms/static/js/pages/container.js b/cms/static/js/pages/container.js
deleted file mode 100644
index 79937020db6..00000000000
--- a/cms/static/js/pages/container.js
+++ /dev/null
@@ -1,8 +0,0 @@
-define(
-    ['js/factories/container', 'common/js/utils/page_factory', 'js/factories/base', 'js/pages/course'],
-    function(ContainerFactory, invokePageFactory) {
-        'use strict';
-        invokePageFactory('ContainerFactory', ContainerFactory);
-    }
-);
-
diff --git a/cms/static/js/pages/course.js b/cms/static/js/pages/course.js
deleted file mode 100644
index 5334fedbdec..00000000000
--- a/cms/static/js/pages/course.js
+++ /dev/null
@@ -1,8 +0,0 @@
-define(
-    ['js/models/course'],
-    function(ContextCourse) {
-        'use strict';
-        window.course = new ContextCourse(window.pageFactoryArguments.ContextCourse[0]);
-    }
-);
-
diff --git a/cms/static/js/pages/library.js b/cms/static/js/pages/library.js
deleted file mode 100644
index 846ba7b1112..00000000000
--- a/cms/static/js/pages/library.js
+++ /dev/null
@@ -1,8 +0,0 @@
-define(
-    ['js/factories/library', 'common/js/utils/page_factory', 'js/factories/base'],
-    function(LibraryFactory, invokePageFactory) {
-        'use strict';
-        invokePageFactory('LibraryFactory', LibraryFactory);
-    }
-);
-
diff --git a/cms/static/js/pages/login.js b/cms/static/js/pages/login.js
deleted file mode 100644
index 449a1190f64..00000000000
--- a/cms/static/js/pages/login.js
+++ /dev/null
@@ -1,8 +0,0 @@
-define(
-    ['js/factories/login', 'common/js/utils/page_factory', 'js/factories/base'],
-    function(LoginFactory, invokePageFactory) {
-        'use strict';
-        invokePageFactory('LoginFactory', LoginFactory);
-    }
-);
-
diff --git a/cms/static/js/pages/textbooks.js b/cms/static/js/pages/textbooks.js
deleted file mode 100644
index 7d524fbd290..00000000000
--- a/cms/static/js/pages/textbooks.js
+++ /dev/null
@@ -1,7 +0,0 @@
-define(
-    ['js/factories/textbooks', 'common/js/utils/page_factory', 'js/factories/base', 'js/pages/course'],
-    function(TextbooksFactory, invokePageFactory) {
-        'use strict';
-        invokePageFactory('TextbooksFactory', TextbooksFactory);
-    }
-);
diff --git a/cms/static/js/pages/xblock_validation.js b/cms/static/js/pages/xblock_validation.js
deleted file mode 100644
index b0a4cb7a7cd..00000000000
--- a/cms/static/js/pages/xblock_validation.js
+++ /dev/null
@@ -1,8 +0,0 @@
-define(
-    ['js/factories/xblock_validation', 'common/js/utils/page_factory'],
-    function(XBlockValidationFactory, invokePageFactory) {
-        'use strict';
-        invokePageFactory('XBlockValidationFactory', XBlockValidationFactory);
-    }
-);
-
diff --git a/cms/static/js/sock.js b/cms/static/js/sock.js
index 44653981ff6..a68bb55bcc4 100644
--- a/cms/static/js/sock.js
+++ b/cms/static/js/sock.js
@@ -1,39 +1,39 @@
-define(['domReady', 'jquery', 'jquery.smoothScroll'],
-    function(domReady, $) {
-        'use strict';
+import * as domReady from 'domReady';
+import * as $ from 'jquery';
+import 'jquery.smoothScroll';
 
-        var toggleSock = function(e) {
-            e.preventDefault();
+'use strict';
 
-            var $btnShowSockLabel = $(this).find('.copy-show');
-            var $btnHideSockLabel = $(this).find('.copy-hide');
-            var $sock = $('.wrapper-sock');
-            var $sockContent = $sock.find('.wrapper-inner');
+var toggleSock = function(e) {
+    e.preventDefault();
 
-            if ($sock.hasClass('is-shown')) {
-                $sock.removeClass('is-shown');
-                $sockContent.hide('fast');
-                $btnHideSockLabel.removeClass('is-shown').addClass('is-hidden');
-                $btnShowSockLabel.removeClass('is-hidden').addClass('is-shown');
-            } else {
-                $sock.addClass('is-shown');
-                $sockContent.show('fast');
-                $btnHideSockLabel.removeClass('is-hidden').addClass('is-shown');
-                $btnShowSockLabel.removeClass('is-shown').addClass('is-hidden');
-            }
+    var $btnShowSockLabel = $(this).find('.copy-show');
+    var $btnHideSockLabel = $(this).find('.copy-hide');
+    var $sock = $('.wrapper-sock');
+    var $sockContent = $sock.find('.wrapper-inner');
 
-            $.smoothScroll({
-                offset: -200,
-                easing: 'swing',
-                speed: 1000,
-                scrollElement: null,
-                scrollTarget: $sock
-            });
-        };
-
-        domReady(function() {
-            // toggling footer additional support
-            $('.cta-show-sock').bind('click', toggleSock);
-        });
+    if ($sock.hasClass('is-shown')) {
+        $sock.removeClass('is-shown');
+        $sockContent.hide('fast');
+        $btnHideSockLabel.removeClass('is-shown').addClass('is-hidden');
+        $btnShowSockLabel.removeClass('is-hidden').addClass('is-shown');
+    } else {
+        $sock.addClass('is-shown');
+        $sockContent.show('fast');
+        $btnHideSockLabel.removeClass('is-hidden').addClass('is-shown');
+        $btnShowSockLabel.removeClass('is-shown').addClass('is-hidden');
     }
-);
+
+    $.smoothScroll({
+        offset: -200,
+        easing: 'swing',
+        speed: 1000,
+        scrollElement: null,
+        scrollTarget: $sock
+    });
+};
+
+domReady(function() {
+    // toggling footer additional support
+    $('.cta-show-sock').bind('click', toggleSock);
+});
diff --git a/cms/templates/base.html b/cms/templates/base.html
index 0a6b896f473..ccf1a535955 100644
--- a/cms/templates/base.html
+++ b/cms/templates/base.html
@@ -125,12 +125,9 @@ from openedx.core.release import RELEASE_LINE
     <%block name="jsextra"></%block>
 
     % if context_course:
+      <%static:webpack entry="js/factories/context_course"/>
       <script type="text/javascript">
-        if (typeof window.pageFactoryArguments == "undefined") {
-            window.pageFactoryArguments = {};
-        }
-
-        window.pageFactoryArguments['ContextCourse'] = {
+        window.course = new ContextCourse({
           id: "${context_course.id | n, js_escaped_string}",
           name: "${context_course.display_name_with_default | n, js_escaped_string}",
           url_name: "${context_course.location.block_id | n, js_escaped_string}",
@@ -139,21 +136,16 @@ from openedx.core.release import RELEASE_LINE
           display_course_number: "${context_course.display_coursenumber | n, js_escaped_string}",
           revision: "${context_course.location.branch | n, js_escaped_string}",
           self_paced: ${ context_course.self_paced | n, dump_js_escaped_json }
-        }
+        });
       </script>
     % endif
     % if user.is_authenticated:
-      <%static:invoke_page_bundle page_name='js/sock'/>
+      <%static:webpack entry='js/sock'/>
     % endif
     <%block name='page_bundle'>
       <script type="text/javascript">
       require(['js/factories/base'], function () {
-          require(['js/models/course'], function(Course) {
-              % if context_course:
-                  window.course = new Course(window.pageFactoryArguments['ContextCourse']);
-              % endif
-              <%block name='requirejs'></%block>
-          });
+        <%block name='requirejs'></%block>
       });
       </script>
     </%block>
diff --git a/cms/templates/login.html b/cms/templates/login.html
index c4966f8b669..5386d96c518 100644
--- a/cms/templates/login.html
+++ b/cms/templates/login.html
@@ -55,7 +55,7 @@ from openedx.core.djangolib.js_utils import js_escaped_string
 </%block>
 
 <%block name="page_bundle">
-  <%static:invoke_page_bundle page_name="js/pages/login" class_name="LoginFactory">
-    "${reverse('homepage') | n, js_escaped_string}"
-  </%static:invoke_page_bundle>
+  <%static:webpack entry="js/factories/login">
+    LoginFactory("${reverse('homepage') | n, js_escaped_string}");
+  </%static:webpack>
 </%block>
diff --git a/cms/templates/studio_xblock_wrapper.html b/cms/templates/studio_xblock_wrapper.html
index f4a2e45ac98..d6e568a849e 100644
--- a/cms/templates/studio_xblock_wrapper.html
+++ b/cms/templates/studio_xblock_wrapper.html
@@ -27,12 +27,14 @@ block_is_unit = is_unit(xblock)
 </script>
 </%block>
 
-<%static:webpack page_name="js/pages/xblock_validation" class_name="XBlockValidationFactory">
-    ${messages | n, dump_js_escaped_json},
-    ${bool(xblock_url) | n, dump_js_escaped_json},  // xblock_url will be None or a string
-    ${bool(is_root) | n, dump_js_escaped_json},  // is_root will be None or a boolean
-    ${bool(block_is_unit) | n, dump_js_escaped_json},  // block_is_unit will be None or a boolean
-    $('div.xblock-validation-messages[data-locator="${xblock.location | n, js_escaped_string}"]')
+<%static:webpack entry="js/factories/xblock_validation">
+    XBlockValidationFactory(
+        ${messages | n, dump_js_escaped_json},
+        ${bool(xblock_url) | n, dump_js_escaped_json},  // xblock_url will be None or a string
+        ${bool(is_root) | n, dump_js_escaped_json},  // is_root will be None or a boolean
+        ${bool(block_is_unit) | n, dump_js_escaped_json},  // block_is_unit will be None or a boolean
+        $('div.xblock-validation-messages[data-locator="${xblock.location | n, js_escaped_string}"]')
+    );
 </%static:webpack>
 
 % if isinstance(xblock, (XModule, XModuleDescriptor)):
diff --git a/cms/templates/textbooks.html b/cms/templates/textbooks.html
index 49184248aa1..2a5c9552240 100644
--- a/cms/templates/textbooks.html
+++ b/cms/templates/textbooks.html
@@ -28,9 +28,9 @@ CMS.URL.LMS_BASE = "${settings.LMS_BASE}"
 </%block>
 
 <%block name="page_bundle">
-  <%static:invoke_page_bundle page_name="js/pages/textbooks" class_name="TextbooksFactory">
-     ${textbooks | n, dump_js_escaped_json}
-  </%static:invoke_page_bundle>
+  <%static:webpack entry="js/factories/textbooks">
+    TextbooksFactory(${textbooks | n, dump_js_escaped_json});
+  </%static:webpack>
 </%block>
 
 <%block name="content">
diff --git a/common/djangoapps/pipeline_mako/templates/static_content.html b/common/djangoapps/pipeline_mako/templates/static_content.html
index 91eba8da714..56461ed7c8b 100644
--- a/common/djangoapps/pipeline_mako/templates/static_content.html
+++ b/common/djangoapps/pipeline_mako/templates/static_content.html
@@ -164,31 +164,6 @@ source, template_path = Loader(engine).load_template_source(path)
     </script>
 </%def>
 
-<%def name="invoke_page_bundle(page_name, class_name=None)">
-    <%doc>
-      Loads Javascript onto your page synchronously.
-      Uses RequireJS in development and a plain script tag in production.
-      The body of the tag should be a comma-separated list of arguments
-      to be passed to the page factory specified by the class_name argument.
-    </%doc>
-    <%
-        body = capture(caller.body)
-    %>
-    % if class_name:
-        <script type="text/javascript">
-            if (typeof pageFactoryArguments == "undefined") {
-                var pageFactoryArguments = {};
-            }
-            % if body:
-                pageFactoryArguments['${class_name | n, js_escaped_string}'] = [${ body | n, decode.utf8 }];
-            % else:
-                pageFactoryArguments['${class_name | n, js_escaped_string}'] = [];
-            % endif
-        </script>
-    % endif
-    <%self:webpack entry="${page_name}"/>
-</%def>
-
 <%def name="require_module(module_name, class_name)">
     <%doc>
       Loads Javascript onto your page synchronously.
diff --git a/common/djangoapps/terrain/ui_helpers.py b/common/djangoapps/terrain/ui_helpers.py
index 2b92fbd00d3..b43b4855029 100644
--- a/common/djangoapps/terrain/ui_helpers.py
+++ b/common/djangoapps/terrain/ui_helpers.py
@@ -48,7 +48,7 @@ REQUIREJS_WAIT = {
 
     # Dashboard
     re.compile(r'^Studio Home \|'): [
-        "js/sock", "gettext", "js/base",
+        "gettext", "js/base",
         "jquery.ui", "cms/js/main", "underscore"],
 
     # Pages
@@ -66,12 +66,10 @@ def wait(seconds):
 
 @world.absorb
 def wait_for_js_to_load():
-    requirements = None
     for test, req in REQUIREJS_WAIT.items():
         if test.search(world.browser.title):
-            requirements = req
+            world.wait_for_requirejs(req)
             break
-    world.wait_for_requirejs(requirements)
 
 
 # Selenium's `execute_async_script` function pauses Selenium's execution
diff --git a/common/static/common/js/utils/page_factory.js b/common/static/common/js/utils/page_factory.js
deleted file mode 100644
index 3ae02bca255..00000000000
--- a/common/static/common/js/utils/page_factory.js
+++ /dev/null
@@ -1,27 +0,0 @@
-define([], function() {
-    'use strict';
-
-    return function invokePageFactory(name, factory) {
-        var args;
-
-        if (typeof window.pageFactoryArguments === 'undefined') {
-            throw Error(
-                'window.pageFactoryArguments must be initialized before calling invokePageFactory(' +
-                name +
-                '). Use the <%static:invoke_page_bundle> template tag.'
-            );
-        }
-        args = window.pageFactoryArguments[name];
-
-        if (typeof args === 'undefined') {
-            throw Error(
-                'window.pageFactoryArguments["' +
-                name +
-                '"] must be initialized before calling invokePageFactory(' +
-                name +
-                '). Use the <%static:invoke_page_bundle> template tag.'
-            );
-        }
-        factory.apply(null, window.pageFactoryArguments[name]);
-    };
-});
diff --git a/common/static/common/js/xblock/runtime.v1.js b/common/static/common/js/xblock/runtime.v1.js
index c49d7ea886d..8cf49b5c6cf 100644
--- a/common/static/common/js/xblock/runtime.v1.js
+++ b/common/static/common/js/xblock/runtime.v1.js
@@ -1,7 +1,7 @@
 (function() {
     'use strict';
 
-    XBlock.Runtime.v1 = (function() {
+    this.XBlock.Runtime.v1 = (function() {
         function v1() {
             var _this = this;
             this.childMap = function() {
diff --git a/common/test/test-theme/cms/templates/login.html b/common/test/test-theme/cms/templates/login.html
index 4e13b5cdc9e..ba5d5163b62 100644
--- a/common/test/test-theme/cms/templates/login.html
+++ b/common/test/test-theme/cms/templates/login.html
@@ -53,7 +53,7 @@ from openedx.core.djangolib.js_utils import js_escaped_string
 </%block>
 
 <%block name="page_bundle">
-  <%static:invoke_page_bundle page_name="js/pages/login" class_name="LoginFactory">
-    "${reverse('homepage') | n, js_escaped_string}"
-  </%static:invoke_page_bundle>
+  <%static:webpack entry="js/factories/login">
+     LoginFactory("${reverse('homepage') | n, js_escaped_string}");
+  </%static:webpack>
 </%block>
diff --git a/package-lock.json b/package-lock.json
index 95235946f15..70f51cb98d4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8219,7 +8219,7 @@
     "redux": {
       "version": "3.7.2",
       "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz",
-      "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==",
+      "integrity": "sha1-BrcxIyFZAdJdBlvjQusCa8HIU3s=",
       "requires": {
         "lodash": "4.17.5",
         "lodash-es": "4.17.6",
diff --git a/scripts/xsslint/xsslint/linters.py b/scripts/xsslint/xsslint/linters.py
index 1ad99e18ca1..1c7cc13774a 100644
--- a/scripts/xsslint/xsslint/linters.py
+++ b/scripts/xsslint/xsslint/linters.py
@@ -1370,8 +1370,6 @@ class MakoTemplateLinter(BaseLinter):
                 </script> |  # script tag end
                 <%static:require_module(_async)?.*?> |  # require js script tag start (optionally the _async version)
                 </%static:require_module(_async)?> | # require js script tag end (optionally the _async version)
-                <%static:invoke_page_bundle.*?> |  # require js script tag start
-                </%static:invoke_page_bundle> |  # require js script tag end
                 <%static:webpack.*?> |  # webpack script tag start
                 </%static:webpack> | # webpack script tag end
                 <%static:studiofrontend.*?> | # studiofrontend script tag start
diff --git a/themes/red-theme/cms/templates/login.html b/themes/red-theme/cms/templates/login.html
index 721104311d0..32f564c2a41 100644
--- a/themes/red-theme/cms/templates/login.html
+++ b/themes/red-theme/cms/templates/login.html
@@ -52,7 +52,7 @@ from django.utils.translation import ugettext as _
 </%block>
 
 <%block name="page_bundle">
-  <%static:invoke_page_bundle page_name="js/pages/login" class_name="LoginFactory">
-    "${reverse('homepage') | n, js_escaped_string}"
-  </%static:invoke_page_bundle>
+  <%static:webpack entry="js/factories/login">
+     LoginFactory("${reverse('homepage') | n, js_escaped_string}");
+  </%static:webpack>
 </%block>
diff --git a/webpack.common.config.js b/webpack.common.config.js
index a75a8817810..6458225f08c 100644
--- a/webpack.common.config.js
+++ b/webpack.common.config.js
@@ -30,11 +30,12 @@ module.exports = {
         // Studio
         Import: './cms/static/js/features/import/factories/import.js',
         CourseOrLibraryListing: './cms/static/js/features_jsx/studio/CourseOrLibraryListing.jsx',
-        'js/pages/login': './cms/static/js/pages/login.js',
-        'js/pages/textbooks': './cms/static/js/pages/textbooks.js',
-        'js/pages/container': './cms/static/js/pages/container.js',
-        'js/pages/library': './cms/static/js/pages/library.js',
-        'js/pages/xblock_validation': './cms/static/js/pages/xblock_validation.js',
+        'js/factories/login': './cms/static/js/factories/login.js',
+        'js/factories/textbooks': './cms/static/js/factories/textbooks.js',
+        'js/factories/container': './cms/static/js/factories/container.js',
+        'js/factories/context_course': './cms/static/js/factories/context_course.js',
+        'js/factories/library': './cms/static/js/factories/library.js',
+        'js/factories/xblock_validation': './cms/static/js/factories/xblock_validation.js',
         'js/sock': './cms/static/js/sock.js',
 
         // LMS
-- 
GitLab