Update client deps: react 16.3, babel 7

This commit is contained in:
Ken-Håvard Lieng 2018-04-05 21:13:32 +02:00
parent 1ae7d867a9
commit 0cbbc1b8ff
46 changed files with 1125 additions and 808 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,34 +1,26 @@
{ {
"presets": [ "presets": [
["es2015", { "modules": false, "loose": true }], ["@babel/preset-env", {
"react", "modules": false,
"stage-0" "loose": true,
], "targets": {
"plugins": [ "browsers": ["ie 11"]
["module-resolver", {
"root": ["./src/js"],
"alias": {
"components": "./src/js/components",
"containers": "./src/js/containers",
"state": "./src/js/state",
"util": "./src/js/util"
} }
}] }],
"@babel/preset-react",
"@babel/preset-stage-0"
], ],
"env": { "env": {
"development": { "development": {
"plugins": ["react-hot-loader/babel"] "plugins": ["react-hot-loader/babel"]
}, },
"test": { "test": {
"plugins": [ "plugins": ["@babel/plugin-transform-modules-commonjs"]
"rewire",
"transform-es2015-modules-commonjs"
]
}, },
"production": { "production": {
"plugins": [ "plugins": [
"transform-react-inline-elements", "@babel/plugin-transform-react-inline-elements",
"transform-react-constant-elements" "@babel/plugin-transform-react-constant-elements"
] ]
} }
} }

View File

@ -24,12 +24,11 @@
"react/prefer-stateless-function": 0, "react/prefer-stateless-function": 0,
"react/prop-types": 0 "react/prop-types": 0
}, },
"globals": {
"DEV": true
},
"settings": { "settings": {
"import/resolver": { "import/resolver": {
"babel-module": {} "webpack": {
"config": "webpack.config.prod.js"
}
} }
} }
} }

View File

@ -7,7 +7,6 @@ var gutil = require('gulp-util');
var nano = require('gulp-cssnano'); var nano = require('gulp-cssnano');
var autoprefixer = require('gulp-autoprefixer'); var autoprefixer = require('gulp-autoprefixer');
var concat = require('gulp-concat'); var concat = require('gulp-concat');
var cache = require('gulp-cached');
var express = require('express'); var express = require('express');
var proxy = require('express-http-proxy'); var proxy = require('express-http-proxy');
var webpack = require('webpack'); var webpack = require('webpack');
@ -33,7 +32,9 @@ function brotli(opts) {
gulp.task('css', function() { gulp.task('css', function() {
return gulp.src(['src/css/fonts.css', 'src/css/fontello.css', 'src/css/style.css']) return gulp.src(['src/css/fonts.css', 'src/css/fontello.css', 'src/css/style.css'])
.pipe(concat('bundle.css')) .pipe(concat('bundle.css'))
.pipe(autoprefixer()) .pipe(autoprefixer({
browsers: ['ie 11']
}))
.pipe(nano()) .pipe(nano())
.pipe(gulp.dest('dist')); .pipe(gulp.dest('dist'));
}); });

View File

@ -5,22 +5,21 @@
"license": "MIT", "license": "MIT",
"main": "index.js", "main": "index.js",
"devDependencies": { "devDependencies": {
"babel-core": "^6.23.1", "@babel/core": "^7.0.0-beta.44",
"@babel/plugin-transform-react-constant-elements": "^7.0.0-beta.44",
"@babel/plugin-transform-react-inline-elements": "^7.0.0-beta.44",
"@babel/preset-env": "^7.0.0-beta.44",
"@babel/preset-react": "^7.0.0-beta.44",
"@babel/preset-stage-0": "^7.0.0-beta.44",
"babel-core": "^7.0.0-0",
"babel-eslint": "^8.2.2", "babel-eslint": "^8.2.2",
"babel-jest": "^22.4.3", "babel-jest": "^22.4.3",
"babel-loader": "^7.0.0", "babel-loader": "^8.0.0-beta",
"babel-plugin-module-resolver": "^3.1.1",
"babel-plugin-rewire": "^1.1.0",
"babel-plugin-transform-react-constant-elements": "^6.23.0",
"babel-plugin-transform-react-inline-elements": "^6.22.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.23.0",
"babel-preset-stage-0": "^6.22.0",
"brotli": "^1.3.1", "brotli": "^1.3.1",
"css-loader": "^0.28.0", "css-loader": "^0.28.0",
"eslint": "^4.19.1", "eslint": "^4.19.1",
"eslint-config-airbnb": "^16.1.0", "eslint-config-airbnb": "^16.1.0",
"eslint-import-resolver-babel-module": "^4.0.0", "eslint-import-resolver-webpack": "^0.9.0",
"eslint-loader": "^2.0.0", "eslint-loader": "^2.0.0",
"eslint-plugin-import": "^2.2.0", "eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-jsx-a11y": "^6.0.3",
@ -29,7 +28,6 @@
"express-http-proxy": "^1.0.1", "express-http-proxy": "^1.0.1",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-autoprefixer": "^5.0.0", "gulp-autoprefixer": "^5.0.0",
"gulp-cached": "^1.1.1",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
"gulp-cssnano": "^2.1.2", "gulp-cssnano": "^2.1.2",
"gulp-util": "^3.0.8", "gulp-util": "^3.0.8",
@ -63,5 +61,13 @@
"test": "jest", "test": "jest",
"test:verbose": "jest --verbose", "test:verbose": "jest --verbose",
"test:watch": "jest --watch" "test:watch": "jest --watch"
},
"jest": {
"moduleNameMapper": {
"^components(.*)$": "<rootDir>/src/js/components$1",
"^containers(.*)$": "<rootDir>/src/js/containers$1",
"^state(.*)$": "<rootDir>/src/js/state$1",
"^utils(.*)$": "<rootDir>/src/js/utils$1"
}
} }
} }

