Skip to content

Commit

Permalink
feat: ssr-preload-include support specify rel (web-infra-dev#4568)
Browse files Browse the repository at this point in the history
  • Loading branch information
GiveMe-A-Name committed Sep 4, 2023
1 parent 5ffb74a commit 5240e5d
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 122 deletions.
7 changes: 7 additions & 0 deletions .changeset/green-maps-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modern-js/prod-server': minor
'@modern-js/server-core': minor
---

feat: ssr-preload.include support specify rel
feat: ssr-preload.include 支持指定 rel 属性
4 changes: 3 additions & 1 deletion packages/server/core/src/types/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ type Route =
};
export type Routes = Record<string, Route>;

type PreloadInclude = Array<string | { url: string; type: string }>;
type PreloadInclude = Array<
string | { url: string; type: string; rel?: string }
>;
interface PreloadAttributes {
script?: Record<string, boolean | string>;
style?: Record<string, boolean | string>;
Expand Down
6 changes: 6 additions & 0 deletions packages/server/prod-server/src/libs/preload/parseLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import { matchEntry } from '@modern-js/utils/runtime-node';

export interface Link {
uri: string;

rel?: string;

as?: 'script' | 'style' | 'image' | 'video' | 'font' | string;

/** rest str(like attributes) that need add to link */
rest?: string;
}

export interface ParseLinksOptions {
Expand Down
221 changes: 116 additions & 105 deletions packages/server/prod-server/src/libs/preload/transformLinks2String.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,125 +7,136 @@ export function transformLinks2String(
preload: SSRPreload | boolean,
): string {
if (typeof preload === 'boolean') {
return dedup(links)
.map(({ uri, as }) =>
as ? `<${uri}>; rel=preload; as=${as}` : `<${uri}>; rel=preload`,
)
.join(', ');
return transformLinkToString(dedup(links));
}
const { include, exclude, attributes } = preload;

const resolveLinks = addAttributes(
dedup(removeExclude(addInclude(links, include), exclude)),
attributes,
const resolveLinks = transformLinkToString(
addAttributes(
dedup(removeExclude(addInclude(links, include), exclude)),
attributes,
),
);

return resolveLinks.join(', ');

function addInclude(links: Link[], include?: SSRPreload['include']) {
const images = [
'gif',
'jpg',
'jpeg',
'png',
'webp',
'bmp',
'tiff',
'anpg',
'ico',
];
const videos = ['mp4', 'webm', 'ogm', 'ogv', 'ogg'];
const fonts = ['woff', 'woff2', 'eot', 'ttf', 'otf'];
const includes =
include?.map(item => {
if (typeof item === 'string') {
// eslint-disable-next-line consistent-return
const type = (() => {
if (item.endsWith('.js')) {
return 'script';
}
return resolveLinks;
}

if (item.endsWith('.css')) {
return 'style';
}
function addInclude(links: Link[], include?: SSRPreload['include']) {
const images = [
'gif',
'jpg',
'jpeg',
'png',
'webp',
'bmp',
'tiff',
'anpg',
'ico',
];
const videos = ['mp4', 'webm', 'ogm', 'ogv', 'ogg'];
const fonts = ['woff', 'woff2', 'eot', 'ttf', 'otf'];
const includes =
include?.map(item => {
if (typeof item === 'string') {
// eslint-disable-next-line consistent-return
const type = (() => {
if (item.endsWith('.js')) {
return 'script';
}

if (images.some(image => item.endsWith(`.${image}`))) {
return 'image';
}
if (item.endsWith('.css')) {
return 'style';
}

if (videos.some(video => item.endsWith(`.${video}`))) {
return 'video';
}
if (images.some(image => item.endsWith(`.${image}`))) {
return 'image';
}

if (fonts.some(font => item.endsWith(`.${font}`))) {
return 'font';
}
})();
return { uri: item, as: type };
}
return { uri: item.url, as: item.type };
}) || [];
if (videos.some(video => item.endsWith(`.${video}`))) {
return 'video';
}

return links.concat(includes);
}
if (fonts.some(font => item.endsWith(`.${font}`))) {
return 'font';
}
})();
return { uri: item, as: type };
}
return { uri: item.url, as: item.type, rel: item.rel };
}) || [];

function removeExclude(links: Link[], exclude?: SSRPreload['exclude']) {
return exclude
? links.filter(({ uri }) => !transformToRegExp(exclude).test(uri))
: links;
}
return links.concat(includes);
}

function addAttributes(
links: Link[],
attributes?: SSRPreload['attributes'],
): string[] {
const parseAttributes = (
_attributes?: Record<string, boolean | string>,
) => {
return Object.entries(_attributes || {})
.reduce((results, [key, value]) => {
if (typeof value === 'boolean') {
value && results.push(`; ${key}`);
return results;
}
function removeExclude(links: Link[], exclude?: SSRPreload['exclude']) {
return exclude
? links.filter(({ uri }) => !transformToRegExp(exclude).test(uri))
: links;
}

results.push(`; ${key}=${value}`);
function addAttributes(
links: Link[],
attributes?: SSRPreload['attributes'],
): Link[] {
const parseAttributes = (_attributes?: Record<string, boolean | string>) => {
return Object.entries(_attributes || {})
.reduce((results, [key, value]) => {
if (typeof value === 'boolean') {
value && results.push(`; ${key}`);
return results;
}, [] as string[])
.join('');
};
}

return links.map(({ uri, as }) => {
if (as) {
const attributesStr = (() => {
const { style, script, image, font } = attributes || {};
switch (as) {
case 'script':
return parseAttributes(script);
case 'style':
return parseAttributes(style);
case 'image':
return parseAttributes(image);
case 'font':
return parseAttributes(font);
default:
return '';
}
})();
return `<${uri}>; rel=preload; as=${as}${attributesStr}`;
}
return `<${uri}>; rel=preload`;
});
}
results.push(`; ${key}=${value}`);
return results;
}, [] as string[])
.join('');
};

function dedup(links: Link[]) {
const set = new Set<string>();
return links.filter(({ uri }) => {
if (set.has(uri)) {
return false;
}
set.add(uri);
return true;
});
}
return links.map(link => {
const { as } = link;
if (as) {
const attributesStr = (() => {
const { style, script, image, font } = attributes || {};
switch (as) {
case 'script':
return parseAttributes(script);
case 'style':
return parseAttributes(style);
case 'image':
return parseAttributes(image);
case 'font':
return parseAttributes(font);
default:
return '';
}
})();
return {
...link,
rest: attributesStr,
};
}
return link;
});
}

function dedup(links: Link[]) {
const set = new Set<string>();
return links.filter(({ uri }) => {
if (set.has(uri)) {
return false;
}
set.add(uri);
return true;
});
}

function transformLinkToString(links: Link[]): string {
return links
.map(({ uri, as, rel: originalRel, rest }) => {
const rel = originalRel || 'preload';
return as
? `<${uri}>; rel=${rel}; as=${as}${rest || ''}`
: `<${uri}>; rel=${rel}${rest || ''}`;
})
.join(', ');
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@ exports[`test preload test flushServerHeader 1`] = `
{
"key": "link",
"value": [
"</static/js/async/three_user/layout.js>; rel=preload; as=script",
"</static/js/async/three_user/(id)/page.js>; rel=preload; as=script",
"</static/js/lib-react.js>; rel=preload; as=script",
"</static/js/lib-polyfill.js>; rel=preload; as=script",
"</static/js/lib-router.js>; rel=preload; as=script",
"</static/js/vendors-node_modules_pnpm_pmmmwh_react-refresh-webpack-plugin_0_5_10_react-refresh_0_14_0_web-ec966a.js>; rel=preload; as=script",
"</static/js/vendors-node_modules_pnpm_loadable_component_5_15_3_react_18_2_0_node_modules_loadable_compon-a67b38.js>; rel=preload; as=script",
"</static/js/vendors-node_modules_pnpm_path-to-regexp_6_2_1_node_modules_path-to-regexp_dist_es2015_index_-89b787.js>; rel=preload; as=script",
"</static/js/packages_runtime_plugin-runtime_dist_esm_core_compatible_js-packages_server_server_dist_esm_d-a68cc6.js>; rel=preload; as=script",
"</static/js/packages_runtime_plugin-runtime_dist_esm_router_runtime_index_js-packages_runtime_plugin-runt-1b02a2.js>; rel=preload; as=script",
"</static/js/three.js>; rel=preload; as=script",
"</example1/index.js>; rel=preload; as=script",
"</example2/index.js>; rel=preload; as=script",
"<style1.css>; rel=preload; as=style",
"<style2.css>; rel=preload; as=style",
"<http://example.com>; rel=preload; as=script",
"</static/js/async/three_user/layout.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/async/three_user/(id)/page.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/lib-react.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/lib-polyfill.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/lib-router.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/vendors-node_modules_pnpm_pmmmwh_react-refresh-webpack-plugin_0_5_10_react-refresh_0_14_0_web-ec966a.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/vendors-node_modules_pnpm_loadable_component_5_15_3_react_18_2_0_node_modules_loadable_compon-a67b38.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/vendors-node_modules_pnpm_path-to-regexp_6_2_1_node_modules_path-to-regexp_dist_es2015_index_-89b787.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/packages_runtime_plugin-runtime_dist_esm_core_compatible_js-packages_server_server_dist_esm_d-a68cc6.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/packages_runtime_plugin-runtime_dist_esm_router_runtime_index_js-packages_runtime_plugin-runt-1b02a2.js>; rel=preload; as=script; crossorigin; id=script_id",
"</static/js/three.js>; rel=preload; as=script; crossorigin; id=script_id",
"</example1/index.js>; rel=preload; as=script; crossorigin; id=script_id",
"</example2/index.js>; rel=preload; as=script; crossorigin; id=script_id",
"<style1.css>; rel=preload; as=style; id=css_link_id",
"<style2.css>; rel=preload; as=style; id=css_link_id",
"<http://example.com>; rel=preload; as=script; crossorigin; id=script_id",
"<http://example3.com>; rel=dns-prefetch; as=script; crossorigin; id=script_id",
],
},
{
Expand Down
14 changes: 14 additions & 0 deletions packages/server/prod-server/tests/preload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,22 @@ describe('test preload', () => {
include: [
{ url: 'http://example.com', type: 'script' },
{ url: 'http://example.com', type: 'script' },
{
url: 'http://example3.com',
type: 'script',
rel: 'dns-prefetch',
},
'/static/js/async/three_user/layout.js',
],
attributes: {
script: {
crossorigin: true,
id: 'script_id',
},
style: {
id: 'css_link_id',
},
},
},
},
},
Expand Down

0 comments on commit 5240e5d

Please sign in to comment.