eslint-plugin-next-edge-boundary 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 +110 -0
- package/dist/configs/recommended.d.ts +3 -0
- package/dist/configs/recommended.d.ts.map +1 -0
- package/dist/configs/recommended.js +13 -0
- package/dist/configs/recommended.js.map +1 -0
- package/dist/configs/strict.d.ts +3 -0
- package/dist/configs/strict.d.ts.map +1 -0
- package/dist/configs/strict.js +13 -0
- package/dist/configs/strict.js.map +1 -0
- package/dist/core/classify.d.ts +14 -0
- package/dist/core/classify.d.ts.map +1 -0
- package/dist/core/classify.js +59 -0
- package/dist/core/classify.js.map +1 -0
- package/dist/core/constants.d.ts +10 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +92 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/entrypoints.d.ts +2 -0
- package/dist/core/entrypoints.d.ts.map +1 -0
- package/dist/core/entrypoints.js +20 -0
- package/dist/core/entrypoints.js.map +1 -0
- package/dist/core/format-chain.d.ts +5 -0
- package/dist/core/format-chain.d.ts.map +1 -0
- package/dist/core/format-chain.js +35 -0
- package/dist/core/format-chain.js.map +1 -0
- package/dist/core/import-graph.d.ts +35 -0
- package/dist/core/import-graph.d.ts.map +1 -0
- package/dist/core/import-graph.js +169 -0
- package/dist/core/import-graph.js.map +1 -0
- package/dist/core/import-kind.d.ts +10 -0
- package/dist/core/import-kind.d.ts.map +1 -0
- package/dist/core/import-kind.js +19 -0
- package/dist/core/import-kind.js.map +1 -0
- package/dist/core/options.d.ts +26 -0
- package/dist/core/options.d.ts.map +1 -0
- package/dist/core/options.js +116 -0
- package/dist/core/options.js.map +1 -0
- package/dist/core/resolve-module.d.ts +24 -0
- package/dist/core/resolve-module.d.ts.map +1 -0
- package/dist/core/resolve-module.js +232 -0
- package/dist/core/resolve-module.js.map +1 -0
- package/dist/core/size-estimate.d.ts +17 -0
- package/dist/core/size-estimate.d.ts.map +1 -0
- package/dist/core/size-estimate.js +48 -0
- package/dist/core/size-estimate.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/max-graph-bytes.d.ts +3 -0
- package/dist/rules/max-graph-bytes.d.ts.map +1 -0
- package/dist/rules/max-graph-bytes.js +123 -0
- package/dist/rules/max-graph-bytes.js.map +1 -0
- package/dist/rules/no-barrels.d.ts +3 -0
- package/dist/rules/no-barrels.d.ts.map +1 -0
- package/dist/rules/no-barrels.js +93 -0
- package/dist/rules/no-barrels.js.map +1 -0
- package/dist/rules/no-node-apis.d.ts +3 -0
- package/dist/rules/no-node-apis.d.ts.map +1 -0
- package/dist/rules/no-node-apis.js +118 -0
- package/dist/rules/no-node-apis.js.map +1 -0
- package/dist/rules/types.d.ts +30 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +2 -0
- package/dist/rules/types.js.map +1 -0
- package/docs/hero.png +0 -0
- package/package.json +63 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GraphNode } from "./import-graph.js";
|
|
2
|
+
export type SizeContributor = {
|
|
3
|
+
id: string;
|
|
4
|
+
bytes: number;
|
|
5
|
+
};
|
|
6
|
+
export type SizeEstimate = {
|
|
7
|
+
totalBytes: number;
|
|
8
|
+
contributors: SizeContributor[];
|
|
9
|
+
};
|
|
10
|
+
export declare function estimateFileBytes(filePath: string): number;
|
|
11
|
+
export declare function estimateGraphBytes(nodes: GraphNode[], options?: {
|
|
12
|
+
includeNodeModules?: boolean;
|
|
13
|
+
packageWeights?: Record<string, number>;
|
|
14
|
+
ignorePatterns?: string[];
|
|
15
|
+
cwd?: string;
|
|
16
|
+
}): SizeEstimate;
|
|
17
|
+
//# sourceMappingURL=size-estimate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"size-estimate.d.ts","sourceRoot":"","sources":["../../src/core/size-estimate.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,eAAe,EAAE,CAAC;CACjC,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,EAAE,EAClB,OAAO,GAAE;IACP,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,YAAY,CAsCd"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEFAULT_PACKAGE_WEIGHTS, } from "./constants.js";
|
|
4
|
+
import { matchesIgnorePattern } from "./options.js";
|
|
5
|
+
export function estimateFileBytes(filePath) {
|
|
6
|
+
try {
|
|
7
|
+
return fs.statSync(filePath).size;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function estimateGraphBytes(nodes, options = {}) {
|
|
14
|
+
const weights = {
|
|
15
|
+
...DEFAULT_PACKAGE_WEIGHTS,
|
|
16
|
+
...options.packageWeights,
|
|
17
|
+
};
|
|
18
|
+
const contributors = [];
|
|
19
|
+
let totalBytes = 0;
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
for (const node of nodes) {
|
|
22
|
+
if (seen.has(node.id)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
seen.add(node.id);
|
|
26
|
+
if (node.kind === "file") {
|
|
27
|
+
if (matchesIgnorePattern(node.id, options.ignorePatterns)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (!options.includeNodeModules && node.id.includes(`${path.sep}node_modules${path.sep}`)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const bytes = estimateFileBytes(node.id);
|
|
34
|
+
totalBytes += bytes;
|
|
35
|
+
contributors.push({ id: node.id, bytes });
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (node.kind === "package" && options.includeNodeModules) {
|
|
39
|
+
const packageName = node.packageName ?? node.id;
|
|
40
|
+
const bytes = weights[packageName] ?? 5_000;
|
|
41
|
+
totalBytes += bytes;
|
|
42
|
+
contributors.push({ id: packageName, bytes });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
contributors.sort((a, b) => b.bytes - a.bytes);
|
|
46
|
+
return { totalBytes, contributors };
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=size-estimate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"size-estimate.js","sourceRoot":"","sources":["../../src/core/size-estimate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAapD,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAAkB,EAClB,UAKI,EAAE;IAEN,MAAM,OAAO,GAAG;QACd,GAAG,uBAAuB;QAC1B,GAAG,OAAO,CAAC,cAAc;KAC1B,CAAC;IACF,MAAM,YAAY,GAAsB,EAAE,CAAC;IAC3C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YACtB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAElB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACzB,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC1D,SAAS;YACX,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,kBAAkB,IAAI,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,eAAe,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;gBAC1F,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzC,UAAU,IAAI,KAAK,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1C,SAAS;QACX,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;YAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;YAC5C,UAAU,IAAI,KAAK,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AACtC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ESLint } from "eslint";
|
|
2
|
+
declare const rules: {
|
|
3
|
+
"no-node-apis": import("./rules/types.js").RuleModule;
|
|
4
|
+
"max-graph-bytes": import("./rules/types.js").RuleModule;
|
|
5
|
+
"no-barrels": import("./rules/types.js").RuleModule;
|
|
6
|
+
};
|
|
7
|
+
declare const plugin: {
|
|
8
|
+
meta: {
|
|
9
|
+
name: string;
|
|
10
|
+
version: string;
|
|
11
|
+
};
|
|
12
|
+
rules: ESLint.Plugin["rules"];
|
|
13
|
+
configs: NonNullable<ESLint.Plugin["configs"]>;
|
|
14
|
+
};
|
|
15
|
+
export default plugin;
|
|
16
|
+
export { rules };
|
|
17
|
+
export type { SharedOptions, MaxGraphBytesOptions, NoBarrelsOptions, } from "./core/options.js";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAQ7C,QAAA,MAAM,KAAK;;;;CAIV,CAAC;AAEF,QAAA,MAAM,MAAM;;;;;WAKM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;aACvB,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;CAC7B,CAAC;AAsB1B,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,KAAK,EAAE,CAAC;AACjB,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PACKAGE_NAME } from "./core/constants.js";
|
|
2
|
+
import { recommendedConfig } from "./configs/recommended.js";
|
|
3
|
+
import { strictConfig } from "./configs/strict.js";
|
|
4
|
+
import { maxGraphBytesRule } from "./rules/max-graph-bytes.js";
|
|
5
|
+
import { noBarrelsRule } from "./rules/no-barrels.js";
|
|
6
|
+
import { noNodeApisRule } from "./rules/no-node-apis.js";
|
|
7
|
+
const rules = {
|
|
8
|
+
"no-node-apis": noNodeApisRule,
|
|
9
|
+
"max-graph-bytes": maxGraphBytesRule,
|
|
10
|
+
"no-barrels": noBarrelsRule,
|
|
11
|
+
};
|
|
12
|
+
const plugin = {
|
|
13
|
+
meta: {
|
|
14
|
+
name: PACKAGE_NAME,
|
|
15
|
+
version: "0.1.0",
|
|
16
|
+
},
|
|
17
|
+
rules: rules,
|
|
18
|
+
configs: {},
|
|
19
|
+
};
|
|
20
|
+
function withPlugin(config) {
|
|
21
|
+
return {
|
|
22
|
+
...config,
|
|
23
|
+
plugins: {
|
|
24
|
+
"next-edge-boundary": plugin,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
plugin.configs.recommended = withPlugin(recommendedConfig);
|
|
29
|
+
plugin.configs.strict = withPlugin(strictConfig);
|
|
30
|
+
plugin.configs["recommended-legacy"] = {
|
|
31
|
+
plugins: ["next-edge-boundary"],
|
|
32
|
+
rules: recommendedConfig.rules,
|
|
33
|
+
};
|
|
34
|
+
plugin.configs["strict-legacy"] = {
|
|
35
|
+
plugins: ["next-edge-boundary"],
|
|
36
|
+
rules: strictConfig.rules,
|
|
37
|
+
};
|
|
38
|
+
export default plugin;
|
|
39
|
+
export { rules };
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,KAAK,GAAG;IACZ,cAAc,EAAE,cAAc;IAC9B,iBAAiB,EAAE,iBAAiB;IACpC,YAAY,EAAE,aAAa;CAC5B,CAAC;AAEF,MAAM,MAAM,GAAG;IACb,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,OAAO;KACjB;IACD,KAAK,EAAE,KAA+B;IACtC,OAAO,EAAE,EAA2C;CAC7B,CAAC;AAE1B,SAAS,UAAU,CAAC,MAAqB;IACvC,OAAO;QACL,GAAG,MAAM;QACT,OAAO,EAAE;YACP,oBAAoB,EAAE,MAAM;SAC7B;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;AAC3D,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;AACjD,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG;IACrC,OAAO,EAAE,CAAC,oBAAoB,CAAC;IAC/B,KAAK,EAAE,iBAAiB,CAAC,KAAK;CAC/B,CAAC;AACF,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG;IAChC,OAAO,EAAE,CAAC,oBAAoB,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC,KAAK;CAC1B,CAAC;AAEF,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-graph-bytes.d.ts","sourceRoot":"","sources":["../../src/rules/max-graph-bytes.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1D,eAAO,MAAM,iBAAiB,EAAE,UA8H/B,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { DEFAULT_MAX_BYTES_RECOMMENDED } from "../core/constants.js";
|
|
2
|
+
import { isEdgeEntrypoint } from "../core/entrypoints.js";
|
|
3
|
+
import { formatBytes, formatChain } from "../core/format-chain.js";
|
|
4
|
+
import { walkImportGraph } from "../core/import-graph.js";
|
|
5
|
+
import { parseMaxGraphBytesOptions, } from "../core/options.js";
|
|
6
|
+
import { estimateGraphBytes } from "../core/size-estimate.js";
|
|
7
|
+
export const maxGraphBytesRule = {
|
|
8
|
+
meta: {
|
|
9
|
+
type: "problem",
|
|
10
|
+
docs: {
|
|
11
|
+
description: "Limit the estimated local import-graph size reachable from Edge entries.",
|
|
12
|
+
},
|
|
13
|
+
schema: [
|
|
14
|
+
{
|
|
15
|
+
type: "object",
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
properties: {
|
|
18
|
+
max: { type: "number" },
|
|
19
|
+
includeNodeModules: { type: "boolean" },
|
|
20
|
+
packageWeights: {
|
|
21
|
+
type: "object",
|
|
22
|
+
additionalProperties: { type: "number" },
|
|
23
|
+
},
|
|
24
|
+
allowModules: { type: "array", items: { type: "string" } },
|
|
25
|
+
denyModules: { type: "array", items: { type: "string" } },
|
|
26
|
+
ignorePatterns: { type: "array", items: { type: "string" } },
|
|
27
|
+
tsconfigPath: { type: "string" },
|
|
28
|
+
allowImportChains: {
|
|
29
|
+
type: "array",
|
|
30
|
+
items: {
|
|
31
|
+
type: "array",
|
|
32
|
+
items: { type: "string" },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
messages: {
|
|
39
|
+
tooLarge: "Edge import graph is ~{{total}} (budget {{budget}}).\n\nHeaviest contributors:\n{{contributors}}\n\nImport a thinner Edge slice from the entry file.",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
create(context) {
|
|
43
|
+
const filename = context.filename ?? context.getFilename();
|
|
44
|
+
if (!isEdgeEntrypoint(filename)) {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
let options;
|
|
48
|
+
try {
|
|
49
|
+
options = parseMaxGraphBytesOptions(context.options[0]);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
53
|
+
}
|
|
54
|
+
const max = options.max ?? DEFAULT_MAX_BYTES_RECOMMENDED;
|
|
55
|
+
const entryImports = [];
|
|
56
|
+
let programNode = null;
|
|
57
|
+
return {
|
|
58
|
+
Program(node) {
|
|
59
|
+
programNode = node;
|
|
60
|
+
},
|
|
61
|
+
ImportDeclaration(node) {
|
|
62
|
+
if (node.importKind === "type") {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (typeof node.source.value !== "string") {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
entryImports.push({ specifier: node.source.value, node });
|
|
69
|
+
},
|
|
70
|
+
ExportNamedDeclaration(node) {
|
|
71
|
+
if (node.exportKind === "type" || !node.source) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (typeof node.source.value !== "string") {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
entryImports.push({ specifier: node.source.value, node });
|
|
78
|
+
},
|
|
79
|
+
ExportAllDeclaration(node) {
|
|
80
|
+
if (node.exportKind === "type") {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (typeof node.source.value !== "string") {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
entryImports.push({ specifier: node.source.value, node });
|
|
87
|
+
},
|
|
88
|
+
"Program:exit"() {
|
|
89
|
+
const walk = walkImportGraph(filename, options);
|
|
90
|
+
const estimate = estimateGraphBytes(walk.nodes, {
|
|
91
|
+
includeNodeModules: options.includeNodeModules,
|
|
92
|
+
packageWeights: options.packageWeights,
|
|
93
|
+
ignorePatterns: options.ignorePatterns,
|
|
94
|
+
});
|
|
95
|
+
if (estimate.totalBytes <= max) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const top = estimate.contributors.slice(0, 5);
|
|
99
|
+
const contributors = top
|
|
100
|
+
.map((item) => {
|
|
101
|
+
const label = formatChain([item.id]).trim();
|
|
102
|
+
const size = formatBytes(item.bytes).padStart(5, " ");
|
|
103
|
+
return ` +${size} ${label}`;
|
|
104
|
+
})
|
|
105
|
+
.join("\n");
|
|
106
|
+
const reportNode = entryImports[0]?.node ?? programNode;
|
|
107
|
+
if (!reportNode) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
context.report({
|
|
111
|
+
node: reportNode,
|
|
112
|
+
messageId: "tooLarge",
|
|
113
|
+
data: {
|
|
114
|
+
total: formatBytes(estimate.totalBytes),
|
|
115
|
+
budget: formatBytes(max),
|
|
116
|
+
contributors,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=max-graph-bytes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-graph-bytes.js","sourceRoot":"","sources":["../../src/rules/max-graph-bytes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-barrels.d.ts","sourceRoot":"","sources":["../../src/rules/no-barrels.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,YAAY,CAAC;AAgB1D,eAAO,MAAM,aAAa,EAAE,UAwF3B,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { DEFAULT_BARREL_PATTERNS } from "../core/constants.js";
|
|
2
|
+
import { isEdgeEntrypoint } from "../core/entrypoints.js";
|
|
3
|
+
import { matchesAllowOrDeny, parseNoBarrelsOptions, } from "../core/options.js";
|
|
4
|
+
function matchesBarrelPattern(specifier, patterns) {
|
|
5
|
+
const normalized = specifier.replace(/\/index$/, "");
|
|
6
|
+
return patterns.some((pattern) => {
|
|
7
|
+
const p = pattern.replace(/\/index$/, "");
|
|
8
|
+
return (specifier === pattern ||
|
|
9
|
+
normalized === p ||
|
|
10
|
+
specifier === `${p}/index` ||
|
|
11
|
+
specifier.endsWith(`/${pattern}`) ||
|
|
12
|
+
specifier.endsWith(`/${p}`));
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export const noBarrelsRule = {
|
|
16
|
+
meta: {
|
|
17
|
+
type: "problem",
|
|
18
|
+
docs: {
|
|
19
|
+
description: "Disallow barrel and heavy namespace imports directly from Edge entries.",
|
|
20
|
+
},
|
|
21
|
+
schema: [
|
|
22
|
+
{
|
|
23
|
+
type: "object",
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
properties: {
|
|
26
|
+
barrelPatterns: { type: "array", items: { type: "string" } },
|
|
27
|
+
forbidNamespaceImports: { type: "boolean" },
|
|
28
|
+
allowModules: { type: "array", items: { type: "string" } },
|
|
29
|
+
denyModules: { type: "array", items: { type: "string" } },
|
|
30
|
+
ignorePatterns: { type: "array", items: { type: "string" } },
|
|
31
|
+
tsconfigPath: { type: "string" },
|
|
32
|
+
allowImportChains: {
|
|
33
|
+
type: "array",
|
|
34
|
+
items: {
|
|
35
|
+
type: "array",
|
|
36
|
+
items: { type: "string" },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
messages: {
|
|
43
|
+
barrel: "Edge entry imports barrel module \"{{specifier}}\". Import a specific Edge-safe module instead.",
|
|
44
|
+
namespace: "Edge entry uses namespace import from \"{{specifier}}\". Prefer named imports of a thin Edge slice.",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
create(context) {
|
|
48
|
+
const filename = context.filename ?? context.getFilename();
|
|
49
|
+
if (!isEdgeEntrypoint(filename)) {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
let options;
|
|
53
|
+
try {
|
|
54
|
+
options = parseNoBarrelsOptions(context.options[0]);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
58
|
+
}
|
|
59
|
+
const patterns = options.barrelPatterns ?? [...DEFAULT_BARREL_PATTERNS];
|
|
60
|
+
const forbidNamespace = options.forbidNamespaceImports ?? true;
|
|
61
|
+
return {
|
|
62
|
+
ImportDeclaration(node) {
|
|
63
|
+
if (node.importKind === "type") {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (typeof node.source.value !== "string") {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const specifier = node.source.value;
|
|
70
|
+
if (matchesAllowOrDeny(specifier, options.allowModules)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (matchesBarrelPattern(specifier, patterns)) {
|
|
74
|
+
context.report({
|
|
75
|
+
node,
|
|
76
|
+
messageId: "barrel",
|
|
77
|
+
data: { specifier },
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (forbidNamespace &&
|
|
82
|
+
node.specifiers.some((spec) => spec.type === "ImportNamespaceSpecifier")) {
|
|
83
|
+
context.report({
|
|
84
|
+
node,
|
|
85
|
+
messageId: "namespace",
|
|
86
|
+
data: { specifier },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=no-barrels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-barrels.js","sourceRoot":"","sources":["../../src/rules/no-barrels.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-node-apis.d.ts","sourceRoot":"","sources":["../../src/rules/no-node-apis.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,YAAY,CAAC;AAoB1D,eAAO,MAAM,cAAc,EAAE,UA0G5B,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { isEdgeEntrypoint } from "../core/entrypoints.js";
|
|
2
|
+
import { findDeniedPaths, walkImportGraph, } from "../core/import-graph.js";
|
|
3
|
+
import { formatChain } from "../core/format-chain.js";
|
|
4
|
+
import { matchesAllowOrDeny, parseSharedOptions, } from "../core/options.js";
|
|
5
|
+
function chainAllowed(chain, allowImportChains) {
|
|
6
|
+
if (!allowImportChains || allowImportChains.length === 0) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
return allowImportChains.some((allowed) => {
|
|
10
|
+
if (allowed.length !== chain.length) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return allowed.every((part, index) => {
|
|
14
|
+
const actual = chain[index] ?? "";
|
|
15
|
+
return actual === part || actual.endsWith(part) || actual.includes(part);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export const noNodeApisRule = {
|
|
20
|
+
meta: {
|
|
21
|
+
type: "problem",
|
|
22
|
+
docs: {
|
|
23
|
+
description: "Disallow Node builtins and Edge-hostile packages reachable from Edge entry imports.",
|
|
24
|
+
},
|
|
25
|
+
schema: [
|
|
26
|
+
{
|
|
27
|
+
type: "object",
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
properties: {
|
|
30
|
+
allowModules: { type: "array", items: { type: "string" } },
|
|
31
|
+
denyModules: { type: "array", items: { type: "string" } },
|
|
32
|
+
ignorePatterns: { type: "array", items: { type: "string" } },
|
|
33
|
+
tsconfigPath: { type: "string" },
|
|
34
|
+
allowImportChains: {
|
|
35
|
+
type: "array",
|
|
36
|
+
items: {
|
|
37
|
+
type: "array",
|
|
38
|
+
items: { type: "string" },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
messages: {
|
|
45
|
+
forbidden: "Edge boundary violated: Node / forbidden module \"{{moduleId}}\" is reachable.\n\n{{chain}}\n\nSplit an Edge-safe module that only exports what middleware needs.",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
create(context) {
|
|
49
|
+
const filename = context.filename ?? context.getFilename();
|
|
50
|
+
if (!isEdgeEntrypoint(filename)) {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
let options;
|
|
54
|
+
try {
|
|
55
|
+
options = parseSharedOptions(context.options[0]);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
59
|
+
}
|
|
60
|
+
const entryImports = [];
|
|
61
|
+
return {
|
|
62
|
+
ImportDeclaration(node) {
|
|
63
|
+
if (node.importKind === "type") {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (typeof node.source.value !== "string") {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
entryImports.push({ specifier: node.source.value, node });
|
|
70
|
+
},
|
|
71
|
+
ExportNamedDeclaration(node) {
|
|
72
|
+
if (node.exportKind === "type" || !node.source) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (typeof node.source.value !== "string") {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
entryImports.push({ specifier: node.source.value, node });
|
|
79
|
+
},
|
|
80
|
+
ExportAllDeclaration(node) {
|
|
81
|
+
if (node.exportKind === "type") {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (typeof node.source.value !== "string") {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
entryImports.push({ specifier: node.source.value, node });
|
|
88
|
+
},
|
|
89
|
+
"Program:exit"() {
|
|
90
|
+
for (const entryImport of entryImports) {
|
|
91
|
+
if (matchesAllowOrDeny(entryImport.specifier, options.allowModules)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const walk = walkImportGraph(filename, {
|
|
95
|
+
...options,
|
|
96
|
+
seedSpecifiers: [entryImport.specifier],
|
|
97
|
+
});
|
|
98
|
+
const denied = findDeniedPaths(walk);
|
|
99
|
+
for (const hit of denied) {
|
|
100
|
+
if (chainAllowed(hit.chain, options.allowImportChains)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
context.report({
|
|
104
|
+
node: entryImport.node,
|
|
105
|
+
messageId: "forbidden",
|
|
106
|
+
data: {
|
|
107
|
+
moduleId: hit.moduleId,
|
|
108
|
+
chain: formatChain(hit.chain),
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=no-node-apis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-node-apis.js","sourceRoot":"","sources":["../../src/rules/no-node-apis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { TSESLint, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
export type RuleListener = TSESLint.RuleListener & {
|
|
3
|
+
ImportDeclaration?(node: TSESTree.ImportDeclaration): void;
|
|
4
|
+
ExportNamedDeclaration?(node: TSESTree.ExportNamedDeclaration): void;
|
|
5
|
+
ExportAllDeclaration?(node: TSESTree.ExportAllDeclaration): void;
|
|
6
|
+
Program?(node: TSESTree.Program): void;
|
|
7
|
+
"Program:exit"?(): void;
|
|
8
|
+
};
|
|
9
|
+
export type RuleContext = {
|
|
10
|
+
filename?: string;
|
|
11
|
+
getFilename(): string;
|
|
12
|
+
options: readonly unknown[];
|
|
13
|
+
report(descriptor: {
|
|
14
|
+
node: TSESTree.Node;
|
|
15
|
+
messageId: string;
|
|
16
|
+
data?: Record<string, string>;
|
|
17
|
+
}): void;
|
|
18
|
+
};
|
|
19
|
+
export type RuleModule = {
|
|
20
|
+
meta: {
|
|
21
|
+
type: "problem" | "suggestion" | "layout";
|
|
22
|
+
docs: {
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
schema: unknown[];
|
|
26
|
+
messages: Record<string, string>;
|
|
27
|
+
};
|
|
28
|
+
create(context: RuleContext): RuleListener;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEnE,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,GAAG;IACjD,iBAAiB,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAC3D,sBAAsB,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,sBAAsB,GAAG,IAAI,CAAC;IACrE,oBAAoB,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,oBAAoB,GAAG,IAAI,CAAC;IACjE,OAAO,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;IACvC,cAAc,CAAC,IAAI,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,IAAI,MAAM,CAAC;IACtB,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IAC5B,MAAM,CAAC,UAAU,EAAE;QACjB,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,GAAG,IAAI,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;QAC1C,IAAI,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;QAC9B,MAAM,EAAE,OAAO,EAAE,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC;IACF,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,CAAC;CAC5C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":""}
|
package/docs/hero.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eslint-plugin-next-edge-boundary",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lint-time import-graph guardrails for Next.js Edge entries — transitive Node denial, byte budgets, and barrel bans.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"docs",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -p tsconfig.build.json",
|
|
27
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
28
|
+
"test": "pnpm build && vitest run",
|
|
29
|
+
"lint": "eslint ."
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"eslint": ">=9",
|
|
33
|
+
"typescript": ">=5"
|
|
34
|
+
},
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"typescript": {
|
|
37
|
+
"optional": true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.13.10",
|
|
42
|
+
"@typescript-eslint/parser": "^8.26.1",
|
|
43
|
+
"@typescript-eslint/utils": "^8.26.1",
|
|
44
|
+
"eslint": "^9.22.0",
|
|
45
|
+
"typescript": "^5.8.2",
|
|
46
|
+
"typescript-eslint": "^8.26.1",
|
|
47
|
+
"vitest": "^3.0.8"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"eslint",
|
|
51
|
+
"eslint-plugin",
|
|
52
|
+
"nextjs",
|
|
53
|
+
"edge",
|
|
54
|
+
"middleware",
|
|
55
|
+
"proxy"
|
|
56
|
+
],
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/asyncfinkd/eslint-plugin-next-edge-boundary.git"
|
|
60
|
+
},
|
|
61
|
+
"author": "asyncfinkd",
|
|
62
|
+
"sideEffects": false
|
|
63
|
+
}
|