UNPKG

@mcdevsl/superset-ui

Version:
91 lines (80 loc) 3.45 kB
/** * 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. */ /** The type of an imported module. Don't fully understand this, yet. */ export type Module = any; /** * This is where packages are stored. We use window, because it plays well with Webpack. * To avoid * Have to amend the type of window, because window's usual type doesn't describe these fields. */ interface ModuleReferencer { [packageKey: string]: Promise<Module>; } declare const window: Window & typeof globalThis & ModuleReferencer; const modulePromises: { [key: string]: Promise<Module> } = {}; const withNamespace = (name: string) => `__superset__/${name}`; /** * 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. * * This defines a dynamically imported js module that can be used to import from * multiple different plugins. * * When importing a common module (such as react or lodash or superset-ui) * from a plugin, the plugin's build config will be able to * reference these globals instead of rebuilding them. * * @param name the module's name (should match name in package.json) * @param promise the promise resulting from a call to `import(name)` */ export async function defineSharedModule(name: string, fetchModule: () => Promise<Module>) { // this field on window is used by dynamic plugins to reference the module const moduleKey = withNamespace(name); if (!window[moduleKey] && !modulePromises[name]) { // if the module has not been loaded, load it const modulePromise = fetchModule(); modulePromises[name] = modulePromise; // wait for the module to load, and attach the result to window // so that it can be referenced by plugins window[moduleKey] = await modulePromise; } // we always return a reference to the promise. // Multiple consumers can `.then()` or `await` the same promise, // even long after it has completed, // and it will always call back with the same reference. return modulePromises[name]; } /** * Define multiple shared modules at once, using a map of name -> `import(name)` * * @see defineSharedModule * @param moduleMap */ export async function defineSharedModules(moduleMap: { [key: string]: () => Promise<Module> }) { return Promise.all( Object.entries(moduleMap).map(([name, fetchModule]) => defineSharedModule(name, fetchModule)), ); } // only exposed for tests export function reset() { Object.keys(modulePromises).forEach(key => { delete window[withNamespace(key)]; delete modulePromises[key]; }); }