@openfairygui/functions 0.2.0-alpha.1 → 0.2.0-alpha.12
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 +32 -3
- package/dist/atlas-CDn6TirX.d.ts +193 -0
- package/dist/atlas-CHsu2Y8i.d.cts +193 -0
- package/dist/codegen-C7PPZrB3.d.cts +241 -0
- package/dist/codegen-CEnmtjBP.d.ts +241 -0
- package/dist/index.cjs +98 -2896
- package/dist/index.d.cts +5 -274
- package/dist/index.d.ts +5 -274
- package/dist/index.js +86 -2888
- package/dist/node.cjs +128 -0
- package/dist/node.d.cts +31 -0
- package/dist/node.d.ts +31 -0
- package/dist/node.js +127 -0
- package/dist/publish-Bl-GK9kt.js +3337 -0
- package/dist/publish-CtCXsMdj.cjs +3408 -0
- package/dist/uam-transaction.cjs +79 -0
- package/dist/uam-transaction.d.cts +42 -0
- package/dist/uam-transaction.d.ts +42 -0
- package/dist/uam-transaction.js +78 -0
- package/dist/web.cjs +272 -0
- package/dist/web.d.cts +41 -0
- package/dist/web.d.ts +41 -0
- package/dist/web.js +271 -0
- package/package.json +86 -53
- package/src/adapters/node/plugins.ts +82 -0
- package/src/adapters/node/publish.ts +129 -0
- package/src/adapters/web/publish.ts +404 -0
- package/src/atlas.ts +702 -254
- package/src/codegen.ts +106 -67
- package/src/index.ts +43 -3
- package/src/node.ts +4 -0
- package/src/plugins/types.ts +56 -0
- package/src/publish/contracts.ts +80 -0
- package/src/publish.ts +593 -170
- package/src/restore.ts +12 -5
- package/src/shared-types.ts +4 -8
- package/src/uam-transaction.ts +69 -1
- package/src/utils.ts +28 -0
- package/src/web.ts +11 -0
package/src/publish.ts
CHANGED
|
@@ -17,21 +17,22 @@ import {
|
|
|
17
17
|
} from '@openfairygui/core';
|
|
18
18
|
import { createTransform } from './utils.js';
|
|
19
19
|
import { atlas, type AtlasOptions } from './atlas.js';
|
|
20
|
-
import { publishCodeGeneration } from './codegen.js';
|
|
20
|
+
import { publishCodeGeneration, resolveProjectBasePath } from './codegen.js';
|
|
21
|
+
import { formatPluginError, type LoadedPlugin } from './plugins/types.js';
|
|
22
|
+
import type { AtlasRasterBackend, PublishFileSystem } from './publish/contracts.js';
|
|
21
23
|
import type {
|
|
22
24
|
CliPublishSettings,
|
|
23
25
|
HasOptionalFont,
|
|
24
26
|
PackagePublishArtifactsExtras,
|
|
25
|
-
PublishFileSystem,
|
|
26
27
|
RootProjectSettings,
|
|
27
28
|
} from './shared-types.js';
|
|
28
29
|
|
|
29
30
|
export interface PublishOptions {
|
|
30
31
|
/**
|
|
31
|
-
* Output directory for published files (.fui + atlas PNGs).
|
|
32
|
-
*
|
|
32
|
+
* Output directory override for published files (.fui + atlas PNGs).
|
|
33
|
+
* When omitted, publish uses package-level or project-level publish paths.
|
|
33
34
|
*/
|
|
34
|
-
output
|
|
35
|
+
output?: string;
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Compress the binary data with zlib raw deflate. Default: false.
|
|
@@ -45,10 +46,10 @@ export interface PublishOptions {
|
|
|
45
46
|
fileExtension?: string;
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
|
-
*
|
|
49
|
+
* Raster backend for atlas image compositing.
|
|
49
50
|
* If not provided, atlas packing only computes layout (no PNGs generated).
|
|
50
51
|
*/
|
|
51
|
-
encoder?:
|
|
52
|
+
encoder?: AtlasRasterBackend;
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* Base path for reading source images (project assets root).
|
|
@@ -78,9 +79,33 @@ export interface PublishOptions {
|
|
|
78
79
|
* Empty or omitted means publishing the main branch.
|
|
79
80
|
*/
|
|
80
81
|
branch?: string;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Publish hooks supplied by the host adapter.
|
|
85
|
+
*
|
|
86
|
+
* Node adapters load project plugins. Browser adapters pass an empty list.
|
|
87
|
+
*/
|
|
88
|
+
plugins?: LoadedPlugin[];
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Run generic code generation after runtime artifacts. Default: true.
|
|
92
|
+
*/
|
|
93
|
+
codeGeneration?: boolean;
|
|
81
94
|
}
|
|
82
95
|
|
|
83
|
-
export interface ResolvedPublishAtlasOptions
|
|
96
|
+
export interface ResolvedPublishAtlasOptions
|
|
97
|
+
extends Pick<
|
|
98
|
+
AtlasOptions,
|
|
99
|
+
| 'maxSize'
|
|
100
|
+
| 'fast'
|
|
101
|
+
| 'allowRotation'
|
|
102
|
+
| 'padding'
|
|
103
|
+
| 'powerOfTwo'
|
|
104
|
+
| 'square'
|
|
105
|
+
| 'multiPage'
|
|
106
|
+
| 'trimImage'
|
|
107
|
+
| 'extractAlpha'
|
|
108
|
+
> {}
|
|
84
109
|
|
|
85
110
|
export interface ResolvePublishOptionsOverrides {
|
|
86
111
|
compressed?: boolean;
|
|
@@ -96,6 +121,48 @@ export interface ResolvedPublishOptions {
|
|
|
96
121
|
atlas: ResolvedPublishAtlasOptions;
|
|
97
122
|
}
|
|
98
123
|
|
|
124
|
+
interface ResolvedProjectPublishConfig extends ResolvedPublishOptions {
|
|
125
|
+
projectType: number;
|
|
126
|
+
includeBranches: boolean;
|
|
127
|
+
activeBranch: string;
|
|
128
|
+
includeHighResolution: number;
|
|
129
|
+
separatedAtlasForBranch: boolean;
|
|
130
|
+
globalOutputPath: string;
|
|
131
|
+
globalBranchOutputPath: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface ResolvedPackagePublishPlan {
|
|
135
|
+
pkg: Package;
|
|
136
|
+
outputDir?: string;
|
|
137
|
+
publishName: string;
|
|
138
|
+
fileName: string;
|
|
139
|
+
compressed: boolean;
|
|
140
|
+
fileExtension: string;
|
|
141
|
+
includeBranches: boolean;
|
|
142
|
+
activeBranch: string;
|
|
143
|
+
includeHighResolution: number;
|
|
144
|
+
separatedAtlasForBranch: boolean;
|
|
145
|
+
atlas: ResolvedPublishAtlasOptions;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function runPublishPluginHook(
|
|
149
|
+
plugins: LoadedPlugin[],
|
|
150
|
+
hook: 'onPublishStart' | 'onPublishEnd',
|
|
151
|
+
doc: Document,
|
|
152
|
+
options: PublishOptions,
|
|
153
|
+
): Promise<void> {
|
|
154
|
+
const logger = doc.getLogger();
|
|
155
|
+
for (const plugin of plugins) {
|
|
156
|
+
const fn = plugin.plugin[hook];
|
|
157
|
+
if (typeof fn !== 'function') continue;
|
|
158
|
+
try {
|
|
159
|
+
await fn(doc, options);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
logger.warn(`publish: Plugin "${plugin.name}" ${hook} failed: ${formatPluginError(error)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
99
166
|
interface ImageResourceExtras extends Record<string, unknown> {
|
|
100
167
|
_fileName?: string;
|
|
101
168
|
}
|
|
@@ -112,7 +179,9 @@ interface BranchAwarePublishedResource {
|
|
|
112
179
|
interface PackagePublishContext {
|
|
113
180
|
referencedIds: Set<string>;
|
|
114
181
|
publishedResourceIds: Set<string>;
|
|
182
|
+
exportedResourceIds: Set<string>;
|
|
115
183
|
pixelHitTestImageIds: Set<string>;
|
|
184
|
+
highResolutionItemIds: Map<string, Array<string | null>>;
|
|
116
185
|
effectiveResourceIds: Map<string, string>;
|
|
117
186
|
includeBranches: boolean;
|
|
118
187
|
}
|
|
@@ -138,8 +207,10 @@ interface TransitionWithPublishRefs {
|
|
|
138
207
|
|
|
139
208
|
interface ChildWithPublishRefs extends HasOptionalFont {
|
|
140
209
|
getId?(): string;
|
|
210
|
+
getPackageId?(): string;
|
|
141
211
|
getSrc?(): string;
|
|
142
212
|
getUrl?(): string;
|
|
213
|
+
getInstanceSound?(): string;
|
|
143
214
|
getDefaultItem?(): string;
|
|
144
215
|
getIcon?(): string;
|
|
145
216
|
getSelectedIcon?(): string;
|
|
@@ -172,27 +243,6 @@ interface ComponentWithPublishRefs {
|
|
|
172
243
|
listTransitions?(): TransitionWithPublishRefs[];
|
|
173
244
|
}
|
|
174
245
|
|
|
175
|
-
interface PublishEncoderMetadata {
|
|
176
|
-
width?: number;
|
|
177
|
-
height?: number;
|
|
178
|
-
channels?: number;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
interface PublishEncoderResolvedBuffer {
|
|
182
|
-
data: Uint8Array;
|
|
183
|
-
info: Required<Pick<PublishEncoderMetadata, 'width' | 'height' | 'channels'>> & PublishEncoderMetadata;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
interface PublishEncoderPipeline {
|
|
187
|
-
ensureAlpha(): PublishEncoderPipeline;
|
|
188
|
-
resize(options: { width: number; height: number; fit: 'fill' }): PublishEncoderPipeline;
|
|
189
|
-
raw(): PublishEncoderPipeline;
|
|
190
|
-
toBuffer(options: { resolveWithObject: true }): Promise<PublishEncoderResolvedBuffer>;
|
|
191
|
-
metadata(): Promise<PublishEncoderMetadata>;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
type PublishEncoder = (input: string | Uint8Array) => PublishEncoderPipeline;
|
|
195
|
-
|
|
196
246
|
const UNITY_PROJECT_TYPE = ProjectType.Unity;
|
|
197
247
|
const COCOS_CREATOR_PROJECT_TYPE = ProjectType.CocosCreator;
|
|
198
248
|
|
|
@@ -245,8 +295,7 @@ export function resolvePublishOptions(
|
|
|
245
295
|
const atlasSetting = publishSettings.atlasSetting ?? {};
|
|
246
296
|
const projectType = root.getProjectType();
|
|
247
297
|
|
|
248
|
-
const fileExtension = overrides.fileExtension
|
|
249
|
-
?? resolveDefaultPublishFileExtension(projectType, publishSettings);
|
|
298
|
+
const fileExtension = overrides.fileExtension ?? resolveDefaultPublishFileExtension(projectType, publishSettings);
|
|
250
299
|
|
|
251
300
|
let compressed = overrides.compressed ?? publishSettings.compressDesc ?? false;
|
|
252
301
|
if (projectType === UNITY_PROJECT_TYPE) {
|
|
@@ -273,6 +322,23 @@ export function resolvePublishOptions(
|
|
|
273
322
|
};
|
|
274
323
|
}
|
|
275
324
|
|
|
325
|
+
function trimTrailingSlashes(value: string): string {
|
|
326
|
+
return value.replace(/[/\\]+$/, '');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function isAbsolutePathLike(value: string): boolean {
|
|
330
|
+
return /^(?:[a-zA-Z]:[/\\]|[/\\]{1,2})/u.test(value);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function joinPathSegments(left: string, right: string): string {
|
|
334
|
+
const normalizedLeft = trimTrailingSlashes(left);
|
|
335
|
+
const normalizedRight = right.replace(/^[/\\]+/, '');
|
|
336
|
+
if (!normalizedLeft) return normalizedRight;
|
|
337
|
+
if (!normalizedRight) return normalizedLeft;
|
|
338
|
+
const separator = normalizedLeft.includes('\\') ? '\\' : '/';
|
|
339
|
+
return `${normalizedLeft}${separator}${normalizedRight}`;
|
|
340
|
+
}
|
|
341
|
+
|
|
276
342
|
function dirname(filePath: string): string {
|
|
277
343
|
const trimmed = filePath.replace(/[/\\]+$/, '');
|
|
278
344
|
const match = trimmed.match(/^(.*)[/\\][^/\\]+$/);
|
|
@@ -311,6 +377,12 @@ function isMovieClipResource(resource: ReturnType<Package['listResources']>[numb
|
|
|
311
377
|
return resource.propertyType === 'MovieClipResource';
|
|
312
378
|
}
|
|
313
379
|
|
|
380
|
+
function isHighResolutionResource(
|
|
381
|
+
resource: ReturnType<Package['listResources']>[number],
|
|
382
|
+
): resource is ImageResource | MovieClipResource {
|
|
383
|
+
return isImageResource(resource) || isMovieClipResource(resource);
|
|
384
|
+
}
|
|
385
|
+
|
|
314
386
|
function isMiscResource(resource: ReturnType<Package['listResources']>[number]): resource is MiscResource {
|
|
315
387
|
return resource.propertyType === 'MiscResource';
|
|
316
388
|
}
|
|
@@ -327,7 +399,9 @@ function isSpineResource(resource: ReturnType<Package['listResources']>[number])
|
|
|
327
399
|
return resource.propertyType === 'SpineResource';
|
|
328
400
|
}
|
|
329
401
|
|
|
330
|
-
function isDragonBonesResource(
|
|
402
|
+
function isDragonBonesResource(
|
|
403
|
+
resource: ReturnType<Package['listResources']>[number],
|
|
404
|
+
): resource is DragonBonesResource {
|
|
331
405
|
return resource.propertyType === 'DragonBonesResource';
|
|
332
406
|
}
|
|
333
407
|
|
|
@@ -374,10 +448,7 @@ function addLocalFontRef(target: Set<string>, pkgId: string, value: string | str
|
|
|
374
448
|
addLocalUiResourceRef(target, pkgId, value ?? undefined);
|
|
375
449
|
}
|
|
376
450
|
|
|
377
|
-
function resolvePackageAssetsBasePath(
|
|
378
|
-
basePath: string,
|
|
379
|
-
resource: BranchAwarePublishedResource | undefined,
|
|
380
|
-
): string {
|
|
451
|
+
function resolvePackageAssetsBasePath(basePath: string, resource: BranchAwarePublishedResource | undefined): string {
|
|
381
452
|
const branchName = resource?.getBranch?.() ?? '';
|
|
382
453
|
if (!branchName) return basePath;
|
|
383
454
|
const normalized = basePath.replace(/[/\\]+$/, '');
|
|
@@ -423,14 +494,19 @@ function extname(fileName: string): string {
|
|
|
423
494
|
return normalized.slice(lastDot);
|
|
424
495
|
}
|
|
425
496
|
|
|
426
|
-
function resolvePublishedMiscFileName(resource: MiscResource): string {
|
|
497
|
+
function resolvePublishedMiscFileName(resource: MiscResource, projectType: number): string {
|
|
427
498
|
const file = resource.getFile();
|
|
499
|
+
if (projectType !== UNITY_PROJECT_TYPE) return file;
|
|
428
500
|
if (file.toLowerCase().endsWith('.atlas')) return `${file}.txt`;
|
|
429
501
|
return file;
|
|
430
502
|
}
|
|
431
503
|
|
|
432
|
-
function resolvePublishedSkeletonFileName(resource: SpineResource | DragonBonesResource): string {
|
|
433
|
-
if (
|
|
504
|
+
function resolvePublishedSkeletonFileName(resource: SpineResource | DragonBonesResource, projectType: number): string {
|
|
505
|
+
if (
|
|
506
|
+
projectType === UNITY_PROJECT_TYPE &&
|
|
507
|
+
isSpineResource(resource) &&
|
|
508
|
+
resource.getFile().toLowerCase().endsWith('.skel')
|
|
509
|
+
) {
|
|
434
510
|
return `${resource.getFile()}.bytes`;
|
|
435
511
|
}
|
|
436
512
|
return resource.getFile();
|
|
@@ -448,7 +524,11 @@ function setPublishedFileExtra(
|
|
|
448
524
|
}
|
|
449
525
|
|
|
450
526
|
function setPublishedIdExtra(
|
|
451
|
-
resource: {
|
|
527
|
+
resource: {
|
|
528
|
+
getId(): string;
|
|
529
|
+
getExtras(): Record<string, unknown> | undefined;
|
|
530
|
+
setExtras(value: Record<string, unknown>): unknown;
|
|
531
|
+
},
|
|
452
532
|
effectiveId: string | null,
|
|
453
533
|
): void {
|
|
454
534
|
const extras = (resource.getExtras() as PublishFileExtras | undefined) ?? {};
|
|
@@ -473,19 +553,103 @@ function getBranchName(resource: BranchAwarePublishedResource | undefined): stri
|
|
|
473
553
|
return resource?.getBranch?.() ?? '';
|
|
474
554
|
}
|
|
475
555
|
|
|
476
|
-
function buildBranchResourceKey(resource: {
|
|
477
|
-
propertyType: string;
|
|
478
|
-
getPath(): string;
|
|
479
|
-
getName(): string;
|
|
480
|
-
}): string {
|
|
556
|
+
function buildBranchResourceKey(resource: { propertyType: string; getPath(): string; getName(): string }): string {
|
|
481
557
|
return `${resource.propertyType}|${resource.getPath() ?? ''}|${resource.getName() ?? ''}`;
|
|
482
558
|
}
|
|
483
559
|
|
|
560
|
+
const HIGH_RESOLUTION_LEVELS = [
|
|
561
|
+
{ scale: 2, bit: 1, slot: 0 },
|
|
562
|
+
{ scale: 3, bit: 2, slot: 1 },
|
|
563
|
+
{ scale: 4, bit: 4, slot: 2 },
|
|
564
|
+
] as const;
|
|
565
|
+
|
|
566
|
+
function buildHighResolutionResourceKey(
|
|
567
|
+
resource: {
|
|
568
|
+
propertyType: string;
|
|
569
|
+
getPath(): string;
|
|
570
|
+
getName(): string;
|
|
571
|
+
getBranch?(): string;
|
|
572
|
+
},
|
|
573
|
+
name = resource.getName(),
|
|
574
|
+
): string {
|
|
575
|
+
return `${resource.propertyType}|${resource.getBranch?.() ?? ''}|${resource.getPath() ?? ''}|${name}`;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function isHighResolutionVariantName(name: string): boolean {
|
|
579
|
+
return /@(?:2|3|4)x(?:\.[^./\\]+)?$/iu.test(name);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function appendHighResolutionScaleToName(name: string, scale: number): string {
|
|
583
|
+
const extensionIndex = name.lastIndexOf('.');
|
|
584
|
+
if (extensionIndex > 0) {
|
|
585
|
+
return `${name.slice(0, extensionIndex)}@${scale}x${name.slice(extensionIndex)}`;
|
|
586
|
+
}
|
|
587
|
+
return `${name}@${scale}x`;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function trimTrailingMissingHighResolutionIds(ids: Array<string | null>): Array<string | null> {
|
|
591
|
+
while (ids.length > 0 && !ids[ids.length - 1]) {
|
|
592
|
+
ids.pop();
|
|
593
|
+
}
|
|
594
|
+
return ids;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function collectHighResolutionItemIds(
|
|
598
|
+
resources: ReturnType<Package['listResources']>,
|
|
599
|
+
publishedResourceIds: Set<string>,
|
|
600
|
+
includeHighResolution: number,
|
|
601
|
+
): Map<string, Array<string | null>> {
|
|
602
|
+
const result = new Map<string, Array<string | null>>();
|
|
603
|
+
if (includeHighResolution <= 0) return result;
|
|
604
|
+
|
|
605
|
+
const highResolutionResourceByKey = new Map<string, ImageResource | MovieClipResource>();
|
|
606
|
+
for (const resource of resources) {
|
|
607
|
+
if (!isHighResolutionResource(resource)) continue;
|
|
608
|
+
highResolutionResourceByKey.set(buildHighResolutionResourceKey(resource), resource);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
for (const resource of resources) {
|
|
612
|
+
if (!isHighResolutionResource(resource)) continue;
|
|
613
|
+
if (!publishedResourceIds.has(resource.getId())) continue;
|
|
614
|
+
if (isHighResolutionVariantName(resource.getName())) continue;
|
|
615
|
+
|
|
616
|
+
const ids: Array<string | null> = [];
|
|
617
|
+
for (const level of HIGH_RESOLUTION_LEVELS) {
|
|
618
|
+
if ((includeHighResolution & level.bit) === 0) {
|
|
619
|
+
ids[level.slot] = null;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const highResolutionResource = highResolutionResourceByKey.get(
|
|
624
|
+
buildHighResolutionResourceKey(
|
|
625
|
+
resource,
|
|
626
|
+
appendHighResolutionScaleToName(resource.getName(), level.scale),
|
|
627
|
+
),
|
|
628
|
+
);
|
|
629
|
+
if (!highResolutionResource) {
|
|
630
|
+
ids[level.slot] = null;
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const highResolutionId = highResolutionResource.getId();
|
|
635
|
+
publishedResourceIds.add(highResolutionId);
|
|
636
|
+
ids[level.slot] = highResolutionId;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
trimTrailingMissingHighResolutionIds(ids);
|
|
640
|
+
if (ids.length > 0) result.set(resource.getId(), ids);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
return result;
|
|
644
|
+
}
|
|
645
|
+
|
|
484
646
|
function collectPackagePublishContext(
|
|
485
647
|
pkg: Package,
|
|
486
648
|
options: {
|
|
649
|
+
projectType: number;
|
|
487
650
|
includeBranches: boolean;
|
|
488
651
|
activeBranch: string;
|
|
652
|
+
includeHighResolution: number;
|
|
489
653
|
},
|
|
490
654
|
): PackagePublishContext {
|
|
491
655
|
const pkgId = pkg.getId();
|
|
@@ -494,6 +658,27 @@ function collectPackagePublishContext(
|
|
|
494
658
|
const referencedIds = new Set<string>();
|
|
495
659
|
const pixelHitTestImageIds = new Set<string>();
|
|
496
660
|
const spriteItemIds = new Set<string>();
|
|
661
|
+
const collectExportedResourceIds = (
|
|
662
|
+
sourceResources: ReturnType<Package['listResources']>,
|
|
663
|
+
sourcePublishedResourceIds: Set<string>,
|
|
664
|
+
): Set<string> => {
|
|
665
|
+
const exportedResourceIds = new Set<string>(sourcePublishedResourceIds);
|
|
666
|
+
const resourcesById = new Map(sourceResources.map((resource) => [resource.getId(), resource] as const));
|
|
667
|
+
let changed = true;
|
|
668
|
+
while (changed) {
|
|
669
|
+
changed = false;
|
|
670
|
+
for (const resourceId of [...exportedResourceIds]) {
|
|
671
|
+
const resource = resourcesById.get(resourceId);
|
|
672
|
+
if (!resource || !isSkeletonResource(resource)) continue;
|
|
673
|
+
for (const requiredId of resource.getRequireIds()) {
|
|
674
|
+
if (!requiredId || exportedResourceIds.has(requiredId)) continue;
|
|
675
|
+
exportedResourceIds.add(requiredId);
|
|
676
|
+
changed = true;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return exportedResourceIds;
|
|
681
|
+
};
|
|
497
682
|
|
|
498
683
|
for (const atlas of pkg.listAtlases()) {
|
|
499
684
|
for (const sprite of atlas.listSprites()) {
|
|
@@ -531,6 +716,7 @@ function collectPackagePublishContext(
|
|
|
531
716
|
child.getSelectedIcon?.(),
|
|
532
717
|
child.getDropdown?.(),
|
|
533
718
|
child.getSound?.(),
|
|
719
|
+
child.getInstanceSound?.(),
|
|
534
720
|
child.getInstanceIcon?.(),
|
|
535
721
|
child.getInstanceSelectedIcon?.(),
|
|
536
722
|
child.getVtScrollBarRes?.(),
|
|
@@ -583,7 +769,12 @@ function collectPackagePublishContext(
|
|
|
583
769
|
continue;
|
|
584
770
|
}
|
|
585
771
|
if (isImageResource(resource)) {
|
|
586
|
-
if (
|
|
772
|
+
if (
|
|
773
|
+
resource.getExported() ||
|
|
774
|
+
referencedIds.has(resourceId) ||
|
|
775
|
+
spriteItemIds.has(resourceId) ||
|
|
776
|
+
pixelHitTestImageIds.has(resourceId)
|
|
777
|
+
) {
|
|
587
778
|
publishedResourceIds.add(resourceId);
|
|
588
779
|
}
|
|
589
780
|
continue;
|
|
@@ -608,20 +799,16 @@ function collectPackagePublishContext(
|
|
|
608
799
|
}
|
|
609
800
|
}
|
|
610
801
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
changed = false;
|
|
614
|
-
for (const resource of resources) {
|
|
615
|
-
if (!isSkeletonResource(resource)) continue;
|
|
616
|
-
if (!publishedResourceIds.has(resource.getId())) continue;
|
|
617
|
-
for (const requiredId of resource.getRequireIds()) {
|
|
618
|
-
if (!requiredId || publishedResourceIds.has(requiredId)) continue;
|
|
619
|
-
publishedResourceIds.add(requiredId);
|
|
620
|
-
changed = true;
|
|
621
|
-
}
|
|
622
|
-
}
|
|
802
|
+
for (const resourceId of collectExportedResourceIds(resources, publishedResourceIds)) {
|
|
803
|
+
publishedResourceIds.add(resourceId);
|
|
623
804
|
}
|
|
624
805
|
|
|
806
|
+
const highResolutionItemIds = collectHighResolutionItemIds(
|
|
807
|
+
resources,
|
|
808
|
+
publishedResourceIds,
|
|
809
|
+
options.includeHighResolution,
|
|
810
|
+
);
|
|
811
|
+
|
|
625
812
|
if (!options.includeBranches) {
|
|
626
813
|
const mainByKey = new Map<string, ReturnType<Package['listResources']>[number]>();
|
|
627
814
|
const activeBranchByKey = new Map<string, ReturnType<Package['listResources']>[number]>();
|
|
@@ -685,7 +872,9 @@ function collectPackagePublishContext(
|
|
|
685
872
|
return {
|
|
686
873
|
referencedIds,
|
|
687
874
|
publishedResourceIds,
|
|
875
|
+
exportedResourceIds: collectExportedResourceIds(resources, publishedResourceIds),
|
|
688
876
|
pixelHitTestImageIds,
|
|
877
|
+
highResolutionItemIds,
|
|
689
878
|
effectiveResourceIds,
|
|
690
879
|
includeBranches: false,
|
|
691
880
|
};
|
|
@@ -694,7 +883,9 @@ function collectPackagePublishContext(
|
|
|
694
883
|
return {
|
|
695
884
|
referencedIds,
|
|
696
885
|
publishedResourceIds,
|
|
886
|
+
exportedResourceIds: collectExportedResourceIds(resources, publishedResourceIds),
|
|
697
887
|
pixelHitTestImageIds,
|
|
888
|
+
highResolutionItemIds,
|
|
698
889
|
effectiveResourceIds: new Map([...publishedResourceIds].map((resourceId) => [resourceId, resourceId])),
|
|
699
890
|
includeBranches: true,
|
|
700
891
|
};
|
|
@@ -704,7 +895,7 @@ async function applyPixelHitTests(
|
|
|
704
895
|
pkg: Package,
|
|
705
896
|
imageIds: Set<string>,
|
|
706
897
|
basePath: string | undefined,
|
|
707
|
-
encoder:
|
|
898
|
+
encoder: AtlasRasterBackend | undefined,
|
|
708
899
|
): Promise<void> {
|
|
709
900
|
const images = pkg.listImageResources();
|
|
710
901
|
for (const image of images) {
|
|
@@ -766,31 +957,44 @@ async function applyPixelHitTests(
|
|
|
766
957
|
async function annotatePackagePublishArtifacts(
|
|
767
958
|
pkg: Package,
|
|
768
959
|
basePath: string | undefined,
|
|
769
|
-
encoder:
|
|
960
|
+
encoder: AtlasRasterBackend | undefined,
|
|
770
961
|
options: {
|
|
962
|
+
projectType: number;
|
|
771
963
|
includeBranches: boolean;
|
|
772
964
|
activeBranch: string;
|
|
965
|
+
includeHighResolution: number;
|
|
773
966
|
},
|
|
774
967
|
): Promise<void> {
|
|
775
|
-
const {
|
|
968
|
+
const {
|
|
969
|
+
publishedResourceIds,
|
|
970
|
+
exportedResourceIds,
|
|
971
|
+
pixelHitTestImageIds,
|
|
972
|
+
highResolutionItemIds,
|
|
973
|
+
effectiveResourceIds,
|
|
974
|
+
includeBranches,
|
|
975
|
+
} = collectPackagePublishContext(pkg, options);
|
|
776
976
|
for (const resource of pkg.listResources()) {
|
|
777
977
|
setPublishedIdExtra(resource, effectiveResourceIds.get(resource.getId()) ?? null);
|
|
978
|
+
if (isHighResolutionResource(resource)) {
|
|
979
|
+
resource.setHighResolutionItemIds(highResolutionItemIds.get(resource.getId()) ?? []);
|
|
980
|
+
}
|
|
778
981
|
}
|
|
779
982
|
await applyPixelHitTests(pkg, pixelHitTestImageIds, basePath, encoder);
|
|
780
983
|
const extras = (pkg.getExtras() as PackagePublishArtifactsExtras | undefined) ?? {};
|
|
781
984
|
pkg.setExtras({
|
|
782
985
|
...extras,
|
|
783
986
|
publishedResourceIds: [...publishedResourceIds].sort((a, b) => a.localeCompare(b)),
|
|
987
|
+
exportedResourceIds: [...exportedResourceIds].sort((a, b) => a.localeCompare(b)),
|
|
784
988
|
publishedIncludeBranches: includeBranches,
|
|
785
989
|
publishedEffectiveResourceIds: Object.fromEntries(effectiveResourceIds),
|
|
786
990
|
});
|
|
787
991
|
for (const resource of pkg.listResources()) {
|
|
788
992
|
if (isMiscResource(resource)) {
|
|
789
|
-
setPublishedFileExtra(resource, resolvePublishedMiscFileName(resource));
|
|
993
|
+
setPublishedFileExtra(resource, resolvePublishedMiscFileName(resource, options.projectType));
|
|
790
994
|
continue;
|
|
791
995
|
}
|
|
792
996
|
if (isSkeletonResource(resource)) {
|
|
793
|
-
setPublishedFileExtra(resource, resolvePublishedSkeletonFileName(resource));
|
|
997
|
+
setPublishedFileExtra(resource, resolvePublishedSkeletonFileName(resource, options.projectType));
|
|
794
998
|
}
|
|
795
999
|
}
|
|
796
1000
|
}
|
|
@@ -800,10 +1004,12 @@ function getAnnotatedPublishedResourceIds(pkg: Package): Set<string> {
|
|
|
800
1004
|
return new Set(extras.publishedResourceIds ?? []);
|
|
801
1005
|
}
|
|
802
1006
|
|
|
803
|
-
function
|
|
804
|
-
pkg
|
|
805
|
-
|
|
806
|
-
|
|
1007
|
+
function getAnnotatedExportedResourceIds(pkg: Package): Set<string> {
|
|
1008
|
+
const extras = (pkg.getExtras() as PackagePublishArtifactsExtras | undefined) ?? {};
|
|
1009
|
+
return new Set(extras.exportedResourceIds ?? []);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
function getPublishedSkeletonDependencyImageIds(pkg: Package, publishedResourceIds: Set<string>): Set<string> {
|
|
807
1013
|
const imageIds = new Set<string>();
|
|
808
1014
|
const resourcesById = new Map(pkg.listResources().map((resource) => [resource.getId(), resource] as const));
|
|
809
1015
|
for (const resource of pkg.listResources()) {
|
|
@@ -833,7 +1039,9 @@ async function exportPackageSounds(
|
|
|
833
1039
|
return isSoundResource(resource) && publishedResourceIds.has(resource.getId());
|
|
834
1040
|
});
|
|
835
1041
|
if (hasPublishedSound) {
|
|
836
|
-
logger.warn(
|
|
1042
|
+
logger.warn(
|
|
1043
|
+
`publish: Sound resources in package "${pkg.getName()}" were not exported because basePath/readFileRaw is unavailable.`,
|
|
1044
|
+
);
|
|
837
1045
|
}
|
|
838
1046
|
return;
|
|
839
1047
|
}
|
|
@@ -863,25 +1071,29 @@ async function exportPackageExternalResources(
|
|
|
863
1071
|
readFileRaw: PublishFileSystem['readFileRaw'] | undefined,
|
|
864
1072
|
logger: Document['getLogger'] extends () => infer T ? T : never,
|
|
865
1073
|
): Promise<void> {
|
|
866
|
-
const
|
|
867
|
-
const skeletonDependencyImageIds = getPublishedSkeletonDependencyImageIds(pkg,
|
|
868
|
-
if (
|
|
1074
|
+
const exportedResourceIds = getAnnotatedExportedResourceIds(pkg);
|
|
1075
|
+
const skeletonDependencyImageIds = getPublishedSkeletonDependencyImageIds(pkg, exportedResourceIds);
|
|
1076
|
+
if (exportedResourceIds.size === 0) return;
|
|
869
1077
|
if (!basePath || !readFileRaw) {
|
|
870
1078
|
const hasPublishedExternal = pkg.listResources().some((resource) => {
|
|
871
1079
|
return (
|
|
872
|
-
(isMiscResource(resource) || isSkeletonResource(resource))
|
|
873
|
-
|
|
874
|
-
|
|
1080
|
+
((isMiscResource(resource) || isSkeletonResource(resource)) &&
|
|
1081
|
+
exportedResourceIds.has(resource.getId())) ||
|
|
1082
|
+
skeletonDependencyImageIds.has(resource.getId())
|
|
1083
|
+
);
|
|
875
1084
|
});
|
|
876
1085
|
if (hasPublishedExternal) {
|
|
877
|
-
logger.warn(
|
|
1086
|
+
logger.warn(
|
|
1087
|
+
`publish: External resources in package "${pkg.getName()}" were not exported because basePath/readFileRaw is unavailable.`,
|
|
1088
|
+
);
|
|
878
1089
|
}
|
|
879
1090
|
return;
|
|
880
1091
|
}
|
|
881
1092
|
|
|
882
1093
|
for (const resource of pkg.listResources()) {
|
|
883
1094
|
const resourceId = resource.getId();
|
|
884
|
-
const isSkeletonExternal =
|
|
1095
|
+
const isSkeletonExternal =
|
|
1096
|
+
exportedResourceIds.has(resourceId) && (isMiscResource(resource) || isSkeletonResource(resource));
|
|
885
1097
|
const isSkeletonImageDependency = skeletonDependencyImageIds.has(resourceId) && isImageResource(resource);
|
|
886
1098
|
if (!isSkeletonExternal && !isSkeletonImageDependency) continue;
|
|
887
1099
|
|
|
@@ -892,7 +1104,8 @@ async function exportPackageExternalResources(
|
|
|
892
1104
|
targetName = resolveImageFileName(resource);
|
|
893
1105
|
} else if (isMiscResource(resource) || isSkeletonResource(resource)) {
|
|
894
1106
|
sourcePath = resolveGenericResourcePath(resource, pkg, basePath);
|
|
895
|
-
targetName =
|
|
1107
|
+
targetName =
|
|
1108
|
+
((resource.getExtras() as PublishFileExtras | undefined) ?? {})._publishedFile ?? resource.getFile();
|
|
896
1109
|
} else {
|
|
897
1110
|
continue;
|
|
898
1111
|
}
|
|
@@ -902,7 +1115,9 @@ async function exportPackageExternalResources(
|
|
|
902
1115
|
const data = await readFileRaw(sourcePath);
|
|
903
1116
|
await fs.writeFileRaw(targetPath, data);
|
|
904
1117
|
} catch {
|
|
905
|
-
logger.warn(
|
|
1118
|
+
logger.warn(
|
|
1119
|
+
`publish: Could not export external resource "${resource.getId()}" from package "${pkg.getName()}".`,
|
|
1120
|
+
);
|
|
906
1121
|
}
|
|
907
1122
|
}
|
|
908
1123
|
}
|
|
@@ -911,10 +1126,13 @@ async function exportPackageExternalResources(
|
|
|
911
1126
|
* Publishes a FairyGUI project.
|
|
912
1127
|
*
|
|
913
1128
|
* Orchestrates:
|
|
914
|
-
* 1. Atlas packing (MaxRects layout + optional
|
|
1129
|
+
* 1. Atlas packing (MaxRects layout + optional raster compositing)
|
|
915
1130
|
* 2. Per-package .fui binary serialization
|
|
916
1131
|
* 3. File writing to the output directory
|
|
917
1132
|
*
|
|
1133
|
+
* This is the capability-injected core. Standard hosts should use
|
|
1134
|
+
* `publishNode()` or `publishBrowser()` through their dedicated entries.
|
|
1135
|
+
*
|
|
918
1136
|
* ```ts
|
|
919
1137
|
* import sharp from 'sharp';
|
|
920
1138
|
* const io = new NodeIO();
|
|
@@ -932,17 +1150,152 @@ async function exportPackageExternalResources(
|
|
|
932
1150
|
*/
|
|
933
1151
|
export function publish(options: PublishOptions): Transform {
|
|
934
1152
|
return createTransform('publish', async (doc: Document): Promise<void> => {
|
|
1153
|
+
const resolveConfiguredOutputPath = (value?: string, projectBasePath?: string): string | undefined => {
|
|
1154
|
+
const trimmed = value?.trim();
|
|
1155
|
+
if (!trimmed) return undefined;
|
|
1156
|
+
if (isAbsolutePathLike(trimmed) || !projectBasePath) {
|
|
1157
|
+
return trimTrailingSlashes(trimmed);
|
|
1158
|
+
}
|
|
1159
|
+
return trimTrailingSlashes(
|
|
1160
|
+
options.fs ? options.fs.join(projectBasePath, trimmed) : joinPathSegments(projectBasePath, trimmed),
|
|
1161
|
+
);
|
|
1162
|
+
};
|
|
1163
|
+
|
|
1164
|
+
const resolveProjectPublishConfig = (): ResolvedProjectPublishConfig => {
|
|
1165
|
+
const settings = (doc.getRoot().getSettings?.() ?? {}) as RootProjectSettings;
|
|
1166
|
+
const publishSettings: CliPublishSettings = settings.publish ?? {};
|
|
1167
|
+
const resolved = resolvePublishOptions(doc, {
|
|
1168
|
+
compressed: options.compressed,
|
|
1169
|
+
fileExtension: options.fileExtension,
|
|
1170
|
+
packages: options.packages,
|
|
1171
|
+
atlas: options.atlas,
|
|
1172
|
+
});
|
|
1173
|
+
const branchProcessing = publishSettings.branchProcessing ?? 0;
|
|
1174
|
+
const includeBranches = branchProcessing === 0;
|
|
1175
|
+
|
|
1176
|
+
return {
|
|
1177
|
+
...resolved,
|
|
1178
|
+
projectType: doc.getRoot().getProjectType(),
|
|
1179
|
+
includeBranches,
|
|
1180
|
+
activeBranch: includeBranches ? '' : (options.branch ?? ''),
|
|
1181
|
+
includeHighResolution: publishSettings.includeHighResolution ?? 0,
|
|
1182
|
+
separatedAtlasForBranch: includeBranches && publishSettings.seperatedAtlasForBranch === true,
|
|
1183
|
+
globalOutputPath: publishSettings.path?.trim() ?? '',
|
|
1184
|
+
globalBranchOutputPath: publishSettings.branchPath?.trim() ?? '',
|
|
1185
|
+
};
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
const resolvePackagePublishPlan = (
|
|
1189
|
+
pkg: Package,
|
|
1190
|
+
config: ResolvedProjectPublishConfig,
|
|
1191
|
+
projectBasePath?: string,
|
|
1192
|
+
): ResolvedPackagePublishPlan => {
|
|
1193
|
+
let outputDir: string | undefined;
|
|
1194
|
+
|
|
1195
|
+
if (options.output) {
|
|
1196
|
+
outputDir = trimTrailingSlashes(options.output);
|
|
1197
|
+
} else {
|
|
1198
|
+
const candidates: Array<string | undefined> = [];
|
|
1199
|
+
if (!config.includeBranches && config.activeBranch) {
|
|
1200
|
+
candidates.push(pkg.getPublishBranchPath(), config.globalBranchOutputPath);
|
|
1201
|
+
}
|
|
1202
|
+
candidates.push(pkg.getPublishPath(), config.globalOutputPath);
|
|
1203
|
+
|
|
1204
|
+
for (const candidate of candidates) {
|
|
1205
|
+
const resolved = resolveConfiguredOutputPath(candidate, projectBasePath);
|
|
1206
|
+
if (!resolved) continue;
|
|
1207
|
+
outputDir = resolved;
|
|
1208
|
+
break;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
const publishName = pkg.getPublishName() || pkg.getName();
|
|
1212
|
+
|
|
1213
|
+
return {
|
|
1214
|
+
pkg,
|
|
1215
|
+
outputDir,
|
|
1216
|
+
publishName,
|
|
1217
|
+
fileName: resolvePublishFileName(publishName, config.fileExtension),
|
|
1218
|
+
compressed: config.compressed,
|
|
1219
|
+
fileExtension: config.fileExtension,
|
|
1220
|
+
includeBranches: config.includeBranches,
|
|
1221
|
+
activeBranch: config.activeBranch,
|
|
1222
|
+
includeHighResolution: config.includeHighResolution,
|
|
1223
|
+
separatedAtlasForBranch: config.separatedAtlasForBranch,
|
|
1224
|
+
atlas: config.atlas,
|
|
1225
|
+
};
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
const createNoopPublishFs = (): PublishFileSystem => ({
|
|
1229
|
+
async writeFileRaw(): Promise<void> {
|
|
1230
|
+
// No-op for layout-only publish flows.
|
|
1231
|
+
},
|
|
1232
|
+
async mkdir(): Promise<void> {
|
|
1233
|
+
// No-op for layout-only publish flows.
|
|
1234
|
+
},
|
|
1235
|
+
join(...paths: string[]): string {
|
|
1236
|
+
return paths.join('/');
|
|
1237
|
+
},
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
const publishPackage = async (plan: ResolvedPackagePublishPlan, writerFs: FileSystem, packageIndex: number) => {
|
|
1241
|
+
const atlasRuntimeOptions = resolvePublishAtlasRuntimeOptions(plan.fileExtension);
|
|
1242
|
+
await atlas({
|
|
1243
|
+
...plan.atlas,
|
|
1244
|
+
...(options.atlas ?? {}),
|
|
1245
|
+
separatedAtlasForBranch: plan.separatedAtlasForBranch,
|
|
1246
|
+
encoder: options.encoder,
|
|
1247
|
+
basePath: options.basePath,
|
|
1248
|
+
outputPath: options.fs ? plan.outputDir : undefined,
|
|
1249
|
+
mkdir: options.fs ? options.fs.mkdir : undefined,
|
|
1250
|
+
readFileRaw: options.atlas?.readFileRaw ?? options.fs?.readFileRaw,
|
|
1251
|
+
packages: [plan.pkg.getName()],
|
|
1252
|
+
...atlasRuntimeOptions,
|
|
1253
|
+
})(doc);
|
|
1254
|
+
|
|
1255
|
+
if (!options.fs) return;
|
|
1256
|
+
if (!plan.outputDir) {
|
|
1257
|
+
throw new Error(
|
|
1258
|
+
'publish: no output directory resolved. Provide --output, or configure global publish.path / package publishPath.',
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
await options.fs.mkdir(plan.outputDir);
|
|
1263
|
+
|
|
1264
|
+
const filePath = options.fs.join(plan.outputDir, plan.fileName);
|
|
1265
|
+
const bwOptions: BinaryWriterOptions = {
|
|
1266
|
+
compressed: plan.compressed,
|
|
1267
|
+
packageIndex,
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
const bw = new BinaryWriter(writerFs);
|
|
1271
|
+
await bw.write(doc, filePath, bwOptions);
|
|
1272
|
+
await exportPackageSounds(
|
|
1273
|
+
plan.pkg,
|
|
1274
|
+
plan.outputDir,
|
|
1275
|
+
options.basePath,
|
|
1276
|
+
options.fs,
|
|
1277
|
+
options.atlas?.readFileRaw ?? options.fs.readFileRaw,
|
|
1278
|
+
logger,
|
|
1279
|
+
);
|
|
1280
|
+
await exportPackageExternalResources(
|
|
1281
|
+
plan.pkg,
|
|
1282
|
+
plan.outputDir,
|
|
1283
|
+
options.basePath,
|
|
1284
|
+
options.fs,
|
|
1285
|
+
options.atlas?.readFileRaw ?? options.fs.readFileRaw,
|
|
1286
|
+
logger,
|
|
1287
|
+
);
|
|
1288
|
+
|
|
1289
|
+
logger.info(`publish: Written ${plan.fileName}`);
|
|
1290
|
+
};
|
|
1291
|
+
|
|
935
1292
|
const root = doc.getRoot();
|
|
936
1293
|
const logger = doc.getLogger();
|
|
937
|
-
const
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
packages: options.packages,
|
|
943
|
-
atlas: options.atlas,
|
|
944
|
-
});
|
|
945
|
-
const ext = resolved.fileExtension;
|
|
1294
|
+
const projectBasePath = resolveProjectBasePath(options.basePath) || doc.getProjectDir?.() || '';
|
|
1295
|
+
const plugins = options.plugins ?? [];
|
|
1296
|
+
await runPublishPluginHook(plugins, 'onPublishStart', doc, options);
|
|
1297
|
+
|
|
1298
|
+
const resolved = resolveProjectPublishConfig();
|
|
946
1299
|
|
|
947
1300
|
// Step 1: Determine which packages to publish
|
|
948
1301
|
let allPackages = root.listPackages();
|
|
@@ -953,14 +1306,10 @@ export function publish(options: PublishOptions): Transform {
|
|
|
953
1306
|
|
|
954
1307
|
if (allPackages.length === 0) {
|
|
955
1308
|
logger.warn('publish: No packages to publish.');
|
|
1309
|
+
await runPublishPluginHook(plugins, 'onPublishEnd', doc, options);
|
|
956
1310
|
return;
|
|
957
1311
|
}
|
|
958
1312
|
|
|
959
|
-
const branchProcessing = publishSettings.branchProcessing ?? 0;
|
|
960
|
-
const includeBranches = branchProcessing === 0;
|
|
961
|
-
const activeBranch = includeBranches ? '' : (options.branch ?? '');
|
|
962
|
-
const atlasRuntimeOptions = resolvePublishAtlasRuntimeOptions(ext);
|
|
963
|
-
|
|
964
1313
|
const allDocPackages = root.listPackages();
|
|
965
1314
|
// Build a pkgId→name map for dependency resolution
|
|
966
1315
|
const pkgMap = new Map<string, Package>();
|
|
@@ -971,82 +1320,62 @@ export function publish(options: PublishOptions): Transform {
|
|
|
971
1320
|
for (const pkg of allPackages) {
|
|
972
1321
|
// Compute dependency list and selected publish artifacts before atlas packing,
|
|
973
1322
|
// so merged-branch publishes can pack the overridden resources with main IDs.
|
|
974
|
-
_computeDependencies(pkg, pkgMap);
|
|
975
|
-
await annotatePackagePublishArtifacts(
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
activeBranch,
|
|
982
|
-
},
|
|
983
|
-
);
|
|
1323
|
+
_computeDependencies(doc, pkg, pkgMap);
|
|
1324
|
+
await annotatePackagePublishArtifacts(pkg, options.basePath, options.encoder, {
|
|
1325
|
+
projectType: resolved.projectType,
|
|
1326
|
+
includeBranches: resolved.includeBranches,
|
|
1327
|
+
activeBranch: resolved.activeBranch,
|
|
1328
|
+
includeHighResolution: resolved.includeHighResolution,
|
|
1329
|
+
});
|
|
984
1330
|
}
|
|
985
1331
|
|
|
986
|
-
|
|
987
|
-
const atlasOpts: AtlasOptions = {
|
|
988
|
-
...resolved.atlas,
|
|
989
|
-
...(options.atlas ?? {}),
|
|
990
|
-
separatedAtlasForBranch: includeBranches && publishSettings.seperatedAtlasForBranch === true,
|
|
991
|
-
encoder: options.encoder,
|
|
992
|
-
basePath: options.basePath,
|
|
993
|
-
outputPath: options.fs ? options.output : undefined,
|
|
994
|
-
mkdir: options.fs ? options.fs.mkdir : undefined,
|
|
995
|
-
readFileRaw: options.atlas?.readFileRaw ?? options.fs?.readFileRaw,
|
|
996
|
-
...atlasRuntimeOptions,
|
|
997
|
-
};
|
|
998
|
-
await atlas(atlasOpts)(doc);
|
|
1332
|
+
const plans = allPackages.map((pkg) => resolvePackagePublishPlan(pkg, resolved, projectBasePath));
|
|
999
1333
|
|
|
1000
|
-
// Step 3: Write .fui binary per package
|
|
1001
1334
|
if (!options.fs) {
|
|
1002
|
-
logger.info(
|
|
1335
|
+
logger.info(
|
|
1336
|
+
`publish: No fs provided — layout computed for ${allPackages.length} package(s), skipping file output.`,
|
|
1337
|
+
);
|
|
1338
|
+
const noopWriterFs = toBinaryWriterFileSystem(createNoopPublishFs());
|
|
1339
|
+
for (const plan of plans) {
|
|
1340
|
+
await publishPackage(plan, noopWriterFs, allDocPackages.indexOf(plan.pkg));
|
|
1341
|
+
}
|
|
1342
|
+
await runPublishPluginHook(plugins, 'onPublishEnd', doc, options);
|
|
1003
1343
|
return;
|
|
1004
1344
|
}
|
|
1005
1345
|
|
|
1006
|
-
|
|
1346
|
+
const unresolvedPlan = plans.find((plan) => !plan.outputDir);
|
|
1347
|
+
if (unresolvedPlan) {
|
|
1348
|
+
throw new Error(
|
|
1349
|
+
`publish: no output directory resolved for package "${unresolvedPlan.pkg.getName()}". ` +
|
|
1350
|
+
'Provide --output, or configure global publish.path / package publishPath.',
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1007
1353
|
|
|
1008
1354
|
const writerFs = toBinaryWriterFileSystem(options.fs);
|
|
1009
1355
|
|
|
1010
|
-
for (const
|
|
1011
|
-
const pkgIndex = allDocPackages.indexOf(pkg);
|
|
1012
|
-
|
|
1013
|
-
const fileName = resolvePublishFileName(publishName, ext);
|
|
1014
|
-
const filePath = options.fs.join(options.output, fileName);
|
|
1015
|
-
|
|
1016
|
-
const bwOptions: BinaryWriterOptions = {
|
|
1017
|
-
compressed: resolved.compressed,
|
|
1018
|
-
packageIndex: pkgIndex,
|
|
1019
|
-
};
|
|
1020
|
-
|
|
1021
|
-
const bw = new BinaryWriter(writerFs);
|
|
1022
|
-
await bw.write(doc, filePath, bwOptions);
|
|
1023
|
-
await exportPackageSounds(
|
|
1024
|
-
pkg,
|
|
1025
|
-
options.output,
|
|
1026
|
-
options.basePath,
|
|
1027
|
-
options.fs,
|
|
1028
|
-
options.atlas?.readFileRaw ?? options.fs.readFileRaw,
|
|
1029
|
-
logger,
|
|
1030
|
-
);
|
|
1031
|
-
await exportPackageExternalResources(
|
|
1032
|
-
pkg,
|
|
1033
|
-
options.output,
|
|
1034
|
-
options.basePath,
|
|
1035
|
-
options.fs,
|
|
1036
|
-
options.atlas?.readFileRaw ?? options.fs.readFileRaw,
|
|
1037
|
-
logger,
|
|
1038
|
-
);
|
|
1039
|
-
|
|
1040
|
-
logger.info(`publish: Written ${fileName}`);
|
|
1356
|
+
for (const plan of plans) {
|
|
1357
|
+
const pkgIndex = allDocPackages.indexOf(plan.pkg);
|
|
1358
|
+
await publishPackage(plan, writerFs, pkgIndex);
|
|
1041
1359
|
}
|
|
1042
1360
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1361
|
+
if (options.codeGeneration !== false) {
|
|
1362
|
+
await publishCodeGeneration(doc, {
|
|
1363
|
+
basePath: options.basePath,
|
|
1364
|
+
fs: options.fs,
|
|
1365
|
+
packages: allPackages,
|
|
1366
|
+
plugins,
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1048
1369
|
|
|
1049
|
-
|
|
1370
|
+
const publishedTargets = [
|
|
1371
|
+
...new Set(plans.map((plan) => plan.outputDir).filter((value): value is string => Boolean(value))),
|
|
1372
|
+
];
|
|
1373
|
+
logger.info(
|
|
1374
|
+
publishedTargets.length > 0
|
|
1375
|
+
? `publish: Published ${allPackages.length} package(s) to ${publishedTargets.join(', ')}`
|
|
1376
|
+
: `publish: Published ${allPackages.length} package(s)`,
|
|
1377
|
+
);
|
|
1378
|
+
await runPublishPluginHook(plugins, 'onPublishEnd', doc, options);
|
|
1050
1379
|
});
|
|
1051
1380
|
}
|
|
1052
1381
|
|
|
@@ -1055,25 +1384,114 @@ export function publish(options: PublishOptions): Transform {
|
|
|
1055
1384
|
* The editor only adds dependencies for packages referenced via bitmap font URLs.
|
|
1056
1385
|
* @internal
|
|
1057
1386
|
*/
|
|
1058
|
-
function _computeDependencies(pkg: Package, pkgMap: Map<string, Package>): void {
|
|
1387
|
+
function _computeDependencies(doc: Document, pkg: Package, pkgMap: Map<string, Package>): void {
|
|
1059
1388
|
const referencedPkgIds = new Set<string>();
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1389
|
+
const pkgId = pkg.getId();
|
|
1390
|
+
const packageOrder = new Map(
|
|
1391
|
+
doc
|
|
1392
|
+
.getRoot()
|
|
1393
|
+
.listPackages()
|
|
1394
|
+
.map((entry, index) => [entry.getId(), index] as const),
|
|
1395
|
+
);
|
|
1396
|
+
const addDependencyPackageId = (dependencyPkgId: string | null | undefined): void => {
|
|
1397
|
+
const normalized = dependencyPkgId?.trim() ?? '';
|
|
1398
|
+
if (!normalized || normalized === pkgId) return;
|
|
1399
|
+
referencedPkgIds.add(normalized);
|
|
1400
|
+
};
|
|
1401
|
+
const extractPackageIdFromUiUrl = (value: string): string | null => {
|
|
1402
|
+
if (!value.startsWith('ui://')) return null;
|
|
1403
|
+
const rest = value.slice(5);
|
|
1404
|
+
if (!rest) return null;
|
|
1405
|
+
const slashIndex = rest.indexOf('/');
|
|
1406
|
+
if (slashIndex >= 0) {
|
|
1407
|
+
return rest.slice(0, slashIndex) || null;
|
|
1408
|
+
}
|
|
1066
1409
|
if (rest.length >= 8) {
|
|
1067
|
-
|
|
1068
|
-
if (depPkgId !== pkg.getId()) referencedPkgIds.add(depPkgId);
|
|
1410
|
+
return rest.slice(0, 8);
|
|
1069
1411
|
}
|
|
1070
|
-
|
|
1412
|
+
return null;
|
|
1413
|
+
};
|
|
1414
|
+
const addDependencyPackageIdFromUiValue = (value: string | null | undefined): void => {
|
|
1415
|
+
if (!value || typeof value !== 'string') return;
|
|
1416
|
+
addDependencyPackageId(extractPackageIdFromUiUrl(value));
|
|
1417
|
+
};
|
|
1418
|
+
const addDependencyPackageIdsFromText = (value: string | null | undefined): void => {
|
|
1419
|
+
if (!value || typeof value !== 'string') return;
|
|
1420
|
+
const matches = value.matchAll(/ui:\/\/([0-9a-z]{8})/giu);
|
|
1421
|
+
for (const match of matches) {
|
|
1422
|
+
addDependencyPackageId(match[1] ?? '');
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
const addDependencyPackageIdsFromUnknown = (value: unknown): void => {
|
|
1426
|
+
if (Array.isArray(value)) {
|
|
1427
|
+
for (const entry of value) addDependencyPackageIdsFromUnknown(entry);
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
if (typeof value === 'string') {
|
|
1431
|
+
addDependencyPackageIdFromUiValue(value);
|
|
1432
|
+
addDependencyPackageIdsFromText(value);
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
const addDependencyFontRef = (value: string | string[] | null | undefined): void => {
|
|
1436
|
+
if (Array.isArray(value)) {
|
|
1437
|
+
for (const entry of value) addDependencyPackageIdFromUiValue(entry);
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
addDependencyPackageIdFromUiValue(value ?? undefined);
|
|
1441
|
+
};
|
|
1071
1442
|
|
|
1072
1443
|
for (const res of pkg.listResources()) {
|
|
1073
1444
|
if (res.propertyType !== 'Component') continue;
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1445
|
+
const component = res as ComponentWithPublishRefs;
|
|
1446
|
+
for (const child of component.listChildren?.() ?? []) {
|
|
1447
|
+
addDependencyPackageId(child.getPackageId?.());
|
|
1448
|
+
addDependencyFontRef(child.getFont?.());
|
|
1449
|
+
addDependencyPackageIdsFromText(child.getText?.());
|
|
1450
|
+
for (const ref of [
|
|
1451
|
+
child.getUrl?.(),
|
|
1452
|
+
child.getDefaultItem?.(),
|
|
1453
|
+
child.getIcon?.(),
|
|
1454
|
+
child.getSelectedIcon?.(),
|
|
1455
|
+
child.getDropdown?.(),
|
|
1456
|
+
child.getSound?.(),
|
|
1457
|
+
child.getInstanceSound?.(),
|
|
1458
|
+
child.getInstanceIcon?.(),
|
|
1459
|
+
child.getInstanceSelectedIcon?.(),
|
|
1460
|
+
child.getVtScrollBarRes?.(),
|
|
1461
|
+
child.getHzScrollBarRes?.(),
|
|
1462
|
+
child.getHeaderRes?.(),
|
|
1463
|
+
child.getFooterRes?.(),
|
|
1464
|
+
]) {
|
|
1465
|
+
addDependencyPackageIdFromUiValue(ref);
|
|
1466
|
+
}
|
|
1467
|
+
for (const item of child.getInstanceComboItems?.() ?? []) {
|
|
1468
|
+
addDependencyPackageIdFromUiValue(item.icon ?? undefined);
|
|
1469
|
+
}
|
|
1470
|
+
for (const item of child.getListItems?.() ?? []) {
|
|
1471
|
+
addDependencyPackageIdFromUiValue(item.icon ?? undefined);
|
|
1472
|
+
addDependencyPackageIdFromUiValue(item.url ?? undefined);
|
|
1473
|
+
}
|
|
1474
|
+
for (const gear of child.listGears?.() ?? []) {
|
|
1475
|
+
addDependencyPackageIdsFromUnknown(gear.getValues?.());
|
|
1476
|
+
addDependencyPackageIdsFromUnknown(gear.getDefaultValue?.());
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
addDependencyFontRef(component.getFont?.());
|
|
1480
|
+
for (const ref of [
|
|
1481
|
+
component.getDropdown?.(),
|
|
1482
|
+
component.getHeaderRes?.(),
|
|
1483
|
+
component.getFooterRes?.(),
|
|
1484
|
+
component.getVtScrollBarRes?.(),
|
|
1485
|
+
component.getHzScrollBarRes?.(),
|
|
1486
|
+
component.getSound?.(),
|
|
1487
|
+
]) {
|
|
1488
|
+
addDependencyPackageIdFromUiValue(ref);
|
|
1489
|
+
}
|
|
1490
|
+
for (const transition of component.listTransitions?.() ?? []) {
|
|
1491
|
+
for (const item of transition.listItems?.() ?? []) {
|
|
1492
|
+
addDependencyPackageIdsFromUnknown(item.getStartValue?.());
|
|
1493
|
+
addDependencyPackageIdsFromUnknown(item.getEndValue?.());
|
|
1494
|
+
}
|
|
1077
1495
|
}
|
|
1078
1496
|
}
|
|
1079
1497
|
|
|
@@ -1082,7 +1500,12 @@ function _computeDependencies(pkg: Package, pkgMap: Map<string, Package>): void
|
|
|
1082
1500
|
}
|
|
1083
1501
|
|
|
1084
1502
|
if (referencedPkgIds.size > 0) {
|
|
1085
|
-
const sortedIds = [...referencedPkgIds].sort((a, b) =>
|
|
1503
|
+
const sortedIds = [...referencedPkgIds].sort((a, b) => {
|
|
1504
|
+
const orderA = packageOrder.get(a) ?? Number.MAX_SAFE_INTEGER;
|
|
1505
|
+
const orderB = packageOrder.get(b) ?? Number.MAX_SAFE_INTEGER;
|
|
1506
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
1507
|
+
return a.localeCompare(b);
|
|
1508
|
+
});
|
|
1086
1509
|
for (const refId of sortedIds) {
|
|
1087
1510
|
const depPkg = pkgMap.get(refId);
|
|
1088
1511
|
if (depPkg) {
|