@clerk/eslint-plugin 0.0.1 → 0.1.0
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/LICENSE +21 -0
- package/README.md +263 -0
- package/dist/chunk-C_NdSu1c.js +34 -0
- package/dist/next/fix-auth-protection-cli.d.mts +1 -0
- package/dist/next/fix-auth-protection-cli.d.ts +1 -0
- package/dist/next/fix-auth-protection-cli.js +101 -0
- package/dist/next/fix-auth-protection-cli.js.map +1 -0
- package/dist/next/fix-auth-protection-cli.mjs +100 -0
- package/dist/next/fix-auth-protection-cli.mjs.map +1 -0
- package/dist/next/fix-auth-protection.d.mts +59 -0
- package/dist/next/fix-auth-protection.d.mts.map +1 -0
- package/dist/next/fix-auth-protection.d.ts +59 -0
- package/dist/next/fix-auth-protection.d.ts.map +1 -0
- package/dist/next/fix-auth-protection.js +149 -0
- package/dist/next/fix-auth-protection.js.map +1 -0
- package/dist/next/fix-auth-protection.mjs +148 -0
- package/dist/next/fix-auth-protection.mjs.map +1 -0
- package/dist/next.d.mts +7 -0
- package/dist/next.d.mts.map +1 -0
- package/dist/next.d.ts +6 -0
- package/dist/next.d.ts.map +1 -0
- package/dist/next.js +833 -0
- package/dist/next.js.map +1 -0
- package/dist/next.mjs +831 -0
- package/dist/next.mjs.map +1 -0
- package/package.json +81 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ESLint } from "eslint";
|
|
2
|
+
|
|
3
|
+
//#region src/next/fix-auth-protection.d.ts
|
|
4
|
+
interface FixAuthProtectionOptions {
|
|
5
|
+
/** File, directory, or glob patterns to scan. Defaults to `['.']`. */
|
|
6
|
+
patterns?: string[];
|
|
7
|
+
/** Working directory ESLint resolves config and files against. Defaults to `process.cwd()`. */
|
|
8
|
+
cwd?: string;
|
|
9
|
+
/** Compute the changes without writing them to disk. */
|
|
10
|
+
dryRun?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Advanced/escape hatch: a pre-configured ESLint instance to lint with. When
|
|
13
|
+
* omitted, a default `new ESLint({ cwd })` is used, which discovers the
|
|
14
|
+
* consumer's flat config. Mainly useful for tests.
|
|
15
|
+
*/
|
|
16
|
+
eslint?: ESLint;
|
|
17
|
+
/**
|
|
18
|
+
* Called before scanning with the path to the ESLint config file that will be
|
|
19
|
+
* used (or `undefined` when none is found / an instance was injected).
|
|
20
|
+
*/
|
|
21
|
+
onConfigResolved?: (configFilePath: string | undefined) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Called once linting finishes and per-file fixing begins, with the number of
|
|
24
|
+
* files that have flagged resources. Useful for reporting progress, since the
|
|
25
|
+
* initial lint can be slow on large projects.
|
|
26
|
+
*/
|
|
27
|
+
onScanComplete?: (fileCount: number) => void;
|
|
28
|
+
/** Called after each file is fixed (or, in `dryRun`, would be fixed). */
|
|
29
|
+
onFileFixed?: (file: FixedFile) => void;
|
|
30
|
+
}
|
|
31
|
+
interface FixedFile {
|
|
32
|
+
filePath: string;
|
|
33
|
+
/** Number of resources that had `await auth.protect()` applied. */
|
|
34
|
+
protections: number;
|
|
35
|
+
}
|
|
36
|
+
interface UnresolvedIssue {
|
|
37
|
+
line: number;
|
|
38
|
+
column: number;
|
|
39
|
+
message: string;
|
|
40
|
+
}
|
|
41
|
+
interface UnresolvedFile {
|
|
42
|
+
filePath: string;
|
|
43
|
+
issues: UnresolvedIssue[];
|
|
44
|
+
}
|
|
45
|
+
interface FixAuthProtectionResult {
|
|
46
|
+
/** Files that were (or, in `dryRun`, would be) modified. */
|
|
47
|
+
fixed: FixedFile[];
|
|
48
|
+
/** Files with flagged resources that have no safe automatic fix. */
|
|
49
|
+
unresolved: UnresolvedFile[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Lint the given patterns with the consumer's ESLint config and apply the
|
|
53
|
+
* `require-auth-protection` rule's `await auth.protect()` suggestions to every
|
|
54
|
+
* resource it can safely fix.
|
|
55
|
+
*/
|
|
56
|
+
declare function fixAuthProtection(options?: FixAuthProtectionOptions): Promise<FixAuthProtectionResult>;
|
|
57
|
+
//#endregion
|
|
58
|
+
export { FixAuthProtectionOptions, FixAuthProtectionResult, FixedFile, UnresolvedFile, UnresolvedIssue, fixAuthProtection };
|
|
59
|
+
//# sourceMappingURL=fix-auth-protection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-auth-protection.d.ts","names":[],"sources":["../../src/next/fix-auth-protection.ts"],"mappings":";;;UAyBiB,wBAAA;EAyBA;EAvBf,QAAA;EAuB8B;EArB9B,GAAA;EAwBwB;EAtBxB,MAAA;EAuBA;AAEW;AAGb;;;EAtBE,MAAA,GAAS,MAAA;EAuBT;;;;EAlBA,gBAAA,IAAoB,cAAA;EAuBL;;;;;EAjBf,cAAA,IAAkB,SAAA;EAmBV;EAjBR,WAAA,IAAe,IAAA,EAAM,SAAS;AAAA;AAAA,UAGf,SAAA;EACf,QAAA;;EAEA,WAAW;AAAA;AAAA,UAGI,eAAA;EACf,IAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA;EACf,QAAA;EACA,MAAA,EAAQ,eAAe;AAAA;AAAA,UAGR,uBAAA;EAmGgE;EAjG/E,KAAA,EAAO,SAAA;EAiG+E;EA/FtF,UAAA,EAAY,cAAc;AAAA;;;;AA+FoF;;iBAA1F,iBAAA,CAAkB,OAAA,GAAS,wBAAA,GAAgC,OAAA,CAAQ,uBAAA"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let node_fs_promises = require("node:fs/promises");
|
|
3
|
+
let eslint = require("eslint");
|
|
4
|
+
|
|
5
|
+
//#region src/next/fix-auth-protection.ts
|
|
6
|
+
/**
|
|
7
|
+
* Programmatic auto-fixer for the `require-auth-protection` rule.
|
|
8
|
+
*
|
|
9
|
+
* The rule exposes its `await auth.protect()` insertion as an opt-in
|
|
10
|
+
* *suggestion* rather than an autofix, so `eslint --fix` deliberately leaves it
|
|
11
|
+
* alone (adding a protection check changes runtime behavior). This runner lets
|
|
12
|
+
* you apply those suggestions in bulk on demand — from a script via
|
|
13
|
+
* `fixAuthProtection()` or from the terminal via the
|
|
14
|
+
* `clerk-next-fix-auth-protection` command.
|
|
15
|
+
*
|
|
16
|
+
* It works by linting with the consumer's own ESLint config (so the
|
|
17
|
+
* protected/public folder globs are honored) and applying the rule's
|
|
18
|
+
* `addAuthProtect` suggestion to each flagged resource. Resources the rule
|
|
19
|
+
* cannot safely fix (imported/wrapped exports, unacknowledged mixed-scope
|
|
20
|
+
* layouts) are reported back as `unresolved` for manual follow-up.
|
|
21
|
+
*/
|
|
22
|
+
const RULE_NAME = "require-auth-protection";
|
|
23
|
+
const SUGGESTION_MESSAGE_ID = "addAuthProtect";
|
|
24
|
+
const UNFIXABLE_MESSAGE_IDS = new Set([
|
|
25
|
+
"exportImported",
|
|
26
|
+
"unverifiableExport",
|
|
27
|
+
"unlistedMixedScopeLayout"
|
|
28
|
+
]);
|
|
29
|
+
function isAuthProtectionRule(ruleId) {
|
|
30
|
+
return ruleId === RULE_NAME || (ruleId?.endsWith(`/${RULE_NAME}`) ?? false);
|
|
31
|
+
}
|
|
32
|
+
function collectSuggestionFixes(messages) {
|
|
33
|
+
const fixes = [];
|
|
34
|
+
for (const message of messages) {
|
|
35
|
+
if (!isAuthProtectionRule(message.ruleId)) continue;
|
|
36
|
+
const suggestion = message.suggestions?.find((s) => s.messageId === SUGGESTION_MESSAGE_ID);
|
|
37
|
+
if (suggestion?.fix) fixes.push({
|
|
38
|
+
range: [suggestion.fix.range[0], suggestion.fix.range[1]],
|
|
39
|
+
text: suggestion.fix.text
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return fixes;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Apply as many non-overlapping fixes as possible in a single pass, mirroring
|
|
46
|
+
* ESLint's own `SourceCodeFixer`: sort by position and skip any fix that starts
|
|
47
|
+
* before the previous one ended. Overlapping fixes are left for a later pass.
|
|
48
|
+
*/
|
|
49
|
+
function applyFixes(source, fixes) {
|
|
50
|
+
const sorted = [...fixes].sort((a, b) => a.range[0] - b.range[0] || a.range[1] - b.range[1]);
|
|
51
|
+
let output = "";
|
|
52
|
+
let lastPos = 0;
|
|
53
|
+
let applied = 0;
|
|
54
|
+
for (const fix of sorted) {
|
|
55
|
+
const [start, end] = fix.range;
|
|
56
|
+
if (start < lastPos) continue;
|
|
57
|
+
output += source.slice(lastPos, start) + fix.text;
|
|
58
|
+
lastPos = end;
|
|
59
|
+
applied++;
|
|
60
|
+
}
|
|
61
|
+
output += source.slice(lastPos);
|
|
62
|
+
return {
|
|
63
|
+
output,
|
|
64
|
+
applied
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async function applyFileFixes(eslint$1, filePath, source) {
|
|
68
|
+
const MAX_FIX_PASSES = 10;
|
|
69
|
+
let current = source;
|
|
70
|
+
let total = 0;
|
|
71
|
+
let passes = 0;
|
|
72
|
+
for (let i = 0; i < 11; i += 1) {
|
|
73
|
+
const [result] = await eslint$1.lintText(current, { filePath });
|
|
74
|
+
if (!result) break;
|
|
75
|
+
const fixes = collectSuggestionFixes(result.messages);
|
|
76
|
+
if (fixes.length === 0) break;
|
|
77
|
+
if (passes >= MAX_FIX_PASSES) throw new Error(`Auth-protect fixes for ${filePath} did not converge after ${MAX_FIX_PASSES} passes. This is unexpected; please report it at https://github.com/clerk/javascript/issues.`);
|
|
78
|
+
const { output, applied } = applyFixes(current, fixes);
|
|
79
|
+
if (applied === 0) break;
|
|
80
|
+
current = output;
|
|
81
|
+
total += applied;
|
|
82
|
+
passes += 1;
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
output: current,
|
|
86
|
+
applied: total
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Lint the given patterns with the consumer's ESLint config and apply the
|
|
91
|
+
* `require-auth-protection` rule's `await auth.protect()` suggestions to every
|
|
92
|
+
* resource it can safely fix.
|
|
93
|
+
*/
|
|
94
|
+
async function fixAuthProtection(options = {}) {
|
|
95
|
+
const cwd = options.cwd ?? process.cwd();
|
|
96
|
+
const patterns = options.patterns && options.patterns.length > 0 ? options.patterns : ["."];
|
|
97
|
+
const dryRun = options.dryRun ?? false;
|
|
98
|
+
const eslint$2 = options.eslint ?? new eslint.ESLint({
|
|
99
|
+
cwd,
|
|
100
|
+
ruleFilter: ({ ruleId }) => isAuthProtectionRule(ruleId)
|
|
101
|
+
});
|
|
102
|
+
if (options.onConfigResolved) {
|
|
103
|
+
let configFile;
|
|
104
|
+
try {
|
|
105
|
+
configFile = await eslint$2.findConfigFile();
|
|
106
|
+
} catch {
|
|
107
|
+
configFile = void 0;
|
|
108
|
+
}
|
|
109
|
+
options.onConfigResolved(configFile);
|
|
110
|
+
}
|
|
111
|
+
const results = await eslint$2.lintFiles(patterns);
|
|
112
|
+
const fixed = [];
|
|
113
|
+
const unresolved = [];
|
|
114
|
+
const flaggedResults = results.filter((result) => result.messages.some((message) => isAuthProtectionRule(message.ruleId)));
|
|
115
|
+
options.onScanComplete?.(flaggedResults.length);
|
|
116
|
+
for (const result of flaggedResults) {
|
|
117
|
+
const ruleMessages = result.messages.filter((message) => isAuthProtectionRule(message.ruleId));
|
|
118
|
+
if (ruleMessages.some((message) => message.suggestions?.some((s) => s.messageId === SUGGESTION_MESSAGE_ID) ?? false)) {
|
|
119
|
+
const source = await (0, node_fs_promises.readFile)(result.filePath, "utf8");
|
|
120
|
+
const { output, applied } = await applyFileFixes(eslint$2, result.filePath, source);
|
|
121
|
+
if (applied > 0) {
|
|
122
|
+
if (!dryRun) await (0, node_fs_promises.writeFile)(result.filePath, output, "utf8");
|
|
123
|
+
const fixedFile = {
|
|
124
|
+
filePath: result.filePath,
|
|
125
|
+
protections: applied
|
|
126
|
+
};
|
|
127
|
+
fixed.push(fixedFile);
|
|
128
|
+
options.onFileFixed?.(fixedFile);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const issues = ruleMessages.filter((message) => UNFIXABLE_MESSAGE_IDS.has(message.messageId ?? "")).map((message) => ({
|
|
132
|
+
line: message.line,
|
|
133
|
+
column: message.column,
|
|
134
|
+
message: message.message
|
|
135
|
+
}));
|
|
136
|
+
if (issues.length > 0) unresolved.push({
|
|
137
|
+
filePath: result.filePath,
|
|
138
|
+
issues
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
fixed,
|
|
143
|
+
unresolved
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
exports.fixAuthProtection = fixAuthProtection;
|
|
149
|
+
//# sourceMappingURL=fix-auth-protection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-auth-protection.js","names":["eslint","ESLint"],"sources":["../../src/next/fix-auth-protection.ts"],"sourcesContent":["/**\n * Programmatic auto-fixer for the `require-auth-protection` rule.\n *\n * The rule exposes its `await auth.protect()` insertion as an opt-in\n * *suggestion* rather than an autofix, so `eslint --fix` deliberately leaves it\n * alone (adding a protection check changes runtime behavior). This runner lets\n * you apply those suggestions in bulk on demand — from a script via\n * `fixAuthProtection()` or from the terminal via the\n * `clerk-next-fix-auth-protection` command.\n *\n * It works by linting with the consumer's own ESLint config (so the\n * protected/public folder globs are honored) and applying the rule's\n * `addAuthProtect` suggestion to each flagged resource. Resources the rule\n * cannot safely fix (imported/wrapped exports, unacknowledged mixed-scope\n * layouts) are reported back as `unresolved` for manual follow-up.\n */\n\nimport { readFile, writeFile } from 'node:fs/promises';\n\nimport { ESLint, type Linter } from 'eslint';\n\nconst RULE_NAME = 'require-auth-protection';\nconst SUGGESTION_MESSAGE_ID = 'addAuthProtect';\nconst UNFIXABLE_MESSAGE_IDS = new Set(['exportImported', 'unverifiableExport', 'unlistedMixedScopeLayout']);\n\nexport interface FixAuthProtectionOptions {\n /** File, directory, or glob patterns to scan. Defaults to `['.']`. */\n patterns?: string[];\n /** Working directory ESLint resolves config and files against. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Compute the changes without writing them to disk. */\n dryRun?: boolean;\n /**\n * Advanced/escape hatch: a pre-configured ESLint instance to lint with. When\n * omitted, a default `new ESLint({ cwd })` is used, which discovers the\n * consumer's flat config. Mainly useful for tests.\n */\n eslint?: ESLint;\n /**\n * Called before scanning with the path to the ESLint config file that will be\n * used (or `undefined` when none is found / an instance was injected).\n */\n onConfigResolved?: (configFilePath: string | undefined) => void;\n /**\n * Called once linting finishes and per-file fixing begins, with the number of\n * files that have flagged resources. Useful for reporting progress, since the\n * initial lint can be slow on large projects.\n */\n onScanComplete?: (fileCount: number) => void;\n /** Called after each file is fixed (or, in `dryRun`, would be fixed). */\n onFileFixed?: (file: FixedFile) => void;\n}\n\nexport interface FixedFile {\n filePath: string;\n /** Number of resources that had `await auth.protect()` applied. */\n protections: number;\n}\n\nexport interface UnresolvedIssue {\n line: number;\n column: number;\n message: string;\n}\n\nexport interface UnresolvedFile {\n filePath: string;\n issues: UnresolvedIssue[];\n}\n\nexport interface FixAuthProtectionResult {\n /** Files that were (or, in `dryRun`, would be) modified. */\n fixed: FixedFile[];\n /** Files with flagged resources that have no safe automatic fix. */\n unresolved: UnresolvedFile[];\n}\n\ntype Fix = { range: [number, number]; text: string };\n\nfunction isAuthProtectionRule(ruleId: string | null): boolean {\n // The plugin can be registered under any namespace (e.g. `@clerk/next/...`),\n // so match on the rule name rather than a fixed, fully-qualified id.\n return ruleId === RULE_NAME || (ruleId?.endsWith(`/${RULE_NAME}`) ?? false);\n}\n\nfunction collectSuggestionFixes(messages: Linter.LintMessage[]): Fix[] {\n const fixes: Fix[] = [];\n for (const message of messages) {\n if (!isAuthProtectionRule(message.ruleId)) {\n continue;\n }\n const suggestion = message.suggestions?.find(s => s.messageId === SUGGESTION_MESSAGE_ID);\n if (suggestion?.fix) {\n fixes.push({ range: [suggestion.fix.range[0], suggestion.fix.range[1]], text: suggestion.fix.text });\n }\n }\n return fixes;\n}\n\n/**\n * Apply as many non-overlapping fixes as possible in a single pass, mirroring\n * ESLint's own `SourceCodeFixer`: sort by position and skip any fix that starts\n * before the previous one ended. Overlapping fixes are left for a later pass.\n */\nfunction applyFixes(source: string, fixes: Fix[]): { output: string; applied: number } {\n const sorted = [...fixes].sort((a, b) => a.range[0] - b.range[0] || a.range[1] - b.range[1]);\n let output = '';\n let lastPos = 0;\n let applied = 0;\n for (const fix of sorted) {\n const [start, end] = fix.range;\n if (start < lastPos) {\n continue;\n }\n output += source.slice(lastPos, start) + fix.text;\n lastPos = end;\n applied++;\n }\n output += source.slice(lastPos);\n return { output, applied };\n}\n\nasync function applyFileFixes(\n eslint: ESLint,\n filePath: string,\n source: string,\n): Promise<{ output: string; applied: number }> {\n // Applying a file's suggestions should converge in at most two passes: the first pass\n // fixes one resource and adds the shared top-of-file `auth` import, after which\n // every remaining resource is independent and applied in the second pass.\n // We allow up to 10 passes to allow for unaccounted for edge cases, or future\n // changes to the fixer, but throw an error if it fails to converge.\n const MAX_FIX_PASSES = 10;\n\n let current = source;\n let total = 0;\n let passes = 0;\n // Run one extra time so we can throw an error if the fixes don't converge.\n for (let i = 0; i < MAX_FIX_PASSES + 1; i += 1) {\n const [result] = await eslint.lintText(current, { filePath });\n if (!result) {\n break;\n }\n const fixes = collectSuggestionFixes(result.messages);\n if (fixes.length === 0) {\n break;\n }\n if (passes >= MAX_FIX_PASSES) {\n throw new Error(\n `Auth-protect fixes for ${filePath} did not converge after ${MAX_FIX_PASSES} passes. ` +\n 'This is unexpected; please report it at https://github.com/clerk/javascript/issues.',\n );\n }\n const { output, applied } = applyFixes(current, fixes);\n if (applied === 0) {\n break;\n }\n current = output;\n total += applied;\n passes += 1;\n }\n return { output: current, applied: total };\n}\n\n/**\n * Lint the given patterns with the consumer's ESLint config and apply the\n * `require-auth-protection` rule's `await auth.protect()` suggestions to every\n * resource it can safely fix.\n */\nexport async function fixAuthProtection(options: FixAuthProtectionOptions = {}): Promise<FixAuthProtectionResult> {\n const cwd = options.cwd ?? process.cwd();\n const patterns = options.patterns && options.patterns.length > 0 ? options.patterns : ['.'];\n const dryRun = options.dryRun ?? false;\n\n // Only run our rule. The consumer's config (and its protected/public globs)\n // still applies, but skipping every other rule avoids the cost of linting the\n // whole project with the full ruleset on each pass.\n const eslint = options.eslint ?? new ESLint({ cwd, ruleFilter: ({ ruleId }) => isAuthProtectionRule(ruleId) });\n\n if (options.onConfigResolved) {\n let configFile: string | undefined;\n try {\n configFile = await eslint.findConfigFile();\n } catch {\n configFile = undefined;\n }\n options.onConfigResolved(configFile);\n }\n\n const results = await eslint.lintFiles(patterns);\n\n const fixed: FixedFile[] = [];\n const unresolved: UnresolvedFile[] = [];\n\n const flaggedResults = results.filter(result =>\n result.messages.some(message => isAuthProtectionRule(message.ruleId)),\n );\n options.onScanComplete?.(flaggedResults.length);\n\n for (const result of flaggedResults) {\n const ruleMessages = result.messages.filter(message => isAuthProtectionRule(message.ruleId));\n\n const hasFixable = ruleMessages.some(\n message => message.suggestions?.some(s => s.messageId === SUGGESTION_MESSAGE_ID) ?? false,\n );\n if (hasFixable) {\n const source = await readFile(result.filePath, 'utf8');\n const { output, applied } = await applyFileFixes(eslint, result.filePath, source);\n if (applied > 0) {\n if (!dryRun) {\n await writeFile(result.filePath, output, 'utf8');\n }\n const fixedFile = { filePath: result.filePath, protections: applied };\n fixed.push(fixedFile);\n options.onFileFixed?.(fixedFile);\n }\n }\n\n // Messages without a suggestion (imported/wrapped exports, mixed-scope\n // layouts) need a human; surface them so they aren't silently skipped.\n const issues = ruleMessages\n .filter(message => UNFIXABLE_MESSAGE_IDS.has(message.messageId ?? ''))\n .map(message => ({ line: message.line, column: message.column, message: message.message }));\n if (issues.length > 0) {\n unresolved.push({ filePath: result.filePath, issues });\n }\n }\n\n return { fixed, unresolved };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { ESLint } from "eslint";
|
|
3
|
+
|
|
4
|
+
//#region src/next/fix-auth-protection.ts
|
|
5
|
+
/**
|
|
6
|
+
* Programmatic auto-fixer for the `require-auth-protection` rule.
|
|
7
|
+
*
|
|
8
|
+
* The rule exposes its `await auth.protect()` insertion as an opt-in
|
|
9
|
+
* *suggestion* rather than an autofix, so `eslint --fix` deliberately leaves it
|
|
10
|
+
* alone (adding a protection check changes runtime behavior). This runner lets
|
|
11
|
+
* you apply those suggestions in bulk on demand — from a script via
|
|
12
|
+
* `fixAuthProtection()` or from the terminal via the
|
|
13
|
+
* `clerk-next-fix-auth-protection` command.
|
|
14
|
+
*
|
|
15
|
+
* It works by linting with the consumer's own ESLint config (so the
|
|
16
|
+
* protected/public folder globs are honored) and applying the rule's
|
|
17
|
+
* `addAuthProtect` suggestion to each flagged resource. Resources the rule
|
|
18
|
+
* cannot safely fix (imported/wrapped exports, unacknowledged mixed-scope
|
|
19
|
+
* layouts) are reported back as `unresolved` for manual follow-up.
|
|
20
|
+
*/
|
|
21
|
+
const RULE_NAME = "require-auth-protection";
|
|
22
|
+
const SUGGESTION_MESSAGE_ID = "addAuthProtect";
|
|
23
|
+
const UNFIXABLE_MESSAGE_IDS = new Set([
|
|
24
|
+
"exportImported",
|
|
25
|
+
"unverifiableExport",
|
|
26
|
+
"unlistedMixedScopeLayout"
|
|
27
|
+
]);
|
|
28
|
+
function isAuthProtectionRule(ruleId) {
|
|
29
|
+
return ruleId === RULE_NAME || (ruleId?.endsWith(`/${RULE_NAME}`) ?? false);
|
|
30
|
+
}
|
|
31
|
+
function collectSuggestionFixes(messages) {
|
|
32
|
+
const fixes = [];
|
|
33
|
+
for (const message of messages) {
|
|
34
|
+
if (!isAuthProtectionRule(message.ruleId)) continue;
|
|
35
|
+
const suggestion = message.suggestions?.find((s) => s.messageId === SUGGESTION_MESSAGE_ID);
|
|
36
|
+
if (suggestion?.fix) fixes.push({
|
|
37
|
+
range: [suggestion.fix.range[0], suggestion.fix.range[1]],
|
|
38
|
+
text: suggestion.fix.text
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return fixes;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Apply as many non-overlapping fixes as possible in a single pass, mirroring
|
|
45
|
+
* ESLint's own `SourceCodeFixer`: sort by position and skip any fix that starts
|
|
46
|
+
* before the previous one ended. Overlapping fixes are left for a later pass.
|
|
47
|
+
*/
|
|
48
|
+
function applyFixes(source, fixes) {
|
|
49
|
+
const sorted = [...fixes].sort((a, b) => a.range[0] - b.range[0] || a.range[1] - b.range[1]);
|
|
50
|
+
let output = "";
|
|
51
|
+
let lastPos = 0;
|
|
52
|
+
let applied = 0;
|
|
53
|
+
for (const fix of sorted) {
|
|
54
|
+
const [start, end] = fix.range;
|
|
55
|
+
if (start < lastPos) continue;
|
|
56
|
+
output += source.slice(lastPos, start) + fix.text;
|
|
57
|
+
lastPos = end;
|
|
58
|
+
applied++;
|
|
59
|
+
}
|
|
60
|
+
output += source.slice(lastPos);
|
|
61
|
+
return {
|
|
62
|
+
output,
|
|
63
|
+
applied
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async function applyFileFixes(eslint, filePath, source) {
|
|
67
|
+
const MAX_FIX_PASSES = 10;
|
|
68
|
+
let current = source;
|
|
69
|
+
let total = 0;
|
|
70
|
+
let passes = 0;
|
|
71
|
+
for (let i = 0; i < 11; i += 1) {
|
|
72
|
+
const [result] = await eslint.lintText(current, { filePath });
|
|
73
|
+
if (!result) break;
|
|
74
|
+
const fixes = collectSuggestionFixes(result.messages);
|
|
75
|
+
if (fixes.length === 0) break;
|
|
76
|
+
if (passes >= MAX_FIX_PASSES) throw new Error(`Auth-protect fixes for ${filePath} did not converge after ${MAX_FIX_PASSES} passes. This is unexpected; please report it at https://github.com/clerk/javascript/issues.`);
|
|
77
|
+
const { output, applied } = applyFixes(current, fixes);
|
|
78
|
+
if (applied === 0) break;
|
|
79
|
+
current = output;
|
|
80
|
+
total += applied;
|
|
81
|
+
passes += 1;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
output: current,
|
|
85
|
+
applied: total
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Lint the given patterns with the consumer's ESLint config and apply the
|
|
90
|
+
* `require-auth-protection` rule's `await auth.protect()` suggestions to every
|
|
91
|
+
* resource it can safely fix.
|
|
92
|
+
*/
|
|
93
|
+
async function fixAuthProtection(options = {}) {
|
|
94
|
+
const cwd = options.cwd ?? process.cwd();
|
|
95
|
+
const patterns = options.patterns && options.patterns.length > 0 ? options.patterns : ["."];
|
|
96
|
+
const dryRun = options.dryRun ?? false;
|
|
97
|
+
const eslint = options.eslint ?? new ESLint({
|
|
98
|
+
cwd,
|
|
99
|
+
ruleFilter: ({ ruleId }) => isAuthProtectionRule(ruleId)
|
|
100
|
+
});
|
|
101
|
+
if (options.onConfigResolved) {
|
|
102
|
+
let configFile;
|
|
103
|
+
try {
|
|
104
|
+
configFile = await eslint.findConfigFile();
|
|
105
|
+
} catch {
|
|
106
|
+
configFile = void 0;
|
|
107
|
+
}
|
|
108
|
+
options.onConfigResolved(configFile);
|
|
109
|
+
}
|
|
110
|
+
const results = await eslint.lintFiles(patterns);
|
|
111
|
+
const fixed = [];
|
|
112
|
+
const unresolved = [];
|
|
113
|
+
const flaggedResults = results.filter((result) => result.messages.some((message) => isAuthProtectionRule(message.ruleId)));
|
|
114
|
+
options.onScanComplete?.(flaggedResults.length);
|
|
115
|
+
for (const result of flaggedResults) {
|
|
116
|
+
const ruleMessages = result.messages.filter((message) => isAuthProtectionRule(message.ruleId));
|
|
117
|
+
if (ruleMessages.some((message) => message.suggestions?.some((s) => s.messageId === SUGGESTION_MESSAGE_ID) ?? false)) {
|
|
118
|
+
const source = await readFile(result.filePath, "utf8");
|
|
119
|
+
const { output, applied } = await applyFileFixes(eslint, result.filePath, source);
|
|
120
|
+
if (applied > 0) {
|
|
121
|
+
if (!dryRun) await writeFile(result.filePath, output, "utf8");
|
|
122
|
+
const fixedFile = {
|
|
123
|
+
filePath: result.filePath,
|
|
124
|
+
protections: applied
|
|
125
|
+
};
|
|
126
|
+
fixed.push(fixedFile);
|
|
127
|
+
options.onFileFixed?.(fixedFile);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const issues = ruleMessages.filter((message) => UNFIXABLE_MESSAGE_IDS.has(message.messageId ?? "")).map((message) => ({
|
|
131
|
+
line: message.line,
|
|
132
|
+
column: message.column,
|
|
133
|
+
message: message.message
|
|
134
|
+
}));
|
|
135
|
+
if (issues.length > 0) unresolved.push({
|
|
136
|
+
filePath: result.filePath,
|
|
137
|
+
issues
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
fixed,
|
|
142
|
+
unresolved
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
export { fixAuthProtection };
|
|
148
|
+
//# sourceMappingURL=fix-auth-protection.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-auth-protection.mjs","names":[],"sources":["../../src/next/fix-auth-protection.ts"],"sourcesContent":["/**\n * Programmatic auto-fixer for the `require-auth-protection` rule.\n *\n * The rule exposes its `await auth.protect()` insertion as an opt-in\n * *suggestion* rather than an autofix, so `eslint --fix` deliberately leaves it\n * alone (adding a protection check changes runtime behavior). This runner lets\n * you apply those suggestions in bulk on demand — from a script via\n * `fixAuthProtection()` or from the terminal via the\n * `clerk-next-fix-auth-protection` command.\n *\n * It works by linting with the consumer's own ESLint config (so the\n * protected/public folder globs are honored) and applying the rule's\n * `addAuthProtect` suggestion to each flagged resource. Resources the rule\n * cannot safely fix (imported/wrapped exports, unacknowledged mixed-scope\n * layouts) are reported back as `unresolved` for manual follow-up.\n */\n\nimport { readFile, writeFile } from 'node:fs/promises';\n\nimport { ESLint, type Linter } from 'eslint';\n\nconst RULE_NAME = 'require-auth-protection';\nconst SUGGESTION_MESSAGE_ID = 'addAuthProtect';\nconst UNFIXABLE_MESSAGE_IDS = new Set(['exportImported', 'unverifiableExport', 'unlistedMixedScopeLayout']);\n\nexport interface FixAuthProtectionOptions {\n /** File, directory, or glob patterns to scan. Defaults to `['.']`. */\n patterns?: string[];\n /** Working directory ESLint resolves config and files against. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Compute the changes without writing them to disk. */\n dryRun?: boolean;\n /**\n * Advanced/escape hatch: a pre-configured ESLint instance to lint with. When\n * omitted, a default `new ESLint({ cwd })` is used, which discovers the\n * consumer's flat config. Mainly useful for tests.\n */\n eslint?: ESLint;\n /**\n * Called before scanning with the path to the ESLint config file that will be\n * used (or `undefined` when none is found / an instance was injected).\n */\n onConfigResolved?: (configFilePath: string | undefined) => void;\n /**\n * Called once linting finishes and per-file fixing begins, with the number of\n * files that have flagged resources. Useful for reporting progress, since the\n * initial lint can be slow on large projects.\n */\n onScanComplete?: (fileCount: number) => void;\n /** Called after each file is fixed (or, in `dryRun`, would be fixed). */\n onFileFixed?: (file: FixedFile) => void;\n}\n\nexport interface FixedFile {\n filePath: string;\n /** Number of resources that had `await auth.protect()` applied. */\n protections: number;\n}\n\nexport interface UnresolvedIssue {\n line: number;\n column: number;\n message: string;\n}\n\nexport interface UnresolvedFile {\n filePath: string;\n issues: UnresolvedIssue[];\n}\n\nexport interface FixAuthProtectionResult {\n /** Files that were (or, in `dryRun`, would be) modified. */\n fixed: FixedFile[];\n /** Files with flagged resources that have no safe automatic fix. */\n unresolved: UnresolvedFile[];\n}\n\ntype Fix = { range: [number, number]; text: string };\n\nfunction isAuthProtectionRule(ruleId: string | null): boolean {\n // The plugin can be registered under any namespace (e.g. `@clerk/next/...`),\n // so match on the rule name rather than a fixed, fully-qualified id.\n return ruleId === RULE_NAME || (ruleId?.endsWith(`/${RULE_NAME}`) ?? false);\n}\n\nfunction collectSuggestionFixes(messages: Linter.LintMessage[]): Fix[] {\n const fixes: Fix[] = [];\n for (const message of messages) {\n if (!isAuthProtectionRule(message.ruleId)) {\n continue;\n }\n const suggestion = message.suggestions?.find(s => s.messageId === SUGGESTION_MESSAGE_ID);\n if (suggestion?.fix) {\n fixes.push({ range: [suggestion.fix.range[0], suggestion.fix.range[1]], text: suggestion.fix.text });\n }\n }\n return fixes;\n}\n\n/**\n * Apply as many non-overlapping fixes as possible in a single pass, mirroring\n * ESLint's own `SourceCodeFixer`: sort by position and skip any fix that starts\n * before the previous one ended. Overlapping fixes are left for a later pass.\n */\nfunction applyFixes(source: string, fixes: Fix[]): { output: string; applied: number } {\n const sorted = [...fixes].sort((a, b) => a.range[0] - b.range[0] || a.range[1] - b.range[1]);\n let output = '';\n let lastPos = 0;\n let applied = 0;\n for (const fix of sorted) {\n const [start, end] = fix.range;\n if (start < lastPos) {\n continue;\n }\n output += source.slice(lastPos, start) + fix.text;\n lastPos = end;\n applied++;\n }\n output += source.slice(lastPos);\n return { output, applied };\n}\n\nasync function applyFileFixes(\n eslint: ESLint,\n filePath: string,\n source: string,\n): Promise<{ output: string; applied: number }> {\n // Applying a file's suggestions should converge in at most two passes: the first pass\n // fixes one resource and adds the shared top-of-file `auth` import, after which\n // every remaining resource is independent and applied in the second pass.\n // We allow up to 10 passes to allow for unaccounted for edge cases, or future\n // changes to the fixer, but throw an error if it fails to converge.\n const MAX_FIX_PASSES = 10;\n\n let current = source;\n let total = 0;\n let passes = 0;\n // Run one extra time so we can throw an error if the fixes don't converge.\n for (let i = 0; i < MAX_FIX_PASSES + 1; i += 1) {\n const [result] = await eslint.lintText(current, { filePath });\n if (!result) {\n break;\n }\n const fixes = collectSuggestionFixes(result.messages);\n if (fixes.length === 0) {\n break;\n }\n if (passes >= MAX_FIX_PASSES) {\n throw new Error(\n `Auth-protect fixes for ${filePath} did not converge after ${MAX_FIX_PASSES} passes. ` +\n 'This is unexpected; please report it at https://github.com/clerk/javascript/issues.',\n );\n }\n const { output, applied } = applyFixes(current, fixes);\n if (applied === 0) {\n break;\n }\n current = output;\n total += applied;\n passes += 1;\n }\n return { output: current, applied: total };\n}\n\n/**\n * Lint the given patterns with the consumer's ESLint config and apply the\n * `require-auth-protection` rule's `await auth.protect()` suggestions to every\n * resource it can safely fix.\n */\nexport async function fixAuthProtection(options: FixAuthProtectionOptions = {}): Promise<FixAuthProtectionResult> {\n const cwd = options.cwd ?? process.cwd();\n const patterns = options.patterns && options.patterns.length > 0 ? options.patterns : ['.'];\n const dryRun = options.dryRun ?? false;\n\n // Only run our rule. The consumer's config (and its protected/public globs)\n // still applies, but skipping every other rule avoids the cost of linting the\n // whole project with the full ruleset on each pass.\n const eslint = options.eslint ?? new ESLint({ cwd, ruleFilter: ({ ruleId }) => isAuthProtectionRule(ruleId) });\n\n if (options.onConfigResolved) {\n let configFile: string | undefined;\n try {\n configFile = await eslint.findConfigFile();\n } catch {\n configFile = undefined;\n }\n options.onConfigResolved(configFile);\n }\n\n const results = await eslint.lintFiles(patterns);\n\n const fixed: FixedFile[] = [];\n const unresolved: UnresolvedFile[] = [];\n\n const flaggedResults = results.filter(result =>\n result.messages.some(message => isAuthProtectionRule(message.ruleId)),\n );\n options.onScanComplete?.(flaggedResults.length);\n\n for (const result of flaggedResults) {\n const ruleMessages = result.messages.filter(message => isAuthProtectionRule(message.ruleId));\n\n const hasFixable = ruleMessages.some(\n message => message.suggestions?.some(s => s.messageId === SUGGESTION_MESSAGE_ID) ?? false,\n );\n if (hasFixable) {\n const source = await readFile(result.filePath, 'utf8');\n const { output, applied } = await applyFileFixes(eslint, result.filePath, source);\n if (applied > 0) {\n if (!dryRun) {\n await writeFile(result.filePath, output, 'utf8');\n }\n const fixedFile = { filePath: result.filePath, protections: applied };\n fixed.push(fixedFile);\n options.onFileFixed?.(fixedFile);\n }\n }\n\n // Messages without a suggestion (imported/wrapped exports, mixed-scope\n // layouts) need a human; surface them so they aren't silently skipped.\n const issues = ruleMessages\n .filter(message => UNFIXABLE_MESSAGE_IDS.has(message.messageId ?? ''))\n .map(message => ({ line: message.line, column: message.column, message: message.message }));\n if (issues.length > 0) {\n unresolved.push({ filePath: result.filePath, issues });\n }\n }\n\n return { fixed, unresolved };\n}\n"],"mappings":"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"}
|
package/dist/next.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.d.mts","names":[],"sources":["../src/next/index.ts"],"mappings":";;;cAIM,MAAA,EAAQ,MAAA,CAAO,MAQpB"}
|
package/dist/next.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.d.ts","names":[],"sources":["../src/next/index.ts"],"mappings":";;;cAIM,MAAA,EAAQ,MAAA,CAAO,MAQpB;AAAA"}
|