Skip to content

Commit

Permalink
upgrade webpack to v4
Browse files Browse the repository at this point in the history
Closes: CORE-1143

Test plan:
* load canvas in prod mode in a non-English language. Click around
  And make sure everything works
* in prod mode, do a test to compare load time to what’s on beta.
* page load time and js bundle size should be smaller
* click around in the quizzes client apps and the ember grade book
  And make sure those things work

Change-Id: I93c28c4a6d22db95cd1c7e59cd3f5221d46fe1ed
Reviewed-on: https://gerrit.instructure.com/143422
Tested-by: Jenkins
QA-Review: Jeremy Putnam <jeremyp@instructure.com>
Product-Review: Ryan Shaw <ryan@instructure.com>
Reviewed-by: Clay Diffrient <cdiffrient@instructure.com>
  • Loading branch information
ryankshaw committed May 16, 2019
1 parent 5e555b3 commit 4944bcc
Show file tree
Hide file tree
Showing 19 changed files with 354 additions and 531 deletions.
69 changes: 50 additions & 19 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -170,38 +170,57 @@ def js_base_url
(use_optimized_js? ? '/dist/webpack-production' : '/dist/webpack-dev').freeze
end

# puts the "main" webpack entry and the moment & timezone files in the <head> of the document
def include_head_js
# This contains the webpack runtime, it needs to be loaded first
paths = ["#{js_base_url}/vendor"]

paths = []
# We preemptive load these timezone/locale data files so they are ready
# by the time our app-code runs and so webpack doesn't need to know how to load them
paths << "/timezone/#{js_env[:TIMEZONE]}.js" if js_env[:TIMEZONE]
paths << "/timezone/#{js_env[:CONTEXT_TIMEZONE]}.js" if js_env[:CONTEXT_TIMEZONE]
paths << "/timezone/#{js_env[:BIGEASY_LOCALE]}.js" if js_env[:BIGEASY_LOCALE]
paths << "#{js_base_url}/moment/locale/#{js_env[:MOMENT_LOCALE]}" if js_env[:MOMENT_LOCALE] && js_env[:MOMENT_LOCALE] != 'en'

paths << "#{js_base_url}/appBootstrap"
paths << "#{js_base_url}/common"

js_bundles.each do |(bundle, plugin)|
paths << "#{js_base_url}/#{plugin ? "#{plugin}-" : ''}#{bundle}"
@script_chunks = []
# if there is a moment locale besides english set, put a script tag for it
# so it is loaded and ready before we run any of our app code
if js_env[:MOMENT_LOCALE] && js_env[:MOMENT_LOCALE] != 'en'
@script_chunks += Canvas::Cdn::RevManifest.all_webpack_chunks_for("moment/locale/#{js_env[:MOMENT_LOCALE]}")
end
# now that we've rendered out a script tag for each bundle we were told about in controllers,
# empty out the js_bundles array so we don't re-render them later
@js_bundles_included_in_head = js_bundles.dup
js_bundles.clear
@script_chunks += Canvas::Cdn::RevManifest.all_webpack_chunks_for("main")
@script_chunks.uniq!

chunk_urls = @script_chunks.map{ |s| "#{js_base_url}/#{s}"}

capture do
# if we don't also put preload tags for these, the browser will prioritize and
# download the bundle chunks we preload below before these scripts
chunk_urls.each { |url| concat preload_link_tag(url) }

javascript_include_tag(*paths, defer: true)
concat javascript_include_tag(*(paths + chunk_urls), defer: true)
end
end

# Returns a <script> tag for each registered js_bundle
def include_js_bundles
paths = []
(js_bundles - (@js_bundles_included_in_head || [])).each do |(bundle, plugin)|
paths << "#{js_base_url}/#{plugin ? "#{plugin}-" : ''}#{bundle}"
# This is purely a performance optimization to reduce the steps of the waterfall
# and let the browser know it needs to start downloading all of these chunks
# even before any webpack code runs. It will put a <link rel="preload" ...>
# for every chunk that is needed by any of the things you `js_bundle` in your rails controllers/views
preload_chunks = js_bundles.map do |(bundle, plugin)|
key = "#{plugin ? "#{plugin}-" : ''}#{bundle}"
Canvas::Cdn::RevManifest.all_webpack_chunks_for(key)
end.flatten.uniq - @script_chunks # subtract out the ones we already preloaded in the <head>

capture do
preload_chunks.each { |url| concat preload_link_tag("#{js_base_url}/#{url}") }

