storybook/code/lib/core-client/src/executeLoadable.ts
Norbert de Langen c80f4079f9
remove dead code
2022-10-27 20:16:21 +02:00

102 lines
3.4 KiB
TypeScript

/// <reference types="node" />
/// <reference types="webpack-env" />
import { logger } from '@storybook/client-logger';
import type {
Path,
Store_ModuleExports,
Loadable,
CoreClient_RequireContext,
CoreClient_LoaderFunction,
} from '@storybook/types';
/**
* Executes a Loadable (function that returns exports or require context(s))
* and returns a map of filename => module exports
*
* @param loadable Loadable
* @returns Map<Path, ModuleExports>
*/
export function executeLoadable(loadable: Loadable) {
let reqs = null;
// todo discuss / improve type check
if (Array.isArray(loadable)) {
reqs = loadable;
} else if ((loadable as CoreClient_RequireContext).keys) {
reqs = [loadable as CoreClient_RequireContext];
}
let exportsMap = new Map<Path, Store_ModuleExports>();
if (reqs) {
reqs.forEach((req) => {
req.keys().forEach((filename: string) => {
try {
const fileExports = req(filename) as Store_ModuleExports;
exportsMap.set(
typeof req.resolve === 'function' ? req.resolve(filename) : filename,
fileExports
);
} catch (error) {
const errorString =
error.message && error.stack ? `${error.message}\n ${error.stack}` : error.toString();
logger.error(`Unexpected error while loading ${filename}: ${errorString}`);
}
});
});
} else {
const exported = (loadable as CoreClient_LoaderFunction)();
if (Array.isArray(exported) && exported.every((obj) => obj.default != null)) {
exportsMap = new Map(
exported.map((fileExports, index) => [`exports-map-${index}`, fileExports])
);
} else if (exported) {
logger.warn(
`Loader function passed to 'configure' should return void or an array of module exports that all contain a 'default' export. Received: ${JSON.stringify(
exported
)}`
);
}
}
return exportsMap;
}
/**
* Executes a Loadable (function that returns exports or require context(s))
* and compares it's output to the last time it was run (as stored on a node module)
*
* @param loadable Loadable
* @param m NodeModule
* @returns { added: Map<Path, ModuleExports>, removed: Map<Path, ModuleExports> }
*/
export function executeLoadableForChanges(loadable: Loadable, m?: NodeModule) {
let lastExportsMap: ReturnType<typeof executeLoadable> =
m?.hot?.data?.lastExportsMap || new Map();
if (m?.hot?.dispose) {
m.hot.accept();
m.hot.dispose((data) => {
// eslint-disable-next-line no-param-reassign
data.lastExportsMap = lastExportsMap;
});
}
const exportsMap = executeLoadable(loadable);
const added = new Map<Path, Store_ModuleExports>();
Array.from(exportsMap.entries())
// Ignore files that do not have a default export
.filter(([, fileExports]) => !!fileExports.default)
// Ignore exports that are equal (by reference) to last time, this means the file hasn't changed
.filter(([fileName, fileExports]) => lastExportsMap.get(fileName) !== fileExports)
.forEach(([fileName, fileExports]) => added.set(fileName, fileExports));
const removed = new Map<Path, Store_ModuleExports>();
Array.from(lastExportsMap.keys())
.filter((fileName) => !exportsMap.has(fileName))
.forEach((fileName) => removed.set(fileName, lastExportsMap.get(fileName)));
// Save the value for the dispose() call above
lastExportsMap = exportsMap;
return { added, removed };
}