Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(builder): preload resource can't be used if crossOrigin attr not match #5004

Merged
merged 4 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/lazy-days-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modern-js/builder-shared': patch
---

fix(builder): add crossOrigin attr for preload resource when origin resource is crossOrigin

fix(builder): preload 资源的 crossOrigin 属性与原资源的 crossOrigin 属性保持一致
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ export class HtmlCrossOriginPlugin implements WebpackPluginInstance {
}

apply(compiler: Compiler): void {
if (!this.crossOrigin) {
if (
!this.crossOrigin ||
// align with webpack crossOriginLoading logic
// https://github.com/web-infra-dev/rspack/blob/bc8e67b5419adda15c2b389517c9b37d02c8240f/crates/rspack_plugin_runtime/src/runtime_module/load_script.rs#L39
(compiler.options.output.publicPath === '/' &&
this.crossOrigin !== ('use-credentials' as const))
) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ function generateLinks(
const sortedFilteredFiles = filteredFiles.sort();
const links: HtmlWebpackPlugin.HtmlTagObject[] = [];
const publicPath = getPublicPathFromCompiler(compilation.compiler);
const { crossOriginLoading } = compilation.compiler.options.output;

for (const file of sortedFilteredFiles) {
const href = withPublicPath(file, publicPath);
Expand All @@ -138,6 +139,16 @@ function generateLinks(
if (attributes.as === 'font') {
attributes.crossorigin = '';
}

if (attributes.as === 'script' || attributes.as === 'style') {
if (
crossOriginLoading &&
!(crossOriginLoading !== 'use-credentials' && publicPath === '/')
) {
attributes.crossorigin =
crossOriginLoading === 'anonymous' ? '' : crossOriginLoading;
}
}
}

links.push({
Expand Down
59 changes: 59 additions & 0 deletions tests/e2e/builder/cases/html/cross-origin/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import path from 'path';
import { expect, test } from '@playwright/test';
import { build } from '@scripts/shared';

test('should not apply crossOrigin by default', async () => {
const builder = await build({
cwd: __dirname,
entry: { index: path.resolve(__dirname, './src/index.js') },
builderConfig: {
html: {
scriptLoading: 'blocking',
},
},
});
const files = await builder.unwrapOutputJSON();
const html =
files[Object.keys(files).find(file => file.endsWith('index.html'))!];

expect(html).not.toContain('crossorigin');
});

test('should not apply crossOrigin when same origin', async () => {
const builder = await build({
cwd: __dirname,
entry: { index: path.resolve(__dirname, './src/index.js') },
builderConfig: {
html: {
scriptLoading: 'blocking',
crossorigin: 'anonymous',
},
},
});
const files = await builder.unwrapOutputJSON();
const html =
files[Object.keys(files).find(file => file.endsWith('index.html'))!];

expect(html).not.toContain('crossorigin');
});

test('should apply crossOrigin when crossorigin is "anonymous" and not same origin', async () => {
const builder = await build({
cwd: __dirname,
entry: { index: path.resolve(__dirname, './src/index.js') },
builderConfig: {
html: {
scriptLoading: 'blocking',
crossorigin: 'anonymous',
},
output: {
assetPrefix: '//aaaa.com',
},
},
});
const files = await builder.unwrapOutputJSON();
const html =
files[Object.keys(files).find(file => file.endsWith('index.html'))!];

expect(html).toContain('crossorigin="anonymous"></script>');
});
1 change: 1 addition & 0 deletions tests/e2e/builder/cases/html/cross-origin/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('1');
9 changes: 0 additions & 9 deletions tests/e2e/builder/cases/html/html.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ test.describe('html element set', () => {
description: 'a description of the page',
},
inject: 'body',
crossorigin: 'anonymous',
appIcon: './src/assets/icon.png',
favicon: './src/assets/icon.png',
},
Expand Down Expand Up @@ -137,14 +136,6 @@ test.describe('html element set', () => {
),
).toBeTruthy();
});

test('custom crossorigin', async () => {
const allScripts = /(<script [\s\S]*?>)/g.exec(mainContent);

expect(
allScripts?.every(data => data.includes('crossorigin="anonymous"')),
).toBeTruthy();
});
});

test('custom title', async ({ page }) => {
Expand Down
77 changes: 77 additions & 0 deletions tests/e2e/builder/cases/performance/load-resource/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,80 @@ test('should generate preload link when preload is defined', async () => {
),
).toBeTruthy();
});

test('should generate preload link with crossOrigin', async () => {
const builder = await build({
cwd: fixtures,
entry: {
main: join(fixtures, 'src/page1/index.ts'),
},
builderConfig: {
html: {
crossorigin: 'anonymous',
},
output: {
assetPrefix: '//aaa.com',
},
performance: {
preload: true,
},
},
});

const files = await builder.unwrapOutputJSON();

const asyncFileName = Object.keys(files).find(file =>
file.includes('/static/js/async/'),
)!;
const [, content] = Object.entries(files).find(([name]) =>
name.endsWith('.html'),
)!;

// test.js、test.css、test.png
expect(content.match(/rel="preload"/g)?.length).toBe(3);

expect(
content.includes(
`<link href="//aaa.com${asyncFileName.slice(
asyncFileName.indexOf('/static/js/async/'),
)}" rel="preload" as="script" crossorigin="">`,
),
).toBeTruthy();
});

test('should generate preload link without crossOrigin when same origin', async () => {
const builder = await build({
cwd: fixtures,
entry: {
main: join(fixtures, 'src/page1/index.ts'),
},
builderConfig: {
html: {
crossorigin: 'anonymous',
},
performance: {
preload: true,
},
},
});

const files = await builder.unwrapOutputJSON();

const asyncFileName = Object.keys(files).find(file =>
file.includes('/static/js/async/'),
)!;
const [, content] = Object.entries(files).find(([name]) =>
name.endsWith('.html'),
)!;

// test.js、test.css、test.png
expect(content.match(/rel="preload"/g)?.length).toBe(3);

expect(
content.includes(
`<link href="${asyncFileName.slice(
asyncFileName.indexOf('/static/js/async/'),
)}" rel="preload" as="script">`,
),
).toBeTruthy();
});