eslint-plugin-nextjs 0.1.1 → 1.0.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/README.md +13 -13
- package/dist/index.d.ts +4 -39
- package/dist/index.js +403 -274
- package/dist/index.js.map +1 -1
- package/package.json +15 -10
- package/dist/index.cjs +0 -1494
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -43
- package/dist/rules/google-font-display.cjs +0 -119
- package/dist/rules/google-font-display.cjs.map +0 -1
- package/dist/rules/google-font-display.d.cts +0 -8
- package/dist/rules/google-font-display.d.ts +0 -8
- package/dist/rules/google-font-display.js +0 -92
- package/dist/rules/google-font-display.js.map +0 -1
- package/dist/rules/google-font-preconnect.cjs +0 -109
- package/dist/rules/google-font-preconnect.cjs.map +0 -1
- package/dist/rules/google-font-preconnect.d.cts +0 -5
- package/dist/rules/google-font-preconnect.d.ts +0 -5
- package/dist/rules/google-font-preconnect.js +0 -82
- package/dist/rules/google-font-preconnect.js.map +0 -1
- package/dist/rules/inline-script-id.cjs +0 -94
- package/dist/rules/inline-script-id.cjs.map +0 -1
- package/dist/rules/inline-script-id.d.cts +0 -5
- package/dist/rules/inline-script-id.d.ts +0 -5
- package/dist/rules/inline-script-id.js +0 -67
- package/dist/rules/inline-script-id.js.map +0 -1
- package/dist/rules/next-script-for-ga.cjs +0 -129
- package/dist/rules/next-script-for-ga.cjs.map +0 -1
- package/dist/rules/next-script-for-ga.d.cts +0 -5
- package/dist/rules/next-script-for-ga.d.ts +0 -5
- package/dist/rules/next-script-for-ga.js +0 -102
- package/dist/rules/next-script-for-ga.js.map +0 -1
- package/dist/rules/no-assign-module-variable.cjs +0 -64
- package/dist/rules/no-assign-module-variable.cjs.map +0 -1
- package/dist/rules/no-assign-module-variable.d.cts +0 -5
- package/dist/rules/no-assign-module-variable.d.ts +0 -5
- package/dist/rules/no-assign-module-variable.js +0 -37
- package/dist/rules/no-assign-module-variable.js.map +0 -1
- package/dist/rules/no-async-client-component.cjs +0 -99
- package/dist/rules/no-async-client-component.cjs.map +0 -1
- package/dist/rules/no-async-client-component.d.cts +0 -5
- package/dist/rules/no-async-client-component.d.ts +0 -5
- package/dist/rules/no-async-client-component.js +0 -72
- package/dist/rules/no-async-client-component.js.map +0 -1
- package/dist/rules/no-before-interactive-script-outside-document.cjs +0 -94
- package/dist/rules/no-before-interactive-script-outside-document.cjs.map +0 -1
- package/dist/rules/no-before-interactive-script-outside-document.d.cts +0 -5
- package/dist/rules/no-before-interactive-script-outside-document.d.ts +0 -5
- package/dist/rules/no-before-interactive-script-outside-document.js +0 -59
- package/dist/rules/no-before-interactive-script-outside-document.js.map +0 -1
- package/dist/rules/no-css-tags.cjs +0 -70
- package/dist/rules/no-css-tags.cjs.map +0 -1
- package/dist/rules/no-css-tags.d.cts +0 -5
- package/dist/rules/no-css-tags.d.ts +0 -5
- package/dist/rules/no-css-tags.js +0 -43
- package/dist/rules/no-css-tags.js.map +0 -1
- package/dist/rules/no-document-import-in-page.cjs +0 -74
- package/dist/rules/no-document-import-in-page.cjs.map +0 -1
- package/dist/rules/no-document-import-in-page.d.cts +0 -5
- package/dist/rules/no-document-import-in-page.d.ts +0 -5
- package/dist/rules/no-document-import-in-page.js +0 -39
- package/dist/rules/no-document-import-in-page.js.map +0 -1
- package/dist/rules/no-duplicate-head.cjs +0 -87
- package/dist/rules/no-duplicate-head.cjs.map +0 -1
- package/dist/rules/no-duplicate-head.d.cts +0 -5
- package/dist/rules/no-duplicate-head.d.ts +0 -5
- package/dist/rules/no-duplicate-head.js +0 -60
- package/dist/rules/no-duplicate-head.js.map +0 -1
- package/dist/rules/no-head-element.cjs +0 -76
- package/dist/rules/no-head-element.cjs.map +0 -1
- package/dist/rules/no-head-element.d.cts +0 -5
- package/dist/rules/no-head-element.d.ts +0 -5
- package/dist/rules/no-head-element.js +0 -41
- package/dist/rules/no-head-element.js.map +0 -1
- package/dist/rules/no-head-import-in-document.cjs +0 -76
- package/dist/rules/no-head-import-in-document.cjs.map +0 -1
- package/dist/rules/no-head-import-in-document.d.cts +0 -5
- package/dist/rules/no-head-import-in-document.d.ts +0 -5
- package/dist/rules/no-head-import-in-document.js +0 -41
- package/dist/rules/no-head-import-in-document.js.map +0 -1
- package/dist/rules/no-html-link-for-pages.cjs +0 -302
- package/dist/rules/no-html-link-for-pages.cjs.map +0 -1
- package/dist/rules/no-html-link-for-pages.d.cts +0 -5
- package/dist/rules/no-html-link-for-pages.d.ts +0 -5
- package/dist/rules/no-html-link-for-pages.js +0 -267
- package/dist/rules/no-html-link-for-pages.js.map +0 -1
- package/dist/rules/no-img-element.cjs +0 -83
- package/dist/rules/no-img-element.cjs.map +0 -1
- package/dist/rules/no-img-element.d.cts +0 -5
- package/dist/rules/no-img-element.d.ts +0 -5
- package/dist/rules/no-img-element.js +0 -48
- package/dist/rules/no-img-element.js.map +0 -1
- package/dist/rules/no-page-custom-font.cjs +0 -184
- package/dist/rules/no-page-custom-font.cjs.map +0 -1
- package/dist/rules/no-page-custom-font.d.cts +0 -5
- package/dist/rules/no-page-custom-font.d.ts +0 -5
- package/dist/rules/no-page-custom-font.js +0 -159
- package/dist/rules/no-page-custom-font.js.map +0 -1
- package/dist/rules/no-script-component-in-head.cjs +0 -74
- package/dist/rules/no-script-component-in-head.cjs.map +0 -1
- package/dist/rules/no-script-component-in-head.d.cts +0 -5
- package/dist/rules/no-script-component-in-head.d.ts +0 -5
- package/dist/rules/no-script-component-in-head.js +0 -47
- package/dist/rules/no-script-component-in-head.js.map +0 -1
- package/dist/rules/no-styled-jsx-in-document.cjs +0 -78
- package/dist/rules/no-styled-jsx-in-document.cjs.map +0 -1
- package/dist/rules/no-styled-jsx-in-document.d.cts +0 -5
- package/dist/rules/no-styled-jsx-in-document.d.ts +0 -5
- package/dist/rules/no-styled-jsx-in-document.js +0 -43
- package/dist/rules/no-styled-jsx-in-document.js.map +0 -1
- package/dist/rules/no-sync-scripts.cjs +0 -64
- package/dist/rules/no-sync-scripts.cjs.map +0 -1
- package/dist/rules/no-sync-scripts.d.cts +0 -5
- package/dist/rules/no-sync-scripts.d.ts +0 -5
- package/dist/rules/no-sync-scripts.js +0 -37
- package/dist/rules/no-sync-scripts.js.map +0 -1
- package/dist/rules/no-title-in-document-head.cjs +0 -78
- package/dist/rules/no-title-in-document-head.cjs.map +0 -1
- package/dist/rules/no-title-in-document-head.d.cts +0 -5
- package/dist/rules/no-title-in-document-head.d.ts +0 -5
- package/dist/rules/no-title-in-document-head.js +0 -51
- package/dist/rules/no-title-in-document-head.js.map +0 -1
- package/dist/rules/no-typos.cjs +0 -133
- package/dist/rules/no-typos.cjs.map +0 -1
- package/dist/rules/no-typos.d.cts +0 -5
- package/dist/rules/no-typos.d.ts +0 -5
- package/dist/rules/no-typos.js +0 -98
- package/dist/rules/no-typos.js.map +0 -1
- package/dist/rules/no-unwanted-polyfillio.cjs +0 -164
- package/dist/rules/no-unwanted-polyfillio.cjs.map +0 -1
- package/dist/rules/no-unwanted-polyfillio.d.cts +0 -5
- package/dist/rules/no-unwanted-polyfillio.d.ts +0 -5
- package/dist/rules/no-unwanted-polyfillio.js +0 -137
- package/dist/rules/no-unwanted-polyfillio.js.map +0 -1
- package/dist/utils/define-rule.cjs +0 -31
- package/dist/utils/define-rule.cjs.map +0 -1
- package/dist/utils/define-rule.d.cts +0 -5
- package/dist/utils/define-rule.d.ts +0 -5
- package/dist/utils/define-rule.js +0 -6
- package/dist/utils/define-rule.js.map +0 -1
- package/dist/utils/get-root-dirs.cjs +0 -60
- package/dist/utils/get-root-dirs.cjs.map +0 -1
- package/dist/utils/get-root-dirs.d.cts +0 -8
- package/dist/utils/get-root-dirs.d.ts +0 -8
- package/dist/utils/get-root-dirs.js +0 -25
- package/dist/utils/get-root-dirs.js.map +0 -1
- package/dist/utils/node-attributes.cjs +0 -67
- package/dist/utils/node-attributes.cjs.map +0 -1
- package/dist/utils/node-attributes.d.cts +0 -15
- package/dist/utils/node-attributes.d.ts +0 -15
- package/dist/utils/node-attributes.js +0 -46
- package/dist/utils/node-attributes.js.map +0 -1
- package/dist/utils/url.cjs +0 -167
- package/dist/utils/url.cjs.map +0 -1
- package/dist/utils/url.d.cts +0 -35
- package/dist/utils/url.d.ts +0 -35
- package/dist/utils/url.js +0 -128
- package/dist/utils/url.js.map +0 -1
package/dist/index.cjs
DELETED
@@ -1,1494 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
var __create = Object.create;
|
3
|
-
var __defProp = Object.defineProperty;
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
8
|
-
var __export = (target, all) => {
|
9
|
-
for (var name2 in all)
|
10
|
-
__defProp(target, name2, { get: all[name2], enumerable: true });
|
11
|
-
};
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
14
|
-
for (let key of __getOwnPropNames(from))
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
17
|
-
}
|
18
|
-
return to;
|
19
|
-
};
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
26
|
-
mod
|
27
|
-
));
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
29
|
-
|
30
|
-
// src/index.ts
|
31
|
-
var index_exports = {};
|
32
|
-
__export(index_exports, {
|
33
|
-
default: () => index_default,
|
34
|
-
rules: () => rules
|
35
|
-
});
|
36
|
-
module.exports = __toCommonJS(index_exports);
|
37
|
-
|
38
|
-
// src/utils/define-rule.ts
|
39
|
-
var defineRule = (rule) => rule;
|
40
|
-
|
41
|
-
// src/utils/node-attributes.ts
|
42
|
-
var NodeAttributes = class {
|
43
|
-
attributes;
|
44
|
-
constructor(ASTnode) {
|
45
|
-
this.attributes = {};
|
46
|
-
ASTnode.attributes.forEach((attribute) => {
|
47
|
-
if (!attribute.type || attribute.type !== "JSXAttribute") {
|
48
|
-
return;
|
49
|
-
}
|
50
|
-
if (attribute.value) {
|
51
|
-
const value = typeof attribute.value.value === "string" ? attribute.value.value : typeof attribute.value.expression?.value !== "undefined" ? attribute.value.expression.value : attribute.value.expression?.properties;
|
52
|
-
this.attributes[attribute.name.name] = {
|
53
|
-
hasValue: true,
|
54
|
-
value
|
55
|
-
};
|
56
|
-
} else {
|
57
|
-
this.attributes[attribute.name.name] = {
|
58
|
-
hasValue: false
|
59
|
-
};
|
60
|
-
}
|
61
|
-
});
|
62
|
-
}
|
63
|
-
has(attrName) {
|
64
|
-
return Boolean(this.attributes[attrName]);
|
65
|
-
}
|
66
|
-
hasAny() {
|
67
|
-
return Boolean(Object.keys(this.attributes).length);
|
68
|
-
}
|
69
|
-
hasValue(attrName) {
|
70
|
-
return Boolean(this.attributes[attrName]?.hasValue);
|
71
|
-
}
|
72
|
-
value(attrName) {
|
73
|
-
const attr = this.attributes[attrName];
|
74
|
-
if (!attr) {
|
75
|
-
return true;
|
76
|
-
}
|
77
|
-
if ("hasValue" in attr && attr.hasValue) {
|
78
|
-
return attr.value;
|
79
|
-
}
|
80
|
-
return void 0;
|
81
|
-
}
|
82
|
-
};
|
83
|
-
|
84
|
-
// src/rules/google-font-display.ts
|
85
|
-
var url = "https://nextjs.org/docs/messages/google-font-display";
|
86
|
-
var googleFontDisplay = defineRule({
|
87
|
-
create: (context) => ({
|
88
|
-
JSXOpeningElement: (node) => {
|
89
|
-
let message2;
|
90
|
-
if (node.name.name !== "link") {
|
91
|
-
return;
|
92
|
-
}
|
93
|
-
const attributes = new NodeAttributes(node);
|
94
|
-
if (!attributes.has("href") || !attributes.hasValue("href")) {
|
95
|
-
return;
|
96
|
-
}
|
97
|
-
const hrefValue = attributes.value("href");
|
98
|
-
const isGoogleFont = typeof hrefValue === "string" && hrefValue.startsWith("https://fonts.googleapis.com/css");
|
99
|
-
if (isGoogleFont) {
|
100
|
-
const params = new URLSearchParams(hrefValue.split("?", 2)[1]);
|
101
|
-
const displayValue = params.get("display");
|
102
|
-
if (!params.has("display")) {
|
103
|
-
message2 = "A font-display parameter is missing (adding `&display=optional` is recommended).";
|
104
|
-
} else if (displayValue === "auto" || displayValue === "block" || displayValue === "fallback") {
|
105
|
-
message2 = `${displayValue[0]?.toUpperCase() + displayValue.slice(1)} is not recommended.`;
|
106
|
-
}
|
107
|
-
}
|
108
|
-
if (message2) {
|
109
|
-
context.report({
|
110
|
-
message: `${message2} See: ${url}`,
|
111
|
-
node
|
112
|
-
});
|
113
|
-
}
|
114
|
-
}
|
115
|
-
}),
|
116
|
-
meta: {
|
117
|
-
docs: {
|
118
|
-
description: "Enforce font-display behavior with Google Fonts.",
|
119
|
-
recommended: true,
|
120
|
-
url
|
121
|
-
},
|
122
|
-
schema: [],
|
123
|
-
type: "problem"
|
124
|
-
}
|
125
|
-
});
|
126
|
-
|
127
|
-
// src/rules/google-font-preconnect.ts
|
128
|
-
var url2 = "https://nextjs.org/docs/messages/google-font-preconnect";
|
129
|
-
var googleFontPreconnect = defineRule({
|
130
|
-
create: (context) => ({
|
131
|
-
JSXOpeningElement: (node) => {
|
132
|
-
if (node.name.name !== "link") {
|
133
|
-
return;
|
134
|
-
}
|
135
|
-
const attributes = new NodeAttributes(node);
|
136
|
-
if (!attributes.has("href") || !attributes.hasValue("href")) {
|
137
|
-
return;
|
138
|
-
}
|
139
|
-
const hrefValue = attributes.value("href");
|
140
|
-
const preconnectMissing = !attributes.has("rel") || !attributes.hasValue("rel") || attributes.value("rel") !== "preconnect";
|
141
|
-
if (typeof hrefValue === "string" && hrefValue.startsWith("https://fonts.gstatic.com") && preconnectMissing) {
|
142
|
-
context.report({
|
143
|
-
message: `\`rel="preconnect"\` is missing from Google Font. See: ${url2}`,
|
144
|
-
node
|
145
|
-
});
|
146
|
-
}
|
147
|
-
}
|
148
|
-
}),
|
149
|
-
meta: {
|
150
|
-
docs: {
|
151
|
-
description: "Ensure `preconnect` is used with Google Fonts.",
|
152
|
-
recommended: true,
|
153
|
-
url: url2
|
154
|
-
},
|
155
|
-
schema: [],
|
156
|
-
type: "problem"
|
157
|
-
}
|
158
|
-
});
|
159
|
-
|
160
|
-
// src/rules/inline-script-id.ts
|
161
|
-
var url3 = "https://nextjs.org/docs/messages/inline-script-id";
|
162
|
-
var inlineScriptId = defineRule({
|
163
|
-
create: (context) => {
|
164
|
-
let nextScriptImportName = null;
|
165
|
-
return {
|
166
|
-
ImportDeclaration: (node) => {
|
167
|
-
if (node.source.value === "next/script") {
|
168
|
-
nextScriptImportName = node.specifiers[0].local.name;
|
169
|
-
}
|
170
|
-
},
|
171
|
-
JSXElement: (node) => {
|
172
|
-
if (nextScriptImportName === null) {
|
173
|
-
return;
|
174
|
-
}
|
175
|
-
if (node.openingElement?.name && node.openingElement.name.name !== nextScriptImportName) {
|
176
|
-
return;
|
177
|
-
}
|
178
|
-
const attributeNames = /* @__PURE__ */ new Set();
|
179
|
-
let hasNonCheckableSpreadAttribute = false;
|
180
|
-
node.openingElement.attributes.forEach((attribute) => {
|
181
|
-
if (hasNonCheckableSpreadAttribute) {
|
182
|
-
return;
|
183
|
-
}
|
184
|
-
if (attribute.type === "JSXAttribute") {
|
185
|
-
attributeNames.add(attribute.name.name);
|
186
|
-
} else if (attribute.type === "JSXSpreadAttribute") {
|
187
|
-
if (attribute.argument?.properties) {
|
188
|
-
attribute.argument.properties.forEach((property) => {
|
189
|
-
attributeNames.add(property.key.name);
|
190
|
-
});
|
191
|
-
} else {
|
192
|
-
hasNonCheckableSpreadAttribute = true;
|
193
|
-
}
|
194
|
-
}
|
195
|
-
});
|
196
|
-
if (hasNonCheckableSpreadAttribute) {
|
197
|
-
return;
|
198
|
-
}
|
199
|
-
if (node.children.length > 0 || attributeNames.has("dangerouslySetInnerHTML")) {
|
200
|
-
if (!attributeNames.has("id")) {
|
201
|
-
context.report({
|
202
|
-
message: `\`next/script\` components with inline content must specify an \`id\` attribute. See: ${url3}`,
|
203
|
-
node
|
204
|
-
});
|
205
|
-
}
|
206
|
-
}
|
207
|
-
}
|
208
|
-
};
|
209
|
-
},
|
210
|
-
meta: {
|
211
|
-
docs: {
|
212
|
-
description: "Enforce `id` attribute on `next/script` components with inline content.",
|
213
|
-
recommended: true,
|
214
|
-
url: url3
|
215
|
-
},
|
216
|
-
schema: [],
|
217
|
-
type: "problem"
|
218
|
-
}
|
219
|
-
});
|
220
|
-
|
221
|
-
// src/rules/next-script-for-ga.ts
|
222
|
-
var SUPPORTED_SRCS = [
|
223
|
-
"www.google-analytics.com/analytics.js",
|
224
|
-
"www.googletagmanager.com/gtag/js"
|
225
|
-
];
|
226
|
-
var SUPPORTED_HTML_CONTENT_URLS = [
|
227
|
-
"www.google-analytics.com/analytics.js",
|
228
|
-
"www.googletagmanager.com/gtm.js"
|
229
|
-
];
|
230
|
-
var description = "Prefer `next/script` component when using the inline script for Google Analytics.";
|
231
|
-
var url4 = "https://nextjs.org/docs/messages/next-script-for-ga";
|
232
|
-
var ERROR_MSG = `${description} See: ${url4}`;
|
233
|
-
var containsStr = (str, strList) => {
|
234
|
-
return strList.some((s) => str.includes(s));
|
235
|
-
};
|
236
|
-
var nextScriptForGa = defineRule({
|
237
|
-
create: (context) => ({
|
238
|
-
JSXOpeningElement: (node) => {
|
239
|
-
if (node.name.name !== "script") {
|
240
|
-
return;
|
241
|
-
}
|
242
|
-
if (node.attributes.length === 0) {
|
243
|
-
return;
|
244
|
-
}
|
245
|
-
const attributes = new NodeAttributes(node);
|
246
|
-
if (typeof attributes.value("src") === "string" && containsStr(attributes.value("src"), SUPPORTED_SRCS)) {
|
247
|
-
return context.report({
|
248
|
-
message: ERROR_MSG,
|
249
|
-
node
|
250
|
-
});
|
251
|
-
}
|
252
|
-
if (attributes.value("dangerouslySetInnerHTML") && attributes.value("dangerouslySetInnerHTML").length > 0) {
|
253
|
-
const htmlContent = attributes.value("dangerouslySetInnerHTML")[0].value.quasis?.[0].value.raw;
|
254
|
-
if (htmlContent && containsStr(htmlContent, SUPPORTED_HTML_CONTENT_URLS)) {
|
255
|
-
context.report({
|
256
|
-
message: ERROR_MSG,
|
257
|
-
node
|
258
|
-
});
|
259
|
-
}
|
260
|
-
}
|
261
|
-
}
|
262
|
-
}),
|
263
|
-
meta: {
|
264
|
-
docs: {
|
265
|
-
description,
|
266
|
-
recommended: true,
|
267
|
-
url: url4
|
268
|
-
},
|
269
|
-
schema: [],
|
270
|
-
type: "problem"
|
271
|
-
}
|
272
|
-
});
|
273
|
-
|
274
|
-
// src/rules/no-assign-module-variable.ts
|
275
|
-
var url5 = "https://nextjs.org/docs/messages/no-assign-module-variable";
|
276
|
-
var noAssignModuleVariable = defineRule({
|
277
|
-
create: (context) => ({
|
278
|
-
VariableDeclaration: (node) => {
|
279
|
-
const moduleVariableFound = node.declarations.some((declaration) => {
|
280
|
-
if ("name" in declaration.id) {
|
281
|
-
return declaration.id.name === "module";
|
282
|
-
}
|
283
|
-
return false;
|
284
|
-
});
|
285
|
-
if (!moduleVariableFound) {
|
286
|
-
return;
|
287
|
-
}
|
288
|
-
context.report({
|
289
|
-
message: `Do not assign to the variable \`module\`. See: ${url5}`,
|
290
|
-
node
|
291
|
-
});
|
292
|
-
}
|
293
|
-
}),
|
294
|
-
meta: {
|
295
|
-
docs: {
|
296
|
-
description: "Prevent assignment to the `module` variable.",
|
297
|
-
recommended: true,
|
298
|
-
url: url5
|
299
|
-
},
|
300
|
-
schema: [],
|
301
|
-
type: "problem"
|
302
|
-
}
|
303
|
-
});
|
304
|
-
|
305
|
-
// src/rules/no-async-client-component.ts
|
306
|
-
var url6 = "https://nextjs.org/docs/messages/no-async-client-component";
|
307
|
-
var description2 = "Prevent client components from being async functions.";
|
308
|
-
var message = `${description2} See: ${url6}`;
|
309
|
-
var isCapitalized = (str) => /[A-Z]/.test(str[0] ?? "");
|
310
|
-
var noAsyncClientComponent = defineRule({
|
311
|
-
create: (context) => ({
|
312
|
-
Program: (node) => {
|
313
|
-
let isClientComponent = false;
|
314
|
-
for (const block of node.body) {
|
315
|
-
if (block.type === "ExpressionStatement" && block.expression.type === "Literal" && block.expression.value === "use client") {
|
316
|
-
isClientComponent = true;
|
317
|
-
}
|
318
|
-
if (block.type === "ExportDefaultDeclaration" && isClientComponent) {
|
319
|
-
if (block.declaration?.type === "FunctionDeclaration" && block.declaration.async && isCapitalized(block.declaration.id.name)) {
|
320
|
-
context.report({
|
321
|
-
message,
|
322
|
-
node: block
|
323
|
-
});
|
324
|
-
}
|
325
|
-
if (block.declaration.type === "Identifier" && isCapitalized(block.declaration.name)) {
|
326
|
-
const targetName = block.declaration.name;
|
327
|
-
const functionDeclaration = node.body.find((localBlock) => {
|
328
|
-
if (localBlock.type === "FunctionDeclaration" && localBlock.id.name === targetName) {
|
329
|
-
return true;
|
330
|
-
}
|
331
|
-
if (localBlock.type === "VariableDeclaration" && localBlock.declarations.find(
|
332
|
-
(declaration) => declaration.id?.type === "Identifier" && declaration.id.name === targetName
|
333
|
-
)) {
|
334
|
-
return true;
|
335
|
-
}
|
336
|
-
return false;
|
337
|
-
});
|
338
|
-
if (functionDeclaration?.type === "FunctionDeclaration" && functionDeclaration.async) {
|
339
|
-
context.report({
|
340
|
-
message,
|
341
|
-
node: functionDeclaration
|
342
|
-
});
|
343
|
-
}
|
344
|
-
if (functionDeclaration?.type === "VariableDeclaration") {
|
345
|
-
const varDeclarator = functionDeclaration.declarations.find(
|
346
|
-
(declaration) => declaration.id?.type === "Identifier" && declaration.id.name === targetName
|
347
|
-
);
|
348
|
-
if (varDeclarator?.init?.type === "ArrowFunctionExpression" && varDeclarator.init.async) {
|
349
|
-
context.report({
|
350
|
-
message,
|
351
|
-
node: functionDeclaration
|
352
|
-
});
|
353
|
-
}
|
354
|
-
}
|
355
|
-
}
|
356
|
-
}
|
357
|
-
}
|
358
|
-
}
|
359
|
-
}),
|
360
|
-
meta: {
|
361
|
-
docs: {
|
362
|
-
description: description2,
|
363
|
-
recommended: true,
|
364
|
-
url: url6
|
365
|
-
},
|
366
|
-
schema: [],
|
367
|
-
type: "problem"
|
368
|
-
}
|
369
|
-
});
|
370
|
-
|
371
|
-
// src/rules/no-before-interactive-script-outside-document.ts
|
372
|
-
var path = __toESM(require("path"), 1);
|
373
|
-
var url7 = "https://nextjs.org/docs/messages/no-before-interactive-script-outside-document";
|
374
|
-
var convertToCorrectSeparator = (str) => str.replaceAll(/[/\\]/g, path.sep);
|
375
|
-
var noBeforeInteractiveScriptOutsideDocument = defineRule({
|
376
|
-
create: (context) => {
|
377
|
-
let scriptImportName = null;
|
378
|
-
return {
|
379
|
-
'ImportDeclaration[source.value="next/script"] > ImportDefaultSpecifier'(node) {
|
380
|
-
scriptImportName = node.local.name;
|
381
|
-
},
|
382
|
-
JSXOpeningElement: (node) => {
|
383
|
-
const pathname = convertToCorrectSeparator(context.filename);
|
384
|
-
const isInAppDir = pathname.includes(`${path.sep}app${path.sep}`);
|
385
|
-
if (isInAppDir) {
|
386
|
-
return;
|
387
|
-
}
|
388
|
-
if (!scriptImportName) {
|
389
|
-
return;
|
390
|
-
}
|
391
|
-
if (node.name && node.name.name !== scriptImportName) {
|
392
|
-
return;
|
393
|
-
}
|
394
|
-
const strategy = node.attributes.find(
|
395
|
-
(child) => child.name && child.name.name === "strategy"
|
396
|
-
);
|
397
|
-
if (!strategy?.value || strategy.value.value !== "beforeInteractive") {
|
398
|
-
return;
|
399
|
-
}
|
400
|
-
const document = context.filename.split("pages", 2)[1];
|
401
|
-
if (document && path.parse(document).name.startsWith("_document")) {
|
402
|
-
return;
|
403
|
-
}
|
404
|
-
context.report({
|
405
|
-
message: `\`next/script\`'s \`beforeInteractive\` strategy should not be used outside of \`pages/_document.js\`. See: ${url7}`,
|
406
|
-
node
|
407
|
-
});
|
408
|
-
}
|
409
|
-
};
|
410
|
-
},
|
411
|
-
meta: {
|
412
|
-
docs: {
|
413
|
-
description: "Prevent usage of `next/script`'s `beforeInteractive` strategy outside of `pages/_document.js`.",
|
414
|
-
recommended: true,
|
415
|
-
url: url7
|
416
|
-
},
|
417
|
-
schema: [],
|
418
|
-
type: "problem"
|
419
|
-
}
|
420
|
-
});
|
421
|
-
|
422
|
-
// src/rules/no-css-tags.ts
|
423
|
-
var url8 = "https://nextjs.org/docs/messages/no-css-tags";
|
424
|
-
var noCssTags = defineRule({
|
425
|
-
create: (context) => ({
|
426
|
-
JSXOpeningElement: (node) => {
|
427
|
-
if (node.name.name !== "link") {
|
428
|
-
return;
|
429
|
-
}
|
430
|
-
if (node.attributes.length === 0) {
|
431
|
-
return;
|
432
|
-
}
|
433
|
-
const attributes = node.attributes.filter(
|
434
|
-
(attr) => attr.type === "JSXAttribute"
|
435
|
-
);
|
436
|
-
if (attributes.find(
|
437
|
-
(attr) => attr.name.name === "rel" && attr.value.value === "stylesheet"
|
438
|
-
) && attributes.find(
|
439
|
-
(attr) => attr.name.name === "href" && attr.value.type === "Literal" && !/^https?/.test(attr.value.value)
|
440
|
-
)) {
|
441
|
-
context.report({
|
442
|
-
message: `Do not include stylesheets manually. See: ${url8}`,
|
443
|
-
node
|
444
|
-
});
|
445
|
-
}
|
446
|
-
}
|
447
|
-
}),
|
448
|
-
meta: {
|
449
|
-
docs: {
|
450
|
-
description: "Prevent manual stylesheet tags.",
|
451
|
-
recommended: true,
|
452
|
-
url: url8
|
453
|
-
},
|
454
|
-
schema: [],
|
455
|
-
type: "problem"
|
456
|
-
}
|
457
|
-
});
|
458
|
-
|
459
|
-
// src/rules/no-document-import-in-page.ts
|
460
|
-
var path2 = __toESM(require("path"), 1);
|
461
|
-
var url9 = "https://nextjs.org/docs/messages/no-document-import-in-page";
|
462
|
-
var noDocumentImportInPage = defineRule({
|
463
|
-
create: (context) => ({
|
464
|
-
ImportDeclaration: (node) => {
|
465
|
-
if (node.source.value !== "next/document") {
|
466
|
-
return;
|
467
|
-
}
|
468
|
-
const paths = context.filename.split("pages");
|
469
|
-
const page = paths[paths.length - 1];
|
470
|
-
if (!page || page.startsWith(`${path2.sep}_document`) || page.startsWith(`${path2.posix.sep}_document`)) {
|
471
|
-
return;
|
472
|
-
}
|
473
|
-
context.report({
|
474
|
-
message: `\`<Document />\` from \`next/document\` should not be imported outside of \`pages/_document.js\`. See: ${url9}`,
|
475
|
-
node
|
476
|
-
});
|
477
|
-
}
|
478
|
-
}),
|
479
|
-
meta: {
|
480
|
-
docs: {
|
481
|
-
description: "Prevent importing `next/document` outside of `pages/_document.js`.",
|
482
|
-
recommended: true,
|
483
|
-
url: url9
|
484
|
-
},
|
485
|
-
schema: [],
|
486
|
-
type: "problem"
|
487
|
-
}
|
488
|
-
});
|
489
|
-
|
490
|
-
// src/rules/no-duplicate-head.ts
|
491
|
-
var url10 = "https://nextjs.org/docs/messages/no-duplicate-head";
|
492
|
-
var noDuplicateHead = defineRule({
|
493
|
-
create: (context) => {
|
494
|
-
const { sourceCode } = context;
|
495
|
-
let documentImportName = null;
|
496
|
-
return {
|
497
|
-
ImportDeclaration: (node) => {
|
498
|
-
if (node.source.value === "next/document") {
|
499
|
-
const documentImport = node.specifiers.find(
|
500
|
-
({ type }) => type === "ImportDefaultSpecifier"
|
501
|
-
);
|
502
|
-
if (documentImport?.local) {
|
503
|
-
documentImportName = documentImport.local.name;
|
504
|
-
}
|
505
|
-
}
|
506
|
-
},
|
507
|
-
ReturnStatement: (node) => {
|
508
|
-
const ancestors = sourceCode.getAncestors(node);
|
509
|
-
const documentClass = ancestors.find(
|
510
|
-
(ancestorNode) => ancestorNode.type === "ClassDeclaration" && ancestorNode.superClass && "name" in ancestorNode.superClass && ancestorNode.superClass.name === documentImportName
|
511
|
-
);
|
512
|
-
if (!documentClass) {
|
513
|
-
return;
|
514
|
-
}
|
515
|
-
if (node.argument && "children" in node.argument && // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
516
|
-
node.argument.children) {
|
517
|
-
const headComponents = node.argument.children.filter(
|
518
|
-
(childrenNode) => childrenNode.openingElement?.name && childrenNode.openingElement.name.name === "Head"
|
519
|
-
);
|
520
|
-
if (headComponents.length > 1) {
|
521
|
-
for (let i = 1; i < headComponents.length; i++) {
|
522
|
-
context.report({
|
523
|
-
message: `Do not include multiple instances of \`<Head/>\`. See: ${url10}`,
|
524
|
-
// @ts-expect-error initial override, TODO: fix
|
525
|
-
node: headComponents[i]
|
526
|
-
});
|
527
|
-
}
|
528
|
-
}
|
529
|
-
}
|
530
|
-
}
|
531
|
-
};
|
532
|
-
},
|
533
|
-
meta: {
|
534
|
-
docs: {
|
535
|
-
description: "Prevent duplicate usage of `<Head>` in `pages/_document.js`.",
|
536
|
-
recommended: true,
|
537
|
-
url: url10
|
538
|
-
},
|
539
|
-
schema: [],
|
540
|
-
type: "problem"
|
541
|
-
}
|
542
|
-
});
|
543
|
-
|
544
|
-
// src/rules/no-head-element.ts
|
545
|
-
var import_node_path = __toESM(require("path"), 1);
|
546
|
-
var url11 = "https://nextjs.org/docs/messages/no-head-element";
|
547
|
-
var noHeadElement = defineRule({
|
548
|
-
create: (context) => ({
|
549
|
-
JSXOpeningElement: (node) => {
|
550
|
-
const paths = context.filename;
|
551
|
-
const isInAppDir = () => (
|
552
|
-
// TODO: fix
|
553
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
554
|
-
paths.includes(`app${import_node_path.default.sep}`) || paths.includes(`app${import_node_path.default.posix.sep}`)
|
555
|
-
);
|
556
|
-
if (node.name.name !== "head" || isInAppDir()) {
|
557
|
-
return;
|
558
|
-
}
|
559
|
-
context.report({
|
560
|
-
message: `Do not use \`<head>\` element. Use \`<Head />\` from \`next/head\` instead. See: ${url11}`,
|
561
|
-
node
|
562
|
-
});
|
563
|
-
}
|
564
|
-
}),
|
565
|
-
meta: {
|
566
|
-
docs: {
|
567
|
-
category: "HTML",
|
568
|
-
description: "Prevent usage of `<head>` element.",
|
569
|
-
recommended: true,
|
570
|
-
url: url11
|
571
|
-
},
|
572
|
-
schema: [],
|
573
|
-
type: "problem"
|
574
|
-
}
|
575
|
-
});
|
576
|
-
|
577
|
-
// src/rules/no-head-import-in-document.ts
|
578
|
-
var path4 = __toESM(require("path"), 1);
|
579
|
-
var url12 = "https://nextjs.org/docs/messages/no-head-import-in-document";
|
580
|
-
var noHeadImportInDocument = defineRule({
|
581
|
-
create: (context) => ({
|
582
|
-
ImportDeclaration: (node) => {
|
583
|
-
if (node.source.value !== "next/head") {
|
584
|
-
return;
|
585
|
-
}
|
586
|
-
const document = context.filename.split("pages", 2)[1];
|
587
|
-
if (!document) {
|
588
|
-
return;
|
589
|
-
}
|
590
|
-
const { dir, name: name2 } = path4.parse(document);
|
591
|
-
if (name2.startsWith("_document") || dir === "/_document" && name2 === "index") {
|
592
|
-
context.report({
|
593
|
-
message: `\`next/head\` should not be imported in \`pages${document}\`. Use \`<Head />\` from \`next/document\` instead. See: ${url12}`,
|
594
|
-
node
|
595
|
-
});
|
596
|
-
}
|
597
|
-
}
|
598
|
-
}),
|
599
|
-
meta: {
|
600
|
-
docs: {
|
601
|
-
description: "Prevent usage of `next/head` in `pages/_document.js`.",
|
602
|
-
recommended: true,
|
603
|
-
url: url12
|
604
|
-
},
|
605
|
-
schema: [],
|
606
|
-
type: "problem"
|
607
|
-
}
|
608
|
-
});
|
609
|
-
|
610
|
-
// src/rules/no-html-link-for-pages.ts
|
611
|
-
var fs2 = __toESM(require("fs"), 1);
|
612
|
-
var path6 = __toESM(require("path"), 1);
|
613
|
-
|
614
|
-
// src/utils/get-root-dirs.ts
|
615
|
-
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
616
|
-
var processRootDir = (rootDir) => {
|
617
|
-
return import_fast_glob.default.globSync(rootDir.replaceAll("\\", "/"), {
|
618
|
-
onlyDirectories: true
|
619
|
-
});
|
620
|
-
};
|
621
|
-
var getRootDirs = (context) => {
|
622
|
-
let rootDirs = [context.cwd];
|
623
|
-
const nextSettings = (
|
624
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
625
|
-
context.settings.next || {}
|
626
|
-
);
|
627
|
-
const rootDir = nextSettings.rootDir;
|
628
|
-
if (typeof rootDir === "string") {
|
629
|
-
rootDirs = processRootDir(rootDir);
|
630
|
-
} else if (Array.isArray(rootDir)) {
|
631
|
-
rootDirs = rootDir.map((dir) => typeof dir === "string" ? processRootDir(dir) : []).flat();
|
632
|
-
}
|
633
|
-
return rootDirs;
|
634
|
-
};
|
635
|
-
|
636
|
-
// src/utils/url.ts
|
637
|
-
var fs = __toESM(require("fs"), 1);
|
638
|
-
var path5 = __toESM(require("path"), 1);
|
639
|
-
var fsReadDirSyncCache = {};
|
640
|
-
var parseUrlForPages = (urlprefix, directory) => {
|
641
|
-
fsReadDirSyncCache[directory] ??= fs.readdirSync(directory, {
|
642
|
-
withFileTypes: true
|
643
|
-
});
|
644
|
-
const res = [];
|
645
|
-
fsReadDirSyncCache[directory].forEach((dirent) => {
|
646
|
-
if (/(?<temp2>\.(?<temp1>j|t)sx?)$/.test(dirent.name)) {
|
647
|
-
if (/^index(?<temp2>\.(?<temp1>j|t)sx?)$/.test(dirent.name)) {
|
648
|
-
res.push(
|
649
|
-
`${urlprefix}${dirent.name.replace(/^index(?<temp2>\.(?<temp1>j|t)sx?)$/, "")}`
|
650
|
-
);
|
651
|
-
}
|
652
|
-
res.push(
|
653
|
-
`${urlprefix}${dirent.name.replace(/(?<temp2>\.(?<temp1>j|t)sx?)$/, "")}`
|
654
|
-
);
|
655
|
-
} else {
|
656
|
-
const dirPath = path5.join(directory, dirent.name);
|
657
|
-
if (dirent.isDirectory() && !dirent.isSymbolicLink()) {
|
658
|
-
res.push(...parseUrlForPages(`${urlprefix + dirent.name}/`, dirPath));
|
659
|
-
}
|
660
|
-
}
|
661
|
-
});
|
662
|
-
return res;
|
663
|
-
};
|
664
|
-
var parseUrlForAppDir = (urlprefix, directory) => {
|
665
|
-
fsReadDirSyncCache[directory] ??= fs.readdirSync(directory, {
|
666
|
-
withFileTypes: true
|
667
|
-
});
|
668
|
-
const res = [];
|
669
|
-
fsReadDirSyncCache[directory].forEach((dirent) => {
|
670
|
-
if (/(?<temp2>\.(?<temp1>j|t)sx?)$/.test(dirent.name)) {
|
671
|
-
if (/^page(?<temp2>\.(?<temp1>j|t)sx?)$/.test(dirent.name)) {
|
672
|
-
res.push(
|
673
|
-
`${urlprefix}${dirent.name.replace(/^page(?<temp2>\.(?<temp1>j|t)sx?)$/, "")}`
|
674
|
-
);
|
675
|
-
} else if (!/^layout(?<temp2>\.(?<temp1>j|t)sx?)$/.test(dirent.name)) {
|
676
|
-
res.push(
|
677
|
-
`${urlprefix}${dirent.name.replace(/(?<temp2>\.(?<temp1>j|t)sx?)$/, "")}`
|
678
|
-
);
|
679
|
-
}
|
680
|
-
} else {
|
681
|
-
const dirPath = path5.join(directory, dirent.name);
|
682
|
-
if (dirent.isDirectory(dirPath) && !dirent.isSymbolicLink()) {
|
683
|
-
res.push(...parseUrlForPages(`${urlprefix + dirent.name}/`, dirPath));
|
684
|
-
}
|
685
|
-
}
|
686
|
-
});
|
687
|
-
return res;
|
688
|
-
};
|
689
|
-
var normalizeURL = (url21) => {
|
690
|
-
if (!url21) {
|
691
|
-
return;
|
692
|
-
}
|
693
|
-
url21 = url21.split("?", 1)[0];
|
694
|
-
url21 = url21.split("#", 1)[0];
|
695
|
-
url21 = url21.replace(/(?<temp1>\/index\.html)$/, "/");
|
696
|
-
if (url21 === "") {
|
697
|
-
return url21;
|
698
|
-
}
|
699
|
-
url21 = url21.endsWith("/") ? url21 : `${url21}/`;
|
700
|
-
return url21;
|
701
|
-
};
|
702
|
-
var normalizeAppPath = (route) => ensureLeadingSlash(
|
703
|
-
route.split("/").reduce((pathname, segment, index, segments) => {
|
704
|
-
if (!segment) {
|
705
|
-
return pathname;
|
706
|
-
}
|
707
|
-
if (isGroupSegment(segment)) {
|
708
|
-
return pathname;
|
709
|
-
}
|
710
|
-
if (segment.startsWith("@")) {
|
711
|
-
return pathname;
|
712
|
-
}
|
713
|
-
if ((segment === "page" || segment === "route") && index === segments.length - 1) {
|
714
|
-
return pathname;
|
715
|
-
}
|
716
|
-
return `${pathname}/${segment}`;
|
717
|
-
}, "")
|
718
|
-
);
|
719
|
-
var getUrlFromPagesDirectories = (urlPrefix, directories) => Array.from(
|
720
|
-
// De-duplicate similar pages across multiple directories.
|
721
|
-
new Set(
|
722
|
-
directories.flatMap((directory) => parseUrlForPages(urlPrefix, directory)).map(
|
723
|
-
// Since the URLs are normalized we add `^` and `$` to the RegExp to make sure they match exactly.
|
724
|
-
(url21) => `^${normalizeURL(url21)}$`
|
725
|
-
)
|
726
|
-
)
|
727
|
-
).map((urlReg) => {
|
728
|
-
urlReg = urlReg.replaceAll(/\[.*]/g, "((?!.+?\\..+?).*?)");
|
729
|
-
return new RegExp(urlReg);
|
730
|
-
});
|
731
|
-
var getUrlFromAppDirectory = (urlPrefix, directories) => Array.from(
|
732
|
-
// De-duplicate similar pages across multiple directories.
|
733
|
-
new Set(
|
734
|
-
directories.map((directory) => parseUrlForAppDir(urlPrefix, directory)).flat().map(
|
735
|
-
// Since the URLs are normalized we add `^` and `$` to the RegExp to make sure they match exactly.
|
736
|
-
(url21) => `^${normalizeAppPath(url21)}$`
|
737
|
-
)
|
738
|
-
)
|
739
|
-
).map((urlReg) => {
|
740
|
-
urlReg = urlReg.replaceAll(/\[.*]/g, "((?!.+?\\..+?).*?)");
|
741
|
-
return new RegExp(urlReg);
|
742
|
-
});
|
743
|
-
var execOnce = (fn) => {
|
744
|
-
let used = false;
|
745
|
-
let result;
|
746
|
-
return (...args) => {
|
747
|
-
if (!used) {
|
748
|
-
used = true;
|
749
|
-
result = fn(...args);
|
750
|
-
}
|
751
|
-
return result;
|
752
|
-
};
|
753
|
-
};
|
754
|
-
var ensureLeadingSlash = (route) => route.startsWith("/") ? route : `/${route}`;
|
755
|
-
var isGroupSegment = (segment) => segment.startsWith("(") && segment.endsWith(")");
|
756
|
-
|
757
|
-
// src/rules/no-html-link-for-pages.ts
|
758
|
-
var pagesDirWarning = execOnce((pagesDirs) => {
|
759
|
-
console.warn(
|
760
|
-
`Pages directory cannot be found at ${pagesDirs.join(" or ")}. If using a custom path, please configure with the \`no-html-link-for-pages\` rule in your eslint config file.`
|
761
|
-
);
|
762
|
-
});
|
763
|
-
var fsExistsSyncCache = {};
|
764
|
-
var memoize = (fn) => {
|
765
|
-
const cache = {};
|
766
|
-
return (...args) => {
|
767
|
-
const key = JSON.stringify(args);
|
768
|
-
cache[key] ??= fn(...args);
|
769
|
-
return cache[key];
|
770
|
-
};
|
771
|
-
};
|
772
|
-
var cachedGetUrlFromPagesDirectories = memoize(getUrlFromPagesDirectories);
|
773
|
-
var cachedGetUrlFromAppDirectory = memoize(getUrlFromAppDirectory);
|
774
|
-
var url13 = "https://nextjs.org/docs/messages/no-html-link-for-pages";
|
775
|
-
var noHtmlLinkForPages = defineRule({
|
776
|
-
/**
|
777
|
-
* Creates an ESLint rule listener.
|
778
|
-
*/
|
779
|
-
create: (context) => {
|
780
|
-
const ruleOptions = context.options;
|
781
|
-
const [customPagesDirectory] = ruleOptions;
|
782
|
-
const rootDirs = getRootDirs(context);
|
783
|
-
const pagesDirs = (customPagesDirectory ? [customPagesDirectory] : rootDirs.map((dir) => [
|
784
|
-
path6.join(dir, "pages"),
|
785
|
-
path6.join(dir, "src", "pages")
|
786
|
-
])).flat();
|
787
|
-
const foundPagesDirs = pagesDirs.filter((dir) => {
|
788
|
-
fsExistsSyncCache[dir] ??= fs2.existsSync(dir);
|
789
|
-
return fsExistsSyncCache[dir];
|
790
|
-
});
|
791
|
-
const appDirs = rootDirs.map((dir) => [path6.join(dir, "app"), path6.join(dir, "src", "app")]).flat();
|
792
|
-
const foundAppDirs = appDirs.filter((dir) => {
|
793
|
-
fsExistsSyncCache[dir] ??= fs2.existsSync(dir);
|
794
|
-
return fsExistsSyncCache[dir];
|
795
|
-
});
|
796
|
-
if (foundPagesDirs.length === 0 && foundAppDirs.length === 0) {
|
797
|
-
pagesDirWarning(pagesDirs);
|
798
|
-
return {};
|
799
|
-
}
|
800
|
-
const pageUrls = cachedGetUrlFromPagesDirectories("/", foundPagesDirs);
|
801
|
-
const appDirUrls = cachedGetUrlFromAppDirectory("/", foundAppDirs);
|
802
|
-
const allUrlRegex = [...pageUrls, ...appDirUrls];
|
803
|
-
return {
|
804
|
-
JSXOpeningElement: (node) => {
|
805
|
-
if (node.name.name !== "a") {
|
806
|
-
return;
|
807
|
-
}
|
808
|
-
if (node.attributes.length === 0) {
|
809
|
-
return;
|
810
|
-
}
|
811
|
-
const target = node.attributes.find(
|
812
|
-
(attr) => attr.type === "JSXAttribute" && attr.name.name === "target"
|
813
|
-
);
|
814
|
-
if (target && target.value.value === "_blank") {
|
815
|
-
return;
|
816
|
-
}
|
817
|
-
const href = node.attributes.find(
|
818
|
-
(attr) => attr.type === "JSXAttribute" && attr.name.name === "href"
|
819
|
-
);
|
820
|
-
if (!href || href.value && href.value.type !== "Literal") {
|
821
|
-
return;
|
822
|
-
}
|
823
|
-
const hasDownloadAttr = node.attributes.find(
|
824
|
-
(attr) => attr.type === "JSXAttribute" && attr.name.name === "download"
|
825
|
-
);
|
826
|
-
if (hasDownloadAttr) {
|
827
|
-
return;
|
828
|
-
}
|
829
|
-
const hrefPath = normalizeURL(href.value.value);
|
830
|
-
if (/^(?<temp1>https?:\/\/|\/\/)/.test(hrefPath)) {
|
831
|
-
return;
|
832
|
-
}
|
833
|
-
allUrlRegex.forEach((foundUrl) => {
|
834
|
-
if (hrefPath && foundUrl.test(normalizeURL(hrefPath))) {
|
835
|
-
context.report({
|
836
|
-
message: `Do not use an \`<a>\` element to navigate to \`${hrefPath}\`. Use \`<Link />\` from \`next/link\` instead. See: ${url13}`,
|
837
|
-
node
|
838
|
-
});
|
839
|
-
}
|
840
|
-
});
|
841
|
-
}
|
842
|
-
};
|
843
|
-
},
|
844
|
-
meta: {
|
845
|
-
docs: {
|
846
|
-
category: "HTML",
|
847
|
-
description: "Prevent usage of `<a>` elements to navigate to internal Next.js pages.",
|
848
|
-
recommended: true,
|
849
|
-
url: url13
|
850
|
-
},
|
851
|
-
schema: [
|
852
|
-
{
|
853
|
-
oneOf: [
|
854
|
-
{
|
855
|
-
type: "string"
|
856
|
-
},
|
857
|
-
{
|
858
|
-
items: {
|
859
|
-
type: "string"
|
860
|
-
},
|
861
|
-
type: "array",
|
862
|
-
uniqueItems: true
|
863
|
-
}
|
864
|
-
]
|
865
|
-
}
|
866
|
-
],
|
867
|
-
type: "problem"
|
868
|
-
}
|
869
|
-
});
|
870
|
-
|
871
|
-
// src/rules/no-img-element.ts
|
872
|
-
var import_node_path2 = __toESM(require("path"), 1);
|
873
|
-
var url14 = "https://nextjs.org/docs/messages/no-img-element";
|
874
|
-
var noImgElement = defineRule({
|
875
|
-
create: (context) => {
|
876
|
-
const relativePath = context.filename.replace(import_node_path2.default.sep, "/").replace(context.cwd, "").replace(/^\//, "");
|
877
|
-
const isAppDir = /^(?<temp1>src\/)?app\//.test(relativePath);
|
878
|
-
return {
|
879
|
-
JSXOpeningElement: (node) => {
|
880
|
-
if (node.name.name !== "img") {
|
881
|
-
return;
|
882
|
-
}
|
883
|
-
if (node.attributes.length === 0) {
|
884
|
-
return;
|
885
|
-
}
|
886
|
-
if (node.parent?.parent?.openingElement?.name?.name === "picture") {
|
887
|
-
return;
|
888
|
-
}
|
889
|
-
if (isAppDir && /\/opengraph-image|twitter-image|icon\.\w+$/.test(relativePath)) {
|
890
|
-
return;
|
891
|
-
}
|
892
|
-
context.report({
|
893
|
-
message: `Using \`<img>\` could result in slower LCP and higher bandwidth. Consider using \`<Image />\` from \`next/image\` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: ${url14}`,
|
894
|
-
node
|
895
|
-
});
|
896
|
-
}
|
897
|
-
};
|
898
|
-
},
|
899
|
-
meta: {
|
900
|
-
docs: {
|
901
|
-
category: "HTML",
|
902
|
-
description: "Prevent usage of `<img>` element due to slower LCP and higher bandwidth.",
|
903
|
-
recommended: true,
|
904
|
-
url: url14
|
905
|
-
},
|
906
|
-
schema: [],
|
907
|
-
type: "problem"
|
908
|
-
}
|
909
|
-
});
|
910
|
-
|
911
|
-
// src/rules/no-page-custom-font.ts
|
912
|
-
var import_node_path3 = require("path");
|
913
|
-
var url15 = "https://nextjs.org/docs/messages/no-page-custom-font";
|
914
|
-
var isIdentifierMatch = (id1, id2) => id1 === null && id2 === null || id1 && id2 && id1.name === id2.name;
|
915
|
-
var noPageCustomFont = defineRule({
|
916
|
-
create: (context) => {
|
917
|
-
const { sourceCode } = context;
|
918
|
-
const paths = context.filename.split("pages");
|
919
|
-
const page = paths[paths.length - 1];
|
920
|
-
if (!page) {
|
921
|
-
return {};
|
922
|
-
}
|
923
|
-
const isDocument = (
|
924
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
925
|
-
page.startsWith(`${import_node_path3.sep}_document`) || page.startsWith(`${import_node_path3.posix.sep}_document`)
|
926
|
-
);
|
927
|
-
let documentImportName;
|
928
|
-
let localDefaultExportId;
|
929
|
-
let exportDeclarationType;
|
930
|
-
return {
|
931
|
-
ExportDefaultDeclaration: (node) => {
|
932
|
-
exportDeclarationType = node.declaration.type;
|
933
|
-
if (node.declaration.type === "FunctionDeclaration") {
|
934
|
-
localDefaultExportId = node.declaration.id;
|
935
|
-
return;
|
936
|
-
}
|
937
|
-
if (node.declaration.type === "ClassDeclaration" && node.declaration.superClass && "name" in node.declaration.superClass && node.declaration.superClass.name === documentImportName) {
|
938
|
-
localDefaultExportId = node.declaration.id;
|
939
|
-
}
|
940
|
-
},
|
941
|
-
ImportDeclaration: (node) => {
|
942
|
-
if (node.source.value === "next/document") {
|
943
|
-
const documentImport = node.specifiers.find(
|
944
|
-
({ type }) => type === "ImportDefaultSpecifier"
|
945
|
-
);
|
946
|
-
if (documentImport?.local) {
|
947
|
-
documentImportName = documentImport.local.name;
|
948
|
-
}
|
949
|
-
}
|
950
|
-
},
|
951
|
-
JSXOpeningElement: (node) => {
|
952
|
-
if (node.name.name !== "link") {
|
953
|
-
return;
|
954
|
-
}
|
955
|
-
const ancestors = sourceCode.getAncestors(node);
|
956
|
-
if (!localDefaultExportId) {
|
957
|
-
const program = ancestors.find(
|
958
|
-
(ancestor) => ancestor.type === "Program"
|
959
|
-
);
|
960
|
-
for (let i = 0; i <= program.tokens.length - 1; i++) {
|
961
|
-
if (localDefaultExportId) {
|
962
|
-
break;
|
963
|
-
}
|
964
|
-
const token = program.tokens[i];
|
965
|
-
if (token?.type === "Keyword" && token.value === "export") {
|
966
|
-
const nextToken = program.tokens[i + 1];
|
967
|
-
if (nextToken && nextToken.type === "Keyword" && nextToken.value === "default") {
|
968
|
-
const maybeIdentifier = program.tokens[i + 2];
|
969
|
-
if (maybeIdentifier && maybeIdentifier.type === "Identifier") {
|
970
|
-
localDefaultExportId = { name: maybeIdentifier.value };
|
971
|
-
}
|
972
|
-
}
|
973
|
-
}
|
974
|
-
}
|
975
|
-
}
|
976
|
-
const parentComponent = ancestors.find((ancestor) => {
|
977
|
-
if (exportDeclarationType === "ClassDeclaration") {
|
978
|
-
return ancestor.type === exportDeclarationType && "superClass" in ancestor && ancestor.superClass && "name" in ancestor.superClass && ancestor.superClass.name === documentImportName;
|
979
|
-
}
|
980
|
-
if ("id" in ancestor) {
|
981
|
-
if (exportDeclarationType === "FunctionDeclaration") {
|
982
|
-
return ancestor.type === exportDeclarationType && isIdentifierMatch(ancestor.id, localDefaultExportId);
|
983
|
-
}
|
984
|
-
return isIdentifierMatch(ancestor.id, localDefaultExportId);
|
985
|
-
}
|
986
|
-
return false;
|
987
|
-
});
|
988
|
-
if (isDocument && parentComponent) {
|
989
|
-
return;
|
990
|
-
}
|
991
|
-
const attributes = new NodeAttributes(node);
|
992
|
-
if (!attributes.has("href") || !attributes.hasValue("href")) {
|
993
|
-
return;
|
994
|
-
}
|
995
|
-
const hrefValue = attributes.value("href");
|
996
|
-
const isGoogleFont = typeof hrefValue === "string" && hrefValue.startsWith("https://fonts.googleapis.com/css");
|
997
|
-
if (isGoogleFont) {
|
998
|
-
const end = `This is discouraged. See: ${url15}`;
|
999
|
-
const message2 = isDocument ? `Using \`<link />\` outside of \`<Head>\` will disable automatic font optimization. ${end}` : `Custom fonts not added in \`pages/_document.js\` will only load for a single page. ${end}`;
|
1000
|
-
context.report({
|
1001
|
-
message: message2,
|
1002
|
-
node
|
1003
|
-
});
|
1004
|
-
}
|
1005
|
-
}
|
1006
|
-
};
|
1007
|
-
},
|
1008
|
-
meta: {
|
1009
|
-
docs: {
|
1010
|
-
description: "Prevent page-only custom fonts.",
|
1011
|
-
recommended: true,
|
1012
|
-
url: url15
|
1013
|
-
},
|
1014
|
-
schema: [],
|
1015
|
-
type: "problem"
|
1016
|
-
}
|
1017
|
-
});
|
1018
|
-
|
1019
|
-
// src/rules/no-script-component-in-head.ts
|
1020
|
-
var url16 = "https://nextjs.org/docs/messages/no-script-component-in-head";
|
1021
|
-
var noScriptComponentInHead = defineRule({
|
1022
|
-
create: (context) => {
|
1023
|
-
let isNextHead = null;
|
1024
|
-
return {
|
1025
|
-
ImportDeclaration: (node) => {
|
1026
|
-
if (node.source.value === "next/head") {
|
1027
|
-
isNextHead = node.source.value;
|
1028
|
-
}
|
1029
|
-
},
|
1030
|
-
JSXElement: (node) => {
|
1031
|
-
if (!isNextHead) {
|
1032
|
-
return;
|
1033
|
-
}
|
1034
|
-
if (node.openingElement?.name && node.openingElement.name.name !== "Head") {
|
1035
|
-
return;
|
1036
|
-
}
|
1037
|
-
const scriptTag = node.children.find(
|
1038
|
-
(child) => child.openingElement?.name && child.openingElement.name.name === "Script"
|
1039
|
-
);
|
1040
|
-
if (scriptTag) {
|
1041
|
-
context.report({
|
1042
|
-
message: `\`next/script\` should not be used in \`next/head\` component. Move \`<Script />\` outside of \`<Head>\` instead. See: ${url16}`,
|
1043
|
-
node
|
1044
|
-
});
|
1045
|
-
}
|
1046
|
-
}
|
1047
|
-
};
|
1048
|
-
},
|
1049
|
-
meta: {
|
1050
|
-
docs: {
|
1051
|
-
description: "Prevent usage of `next/script` in `next/head` component.",
|
1052
|
-
recommended: true,
|
1053
|
-
url: url16
|
1054
|
-
},
|
1055
|
-
schema: [],
|
1056
|
-
type: "problem"
|
1057
|
-
}
|
1058
|
-
});
|
1059
|
-
|
1060
|
-
// src/rules/no-styled-jsx-in-document.ts
|
1061
|
-
var path8 = __toESM(require("path"), 1);
|
1062
|
-
var url17 = "https://nextjs.org/docs/messages/no-styled-jsx-in-document";
|
1063
|
-
var noStyledJsxInDocument = defineRule({
|
1064
|
-
create: (context) => ({
|
1065
|
-
JSXOpeningElement: (node) => {
|
1066
|
-
const document = context.filename.split("pages", 2)[1];
|
1067
|
-
if (!document) {
|
1068
|
-
return;
|
1069
|
-
}
|
1070
|
-
const { dir, name: name2 } = path8.parse(document);
|
1071
|
-
if (!(name2.startsWith("_document") || dir === "/_document" && name2 === "index")) {
|
1072
|
-
return;
|
1073
|
-
}
|
1074
|
-
if (node.name.name === "style" && node.attributes.find(
|
1075
|
-
(attr) => attr.type === "JSXAttribute" && attr.name.name === "jsx"
|
1076
|
-
)) {
|
1077
|
-
context.report({
|
1078
|
-
message: `\`styled-jsx\` should not be used in \`pages/_document.js\`. See: ${url17}`,
|
1079
|
-
node
|
1080
|
-
});
|
1081
|
-
}
|
1082
|
-
}
|
1083
|
-
}),
|
1084
|
-
meta: {
|
1085
|
-
docs: {
|
1086
|
-
description: "Prevent usage of `styled-jsx` in `pages/_document.js`.",
|
1087
|
-
recommended: true,
|
1088
|
-
url: url17
|
1089
|
-
},
|
1090
|
-
schema: [],
|
1091
|
-
type: "problem"
|
1092
|
-
}
|
1093
|
-
});
|
1094
|
-
|
1095
|
-
// src/rules/no-sync-scripts.ts
|
1096
|
-
var url18 = "https://nextjs.org/docs/messages/no-sync-scripts";
|
1097
|
-
var noSyncScripts = defineRule({
|
1098
|
-
create: (context) => ({
|
1099
|
-
JSXOpeningElement: (node) => {
|
1100
|
-
if (node.name.name !== "script") {
|
1101
|
-
return;
|
1102
|
-
}
|
1103
|
-
if (node.attributes.length === 0) {
|
1104
|
-
return;
|
1105
|
-
}
|
1106
|
-
const attributeNames = node.attributes.filter((attr) => attr.type === "JSXAttribute").map((attr) => attr.name.name);
|
1107
|
-
if (attributeNames.includes("src") && !attributeNames.includes("async") && !attributeNames.includes("defer")) {
|
1108
|
-
context.report({
|
1109
|
-
message: `Synchronous scripts should not be used. See: ${url18}`,
|
1110
|
-
node
|
1111
|
-
});
|
1112
|
-
}
|
1113
|
-
}
|
1114
|
-
}),
|
1115
|
-
meta: {
|
1116
|
-
docs: {
|
1117
|
-
description: "Prevent synchronous scripts.",
|
1118
|
-
recommended: true,
|
1119
|
-
url: url18
|
1120
|
-
},
|
1121
|
-
schema: [],
|
1122
|
-
type: "problem"
|
1123
|
-
}
|
1124
|
-
});
|
1125
|
-
|
1126
|
-
// src/rules/no-title-in-document-head.ts
|
1127
|
-
var url19 = "https://nextjs.org/docs/messages/no-title-in-document-head";
|
1128
|
-
var noTitleInDocumentHead = defineRule({
|
1129
|
-
create: (context) => {
|
1130
|
-
let headFromNextDocument = false;
|
1131
|
-
return {
|
1132
|
-
ImportDeclaration: (node) => {
|
1133
|
-
if (node.source.value === "next/document") {
|
1134
|
-
if (node.specifiers.some(
|
1135
|
-
({ local }) => local.name === "Head"
|
1136
|
-
)) {
|
1137
|
-
headFromNextDocument = true;
|
1138
|
-
}
|
1139
|
-
}
|
1140
|
-
},
|
1141
|
-
JSXElement: (node) => {
|
1142
|
-
if (!headFromNextDocument) {
|
1143
|
-
return;
|
1144
|
-
}
|
1145
|
-
if (node.openingElement?.name && node.openingElement.name.name !== "Head") {
|
1146
|
-
return;
|
1147
|
-
}
|
1148
|
-
const titleTag = node.children.find(
|
1149
|
-
(child) => child.openingElement?.name && child.openingElement.name.type === "JSXIdentifier" && child.openingElement.name.name === "title"
|
1150
|
-
);
|
1151
|
-
if (titleTag) {
|
1152
|
-
context.report({
|
1153
|
-
message: `Do not use \`<title>\` element with \`<Head />\` component from \`next/document\`. Titles should defined at the page-level using \`<Head />\` from \`next/head\` instead. See: ${url19}`,
|
1154
|
-
node: titleTag
|
1155
|
-
});
|
1156
|
-
}
|
1157
|
-
}
|
1158
|
-
};
|
1159
|
-
},
|
1160
|
-
meta: {
|
1161
|
-
docs: {
|
1162
|
-
description: "Prevent usage of `<title>` with `Head` component from `next/document`.",
|
1163
|
-
recommended: true,
|
1164
|
-
url: url19
|
1165
|
-
},
|
1166
|
-
schema: [],
|
1167
|
-
type: "problem"
|
1168
|
-
}
|
1169
|
-
});
|
1170
|
-
|
1171
|
-
// src/rules/no-typos.ts
|
1172
|
-
var path9 = __toESM(require("path"), 1);
|
1173
|
-
var NEXT_EXPORT_FUNCTIONS = [
|
1174
|
-
"getStaticProps",
|
1175
|
-
"getStaticPaths",
|
1176
|
-
"getServerSideProps"
|
1177
|
-
];
|
1178
|
-
var THRESHOLD = 1;
|
1179
|
-
var minDistance = (a, b) => {
|
1180
|
-
const m = a.length;
|
1181
|
-
const n = b.length;
|
1182
|
-
if (m < n) {
|
1183
|
-
return minDistance(b, a);
|
1184
|
-
}
|
1185
|
-
if (n === 0) {
|
1186
|
-
return m;
|
1187
|
-
}
|
1188
|
-
let previousRow = Array.from({ length: n + 1 }, (_, i) => i);
|
1189
|
-
for (let i = 0; i < m; i++) {
|
1190
|
-
const s1 = a[i];
|
1191
|
-
const currentRow = [i + 1];
|
1192
|
-
for (let j = 0; j < n; j++) {
|
1193
|
-
const s2 = b[j];
|
1194
|
-
const insertions = previousRow[j + 1] + 1;
|
1195
|
-
const deletions = currentRow[j] + 1;
|
1196
|
-
const substitutions = previousRow[j] + Number(s1 !== s2);
|
1197
|
-
currentRow.push(Math.min(insertions, deletions, substitutions));
|
1198
|
-
}
|
1199
|
-
previousRow = currentRow;
|
1200
|
-
}
|
1201
|
-
return previousRow[previousRow.length - 1];
|
1202
|
-
};
|
1203
|
-
var noTypos = defineRule({
|
1204
|
-
create: (context) => {
|
1205
|
-
const checkTypos = (node, name2) => {
|
1206
|
-
if (NEXT_EXPORT_FUNCTIONS.includes(name2)) {
|
1207
|
-
return;
|
1208
|
-
}
|
1209
|
-
const potentialTypos = NEXT_EXPORT_FUNCTIONS.map((o) => ({
|
1210
|
-
distance: minDistance(o, name2) ?? Infinity,
|
1211
|
-
option: o
|
1212
|
-
})).filter(({ distance }) => distance <= THRESHOLD && distance > 0).sort((a, b) => a.distance - b.distance);
|
1213
|
-
if (potentialTypos.length) {
|
1214
|
-
context.report({
|
1215
|
-
message: `${name2} may be a typo. Did you mean ${potentialTypos[0]?.option}?`,
|
1216
|
-
node
|
1217
|
-
});
|
1218
|
-
}
|
1219
|
-
};
|
1220
|
-
return {
|
1221
|
-
ExportNamedDeclaration: (node) => {
|
1222
|
-
const page = context.filename.split("pages", 2)[1];
|
1223
|
-
if (!page || path9.parse(page).dir.startsWith("/api")) {
|
1224
|
-
return;
|
1225
|
-
}
|
1226
|
-
const decl = node.declaration;
|
1227
|
-
if (!decl) {
|
1228
|
-
return;
|
1229
|
-
}
|
1230
|
-
switch (decl.type) {
|
1231
|
-
case "FunctionDeclaration": {
|
1232
|
-
checkTypos(node, decl.id.name);
|
1233
|
-
break;
|
1234
|
-
}
|
1235
|
-
case "VariableDeclaration": {
|
1236
|
-
decl.declarations.forEach((d) => {
|
1237
|
-
if (d.id.type !== "Identifier") {
|
1238
|
-
return;
|
1239
|
-
}
|
1240
|
-
checkTypos(node, d.id.name);
|
1241
|
-
});
|
1242
|
-
break;
|
1243
|
-
}
|
1244
|
-
default: {
|
1245
|
-
break;
|
1246
|
-
}
|
1247
|
-
}
|
1248
|
-
}
|
1249
|
-
};
|
1250
|
-
},
|
1251
|
-
meta: {
|
1252
|
-
docs: {
|
1253
|
-
description: "Prevent common typos in Next.js data fetching functions.",
|
1254
|
-
recommended: true
|
1255
|
-
},
|
1256
|
-
schema: [],
|
1257
|
-
type: "problem"
|
1258
|
-
}
|
1259
|
-
});
|
1260
|
-
|
1261
|
-
// src/rules/no-unwanted-polyfillio.ts
|
1262
|
-
var NEXT_POLYFILLED_FEATURES = [
|
1263
|
-
"Array.prototype.@@iterator",
|
1264
|
-
"Array.prototype.at",
|
1265
|
-
"Array.prototype.copyWithin",
|
1266
|
-
"Array.prototype.fill",
|
1267
|
-
"Array.prototype.find",
|
1268
|
-
"Array.prototype.findIndex",
|
1269
|
-
"Array.prototype.flatMap",
|
1270
|
-
"Array.prototype.flat",
|
1271
|
-
"Array.from",
|
1272
|
-
"Array.prototype.includes",
|
1273
|
-
"Array.of",
|
1274
|
-
"Function.prototype.name",
|
1275
|
-
"fetch",
|
1276
|
-
"Map",
|
1277
|
-
"Number.EPSILON",
|
1278
|
-
"Number.Epsilon",
|
1279
|
-
"Number.isFinite",
|
1280
|
-
"Number.isNaN",
|
1281
|
-
"Number.isInteger",
|
1282
|
-
"Number.isSafeInteger",
|
1283
|
-
"Number.MAX_SAFE_INTEGER",
|
1284
|
-
"Number.MIN_SAFE_INTEGER",
|
1285
|
-
"Number.parseFloat",
|
1286
|
-
"Number.parseInt",
|
1287
|
-
"Object.assign",
|
1288
|
-
"Object.entries",
|
1289
|
-
"Object.fromEntries",
|
1290
|
-
"Object.getOwnPropertyDescriptor",
|
1291
|
-
"Object.getOwnPropertyDescriptors",
|
1292
|
-
"Object.hasOwn",
|
1293
|
-
"Object.is",
|
1294
|
-
"Object.keys",
|
1295
|
-
"Object.values",
|
1296
|
-
"Reflect",
|
1297
|
-
"Set",
|
1298
|
-
"Symbol",
|
1299
|
-
"Symbol.asyncIterator",
|
1300
|
-
"String.prototype.codePointAt",
|
1301
|
-
"String.prototype.endsWith",
|
1302
|
-
"String.fromCodePoint",
|
1303
|
-
"String.prototype.includes",
|
1304
|
-
"String.prototype.@@iterator",
|
1305
|
-
"String.prototype.padEnd",
|
1306
|
-
"String.prototype.padStart",
|
1307
|
-
"String.prototype.repeat",
|
1308
|
-
"String.raw",
|
1309
|
-
"String.prototype.startsWith",
|
1310
|
-
"String.prototype.trimEnd",
|
1311
|
-
"String.prototype.trimStart",
|
1312
|
-
"URL",
|
1313
|
-
"URL.prototype.toJSON",
|
1314
|
-
"URLSearchParams",
|
1315
|
-
"WeakMap",
|
1316
|
-
"WeakSet",
|
1317
|
-
"Promise",
|
1318
|
-
"Promise.prototype.finally",
|
1319
|
-
"es2015",
|
1320
|
-
// Should be covered by babel-preset-env instead.
|
1321
|
-
"es2016",
|
1322
|
-
// contains polyfilled 'Array.prototype.includes', 'String.prototype.padEnd' and 'String.prototype.padStart'
|
1323
|
-
"es2017",
|
1324
|
-
// contains polyfilled 'Object.entries', 'Object.getOwnPropertyDescriptors', 'Object.values', 'String.prototype.padEnd' and 'String.prototype.padStart'
|
1325
|
-
"es2018",
|
1326
|
-
// contains polyfilled 'Promise.prototype.finally' and ''Symbol.asyncIterator'
|
1327
|
-
"es2019",
|
1328
|
-
// Contains polyfilled 'Object.fromEntries' and polyfilled 'Array.prototype.flat', 'Array.prototype.flatMap', 'String.prototype.trimEnd' and 'String.prototype.trimStart'
|
1329
|
-
"es5",
|
1330
|
-
// Should be covered by babel-preset-env instead.
|
1331
|
-
"es6",
|
1332
|
-
// Should be covered by babel-preset-env instead.
|
1333
|
-
"es7"
|
1334
|
-
// contains polyfilled 'Array.prototype.includes', 'String.prototype.padEnd' and 'String.prototype.padStart'
|
1335
|
-
];
|
1336
|
-
var url20 = "https://nextjs.org/docs/messages/no-unwanted-polyfillio";
|
1337
|
-
var noUnwantedPolyfillio = defineRule({
|
1338
|
-
create: (context) => {
|
1339
|
-
let scriptImport = null;
|
1340
|
-
return {
|
1341
|
-
ImportDeclaration: (node) => {
|
1342
|
-
if (node.source && node.source.value === "next/script") {
|
1343
|
-
scriptImport = node.specifiers[0].local.name;
|
1344
|
-
}
|
1345
|
-
},
|
1346
|
-
JSXOpeningElement: (node) => {
|
1347
|
-
if (node.name && node.name.name !== "script" && node.name.name !== scriptImport) {
|
1348
|
-
return;
|
1349
|
-
}
|
1350
|
-
if (node.attributes.length === 0) {
|
1351
|
-
return;
|
1352
|
-
}
|
1353
|
-
const srcNode = node.attributes.find(
|
1354
|
-
(attr) => attr.type === "JSXAttribute" && attr.name.name === "src"
|
1355
|
-
);
|
1356
|
-
if (!srcNode || srcNode.value.type !== "Literal") {
|
1357
|
-
return;
|
1358
|
-
}
|
1359
|
-
const src = srcNode.value.value;
|
1360
|
-
if (src.startsWith("https://cdn.polyfill.io/v2/") || src.startsWith("https://polyfill.io/v3/") || // https://community.fastly.com/t/new-options-for-polyfill-io-users/2540
|
1361
|
-
src.startsWith("https://polyfill-fastly.net/") || src.startsWith("https://polyfill-fastly.io/") || // https://blog.cloudflare.com/polyfill-io-now-available-on-cdnjs-reduce-your-supply-chain-risk
|
1362
|
-
src.startsWith("https://cdnjs.cloudflare.com/polyfill/")) {
|
1363
|
-
const featureQueryString = new URL(src).searchParams.get("features");
|
1364
|
-
const featuresRequested = (featureQueryString ?? "").split(",");
|
1365
|
-
const unwantedFeatures = featuresRequested.filter(
|
1366
|
-
(feature) => NEXT_POLYFILLED_FEATURES.includes(feature)
|
1367
|
-
);
|
1368
|
-
if (unwantedFeatures.length > 0) {
|
1369
|
-
context.report({
|
1370
|
-
message: `No duplicate polyfills from Polyfill.io are allowed. ${unwantedFeatures.join(
|
1371
|
-
", "
|
1372
|
-
)} ${unwantedFeatures.length > 1 ? "are" : "is"} already shipped with Next.js. See: ${url20}`,
|
1373
|
-
node
|
1374
|
-
});
|
1375
|
-
}
|
1376
|
-
}
|
1377
|
-
}
|
1378
|
-
};
|
1379
|
-
},
|
1380
|
-
meta: {
|
1381
|
-
docs: {
|
1382
|
-
category: "HTML",
|
1383
|
-
description: "Prevent duplicate polyfills from Polyfill.io.",
|
1384
|
-
recommended: true,
|
1385
|
-
url: url20
|
1386
|
-
},
|
1387
|
-
schema: [],
|
1388
|
-
type: "problem"
|
1389
|
-
}
|
1390
|
-
});
|
1391
|
-
|
1392
|
-
// src/index.ts
|
1393
|
-
var name = "nextjs";
|
1394
|
-
var plugin = {
|
1395
|
-
name,
|
1396
|
-
rules: {
|
1397
|
-
[`${name}/google-font-display`]: googleFontDisplay,
|
1398
|
-
[`${name}/google-font-preconnect`]: googleFontPreconnect,
|
1399
|
-
[`${name}/inline-script-id`]: inlineScriptId,
|
1400
|
-
[`${name}/next-script-for-ga`]: nextScriptForGa,
|
1401
|
-
[`${name}/no-assign-module-variable`]: noAssignModuleVariable,
|
1402
|
-
[`${name}/no-async-client-component`]: noAsyncClientComponent,
|
1403
|
-
[`${name}/no-before-interactive-script-outside-document`]: noBeforeInteractiveScriptOutsideDocument,
|
1404
|
-
[`${name}/no-css-tags`]: noCssTags,
|
1405
|
-
[`${name}/no-document-import-in-page`]: noDocumentImportInPage,
|
1406
|
-
[`${name}/no-duplicate-head`]: noDuplicateHead,
|
1407
|
-
[`${name}/no-head-element`]: noHeadElement,
|
1408
|
-
[`${name}/no-head-import-in-document`]: noHeadImportInDocument,
|
1409
|
-
[`${name}/no-html-link-for-pages`]: noHtmlLinkForPages,
|
1410
|
-
[`${name}/no-img-element`]: noImgElement,
|
1411
|
-
[`${name}/no-page-custom-font`]: noPageCustomFont,
|
1412
|
-
[`${name}/no-script-component-in-head`]: noScriptComponentInHead,
|
1413
|
-
[`${name}/no-styled-jsx-in-document`]: noStyledJsxInDocument,
|
1414
|
-
[`${name}/no-sync-scripts`]: noSyncScripts,
|
1415
|
-
[`${name}/no-title-in-document-head`]: noTitleInDocumentHead,
|
1416
|
-
[`${name}/no-typos`]: noTypos,
|
1417
|
-
[`${name}/no-unwanted-polyfillio`]: noUnwantedPolyfillio
|
1418
|
-
}
|
1419
|
-
};
|
1420
|
-
var recommendedRules = {
|
1421
|
-
// warnings
|
1422
|
-
"nextjs/google-font-display": "warn",
|
1423
|
-
"nextjs/google-font-preconnect": "warn",
|
1424
|
-
// errors
|
1425
|
-
"nextjs/inline-script-id": "error",
|
1426
|
-
"nextjs/next-script-for-ga": "warn",
|
1427
|
-
"nextjs/no-assign-module-variable": "error",
|
1428
|
-
"nextjs/no-async-client-component": "warn",
|
1429
|
-
"nextjs/no-before-interactive-script-outside-document": "warn",
|
1430
|
-
"nextjs/no-css-tags": "warn",
|
1431
|
-
"nextjs/no-document-import-in-page": "error",
|
1432
|
-
"nextjs/no-duplicate-head": "error",
|
1433
|
-
"nextjs/no-head-element": "warn",
|
1434
|
-
"nextjs/no-head-import-in-document": "error",
|
1435
|
-
"nextjs/no-html-link-for-pages": "warn",
|
1436
|
-
"nextjs/no-img-element": "warn",
|
1437
|
-
"nextjs/no-page-custom-font": "warn",
|
1438
|
-
"nextjs/no-script-component-in-head": "error",
|
1439
|
-
"nextjs/no-styled-jsx-in-document": "warn",
|
1440
|
-
"nextjs/no-sync-scripts": "warn",
|
1441
|
-
"nextjs/no-title-in-document-head": "warn",
|
1442
|
-
"nextjs/no-typos": "warn",
|
1443
|
-
"nextjs/no-unwanted-polyfillio": "warn"
|
1444
|
-
};
|
1445
|
-
var coreWebVitalsRules = {
|
1446
|
-
...recommendedRules,
|
1447
|
-
"nextjs/no-html-link-for-pages": "error",
|
1448
|
-
"nextjs/no-sync-scripts": "error"
|
1449
|
-
};
|
1450
|
-
var createRuleConfig = (pluginName, rules2, isFlat = false) => {
|
1451
|
-
return {
|
1452
|
-
plugins: isFlat ? { [pluginName]: plugin } : [pluginName],
|
1453
|
-
rules: rules2
|
1454
|
-
};
|
1455
|
-
};
|
1456
|
-
var recommendedFlatConfig = createRuleConfig(name, recommendedRules, true);
|
1457
|
-
var recommendedLegacyConfig = createRuleConfig(name, recommendedRules, false);
|
1458
|
-
var coreWebVitalsFlatConfig = createRuleConfig(
|
1459
|
-
name,
|
1460
|
-
coreWebVitalsRules,
|
1461
|
-
true
|
1462
|
-
);
|
1463
|
-
var coreWebVitalsLegacyConfig = createRuleConfig(
|
1464
|
-
name,
|
1465
|
-
coreWebVitalsRules,
|
1466
|
-
false
|
1467
|
-
);
|
1468
|
-
var index_default = {
|
1469
|
-
...plugin,
|
1470
|
-
configs: {
|
1471
|
-
/**
|
1472
|
-
* Legacy config (ESLint < v9) with Core Web Vitals rules (recommended with some warnings upgrade to errors)
|
1473
|
-
*/
|
1474
|
-
"core-web-vitals": coreWebVitalsLegacyConfig,
|
1475
|
-
/**
|
1476
|
-
* Flat config (ESLint v9+) with Core Web Vitals rules (recommended with some warnings upgrade to errors)
|
1477
|
-
*/
|
1478
|
-
"core-web-vitals/flat": coreWebVitalsFlatConfig,
|
1479
|
-
/**
|
1480
|
-
* Legacy config (ESLint < v9) with recommended rules
|
1481
|
-
*/
|
1482
|
-
recommended: recommendedLegacyConfig,
|
1483
|
-
/**
|
1484
|
-
* Flat config (ESLint v9+) with recommended rules
|
1485
|
-
*/
|
1486
|
-
"recommended/flat": recommendedFlatConfig
|
1487
|
-
}
|
1488
|
-
};
|
1489
|
-
var rules = plugin.rules;
|
1490
|
-
// Annotate the CommonJS export names for ESM import in node:
|
1491
|
-
0 && (module.exports = {
|
1492
|
-
rules
|
1493
|
-
});
|
1494
|
-
//# sourceMappingURL=index.cjs.map
|