# if you look the app/jsx/main.js, there is a function there that will
# process anything on window.bundles and knows how to load everything it needs
# to load that "js_bundle". And by the time that runs, the browser will have already
# started downloading those script urls because of those preload tags above,
# so it will not cause a new request to be made.
concat javascript_tag js_bundles.map { |(bundle, plugin)|
"(window.bundles || (window.bundles = [])).push('#{plugin ? "#{plugin}-" : ''}#{bundle}');"
}.join("\n")
end
javascript_include_tag(*paths, defer: true)
end

def include_css_bundles
Expand Down Expand Up @@ -1173,6 +1192,18 @@ def thumbnail_image_url(attachment, uuid=nil, url_options={})
super(attachment, uuid || attachment.uuid, url_options)
end

if CANVAS_RAILS5_1
# this is for rails 5.1. it can be deleted when we upgrade to RAILS_5.2. rails 5.2 includes a preload_link_tag method
def preload_link_tag(source, options = {})
tag.link({
rel: "preload",
href: asset_path(source),
as: "script",
type: "text/javascript"
}.merge(options.symbolize_keys))
end
end

def browser_performance_monitor_embed
# stub
end
Expand Down
7 changes: 6 additions & 1 deletion app/jsx/bundles/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,10 @@ if (!supportsCSSVars) {
}

$(() => {
if (isMathMLOnPage()) loadMathJax('TeX-MML-AM_HTMLorMML')
// This is in a setTimeout to have it run on the next time through the event loop
// so that the code that actually renders the user_content runs first,
// because it has to be rendered before we can check if isMathMLOnPage
setTimeout(() => {
if (isMathMLOnPage()) loadMathJax('TeX-MML-AM_HTMLorMML')
}, 5)
})
2 changes: 1 addition & 1 deletion app/jsx/courses/show.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ const addToDoSidebar = (parent) => {
renderToDoSidebar(parent)
}

document.addEventListener('DOMContentLoaded', () => {
$(() => {
const todo_container = document.querySelector('.todo-list')
if (todo_container) {
addToDoSidebar(todo_container)
Expand Down
30 changes: 30 additions & 0 deletions app/jsx/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import 'setWebpackCdnHost'
import 'jquery.instructure_jquery_patches' // this needs to be before anything else that requires jQuery

import './appBootstrap'
import './bundles/common'

import loadBundle from 'bundles-generated'

if (!window.bundles) window.bundles = []
window.bundles.push = loadBundle
// process any queued ones
window.bundles.forEach(loadBundle)
28 changes: 16 additions & 12 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,23 @@
*/

module.exports = {
"presets": [
["@instructure/ui-babel-preset", {
// this tells @babel/plugin-transform-runtime to not use ESModules in jest.
esModules: !process.env.JEST_WORKER_ID,
// this tells it to transform imports to commonJS in jest
node: !!process.env.JEST_WORKER_ID
}]
presets: [
[
'@instructure/ui-babel-preset',
{
esModules:
'USE_ES_MODULES' in process.env
? process.env.USE_ES_MODULES !== 'false'
: !process.env.JEST_WORKER_ID,
node: !!process.env.JEST_WORKER_ID
}
]
],
"env": {
"production": {
"plugins": [
"transform-react-remove-prop-types",
"@babel/plugin-transform-react-inline-elements"
env: {
production: {
plugins: [
'transform-react-remove-prop-types',
'@babel/plugin-transform-react-inline-elements'
]
}
}
Expand Down
13 changes: 11 additions & 2 deletions frontend_build/SelinimumManifestPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,18 @@ class SelinimumManifestPlugin {
compiler.plugin('emit', (compilation, done) => {
const stats = compilation.getStats().toJson({chunkModules: true})
const entrypointsByBundle = this.getEntrypointsByModule(stats)

const outputName = 'selinimum-manifest.json'
const output = JSON.stringify(entrypointsByBundle)

compilation.assets[outputName] = {
source: () => output,
size: () => output.length
}

mkdirp.sync(compiler.options.output.path)
const manifestPath = path.join(compiler.options.output.path, 'selinimum-manifest.json')
fs.writeFileSync(manifestPath, JSON.stringify(entrypointsByBundle))
const manifestPath = path.join(compiler.options.output.path, outputName)
fs.writeFileSync(manifestPath, output)
done()
})
}
Expand Down
Loading

0 comments on commit 4944bcc

Please sign in to comment.