UNPKG

14.4 kBJavaScriptView Raw
1"use strict";
2
3exports.__esModule = true;
4exports.htmlReducer = htmlReducer;
5const FLAG_DIRTY_CLEARED_CACHE = 0b0000001;
6const FLAG_DIRTY_NEW_ENTRY = 0b0000010;
7const FLAG_DIRTY_DATA_CHANGED = 0b0000100;
8const FLAG_DIRTY_STATIC_QUERY_FIRST_RUN = 0b0001000;
9const FLAG_DIRTY_STATIC_QUERY_RESULT_CHANGED = 0b0010000;
10const FLAG_DIRTY_BROWSER_COMPILATION_HASH = 0b0100000;
11const FLAG_DIRTY_SSR_COMPILATION_HASH = 0b1000000;
12function initialState() {
13 return {
14 trackedHtmlFiles: new Map(),
15 slicesProps: {
16 bySliceId: new Map(),
17 byPagePath: new Map(),
18 bySliceName: new Map()
19 },
20 browserCompilationHash: ``,
21 ssrCompilationHash: ``,
22 trackedStaticQueryResults: new Map(),
23 unsafeBuiltinWasUsedInSSR: false,
24 templateCompilationHashes: {},
25 pagesThatNeedToStitchSlices: new Set()
26 };
27}
28function htmlReducer(state = initialState(), action) {
29 switch (action.type) {
30 case `DELETE_CACHE`:
31 {
32 if (action.cacheIsCorrupt) {
33 // `public` doesn't exist so we can start fresh
34 return initialState();
35 } else {
36 // we can't just clear the cache here - we want to keep track of pages, so we mark them all as "deleted"
37 // if they are recreated "isDeleted" flag will be removed
38 state.browserCompilationHash = ``;
39 state.ssrCompilationHash = ``;
40 state.templateCompilationHashes = {};
41 state.trackedStaticQueryResults.clear();
42 state.unsafeBuiltinWasUsedInSSR = false;
43 state.trackedHtmlFiles.forEach(htmlFile => {
44 htmlFile.isDeleted = true;
45 // there was a change somewhere, so just in case we mark those files are dirty as well
46 htmlFile.dirty |= FLAG_DIRTY_CLEARED_CACHE;
47 });
48 // slice html don't need to be deleted, they are just cleared
49 state.slicesProps = {
50 bySliceId: new Map(),
51 byPagePath: new Map(),
52 bySliceName: new Map()
53 };
54 state.pagesThatNeedToStitchSlices = new Set();
55 return state;
56 }
57 }
58 case `CREATE_PAGE`:
59 {
60 // CREATE_PAGE can be called even if page already exist, so we only want to do anything
61 // if we don't track given page yet or if page is marked as deleted
62 const {
63 path
64 } = action.payload;
65 let htmlFile = state.trackedHtmlFiles.get(path);
66 if (!htmlFile) {
67 htmlFile = {
68 dirty: FLAG_DIRTY_NEW_ENTRY,
69 isDeleted: false,
70 pageDataHash: ``
71 };
72 state.trackedHtmlFiles.set(path, htmlFile);
73 } else if (htmlFile.isDeleted) {
74 // page was recreated so we remove `isDeleted` flag
75 // TBD if dirtiness need to change
76 htmlFile.isDeleted = false;
77 }
78 return state;
79 }
80 case `CREATE_SLICE`:
81 {
82 const existing = state.slicesProps.bySliceName.get(action.payload.name);
83 if (!existing) {
84 state.slicesProps.bySliceName.set(action.payload.name, {
85 dirty: FLAG_DIRTY_NEW_ENTRY,
86 props: new Set(),
87 sliceDataHash: ``
88 });
89 }
90 return state;
91 }
92 case `DELETE_PAGE`:
93 {
94 const {
95 path
96 } = action.payload;
97 const htmlFile = state.trackedHtmlFiles.get(path);
98 if (!htmlFile) {
99 // invariant
100 throw new Error(`[html reducer] how can I delete page that wasn't created (?)`);
101 }
102 htmlFile.isDeleted = true;
103 // TBD if dirtiness need to change
104 return state;
105 }
106 case `PAGE_QUERY_RUN`:
107 {
108 // Despite action name, this action is actually emitted for both page and static queries.
109 // In here we actually only care about static query result (particularly its hash).
110 // We don't care about page query result because we don't actually use page query result
111 // directly when generating html. We care about page-data (which contains page query result).
112 // Handling of page-data that transitively handles page query result is done in handler for
113 // `ADD_PAGE_DATA_STATS` action.
114 if (action.payload.queryType === `static`) {
115 // static query case
116 let staticQueryResult = state.trackedStaticQueryResults.get(action.payload.queryHash);
117 if (!staticQueryResult) {
118 staticQueryResult = {
119 dirty: FLAG_DIRTY_STATIC_QUERY_FIRST_RUN,
120 staticQueryResultHash: action.payload.resultHash
121 };
122 state.trackedStaticQueryResults.set(action.payload.queryHash, staticQueryResult);
123 } else if (staticQueryResult.staticQueryResultHash !== action.payload.resultHash) {
124 staticQueryResult.dirty |= FLAG_DIRTY_STATIC_QUERY_RESULT_CHANGED;
125 staticQueryResult.staticQueryResultHash = action.payload.resultHash;
126 }
127 }
128 return state;
129 }
130 case `ADD_PAGE_DATA_STATS`:
131 {
132 const htmlFile = state.trackedHtmlFiles.get(action.payload.pagePath);
133 if (!htmlFile) {
134 // invariant
135 throw new Error(`[html reducer] I received event that query for a page finished running, but I'm not aware of the page it ran for (?)`);
136 }
137 if (htmlFile.pageDataHash !== action.payload.pageDataHash) {
138 htmlFile.pageDataHash = action.payload.pageDataHash;
139 htmlFile.dirty |= FLAG_DIRTY_DATA_CHANGED;
140 }
141 return state;
142 }
143 case `SET_WEBPACK_COMPILATION_HASH`:
144 {
145 if (!("5" === `5` && process.env.GATSBY_SLICES)) {
146 if (state.browserCompilationHash !== action.payload) {
147 state.browserCompilationHash = action.payload;
148 state.trackedHtmlFiles.forEach(htmlFile => {
149 htmlFile.dirty |= FLAG_DIRTY_BROWSER_COMPILATION_HASH;
150 });
151 }
152 }
153 return state;
154 }
155 case `ADD_SLICE_DATA_STATS`:
156 {
157 const sliceProps = state.slicesProps.bySliceName.get(action.payload.sliceName);
158 if (!sliceProps) {
159 throw new Error(`no slice props for ${action.payload.sliceName}`);
160 }
161 if (sliceProps.sliceDataHash !== action.payload.sliceDataHash) {
162 sliceProps.sliceDataHash = action.payload.sliceDataHash;
163 sliceProps.dirty |= FLAG_DIRTY_DATA_CHANGED;
164 }
165 return state;
166 }
167 case `SET_SSR_TEMPLATE_WEBPACK_COMPILATION_HASH`:
168 {
169 if (state.templateCompilationHashes[action.payload.templatePath] !== action.payload.templateHash) {
170 state.templateCompilationHashes[action.payload.templatePath] = action.payload.templateHash;
171 if (action.payload.isSlice) {
172 for (const sliceName of action.payload.pages) {
173 const sliceTemplate = state.slicesProps.bySliceName.get(sliceName);
174 if (sliceTemplate) {
175 sliceTemplate.dirty |= FLAG_DIRTY_SSR_COMPILATION_HASH;
176 }
177 }
178 } else {
179 if (action.payload.pages) {
180 action.payload.pages.forEach(pagePath => {
181 const htmlFile = state.trackedHtmlFiles.get(pagePath);
182 if (htmlFile) {
183 htmlFile.dirty |= FLAG_DIRTY_SSR_COMPILATION_HASH;
184 }
185 });
186 } else {
187 process.stdout.write(`---no pages for:\n${JSON.stringify(action.payload, null, 2)}\n`);
188 }
189 }
190 }
191 return state;
192 }
193 case `SET_SSR_WEBPACK_COMPILATION_HASH`:
194 {
195 if (state.ssrCompilationHash !== action.payload) {
196 state.ssrCompilationHash = action.payload;
197 // we will mark every html file as dirty, so we can safely reset
198 // unsafeBuiltinWasUsedInSSR flag, which might be set again if
199 // ssr bundle continue to use those
200 state.unsafeBuiltinWasUsedInSSR = false;
201 state.trackedHtmlFiles.forEach(htmlFile => {
202 htmlFile.dirty |= FLAG_DIRTY_SSR_COMPILATION_HASH;
203 });
204 }
205 return state;
206 }
207 case `HTML_REMOVED`:
208 {
209 state.trackedHtmlFiles.delete(action.payload);
210 return state;
211 }
212 case `HTML_TRACKED_PAGES_CLEANUP`:
213 {
214 // this is to cleanup variants of page paths that don't result in artifacts deletion
215 // but page path should be pruned for cases like page changing path from "/foo" to "/foo/" (or vice versa)
216 // where produced artifacts filenames are the same and we don't want to delete them after building,
217 // but we still want to cleanup state here.
218 for (const path of action.payload) {
219 state.trackedHtmlFiles.delete(path);
220 }
221 return state;
222 }
223 case `HTML_GENERATED`:
224 {
225 for (const path of action.payload) {
226 const htmlFile = state.trackedHtmlFiles.get(path);
227 if (htmlFile) {
228 htmlFile.dirty = 0;
229 // if we regenerated html, slice placeholders will be empty and we will have to fill
230 // them in, so we are marking that page for stitching
231 state.pagesThatNeedToStitchSlices.add(path);
232 }
233 }
234 return state;
235 }
236 case `HTML_MARK_DIRTY_BECAUSE_STATIC_QUERY_RESULT_CHANGED`:
237 {
238 // mark pages as dirty
239 for (const path of action.payload.pages) {
240 const htmlFile = state.trackedHtmlFiles.get(path);
241 if (htmlFile) {
242 htmlFile.dirty |= FLAG_DIRTY_STATIC_QUERY_RESULT_CHANGED;
243 }
244 }
245 if ("5" === `5` && process.env.GATSBY_SLICES) {
246 // mark slices as dirty
247 for (const sliceName of action.payload.slices) {
248 const sliceProps = state.slicesProps.bySliceName.get(sliceName);
249 if (sliceProps) {
250 sliceProps.dirty |= FLAG_DIRTY_STATIC_QUERY_RESULT_CHANGED;
251 }
252 }
253 }
254
255 // mark static queries as not dirty anymore (we flushed their dirtiness into pages)
256 for (const staticQueryHash of action.payload.staticQueryHashes) {
257 const staticQueryResult = state.trackedStaticQueryResults.get(staticQueryHash);
258 if (staticQueryResult) {
259 staticQueryResult.dirty = 0;
260 }
261 }
262 if ("5" === `5` && process.env.GATSBY_SLICES) {
263 // loop through slice names and mark their slice props as dirty
264 for (const sliceNameInfo of state.slicesProps.bySliceName.values()) {
265 if (sliceNameInfo.dirty) {
266 for (const sliceId of sliceNameInfo.props) {
267 const slicePropInfo = state.slicesProps.bySliceId.get(sliceId);
268 if (slicePropInfo) {
269 slicePropInfo.dirty |= sliceNameInfo.dirty;
270 }
271 }
272 sliceNameInfo.dirty = 0;
273 }
274 }
275 }
276 return state;
277 }
278 case `SSR_USED_UNSAFE_BUILTIN`:
279 {
280 state.unsafeBuiltinWasUsedInSSR = true;
281 return state;
282 }
283 case `SET_SLICES_PROPS`:
284 {
285 for (const [pagePath, slicesDataBySliceId] of Object.entries(action.payload)) {
286 const newListOfSlices = new Set();
287 for (const [sliceId, {
288 props,
289 sliceName,
290 hasChildren
291 }] of Object.entries(slicesDataBySliceId)) {
292 newListOfSlices.add(sliceId);
293 let sliceInfo = state.slicesProps.bySliceId.get(sliceId);
294 if (!sliceInfo) {
295 sliceInfo = {
296 pages: new Set([pagePath]),
297 props,
298 sliceName,
299 hasChildren,
300 dirty: FLAG_DIRTY_NEW_ENTRY
301 };
302 state.slicesProps.bySliceId.set(sliceId, sliceInfo);
303 let existingBySliceName = state.slicesProps.bySliceName.get(sliceName);
304 if (!existingBySliceName) {
305 existingBySliceName = {
306 dirty: 0,
307 sliceDataHash: ``,
308 props: new Set()
309 };
310 state.slicesProps.bySliceName.set(sliceName, existingBySliceName);
311 }
312 existingBySliceName.props.add(sliceId);
313 } else {
314 sliceInfo.pages.add(pagePath);
315 if (hasChildren) {
316 sliceInfo.hasChildren = true;
317 }
318 }
319 }
320 const oldListOfSlices = state.slicesProps.byPagePath.get(pagePath);
321 if (oldListOfSlices) {
322 for (const sliceId of oldListOfSlices) {
323 if (!newListOfSlices.has(sliceId)) {
324 oldListOfSlices.delete(sliceId);
325 const sliceInfo = state.slicesProps.bySliceId.get(sliceId);
326 if (sliceInfo) {
327 sliceInfo.pages.delete(pagePath);
328 }
329 }
330 }
331 }
332 state.slicesProps.byPagePath.set(pagePath, newListOfSlices);
333 }
334 return state;
335 }
336 case `SLICES_PROPS_REMOVE_STALE`:
337 {
338 for (const [sliceId, {
339 pages,
340 sliceName
341 }] of state.slicesProps.bySliceId.entries()) {
342 if (pages.size <= 0) {
343 state.slicesProps.bySliceId.delete(sliceId);
344 const slicePropsListForThisSliceName = state.slicesProps.bySliceName.get(sliceName);
345 if (slicePropsListForThisSliceName) {
346 slicePropsListForThisSliceName.props.delete(sliceId);
347 }
348 }
349 }
350 return state;
351 }
352 case `SLICES_PROPS_RENDERED`:
353 {
354 for (const {
355 sliceId
356 } of action.payload) {
357 const sliceState = state.slicesProps.bySliceId.get(sliceId);
358 if (sliceState) {
359 sliceState.dirty = 0;
360 for (const pagePath of sliceState.pages) {
361 state.pagesThatNeedToStitchSlices.add(pagePath);
362 }
363 }
364 }
365 return state;
366 }
367 case `SLICES_SCRIPTS_REGENERATED`:
368 {
369 for (const [pagePath, htmlPageState] of state.trackedHtmlFiles) {
370 if (!htmlPageState.isDeleted) {
371 state.pagesThatNeedToStitchSlices.add(pagePath);
372 }
373 }
374 return state;
375 }
376 case `SLICES_STITCHED`:
377 {
378 state.pagesThatNeedToStitchSlices.clear();
379 return state;
380 }
381 }
382 return state;
383}
384//# sourceMappingURL=html.js.map
\No newline at end of file