Skip to content

Use webpack to build files and webpack-dev-server for testing#1068

Merged
k4b7 merged 24 commits intoKaTeX:masterfrom
ylemkimon:webpack
Jan 22, 2018
Merged

Use webpack to build files and webpack-dev-server for testing#1068
k4b7 merged 24 commits intoKaTeX:masterfrom
ylemkimon:webpack

Conversation

@ylemkimon
Copy link
Member

@ylemkimon ylemkimon commented Jan 16, 2018

Fixes #1063
Fixes partially #929 (webpack-dev-server reloads when katex.less is changed)
Fixes #692
Fixes #983

Old New
browserify(make *.js) webpack
browserify transform babelify webpack babel-loader
UglifyJS CLI(make *.min.js) webpack UglifyJsPlugin
Less CLI, copy .css(make *.css) webpack less-loader, css-loader, and ExtractTextPlugin
clean-css CLI(make *.min.css) cssnano included in webpack css-loader
fonts(make build/fonts) webpack file-loader
contrib(make contrib and Makefiles for each contrib) webpack entry points in webpack.common.js[1]
make build/katex.js build/katex.min.js build/katex.css build/katex.min.css build/fonts build/contrib Depends on make target webpack, which does npm run build(webpack)
make recompile webpack --watch(npm run watch)
Local testing using server.js(Removed in #902), webpack-dev-server(webpackDevServer.js) Updated webpack-dev-server configuration(renamed to webpack.dev.js), updated documentations
Build stylesheets before starting dev server(npm script prestart) Load(inline) stylesheets using webpack style-loader[2]
Screenshotter using server.js Use webpack-dev-server
Export CommonJS module Export ES6 module[3], expose default export

Possible Breaking Changes:

Ref Changes Impact Migration Path
[1] Removed make target contrib make contrib make build/contrib or npm run build(webpack)
[2] Removed npm script build-css npm run build-css make [.css filename] or npm run build(webpack)
[3] Switched to ES6 module export Directly requiring ./katex.js
(require('./katex.js))
Use default property
(require('./katex.js).default)

I'm not sure there are use cases of above cases.

Created a webpack entry point for KaTeX, which imports katex.less. As
flow[1] and jest[2] doesn't support CSS modules natively, a separate
entry point is used and it is not flowtyped.

[1] https://gist.github.com/lambdahands/d19e0da96285b749f0ef
[2] https://facebook.github.io/jest/docs/en/webpack.html
* Made webpack.config.js export valid webpack configuration
* Use:
browserify -> webpack
babelify -> babel-loader
UglifyJS CLI -> UglifyJsPlugin
Less CLI -> less-loader
cleancss -> cssnano in css-loader
build/fonts -> file-loader
* Inline CSS(Less) using style-loader and export them using
ExtractTextPlugin
* Add `watch` npm script calling `webpack --watch`
* Made webpackDevServer export a valid webpack configuration
* Compile Less and inline CSS using less-loader and style-loader
* Instead of copying files serve files from /static and use file-loader
* Remove old server.js and its dependencies
@khanbot
Copy link

khanbot commented Jan 16, 2018

CLA signature looks good 👍

@ylemkimon ylemkimon changed the title [WIP]Use webpack to build files and webpack-dev-server for testing Use webpack to build files and webpack-dev-server for testing Jan 16, 2018
// @flow
const { targets, createConfig } = require('./webpack.common');
const path = require('path');
const PORT = 7936;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any specific reason using the port 7936?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wanted to avoid a collision with other services that are usually running in the normal dev environment at Khan Academy.

$(BUILDDIR)/contrib/mathtex-script-type.min.js: $(BUILDDIR)/mathtex-script-type.js
$(UGLIFYJS) < $< > $@

$(BUILDDIR)/mathtex-script-type.js: mathtex-script-type.js
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is outputting mathtex-script-type.js to $(BUILDDIR), not $(BUILDDIR)/contrib, and not including in the distribution archive an intended behavior?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

$(UGLIFYJS) < $< > $@

$(BUILDDIR)/contrib/copy-tex.js: copy-tex.js
$(BROWSERIFY) -t [ babelify ] $< --standalone renderMathInElement > $@
Copy link
Member Author

@ylemkimon ylemkimon Jan 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is exporting the copy-tex module to renderMathInElement, a name also used by auto-render, an intended behavior?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this. I guess nobody's been using this as a standalone script.

Copy link
Member

@k4b7 k4b7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I've used webpack before, but haven't used it for building styles so I have a lot of questions about that.

use: [{
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just copies the files into the fonts folder?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never handled fonts like this before. If we didn't do this we'd need a separate way to serve font files when using the dev server correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinbarabash It loads included font files in the stylesheet(fonts.less) and outputs to fonts/.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this to get used, you'd have to import the font into the dependency tree, right? Is that being done?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rrandallcainc Yes. Current dependency tree is: katex.wepack.js imports static/katex.less, static/katex.less imports static/fonts.less, and fonts.less imports(using @font-face) font files.

name: string, // the name of output JS/CSS
entry: string, // the path to the entry point
library?: string // the name of the exported module
|};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using comments with flow to avoid having to transpile?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinbarabash Yes 😄

config.plugins.push(new UglifyJsPlugin({
uglifyOptions: {
output: {
ascii_only: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Makefile used --mangle --beautify ascii_only=true,beautify=false. Does UglifyJsPlugin support the same options?

Copy link
Member Author

@ylemkimon ylemkimon Jan 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinbarabash Yes. mangle is enabled by default and beautify=false was necessary in order to pass ascii_only=true to the CLI(mishoo/UglifyJS#54).

disable: dev,
}),
],
devtool: dev && 'inline-source-map',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool

const cssLoader = {
loader: 'css-loader',
options: {
minimize, // cssnano
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's cssnano?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinbarabash cssnano is a CSS minifier equivalent to clean-css and it is included in the css-loader, which can be enabled by passing minimize: true to its options.

Makefile Outdated
mkdir -p build
$(CLEANCSS) -o $@ $<
mkdir -p build/contrib
mkdir -p build/fonts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't webpack create the directories it needs if they don't exist?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinbarabash Yes, it does. Will remove them.

Makefile Outdated
@# Since everything in build/contrib and build/fonts is put in the built files,
@# make sure there's nothing in there we don't want.
rm -rf build/contrib/*
rm -rf build/fonts/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't if we should just do rm -rf build?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinbarabash I think preserving other files in build/ is no longer necessary as dev server doesn't generate or use files in build/. Will change to rm -rf build/*

(To run this example from a clone of the repository, run `npm start`
in the root KaTeX directory, and then visit
http://0.0.0.0:7936/contrib/auto-render/index.html
http://localhost:7936/contrib/auto-render/index.html
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the docs.

<meta charset="UTF-8">
<title>Auto-render test</title>
<script src="/katex.js" type="text/javascript"></script>
<link href="/katex.css" rel="stylesheet" type="text/css">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we still need katex.css?

<title>Copy-tex test</title>
<script src="/katex.js" type="text/javascript"></script>
<link href="/katex.css" rel="stylesheet" type="text/css">
<link href="./copy-tex.css" rel="stylesheet" type="text/css">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no .css files?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe webpack will now inject these with either a script tag (extract plugin) or directly into the JS.

@k4b7
Copy link
Member

k4b7 commented Jan 17, 2018

Fixes partially #929 (webpack-dev-server reloads when katex.less is changed)

Why is it a partial fix?

@k4b7
Copy link
Member

k4b7 commented Jan 17, 2018

I restarted the travis-ci build b/c I was really confused by the output of the previous run. It should be running npm test, but I couldn't find any of the usual output that I expect from that command. I ran npm test locally on your branch and it seems to work fine.

@ylemkimon
Copy link
Member Author

@kevinbarabash Currently Travis seems to be not working and backlogged.

@ylemkimon
Copy link
Member Author

ylemkimon commented Jan 17, 2018

@kevinbarabash

Why is it a partial fix?

As static/main.js is not handled by webpack, it's not watched and doesn't reload if changed.

/* eslint no-console:0 */
/* global katex */

import katex from "katex";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this result in the bundle that's built for auto-render.js also containing all of KaTeX?

@@ -1,24 +1,22 @@
/* global katex */
import katex from "katex";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern as in auto-render.js.

Copy link
Member Author

@ylemkimon ylemkimon Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinbarabash The module katex is declared as externals in the webpack configuration. As a result, webpack doesn't bundle the KaTeX, but tries to require the module by require("katex") in CJS, define(["katex"], ...) in AMD, and using global variable katex(old behavior) otherwise. Basically, it adds support for module loaders.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow. That's super cool!

Copy link
Member

@ry-randall ry-randall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really exciting to see, thanks for opening!

use: [{
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this to get used, you'd have to import the font into the dependency tree, right? Is that being done?

@@ -1,61 +1,7 @@
const path = require('path');
// @flow
const { targets, createConfig } = require('./webpack.common');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we want to rename this to webpack.prod.js?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rrandallcainc As webpack's default configuration filename is webpack.config.js, if webpack is called without any arguments, production build will be run.

<title>Copy-tex test</title>
<script src="/katex.js" type="text/javascript"></script>
<link href="/katex.css" rel="stylesheet" type="text/css">
<link href="./copy-tex.css" rel="stylesheet" type="text/css">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe webpack will now inject these with either a script tag (extract plugin) or directly into the JS.

// Now we need to find an IP the container can connect to.
// First, install a server component to get notified of successful connects
app.get("/ss-connect.js", function(req, res, next) {
devServer.app.get("/ss-connect.js", function(req, res, next) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

};

module.exports = {
export default {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a breaking change for CJS users?

Copy link
Member Author

@ylemkimon ylemkimon Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rrandallcainc For dist/katex.js, which is distribution file and entry point of katex module, as webpack exports an UMD module and default export is exposed, I think there'll be no breaking changes.

Copy link
Member Author

@ylemkimon ylemkimon Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(ii) katex.js: transpilers and/or bundlers uses interopDefault, which imports default export when importing ES6 module
I think there'll be no breaking changes.

@rrandallcainc I'm don't know there is a use case of directly requiring katex.js (from the root), e.g., const katex = require('./node_modules/katex/katex.js);, as it needs transpiling, but in that case, the user has to use .default, e.g., const katex = require('./node_modules/katex/katex.js).default;.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, but I still think katex.js serves as the entry point, right? Even though this will be wrapped in a UMD, I think it's going to be a breaking change when requiring the built artifact. This is due to ES6 modules always being named. When CJS users try and import Katex, they'll need to change their import from require('katex') to require('katex').default.

Perhaps I am missing something though.

Copy link
Member Author

@ylemkimon ylemkimon Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rrandallcainc Webpack has option libraryExport, which configures which module(s) will be exposed via UMD and it is set to default. So requiring katex module(require('katex')) will expose default export and work same.

@@ -1,106 +0,0 @@
/* eslint no-console:0 */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

"version": "0.10.0-pre",
"description": "Fast math typesetting for the web.",
"main": "dist/katex.js",
"browser": "dist/katex.min.js",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this reverted?

Copy link
Member Author

@ylemkimon ylemkimon Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinbarabash #1057 is no longer needed, since webpack doesn't use name require and browserify/browserify#374 is not an issue.

@ylemkimon
Copy link
Member Author

Updated PR description 😄

ylemkimon and others added 3 commits January 18, 2018 18:55
Do not transform modules to commonjs in Babel. However Jest doesn't not
support ES6 modules, so transfrom modules to commonjs when NODE_ENV is
`test`.
"env": {
"test": {
"plugins": [
"transform-es2015-modules-commonjs"
Copy link
Member Author

@ylemkimon ylemkimon Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for Jest, as it doesn't support ES6 modules.
In Babel 6, env configurations doesn't overrides configuration, rather are merged to the root configuration(babel/babel#5276).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you avoid adding this dependency by adding the .default to your imports instead?

Copy link
Member Author

@ylemkimon ylemkimon Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rrandallcainc Node doesn't support ES6 import/export module syntaxes, so I think adding .default won't help it. Also this plugin is part of babel-preset-es2015, so there won't be any additional dependencies.

@k4b7
Copy link
Member

k4b7 commented Jan 18, 2018

@ylemkimon thanks for updating the comment at the top. I'm pretty busy this week. I'll try to do another pass on the weekend.

Changed version range to include IE-compatible version
@@ -0,0 +1,121 @@
// @flow
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems it's possible to
(i) use ES6 in webpack.config.js by using the extension .babel.js, which transpiles the file before loading
(ii) put all configurations in one file by webpack.config.js exporting a function, which takes environment variables as an argument and returns the webpack configuration

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having the whole config in a single file and using environment variables to control things might be nice. I'm going land this as is though and any additional work could happen in a followup PR.

@k4b7
Copy link
Member

k4b7 commented Jan 22, 2018

@ylemkimon thanks for listing out the breaking changes. I think the only breaking change that might affect users would be the third one, but my guess would be that most users are using the compiled bundle so this shouldn't be an issue. We can add it to the breaking changes part of the release notes when we do a release.

From the "old/new" section:

Export CommonJS module | Export ES6 module[3], expose default export

This sounds scarier than it is b/c the bundle is built in such a way that it exposes a commonjs interface as well as a katex global as per the discussion in the comments. Could you update your comment to make it more explicit that the interface the published module exports remains unchanged?

Copy link
Member

@k4b7 k4b7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I'm really happy that we have a more consistent build system. Also, looking forward to being able to experiment with webpack plugins such as ModuleConcatenationPlugin.

@k4b7 k4b7 merged commit 5f32b71 into KaTeX:master Jan 22, 2018
@edemaine
Copy link
Member

edemaine commented Jan 22, 2018

Sounds great! Just checking: Does require('katex') still work on Node as it did? We'll definitely want to do another beta (not final) release with this new system.

@k4b7
Copy link
Member

k4b7 commented Jan 22, 2018

I did the following as a quick check:

  • npm run build
  • cd build
  • node
  • const katex = require('katex') // this returned the katex object as expected

@k4b7
Copy link
Member

k4b7 commented Jan 22, 2018

@davidflanagan just let me know that this change "shaved 40Kb off of katex.min.js!"

Copy link
Member

@ry-randall ry-randall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants