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