UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

370 lines (369 loc) 14.3 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const IFile_1 = require("./IFile"); const StorageUtilities_1 = __importDefault(require("./StorageUtilities")); const md5 = __importStar(require("js-md5")); const Log_1 = __importDefault(require("../core/Log")); const ste_events_1 = require("ste-events"); const Utilities_1 = __importDefault(require("../core/Utilities")); const JsonUtilities_1 = __importDefault(require("../core/JsonUtilities")); class FileBase { priorVersions = []; isDisposed = false; isInErrorState; errorStateMessage; /** * Cache for comment-json parsed object. * When set, contains the parsed JSON with comment metadata preserved as Symbol properties. */ commentJsonCache; _content; #fileContainerStorage = null; #onFileContentUpdated = new ste_events_1.EventDispatcher(); get fileContainerStorage() { return this.#fileContainerStorage; } get onFileContentUpdated() { return this.#onFileContentUpdated.asEvent(); } set fileContainerStorage(newStorage) { this.#fileContainerStorage = newStorage; } get isString() { return this._content !== null && typeof this._content === "string"; } get canIgnore() { return StorageUtilities_1.default.canIgnoreFileName(this.name) || StorageUtilities_1.default.canIgnoreFileExtension(this.type); } get isBinary() { if (this._content === undefined) { return false; } if (this._content instanceof Uint8Array) { return true; } return false; } get content() { return this._content; } get latestModified() { if (this.modified == null) { return this.modifiedAtLoad; } if (this.modifiedAtLoad == null) { return null; } if (this.modified.getTime() > this.modifiedAtLoad.getTime()) { return this.modified; } return this.modifiedAtLoad; } modified; modifiedAtLoad; lastLoadedOrSaved; manager; tag; get type() { return StorageUtilities_1.default.getTypeFromName(this.name); } get needsSave() { if (this.isDisposed) { Log_1.default.throwIsDisposed(); } return this.modified != null && (this.lastLoadedOrSaved == null || this.modified >= this.lastLoadedOrSaved); } get fullPath() { return this.storageRelativePath; } get extendedPath() { let start = ""; if (this.parentFolder.storage.storagePath) { start = this.parentFolder.storage.storagePath; } return start + this.fullPath; } get storageRelativePath() { return this.parentFolder.storageRelativePath + this.name; } getRootRelativePath() { if (this.isDisposed) { Log_1.default.throwIsDisposed(); } return this.getFolderRelativePath(this.parentFolder.storage.rootFolder); } getFolderRelativePath(toFolder) { if (this.isDisposed) { Log_1.default.throwIsDisposed(); } let result = this.parentFolder.getFolderRelativePath(toFolder); if (result === undefined) { return undefined; } return result + this.name; } get coreContentLength() { if (!this.content) { return 0; } if (this.isBinary || typeof this.content !== "string") { return this.content.length; } return this.content.replace(/\s/g, "").length; } constructor() { this.modified = null; this.modifiedAtLoad = null; this.lastLoadedOrSaved = null; this._content = null; } contentWasModified(oldContent, updateType, sourceId) { if (this.isDisposed) { Log_1.default.throwIsDisposed(); } let oldVersionContent = undefined; if (updateType !== IFile_1.FileUpdateType.versionRestoration && updateType !== IFile_1.FileUpdateType.versionlessEdit) { let oldModified = this.modified; if (oldModified === null) { oldModified = this.lastLoadedOrSaved; } oldVersionContent = { id: Utilities_1.default.createRandomId(10), content: oldContent, file: this, versionTime: oldModified, }; this.parentFolder.storage.addVersion(oldVersionContent, updateType ? updateType : IFile_1.FileUpdateType.regularEdit); } this.modified = new Date(); this.notifyFileContentUpdated(); if (this.parentFolder.storage) { this.parentFolder.storage.notifyFileContentsUpdated({ file: this, updateType: updateType ? updateType : IFile_1.FileUpdateType.regularEdit, sourceId: sourceId, priorVersion: oldVersionContent, }); } } notifyFileContentUpdated() { this.#onFileContentUpdated.dispatch(this, this); } async getHash() { if (!this.isContentLoaded) { await this.loadContent(false); } if (this._content === undefined || this._content === null) { return undefined; } return md5.md5(this._content); } unload() { this._content = null; this.lastLoadedOrSaved = null; // Also clear the manager (typically a Definition object with parsed JSON) to fully release memory this.manager = undefined; // Clear cached parsed JSON to release memory this.commentJsonCache = undefined; } dispose() { this.manager = undefined; this._content = null; this.lastLoadedOrSaved = null; this.commentJsonCache = undefined; this.isDisposed = true; } async exists() { if (this.isDisposed) { Log_1.default.throwIsDisposed(); } await this.loadContent(false); return this._content !== null; } getRelativePathFor(file) { if (file.parentFolder.storage !== this.parentFolder.storage) { return undefined; } const foldersByPath = {}; let targetParentFolder = file.parentFolder; while (targetParentFolder) { foldersByPath[targetParentFolder.storageRelativePath] = targetParentFolder; targetParentFolder = targetParentFolder.parentFolder; } let myParentFolder = this.parentFolder; let relativePath = "." + myParentFolder.storage.folderDelimiter; while (myParentFolder && foldersByPath[myParentFolder.storageRelativePath] === undefined) { relativePath += ".." + myParentFolder.storage.folderDelimiter; myParentFolder = myParentFolder.parentFolder; } if (!myParentFolder) { return undefined; } const folderRelativePath = file.getFolderRelativePath(myParentFolder); if (!folderRelativePath) { return undefined; } return relativePath + StorageUtilities_1.default.ensureNotStartsWithDelimiter(folderRelativePath); } setObjectContentIfSemanticallyDifferent(value, updateType, sourceId) { if (value === null || value === undefined) { if (this._content !== null) { this.setContent(null, updateType, sourceId); this.commentJsonCache = undefined; return true; } return false; } if (!(typeof this._content === "string")) { // No existing content - check if the new value has comment metadata if (JsonUtilities_1.default.hasCommentMetadata(value)) { this.setContent(JsonUtilities_1.default.stringifyJsonWithComments(value), updateType, sourceId); this.commentJsonCache = value; } else { this.setContent(JSON.stringify(value, null, 2), updateType, sourceId); } return true; } try { // If the value has comment metadata, use comment-preserving comparison and stringify if (JsonUtilities_1.default.hasCommentMetadata(value)) { const currentObj = this.commentJsonCache ?? JsonUtilities_1.default.parseJsonWithComments(this._content); if (!JsonUtilities_1.default.jsonObjectsSemanticallyEqual(currentObj, value)) { this.setContent(JsonUtilities_1.default.stringifyJsonWithComments(value), updateType, sourceId); this.commentJsonCache = value; return true; } return false; } // Standard path for objects without comment metadata const currentObj = JSON.parse(this._content); if (Utilities_1.default.consistentStringify(currentObj) !== Utilities_1.default.consistentStringify(value)) { this.setContent(JSON.stringify(value, null, 2), updateType, sourceId); return true; } } catch (e) { // JSON.parse failed — current content may have comments or trailing commas. // Try comment-aware parsing before unconditionally overwriting (prevents phantom edits). try { const currentObj = JSON.parse(Utilities_1.default.fixJsonContent(this._content)); if (Utilities_1.default.consistentStringify(currentObj) === Utilities_1.default.consistentStringify(value)) { return false; } } catch { // Both parses failed — content is truly unparseable, proceed with overwrite } if (JsonUtilities_1.default.hasCommentMetadata(value)) { this.setContent(JsonUtilities_1.default.stringifyJsonWithComments(value), updateType, sourceId); this.commentJsonCache = value; } else { this.setContent(JSON.stringify(value, null, 2), updateType, sourceId); } return true; } return false; } async reloadAfterExternalUpdate() { let existingContent = this._content; await this.loadContent(true); if (this._content !== existingContent) { this.contentWasModified(existingContent, IFile_1.FileUpdateType.externalChange); } } setContentIfSemanticallyDifferent(value, updateType, sourceId) { if (value === null) { if (this._content !== null) { this.setContent(null, updateType, sourceId); return true; } return false; } if (value instanceof Uint8Array) { if (!(this._content instanceof Uint8Array) || this._content.length !== value.length) { this.setContent(value, updateType, sourceId); return true; } else { for (let i = 0; i < value.length; i++) { if (this._content[i] !== value[i]) { this.setContent(value, updateType, sourceId); return true; } } } } else if (typeof value === "string") { if (!(typeof this._content === "string")) { this.setContent(value, updateType, sourceId); return true; } else { if (this.type === "json") { try { const currentObj = JSON.parse(this._content); const newObj = JSON.parse(value); if (Utilities_1.default.consistentStringify(currentObj) !== Utilities_1.default.consistentStringify(newObj)) { return this.setContent(value, updateType, sourceId); } } catch (e) { return this.setContent(value); } } else { if (this._content.length !== value.length) { return this.setContent(value, updateType, sourceId); } for (let i = 0; i < value.length; i++) { if (this._content[i] !== value[i]) { return this.setContent(value, updateType, sourceId); } } } } } return false; } } exports.default = FileBase;