prodlint 0.8.0 → 0.8.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.
package/README.md CHANGED
@@ -13,7 +13,7 @@ npx prodlint
13
13
  ```
14
14
 
15
15
  ```
16
- prodlint v0.8.0
16
+ prodlint v0.8.1
17
17
  Scanned 148 files · 3 critical · 5 warnings
18
18
 
19
19
  src/app/api/checkout/route.ts
package/dist/cli.js CHANGED
@@ -2260,7 +2260,7 @@ var codebaseConsistencyRule = {
2260
2260
  // src/rules/dead-exports.ts
2261
2261
  function isEntryPoint(relativePath) {
2262
2262
  const name = relativePath.split("/").pop() ?? "";
2263
- return /^(page|layout|loading|error|not-found|route|middleware|instrumentation)\.(tsx?|jsx?)$/.test(name) || /^index\.(tsx?|jsx?)$/.test(name) || name === "global-error.tsx" || name === "global-error.jsx";
2263
+ return /^(page|layout|loading|error|not-found|route|middleware|instrumentation|opengraph-image|twitter-image|icon|apple-icon|sitemap|robots|manifest)\.(tsx?|jsx?)$/.test(name) || /^index\.(tsx?|jsx?)$/.test(name) || name === "global-error.tsx" || name === "global-error.jsx";
2264
2264
  }
2265
2265
  var THRESHOLD = 5;
2266
2266
  var deadExportsRule = {
@@ -2283,8 +2283,31 @@ var deadExportsRule = {
2283
2283
  const importedFiles = /* @__PURE__ */ new Set();
2284
2284
  for (const file of sourceFiles) {
2285
2285
  if (isEntryPoint(file.relativePath)) continue;
2286
+ let inTemplateLiteral = false;
2286
2287
  for (let i = 0; i < file.lines.length; i++) {
2287
2288
  const line = file.lines[i];
2289
+ let backtickCount = 0;
2290
+ for (let j = 0; j < line.length; j++) {
2291
+ if (line[j] === "\\") {
2292
+ j++;
2293
+ continue;
2294
+ }
2295
+ if (line[j] === "`") backtickCount++;
2296
+ }
2297
+ if (backtickCount % 2 === 1) inTemplateLiteral = !inTemplateLiteral;
2298
+ if (inTemplateLiteral && backtickCount % 2 === 0) continue;
2299
+ const exportIdx = line.indexOf("export");
2300
+ if (exportIdx >= 0) {
2301
+ let inStr = false;
2302
+ for (let j = 0; j < exportIdx; j++) {
2303
+ if (line[j] === "\\") {
2304
+ j++;
2305
+ continue;
2306
+ }
2307
+ if (line[j] === "'" || line[j] === '"' || line[j] === "`") inStr = !inStr;
2308
+ }
2309
+ if (inStr) continue;
2310
+ }
2288
2311
  let match;
2289
2312
  const namedRe = /export\s+(?:async\s+)?(?:function|const|let|class|enum)\s+(\w+)/g;
2290
2313
  while ((match = namedRe.exec(line)) !== null) {
@@ -3853,6 +3876,8 @@ var hydrationMismatchRule = {
3853
3876
  if (isClientComponent(file.content)) return [];
3854
3877
  if (!/(?:^|\/)(?:src\/)?app\//.test(file.relativePath)) return [];
3855
3878
  if (/route\.[jt]sx?$/.test(file.relativePath)) return [];
3879
+ if (/(?:^|\/)(?:src\/)?app\/(?:lib|utils|helpers|server|actions)\//.test(file.relativePath)) return [];
3880
+ if (/\.[jt]s$/.test(file.relativePath) && !/<[A-Z]/.test(file.content)) return [];
3856
3881
  const findings = [];
3857
3882
  let useEffectRanges = [];
3858
3883
  if (file.ast) {
package/dist/index.js CHANGED
@@ -2255,7 +2255,7 @@ var codebaseConsistencyRule = {
2255
2255
  // src/rules/dead-exports.ts
2256
2256
  function isEntryPoint(relativePath) {
2257
2257
  const name = relativePath.split("/").pop() ?? "";
2258
- return /^(page|layout|loading|error|not-found|route|middleware|instrumentation)\.(tsx?|jsx?)$/.test(name) || /^index\.(tsx?|jsx?)$/.test(name) || name === "global-error.tsx" || name === "global-error.jsx";
2258
+ return /^(page|layout|loading|error|not-found|route|middleware|instrumentation|opengraph-image|twitter-image|icon|apple-icon|sitemap|robots|manifest)\.(tsx?|jsx?)$/.test(name) || /^index\.(tsx?|jsx?)$/.test(name) || name === "global-error.tsx" || name === "global-error.jsx";
2259
2259
  }
2260
2260
  var THRESHOLD = 5;
2261
2261
  var deadExportsRule = {
@@ -2278,8 +2278,31 @@ var deadExportsRule = {
2278
2278
  const importedFiles = /* @__PURE__ */ new Set();
2279
2279
  for (const file of sourceFiles) {
2280
2280
  if (isEntryPoint(file.relativePath)) continue;
2281
+ let inTemplateLiteral = false;
2281
2282
  for (let i = 0; i < file.lines.length; i++) {
2282
2283
  const line = file.lines[i];
2284
+ let backtickCount = 0;
2285
+ for (let j = 0; j < line.length; j++) {
2286
+ if (line[j] === "\\") {
2287
+ j++;
2288
+ continue;
2289
+ }
2290
+ if (line[j] === "`") backtickCount++;
2291
+ }
2292
+ if (backtickCount % 2 === 1) inTemplateLiteral = !inTemplateLiteral;
2293
+ if (inTemplateLiteral && backtickCount % 2 === 0) continue;
2294
+ const exportIdx = line.indexOf("export");
2295
+ if (exportIdx >= 0) {
2296
+ let inStr = false;
2297
+ for (let j = 0; j < exportIdx; j++) {
2298
+ if (line[j] === "\\") {
2299
+ j++;
2300
+ continue;
2301
+ }
2302
+ if (line[j] === "'" || line[j] === '"' || line[j] === "`") inStr = !inStr;
2303
+ }
2304
+ if (inStr) continue;
2305
+ }
2283
2306
  let match;
2284
2307
  const namedRe = /export\s+(?:async\s+)?(?:function|const|let|class|enum)\s+(\w+)/g;
2285
2308
  while ((match = namedRe.exec(line)) !== null) {
@@ -3848,6 +3871,8 @@ var hydrationMismatchRule = {
3848
3871
  if (isClientComponent(file.content)) return [];
3849
3872
  if (!/(?:^|\/)(?:src\/)?app\//.test(file.relativePath)) return [];
3850
3873
  if (/route\.[jt]sx?$/.test(file.relativePath)) return [];
3874
+ if (/(?:^|\/)(?:src\/)?app\/(?:lib|utils|helpers|server|actions)\//.test(file.relativePath)) return [];
3875
+ if (/\.[jt]s$/.test(file.relativePath) && !/<[A-Z]/.test(file.content)) return [];
3851
3876
  const findings = [];
3852
3877
  let useEffectRanges = [];
3853
3878
  if (file.ast) {
package/dist/mcp.js CHANGED
@@ -2264,7 +2264,7 @@ var codebaseConsistencyRule = {
2264
2264
  // src/rules/dead-exports.ts
2265
2265
  function isEntryPoint(relativePath) {
2266
2266
  const name = relativePath.split("/").pop() ?? "";
2267
- return /^(page|layout|loading|error|not-found|route|middleware|instrumentation)\.(tsx?|jsx?)$/.test(name) || /^index\.(tsx?|jsx?)$/.test(name) || name === "global-error.tsx" || name === "global-error.jsx";
2267
+ return /^(page|layout|loading|error|not-found|route|middleware|instrumentation|opengraph-image|twitter-image|icon|apple-icon|sitemap|robots|manifest)\.(tsx?|jsx?)$/.test(name) || /^index\.(tsx?|jsx?)$/.test(name) || name === "global-error.tsx" || name === "global-error.jsx";
2268
2268
  }
2269
2269
  var THRESHOLD = 5;
2270
2270
  var deadExportsRule = {
@@ -2287,8 +2287,31 @@ var deadExportsRule = {
2287
2287
  const importedFiles = /* @__PURE__ */ new Set();
2288
2288
  for (const file of sourceFiles) {
2289
2289
  if (isEntryPoint(file.relativePath)) continue;
2290
+ let inTemplateLiteral = false;
2290
2291
  for (let i = 0; i < file.lines.length; i++) {
2291
2292
  const line = file.lines[i];
2293
+ let backtickCount = 0;
2294
+ for (let j = 0; j < line.length; j++) {
2295
+ if (line[j] === "\\") {
2296
+ j++;
2297
+ continue;
2298
+ }
2299
+ if (line[j] === "`") backtickCount++;
2300
+ }
2301
+ if (backtickCount % 2 === 1) inTemplateLiteral = !inTemplateLiteral;
2302
+ if (inTemplateLiteral && backtickCount % 2 === 0) continue;
2303
+ const exportIdx = line.indexOf("export");
2304
+ if (exportIdx >= 0) {
2305
+ let inStr = false;
2306
+ for (let j = 0; j < exportIdx; j++) {
2307
+ if (line[j] === "\\") {
2308
+ j++;
2309
+ continue;
2310
+ }
2311
+ if (line[j] === "'" || line[j] === '"' || line[j] === "`") inStr = !inStr;
2312
+ }
2313
+ if (inStr) continue;
2314
+ }
2292
2315
  let match;
2293
2316
  const namedRe = /export\s+(?:async\s+)?(?:function|const|let|class|enum)\s+(\w+)/g;
2294
2317
  while ((match = namedRe.exec(line)) !== null) {
@@ -3857,6 +3880,8 @@ var hydrationMismatchRule = {
3857
3880
  if (isClientComponent(file.content)) return [];
3858
3881
  if (!/(?:^|\/)(?:src\/)?app\//.test(file.relativePath)) return [];
3859
3882
  if (/route\.[jt]sx?$/.test(file.relativePath)) return [];
3883
+ if (/(?:^|\/)(?:src\/)?app\/(?:lib|utils|helpers|server|actions)\//.test(file.relativePath)) return [];
3884
+ if (/\.[jt]s$/.test(file.relativePath) && !/<[A-Z]/.test(file.content)) return [];
3860
3885
  const findings = [];
3861
3886
  let useEffectRanges = [];
3862
3887
  if (file.ast) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prodlint",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Production readiness for vibe-coded apps — know your AI code is ready to ship",
5
5
  "license": "MIT",
6
6
  "author": "prodlint contributors",