@openfairygui/functions 0.2.0-alpha.30 → 0.2.0-alpha.32
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/dist/index.cjs +2 -2
- package/dist/index.js +2 -2
- package/dist/node.cjs +2 -2
- package/dist/node.js +2 -2
- package/dist/{publish-BJ_eelME.js → publish-BJk8UzRP.js} +2546 -2567
- package/dist/{publish-xFWT9Slz.cjs → publish-DCP0AYx2.cjs} +2563 -2566
- package/dist/{restore-BW2xacB3.cjs → restore-BUaCK8ui.cjs} +13 -38
- package/dist/{restore-Clk62n0O.js → restore-C98ugT2H.js} +1 -26
- package/dist/web.cjs +1 -1
- package/dist/web.js +1 -1
- package/package.json +2 -2
- package/src/atlas/inputs.ts +7 -42
- package/src/atlas/packing.ts +5 -10
- package/src/atlas.ts +7 -5
- package/src/codegen.ts +2 -15
- package/src/path-utils.ts +40 -0
- package/src/publish/package-context.ts +3 -3
- package/src/publish.ts +1 -14
- package/src/restore-internals/output-transaction.ts +2 -42
|
@@ -1,43 +1,18 @@
|
|
|
1
|
+
const require_publish = require("./publish-DCP0AYx2.cjs");
|
|
1
2
|
let _openfairygui_core = require("@openfairygui/core");
|
|
2
3
|
//#region src/restore-internals/output-transaction.ts
|
|
3
|
-
function trimTrailingSlashes(value) {
|
|
4
|
-
return value.replace(/[/\\]+$/, "");
|
|
5
|
-
}
|
|
6
|
-
function normalizeComparablePath(value) {
|
|
7
|
-
const normalized = trimTrailingSlashes(value).replace(/\\/g, "/");
|
|
8
|
-
const driveMatch = normalized.match(/^([a-z]:)(?:\/(.*))?$/i);
|
|
9
|
-
const drivePrefix = driveMatch?.[1].toLowerCase() ?? "";
|
|
10
|
-
const remainder = driveMatch ? driveMatch[2] ?? "" : normalized;
|
|
11
|
-
const hasRoot = driveMatch ? true : remainder.startsWith("/");
|
|
12
|
-
const rawSegments = remainder.split("/").filter((segment) => segment.length > 0);
|
|
13
|
-
const segments = [];
|
|
14
|
-
for (const segment of rawSegments) {
|
|
15
|
-
if (segment === ".") continue;
|
|
16
|
-
if (segment === "..") {
|
|
17
|
-
if (segments.length > 0 && segments[segments.length - 1] !== "..") segments.pop();
|
|
18
|
-
else if (!hasRoot) segments.push("..");
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
segments.push(segment);
|
|
22
|
-
}
|
|
23
|
-
const joined = segments.join("/");
|
|
24
|
-
return (drivePrefix ? `${drivePrefix}/${joined}`.replace(/\/$/, "") : hasRoot ? `/${joined}`.replace(/\/$/, "") : joined || ".").toLowerCase();
|
|
25
|
-
}
|
|
26
4
|
function isPathWithin(root, candidate) {
|
|
27
|
-
const normalizedRoot = normalizeComparablePath(root);
|
|
28
|
-
return normalizeComparablePath(candidate).startsWith(`${normalizedRoot}/`);
|
|
29
|
-
}
|
|
30
|
-
function basename(filePath) {
|
|
31
|
-
return trimTrailingSlashes(filePath).match(/([^/\\]+)$/)?.[1] ?? "";
|
|
5
|
+
const normalizedRoot = require_publish.normalizeComparablePath(root);
|
|
6
|
+
return require_publish.normalizeComparablePath(candidate).startsWith(`${normalizedRoot}/`);
|
|
32
7
|
}
|
|
33
8
|
function normalizeRestoreOutputDir(output) {
|
|
34
|
-
const normalized = trimTrailingSlashes(output);
|
|
35
|
-
const name = basename(normalized);
|
|
9
|
+
const normalized = require_publish.trimTrailingSlashes(output);
|
|
10
|
+
const name = require_publish.basename(normalized);
|
|
36
11
|
if (!normalized || /\.fairy$/i.test(normalized) || !name || name === "." || name === ".." || /^[a-z]:$/iu.test(name)) throw new Error("restore: Output must be a non-root project directory, not a .fairy file.");
|
|
37
12
|
return normalized;
|
|
38
13
|
}
|
|
39
14
|
function resolveOutputProjectPath(outputDir, fs) {
|
|
40
|
-
return fs.join(outputDir, `${basename(outputDir)}.fairy`);
|
|
15
|
+
return fs.join(outputDir, `${require_publish.basename(outputDir)}.fairy`);
|
|
41
16
|
}
|
|
42
17
|
async function resolvePathForContainment(filePath, fs) {
|
|
43
18
|
const missingSegments = [];
|
|
@@ -45,7 +20,7 @@ async function resolvePathForContainment(filePath, fs) {
|
|
|
45
20
|
while (!await fs.exists(existingPath)) {
|
|
46
21
|
const parentPath = fs.dirname(existingPath);
|
|
47
22
|
if (!parentPath || parentPath === existingPath) return Promise.resolve(fs.resolvePath(filePath));
|
|
48
|
-
missingSegments.unshift(basename(existingPath));
|
|
23
|
+
missingSegments.unshift(require_publish.basename(existingPath));
|
|
49
24
|
existingPath = parentPath;
|
|
50
25
|
}
|
|
51
26
|
const resolvedExistingPath = await Promise.resolve(fs.resolvePath(existingPath));
|
|
@@ -53,8 +28,8 @@ async function resolvePathForContainment(filePath, fs) {
|
|
|
53
28
|
}
|
|
54
29
|
async function assertRestoreOutputDir(inputDir, outputDir, fs, force) {
|
|
55
30
|
const [resolvedInputDir, resolvedOutputDir] = await Promise.all([resolvePathForContainment(inputDir, fs), resolvePathForContainment(outputDir, fs)]);
|
|
56
|
-
const normalizedInputDir = normalizeComparablePath(resolvedInputDir);
|
|
57
|
-
const normalizedOutputDir = normalizeComparablePath(resolvedOutputDir);
|
|
31
|
+
const normalizedInputDir = require_publish.normalizeComparablePath(resolvedInputDir);
|
|
32
|
+
const normalizedOutputDir = require_publish.normalizeComparablePath(resolvedOutputDir);
|
|
58
33
|
if (normalizedInputDir === normalizedOutputDir || isPathWithin(normalizedInputDir, normalizedOutputDir) || isPathWithin(normalizedOutputDir, normalizedInputDir)) throw new Error("Restore output directory must be independent from the published input directory.");
|
|
59
34
|
if (!await fs.exists(outputDir)) return;
|
|
60
35
|
let entries;
|
|
@@ -70,7 +45,7 @@ async function createRestoreStagingDir(outputDir, fs) {
|
|
|
70
45
|
const parentDir = fs.dirname(outputDir) || ".";
|
|
71
46
|
await fs.mkdir(parentDir);
|
|
72
47
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
73
|
-
const stagingDir = fs.join(parentDir, `.${basename(outputDir)}.restore-${(0, _openfairygui_core.generateId)()}`);
|
|
48
|
+
const stagingDir = fs.join(parentDir, `.${require_publish.basename(outputDir)}.restore-${(0, _openfairygui_core.generateId)()}`);
|
|
74
49
|
if (await fs.exists(stagingDir)) continue;
|
|
75
50
|
await fs.mkdir(stagingDir);
|
|
76
51
|
return stagingDir;
|
|
@@ -85,7 +60,7 @@ async function commitRestoreOutput(stagingDir, outputDir, fs) {
|
|
|
85
60
|
const parentDir = fs.dirname(outputDir) || ".";
|
|
86
61
|
let backupDir = "";
|
|
87
62
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
88
|
-
const candidate = fs.join(parentDir, `.${basename(outputDir)}.restore-backup-${(0, _openfairygui_core.generateId)()}`);
|
|
63
|
+
const candidate = fs.join(parentDir, `.${require_publish.basename(outputDir)}.restore-backup-${(0, _openfairygui_core.generateId)()}`);
|
|
89
64
|
if (!await fs.exists(candidate)) {
|
|
90
65
|
backupDir = candidate;
|
|
91
66
|
break;
|
|
@@ -413,7 +388,7 @@ function inferPackageName(fileName) {
|
|
|
413
388
|
return fileName.replace(/\.bin$/i, "");
|
|
414
389
|
}
|
|
415
390
|
async function restore(options) {
|
|
416
|
-
const sourceDir = trimTrailingSlashes(options.inputDir);
|
|
391
|
+
const sourceDir = require_publish.trimTrailingSlashes(options.inputDir);
|
|
417
392
|
const outputDir = normalizeRestoreOutputDir(options.output);
|
|
418
393
|
const outputProjectPath = resolveOutputProjectPath(outputDir, options.fs);
|
|
419
394
|
await assertRestoreOutputDir(sourceDir, outputDir, options.fs, options.force === true);
|
|
@@ -433,7 +408,7 @@ async function restore(options) {
|
|
|
433
408
|
extractImage: options.extractImage
|
|
434
409
|
});
|
|
435
410
|
const stagingDir = await createRestoreStagingDir(outputDir, options.fs);
|
|
436
|
-
const stagingProjectPath = options.fs.join(stagingDir, basename(outputProjectPath));
|
|
411
|
+
const stagingProjectPath = options.fs.join(stagingDir, require_publish.basename(outputProjectPath));
|
|
437
412
|
const warnings = [];
|
|
438
413
|
try {
|
|
439
414
|
await restorer.write(document, {
|
|
@@ -1,35 +1,10 @@
|
|
|
1
|
+
import { d as normalizeComparablePath, f as trimTrailingSlashes, u as basename } from "./publish-BJk8UzRP.js";
|
|
1
2
|
import { BinaryReader, ProjectType, ProjectWriter, generateId } from "@openfairygui/core";
|
|
2
3
|
//#region src/restore-internals/output-transaction.ts
|
|
3
|
-
function trimTrailingSlashes(value) {
|
|
4
|
-
return value.replace(/[/\\]+$/, "");
|
|
5
|
-
}
|
|
6
|
-
function normalizeComparablePath(value) {
|
|
7
|
-
const normalized = trimTrailingSlashes(value).replace(/\\/g, "/");
|
|
8
|
-
const driveMatch = normalized.match(/^([a-z]:)(?:\/(.*))?$/i);
|
|
9
|
-
const drivePrefix = driveMatch?.[1].toLowerCase() ?? "";
|
|
10
|
-
const remainder = driveMatch ? driveMatch[2] ?? "" : normalized;
|
|
11
|
-
const hasRoot = driveMatch ? true : remainder.startsWith("/");
|
|
12
|
-
const rawSegments = remainder.split("/").filter((segment) => segment.length > 0);
|
|
13
|
-
const segments = [];
|
|
14
|
-
for (const segment of rawSegments) {
|
|
15
|
-
if (segment === ".") continue;
|
|
16
|
-
if (segment === "..") {
|
|
17
|
-
if (segments.length > 0 && segments[segments.length - 1] !== "..") segments.pop();
|
|
18
|
-
else if (!hasRoot) segments.push("..");
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
segments.push(segment);
|
|
22
|
-
}
|
|
23
|
-
const joined = segments.join("/");
|
|
24
|
-
return (drivePrefix ? `${drivePrefix}/${joined}`.replace(/\/$/, "") : hasRoot ? `/${joined}`.replace(/\/$/, "") : joined || ".").toLowerCase();
|
|
25
|
-
}
|
|
26
4
|
function isPathWithin(root, candidate) {
|
|
27
5
|
const normalizedRoot = normalizeComparablePath(root);
|
|
28
6
|
return normalizeComparablePath(candidate).startsWith(`${normalizedRoot}/`);
|
|
29
7
|
}
|
|
30
|
-
function basename(filePath) {
|
|
31
|
-
return trimTrailingSlashes(filePath).match(/([^/\\]+)$/)?.[1] ?? "";
|
|
32
|
-
}
|
|
33
8
|
function normalizeRestoreOutputDir(output) {
|
|
34
9
|
const normalized = trimTrailingSlashes(output);
|
|
35
10
|
const name = basename(normalized);
|
package/dist/web.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_publish = require("./publish-
|
|
2
|
+
const require_publish = require("./publish-DCP0AYx2.cjs");
|
|
3
3
|
let _openfairygui_core = require("@openfairygui/core");
|
|
4
4
|
//#region src/adapters/web/raster.ts
|
|
5
5
|
function getBrowserContext(canvas) {
|
package/dist/web.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfairygui/functions",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.32",
|
|
4
4
|
"description": "FairyGUI Headless Authoring SDK — composable transform functions.",
|
|
5
5
|
"author": "OpenFairyGUI Contributors",
|
|
6
6
|
"license": "MIT",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
],
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"jiti": "^2.6.1",
|
|
77
|
-
"@openfairygui/core": "0.2.0-alpha.
|
|
77
|
+
"@openfairygui/core": "0.2.0-alpha.32"
|
|
78
78
|
},
|
|
79
79
|
"optionalDependencies": {
|
|
80
80
|
"sharp": ">=0.33.0"
|
package/src/atlas/inputs.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
Component,
|
|
3
2
|
Document,
|
|
4
|
-
DragonBonesResource,
|
|
5
3
|
FontResource,
|
|
6
4
|
ILogger,
|
|
7
5
|
ImageResource,
|
|
8
6
|
MovieClipResource,
|
|
9
7
|
Package,
|
|
10
|
-
SpineResource,
|
|
11
8
|
} from '@openfairygui/core';
|
|
12
9
|
import type { AtlasOptions } from '../atlas.js';
|
|
13
10
|
import type {
|
|
@@ -15,6 +12,13 @@ import type {
|
|
|
15
12
|
AtlasRasterInput,
|
|
16
13
|
AtlasRasterResolvedBuffer,
|
|
17
14
|
} from '../publish/contracts.js';
|
|
15
|
+
import {
|
|
16
|
+
isFontResource,
|
|
17
|
+
isImageResource,
|
|
18
|
+
isMovieClipResource,
|
|
19
|
+
resolveImageFileName,
|
|
20
|
+
resolveImagePath,
|
|
21
|
+
} from '../publish/package-context.js';
|
|
18
22
|
import type { ExtrasMap } from '../shared-types.js';
|
|
19
23
|
import { parseFnt } from './font.js';
|
|
20
24
|
import { extractJtaFrames } from './jta.js';
|
|
@@ -46,7 +50,6 @@ export function getPublishedItemId(resource: { getId(): string; getExtras(): Ext
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
interface ImageResourceExtras extends ExtrasMap {
|
|
49
|
-
_fileName?: string;
|
|
50
53
|
_publishedId?: string;
|
|
51
54
|
}
|
|
52
55
|
|
|
@@ -63,11 +66,6 @@ export function resolveFontFileName(fontName: string): string {
|
|
|
63
66
|
return /\.fnt$/i.test(fontName) ? fontName : `${fontName}.fnt`;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
export function resolveImageFileName(resource: ImageResource): string {
|
|
67
|
-
const extras = resource.getExtras() as ImageResourceExtras;
|
|
68
|
-
return resource.getFileName() || extras._fileName || resource.getName();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
69
|
/**
|
|
72
70
|
* Trim transparent edges from an image using the host raster backend.
|
|
73
71
|
* Returns the trimmed buffer, dimensions, and offsets.
|
|
@@ -154,19 +152,6 @@ async function _trimImage(
|
|
|
154
152
|
/**
|
|
155
153
|
* Resolve an ImageResource to its actual file path on disk.
|
|
156
154
|
*/
|
|
157
|
-
export function resolveImagePath(resource: ImageResource, pkg: Package, basePath: string): string {
|
|
158
|
-
const imgPath = resource.getPath() ?? '/';
|
|
159
|
-
const fileName = resolveImageFileName(resource);
|
|
160
|
-
const branchName = resource.getBranch?.() ?? '';
|
|
161
|
-
const normalizedBasePath = basePath.replace(/[/\\]+$/, '');
|
|
162
|
-
const packageBasePath = !branchName
|
|
163
|
-
? normalizedBasePath
|
|
164
|
-
: /[\\/]assets$/i.test(normalizedBasePath)
|
|
165
|
-
? normalizedBasePath.replace(/([\\/])assets$/i, `$1assets_${branchName}`)
|
|
166
|
-
: `${normalizedBasePath}_${branchName}`;
|
|
167
|
-
return `${packageBasePath}/${pkg.getName()}${imgPath}${fileName}`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
155
|
export type InputItem = {
|
|
171
156
|
id: string;
|
|
172
157
|
width: number;
|
|
@@ -486,26 +471,6 @@ export async function collectFontTexture(
|
|
|
486
471
|
}
|
|
487
472
|
}
|
|
488
473
|
|
|
489
|
-
export function isComponentResource(resource: PackageResource): resource is Component {
|
|
490
|
-
return resource.propertyType === 'Component';
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
export function isImageResource(resource: PackageResource): resource is ImageResource {
|
|
494
|
-
return resource.propertyType === 'ImageResource';
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
export function isMovieClipResource(resource: PackageResource): resource is MovieClipResource {
|
|
498
|
-
return resource.propertyType === 'MovieClipResource';
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
export function isSkeletonResource(resource: PackageResource): resource is SpineResource | DragonBonesResource {
|
|
502
|
-
return resource.propertyType === 'SpineResource' || resource.propertyType === 'DragonBonesResource';
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
export function isFontResource(resource: PackageResource): resource is FontResource {
|
|
506
|
-
return resource.propertyType === 'FontResource';
|
|
507
|
-
}
|
|
508
|
-
|
|
509
474
|
export function isPackableResource(resource: PackageResource): resource is PackableResource {
|
|
510
475
|
return isImageResource(resource) || isMovieClipResource(resource) || isFontResource(resource);
|
|
511
476
|
}
|
package/src/atlas/packing.ts
CHANGED
|
@@ -3,13 +3,16 @@ import type { AtlasOptions } from '../atlas.js';
|
|
|
3
3
|
import { COMPAT_NODE_RECT_FLAGS, type CompatNodeRect } from '../max-rects-compat.js';
|
|
4
4
|
import { MaxRectsPackerCompat } from '../max-rects-packer-compat.js';
|
|
5
5
|
import type { AtlasRasterBackend } from '../publish/contracts.js';
|
|
6
|
-
import { parseTextureSetMode, type TextureSetMode } from '../utils.js';
|
|
7
6
|
import {
|
|
8
|
-
|
|
7
|
+
extname,
|
|
9
8
|
isFontResource,
|
|
10
9
|
isImageResource,
|
|
11
10
|
resolveImageFileName,
|
|
12
11
|
resolveImagePath,
|
|
12
|
+
} from '../publish/package-context.js';
|
|
13
|
+
import { parseTextureSetMode, type TextureSetMode } from '../utils.js';
|
|
14
|
+
import {
|
|
15
|
+
getPublishedItemId,
|
|
13
16
|
type FontResourceExtras,
|
|
14
17
|
type InputItem,
|
|
15
18
|
type PackageResource,
|
|
@@ -612,14 +615,6 @@ function resolveStandaloneAtlasSize(
|
|
|
612
615
|
return resolveDirectOutputAtlasSize(width, height, options);
|
|
613
616
|
}
|
|
614
617
|
|
|
615
|
-
function extname(fileName: string): string {
|
|
616
|
-
const normalized = fileName.replace(/\\/g, '/');
|
|
617
|
-
const lastSlash = normalized.lastIndexOf('/');
|
|
618
|
-
const lastDot = normalized.lastIndexOf('.');
|
|
619
|
-
if (lastDot <= lastSlash) return '';
|
|
620
|
-
return normalized.slice(lastDot);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
618
|
function insertFileNameSuffix(fileName: string, suffix: string): string {
|
|
624
619
|
const extension = extname(fileName);
|
|
625
620
|
if (!extension) return `${fileName}${suffix}`;
|
package/src/atlas.ts
CHANGED
|
@@ -7,6 +7,13 @@ import {
|
|
|
7
7
|
TransitionActionType,
|
|
8
8
|
} from '@openfairygui/core';
|
|
9
9
|
import type { AtlasRasterBackend } from './publish/contracts.js';
|
|
10
|
+
import {
|
|
11
|
+
isComponentResource,
|
|
12
|
+
isFontResource,
|
|
13
|
+
isImageResource,
|
|
14
|
+
isMovieClipResource,
|
|
15
|
+
isSkeletonResource,
|
|
16
|
+
} from './publish/package-context.js';
|
|
10
17
|
import { collectPackageResourceReferences } from './publish/resource-references.js';
|
|
11
18
|
import type { ExtrasMap, HasOptionalSrc, HasOptionalUrl } from './shared-types.js';
|
|
12
19
|
import { createTransform } from './utils.js';
|
|
@@ -14,12 +21,7 @@ import {
|
|
|
14
21
|
collectFontTexture,
|
|
15
22
|
collectImage,
|
|
16
23
|
collectMovieClipFrames,
|
|
17
|
-
isComponentResource,
|
|
18
|
-
isFontResource,
|
|
19
|
-
isImageResource,
|
|
20
|
-
isMovieClipResource,
|
|
21
24
|
isPackableResource,
|
|
22
|
-
isSkeletonResource,
|
|
23
25
|
resolveFontFileName,
|
|
24
26
|
type InputItem,
|
|
25
27
|
type PackageResource,
|
package/src/codegen.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
UNITY_COMPONENT_TEMPLATE,
|
|
14
14
|
} from './codegen-templates.js';
|
|
15
15
|
import { formatPluginError, type LoadedPlugin } from './plugins/types.js';
|
|
16
|
+
import { dirname, isAbsolutePathLike, trimTrailingSlashes } from './path-utils.js';
|
|
16
17
|
import type { PublishFileSystem } from './publish/contracts.js';
|
|
17
18
|
import type { CliCodeGenerationSettings, RootProjectSettings } from './shared-types.js';
|
|
18
19
|
|
|
@@ -583,7 +584,7 @@ function resolveCodePath(
|
|
|
583
584
|
basePath: string | undefined,
|
|
584
585
|
fs: Pick<PublishFileSystem, 'join'>,
|
|
585
586
|
): string {
|
|
586
|
-
if (
|
|
587
|
+
if (isAbsolutePathLike(codePath)) return trimTrailingSlashes(codePath);
|
|
587
588
|
const projectBasePath = resolveProjectBasePath(basePath);
|
|
588
589
|
return projectBasePath ? trimTrailingSlashes(fs.join(projectBasePath, codePath)) : trimTrailingSlashes(codePath);
|
|
589
590
|
}
|
|
@@ -596,20 +597,6 @@ export function resolveProjectBasePath(basePath: string | undefined): string {
|
|
|
596
597
|
return dirname(normalized);
|
|
597
598
|
}
|
|
598
599
|
|
|
599
|
-
function dirname(filePath: string): string {
|
|
600
|
-
const trimmed = trimTrailingSlashes(filePath);
|
|
601
|
-
const match = trimmed.match(/^(.*)[/\\][^/\\]+$/);
|
|
602
|
-
return match?.[1] ?? '';
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
function trimTrailingSlashes(value: string): string {
|
|
606
|
-
return value.replace(/[/\\]+$/, '');
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
function isAbsolutePath(value: string): boolean {
|
|
610
|
-
return /^[a-z]:[/\\]/i.test(value) || value.startsWith('/') || value.startsWith('\\\\');
|
|
611
|
-
}
|
|
612
|
-
|
|
613
600
|
function isDefaultMemberName(ownerType: string, kind: CodegenMember['kind'], name: string): boolean {
|
|
614
601
|
if (kind === 'controller') {
|
|
615
602
|
return (ownerType === 'GButton' || ownerType === 'GComboBox') && name === 'button';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function trimTrailingSlashes(value: string): string {
|
|
2
|
+
return value.replace(/[/\\]+$/, '');
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function dirname(filePath: string): string {
|
|
6
|
+
const match = trimTrailingSlashes(filePath).match(/^(.*)[/\\][^/\\]+$/);
|
|
7
|
+
return match?.[1] ?? '';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function basename(filePath: string): string {
|
|
11
|
+
const match = trimTrailingSlashes(filePath).match(/([^/\\]+)$/);
|
|
12
|
+
return match?.[1] ?? '';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isAbsolutePathLike(value: string): boolean {
|
|
16
|
+
return /^(?:[a-zA-Z]:[/\\]|[/\\]{1,2})/u.test(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function normalizeComparablePath(value: string): string {
|
|
20
|
+
const normalized = trimTrailingSlashes(value).replace(/\\/g, '/');
|
|
21
|
+
const driveMatch = normalized.match(/^([a-z]:)(?:\/(.*))?$/i);
|
|
22
|
+
const drivePrefix = driveMatch?.[1].toLowerCase() ?? '';
|
|
23
|
+
const remainder = driveMatch ? (driveMatch[2] ?? '') : normalized;
|
|
24
|
+
const hasRoot = driveMatch ? true : remainder.startsWith('/');
|
|
25
|
+
const segments: string[] = [];
|
|
26
|
+
|
|
27
|
+
for (const segment of remainder.split('/').filter(Boolean)) {
|
|
28
|
+
if (segment === '.') continue;
|
|
29
|
+
if (segment === '..') {
|
|
30
|
+
if (segments.length > 0 && segments.at(-1) !== '..') segments.pop();
|
|
31
|
+
else if (!hasRoot) segments.push('..');
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
segments.push(segment);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const joined = segments.join('/');
|
|
38
|
+
const comparable = drivePrefix ? `${drivePrefix}/${joined}` : hasRoot ? `/${joined}` : joined || '.';
|
|
39
|
+
return comparable.replace(/\/$/, '').toLowerCase();
|
|
40
|
+
}
|
|
@@ -39,7 +39,7 @@ interface PackagePublishContext {
|
|
|
39
39
|
|
|
40
40
|
const UNITY_PROJECT_TYPE = ProjectType.Unity;
|
|
41
41
|
|
|
42
|
-
function isComponentResource(resource: ReturnType<Package['listResources']>[number]): resource is Component {
|
|
42
|
+
export function isComponentResource(resource: ReturnType<Package['listResources']>[number]): resource is Component {
|
|
43
43
|
return resource.propertyType === 'Component';
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -47,7 +47,7 @@ export function isImageResource(resource: ReturnType<Package['listResources']>[n
|
|
|
47
47
|
return resource.propertyType === 'ImageResource';
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function isMovieClipResource(resource: ReturnType<Package['listResources']>[number]): resource is MovieClipResource {
|
|
50
|
+
export function isMovieClipResource(resource: ReturnType<Package['listResources']>[number]): resource is MovieClipResource {
|
|
51
51
|
return resource.propertyType === 'MovieClipResource';
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -61,7 +61,7 @@ export function isMiscResource(resource: ReturnType<Package['listResources']>[nu
|
|
|
61
61
|
return resource.propertyType === 'MiscResource';
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function isFontResource(resource: ReturnType<Package['listResources']>[number]): resource is FontResource {
|
|
64
|
+
export function isFontResource(resource: ReturnType<Package['listResources']>[number]): resource is FontResource {
|
|
65
65
|
return resource.propertyType === 'FontResource';
|
|
66
66
|
}
|
|
67
67
|
|
package/src/publish.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '@openfairygui/core';
|
|
9
9
|
import { atlas } from './atlas.js';
|
|
10
10
|
import { publishCodeGeneration, resolveProjectBasePath } from './codegen.js';
|
|
11
|
+
import { dirname, isAbsolutePathLike, trimTrailingSlashes } from './path-utils.js';
|
|
11
12
|
import { formatPluginError, type LoadedPlugin } from './plugins/types.js';
|
|
12
13
|
import type { PublishFileSystem } from './publish/contracts.js';
|
|
13
14
|
import {
|
|
@@ -73,14 +74,6 @@ async function runPublishPluginHook(
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
function trimTrailingSlashes(value: string): string {
|
|
77
|
-
return value.replace(/[/\\]+$/, '');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function isAbsolutePathLike(value: string): boolean {
|
|
81
|
-
return /^(?:[a-zA-Z]:[/\\]|[/\\]{1,2})/u.test(value);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
77
|
function joinPathSegments(left: string, right: string): string {
|
|
85
78
|
const normalizedLeft = trimTrailingSlashes(left);
|
|
86
79
|
const normalizedRight = right.replace(/^[/\\]+/, '');
|
|
@@ -90,12 +83,6 @@ function joinPathSegments(left: string, right: string): string {
|
|
|
90
83
|
return `${normalizedLeft}${separator}${normalizedRight}`;
|
|
91
84
|
}
|
|
92
85
|
|
|
93
|
-
function dirname(filePath: string): string {
|
|
94
|
-
const trimmed = filePath.replace(/[/\\]+$/, '');
|
|
95
|
-
const match = trimmed.match(/^(.*)[/\\][^/\\]+$/);
|
|
96
|
-
return match?.[1] ?? '';
|
|
97
|
-
}
|
|
98
|
-
|
|
99
86
|
function createUnsupportedFsOperation(name: keyof FileSystem) {
|
|
100
87
|
return async (): Promise<never> => {
|
|
101
88
|
throw new Error(`publish: FileSystem.${name}() is not available in the publish writer adapter.`);
|
|
@@ -1,42 +1,8 @@
|
|
|
1
1
|
import { generateId } from '@openfairygui/core';
|
|
2
|
+
import { basename, normalizeComparablePath, trimTrailingSlashes } from '../path-utils.js';
|
|
2
3
|
import type { RestoreFileSystem } from '../restore.js';
|
|
3
4
|
|
|
4
|
-
export
|
|
5
|
-
return value.replace(/[/\\]+$/, '');
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function normalizeComparablePath(value: string): string {
|
|
9
|
-
const normalized = trimTrailingSlashes(value).replace(/\\/g, '/');
|
|
10
|
-
const driveMatch = normalized.match(/^([a-z]:)(?:\/(.*))?$/i);
|
|
11
|
-
const drivePrefix = driveMatch?.[1].toLowerCase() ?? '';
|
|
12
|
-
const remainder = driveMatch ? (driveMatch[2] ?? '') : normalized;
|
|
13
|
-
const hasRoot = driveMatch ? true : remainder.startsWith('/');
|
|
14
|
-
const rawSegments = remainder.split('/').filter((segment) => segment.length > 0);
|
|
15
|
-
const segments: string[] = [];
|
|
16
|
-
|
|
17
|
-
for (const segment of rawSegments) {
|
|
18
|
-
if (segment === '.') continue;
|
|
19
|
-
if (segment === '..') {
|
|
20
|
-
if (segments.length > 0 && segments[segments.length - 1] !== '..') {
|
|
21
|
-
segments.pop();
|
|
22
|
-
} else if (!hasRoot) {
|
|
23
|
-
segments.push('..');
|
|
24
|
-
}
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
segments.push(segment);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const joined = segments.join('/');
|
|
31
|
-
const comparable = drivePrefix
|
|
32
|
-
? `${drivePrefix}/${joined}`.replace(/\/$/, '')
|
|
33
|
-
: hasRoot
|
|
34
|
-
? `/${joined}`.replace(/\/$/, '')
|
|
35
|
-
: joined || '.';
|
|
36
|
-
// Restore prefers a conservative same-directory guard: false positives are safer than
|
|
37
|
-
// missing a Windows-style case-only path alias and deleting the source publish dir.
|
|
38
|
-
return comparable.toLowerCase();
|
|
39
|
-
}
|
|
5
|
+
export { basename, trimTrailingSlashes } from '../path-utils.js';
|
|
40
6
|
|
|
41
7
|
export function isPathWithin(root: string, candidate: string): boolean {
|
|
42
8
|
const normalizedRoot = normalizeComparablePath(root);
|
|
@@ -44,12 +10,6 @@ export function isPathWithin(root: string, candidate: string): boolean {
|
|
|
44
10
|
return normalizedCandidate.startsWith(`${normalizedRoot}/`);
|
|
45
11
|
}
|
|
46
12
|
|
|
47
|
-
export function basename(filePath: string): string {
|
|
48
|
-
const trimmed = trimTrailingSlashes(filePath);
|
|
49
|
-
const match = trimmed.match(/([^/\\]+)$/);
|
|
50
|
-
return match?.[1] ?? '';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
13
|
export function normalizeRestoreOutputDir(output: string): string {
|
|
54
14
|
const normalized = trimTrailingSlashes(output);
|
|
55
15
|
const name = basename(normalized);
|