@shipstatic/drop 2.10.4-beta.2 → 2.10.4-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -932,6 +932,8 @@ function isZipFile(file) {
932
932
  }
933
933
 
934
934
  // src/process.ts
935
+ var CANT_DEPLOY = "Can't deploy this";
936
+ var COULDNT_PREPARE = "Couldn't prepare the files";
935
937
  function toValidatable(file) {
936
938
  return { name: file.path, size: file.size };
937
939
  }
@@ -983,7 +985,7 @@ async function processFiles(input, { limits, onStatus }) {
983
985
  if (!hasIndex) {
984
986
  const details = needsBuild ? "No index.html found \u2014 every web project needs an index.html entry point" : "No index.html at root \u2014 the entry point must be in the top-level directory";
985
987
  return failure({
986
- title: "Validation Failed",
988
+ title: CANT_DEPLOY,
987
989
  details,
988
990
  sourceName,
989
991
  needsBuild,
@@ -1011,16 +1013,16 @@ async function processFiles(input, { limits, onStatus }) {
1011
1013
  statusMessage: validation.files[i]?.statusMessage ?? file.statusMessage
1012
1014
  }));
1013
1015
  if (!validation.canDeploy) {
1016
+ const issues = validation.errors.map((e) => e.message);
1014
1017
  return failure({
1015
- title: "Validation Failed",
1016
- details: `${pluralize(validation.errors.length, "file", "files", true)} failed validation`,
1017
- errors: validation.errors.map((e) => `${e.file}: ${e.message}`),
1018
+ title: CANT_DEPLOY,
1019
+ ...issues.length === 1 ? { details: issues[0] } : { details: `${issues.length} files refused`, errors: issues },
1018
1020
  files: validated,
1019
1021
  sourceName
1020
1022
  });
1021
1023
  }
1022
1024
  const readyCount = validated.filter((f) => f.status === FileValidationStatus.READY).length;
1023
- const warnings = validation.warnings.map((w) => `${w.file}: ${w.message}`);
1025
+ const warnings = validation.warnings.map((w) => w.message);
1024
1026
  if (readyCount > 0) {
1025
1027
  let details = `${pluralize(readyCount, "file", "files", true)} ready`;
1026
1028
  if (validation.warnings.length > 0) {
@@ -1034,30 +1036,22 @@ async function processFiles(input, { limits, onStatus }) {
1034
1036
  status: { title: "Ready", details, ...warnings.length ? { warnings } : {} }
1035
1037
  };
1036
1038
  }
1037
- if (validation.errors.length === 0 && validation.warnings.length > 0) {
1038
- return {
1039
- phase: "ready",
1040
- files: validated,
1041
- sourceName,
1042
- needsBuild: false,
1043
- status: {
1044
- title: "All files excluded",
1045
- details: `${pluralize(validation.warnings.length, "file", "files", true)} excluded (empty files cannot be deployed)`,
1046
- warnings
1047
- }
1048
- };
1049
- }
1050
- return failure({
1051
- title: "No Valid Files",
1052
- details: "None of the provided files could be processed.",
1039
+ return {
1040
+ phase: "ready",
1053
1041
  files: validated,
1054
- sourceName
1055
- });
1042
+ sourceName,
1043
+ needsBuild: false,
1044
+ status: {
1045
+ title: "All files excluded",
1046
+ details: `${pluralize(validation.warnings.length, "file", "files", true)} excluded (empty files cannot be deployed)`,
1047
+ warnings
1048
+ }
1049
+ };
1056
1050
  } catch (error) {
1057
1051
  const message = error instanceof Error ? error.message : String(error);
1058
1052
  const isValidation = isShipError(error);
1059
1053
  return failure({
1060
- title: isValidation ? "Validation Failed" : "Processing Failed",
1054
+ title: isValidation ? CANT_DEPLOY : COULDNT_PREPARE,
1061
1055
  details: isValidation ? message : `Failed to process files: ${message}`,
1062
1056
  sourceName
1063
1057
  });
@@ -1108,7 +1102,7 @@ var initialState = {
1108
1102
  };
1109
1103
  function useDrop({ ship }) {
1110
1104
  const [state, setState] = useState(initialState);
1111
- const isProcessingRef = useRef(false);
1105
+ const runRef = useRef(null);
1112
1106
  const folderInputRef = useRef(null);
1113
1107
  const filesInputRef = useRef(null);
1114
1108
  const isProcessing = state.phase === "processing";
@@ -1119,24 +1113,40 @@ function useDrop({ ship }) {
1119
1113
  [state.files]
1120
1114
  );
1121
1115
  const getFilesForUpload = useCallback(() => validFiles.map((f) => f.file), [validFiles]);
1122
- const processFiles2 = useCallback(
1123
- async (newFiles) => {
1124
- if (isProcessingRef.current) {
1116
+ const prepare = useCallback(
1117
+ async (collect) => {
1118
+ if (runRef.current) {
1125
1119
  console.warn("File processing already in progress. Ignoring duplicate call.");
1126
1120
  return;
1127
1121
  }
1128
- if (!newFiles || newFiles.length === 0) return;
1129
- isProcessingRef.current = true;
1122
+ const run = new AbortController();
1123
+ runRef.current = run;
1130
1124
  setState({
1131
1125
  ...initialState,
1132
1126
  phase: "processing",
1133
1127
  status: { title: "Processing...", details: "Validating and preparing files." }
1134
1128
  });
1135
1129
  try {
1136
- const outcome = await processFiles(newFiles, {
1137
- limits: await ship.getLimits(),
1130
+ const [files, limits] = await Promise.all([
1131
+ collect(),
1132
+ ship.getLimits({ signal: run.signal })
1133
+ ]);
1134
+ if (run.signal.aborted) return;
1135
+ if (files.length === 0) {
1136
+ setState({
1137
+ ...initialState,
1138
+ phase: "error",
1139
+ status: { title: CANT_DEPLOY, details: "The folder has no files." }
1140
+ });
1141
+ return;
1142
+ }
1143
+ const outcome = await processFiles(files, {
1144
+ limits,
1145
+ // The pipeline says its status before its first wait, so a
1146
+ // cancelled run has none left to say.
1138
1147
  onStatus: (status) => setState((prev) => ({ ...prev, status }))
1139
1148
  });
1149
+ if (run.signal.aborted) return;
1140
1150
  setState({
1141
1151
  phase: outcome.phase,
1142
1152
  isDragging: false,
@@ -1145,15 +1155,33 @@ function useDrop({ ship }) {
1145
1155
  status: outcome.status,
1146
1156
  needsBuild: outcome.needsBuild
1147
1157
  });
1158
+ } catch (error) {
1159
+ if (run.signal.aborted) return;
1160
+ setState({
1161
+ ...initialState,
1162
+ phase: "error",
1163
+ status: {
1164
+ title: COULDNT_PREPARE,
1165
+ details: error instanceof Error ? error.message : String(error)
1166
+ }
1167
+ });
1148
1168
  } finally {
1149
- isProcessingRef.current = false;
1169
+ if (runRef.current === run) runRef.current = null;
1150
1170
  }
1151
1171
  },
1152
1172
  [ship]
1153
1173
  );
1174
+ const processFiles2 = useCallback(
1175
+ async (newFiles) => {
1176
+ if (!newFiles || newFiles.length === 0) return;
1177
+ await prepare(async () => newFiles);
1178
+ },
1179
+ [prepare]
1180
+ );
1154
1181
  const reset = useCallback(() => {
1182
+ runRef.current?.abort();
1183
+ runRef.current = null;
1155
1184
  setState(initialState);
1156
- isProcessingRef.current = false;
1157
1185
  }, []);
1158
1186
  const handleDragOver = useCallback((e) => {
1159
1187
  e.preventDefault();
@@ -1188,13 +1216,13 @@ function useDrop({ ship }) {
1188
1216
  if (file) files.push(file);
1189
1217
  }
1190
1218
  }
1191
- await Promise.all(directories.map((d) => traverseFileTree(d.entry, files, d.path)));
1192
- if (files.length === 0 && e.dataTransfer.files.length > 0) {
1193
- files.push(...Array.from(e.dataTransfer.files));
1194
- }
1195
- if (files.length > 0) await processFiles2(files);
1219
+ if (directories.length === 0 && files.length === 0) return;
1220
+ await prepare(async () => {
1221
+ await Promise.all(directories.map((d) => traverseFileTree(d.entry, files, d.path)));
1222
+ return files;
1223
+ });
1196
1224
  },
1197
- [processFiles2]
1225
+ [prepare]
1198
1226
  );
1199
1227
  const handleInputChange = useCallback(
1200
1228
  (e) => {