Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async JavaScript Lazy Loading #899

Merged
merged 70 commits into from
Oct 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
6fbaa15
update props waits for lazy components to be ready
Aug 30, 2019
d4b56ab
lint
Aug 30, 2019
937bc30
format
Aug 30, 2019
81408b9
Merge remote-tracking branch 'origin/dev' into exp-dynamic
Sep 17, 2019
3c1620d
usage
Sep 17, 2019
d364090
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Sep 20, 2019
42f6580
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Sep 24, 2019
93cfc24
(wip) Strict 'isAppReady'
Sep 24, 2019
51b4ddf
Merge branch 'exp-dynamic' of github.com:plotly/dash into exp-dynamic
Sep 24, 2019
a70f3c2
format
Sep 24, 2019
72fe8c2
handle app ready state in store
Sep 24, 2019
3077b80
clean up
Sep 24, 2019
917f536
:loud_sound: add logs for resource serving
Sep 25, 2019
231765e
compt to py2.7
Sep 25, 2019
a387525
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Sep 26, 2019
df362a1
[WIP] Dash support for `async` flag on js_dist
Sep 26, 2019
8ee541a
lint
Sep 26, 2019
10bde03
cheat the lint...
Sep 26, 2019
96cf8f5
lint
Sep 26, 2019
659496f
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Sep 27, 2019
4e787b6
lint
Sep 27, 2019
cd1d1dc
lint
Sep 27, 2019
9429047
lint
Sep 27, 2019
cd69f6b
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Sep 27, 2019
ba03895
lazy by default, force makes eager, fix logic
Sep 27, 2019
35ba28a
lint
Sep 27, 2019
953f65b
clean up
Sep 27, 2019
610ff28
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Oct 1, 2019
b4e105a
- update app readiness on prop update
Oct 1, 2019
9d24848
Merge branch 'exp-dynamic' of github.com:plotly/dash into exp-dynamic
Oct 1, 2019
f32edbd
- share isSimpleComponent
Oct 1, 2019
91771d1
- format and redudant bool
Oct 1, 2019
58034cd
lint
Oct 1, 2019
5478f83
black / lint
Oct 1, 2019
9d95615
lint
Oct 1, 2019
2f303af
revert
Oct 1, 2019
ee608a0
revert
Oct 1, 2019
ab5801f
lint
Oct 1, 2019
be2eb1d
lint
Oct 1, 2019
c5a1636
lint (dash-renderer)
Oct 1, 2019
0a18d3c
- make `isComponentReady` part of dash-renderer
Oct 2, 2019
24712eb
standalone isComponentReady
Oct 2, 2019
37e4c9e
add project webpack-dash-dynamic-plugin
Oct 2, 2019
4daa8eb
typo
Oct 2, 2019
2c87000
lint
Oct 2, 2019
deec3d6
umd library target
Oct 2, 2019
42b62f4
changelog
Oct 2, 2019
766dad2
umd externals
Oct 2, 2019
9b06789
structure
Oct 3, 2019
99e437c
new packages
Oct 3, 2019
2650fa9
update @plotly utility packages
Oct 3, 2019
2ea2d48
update usage
Oct 3, 2019
1ce9792
changelog
Oct 3, 2019
3c3c81c
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Oct 3, 2019
af35006
update @plotly/dash-component-plugins
Oct 4, 2019
247bb64
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Oct 10, 2019
3e13e82
unit test async resources
Oct 10, 2019
884c74a
Merge branch 'exp-dynamic' of github.com:plotly/dash into exp-dynamic
Oct 10, 2019
a598dae
black
Oct 10, 2019
8d6c137
is
Oct 10, 2019
0a6e4c6
rename
Oct 10, 2019
8b864cb
e2e eager/lazy tests
Oct 10, 2019
92a5699
dcc exp-dynamic-2
Oct 11, 2019
558bf6c
cleanup
Oct 11, 2019
13e274e
use table instead of graph for devtools tests
Oct 11, 2019
1a7a470
use async table branch
Oct 11, 2019
7c3edaa
add tests for notifyObserver
Oct 11, 2019
7852312
update app ready state only for set_layout and update_props with chil…
Oct 15, 2019
063185e
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Oct 17, 2019
aa79b2f
Merge branch 'dev' into exp-dynamic
Marc-Andre-Rivet Oct 18, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ jobs:
command: |
. venv/bin/activate && pip install --no-cache-dir --upgrade -e . --progress-bar off && mkdir packages
cd dash-renderer && renderer build && python setup.py sdist && mv dist/* ../packages/ && cd ..
git clone --depth 1 https://github.com/plotly/dash-core-components.git
git clone --depth 1 -b exp-dynamic-2 https://github.com/plotly/dash-core-components.git
cd dash-core-components && npm install --ignore-scripts && npm run build && python setup.py sdist && mv dist/* ../packages/ && cd ..
git clone --depth 1 https://github.com/plotly/dash-renderer-test-components
cd dash-renderer-test-components && npm install --ignore-scripts && npm run build:all && python setup.py sdist && mv dist/* ../packages/ && cd ..
Expand Down Expand Up @@ -159,7 +159,7 @@ jobs:
name: ️️🏗️ build misc
command: |
. venv/bin/activate && pip install --no-cache-dir --upgrade -e . --progress-bar off && mkdir packages
git clone --depth 1 https://github.com/plotly/dash-table.git
git clone --depth 1 -b exp-dynamic https://github.com/plotly/dash-table.git
cd dash-table && npm install --ignore-scripts && npm run build && python setup.py sdist && mv dist/* ../packages/ && cd ..
git clone --depth 1 https://github.com/plotly/dash-html-components.git
cd dash-html-components && npm install --ignore-scripts && npm run build && python setup.py sdist && mv dist/* ../packages/ && cd ..
Expand Down
21 changes: 21 additions & 0 deletions @plotly/dash-component-plugins/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 Plotly

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
16 changes: 16 additions & 0 deletions @plotly/dash-component-plugins/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@plotly/dash-component-plugins",
"version": "1.0.1",
"description": "Plugins for Dash Components",
"repository": {
"type": "git",
"url": "git@github.com:plotly/dash.git"
},
"bugs": {
"url": "https://github.com/plotly/dash/issues"
},
"homepage": "https://github.com/plotly/dash",
"main": "src/index.js",
"author": "Marc-André Rivet",
"license": "MIT"
}
31 changes: 31 additions & 0 deletions @plotly/dash-component-plugins/src/asyncImport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { lazy } from 'react';

export const asyncDecorator = (target, promise) => {
let resolve;
const isReady = new Promise(r => {
resolve = r;
});

const state = {
isReady,
get: lazy(() => {
return Promise.resolve(promise()).then(res => {
setTimeout(async () => {
await resolve(true);
state.isReady = true;
}, 0);

return res;
});
}),
};

Object.defineProperty(target, '_dashprivate_isLazyComponentReady', {
get: () => state.isReady,
});

return state.get;
};

export const isReady = target => target &&
target._dashprivate_isLazyComponentReady;
3 changes: 3 additions & 0 deletions @plotly/dash-component-plugins/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { asyncDecorator, isReady } from './dynamicImport';

export { asyncDecorator, isReady };
21 changes: 21 additions & 0 deletions @plotly/webpack-dash-dynamic-import/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 Plotly

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
16 changes: 16 additions & 0 deletions @plotly/webpack-dash-dynamic-import/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@plotly/webpack-dash-dynamic-import",
"version": "1.0.0",
"description": "Webpack Plugin for Dynamic Import in Dash",
"repository": {
"type": "git",
"url": "git@github.com:plotly/dash.git"
},
"bugs": {
"url": "https://github.com/plotly/dash/issues"
},
"homepage": "https://github.com/plotly/dash",
"main": "src/index.js",
"author": "Marc-André Rivet",
"license": "MIT"
}
33 changes: 33 additions & 0 deletions @plotly/webpack-dash-dynamic-import/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const resolveImportSource = `\
Object.defineProperty(__webpack_require__, 'p', {
get: (function () {
let script = document.currentScript;
if (!script) {
/* Shim for IE11 and below */
/* Do not take into account async scripts and inline scripts */
const scripts = Array.from(document.getElementsByTagName('script')).filter(function(s) { return !s.async && !s.text && !s.textContent; });
script = scripts.slice(-1)[0];
}

