@openfairygui/functions 0.2.0-alpha.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -6
- package/dist/atlas-C6tbl7nn.d.ts +193 -0
- package/dist/atlas-CHsu2Y8i.d.cts +193 -0
- package/dist/index.cjs +17 -3603
- package/dist/index.d.cts +5 -294
- package/dist/index.d.ts +5 -294
- package/dist/index.js +4 -3595
- package/dist/node.cjs +256 -0
- package/dist/node.d.cts +36 -0
- package/dist/node.d.ts +36 -0
- package/dist/node.js +254 -0
- package/dist/publish-CykUJfVa.cjs +3269 -0
- package/dist/publish-DXoaC1Nl.js +3174 -0
- package/dist/restore-BQp01WY3.js +914 -0
- package/dist/restore-BeWaJNjR.d.cts +288 -0
- package/dist/restore-CEywQUHz.cjs +919 -0
- package/dist/restore-Dh0-Nvms.d.ts +288 -0
- package/dist/uam-transaction.cjs +29 -14
- package/dist/uam-transaction.d.cts +2 -1
- package/dist/uam-transaction.d.ts +2 -1
- package/dist/uam-transaction.js +30 -16
- package/dist/web.cjs +440 -0
- package/dist/web.d.cts +44 -0
- package/dist/web.d.ts +44 -0
- package/dist/web.js +439 -0
- package/package.json +29 -4
- package/src/adapters/node/plugins.ts +82 -0
- package/src/adapters/node/publish.ts +130 -0
- package/src/adapters/node/restore.ts +187 -0
- package/src/adapters/web/publish.ts +196 -0
- package/src/adapters/web/raster.ts +421 -0
- package/src/atlas/font.ts +95 -0
- package/src/atlas/inputs.ts +445 -0
- package/src/atlas/jta.ts +157 -0
- package/src/atlas/packing.ts +762 -0
- package/src/atlas.ts +129 -1221
- package/src/codegen.ts +108 -82
- package/src/index.ts +43 -3
- package/src/node.ts +8 -0
- package/src/path-utils.ts +40 -0
- package/src/plugins/types.ts +56 -0
- package/src/publish/contracts.ts +80 -0
- package/src/publish/external-resources.ts +117 -0
- package/src/publish/options.ts +180 -0
- package/src/publish/package-context.ts +608 -0
- package/src/publish/resource-references.ts +210 -0
- package/src/publish.ts +327 -975
- package/src/restore-internals/font.ts +100 -0
- package/src/restore-internals/movie-clip.ts +104 -0
- package/src/restore-internals/output-transaction.ts +124 -0
- package/src/restore.ts +122 -311
- package/src/shared-types.ts +4 -8
- package/src/uam-transaction.ts +34 -17
- package/src/utils.ts +28 -0
- package/src/web.ts +11 -0
package/src/publish.ts
CHANGED
|
@@ -1,282 +1,91 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type BinaryWriterOptions,
|
|
3
2
|
BinaryWriter,
|
|
4
|
-
type
|
|
5
|
-
type DragonBonesResource,
|
|
3
|
+
type BinaryWriterOptions,
|
|
6
4
|
type Document,
|
|
7
5
|
type FileSystem,
|
|
8
|
-
type FontResource,
|
|
9
|
-
type ImageResource,
|
|
10
|
-
type MiscResource,
|
|
11
6
|
type MovieClipResource,
|
|
12
7
|
type Package,
|
|
13
|
-
ProjectType,
|
|
14
|
-
type SpineResource,
|
|
15
|
-
type SoundResource,
|
|
16
8
|
type Transform,
|
|
17
9
|
} from '@openfairygui/core';
|
|
10
|
+
import { atlas } from './atlas.js';
|
|
11
|
+
import { prepareMovieClipResource } from './atlas/inputs.js';
|
|
12
|
+
import type { PreparedJtaData } from './atlas/jta.js';
|
|
13
|
+
import { publishCodeGeneration, resolveProjectBasePath } from './codegen.js';
|
|
14
|
+
import { dirname, isAbsolutePathLike, trimTrailingSlashes } from './path-utils.js';
|
|
15
|
+
import { formatPluginError, type LoadedPlugin } from './plugins/types.js';
|
|
16
|
+
import type { PublishFileSystem } from './publish/contracts.js';
|
|
17
|
+
import {
|
|
18
|
+
annotatePackagePublishArtifacts,
|
|
19
|
+
getAnnotatedPublishedResourceIds,
|
|
20
|
+
isMovieClipResource,
|
|
21
|
+
} from './publish/package-context.js';
|
|
22
|
+
import {
|
|
23
|
+
exportPackageExternalResources,
|
|
24
|
+
exportPackageSounds,
|
|
25
|
+
} from './publish/external-resources.js';
|
|
26
|
+
import {
|
|
27
|
+
resolvePublishAtlasRuntimeOptions,
|
|
28
|
+
resolvePublishFileName,
|
|
29
|
+
resolvePublishOptions,
|
|
30
|
+
type PublishOptions,
|
|
31
|
+
type ResolvedPublishAtlasOptions,
|
|
32
|
+
type ResolvedPublishOptions,
|
|
33
|
+
} from './publish/options.js';
|
|
34
|
+
import { collectPackageResourceReferences } from './publish/resource-references.js';
|
|
35
|
+
import type { CliPublishSettings, RootProjectSettings } from './shared-types.js';
|
|
18
36
|
import { createTransform } from './utils.js';
|
|
19
|
-
import { atlas, type AtlasOptions } from './atlas.js';
|
|
20
|
-
import { publishCodeGeneration } from './codegen.js';
|
|
21
|
-
import type {
|
|
22
|
-
CliPublishSettings,
|
|
23
|
-
HasOptionalFont,
|
|
24
|
-
PackagePublishArtifactsExtras,
|
|
25
|
-
PublishFileSystem,
|
|
26
|
-
RootProjectSettings,
|
|
27
|
-
} from './shared-types.js';
|
|
28
|
-
|
|
29
|
-
export interface PublishOptions {
|
|
30
|
-
/**
|
|
31
|
-
* Output directory for published files (.fui + atlas PNGs).
|
|
32
|
-
* Required.
|
|
33
|
-
*/
|
|
34
|
-
output: string;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Compress the binary data with zlib raw deflate. Default: false.
|
|
38
|
-
*/
|
|
39
|
-
compressed?: boolean;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* File extension for the binary output. Default: 'fui'.
|
|
43
|
-
* Unity projects typically use 'bytes'.
|
|
44
|
-
*/
|
|
45
|
-
fileExtension?: string;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Sharp module instance for atlas image compositing.
|
|
49
|
-
* If not provided, atlas packing only computes layout (no PNGs generated).
|
|
50
|
-
*/
|
|
51
|
-
encoder?: unknown;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Base path for reading source images (project assets root).
|
|
55
|
-
* Required when encoder is provided.
|
|
56
|
-
*/
|
|
57
|
-
basePath?: string;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Atlas packing options.
|
|
61
|
-
*/
|
|
62
|
-
atlas?: Omit<AtlasOptions, 'encoder' | 'basePath' | 'outputPath'>;
|
|
63
37
|
|
|
64
|
-
|
|
65
|
-
* Filter which packages to publish by name. If not set, all packages are published.
|
|
66
|
-
*/
|
|
67
|
-
packages?: string[];
|
|
38
|
+
export * from './publish/options.js';
|
|
68
39
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
export interface ResolvePublishOptionsOverrides {
|
|
86
|
-
compressed?: boolean;
|
|
87
|
-
fileExtension?: string;
|
|
88
|
-
packages?: string[];
|
|
89
|
-
atlas?: Partial<ResolvedPublishAtlasOptions>;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export interface ResolvedPublishOptions {
|
|
40
|
+
interface ResolvedProjectPublishConfig extends ResolvedPublishOptions {
|
|
41
|
+
projectType: number;
|
|
42
|
+
includeBranches: boolean;
|
|
43
|
+
activeBranch: string;
|
|
44
|
+
includeHighResolution: number;
|
|
45
|
+
separatedAtlasForBranch: boolean;
|
|
46
|
+
globalOutputPath: string;
|
|
47
|
+
globalBranchOutputPath: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface ResolvedPackagePublishPlan {
|
|
51
|
+
pkg: Package;
|
|
52
|
+
outputDir?: string;
|
|
53
|
+
publishName: string;
|
|
54
|
+
fileName: string;
|
|
93
55
|
compressed: boolean;
|
|
94
56
|
fileExtension: string;
|
|
95
|
-
packages?: string[];
|
|
96
|
-
atlas: ResolvedPublishAtlasOptions;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
interface ImageResourceExtras extends Record<string, unknown> {
|
|
100
|
-
_fileName?: string;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
interface PublishFileExtras extends Record<string, unknown> {
|
|
104
|
-
_publishedFile?: string;
|
|
105
|
-
_publishedId?: string;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
interface BranchAwarePublishedResource {
|
|
109
|
-
getBranch?(): string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
interface PackagePublishContext {
|
|
113
|
-
referencedIds: Set<string>;
|
|
114
|
-
publishedResourceIds: Set<string>;
|
|
115
|
-
pixelHitTestImageIds: Set<string>;
|
|
116
|
-
effectiveResourceIds: Map<string, string>;
|
|
117
57
|
includeBranches: boolean;
|
|
58
|
+
activeBranch: string;
|
|
59
|
+
includeHighResolution: number;
|
|
60
|
+
separatedAtlasForBranch: boolean;
|
|
61
|
+
atlas: ResolvedPublishAtlasOptions;
|
|
118
62
|
}
|
|
119
63
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
interface GearWithPublishRefs {
|
|
126
|
-
getValues?(): string;
|
|
127
|
-
getDefaultValue?(): unknown;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
interface TransitionItemWithPublishRefs {
|
|
131
|
-
getStartValue?(): unknown;
|
|
132
|
-
getEndValue?(): unknown;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
interface TransitionWithPublishRefs {
|
|
136
|
-
listItems?(): TransitionItemWithPublishRefs[];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
interface ChildWithPublishRefs extends HasOptionalFont {
|
|
140
|
-
getId?(): string;
|
|
141
|
-
getSrc?(): string;
|
|
142
|
-
getUrl?(): string;
|
|
143
|
-
getDefaultItem?(): string;
|
|
144
|
-
getIcon?(): string;
|
|
145
|
-
getSelectedIcon?(): string;
|
|
146
|
-
getDropdown?(): string;
|
|
147
|
-
getSound?(): string;
|
|
148
|
-
getText?(): string;
|
|
149
|
-
getInstanceIcon?(): string;
|
|
150
|
-
getInstanceSelectedIcon?(): string;
|
|
151
|
-
getVtScrollBarRes?(): string;
|
|
152
|
-
getHzScrollBarRes?(): string;
|
|
153
|
-
getHeaderRes?(): string;
|
|
154
|
-
getFooterRes?(): string;
|
|
155
|
-
getInstanceComboItems?(): Array<{ icon: string | null }>;
|
|
156
|
-
getListItems?(): ChildReferenceItem[];
|
|
157
|
-
listGears?(): GearWithPublishRefs[];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
interface ComponentWithPublishRefs {
|
|
161
|
-
getId(): string;
|
|
162
|
-
getExported(): boolean;
|
|
163
|
-
listChildren(): ChildWithPublishRefs[];
|
|
164
|
-
getHitTest?(): string;
|
|
165
|
-
getDropdown?(): string;
|
|
166
|
-
getHeaderRes?(): string;
|
|
167
|
-
getFooterRes?(): string;
|
|
168
|
-
getVtScrollBarRes?(): string;
|
|
169
|
-
getHzScrollBarRes?(): string;
|
|
170
|
-
getSound?(): string;
|
|
171
|
-
getFont?(): string | string[] | null | undefined;
|
|
172
|
-
listTransitions?(): TransitionWithPublishRefs[];
|
|
173
|
-
}
|
|
174
|
-
|
|
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
|
-
const UNITY_PROJECT_TYPE = ProjectType.Unity;
|
|
197
|
-
const COCOS_CREATOR_PROJECT_TYPE = ProjectType.CocosCreator;
|
|
198
|
-
|
|
199
|
-
function resolveDefaultPublishFileExtension(projectType: number, publishSettings: CliPublishSettings): string {
|
|
200
|
-
if (projectType === UNITY_PROJECT_TYPE) {
|
|
201
|
-
return 'bytes';
|
|
202
|
-
}
|
|
203
|
-
if (projectType === COCOS_CREATOR_PROJECT_TYPE) {
|
|
204
|
-
return publishSettings.fileExtension || 'bin';
|
|
205
|
-
}
|
|
206
|
-
// publish() is currently a binary forward-publish path. For non-Unity projects,
|
|
207
|
-
// keep the emitted contract driven by the configured extension, falling back to
|
|
208
|
-
// `fui`, which intentionally covers the shared generic binary contract outside
|
|
209
|
-
// Unity and the Creator-specific default-to-bin rule.
|
|
210
|
-
return publishSettings.fileExtension || 'fui';
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export interface PublishAtlasRuntimeOptions {
|
|
214
|
-
preserveInputOrderOnTie: boolean;
|
|
215
|
-
directSingleImageOutput: boolean;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export function resolvePublishAtlasRuntimeOptions(fileExtension: string): PublishAtlasRuntimeOptions {
|
|
219
|
-
return {
|
|
220
|
-
preserveInputOrderOnTie: fileExtension === 'fui',
|
|
221
|
-
directSingleImageOutput: fileExtension === 'bytes',
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function resolvePublishFileName(publishName: string, fileExtension: string): string {
|
|
226
|
-
if (fileExtension === 'bytes') {
|
|
227
|
-
return `${publishName}_fui.bytes`;
|
|
228
|
-
}
|
|
229
|
-
return `${publishName}.${fileExtension}`;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Resolve publish defaults from the document's project settings.
|
|
234
|
-
*
|
|
235
|
-
* This keeps the editor-aligned publish rules reusable across environments,
|
|
236
|
-
* while callers still provide environment-specific concerns such as fs/encoder/basePath.
|
|
237
|
-
*/
|
|
238
|
-
export function resolvePublishOptions(
|
|
64
|
+
async function runPublishPluginHook(
|
|
65
|
+
plugins: LoadedPlugin[],
|
|
66
|
+
hook: 'onPublishStart' | 'onPublishEnd',
|
|
239
67
|
doc: Document,
|
|
240
|
-
|
|
241
|
-
):
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
let compressed = overrides.compressed ?? publishSettings.compressDesc ?? false;
|
|
252
|
-
if (projectType === UNITY_PROJECT_TYPE) {
|
|
253
|
-
compressed = overrides.compressed ?? false;
|
|
68
|
+
options: PublishOptions,
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
const logger = doc.getLogger();
|
|
71
|
+
for (const plugin of plugins) {
|
|
72
|
+
const fn = plugin.plugin[hook];
|
|
73
|
+
if (typeof fn !== 'function') continue;
|
|
74
|
+
try {
|
|
75
|
+
await fn(doc, options);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logger.warn(`publish: Plugin "${plugin.name}" ${hook} failed: ${formatPluginError(error)}`);
|
|
78
|
+
}
|
|
254
79
|
}
|
|
255
|
-
|
|
256
|
-
const atlasOptions: ResolvedPublishAtlasOptions = {
|
|
257
|
-
maxSize: overrides.atlas?.maxSize ?? atlasSetting.maxSize ?? 2048,
|
|
258
|
-
fast: overrides.atlas?.fast ?? atlasSetting.fast ?? true,
|
|
259
|
-
allowRotation: overrides.atlas?.allowRotation ?? atlasSetting.allowRotation ?? false,
|
|
260
|
-
padding: overrides.atlas?.padding ?? atlasSetting.padding ?? 2,
|
|
261
|
-
powerOfTwo: overrides.atlas?.powerOfTwo ?? atlasSetting.sizeOption === 'pot',
|
|
262
|
-
square: overrides.atlas?.square ?? atlasSetting.forceSquare ?? false,
|
|
263
|
-
multiPage: overrides.atlas?.multiPage ?? atlasSetting.paging ?? true,
|
|
264
|
-
trimImage: overrides.atlas?.trimImage ?? atlasSetting.trimImage ?? false,
|
|
265
|
-
extractAlpha: overrides.atlas?.extractAlpha ?? atlasSetting.extractAlpha ?? false,
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
return {
|
|
269
|
-
compressed,
|
|
270
|
-
fileExtension,
|
|
271
|
-
packages: overrides.packages,
|
|
272
|
-
atlas: atlasOptions,
|
|
273
|
-
};
|
|
274
80
|
}
|
|
275
81
|
|
|
276
|
-
function
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
|
|
82
|
+
function joinPathSegments(left: string, right: string): string {
|
|
83
|
+
const normalizedLeft = trimTrailingSlashes(left);
|
|
84
|
+
const normalizedRight = right.replace(/^[/\\]+/, '');
|
|
85
|
+
if (!normalizedLeft) return normalizedRight;
|
|
86
|
+
if (!normalizedRight) return normalizedLeft;
|
|
87
|
+
const separator = normalizedLeft.includes('\\') ? '\\' : '/';
|
|
88
|
+
return `${normalizedLeft}${separator}${normalizedRight}`;
|
|
280
89
|
}
|
|
281
90
|
|
|
282
91
|
function createUnsupportedFsOperation(name: keyof FileSystem) {
|
|
@@ -299,650 +108,187 @@ function toBinaryWriterFileSystem(fs: PublishFileSystem): FileSystem {
|
|
|
299
108
|
};
|
|
300
109
|
}
|
|
301
110
|
|
|
302
|
-
function isComponentResource(resource: ReturnType<Package['listResources']>[number]): resource is Component {
|
|
303
|
-
return resource.propertyType === 'Component';
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function isImageResource(resource: ReturnType<Package['listResources']>[number]): resource is ImageResource {
|
|
307
|
-
return resource.propertyType === 'ImageResource';
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function isMovieClipResource(resource: ReturnType<Package['listResources']>[number]): resource is MovieClipResource {
|
|
311
|
-
return resource.propertyType === 'MovieClipResource';
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function isMiscResource(resource: ReturnType<Package['listResources']>[number]): resource is MiscResource {
|
|
315
|
-
return resource.propertyType === 'MiscResource';
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function isFontResource(resource: ReturnType<Package['listResources']>[number]): resource is FontResource {
|
|
319
|
-
return resource.propertyType === 'FontResource';
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function isSoundResource(resource: ReturnType<Package['listResources']>[number]): resource is SoundResource {
|
|
323
|
-
return resource.propertyType === 'SoundResource';
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function isSpineResource(resource: ReturnType<Package['listResources']>[number]): resource is SpineResource {
|
|
327
|
-
return resource.propertyType === 'SpineResource';
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function isDragonBonesResource(resource: ReturnType<Package['listResources']>[number]): resource is DragonBonesResource {
|
|
331
|
-
return resource.propertyType === 'DragonBonesResource';
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function isSkeletonResource(
|
|
335
|
-
resource: ReturnType<Package['listResources']>[number],
|
|
336
|
-
): resource is SpineResource | DragonBonesResource {
|
|
337
|
-
return isSpineResource(resource) || isDragonBonesResource(resource);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function addLocalUiResourceRef(target: Set<string>, pkgId: string, value: string | null | undefined): void {
|
|
341
|
-
if (!value || typeof value !== 'string' || !value.startsWith(`ui://${pkgId}`) || value.length <= 13) return;
|
|
342
|
-
target.add(value.slice(13));
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function addLocalUiResourceRefsFromText(target: Set<string>, pkgId: string, value: string | null | undefined): void {
|
|
346
|
-
if (!value || typeof value !== 'string') return;
|
|
347
|
-
const prefix = `ui://${pkgId}`;
|
|
348
|
-
let index = value.indexOf(prefix);
|
|
349
|
-
while (index !== -1) {
|
|
350
|
-
const start = index + prefix.length;
|
|
351
|
-
let end = start;
|
|
352
|
-
while (end < value.length && /[0-9a-z]/i.test(value[end] ?? '')) end++;
|
|
353
|
-
if (end > start) target.add(value.slice(start, end));
|
|
354
|
-
index = value.indexOf(prefix, end);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function addLocalUiResourceRefsFromUnknown(target: Set<string>, pkgId: string, value: unknown): void {
|
|
359
|
-
if (Array.isArray(value)) {
|
|
360
|
-
for (const entry of value) addLocalUiResourceRefsFromUnknown(target, pkgId, entry);
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
if (typeof value === 'string') {
|
|
364
|
-
addLocalUiResourceRef(target, pkgId, value);
|
|
365
|
-
addLocalUiResourceRefsFromText(target, pkgId, value);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function addLocalFontRef(target: Set<string>, pkgId: string, value: string | string[] | null | undefined): void {
|
|
370
|
-
if (Array.isArray(value)) {
|
|
371
|
-
for (const entry of value) addLocalUiResourceRef(target, pkgId, entry);
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
addLocalUiResourceRef(target, pkgId, value ?? undefined);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function resolvePackageAssetsBasePath(
|
|
378
|
-
basePath: string,
|
|
379
|
-
resource: BranchAwarePublishedResource | undefined,
|
|
380
|
-
): string {
|
|
381
|
-
const branchName = resource?.getBranch?.() ?? '';
|
|
382
|
-
if (!branchName) return basePath;
|
|
383
|
-
const normalized = basePath.replace(/[/\\]+$/, '');
|
|
384
|
-
if (/[\\/]assets$/i.test(normalized)) {
|
|
385
|
-
return normalized.replace(/([\\/])assets$/i, `$1assets_${branchName}`);
|
|
386
|
-
}
|
|
387
|
-
return `${normalized}_${branchName}`;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function resolveImagePath(resource: ImageResource, pkg: Package, basePath: string): string {
|
|
391
|
-
const fileName = resolveImageFileName(resource);
|
|
392
|
-
const resourcePath = resource.getPath() ?? '/';
|
|
393
|
-
const packageBasePath = resolvePackageAssetsBasePath(basePath, resource);
|
|
394
|
-
return `${packageBasePath}/${pkg.getName()}${resourcePath}${fileName}`;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function resolveImageFileName(resource: ImageResource): string {
|
|
398
|
-
const extras = (resource.getExtras() as ImageResourceExtras | undefined) ?? {};
|
|
399
|
-
return resource.getFileName() || extras._fileName || resource.getName();
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
function resolveSoundPath(resource: SoundResource, pkg: Package, basePath: string): string {
|
|
403
|
-
const resourcePath = resource.getPath() ?? '/';
|
|
404
|
-
const packageBasePath = resolvePackageAssetsBasePath(basePath, resource);
|
|
405
|
-
return `${packageBasePath}/${pkg.getName()}${resourcePath}${resource.getFile()}`;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function resolveGenericResourcePath(
|
|
409
|
-
resource: { getPath(): string; getFile(): string; getBranch?(): string },
|
|
410
|
-
pkg: Package,
|
|
411
|
-
basePath: string,
|
|
412
|
-
): string {
|
|
413
|
-
const resourcePath = resource.getPath() ?? '/';
|
|
414
|
-
const packageBasePath = resolvePackageAssetsBasePath(basePath, resource);
|
|
415
|
-
return `${packageBasePath}/${pkg.getName()}${resourcePath}${resource.getFile()}`;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
function extname(fileName: string): string {
|
|
419
|
-
const normalized = fileName.replace(/\\/g, '/');
|
|
420
|
-
const lastSlash = normalized.lastIndexOf('/');
|
|
421
|
-
const lastDot = normalized.lastIndexOf('.');
|
|
422
|
-
if (lastDot <= lastSlash) return '';
|
|
423
|
-
return normalized.slice(lastDot);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
function resolvePublishedMiscFileName(resource: MiscResource): string {
|
|
427
|
-
const file = resource.getFile();
|
|
428
|
-
if (file.toLowerCase().endsWith('.atlas')) return `${file}.txt`;
|
|
429
|
-
return file;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
function resolvePublishedSkeletonFileName(resource: SpineResource | DragonBonesResource): string {
|
|
433
|
-
if (isSpineResource(resource) && resource.getFile().toLowerCase().endsWith('.skel')) {
|
|
434
|
-
return `${resource.getFile()}.bytes`;
|
|
435
|
-
}
|
|
436
|
-
return resource.getFile();
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function setPublishedFileExtra(
|
|
440
|
-
resource: { getExtras(): Record<string, unknown> | undefined; setExtras(value: Record<string, unknown>): unknown },
|
|
441
|
-
fileName: string,
|
|
442
|
-
): void {
|
|
443
|
-
const extras = (resource.getExtras() as PublishFileExtras | undefined) ?? {};
|
|
444
|
-
resource.setExtras({
|
|
445
|
-
...extras,
|
|
446
|
-
_publishedFile: fileName,
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
function setPublishedIdExtra(
|
|
451
|
-
resource: { getId(): string; getExtras(): Record<string, unknown> | undefined; setExtras(value: Record<string, unknown>): unknown },
|
|
452
|
-
effectiveId: string | null,
|
|
453
|
-
): void {
|
|
454
|
-
const extras = (resource.getExtras() as PublishFileExtras | undefined) ?? {};
|
|
455
|
-
if (!effectiveId || effectiveId === resource.getId()) {
|
|
456
|
-
if (!('_publishedId' in extras)) return;
|
|
457
|
-
const { _publishedId: _ignored, ...rest } = extras;
|
|
458
|
-
resource.setExtras(rest);
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
resource.setExtras({
|
|
462
|
-
...extras,
|
|
463
|
-
_publishedId: effectiveId,
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function getPublishedId(resource: { getId(): string; getExtras(): Record<string, unknown> | undefined }): string {
|
|
468
|
-
const extras = (resource.getExtras() as PublishFileExtras | undefined) ?? {};
|
|
469
|
-
return extras._publishedId ?? resource.getId();
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function getBranchName(resource: BranchAwarePublishedResource | undefined): string {
|
|
473
|
-
return resource?.getBranch?.() ?? '';
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function buildBranchResourceKey(resource: {
|
|
477
|
-
propertyType: string;
|
|
478
|
-
getPath(): string;
|
|
479
|
-
getName(): string;
|
|
480
|
-
}): string {
|
|
481
|
-
return `${resource.propertyType}|${resource.getPath() ?? ''}|${resource.getName() ?? ''}`;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
function collectPackagePublishContext(
|
|
485
|
-
pkg: Package,
|
|
486
|
-
options: {
|
|
487
|
-
includeBranches: boolean;
|
|
488
|
-
activeBranch: string;
|
|
489
|
-
},
|
|
490
|
-
): PackagePublishContext {
|
|
491
|
-
const pkgId = pkg.getId();
|
|
492
|
-
const resources = pkg.listResources();
|
|
493
|
-
const resourceMap = new Map(resources.map((resource) => [resource.getId(), resource]));
|
|
494
|
-
const referencedIds = new Set<string>();
|
|
495
|
-
const pixelHitTestImageIds = new Set<string>();
|
|
496
|
-
const spriteItemIds = new Set<string>();
|
|
497
|
-
|
|
498
|
-
for (const atlas of pkg.listAtlases()) {
|
|
499
|
-
for (const sprite of atlas.listSprites()) {
|
|
500
|
-
spriteItemIds.add(sprite.getItemId());
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
for (const resource of resources) {
|
|
505
|
-
if (!isComponentResource(resource)) continue;
|
|
506
|
-
const component = resource as ComponentWithPublishRefs;
|
|
507
|
-
const children = component.listChildren();
|
|
508
|
-
const childMap = new Map(children.map((child) => [child.getId?.() ?? '', child]));
|
|
509
|
-
|
|
510
|
-
const hitTest = component.getHitTest?.()?.trim();
|
|
511
|
-
if (hitTest && !hitTest.includes(',')) {
|
|
512
|
-
const targetChild = childMap.get(hitTest);
|
|
513
|
-
const sourceId = targetChild?.getSrc?.();
|
|
514
|
-
if (sourceId) {
|
|
515
|
-
const sourceResource = resourceMap.get(sourceId);
|
|
516
|
-
if (sourceResource && isImageResource(sourceResource)) {
|
|
517
|
-
pixelHitTestImageIds.add(sourceId);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
for (const child of children) {
|
|
523
|
-
const src = child.getSrc?.();
|
|
524
|
-
if (src) referencedIds.add(src);
|
|
525
|
-
addLocalFontRef(referencedIds, pkgId, child.getFont?.());
|
|
526
|
-
addLocalUiResourceRefsFromText(referencedIds, pkgId, child.getText?.());
|
|
527
|
-
for (const ref of [
|
|
528
|
-
child.getUrl?.(),
|
|
529
|
-
child.getDefaultItem?.(),
|
|
530
|
-
child.getIcon?.(),
|
|
531
|
-
child.getSelectedIcon?.(),
|
|
532
|
-
child.getDropdown?.(),
|
|
533
|
-
child.getSound?.(),
|
|
534
|
-
child.getInstanceIcon?.(),
|
|
535
|
-
child.getInstanceSelectedIcon?.(),
|
|
536
|
-
child.getVtScrollBarRes?.(),
|
|
537
|
-
child.getHzScrollBarRes?.(),
|
|
538
|
-
child.getHeaderRes?.(),
|
|
539
|
-
child.getFooterRes?.(),
|
|
540
|
-
]) {
|
|
541
|
-
addLocalUiResourceRef(referencedIds, pkgId, ref);
|
|
542
|
-
}
|
|
543
|
-
for (const item of child.getInstanceComboItems?.() ?? []) {
|
|
544
|
-
addLocalUiResourceRef(referencedIds, pkgId, item.icon ?? undefined);
|
|
545
|
-
}
|
|
546
|
-
for (const item of child.getListItems?.() ?? []) {
|
|
547
|
-
addLocalUiResourceRef(referencedIds, pkgId, item.icon ?? undefined);
|
|
548
|
-
addLocalUiResourceRef(referencedIds, pkgId, item.url ?? undefined);
|
|
549
|
-
}
|
|
550
|
-
for (const gear of child.listGears?.() ?? []) {
|
|
551
|
-
addLocalUiResourceRefsFromUnknown(referencedIds, pkgId, gear.getValues?.());
|
|
552
|
-
addLocalUiResourceRefsFromUnknown(referencedIds, pkgId, gear.getDefaultValue?.());
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
addLocalFontRef(referencedIds, pkgId, component.getFont?.());
|
|
557
|
-
for (const ref of [
|
|
558
|
-
component.getDropdown?.(),
|
|
559
|
-
component.getHeaderRes?.(),
|
|
560
|
-
component.getFooterRes?.(),
|
|
561
|
-
component.getVtScrollBarRes?.(),
|
|
562
|
-
component.getHzScrollBarRes?.(),
|
|
563
|
-
component.getSound?.(),
|
|
564
|
-
]) {
|
|
565
|
-
addLocalUiResourceRef(referencedIds, pkgId, ref);
|
|
566
|
-
}
|
|
567
|
-
for (const transition of component.listTransitions?.() ?? []) {
|
|
568
|
-
for (const item of transition.listItems?.() ?? []) {
|
|
569
|
-
addLocalUiResourceRefsFromUnknown(referencedIds, pkgId, item.getStartValue?.());
|
|
570
|
-
addLocalUiResourceRefsFromUnknown(referencedIds, pkgId, item.getEndValue?.());
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
const publishedResourceIds = new Set<string>(spriteItemIds);
|
|
576
|
-
for (const resource of resources) {
|
|
577
|
-
const resourceId = resource.getId();
|
|
578
|
-
if (!resourceId) continue;
|
|
579
|
-
if (isComponentResource(resource)) {
|
|
580
|
-
if (resource.getExported() || referencedIds.has(resourceId)) {
|
|
581
|
-
publishedResourceIds.add(resourceId);
|
|
582
|
-
}
|
|
583
|
-
continue;
|
|
584
|
-
}
|
|
585
|
-
if (isImageResource(resource)) {
|
|
586
|
-
if (resource.getExported() || referencedIds.has(resourceId) || spriteItemIds.has(resourceId) || pixelHitTestImageIds.has(resourceId)) {
|
|
587
|
-
publishedResourceIds.add(resourceId);
|
|
588
|
-
}
|
|
589
|
-
continue;
|
|
590
|
-
}
|
|
591
|
-
if (isMovieClipResource(resource) || isSoundResource(resource)) {
|
|
592
|
-
if (resource.getExported() || referencedIds.has(resourceId)) publishedResourceIds.add(resourceId);
|
|
593
|
-
continue;
|
|
594
|
-
}
|
|
595
|
-
if (isMiscResource(resource) || isSkeletonResource(resource)) {
|
|
596
|
-
if (resource.getExported() || referencedIds.has(resourceId)) publishedResourceIds.add(resourceId);
|
|
597
|
-
continue;
|
|
598
|
-
}
|
|
599
|
-
if (isFontResource(resource)) {
|
|
600
|
-
if (resource.getExported() || referencedIds.has(resourceId)) {
|
|
601
|
-
publishedResourceIds.add(resourceId);
|
|
602
|
-
}
|
|
603
|
-
continue;
|
|
604
|
-
}
|
|
605
|
-
const genericResource = resource as ReturnType<Package['listResources']>[number];
|
|
606
|
-
if (genericResource.getExported() || referencedIds.has(resourceId)) {
|
|
607
|
-
publishedResourceIds.add(resourceId);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
let changed = true;
|
|
612
|
-
while (changed) {
|
|
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
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
if (!options.includeBranches) {
|
|
626
|
-
const mainByKey = new Map<string, ReturnType<Package['listResources']>[number]>();
|
|
627
|
-
const activeBranchByKey = new Map<string, ReturnType<Package['listResources']>[number]>();
|
|
628
|
-
for (const resource of resources) {
|
|
629
|
-
const branchName = getBranchName(resource);
|
|
630
|
-
const key = buildBranchResourceKey(resource);
|
|
631
|
-
if (!branchName) {
|
|
632
|
-
mainByKey.set(key, resource);
|
|
633
|
-
} else if (branchName === options.activeBranch) {
|
|
634
|
-
activeBranchByKey.set(key, resource);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
const mergedPublishedResourceIds = new Set<string>();
|
|
639
|
-
const effectiveResourceIds = new Map<string, string>();
|
|
640
|
-
for (const resource of resources) {
|
|
641
|
-
const resourceId = resource.getId();
|
|
642
|
-
if (!publishedResourceIds.has(resourceId)) continue;
|
|
643
|
-
|
|
644
|
-
const branchName = getBranchName(resource);
|
|
645
|
-
const key = buildBranchResourceKey(resource);
|
|
646
|
-
if (branchName) {
|
|
647
|
-
if (branchName !== options.activeBranch) continue;
|
|
648
|
-
const mainResource = mainByKey.get(key);
|
|
649
|
-
mergedPublishedResourceIds.add(resourceId);
|
|
650
|
-
effectiveResourceIds.set(resourceId, mainResource?.getId() ?? resourceId);
|
|
651
|
-
continue;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
const override = activeBranchByKey.get(key);
|
|
655
|
-
if (override) {
|
|
656
|
-
mergedPublishedResourceIds.add(override.getId());
|
|
657
|
-
effectiveResourceIds.set(override.getId(), resourceId);
|
|
658
|
-
continue;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
mergedPublishedResourceIds.add(resourceId);
|
|
662
|
-
effectiveResourceIds.set(resourceId, resourceId);
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
publishedResourceIds.clear();
|
|
666
|
-
for (const resourceId of mergedPublishedResourceIds) {
|
|
667
|
-
publishedResourceIds.add(resourceId);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
const mergedPixelHitTestImageIds = new Set<string>();
|
|
671
|
-
for (const resource of resources) {
|
|
672
|
-
if (!isImageResource(resource)) continue;
|
|
673
|
-
const resourceId = resource.getId();
|
|
674
|
-
if (!publishedResourceIds.has(resourceId)) continue;
|
|
675
|
-
const effectiveId = effectiveResourceIds.get(resourceId) ?? resourceId;
|
|
676
|
-
if (pixelHitTestImageIds.has(effectiveId)) {
|
|
677
|
-
mergedPixelHitTestImageIds.add(resourceId);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
pixelHitTestImageIds.clear();
|
|
681
|
-
for (const resourceId of mergedPixelHitTestImageIds) {
|
|
682
|
-
pixelHitTestImageIds.add(resourceId);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
return {
|
|
686
|
-
referencedIds,
|
|
687
|
-
publishedResourceIds,
|
|
688
|
-
pixelHitTestImageIds,
|
|
689
|
-
effectiveResourceIds,
|
|
690
|
-
includeBranches: false,
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
return {
|
|
695
|
-
referencedIds,
|
|
696
|
-
publishedResourceIds,
|
|
697
|
-
pixelHitTestImageIds,
|
|
698
|
-
effectiveResourceIds: new Map([...publishedResourceIds].map((resourceId) => [resourceId, resourceId])),
|
|
699
|
-
includeBranches: true,
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
async function applyPixelHitTests(
|
|
704
|
-
pkg: Package,
|
|
705
|
-
imageIds: Set<string>,
|
|
706
|
-
basePath: string | undefined,
|
|
707
|
-
encoder: PublishEncoder | undefined,
|
|
708
|
-
): Promise<void> {
|
|
709
|
-
const images = pkg.listImageResources();
|
|
710
|
-
for (const image of images) {
|
|
711
|
-
image.setPixelHitTestData(null);
|
|
712
|
-
}
|
|
713
|
-
if (!basePath || !encoder || imageIds.size === 0) return;
|
|
714
|
-
|
|
715
|
-
for (const image of images) {
|
|
716
|
-
const imageId = image.getId();
|
|
717
|
-
if (!imageIds.has(imageId)) continue;
|
|
718
|
-
try {
|
|
719
|
-
const sourcePath = resolveImagePath(image, pkg, basePath);
|
|
720
|
-
const metadata = await encoder(sourcePath).metadata();
|
|
721
|
-
if (!metadata.width || !metadata.height) continue;
|
|
722
|
-
|
|
723
|
-
const resizedWidth = Math.max(1, Math.floor(metadata.width / 2));
|
|
724
|
-
const resizedHeight = Math.max(1, Math.floor(metadata.height / 2));
|
|
725
|
-
const { data, info } = await encoder(sourcePath)
|
|
726
|
-
.ensureAlpha()
|
|
727
|
-
.resize({
|
|
728
|
-
width: resizedWidth,
|
|
729
|
-
height: resizedHeight,
|
|
730
|
-
fit: 'fill',
|
|
731
|
-
})
|
|
732
|
-
.raw()
|
|
733
|
-
.toBuffer({ resolveWithObject: true });
|
|
734
|
-
|
|
735
|
-
const pixelCount = info.width * info.height;
|
|
736
|
-
const maskBytes = new Uint8Array(Math.ceil(pixelCount / 8));
|
|
737
|
-
let byteValue = 0;
|
|
738
|
-
let bitIndex = 0;
|
|
739
|
-
let maskIndex = 0;
|
|
740
|
-
|
|
741
|
-
for (let pixel = 0; pixel < pixelCount; pixel++) {
|
|
742
|
-
const alpha = data[pixel * info.channels + 3];
|
|
743
|
-
if (alpha > 10) byteValue |= 1 << bitIndex;
|
|
744
|
-
bitIndex++;
|
|
745
|
-
if (bitIndex === 8) {
|
|
746
|
-
maskBytes[maskIndex++] = byteValue;
|
|
747
|
-
bitIndex = 0;
|
|
748
|
-
byteValue = 0;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
if (bitIndex !== 0) {
|
|
752
|
-
maskBytes[maskIndex] = byteValue;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
image.setPixelHitTestData({
|
|
756
|
-
pixelWidth: info.width,
|
|
757
|
-
scaleDenominator: 2,
|
|
758
|
-
pixels: maskBytes,
|
|
759
|
-
});
|
|
760
|
-
} catch {
|
|
761
|
-
image.setPixelHitTestData(null);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
async function annotatePackagePublishArtifacts(
|
|
767
|
-
pkg: Package,
|
|
768
|
-
basePath: string | undefined,
|
|
769
|
-
encoder: PublishEncoder | undefined,
|
|
770
|
-
options: {
|
|
771
|
-
includeBranches: boolean;
|
|
772
|
-
activeBranch: string;
|
|
773
|
-
},
|
|
774
|
-
): Promise<void> {
|
|
775
|
-
const { publishedResourceIds, pixelHitTestImageIds, effectiveResourceIds, includeBranches } = collectPackagePublishContext(pkg, options);
|
|
776
|
-
for (const resource of pkg.listResources()) {
|
|
777
|
-
setPublishedIdExtra(resource, effectiveResourceIds.get(resource.getId()) ?? null);
|
|
778
|
-
}
|
|
779
|
-
await applyPixelHitTests(pkg, pixelHitTestImageIds, basePath, encoder);
|
|
780
|
-
const extras = (pkg.getExtras() as PackagePublishArtifactsExtras | undefined) ?? {};
|
|
781
|
-
pkg.setExtras({
|
|
782
|
-
...extras,
|
|
783
|
-
publishedResourceIds: [...publishedResourceIds].sort((a, b) => a.localeCompare(b)),
|
|
784
|
-
publishedIncludeBranches: includeBranches,
|
|
785
|
-
publishedEffectiveResourceIds: Object.fromEntries(effectiveResourceIds),
|
|
786
|
-
});
|
|
787
|
-
for (const resource of pkg.listResources()) {
|
|
788
|
-
if (isMiscResource(resource)) {
|
|
789
|
-
setPublishedFileExtra(resource, resolvePublishedMiscFileName(resource));
|
|
790
|
-
continue;
|
|
791
|
-
}
|
|
792
|
-
if (isSkeletonResource(resource)) {
|
|
793
|
-
setPublishedFileExtra(resource, resolvePublishedSkeletonFileName(resource));
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
function getAnnotatedPublishedResourceIds(pkg: Package): Set<string> {
|
|
799
|
-
const extras = (pkg.getExtras() as PackagePublishArtifactsExtras | undefined) ?? {};
|
|
800
|
-
return new Set(extras.publishedResourceIds ?? []);
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
function getPublishedSkeletonDependencyImageIds(
|
|
804
|
-
pkg: Package,
|
|
805
|
-
publishedResourceIds: Set<string>,
|
|
806
|
-
): Set<string> {
|
|
807
|
-
const imageIds = new Set<string>();
|
|
808
|
-
const resourcesById = new Map(pkg.listResources().map((resource) => [resource.getId(), resource] as const));
|
|
809
|
-
for (const resource of pkg.listResources()) {
|
|
810
|
-
if (!isSkeletonResource(resource)) continue;
|
|
811
|
-
if (!publishedResourceIds.has(resource.getId())) continue;
|
|
812
|
-
for (const requiredId of resource.getRequireIds()) {
|
|
813
|
-
if (!requiredId) continue;
|
|
814
|
-
const required = resourcesById.get(requiredId);
|
|
815
|
-
if (required && isImageResource(required)) imageIds.add(requiredId);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
return imageIds;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
async function exportPackageSounds(
|
|
822
|
-
pkg: Package,
|
|
823
|
-
outputDir: string,
|
|
824
|
-
basePath: string | undefined,
|
|
825
|
-
fs: PublishFileSystem,
|
|
826
|
-
readFileRaw: PublishFileSystem['readFileRaw'] | undefined,
|
|
827
|
-
logger: Document['getLogger'] extends () => infer T ? T : never,
|
|
828
|
-
): Promise<void> {
|
|
829
|
-
const publishedResourceIds = getAnnotatedPublishedResourceIds(pkg);
|
|
830
|
-
if (publishedResourceIds.size === 0) return;
|
|
831
|
-
if (!basePath || !readFileRaw) {
|
|
832
|
-
const hasPublishedSound = pkg.listResources().some((resource) => {
|
|
833
|
-
return isSoundResource(resource) && publishedResourceIds.has(resource.getId());
|
|
834
|
-
});
|
|
835
|
-
if (hasPublishedSound) {
|
|
836
|
-
logger.warn(`publish: Sound resources in package "${pkg.getName()}" were not exported because basePath/readFileRaw is unavailable.`);
|
|
837
|
-
}
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
for (const resource of pkg.listResources()) {
|
|
842
|
-
if (!isSoundResource(resource)) continue;
|
|
843
|
-
if (!publishedResourceIds.has(resource.getId())) continue;
|
|
844
|
-
|
|
845
|
-
const sourcePath = resolveSoundPath(resource, pkg, basePath);
|
|
846
|
-
const targetName = `${pkg.getPublishName() || pkg.getName()}_${getPublishedId(resource)}${extname(resource.getFile() || '')}`;
|
|
847
|
-
const targetPath = fs.join(outputDir, targetName);
|
|
848
|
-
|
|
849
|
-
try {
|
|
850
|
-
const data = await readFileRaw(sourcePath);
|
|
851
|
-
await fs.writeFileRaw(targetPath, data);
|
|
852
|
-
} catch {
|
|
853
|
-
logger.warn(`publish: Could not export sound "${resource.getId()}" from package "${pkg.getName()}".`);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
async function exportPackageExternalResources(
|
|
859
|
-
pkg: Package,
|
|
860
|
-
outputDir: string,
|
|
861
|
-
basePath: string | undefined,
|
|
862
|
-
fs: PublishFileSystem,
|
|
863
|
-
readFileRaw: PublishFileSystem['readFileRaw'] | undefined,
|
|
864
|
-
logger: Document['getLogger'] extends () => infer T ? T : never,
|
|
865
|
-
): Promise<void> {
|
|
866
|
-
const publishedResourceIds = getAnnotatedPublishedResourceIds(pkg);
|
|
867
|
-
const skeletonDependencyImageIds = getPublishedSkeletonDependencyImageIds(pkg, publishedResourceIds);
|
|
868
|
-
if (publishedResourceIds.size === 0) return;
|
|
869
|
-
if (!basePath || !readFileRaw) {
|
|
870
|
-
const hasPublishedExternal = pkg.listResources().some((resource) => {
|
|
871
|
-
return (
|
|
872
|
-
(isMiscResource(resource) || isSkeletonResource(resource))
|
|
873
|
-
&& publishedResourceIds.has(resource.getId())
|
|
874
|
-
) || skeletonDependencyImageIds.has(resource.getId());
|
|
875
|
-
});
|
|
876
|
-
if (hasPublishedExternal) {
|
|
877
|
-
logger.warn(`publish: External resources in package "${pkg.getName()}" were not exported because basePath/readFileRaw is unavailable.`);
|
|
878
|
-
}
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
for (const resource of pkg.listResources()) {
|
|
883
|
-
const resourceId = resource.getId();
|
|
884
|
-
const isSkeletonExternal = publishedResourceIds.has(resourceId) && (isMiscResource(resource) || isSkeletonResource(resource));
|
|
885
|
-
const isSkeletonImageDependency = skeletonDependencyImageIds.has(resourceId) && isImageResource(resource);
|
|
886
|
-
if (!isSkeletonExternal && !isSkeletonImageDependency) continue;
|
|
887
|
-
|
|
888
|
-
let sourcePath: string;
|
|
889
|
-
let targetName: string;
|
|
890
|
-
if (isSkeletonImageDependency) {
|
|
891
|
-
sourcePath = resolveImagePath(resource, pkg, basePath);
|
|
892
|
-
targetName = resolveImageFileName(resource);
|
|
893
|
-
} else if (isMiscResource(resource) || isSkeletonResource(resource)) {
|
|
894
|
-
sourcePath = resolveGenericResourcePath(resource, pkg, basePath);
|
|
895
|
-
targetName = ((resource.getExtras() as PublishFileExtras | undefined) ?? {})._publishedFile ?? resource.getFile();
|
|
896
|
-
} else {
|
|
897
|
-
continue;
|
|
898
|
-
}
|
|
899
|
-
const targetPath = fs.join(outputDir, targetName);
|
|
900
|
-
|
|
901
|
-
try {
|
|
902
|
-
const data = await readFileRaw(sourcePath);
|
|
903
|
-
await fs.writeFileRaw(targetPath, data);
|
|
904
|
-
} catch {
|
|
905
|
-
logger.warn(`publish: Could not export external resource "${resource.getId()}" from package "${pkg.getName()}".`);
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
|
|
910
111
|
/**
|
|
911
112
|
* Publishes a FairyGUI project.
|
|
912
113
|
*
|
|
913
114
|
* Orchestrates:
|
|
914
|
-
* 1. Atlas packing (MaxRects layout + optional
|
|
115
|
+
* 1. Atlas packing (MaxRects layout + optional raster compositing)
|
|
915
116
|
* 2. Per-package .fui binary serialization
|
|
916
117
|
* 3. File writing to the output directory
|
|
917
118
|
*
|
|
119
|
+
* This is the capability-injected core. Standard hosts should use
|
|
120
|
+
* `publishNode()` or `publishBrowser()` through their dedicated entries.
|
|
121
|
+
*
|
|
918
122
|
* ```ts
|
|
919
|
-
* import
|
|
920
|
-
*
|
|
921
|
-
* const doc = await
|
|
123
|
+
* import { NodeIO } from '@openfairygui/core/node';
|
|
124
|
+
* import { publishNode } from '@openfairygui/functions/node';
|
|
125
|
+
* const doc = await new NodeIO().readProject('./project.fairy');
|
|
922
126
|
*
|
|
923
|
-
* await
|
|
127
|
+
* await publishNode({
|
|
128
|
+
* document: doc,
|
|
924
129
|
* output: './release/',
|
|
925
130
|
* compressed: true,
|
|
926
|
-
*
|
|
927
|
-
* basePath: './assets/',
|
|
131
|
+
* assetsPath: './assets/',
|
|
928
132
|
* fileExtension: 'bytes',
|
|
929
|
-
*
|
|
930
|
-
* }));
|
|
133
|
+
* });
|
|
931
134
|
* ```
|
|
932
135
|
*/
|
|
933
136
|
export function publish(options: PublishOptions): Transform {
|
|
934
137
|
return createTransform('publish', async (doc: Document): Promise<void> => {
|
|
138
|
+
const resolveConfiguredOutputPath = (value?: string, projectBasePath?: string): string | undefined => {
|
|
139
|
+
const trimmed = value?.trim();
|
|
140
|
+
if (!trimmed) return undefined;
|
|
141
|
+
if (isAbsolutePathLike(trimmed) || !projectBasePath) {
|
|
142
|
+
return trimTrailingSlashes(trimmed);
|
|
143
|
+
}
|
|
144
|
+
return trimTrailingSlashes(
|
|
145
|
+
options.fs ? options.fs.join(projectBasePath, trimmed) : joinPathSegments(projectBasePath, trimmed),
|
|
146
|
+
);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const resolveProjectPublishConfig = (): ResolvedProjectPublishConfig => {
|
|
150
|
+
const settings = (doc.getRoot().getSettings?.() ?? {}) as RootProjectSettings;
|
|
151
|
+
const publishSettings: CliPublishSettings = settings.publish ?? {};
|
|
152
|
+
const resolved = resolvePublishOptions(doc, {
|
|
153
|
+
compressed: options.compressed,
|
|
154
|
+
fileExtension: options.fileExtension,
|
|
155
|
+
packages: options.packages,
|
|
156
|
+
atlas: options.atlas,
|
|
157
|
+
});
|
|
158
|
+
const branchProcessing = publishSettings.branchProcessing ?? 0;
|
|
159
|
+
const includeBranches = branchProcessing === 0;
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
...resolved,
|
|
163
|
+
projectType: doc.getRoot().getProjectType(),
|
|
164
|
+
includeBranches,
|
|
165
|
+
activeBranch: includeBranches ? '' : (options.branch ?? ''),
|
|
166
|
+
includeHighResolution: publishSettings.includeHighResolution ?? 0,
|
|
167
|
+
separatedAtlasForBranch: includeBranches && publishSettings.seperatedAtlasForBranch === true,
|
|
168
|
+
globalOutputPath: publishSettings.path?.trim() ?? '',
|
|
169
|
+
globalBranchOutputPath: publishSettings.branchPath?.trim() ?? '',
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const resolvePackagePublishPlan = (
|
|
174
|
+
pkg: Package,
|
|
175
|
+
config: ResolvedProjectPublishConfig,
|
|
176
|
+
projectBasePath?: string,
|
|
177
|
+
): ResolvedPackagePublishPlan => {
|
|
178
|
+
let outputDir: string | undefined;
|
|
179
|
+
|
|
180
|
+
if (options.output) {
|
|
181
|
+
outputDir = trimTrailingSlashes(options.output);
|
|
182
|
+
} else {
|
|
183
|
+
const candidates: Array<string | undefined> = [];
|
|
184
|
+
if (!config.includeBranches && config.activeBranch) {
|
|
185
|
+
candidates.push(pkg.getPublishBranchPath(), config.globalBranchOutputPath);
|
|
186
|
+
}
|
|
187
|
+
candidates.push(pkg.getPublishPath(), config.globalOutputPath);
|
|
188
|
+
|
|
189
|
+
for (const candidate of candidates) {
|
|
190
|
+
const resolved = resolveConfiguredOutputPath(candidate, projectBasePath);
|
|
191
|
+
if (!resolved) continue;
|
|
192
|
+
outputDir = resolved;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const publishName = pkg.getPublishName() || pkg.getName();
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
pkg,
|
|
200
|
+
outputDir,
|
|
201
|
+
publishName,
|
|
202
|
+
fileName: resolvePublishFileName(publishName, config.fileExtension),
|
|
203
|
+
compressed: config.compressed,
|
|
204
|
+
fileExtension: config.fileExtension,
|
|
205
|
+
includeBranches: config.includeBranches,
|
|
206
|
+
activeBranch: config.activeBranch,
|
|
207
|
+
includeHighResolution: config.includeHighResolution,
|
|
208
|
+
separatedAtlasForBranch: config.separatedAtlasForBranch,
|
|
209
|
+
atlas: config.atlas,
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const createNoopPublishFs = (): PublishFileSystem => ({
|
|
214
|
+
async writeFileRaw(): Promise<void> {
|
|
215
|
+
// No-op for layout-only publish flows.
|
|
216
|
+
},
|
|
217
|
+
async mkdir(): Promise<void> {
|
|
218
|
+
// No-op for layout-only publish flows.
|
|
219
|
+
},
|
|
220
|
+
join(...paths: string[]): string {
|
|
221
|
+
return paths.join('/');
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const publishPackage = async (
|
|
226
|
+
plan: ResolvedPackagePublishPlan,
|
|
227
|
+
writerFs: FileSystem,
|
|
228
|
+
packageIndex: number,
|
|
229
|
+
preparedMovieClips?: ReadonlyMap<MovieClipResource, PreparedJtaData>,
|
|
230
|
+
) => {
|
|
231
|
+
if (options.fs && !plan.outputDir) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
'publish: no output directory resolved. Provide --output, or configure global publish.path / package publishPath.',
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (options.fs) {
|
|
238
|
+
await options.fs.mkdir(plan.outputDir!);
|
|
239
|
+
await exportPackageSounds(
|
|
240
|
+
plan.pkg,
|
|
241
|
+
plan.outputDir!,
|
|
242
|
+
options.basePath,
|
|
243
|
+
options.fs,
|
|
244
|
+
options.atlas?.readFileRaw ?? options.fs.readFileRaw,
|
|
245
|
+
);
|
|
246
|
+
await exportPackageExternalResources(
|
|
247
|
+
plan.pkg,
|
|
248
|
+
plan.outputDir!,
|
|
249
|
+
options.basePath,
|
|
250
|
+
options.fs,
|
|
251
|
+
options.atlas?.readFileRaw ?? options.fs.readFileRaw,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const atlasRuntimeOptions = resolvePublishAtlasRuntimeOptions(plan.fileExtension);
|
|
256
|
+
await atlas({
|
|
257
|
+
...plan.atlas,
|
|
258
|
+
...(options.atlas ?? {}),
|
|
259
|
+
separatedAtlasForBranch: plan.separatedAtlasForBranch,
|
|
260
|
+
encoder: options.encoder,
|
|
261
|
+
basePath: options.basePath,
|
|
262
|
+
outputPath: options.fs ? plan.outputDir : undefined,
|
|
263
|
+
mkdir: options.fs ? options.fs.mkdir : undefined,
|
|
264
|
+
readFileRaw: options.atlas?.readFileRaw ?? options.fs?.readFileRaw,
|
|
265
|
+
strictOutput: options.fs !== undefined,
|
|
266
|
+
preparedMovieClips,
|
|
267
|
+
packages: [plan.pkg.getName()],
|
|
268
|
+
...atlasRuntimeOptions,
|
|
269
|
+
})(doc);
|
|
270
|
+
|
|
271
|
+
if (!options.fs) return;
|
|
272
|
+
|
|
273
|
+
const filePath = options.fs.join(plan.outputDir!, plan.fileName);
|
|
274
|
+
const bwOptions: BinaryWriterOptions = {
|
|
275
|
+
compressed: plan.compressed,
|
|
276
|
+
packageIndex,
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const bw = new BinaryWriter(writerFs);
|
|
280
|
+
await bw.write(doc, filePath, bwOptions);
|
|
281
|
+
|
|
282
|
+
logger.info(`publish: Written ${plan.fileName}`);
|
|
283
|
+
};
|
|
284
|
+
|
|
935
285
|
const root = doc.getRoot();
|
|
936
286
|
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;
|
|
287
|
+
const projectBasePath = resolveProjectBasePath(options.basePath) || doc.getProjectDir?.() || '';
|
|
288
|
+
const plugins = options.plugins ?? [];
|
|
289
|
+
await runPublishPluginHook(plugins, 'onPublishStart', doc, options);
|
|
290
|
+
|
|
291
|
+
const resolved = resolveProjectPublishConfig();
|
|
946
292
|
|
|
947
293
|
// Step 1: Determine which packages to publish
|
|
948
294
|
let allPackages = root.listPackages();
|
|
@@ -953,14 +299,10 @@ export function publish(options: PublishOptions): Transform {
|
|
|
953
299
|
|
|
954
300
|
if (allPackages.length === 0) {
|
|
955
301
|
logger.warn('publish: No packages to publish.');
|
|
302
|
+
await runPublishPluginHook(plugins, 'onPublishEnd', doc, options);
|
|
956
303
|
return;
|
|
957
304
|
}
|
|
958
305
|
|
|
959
|
-
const branchProcessing = publishSettings.branchProcessing ?? 0;
|
|
960
|
-
const includeBranches = branchProcessing === 0;
|
|
961
|
-
const activeBranch = includeBranches ? '' : (options.branch ?? '');
|
|
962
|
-
const atlasRuntimeOptions = resolvePublishAtlasRuntimeOptions(ext);
|
|
963
|
-
|
|
964
306
|
const allDocPackages = root.listPackages();
|
|
965
307
|
// Build a pkgId→name map for dependency resolution
|
|
966
308
|
const pkgMap = new Map<string, Package>();
|
|
@@ -971,82 +313,101 @@ export function publish(options: PublishOptions): Transform {
|
|
|
971
313
|
for (const pkg of allPackages) {
|
|
972
314
|
// Compute dependency list and selected publish artifacts before atlas packing,
|
|
973
315
|
// 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
|
-
);
|
|
316
|
+
_computeDependencies(doc, pkg, pkgMap);
|
|
317
|
+
await annotatePackagePublishArtifacts(pkg, options.basePath, options.encoder, {
|
|
318
|
+
projectType: resolved.projectType,
|
|
319
|
+
includeBranches: resolved.includeBranches,
|
|
320
|
+
activeBranch: resolved.activeBranch,
|
|
321
|
+
includeHighResolution: resolved.includeHighResolution,
|
|
322
|
+
});
|
|
984
323
|
}
|
|
985
324
|
|
|
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);
|
|
325
|
+
const plans = allPackages.map((pkg) => resolvePackagePublishPlan(pkg, resolved, projectBasePath));
|
|
999
326
|
|
|
1000
|
-
// Step 3: Write .fui binary per package
|
|
1001
327
|
if (!options.fs) {
|
|
1002
|
-
|
|
328
|
+
const outputPlan = plans.find((plan) => !!plan.outputDir);
|
|
329
|
+
if (outputPlan) {
|
|
330
|
+
throw new Error(
|
|
331
|
+
`publish: Output for package "${outputPlan.pkg.getName()}" requires a filesystem. ` +
|
|
332
|
+
'Omit output and publish paths to run a layout-only transform.',
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
logger.info(
|
|
336
|
+
`publish: Layout computed for ${allPackages.length} package(s); no output directory was requested.`,
|
|
337
|
+
);
|
|
338
|
+
const noopWriterFs = toBinaryWriterFileSystem(createNoopPublishFs());
|
|
339
|
+
for (const plan of plans) {
|
|
340
|
+
await publishPackage(plan, noopWriterFs, allDocPackages.indexOf(plan.pkg));
|
|
341
|
+
}
|
|
342
|
+
await runPublishPluginHook(plugins, 'onPublishEnd', doc, options);
|
|
1003
343
|
return;
|
|
1004
344
|
}
|
|
1005
345
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
346
|
+
const unresolvedPlan = plans.find((plan) => !plan.outputDir);
|
|
347
|
+
if (unresolvedPlan) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
`publish: no output directory resolved for package "${unresolvedPlan.pkg.getName()}". ` +
|
|
350
|
+
'Provide --output, or configure global publish.path / package publishPath.',
|
|
351
|
+
);
|
|
352
|
+
}
|
|
1009
353
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
const
|
|
1014
|
-
|
|
354
|
+
// publishPackage starts with mkdir and loose-resource writes. Preflight the complete
|
|
355
|
+
// selected MovieClip set first so a failure in a later package leaves zero output.
|
|
356
|
+
const publishedMovieClips = allPackages.flatMap((pkg) => {
|
|
357
|
+
const publishedResourceIds = getAnnotatedPublishedResourceIds(pkg);
|
|
358
|
+
return pkg
|
|
359
|
+
.listResources()
|
|
360
|
+
.filter((resource): resource is MovieClipResource => {
|
|
361
|
+
return publishedResourceIds.has(resource.getId()) && isMovieClipResource(resource);
|
|
362
|
+
})
|
|
363
|
+
.map((resource) => ({ pkg, resource }));
|
|
364
|
+
});
|
|
365
|
+
const preparedMovieClips = new Map<MovieClipResource, PreparedJtaData>();
|
|
366
|
+
if (publishedMovieClips.length > 0) {
|
|
367
|
+
if (!options.encoder) {
|
|
368
|
+
throw new Error('publish: MovieClip output requires an encoder.');
|
|
369
|
+
}
|
|
370
|
+
if (!options.basePath) {
|
|
371
|
+
throw new Error('publish: MovieClip output requires basePath.');
|
|
372
|
+
}
|
|
373
|
+
const readFileRaw = options.atlas?.readFileRaw ?? options.fs.readFileRaw;
|
|
374
|
+
if (!readFileRaw) {
|
|
375
|
+
throw new Error('publish: MovieClip output requires readFileRaw.');
|
|
376
|
+
}
|
|
1015
377
|
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
378
|
+
for (const { pkg, resource } of publishedMovieClips) {
|
|
379
|
+
preparedMovieClips.set(
|
|
380
|
+
resource,
|
|
381
|
+
await prepareMovieClipResource(resource, pkg, options.encoder, options.basePath, readFileRaw),
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
1020
385
|
|
|
1021
|
-
|
|
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
|
-
);
|
|
386
|
+
const writerFs = toBinaryWriterFileSystem(options.fs);
|
|
1039
387
|
|
|
1040
|
-
|
|
388
|
+
for (const plan of plans) {
|
|
389
|
+
const pkgIndex = allDocPackages.indexOf(plan.pkg);
|
|
390
|
+
await publishPackage(plan, writerFs, pkgIndex, preparedMovieClips);
|
|
1041
391
|
}
|
|
1042
392
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
393
|
+
if (options.codeGeneration !== false) {
|
|
394
|
+
await publishCodeGeneration(doc, {
|
|
395
|
+
basePath: options.basePath,
|
|
396
|
+
fs: options.fs,
|
|
397
|
+
packages: allPackages,
|
|
398
|
+
plugins,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
1048
401
|
|
|
1049
|
-
|
|
402
|
+
const publishedTargets = [
|
|
403
|
+
...new Set(plans.map((plan) => plan.outputDir).filter((value): value is string => Boolean(value))),
|
|
404
|
+
];
|
|
405
|
+
logger.info(
|
|
406
|
+
publishedTargets.length > 0
|
|
407
|
+
? `publish: Published ${allPackages.length} package(s) to ${publishedTargets.join(', ')}`
|
|
408
|
+
: `publish: Published ${allPackages.length} package(s)`,
|
|
409
|
+
);
|
|
410
|
+
await runPublishPluginHook(plugins, 'onPublishEnd', doc, options);
|
|
1050
411
|
});
|
|
1051
412
|
}
|
|
1052
413
|
|
|
@@ -1055,34 +416,25 @@ export function publish(options: PublishOptions): Transform {
|
|
|
1055
416
|
* The editor only adds dependencies for packages referenced via bitmap font URLs.
|
|
1056
417
|
* @internal
|
|
1057
418
|
*/
|
|
1058
|
-
function _computeDependencies(pkg: Package, pkgMap: Map<string, Package>): void {
|
|
1059
|
-
const referencedPkgIds =
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
if (rest.length >= 8) {
|
|
1067
|
-
const depPkgId = rest.slice(0, 8);
|
|
1068
|
-
if (depPkgId !== pkg.getId()) referencedPkgIds.add(depPkgId);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
for (const res of pkg.listResources()) {
|
|
1073
|
-
if (res.propertyType !== 'Component') continue;
|
|
1074
|
-
for (const child of res.listChildren?.() ?? []) {
|
|
1075
|
-
// Only font="ui://..." references generate dependencies
|
|
1076
|
-
scanFontUrl((child as HasOptionalFont).getFont?.());
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
|
|
419
|
+
function _computeDependencies(doc: Document, pkg: Package, pkgMap: Map<string, Package>): void {
|
|
420
|
+
const referencedPkgIds = collectPackageResourceReferences(pkg).packageIds;
|
|
421
|
+
const packageOrder = new Map(
|
|
422
|
+
doc
|
|
423
|
+
.getRoot()
|
|
424
|
+
.listPackages()
|
|
425
|
+
.map((entry, index) => [entry.getId(), index] as const),
|
|
426
|
+
);
|
|
1080
427
|
for (const dep of pkg.listDependencies()) {
|
|
1081
428
|
pkg.removeDependency(dep);
|
|
1082
429
|
}
|
|
1083
430
|
|
|
1084
431
|
if (referencedPkgIds.size > 0) {
|
|
1085
|
-
const sortedIds = [...referencedPkgIds].sort((a, b) =>
|
|
432
|
+
const sortedIds = [...referencedPkgIds].sort((a, b) => {
|
|
433
|
+
const orderA = packageOrder.get(a) ?? Number.MAX_SAFE_INTEGER;
|
|
434
|
+
const orderB = packageOrder.get(b) ?? Number.MAX_SAFE_INTEGER;
|
|
435
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
436
|
+
return a.localeCompare(b);
|
|
437
|
+
});
|
|
1086
438
|
for (const refId of sortedIds) {
|
|
1087
439
|
const depPkg = pkgMap.get(refId);
|
|
1088
440
|
if (depPkg) {
|