shfs 0.3.0 → 0.3.1

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.
@@ -791,8 +791,12 @@ function compileFind(command) {
791
791
  function parseFindArgs(argv) {
792
792
  const state = {
793
793
  action: { ...DEFAULT_ACTION },
794
+ currentBranch: [],
795
+ currentSideAllowsEmptyBranch: false,
794
796
  diagnostics: [],
795
- predicates: [],
797
+ lastOrTokenIndex: null,
798
+ predicateBranches: [],
799
+ sawOr: false,
796
800
  traversal: { ...DEFAULT_TRAVERSAL }
797
801
  };
798
802
  const predicateStartIndex = findPredicateStartIndex(argv);
@@ -807,7 +811,7 @@ function parseFindArgs(argv) {
807
811
  return {
808
812
  action: state.action,
809
813
  diagnostics: state.diagnostics,
810
- predicates: state.predicates,
814
+ predicateBranches: finalizePredicateBranches(state),
811
815
  startPaths,
812
816
  traversal: state.traversal,
813
817
  usageError: state.diagnostics.length > 0
@@ -822,6 +826,9 @@ function createDiagnostic$1(code, token, tokenIndex, message) {
822
826
  function createMissingValueDiagnostic(token, tokenIndex) {
823
827
  return createDiagnostic$1("missing-value", token, tokenIndex, `find: missing argument to ${token}`);
824
828
  }
829
+ function createMissingExpressionDiagnostic(side, tokenIndex) {
830
+ return createDiagnostic$1("invalid-expression", "-o", tokenIndex, `find: -o is missing a ${side} predicate expression`);
831
+ }
825
832
  function findPredicateStartIndex(argv) {
826
833
  for (const [index, word] of argv.entries()) if (expandedWordToString(word).startsWith("-")) return index;
827
834
  return argv.length;
@@ -829,13 +836,16 @@ function findPredicateStartIndex(argv) {
829
836
  function parseFindToken(argv, index, token, state) {
830
837
  if (token === "-name" || token === "-path") return parseStringPredicate(argv, index, token, state);
831
838
  if (token === "-type") return parseTypePredicate(argv, index, state);
839
+ if (token === "-o" || token === "-or") return parseOrPredicateSeparator(index, state);
832
840
  if (token === "-maxdepth" || token === "-mindepth") return parseTraversalOption(argv, index, token, state);
833
841
  if (token === "-depth") {
834
842
  state.traversal.depth = true;
843
+ state.currentSideAllowsEmptyBranch = true;
835
844
  return index + 1;
836
845
  }
837
846
  if (token === "-print") {
838
847
  state.action.explicit = true;
848
+ state.currentSideAllowsEmptyBranch = true;
839
849
  return index + 1;
840
850
  }
841
851
  if (token.startsWith("-")) {
@@ -845,13 +855,25 @@ function parseFindToken(argv, index, token, state) {
845
855
  state.diagnostics.push(createDiagnostic$1("unexpected-operand", token, index, `find: unexpected argument: ${token}`));
846
856
  return index + 1;
847
857
  }
858
+ function parseOrPredicateSeparator(index, state) {
859
+ state.sawOr = true;
860
+ state.lastOrTokenIndex = index;
861
+ if (state.currentBranch.length === 0 && !state.currentSideAllowsEmptyBranch) {
862
+ state.diagnostics.push(createMissingExpressionDiagnostic("left", index));
863
+ return index + 1;
864
+ }
865
+ state.predicateBranches.push(state.currentBranch);
866
+ state.currentBranch = [];
867
+ state.currentSideAllowsEmptyBranch = false;
868
+ return index + 1;
869
+ }
848
870
  function parseStringPredicate(argv, index, token, state) {
849
871
  const valueWord = argv[index + 1];
850
872
  if (!valueWord) {
851
873
  state.diagnostics.push(createMissingValueDiagnostic(token, index));
852
874
  return index + 1;
853
875
  }
854
- state.predicates.push({
876
+ state.currentBranch.push({
855
877
  kind: token === "-name" ? "name" : "path",
856
878
  pattern: valueWord
857
879
  });
@@ -865,12 +887,23 @@ function parseTypePredicate(argv, index, state) {
865
887
  }
866
888
  const parsedType = parseFindTypeValue(valueWord, index + 1);
867
889
  if ("diagnostic" in parsedType) state.diagnostics.push(parsedType.diagnostic);
868
- else state.predicates.push({
890
+ else state.currentBranch.push({
869
891
  kind: "type",
870
892
  types: parsedType.types
871
893
  });
872
894
  return index + 2;
873
895
  }
896
+ function finalizePredicateBranches(state) {
897
+ if (state.currentBranch.length > 0) state.predicateBranches.push(state.currentBranch);
898
+ else if (state.sawOr && state.lastOrTokenIndex !== null) {
899
+ if (state.currentSideAllowsEmptyBranch) {
900
+ state.predicateBranches.push(state.currentBranch);
901
+ return state.predicateBranches;
902
+ }
903
+ state.diagnostics.push(createMissingExpressionDiagnostic("right", state.lastOrTokenIndex));
904
+ }
905
+ return state.predicateBranches;
906
+ }
874
907
  function parseTraversalOption(argv, index, token, state) {
875
908
  const valueWord = argv[index + 1];
876
909
  if (!valueWord) {
@@ -884,6 +917,7 @@ function parseTraversalOption(argv, index, token, state) {
884
917
  }
885
918
  if (token === "-maxdepth") state.traversal.maxdepth = parsedNumericValue.value;
886
919
  else state.traversal.mindepth = parsedNumericValue.value;
920
+ state.currentSideAllowsEmptyBranch = true;
887
921
  return index + 2;
888
922
  }
889
923
  function parseFindTypeValue(word, tokenIndex) {
@@ -948,7 +982,9 @@ const parseGrepWords = createWordParser({
948
982
  long: "before-context",
949
983
  short: "B"
950
984
  }),
985
+ binaryFiles: grepValueFlag({ long: "binary-files" }),
951
986
  binaryFile: grepValueFlag({ long: "binary-file" }),
987
+ binaryWithoutMatch: grepBooleanFlag({ short: "I" }),
952
988
  byteOffset: grepBooleanFlag({
953
989
  long: "byte-offset",
954
990
  short: "b"
@@ -1085,7 +1121,7 @@ function parseGrepArgs(argv) {
1085
1121
  applyModeOption(parsed, options);
1086
1122
  applyFilenameMode(parsed, options);
1087
1123
  applyFileListingMode(parsed, options);
1088
- applyBinaryFileOption(parsed, argv, options);
1124
+ applyBinaryFileOption(parsed, argv, options, diagnostics);
1089
1125
  applyDirectoriesOption(parsed, argv, options);
1090
1126
  applyMaxCountOption(parsed, argv, options, diagnostics);
1091
1127
  applyContextOptions(parsed, argv, options, diagnostics);
@@ -1194,8 +1230,58 @@ function applyFileListingMode(parsed, options) {
1194
1230
  options.listFilesWithMatches = false;
1195
1231
  }
1196
1232
  }
1197
- function applyBinaryFileOption(parsed, argv, options) {
1198
- for (const occurrence of getValueOccurrences(parsed, argv, "binaryFile")) options.binaryWithoutMatch = occurrence.value === "without-match";
1233
+ function applyBinaryFileOption(parsed, argv, options, diagnostics) {
1234
+ const modeOccurrences = [];
1235
+ for (const occurrence of getValueOccurrences(parsed, argv, "binaryFile")) {
1236
+ const mode = parseBinaryMode(occurrence.value);
1237
+ if (mode === null) {
1238
+ diagnostics.push(makeDiagnostic("invalid-value", occurrence.token, occurrence.tokenIndex, `Invalid value for option ${splitLongOption(occurrence.token)}.`));
1239
+ continue;
1240
+ }
1241
+ modeOccurrences.push({
1242
+ mode,
1243
+ order: occurrence.order
1244
+ });
1245
+ }
1246
+ for (const occurrence of getValueOccurrences(parsed, argv, "binaryFiles")) {
1247
+ const mode = parseBinaryMode(occurrence.value);
1248
+ if (mode === null) {
1249
+ diagnostics.push(makeDiagnostic("invalid-value", occurrence.token, occurrence.tokenIndex, `Invalid value for option ${splitLongOption(occurrence.token)}.`));
1250
+ continue;
1251
+ }
1252
+ modeOccurrences.push({
1253
+ mode,
1254
+ order: occurrence.order
1255
+ });
1256
+ }
1257
+ for (const order of parsed.flagOccurrenceOrder.binaryWithoutMatch ?? []) modeOccurrences.push({
1258
+ mode: "without-match",
1259
+ order
1260
+ });
1261
+ for (const order of parsed.flagOccurrenceOrder.textMode ?? []) modeOccurrences.push({
1262
+ mode: "text",
1263
+ order
1264
+ });
1265
+ modeOccurrences.sort((a, b) => a.order - b.order);
1266
+ for (const occurrence of modeOccurrences) switch (occurrence.mode) {
1267
+ case "binary":
1268
+ options.binaryWithoutMatch = false;
1269
+ options.textMode = false;
1270
+ break;
1271
+ case "text":
1272
+ options.binaryWithoutMatch = false;
1273
+ options.textMode = true;
1274
+ break;
1275
+ case "without-match":
1276
+ options.binaryWithoutMatch = true;
1277
+ options.textMode = false;
1278
+ break;
1279
+ default: break;
1280
+ }
1281
+ }
1282
+ function parseBinaryMode(value) {
1283
+ if (value === "binary" || value === "text" || value === "without-match") return value;
1284
+ return null;
1199
1285
  }
1200
1286
  function applyDirectoriesOption(parsed, argv, options) {
1201
1287
  for (const occurrence of getValueOccurrences(parsed, argv, "directories")) options.directories = occurrence.value === "skip" ? "skip" : "read";
@@ -4179,9 +4265,9 @@ async function* find(fs, context, args) {
4179
4265
  writeDiagnosticsToStderr(context, args.diagnostics);
4180
4266
  return;
4181
4267
  }
4182
- let resolvedPredicates;
4268
+ let resolvedPredicateBranches;
4183
4269
  try {
4184
- resolvedPredicates = await resolvePredicates(args.predicates, fs, context);
4270
+ resolvedPredicateBranches = await resolvePredicates(args.predicateBranches, fs, context);
4185
4271
  } catch (error) {
4186
4272
  context.status = diagnosticExitCode(error);
4187
4273
  writeErrorToStderr(context, error);
@@ -4209,12 +4295,12 @@ async function* find(fs, context, args) {
4209
4295
  ...startPath,
4210
4296
  depth: 0,
4211
4297
  isDirectory: startStat.isDirectory
4212
- }, args, resolvedPredicates, state);
4298
+ }, args, resolvedPredicateBranches, state);
4213
4299
  }
4214
4300
  context.status = state.hadError ? 1 : 0;
4215
4301
  }
4216
- async function* walkEntry(fs, context, entry, args, predicates, state) {
4217
- const matches = entry.depth >= args.traversal.mindepth && matchesPredicates(entry, predicates);
4302
+ async function* walkEntry(fs, context, entry, args, predicateBranches, state) {
4303
+ const matches = entry.depth >= args.traversal.mindepth && matchesPredicates(entry, predicateBranches);
4218
4304
  if (!args.traversal.depth && matches) yield toFileRecord(entry);
4219
4305
  if (entry.isDirectory && (args.traversal.maxdepth === null || entry.depth < args.traversal.maxdepth)) {
4220
4306
  let childPaths;
@@ -4239,46 +4325,50 @@ async function* walkEntry(fs, context, entry, args, predicates, state) {
4239
4325
  displayPath: appendDisplayPath(entry.displayPath, basename$2(childAbsolutePath)),
4240
4326
  depth: entry.depth + 1,
4241
4327
  isDirectory: childStat.isDirectory
4242
- }, args, predicates, state);
4328
+ }, args, predicateBranches, state);
4243
4329
  }
4244
4330
  }
4245
4331
  if (args.traversal.depth && matches) yield toFileRecord(entry);
4246
4332
  }
4247
- async function resolvePredicates(predicates, fs, context) {
4333
+ async function resolvePredicates(predicateBranches, fs, context) {
4248
4334
  const resolved = [];
4249
- for (const predicate of predicates) switch (predicate.kind) {
4250
- case "name": {
4251
- const pattern = await evaluateExpandedWord(predicate.pattern, fs, context);
4252
- resolved.push({
4253
- kind: "name",
4254
- matcher: picomatch(pattern, {
4255
- bash: true,
4256
- dot: true
4257
- })
4258
- });
4259
- break;
4260
- }
4261
- case "path": {
4262
- const pattern = await evaluateExpandedWord(predicate.pattern, fs, context);
4263
- resolved.push({
4264
- kind: "path",
4265
- matcher: picomatch(pattern, {
4266
- bash: true,
4267
- dot: true
4268
- })
4269
- });
4270
- break;
4271
- }
4272
- case "type":
4273
- resolved.push({
4274
- kind: "type",
4275
- types: new Set(predicate.types)
4276
- });
4277
- break;
4278
- default: {
4279
- const _exhaustive = predicate;
4280
- throw new Error(`Unsupported find predicate: ${JSON.stringify(_exhaustive)}`);
4335
+ for (const branch of predicateBranches) {
4336
+ const resolvedBranch = [];
4337
+ for (const predicate of branch) switch (predicate.kind) {
4338
+ case "name": {
4339
+ const pattern = await evaluateExpandedWord(predicate.pattern, fs, context);
4340
+ resolvedBranch.push({
4341
+ kind: "name",
4342
+ matcher: picomatch(pattern, {
4343
+ bash: true,
4344
+ dot: true
4345
+ })
4346
+ });
4347
+ break;
4348
+ }
4349
+ case "path": {
4350
+ const pattern = await evaluateExpandedWord(predicate.pattern, fs, context);
4351
+ resolvedBranch.push({
4352
+ kind: "path",
4353
+ matcher: picomatch(pattern, {
4354
+ bash: true,
4355
+ dot: true
4356
+ })
4357
+ });
4358
+ break;
4359
+ }
4360
+ case "type":
4361
+ resolvedBranch.push({
4362
+ kind: "type",
4363
+ types: new Set(predicate.types)
4364
+ });
4365
+ break;
4366
+ default: {
4367
+ const _exhaustive = predicate;
4368
+ throw new Error(`Unsupported find predicate: ${JSON.stringify(_exhaustive)}`);
4369
+ }
4281
4370
  }
4371
+ resolved.push(resolvedBranch);
4282
4372
  }
4283
4373
  return resolved;
4284
4374
  }
@@ -4296,21 +4386,21 @@ async function resolveStartPaths(fs, context, startPathWords) {
4296
4386
  }
4297
4387
  return startPaths;
4298
4388
  }
4299
- function matchesPredicates(entry, predicates) {
4300
- for (const predicate of predicates) {
4301
- if (predicate.kind === "name") {
4302
- if (!predicate.matcher(basename$2(entry.displayPath))) return false;
4303
- continue;
4304
- }
4305
- if (predicate.kind === "path") {
4306
- if (!predicate.matcher(entry.displayPath)) return false;
4307
- continue;
4308
- }
4309
- const entryType = entry.isDirectory ? "d" : "f";
4310
- if (!predicate.types.has(entryType)) return false;
4311
- }
4389
+ function matchesPredicates(entry, predicateBranches) {
4390
+ if (predicateBranches.length === 0) return true;
4391
+ const entryType = entry.isDirectory ? "d" : "f";
4392
+ for (const branch of predicateBranches) if (matchesBranch(entry, entryType, branch)) return true;
4393
+ return false;
4394
+ }
4395
+ function matchesBranch(entry, entryType, branch) {
4396
+ for (const predicate of branch) if (!matchesPredicate(entry, entryType, predicate)) return false;
4312
4397
  return true;
4313
4398
  }
4399
+ function matchesPredicate(entry, entryType, predicate) {
4400
+ if (predicate.kind === "name") return predicate.matcher(basename$2(entry.displayPath));
4401
+ if (predicate.kind === "path") return predicate.matcher(entry.displayPath);
4402
+ return predicate.types.has(entryType);
4403
+ }
4314
4404
  async function readChildren(fs, path) {
4315
4405
  const children = [];
4316
4406
  for await (const childPath of fs.readdir(path)) children.push(childPath);
@@ -4486,23 +4576,21 @@ async function runGrepCommandInner(options) {
4486
4576
  lines: [],
4487
4577
  selectedLineCount: 0
4488
4578
  };
4489
- if (target.stdin) result = searchBuffer(stdinBytes ?? new Uint8Array(), target.displayPath, matcherBuild.patterns, parsed.options, displayFilename);
4579
+ let binaryInput = false;
4580
+ let targetBytes = null;
4581
+ if (target.stdin) targetBytes = stdinBytes ?? new Uint8Array();
4490
4582
  else {
4491
4583
  if (target.absolutePath === null) continue;
4492
- let bytes;
4493
4584
  try {
4494
- bytes = await options.fs.readFile(target.absolutePath);
4585
+ targetBytes = await options.fs.readFile(target.absolutePath);
4495
4586
  } catch {
4496
4587
  hadError = true;
4497
4588
  continue;
4498
4589
  }
4499
- if (parsed.options.binaryWithoutMatch && !parsed.options.textMode && isBinaryBuffer(bytes)) result = {
4500
- hasSelectedLine: false,
4501
- lines: [],
4502
- selectedLineCount: 0
4503
- };
4504
- else result = searchBuffer(bytes, target.displayPath, matcherBuild.patterns, parsed.options, displayFilename);
4505
4590
  }
4591
+ if (targetBytes === null) continue;
4592
+ binaryInput = shouldTreatAsBinaryInput(targetBytes, parsed.options);
4593
+ if (!(binaryInput && parsed.options.binaryWithoutMatch)) result = searchBuffer(targetBytes, target.displayPath, matcherBuild.patterns, parsed.options, displayFilename);
4506
4594
  if (parsed.options.listFilesWithMatches) {
4507
4595
  if (result.hasSelectedLine) {
4508
4596
  lines.push(target.displayPath);
@@ -4528,7 +4616,8 @@ async function runGrepCommandInner(options) {
4528
4616
  continue;
4529
4617
  }
4530
4618
  if (result.hasSelectedLine) anySelected = true;
4531
- if (!parsed.options.quiet) lines.push(...result.lines);
4619
+ if (!parsed.options.quiet) if (shouldPrintBinaryMatchMessage(binaryInput, result.hasSelectedLine, parsed.options)) lines.push(`Binary file ${target.displayPath} matches`);
4620
+ else lines.push(...result.lines);
4532
4621
  if (parsed.options.quiet && anySelected) break;
4533
4622
  }
4534
4623
  const corpusOverride = await maybeOverrideWithCorpusStatus(parsed.options.mode, normalized.patterns, searchTargets.targets, options.fs);
@@ -5287,6 +5376,15 @@ function isValidUtf8(bytes) {
5287
5376
  function isBinaryBuffer(bytes) {
5288
5377
  return bytes.includes(0);
5289
5378
  }
5379
+ function shouldTreatAsBinaryInput(bytes, options) {
5380
+ if (options.textMode || options.nullData) return false;
5381
+ return isBinaryBuffer(bytes);
5382
+ }
5383
+ function shouldPrintBinaryMatchMessage(binaryInput, hasSelectedLine, options) {
5384
+ if (!(binaryInput && hasSelectedLine)) return false;
5385
+ if (options.binaryWithoutMatch || options.countOnly || options.listFilesWithMatches || options.listFilesWithoutMatch) return false;
5386
+ return true;
5387
+ }
5290
5388
  function byteLengthOfPrefix(text, charIndex) {
5291
5389
  return UTF8_ENCODER.encode(text.slice(0, charIndex)).byteLength;
5292
5390
  }
@@ -5857,4 +5955,4 @@ function normalizeContext(context) {
5857
5955
  //#endregion
5858
5956
  export { formatStderr as a, compile as c, writeDiagnosticsToStderr as i, parse as l, execute_exports as n, formatStdoutRecord as o, isShellDiagnosticError as r, ParseSyntaxError as s, execute as t };
5859
5957
 
5860
- //# sourceMappingURL=execute-khztC2iY.mjs.map
5958
+ //# sourceMappingURL=execute-1PmTy1KA.mjs.map