Fix playground bugs and remove TypeScript compiler

- Fix BUG1: JavaScript timeout with proper thread interruption using ScheduledExecutorService
- Fix BUG2: Add URL regex validation before execution in playground test API
- Fix BUG3: Register published parsers to CustomParserRegistry on save/update/delete
- Remove TypeScript compiler functionality (tsCompiler.js, dependencies, UI)
- Add password authentication for playground access
- Add mobile responsive layout support
- Load playground parsers on application startup
This commit is contained in:
q
2026-01-02 19:24:47 +08:00
parent c79702eba8
commit 66b9bcc53a
10 changed files with 865 additions and 490 deletions

View File

@@ -57,11 +57,10 @@
<div class="card-header">
<div class="header-left">
<span class="title">JS解析器演练场</span>
<!-- 语言选择器 -->
<el-radio-group v-model="codeLanguage" size="small" style="margin-left: 15px;" @change="onLanguageChange">
<el-radio-button :label="LANGUAGE.JAVASCRIPT">JavaScript</el-radio-button>
<el-radio-button :label="LANGUAGE.TYPESCRIPT">TypeScript</el-radio-button>
</el-radio-group>
<!-- 语言显示仅支持JavaScript -->
<span style="margin-left: 15px; color: var(--el-text-color-secondary); font-size: 12px;">
JavaScript (ES5)
</span>
</div>
<div class="header-actions">
<!-- 主要操作 -->
@@ -536,7 +535,6 @@ import 'splitpanes/dist/splitpanes.css';
import MonacoEditor from '@/components/MonacoEditor.vue';
import { playgroundApi } from '@/utils/playgroundApi';
import { configureMonacoTypes, loadTypesFromApi } from '@/utils/monacoTypes';
import { compileToES5, isTypeScriptCode, formatCompileErrors } from '@/utils/tsCompiler';
import JsonViewer from 'vue3-json-viewer';
export default {
@@ -550,15 +548,11 @@ export default {
setup() {
// 语言常量
const LANGUAGE = {
JAVASCRIPT: 'JavaScript',
TYPESCRIPT: 'TypeScript'
JAVASCRIPT: 'JavaScript'
};
const editorRef = ref(null);
const jsCode = ref('');
const codeLanguage = ref(LANGUAGE.JAVASCRIPT); // 新增:代码语言选择
const compiledES5Code = ref(''); // 新增编译后的ES5代码
const compileStatus = ref({ success: true, errors: [] }); // 新增:编译状态
// ===== 加载和认证状态 =====
const loading = ref(true);
@@ -699,90 +693,6 @@ function parseById(shareLinkInfo, http, logger) {
return "https://example.com/download?id=" + fileId;
}`;
// TypeScript示例代码模板
const exampleTypeScriptCode = `// ==UserScript==
// @name TypeScript示例解析器
// @type ts_example_parser
// @displayName TypeScript示例网盘
// @description 使用TypeScript实现的示例解析器
// @match https?://example\.com/s/(?<KEY>\\w+)
// @author yourname
// @version 1.0.0
// ==/UserScript==
/**
* 解析单个文件下载链接
* @param shareLinkInfo - 分享链接信息
* @param http - HTTP客户端
* @param logger - 日志对象
* @returns 下载链接
*/
async function parse(
shareLinkInfo: any,
http: any,
logger: any
): Promise<string> {
const url: string = shareLinkInfo.getShareUrl();
logger.info(\`开始解析: \${url}\`);
// 使用fetch API (已在后端实现polyfill)
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(\`请求失败: \${response.status}\`);
}
const html: string = await response.text();
// 这里添加你的解析逻辑
// 例如:使用正则表达式提取下载链接
const match = html.match(/download-url="([^"]+)"/);
if (match) {
return match[1];
}
return "https://example.com/download/file.zip";
} catch (error: any) {
logger.error(\`解析失败: \${error.message}\`);
throw error;
}
}
/**
* 解析文件列表(可选)
*/
async function parseFileList(
shareLinkInfo: any,
http: any,
logger: any
): Promise<any[]> {
const dirId: string = shareLinkInfo.getOtherParam("dirId") || "0";
logger.info(\`解析文件列表目录ID: \${dirId}\`);
const fileList: any[] = [];
// 这里添加你的文件列表解析逻辑
return fileList;
}
/**
* 根据文件ID获取下载链接可选
*/
async function parseById(
shareLinkInfo: any,
http: any,
logger: any
): Promise<string> {
const paramJson = shareLinkInfo.getOtherParam("paramJson");
const fileId: string = paramJson.fileId;
logger.info(\`根据ID解析: \${fileId}\`);
// 这里添加你的按ID解析逻辑
return \`https://example.com/download?id=\${fileId}\`;
}`;
// 编辑器主题
const editorTheme = computed(() => {
return isDarkMode.value ? 'vs-dark' : 'vs';
@@ -880,15 +790,6 @@ async function parseById(
jsCode.value = exampleCode;
}
// 加载保存的语言选择
const savedLanguage = localStorage.getItem('playground_language');
if (savedLanguage) {
codeLanguage.value = savedLanguage;
}
setProgress(40, '准备加载TypeScript编译器...');
await new Promise(resolve => setTimeout(resolve, 100));
setProgress(50, '初始化Monaco Editor类型定义...');
await initMonacoTypes();
@@ -965,8 +866,8 @@ async function parseById(
// 加载示例代码
const loadTemplate = () => {
jsCode.value = codeLanguage.value === LANGUAGE.TYPESCRIPT ? exampleTypeScriptCode : exampleCode;
ElMessage.success(`已加载${codeLanguage.value}示例代码`);
jsCode.value = exampleCode;
ElMessage.success('已加载JavaScript示例代码');
};
// 格式化代码
@@ -1005,59 +906,6 @@ async function parseById(
};
// 语言切换处理
const onLanguageChange = (newLanguage) => {
console.log('切换语言:', newLanguage);
// 保存当前语言选择
localStorage.setItem('playground_language', newLanguage);
// 如果切换到TypeScript尝试编译当前代码
if (newLanguage === 'TypeScript' && jsCode.value.trim()) {
compileTypeScriptCode();
}
};
// 编译TypeScript代码
const compileTypeScriptCode = () => {
if (!jsCode.value.trim()) {
compiledES5Code.value = '';
compileStatus.value = { success: true, errors: [] };
return;
}
try {
const result = compileToES5(jsCode.value);
compiledES5Code.value = result.code;
compileStatus.value = {
success: result.success,
errors: result.errors || [],
hasWarnings: result.hasWarnings
};
if (!result.success) {
const errorMsg = formatCompileErrors(result.errors);
ElMessage.error({
message: '编译失败,请检查代码语法\n' + errorMsg,
duration: 5000,
showClose: true
});
} else if (result.hasWarnings) {
ElMessage.warning({
message: '编译成功,但存在警告',
duration: 3000
});
} else {
ElMessage.success('编译成功');
}
} catch (error) {
console.error('编译错误:', error);
compileStatus.value = {
success: false,
errors: [{ message: error.message || '编译失败' }]
};
ElMessage.error('编译失败: ' + error.message);
}
};
// 执行测试
const executeTest = async () => {
if (!jsCode.value.trim()) {
@@ -1101,67 +949,9 @@ async function parseById(
testResult.value = null;
consoleLogs.value = []; // 清空控制台
// 确定要执行的代码TypeScript需要先编译
let codeToExecute = jsCode.value;
// 优先使用显式语言选择如果是JavaScript模式但代码是TS给出提示
if (codeLanguage.value === LANGUAGE.TYPESCRIPT) {
// TypeScript模式始终编译
try {
const compileResult = compileToES5(jsCode.value);
if (!compileResult.success) {
testing.value = false;
const errorMsg = formatCompileErrors(compileResult.errors);
ElMessage.error({
message: 'TypeScript编译失败请修复错误后再试\n' + errorMsg,
duration: 5000,
showClose: true
});
testResult.value = {
success: false,
error: 'TypeScript编译失败',
stackTrace: errorMsg,
logs: [],
executionTime: 0
};
return;
}
// 使用编译后的ES5代码
codeToExecute = compileResult.code;
compiledES5Code.value = compileResult.code;
compileStatus.value = {
success: true,
errors: compileResult.errors || [],
hasWarnings: compileResult.hasWarnings
};
if (compileResult.hasWarnings) {
ElMessage.warning('编译成功,但存在警告');
}
} catch (error) {
testing.value = false;
ElMessage.error('TypeScript编译失败: ' + error.message);
testResult.value = {
success: false,
error: 'TypeScript编译失败: ' + error.message,
logs: [],
executionTime: 0
};
return;
}
} else if (isTypeScriptCode(jsCode.value)) {
// JavaScript模式但检测到TypeScript语法给出提示
ElMessage.warning({
message: '检测到TypeScript语法建议切换到TypeScript模式',
duration: 3000
});
}
try {
const result = await playgroundApi.testScript(
codeToExecute, // 使用编译后的代码或原始JS代码
jsCode.value, // 直接使用JavaScript代码
testParams.value.shareUrl,
testParams.value.pwd,
testParams.value.method
@@ -1610,9 +1400,6 @@ curl "${baseUrl}/json/parser?url=${encodeURIComponent(exampleUrl)}"</pre>
LANGUAGE,
editorRef,
jsCode,
codeLanguage,
compiledES5Code,
compileStatus,
testParams,
testResult,
testing,
@@ -1635,8 +1422,6 @@ curl "${baseUrl}/json/parser?url=${encodeURIComponent(exampleUrl)}"</pre>
isMobile,
updateIsMobile,
onCodeChange,
onLanguageChange,
compileTypeScriptCode,
loadTemplate,
formatCode,
saveCode,