@aiready/context-analyzer 0.7.19 → 0.8.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/.turbo/turbo-build.log +11 -9
- package/.turbo/turbo-test.log +10 -10
- package/README.md +37 -0
- package/dist/chunk-JZ2SE4DB.mjs +1116 -0
- package/dist/chunk-RQCIJO5Z.mjs +1116 -0
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/cli.js +264 -17
- package/dist/cli.mjs +2 -1
- package/dist/index.js +259 -6
- package/dist/index.mjs +2 -1
- package/dist/python-context-UOPTQH44.mjs +192 -0
- package/package.json +2 -2
- package/src/analyzers/python-context.ts +323 -0
- package/src/index.ts +55 -5
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
9
16
|
var __copyProps = (to, from, except, desc) => {
|
|
10
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,11 +30,207 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
30
|
mod
|
|
24
31
|
));
|
|
25
32
|
|
|
33
|
+
// src/analyzers/python-context.ts
|
|
34
|
+
var python_context_exports = {};
|
|
35
|
+
__export(python_context_exports, {
|
|
36
|
+
analyzePythonContext: () => analyzePythonContext
|
|
37
|
+
});
|
|
38
|
+
async function analyzePythonContext(files, rootDir) {
|
|
39
|
+
const results = [];
|
|
40
|
+
const parser = (0, import_core2.getParser)("dummy.py");
|
|
41
|
+
if (!parser) {
|
|
42
|
+
console.warn("Python parser not available");
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
|
|
46
|
+
const dependencyGraph = await buildPythonDependencyGraph(pythonFiles, rootDir);
|
|
47
|
+
for (const file of pythonFiles) {
|
|
48
|
+
try {
|
|
49
|
+
const fs = await import("fs");
|
|
50
|
+
const code = await fs.promises.readFile(file, "utf-8");
|
|
51
|
+
const result = parser.parse(code, file);
|
|
52
|
+
const imports = result.imports.map((imp) => ({
|
|
53
|
+
source: imp.source,
|
|
54
|
+
specifiers: imp.specifiers,
|
|
55
|
+
isRelative: imp.source.startsWith("."),
|
|
56
|
+
resolvedPath: resolvePythonImport(file, imp.source, rootDir)
|
|
57
|
+
}));
|
|
58
|
+
const exports2 = result.exports.map((exp) => ({
|
|
59
|
+
name: exp.name,
|
|
60
|
+
type: exp.type
|
|
61
|
+
}));
|
|
62
|
+
const linesOfCode = code.split("\n").length;
|
|
63
|
+
const importDepth = await calculatePythonImportDepth(file, dependencyGraph, /* @__PURE__ */ new Set());
|
|
64
|
+
const contextBudget = estimateContextBudget(code, imports, dependencyGraph);
|
|
65
|
+
const cohesion = calculatePythonCohesion(exports2, imports);
|
|
66
|
+
const circularDependencies = detectCircularDependencies2(file, dependencyGraph);
|
|
67
|
+
results.push({
|
|
68
|
+
file,
|
|
69
|
+
importDepth,
|
|
70
|
+
contextBudget,
|
|
71
|
+
cohesion,
|
|
72
|
+
imports,
|
|
73
|
+
exports: exports2,
|
|
74
|
+
metrics: {
|
|
75
|
+
linesOfCode,
|
|
76
|
+
importCount: imports.length,
|
|
77
|
+
exportCount: exports2.length,
|
|
78
|
+
circularDependencies
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn(`Failed to analyze ${file}:`, error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
async function buildPythonDependencyGraph(files, rootDir) {
|
|
88
|
+
const graph = /* @__PURE__ */ new Map();
|
|
89
|
+
const parser = (0, import_core2.getParser)("dummy.py");
|
|
90
|
+
if (!parser) return graph;
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
try {
|
|
93
|
+
const fs = await import("fs");
|
|
94
|
+
const code = await fs.promises.readFile(file, "utf-8");
|
|
95
|
+
const result = parser.parse(code, file);
|
|
96
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
97
|
+
for (const imp of result.imports) {
|
|
98
|
+
const resolved = resolvePythonImport(file, imp.source, rootDir);
|
|
99
|
+
if (resolved && files.includes(resolved)) {
|
|
100
|
+
dependencies.add(resolved);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
graph.set(file, dependencies);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return graph;
|
|
108
|
+
}
|
|
109
|
+
function resolvePythonImport(fromFile, importPath, rootDir) {
|
|
110
|
+
const dir = (0, import_path.dirname)(fromFile);
|
|
111
|
+
if (importPath.startsWith(".")) {
|
|
112
|
+
const parts = importPath.split(".");
|
|
113
|
+
let upCount = 0;
|
|
114
|
+
while (parts[0] === "") {
|
|
115
|
+
upCount++;
|
|
116
|
+
parts.shift();
|
|
117
|
+
}
|
|
118
|
+
let targetDir = dir;
|
|
119
|
+
for (let i = 0; i < upCount - 1; i++) {
|
|
120
|
+
targetDir = (0, import_path.dirname)(targetDir);
|
|
121
|
+
}
|
|
122
|
+
const modulePath = parts.join("/");
|
|
123
|
+
const possiblePaths = [
|
|
124
|
+
(0, import_path.resolve)(targetDir, `${modulePath}.py`),
|
|
125
|
+
(0, import_path.resolve)(targetDir, modulePath, "__init__.py")
|
|
126
|
+
];
|
|
127
|
+
const fs = require("fs");
|
|
128
|
+
for (const path of possiblePaths) {
|
|
129
|
+
if (fs.existsSync(path)) {
|
|
130
|
+
return path;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
const modulePath = importPath.replace(/\./g, "/");
|
|
135
|
+
const possiblePaths = [
|
|
136
|
+
(0, import_path.resolve)(rootDir, `${modulePath}.py`),
|
|
137
|
+
(0, import_path.resolve)(rootDir, modulePath, "__init__.py")
|
|
138
|
+
];
|
|
139
|
+
const fs = require("fs");
|
|
140
|
+
for (const path of possiblePaths) {
|
|
141
|
+
if (fs.existsSync(path)) {
|
|
142
|
+
return path;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
async function calculatePythonImportDepth(file, dependencyGraph, visited, depth = 0) {
|
|
149
|
+
if (visited.has(file)) {
|
|
150
|
+
return depth;
|
|
151
|
+
}
|
|
152
|
+
visited.add(file);
|
|
153
|
+
const dependencies = dependencyGraph.get(file) || /* @__PURE__ */ new Set();
|
|
154
|
+
if (dependencies.size === 0) {
|
|
155
|
+
return depth;
|
|
156
|
+
}
|
|
157
|
+
let maxDepth = depth;
|
|
158
|
+
for (const dep of dependencies) {
|
|
159
|
+
const depDepth = await calculatePythonImportDepth(
|
|
160
|
+
dep,
|
|
161
|
+
dependencyGraph,
|
|
162
|
+
new Set(visited),
|
|
163
|
+
depth + 1
|
|
164
|
+
);
|
|
165
|
+
maxDepth = Math.max(maxDepth, depDepth);
|
|
166
|
+
}
|
|
167
|
+
return maxDepth;
|
|
168
|
+
}
|
|
169
|
+
function estimateContextBudget(code, imports, dependencyGraph) {
|
|
170
|
+
let budget = (0, import_core2.estimateTokens)(code);
|
|
171
|
+
const avgTokensPerDep = 500;
|
|
172
|
+
budget += imports.length * avgTokensPerDep;
|
|
173
|
+
return budget;
|
|
174
|
+
}
|
|
175
|
+
function calculatePythonCohesion(exports2, imports) {
|
|
176
|
+
if (exports2.length === 0) return 1;
|
|
177
|
+
const exportCount = exports2.length;
|
|
178
|
+
const importCount = imports.length;
|
|
179
|
+
let cohesion = 1;
|
|
180
|
+
if (exportCount > 10) {
|
|
181
|
+
cohesion *= 0.6;
|
|
182
|
+
} else if (exportCount > 5) {
|
|
183
|
+
cohesion *= 0.8;
|
|
184
|
+
}
|
|
185
|
+
if (exportCount > 0) {
|
|
186
|
+
const ratio = importCount / exportCount;
|
|
187
|
+
if (ratio > 2) {
|
|
188
|
+
cohesion *= 1.1;
|
|
189
|
+
} else if (ratio < 0.5) {
|
|
190
|
+
cohesion *= 0.9;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return Math.min(1, Math.max(0, cohesion));
|
|
194
|
+
}
|
|
195
|
+
function detectCircularDependencies2(file, dependencyGraph) {
|
|
196
|
+
const circular = [];
|
|
197
|
+
const visited = /* @__PURE__ */ new Set();
|
|
198
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
199
|
+
function dfs(current, path) {
|
|
200
|
+
if (recursionStack.has(current)) {
|
|
201
|
+
const cycleStart = path.indexOf(current);
|
|
202
|
+
const cycle = path.slice(cycleStart).concat([current]);
|
|
203
|
+
circular.push(cycle.join(" \u2192 "));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (visited.has(current)) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
visited.add(current);
|
|
210
|
+
recursionStack.add(current);
|
|
211
|
+
const dependencies = dependencyGraph.get(current) || /* @__PURE__ */ new Set();
|
|
212
|
+
for (const dep of dependencies) {
|
|
213
|
+
dfs(dep, [...path, current]);
|
|
214
|
+
}
|
|
215
|
+
recursionStack.delete(current);
|
|
216
|
+
}
|
|
217
|
+
dfs(file, []);
|
|
218
|
+
return [...new Set(circular)];
|
|
219
|
+
}
|
|
220
|
+
var import_core2, import_path;
|
|
221
|
+
var init_python_context = __esm({
|
|
222
|
+
"src/analyzers/python-context.ts"() {
|
|
223
|
+
"use strict";
|
|
224
|
+
import_core2 = require("@aiready/core");
|
|
225
|
+
import_path = require("path");
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
26
229
|
// src/cli.ts
|
|
27
230
|
var import_commander = require("commander");
|
|
28
231
|
|
|
29
232
|
// src/index.ts
|
|
30
|
-
var
|
|
233
|
+
var import_core3 = require("@aiready/core");
|
|
31
234
|
|
|
32
235
|
// src/analyzer.ts
|
|
33
236
|
var import_core = require("@aiready/core");
|
|
@@ -598,20 +801,63 @@ async function analyzeContext(options) {
|
|
|
598
801
|
includeNodeModules = false,
|
|
599
802
|
...scanOptions
|
|
600
803
|
} = options;
|
|
601
|
-
const files = await (0,
|
|
804
|
+
const files = await (0, import_core3.scanFiles)({
|
|
602
805
|
...scanOptions,
|
|
603
806
|
// Only add node_modules to exclude if includeNodeModules is false
|
|
604
807
|
// The DEFAULT_EXCLUDE already includes node_modules, so this is only needed
|
|
605
808
|
// if user overrides the default exclude list
|
|
606
809
|
exclude: includeNodeModules && scanOptions.exclude ? scanOptions.exclude.filter((pattern) => pattern !== "**/node_modules/**") : scanOptions.exclude
|
|
607
810
|
});
|
|
811
|
+
const pythonFiles = files.filter((f) => f.toLowerCase().endsWith(".py"));
|
|
812
|
+
const tsJsFiles = files.filter((f) => !f.toLowerCase().endsWith(".py"));
|
|
608
813
|
const fileContents = await Promise.all(
|
|
609
814
|
files.map(async (file) => ({
|
|
610
815
|
file,
|
|
611
|
-
content: await (0,
|
|
816
|
+
content: await (0, import_core3.readFileContent)(file)
|
|
612
817
|
}))
|
|
613
818
|
);
|
|
614
|
-
const graph = buildDependencyGraph(fileContents);
|
|
819
|
+
const graph = buildDependencyGraph(fileContents.filter((f) => !f.file.toLowerCase().endsWith(".py")));
|
|
820
|
+
let pythonResults = [];
|
|
821
|
+
if (pythonFiles.length > 0) {
|
|
822
|
+
const { analyzePythonContext: analyzePythonContext2 } = await Promise.resolve().then(() => (init_python_context(), python_context_exports));
|
|
823
|
+
const pythonMetrics = await analyzePythonContext2(pythonFiles, scanOptions.rootDir || options.rootDir || ".");
|
|
824
|
+
pythonResults = pythonMetrics.map((metric) => {
|
|
825
|
+
const { severity, issues, recommendations, potentialSavings } = analyzeIssues({
|
|
826
|
+
file: metric.file,
|
|
827
|
+
importDepth: metric.importDepth,
|
|
828
|
+
contextBudget: metric.contextBudget,
|
|
829
|
+
cohesionScore: metric.cohesion,
|
|
830
|
+
fragmentationScore: 0,
|
|
831
|
+
// Python analyzer doesn't calculate fragmentation yet
|
|
832
|
+
maxDepth,
|
|
833
|
+
maxContextBudget,
|
|
834
|
+
minCohesion,
|
|
835
|
+
maxFragmentation,
|
|
836
|
+
circularDeps: metric.metrics.circularDependencies.map((cycle) => cycle.split(" \u2192 "))
|
|
837
|
+
});
|
|
838
|
+
return {
|
|
839
|
+
file: metric.file,
|
|
840
|
+
tokenCost: Math.floor(metric.contextBudget / (1 + metric.imports.length || 1)),
|
|
841
|
+
// Estimate
|
|
842
|
+
linesOfCode: metric.metrics.linesOfCode,
|
|
843
|
+
importDepth: metric.importDepth,
|
|
844
|
+
dependencyCount: metric.imports.length,
|
|
845
|
+
dependencyList: metric.imports.map((imp) => imp.resolvedPath || imp.source),
|
|
846
|
+
circularDeps: metric.metrics.circularDependencies.map((cycle) => cycle.split(" \u2192 ")),
|
|
847
|
+
cohesionScore: metric.cohesion,
|
|
848
|
+
domains: ["python"],
|
|
849
|
+
// Generic for now
|
|
850
|
+
exportCount: metric.exports.length,
|
|
851
|
+
contextBudget: metric.contextBudget,
|
|
852
|
+
fragmentationScore: 0,
|
|
853
|
+
relatedFiles: [],
|
|
854
|
+
severity,
|
|
855
|
+
issues,
|
|
856
|
+
recommendations,
|
|
857
|
+
potentialSavings
|
|
858
|
+
};
|
|
859
|
+
});
|
|
860
|
+
}
|
|
615
861
|
const circularDeps = detectCircularDependencies(graph);
|
|
616
862
|
const clusters = detectModuleClusters(graph);
|
|
617
863
|
const fragmentationMap = /* @__PURE__ */ new Map();
|
|
@@ -671,7 +917,8 @@ async function analyzeContext(options) {
|
|
|
671
917
|
potentialSavings
|
|
672
918
|
});
|
|
673
919
|
}
|
|
674
|
-
const
|
|
920
|
+
const allResults = [...results, ...pythonResults];
|
|
921
|
+
const issuesOnly = allResults.filter((r) => r.severity !== "info");
|
|
675
922
|
const sorted = issuesOnly.sort((a, b) => {
|
|
676
923
|
const severityOrder = { critical: 0, major: 1, minor: 2, info: 3 };
|
|
677
924
|
const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
|
|
@@ -876,8 +1123,8 @@ function downgradeSeverity(s) {
|
|
|
876
1123
|
// src/cli.ts
|
|
877
1124
|
var import_chalk = __toESM(require("chalk"));
|
|
878
1125
|
var import_fs = require("fs");
|
|
879
|
-
var
|
|
880
|
-
var
|
|
1126
|
+
var import_path2 = require("path");
|
|
1127
|
+
var import_core4 = require("@aiready/core");
|
|
881
1128
|
var import_prompts = __toESM(require("prompts"));
|
|
882
1129
|
var program = new import_commander.Command();
|
|
883
1130
|
program.name("aiready-context").description("Analyze AI context window cost and code structure").version("0.1.0").addHelpText("after", "\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings").argument("<directory>", "Directory to analyze").option("--max-depth <number>", "Maximum acceptable import depth").option(
|
|
@@ -908,7 +1155,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
908
1155
|
exclude: void 0,
|
|
909
1156
|
maxResults: 10
|
|
910
1157
|
};
|
|
911
|
-
let finalOptions = await (0,
|
|
1158
|
+
let finalOptions = await (0, import_core4.loadMergedConfig)(directory, defaults, {
|
|
912
1159
|
maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
|
|
913
1160
|
maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
|
|
914
1161
|
minCohesion: options.minCohesion ? parseFloat(options.minCohesion) : void 0,
|
|
@@ -923,7 +1170,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
923
1170
|
finalOptions = await runInteractiveSetup(directory, finalOptions);
|
|
924
1171
|
}
|
|
925
1172
|
const results = await analyzeContext(finalOptions);
|
|
926
|
-
const elapsedTime = (0,
|
|
1173
|
+
const elapsedTime = (0, import_core4.getElapsedTime)(startTime);
|
|
927
1174
|
const summary = generateSummary(results);
|
|
928
1175
|
if (options.output === "json") {
|
|
929
1176
|
const jsonOutput = {
|
|
@@ -932,23 +1179,23 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
932
1179
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
933
1180
|
analysisTime: elapsedTime
|
|
934
1181
|
};
|
|
935
|
-
const outputPath = (0,
|
|
1182
|
+
const outputPath = (0, import_core4.resolveOutputPath)(
|
|
936
1183
|
options.outputFile,
|
|
937
1184
|
`context-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`,
|
|
938
1185
|
directory
|
|
939
1186
|
);
|
|
940
|
-
(0,
|
|
1187
|
+
(0, import_core4.handleJSONOutput)(jsonOutput, outputPath, `
|
|
941
1188
|
\u2713 JSON report saved to ${outputPath}`);
|
|
942
1189
|
return;
|
|
943
1190
|
}
|
|
944
1191
|
if (options.output === "html") {
|
|
945
1192
|
const html = generateHTMLReport(summary, results);
|
|
946
|
-
const outputPath = (0,
|
|
1193
|
+
const outputPath = (0, import_core4.resolveOutputPath)(
|
|
947
1194
|
options.outputFile,
|
|
948
1195
|
`context-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.html`,
|
|
949
1196
|
directory
|
|
950
1197
|
);
|
|
951
|
-
const dir = (0,
|
|
1198
|
+
const dir = (0, import_path2.dirname)(outputPath);
|
|
952
1199
|
if (!(0, import_fs.existsSync)(dir)) {
|
|
953
1200
|
(0, import_fs.mkdirSync)(dir, { recursive: true });
|
|
954
1201
|
}
|
|
@@ -960,7 +1207,7 @@ program.name("aiready-context").description("Analyze AI context window cost and
|
|
|
960
1207
|
displayConsoleReport(summary, results, elapsedTime, finalOptions.maxResults);
|
|
961
1208
|
displayTuningGuidance(results, finalOptions);
|
|
962
1209
|
} catch (error) {
|
|
963
|
-
(0,
|
|
1210
|
+
(0, import_core4.handleCLIError)(error, "Analysis");
|
|
964
1211
|
}
|
|
965
1212
|
});
|
|
966
1213
|
program.parse();
|
|
@@ -1300,7 +1547,7 @@ function generateHTMLReport(summary, results) {
|
|
|
1300
1547
|
}
|
|
1301
1548
|
async function runInteractiveSetup(directory, current) {
|
|
1302
1549
|
console.log(import_chalk.default.yellow("\u{1F9ED} Interactive mode: let\u2019s tailor the analysis."));
|
|
1303
|
-
const pkgPath = (0,
|
|
1550
|
+
const pkgPath = (0, import_path2.join)(directory, "package.json");
|
|
1304
1551
|
let deps = {};
|
|
1305
1552
|
if ((0, import_fs.existsSync)(pkgPath)) {
|
|
1306
1553
|
try {
|
|
@@ -1309,8 +1556,8 @@ async function runInteractiveSetup(directory, current) {
|
|
|
1309
1556
|
} catch {
|
|
1310
1557
|
}
|
|
1311
1558
|
}
|
|
1312
|
-
const hasNextJs = (0, import_fs.existsSync)((0,
|
|
1313
|
-
const hasCDK = (0, import_fs.existsSync)((0,
|
|
1559
|
+
const hasNextJs = (0, import_fs.existsSync)((0, import_path2.join)(directory, ".next")) || !!deps["next"];
|
|
1560
|
+
const hasCDK = (0, import_fs.existsSync)((0, import_path2.join)(directory, "cdk.out")) || !!deps["aws-cdk-lib"] || Object.keys(deps).some((d) => d.startsWith("@aws-cdk/"));
|
|
1314
1561
|
const recommendedExcludes = new Set(current.exclude || []);
|
|
1315
1562
|
if (hasNextJs && !Array.from(recommendedExcludes).some((p) => p.includes(".next"))) {
|
|
1316
1563
|
recommendedExcludes.add("**/.next/**");
|