@uniformdev/cli 19.214.1-alpha.17 → 19.214.1-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.
Files changed (2) hide show
  1. package/dist/index.mjs +493 -287
  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,383 @@ 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);
680
- };
656
+
657
+ // src/files/deleteDownloadedFileByUrl.ts
681
658
  var deleteDownloadedFileByUrl = async (url, options) => {
682
659
  const writeDirectory = getFilesDirectory(options.directory);
683
660
  const fileName = urlToFileName(url);
684
- const fileToDelete = join2(writeDirectory, FILES_DIRECTORY_NAME, fileName);
661
+ const fileToDelete = join3(writeDirectory, FILES_DIRECTORY_NAME, fileName);
685
662
  try {
686
663
  await fsj.removeAsync(fileToDelete);
687
664
  } catch {
688
665
  console.warn(`Failed to delete a local file ${fileToDelete}`);
689
666
  }
690
667
  };
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 () => {
668
+
669
+ // src/files/files.ts
670
+ import {
671
+ ASSETS_SOURCE_UNIFORM,
672
+ getPropertiesValue as getPropertiesValue2,
673
+ isAssetParamValue,
674
+ isAssetParamValueItem,
675
+ walkNodeTree as walkNodeTree2,
676
+ walkPropertyValues as walkPropertyValues2
677
+ } from "@uniformdev/canvas";
678
+ import { isRichTextNodeType, isRichTextValue, walkRichTextTree } from "@uniformdev/richtext";
679
+ import fsj4 from "fs-jetpack";
680
+ import PQueue3 from "p-queue";
681
+ import { join as join6 } from "path";
682
+
683
+ // src/files/downloadFile.ts
684
+ import fsj2 from "fs-jetpack";
685
+ import { join as join4 } from "path";
686
+ var downloadFile = async ({
687
+ fileClient,
688
+ fileUrl,
689
+ directory
690
+ }) => {
691
+ const writeDirectory = getFilesDirectory(directory);
692
+ const fileName = urlToFileName(fileUrl.toString());
693
+ const fileAlreadyExists = await fsj2.existsAsync(join4(writeDirectory, FILES_DIRECTORY_NAME, fileName));
694
+ if (fileAlreadyExists) {
695
+ return { url: fileUrl };
696
+ }
697
+ const file = await fileClient.get({ url: fileUrl }).catch(() => null);
698
+ if (!file) {
699
+ console.warn(`Skipping file ${fileUrl} as it does not exist in the project anymore`);
700
+ return null;
701
+ }
702
+ if (file.sourceId) {
703
+ try {
704
+ const hashAlreadyExists = await fsj2.findAsync(join4(writeDirectory, FILES_DIRECTORY_NAME), {
705
+ matching: [file.sourceId, `${file.sourceId}.*`]
706
+ });
707
+ if (hashAlreadyExists.length > 0) {
708
+ return { id: file.id, url: fileUrl };
709
+ }
710
+ } catch {
711
+ }
712
+ }
713
+ const fetchUrl = `${fileUrl}?format=original`;
714
+ const response = await fetch(fetchUrl);
715
+ if (!response.ok) {
716
+ return null;
717
+ }
718
+ const fileBuffer = await response.arrayBuffer();
719
+ await fsj2.writeAsync(join4(writeDirectory, FILES_DIRECTORY_NAME, fileName), Buffer.from(fileBuffer));
720
+ return { id: file.id, url: fileUrl };
721
+ };
722
+
723
+ // src/files/uploadFile.ts
724
+ import { preferredType } from "@thi.ng/mime";
725
+ import { FILE_READY_STATE, getFileNameFromUrl } from "@uniformdev/files";
726
+ import { fileTypeFromBuffer } from "file-type";
727
+ import fsj3 from "fs-jetpack";
728
+ import sizeOf from "image-size";
729
+ import PQueue from "p-queue";
730
+ import { join as join5 } from "path";
731
+ var fileUploadQueue = new PQueue({ concurrency: 10 });
732
+ var uploadFile = async ({
733
+ fileClient,
734
+ fileUrl,
735
+ directory,
736
+ fileId
737
+ }) => {
738
+ return await fileUploadQueue.add(async () => {
739
+ try {
740
+ const writeDirectory = getFilesDirectory(directory);
741
+ const hash = urlToHash(fileUrl);
742
+ const fileAlreadyExistsChecks = await Promise.all([
743
+ fileClient.get({ url: fileUrl }).catch(() => null),
744
+ fileClient.get({ sourceId: hash }).catch(() => null)
745
+ ]);
746
+ const file = fileAlreadyExistsChecks.find((check) => check !== null);
747
+ if (file?.url) {
748
+ return { id: file.id, url: file.url };
749
+ }
750
+ const localFileName = urlToFileName(fileUrl);
751
+ const expectedFilePath = join5(writeDirectory, FILES_DIRECTORY_NAME, localFileName);
752
+ const fileExistsLocally = await fsj3.existsAsync(expectedFilePath);
753
+ if (!fileExistsLocally) {
754
+ console.warn(
755
+ `Skipping file ${fileUrl} as we couldn't find a local copy (looked at ${expectedFilePath})`
756
+ );
757
+ return null;
758
+ }
759
+ const fileBuffer = await fsj3.readAsync(expectedFilePath, "buffer");
760
+ if (!fileBuffer) {
761
+ console.warn(`Skipping file ${fileUrl} (${expectedFilePath}) as we couldn't read it`);
762
+ return null;
763
+ }
764
+ const fileName = getFileNameFromUrl(fileUrl);
765
+ let mimeType = expectedFilePath.endsWith(".svg") ? "image/svg+xml" : (await fileTypeFromBuffer(fileBuffer))?.mime;
766
+ if (!mimeType) {
767
+ mimeType = preferredType(fileUrl.split(".").at(-1) ?? "");
768
+ }
769
+ if (mimeType === "audio/x-flac") {
770
+ mimeType = "audio/flac";
771
+ }
772
+ const { width, height } = (() => {
773
+ if (!mimeType.startsWith("image/")) {
774
+ return {
775
+ width: void 0,
776
+ height: void 0
777
+ };
778
+ }
700
779
  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));
780
+ return sizeOf(fileBuffer);
731
781
  } catch {
732
- console.warn(`Failed to download file ${url}`);
782
+ return {
783
+ width: void 0,
784
+ height: void 0
785
+ };
733
786
  }
787
+ })();
788
+ const { id, method, uploadUrl } = await fileClient.insert({
789
+ id: fileId,
790
+ name: fileName,
791
+ mediaType: mimeType,
792
+ size: fileBuffer.length,
793
+ width,
794
+ height,
795
+ sourceId: hash
734
796
  });
797
+ const uploadResponse = await fetch(uploadUrl, {
798
+ method,
799
+ body: fileBuffer,
800
+ headers: {
801
+ "Content-Type": mimeType,
802
+ "Content-Length": fileBuffer.length.toString()
803
+ }
804
+ });
805
+ if (!uploadResponse.ok) {
806
+ console.warn(`Failed to upload file ${fileUrl} (${expectedFilePath})`);
807
+ return null;
808
+ }
809
+ let error;
810
+ const checkForFile = async () => {
811
+ if (error) {
812
+ throw error;
813
+ }
814
+ const file2 = await fileClient.get({ id });
815
+ if (!file2 || file2.state !== FILE_READY_STATE || !file2.url) {
816
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
817
+ return checkForFile();
818
+ }
819
+ return file2.url;
820
+ };
821
+ const abortTimeout = setTimeout(() => {
822
+ error = new Error(`Failed to upload file ${fileUrl} (${expectedFilePath}) - upload timed out`);
823
+ }, 6e4);
824
+ const uploadedFileUrl = await checkForFile();
825
+ clearTimeout(abortTimeout);
826
+ return { id, url: uploadedFileUrl };
827
+ } catch (e) {
828
+ console.warn(`Failed to upload file ${fileUrl}`, e);
829
+ return null;
735
830
  }
736
- await fileDownloadQueue.onIdle();
737
- }
738
- return object;
831
+ }) ?? null;
739
832
  };
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;
833
+
834
+ // src/files/walkFilesForCompositionOrEntry.ts
835
+ import {
836
+ getPropertiesValue,
837
+ walkNodeTree,
838
+ walkPropertyValues
839
+ } from "@uniformdev/canvas";
840
+ import PQueue2 from "p-queue";
841
+ var UNIFORM_FILE_MATCH = /"(https:\/\/([^"]*?)?(img|files)\.uniform\.(rocks|global)\/([^"]*?))"/g;
842
+ var walkFilesForCompositionOrEntry = async ({
843
+ entity,
844
+ directory,
845
+ fileClient,
846
+ callback
847
+ }) => {
848
+ const urlReplacementMap = /* @__PURE__ */ new Map();
849
+ const fileDownloadQueue = new PQueue2({ concurrency: 3 });
850
+ const thumbnail = "entry" in entity ? entity.entry._thumbnail : void 0;
851
+ if (typeof thumbnail === "string") {
852
+ const isUniformFile = `"${thumbnail}"`.match(UNIFORM_FILE_MATCH) !== null;
853
+ if (isUniformFile) {
854
+ fileDownloadQueue.add(async () => {
855
+ const result = await callback({ fileUrl: thumbnail, directory, fileClient });
856
+ if (result) {
857
+ urlReplacementMap.set(thumbnail, result.url);
858
+ }
859
+ });
860
+ }
861
+ }
862
+ walkNodeTree("entry" in entity ? entity.entry : entity.composition, ({ node }) => {
863
+ const properties = getPropertiesValue(node);
864
+ if (!properties) {
865
+ return;
866
+ }
867
+ Object.entries(properties).forEach(([_, property]) => {
868
+ if (property.type !== "image") {
869
+ return;
870
+ }
871
+ walkPropertyValues(property, ({ value }) => {
872
+ if (typeof value !== "string") {
873
+ return;
874
+ }
875
+ const isUniformFile = `"${value}"`.match(UNIFORM_FILE_MATCH) !== null;
876
+ if (!isUniformFile) {
877
+ return;
878
+ }
879
+ fileDownloadQueue.add(async () => {
880
+ const result = await callback({ fileUrl: value, directory, fileClient });
881
+ if (result) {
882
+ urlReplacementMap.set(value, result.url);
768
883
  }
769
- const fileBuffer = await fsj.readAsync(expectedFilePath, "buffer");
770
- if (!fileBuffer) {
771
- console.warn(`Skipping file ${url} (${expectedFilePath}) as we couldn't read it`);
884
+ });
885
+ });
886
+ });
887
+ });
888
+ await fileDownloadQueue.onIdle();
889
+ return urlReplacementMap;
890
+ };
891
+
892
+ // src/files/files.ts
893
+ var downloadFileForAsset = async ({
894
+ asset,
895
+ directory,
896
+ fileClient
897
+ }) => {
898
+ if (asset.asset.fields?.file?.value === void 0 || asset.asset.fields.url?.value === void 0) {
899
+ return null;
900
+ }
901
+ const fileId = asset.asset.fields?.file?.value;
902
+ const fileUrl = asset.asset.fields.url?.value;
903
+ if (fileId === "" || fileUrl === "") {
904
+ return null;
905
+ }
906
+ return downloadFile({ fileUrl, directory, fileClient });
907
+ };
908
+ var uploadFileForAsset = async ({
909
+ asset,
910
+ directory,
911
+ fileClient
912
+ }) => {
913
+ if (asset.asset.fields?.file?.value === void 0 || asset.asset.fields.url?.value === void 0) {
914
+ return null;
915
+ }
916
+ const fileUrl = asset.asset.fields.url.value;
917
+ const fileId = asset.asset.fields.file.value;
918
+ return uploadFile({ fileUrl, directory, fileClient, fileId });
919
+ };
920
+ var removeUrlsFromAssetParameters = (entity) => {
921
+ walkNodeTree2("entry" in entity ? entity.entry : entity.composition, ({ node }) => {
922
+ const properties = getPropertiesValue2(node);
923
+ if (!properties) {
924
+ return;
925
+ }
926
+ Object.entries(properties).forEach(([_, property]) => {
927
+ if (property.type === "asset") {
928
+ walkPropertyValues2(property, ({ value }) => {
929
+ if (!isAssetParamValue(value)) {
772
930
  return;
773
931
  }
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
- };
932
+ value.forEach((asset) => {
933
+ if (!isAssetParamValueItem(asset)) {
934
+ return;
788
935
  }
789
- try {
790
- return sizeOf(fileBuffer);
791
- } catch {
792
- return {
793
- width: void 0,
794
- height: void 0
795
- };
796
- }
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()
936
+ if (asset._source !== ASSETS_SOURCE_UNIFORM || !asset.fields?.url.value) {
937
+ return;
812
938
  }
939
+ asset.fields.url.value = "";
813
940
  });
814
- if (!uploadResponse.ok) {
815
- console.warn(`Failed to upload file ${url} (${expectedFilePath})`);
941
+ });
942
+ } else if (property.type === "richText") {
943
+ walkPropertyValues2(property, ({ value }) => {
944
+ if (!isRichTextValue(value)) {
816
945
  return;
817
946
  }
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();
947
+ walkRichTextTree(value.root, (node2) => {
948
+ if (isRichTextNodeType(node2, "asset")) {
949
+ if (node2.__asset?._source !== ASSETS_SOURCE_UNIFORM || !node2.__asset.fields.url.value) {
950
+ return;
951
+ }
952
+ node2.__asset.fields.url.value = "";
827
953
  }
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
- }
839
- });
840
- }
841
- await fileUploadQueue.onIdle();
954
+ });
955
+ });
956
+ }
957
+ });
958
+ });
959
+ return entity;
960
+ };
961
+ var compareCompositionsOrEntriesWithoutAssetUrls = (source, target) => {
962
+ return serializedDequal(
963
+ removeUrlsFromAssetParameters(structuredClone(source.object)),
964
+ removeUrlsFromAssetParameters(structuredClone(target.object))
965
+ );
966
+ };
967
+ var removeUrlFromAsset = (asset) => {
968
+ if (asset.asset.fields?.url?.value) {
969
+ asset.asset.fields.url.value = "";
842
970
  }
843
- return JSON.parse(objectAsString);
971
+ return asset;
844
972
  };
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 {
865
- }
866
- });
867
- }
868
- await fileUrlReplacementQueue.onIdle();
973
+ var compareAssetsWithoutUrls = (source, target) => {
974
+ return serializedDequal(
975
+ removeUrlFromAsset(structuredClone(source.object)),
976
+ removeUrlFromAsset(structuredClone(target.object))
977
+ );
978
+ };
979
+ var downloadFilesForCompositionOrEntry = async ({
980
+ entity,
981
+ directory,
982
+ fileClient
983
+ }) => {
984
+ await walkFilesForCompositionOrEntry({
985
+ entity,
986
+ directory,
987
+ fileClient,
988
+ callback: downloadFile
989
+ });
990
+ };
991
+ var uploadFilesForCompositionOrEntry = async ({
992
+ entity,
993
+ directory,
994
+ fileClient
995
+ }) => {
996
+ const replacements = await walkFilesForCompositionOrEntry({
997
+ entity: entity.object,
998
+ directory,
999
+ fileClient,
1000
+ callback: uploadFile
1001
+ });
1002
+ let entityAsString = JSON.stringify(entity);
1003
+ for (const [key, value] of replacements.entries()) {
1004
+ entityAsString = entityAsString.replaceAll(`"${key}"`, `"${value}"`);
869
1005
  }
870
- return JSON.parse(objectAsString);
1006
+ return JSON.parse(entityAsString);
871
1007
  };
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];
1008
+ var replaceRemoteUrlsWithLocalReferences = async ({
1009
+ sourceEntity,
1010
+ targetEntity,
1011
+ fileClient,
1012
+ directory
1013
+ }) => {
1014
+ let sourceEntityAsString = JSON.stringify(sourceEntity);
1015
+ const targetEntityAsString = JSON.stringify(targetEntity);
1016
+ const writeDirectory = getFilesDirectory(directory);
1017
+ const fileUrlReplacementQueue = new PQueue3({ concurrency: 3 });
1018
+ await walkFilesForCompositionOrEntry({
1019
+ entity: sourceEntity.object,
1020
+ directory,
1021
+ fileClient,
1022
+ callback: async ({ fileUrl }) => {
881
1023
  fileUrlReplacementQueue.add(async () => {
882
1024
  try {
883
- const localFileName = urlToFileName(url);
884
- const fileExistsLocally = await fsj.existsAsync(
885
- join2(writeDirectory, FILES_DIRECTORY_NAME, localFileName)
1025
+ const localFileName = urlToFileName(fileUrl);
1026
+ const fileExistsLocally = await fsj4.existsAsync(
1027
+ join6(writeDirectory, FILES_DIRECTORY_NAME, localFileName)
886
1028
  );
887
1029
  if (fileExistsLocally) {
888
1030
  return;
889
1031
  }
890
- const file = await options.fileClient.get({ url }).catch(() => null);
1032
+ const file = await fileClient.get({ url: fileUrl }).catch(() => null);
891
1033
  if (!file || !file.sourceId) {
892
1034
  return;
893
1035
  }
@@ -895,36 +1037,74 @@ var replaceRemoteUrlsWithLocalReferences = async (sourceObject, targetObject, op
895
1037
  if (!originalPartialPath) {
896
1038
  return;
897
1039
  }
898
- const originalUrl = findUrlMatchingPartialPathname(targetObjectAsString, originalPartialPath);
1040
+ const originalUrl = findUrlMatchingPartialPathname(targetEntityAsString, originalPartialPath);
899
1041
  if (!originalUrl) {
900
1042
  return;
901
1043
  }
902
- sourceObjectAsString = sourceObjectAsString.replaceAll(`"${url}"`, `"${originalUrl}"`);
1044
+ sourceEntityAsString = sourceEntityAsString.replaceAll(`"${fileUrl}"`, `"${originalUrl}"`);
903
1045
  } catch {
904
1046
  }
905
1047
  });
1048
+ return null;
906
1049
  }
907
- await fileUrlReplacementQueue.onIdle();
908
- }
909
- return JSON.parse(sourceObjectAsString);
1050
+ });
1051
+ await fileUrlReplacementQueue.onIdle();
1052
+ return JSON.parse(sourceEntityAsString);
910
1053
  };
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;
1054
+ var replaceLocalUrlsWithRemoteReferences = async ({
1055
+ entity,
1056
+ directory,
1057
+ fileClient
1058
+ }) => {
1059
+ let entityAsString = JSON.stringify(entity);
1060
+ const fileUrlReplacementQueue = new PQueue3({ concurrency: 3 });
1061
+ await walkFilesForCompositionOrEntry({
1062
+ entity: entity.object,
1063
+ directory,
1064
+ fileClient,
1065
+ callback: async ({ fileUrl }) => {
1066
+ fileUrlReplacementQueue.add(async () => {
1067
+ try {
1068
+ const hash = urlToHash(fileUrl);
1069
+ fileUrlReplacementQueue.add(async () => {
1070
+ try {
1071
+ const file = await fileClient.get({ sourceId: hash }).catch(() => null);
1072
+ if (!file) {
1073
+ return;
1074
+ }
1075
+ entityAsString = entityAsString.replaceAll(`"${fileUrl}"`, `"${file.url}"`);
1076
+ } catch {
1077
+ }
1078
+ });
1079
+ } catch {
1080
+ }
1081
+ });
1082
+ return null;
1083
+ }
1084
+ });
1085
+ await fileUrlReplacementQueue.onIdle();
1086
+ return JSON.parse(entityAsString);
1087
+ };
1088
+ var escapeRegExp = (string) => {
1089
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1090
+ };
1091
+ var hashToPartialPathname = (hash) => {
1092
+ try {
1093
+ return Buffer.from(hash, "base64").toString("utf8");
1094
+ } catch {
1095
+ return null;
918
1096
  }
919
- const file = await options.fileClient.get({ url: fileUrl }).catch(() => null);
920
- if (!file) {
921
- return asset;
1097
+ };
1098
+ var findUrlMatchingPartialPathname = (source, pathname) => {
1099
+ const escapedPathname = escapeRegExp(pathname);
1100
+ const regex = new RegExp(
1101
+ `"(https://([^"]*?)?(img|files).uniform.(rocks|global)${escapedPathname}([^"]*?))"`
1102
+ );
1103
+ const match = source.match(regex);
1104
+ if (match && match[1]) {
1105
+ return match[1];
922
1106
  }
923
- asset.asset.fields.file = {
924
- type: "file",
925
- value: file.id
926
- };
927
- return asset;
1107
+ return null;
928
1108
  };
929
1109
 
930
1110
  // src/commands/canvas/assetEngineDataSource.ts
@@ -1115,27 +1295,25 @@ var AssetPullModule = {
1115
1295
  whatIf,
1116
1296
  allowEmptySource: allowEmptySource ?? true,
1117
1297
  log: createSyncEngineConsoleLogger({ diffMode }),
1118
- onBeforeCompareObjects: async (sourceObject, targetObject) => {
1298
+ onBeforeCompareObjects: async (sourceObject) => {
1119
1299
  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;
1300
+ return sourceObject;
1132
1301
  },
1302
+ compareContents: compareAssetsWithoutUrls,
1133
1303
  onBeforeWriteObject: async (sourceObject) => {
1134
1304
  delete sourceObject.object.asset._author;
1135
- return extractAndDownloadUniformFilesForObject(sourceObject, {
1305
+ if (!sourceObject.object.asset.fields?.file) {
1306
+ return sourceObject;
1307
+ }
1308
+ const downloadedFile = await downloadFileForAsset({
1309
+ asset: sourceObject.object,
1136
1310
  directory,
1137
1311
  fileClient
1138
1312
  });
1313
+ if (downloadedFile?.id) {
1314
+ sourceObject.object.asset.fields.file.value = downloadedFile.id;
1315
+ }
1316
+ return sourceObject;
1139
1317
  }
1140
1318
  });
1141
1319
  }
@@ -1215,29 +1393,29 @@ var AssetPushModule = {
1215
1393
  if (targetObject) {
1216
1394
  delete targetObject.object.asset._author;
1217
1395
  }
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;
1396
+ return sourceObject;
1228
1397
  },
1398
+ compareContents: compareAssetsWithoutUrls,
1229
1399
  onBeforeWriteObject: async (sourceObject) => {
1230
- const sourceObjectWithNewFileUrls = await extractAndUploadUniformFilesForObject(sourceObject, {
1400
+ const uploadedFile = await uploadFileForAsset({
1401
+ asset: sourceObject.object,
1231
1402
  directory,
1232
1403
  fileClient
1233
1404
  });
1234
- sourceObjectWithNewFileUrls.object = await updateAssetFileIdBasedOnUrl(
1235
- sourceObjectWithNewFileUrls.object,
1236
- {
1237
- fileClient
1405
+ if (uploadedFile !== null) {
1406
+ if (sourceObject.object.asset.fields === void 0) {
1407
+ sourceObject.object.asset.fields = {};
1238
1408
  }
1239
- );
1240
- return sourceObjectWithNewFileUrls;
1409
+ sourceObject.object.asset.fields.file = {
1410
+ type: "file",
1411
+ value: uploadedFile.id
1412
+ };
1413
+ sourceObject.object.asset.fields.url = {
1414
+ type: "text",
1415
+ value: uploadedFile.url
1416
+ };
1417
+ }
1418
+ return sourceObject;
1241
1419
  }
1242
1420
  });
1243
1421
  }
@@ -2505,16 +2683,21 @@ var CompositionPullModule = {
2505
2683
  allowEmptySource: allowEmptySource ?? true,
2506
2684
  log: createSyncEngineConsoleLogger({ diffMode }),
2507
2685
  onBeforeCompareObjects: async (sourceObject, targetObject) => {
2508
- return replaceRemoteUrlsWithLocalReferences(sourceObject, targetObject, {
2686
+ return replaceRemoteUrlsWithLocalReferences({
2687
+ sourceEntity: sourceObject,
2688
+ targetEntity: targetObject,
2509
2689
  directory,
2510
2690
  fileClient
2511
2691
  });
2512
2692
  },
2693
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
2513
2694
  onBeforeWriteObject: async (sourceObject) => {
2514
- return extractAndDownloadUniformFilesForObject(sourceObject, {
2695
+ await downloadFilesForCompositionOrEntry({
2696
+ entity: sourceObject.object,
2515
2697
  directory,
2516
2698
  fileClient
2517
2699
  });
2700
+ return sourceObject;
2518
2701
  }
2519
2702
  });
2520
2703
  }
@@ -2658,12 +2841,16 @@ var CompositionPushModule = {
2658
2841
  allowEmptySource,
2659
2842
  log: createSyncEngineConsoleLogger({ diffMode }),
2660
2843
  onBeforeCompareObjects: async (sourceObject) => {
2661
- return swapOutUniformFileUrlsForTargetProject(sourceObject, {
2844
+ return replaceLocalUrlsWithRemoteReferences({
2845
+ entity: sourceObject,
2846
+ directory,
2662
2847
  fileClient
2663
2848
  });
2664
2849
  },
2850
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
2665
2851
  onBeforeWriteObject: async (sourceObject) => {
2666
- return extractAndUploadUniformFilesForObject(sourceObject, {
2852
+ return uploadFilesForCompositionOrEntry({
2853
+ entity: sourceObject,
2667
2854
  directory,
2668
2855
  fileClient
2669
2856
  });
@@ -4257,16 +4444,21 @@ var EntryPullModule = {
4257
4444
  allowEmptySource: allowEmptySource ?? true,
4258
4445
  log: createSyncEngineConsoleLogger({ diffMode }),
4259
4446
  onBeforeCompareObjects: async (sourceObject, targetObject) => {
4260
- return replaceRemoteUrlsWithLocalReferences(sourceObject, targetObject, {
4447
+ return replaceRemoteUrlsWithLocalReferences({
4448
+ sourceEntity: sourceObject,
4449
+ targetEntity: targetObject,
4261
4450
  directory,
4262
4451
  fileClient
4263
4452
  });
4264
4453
  },
4454
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
4265
4455
  onBeforeWriteObject: async (sourceObject) => {
4266
- return extractAndDownloadUniformFilesForObject(sourceObject, {
4456
+ await downloadFilesForCompositionOrEntry({
4457
+ entity: sourceObject.object,
4267
4458
  directory,
4268
4459
  fileClient
4269
4460
  });
4461
+ return sourceObject;
4270
4462
  }
4271
4463
  });
4272
4464
  }
@@ -4346,12 +4538,16 @@ var EntryPushModule = {
4346
4538
  allowEmptySource,
4347
4539
  log: createSyncEngineConsoleLogger({ diffMode }),
4348
4540
  onBeforeCompareObjects: async (sourceObject) => {
4349
- return swapOutUniformFileUrlsForTargetProject(sourceObject, {
4541
+ return replaceLocalUrlsWithRemoteReferences({
4542
+ entity: sourceObject,
4543
+ directory,
4350
4544
  fileClient
4351
4545
  });
4352
4546
  },
4547
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
4353
4548
  onBeforeWriteObject: async (sourceObject) => {
4354
- return extractAndUploadUniformFilesForObject(sourceObject, {
4549
+ return uploadFilesForCompositionOrEntry({
4550
+ entity: sourceObject,
4355
4551
  directory,
4356
4552
  fileClient
4357
4553
  });
@@ -4727,16 +4923,21 @@ var EntryPatternPullModule = {
4727
4923
  allowEmptySource: allowEmptySource ?? true,
4728
4924
  log: createSyncEngineConsoleLogger({ diffMode }),
4729
4925
  onBeforeCompareObjects: async (sourceObject, targetObject) => {
4730
- return replaceRemoteUrlsWithLocalReferences(sourceObject, targetObject, {
4926
+ return replaceRemoteUrlsWithLocalReferences({
4927
+ sourceEntity: sourceObject,
4928
+ targetEntity: targetObject,
4731
4929
  directory,
4732
4930
  fileClient
4733
4931
  });
4734
4932
  },
4933
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
4735
4934
  onBeforeWriteObject: async (sourceObject) => {
4736
- return extractAndDownloadUniformFilesForObject(sourceObject, {
4935
+ await downloadFilesForCompositionOrEntry({
4936
+ entity: sourceObject.object,
4737
4937
  directory,
4738
4938
  fileClient
4739
4939
  });
4940
+ return sourceObject;
4740
4941
  }
4741
4942
  });
4742
4943
  }
@@ -4821,12 +5022,16 @@ var EntryPatternPushModule = {
4821
5022
  allowEmptySource,
4822
5023
  log: createSyncEngineConsoleLogger({ diffMode }),
4823
5024
  onBeforeCompareObjects: async (sourceObject) => {
4824
- return swapOutUniformFileUrlsForTargetProject(sourceObject, {
5025
+ return replaceLocalUrlsWithRemoteReferences({
5026
+ entity: sourceObject,
5027
+ directory,
4825
5028
  fileClient
4826
5029
  });
4827
5030
  },
5031
+ compareContents: compareCompositionsOrEntriesWithoutAssetUrls,
4828
5032
  onBeforeWriteObject: async (sourceObject) => {
4829
- return extractAndUploadUniformFilesForObject(sourceObject, {
5033
+ return uploadFilesForCompositionOrEntry({
5034
+ entity: sourceObject,
4830
5035
  directory,
4831
5036
  fileClient
4832
5037
  });
@@ -8213,6 +8418,7 @@ var package_default = {
8213
8418
  "@uniformdev/canvas": "workspace:*",
8214
8419
  "@uniformdev/context": "workspace:*",
8215
8420
  "@uniformdev/files": "workspace:*",
8421
+ "@uniformdev/richtext": "workspace:*",
8216
8422
  "@uniformdev/project-map": "workspace:*",
8217
8423
  "@uniformdev/redirect": "workspace:*",
8218
8424
  "call-bind": "^1.0.2",
@@ -8222,11 +8428,11 @@ var package_default = {
8222
8428
  diff: "^5.0.0",
8223
8429
  dotenv: "^16.0.3",
8224
8430
  execa: "5.1.1",
8225
- "file-type": "^19.6.0",
8431
+ "file-type": "^20.0.0",
8226
8432
  "fs-jetpack": "5.1.0",
8227
8433
  graphql: "16.9.0",
8228
8434
  "graphql-request": "6.1.0",
8229
- "image-size": "^1.0.2",
8435
+ "image-size": "^1.2.0",
8230
8436
  inquirer: "9.2.17",
8231
8437
  "isomorphic-git": "1.25.2",
8232
8438
  "js-yaml": "^4.1.0",
@@ -8572,7 +8778,7 @@ ${err.message}`);
8572
8778
  // src/projects/cloneStarter.ts
8573
8779
  import crypto2 from "crypto";
8574
8780
  import fs3 from "fs";
8575
- import fsj2 from "fs-jetpack";
8781
+ import fsj5 from "fs-jetpack";
8576
8782
  import * as git from "isomorphic-git";
8577
8783
  import * as http from "isomorphic-git/http/node/index.js";
8578
8784
  import os from "os";
@@ -8603,7 +8809,7 @@ async function cloneStarter({
8603
8809
  throw new Error(`"${targetDir}" is not empty`);
8604
8810
  }
8605
8811
  const starterDir = path.join(cloneDir, ...pathSegments);
8606
- fsj2.copy(starterDir, targetDir, { overwrite: true });
8812
+ fsj5.copy(starterDir, targetDir, { overwrite: true });
8607
8813
  if (dotEnvFile) {
8608
8814
  fs3.writeFileSync(path.resolve(targetDir, ".env"), dotEnvFile, "utf-8");
8609
8815
  }
@@ -10583,14 +10789,14 @@ import { existsSync as existsSync4, promises as fs5 } from "fs";
10583
10789
  import { get as getHttp } from "http";
10584
10790
  import { get as getHttps } from "https";
10585
10791
  import { tmpdir } from "os";
10586
- import { join as join3 } from "path";
10792
+ import { join as join7 } from "path";
10587
10793
  import registryUrl from "registry-url";
10588
10794
  import { URL as URL2 } from "url";
10589
10795
  var compareVersions = (a, b) => a.localeCompare(b, "en-US", { numeric: true });
10590
10796
  var encode = (value) => encodeURIComponent(value).replace(/^%40/, "@");
10591
10797
  var getFile = async (details, distTag) => {
10592
10798
  const rootDir = tmpdir();
10593
- const subDir = join3(rootDir, "update-check");
10799
+ const subDir = join7(rootDir, "update-check");
10594
10800
  if (!existsSync4(subDir)) {
10595
10801
  await fs5.mkdir(subDir);
10596
10802
  }
@@ -10598,7 +10804,7 @@ var getFile = async (details, distTag) => {
10598
10804
  if (details.scope) {
10599
10805
  name = `${details.scope}-${name}`;
10600
10806
  }
10601
- return join3(subDir, name);
10807
+ return join7(subDir, name);
10602
10808
  };
10603
10809
  var evaluateCache = async (file, time, interval) => {
10604
10810
  if (existsSync4(file)) {
@@ -10753,7 +10959,7 @@ var checkForUpdateMiddleware = async ({ verbose }) => {
10753
10959
 
10754
10960
  // src/middleware/checkLocalDepsVersionsMiddleware.ts
10755
10961
  import { magenta, red as red5 } from "colorette";
10756
- import { join as join4 } from "path";
10962
+ import { join as join8 } from "path";
10757
10963
 
10758
10964
  // src/fs.ts
10759
10965
  import { promises as fs6 } from "fs";
@@ -10792,7 +10998,7 @@ var checkLocalDepsVersions = async (args) => {
10792
10998
  try {
10793
10999
  let isOutside = false;
10794
11000
  let warning = `${magenta("Warning:")} Installed Uniform packages should be the same version`;
10795
- const localPackages = await tryReadJSON(join4(process.cwd(), "package.json"));
11001
+ const localPackages = await tryReadJSON(join8(process.cwd(), "package.json"));
10796
11002
  if (!localPackages) return;
10797
11003
  let firstVersion;
10798
11004
  const allDependencies = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniformdev/cli",
3
- "version": "19.214.1-alpha.17+15e689c12c",
3
+ "version": "19.214.1-alpha.32+fb2084b713",
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.17+15e689c12c",
31
- "@uniformdev/canvas": "19.214.1-alpha.17+15e689c12c",
32
- "@uniformdev/context": "19.214.1-alpha.17+15e689c12c",
33
- "@uniformdev/files": "19.214.1-alpha.17+15e689c12c",
34
- "@uniformdev/project-map": "19.214.1-alpha.17+15e689c12c",
35
- "@uniformdev/redirect": "19.214.1-alpha.17+15e689c12c",
30
+ "@uniformdev/assets": "19.214.1-alpha.32+fb2084b713",
31
+ "@uniformdev/canvas": "19.214.1-alpha.32+fb2084b713",
32
+ "@uniformdev/context": "19.214.1-alpha.32+fb2084b713",
33
+ "@uniformdev/files": "19.214.1-alpha.32+fb2084b713",
34
+ "@uniformdev/project-map": "19.214.1-alpha.32+fb2084b713",
35
+ "@uniformdev/redirect": "19.214.1-alpha.32+fb2084b713",
36
+ "@uniformdev/richtext": "19.214.1-alpha.32+fb2084b713",
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": "15e689c12ce848ce17d170f9be8c6f3d2c29ffe1"
82
+ "gitHead": "fb2084b713e551eb79a28c6d8a372f3b3038d02e"
82
83
  }