pickier 0.1.37 → 0.1.38

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/src/index.js CHANGED
@@ -8881,129 +8881,410 @@ var init_config = __esm(() => {
8881
8881
  config5 = defaultConfig4;
8882
8882
  });
8883
8883
 
8884
- // src/format.ts
8885
- function isHangingControlLine(code) {
8886
- const m = RE_CONTROL_OPEN.exec(code);
8887
- if (!m)
8888
- return /^(?:else|do)\s*$/.test(code);
8889
- let depth = 0;
8890
- let inStr = null;
8891
- for (let i = m[0].length - 1;i < code.length; i++) {
8892
- const ch = code[i];
8893
- if (inStr) {
8894
- if (ch === "\\") {
8884
+ // src/rules/shell/_shared.ts
8885
+ function maskShellStrings(line) {
8886
+ const n = line.length;
8887
+ const out = Array.from({ length: n }, (_, i2) => line[i2]);
8888
+ let i = 0;
8889
+ while (i < n) {
8890
+ const ch = line[i];
8891
+ if (ch === "#" && (i === 0 || /\s/.test(line[i - 1]))) {
8892
+ while (i < n) {
8893
+ out[i] = " ";
8895
8894
  i++;
8896
- continue;
8897
8895
  }
8898
- if (ch === inStr)
8899
- inStr = null;
8900
- continue;
8896
+ break;
8901
8897
  }
8902
- if (ch === "'" || ch === '"' || ch === "`") {
8903
- inStr = ch;
8898
+ if (ch === "$" && line[i + 1] === "'") {
8899
+ i += 2;
8900
+ while (i < n) {
8901
+ if (line[i] === "\\" && i + 1 < n) {
8902
+ out[i] = " ";
8903
+ out[i + 1] = " ";
8904
+ i += 2;
8905
+ continue;
8906
+ }
8907
+ if (line[i] === "'") {
8908
+ i++;
8909
+ break;
8910
+ }
8911
+ out[i] = " ";
8912
+ i++;
8913
+ }
8904
8914
  continue;
8905
8915
  }
8906
- if (ch === "(") {
8907
- depth++;
8908
- } else if (ch === ")") {
8909
- depth--;
8910
- if (depth === 0)
8911
- return code.slice(i + 1).trim().length === 0;
8916
+ if (ch === "$" && line[i + 1] === '"') {
8917
+ i++;
8918
+ i = maskDoubleQuoted(line, i, out);
8919
+ continue;
8912
8920
  }
8913
- }
8914
- return false;
8915
- }
8916
- function getFileExt(filePath) {
8917
- const idx = filePath.lastIndexOf(".");
8918
- return idx >= 0 ? filePath.slice(idx) : "";
8919
- }
8920
- function isCodeFileExt(filePath) {
8921
- return CODE_EXTS.has(getFileExt(filePath));
8922
- }
8923
- function isJsonFileExt(filePath) {
8924
- return JSON_EXTS.has(getFileExt(filePath));
8925
- }
8926
- function isYamlFileExt(filePath) {
8927
- return YAML_EXTS.has(getFileExt(filePath));
8928
- }
8929
- function isShellFileExt(filePath) {
8930
- return SHELL_EXTS.has(getFileExt(filePath));
8931
- }
8932
- function isShellFile(filePath, content) {
8933
- return isShellFileExt(filePath) || SHELL_SHEBANG_RE.test(content);
8934
- }
8935
- function processShellLinesFused(content, cfg) {
8936
- const lines = content.split(`
8937
- `);
8938
- const len = lines.length;
8939
- const result = new Array(len);
8940
- const indentSize = cfg.format.indent;
8941
- const useTab = cfg.format.indentStyle === "tabs";
8942
- let indentLevel = 0;
8943
- let inHeredoc = false;
8944
- let heredocDelim = "";
8945
- let inCase = false;
8946
- let caseDepth = 0;
8947
- for (let idx = 0;idx < len; idx++) {
8948
- const line = lines[idx];
8949
- if (line.length === 0 || /^\s*$/.test(line)) {
8950
- result[idx] = "";
8921
+ if (ch === "$" && line[i + 1] === "{") {
8922
+ i += 2;
8923
+ let depth = 1;
8924
+ while (i < n && depth > 0) {
8925
+ const c = line[i];
8926
+ if (c === "\\" && i + 1 < n) {
8927
+ out[i] = " ";
8928
+ out[i + 1] = " ";
8929
+ i += 2;
8930
+ continue;
8931
+ }
8932
+ if (c === "'") {
8933
+ i++;
8934
+ while (i < n && line[i] !== "'") {
8935
+ out[i] = " ";
8936
+ i++;
8937
+ }
8938
+ if (i < n)
8939
+ i++;
8940
+ continue;
8941
+ }
8942
+ if (c === '"') {
8943
+ i = maskDoubleQuoted(line, i, out);
8944
+ continue;
8945
+ }
8946
+ if (c === "{") {
8947
+ depth++;
8948
+ i++;
8949
+ continue;
8950
+ }
8951
+ if (c === "}") {
8952
+ depth--;
8953
+ i++;
8954
+ continue;
8955
+ }
8956
+ out[i] = " ";
8957
+ i++;
8958
+ }
8951
8959
  continue;
8952
8960
  }
8953
- if (inHeredoc) {
8954
- result[idx] = line;
8955
- if (line.trim() === heredocDelim) {
8956
- inHeredoc = false;
8957
- heredocDelim = "";
8961
+ if (ch === "$" && line[i + 1] === "(" && line[i + 2] === "(") {
8962
+ i += 3;
8963
+ let depth = 2;
8964
+ while (i < n && depth > 0) {
8965
+ const c = line[i];
8966
+ if (c === "(") {
8967
+ depth++;
8968
+ i++;
8969
+ continue;
8970
+ }
8971
+ if (c === ")") {
8972
+ depth--;
8973
+ i++;
8974
+ continue;
8975
+ }
8976
+ out[i] = " ";
8977
+ i++;
8958
8978
  }
8959
8979
  continue;
8960
8980
  }
8961
- const heredocMatch = line.match(/<<-?\s*['"]?(\w+)['"]?/);
8962
- if (heredocMatch) {
8963
- inHeredoc = true;
8964
- heredocDelim = heredocMatch[1];
8981
+ if (ch === "$" && line[i + 1] === "(") {
8982
+ i += 2;
8983
+ let depth = 1;
8984
+ while (i < n && depth > 0) {
8985
+ const c = line[i];
8986
+ if (c === "\\" && i + 1 < n) {
8987
+ out[i] = " ";
8988
+ out[i + 1] = " ";
8989
+ i += 2;
8990
+ continue;
8991
+ }
8992
+ if (c === "'") {
8993
+ i++;
8994
+ while (i < n && line[i] !== "'") {
8995
+ out[i] = " ";
8996
+ i++;
8997
+ }
8998
+ if (i < n)
8999
+ i++;
9000
+ continue;
9001
+ }
9002
+ if (c === '"') {
9003
+ i = maskDoubleQuoted(line, i, out);
9004
+ continue;
9005
+ }
9006
+ if (c === "(") {
9007
+ depth++;
9008
+ i++;
9009
+ continue;
9010
+ }
9011
+ if (c === ")") {
9012
+ depth--;
9013
+ i++;
9014
+ continue;
9015
+ }
9016
+ out[i] = " ";
9017
+ i++;
9018
+ }
9019
+ continue;
8965
9020
  }
8966
- let wsEnd = 0;
8967
- while (wsEnd < line.length && (line.charCodeAt(wsEnd) === 32 || line.charCodeAt(wsEnd) === 9))
8968
- wsEnd++;
8969
- const trimmed = line.slice(wsEnd).trimEnd();
8970
- if (trimmed.startsWith("#") && idx > 0) {
8971
- const indent2 = useTab ? "\t".repeat(indentLevel) : " ".repeat(indentLevel * indentSize);
8972
- result[idx] = indent2 + trimmed;
9021
+ if (ch === "'") {
9022
+ i++;
9023
+ while (i < n && line[i] !== "'") {
9024
+ out[i] = " ";
9025
+ i++;
9026
+ }
9027
+ if (i < n)
9028
+ i++;
8973
9029
  continue;
8974
9030
  }
8975
- if (/^case\b/.test(trimmed) && /\bin\s*$/.test(trimmed)) {
8976
- caseDepth++;
8977
- inCase = true;
9031
+ if (ch === '"') {
9032
+ i = maskDoubleQuoted(line, i, out);
9033
+ continue;
8978
9034
  }
8979
- if (/^esac\b/.test(trimmed)) {
8980
- caseDepth = Math.max(0, caseDepth - 1);
8981
- if (caseDepth === 0)
8982
- inCase = false;
9035
+ if (ch === "`") {
9036
+ i++;
9037
+ while (i < n && line[i] !== "`") {
9038
+ if (line[i] === "\\" && i + 1 < n) {
9039
+ out[i] = " ";
9040
+ out[i + 1] = " ";
9041
+ i += 2;
9042
+ continue;
9043
+ }
9044
+ out[i] = " ";
9045
+ i++;
9046
+ }
9047
+ if (i < n)
9048
+ i++;
9049
+ continue;
8983
9050
  }
8984
- const isCasePattern = inCase && /\)\s*$/.test(trimmed) && !/\$\(/.test(trimmed) && !/^\s*\(/.test(trimmed) && !trimmed.startsWith("#");
8985
- const isCaseTerminator = /^;[;&]\s*$/.test(trimmed);
8986
- const shouldDecrease = /^(?:fi|done|esac|elif|else)\b/.test(trimmed) || /^\}/.test(trimmed);
8987
- if (shouldDecrease)
8988
- indentLevel = Math.max(0, indentLevel - 1);
8989
- if (isCaseTerminator)
8990
- indentLevel = Math.max(0, indentLevel - 1);
8991
- const indent = useTab ? "\t".repeat(indentLevel) : " ".repeat(indentLevel * indentSize);
8992
- result[idx] = indent + trimmed;
8993
- if (/^then\b/.test(trimmed) || /^do\b/.test(trimmed) || /^else\b/.test(trimmed))
8994
- indentLevel++;
8995
- else if (/^if\b/.test(trimmed) && /\bthen\s*$/.test(trimmed))
8996
- indentLevel++;
8997
- else if (/^elif\b/.test(trimmed) && /\bthen\s*$/.test(trimmed))
8998
- indentLevel++;
8999
- else if (/^(?:while|for|until)\b/.test(trimmed) && /\bdo\s*$/.test(trimmed))
9000
- indentLevel++;
9001
- else if (/^case\b/.test(trimmed) && /\bin\s*$/.test(trimmed))
9002
- indentLevel++;
9003
- else if (isCasePattern)
9004
- indentLevel++;
9005
- else if (/\{\s*$/.test(trimmed) && !trimmed.startsWith("#"))
9006
- indentLevel++;
9051
+ i++;
9052
+ }
9053
+ return out.join("");
9054
+ }
9055
+ function maskDoubleQuoted(line, start, out) {
9056
+ const n = line.length;
9057
+ let i = start + 1;
9058
+ while (i < n) {
9059
+ const c = line[i];
9060
+ if (c === "\\" && i + 1 < n) {
9061
+ out[i] = " ";
9062
+ out[i + 1] = " ";
9063
+ i += 2;
9064
+ continue;
9065
+ }
9066
+ if (c === '"')
9067
+ return i + 1;
9068
+ if (c === "$" && line[i + 1] === "(" && line[i + 2] === "(") {
9069
+ i += 3;
9070
+ let depth = 2;
9071
+ while (i < n && depth > 0) {
9072
+ const x = line[i];
9073
+ if (x === "(") {
9074
+ depth++;
9075
+ i++;
9076
+ continue;
9077
+ }
9078
+ if (x === ")") {
9079
+ depth--;
9080
+ i++;
9081
+ continue;
9082
+ }
9083
+ out[i] = " ";
9084
+ i++;
9085
+ }
9086
+ continue;
9087
+ }
9088
+ if (c === "$" && line[i + 1] === "(") {
9089
+ i += 2;
9090
+ let depth = 1;
9091
+ while (i < n && depth > 0) {
9092
+ const x = line[i];
9093
+ if (x === "\\" && i + 1 < n) {
9094
+ out[i] = " ";
9095
+ out[i + 1] = " ";
9096
+ i += 2;
9097
+ continue;
9098
+ }
9099
+ if (x === "'") {
9100
+ i++;
9101
+ while (i < n && line[i] !== "'") {
9102
+ out[i] = " ";
9103
+ i++;
9104
+ }
9105
+ if (i < n)
9106
+ i++;
9107
+ continue;
9108
+ }
9109
+ if (x === '"') {
9110
+ i = maskDoubleQuoted(line, i, out);
9111
+ continue;
9112
+ }
9113
+ if (x === "(") {
9114
+ depth++;
9115
+ i++;
9116
+ continue;
9117
+ }
9118
+ if (x === ")") {
9119
+ depth--;
9120
+ i++;
9121
+ continue;
9122
+ }
9123
+ out[i] = " ";
9124
+ i++;
9125
+ }
9126
+ continue;
9127
+ }
9128
+ if (c === "$" && line[i + 1] === "{") {
9129
+ i += 2;
9130
+ let depth = 1;
9131
+ while (i < n && depth > 0) {
9132
+ const x = line[i];
9133
+ if (x === "{") {
9134
+ depth++;
9135
+ i++;
9136
+ continue;
9137
+ }
9138
+ if (x === "}") {
9139
+ depth--;
9140
+ i++;
9141
+ continue;
9142
+ }
9143
+ out[i] = " ";
9144
+ i++;
9145
+ }
9146
+ continue;
9147
+ }
9148
+ out[i] = " ";
9149
+ i++;
9150
+ }
9151
+ return i;
9152
+ }
9153
+ function heredocDelimiter(line) {
9154
+ const masked = maskShellStrings(line);
9155
+ const m = masked.match(/<<-?/);
9156
+ if (!m || m.index === undefined)
9157
+ return null;
9158
+ const after = line.slice(m.index + m[0].length);
9159
+ const delim = after.match(/^\s*(['"]?)(\w+)\1/);
9160
+ if (!delim)
9161
+ return null;
9162
+ return delim[2];
9163
+ }
9164
+
9165
+ // src/format.ts
9166
+ function isHangingControlLine(code) {
9167
+ const m = RE_CONTROL_OPEN.exec(code);
9168
+ if (!m)
9169
+ return /^(?:else|do)\s*$/.test(code);
9170
+ let depth = 0;
9171
+ let inStr = null;
9172
+ for (let i = m[0].length - 1;i < code.length; i++) {
9173
+ const ch = code[i];
9174
+ if (inStr) {
9175
+ if (ch === "\\") {
9176
+ i++;
9177
+ continue;
9178
+ }
9179
+ if (ch === inStr)
9180
+ inStr = null;
9181
+ continue;
9182
+ }
9183
+ if (ch === "'" || ch === '"' || ch === "`") {
9184
+ inStr = ch;
9185
+ continue;
9186
+ }
9187
+ if (ch === "(") {
9188
+ depth++;
9189
+ } else if (ch === ")") {
9190
+ depth--;
9191
+ if (depth === 0)
9192
+ return code.slice(i + 1).trim().length === 0;
9193
+ }
9194
+ }
9195
+ return false;
9196
+ }
9197
+ function getFileExt(filePath) {
9198
+ const idx = filePath.lastIndexOf(".");
9199
+ return idx >= 0 ? filePath.slice(idx) : "";
9200
+ }
9201
+ function isCodeFileExt(filePath) {
9202
+ return CODE_EXTS.has(getFileExt(filePath));
9203
+ }
9204
+ function isJsonFileExt(filePath) {
9205
+ return JSON_EXTS.has(getFileExt(filePath));
9206
+ }
9207
+ function isYamlFileExt(filePath) {
9208
+ return YAML_EXTS.has(getFileExt(filePath));
9209
+ }
9210
+ function isShellFileExt(filePath) {
9211
+ return SHELL_EXTS.has(getFileExt(filePath));
9212
+ }
9213
+ function isShellFile(filePath, content) {
9214
+ return isShellFileExt(filePath) || SHELL_SHEBANG_RE.test(content);
9215
+ }
9216
+ function processShellLinesFused(content, cfg) {
9217
+ const lines = content.split(`
9218
+ `);
9219
+ const len = lines.length;
9220
+ const result = new Array(len);
9221
+ const indentSize = cfg.format.indent;
9222
+ const useTab = cfg.format.indentStyle === "tabs";
9223
+ let indentLevel = 0;
9224
+ let inHeredoc = false;
9225
+ let heredocDelim = "";
9226
+ let inCase = false;
9227
+ let caseDepth = 0;
9228
+ for (let idx = 0;idx < len; idx++) {
9229
+ const line = lines[idx];
9230
+ if (line.length === 0 || /^\s*$/.test(line)) {
9231
+ result[idx] = "";
9232
+ continue;
9233
+ }
9234
+ if (inHeredoc) {
9235
+ result[idx] = line;
9236
+ if (line.trim() === heredocDelim) {
9237
+ inHeredoc = false;
9238
+ heredocDelim = "";
9239
+ }
9240
+ continue;
9241
+ }
9242
+ const delim = heredocDelimiter(line);
9243
+ if (delim) {
9244
+ inHeredoc = true;
9245
+ heredocDelim = delim;
9246
+ }
9247
+ let wsEnd = 0;
9248
+ while (wsEnd < line.length && (line.charCodeAt(wsEnd) === 32 || line.charCodeAt(wsEnd) === 9))
9249
+ wsEnd++;
9250
+ const trimmed = line.slice(wsEnd).trimEnd();
9251
+ if (trimmed.startsWith("#") && idx > 0) {
9252
+ const indent2 = useTab ? "\t".repeat(indentLevel) : " ".repeat(indentLevel * indentSize);
9253
+ result[idx] = indent2 + trimmed;
9254
+ continue;
9255
+ }
9256
+ if (/^case\b/.test(trimmed) && /\bin\s*$/.test(trimmed)) {
9257
+ caseDepth++;
9258
+ inCase = true;
9259
+ }
9260
+ if (/^esac\b/.test(trimmed)) {
9261
+ caseDepth = Math.max(0, caseDepth - 1);
9262
+ if (caseDepth === 0)
9263
+ inCase = false;
9264
+ }
9265
+ const isCasePattern = inCase && /\)\s*$/.test(trimmed) && !/\$\(/.test(trimmed) && !/^\s*\(/.test(trimmed) && !trimmed.startsWith("#");
9266
+ const isCaseTerminator = /^;[;&]\s*$/.test(trimmed);
9267
+ const shouldDecrease = /^(?:fi|done|esac|elif|else)\b/.test(trimmed) || /^\}/.test(trimmed);
9268
+ if (shouldDecrease)
9269
+ indentLevel = Math.max(0, indentLevel - 1);
9270
+ if (isCaseTerminator)
9271
+ indentLevel = Math.max(0, indentLevel - 1);
9272
+ const indent = useTab ? "\t".repeat(indentLevel) : " ".repeat(indentLevel * indentSize);
9273
+ result[idx] = indent + trimmed;
9274
+ if (/^then\b/.test(trimmed) || /^do\b/.test(trimmed) || /^else\b/.test(trimmed))
9275
+ indentLevel++;
9276
+ else if (/^if\b/.test(trimmed) && /\bthen\s*$/.test(trimmed))
9277
+ indentLevel++;
9278
+ else if (/^elif\b/.test(trimmed) && /\bthen\s*$/.test(trimmed))
9279
+ indentLevel++;
9280
+ else if (/^(?:while|for|until)\b/.test(trimmed) && /\bdo\s*$/.test(trimmed))
9281
+ indentLevel++;
9282
+ else if (/^case\b/.test(trimmed) && /\bin\s*$/.test(trimmed))
9283
+ indentLevel++;
9284
+ else if (isCasePattern)
9285
+ indentLevel++;
9286
+ else if (/\{\s*$/.test(trimmed) && !trimmed.startsWith("#"))
9287
+ indentLevel++;
9007
9288
  }
9008
9289
  return result.join(`
9009
9290
  `);
@@ -9031,16 +9312,25 @@ function makeIndent(visualLevels, cfg) {
9031
9312
  }
9032
9313
  function convertDoubleToSingle(str) {
9033
9314
  const inner = str.slice(1, -1);
9034
- let result = inner.replace(/\\"/g, '"');
9035
- result = result.replace(/'/g, '"');
9315
+ const result = inner.replace(/\\"/g, '"');
9036
9316
  return `'${result}'`;
9037
9317
  }
9038
9318
  function convertSingleToDouble(str) {
9039
9319
  const inner = str.slice(1, -1);
9040
- let result = inner.replace(/\\'/g, "'");
9041
- result = result.replace(/"/g, "'");
9320
+ const result = inner.replace(/\\'/g, "'");
9042
9321
  return `"${result}"`;
9043
9322
  }
9323
+ function hasUnescapedChar(content, ch) {
9324
+ for (let i = 0;i < content.length; i++) {
9325
+ if (content[i] === "\\") {
9326
+ i++;
9327
+ continue;
9328
+ }
9329
+ if (content[i] === ch)
9330
+ return true;
9331
+ }
9332
+ return false;
9333
+ }
9044
9334
  function fixQuotes(content, preferred, filePath) {
9045
9335
  if (!isCodeFileExt(filePath))
9046
9336
  return content;
@@ -9115,11 +9405,12 @@ function fixQuotesLine(line, preferred) {
9115
9405
  }
9116
9406
  const closeChar = inString === 1 ? "'" : '"';
9117
9407
  if (ch === closeChar) {
9118
- const needConvert = inString === 2 && wantSingle || inString === 1 && !wantSingle;
9408
+ const stringContent = line.slice(stringStart + 1, i);
9409
+ const targetQuote = wantSingle ? "'" : '"';
9410
+ const needConvert = (inString === 2 && wantSingle || inString === 1 && !wantSingle) && !hasUnescapedChar(stringContent, targetQuote);
9119
9411
  if (needConvert) {
9120
9412
  if (stringStart > segStart)
9121
9413
  parts.push(line.slice(segStart, stringStart));
9122
- const stringContent = line.slice(stringStart + 1, i);
9123
9414
  if (inString === 2)
9124
9415
  parts.push(convertDoubleToSingle(`"${stringContent}"`));
9125
9416
  else
@@ -9495,6 +9786,20 @@ function formatCode(src, cfg, filePath) {
9495
9786
  return `${joined}
9496
9787
  `;
9497
9788
  }
9789
+ function quoteConvertible(line, openIdx, quote, target) {
9790
+ for (let j = openIdx + 1;j < line.length; j++) {
9791
+ const c = line[j];
9792
+ if (c === "\\") {
9793
+ j++;
9794
+ continue;
9795
+ }
9796
+ if (c === quote)
9797
+ return true;
9798
+ if (c === target)
9799
+ return false;
9800
+ }
9801
+ return true;
9802
+ }
9498
9803
  function detectQuoteIssues(line, preferred) {
9499
9804
  if (/^\s*\/\/\/\s*<reference/.test(line)) {
9500
9805
  return [];
@@ -9513,21 +9818,18 @@ function detectQuoteIssues(line, preferred) {
9513
9818
  continue;
9514
9819
  }
9515
9820
  if (!inString) {
9821
+ if (ch === "/" && line[i + 1] === "/") {
9822
+ break;
9823
+ }
9516
9824
  if (ch === "'") {
9517
- if (preferred === "double") {
9518
- const beforeSlash = line.lastIndexOf("//", i);
9519
- if (beforeSlash === -1 || beforeSlash > i) {
9520
- indices.push(i);
9521
- }
9825
+ if (preferred === "double" && quoteConvertible(line, i, "'", '"')) {
9826
+ indices.push(i);
9522
9827
  }
9523
9828
  inString = "single";
9524
9829
  continue;
9525
9830
  } else if (ch === '"') {
9526
- if (preferred === "single") {
9527
- const beforeSlash = line.lastIndexOf("//", i);
9528
- if (beforeSlash === -1 || beforeSlash > i) {
9529
- indices.push(i);
9530
- }
9831
+ if (preferred === "single" && quoteConvertible(line, i, '"', "'")) {
9832
+ indices.push(i);
9531
9833
  }
9532
9834
  inString = "double";
9533
9835
  continue;
@@ -9617,16 +9919,20 @@ function isRegexStart(input, slashIdx) {
9617
9919
  function scanRegexEnd(input, start) {
9618
9920
  let i = start + 1;
9619
9921
  let inClass = false;
9922
+ let classStart = -1;
9620
9923
  while (i < input.length) {
9621
9924
  const c = input[i];
9622
9925
  if (c === "\\") {
9623
9926
  i += 2;
9624
9927
  continue;
9625
9928
  }
9626
- if (c === "[") {
9929
+ if (c === "[" && !inClass) {
9627
9930
  inClass = true;
9628
- } else if (c === "]") {
9629
- inClass = false;
9931
+ classStart = i;
9932
+ } else if (c === "]" && inClass) {
9933
+ const bodyStart = input[classStart + 1] === "^" ? classStart + 2 : classStart + 1;
9934
+ if (i !== bodyStart)
9935
+ inClass = false;
9630
9936
  } else if (c === "/" && !inClass) {
9631
9937
  i++;
9632
9938
  while (i < input.length && /[a-z]/i.test(input[i]))
@@ -18828,7 +19134,7 @@ var init_no_empty = __esm(() => {
18828
19134
  const beforeBlock = line.substring(0, line.lastIndexOf("{")).trim();
18829
19135
  const isStatementBlock = /(?:if|else|for|while|do|try|catch|finally|function|\})\s*$/.test(beforeBlock);
18830
19136
  const isObjectLiteral = /[=:]$/.test(beforeBlock) || /return\s*$/.test(beforeBlock);
18831
- if (isStatementBlock && !isObjectLiteral && j === i + 1) {
19137
+ if (isStatementBlock && !isObjectLiteral) {
18832
19138
  issues.push({
18833
19139
  filePath: ctx.filePath,
18834
19140
  line: i + 1,
@@ -22256,7 +22562,9 @@ var init_prefer_const = __esm(() => {
22256
22562
  for (const { name, fixable } of decls) {
22257
22563
  if (!fixable)
22258
22564
  continue;
22259
- issues.push({ filePath: ctx.filePath, line: i + 1, column: Math.max(1, line.indexOf(name) + 1), ruleId: "prefer-const", message: `'${name}' is never reassigned. Use 'const' instead`, severity: "error", help: `Change 'let ${name}' to 'const ${name}' since the variable is never reassigned. This makes your code more predictable and prevents accidental mutations` });
22565
+ const wordIdx = line.search(new RegExp(`\\b${name.replace(/\$/g, "\\$")}\\b`));
22566
+ const nameIdx = wordIdx !== -1 ? wordIdx : line.indexOf(name);
22567
+ issues.push({ filePath: ctx.filePath, line: i + 1, column: Math.max(1, nameIdx + 1), ruleId: "prefer-const", message: `'${name}' is never reassigned. Use 'const' instead`, severity: "error", help: `Change 'let ${name}' to 'const ${name}' since the variable is never reassigned. This makes your code more predictable and prevents accidental mutations` });
22260
22568
  }
22261
22569
  }
22262
22570
  return issues;
@@ -23757,69 +24065,73 @@ var init_code_block_style = __esm(() => {
23757
24065
  });
23758
24066
 
23759
24067
  // src/rules/markdown/code-fence-style.ts
23760
- var codeFenceStyleRule;
23761
- var init_code_fence_style = __esm(() => {
23762
- codeFenceStyleRule = {
23763
- meta: {
23764
- docs: "Code fence style should be consistent (backtick or tilde)"
24068
+ function fenceBoundaryLines(lines) {
24069
+ const out = new Set;
24070
+ let fenceChar = null;
24071
+ let fenceLen = 0;
24072
+ for (let i = 0;i < lines.length; i++) {
24073
+ const trimmed = lines[i].trim();
24074
+ if (fenceChar === null) {
24075
+ const open = trimmed.match(/^(`{3,}|~{3,})(.*)$/);
24076
+ if (open) {
24077
+ const ch = open[1][0];
24078
+ if (ch === "`" && open[2].includes("`"))
24079
+ continue;
24080
+ fenceChar = ch;
24081
+ fenceLen = open[1].length;
24082
+ out.add(i);
24083
+ }
24084
+ } else {
24085
+ const close = trimmed.match(/^(`{3,}|~{3,})\s*$/);
24086
+ if (close && close[1][0] === fenceChar && close[1].length >= fenceLen) {
24087
+ out.add(i);
24088
+ fenceChar = null;
24089
+ fenceLen = 0;
24090
+ }
24091
+ }
24092
+ }
24093
+ return out;
24094
+ }
24095
+ var codeFenceStyleRule;
24096
+ var init_code_fence_style = __esm(() => {
24097
+ codeFenceStyleRule = {
24098
+ meta: {
24099
+ docs: "Code fence style should be consistent (backtick or tilde)"
23765
24100
  },
23766
24101
  check: (text, ctx) => {
23767
24102
  const issues = [];
23768
24103
  const lines = text.split(/\r?\n/);
24104
+ const boundaries = fenceBoundaryLines(lines);
23769
24105
  const options = ctx.options || {};
23770
24106
  const style = options.style || "consistent";
23771
24107
  let detectedStyle = null;
23772
- for (let i = 0;i < lines.length; i++) {
24108
+ for (const i of boundaries) {
23773
24109
  const line = lines[i];
23774
- const isBacktick = /^`{3,}/.test(line);
23775
- const isTilde = /^~{3,}/.test(line);
23776
- if (isBacktick) {
23777
- if (style === "tilde") {
24110
+ const isBacktick = /^`{3,}/.test(line.trim());
24111
+ const fenceStyle = isBacktick ? "backtick" : "tilde";
24112
+ if (style === "backtick" || style === "tilde") {
24113
+ if (fenceStyle !== style) {
23778
24114
  issues.push({
23779
24115
  filePath: ctx.filePath,
23780
24116
  line: i + 1,
23781
24117
  column: 1,
23782
24118
  ruleId: "markdown/code-fence-style",
23783
- message: "Expected tilde (~~~) code fence",
24119
+ message: style === "tilde" ? "Expected tilde (~~~) code fence" : "Expected backtick (```) code fence",
23784
24120
  severity: "error"
23785
24121
  });
23786
- } else if (style === "consistent") {
23787
- if (detectedStyle === null) {
23788
- detectedStyle = "backtick";
23789
- } else if (detectedStyle === "tilde") {
23790
- issues.push({
23791
- filePath: ctx.filePath,
23792
- line: i + 1,
23793
- column: 1,
23794
- ruleId: "markdown/code-fence-style",
23795
- message: "Code fence style should be consistent throughout document",
23796
- severity: "error"
23797
- });
23798
- }
23799
24122
  }
23800
- } else if (isTilde) {
23801
- if (style === "backtick") {
24123
+ } else if (style === "consistent") {
24124
+ if (detectedStyle === null) {
24125
+ detectedStyle = fenceStyle;
24126
+ } else if (detectedStyle !== fenceStyle) {
23802
24127
  issues.push({
23803
24128
  filePath: ctx.filePath,
23804
24129
  line: i + 1,
23805
24130
  column: 1,
23806
24131
  ruleId: "markdown/code-fence-style",
23807
- message: "Expected backtick (```) code fence",
24132
+ message: "Code fence style should be consistent throughout document",
23808
24133
  severity: "error"
23809
24134
  });
23810
- } else if (style === "consistent") {
23811
- if (detectedStyle === null) {
23812
- detectedStyle = "tilde";
23813
- } else if (detectedStyle === "backtick") {
23814
- issues.push({
23815
- filePath: ctx.filePath,
23816
- line: i + 1,
23817
- column: 1,
23818
- ruleId: "markdown/code-fence-style",
23819
- message: "Code fence style should be consistent throughout document",
23820
- severity: "error"
23821
- });
23822
- }
23823
24135
  }
23824
24136
  }
23825
24137
  }
@@ -23829,32 +24141,22 @@ var init_code_fence_style = __esm(() => {
23829
24141
  const options = ctx.options || {};
23830
24142
  const style = options.style || "consistent";
23831
24143
  const lines = text.split(/\r?\n/);
24144
+ const boundaries = fenceBoundaryLines(lines);
23832
24145
  let targetStyle = "backtick";
23833
- if (style === "backtick") {
23834
- targetStyle = "backtick";
23835
- } else if (style === "tilde") {
23836
- targetStyle = "tilde";
23837
- } else if (style === "consistent") {
23838
- for (const line of lines) {
23839
- if (/^`{3,}/.test(line)) {
23840
- targetStyle = "backtick";
23841
- break;
23842
- } else if (/^~{3,}/.test(line)) {
23843
- targetStyle = "tilde";
23844
- break;
23845
- }
24146
+ if (style === "backtick" || style === "tilde") {
24147
+ targetStyle = style;
24148
+ } else {
24149
+ for (const i of boundaries) {
24150
+ targetStyle = /^`{3,}/.test(lines[i].trim()) ? "backtick" : "tilde";
24151
+ break;
23846
24152
  }
23847
24153
  }
23848
- const fixedLines = lines.map((line) => {
23849
- if (targetStyle === "backtick") {
23850
- return line.replace(/^(~{3,})(.*)$/, (_match, _tildes, rest) => {
23851
- return `\`\`\`${rest}`;
23852
- });
23853
- } else {
23854
- return line.replace(/^(`{3,})(.*)$/, (_match, _backticks, rest) => {
23855
- return `~~~${rest}`;
23856
- });
23857
- }
24154
+ const targetChar = targetStyle === "backtick" ? "`" : "~";
24155
+ const sourceRun = targetStyle === "backtick" ? /^(\s*)(~{3,})(.*)$/ : /^(\s*)(`{3,})(.*)$/;
24156
+ const fixedLines = lines.map((line, i) => {
24157
+ if (!boundaries.has(i))
24158
+ return line;
24159
+ return line.replace(sourceRun, (_m, ws, run, rest) => `${ws}${targetChar.repeat(run.length)}${rest}`);
23858
24160
  });
23859
24161
  return fixedLines.join(`
23860
24162
  `);
@@ -23972,16 +24274,12 @@ var init_emphasis_style = __esm(() => {
23972
24274
  const options = ctx.options || {};
23973
24275
  const style = options.style || "consistent";
23974
24276
  let detectedStyle = null;
23975
- let inFence = false;
24277
+ const inCode = getCodeBlockLines(lines);
23976
24278
  for (let i = 0;i < lines.length; i++) {
23977
24279
  const line = lines[i];
23978
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
23979
- inFence = !inFence;
23980
- continue;
23981
- }
23982
- if (inFence)
24280
+ if (inCode.has(i))
23983
24281
  continue;
23984
- const stripped = line.replace(/``[^`]+``/g, " ").replace(/`[^`]+`/g, " ");
24282
+ const stripped = maskInlineCode(line);
23985
24283
  const asteriskMatches = stripped.matchAll(/(?<!\*)\*(?!\*)([^*]+)\*(?!\*)/g);
23986
24284
  for (const match of asteriskMatches) {
23987
24285
  if (style === "underscore") {
@@ -24957,15 +25255,11 @@ var init_no_bare_urls = __esm(() => {
24957
25255
  check: (text, ctx) => {
24958
25256
  const issues = [];
24959
25257
  const lines = text.split(/\r?\n/);
24960
- let inFence = false;
25258
+ const codeLines = getCodeBlockLines(lines);
24961
25259
  let inHtmlComment = false;
24962
25260
  for (let i = 0;i < lines.length; i++) {
24963
25261
  const line = lines[i];
24964
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
24965
- inFence = !inFence;
24966
- continue;
24967
- }
24968
- if (inFence)
25262
+ if (codeLines.has(i))
24969
25263
  continue;
24970
25264
  if (line.includes("<!--"))
24971
25265
  inHtmlComment = true;
@@ -24995,14 +25289,10 @@ var init_no_bare_urls = __esm(() => {
24995
25289
  },
24996
25290
  fix: (text) => {
24997
25291
  const lines = text.split(/\r?\n/);
24998
- let inFence = false;
25292
+ const codeLines = getCodeBlockLines(lines);
24999
25293
  let inHtmlComment = false;
25000
- const fixedLines = lines.map((line) => {
25001
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
25002
- inFence = !inFence;
25003
- return line;
25004
- }
25005
- if (inFence)
25294
+ const fixedLines = lines.map((line, i) => {
25295
+ if (codeLines.has(i))
25006
25296
  return line;
25007
25297
  if (line.includes("<!--"))
25008
25298
  inHtmlComment = true;
@@ -25324,7 +25614,19 @@ var init_no_hard_tabs = __esm(() => {
25324
25614
  return issues;
25325
25615
  },
25326
25616
  fix: (text) => {
25327
- return text.replace(/\t/g, " ");
25617
+ const lines = text.split(/\r?\n/);
25618
+ let inFence = false;
25619
+ const fixed = lines.map((line) => {
25620
+ if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
25621
+ inFence = !inFence;
25622
+ return line;
25623
+ }
25624
+ if (inFence)
25625
+ return line;
25626
+ return line.replace(/\t/g, " ");
25627
+ });
25628
+ return fixed.join(`
25629
+ `);
25328
25630
  }
25329
25631
  };
25330
25632
  });
@@ -25699,14 +26001,10 @@ var init_no_space_in_code = __esm(() => {
25699
26001
  check: (text, ctx) => {
25700
26002
  const issues = [];
25701
26003
  const lines = text.split(/\r?\n/);
25702
- let inFence = false;
26004
+ const codeLines = getCodeBlockLines(lines);
25703
26005
  for (let i = 0;i < lines.length; i++) {
25704
26006
  const line = lines[i];
25705
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
25706
- inFence = !inFence;
25707
- continue;
25708
- }
25709
- if (inFence)
26007
+ if (codeLines.has(i))
25710
26008
  continue;
25711
26009
  const codeSpanPattern = /(`+)([\s\S]*?)\1/g;
25712
26010
  for (const match of line.matchAll(codeSpanPattern)) {
@@ -25727,15 +26025,11 @@ var init_no_space_in_code = __esm(() => {
25727
26025
  },
25728
26026
  fix: (text) => {
25729
26027
  const lines = text.split(/\r?\n/);
25730
- let inFence = false;
26028
+ const codeLines = getCodeBlockLines(lines);
25731
26029
  const result = [];
25732
- for (const line of lines) {
25733
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
25734
- inFence = !inFence;
25735
- result.push(line);
25736
- continue;
25737
- }
25738
- if (inFence) {
26030
+ for (let i = 0;i < lines.length; i++) {
26031
+ const line = lines[i];
26032
+ if (codeLines.has(i)) {
25739
26033
  result.push(line);
25740
26034
  continue;
25741
26035
  }
@@ -25767,14 +26061,10 @@ var init_no_space_in_emphasis = __esm(() => {
25767
26061
  check: (text, ctx) => {
25768
26062
  const issues = [];
25769
26063
  const lines = text.split(/\r?\n/);
25770
- let inFence = false;
26064
+ const codeLines = getCodeBlockLines(lines);
25771
26065
  for (let i = 0;i < lines.length; i++) {
25772
26066
  const line = lines[i];
25773
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
25774
- inFence = !inFence;
25775
- continue;
25776
- }
25777
- if (inFence)
26067
+ if (codeLines.has(i))
25778
26068
  continue;
25779
26069
  const stripped = line.replace(/`[^`]+`/g, (m) => "\x01".repeat(m.length));
25780
26070
  for (const pattern of EMPHASIS_SPACE_PATTERNS) {
@@ -25794,15 +26084,11 @@ var init_no_space_in_emphasis = __esm(() => {
25794
26084
  },
25795
26085
  fix: (text) => {
25796
26086
  const lines = text.split(/\r?\n/);
25797
- let inFence = false;
26087
+ const codeLines = getCodeBlockLines(lines);
25798
26088
  const result = [];
25799
- for (const line of lines) {
25800
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
25801
- inFence = !inFence;
25802
- result.push(line);
25803
- continue;
25804
- }
25805
- if (inFence) {
26089
+ for (let i = 0;i < lines.length; i++) {
26090
+ const line = lines[i];
26091
+ if (codeLines.has(i)) {
25806
26092
  result.push(line);
25807
26093
  continue;
25808
26094
  }
@@ -25917,7 +26203,14 @@ var init_no_trailing_punctuation = __esm(() => {
25917
26203
  const punctuation = options.punctuation || ".,;:!?";
25918
26204
  const punctRegex = new RegExp(`[${punctuation.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}]+$`);
25919
26205
  const lines = text.split(/\r?\n/);
26206
+ let inFence = false;
25920
26207
  const fixedLines = lines.map((line, i) => {
26208
+ if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
26209
+ inFence = !inFence;
26210
+ return line;
26211
+ }
26212
+ if (inFence)
26213
+ return line;
25921
26214
  const atxMatch = line.match(/^(#{1,6}\s+)(.+?)(\s*#+\s*)?$/);
25922
26215
  if (atxMatch) {
25923
26216
  const prefix = atxMatch[1];
@@ -26758,17 +27051,13 @@ var init_ul_style = __esm(() => {
26758
27051
  check: (text, ctx) => {
26759
27052
  const issues = [];
26760
27053
  const lines = text.split(/\r?\n/);
27054
+ const codeLines = getCodeBlockLines(lines);
26761
27055
  const options = ctx.options || {};
26762
27056
  const style = options.style || "consistent";
26763
27057
  let detectedStyle = null;
26764
- let inFence = false;
26765
27058
  for (let i = 0;i < lines.length; i++) {
26766
27059
  const line = lines[i];
26767
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
26768
- inFence = !inFence;
26769
- continue;
26770
- }
26771
- if (inFence)
27060
+ if (codeLines.has(i))
26772
27061
  continue;
26773
27062
  const match = line.match(/^(\s*)([*\-+])\s+/);
26774
27063
  if (match) {
@@ -26822,6 +27111,7 @@ var init_ul_style = __esm(() => {
26822
27111
  const options = ctx.options || {};
26823
27112
  const style = options.style || "consistent";
26824
27113
  const lines = text.split(/\r?\n/);
27114
+ const codeLines = getCodeBlockLines(lines);
26825
27115
  let targetMarker = "*";
26826
27116
  if (style === "asterisk") {
26827
27117
  targetMarker = "*";
@@ -26830,21 +27120,18 @@ var init_ul_style = __esm(() => {
26830
27120
  } else if (style === "plus") {
26831
27121
  targetMarker = "+";
26832
27122
  } else if (style === "consistent") {
26833
- for (const line of lines) {
26834
- const match = line.match(/^(\s*)([*\-+])\s+/);
27123
+ for (let i = 0;i < lines.length; i++) {
27124
+ if (codeLines.has(i))
27125
+ continue;
27126
+ const match = lines[i].match(/^(\s*)([*\-+])\s+/);
26835
27127
  if (match) {
26836
27128
  targetMarker = match[2];
26837
27129
  break;
26838
27130
  }
26839
27131
  }
26840
27132
  }
26841
- let inFence = false;
26842
- const fixedLines = lines.map((line) => {
26843
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
26844
- inFence = !inFence;
26845
- return line;
26846
- }
26847
- if (inFence)
27133
+ const fixedLines = lines.map((line, i) => {
27134
+ if (codeLines.has(i))
26848
27135
  return line;
26849
27136
  return line.replace(/^(\s*)([*\-+])(\s+)/, `$1${targetMarker}$3`);
26850
27137
  });
@@ -27048,36 +27335,26 @@ var init_prefer_global_buffer = __esm(() => {
27048
27335
  check(content, context) {
27049
27336
  const issues = [];
27050
27337
  const lines = content.split(/\r?\n/);
27051
- const hasBufferImport = content.match(/(?:import|require)\s*\(?.*?['"]buffer['"]/);
27052
- if (!hasBufferImport) {
27053
- for (let i = 0;i < lines.length; i++) {
27054
- const line = lines[i];
27055
- const trimmed = line.trim();
27056
- if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*"))
27057
- continue;
27058
- const bufferUsagePattern = /\bBuffer\./;
27059
- if (bufferUsagePattern.test(line)) {
27060
- const match = line.match(bufferUsagePattern);
27061
- if (match) {
27062
- const beforeMatch = line.slice(0, match.index);
27063
- const singleQuotes = (beforeMatch.match(/'/g) || []).length;
27064
- const doubleQuotes = (beforeMatch.match(/"/g) || []).length;
27065
- const backticks = (beforeMatch.match(/`/g) || []).length;
27066
- if (singleQuotes % 2 === 1 || doubleQuotes % 2 === 1 || backticks % 2 === 1)
27067
- continue;
27068
- const column = (match.index || 0) + 1;
27069
- issues.push({
27070
- filePath: context.filePath,
27071
- line: i + 1,
27072
- column,
27073
- ruleId: "node/prefer-global/buffer",
27074
- message: `Unexpected use of the global variable 'Buffer'. Use 'require("buffer").Buffer' instead`,
27075
- severity: "error",
27076
- help: 'Import Buffer explicitly: const { Buffer } = require("buffer")'
27077
- });
27078
- }
27079
- }
27080
- }
27338
+ const importRe = /(?:import\b[^;\n]*\bfrom\s*|require\s*\(\s*)['"](?:node:)?buffer['"]/;
27339
+ for (let i = 0;i < lines.length; i++) {
27340
+ const line = lines[i];
27341
+ const trimmed = line.trim();
27342
+ if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*"))
27343
+ continue;
27344
+ const match = line.match(importRe);
27345
+ if (!match || match.index === undefined)
27346
+ continue;
27347
+ if (!/\bBuffer\b/.test(line))
27348
+ continue;
27349
+ issues.push({
27350
+ filePath: context.filePath,
27351
+ line: i + 1,
27352
+ column: match.index + 1,
27353
+ ruleId: "node/prefer-global/buffer",
27354
+ message: "Unexpected import of 'Buffer'. Use the global 'Buffer' instead",
27355
+ severity: "error",
27356
+ help: "`Buffer` is a global in Node.js \u2014 remove the import and use it directly."
27357
+ });
27081
27358
  }
27082
27359
  return issues;
27083
27360
  }
@@ -27095,36 +27372,24 @@ var init_prefer_global_process = __esm(() => {
27095
27372
  check(content, context) {
27096
27373
  const issues = [];
27097
27374
  const lines = content.split(/\r?\n/);
27098
- const hasProcessImport = content.match(/(?:import|require)\s*\(?.*?['"](?:node:)?process['"]/);
27099
- if (!hasProcessImport) {
27100
- for (let i = 0;i < lines.length; i++) {
27101
- const line = lines[i];
27102
- const trimmed = line.trim();
27103
- if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*"))
27104
- continue;
27105
- const processUsagePattern = /\bprocess\./;
27106
- if (processUsagePattern.test(line)) {
27107
- const match = line.match(processUsagePattern);
27108
- if (match) {
27109
- const beforeMatch = line.slice(0, match.index);
27110
- const singleQuotes = (beforeMatch.match(/'/g) || []).length;
27111
- const doubleQuotes = (beforeMatch.match(/"/g) || []).length;
27112
- const backticks = (beforeMatch.match(/`/g) || []).length;
27113
- if (singleQuotes % 2 === 1 || doubleQuotes % 2 === 1 || backticks % 2 === 1)
27114
- continue;
27115
- const column = (match.index || 0) + 1;
27116
- issues.push({
27117
- filePath: context.filePath,
27118
- line: i + 1,
27119
- column,
27120
- ruleId: "node/prefer-global/process",
27121
- message: `Unexpected use of the global variable 'process'. Use 'require("process")' instead`,
27122
- severity: "error",
27123
- help: 'Import process explicitly: const process = require("process")'
27124
- });
27125
- }
27126
- }
27127
- }
27375
+ const importRe = /(?:import\b[^;\n]*\bfrom\s*|require\s*\(\s*)['"](?:node:)?process['"]/;
27376
+ for (let i = 0;i < lines.length; i++) {
27377
+ const line = lines[i];
27378
+ const trimmed = line.trim();
27379
+ if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*"))
27380
+ continue;
27381
+ const match = line.match(importRe);
27382
+ if (!match || match.index === undefined)
27383
+ continue;
27384
+ issues.push({
27385
+ filePath: context.filePath,
27386
+ line: i + 1,
27387
+ column: match.index + 1,
27388
+ ruleId: "node/prefer-global/process",
27389
+ message: "Unexpected import of 'process'. Use the global 'process' instead",
27390
+ severity: "error",
27391
+ help: "`process` is a global in Node.js \u2014 remove the import and use it directly."
27392
+ });
27128
27393
  }
27129
27394
  return issues;
27130
27395
  }
@@ -27284,7 +27549,7 @@ function startsWithShebang(code) {
27284
27549
  return /^#!\s*\//.test(code);
27285
27550
  }
27286
27551
  function stripComments(code) {
27287
- return code.replace(/\/\*(.|[\r\n])*?\*\//gm, "").replace(/\/\/.*/g, "");
27552
+ return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*/g, "");
27288
27553
  }
27289
27554
  function crawlExportsOrImports(value, basePath, isImports, visitor) {
27290
27555
  _crawl(value, basePath, isImports, visitor);
@@ -27765,11 +28030,15 @@ var init_local_dependency = __esm(() => {
27765
28030
  if (!pkg)
27766
28031
  return [];
27767
28032
  const issues = [];
27768
- if (pkg.dependencies && typeof pkg.dependencies === "object") {
27769
- for (const depName of Object.keys(pkg.dependencies)) {
27770
- const depVersion = pkg.dependencies[depName];
28033
+ const fields = ["dependencies", "optionalDependencies", "peerDependencies"];
28034
+ for (const field of fields) {
28035
+ const deps = pkg[field];
28036
+ if (!deps || typeof deps !== "object")
28037
+ continue;
28038
+ for (const depName of Object.keys(deps)) {
28039
+ const depVersion = deps[depName];
27771
28040
  if (typeof depVersion === "string" && (depVersion.startsWith("file:") || depVersion.startsWith("link:"))) {
27772
- issues.push(createIssue(context.filePath, content, ["dependencies", depName], "publint/local-dependency", `The "${depName}" dependency references "${depVersion}" that will likely not work when installed by end-users.`, "error", "Local file references (file: or link:) are not portable and will fail for consumers of the package."));
28041
+ issues.push(createIssue(context.filePath, content, [field, depName], "publint/local-dependency", `The "${depName}" dependency references "${depVersion}" that will likely not work when installed by end-users.`, "error", "Local file references (file: or link:) are not portable and will fail for consumers of the package."));
27773
28042
  }
27774
28043
  }
27775
28044
  }
@@ -29458,7 +29727,7 @@ function parseRegexLiteral(content, idx) {
29458
29727
  return null;
29459
29728
  const pattern = content.slice(idx + 1, closedAt);
29460
29729
  let flagEnd = closedAt + 1;
29461
- while (flagEnd < content.length && /[gimsuy]/.test(content[flagEnd]))
29730
+ while (flagEnd < content.length && /[gimsuvy]/.test(content[flagEnd]))
29462
29731
  flagEnd++;
29463
29732
  if (/\\[1-9]/.test(pattern))
29464
29733
  return null;
@@ -29576,7 +29845,7 @@ function skipRegexLiteral(content, idx) {
29576
29845
  inClass = false;
29577
29846
  } else if (c === "/" && !inClass) {
29578
29847
  i++;
29579
- while (i < content.length && /[gimsuy]/.test(content[i]))
29848
+ while (i < content.length && /[gimsuvy]/.test(content[i]))
29580
29849
  i++;
29581
29850
  return i;
29582
29851
  } else if (c === `
@@ -30273,16 +30542,12 @@ var init_quote_variables = __esm(() => {
30273
30542
  inHeredoc = false;
30274
30543
  continue;
30275
30544
  }
30276
- const heredocMatch = line.match(/<<-?\s*(['"])(\w+)\1/);
30277
- if (heredocMatch) {
30278
- inHeredoc = true;
30279
- heredocDelim = heredocMatch[2];
30280
- continue;
30281
- }
30282
- const heredocMatch2 = line.match(/<<-?\s*(\w+)/);
30283
- if (heredocMatch2 && !heredocMatch) {
30545
+ const delim = heredocDelimiter(line);
30546
+ if (delim) {
30284
30547
  inHeredoc = true;
30285
- heredocDelim = heredocMatch2[1];
30548
+ heredocDelim = delim;
30549
+ if (new RegExp(`<<-?\\s*['"]${delim}['"]`).test(line))
30550
+ continue;
30286
30551
  }
30287
30552
  const trimmed = line.replace(/^\s+/, "");
30288
30553
  if (trimmed.startsWith("#"))
@@ -30511,381 +30776,104 @@ var init_indent = __esm(() => {
30511
30776
  inHeredoc = false;
30512
30777
  continue;
30513
30778
  }
30514
- const heredocMatch = line.match(/<<-?\s*['"]?(\w+)['"]?/);
30515
- if (heredocMatch) {
30516
- inHeredoc = true;
30517
- heredocDelim = heredocMatch[1];
30518
- }
30519
- const trimmed = line.replace(/^\s+/, "");
30520
- if (i === 0 && trimmed.startsWith("#!"))
30521
- continue;
30522
- if (/^case\b/.test(trimmed) && /\bin\s*$/.test(trimmed)) {
30523
- caseDepth++;
30524
- inCase = true;
30525
- }
30526
- if (/^esac\b/.test(trimmed)) {
30527
- caseDepth = Math.max(0, caseDepth - 1);
30528
- if (caseDepth === 0)
30529
- inCase = false;
30530
- }
30531
- if (shouldDecreaseBefore(trimmed))
30532
- expectedLevel = Math.max(0, expectedLevel - 1);
30533
- let actualSpaces = 0;
30534
- for (let j = 0;j < line.length; j++) {
30535
- if (line[j] === " ") {
30536
- actualSpaces++;
30537
- } else if (line[j] === "\t") {
30538
- actualSpaces += indentSize;
30539
- } else {
30540
- break;
30541
- }
30542
- }
30543
- const expectedSpaces = expectedLevel * indentSize;
30544
- if (actualSpaces !== expectedSpaces && !trimmed.startsWith("#")) {
30545
- issues.push({
30546
- filePath: ctx.filePath,
30547
- line: i + 1,
30548
- column: 1,
30549
- ruleId: "shell/indent",
30550
- message: `Expected ${expectedSpaces} spaces of indentation, found ${actualSpaces}`,
30551
- severity: "warning"
30552
- });
30553
- }
30554
- if (shouldIncreaseAfter(trimmed, inCase))
30555
- expectedLevel++;
30556
- }
30557
- return issues;
30558
- },
30559
- fix(content, ctx) {
30560
- const lines = content.split(/\r?\n/);
30561
- const indentSize = ctx.config.format.indent || 2;
30562
- const useTab = ctx.config.format.indentStyle === "tabs";
30563
- const result = [];
30564
- let level = 0;
30565
- let inHeredoc = false;
30566
- let heredocDelim = "";
30567
- let inCase = false;
30568
- let caseDepth = 0;
30569
- for (let i = 0;i < lines.length; i++) {
30570
- const line = lines[i];
30571
- if (/^\s*$/.test(line)) {
30572
- result.push("");
30573
- continue;
30574
- }
30575
- if (inHeredoc) {
30576
- result.push(line);
30577
- if (line.trim() === heredocDelim)
30578
- inHeredoc = false;
30579
- continue;
30580
- }
30581
- const heredocMatch = line.match(/<<-?\s*['"]?(\w+)['"]?/);
30582
- if (heredocMatch) {
30583
- inHeredoc = true;
30584
- heredocDelim = heredocMatch[1];
30585
- }
30586
- const trimmed = line.replace(/^\s+/, "").trimEnd();
30587
- if (i === 0 && trimmed.startsWith("#!")) {
30588
- result.push(trimmed);
30589
- continue;
30590
- }
30591
- if (/^case\b/.test(trimmed) && /\bin\s*$/.test(trimmed)) {
30592
- caseDepth++;
30593
- inCase = true;
30594
- }
30595
- if (/^esac\b/.test(trimmed)) {
30596
- caseDepth = Math.max(0, caseDepth - 1);
30597
- if (caseDepth === 0)
30598
- inCase = false;
30599
- }
30600
- if (shouldDecreaseBefore(trimmed))
30601
- level = Math.max(0, level - 1);
30602
- const indent = useTab ? "\t".repeat(level) : " ".repeat(level * indentSize);
30603
- result.push(indent + trimmed);
30604
- if (shouldIncreaseAfter(trimmed, inCase))
30605
- level++;
30606
- }
30607
- return result.join(`
30608
- `);
30609
- }
30610
- };
30611
- });
30612
-
30613
- // src/rules/shell/_shared.ts
30614
- function maskShellStrings(line) {
30615
- const n = line.length;
30616
- const out = Array.from({ length: n }, (_, i2) => line[i2]);
30617
- let i = 0;
30618
- while (i < n) {
30619
- const ch = line[i];
30620
- if (ch === "#" && (i === 0 || /\s/.test(line[i - 1]))) {
30621
- while (i < n) {
30622
- out[i] = " ";
30623
- i++;
30624
- }
30625
- break;
30626
- }
30627
- if (ch === "$" && line[i + 1] === "'") {
30628
- i += 2;
30629
- while (i < n) {
30630
- if (line[i] === "\\" && i + 1 < n) {
30631
- out[i] = " ";
30632
- out[i + 1] = " ";
30633
- i += 2;
30634
- continue;
30635
- }
30636
- if (line[i] === "'") {
30637
- i++;
30638
- break;
30639
- }
30640
- out[i] = " ";
30641
- i++;
30642
- }
30643
- continue;
30644
- }
30645
- if (ch === "$" && line[i + 1] === '"') {
30646
- i++;
30647
- i = maskDoubleQuoted(line, i, out);
30648
- continue;
30649
- }
30650
- if (ch === "$" && line[i + 1] === "{") {
30651
- i += 2;
30652
- let depth = 1;
30653
- while (i < n && depth > 0) {
30654
- const c = line[i];
30655
- if (c === "\\" && i + 1 < n) {
30656
- out[i] = " ";
30657
- out[i + 1] = " ";
30658
- i += 2;
30659
- continue;
30660
- }
30661
- if (c === "'") {
30662
- i++;
30663
- while (i < n && line[i] !== "'") {
30664
- out[i] = " ";
30665
- i++;
30666
- }
30667
- if (i < n)
30668
- i++;
30669
- continue;
30670
- }
30671
- if (c === '"') {
30672
- i = maskDoubleQuoted(line, i, out);
30673
- continue;
30674
- }
30675
- if (c === "{") {
30676
- depth++;
30677
- i++;
30678
- continue;
30679
- }
30680
- if (c === "}") {
30681
- depth--;
30682
- i++;
30683
- continue;
30684
- }
30685
- out[i] = " ";
30686
- i++;
30687
- }
30688
- continue;
30689
- }
30690
- if (ch === "$" && line[i + 1] === "(" && line[i + 2] === "(") {
30691
- i += 3;
30692
- let depth = 2;
30693
- while (i < n && depth > 0) {
30694
- const c = line[i];
30695
- if (c === "(") {
30696
- depth++;
30697
- i++;
30698
- continue;
30699
- }
30700
- if (c === ")") {
30701
- depth--;
30702
- i++;
30703
- continue;
30704
- }
30705
- out[i] = " ";
30706
- i++;
30707
- }
30708
- continue;
30709
- }
30710
- if (ch === "$" && line[i + 1] === "(") {
30711
- i += 2;
30712
- let depth = 1;
30713
- while (i < n && depth > 0) {
30714
- const c = line[i];
30715
- if (c === "\\" && i + 1 < n) {
30716
- out[i] = " ";
30717
- out[i + 1] = " ";
30718
- i += 2;
30719
- continue;
30720
- }
30721
- if (c === "'") {
30722
- i++;
30723
- while (i < n && line[i] !== "'") {
30724
- out[i] = " ";
30725
- i++;
30726
- }
30727
- if (i < n)
30728
- i++;
30729
- continue;
30730
- }
30731
- if (c === '"') {
30732
- i = maskDoubleQuoted(line, i, out);
30733
- continue;
30734
- }
30735
- if (c === "(") {
30736
- depth++;
30737
- i++;
30738
- continue;
30739
- }
30740
- if (c === ")") {
30741
- depth--;
30742
- i++;
30743
- continue;
30744
- }
30745
- out[i] = " ";
30746
- i++;
30747
- }
30748
- continue;
30749
- }
30750
- if (ch === "'") {
30751
- i++;
30752
- while (i < n && line[i] !== "'") {
30753
- out[i] = " ";
30754
- i++;
30755
- }
30756
- if (i < n)
30757
- i++;
30758
- continue;
30759
- }
30760
- if (ch === '"') {
30761
- i = maskDoubleQuoted(line, i, out);
30762
- continue;
30763
- }
30764
- if (ch === "`") {
30765
- i++;
30766
- while (i < n && line[i] !== "`") {
30767
- if (line[i] === "\\" && i + 1 < n) {
30768
- out[i] = " ";
30769
- out[i + 1] = " ";
30770
- i += 2;
30771
- continue;
30772
- }
30773
- out[i] = " ";
30774
- i++;
30775
- }
30776
- if (i < n)
30777
- i++;
30778
- continue;
30779
- }
30780
- i++;
30781
- }
30782
- return out.join("");
30783
- }
30784
- function maskDoubleQuoted(line, start, out) {
30785
- const n = line.length;
30786
- let i = start + 1;
30787
- while (i < n) {
30788
- const c = line[i];
30789
- if (c === "\\" && i + 1 < n) {
30790
- out[i] = " ";
30791
- out[i + 1] = " ";
30792
- i += 2;
30793
- continue;
30794
- }
30795
- if (c === '"')
30796
- return i + 1;
30797
- if (c === "$" && line[i + 1] === "(" && line[i + 2] === "(") {
30798
- i += 3;
30799
- let depth = 2;
30800
- while (i < n && depth > 0) {
30801
- const x = line[i];
30802
- if (x === "(") {
30803
- depth++;
30804
- i++;
30805
- continue;
30806
- }
30807
- if (x === ")") {
30808
- depth--;
30809
- i++;
30810
- continue;
30811
- }
30812
- out[i] = " ";
30813
- i++;
30814
- }
30815
- continue;
30816
- }
30817
- if (c === "$" && line[i + 1] === "(") {
30818
- i += 2;
30819
- let depth = 1;
30820
- while (i < n && depth > 0) {
30821
- const x = line[i];
30822
- if (x === "\\" && i + 1 < n) {
30823
- out[i] = " ";
30824
- out[i + 1] = " ";
30825
- i += 2;
30779
+ const heredocMatch = line.match(/<<-?\s*['"]?(\w+)['"]?/);
30780
+ if (heredocMatch) {
30781
+ inHeredoc = true;
30782
+ heredocDelim = heredocMatch[1];
30783
+ }
30784
+ const trimmed = line.replace(/^\s+/, "");
30785
+ if (i === 0 && trimmed.startsWith("#!"))
30826
30786
  continue;
30787
+ if (/^case\b/.test(trimmed) && /\bin\s*$/.test(trimmed)) {
30788
+ caseDepth++;
30789
+ inCase = true;
30827
30790
  }
30828
- if (x === "'") {
30829
- i++;
30830
- while (i < n && line[i] !== "'") {
30831
- out[i] = " ";
30832
- i++;
30791
+ if (/^esac\b/.test(trimmed)) {
30792
+ caseDepth = Math.max(0, caseDepth - 1);
30793
+ if (caseDepth === 0)
30794
+ inCase = false;
30795
+ }
30796
+ if (shouldDecreaseBefore(trimmed))
30797
+ expectedLevel = Math.max(0, expectedLevel - 1);
30798
+ let actualSpaces = 0;
30799
+ for (let j = 0;j < line.length; j++) {
30800
+ if (line[j] === " ") {
30801
+ actualSpaces++;
30802
+ } else if (line[j] === "\t") {
30803
+ actualSpaces += indentSize;
30804
+ } else {
30805
+ break;
30833
30806
  }
30834
- if (i < n)
30835
- i++;
30836
- continue;
30837
30807
  }
30838
- if (x === '"') {
30839
- i = maskDoubleQuoted(line, i, out);
30840
- continue;
30808
+ const expectedSpaces = expectedLevel * indentSize;
30809
+ if (actualSpaces !== expectedSpaces && !trimmed.startsWith("#")) {
30810
+ issues.push({
30811
+ filePath: ctx.filePath,
30812
+ line: i + 1,
30813
+ column: 1,
30814
+ ruleId: "shell/indent",
30815
+ message: `Expected ${expectedSpaces} spaces of indentation, found ${actualSpaces}`,
30816
+ severity: "warning"
30817
+ });
30841
30818
  }
30842
- if (x === "(") {
30843
- depth++;
30844
- i++;
30819
+ if (shouldIncreaseAfter(trimmed, inCase))
30820
+ expectedLevel++;
30821
+ }
30822
+ return issues;
30823
+ },
30824
+ fix(content, ctx) {
30825
+ const lines = content.split(/\r?\n/);
30826
+ const indentSize = ctx.config.format.indent || 2;
30827
+ const useTab = ctx.config.format.indentStyle === "tabs";
30828
+ const result = [];
30829
+ let level = 0;
30830
+ let inHeredoc = false;
30831
+ let heredocDelim = "";
30832
+ let inCase = false;
30833
+ let caseDepth = 0;
30834
+ for (let i = 0;i < lines.length; i++) {
30835
+ const line = lines[i];
30836
+ if (/^\s*$/.test(line)) {
30837
+ result.push("");
30845
30838
  continue;
30846
30839
  }
30847
- if (x === ")") {
30848
- depth--;
30849
- i++;
30840
+ if (inHeredoc) {
30841
+ result.push(line);
30842
+ if (line.trim() === heredocDelim)
30843
+ inHeredoc = false;
30850
30844
  continue;
30851
30845
  }
30852
- out[i] = " ";
30853
- i++;
30854
- }
30855
- continue;
30856
- }
30857
- if (c === "$" && line[i + 1] === "{") {
30858
- i += 2;
30859
- let depth = 1;
30860
- while (i < n && depth > 0) {
30861
- const x = line[i];
30862
- if (x === "{") {
30863
- depth++;
30864
- i++;
30865
- continue;
30846
+ const heredocMatch = line.match(/<<-?\s*['"]?(\w+)['"]?/);
30847
+ if (heredocMatch) {
30848
+ inHeredoc = true;
30849
+ heredocDelim = heredocMatch[1];
30866
30850
  }
30867
- if (x === "}") {
30868
- depth--;
30869
- i++;
30851
+ const trimmed = line.replace(/^\s+/, "").trimEnd();
30852
+ if (i === 0 && trimmed.startsWith("#!")) {
30853
+ result.push(trimmed);
30870
30854
  continue;
30871
30855
  }
30872
- out[i] = " ";
30873
- i++;
30856
+ if (/^case\b/.test(trimmed) && /\bin\s*$/.test(trimmed)) {
30857
+ caseDepth++;
30858
+ inCase = true;
30859
+ }
30860
+ if (/^esac\b/.test(trimmed)) {
30861
+ caseDepth = Math.max(0, caseDepth - 1);
30862
+ if (caseDepth === 0)
30863
+ inCase = false;
30864
+ }
30865
+ if (shouldDecreaseBefore(trimmed))
30866
+ level = Math.max(0, level - 1);
30867
+ const indent = useTab ? "\t".repeat(level) : " ".repeat(level * indentSize);
30868
+ result.push(indent + trimmed);
30869
+ if (shouldIncreaseAfter(trimmed, inCase))
30870
+ level++;
30874
30871
  }
30875
- continue;
30872
+ return result.join(`
30873
+ `);
30876
30874
  }
30877
- out[i] = " ";
30878
- i++;
30879
- }
30880
- return i;
30881
- }
30882
- function heredocDelimiter(line) {
30883
- const masked = maskShellStrings(line);
30884
- const m = masked.match(/<<-?\s*['"]?(\w+)['"]?/);
30885
- if (!m)
30886
- return null;
30887
- return m[1];
30888
- }
30875
+ };
30876
+ });
30889
30877
 
30890
30878
  // src/rules/shell/keyword-spacing.ts
30891
30879
  var keywordSpacingRule;
@@ -31046,10 +31034,10 @@ var init_no_trailing_whitespace = __esm(() => {
31046
31034
  inHeredoc = false;
31047
31035
  continue;
31048
31036
  }
31049
- const heredocMatch = line.match(/<<-?\s*['"]?(\w+)['"]?/);
31050
- if (heredocMatch) {
31037
+ const delim = heredocDelimiter(line);
31038
+ if (delim) {
31051
31039
  inHeredoc = true;
31052
- heredocDelim = heredocMatch[1];
31040
+ heredocDelim = delim;
31053
31041
  }
31054
31042
  if (/[ \t]+$/.test(line)) {
31055
31043
  const trailingMatch = line.match(/[ \t]+$/);
@@ -31066,7 +31054,24 @@ var init_no_trailing_whitespace = __esm(() => {
31066
31054
  return issues;
31067
31055
  },
31068
31056
  fix(content) {
31069
- return content.replace(/[ \t]+$/gm, "");
31057
+ const lines = content.split(/\r?\n/);
31058
+ let inHeredoc = false;
31059
+ let heredocDelim = "";
31060
+ const fixed = lines.map((line) => {
31061
+ if (inHeredoc) {
31062
+ if (line.trim() === heredocDelim)
31063
+ inHeredoc = false;
31064
+ return line;
31065
+ }
31066
+ const delim = heredocDelimiter(line);
31067
+ if (delim) {
31068
+ inHeredoc = true;
31069
+ heredocDelim = delim;
31070
+ }
31071
+ return line.replace(/[ \t]+$/, "");
31072
+ });
31073
+ return fixed.join(`
31074
+ `);
31070
31075
  }
31071
31076
  };
31072
31077
  });
@@ -31464,21 +31469,26 @@ var init_heredoc_indent = __esm(() => {
31464
31469
  const trimmed = line.replace(/^\s+/, "");
31465
31470
  if (trimmed.startsWith("#"))
31466
31471
  continue;
31467
- const heredocMatch = line.match(/^(\s+).*<<\s*(['"]?)(\w+)\2\s*$/);
31468
- if (heredocMatch && !line.includes("<<-")) {
31469
- const indent = heredocMatch[1];
31470
- const delim = heredocMatch[3];
31471
- if (indent.length > 0) {
31472
- issues.push({
31473
- filePath: ctx.filePath,
31474
- line: i + 1,
31475
- column: line.indexOf("<<") + 1,
31476
- ruleId: "shell/heredoc-indent",
31477
- message: `Use <<- instead of << for indented heredoc (delimiter '${delim}' must be at column 0 otherwise)`,
31478
- severity: "warning",
31479
- help: "Use <<- to strip leading tabs from heredoc content, allowing proper indentation"
31480
- });
31481
- }
31472
+ const indent = line.match(/^\s+/);
31473
+ if (!indent)
31474
+ continue;
31475
+ const masked = maskShellStrings(line);
31476
+ const op = masked.match(/(?<!<)<<(?![<-])/);
31477
+ if (!op || op.index === undefined)
31478
+ continue;
31479
+ const after = line.slice(op.index + 2);
31480
+ const delimMatch = after.match(/^\s*(['"]?)(\w+)\1\s*$/);
31481
+ if (delimMatch) {
31482
+ const delim = delimMatch[2];
31483
+ issues.push({
31484
+ filePath: ctx.filePath,
31485
+ line: i + 1,
31486
+ column: op.index + 1,
31487
+ ruleId: "shell/heredoc-indent",
31488
+ message: `Use <<- instead of << for indented heredoc (delimiter '${delim}' must be at column 0 otherwise)`,
31489
+ severity: "warning",
31490
+ help: "Use <<- to strip leading tabs from heredoc content, allowing proper indentation"
31491
+ });
31482
31492
  }
31483
31493
  }
31484
31494
  return issues;
@@ -31506,17 +31516,18 @@ var init_no_broken_redirect = __esm(() => {
31506
31516
  inHeredoc = false;
31507
31517
  continue;
31508
31518
  }
31509
- const heredocMatch = line.match(/<<-?\s*['"]?(\w+)['"]?/);
31510
- if (heredocMatch) {
31519
+ const delim = heredocDelimiter(line);
31520
+ if (delim) {
31511
31521
  inHeredoc = true;
31512
- heredocDelim = heredocMatch[1];
31522
+ heredocDelim = delim;
31513
31523
  }
31514
31524
  const trimmed = line.replace(/^\s+/, "");
31515
31525
  if (trimmed.startsWith("#"))
31516
31526
  continue;
31517
- const brokenPattern = line.match(/2>&1\s+>(?!&)\s*\S/);
31527
+ const masked = maskShellStrings(line);
31528
+ const brokenPattern = masked.match(/2>&1\s+>(?!&)\s*\S/);
31518
31529
  if (brokenPattern) {
31519
- const col = line.indexOf("2>&1");
31530
+ const col = masked.indexOf("2>&1");
31520
31531
  issues.push({
31521
31532
  filePath: ctx.filePath,
31522
31533
  line: i + 1,
@@ -31814,8 +31825,6 @@ function createSpellRule(ruleId, filterFn) {
31814
31825
  for (const { word, offset: wordOffset } of words) {
31815
31826
  if (word.length < minLen)
31816
31827
  continue;
31817
- if (word.length <= 2)
31818
- continue;
31819
31828
  if (word === word.toUpperCase() && word.length <= 5)
31820
31829
  continue;
31821
31830
  if (userWords.has(word.toLowerCase()))
@@ -32072,9 +32081,36 @@ var init_arrow_spacing = __esm(() => {
32072
32081
  return issues;
32073
32082
  },
32074
32083
  fix(content) {
32075
- let result = content.replace(/(\S)=>/g, "$1 =>");
32076
- result = result.replace(/=>(\S)/g, "=> $1");
32077
- return result;
32084
+ const lines = content.split(`
32085
+ `);
32086
+ for (let li = 0;li < lines.length; li++) {
32087
+ const line = lines[li];
32088
+ const trimmed = line.trim();
32089
+ if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*"))
32090
+ continue;
32091
+ let out = "";
32092
+ let last = 0;
32093
+ ARROW_RE.lastIndex = 0;
32094
+ let m;
32095
+ while ((m = ARROW_RE.exec(line)) !== null) {
32096
+ const idx = m.index;
32097
+ if (isInStringOrComment3(line, idx))
32098
+ continue;
32099
+ const before = line[idx - 1];
32100
+ const after = line[idx + 2];
32101
+ const needBefore = idx > 0 && before !== " " && before !== "\t";
32102
+ const needAfter = after !== undefined && after !== " " && after !== "\t";
32103
+ if (!needBefore && !needAfter)
32104
+ continue;
32105
+ out += line.slice(last, idx);
32106
+ out += `${needBefore ? " " : ""}=>${needAfter ? " " : ""}`;
32107
+ last = idx + 2;
32108
+ }
32109
+ if (last > 0)
32110
+ lines[li] = out + line.slice(last);
32111
+ }
32112
+ return lines.join(`
32113
+ `);
32078
32114
  }
32079
32115
  };
32080
32116
  });
@@ -33331,11 +33367,14 @@ var init_dot_location = __esm(() => {
33331
33367
  check(content, context) {
33332
33368
  const issues = [];
33333
33369
  const lines = content.split(/\r?\n/);
33370
+ const inTemplate = computeLineStartsInTemplate(content);
33334
33371
  for (let i = 0;i < lines.length; i++) {
33335
33372
  const line = lines[i];
33336
33373
  const trimmed = line.trim();
33337
33374
  if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*"))
33338
33375
  continue;
33376
+ if (inTemplate[i + 1])
33377
+ continue;
33339
33378
  if (trimmed.endsWith(".") && i + 1 < lines.length) {
33340
33379
  const nextTrimmed = lines[i + 1].trim();
33341
33380
  if (nextTrimmed && /^[a-zA-Z_$]/.test(nextTrimmed)) {
@@ -33355,10 +33394,11 @@ var init_dot_location = __esm(() => {
33355
33394
  fix(content) {
33356
33395
  const lines = content.split(/\r?\n/);
33357
33396
  const result = [];
33397
+ const inTemplate = computeLineStartsInTemplate(content);
33358
33398
  for (let i = 0;i < lines.length; i++) {
33359
33399
  const line = lines[i];
33360
33400
  const trimmed = line.trim();
33361
- if (trimmed.endsWith(".") && i + 1 < lines.length) {
33401
+ if (trimmed.endsWith(".") && !inTemplate[i + 1] && i + 1 < lines.length) {
33362
33402
  const nextLine = lines[i + 1];
33363
33403
  const nextTrimmed = nextLine.trim();
33364
33404
  if (nextTrimmed && /^[a-zA-Z_$]/.test(nextTrimmed)) {
@@ -37088,32 +37128,60 @@ var init_prefer_nullish_coalescing = __esm(() => {
37088
37128
  const lines = text.split(/\r?\n/);
37089
37129
  let inBlockComment = false;
37090
37130
  for (let i = 0;i < lines.length; i++) {
37091
- let line = lines[i];
37092
- const originalLine = line;
37131
+ const originalLine = lines[i];
37132
+ const chars = originalLine.split("");
37133
+ const n = chars.length;
37134
+ let k = 0;
37093
37135
  if (inBlockComment) {
37094
- const endIdx = line.indexOf("*/");
37095
- if (endIdx >= 0) {
37096
- line = line.slice(endIdx + 2);
37097
- inBlockComment = false;
37098
- } else {
37136
+ const endIdx = originalLine.indexOf("*/");
37137
+ if (endIdx < 0)
37099
37138
  continue;
37100
- }
37139
+ for (let p = 0;p <= endIdx + 1; p++)
37140
+ chars[p] = " ";
37141
+ inBlockComment = false;
37142
+ k = endIdx + 2;
37101
37143
  }
37102
- const blockStart = line.indexOf("/*");
37103
- const lineComment = line.indexOf("//");
37104
- if (blockStart >= 0 && (lineComment === -1 || blockStart < lineComment)) {
37105
- const endIdx = line.indexOf("*/", blockStart + 2);
37106
- if (endIdx >= 0) {
37107
- line = line.slice(0, blockStart) + line.slice(endIdx + 2);
37108
- } else {
37109
- inBlockComment = true;
37110
- line = line.slice(0, blockStart);
37144
+ let inStr = null;
37145
+ for (;k < n; k++) {
37146
+ const ch = chars[k];
37147
+ if (inStr) {
37148
+ if (ch === "\\") {
37149
+ chars[k] = " ";
37150
+ if (k + 1 < n) {
37151
+ chars[k + 1] = " ";
37152
+ k++;
37153
+ }
37154
+ continue;
37155
+ }
37156
+ if (ch === inStr) {
37157
+ inStr = null;
37158
+ continue;
37159
+ }
37160
+ chars[k] = " ";
37161
+ continue;
37111
37162
  }
37163
+ if (ch === "/" && chars[k + 1] === "/") {
37164
+ for (let p = k;p < n; p++)
37165
+ chars[p] = " ";
37166
+ break;
37167
+ }
37168
+ if (ch === "/" && chars[k + 1] === "*") {
37169
+ const endIdx = originalLine.indexOf("*/", k + 2);
37170
+ if (endIdx < 0) {
37171
+ for (let p = k;p < n; p++)
37172
+ chars[p] = " ";
37173
+ inBlockComment = true;
37174
+ break;
37175
+ }
37176
+ for (let p = k;p <= endIdx + 1; p++)
37177
+ chars[p] = " ";
37178
+ k = endIdx + 1;
37179
+ continue;
37180
+ }
37181
+ if (ch === '"' || ch === "'" || ch === "`")
37182
+ inStr = ch;
37112
37183
  }
37113
- if (lineComment >= 0) {
37114
- line = line.slice(0, lineComment);
37115
- }
37116
- const cleanedLine = line.replace(/(['"`])(?:(?!\1)[^\\]|\\.)*?\1/g, '""');
37184
+ const cleanedLine = chars.join("");
37117
37185
  const orPattern = /\|\|/g;
37118
37186
  let match;
37119
37187
  while ((match = orPattern.exec(cleanedLine)) !== null) {
@@ -37208,11 +37276,10 @@ var init_prefer_nullish_coalescing = __esm(() => {
37208
37276
  const looksLikeDefaultValue = /^["'`\d]/.test(rightOperand) || /^\{/.test(rightOperand) || /^[\w$]+\(/.test(rightOperand) || /^[\w$.[\]]+$/.test(rightOperand);
37209
37277
  const looksLikeNullableValue = /^[\w$]+$/.test(leftOperand) || /^[\w$.?[\]()]+$/.test(leftOperand);
37210
37278
  if (looksLikeDefaultValue && looksLikeNullableValue) {
37211
- const actualIdx = originalLine.indexOf("||", Math.max(0, idx - 5));
37212
37279
  issues.push({
37213
37280
  filePath: ctx.filePath,
37214
37281
  line: i + 1,
37215
- column: actualIdx >= 0 ? actualIdx + 1 : idx + 1,
37282
+ column: idx + 1,
37216
37283
  ruleId: "ts/prefer-nullish-coalescing",
37217
37284
  message: "Prefer nullish coalescing operator (??) over logical OR (||)",
37218
37285
  severity: "error",
@@ -38028,7 +38095,7 @@ function createIgnoreMatcher(ignoreGlobs, cwd = process21.cwd()) {
38028
38095
  return (absPath) => {
38029
38096
  const normalizedAbs = toPosixPath(absPath);
38030
38097
  const normalizedCwd = toPosixPath(cwd).replace(/\/$/, "");
38031
- const isOutsideProject = !normalizedAbs.startsWith(normalizedCwd);
38098
+ const isOutsideProject = normalizedAbs !== normalizedCwd && !normalizedAbs.startsWith(`${normalizedCwd}/`);
38032
38099
  const rel = isOutsideProject ? normalizedAbs : normalizedAbs.slice(normalizedCwd.length);
38033
38100
  for (const pattern of compiled) {
38034
38101
  if (isOutsideProject && !universalRaw.has(pattern.raw))
@@ -38310,35 +38377,30 @@ async function runFormat(globs, options) {
38310
38377
  return extSet.has(ext);
38311
38378
  });
38312
38379
  trace("filtered files", files.length);
38313
- let changed = 0;
38314
38380
  let checked = 0;
38381
+ const unformatted = [];
38315
38382
  for (const file of files) {
38316
38383
  trace("format start", relative6(process23.cwd(), file));
38317
38384
  const src = readFileSync9(file, "utf8");
38318
38385
  const fmt = formatCode(src, cfg, file);
38319
- if (options.check) {
38320
- if (fmt !== src) {
38321
- getLogger().debug(`${relative6(process23.cwd(), file)} needs formatting`);
38322
- changed++;
38323
- }
38324
- checked++;
38325
- } else if (options.write) {
38326
- if (fmt !== src) {
38386
+ const differs = fmt !== src;
38387
+ if (options.write) {
38388
+ if (differs)
38327
38389
  writeFileSync10(file, fmt, "utf8");
38328
- changed++;
38329
- }
38330
- checked++;
38331
- } else {
38332
- if (fmt !== src) {
38333
- getLogger().debug(`${relative6(process23.cwd(), file)} needs formatting`);
38334
- changed++;
38335
- }
38336
- checked++;
38337
38390
  }
38391
+ if (differs)
38392
+ unformatted.push(relative6(process23.cwd(), file));
38393
+ checked++;
38338
38394
  }
38339
- const isVerbose = options.verbose !== undefined ? options.verbose : cfg.verbose;
38340
- if (isVerbose) {
38341
- getLogger().debug(colors.gray(`Checked ${checked} files, ${changed} changed.`));
38395
+ const changed = unformatted.length;
38396
+ if (changed > 0) {
38397
+ for (const file of unformatted)
38398
+ console.log(colors.gray(` ${file}`));
38399
+ console.log(options.write ? colors.gray(`Formatted ${changed} of ${checked} file(s).`) : colors.yellow(`${changed} of ${checked} file(s) need formatting. Run with --write to fix.`));
38400
+ } else {
38401
+ const isVerbose = options.verbose !== undefined ? options.verbose : cfg.verbose;
38402
+ if (isVerbose)
38403
+ console.log(colors.gray(`Checked ${checked} files, all formatted.`));
38342
38404
  }
38343
38405
  return options.check && changed > 0 ? 1 : 0;
38344
38406
  }
@@ -38361,7 +38423,7 @@ __export(exports_linter, {
38361
38423
  applyPlugins: () => applyPlugins
38362
38424
  });
38363
38425
  import { readdirSync as readdirSync9, readFileSync as readFileSync10, statSync as statSync6, writeFileSync as writeFileSync11 } from "fs";
38364
- import { isAbsolute as isAbsolute8, join as join11, relative as relative7, resolve as resolve23 } from "path";
38426
+ import { isAbsolute as isAbsolute8, join as join11, relative as relative7, resolve as resolve23, sep } from "path";
38365
38427
  import process25 from "process";
38366
38428
  function getLogger2() {
38367
38429
  if (!_logger2)
@@ -38418,7 +38480,8 @@ async function runLintProgrammatic(globs, options, signal) {
38418
38480
  const isGlobbingOutsideProject = patterns.some((p) => {
38419
38481
  const base = p.replace(/\/?\*\*\/*\*\*$/, "");
38420
38482
  const absBase = isAbsolute8(base) ? base : resolve23(process25.cwd(), base);
38421
- return !absBase.startsWith(process25.cwd());
38483
+ const cwd = process25.cwd();
38484
+ return absBase !== cwd && !absBase.startsWith(cwd + sep);
38422
38485
  });
38423
38486
  const globIgnores = isGlobbingOutsideProject ? [...UNIVERSAL_IGNORES] : cfg.ignores;
38424
38487
  const ignoreMatcher = createIgnoreMatcher(globIgnores);
@@ -39449,7 +39512,10 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
39449
39512
  }
39450
39513
  } else {
39451
39514
  if (inString === "double" && ch === '"' || inString === "single" && ch === "'" || inString === "template" && ch === "`") {
39452
- if (k === 0 || line[k - 1] !== "\\")
39515
+ let backslashes = 0;
39516
+ for (let p = k - 1;p >= 0 && line[p] === "\\"; p--)
39517
+ backslashes++;
39518
+ if (backslashes % 2 === 0)
39453
39519
  inString = null;
39454
39520
  }
39455
39521
  }
@@ -39526,6 +39592,12 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
39526
39592
  const strippedLine = stripComments2(stripRegexLiterals(line));
39527
39593
  const conditionLine = strippedLine.replace(/'[^']*'|"[^"]*"/g, '""');
39528
39594
  const checkCond = (cond) => /[^=!<>]=(?![=>])/.test(cond);
39595
+ const condParenColumn = (keyword) => {
39596
+ const km = line.match(keyword);
39597
+ if (km && km.index !== undefined)
39598
+ return km.index + km[0].length;
39599
+ return Math.max(1, line.indexOf("(") + 1);
39600
+ };
39529
39601
  const m1 = conditionLine.match(/\b(?:if|while)\s*\(([^)]*)\)/);
39530
39602
  if (m1) {
39531
39603
  const cond = m1[1];
@@ -39534,7 +39606,7 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
39534
39606
  issues.push({
39535
39607
  filePath,
39536
39608
  line: lineNo,
39537
- column: Math.max(1, line.indexOf("(") + 1),
39609
+ column: condParenColumn(/\b(?:if|while)\s*\(/),
39538
39610
  ruleId: "no-cond-assign",
39539
39611
  message: "Unexpected assignment within a conditional expression",
39540
39612
  severity: wantNoCondAssign,
@@ -39554,7 +39626,7 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
39554
39626
  issues.push({
39555
39627
  filePath,
39556
39628
  line: lineNo,
39557
- column: Math.max(1, line.indexOf("(") + 1),
39629
+ column: condParenColumn(/\bfor\s*\(/),
39558
39630
  ruleId: "no-cond-assign",
39559
39631
  message: "Unexpected assignment within a conditional expression",
39560
39632
  severity: wantNoCondAssign,
@@ -39621,7 +39693,8 @@ async function runLint(globs, options) {
39621
39693
  const isGlobbingOutsideProject = patterns.some((p) => {
39622
39694
  const base = p.replace(/\/?\*\*\/*\*\*$/, "");
39623
39695
  const absBase = isAbsolute8(base) ? base : resolve23(process25.cwd(), base);
39624
- return !absBase.startsWith(process25.cwd());
39696
+ const cwd = process25.cwd();
39697
+ return absBase !== cwd && !absBase.startsWith(cwd + sep);
39625
39698
  });
39626
39699
  const globIgnores = isGlobbingOutsideProject ? [...UNIVERSAL_IGNORES] : cfg.ignores;
39627
39700
  const ignoreMatcher = createIgnoreMatcher(globIgnores);