@liujitcn/kratos-taro-app-core 0.0.1
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 +36 -0
- package/dist/build.mjs +38 -0
- package/dist/runner.mjs +321 -0
- package/package.json +51 -0
- package/src/api/base/config.ts +20 -0
- package/src/api/base/file.ts +57 -0
- package/src/api/base/login.ts +87 -0
- package/src/api/base/menu.ts +17 -0
- package/src/api/base/oauth.ts +149 -0
- package/src/api/system/auth.ts +54 -0
- package/src/api/system/base_area.ts +34 -0
- package/src/api/system/base_dict.ts +24 -0
- package/src/bootstrap.ts +32 -0
- package/src/build.ts +24 -0
- package/src/components/BootstrapStatus.scss +28 -0
- package/src/components/BootstrapStatus.tsx +25 -0
- package/src/components/KratosPageFrame.scss +41 -0
- package/src/components/KratosPageFrame.tsx +51 -0
- package/src/components/KratosTabBar.scss +34 -0
- package/src/components/KratosTabBar.tsx +45 -0
- package/src/index.ts +36 -0
- package/src/module.ts +63 -0
- package/src/navigation-pattern.d.mts +2 -0
- package/src/navigation-pattern.mjs +29 -0
- package/src/navigation-tree.d.mts +12 -0
- package/src/navigation-tree.mjs +45 -0
- package/src/navigation.ts +336 -0
- package/src/pages.ts +21 -0
- package/src/rpc/base/v1/config.ts +34 -0
- package/src/rpc/base/v1/file.ts +66 -0
- package/src/rpc/base/v1/login.ts +128 -0
- package/src/rpc/base/v1/mcp.ts +20 -0
- package/src/rpc/base/v1/oauth.ts +199 -0
- package/src/rpc/base/v1/sse.ts +22 -0
- package/src/rpc/common/v1/common.ts +71 -0
- package/src/rpc/common/v1/enum.ts +71 -0
- package/src/rpc/common/v1/types.ts +77 -0
- package/src/rpc/google/protobuf/empty.ts +19 -0
- package/src/rpc/google/protobuf/wrappers.ts +124 -0
- package/src/rpc/system/app/v1/auth.ts +54 -0
- package/src/rpc/system/app/v1/base_area.ts +24 -0
- package/src/rpc/system/app/v1/base_dict.ts +37 -0
- package/src/rpc/system/app/v1/base_menu.ts +61 -0
- package/src/rpc/system/common/v1/enum.ts +80 -0
- package/src/runner.ts +390 -0
- package/src/static/images/avatar.png +0 -0
- package/src/static/images/center_bg.png +0 -0
- package/src/static/images/logo_icon.png +0 -0
- package/src/static/images/navigator_bg.png +0 -0
- package/src/static/tabs/home_default.png +0 -0
- package/src/static/tabs/home_selected.png +0 -0
- package/src/static/tabs/user_default.png +0 -0
- package/src/static/tabs/user_selected.png +0 -0
- package/src/stores/index.ts +2 -0
- package/src/stores/setting.ts +43 -0
- package/src/stores/user.ts +152 -0
- package/src/styles/base.scss +45 -0
- package/src/types/assets.d.ts +9 -0
- package/src/utils/auth.ts +55 -0
- package/src/utils/file.ts +48 -0
- package/src/utils/http.ts +262 -0
- package/src/utils/index.ts +35 -0
- package/src/utils/navigation.ts +72 -0
- package/src/utils/passwordCrypto.ts +257 -0
- package/src/views/pages/index/index.scss +137 -0
- package/src/views/pages/index/index.tsx +74 -0
- package/src/views/pages/login/components/BehaviorCaptcha.h5.tsx +46 -0
- package/src/views/pages/login/components/BehaviorCaptcha.tsx +6 -0
- package/src/views/pages/login/components/BehaviorCaptcha.weapp.tsx +6 -0
- package/src/views/pages/login/components/types.ts +32 -0
- package/src/views/pages/login/login.scss +310 -0
- package/src/views/pages/login/login.tsx +493 -0
- package/src/views/pages/login/protocal.scss +84 -0
- package/src/views/pages/login/protocal.tsx +73 -0
- package/src/views/pages/status/index.tsx +42 -0
- package/src/views/pages/webview/webview.scss +43 -0
- package/src/views/pages/webview/webview.tsx +41 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
2
|
+
// versions:
|
|
3
|
+
// protoc-gen-ts_proto v2.7.7
|
|
4
|
+
// protoc unknown
|
|
5
|
+
// source: system/app/v1/base_menu.proto
|
|
6
|
+
|
|
7
|
+
/* eslint-disable */
|
|
8
|
+
|
|
9
|
+
/** 移动端菜单查询条件 */
|
|
10
|
+
export interface ListBaseMenuRequest {
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** 移动端菜单响应 */
|
|
14
|
+
export interface ListBaseMenuResponse {
|
|
15
|
+
/** 移动端扁平菜单 */
|
|
16
|
+
items: BaseMenu[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** 移动端菜单 */
|
|
20
|
+
export interface BaseMenu {
|
|
21
|
+
/** 菜单ID */
|
|
22
|
+
id: number;
|
|
23
|
+
/** 父级菜单ID */
|
|
24
|
+
parent_id: number;
|
|
25
|
+
/** 逻辑路由地址 */
|
|
26
|
+
path: string;
|
|
27
|
+
/** 逻辑路由名称 */
|
|
28
|
+
name: string;
|
|
29
|
+
/** 移动端菜单元信息 */
|
|
30
|
+
meta: BaseMenuMeta | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 移动端菜单元信息 */
|
|
34
|
+
export interface BaseMenuMeta {
|
|
35
|
+
/** 菜单标题 */
|
|
36
|
+
title: string;
|
|
37
|
+
/** 菜单图标 */
|
|
38
|
+
icon?:
|
|
39
|
+
| string
|
|
40
|
+
| undefined;
|
|
41
|
+
/** 移动端页面配置 */
|
|
42
|
+
app: BaseMenuAppMeta | undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 移动端页面配置 */
|
|
46
|
+
export interface BaseMenuAppMeta {
|
|
47
|
+
/** 已注册的稳定视图键 */
|
|
48
|
+
view_key: string;
|
|
49
|
+
/** 页面访问模式 */
|
|
50
|
+
access: string;
|
|
51
|
+
/** 是否显示在自绘标签栏 */
|
|
52
|
+
in_tab_bar: boolean;
|
|
53
|
+
/** 标签栏选中图标 */
|
|
54
|
+
selected_icon?: string | undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** App菜单服务 */
|
|
58
|
+
export interface BaseMenuService {
|
|
59
|
+
/** 查询移动端菜单 */
|
|
60
|
+
ListBaseMenu(request: ListBaseMenuRequest): Promise<ListBaseMenuResponse>;
|
|
61
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
2
|
+
// versions:
|
|
3
|
+
// protoc-gen-ts_proto v2.7.7
|
|
4
|
+
// protoc unknown
|
|
5
|
+
// source: system/common/v1/enum.proto
|
|
6
|
+
|
|
7
|
+
/* eslint-disable */
|
|
8
|
+
|
|
9
|
+
/** 系统配置类型 */
|
|
10
|
+
export enum BaseConfigType {
|
|
11
|
+
UNKNOWN_BCT = 0,
|
|
12
|
+
/** TEXT - 文本 */
|
|
13
|
+
TEXT = 1,
|
|
14
|
+
/** IMAGE - 图片 */
|
|
15
|
+
IMAGE = 2,
|
|
16
|
+
/** RICH_TEXT - 富文本 */
|
|
17
|
+
RICH_TEXT = 3,
|
|
18
|
+
/** DICT - 字典 */
|
|
19
|
+
DICT = 4,
|
|
20
|
+
/** BOOLEAN - 布尔 */
|
|
21
|
+
BOOLEAN = 5,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 定时任务日志状态 */
|
|
25
|
+
export enum BaseJobLogStatus {
|
|
26
|
+
UNKNOWN_BJLS = 0,
|
|
27
|
+
/** SUCCESS - 成功 */
|
|
28
|
+
SUCCESS = 1,
|
|
29
|
+
/** FAIL - 失败 */
|
|
30
|
+
FAIL = 2,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 菜单类型 */
|
|
34
|
+
export enum BaseMenuType {
|
|
35
|
+
UNKNOWN_MT = 0,
|
|
36
|
+
/** FOLDER - 目录 */
|
|
37
|
+
FOLDER = 1,
|
|
38
|
+
/** MENU - 菜单 */
|
|
39
|
+
MENU = 2,
|
|
40
|
+
/** BUTTON - 按钮 */
|
|
41
|
+
BUTTON = 3,
|
|
42
|
+
/** EXT_LINK - 外部链接 */
|
|
43
|
+
EXT_LINK = 4,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 角色数据范围 */
|
|
47
|
+
export enum BaseRoleDataScope {
|
|
48
|
+
UNKNOWN_RDS = 0,
|
|
49
|
+
/** ALL - 全部数据 */
|
|
50
|
+
ALL = 1,
|
|
51
|
+
/** DEPT_AND_CHILDREN - 部门及子部门数据 */
|
|
52
|
+
DEPT_AND_CHILDREN = 2,
|
|
53
|
+
/** SELF_DEPT - 本部门数据 */
|
|
54
|
+
SELF_DEPT = 3,
|
|
55
|
+
/** SELF_USER - 本人数据 */
|
|
56
|
+
SELF_USER = 4,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 用户性别 */
|
|
60
|
+
export enum BaseUserGender {
|
|
61
|
+
UNKNOWN_BUG = 0,
|
|
62
|
+
/** SECRET - 保密 */
|
|
63
|
+
SECRET = 1,
|
|
64
|
+
/** BOY - 男 */
|
|
65
|
+
BOY = 2,
|
|
66
|
+
/** GIRL - 禁用 */
|
|
67
|
+
GIRL = 3,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** 代码生成表状态 */
|
|
71
|
+
export enum CodeGenTableStatus {
|
|
72
|
+
/** UNKNOWN_CGTS - 未指定 */
|
|
73
|
+
UNKNOWN_CGTS = 0,
|
|
74
|
+
/** DRAFT_CGTS - 草稿 */
|
|
75
|
+
DRAFT_CGTS = 1,
|
|
76
|
+
/** GENERATED_CGTS - 已生成 */
|
|
77
|
+
GENERATED_CGTS = 2,
|
|
78
|
+
/** DISABLED_CGTS - 停用 */
|
|
79
|
+
DISABLED_CGTS = 3,
|
|
80
|
+
}
|
package/src/runner.ts
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import {
|
|
2
|
+
copyFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
rmdirSync,
|
|
8
|
+
statSync,
|
|
9
|
+
unlinkSync,
|
|
10
|
+
writeFileSync,
|
|
11
|
+
} from 'node:fs'
|
|
12
|
+
import { createRequire } from 'node:module'
|
|
13
|
+
import { dirname, relative, resolve } from 'node:path'
|
|
14
|
+
import { pathToFileURL } from 'node:url'
|
|
15
|
+
import { spawn } from 'node:child_process'
|
|
16
|
+
import ts from 'typescript'
|
|
17
|
+
import type { KratosTaroBuildModule } from './build'
|
|
18
|
+
|
|
19
|
+
type PageEntry = {
|
|
20
|
+
route: string
|
|
21
|
+
source: string
|
|
22
|
+
style?: Record<string, unknown>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type AppManifest = {
|
|
26
|
+
pages: string[]
|
|
27
|
+
subpackages?: Array<{ root: string; pages: string[] }>
|
|
28
|
+
[key: string]: unknown
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type BuildTransaction = {
|
|
32
|
+
inputDir: string
|
|
33
|
+
appConfigFile: string
|
|
34
|
+
originalAppConfig: string
|
|
35
|
+
generatedFiles: string[]
|
|
36
|
+
generatedStaticFiles: string[]
|
|
37
|
+
ownerPid: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type CliOptions = {
|
|
41
|
+
type: 'h5' | 'weapp'
|
|
42
|
+
mode: string
|
|
43
|
+
watch: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 运行 Taro 构建并在进程结束后恢复临时页面。 */
|
|
47
|
+
async function main(): Promise<void> {
|
|
48
|
+
const options = parseArguments(process.argv.slice(2))
|
|
49
|
+
const hostRoot = process.cwd()
|
|
50
|
+
const inputDir = resolve(hostRoot, 'src')
|
|
51
|
+
const appConfigFile = resolve(inputDir, 'app.config.ts')
|
|
52
|
+
const baseConfigFile = resolve(inputDir, 'app.config.base.json')
|
|
53
|
+
const transactionFile = resolve(hostRoot, '.kratos-taro-app-pages-state.json')
|
|
54
|
+
recoverBuildTransaction(transactionFile, inputDir, appConfigFile)
|
|
55
|
+
|
|
56
|
+
const originalAppConfig = readFileSync(appConfigFile, 'utf8')
|
|
57
|
+
const manifest = JSON.parse(readFileSync(baseConfigFile, 'utf8')) as AppManifest
|
|
58
|
+
const modules = await loadBuildModules(hostRoot, resolve(inputDir, 'module-manifest.ts'))
|
|
59
|
+
const pageMap = new Map<string, PageEntry>()
|
|
60
|
+
modules.forEach((module) => {
|
|
61
|
+
scanViews(resolve(module.root, 'src/views')).forEach((page) => {
|
|
62
|
+
const config = module.pages[page.route] ?? {}
|
|
63
|
+
const route = normalizeRoute(config.route ?? page.route)
|
|
64
|
+
pageMap.set(route, {
|
|
65
|
+
route,
|
|
66
|
+
source: `${module.name}/views/${page.route}`,
|
|
67
|
+
style: config.style,
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const generatedFiles = [...pageMap.values()].flatMap((page) => {
|
|
73
|
+
const pageFile = resolve(inputDir, `${page.route}.tsx`)
|
|
74
|
+
const configFile = resolve(inputDir, `${page.route}.config.ts`)
|
|
75
|
+
return [pageFile, configFile].filter((file) => !existsSync(file))
|
|
76
|
+
})
|
|
77
|
+
const staticRoot = resolve(inputDir, 'static')
|
|
78
|
+
const staticFiles = collectModuleStaticFiles(
|
|
79
|
+
modules.map((module) => resolve(module.root, 'src/static')),
|
|
80
|
+
staticRoot,
|
|
81
|
+
)
|
|
82
|
+
const generatedStaticFiles = staticFiles.map(({ target }) => target)
|
|
83
|
+
const transaction: BuildTransaction = {
|
|
84
|
+
inputDir,
|
|
85
|
+
appConfigFile,
|
|
86
|
+
originalAppConfig,
|
|
87
|
+
generatedFiles,
|
|
88
|
+
generatedStaticFiles,
|
|
89
|
+
ownerPid: process.pid,
|
|
90
|
+
}
|
|
91
|
+
writeFileSync(transactionFile, `${JSON.stringify(transaction, null, 2)}\n`, { flag: 'wx' })
|
|
92
|
+
|
|
93
|
+
let cleaned = false
|
|
94
|
+
const cleanup = () => {
|
|
95
|
+
if (cleaned) return
|
|
96
|
+
cleaned = true
|
|
97
|
+
cleanupTransaction(transaction)
|
|
98
|
+
if (existsSync(transactionFile)) unlinkSync(transactionFile)
|
|
99
|
+
}
|
|
100
|
+
const handleSignal = (signal: NodeJS.Signals) => {
|
|
101
|
+
child?.kill(signal)
|
|
102
|
+
}
|
|
103
|
+
let child: ReturnType<typeof spawn> | undefined
|
|
104
|
+
process.once('SIGINT', () => handleSignal('SIGINT'))
|
|
105
|
+
process.once('SIGTERM', () => handleSignal('SIGTERM'))
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
pageMap.forEach((page) => {
|
|
109
|
+
appendPage(manifest, page.route)
|
|
110
|
+
const pageFile = resolve(inputDir, `${page.route}.tsx`)
|
|
111
|
+
const configFile = resolve(inputDir, `${page.route}.config.ts`)
|
|
112
|
+
if (!existsSync(pageFile)) {
|
|
113
|
+
mkdirSync(dirname(pageFile), { recursive: true })
|
|
114
|
+
writeFileSync(pageFile, createPageWrapper(page))
|
|
115
|
+
}
|
|
116
|
+
if (!existsSync(configFile)) {
|
|
117
|
+
writeFileSync(configFile, createPageConfig(page))
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
staticFiles.forEach(({ source, target }) => {
|
|
121
|
+
mkdirSync(dirname(target), { recursive: true })
|
|
122
|
+
copyFileSync(source, target)
|
|
123
|
+
})
|
|
124
|
+
writeFileSync(
|
|
125
|
+
appConfigFile,
|
|
126
|
+
`export default defineAppConfig(${JSON.stringify(manifest, null, 2)})\n`,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const args = ['exec', 'taro', 'build', '--type', options.type, '--mode', options.mode]
|
|
130
|
+
if (options.watch) args.push('--watch')
|
|
131
|
+
child = spawn(process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm', args, {
|
|
132
|
+
cwd: hostRoot,
|
|
133
|
+
env: {
|
|
134
|
+
...process.env,
|
|
135
|
+
NODE_ENV: options.mode === 'development' ? 'development' : 'production',
|
|
136
|
+
},
|
|
137
|
+
stdio: 'inherit',
|
|
138
|
+
})
|
|
139
|
+
const exitCode = await new Promise<number>((complete, reject) => {
|
|
140
|
+
child?.once('error', reject)
|
|
141
|
+
child?.once('exit', (code, signal) => {
|
|
142
|
+
if (signal) {
|
|
143
|
+
complete(signal === 'SIGINT' ? 130 : 143)
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
complete(code ?? 1)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
if (exitCode !== 0) process.exitCode = exitCode
|
|
150
|
+
} finally {
|
|
151
|
+
cleanup()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function parseArguments(args: string[]): CliOptions {
|
|
156
|
+
let type: CliOptions['type'] | undefined
|
|
157
|
+
let mode = 'production'
|
|
158
|
+
let watch = false
|
|
159
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
160
|
+
const argument = args[index]
|
|
161
|
+
if (argument === '--type') {
|
|
162
|
+
const value = args[++index]
|
|
163
|
+
if (value !== 'h5' && value !== 'weapp') throw new Error(`不支持的 Taro 类型:${value}`)
|
|
164
|
+
type = value
|
|
165
|
+
} else if (argument === '--mode') {
|
|
166
|
+
mode = args[++index] || mode
|
|
167
|
+
} else if (argument === '--watch') {
|
|
168
|
+
watch = true
|
|
169
|
+
} else {
|
|
170
|
+
throw new Error(`未知参数:${argument}`)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (!type) throw new Error('缺少 --type h5|weapp')
|
|
174
|
+
return { type, mode, watch }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function loadBuildModules(
|
|
178
|
+
hostRoot: string,
|
|
179
|
+
manifestFile: string,
|
|
180
|
+
): Promise<KratosTaroBuildModule[]> {
|
|
181
|
+
const source = readFileSync(manifestFile, 'utf8')
|
|
182
|
+
const sourceFile = ts.createSourceFile(
|
|
183
|
+
manifestFile,
|
|
184
|
+
source,
|
|
185
|
+
ts.ScriptTarget.Latest,
|
|
186
|
+
true,
|
|
187
|
+
ts.ScriptKind.TS,
|
|
188
|
+
)
|
|
189
|
+
const imports = new Map<string, string>()
|
|
190
|
+
let moduleNames: string[] = []
|
|
191
|
+
sourceFile.forEachChild((node) => {
|
|
192
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
193
|
+
const importClause = node.importClause
|
|
194
|
+
const bindings = node.importClause?.namedBindings
|
|
195
|
+
const packageName = node.moduleSpecifier.text
|
|
196
|
+
if (importClause?.name) imports.set(importClause.name.text, packageName)
|
|
197
|
+
if (bindings && ts.isNamedImports(bindings)) {
|
|
198
|
+
bindings.elements.forEach((element) => {
|
|
199
|
+
imports.set(element.name.text, packageName)
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
if (!ts.isVariableStatement(node)) return
|
|
205
|
+
node.declarationList.declarations.forEach((declaration) => {
|
|
206
|
+
if (
|
|
207
|
+
ts.isIdentifier(declaration.name) &&
|
|
208
|
+
declaration.name.text === 'moduleManifest' &&
|
|
209
|
+
declaration.initializer &&
|
|
210
|
+
ts.isArrayLiteralExpression(declaration.initializer)
|
|
211
|
+
) {
|
|
212
|
+
moduleNames = declaration.initializer.elements
|
|
213
|
+
.filter(ts.isIdentifier)
|
|
214
|
+
.map((element) => element.text)
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
if (!moduleNames.length) throw new Error(`模块清单未导出 moduleManifest:${manifestFile}`)
|
|
219
|
+
|
|
220
|
+
const hostRequire = createRequire(resolve(hostRoot, 'package.json'))
|
|
221
|
+
return Promise.all(
|
|
222
|
+
moduleNames.map(async (name) => {
|
|
223
|
+
const packageName = imports.get(name)
|
|
224
|
+
if (!packageName) throw new Error(`模块 ${name} 缺少静态 import`)
|
|
225
|
+
const buildEntry = hostRequire.resolve(`${packageName}/build`)
|
|
226
|
+
const loaded = (await import(pathToFileURL(buildEntry).href)) as {
|
|
227
|
+
buildModule?: KratosTaroBuildModule
|
|
228
|
+
}
|
|
229
|
+
if (!loaded.buildModule) throw new Error(`${packageName}/build 未导出 buildModule`)
|
|
230
|
+
return loaded.buildModule
|
|
231
|
+
}),
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function recoverBuildTransaction(
|
|
236
|
+
transactionFile: string,
|
|
237
|
+
inputDir: string,
|
|
238
|
+
appConfigFile: string,
|
|
239
|
+
): void {
|
|
240
|
+
if (!existsSync(transactionFile)) return
|
|
241
|
+
const transaction = JSON.parse(readFileSync(transactionFile, 'utf8')) as BuildTransaction
|
|
242
|
+
if (transaction.inputDir !== inputDir || transaction.appConfigFile !== appConfigFile) {
|
|
243
|
+
throw new Error(`Taro 构建事务目录不匹配:${transactionFile}`)
|
|
244
|
+
}
|
|
245
|
+
if (transaction.ownerPid !== process.pid && isProcessRunning(transaction.ownerPid)) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`页面装配已由进程 ${transaction.ownerPid} 使用,请先停止另一个 H5 或小程序开发进程`,
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
cleanupTransaction(transaction)
|
|
251
|
+
unlinkSync(transactionFile)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function cleanupTransaction(transaction: BuildTransaction): void {
|
|
255
|
+
for (const file of [...transaction.generatedFiles].reverse()) {
|
|
256
|
+
if (existsSync(file)) unlinkSync(file)
|
|
257
|
+
removeEmptyParents(dirname(file), transaction.inputDir)
|
|
258
|
+
}
|
|
259
|
+
removeGeneratedStaticFiles(transaction.generatedStaticFiles, resolve(transaction.inputDir, 'static'))
|
|
260
|
+
writeFileSync(transaction.appConfigFile, transaction.originalAppConfig)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function isProcessRunning(pid: number): boolean {
|
|
264
|
+
try {
|
|
265
|
+
process.kill(pid, 0)
|
|
266
|
+
return true
|
|
267
|
+
} catch (error) {
|
|
268
|
+
return (error as NodeJS.ErrnoException).code === 'EPERM'
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function scanViews(root: string): Array<{ route: string; source: string }> {
|
|
273
|
+
if (!existsSync(root)) return []
|
|
274
|
+
const files: string[] = []
|
|
275
|
+
const visit = (directory: string) => {
|
|
276
|
+
readdirSync(directory).forEach((entry) => {
|
|
277
|
+
const path = resolve(directory, entry)
|
|
278
|
+
if (statSync(path).isDirectory()) {
|
|
279
|
+
if (entry !== 'components') visit(path)
|
|
280
|
+
} else if (entry.endsWith('.tsx')) {
|
|
281
|
+
files.push(path)
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
visit(root)
|
|
286
|
+
return files.map((source) => ({
|
|
287
|
+
route: normalizeRoute(relative(root, source)),
|
|
288
|
+
source,
|
|
289
|
+
}))
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function collectModuleStaticFiles(
|
|
293
|
+
sourceRoots: string[],
|
|
294
|
+
targetRoot: string,
|
|
295
|
+
): Array<{ source: string; target: string }> {
|
|
296
|
+
const fileMap = new Map<string, { source: string; target: string }>()
|
|
297
|
+
sourceRoots.forEach((sourceRoot) => {
|
|
298
|
+
scanFiles(sourceRoot).forEach((source) => {
|
|
299
|
+
const target = resolve(targetRoot, relative(sourceRoot, source))
|
|
300
|
+
if (!existsSync(target)) fileMap.set(target, { source, target })
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
return [...fileMap.values()]
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function scanFiles(root: string): string[] {
|
|
307
|
+
if (!existsSync(root)) return []
|
|
308
|
+
const files: string[] = []
|
|
309
|
+
const visit = (directory: string) => {
|
|
310
|
+
readdirSync(directory).forEach((entry) => {
|
|
311
|
+
const path = resolve(directory, entry)
|
|
312
|
+
if (statSync(path).isDirectory()) visit(path)
|
|
313
|
+
else files.push(path)
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
visit(root)
|
|
317
|
+
return files
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function appendPage(manifest: AppManifest, route: string): void {
|
|
321
|
+
const [first, ...rest] = route.split('/')
|
|
322
|
+
if (!first.startsWith('pages') || first === 'pages') {
|
|
323
|
+
if (!manifest.pages.includes(route)) manifest.pages.push(route)
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
manifest.subpackages ??= []
|
|
327
|
+
let subpackage = manifest.subpackages.find((item) => item.root === first)
|
|
328
|
+
if (!subpackage) {
|
|
329
|
+
subpackage = { root: first, pages: [] }
|
|
330
|
+
manifest.subpackages.push(subpackage)
|
|
331
|
+
}
|
|
332
|
+
const subRoute = rest.join('/')
|
|
333
|
+
if (!subpackage.pages.includes(subRoute)) subpackage.pages.push(subRoute)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function createPageWrapper(page: PageEntry): string {
|
|
337
|
+
return `import KratosPage from ${JSON.stringify(page.source)}
|
|
338
|
+
import { KratosPageFrame } from '@liujitcn/kratos-taro-app-core/components/KratosPageFrame'
|
|
339
|
+
import { KratosTabBar } from '@liujitcn/kratos-taro-app-core/components/KratosTabBar'
|
|
340
|
+
|
|
341
|
+
/** 自动生成的模块页面包装器。 */
|
|
342
|
+
export default function KratosPageWrapper() {
|
|
343
|
+
return (
|
|
344
|
+
<>
|
|
345
|
+
<KratosPageFrame
|
|
346
|
+
navigationStyle=${JSON.stringify(page.style?.navigationStyle ?? 'default')}
|
|
347
|
+
navigationBarTitleText=${JSON.stringify(page.style?.navigationBarTitleText ?? '')}
|
|
348
|
+
navigationBarBackgroundColor=${JSON.stringify(page.style?.navigationBarBackgroundColor ?? '#f8f8f8')}
|
|
349
|
+
navigationBarTextStyle=${JSON.stringify(page.style?.navigationBarTextStyle ?? 'black')}
|
|
350
|
+
>
|
|
351
|
+
<KratosPage />
|
|
352
|
+
</KratosPageFrame>
|
|
353
|
+
<KratosTabBar route=${JSON.stringify(page.route)} />
|
|
354
|
+
</>
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
`
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function createPageConfig(page: PageEntry): string {
|
|
361
|
+
return `export default definePageConfig(${JSON.stringify(page.style ?? {}, null, 2)})\n`
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function normalizeRoute(route: string): string {
|
|
365
|
+
return route
|
|
366
|
+
.replace(/\\/g, '/')
|
|
367
|
+
.replace(/^\/+/, '')
|
|
368
|
+
.replace(/\.tsx$/, '')
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function removeGeneratedStaticFiles(files: string[], staticRoot: string): void {
|
|
372
|
+
for (const file of [...files].reverse()) {
|
|
373
|
+
if (existsSync(file)) unlinkSync(file)
|
|
374
|
+
removeEmptyParents(dirname(file), staticRoot)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function removeEmptyParents(directory: string, boundary: string): void {
|
|
379
|
+
let current = directory
|
|
380
|
+
while (current !== boundary && current.startsWith(boundary) && existsSync(current)) {
|
|
381
|
+
if (readdirSync(current).length) return
|
|
382
|
+
rmdirSync(current)
|
|
383
|
+
current = dirname(current)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
void main().catch((error: unknown) => {
|
|
388
|
+
console.error(error)
|
|
389
|
+
process.exitCode = 1
|
|
390
|
+
})
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { create } from 'zustand'
|
|
2
|
+
import { defConfigService } from '../api/base/config'
|
|
3
|
+
import { BaseConfigSite } from '../rpc/common/v1/enum'
|
|
4
|
+
|
|
5
|
+
const REQUIRED_APP_CONFIGS = [
|
|
6
|
+
{ key: 'serviceProtocol', name: '服务条款' },
|
|
7
|
+
{ key: 'privacyProtocol', name: '隐私协议' },
|
|
8
|
+
{ key: 'captchaType', name: '登录验证码类型' },
|
|
9
|
+
] as const
|
|
10
|
+
|
|
11
|
+
/** 应用配置状态及操作。 */
|
|
12
|
+
export interface SettingStoreState {
|
|
13
|
+
data?: Map<string, string>
|
|
14
|
+
getData: (key: string) => string | undefined
|
|
15
|
+
loadData: () => Promise<void>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let loading: Promise<void> | undefined
|
|
19
|
+
|
|
20
|
+
/** 应用配置 Zustand Store。 */
|
|
21
|
+
export const useSettingStore = create<SettingStoreState>((set, get) => ({
|
|
22
|
+
data: undefined,
|
|
23
|
+
getData(key) {
|
|
24
|
+
return get().data?.get(key)
|
|
25
|
+
},
|
|
26
|
+
async loadData() {
|
|
27
|
+
if (loading) return loading
|
|
28
|
+
loading = (async () => {
|
|
29
|
+
const response = await defConfigService.GetConfig({ site: BaseConfigSite.APP })
|
|
30
|
+
const nextData = new Map(response.configs.map((item) => [item.key, item.value]))
|
|
31
|
+
const missing = REQUIRED_APP_CONFIGS.filter(({ key }) => !nextData.get(key))
|
|
32
|
+
if (missing.length) {
|
|
33
|
+
throw new Error(`移动端配置缺失:${missing.map(({ name }) => name).join('、')}`)
|
|
34
|
+
}
|
|
35
|
+
set({ data: nextData })
|
|
36
|
+
})()
|
|
37
|
+
try {
|
|
38
|
+
await loading
|
|
39
|
+
} finally {
|
|
40
|
+
loading = undefined
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
}))
|