@77-zhike/app-sdk 0.2.0-d7b1.18 → 0.2.0-d7b1.20
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/package.json +2 -2
- package/src/cli/index.js +26 -12
- package/src/cli/local-commands.js +1 -1
- package/src/cli/project-init.js +53 -9
- package/src/client/index.d.ts +5 -0
- package/src/client/index.js +7 -1
- package/src/client/interactions.d.ts +8 -3
- package/src/client/interactions.js +9 -0
- package/src/toolchain/real-build.js +2 -1
- package/src/toolchain/source-discovery.js +11 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@77-zhike/app-sdk",
|
|
3
|
-
"version": "0.2.0-d7b1.
|
|
3
|
+
"version": "0.2.0-d7b1.20",
|
|
4
4
|
"description": "智客 Integration App 一期 SDK 与本地 Toolchain",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"optional": true
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "c7f1b793276cbde656dbd058731fcb8b51b95745"
|
|
71
71
|
}
|
package/src/cli/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
assertExistingProjectTarget,
|
|
11
11
|
initializeExistingProject,
|
|
12
12
|
initializeProject,
|
|
13
|
+
organizationLabel,
|
|
13
14
|
validateAppSlug,
|
|
14
15
|
} from "./project-init.js";
|
|
15
16
|
import { uploadAppBundle } from "./upload-bundle.js";
|
|
@@ -30,6 +31,7 @@ export async function runCli(
|
|
|
30
31
|
fetchImpl = globalThis.fetch,
|
|
31
32
|
openBrowser,
|
|
32
33
|
selectWorkspace,
|
|
34
|
+
selectOrganization,
|
|
33
35
|
isInteractive = Boolean(process.stdin.isTTY),
|
|
34
36
|
developmentRunner = watchDevelopmentApp,
|
|
35
37
|
} = {},
|
|
@@ -104,16 +106,7 @@ export async function runCli(
|
|
|
104
106
|
return { command: "login" };
|
|
105
107
|
}
|
|
106
108
|
if (command === "init") {
|
|
107
|
-
const here = rest
|
|
108
|
-
const slugArgs = rest.filter((arg) => arg !== "--here");
|
|
109
|
-
if (slugArgs.length !== 1 || slugArgs[0].startsWith("-") ||
|
|
110
|
-
rest.length !== (here ? 2 : 1)) {
|
|
111
|
-
throw new ZhikeCliError(
|
|
112
|
-
"zhike_init_usage_invalid",
|
|
113
|
-
"用法:zhike init <app-slug> [--here]",
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
const appSlug = validateAppSlug(slugArgs[0]);
|
|
109
|
+
const { appSlug, here, requestedOrganizationId } = _parseInitArguments(rest);
|
|
117
110
|
const targetDirectory = here ? cwd : resolve(cwd, appSlug);
|
|
118
111
|
if (here) await assertExistingProjectTarget(targetDirectory);
|
|
119
112
|
else await assertEmptyTarget(targetDirectory);
|
|
@@ -132,9 +125,10 @@ export async function runCli(
|
|
|
132
125
|
);
|
|
133
126
|
const result = here
|
|
134
127
|
? await initializeExistingProject({ projectDirectory: targetDirectory, appSlug,
|
|
135
|
-
apiBaseUrl: config.apiBaseUrl, authenticatedApi })
|
|
128
|
+
apiBaseUrl: config.apiBaseUrl, authenticatedApi, requestedOrganizationId, selectOrganization, isInteractive })
|
|
136
129
|
: await initializeProject({ targetDirectory, appSlug, apiBaseUrl: config.apiBaseUrl,
|
|
137
|
-
authenticatedApi, ...(scaffold ? { scaffold } : {}) });
|
|
130
|
+
authenticatedApi, requestedOrganizationId, selectOrganization, isInteractive, ...(scaffold ? { scaffold } : {}) });
|
|
131
|
+
output.write(`开发者空间:${organizationLabel(result.organization)}\n`);
|
|
138
132
|
output.write(
|
|
139
133
|
`${result.created ? "已创建" : "已复用"} App ${result.app.slug},项目位于 ${here ? "当前目录" : `${appSlug}/`}(总耗时 ${_formatElapsed(startedAt)})\n`,
|
|
140
134
|
);
|
|
@@ -214,6 +208,26 @@ function _rejectArguments(args) {
|
|
|
214
208
|
}
|
|
215
209
|
}
|
|
216
210
|
|
|
211
|
+
function _parseInitArguments(args) {
|
|
212
|
+
let here = false;
|
|
213
|
+
let requestedOrganizationId = null;
|
|
214
|
+
let slug = null;
|
|
215
|
+
const invalid = () => new ZhikeCliError("zhike_init_usage_invalid",
|
|
216
|
+
"用法:zhike init <app-slug> [--here] [--organization <organization-id>]");
|
|
217
|
+
for (let index = 0; index < args.length; index++) {
|
|
218
|
+
const arg = args[index];
|
|
219
|
+
if (arg === "--here" && !here) here = true;
|
|
220
|
+
else if (arg === "--organization" && !requestedOrganizationId) {
|
|
221
|
+
const value = args[++index];
|
|
222
|
+
if (!value || !/^[0-9A-HJKMNP-TV-Z]{26}$/.test(value)) throw invalid();
|
|
223
|
+
requestedOrganizationId = value;
|
|
224
|
+
} else if (!arg.startsWith("-") && !slug) slug = arg;
|
|
225
|
+
else throw invalid();
|
|
226
|
+
}
|
|
227
|
+
if (!slug) throw invalid();
|
|
228
|
+
return { appSlug: validateAppSlug(slug), here, requestedOrganizationId };
|
|
229
|
+
}
|
|
230
|
+
|
|
217
231
|
function _parseDevArguments(args) {
|
|
218
232
|
if (args.length === 0) return null;
|
|
219
233
|
if (args.length === 2 && args[0] === "--workspace" && args[1]) {
|
|
@@ -13,7 +13,7 @@ export const commandHelp = {
|
|
|
13
13
|
version: "version [--json]",
|
|
14
14
|
doctor: "doctor [--json]",
|
|
15
15
|
login: "login",
|
|
16
|
-
init: "init <app-slug> [--here]",
|
|
16
|
+
init: "init <app-slug> [--here] [--organization <organization-id>]",
|
|
17
17
|
validate: "validate",
|
|
18
18
|
build: "build",
|
|
19
19
|
upload: "upload",
|
package/src/cli/project-init.js
CHANGED
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
} from "node:fs/promises";
|
|
11
11
|
import { constants } from "node:fs";
|
|
12
12
|
import { basename, dirname, resolve } from "node:path";
|
|
13
|
+
import { createInterface } from "node:readline/promises";
|
|
14
|
+
import { stripVTControlCharacters } from "node:util";
|
|
13
15
|
|
|
14
16
|
import { normalizeOrigin, TOOLING_VERSION } from "../config.js";
|
|
15
17
|
import { ZhikeCliError } from "../errors.js";
|
|
@@ -21,21 +23,27 @@ export async function initializeProject({
|
|
|
21
23
|
apiBaseUrl,
|
|
22
24
|
authenticatedApi,
|
|
23
25
|
scaffold = scaffoldProject,
|
|
26
|
+
requestedOrganizationId = null,
|
|
27
|
+
selectOrganization,
|
|
28
|
+
isInteractive = Boolean(process.stdin.isTTY),
|
|
24
29
|
}) {
|
|
25
30
|
validateAppSlug(appSlug);
|
|
26
31
|
const target = resolve(targetDirectory);
|
|
27
32
|
await assertEmptyTarget(target);
|
|
28
33
|
|
|
29
|
-
const { app, organization, binding, created } = await resolveAppBinding({ appSlug, apiBaseUrl, authenticatedApi
|
|
34
|
+
const { app, organization, binding, created } = await resolveAppBinding({ appSlug, apiBaseUrl, authenticatedApi,
|
|
35
|
+
requestedOrganizationId, selectOrganization, isInteractive });
|
|
30
36
|
await scaffold({ targetDirectory: target, binding });
|
|
31
37
|
return { app, organization, binding, created, targetDirectory: target };
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
export async function initializeExistingProject({ projectDirectory, appSlug, apiBaseUrl, authenticatedApi
|
|
40
|
+
export async function initializeExistingProject({ projectDirectory, appSlug, apiBaseUrl, authenticatedApi,
|
|
41
|
+
requestedOrganizationId = null, selectOrganization, isInteractive = Boolean(process.stdin.isTTY) }) {
|
|
35
42
|
validateAppSlug(appSlug);
|
|
36
43
|
const target = resolve(projectDirectory);
|
|
37
44
|
await assertExistingProjectTarget(target);
|
|
38
|
-
const { app, organization, binding, created } = await resolveAppBinding({ appSlug, apiBaseUrl, authenticatedApi
|
|
45
|
+
const { app, organization, binding, created } = await resolveAppBinding({ appSlug, apiBaseUrl, authenticatedApi,
|
|
46
|
+
requestedOrganizationId, selectOrganization, isInteractive });
|
|
39
47
|
await mkdir(resolve(target, ".zhike"), { recursive: true });
|
|
40
48
|
await writeFile(resolve(target, ".zhike", "project.json"), serializeProjectBinding(binding), { flag: "wx" });
|
|
41
49
|
return { app, organization, binding, created, targetDirectory: target };
|
|
@@ -64,17 +72,33 @@ export async function assertExistingProjectTarget(projectDirectory) {
|
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
|
|
67
|
-
async function resolveAppBinding({ appSlug, apiBaseUrl, authenticatedApi
|
|
75
|
+
async function resolveAppBinding({ appSlug, apiBaseUrl, authenticatedApi,
|
|
76
|
+
requestedOrganizationId, selectOrganization, isInteractive }) {
|
|
68
77
|
const organizations = await authenticatedApi.listOrganizations();
|
|
69
|
-
if (!Array.isArray(organizations) || organizations.
|
|
78
|
+
if (!Array.isArray(organizations) || organizations.some((item) =>
|
|
79
|
+
!item || !/^[0-9A-HJKMNP-TV-Z]{26}$/.test(item.id) || typeof item.name !== "string" || !item.name.trim()) ||
|
|
80
|
+
new Set(organizations.map(item => item.id)).size !== organizations.length) {
|
|
81
|
+
throw new ZhikeCliError("zhike_organizations_response_invalid", "Developer Server 返回了无效的开发者空间列表");
|
|
82
|
+
}
|
|
83
|
+
if (organizations.length === 0) {
|
|
70
84
|
throw new ZhikeCliError(
|
|
71
85
|
"zhike_developer_organization_unavailable",
|
|
72
|
-
|
|
73
|
-
? "请先在 Developer Console 激活开发者组织"
|
|
74
|
-
: "当前账号存在多个开发者组织,D2-2 暂不支持隐式选择",
|
|
86
|
+
"请先在 Developer Console 激活开发者空间",
|
|
75
87
|
);
|
|
76
88
|
}
|
|
77
|
-
|
|
89
|
+
let selectedId = requestedOrganizationId;
|
|
90
|
+
if (!selectedId && organizations.length === 1) selectedId = organizations[0].id;
|
|
91
|
+
if (!selectedId) {
|
|
92
|
+
if (!isInteractive && !selectOrganization) {
|
|
93
|
+
throw new ZhikeCliError("zhike_organization_selection_required",
|
|
94
|
+
"当前账号有多个开发者空间,非交互环境必须指定 --organization <organization-id>");
|
|
95
|
+
}
|
|
96
|
+
selectedId = await (selectOrganization || promptForOrganization)(organizations);
|
|
97
|
+
}
|
|
98
|
+
const organization = organizations.find(item => item.id === selectedId);
|
|
99
|
+
if (!organization) {
|
|
100
|
+
throw new ZhikeCliError("zhike_organization_not_available", "未选择可访问的开发者空间,请使用列表中的空间 ID");
|
|
101
|
+
}
|
|
78
102
|
let app = await authenticatedApi.getAppBySlug(organization.id, appSlug);
|
|
79
103
|
let created = false;
|
|
80
104
|
if (!app) {
|
|
@@ -99,6 +123,26 @@ async function resolveAppBinding({ appSlug, apiBaseUrl, authenticatedApi }) {
|
|
|
99
123
|
return { app, organization, binding, created };
|
|
100
124
|
}
|
|
101
125
|
|
|
126
|
+
export async function promptForOrganization(organizations, { input = process.stdin, output = process.stdout } = {}) {
|
|
127
|
+
const prompt = createInterface({ input, output });
|
|
128
|
+
try {
|
|
129
|
+
output.write("请选择 App 所属的开发者空间:\n");
|
|
130
|
+
organizations.forEach((organization, index) => {
|
|
131
|
+
output.write(` ${index + 1}. ${organizationLabel(organization)}\n`);
|
|
132
|
+
});
|
|
133
|
+
const answer = (await prompt.question("输入序号:")).trim();
|
|
134
|
+
const index = /^\d+$/.test(answer) ? Number(answer) - 1 : -1;
|
|
135
|
+
return organizations[index]?.id ?? "";
|
|
136
|
+
} finally {
|
|
137
|
+
prompt.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function organizationLabel(organization) {
|
|
142
|
+
const name = stripVTControlCharacters(organization.name).replace(/[\x00-\x1f\x7f-\x9f\u2028\u2029]/g, " ").trim();
|
|
143
|
+
return `${name} (${organization.id})`;
|
|
144
|
+
}
|
|
145
|
+
|
|
102
146
|
export async function assertEmptyTarget(targetDirectory) {
|
|
103
147
|
try {
|
|
104
148
|
await stat(targetDirectory);
|
package/src/client/index.d.ts
CHANGED
|
@@ -164,6 +164,8 @@ export interface PageDefinition {
|
|
|
164
164
|
showInNavigation?: boolean;
|
|
165
165
|
/** 默认显示宿主标题栏;false 时由 Page 自行提供标题。移动端仍保留返回导航。 */
|
|
166
166
|
showHeader?: boolean;
|
|
167
|
+
/** 缺省 false,按内容自然滚动;true 由宿主提供扣除导航后的确定高度,内部滚动由 Page 布局负责。 */
|
|
168
|
+
fillHeight?: boolean;
|
|
167
169
|
/** 纯 React Page component,只接收有界业务参数。 */
|
|
168
170
|
Page: ComponentType<PageProps>;
|
|
169
171
|
}
|
|
@@ -267,3 +269,6 @@ export type { RestrictedInputSchema, PreparePdfTemplateInput, OpenPdfDocumentInp
|
|
|
267
269
|
export function preparePdfTemplate(input: import('./documents.js').PreparePdfTemplateInput): Promise<import('./documents.js').PreparedPdfTemplate | null>;
|
|
268
270
|
/** Desktop/H5认证文件交付;started只表示已交浏览器,不证明磁盘保存。 */
|
|
269
271
|
export function openPdfDocument(input: import('./documents.js').OpenPdfDocumentInput): Promise<import('./documents.js').OpenPdfDocumentResult>;
|
|
272
|
+
|
|
273
|
+
/** 以当前成员权限打开稳定报告产物;宿主控制预览,不执行App提供的HTML。 */
|
|
274
|
+
export function openArtifact(input: import('./interactions.js').OpenArtifactOptions): Promise<void>;
|
package/src/client/index.js
CHANGED
|
@@ -36,7 +36,7 @@ export function definePage(definition) {
|
|
|
36
36
|
throw new TypeError("Page definition 必须是对象");
|
|
37
37
|
}
|
|
38
38
|
const keys = Object.keys(definition).sort();
|
|
39
|
-
const allowed = new Set(["Page", "name", "icon", "order", "variant", "showInNavigation", "showHeader"]);
|
|
39
|
+
const allowed = new Set(["Page", "name", "icon", "order", "variant", "showInNavigation", "showHeader", "fillHeight"]);
|
|
40
40
|
if (keys.some(key => !allowed.has(key))) {
|
|
41
41
|
throw new TypeError("Page definition 包含不支持的字段");
|
|
42
42
|
}
|
|
@@ -56,6 +56,9 @@ export function definePage(definition) {
|
|
|
56
56
|
if (typeof definition.Page !== "function") {
|
|
57
57
|
throw new TypeError("Page definition 必须提供 React Page component");
|
|
58
58
|
}
|
|
59
|
+
if (definition.fillHeight !== undefined && typeof definition.fillHeight !== 'boolean') {
|
|
60
|
+
throw new TypeError('Page fillHeight 必须是布尔值');
|
|
61
|
+
}
|
|
59
62
|
if (definition.showHeader !== undefined && typeof definition.showHeader !== 'boolean') {
|
|
60
63
|
throw new TypeError('Page showHeader 必须是布尔值');
|
|
61
64
|
}
|
|
@@ -70,6 +73,7 @@ export function definePage(definition) {
|
|
|
70
73
|
type: "page",
|
|
71
74
|
variant: definition.variant,
|
|
72
75
|
...(definition.showHeader === undefined ? {} : { showHeader: definition.showHeader }),
|
|
76
|
+
...(definition.fillHeight === undefined ? {} : { fillHeight: definition.fillHeight }),
|
|
73
77
|
...(definition.showInNavigation === undefined ? {} : { showInNavigation: definition.showInNavigation }),
|
|
74
78
|
});
|
|
75
79
|
}
|
|
@@ -86,3 +90,5 @@ export function queryAnalysis() { return Promise.reject(new Error('runtime_analy
|
|
|
86
90
|
export function openAssistant() { return Promise.reject(new Error('runtime_host_interaction_unavailable')); }
|
|
87
91
|
export function preparePdfTemplate() { return Promise.reject(new Error('runtime_document_unsupported')); }
|
|
88
92
|
export function openPdfDocument() { return Promise.reject(new Error('runtime_document_unsupported')); }
|
|
93
|
+
|
|
94
|
+
export function openArtifact() { return Promise.reject(new Error('runtime_host_interaction_unavailable')); }
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export interface OpenArtifactOptions {
|
|
2
|
+
/** 私有报告的稳定ID;不承载身份或授予读取权限。 */
|
|
3
|
+
artifactId: string;
|
|
4
|
+
}
|
|
1
5
|
export interface OpenAssistantOptions {
|
|
2
6
|
/** App 内的场景名;同 App/场景跨页面续聊,Host 自动拼接 App 前缀。 */
|
|
3
7
|
scenario: string;
|
|
@@ -11,9 +15,10 @@ export interface ToastOptions { message: string; tone?: 'info' | 'success' | 'er
|
|
|
11
15
|
export interface ConfirmOptions { title: string; description?: string; confirmLabel?: string; cancelLabel?: string }
|
|
12
16
|
export interface OpenExternalUrlOptions { url: string }
|
|
13
17
|
export interface NavigateOptions { destination: import('./destinations.js').AppDestination }
|
|
14
|
-
export type HostInteraction = {operation:'preparePdfTemplate';input:import('./documents.js').PreparePdfTemplateInput} | {operation:'openPdfDocument';input:import('./documents.js').OpenPdfDocumentInput} | {operation:'openAssistant';input:OpenAssistantOptions} | {operation:'showToast';input:ToastOptions} | {operation:'confirm';input:ConfirmOptions} | {operation:'navigate';input:NavigateOptions} | {operation:'openExternal';input:OpenExternalOptions} | {operation:'openExternalUrl';input:OpenExternalUrlOptions};
|
|
15
|
-
export function assertHostInteraction(operation: unknown, input: unknown): Readonly<ToastOptions | ConfirmOptions | NavigateOptions | OpenExternalOptions | OpenExternalUrlOptions | OpenAssistantOptions>;
|
|
16
|
-
export function createClientInteractions(invoke: (operation: string, input: ToastOptions | ConfirmOptions | NavigateOptions | OpenExternalOptions | OpenExternalUrlOptions | OpenAssistantOptions) => Promise<unknown>): {
|
|
18
|
+
export type HostInteraction = {operation:'openArtifact';input:OpenArtifactOptions} | {operation:'preparePdfTemplate';input:import('./documents.js').PreparePdfTemplateInput} | {operation:'openPdfDocument';input:import('./documents.js').OpenPdfDocumentInput} | {operation:'openAssistant';input:OpenAssistantOptions} | {operation:'showToast';input:ToastOptions} | {operation:'confirm';input:ConfirmOptions} | {operation:'navigate';input:NavigateOptions} | {operation:'openExternal';input:OpenExternalOptions} | {operation:'openExternalUrl';input:OpenExternalUrlOptions};
|
|
19
|
+
export function assertHostInteraction(operation: unknown, input: unknown): Readonly<OpenArtifactOptions | ToastOptions | ConfirmOptions | NavigateOptions | OpenExternalOptions | OpenExternalUrlOptions | OpenAssistantOptions>;
|
|
20
|
+
export function createClientInteractions(invoke: (operation: string, input: OpenArtifactOptions | ToastOptions | ConfirmOptions | NavigateOptions | OpenExternalOptions | OpenExternalUrlOptions | OpenAssistantOptions) => Promise<unknown>): {
|
|
21
|
+
openArtifact(input: OpenArtifactOptions): Promise<void>;
|
|
17
22
|
openAssistant(input: OpenAssistantOptions): Promise<void>;
|
|
18
23
|
showToast(input: ToastOptions): Promise<void>;
|
|
19
24
|
confirm(input: ConfirmOptions): Promise<boolean>;
|
|
@@ -12,6 +12,11 @@ const externalUrl = value => {
|
|
|
12
12
|
} catch { return false; }
|
|
13
13
|
};
|
|
14
14
|
export function assertHostInteraction(operation, input) {
|
|
15
|
+
if (operation === 'openArtifact') {
|
|
16
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).length !== 1
|
|
17
|
+
|| typeof input.artifactId !== 'string' || !/^[A-Za-z0-9]{26}$/.test(input.artifactId)) fail();
|
|
18
|
+
return Object.freeze({ artifactId: input.artifactId });
|
|
19
|
+
}
|
|
15
20
|
if (['preparePdfTemplate', 'openPdfDocument'].includes(operation)) return assertDocumentInput(operation, input);
|
|
16
21
|
if (!input || typeof input !== 'object' || Array.isArray(input)) fail();
|
|
17
22
|
const allowed = operation === 'openAssistant' ? ['scenario','context','title'] : operation === 'showToast' ? ['message','tone'] : operation === 'confirm' ? ['title','description','confirmLabel','cancelLabel'] : operation === 'navigate' ? ['destination'] : operation === 'openExternal' ? ['href'] : operation === 'openExternalUrl' ? ['url'] : [];
|
|
@@ -36,6 +41,10 @@ export function assertHostInteraction(operation, input) {
|
|
|
36
41
|
}
|
|
37
42
|
export function createClientInteractions(invoke) {
|
|
38
43
|
return Object.freeze({
|
|
44
|
+
openArtifact(input) {
|
|
45
|
+
const request = assertHostInteraction('openArtifact',input);
|
|
46
|
+
return invoke('openArtifact',request).then(value=>{if(value!==null)fail();});
|
|
47
|
+
},
|
|
39
48
|
openAssistant(input) {
|
|
40
49
|
const request = assertHostInteraction('openAssistant',input);
|
|
41
50
|
return invoke('openAssistant',request).then(value=>{if(value!==null)fail();});
|
|
@@ -392,7 +392,7 @@ function createManifest({ clientBytes, serverBytes, sources }) {
|
|
|
392
392
|
}
|
|
393
393
|
: {}),
|
|
394
394
|
},
|
|
395
|
-
frontendExtensions: sources.frontendExtensions.map(({ key, type, entry, label, icon, objects, name, order, variant, showInNavigation, showHeader }) => ({
|
|
395
|
+
frontendExtensions: sources.frontendExtensions.map(({ key, type, entry, label, icon, objects, name, order, variant, showInNavigation, showHeader, fillHeight }) => ({
|
|
396
396
|
artifact: "client",
|
|
397
397
|
entry,
|
|
398
398
|
key,
|
|
@@ -405,6 +405,7 @@ function createManifest({ clientBytes, serverBytes, sources }) {
|
|
|
405
405
|
...(variant === undefined ? {} : { variant }),
|
|
406
406
|
...(showInNavigation === undefined ? {} : { showInNavigation }),
|
|
407
407
|
...(showHeader === undefined ? {} : { showHeader }),
|
|
408
|
+
...(fillHeight === undefined ? {} : { fillHeight }),
|
|
408
409
|
})),
|
|
409
410
|
lifecycleEvents: sources.lifecycleEvents.map(({ event, key }) => ({
|
|
410
411
|
artifact: "server",
|
|
@@ -308,7 +308,7 @@ function parseWorkspacePage(sourceFile, relativePath, slug) {
|
|
|
308
308
|
const object = requireObjectLiteral(call.arguments[0], relativePath);
|
|
309
309
|
assertOnlyProperties(
|
|
310
310
|
object,
|
|
311
|
-
["name", "icon", "order", "variant", "Page", "showInNavigation", "showHeader"],
|
|
311
|
+
["name", "icon", "order", "variant", "Page", "showInNavigation", "showHeader", "fillHeight"],
|
|
312
312
|
relativePath,
|
|
313
313
|
"zhike_page_definition_invalid",
|
|
314
314
|
"Page definition 包含不支持的字段",
|
|
@@ -360,6 +360,15 @@ function parseWorkspacePage(sourceFile, relativePath, slug) {
|
|
|
360
360
|
}
|
|
361
361
|
showHeader = headerProperty.initializer.kind === ts.SyntaxKind.TrueKeyword;
|
|
362
362
|
}
|
|
363
|
+
const fillHeightProperty = findProperty(object, "fillHeight");
|
|
364
|
+
let fillHeight;
|
|
365
|
+
if (fillHeightProperty !== undefined) {
|
|
366
|
+
if (!ts.isPropertyAssignment(fillHeightProperty)
|
|
367
|
+
|| ![ts.SyntaxKind.TrueKeyword, ts.SyntaxKind.FalseKeyword].includes(fillHeightProperty.initializer.kind)) {
|
|
368
|
+
throw toolchainError("zhike_page_fill_height_invalid", `${relativePath}: fillHeight 必须是静态布尔值`);
|
|
369
|
+
}
|
|
370
|
+
fillHeight = fillHeightProperty.initializer.kind === ts.SyntaxKind.TrueKeyword;
|
|
371
|
+
}
|
|
363
372
|
const pageProperty = findProperty(object, "Page");
|
|
364
373
|
if (
|
|
365
374
|
!pageProperty ||
|
|
@@ -381,6 +390,7 @@ function parseWorkspacePage(sourceFile, relativePath, slug) {
|
|
|
381
390
|
...(order === undefined ? {} : { order }),
|
|
382
391
|
...(showInNavigation === undefined ? {} : { showInNavigation }),
|
|
383
392
|
...(showHeader === undefined ? {} : { showHeader }),
|
|
393
|
+
...(fillHeight === undefined ? {} : { fillHeight }),
|
|
384
394
|
variant,
|
|
385
395
|
});
|
|
386
396
|
}
|