feat(09-22): strip replay decorators in both builds + content list filtering

This commit is contained in:
AncTe committed 2026-10-21 16:10:55 +08:00
1 parent 9d291a6f80
commit 6015628d9b
7 files changed
+913 -150

No files matched your search

+89
View File
@@ -1,5 +1,6 @@
import { Plugin } from 'vite';
import { ModuleInfo } from 'rollup';
import ts from 'typescript';
/** 数据端构建中禁止出现的渲染端模块说明符,第一项为匹配规则,第二项为报错时的命中标记 */
const SPECIFIER_BANS: ReadonlyArray<readonly [RegExp, string]> = [
@@ -76,3 +77,91 @@ export function dataIsolationPlugin(): Plugin {
}
};
}
/** 构建时需要删除的录像装饰器名称 */
const REPLAY_DECORATORS: ReadonlyArray<string> = [
'shouldReplay',
'ignoreReplay'
];
/** 需要处理录像装饰器的源文件后缀 */
const SCRIPT_EXTENSIONS: ReadonlyArray<string> = ['.ts', '.tsx'];
/** 判定第三方依赖路径的 node_modules 片段 */
const NODE_MODULES_SEGMENT = /[/\\]node_modules[/\\]/;
/**
* 删除源码中所有录像装饰器的应用,只按装饰器节点的文本范围切片,不会误删字符串与注释。
* @param code 源码文本
* @param fileName 源码文件名,用于确定脚本种类
*/
export function removeReplayDecorators(code: string, fileName: string): string {
const kind = fileName.endsWith('.tsx')
? ts.ScriptKind.TSX
: ts.ScriptKind.TS;
const sourceFile = ts.createSourceFile(
fileName,
code,
ts.ScriptTarget.Latest,
true,
kind
);
const ranges: Array<[number, number]> = [];
const stack: ts.Node[] = [sourceFile];
while (stack.length > 0) {
const node = stack.pop()!;
if (ts.canHaveDecorators(node)) {
const decorators = ts.getDecorators(node);
if (decorators) {
for (const decorator of decorators) {
const expression = decorator.expression;
const target = ts.isCallExpression(expression)
? expression.expression
: expression;
if (
ts.isIdentifier(target) &&
REPLAY_DECORATORS.includes(target.text)
) {
ranges.push([
decorator.getStart(sourceFile),
decorator.getEnd()
]);
}
}
}
}
ts.forEachChild(node, child => {
stack.push(child);
});
}
if (ranges.length === 0) return code;
let result = code;
ranges.sort((a, b) => b[0] - a[0]);
for (const [start, end] of ranges) {
result = result.slice(0, start) + result.slice(end);
}
return result;
}
/**
* 创建删除录像装饰器的构建插件,仅处理项目自身的 TypeScript 源码,跳过第三方依赖。
*/
export function replayDecoratorPlugin(): Plugin {
return {
name: 'mota-replay-decorator',
enforce: 'pre',
apply: 'build',
transform(code, id) {
const fileName = id.split('?')[0];
if (NODE_MODULES_SEGMENT.test(fileName)) return null;
const matched = SCRIPT_EXTENSIONS.some(extension =>
fileName.endsWith(extension)
);
if (!matched) return null;
const result = removeReplayDecorators(code, fileName);
if (result === code) return null;
return { code: result, map: null };
}
};
}