@uniformdev/cli 19.214.1-alpha.33 → 19.214.1-alpha.34

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.
Files changed (2) hide show
  1. package/dist/index.mjs +483 -286
  2. package/package.json +11 -10
package/dist/index.mjs CHANGED
@@ -615,17 +615,22 @@ var AssetListModule = {
615
615
  }
616
616
  };
617
617
 
618
- // src/files/index.ts
619
- import { preferredType } from "@thi.ng/mime";
620
- import { FILE_READY_STATE, getFileNameFromUrl } from "@uniformdev/files";
621
- import { fileTypeFromBuffer } from "file-type";
618
+ // src/files/deleteDownloadedFileByUrl.ts
622
619
  import fsj from "fs-jetpack";
623
- import sizeOf from "image-size";
624
- import PQueue from "p-queue";
625
- import { dirname, join as join2 } from "path";
620
+ import { join as join3 } from "path";
621
+
622
+ // src/files/urlToFileName.ts
623
+ import { join as join2 } from "path";
624
+ import { dirname } from "path";
626
625
  var FILES_DIRECTORY_NAME = "files";
627
- var escapeRegExp = (string) => {
628
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
626
+ var getFilesDirectory = (directory) => {
627
+ const isPackage = isPathAPackageFile(directory);
628
+ return isPackage ? dirname(directory) : (
629
+ // If we are syncing to a directory, we want to write all files into a
630
+ // top-lvl folder. That way any entities that contain files will sync to the
631
+ // same directory, so there is no duplication
632
+ join2(directory, "..")
633
+ );
629
634
  };
630
635
  var urlToHash = (url) => {
631
636
  return Buffer.from(
@@ -634,24 +639,6 @@ var urlToHash = (url) => {
634
639
  new URL(url).pathname.substring(0, 64)
635
640
  ).toString("base64");
636
641
  };
637
- var hashToPartialPathname = (hash) => {
638
- try {
639
- return Buffer.from(hash, "base64").toString("utf8");
640
- } catch {
641
- return null;
642
- }
643
- };
644
- var findUrlMatchingPartialPathname = (source, pathname) => {
645
- const escapedPathname = escapeRegExp(pathname);
646
- const regex = new RegExp(
647
- `"(https://([^"]*?)?(img|files).uniform.(rocks|global)${escapedPathname}([^"]*?))"`
648
- );
649
- const match = source.match(regex);
650
- if (match && match[1]) {
651
- return match[1];
652
- }
653
- return null;
654
- };
655
642
  var urlToFileExtension = (url) => {
656
643
  try {
657
644
  const urlObject = new URL(url);
@@ -666,228 +653,387 @@ var urlToFileName = (url, hash) => {
666
653
  const fileExtension = urlToFileExtension(url);
667
654
  return `${fileName}${fileExtension ? `.${fileExtension}` : ""}`;
668
655
  };
669
- var getFilesDirectory = (directory) => {
670
- const isPackage = isPathAPackageFile(directory);
671
- return isPackage ? dirname(directory) : (
672
- // If we are syncing to a directory, we want to write all files into a
673
- // top-lvl folder. That way any entities that contain files will sync to the
674
- // same directory, so there is no duplication
675
- join2(directory, "..")
676
- );
677
- };
678
- var getUniformFileUrlMatches = (string) => {
679
- return string.matchAll(/"(https:\/\/([^"]*?)?(img|files)\.uniform\.(rocks|global)\/([^"]*?))"/g);
656
+ var hashToPartialPathname = (hash) => {
657
+ try {
658
+ return Buffer.from(hash, "base64").toString("utf8");
659
+ } catch {
660
+ return null;
661
+ }
680
662
  };
663
+
664
+ // src/files/deleteDownloadedFileByUrl.ts
681
665
  var deleteDownloadedFileByUrl = async (url, options) => {
682
666
  const writeDirectory = getFilesDirectory(options.directory);
683
667
  const fileName = urlToFileName(url);
684
- const fileToDelete = join2(writeDirectory, FILES_DIRECTORY_NAME, fileName);
668
+ const fileToDelete = join3(writeDirectory, FILES_DIRECTORY_NAME, fileName);
685
669
  try {
686
670
  await fsj.removeAsync(fileToDelete);
687
671
  } catch {
688
672
  console.warn(`Failed to delete a local file ${fileToDelete}`);
689
673
  }
690
674
  };
691
- var extractAndDownloadUniformFilesForObject = async (object, options) => {
692
- const objectAsString = JSON.stringify(object);
693
- const uniformFileUrlMatches = getUniformFileUrlMatches(objectAsString);
694
- const writeDirectory = getFilesDirectory(options.directory);
695
- if (uniformFileUrlMatches) {
696
- const fileDownloadQueue = new PQueue({ concurrency: 10 });
697
- for (const match of uniformFileUrlMatches) {
698
- const url = new URL(match[1]);
699
- fileDownloadQueue.add(async () => {
675
+
676
+ // src/files/files.ts
677
+ import {
678
+ ASSETS_SOURCE_UNIFORM,
679
+ getPropertiesValue as getPropertiesValue2,
680
+ isAssetParamValue,
681
+ isAssetParamValueItem,
682
+ walkNodeTree as walkNodeTree2,
683
+ walkPropertyValues as walkPropertyValues2
684
+ } from "@uniformdev/canvas";
685
+ import { isRichTextNodeType, isRichTextValue, walkRichTextTree } from "@uniformdev/richtext";
686
+ import fsj4 from "fs-jetpack";
687
+ import PQueue2 from "p-queue";
688
+ import { join as join6 } from "path";
689
+
690
+ // src/files/downloadFile.ts
691
+ import fsj2 from "fs-jetpack";
692
+ import { join as join4 } from "path";
693
+ var downloadFile = async ({
694
+ fileClient,
695
+ fileUrl,
696
+ directory
697
+ }) => {
698
+ const writeDirectory = getFilesDirectory(directory);
699
+ const fileName = urlToFileName(fileUrl.toString());
700
+ const fileAlreadyExists = await fsj2.existsAsync(join4(writeDirectory, FILES_DIRECTORY_NAME, fileName));
701
+ if (fileAlreadyExists) {
702
+ return { url: fileUrl };
703
+ }
704
+ const file = await fileClient.get({ url: fileUrl }).catch(() => null);
705
+ if (!file) {
706
+ console.warn(`Skipping file ${fileUrl} as it does not exist in the project anymore`);
707
+ return null;
708
+ }
709
+ if (file.sourceId) {
710
+ try {
711
+ const hashAlreadyExists = await fsj2.findAsync(join4(writeDirectory, FILES_DIRECTORY_NAME), {
712
+ matching: [file.sourceId, `${file.sourceId}.*`]
713
+ });
714
+ if (hashAlreadyExists.length > 0) {
715
+ return { id: file.id, url: fileUrl };
716
+ }
717
+ } catch {
718
+ }
719
+ }
720
+ const fetchUrl = `${fileUrl}?format=original`;
721
+ const response = await fetch(fetchUrl);
722
+ if (!response.ok) {
723
+ return null;
724
+ }
725
+ const fileBuffer = await response.arrayBuffer();
726
+ await fsj2.writeAsync(join4(writeDirectory, FILES_DIRECTORY_NAME, fileName), Buffer.from(fileBuffer));
727
+ return { id: file.id, url: fileUrl };
728
+ };
729
+
730
+ // src/files/uploadFile.ts
731
+ import { preferredType } from "@thi.ng/mime";
732
+ import { FILE_READY_STATE, getFileNameFromUrl } from "@uniformdev/files";
733
+ import { fileTypeFromBuffer } from "file-type";
734
+ import fsj3 from "fs-jetpack";
735
+ import sizeOf from "image-size";
736
+ import PQueue from "p-queue";
737
+ import { join as join5 } from "path";
738
+ var fileUploadQueue = new PQueue({ concurrency: 10 });
739
+ var uploadFile = async ({
740
+ fileClient,
741
+ fileUrl,
742
+ directory,
743
+ fileId
744
+ }) => {
745
+ return await fileUploadQueue.add(async () => {
746
+ try {
747
+ const writeDirectory = getFilesDirectory(directory);
748
+ const hash = urlToHash(fileUrl);
749
+ const fileAlreadyExistsChecks = await Promise.all([
750
+ fileClient.get({ url: fileUrl }).catch(() => null),
751
+ fileClient.get({ sourceId: hash }).catch(() => null)
752
+ ]);
753
+ const file = fileAlreadyExistsChecks.find((check) => check !== null);
754
+ if (file?.url) {
755
+ return { id: file.id, url: file.url };
756
+ }
757
+ const localFileName = urlToFileName(fileUrl);
758
+ const expectedFilePath = join5(writeDirectory, FILES_DIRECTORY_NAME, localFileName);
759
+ const fileExistsLocally = await fsj3.existsAsync(expectedFilePath);
760
+ if (!fileExistsLocally) {
761
+ console.warn(
762
+ `Skipping file ${fileUrl} as we couldn't find a local copy (looked at ${expectedFilePath})`
763
+ );
764
+ return null;
765
+ }
766
+ const fileBuffer = await fsj3.readAsync(expectedFilePath, "buffer");
767
+ if (!fileBuffer) {
768
+ console.warn(`Skipping file ${fileUrl} (${expectedFilePath}) as we couldn't read it`);
769
+ return null;
770
+ }
771
+ const fileName = getFileNameFromUrl(fileUrl);
772
+ let mimeType = expectedFilePath.endsWith(".svg") ? "image/svg+xml" : (await fileTypeFromBuffer(fileBuffer))?.mime;
773
+ if (!mimeType) {
774
+ mimeType = preferredType(fileUrl.split(".").at(-1) ?? "");
775
+ }
776
+ if (mimeType === "audio/x-flac") {
777
+ mimeType = "audio/flac";
778
+ }
779
+ const { width, height } = (() => {
780
+ if (!mimeType.startsWith("image/")) {
781
+ return {
782
+ width: void 0,
783
+ height: void 0
784
+ };
785
+ }
700
786
  try {
701
- const fileName = urlToFileName(url.toString());
702
- const fileAlreadyExists = await fsj.existsAsync(
703
- join2(writeDirectory, FILES_DIRECTORY_NAME, fileName)
704
- );
705
- if (fileAlreadyExists) {
706
- return;
707
- }
708
- const file = await options.fileClient.get({ url: url.toString() }).catch(() => null);
709
- if (!file) {
710
- console.warn(`Skipping file ${url} as it does not exist in the project anymore`);
711
- return;
712
- }
713
- if (file.sourceId) {
714
- try {
715
- const hashAlreadyExists = await fsj.findAsync(join2(writeDirectory, FILES_DIRECTORY_NAME), {
716
- matching: [file.sourceId, `${file.sourceId}.*`]
717
- });
718
- if (hashAlreadyExists.length > 0) {
719
- return;
720
- }
721
- } catch {
722
- }
723
- }
724
- const fetchUrl = `${url.origin}${url.pathname}?format=original`;
725
- const response = await fetch(fetchUrl);
726
- if (!response.ok) {
727
- return;
728
- }
729
- const fileBuffer = await response.arrayBuffer();
730
- await fsj.writeAsync(join2(writeDirectory, FILES_DIRECTORY_NAME, fileName), Buffer.from(fileBuffer));
787
+ return sizeOf(fileBuffer);
731
788
  } catch {
732
- console.warn(`Failed to download file ${url}`);
789
+ return {
790
+ width: void 0,
791
+ height: void 0
792
+ };
793
+ }
794
+ })();
795
+ const { id, method, uploadUrl } = await fileClient.insert({
796
+ id: fileId,
797
+ name: fileName,
798
+ mediaType: mimeType,
799
+ size: fileBuffer.length,
800
+ width,
801
+ height,
802
+ sourceId: hash
803
+ });
804
+ const uploadResponse = await fetch(uploadUrl, {
805
+ method,
806
+ body: fileBuffer,
807
+ headers: {
808
+ "Content-Type": mimeType,
809
+ "Content-Length": fileBuffer.length.toString()
733
810
  }
734
811
  });
812
+ if (!uploadResponse.ok) {
813
+ console.warn(`Failed to upload file ${fileUrl} (${expectedFilePath})`);
814
+ return null;
815
+ }
816
+ let error;
817
+ const checkForFile = async () => {
818
+ if (error) {
819
+ throw error;
820
+ }
821
+ const file2 = await fileClient.get({ id });
822
+ if (!file2 || file2.state !== FILE_READY_STATE || !file2.url) {
823
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
824
+ return checkForFile();
825
+ }
826
+ return file2.url;
827
+ };
828
+ const abortTimeout = setTimeout(() => {
829
+ error = new Error(`Failed to upload file ${fileUrl} (${expectedFilePath}) - upload timed out`);
830
+ }, 6e4);
831
+ const uploadedFileUrl = await checkForFile();
832
+ clearTimeout(abortTimeout);
833
+ return { id, url: uploadedFileUrl };
834
+ } catch (e) {
835
+ console.warn(`Failed to upload file ${fileUrl}`, e);
836
+ return null;
837
+ }
838
+ }) ?? null;
839
+ };
840
+
841
+ // src/files/walkFileUrlsForCompositionOrEntry.ts
842
+ import {
843
+ getPropertiesValue,
844
+ walkNodeTree,
845
+ walkPropertyValues
846
+ } from "@uniformdev/canvas";
847
+ var UNIFORM_FILE_MATCH = /"(https:\/\/([^"]*?)?(img|files)\.uniform\.(rocks|global)\/([^"]*?))"/g;
848
+ var walkFileUrlsForCompositionOrEntry = ({
849
+ entity,
850
+ callback
851
+ }) => {
852
+ const thumbnail = "entry" in entity ? entity.entry._thumbnail : void 0;
853
+ if (typeof thumbnail === "string") {
854
+ const isUniformFile = `"${thumbnail}"`.match(UNIFORM_FILE_MATCH) !== null;
855
+ if (isUniformFile) {
856
+ callback({ fileUrl: thumbnail });
735
857
  }
736
- await fileDownloadQueue.onIdle();
737
858
  }
738
- return object;
859
+ walkNodeTree("entry" in entity ? entity.entry : entity.composition, ({ node }) => {
860
+ const properties = getPropertiesValue(node);
861
+ if (!properties) {
862
+ return;
863
+ }
864
+ Object.entries(properties).forEach(([_, property]) => {
865
+ if (property.type !== "image") {
866
+ return;
867
+ }
868
+ walkPropertyValues(property, ({ value }) => {
869
+ if (typeof value !== "string") {
870
+ return;
871
+ }
872
+ const isUniformFile = `"${value}"`.match(UNIFORM_FILE_MATCH) !== null;
873
+ if (!isUniformFile) {
874
+ return;
875
+ }
876
+ callback({ fileUrl: value });
877
+ });
878
+ });
879
+ });
739
880
  };
740
- var extractAndUploadUniformFilesForObject = async (object, options) => {
741
- let objectAsString = JSON.stringify(object);
742
- const uniformFileUrlMatches = getUniformFileUrlMatches(objectAsString);
743
- const writeDirectory = getFilesDirectory(options.directory);
744
- if (uniformFileUrlMatches) {
745
- const fileUploadQueue = new PQueue({ concurrency: 3 });
746
- for (const match of uniformFileUrlMatches) {
747
- const url = match[1];
748
- const hash = urlToHash(url);
749
- fileUploadQueue.add(async () => {
750
- try {
751
- const fileAlreadyExistsChecks = await Promise.all([
752
- options.fileClient.get({ url }).catch(() => null),
753
- options.fileClient.get({ sourceId: hash }).catch(() => null)
754
- ]);
755
- const file = fileAlreadyExistsChecks.find((check) => check !== null);
756
- if (file) {
757
- objectAsString = objectAsString.replaceAll(`"${url}"`, `"${file.url}"`);
758
- return;
759
- }
760
- const localFileName = urlToFileName(url);
761
- const expectedFilePath = join2(writeDirectory, FILES_DIRECTORY_NAME, localFileName);
762
- const fileExistsLocally = await fsj.existsAsync(expectedFilePath);
763
- if (!fileExistsLocally) {
764
- console.warn(
765
- `Skipping file ${url} as we couldn't find a local copy (looked at ${expectedFilePath})`
766
- );
767
- return;
768
- }
769
- const fileBuffer = await fsj.readAsync(expectedFilePath, "buffer");
770
- if (!fileBuffer) {
771
- console.warn(`Skipping file ${url} (${expectedFilePath}) as we couldn't read it`);
881
+
882
+ // src/files/files.ts
883
+ var downloadFileForAsset = async ({
884
+ asset,
885
+ directory,
886
+ fileClient
887
+ }) => {
888
+ if (asset.asset.fields?.file?.value === void 0 || asset.asset.fields.url?.value === void 0) {
889
+ return null;
890
+ }
891
+ const fileId = asset.asset.fields?.file?.value;
892
+ const fileUrl = asset.asset.fields.url?.value;
893
+ if (fileId === "" || fileUrl === "") {
894
+ return null;
895
+ }
896
+ return downloadFile({ fileUrl, directory, fileClient });
897
+ };
898
+ var uploadFileForAsset = async ({
899
+ asset,
900
+ directory,
901
+ fileClient
902
+ }) => {
903
+ if (asset.asset.fields?.file?.value === void 0 || asset.asset.fields.url?.value === void 0) {
904
+ return null;
905
+ }
906
+ const fileUrl = asset.asset.fields.url.value;
907
+ const fileId = asset.asset.fields.file.value;
908
+ return uploadFile({ fileUrl, directory, fileClient, fileId });
909
+ };
910
+ var removeUrlsFromAssetParameters = (entity) => {
911
+ walkNodeTree2("entry" in entity ? entity.entry : entity.composition, ({ node }) => {
912
+ const properties = getPropertiesValue2(node);
913
+ if (!properties) {
914
+ return;
915
+ }
916
+ Object.entries(properties).forEach(([_, property]) => {
917
+ if (property.type === "asset") {
918
+ walkPropertyValues2(property, ({ value }) => {
919
+ if (!isAssetParamValue(value)) {
772
920
  return;
773
921
  }
774
- const fileName = getFileNameFromUrl(url);
775
- let mimeType = expectedFilePath.endsWith(".svg") ? "image/svg+xml" : (await fileTypeFromBuffer(fileBuffer))?.mime;
776
- if (!mimeType) {
777
- mimeType = preferredType(url.split(".").at(-1) ?? "");
778
- }
779
- if (mimeType === "audio/x-flac") {
780
- mimeType = "audio/flac";
781
- }
782
- const { width, height } = (() => {
783
- if (!mimeType.startsWith("image/")) {
784
- return {
785
- width: void 0,
786
- height: void 0
787
- };
788
- }
789
- try {
790
- return sizeOf(fileBuffer);
791
- } catch {
792
- return {
793
- width: void 0,
794
- height: void 0
795
- };
922
+ value.forEach((asset) => {
923
+ if (!isAssetParamValueItem(asset)) {
924
+ return;
796
925
  }
797
- })();
798
- const { id, method, uploadUrl } = await options.fileClient.insert({
799
- name: fileName,
800
- mediaType: mimeType,
801
- size: fileBuffer.length,
802
- width,
803
- height,
804
- sourceId: hash
805
- });
806
- const uploadResponse = await fetch(uploadUrl, {
807
- method,
808
- body: fileBuffer,
809
- headers: {
810
- "Content-Type": mimeType,
811
- "Content-Length": fileBuffer.length.toString()
926
+ if (asset._source !== ASSETS_SOURCE_UNIFORM || !asset.fields?.url.value) {
927
+ return;
812
928
  }
929
+ asset.fields.url.value = "";
813
930
  });
814
- if (!uploadResponse.ok) {
815
- console.warn(`Failed to upload file ${url} (${expectedFilePath})`);
931
+ });
932
+ } else if (property.type === "richText") {
933
+ walkPropertyValues2(property, ({ value }) => {
934
+ if (!isRichTextValue(value)) {
816
935
  return;
817
936
  }
818
- let error;
819
- const checkForFile = async () => {
820
- if (error) {
821
- throw error;
822
- }
823
- const file2 = await options.fileClient.get({ id });
824
- if (!file2 || file2.state !== FILE_READY_STATE || !file2.url) {
825
- await new Promise((resolve) => setTimeout(resolve, 1e3));
826
- return checkForFile();
937
+ walkRichTextTree(value.root, (node2) => {
938
+ if (isRichTextNodeType(node2, "asset")) {
939
+ if (node2.__asset?._source !== ASSETS_SOURCE_UNIFORM || !node2.__asset.fields.url.value) {
940
+ return;
941
+ }
942
+ node2.__asset.fields.url.value = "";
827
943
  }
828
- return file2.url;
829
- };
830
- const abortTimeout = setTimeout(() => {
831
- error = new Error(`Failed to upload file ${url} (${expectedFilePath}) - upload timed out`);
832
- }, 6e4);
833
- const uploadedFileUrl = await checkForFile();
834
- clearTimeout(abortTimeout);
835
- objectAsString = objectAsString.replaceAll(`"${url}"`, `"${uploadedFileUrl}"`);
836
- } catch (e) {
837
- console.warn(`Failed to upload file ${url}`, e);
838
- }
944
+ });
945
+ });
946
+ }
947
+ });
948
+ });
949
+ return entity;
950
+ };
951
+ var compareCompositionsOrEntriesWithoutAssetUrls = (source, target) => {
952
+ return serializedDequal(
953
+ removeUrlsFromAssetParameters(structuredClone(source.object)),
954
+ removeUrlsFromAssetParameters(structuredClone(target.object))
955
+ );
956
+ };
957
+ var removeUrlFromAsset = (asset) => {
958
+ if (asset.asset.fields?.url?.value) {
959
+ asset.asset.fields.url.value = "";
960
+ }
961
+ return asset;
962
+ };
963
+ var compareAssetsWithoutUrls = (source, target) => {
964
+ return serializedDequal(
965
+ removeUrlFromAsset(structuredClone(source.object)),
966
+ removeUrlFromAsset(structuredClone(target.object))
967
+ );
968
+ };
969
+ var downloadFilesForCompositionOrEntry = async ({
970
+ entity,
971
+ directory,
972
+ fileClient
973
+ }) => {
974
+ const fileDownloadQueue = new PQueue2({ concurrency: 3 });
975
+ await walkFileUrlsForCompositionOrEntry({
976
+ entity,
977
+ callback: ({ fileUrl }) => {
978
+ fileDownloadQueue.add(async () => {
979
+ await downloadFile({ fileUrl, directory, fileClient });
839
980
  });
840
981
  }
841
- await fileUploadQueue.onIdle();
842
- }
843
- return JSON.parse(objectAsString);
982
+ });
983
+ await fileDownloadQueue.onIdle();
844
984
  };
845
- var swapOutUniformFileUrlsForTargetProject = async (object, options) => {
846
- let objectAsString = JSON.stringify(object);
847
- const uniformFileUrlMatches = getUniformFileUrlMatches(objectAsString);
848
- if (uniformFileUrlMatches) {
849
- const fileUrlReplacementQueue = new PQueue({ concurrency: 3 });
850
- for (const match of uniformFileUrlMatches) {
851
- const url = match[1];
852
- const hash = urlToHash(url);
853
- fileUrlReplacementQueue.add(async () => {
854
- try {
855
- const fileAlreadyExistsChecks = await Promise.all([
856
- options.fileClient.get({ url }).catch(() => null),
857
- options.fileClient.get({ sourceId: hash }).catch(() => null)
858
- ]);
859
- const file = fileAlreadyExistsChecks.find((check) => check !== null);
860
- if (!file) {
861
- return;
862
- }
863
- objectAsString = objectAsString.replaceAll(`"${url}"`, `"${file.url}"`);
864
- } catch {
985
+ var uploadFilesForCompositionOrEntry = async ({
986
+ entity,
987
+ directory,
988
+ fileClient
989
+ }) => {
990
+ const fileUploadQueue2 = new PQueue2({ concurrency: 3 });
991
+ const urlReplacementMap = /* @__PURE__ */ new Map();
992
+ walkFileUrlsForCompositionOrEntry({
993
+ entity: entity.object,
994
+ callback: async ({ fileUrl }) => {
995
+ fileUploadQueue2.add(async () => {
996
+ const upload = await uploadFile({
997
+ directory,
998
+ fileUrl,
999
+ fileClient
1000
+ });
1001
+ if (upload !== null) {
1002
+ urlReplacementMap.set(fileUrl, upload.url);
865
1003
  }
866
1004
  });
867
1005
  }
868
- await fileUrlReplacementQueue.onIdle();
869
- }
870
- return JSON.parse(objectAsString);
871
- };
872
- var replaceRemoteUrlsWithLocalReferences = async (sourceObject, targetObject, options) => {
873
- let sourceObjectAsString = JSON.stringify(sourceObject);
874
- const targetObjectAsString = JSON.stringify(targetObject);
875
- const uniformFileUrlMatches = getUniformFileUrlMatches(sourceObjectAsString);
876
- const writeDirectory = getFilesDirectory(options.directory);
877
- if (uniformFileUrlMatches) {
878
- const fileUrlReplacementQueue = new PQueue({ concurrency: 3 });
879
- for (const match of uniformFileUrlMatches) {
880
- const url = match[1];
1006
+ });
1007
+ await fileUploadQueue2.onIdle();
1008
+ let entityAsString = JSON.stringify(entity);
1009
+ for (const [key, value] of urlReplacementMap.entries()) {
1010
+ entityAsString = entityAsString.replaceAll(`"${key}"`, `"${value}"`);
1011
+ }
1012
+ return JSON.parse(entityAsString);
1013
+ };
1014
+ var replaceRemoteUrlsWithLocalReferences = async ({
1015
+ sourceEntity,
1016
+ targetEntity,
1017
+ fileClient,
1018
+ directory
1019
+ }) => {
1020
+ let sourceEntityAsString = JSON.stringify(sourceEntity);
1021
+ const targetEntityAsString = JSON.stringify(targetEntity);
1022
+ const writeDirectory = getFilesDirectory(directory);
1023
+ const fileUrlReplacementQueue = new PQueue2({ concurrency: 3 });
1024
+ walkFileUrlsForCompositionOrEntry({
1025
+ entity: sourceEntity.object,
1026
+ callback: ({ fileUrl }) => {
881
1027
  fileUrlReplacementQueue.add(async () => {
882
1028
  try {
883
- const localFileName = urlToFileName(url);
884
- const fileExistsLocally = await fsj.existsAsync(
885
- join2(writeDirectory, FILES_DIRECTORY_NAME, localFileName)
1029
+ const localFileName = urlToFileName(fileUrl);
1030
+ const fileExistsLocally = await fsj4.existsAsync(
1031
+ join6(writeDirectory, FILES_DIRECTORY_NAME, localFileName)
886
1032
  );
887
1033
  if (fileExistsLocally) {
888
1034
  return;
889
1035
  }
890
- const file = await options.fileClient.get({ url }).catch(() => null);
1036
+ const file = await fileClient.get({ url: fileUrl }).catch(() => null);
891
1037
  if (!file || !file.sourceId) {
892
1038
  return;
893
1039
  }
@@ -895,36 +1041,64 @@ var replaceRemoteUrlsWithLocalReferences = async (sourceObject, targetObject, op
895
1041
  if (!originalPartialPath) {
896
1042
  return;
897
1043
  }
898
- const originalUrl = findUrlMatchingPartialPathname(targetObjectAsString, originalPartialPath);
1044
+ const originalUrl = findUrlMatchingPartialPathname(targetEntityAsString, originalPartialPath);
899
1045
  if (!originalUrl) {
900
1046
  return;
901
1047
  }
902
- sourceObjectAsString = sourceObjectAsString.replaceAll(`"${url}"`, `"${originalUrl}"`);
1048
+ sourceEntityAsString = sourceEntityAsString.replaceAll(`"${fileUrl}"`, `"${originalUrl}"`);
903
1049
  } catch {
904
1050
  }
905
1051
  });
1052
+ return null;
906
1053
  }
907
- await fileUrlReplacementQueue.onIdle();
908
- }
909
- return JSON.parse(sourceObjectAsString);
1054
+ });
1055
+ await fileUrlReplacementQueue.onIdle();
1056
+ return JSON.parse(sourceEntityAsString);
910
1057
  };
911
- var updateAssetFileIdBasedOnUrl = async (asset, options) => {
912
- if (!asset.asset.fields) {
913
- return asset;
914
- }
915
- const fileUrl = asset.asset.fields.url?.value;
916
- if (!fileUrl) {
917
- return asset;
918
- }
919
- const file = await options.fileClient.get({ url: fileUrl }).catch(() => null);
920
- if (!file) {
921
- return asset;
1058
+ var replaceLocalUrlsWithRemoteReferences = async ({
1059
+ entity,
1060
+ fileClient
1061
+ }) => {
1062
+ let entityAsString = JSON.stringify(entity);
1063
+ const fileUrlReplacementQueue = new PQueue2({ concurrency: 3 });
1064
+ walkFileUrlsForCompositionOrEntry({
1065
+ entity: entity.object,
1066
+ callback: ({ fileUrl }) => {
1067
+ fileUrlReplacementQueue.add(async () => {
1068
+ try {
1069
+ const hash = urlToHash(fileUrl);
1070
+ fileUrlReplacementQueue.add(async () => {
1071
+ try {
1072
+ const file = await fileClient.get({ sourceId: hash }).catch(() => null);
1073
+ if (!file) {
1074
+ return;
1075
+ }
1076
+ entityAsString = entityAsString.replaceAll(`"${fileUrl}"`, `"${file.url}"`);
1077
+ } catch {
1078
+ }
1079
+ });
1080
+ } catch {
1081
+ }
1082
+ });
1083
+ return null;
1084
+ }
1085
+ });
1086
+ await fileUrlReplacementQueue.onIdle();
1087
+ return JSON.parse(entityAsString);
1088
+ };
1089
+ var escapeRegExp = (string) => {
1090
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1091
+ };
1092
+ var findUrlMatchingPartialPathname = (source, pathname) => {
1093
+ const escapedPathname = escapeRegExp(pathname);
1094
+ const regex = new RegExp(
1095
+ `"(https://([^"]*?)?(img|files).uniform.(rocks|global)${escapedPathname}([^"]*?))"`
1096
+ );
1097
+ const match = source.match(regex);
1098
+ if (match && match[1]) {
1099
+ return match[1];
922
1100
  }
923
- asset.asset.fields.file = {
924
- type: "file",
925
- value: file.id
926
- };
927
- return asset;
1101
+ return null;
928
1102
  };
929
1103
 
930
1104
  // src/commands/canvas/assetEngineDataSource.ts
@@ -1115,27 +1289,25 @@ var AssetPullModule = {
1115
1289
  whatIf,
1116
1290
  allowEmptySource: allowEmptySource ?? true,
1117
1291
  log: createSyncEngineConsoleLogger({ diffMode }),
1118
- onBeforeCompareObjects: async (sourceObject, targetObject) => {
1292
+ onBeforeCompareObjects: async (sourceObject) => {
1119
1293
  delete sourceObject.object.asset._author;
1120
- const sourceObjectWithPotentiallySwappedUrl = await replaceRemoteUrlsWithLocalReferences(
1121
- sourceObject,
1122
- targetObject,
1123
- {
1124
- directory,
1125
- fileClient
1126
- }
1127
- );
1128
- if (sourceObjectWithPotentiallySwappedUrl.object.asset.fields?.url && targetObject.object.asset.fields?.url && sourceObjectWithPotentiallySwappedUrl.object.asset.fields.url.value === targetObject.object.asset.fields.url.value) {
1129
- targetObject.object.asset.fields.file = sourceObjectWithPotentiallySwappedUrl.object.asset.fields.file;
1130
- }
1131
- return sourceObjectWithPotentiallySwappedUrl;
1294
+ return sourceObject;
1132
1295
  },
1296
+ compareContents: compareAssetsWithoutUrls,
1133
1297
  onBeforeWriteObject: async (sourceObject) => {
1134
1298
  delete sourceObject.object.asset._author;
1135
- return extractAndDownloadUniformFilesForObject(sourceObject, {
1299
+ if (!sourceObject.object.asset.fields?.file) {
1300
+ return sourceObject;
1301
+ }
1302
+ const downloadedFile = await downloadFileForAsset({
1303
+ asset: sourceObject.object,
1136
1304
  directory,
1137
1305
  fileClient
1138
1306
  });
1307
+ if (downloadedFile?.id) {
1308
+ sourceObject.object.asset.fields.file.value = downloadedFile.id;
1309
+ }
1310
+ return sourceObject;
1139
1311
  }
1140
1312
  });
1141
1313
  }
@@ -1215,29 +1387,29 @@ var AssetPushModule = {
1215
1387
  if (targetObject) {
1216
1388
  delete targetObject.object.asset._author;
1217
1389
  }
1218
- const sourceObjectWithNewFileUrls = await swapOutUniformFileUrlsForTargetProject(sourceObject, {
1219
- fileClient
1220
- });
1221
- sourceObjectWithNewFileUrls.object = await updateAssetFileIdBasedOnUrl(
1222
- sourceObjectWithNewFileUrls.object,
1223
- {
1224
- fileClient
1225
- }
1226
- );
1227
- return sourceObjectWithNewFileUrls;
1390
+ return sourceObject;
1228
1391
  },
1392
+ compareContents: compareAssetsWithoutUrls,
1229
1393
  onBeforeWriteObject: async (sourceObject) => {
1230
- const sourceObjectWithNewFileUrls = await extractAndUploadUniformFilesForObject(sourceObject, {
1394
+ const uploadedFile = await uploadFileForAsset({
1395
+ asset: sourceObject.object,
1231
1396
  directory,
1232
1397
  fileClient
1233
1398
  });
1234
- sourceObjectWithNewFileUrls.object = await updateAssetFileIdBasedOnUrl(
1235
- sourceObjectWithNewFileUrls.object,
1236
- {
1237
- fileClient
1399
+ if (uploadedFile !== null) {
1400
+ if (sourceObject.object.asset.fields === void 0) {
1401
+ sourceObject.object.asset.fields = {};
1238
1402
  }
1239
- );
1240
- return sourceObjectWithNewFileUrls;
1403
+ sourceObject.object.asset.fields.file = {
1404
+ type: "file",
1405
+ value: uploadedFile.id
1406
+ };
1407
+ sourceObject.object.asset.fields.url = {
1408
+ type: "text",
1409
+ value: uploadedFile.url
1410
+ };
1411
+ }
1412
+ return sourceObject;
1241
1413
  }
1242
1414
  });
1243
1415
  }
@@ -2505,16 +2677,21 @@ var CompositionPullModule = {
2505
2677
  allowEmptySource: allowEmptySource ?? true,
2506
2678
  log: createSyncEngineConsoleLogger({ diffMode }),
2507
2679
  onBeforeCompareObjects: async (sourceObject, targetObject) => {
2508
- return replaceRemoteUrlsWithLocalReferences(sourceObject, targetObject, {
2680
+ return replaceRemoteUrlsWithLocalReferences({
2681
+ sourceEntity: sourceObject,
2682
+ targetEntity: targetObject,
2509
2683
  directory,
2510
2684
  fileClient
2511
2685
  });
2512
2686
  },
2687
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
2513
2688
  onBeforeWriteObject: async (sourceObject) => {
2514
- return extractAndDownloadUniformFilesForObject(sourceObject, {
2689
+ await downloadFilesForCompositionOrEntry({
2690
+ entity: sourceObject.object,
2515
2691
  directory,
2516
2692
  fileClient
2517
2693
  });
2694
+ return sourceObject;
2518
2695
  }
2519
2696
  });
2520
2697
  }
@@ -2658,12 +2835,15 @@ var CompositionPushModule = {
2658
2835
  allowEmptySource,
2659
2836
  log: createSyncEngineConsoleLogger({ diffMode }),
2660
2837
  onBeforeCompareObjects: async (sourceObject) => {
2661
- return swapOutUniformFileUrlsForTargetProject(sourceObject, {
2838
+ return replaceLocalUrlsWithRemoteReferences({
2839
+ entity: sourceObject,
2662
2840
  fileClient
2663
2841
  });
2664
2842
  },
2843
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
2665
2844
  onBeforeWriteObject: async (sourceObject) => {
2666
- return extractAndUploadUniformFilesForObject(sourceObject, {
2845
+ return uploadFilesForCompositionOrEntry({
2846
+ entity: sourceObject,
2667
2847
  directory,
2668
2848
  fileClient
2669
2849
  });
@@ -4257,16 +4437,21 @@ var EntryPullModule = {
4257
4437
  allowEmptySource: allowEmptySource ?? true,
4258
4438
  log: createSyncEngineConsoleLogger({ diffMode }),
4259
4439
  onBeforeCompareObjects: async (sourceObject, targetObject) => {
4260
- return replaceRemoteUrlsWithLocalReferences(sourceObject, targetObject, {
4440
+ return replaceRemoteUrlsWithLocalReferences({
4441
+ sourceEntity: sourceObject,
4442
+ targetEntity: targetObject,
4261
4443
  directory,
4262
4444
  fileClient
4263
4445
  });
4264
4446
  },
4447
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
4265
4448
  onBeforeWriteObject: async (sourceObject) => {
4266
- return extractAndDownloadUniformFilesForObject(sourceObject, {
4449
+ await downloadFilesForCompositionOrEntry({
4450
+ entity: sourceObject.object,
4267
4451
  directory,
4268
4452
  fileClient
4269
4453
  });
4454
+ return sourceObject;
4270
4455
  }
4271
4456
  });
4272
4457
  }
@@ -4346,12 +4531,15 @@ var EntryPushModule = {
4346
4531
  allowEmptySource,
4347
4532
  log: createSyncEngineConsoleLogger({ diffMode }),
4348
4533
  onBeforeCompareObjects: async (sourceObject) => {
4349
- return swapOutUniformFileUrlsForTargetProject(sourceObject, {
4534
+ return replaceLocalUrlsWithRemoteReferences({
4535
+ entity: sourceObject,
4350
4536
  fileClient
4351
4537
  });
4352
4538
  },
4539
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
4353
4540
  onBeforeWriteObject: async (sourceObject) => {
4354
- return extractAndUploadUniformFilesForObject(sourceObject, {
4541
+ return uploadFilesForCompositionOrEntry({
4542
+ entity: sourceObject,
4355
4543
  directory,
4356
4544
  fileClient
4357
4545
  });
@@ -4727,16 +4915,21 @@ var EntryPatternPullModule = {
4727
4915
  allowEmptySource: allowEmptySource ?? true,
4728
4916
  log: createSyncEngineConsoleLogger({ diffMode }),
4729
4917
  onBeforeCompareObjects: async (sourceObject, targetObject) => {
4730
- return replaceRemoteUrlsWithLocalReferences(sourceObject, targetObject, {
4918
+ return replaceRemoteUrlsWithLocalReferences({
4919
+ sourceEntity: sourceObject,
4920
+ targetEntity: targetObject,
4731
4921
  directory,
4732
4922
  fileClient
4733
4923
  });
4734
4924
  },
4925
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
4735
4926
  onBeforeWriteObject: async (sourceObject) => {
4736
- return extractAndDownloadUniformFilesForObject(sourceObject, {
4927
+ await downloadFilesForCompositionOrEntry({
4928
+ entity: sourceObject.object,
4737
4929
  directory,
4738
4930
  fileClient
4739
4931
  });
4932
+ return sourceObject;
4740
4933
  }
4741
4934
  });
4742
4935
  }
@@ -4821,12 +5014,15 @@ var EntryPatternPushModule = {
4821
5014
  allowEmptySource,
4822
5015
  log: createSyncEngineConsoleLogger({ diffMode }),
4823
5016
  onBeforeCompareObjects: async (sourceObject) => {
4824
- return swapOutUniformFileUrlsForTargetProject(sourceObject, {
5017
+ return replaceLocalUrlsWithRemoteReferences({
5018
+ entity: sourceObject,
4825
5019
  fileClient
4826
5020
  });
4827
5021
  },
5022
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
4828
5023
  onBeforeWriteObject: async (sourceObject) => {
4829
- return extractAndUploadUniformFilesForObject(sourceObject, {
5024
+ return uploadFilesForCompositionOrEntry({
5025
+ entity: sourceObject,
4830
5026
  directory,
4831
5027
  fileClient
4832
5028
  });
@@ -8182,7 +8378,7 @@ import { PostHog } from "posthog-node";
8182
8378
  // package.json
8183
8379
  var package_default = {
8184
8380
  name: "@uniformdev/cli",
8185
- version: "19.214.0",
8381
+ version: "20.0.0",
8186
8382
  description: "Uniform command line interface tool",
8187
8383
  license: "SEE LICENSE IN LICENSE.txt",
8188
8384
  main: "./cli.js",
@@ -8213,6 +8409,7 @@ var package_default = {
8213
8409
  "@uniformdev/canvas": "workspace:*",
8214
8410
  "@uniformdev/context": "workspace:*",
8215
8411
  "@uniformdev/files": "workspace:*",
8412
+ "@uniformdev/richtext": "workspace:*",
8216
8413
  "@uniformdev/project-map": "workspace:*",
8217
8414
  "@uniformdev/redirect": "workspace:*",
8218
8415
  "call-bind": "^1.0.2",
@@ -8222,11 +8419,11 @@ var package_default = {
8222
8419
  diff: "^5.0.0",
8223
8420
  dotenv: "^16.0.3",
8224
8421
  execa: "5.1.1",
8225
- "file-type": "^19.6.0",
8422
+ "file-type": "^20.0.0",
8226
8423
  "fs-jetpack": "5.1.0",
8227
8424
  graphql: "16.9.0",
8228
8425
  "graphql-request": "6.1.0",
8229
- "image-size": "^1.0.2",
8426
+ "image-size": "^1.2.0",
8230
8427
  inquirer: "9.2.17",
8231
8428
  "isomorphic-git": "1.25.2",
8232
8429
  "js-yaml": "^4.1.0",
@@ -8572,7 +8769,7 @@ ${err.message}`);
8572
8769
  // src/projects/cloneStarter.ts
8573
8770
  import crypto2 from "crypto";
8574
8771
  import fs3 from "fs";
8575
- import fsj2 from "fs-jetpack";
8772
+ import fsj5 from "fs-jetpack";
8576
8773
  import * as git from "isomorphic-git";
8577
8774
  import * as http from "isomorphic-git/http/node/index.js";
8578
8775
  import os from "os";
@@ -8603,7 +8800,7 @@ async function cloneStarter({
8603
8800
  throw new Error(`"${targetDir}" is not empty`);
8604
8801
  }
8605
8802
  const starterDir = path.join(cloneDir, ...pathSegments);
8606
- fsj2.copy(starterDir, targetDir, { overwrite: true });
8803
+ fsj5.copy(starterDir, targetDir, { overwrite: true });
8607
8804
  if (dotEnvFile) {
8608
8805
  fs3.writeFileSync(path.resolve(targetDir, ".env"), dotEnvFile, "utf-8");
8609
8806
  }
@@ -10583,14 +10780,14 @@ import { existsSync as existsSync4, promises as fs5 } from "fs";
10583
10780
  import { get as getHttp } from "http";
10584
10781
  import { get as getHttps } from "https";
10585
10782
  import { tmpdir } from "os";
10586
- import { join as join3 } from "path";
10783
+ import { join as join7 } from "path";
10587
10784
  import registryUrl from "registry-url";
10588
10785
  import { URL as URL2 } from "url";
10589
10786
  var compareVersions = (a, b) => a.localeCompare(b, "en-US", { numeric: true });
10590
10787
  var encode = (value) => encodeURIComponent(value).replace(/^%40/, "@");
10591
10788
  var getFile = async (details, distTag) => {
10592
10789
  const rootDir = tmpdir();
10593
- const subDir = join3(rootDir, "update-check");
10790
+ const subDir = join7(rootDir, "update-check");
10594
10791
  if (!existsSync4(subDir)) {
10595
10792
  await fs5.mkdir(subDir);
10596
10793
  }
@@ -10598,7 +10795,7 @@ var getFile = async (details, distTag) => {
10598
10795
  if (details.scope) {
10599
10796
  name = `${details.scope}-${name}`;
10600
10797
  }
10601
- return join3(subDir, name);
10798
+ return join7(subDir, name);
10602
10799
  };
10603
10800
  var evaluateCache = async (file, time, interval) => {
10604
10801
  if (existsSync4(file)) {
@@ -10753,7 +10950,7 @@ var checkForUpdateMiddleware = async ({ verbose }) => {
10753
10950
 
10754
10951
  // src/middleware/checkLocalDepsVersionsMiddleware.ts
10755
10952
  import { magenta, red as red5 } from "colorette";
10756
- import { join as join4 } from "path";
10953
+ import { join as join8 } from "path";
10757
10954
 
10758
10955
  // src/fs.ts
10759
10956
  import { promises as fs6 } from "fs";
@@ -10792,7 +10989,7 @@ var checkLocalDepsVersions = async (args) => {
10792
10989
  try {
10793
10990
  let isOutside = false;
10794
10991
  let warning = `${magenta("Warning:")} Installed Uniform packages should be the same version`;
10795
- const localPackages = await tryReadJSON(join4(process.cwd(), "package.json"));
10992
+ const localPackages = await tryReadJSON(join8(process.cwd(), "package.json"));
10796
10993
  if (!localPackages) return;
10797
10994
  let firstVersion;
10798
10995
  const allDependencies = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniformdev/cli",
3
- "version": "19.214.1-alpha.33+be2bde7eba",
3
+ "version": "19.214.1-alpha.34+476b7be79b",
4
4
  "description": "Uniform command line interface tool",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "main": "./cli.js",
@@ -27,12 +27,13 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@thi.ng/mime": "^2.2.23",
30
- "@uniformdev/assets": "19.214.1-alpha.33+be2bde7eba",
31
- "@uniformdev/canvas": "19.214.1-alpha.33+be2bde7eba",
32
- "@uniformdev/context": "19.214.1-alpha.33+be2bde7eba",
33
- "@uniformdev/files": "19.214.1-alpha.33+be2bde7eba",
34
- "@uniformdev/project-map": "19.214.1-alpha.33+be2bde7eba",
35
- "@uniformdev/redirect": "19.214.1-alpha.33+be2bde7eba",
30
+ "@uniformdev/assets": "19.214.1-alpha.34+476b7be79b",
31
+ "@uniformdev/canvas": "19.214.1-alpha.34+476b7be79b",
32
+ "@uniformdev/context": "19.214.1-alpha.34+476b7be79b",
33
+ "@uniformdev/files": "19.214.1-alpha.34+476b7be79b",
34
+ "@uniformdev/project-map": "19.214.1-alpha.34+476b7be79b",
35
+ "@uniformdev/redirect": "19.214.1-alpha.34+476b7be79b",
36
+ "@uniformdev/richtext": "19.214.1-alpha.34+476b7be79b",
36
37
  "call-bind": "^1.0.2",
37
38
  "colorette": "2.0.20",
38
39
  "cosmiconfig": "9.0.0",
@@ -40,11 +41,11 @@
40
41
  "diff": "^5.0.0",
41
42
  "dotenv": "^16.0.3",
42
43
  "execa": "5.1.1",
43
- "file-type": "^19.6.0",
44
+ "file-type": "^20.0.0",
44
45
  "fs-jetpack": "5.1.0",
45
46
  "graphql": "16.9.0",
46
47
  "graphql-request": "6.1.0",
47
- "image-size": "^1.0.2",
48
+ "image-size": "^1.2.0",
48
49
  "inquirer": "9.2.17",
49
50
  "isomorphic-git": "1.25.2",
50
51
  "js-yaml": "^4.1.0",
@@ -78,5 +79,5 @@
78
79
  "publishConfig": {
79
80
  "access": "public"
80
81
  },
81
- "gitHead": "be2bde7eba9949b3a2841fdac6d27e54565f7897"
82
+ "gitHead": "476b7be79b950e65641ffb8698a13bc5d9b966f8"
82
83
  }