Skip to content

Commit

Permalink
perf(asets-retry): use SWC to transform and minify code (#4312)
Browse files Browse the repository at this point in the history
  • Loading branch information
chenjiahan authored Jan 2, 2025
1 parent 1bc65ee commit bf2ed4c
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 169 deletions.
5 changes: 1 addition & 4 deletions packages/plugin-assets-retry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,11 @@
"dev": "rslib build --watch"
},
"devDependencies": {
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/preset-typescript": "^7.26.0",
"@rsbuild/core": "workspace:*",
"@rslib/core": "0.2.2",
"@swc/core": "^1.10.4",
"@types/serialize-javascript": "^5.0.4",
"serialize-javascript": "^6.0.2",
"terser": "5.37.0",
"typescript": "^5.7.2"
},
"peerDependencies": {
Expand Down
42 changes: 20 additions & 22 deletions packages/plugin-assets-retry/scripts/postCompile.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// @ts-check
import { existsSync } from 'node:fs';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { performance } from 'node:perf_hooks';
import { fileURLToPath } from 'node:url';
import { transformAsync } from '@babel/core';
import { minify, transform } from '@swc/core';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Expand All @@ -17,34 +18,31 @@ const __dirname = path.dirname(__filename);
async function compileRuntimeFile(filename) {
const sourceFilePath = path.join(__dirname, `../src/runtime/${filename}.ts`);

const { minify } = await import('terser');
const runtimeCode = await readFile(sourceFilePath, 'utf8');
const distPath = path.join(__dirname, `../dist/runtime/${filename}.js`);
const distMinPath = path.join(
__dirname,
`../dist/runtime/${filename}.min.js`,
);
const { code } = await transformAsync(runtimeCode, {
presets: [
'@babel/preset-typescript',
[
'@babel/preset-env',
{
targets:
'iOS >= 9, Android >= 4.4, last 2 versions, > 0.2%, not dead',
},
],
],
filename: sourceFilePath,
});
const { code: minifiedRuntimeCode } = await minify(
{
[distPath]: code,
},
{
ecma: 5,

const { code } = await transform(runtimeCode, {
jsc: {
target: 'es5',
parser: {
syntax: 'typescript',
},
},
);
// Output script file to be used in `<script>` tag
isModule: false,
sourceFileName: sourceFilePath,
});

const { code: minifiedRuntimeCode } = await minify(code, {
ecma: 5,
// allows SWC to mangle function names
module: true,
});

await Promise.all([
writeFile(distPath, code),
writeFile(distMinPath, minifiedRuntimeCode),
Expand Down
5 changes: 4 additions & 1 deletion packages/plugin-assets-retry/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ async function getRetryCode(
);
const runtimeCode = await fs.promises.readFile(runtimeFilePath, 'utf-8');

return `(function(){${runtimeCode};init(${serialize(restOptions)});})()`;
return `(function(){${runtimeCode}})()`.replace(
'__RUNTIME_GLOBALS_OPTIONS__',
serialize(restOptions),
);
}

export const pluginAssetsRetry = (
Expand Down
2 changes: 0 additions & 2 deletions packages/plugin-assets-retry/src/runtime/asyncChunkRetry.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// rsbuild/runtime/async-chunk-retry
import type { AssetsRetryHookContext, RuntimeRetryOptions } from '../types.js';

type ChunkId = string; // e.g: src_AsyncCompTest_tsx
type ChunkFilename = string; // e.g: static/js/async/src_AsyncCompTest_tsx.js
type ChunkSrcUrl = string; // publicPath + ChunkFilename e.g: http://localhost:3000/static/js/async/src_AsyncCompTest_tsx.js
Expand Down
94 changes: 45 additions & 49 deletions packages/plugin-assets-retry/src/runtime/initialChunkRetry.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
// rsbuild/runtime/initial-chunk-retry
import type { CrossOrigin } from '@rsbuild/core';
import type { AssetsRetryHookContext, RuntimeRetryOptions } from '../types.js';

interface ScriptElementAttributes {
url: string;
times: number;
Expand All @@ -20,6 +17,7 @@ const TYPES = Object.keys(TAG_TYPE);
declare global {
// global variables shared with async chunk
var __RB_ASYNC_CHUNKS__: Record<string, boolean>;
var __RUNTIME_GLOBALS_OPTIONS__: RuntimeRetryOptions;
}

// this function is the same as async chunk retry
Expand Down Expand Up @@ -354,55 +352,53 @@ function resourceMonitor(
}
}

// @ts-expect-error init is a global function, ignore ts(6133)
function init(options: RuntimeRetryOptions) {
const config: RuntimeRetryOptions = {};
const config: RuntimeRetryOptions = {};
const options = __RUNTIME_GLOBALS_OPTIONS__;

for (const key in defaultConfig) {
// @ts-ignore
config[key] = defaultConfig[key];
}
for (const key in defaultConfig) {
// @ts-ignore
config[key] = defaultConfig[key];
}

for (const key in options) {
// @ts-ignore
config[key] = options[key];
}
for (const key in options) {
// @ts-ignore
config[key] = options[key];
}

// Normalize config
if (!Array.isArray(config.type) || config.type.length === 0) {
config.type = defaultConfig.type;
}
if (!Array.isArray(config.domain) || config.domain.length === 0) {
config.domain = defaultConfig.domain;
}
// Normalize config
if (!Array.isArray(config.type) || config.type.length === 0) {
config.type = defaultConfig.type;
}
if (!Array.isArray(config.domain) || config.domain.length === 0) {
config.domain = defaultConfig.domain;
}

if (Array.isArray(config.domain)) {
config.domain = config.domain.filter(Boolean);
}
if (Array.isArray(config.domain)) {
config.domain = config.domain.filter(Boolean);
}

// init global variables shared with async chunk
if (typeof window !== 'undefined' && !window.__RB_ASYNC_CHUNKS__) {
window.__RB_ASYNC_CHUNKS__ = {};
}
// Bind event in window
try {
resourceMonitor(
(e: Event) => {
try {
retry(config, e);
} catch (err) {
console.error('retry error captured', err);
}
},
(e: Event) => {
try {
load(config, e);
} catch (err) {
console.error('load error captured', err);
}
},
);
} catch (err) {
console.error('monitor error captured', err);
}
// init global variables shared with async chunk
if (typeof window !== 'undefined' && !window.__RB_ASYNC_CHUNKS__) {
window.__RB_ASYNC_CHUNKS__ = {};
}
// Bind event in window
try {
resourceMonitor(
(e: Event) => {
try {
retry(config, e);
} catch (err) {
console.error('retry error captured', err);
}
},
(e: Event) => {
try {
load(config, e);
} catch (err) {
console.error('load error captured', err);
}
},
);
} catch (err) {
console.error('monitor error captured', err);
}
6 changes: 6 additions & 0 deletions packages/plugin-assets-retry/src/runtime/runtime.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Declare types for runtime code
// We use SWC transform to compile runtime code, so we can not import types from other modules
declare type CrossOrigin = import('@rsbuild/core').CrossOrigin;
declare type AssetsRetryHookContext =
import('../types.js').AssetsRetryHookContext;
declare type RuntimeRetryOptions = import('../types.js').RuntimeRetryOptions;
Loading

1 comment on commit bf2ed4c

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ran ecosystem CI: Open

suite result
modernjs ❌ failure
plugins ❌ failure
rspress ✅ success
rslib ❌ failure
examples ✅ success

Please sign in to comment.