提交qiming-dev-inject
This commit is contained in:
206
qiming-dev-inject/lib/inject.js
Normal file
206
qiming-dev-inject/lib/inject.js
Normal file
@@ -0,0 +1,206 @@
|
||||
import {
|
||||
lookupFiles,
|
||||
readFile,
|
||||
writeFile,
|
||||
parseRemoteType,
|
||||
generateScriptTag,
|
||||
injectScriptToHtml,
|
||||
removeInjectedScripts,
|
||||
log,
|
||||
logInfo,
|
||||
logSuccess,
|
||||
logError,
|
||||
} from './utils.js';
|
||||
import {
|
||||
detectProjectType,
|
||||
smartInject as smartInjectFramework,
|
||||
} from './framework-inject.js';
|
||||
import { readFileSync } from 'fs';
|
||||
import { dirname, join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
* 获取当前包版本号
|
||||
*/
|
||||
function getVersion() {
|
||||
try {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const packagePath = join(__dirname, '..', 'package.json');
|
||||
const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
|
||||
return packageJson.version;
|
||||
} catch (error) {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装脚本 - 支持框架感知
|
||||
*/
|
||||
export async function installScript(options = {}) {
|
||||
const {
|
||||
remote,
|
||||
file,
|
||||
dryRun = false,
|
||||
verbose = false,
|
||||
framework = false,
|
||||
} = options;
|
||||
|
||||
const version = getVersion();
|
||||
logInfo(`开始执行脚本注入... (v${version})`);
|
||||
|
||||
// 检测是否使用框架注入模式
|
||||
if (framework) {
|
||||
logInfo('使用框架感知注入模式...');
|
||||
|
||||
// 检测项目类型(只在 verbose 模式下输出,避免重复)
|
||||
const projectType = detectProjectType();
|
||||
log(`检测到项目类型: ${projectType}`, verbose);
|
||||
|
||||
// 对于现代框架,使用智能注入
|
||||
if (
|
||||
['vite', 'next-app', 'next-pages', 'next-hybrid'].includes(projectType)
|
||||
) {
|
||||
try {
|
||||
const success = smartInjectFramework({ remote, dryRun, verbose });
|
||||
// smartInjectFramework 内部已经输出了所有必要的日志,这里不需要重复输出
|
||||
return success;
|
||||
} catch (error) {
|
||||
logError(`框架注入失败: ${error.message}`);
|
||||
logInfo('回退到传统 HTML 注入模式...');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 传统 HTML 注入模式(兜底方案)
|
||||
logInfo('使用传统 HTML 注入模式...');
|
||||
|
||||
// 查找 HTML 文件
|
||||
const targetFiles = file ? [file] : lookupFiles();
|
||||
|
||||
if (targetFiles.length === 0) {
|
||||
throw new Error('未找到 HTML 文件');
|
||||
}
|
||||
|
||||
log(`找到 ${targetFiles.length} 个 HTML 文件:`, verbose);
|
||||
targetFiles.forEach(f => log(` - ${f}`, verbose));
|
||||
|
||||
// 解析远程脚本类型和生成脚本标签
|
||||
const remoteType = parseRemoteType(remote);
|
||||
const scriptTag = generateScriptTag(remote, remoteType);
|
||||
|
||||
log(`脚本类型: ${remoteType}`, verbose);
|
||||
log(`脚本标签: ${scriptTag}`, verbose);
|
||||
|
||||
// 处理每个 HTML 文件
|
||||
let successCount = 0;
|
||||
|
||||
for (const htmlFile of targetFiles) {
|
||||
try {
|
||||
// 读取 HTML 内容
|
||||
let htmlContent = readFile(htmlFile);
|
||||
const originalContent = htmlContent;
|
||||
|
||||
// 移除之前注入的脚本(避免重复)
|
||||
htmlContent = removeInjectedScripts(htmlContent);
|
||||
|
||||
// 注入新脚本 - 传入 remote URL 以使用动态时间戳
|
||||
const newContent = injectScriptToHtml(htmlContent, scriptTag, 'dev-inject', remote);
|
||||
|
||||
// 检查是否有变化
|
||||
if (newContent === originalContent) {
|
||||
log(`文件 ${htmlFile} 无需更新`, verbose);
|
||||
successCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if (!dryRun) {
|
||||
writeFile(htmlFile, newContent);
|
||||
logSuccess(`已注入脚本到 ${htmlFile}`);
|
||||
} else {
|
||||
logInfo(`[DRY RUN] 将注入脚本到 ${htmlFile}`);
|
||||
}
|
||||
|
||||
successCount++;
|
||||
} catch (error) {
|
||||
logError(`处理文件 ${htmlFile} 失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (successCount > 0) {
|
||||
logSuccess(`成功处理 ${successCount} 个文件`);
|
||||
logInfo(`脚本地址: ${remote}`);
|
||||
|
||||
if (remoteType === 'absolute-path') {
|
||||
logInfo('提示:请确保静态文件服务器可以访问该路径');
|
||||
}
|
||||
} else {
|
||||
logError('没有成功处理任何文件');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载脚本从 HTML 文件
|
||||
*/
|
||||
export async function uninstallScript(options = {}) {
|
||||
const { file, dryRun = false, verbose = false, framework } = options;
|
||||
|
||||
// 如果指定了 framework 标志,使用框架感知卸载
|
||||
if (framework) {
|
||||
const { smartUninstall } = await import('./framework-inject.js');
|
||||
return smartUninstall(options);
|
||||
}
|
||||
|
||||
logInfo('开始移除注入的脚本...');
|
||||
|
||||
// 查找 HTML 文件
|
||||
const targetFiles = file ? [file] : lookupFiles();
|
||||
|
||||
if (targetFiles.length === 0) {
|
||||
logInfo('未找到 HTML 文件');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`找到 ${targetFiles.length} 个 HTML 文件:`, verbose);
|
||||
targetFiles.forEach(f => log(` - ${f}`, verbose));
|
||||
|
||||
// 处理每个 HTML 文件
|
||||
let successCount = 0;
|
||||
|
||||
for (const htmlFile of targetFiles) {
|
||||
try {
|
||||
// 读取 HTML 内容
|
||||
let htmlContent = readFile(htmlFile);
|
||||
const originalContent = htmlContent;
|
||||
|
||||
// 移除注入的脚本
|
||||
const newContent = removeInjectedScripts(htmlContent);
|
||||
|
||||
// 检查是否有变化
|
||||
if (newContent === originalContent) {
|
||||
log(`文件 ${htmlFile} 无需清理`, verbose);
|
||||
successCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if (!dryRun) {
|
||||
writeFile(htmlFile, newContent);
|
||||
logSuccess(`已从 ${htmlFile} 移除注入的脚本`);
|
||||
} else {
|
||||
logInfo(`[DRY RUN] 将从 ${htmlFile} 移除注入的脚本`);
|
||||
}
|
||||
|
||||
successCount++;
|
||||
} catch (error) {
|
||||
logError(`处理文件 ${htmlFile} 失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (successCount > 0) {
|
||||
logSuccess(`成功清理 ${successCount} 个文件`);
|
||||
} else {
|
||||
logInfo('没有找到需要清理的注入脚本');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user