feat(runtime): 全站前端脚本统一接入 AksrtFrontend 全局 DOM 生命周期
- rehydrate.js 新增 boot()/aksrt:ready 事件,支持 boot 后晚注册立即以 init 触发 - aksrt-runtime.js 初始加载改走 AksrtFrontend.boot,tooltip/倒计时注册为组件 - 约 30 个前端脚本(首页、评论、支付、路由、页面叶子、widgets) 从直接 DOMContentLoaded 迁移为 AksrtFrontend.register,并加幂等标记 - section-navs 依赖改为 aksrt-runtime,保证注册时序 - 清理死代码:删除孤儿 loader.js/main.js 及 runtime/page-navs 遗留副本 - loader.js 移除已迁移模块的 tbquire 路径映射,main.js 相应瘦身 - build-public-assets.mjs 增加 runtime/main.min.js 自动压缩步骤 - 新增 4 个契约测试(homepage-dom/rehydrate-runtime/global-dom-init/ public-bundle-parity),3 个既有 harness 适配注册表初始化方式
This commit is contained in:
1 parent
5984d4e42d
commit
1f4a39d8f8
60 files changed
+1810
-1742
No files matched your search
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
|
||||
const root = path.resolve(__dirname, '..', '..');
|
||||
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
||||
|
||||
const publicSources = [
|
||||
'assets/js/core/comment-fixed-input.js',
|
||||
'assets/js/core/misc.js',
|
||||
'assets/js/follow.js',
|
||||
'assets/js/enhancements.js',
|
||||
'assets/js/enhancements2.js',
|
||||
'assets/js/color-extract.js',
|
||||
'assets/js/effects.js',
|
||||
'assets/js/captcha-front.js',
|
||||
'assets/widgets/frontend.js',
|
||||
];
|
||||
|
||||
const routedSources = [
|
||||
'assets/js/auth.js',
|
||||
'assets/js/author.js',
|
||||
'assets/js/routes/user-center.js',
|
||||
'assets/js/order-center.js',
|
||||
'assets/js/credits.js',
|
||||
'assets/js/signin.js',
|
||||
'assets/js/reward.js',
|
||||
'assets/js/report.js',
|
||||
'assets/js/page-navs.js',
|
||||
'assets/js/section-navs.js',
|
||||
'assets/js/posts-navs-page.js',
|
||||
'assets/js/front-submit.js',
|
||||
'assets/js/mini-upload.js',
|
||||
'assets/js/page-template.js',
|
||||
'assets/js/page-edit.js',
|
||||
'assets/js/precode.js',
|
||||
'assets/js/post-share.js',
|
||||
'assets/js/poster-share.js',
|
||||
'assets/js/pay-vip.js',
|
||||
'assets/js/video-player.js',
|
||||
'assets/js/download-status.js',
|
||||
'assets/js/ban-appeal-page.js',
|
||||
'assets/js/docs-navigation.js',
|
||||
'assets/js/input-expand.js',
|
||||
'assets/js/waterfall.js',
|
||||
'assets/js/yiyan.js',
|
||||
'assets/js/messages.js',
|
||||
'assets/js/site-notice.js',
|
||||
'assets/js/user-badge-actions.js',
|
||||
];
|
||||
|
||||
const readyPattern = /(?:document\.addEventListener\(\s*['"]DOMContentLoaded['"]|document\.readyState\s*===|jQuery\(document\)\.ready\s*\()/;
|
||||
|
||||
test('global DOM boot is the only initial page lifecycle owner', () => {
|
||||
const runtime = read('assets/js/aksrt-runtime.js');
|
||||
const registry = read('assets/js/rehydrate.js');
|
||||
assert.match(registry, /boot:\s*function/);
|
||||
assert.match(registry, /register:[\s\S]*?_booted/);
|
||||
assert.match(runtime, /AksrtFrontend\.boot\(root\)/);
|
||||
assert.match(runtime, /aksrt\.autoFun\(document, 'init'\)/);
|
||||
assert.match(runtime, /reason === 'init'[\s\S]*?AksrtFrontend\.boot\(root\)[\s\S]*?else[\s\S]*?AksrtFrontend\.rehydrate\(root, reason\)/);
|
||||
});
|
||||
|
||||
test('public bundle components register instead of owning DOM ready', () => {
|
||||
for (const file of publicSources) {
|
||||
const source = read(file);
|
||||
assert.doesNotMatch(source, readyPattern, `${file} still owns a DOM-ready boot`);
|
||||
assert.match(source, /AksrtFrontend\.register\(/, `${file} must register with the global lifecycle`);
|
||||
}
|
||||
});
|
||||
|
||||
test('active routed UI scripts register with the global lifecycle', () => {
|
||||
for (const file of routedSources) {
|
||||
const source = read(file);
|
||||
if (/API|coordinator|runtime/i.test(source.slice(0, 500)) && !/querySelector|addEventListener/.test(source)) continue;
|
||||
assert.doesNotMatch(source, readyPattern, `${file} still owns a DOM-ready boot`);
|
||||
assert.match(source, /AksrtFrontend\.register\(/, `${file} must register with the global lifecycle`);
|
||||
}
|
||||
});
|
||||
|
||||
test('high-risk routed components remain idempotent under global rehydrate', () => {
|
||||
const pageNavs = read('assets/js/page-navs.js');
|
||||
const sectionNavs = read('assets/js/section-navs.js');
|
||||
const poster = read('assets/js/poster-share.js');
|
||||
|
||||
assert.match(pageNavs, /__aksrtPageNavInitialized/);
|
||||
assert.match(pageNavs, /var grid = self\.root\.querySelector\('\.aksrt-doc-grid'\)/);
|
||||
assert.match(sectionNavs, /if \(scrollspyBound\) return/);
|
||||
assert.match(sectionNavs, /if \(autoHashHandled \|\| !window\.location\.hash\) return/);
|
||||
assert.match(poster, /triggers\.filter\(function \(el\) \{ return el\.dataset\.posterInit !== '1'; \}\)/);
|
||||
assert.doesNotMatch(poster, /triggers\.some\(function \(el\) \{ return el\.dataset\.posterInit === '1'; \}\)/);
|
||||
});
|
||||
|
||||
test('resource and SDK load listeners are not mistaken for page boot', () => {
|
||||
const captcha = read('assets/js/captcha-front.js');
|
||||
const color = read('assets/js/color-extract.js');
|
||||
assert.match(captcha, /\.onload\s*=|addEventListener\(['"]load['"]/);
|
||||
assert.match(color, /addEventListener\(['"]load['"]/);
|
||||
assert.doesNotMatch(captcha, /DOMContentLoaded/);
|
||||
assert.doesNotMatch(color, /DOMContentLoaded/);
|
||||
});
|
||||
|
||||
test('route scripts that use the registry load after the public runtime', () => {
|
||||
const scripts = read('src/Core/ScriptsPart1b.php') + read('src/Core/ScriptsPart1c.php');
|
||||
const files = ['section-navs.js', 'page-navs.js', 'posts-navs-page.js', 'author.js', 'auth.js'];
|
||||
for (const file of files) {
|
||||
const uri = scripts.indexOf("/assets/js/" + file);
|
||||
assert.notEqual(uri, -1, `${file} must be enqueued`);
|
||||
const blockStart = scripts.lastIndexOf('wp_enqueue_script(', uri);
|
||||
const blockEnd = scripts.indexOf(');', uri);
|
||||
const block = scripts.slice(blockStart, blockEnd + 2);
|
||||
assert.match(block, /aksrt-runtime/, `${file} must depend on aksrt-runtime`);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user