suddjian commented on a change in pull request #10288: URL: https://github.com/apache/incubator-superset/pull/10288#discussion_r544930426
########## File path: superset-frontend/src/components/DynamicPlugins/DynamicPluginProvider.tsx ########## @@ -0,0 +1,181 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useEffect, useReducer } from 'react'; +import { SupersetClient, JsonObject } from '@superset-ui/connection'; +import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; +import { + PluginContext, + PluginContextType, + dummyPluginContext, +} from './PluginContext'; + +// the plugin returned from the API +type Plugin = { + name: string; + key: string; + bundle_url: string; + id: number; +}; + +// TODO: Make this function an export of @superset-ui/chart or some such +async function defineSharedModule(name: string, promise: Promise<any>) { + // dependency management using global variables, because for the life of me + // I can't figure out how to hook into UMD from a dynamically imported package. + // Maybe someone else can help figure that out. + const loadingKey = `__superset__loading__/${name}`; + const packageKey = `__superset__/${name}`; + if (window[loadingKey]) { + await window[loadingKey]; + return window[packageKey]; + } + window[loadingKey] = promise; + const pkg = await promise; + window[packageKey] = pkg; + return pkg; +} + +async function defineSharedModules(moduleMap: { [key: string]: Promise<any> }) { + return Promise.all( + Object.entries(moduleMap).map(([name, promise]) => { + defineSharedModule(name, promise); + return promise; + }), + ); +} + +type CompleteAction = { + type: 'complete'; + key: string; + error: null | Error; +}; + +type BeginAction = { + type: 'begin'; + keys: string[]; +}; + +function pluginContextReducer( + state: PluginContextType, + action: BeginAction | CompleteAction, +): PluginContextType { + switch (action.type) { + case 'begin': { + const plugins = { ...state.plugins }; + for (const key of action.keys) { + plugins[key] = { key, error: null, loading: true }; + } + return { + ...state, + loading: true, + plugins, + }; + } + case 'complete': { + return { + ...state, + loading: Object.values(state.plugins).some( + plugin => plugin.loading && plugin.key !== action.key, + ), + plugins: { + ...state.plugins, + [action.key]: { + key: action.key, + loading: false, + error: action.error, + }, + }, + }; + } + default: + return state; + } +} + +export type Props = React.PropsWithChildren<{}>; + +export default function DynamicPluginProvider({ children }: Props) { + const [pluginState, dispatch] = useReducer(pluginContextReducer, { + // use the dummy plugin context, and override the methods + ...dummyPluginContext, + // eslint-disable-next-line @typescript-eslint/no-use-before-define + fetchAll, + loading: isFeatureEnabled(FeatureFlag.DYNAMIC_PLUGINS), + // TODO: Write fetchByKeys + }); + + async function fetchAll() { + try { + await defineSharedModules({ + react: import('react'), + lodash: import('lodash'), + 'react-dom': import('react-dom'), + '@superset-ui/chart': import('@superset-ui/chart'), + '@superset-ui/chart-controls': import('@superset-ui/chart-controls'), + '@superset-ui/connection': import('@superset-ui/connection'), + '@superset-ui/color': import('@superset-ui/color'), + '@superset-ui/core': import('@superset-ui/core'), + '@superset-ui/dimension': import('@superset-ui/dimension'), + '@superset-ui/query': import('@superset-ui/query'), + '@superset-ui/style': import('@superset-ui/style'), + '@superset-ui/translation': import('@superset-ui/translation'), + '@superset-ui/validator': import('@superset-ui/validator'), + }); Review comment: I would like to do loading on demand, but the way I got the example plugin's webpack to work is it references global variables on the `window`. I intend to define a `superset-plugin.json` manifest file for dynamic plugins in the future, which would be a good place to put dependency info. For now I'd like to keep this minimal. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