View File

@ -3,7 +3,7 @@ import { join, part, invite, kick, setTopic } from 'state/channels';
import { sendMessage, raw } from 'state/messages'; import { sendMessage, raw } from 'state/messages';
import { setNick, disconnect, whois, away } from 'state/servers'; import { setNick, disconnect, whois, away } from 'state/servers';
import { select } from 'state/tab'; import { select } from 'state/tab';
import { find } from 'util'; import { find } from 'utils';
import createCommandMiddleware, { beforeHandler, notFoundHandler } from './middleware/command'; import createCommandMiddleware, { beforeHandler, notFoundHandler } from './middleware/command';
const help = [ const help = [

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { hot } from 'react-hot-loader';
import App from 'containers/App'; import App from 'containers/App';
const Root = ({ store }) => ( const Root = ({ store }) => (
@ -8,4 +9,4 @@ const Root = ({ store }) => (
</Provider> </Provider>
); );
export default Root; export default hot(module)(Root);

View File

@ -3,7 +3,7 @@ import { List } from 'immutable';
import Navicon from 'containers/Navicon'; import Navicon from 'containers/Navicon';
import Editable from 'components/ui/Editable'; import Editable from 'components/ui/Editable';
import { isValidServerName } from 'state/servers'; import { isValidServerName } from 'state/servers';
import { linkify } from 'util'; import { linkify } from 'utils';
export default class ChatTitle extends PureComponent { export default class ChatTitle extends PureComponent {
render() { render() {

View File

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import { List } from 'react-virtualized/dist/commonjs/List'; import { List } from 'react-virtualized/dist/commonjs/List';
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'; import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { getScrollPos, saveScrollPos } from 'util/scrollPosition'; import { getScrollPos, saveScrollPos } from 'utils/scrollPosition';
import Message from './Message'; import Message from './Message';
const fetchThreshold = 600; const fetchThreshold = 600;

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { timestamp, linkify } from 'util'; import { timestamp, linkify } from 'utils';
export default class SearchResult extends PureComponent { export default class SearchResult extends PureComponent {
render() { render() {

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { stringWidth } from 'util'; import { stringWidth } from 'utils';
export default class Editable extends PureComponent { export default class Editable extends PureComponent {
static defaultProps = { static defaultProps = {

View File

@ -7,7 +7,7 @@ import { getSortedPrivateChats } from 'state/privateChats';
import { getServers } from 'state/servers'; import { getServers } from 'state/servers';
import { getSelectedTab, select } from 'state/tab'; import { getSelectedTab, select } from 'state/tab';
import { getShowTabList, hideMenu } from 'state/ui'; import { getShowTabList, hideMenu } from 'state/ui';
import { push } from 'util/router'; import { push } from 'utils/router';
const mapState = createStructuredSelector({ const mapState = createStructuredSelector({
channels: getSortedChannels, channels: getSortedChannels,

View File

@ -1,31 +1,20 @@
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import 'react-virtualized/styles.css'; import 'react-virtualized/styles.css';
import Root from 'components/Root'; import Root from 'components/Root';
import initRouter from 'util/router'; import initRouter from 'utils/router';
import Socket from 'util/Socket'; import Socket from 'utils/Socket';
import configureStore from './store'; import configureStore from './store';
import routes from './routes'; import routes from './routes';
import runModules from './modules'; import runModules from './modules';
const host = DEV ? `${window.location.hostname}:1337` : window.location.host; const production = process.env.NODE_ENV === 'production';
const host = production ? window.location.host : `${window.location.hostname}:1337`;
const socket = new Socket(host); const socket = new Socket(host);
const store = configureStore(socket); const store = configureStore(socket);
initRouter(routes, store); initRouter(routes, store);
runModules({ store, socket }); runModules({ store, socket });
const renderRoot = () => render( render(<Root store={store} />, document.getElementById('root'));
<AppContainer>
<Root store={store} />
</AppContainer>,
document.getElementById('root')
);
renderRoot();
if (module.hot) {
module.hot.accept('./components/Root', () => renderRoot());
}

View File

@ -1,5 +1,5 @@
import { addMessages, inform, print } from 'state/messages'; import { addMessages, inform, print } from 'state/messages';
import { isChannel } from 'util'; import { isChannel } from 'utils';
export const beforeHandler = '_before'; export const beforeHandler = '_before';
export const notFoundHandler = 'commandNotFound'; export const notFoundHandler = 'commandNotFound';

View File

@ -1,7 +1,7 @@
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import { getRouter } from 'state'; import { getRouter } from 'state';
import { getCurrentServerName } from 'state/servers'; import { getCurrentServerName } from 'state/servers';
import { observe } from 'util/observe'; import { observe } from 'utils/observe';
export default function documentTitle({ store }) { export default function documentTitle({ store }) {
observe(store, [getRouter, getCurrentServerName], (router, serverName) => { observe(store, [getRouter, getCurrentServerName], (router, serverName) => {

View File

@ -1,6 +1,6 @@
import FontFaceObserver from 'fontfaceobserver'; import FontFaceObserver from 'fontfaceobserver';
import { setCharWidth } from 'state/app'; import { setCharWidth } from 'state/app';
import { stringWidth } from 'util'; import { stringWidth } from 'utils';
export default function fonts({ store }) { export default function fonts({ store }) {
let { charWidth } = localStorage; let { charWidth } = localStorage;

View File

@ -3,9 +3,9 @@ import { socket as socketActions } from 'state/actions';
import { getWrapWidth, setConnectDefaults } from 'state/app'; import { getWrapWidth, setConnectDefaults } from 'state/app';
import { addMessages } from 'state/messages'; import { addMessages } from 'state/messages';
import { select, updateSelection } from 'state/tab'; import { select, updateSelection } from 'state/tab';
import { find } from 'util'; import { find } from 'utils';
import { when } from 'util/observe'; import { when } from 'utils/observe';
import { replace } from 'util/router'; import { replace } from 'utils/router';
export default function initialState({ store }) { export default function initialState({ store }) {
const env = JSON.parse(document.getElementById('env').innerHTML); const env = JSON.parse(document.getElementById('env').innerHTML);

View File

@ -3,7 +3,7 @@ import { setConnected } from 'state/app';
import { broadcast, inform, print, addMessage, addMessages } from 'state/messages'; import { broadcast, inform, print, addMessage, addMessages } from 'state/messages';
import { reconnect } from 'state/servers'; import { reconnect } from 'state/servers';
import { select } from 'state/tab'; import { select } from 'state/tab';
import { normalizeChannel } from 'util'; import { normalizeChannel } from 'utils';
function withReason(message, reason) { function withReason(message, reason) {
return message + (reason ? ` (${reason})` : ''); return message + (reason ? ` (${reason})` : '');

View File

@ -1,7 +1,7 @@
import Cookie from 'js-cookie'; import Cookie from 'js-cookie';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { getSelectedTab } from 'state/tab'; import { getSelectedTab } from 'state/tab';
import { observe } from 'util/observe'; import { observe } from 'utils/observe';
const saveTab = debounce( const saveTab = debounce(
tab => Cookie.set('tab', tab.toString(), { expires: 30 }), tab => Cookie.set('tab', tab.toString(), { expires: 30 }),

View File

@ -1,7 +1,7 @@
import { getCharWidth } from 'state/app'; import { getCharWidth } from 'state/app';
import { updateMessageHeight } from 'state/messages'; import { updateMessageHeight } from 'state/messages';
import { when } from 'util/observe'; import { when } from 'utils/observe';
import { measureScrollBarWidth } from 'util'; import { measureScrollBarWidth } from 'utils';
const menuWidth = 200; const menuWidth = 200;
const messagePadding = 30; const messagePadding = 30;

View File

@ -1,5 +1,5 @@
import Immutable from 'immutable'; import Immutable from 'immutable';
import reducer from '../channels'; import reducer, { compareUsers } from '../channels';
import { connect } from '../servers'; import { connect } from '../servers';
import * as actions from '../actions'; import * as actions from '../actions';
@ -294,7 +294,6 @@ function socket_mode(server, channel, user, add, remove) {
describe('compareUsers()', () => { describe('compareUsers()', () => {
it('compares users correctly', () => { it('compares users correctly', () => {
const compareUsers = reducer.__get__('compareUsers');
expect([ expect([
{ renderName: 'user5' }, { renderName: 'user5' },
{ renderName: '@user2' }, { renderName: '@user2' },

View File

@ -1,5 +1,5 @@
import { Map, fromJS } from 'immutable'; import { Map, fromJS } from 'immutable';
import reducer, { broadcast } from '../messages'; import reducer, { broadcast, getMessageTab } from '../messages';
import * as actions from '../actions'; import * as actions from '../actions';
import appReducer from '../app'; import appReducer from '../app';
@ -178,8 +178,6 @@ describe('message reducer', () => {
}); });
describe('getMessageTab()', () => { describe('getMessageTab()', () => {
const getMessageTab = reducer.__get__('getMessageTab');
it('returns the correct tab', () => { it('returns the correct tab', () => {
const srv = 'chat.freenode.net'; const srv = 'chat.freenode.net';
[ [

View File

@ -1,6 +1,6 @@
import reducer, { setSelectedTab } from '../tab'; import reducer, { setSelectedTab } from '../tab';
import * as actions from '../actions'; import * as actions from '../actions';
import { locationChanged } from '../../util/router'; import { locationChanged } from 'utils/router';
describe('tab reducer', () => { describe('tab reducer', () => {
it('selects the tab and adds it to history', () => { it('selects the tab and adds it to history', () => {

View File

@ -1,5 +1,5 @@
import { Record } from 'immutable'; import { Record } from 'immutable';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import * as actions from './actions'; import * as actions from './actions';
export const getApp = state => state.app; export const getApp = state => state.app;

View File

@ -1,6 +1,6 @@
import { Map, List, Record } from 'immutable'; import { Map, List, Record } from 'immutable';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import { getSelectedTab, updateSelection } from './tab'; import { getSelectedTab, updateSelection } from './tab';
import * as actions from './actions'; import * as actions from './actions';
@ -52,7 +52,7 @@ function loadUser(nick) {
return createUser(nick); return createUser(nick);
} }
function compareUsers(a, b) { export function compareUsers(a, b) {
a = a.renderName.toLowerCase(); a = a.renderName.toLowerCase();
b = b.renderName.toLowerCase(); b = b.renderName.toLowerCase();

View File

@ -1,5 +1,5 @@
import { List, Record } from 'immutable'; import { List, Record } from 'immutable';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import * as actions from './actions'; import * as actions from './actions';
const HISTORY_MAX_LENGTH = 128; const HISTORY_MAX_LENGTH = 128;

View File

@ -1,7 +1,7 @@
import { List, Map, Record } from 'immutable'; import { List, Map, Record } from 'immutable';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { findBreakpoints, messageHeight, linkify, timestamp, isChannel } from 'util'; import { findBreakpoints, messageHeight, linkify, timestamp, isChannel } from 'utils';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import { getApp } from './app'; import { getApp } from './app';
import { getSelectedTab } from './tab'; import { getSelectedTab } from './tab';
import * as actions from './actions'; import * as actions from './actions';
@ -117,7 +117,7 @@ function initMessage(message, tab, state) {
return message; return message;
} }
function getMessageTab(server, to) { export function getMessageTab(server, to) {
if (!to || to === '*' || (!isChannel(to) && to.indexOf('.') !== -1)) { if (!to || to === '*' || (!isChannel(to) && to.indexOf('.') !== -1)) {
return server; return server;
} }

View File

@ -1,6 +1,6 @@
import { Set, Map } from 'immutable'; import { Set, Map } from 'immutable';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import { updateSelection } from './tab'; import { updateSelection } from './tab';
import * as actions from './actions'; import * as actions from './actions';

View File

@ -1,5 +1,5 @@
import { List, Record } from 'immutable'; import { List, Record } from 'immutable';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import * as actions from './actions'; import * as actions from './actions';
const State = Record({ const State = Record({

View File

@ -1,6 +1,6 @@
import { Map, Record } from 'immutable'; import { Map, Record } from 'immutable';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import { getSelectedTab, updateSelection } from './tab'; import { getSelectedTab, updateSelection } from './tab';
import * as actions from './actions'; import * as actions from './actions';

View File

@ -1,6 +1,6 @@
import { Map } from 'immutable'; import { Map } from 'immutable';
import base64 from 'base64-arraybuffer'; import base64 from 'base64-arraybuffer';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import * as actions from './actions'; import * as actions from './actions';
export const getSettings = state => state.settings; export const getSettings = state => state.settings;

View File

@ -1,6 +1,6 @@
import { Record, List } from 'immutable'; import { Record, List } from 'immutable';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import { push, replace, LOCATION_CHANGED } from 'util/router'; import { push, replace, LOCATION_CHANGED } from 'utils/router';
import * as actions from './actions'; import * as actions from './actions';
const TabRecord = Record({ const TabRecord = Record({

View File

@ -1,6 +1,6 @@
import { Record } from 'immutable'; import { Record } from 'immutable';
import createReducer from 'util/createReducer'; import createReducer from 'utils/createReducer';
import { LOCATION_CHANGED } from 'util/router'; import { LOCATION_CHANGED } from 'utils/router';
import * as actions from './actions'; import * as actions from './actions';
const State = Record({ const State = Record({

View File

@ -1,7 +1,7 @@
import { createStore, applyMiddleware, compose } from 'redux'; import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import createReducer from 'state'; import createReducer from 'state';
import { routeReducer, routeMiddleware } from 'util/router'; import { routeReducer, routeMiddleware } from 'utils/router';
import message from './middleware/message'; import message from './middleware/message';
import createSocketMiddleware from './middleware/socket'; import createSocketMiddleware from './middleware/socket';
import commands from './commands'; import commands from './commands';

View File

@ -1,7 +1,7 @@
import padStart from 'lodash/padStart'; import padStart from 'lodash/padStart';
export { findBreakpoints, messageHeight } from './messageHeight'; export { findBreakpoints, messageHeight } from './messageHeight';
export linkify from './linkify'; export { default as linkify } from './linkify';
export function normalizeChannel(channel) { export function normalizeChannel(channel) {
if (channel.indexOf('#') !== 0) { if (channel.indexOf('#') !== 0) {

View File

@ -1,9 +1,9 @@
var path = require('path');
var webpack = require('webpack'); var webpack = require('webpack');
module.exports = { module.exports = {
mode: 'development', mode: 'development',
entry: [ entry: [
'react-hot-loader/patch',
'webpack-hot-middleware/client', 'webpack-hot-middleware/client',
'./src/js/index' './src/js/index'
], ],
@ -11,6 +11,14 @@ module.exports = {
filename: 'bundle.js', filename: 'bundle.js',
publicPath: '/' publicPath: '/'
}, },
resolve: {
alias: {
components: path.resolve(__dirname, 'src/js/components'),
containers: path.resolve(__dirname, 'src/js/containers'),
state: path.resolve(__dirname, 'src/js/state'),
utils: path.resolve(__dirname, 'src/js/utils')
}
},
module: { module: {
rules: [ rules: [
{ test: /\.js$/, loader: 'eslint-loader', exclude: /node_modules/, enforce: 'pre' }, { test: /\.js$/, loader: 'eslint-loader', exclude: /node_modules/, enforce: 'pre' },
@ -19,9 +27,6 @@ module.exports = {
] ]
}, },
plugins: [ plugins: [
new webpack.DefinePlugin({
DEV: true
}),
new webpack.HotModuleReplacementPlugin() new webpack.HotModuleReplacementPlugin()
] ]
}; };

View File

@ -1,3 +1,4 @@
var path = require('path');
var webpack = require('webpack'); var webpack = require('webpack');
module.exports = { module.exports = {
@ -8,16 +9,19 @@ module.exports = {
output: { output: {
filename: 'bundle.js' filename: 'bundle.js'
}, },
resolve: {
alias: {
components: path.resolve(__dirname, 'src/js/components'),
containers: path.resolve(__dirname, 'src/js/containers'),
state: path.resolve(__dirname, 'src/js/state'),
utils: path.resolve(__dirname, 'src/js/utils')
}
},
module: { module: {
rules: [ rules: [
{ test: /\.js$/, loader: 'eslint-loader', exclude: /node_modules/, enforce: 'pre' }, { test: /\.js$/, loader: 'eslint-loader', exclude: /node_modules/, enforce: 'pre' },
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
{ test: /\.css$/, loader: 'style-loader!css-loader' } { test: /\.css$/, loader: 'style-loader!css-loader' }
] ]
}, }
plugins: [
new webpack.DefinePlugin({
DEV: false
})
]
}; };

File diff suppressed because it is too large Load Diff