• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rtCamp / snapwp / 14556817188

20 Apr 2025 06:26AM UTC coverage: 56.907% (+3.3%) from 53.621%
14556817188

Pull #123

github

web-flow
Merge b3356e4a0 into 4749ca512
Pull Request #123: feat: SEO support

188 of 409 branches covered (45.97%)

Branch coverage included in aggregate %.

1 of 10 new or added lines in 1 file covered. (10.0%)

23 existing lines in 2 files now uncovered.

397 of 619 relevant lines covered (64.14%)

6.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.2
/packages/query/src/utils/parse-template.ts
1
import {
2✔
2
        Logger,
3
        TemplateParseError,
4
        type EnqueuedScriptProps,
5
        type ScriptModuleProps,
6
        type StyleSheetProps,
7
} from '@snapwp/core';
8

9
import type { ApolloQueryResult } from '@apollo/client';
10
import type { GetCurrentTemplateQuery } from '@graphqlTypes/graphql';
11
import type { BlockData } from '@snapwp/types';
12

13
/**
14
 * Parses template query data into props for rendering a template.
15
 *
16
 * @param {ApolloQueryResult<GetCurrentTemplateQuery>} queryData    The data fetched from the template query.
17
 * @param {string}                                     wordpressUrl The base URL of the WordPress site.
18
 * @param {string}                                     uri          The URI of the template.
19
 *
20
 * @return An object containing parsed template data.
21
 */
22
export function parseQueryResult(
2✔
23
        queryData: ApolloQueryResult< GetCurrentTemplateQuery >,
24
        wordpressUrl: string,
25
        uri: string
26
): {
27
        stylesheets: StyleSheetProps[] | undefined;
28
        editorBlocks: BlockData< Record< string, unknown > >[] | undefined;
29
        scripts: EnqueuedScriptProps[] | undefined;
30
        scriptModules: ScriptModuleProps[] | undefined;
31
        bodyClasses: string[] | undefined;
32
        is404: boolean;
33
} {
34
        if ( queryData.errors?.length ) {
3✔
35
                queryData.errors?.forEach( ( error ) => {
1✔
36
                        Logger.error(
1✔
37
                                `Error fetching template data: ${ error?.message }.`,
38
                                '(Please refer to our FAQs for steps to debug and fix)', // @TODO: update FAQs with URL.
39
                                error
40
                        );
41
                } );
42
        }
43

44
        // If there is no data or templateByUri in the query data and there are errors, throw an error.
45
        if (
3✔
46
                ( ! queryData.data || ! queryData.data.templateByUri ) &&
7✔
47
                queryData.errors?.length
48
        ) {
49
                throw new TemplateParseError(
1✔
50
                        `Error fetching template data for uri: ${ uri }`
51
                );
52
        }
53

54
        const templateByUri = queryData.data?.templateByUri;
2✔
55
        const is404 = templateByUri?.is404 ?? false;
2✔
56

57
        return {
2✔
58
                stylesheets: parseEnqueuedStylesheets( wordpressUrl, templateByUri ),
59
                editorBlocks: parseEditorBlocks( templateByUri ),
60
                scripts: parseEnqueuedScripts( templateByUri, wordpressUrl ),
61
                scriptModules: parseScriptModules( templateByUri, wordpressUrl ),
62
                bodyClasses: parseBodyClasses( templateByUri ),
63
                is404,
64
        };
65
}
66

67
/**
68
 * Gets and validates the body classes from the query data.
69
 *
70
 * @param {GetCurrentTemplateQuery['templateByUri']} templateByUri The template data fetched for the uri.
71
 *
72
 * @return The body classes.
73
 */
74
function parseBodyClasses(
75
        templateByUri: GetCurrentTemplateQuery[ 'templateByUri' ]
76
): string[] | undefined {
77
        const bodyClasses: string[] | undefined = [];
2✔
78

79
        templateByUri?.bodyClasses?.forEach( ( bodyClass: unknown ) => {
2✔
80
                if ( bodyClass && 'string' === typeof bodyClass ) {
4✔
81
                        bodyClasses.push( bodyClass );
4✔
82
                }
83
        } );
84

85
        if ( bodyClasses.length ) {
2✔
86
                return bodyClasses;
2✔
87
        }
88

UNCOV
89
        return undefined;
×
90
}
91

92
/**
93
 * Gets and validates the enqueued scripts from the query data.
94
 *
95
 * @param {GetCurrentTemplateQuery['templateByUri']} templateByUri The template data fetched for the uri.
96
 * @param {string}                                   wordpressUrl  The base URL of the WordPress site.
97
 *
98
 * @return The enqueued scripts.
99
 */
