@quicktvui/web-cli 2.4.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/bin/qt-web-cli-watch.js +0 -0
- package/bin/qt-web-cli.js +27 -157
- package/lib/DevServer.js +70 -24
- package/lib/entry-package.js +6 -73
- package/lib/index.js +0 -10
- package/lib/webpack.config.js +19 -0
- package/package.json +7 -6
- package/runtime/web-runtime.600135453542ce1e4bff.js +2 -0
- package/templates/dev-renderer.html +172 -50
- package/runtime/web-runtime.fb6413de579c6da39a07.js +0 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# @quicktvui/web-cli
|
|
1
|
+
# @quicktvui/web-cli v3
|
|
2
2
|
|
|
3
|
-
QuickTVUI Web 开发服务器 CLI 工具,
|
|
3
|
+
QuickTVUI Web 开发服务器 CLI 工具,v3 采用委托构建 + Bundle 加载模式。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## v3 架构说明
|
|
6
6
|
|
|
7
7
|
| 项 | v1 | v2 |
|
|
8
8
|
|---|---|---|
|
|
@@ -16,11 +16,11 @@ QuickTVUI Web 开发服务器 CLI 工具,v2 采用委托构建 + Bundle 加载
|
|
|
16
16
|
## 安装
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm install @quicktvui/web-cli@
|
|
19
|
+
npm install @quicktvui/web-cli@3 --save-dev
|
|
20
20
|
# 或
|
|
21
|
-
yarn add @quicktvui/web-cli@
|
|
21
|
+
yarn add @quicktvui/web-cli@3 --dev
|
|
22
22
|
# 或
|
|
23
|
-
pnpm add @quicktvui/web-cli@
|
|
23
|
+
pnpm add @quicktvui/web-cli@3 -D
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## 前置要求
|
|
@@ -63,11 +63,11 @@ npx qt-web-cli
|
|
|
63
63
|
-o, --open 自动打开浏览器
|
|
64
64
|
-h, --help 显示帮助信息
|
|
65
65
|
-v, --version 显示 CLI 版本
|
|
66
|
-
--info 显示 CLI 和 web-
|
|
66
|
+
--info 显示 CLI 和 web-runtime 版本
|
|
67
67
|
--dev-script <name> 指定要运行的 npm script (默认: dev)
|
|
68
68
|
--dist-dir <path> 指定构建产物目录 (默认: dist/dev)
|
|
69
69
|
--no-auto-build 跳过自动调用 dev 脚本,仅启动服务器
|
|
70
|
-
--v1
|
|
70
|
+
--v1 已移除,仅保留参数兼容提示
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
### 使用场景
|
|
@@ -94,11 +94,11 @@ qt-web-cli --dev-script build
|
|
|
94
94
|
# 使用 npm run build 替代 npm run dev
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
#### 4.
|
|
97
|
+
#### 4. `--v1` 兼容提示
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
100
|
qt-web-cli --v1
|
|
101
|
-
#
|
|
101
|
+
# 提示 v1 已移除,请使用默认 web-runtime 模式
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
## 工作流程
|
package/bin/qt-web-cli-watch.js
CHANGED
|
File without changes
|
package/bin/qt-web-cli.js
CHANGED
|
@@ -44,31 +44,29 @@ if (args.v || args.version) {
|
|
|
44
44
|
process.exit(0)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
function resolvePackageJson(possiblePaths) {
|
|
48
|
+
for (const pkgPath of possiblePaths) {
|
|
49
|
+
try {
|
|
50
|
+
return require(pkgPath)
|
|
51
|
+
} catch (e) {}
|
|
52
|
+
}
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
// 显示详细信息
|
|
48
57
|
if (args.info) {
|
|
49
58
|
const cliPkg = require(path.join(__dirname, '../package.json'))
|
|
50
59
|
console.log(`@quicktvui/web-cli v${cliPkg.version}`)
|
|
51
|
-
console.log(` Mode: v2 (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
rendererPkg = require(pkgPath)
|
|
62
|
-
break
|
|
63
|
-
} catch (e) {}
|
|
64
|
-
}
|
|
65
|
-
if (rendererPkg) {
|
|
66
|
-
console.log(`@quicktvui/web-renderer v${rendererPkg.version}`)
|
|
67
|
-
} else {
|
|
68
|
-
console.log('@quicktvui/web-renderer (not installed)')
|
|
69
|
-
}
|
|
70
|
-
} catch (e) {
|
|
71
|
-
console.log('@quicktvui/web-renderer (not installed)')
|
|
60
|
+
console.log(` Mode: v2 (web-runtime)`)
|
|
61
|
+
const runtimePkg = resolvePackageJson([
|
|
62
|
+
'@quicktvui/web-runtime/package.json',
|
|
63
|
+
path.join(__dirname, '../../web-runtime/package.json'),
|
|
64
|
+
path.join(__dirname, '../../../web-runtime/package.json'),
|
|
65
|
+
])
|
|
66
|
+
if (runtimePkg) {
|
|
67
|
+
console.log(`@quicktvui/web-runtime v${runtimePkg.version}`)
|
|
68
|
+
} else {
|
|
69
|
+
console.log('@quicktvui/web-runtime (not installed)')
|
|
72
70
|
}
|
|
73
71
|
process.exit(0)
|
|
74
72
|
}
|
|
@@ -86,11 +84,11 @@ Options:
|
|
|
86
84
|
-o, --open 自动打开浏览器
|
|
87
85
|
-h, --help 显示帮助信息
|
|
88
86
|
-v, --version 显示 CLI 版本
|
|
89
|
-
--info 显示 CLI 和 web-
|
|
87
|
+
--info 显示 CLI 和 web-runtime 版本
|
|
90
88
|
--dev-script <name> 指定要运行的 npm script (默认: dev)
|
|
91
89
|
--dist-dir <path> 指定构建产物目录 (默认: dist/dev)
|
|
92
90
|
--no-auto-build 跳过自动调用 dev 脚本,仅启动服务器
|
|
93
|
-
--v1
|
|
91
|
+
--v1 已移除,仅保留参数兼容提示
|
|
94
92
|
|
|
95
93
|
v2 模式 (默认):
|
|
96
94
|
自动检测项目的 dev 脚本,调用 npm run dev 构建产物
|
|
@@ -98,14 +96,14 @@ v2 模式 (默认):
|
|
|
98
96
|
支持热更新 (SSE)
|
|
99
97
|
|
|
100
98
|
v1 模式 (--v1):
|
|
101
|
-
|
|
99
|
+
已移除,web-cli 统一走 web-runtime
|
|
102
100
|
|
|
103
101
|
Examples:
|
|
104
102
|
qt-web-cli # v2 模式,自动构建+启动
|
|
105
103
|
qt-web-cli --no-auto-build # v2 模式,仅启动服务器(需先手动 npm run dev)
|
|
106
104
|
qt-web-cli --dev-script build # 使用 build 脚本替代 dev
|
|
107
105
|
qt-web-cli --port 8080 # 指定端口
|
|
108
|
-
qt-web-cli --v1 #
|
|
106
|
+
qt-web-cli --v1 # 提示 v1 已移除
|
|
109
107
|
`)
|
|
110
108
|
process.exit(0)
|
|
111
109
|
}
|
|
@@ -117,8 +115,8 @@ Examples:
|
|
|
117
115
|
async function main() {
|
|
118
116
|
// 强制使用 v1 模式
|
|
119
117
|
if (args.v1) {
|
|
120
|
-
signale.
|
|
121
|
-
|
|
118
|
+
signale.error('v1 模式已移除,请使用默认的 web-runtime 模式')
|
|
119
|
+
process.exit(1)
|
|
122
120
|
}
|
|
123
121
|
|
|
124
122
|
signale.pending('正在启动 QuickTVUI Web CLI v2 ...')
|
|
@@ -144,8 +142,8 @@ async function main() {
|
|
|
144
142
|
const distFullPath = path.join(projectRoot, distDir)
|
|
145
143
|
|
|
146
144
|
if (!devScript && !fs.existsSync(distFullPath)) {
|
|
147
|
-
signale.
|
|
148
|
-
|
|
145
|
+
signale.error('项目未配置 dev 脚本,且 dist/dev 目录不存在,无法启动 web-runtime 模式')
|
|
146
|
+
process.exit(1)
|
|
149
147
|
}
|
|
150
148
|
|
|
151
149
|
// ============================================================================
|
|
@@ -336,127 +334,6 @@ async function main() {
|
|
|
336
334
|
process.on('SIGTERM', cleanup)
|
|
337
335
|
}
|
|
338
336
|
|
|
339
|
-
// ============================================================================
|
|
340
|
-
// v1 模式 (webpack 自构建,已废弃)
|
|
341
|
-
// ============================================================================
|
|
342
|
-
|
|
343
|
-
function runV1() {
|
|
344
|
-
signale.pending('正在启动 QuickTVUI Web CLI v1 (webpack 模式)...')
|
|
345
|
-
|
|
346
|
-
// 查找项目根目录
|
|
347
|
-
const projectRoot = findProjectRoot()
|
|
348
|
-
if (!projectRoot) {
|
|
349
|
-
signale.error('无法找到项目根目录')
|
|
350
|
-
process.exit(1)
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
signale.info(`项目根目录: ${projectRoot}`)
|
|
354
|
-
|
|
355
|
-
// 读取 package.json
|
|
356
|
-
let pkg = {}
|
|
357
|
-
try {
|
|
358
|
-
pkg = require(path.join(projectRoot, 'package.json'))
|
|
359
|
-
} catch (e) {}
|
|
360
|
-
|
|
361
|
-
// 检测入口文件
|
|
362
|
-
let mainEntry
|
|
363
|
-
let entryType
|
|
364
|
-
|
|
365
|
-
const entryCandidates = [
|
|
366
|
-
{ name: 'src/main-native.ts', path: './src/main-native.ts', type: 'package' },
|
|
367
|
-
{ name: 'src/main-native.js', path: './src/main-native.js', type: 'package' },
|
|
368
|
-
{ name: 'src/main.ts', path: './src/main.ts', type: 'package' },
|
|
369
|
-
{ name: 'src/main.js', path: './src/main.js', type: 'package' },
|
|
370
|
-
{ name: 'main (package.json)', path: pkg.main, type: 'package' },
|
|
371
|
-
]
|
|
372
|
-
|
|
373
|
-
for (const candidate of entryCandidates) {
|
|
374
|
-
if (!candidate.path) continue
|
|
375
|
-
const entryFullPath = path.resolve(projectRoot, candidate.path)
|
|
376
|
-
if (fs.existsSync(entryFullPath)) {
|
|
377
|
-
mainEntry = candidate.path
|
|
378
|
-
entryType = candidate.type
|
|
379
|
-
signale.info(`主入口: ${mainEntry}`)
|
|
380
|
-
break
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (!mainEntry) {
|
|
385
|
-
signale.error('无法找到有效的入口文件')
|
|
386
|
-
process.exit(1)
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const mainEntryPath = path.resolve(projectRoot, mainEntry)
|
|
390
|
-
|
|
391
|
-
// 设置环境变量
|
|
392
|
-
process.env.NODE_ENV = 'development'
|
|
393
|
-
process.env.QUICKTVUI_PROJECT_ROOT = projectRoot
|
|
394
|
-
process.env.QUICKTVUI_MAIN_ENTRY = mainEntryPath
|
|
395
|
-
process.env.QUICKTVUI_ENTRY_TYPE = entryType
|
|
396
|
-
process.env.QUICKTVUI_PORT = String(args.port)
|
|
397
|
-
|
|
398
|
-
// 检测 OpenSSL 版本
|
|
399
|
-
const needLegacyProvider = checkOpenSSLVersion(process.versions.openssl)
|
|
400
|
-
if (needLegacyProvider) {
|
|
401
|
-
signale.warn(`检测到 OpenSSL ${process.versions.openssl},已添加 --openssl-legacy-provider`)
|
|
402
|
-
process.env.NODE_OPTIONS = '--openssl-legacy-provider'
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
signale.pending(`启动开发服务器: http://localhost:${args.port}`)
|
|
406
|
-
|
|
407
|
-
// 使用 webpack Node API 启动开发服务器
|
|
408
|
-
const defaultConfigPath = path.resolve(__dirname, '../lib/webpack.config.js')
|
|
409
|
-
const userConfigPath = args.config ? path.resolve(projectRoot, args.config) : null
|
|
410
|
-
startDevServer(defaultConfigPath, args.port, args.open, userConfigPath)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* 使用 webpack Node API 启动开发服务器 (v1)
|
|
415
|
-
*/
|
|
416
|
-
function startDevServer(configPath, port, shouldOpen, userConfigPath) {
|
|
417
|
-
const webpack = require('webpack')
|
|
418
|
-
const WebpackDevServer = require('webpack-dev-server')
|
|
419
|
-
const { merge } = require('webpack-merge')
|
|
420
|
-
|
|
421
|
-
let config = require(configPath)
|
|
422
|
-
|
|
423
|
-
if (userConfigPath) {
|
|
424
|
-
if (!fs.existsSync(userConfigPath)) {
|
|
425
|
-
signale.error(`自定义配置文件不存在: ${userConfigPath}`)
|
|
426
|
-
process.exit(1)
|
|
427
|
-
}
|
|
428
|
-
signale.info(`加载用户配置: ${userConfigPath}`)
|
|
429
|
-
const userConfig = require(userConfigPath)
|
|
430
|
-
config = merge(config, userConfig)
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
config.devServer = config.devServer || {}
|
|
434
|
-
config.devServer.port = port
|
|
435
|
-
config.devServer.open = false
|
|
436
|
-
|
|
437
|
-
const compiler = webpack(config)
|
|
438
|
-
const server = new WebpackDevServer(config.devServer, compiler)
|
|
439
|
-
|
|
440
|
-
server.startCallback((err) => {
|
|
441
|
-
if (err) {
|
|
442
|
-
signale.error('启动开发服务器失败:', err.message)
|
|
443
|
-
process.exit(1)
|
|
444
|
-
}
|
|
445
|
-
signale.success(`开发服务器已启动: http://localhost:${port}`)
|
|
446
|
-
|
|
447
|
-
if (shouldOpen) {
|
|
448
|
-
openBrowser(`http://localhost:${port}`)
|
|
449
|
-
}
|
|
450
|
-
})
|
|
451
|
-
|
|
452
|
-
process.on('SIGINT', () => {
|
|
453
|
-
server.stopCallback(() => {
|
|
454
|
-
signale.info('开发服务器已停止')
|
|
455
|
-
process.exit(0)
|
|
456
|
-
})
|
|
457
|
-
})
|
|
458
|
-
}
|
|
459
|
-
|
|
460
337
|
// ============================================================================
|
|
461
338
|
// 工具函数
|
|
462
339
|
// ============================================================================
|
|
@@ -483,13 +360,6 @@ function detectDevScript(pkg, scriptName) {
|
|
|
483
360
|
return null
|
|
484
361
|
}
|
|
485
362
|
|
|
486
|
-
function checkOpenSSLVersion(version) {
|
|
487
|
-
if (!version) return false
|
|
488
|
-
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version)
|
|
489
|
-
if (!match) return false
|
|
490
|
-
return parseInt(match[1], 10) >= 3
|
|
491
|
-
}
|
|
492
|
-
|
|
493
363
|
function formatSize(bytes) {
|
|
494
364
|
if (bytes < 1024) return bytes + ' B'
|
|
495
365
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
|
package/lib/DevServer.js
CHANGED
|
@@ -400,7 +400,7 @@ class DevServer {
|
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
// proxy 代理
|
|
403
|
-
if (pathname.startsWith('/proxy/')) {
|
|
403
|
+
if (pathname === '/proxy' || pathname.startsWith('/proxy/')) {
|
|
404
404
|
this._handleProxy(req, res, pathname)
|
|
405
405
|
return
|
|
406
406
|
}
|
|
@@ -508,27 +508,48 @@ class DevServer {
|
|
|
508
508
|
/**
|
|
509
509
|
* 处理代理请求
|
|
510
510
|
*
|
|
511
|
-
* URL 格式:
|
|
511
|
+
* URL 格式:
|
|
512
|
+
* 1. /proxy/{http|https}/{host}[:port]/{path}[?query]
|
|
513
|
+
* 2. /proxy?url={encodeURIComponent(fullUrl)}
|
|
512
514
|
* 示例:
|
|
513
515
|
* /proxy/https/api.example.com/v1/data → https://api.example.com/v1/data
|
|
514
516
|
* /proxy/https/api.example.com:8443/v1/data → https://api.example.com:8443/v1/data
|
|
517
|
+
* /proxy?url=https%3A%2F%2Fapi.example.com%2Fv1%2Fdata → https://api.example.com/v1/data
|
|
515
518
|
*/
|
|
516
519
|
_handleProxy(req, res, pathname) {
|
|
517
|
-
// 从完整 URL 中提取查询参数(pathname 只有路径部分)
|
|
518
520
|
const fullUrl = url.parse(req.url, true)
|
|
519
|
-
const
|
|
521
|
+
const rawTargetUrl = fullUrl.query && fullUrl.query.url
|
|
522
|
+
|
|
523
|
+
let protocol
|
|
524
|
+
let hostPort
|
|
525
|
+
let proxyPath
|
|
526
|
+
|
|
527
|
+
if (rawTargetUrl) {
|
|
528
|
+
try {
|
|
529
|
+
const targetUrl = new URL(rawTargetUrl)
|
|
530
|
+
protocol = targetUrl.protocol.replace(':', '')
|
|
531
|
+
hostPort = targetUrl.host
|
|
532
|
+
proxyPath = `${targetUrl.pathname}${targetUrl.search}`
|
|
533
|
+
} catch (e) {
|
|
534
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' })
|
|
535
|
+
res.end('Invalid proxy url query param')
|
|
536
|
+
return
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
// 从完整 URL 中提取查询参数(pathname 只有路径部分)
|
|
540
|
+
const queryString = fullUrl.search || ''
|
|
541
|
+
const match = pathname.match(/^\/proxy\/(https?)\/([^/]+)(\/.*)?$/)
|
|
542
|
+
if (!match) {
|
|
543
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' })
|
|
544
|
+
res.end('Invalid proxy URL format. Use: /proxy/{http|https}/{host}[:port]/{path}')
|
|
545
|
+
return
|
|
546
|
+
}
|
|
520
547
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
res.end('Invalid proxy URL format. Use: /proxy/{http|https}/{host}[:port]/{path}')
|
|
525
|
-
return
|
|
548
|
+
protocol = match[1]
|
|
549
|
+
hostPort = match[2]
|
|
550
|
+
proxyPath = (match[3] || '/') + queryString
|
|
526
551
|
}
|
|
527
552
|
|
|
528
|
-
const protocol = match[1]
|
|
529
|
-
const hostPort = match[2]
|
|
530
|
-
const proxyPath = (match[3] || '/') + queryString
|
|
531
|
-
|
|
532
553
|
// 分离 host 和 port
|
|
533
554
|
let hostname = hostPort
|
|
534
555
|
let port = protocol === 'https' ? 443 : 80
|
|
@@ -538,17 +559,33 @@ class DevServer {
|
|
|
538
559
|
port = parseInt(parts[1], 10) || port
|
|
539
560
|
}
|
|
540
561
|
|
|
541
|
-
//
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
headers
|
|
562
|
+
// console.log('[Proxy] incoming request:', {
|
|
563
|
+
// method: req.method,
|
|
564
|
+
// requestUrl: req.url,
|
|
565
|
+
// target: `${protocol}://${hostPort}${proxyPath}`,
|
|
566
|
+
// range: req.headers && req.headers.range,
|
|
567
|
+
// })
|
|
568
|
+
|
|
569
|
+
// Media/file download proxy should forward a minimal header set.
|
|
570
|
+
// Forwarding browser-only headers (origin, referer, sec-fetch-*, cookies)
|
|
571
|
+
// can cause some signed upstream URLs to be rejected with 400.
|
|
572
|
+
const headers = {
|
|
573
|
+
host: hostPort,
|
|
574
|
+
'user-agent': req.headers['user-agent'] || 'Mozilla/5.0',
|
|
575
|
+
accept: req.headers.accept || '*/*',
|
|
576
|
+
}
|
|
577
|
+
if (req.headers.range) {
|
|
578
|
+
headers.range = req.headers.range
|
|
579
|
+
}
|
|
580
|
+
if (req.headers['if-range']) {
|
|
581
|
+
headers['if-range'] = req.headers['if-range']
|
|
582
|
+
}
|
|
583
|
+
if (req.headers['if-none-match']) {
|
|
584
|
+
headers['if-none-match'] = req.headers['if-none-match']
|
|
585
|
+
}
|
|
586
|
+
if (req.headers['if-modified-since']) {
|
|
587
|
+
headers['if-modified-since'] = req.headers['if-modified-since']
|
|
588
|
+
}
|
|
552
589
|
// 标记请求来自代理(方便调试)
|
|
553
590
|
headers['x-forwarded-for'] = req.socket.remoteAddress
|
|
554
591
|
headers['x-forwarded-proto'] = protocol
|
|
@@ -564,6 +601,14 @@ class DevServer {
|
|
|
564
601
|
|
|
565
602
|
const httpModule = protocol === 'https' ? require('https') : require('http')
|
|
566
603
|
const proxyReq = httpModule.request(options, (proxyRes) => {
|
|
604
|
+
// console.log('[Proxy] upstream response:', {
|
|
605
|
+
// method: req.method,
|
|
606
|
+
// requestUrl: req.url,
|
|
607
|
+
// target: `${protocol}://${hostPort}${proxyPath}`,
|
|
608
|
+
// statusCode: proxyRes.statusCode,
|
|
609
|
+
// contentType: proxyRes.headers['content-type'],
|
|
610
|
+
// contentRange: proxyRes.headers['content-range'],
|
|
611
|
+
// })
|
|
567
612
|
// 代理响应添加 CORS 头
|
|
568
613
|
const responseHeaders = { ...proxyRes.headers }
|
|
569
614
|
responseHeaders['access-control-allow-origin'] = this.corsOrigin
|
|
@@ -660,6 +705,7 @@ class DevServer {
|
|
|
660
705
|
c.style.visibility = 'visible';
|
|
661
706
|
}
|
|
662
707
|
}
|
|
708
|
+
|
|
663
709
|
scaleApp();
|
|
664
710
|
window.addEventListener('resize', scaleApp);
|
|
665
711
|
</script>
|
package/lib/entry-package.js
CHANGED
|
@@ -1,74 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Legacy v1 browser entry.
|
|
2
|
+
// web-cli now serves web-runtime and lets web-runtime own the renderer bootstrap.
|
|
3
|
+
// This stub intentionally avoids importing web-renderer directly.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
initAsyncLocalStorage,
|
|
9
|
-
initAutoProxy,
|
|
10
|
-
setupSceneBuilder,
|
|
11
|
-
createWebEngine,
|
|
12
|
-
applyAllPatches,
|
|
13
|
-
TVFocusManager,
|
|
14
|
-
startWebEngine,
|
|
15
|
-
APP_NAME,
|
|
16
|
-
IJKPlayerComponent,
|
|
17
|
-
} from '@quicktvui/web-renderer'
|
|
18
|
-
import { setupGlobalErrorHandler } from './error-badge.js'
|
|
19
|
-
|
|
20
|
-
// 初始化 async localStorage
|
|
21
|
-
initAsyncLocalStorage()
|
|
22
|
-
|
|
23
|
-
// 初始化自动代理
|
|
24
|
-
initAutoProxy()
|
|
25
|
-
|
|
26
|
-
// 注册 IJKPlayerComponent
|
|
27
|
-
global.__WEB_COMPONENTS__ = global.__WEB_COMPONENTS__ || {}
|
|
28
|
-
global.__WEB_COMPONENTS__['IJKPlayerComponent'] = IJKPlayerComponent
|
|
29
|
-
|
|
30
|
-
// 设置 SceneBuilder
|
|
31
|
-
setupSceneBuilder()
|
|
32
|
-
|
|
33
|
-
// 创建 Web 引擎
|
|
34
|
-
const engine = createWebEngine()
|
|
35
|
-
|
|
36
|
-
// 应用所有补丁
|
|
37
|
-
applyAllPatches(engine)
|
|
38
|
-
|
|
39
|
-
// 初始化 TV 焦点管理器
|
|
40
|
-
global.__TV_FOCUS_MANAGER__ = new TVFocusManager()
|
|
41
|
-
console.log('[Web Renderer] TVFocusManager initialized')
|
|
42
|
-
|
|
43
|
-
// 初始化错误提示组件(在 DOM 准备好后)
|
|
44
|
-
if (document.readyState === 'loading') {
|
|
45
|
-
document.addEventListener('DOMContentLoaded', () => setupGlobalErrorHandler())
|
|
46
|
-
} else {
|
|
47
|
-
setupGlobalErrorHandler()
|
|
48
|
-
}
|
|
49
|
-
console.log('[Web Renderer] Error badge initialized')
|
|
50
|
-
|
|
51
|
-
// 注入全局 CSS
|
|
52
|
-
const styleEl = document.createElement('style')
|
|
53
|
-
styleEl.id = 'web-platform-reset'
|
|
54
|
-
styleEl.textContent = `
|
|
55
|
-
#app, #app * { align-items: flex-start; }
|
|
56
|
-
[style*="align-items"] { align-items: var(--align-items, center) !important; }
|
|
57
|
-
`
|
|
58
|
-
document.head.appendChild(styleEl)
|
|
59
|
-
console.log('[Web Renderer] Global CSS reset injected')
|
|
60
|
-
|
|
61
|
-
// 动态加载主入口,等待加载完成后再启动引擎
|
|
62
|
-
console.log('[Web Renderer] Importing main module...')
|
|
63
|
-
import(__QUICKTVUI_MAIN_ENTRY__)
|
|
64
|
-
.then(() => {
|
|
65
|
-
console.log('[Web Renderer] Main module loaded')
|
|
66
|
-
console.log('[Web Renderer] Starting engine...')
|
|
67
|
-
startWebEngine(engine, APP_NAME)
|
|
68
|
-
setTimeout(() => {
|
|
69
|
-
console.log('[Web Renderer] === Initialization complete ===')
|
|
70
|
-
}, 1000)
|
|
71
|
-
})
|
|
72
|
-
.catch((err) => {
|
|
73
|
-
console.error('[Web Renderer] Failed to load main module:', err)
|
|
74
|
-
})
|
|
5
|
+
throw new Error(
|
|
6
|
+
'[web-cli] v1 mode has been removed. Use the default web-runtime based flow instead of direct web-renderer bootstrap.'
|
|
7
|
+
)
|
package/lib/index.js
CHANGED
|
@@ -74,16 +74,6 @@ function detectDevBundle(projectRoot) {
|
|
|
74
74
|
* 检测 web renderer 类型(v1 兼容)
|
|
75
75
|
*/
|
|
76
76
|
function detectWebRendererType(projectRoot) {
|
|
77
|
-
if (fs.existsSync(path.join(projectRoot, 'src/web/index.js'))) {
|
|
78
|
-
return 'local'
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
const projectPkg = require(path.join(projectRoot, 'package.json'))
|
|
82
|
-
const deps = { ...projectPkg?.dependencies, ...projectPkg?.devDependencies }
|
|
83
|
-
if (deps['@quicktvui/web-renderer']) {
|
|
84
|
-
return 'package'
|
|
85
|
-
}
|
|
86
|
-
} catch (e) {}
|
|
87
77
|
return 'package'
|
|
88
78
|
}
|
|
89
79
|
|
package/lib/webpack.config.js
CHANGED
|
@@ -162,6 +162,14 @@ module.exports = {
|
|
|
162
162
|
changeOrigin: true,
|
|
163
163
|
secure: false,
|
|
164
164
|
router: (req) => {
|
|
165
|
+
try {
|
|
166
|
+
const requestUrl = new URL(req.url, 'http://localhost')
|
|
167
|
+
const rawUrl = requestUrl.searchParams.get('url')
|
|
168
|
+
if (rawUrl) {
|
|
169
|
+
const targetUrl = new URL(rawUrl)
|
|
170
|
+
return `${targetUrl.protocol}//${targetUrl.host}`
|
|
171
|
+
}
|
|
172
|
+
} catch (e) {}
|
|
165
173
|
const match = req.url.match(/^\/proxy\/(https?)\/([^/]+)(\/.*)?$/)
|
|
166
174
|
if (match) {
|
|
167
175
|
return `${match[1]}://${match[2]}`
|
|
@@ -169,6 +177,14 @@ module.exports = {
|
|
|
169
177
|
return 'http://placeholder'
|
|
170
178
|
},
|
|
171
179
|
pathRewrite: (path, req) => {
|
|
180
|
+
try {
|
|
181
|
+
const requestUrl = new URL(path, 'http://localhost')
|
|
182
|
+
const rawUrl = requestUrl.searchParams.get('url')
|
|
183
|
+
if (rawUrl) {
|
|
184
|
+
const targetUrl = new URL(rawUrl)
|
|
185
|
+
return `${targetUrl.pathname}${targetUrl.search}`
|
|
186
|
+
}
|
|
187
|
+
} catch (e) {}
|
|
172
188
|
const match = path.match(/^\/proxy\/(https?)\/[^/]+(\/.*)?$/)
|
|
173
189
|
if (match) {
|
|
174
190
|
return match[2] || '/'
|
|
@@ -257,6 +273,9 @@ module.exports = {
|
|
|
257
273
|
{
|
|
258
274
|
test: /\.js$/,
|
|
259
275
|
exclude: /node_modules/,
|
|
276
|
+
resolve: {
|
|
277
|
+
fullySpecified: false,
|
|
278
|
+
},
|
|
260
279
|
use: [
|
|
261
280
|
{
|
|
262
281
|
loader: 'babel-loader',
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quicktvui/web-cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "CLI tool for QuickTVUI web development
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "CLI tool for QuickTVUI web development v3 - delegate build & bundle loading",
|
|
5
5
|
"author": "QuickTVUI Team",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"bin": {
|
|
8
8
|
"qt-web-cli": "./bin/qt-web-cli.js"
|
|
9
9
|
},
|
|
10
10
|
"main": "lib/index.js",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"sync-runtime": "rm -rf runtime/*.js && cp ../web-runtime/dist/web-runtime.*.js runtime/",
|
|
13
|
+
"prepublishOnly": "npm run sync-runtime"
|
|
14
|
+
},
|
|
11
15
|
"files": [
|
|
12
16
|
"bin",
|
|
13
17
|
"lib",
|
|
@@ -82,8 +86,5 @@
|
|
|
82
86
|
},
|
|
83
87
|
"engines": {
|
|
84
88
|
"node": ">=16.0.0"
|
|
85
|
-
},
|
|
86
|
-
"scripts": {
|
|
87
|
-
"sync-runtime": "rm -rf runtime/*.js && cp ../web-runtime/dist/web-runtime.*.js runtime/"
|
|
88
89
|
}
|
|
89
|
-
}
|
|
90
|
+
}
|