var url = script.src.split('/').slice(0, -1).join('/') + '/';

return function() {
return url;
};
})()
});`

class WebpackDashDynamicImport {
apply(compiler) {
compiler.hooks.compilation.tap('WebpackDashDynamicImport', compilation => {
compilation.mainTemplate.hooks.requireExtensions.tap('WebpackDashDynamicImport > RequireExtensions', (source, chunk, hash) => {
return [
source,
resolveImportSource
]
});
});
}
}

module.exports = WebpackDashDynamicImport;
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ updates in clientside functions.
- Three new `dash.testing` methods: `clear_local_storage`, `clear_session_storage`, and `clear_storage` (to clear both together)
- [#937](https://github.com/plotly/dash/pull/937) `dash.testing` adds two APIs `zoom_in_graph_by_ratio` and `click_at_coord_fractions` about advanced interactions using mouse `ActionChain`
- [#938](https://github.com/plotly/dash/issues/938) Add debugging traces to dash backend about serving component suites, to verify the installed packages whenever in doubt.
- [#899](https://github.com/plotly/dash/pull/899) Add support for async dependencies and components

### Fixed
- [#944](https://github.com/plotly/dash/pull/944) Fix a bug with persistence being toggled on/off on an existing component.
Expand Down
6 changes: 0 additions & 6 deletions dash-renderer/.babelrc

This file was deleted.

13 changes: 13 additions & 0 deletions dash-renderer/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = {
presets: [['@babel/preset-env', {
useBuiltIns: 'usage',
corejs: 3
}], '@babel/preset-react'],
env: {
test: {
plugins: [
'@babel/plugin-transform-modules-commonjs'
]
}
}
};
6 changes: 3 additions & 3 deletions dash-renderer/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ module.exports = {
// transform: null,

// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/"
// ],
transformIgnorePatterns: [
"/node_modules/(?!@plotly).+\\.js"
],

// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
Expand Down
6 changes: 6 additions & 0 deletions dash-renderer/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion dash-renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@
},
"devDependencies": {
"@babel/core": "^7.6.0",
"@babel/plugin-transform-modules-commonjs": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0",
"@plotly/dash-component-plugins": "^1.0.1",
"@svgr/webpack": "^4.1.0",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
Expand All @@ -57,9 +59,9 @@
"prettier-stylelint": "^0.4.2",
"raw-loader": "^3.1.0",
"style-loader": "^1.0.0",
"webpack-dev-server": "^3.1.11",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.8",
"webpack-dev-server": "^3.1.11",
"webpack-serve": "^3.1.1",
"whatwg-fetch": "^2.0.2"
}
Expand Down
2 changes: 2 additions & 0 deletions dash-renderer/src/APIController.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
computePaths,
hydrateInitialOutputs,
setLayout,
setAppIsReady,
} from './actions/index';
import {applyPersistence} from './persistence';
import apiThunk from './actions/api';
Expand Down Expand Up @@ -54,6 +55,7 @@ class UnconnectedContainer extends Component {
dispatch
);
dispatch(setLayout(finalLayout));
dispatch(setAppIsReady());
Marc-Andre-Rivet marked this conversation as resolved.
Show resolved Hide resolved
} else if (isNil(paths)) {
dispatch(computePaths({subTree: layout, startingPath: []}));
}
Expand Down
5 changes: 1 addition & 4 deletions dash-renderer/src/TreeContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,11 @@ import {
type,
} from 'ramda';
import {notifyObservers, updateProps} from './actions';
import isSimpleComponent from './isSimpleComponent';
import {recordUiEdit} from './persistence';
import ComponentErrorBoundary from './components/error/ComponentErrorBoundary.react';
import checkPropTypes from 'check-prop-types';

const SIMPLE_COMPONENT_TYPES = ['String', 'Number', 'Null', 'Boolean'];
const isSimpleComponent = component =>
includes(type(component), SIMPLE_COMPONENT_TYPES);

function validateComponent(componentDefinition) {
if (type(componentDefinition) === 'Array') {
throw new Error(
Expand Down
1 change: 1 addition & 0 deletions dash-renderer/src/actions/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const actionList = {
SET_CONFIG: 'SET_CONFIG',
ON_ERROR: 'ON_ERROR',
SET_HOOKS: 'SET_HOOKS',
SET_APP_READY: 'SET_APP_READY',
};

export const getAction = action => {
Expand Down
20 changes: 15 additions & 5 deletions dash-renderer/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,20 @@ import cookie from 'cookie';
import {uid, urlBase, isMultiOutputProp, parseMultipleOutputs} from '../utils';
import {STATUS} from '../constants/constants';
import {applyPersistence, prunePersistence} from '../persistence';
import setAppIsReady from './setAppReadyState';

export const updateProps = createAction(getAction('ON_PROP_CHANGE'));
export const setRequestQueue = createAction(getAction('SET_REQUEST_QUEUE'));
export const computeGraphs = createAction(getAction('COMPUTE_GRAPHS'));
export const computePaths = createAction(getAction('COMPUTE_PATHS'));
export const setLayout = createAction(getAction('SET_LAYOUT'));
export const setAppLifecycle = createAction(getAction('SET_APP_LIFECYCLE'));
export const setConfig = createAction(getAction('SET_CONFIG'));
export const setHooks = createAction(getAction('SET_HOOKS'));
export const setLayout = createAction(getAction('SET_LAYOUT'));
export const onError = createAction(getAction('ON_ERROR'));

export {setAppIsReady};

export function hydrateInitialOutputs() {
return function(dispatch, getState) {
triggerDefaultState(dispatch, getState);
Expand Down Expand Up @@ -175,7 +178,7 @@ function reduceInputIds(nodeIds, InputGraph) {
/*
* Create input-output(s) pairs,
* sort by number of outputs,
* and remove redudant inputs (inputs that update the same output)
Marc-Andre-Rivet marked this conversation as resolved.
Show resolved Hide resolved
* and remove redundant inputs (inputs that update the same output)
*/
const inputOutputPairs = nodeIds.map(nodeId => ({
input: nodeId,
Expand All @@ -194,7 +197,7 @@ function reduceInputIds(nodeIds, InputGraph) {
* trigger components to update multiple times.
*
* For example, [A, B] => C and [A, D] => E
* The unique inputs might be [A, B, D] but that is redudant.
* The unique inputs might be [A, B, D] but that is redundant.
* We only need to update B and D or just A.
*
* In these cases, we'll supply an additional list of outputs
Expand All @@ -215,10 +218,15 @@ function reduceInputIds(nodeIds, InputGraph) {
}

export function notifyObservers(payload) {
return function(dispatch, getState) {
return async function(dispatch, getState) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

notifyObservers may now have to wait defensively before sending callbacks

const {id, props, excludedOutputs} = payload;

const {graphs, requestQueue} = getState();
const {graphs, isAppReady, requestQueue} = getState();

if (isAppReady !== true) {
await isAppReady;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If a known component is not ready, wait for it


const {InputGraph} = graphs;
/*
* Figure out all of the output id's that depend on this input.
Expand Down Expand Up @@ -942,6 +950,8 @@ function updateOutput(
);
});
}

dispatch(setAppIsReady());
alexcjohnson marked this conversation as resolved.
Show resolved Hide resolved
}
};
if (multi) {
Expand Down
Loading