100
function parseEnqueuedScripts(
101
        templateByUri: GetCurrentTemplateQuery[ 'templateByUri' ],
102
        wordpressUrl: string
103
): EnqueuedScriptProps[] | undefined {
104
        return templateByUri?.enqueuedScripts?.nodes?.map(
2✔
105
                ( script: EnqueuedScriptProps ) => {
106
                        return {
4✔
107
                                // Ensure the src is an absolute URL.
108
                                src: script?.src?.startsWith( '/' )
4✔
109
                                        ? wordpressUrl + script.src
110
                                        : script.src ?? null,
2!
111
                                handle: script.handle ? script.handle : null,
4!
112
                        };
113
                }
114
        );
115
}
116

117
/**
118
 * Gets and validates the editor blocks from the query data.
119
 *
120
 * @param {GetCurrentTemplateQuery['templateByUri']} templateByUri The template data fetched for the uri.
121
 *
122
 * @return The editor blocks.
123
 */
124
function parseEditorBlocks(
125
        templateByUri: GetCurrentTemplateQuery[ 'templateByUri' ]
126
): BlockData[] | undefined {
127
        const editorBlocks: BlockData[] | undefined = [];
2✔
128

129
        templateByUri?.editorBlocks?.forEach( ( editorBlock: unknown ) => {
2✔
130
                if ( editorBlock && 'object' === typeof editorBlock ) {
2✔
131
                        editorBlocks.push( editorBlock as BlockData );
2✔
132
                }
133
        } );
134

135
        if ( editorBlocks.length ) {
2✔
136
                return editorBlocks;
2✔
137
        }
138

UNCOV
139
        return undefined;
×
140
}
141

142
/**
143
 * Gets and validates the enqueued stylesheets from the query data.
144
 *
145
 * @param {string}                                   wordpressUrl  The base URL of the WordPress site.
146
 * @param {GetCurrentTemplateQuery['templateByUri']} templateByUri The template data fetched for the uri.
147
 *
148
 * @return The enqueued stylesheets.
149
 */
150
function parseEnqueuedStylesheets(
151
        wordpressUrl: string,
152
        templateByUri?: GetCurrentTemplateQuery[ 'templateByUri' ]
153
): StyleSheetProps[] | undefined {
154
        return templateByUri?.enqueuedStylesheets?.nodes?.map( ( stylesheet ) => ( {
2✔
155
                before: stylesheet?.before?.join( '' ) || null,
2!
156
                after: stylesheet?.after?.join( '' ) || null,
2!
157
                src:
158
                        ( stylesheet?.src?.startsWith( '/' )
4!
159
                                ? wordpressUrl + stylesheet.src
160
                                : stylesheet?.src ) || null,
161
                handle: stylesheet?.handle || null,
2!
162
        } ) );
163
}
164

165
/**
166
 * Gets and validates the script modules from the query data.
167
 *
168
 * @param {GetCurrentTemplateQuery['templateByUri']} templateByUri The template data fetched for the uri.
169
 * @param {string}                                   wordpressUrl  The base URL of the WordPress site.
170
 *
171
 * @return The script modules.
172
 */
173
function parseScriptModules(
174
        templateByUri: GetCurrentTemplateQuery[ 'templateByUri' ],
175
        wordpressUrl: string
176
): ScriptModuleProps[] | undefined {
177
        return (
2✔
UNCOV
178
                templateByUri?.enqueuedScriptModules?.nodes?.map( ( script ) => ( {
✔
179
                        handle: script?.handle ?? null,
×
180
                        src: script?.src?.startsWith( '/' )
×
181
                                ? `${ wordpressUrl }${ script.src }`
182
                                : script?.src ?? null,
×
183
                        extraData: script?.extraData || null,
×
184
                        dependencies:
185
                                script?.dependencies
×
186
                                        ?.filter(
187
                                                ( dep ): dep is NonNullable< typeof dep > =>
UNCOV
188
                                                        !! dep &&
×
189
                                                        !! dep.connectedScriptModule?.handle &&
190
                                                        !! dep.connectedScriptModule?.src
191
                                        )
UNCOV
192
                                        .map( ( dep ) => ( {
×
193
                                                importType: dep.importType ?? null,
×
194
                                                connectedScriptModule: {
195
                                                        handle: dep.connectedScriptModule!.handle!,
196
                                                        src: dep.connectedScriptModule!.src!.startsWith(
×
197
                                                                '/'
198
                                                        )
199
                                                                ? `${ wordpressUrl }${
200
                                                                                dep.connectedScriptModule!.src
201
                                                                  }`
202
                                                                : dep.connectedScriptModule!.src!,
203
                                                },
204
                                        } ) ) ?? null,
205
                } ) ) ?? undefined
206
        );
207
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc