/
getDependentStoryFiles.js
94 lines (74 loc) · 2.68 KB
/
getDependentStoryFiles.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
const isUserCode = (mod) => !mod.name.match(/(node_modules|webpack\/runtime)/);
const modToParts = (m) => [
m.name,
m.modules && m.modules.map(modToParts),
[...new Set(m.reasons.map((r) => r.moduleName))],
];
// TODO -- obviously this can depend on (-C)
const STORYBOOK_DIR = './.storybook';
// NOTE: this only works with `main:stories` -- if stories are imported from files in `.storybook/preview.js`
// we'll need a different approach to figure out CSF files (maybe the user should pass a glob?).
const STORIES_ENTRY = `${STORYBOOK_DIR}/generated-stories-entry.js`;
export function statsToDependencies(stats) {
const { modules } = stats;
// console.dir(modules.filter(isUserCode).map(modToParts), { depth: null });
const idsMap = {}; // Map module name to id
const reasonsMap = {}; // A reason is a dependent ==> map id to reasons
const isCsfGlob = {}; // Is a given module name a CSF glob specified in a `require.context()`
modules.filter(isUserCode).forEach((mod) => {
if (mod.id) {
idsMap[mod.name] = mod.id;
(mod.modules ? mod.modules.map((m) => m.name) : []).forEach((name) => {
idsMap[name] = mod.id;
});
}
reasonsMap[mod.id] = mod.reasons
.map((r) => r.moduleName)
.filter(Boolean)
.filter((n) => n !== mod.name);
if (reasonsMap[mod.id].includes(STORIES_ENTRY)) {
isCsfGlob[mod.name] = true;
}
});
return { idsMap, reasonsMap, isCsfGlob };
}
export function getDependentStoryFiles(changedFiles, stats) {
const { idsMap, reasonsMap, isCsfGlob } = statsToDependencies(stats);
const reverseIdsMap = Object.fromEntries(Object.entries(idsMap).map(([name, id]) => [id, name]));
const checkedIds = {};
const toCheck = [];
const allChangedNames = new Set();
let bailFile; // We need to bail out and check everything
const changedCsfIds = new Set();
function reachName(name) {
// Don't look any further, we've reached the CSF glob.
if (isCsfGlob[name]) {
return;
}
allChangedNames.add(name);
if (name.startsWith(STORYBOOK_DIR) && name !== STORIES_ENTRY) {
bailFile = name;
}
const id = idsMap[name];
if (!id) {
return;
// throw new Error(`Didn't find module ${name}`);
}
if (checkedIds[id]) {
return;
}
// Schedule this module to be checked
toCheck.push(id);
const isCsf = !!reasonsMap[id].find((reasonName) => isCsfGlob[reasonName]);
if (isCsf) {
changedCsfIds.add(id);
}
}
changedFiles.map(reachName);
while (toCheck.length > 0) {
const id = toCheck.pop();
checkedIds[id] = true;
reasonsMap[id].map(reachName);
}
return bailFile ? false : [...changedCsfIds].map((id) => reverseIdsMap[id]);
}