优化钩子调用方式

This commit is contained in:
ProjectXero 2024-09-30 17:49:16 +08:00
parent 6f82e2a69e
commit 2d0734efa8
No known key found for this signature in database
GPG Key ID: 5B1AA72F4425593E
8 changed files with 128 additions and 100 deletions

View File

@ -3,14 +3,10 @@ const { Project } = require('ts-morph');
const { createRequire } = require('module');
const { resolve: resolvePath, relative: relativePath } = require('path');
const { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync } = require('fs');
const { split, replacePieces } = require('./split');
const { split, replacePieces } = require('./split.js');
const { execSync } = require('child_process');
const basePath = resolvePath(__dirname, '..');
const originalPath = resolvePath(basePath, 'original');
const translatedPath = resolvePath(basePath, 'translated');
const distPath = resolvePath(basePath, 'dist');
const hookPath = resolvePath(__dirname, 'hooks');
const { loadHooks } = require('./hooks.js');
const { basePath, originalPath, translatedPath, distPath } = require('./utils.js');
const namespacePrefix = '@minecraft/';
const botModules = ['@minecraft/vanilla-data'];
@ -82,30 +78,8 @@ function getCommonStringFromStart(a, b) {
}
async function build(translated) {
// 加载钩子
mkdirSync(hookPath, { recursive: true });
const hookScripts = readdirSync(hookPath).filter((name) => /\.(cjs|js)$/i.test(name));
hookScripts.sort();
const scriptRequire = createRequire(hookPath);
const scriptHooks = hookScripts.map((name) => scriptRequire(resolvePath(hookPath, name)));
const runHooks = async (event, arg) => {
for (let i = 0; i < scriptHooks.length; i++) {
const scriptHook = scriptHooks[i];
const logName = `[${event}] ${hookScripts[i]}`;
let hookFunc;
if (typeof scriptHooks === 'function') {
hookFunc = scriptHook.bind(null, event);
} else {
hookFunc = scriptHook[event];
}
if (hookFunc) {
console.time(logName);
const result = hookFunc(arg);
if (result && result.then) await result;
console.timeEnd(logName);
}
}
};
const runHooks = loadHooks();
const hookEventContext = {};
// 尝试加载翻译文件对应版本的 package.json
console.time('[restoreDependencies] Total');
@ -129,7 +103,8 @@ async function build(translated) {
// 从依赖中构建用于生成文档的项目
console.time('[loadOriginal] Total');
await runHooks('beforeLoad', {});
Object.assign(hookEventContext, { basePath });
await runHooks('beforeLoad', hookEventContext);
const tsConfigFilePath = resolvePath(translatedPath, 'tsconfig.json');
const project = new Project({
tsConfigFilePath,
@ -197,7 +172,8 @@ async function build(translated) {
dependencies[moduleName] = version;
}
});
await runHooks('afterLoad', { project, sourceFiles, dependencies });
Object.assign(hookEventContext, { basePath, project, sourceFiles, dependencies });
await runHooks('afterLoad', hookEventContext);
console.timeEnd('[loadOriginal] Total');
if (translated) {
@ -207,7 +183,7 @@ async function build(translated) {
const pieces = split(sourceFile);
replacePieces(sourceFile, pieces);
});
await runHooks('afterTranslate', { project, sourceFiles, dependencies });
await runHooks('afterTranslate', hookEventContext);
console.timeEnd('[translate] Total');
}
@ -223,20 +199,22 @@ async function build(translated) {
},
[new TypeDoc.TSConfigReader()]
);
await runHooks('beforeConvert', { project, sourceFiles, dependencies, tsdocApplication });
Object.assign(hookEventContext, { tsdocApplication });
await runHooks('beforeConvert', hookEventContext);
const tsdocProject = await tsdocApplication.convert();
console.timeEnd('[analyze] Total');
if (tsdocProject) {
console.time('[emit] Total');
await runHooks('afterConvert', { project, sourceFiles, dependencies, tsdocApplication, tsdocProject });
Object.assign(hookEventContext, { tsdocProject });
await runHooks('afterConvert', hookEventContext);
await tsdocApplication.generateDocs(tsdocProject, distPath);
await tsdocApplication.generateJson(tsdocProject, resolvePath(distPath, 'index.json'));
await runHooks('afterEmit', { project, sourceFiles, dependencies, tsdocApplication, tsdocProject });
await runHooks('afterEmit', hookEventContext);
console.timeEnd('[emit] Total');
} else {
throw new Error('Convert failed');
}
return { project, sourceFiles, dependencies, tsdocProject };
return hookEventContext;
}
module.exports = {

34
script/hooks.js Normal file
View File

@ -0,0 +1,34 @@
const { createRequire } = require('module');
const { resolve: resolvePath } = require('path');
const { readdirSync, mkdirSync } = require('fs');
const hookPath = resolvePath(__dirname, 'hooks');
function loadHooks() {
mkdirSync(hookPath, { recursive: true });
const hookScripts = readdirSync(hookPath).filter((name) => /\.(cjs|js)$/i.test(name));
hookScripts.sort();
const scriptRequire = createRequire(hookPath);
const scriptHooks = hookScripts.map((name) => scriptRequire(resolvePath(hookPath, name)));
const runHooks = async (event, arg) => {
for (let i = 0; i < scriptHooks.length; i++) {
const scriptHook = scriptHooks[i];
const logName = `[${event}] ${hookScripts[i]}`;
let hookFunc;
if (typeof scriptHooks === 'function') {
hookFunc = scriptHook.bind(null, event);
} else {
hookFunc = scriptHook[event];
}
if (hookFunc) {
console.time(logName);
const result = hookFunc(arg);
if (result && result.then) await result;
console.timeEnd(logName);
}
}
};
return runHooks;
}
module.exports = { loadHooks };

View File

@ -1,8 +1,7 @@
const { execSync } = require('child_process');
const { resolve: resolvePath } = require('path');
const { ReflectionKind } = require('typedoc');
const basePath = resolvePath(__dirname, '..', '..');
const { git, translatedPath } = require('../utils.js');
const { readFileSync, writeFileSync } = require('fs');
const kindMap = [
[ReflectionKind.Enum, 'enums', '枚举', 'enums'],
@ -35,10 +34,6 @@ function getReflectionUrl(refl) {
return `./${kindInfo.docMapping}/${parts.join('.')}.html`;
}
function git(args) {
return execSync(`git ${args}`, { cwd: basePath });
}
function getCurrentHead() {
return git('rev-parse --abbrev-ref HEAD').toString('utf-8').trim();
}
@ -195,6 +190,17 @@ function analyzeTranslateState() {
return statusMap;
}
function extractVersionInfo(versionString) {
const match = /^([\d.]+-\w+)\.([\d.]+)-(\w+)(\.\d+)?$/.exec(versionString);
if (match) {
const [, version, gameVersion, gamePreRelease, gameBuild] = match;
if (gameBuild) {
return { version, gamePreRelease, gameVersion: gameVersion + gameBuild };
}
return { version, gamePreRelease, gameVersion };
}
}
const namespacePrefix = '@minecraft/';
const stateDescMap = {
untranslated: '未翻译',
@ -291,5 +297,29 @@ module.exports = {
if (reviewPieces.length > 0) {
reviewPieces.forEach((piecePath) => console.log(`[review] Review required: ${piecePath}`));
}
},
afterUpdate({ dependencies }) {
const readMePath = resolvePath(translatedPath, 'README.md');
const readMe = readFileSync(readMePath, 'utf-8');
const summaryLines = ['<!-- summary start -->', '', 'NPM 包:', '', '|包名|版本|', '| - | - |'];
let gameVersion;
Object.entries(dependencies).forEach(([moduleName, version]) => {
let versionString = version;
const versionInfo = extractVersionInfo(version);
if (versionInfo) {
if (!gameVersion) gameVersion = versionInfo.gameVersion;
versionString = versionInfo.version;
}
const npmURL = `https://www.npmjs.com/package/${moduleName}`;
summaryLines.push(`|[${moduleName}](${npmURL})|\`${versionString}\`|`);
});
summaryLines.push(['', `游戏版本号:\`${gameVersion}\``, '', '<!-- summary end -->']);
const newReadMe = readMe.replace(
/<!-- summary start -->\n\n[^]+\n\n<!-- summary end -->/,
summaryLines.flat().join('\n')
);
writeFileSync(readMePath, newReadMe);
}
};

View File

@ -3,7 +3,15 @@ import { Application, ProjectReflection } from 'typedoc';
declare type HookFunction<Context> = (context: Context) => void | Promise<void>;
declare interface TranslateHookContext {
declare interface HookContext {
basePath: string;
originalPath: string;
translatingPath: string;
translatedPath: string;
distPath: string;
}
declare interface TranslateHookContext extends HookContext {
project: Project;
sourceFiles: SourceFile[];
dependencies: Record<string, string>;
@ -18,10 +26,11 @@ declare interface AfterConvertHookContext extends BeforeConvertHookContext {
}
declare interface Hook {
beforeLoad?: HookFunction<void>;
beforeLoad?: HookFunction<HookContext>;
afterLoad?: HookFunction<TranslateHookContext>;
afterTranslate?: HookFunction<TranslateHookContext>;
beforeConvert?: HookFunction<BeforeConvertHookContext>;
afterConvert?: HookFunction<AfterConvertHookContext>;
afterEmit?: HookFunction<AfterConvertHookContext>;
afterUpdate?: HookFunction<AfterConvertHookContext>;
}

View File

@ -1,11 +1,4 @@
const { execSync } = require('child_process');
const { resolve: resolvePath } = require('path');
const basePath = resolvePath(__dirname, '..');
function git(args) {
return execSync(`git ${args}`, { cwd: basePath });
}
const { git } = require('./utils.js');
function listTrackingFiles(branch) {
const files = {};

View File

@ -2,10 +2,7 @@ const { SyntaxKind, ts } = require('ts-morph');
const { resolve: resolvePath, relative: relativePath, sep: pathSep } = require('path');
const { sep: pathSepPosix } = require('path/posix');
const { mkdirSync, writeFileSync, existsSync, readFileSync } = require('fs');
const basePath = resolvePath(__dirname, '..');
const translatingPath = resolvePath(basePath, 'translate-pieces');
const translatedPath = resolvePath(basePath, 'translated');
const { translatingPath, translatedPath } = require('./utils');
const SkippedTopLevelSyntaxKinds = [
SyntaxKind.EndOfFileToken,

View File

@ -3,26 +3,14 @@ const { readFileSync, writeFileSync, rmSync, existsSync } = require('fs');
const { resolve: resolvePath } = require('path');
const { build } = require('./build.js');
const { split, writePiece } = require('./split.js');
const basePath = resolvePath(__dirname, '..');
const originalPath = resolvePath(basePath, 'original');
const translatingPath = resolvePath(basePath, 'translate-pieces');
const translatedPath = resolvePath(basePath, 'translated');
function extractVersionInfo(versionString) {
const match = /^([\d.]+-\w+)\.([\d.]+)-(\w+)(\.\d+)?$/.exec(versionString);
if (match) {
const [, version, gameVersion, gamePreRelease, gameBuild] = match;
if (gameBuild) {
return { version, gamePreRelease, gameVersion: gameVersion + gameBuild };
}
return { version, gamePreRelease, gameVersion };
}
}
const { loadHooks } = require('./hooks.js');
const { basePath, originalPath, translatingPath, translatedPath } = require('./utils');
const excludedPackages = ['@minecraft/dummy-package', '@minecraft/core-build-tasks', '@minecraft/creator-tools'];
async function main() {
const runHooks = loadHooks();
// 强制检出 original 分支
const head = execSync('git rev-parse --abbrev-ref HEAD', {
cwd: basePath
@ -61,7 +49,8 @@ async function main() {
}
// 不使用翻译构建项目
const { sourceFiles, dependencies } = await build(false);
const buildResult = await build(false);
const { sourceFiles, dependencies } = buildResult;
// 检查是否所有包都在依赖中
const missingDependencies = onlinePackageNames.filter((packageName) => !(packageName in dependencies));
@ -75,30 +64,7 @@ async function main() {
const pieces = split(sourceFile);
pieces.forEach((piece) => writePiece(sourceFile, piece));
});
// 生成 README.md
const readMePath = resolvePath(translatedPath, 'README.md');
const readMe = readFileSync(readMePath, 'utf-8');
const summaryLines = ['<!-- summary start -->', '', 'NPM 包:', '', '|包名|版本|', '| - | - |'];
let gameVersion;
Object.entries(dependencies).forEach(([moduleName, version]) => {
let versionString = version;
const versionInfo = extractVersionInfo(version);
if (versionInfo) {
if (!gameVersion) gameVersion = versionInfo.gameVersion;
versionString = versionInfo.version;
}
const npmURL = `https://www.npmjs.com/package/${moduleName}`;
summaryLines.push(`|[${moduleName}](${npmURL})|\`${versionString}\`|`);
});
summaryLines.push(['', `游戏版本号:\`${gameVersion}\``, '', '<!-- summary end -->']);
const newReadMe = readMe.replace(
/<!-- summary start -->\n\n[^]+\n\n<!-- summary end -->/,
summaryLines.flat().join('\n')
);
writeFileSync(readMePath, newReadMe);
await runHooks('afterUpdate', buildResult);
// 生成 package.json 快照
const packageInfo = JSON.parse(readFileSync(packageInfoPath, 'utf-8'));

21
script/utils.js Normal file
View File

@ -0,0 +1,21 @@
const { execSync } = require('child_process');
const { resolve: resolvePath } = require('path');
const basePath = resolvePath(__dirname, '..');
const originalPath = resolvePath(basePath, 'original');
const translatingPath = resolvePath(basePath, 'translate-pieces');
const translatedPath = resolvePath(basePath, 'translated');
const distPath = resolvePath(basePath, 'dist');
function git(args) {
return execSync(`git ${args}`, { cwd: basePath });
}
module.exports = {
basePath,
originalPath,
translatingPath,
translatedPath,
distPath,
git
};