@shipstatic/drop 0.3.0 → 0.3.2

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
@@ -9384,7 +9384,7 @@ var require_mime_db = __commonJS({
9384
9384
  }
9385
9385
  });
9386
9386
 
9387
- // node_modules/.pnpm/@shipstatic+types@0.7.5/node_modules/@shipstatic/types/dist/index.js
9387
+ // node_modules/.pnpm/@shipstatic+types@0.7.7/node_modules/@shipstatic/types/dist/index.js
9388
9388
  var ErrorType;
9389
9389
  (function(ErrorType2) {
9390
9390
  ErrorType2["Validation"] = "validation_failed";
@@ -9406,6 +9406,14 @@ var ErrorType;
9406
9406
  function isShipError(error) {
9407
9407
  return error !== null && typeof error === "object" && "name" in error && error.name === "ShipError" && "status" in error;
9408
9408
  }
9409
+ var UNBUILT_PROJECT_MARKERS = /* @__PURE__ */ new Set([
9410
+ "node_modules",
9411
+ "package.json"
9412
+ ]);
9413
+ function hasUnbuiltMarker(filePath) {
9414
+ const segments = filePath.replace(/\\/g, "/").split("/").filter(Boolean);
9415
+ return segments.some((s) => UNBUILT_PROJECT_MARKERS.has(s));
9416
+ }
9409
9417
  var FileValidationStatus = {
9410
9418
  /** File is pending validation */
9411
9419
  PENDING: "pending",
@@ -10023,6 +10031,9 @@ async function traverseFileTree(entry, files, currentPath = "") {
10023
10031
  };
10024
10032
  await readEntriesBatch();
10025
10033
  for (const childEntry of allEntries) {
10034
+ if (childEntry.isDirectory && childEntry.name === "node_modules") {
10035
+ continue;
10036
+ }
10026
10037
  const entryPath = childEntry.isDirectory ? currentPath ? `${currentPath}/${childEntry.name}` : childEntry.name : currentPath;
10027
10038
  await traverseFileTree(childEntry, files, entryPath);
10028
10039
  }
@@ -10035,7 +10046,8 @@ var initialState = {
10035
10046
  value: "idle",
10036
10047
  files: [],
10037
10048
  sourceName: "",
10038
- status: null
10049
+ status: null,
10050
+ needsBuild: false
10039
10051
  };
10040
10052
  function useDrop(options) {
10041
10053
  const {
@@ -10051,7 +10063,7 @@ function useDrop(options) {
10051
10063
  const isDragging = state.value === "dragging";
10052
10064
  const isInteractive = state.value === "idle" || state.value === "dragging" || state.value === "ready";
10053
10065
  const hasError = state.value === "error";
10054
- const validFiles = react.useMemo(() => state.files.filter((f) => f.status === "ready"), [state.files]);
10066
+ const validFiles = react.useMemo(() => state.files.filter((f) => f.status === FILE_STATUSES.READY), [state.files]);
10055
10067
  const getFilesForUpload = react.useCallback(() => {
10056
10068
  return validFiles.map((f) => f.file);
10057
10069
  }, [validFiles]);
@@ -10068,7 +10080,8 @@ function useDrop(options) {
10068
10080
  value: "processing",
10069
10081
  files: [],
10070
10082
  sourceName: "",
10071
- status: { title: "Processing...", details: "Validating and preparing files." }
10083
+ status: { title: "Processing...", details: "Validating and preparing files." },
10084
+ needsBuild: false
10072
10085
  });
10073
10086
  let detectedSourceName = "";
10074
10087
  try {
@@ -10102,8 +10115,18 @@ function useDrop(options) {
10102
10115
  const webkitPath = f.webkitRelativePath;
10103
10116
  return webkitPath && webkitPath.trim() ? webkitPath : f.name;
10104
10117
  };
10105
- const filePaths = allFiles.map(getFilePath);
10106
- const validPaths = new Set(ship.filterJunk(filePaths));
10118
+ let filePaths = allFiles.map(getFilePath);
10119
+ const needsBuild = filePaths.some((p) => hasUnbuiltMarker(p));
10120
+ if (needsBuild) {
10121
+ const filtered = allFiles.filter((f) => {
10122
+ const segments = getFilePath(f).replace(/\\/g, "/").split("/");
10123
+ return !segments.includes("node_modules");
10124
+ });
10125
+ allFiles.length = 0;
10126
+ allFiles.push(...filtered);
10127
+ filePaths = allFiles.map(getFilePath);
10128
+ }
10129
+ const validPaths = new Set(ship.filterJunk(filePaths, { allowUnbuilt: needsBuild }));
10107
10130
  const cleanFiles = allFiles.filter((f) => validPaths.has(getFilePath(f)));
10108
10131
  setState((prev) => ({
10109
10132
  ...prev,
@@ -10111,6 +10134,47 @@ function useDrop(options) {
10111
10134
  }));
10112
10135
  const processedFiles = cleanFiles.map((file) => createProcessedFile(file));
10113
10136
  const finalFiles = stripPrefix ? stripCommonPrefix(processedFiles) : processedFiles;
10137
+ if (finalFiles.length > 0) {
10138
+ const hasIndexHtml = needsBuild ? finalFiles.some((f) => f.path === "index.html" || f.path.endsWith("/index.html")) : finalFiles.some((f) => f.path === "index.html");
10139
+ if (!hasIndexHtml) {
10140
+ const message = 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";
10141
+ const filesWithStatus2 = finalFiles.map((f) => ({
10142
+ ...f,
10143
+ status: FILE_STATUSES.VALIDATION_FAILED,
10144
+ statusMessage: message
10145
+ }));
10146
+ setState({
10147
+ value: "error",
10148
+ files: filesWithStatus2,
10149
+ sourceName: detectedSourceName,
10150
+ needsBuild,
10151
+ status: {
10152
+ title: "Validation Failed",
10153
+ details: message,
10154
+ errors: [message]
10155
+ }
10156
+ });
10157
+ onValidationError?.({
10158
+ error: "Validation Failed",
10159
+ details: message,
10160
+ errors: [message],
10161
+ isClientError: true
10162
+ });
10163
+ return;
10164
+ }
10165
+ }
10166
+ if (needsBuild) {
10167
+ const filesWithStatus2 = finalFiles.map((f) => ({ ...f, status: FILE_STATUSES.READY }));
10168
+ setState({
10169
+ value: "ready",
10170
+ files: filesWithStatus2,
10171
+ sourceName: detectedSourceName,
10172
+ needsBuild: true,
10173
+ status: { title: "Ready", details: `${filesWithStatus2.length} file(s) ready \u2014 project will be built` }
10174
+ });
10175
+ onFilesReady?.(filesWithStatus2);
10176
+ return;
10177
+ }
10114
10178
  const validatableFiles = finalFiles.map((f) => ({
10115
10179
  name: f.path,
10116
10180
  // Use full path to match server-side validation
@@ -10131,6 +10195,7 @@ function useDrop(options) {
10131
10195
  value: "error",
10132
10196
  files: filesWithStatus,
10133
10197
  sourceName: detectedSourceName,
10198
+ needsBuild: false,
10134
10199
  status: {
10135
10200
  title: "Validation Failed",
10136
10201
  details: `${validation.errors.length} file(s) failed validation`,
@@ -10152,6 +10217,7 @@ function useDrop(options) {
10152
10217
  value: "ready",
10153
10218
  files: filesWithStatus,
10154
10219
  sourceName: detectedSourceName,
10220
+ needsBuild: false,
10155
10221
  status: {
10156
10222
  title: "Ready",
10157
10223
  details,
@@ -10159,7 +10225,7 @@ function useDrop(options) {
10159
10225
  }
10160
10226
  });
10161
10227
  onFilesReady?.(filesWithStatus.filter(
10162
- (f, idx) => validation.files[idx]?.status === "ready"
10228
+ (f, idx) => validation.files[idx]?.status === FILE_STATUSES.READY
10163
10229
  ));
10164
10230
  } else {
10165
10231
  const hasOnlyWarnings = validation.errors.length === 0 && validation.warnings.length > 0;
@@ -10168,6 +10234,7 @@ function useDrop(options) {
10168
10234
  value: "ready",
10169
10235
  files: filesWithStatus,
10170
10236
  sourceName: detectedSourceName,
10237
+ needsBuild: false,
10171
10238
  status: {
10172
10239
  title: "All files excluded",
10173
10240
  details: `${validation.warnings.length} file(s) excluded (empty files cannot be deployed)`,
@@ -10185,6 +10252,7 @@ function useDrop(options) {
10185
10252
  value: "error",
10186
10253
  files: filesWithStatus,
10187
10254
  sourceName: detectedSourceName,
10255
+ needsBuild: false,
10188
10256
  status: { title: noValidError.error, details: noValidError.details }
10189
10257
  });
10190
10258
  onValidationError?.(noValidError);
@@ -10203,6 +10271,7 @@ function useDrop(options) {
10203
10271
  value: "error",
10204
10272
  files: [],
10205
10273
  sourceName: detectedSourceName,
10274
+ needsBuild: false,
10206
10275
  status: {
10207
10276
  title: clientError.error,
10208
10277
  details: clientError.details,
@@ -10326,6 +10395,7 @@ function useDrop(options) {
10326
10395
  files: state.files,
10327
10396
  sourceName: state.sourceName,
10328
10397
  status: state.status,
10398
+ needsBuild: state.needsBuild,
10329
10399
  // Primary API: Prop getters
10330
10400
  getDropzoneProps,
10331
10401
  getInputProps,