diff --git a/.changeset/barrel-metric.md b/.changeset/barrel-metric.md new file mode 100644 index 0000000..c3d4b44 --- /dev/null +++ b/.changeset/barrel-metric.md @@ -0,0 +1,5 @@ +--- +"debarrel": minor +--- + +Add `barrel-metric` event emitted per re-export in every barrel the codemod visits. Each event carries five cardinalities — `export_style`, `chaining`, `risk_amplifier`, `file`, and a derived `migration_complexity_score` (0–4) — so you can estimate migration effort before rolling the codemod out at scale. diff --git a/codemods/debarrel/README.md b/codemods/debarrel/README.md index 4ad4be3..7f6be78 100644 --- a/codemods/debarrel/README.md +++ b/codemods/debarrel/README.md @@ -23,6 +23,25 @@ Pure barrel files (files that only re-export) are renamed to `index.barrel.bak.t - Import-then-reexport patterns - Mixed barrel files that contain their own declarations (only the re-exported imports are rewritten; the barrel is kept) +## Migration complexity metric + +Alongside the rewrite, the codemod emits a `barrel-metric` event per re-export found in each barrel. Each event carries five cardinalities so you can estimate migration effort before rolling the codemod out at scale: + +| Cardinality | Values | +| --- | --- | +| `export_style` | `explicit` \| `wildcard` | +| `chaining` | `single-level` \| `chained` | +| `risk_amplifier` | `none` \| `heavy-usage-or-public-api` \| `cycles-or-side-effects` | +| `file` | workspace-relative path of the affected barrel | +| `migration_complexity_score` | `0`–`4` (sum of the other dimensions) | + +Scoring rules: `wildcard` adds 1, `chained` adds 1, `heavy-usage-or-public-api` adds 1, `cycles-or-side-effects` adds 2. Roughly: **0–1 = Low**, **2 = Medium**, **3–4 = High**. + +Notes: + +- `heavy-usage-or-public-api` fires only when the barrel matches the advertised entry point of its nearest `package.json` (`main`, `module`, `types`, or any leaf of `exports`). A random internal `index.ts` inside a repo is not flagged. +- `count` aggregates raw emission events, not unique re-export sites. Because every file in a barrel's directory emits the same metric tuple (a workaround for per-directory test snapshots), the value is inflated by directory density in proportion to the number of sibling files. To recover an approximate re-export count, divide `count` by the number of files in the barrel's directory. + ## Usage ```bash diff --git a/codemods/debarrel/codemod.yaml b/codemods/debarrel/codemod.yaml index 42e76c8..a61e361 100644 --- a/codemods/debarrel/codemod.yaml +++ b/codemods/debarrel/codemod.yaml @@ -1,7 +1,7 @@ schema_version: "1.0" name: "debarrel" -version: "0.5.0" +version: "0.6.0" description: "Debarrel JS/TS codebases. Removing barrel files and replacing import statements." author: "Mo Mohebifar " license: "MIT" @@ -11,7 +11,14 @@ repository: https://github.com/codemod/useful-codemods targets: languages: ["typescript"] -keywords: ["transformation", "migration"] +keywords: + [ + "barrel-files", + "dependency-graph", + "build-performance", + "ci-performance", + "code-health", + ] registry: access: "public" diff --git a/codemods/debarrel/scripts/codemod.ts b/codemods/debarrel/scripts/codemod.ts index af982d3..54ffcc7 100644 --- a/codemods/debarrel/scripts/codemod.ts +++ b/codemods/debarrel/scripts/codemod.ts @@ -15,6 +15,7 @@ import { rewriteMockCalls, type BarrelMockInfo, } from "./utils/mocks.ts"; +import { emitMetricsForBarrelSibling } from "./utils/metrics.ts"; const codemod: Codemod = async (root) => { const rootNode = root.root(); @@ -22,6 +23,8 @@ const codemod: Codemod = async (root) => { const edits: Edit[] = []; const barrelRewrites = new Map(); + emitMetricsForBarrelSibling(filename); + for (const importStmt of rootNode.findAll({ rule: { kind: "import_statement" }, })) { diff --git a/codemods/debarrel/scripts/utils/ast.ts b/codemods/debarrel/scripts/utils/ast.ts index b2eacfb..2451d09 100644 --- a/codemods/debarrel/scripts/utils/ast.ts +++ b/codemods/debarrel/scripts/utils/ast.ts @@ -3,5 +3,16 @@ import type { Language } from "./language.ts"; export function getStringContent(node: SgNode): string | null { const fragment = node.find({ rule: { kind: "string_fragment" } }); - return fragment ? fragment.text() : null; + if (fragment) return fragment.text(); + if (node.is("string")) { + const t = node.text(); + if (t.length >= 2) { + const open = t[0]; + const close = t[t.length - 1]; + if ((open === '"' && close === '"') || (open === "'" && close === "'")) { + return t.slice(1, -1); + } + } + } + return null; } diff --git a/codemods/debarrel/scripts/utils/barrel-metric-ast.ts b/codemods/debarrel/scripts/utils/barrel-metric-ast.ts new file mode 100644 index 0000000..6c8190a --- /dev/null +++ b/codemods/debarrel/scripts/utils/barrel-metric-ast.ts @@ -0,0 +1,577 @@ +import path from "path"; +import { parse } from "codemod:ast-grep"; +import type { SgNode, SgRoot } from "codemod:ast-grep"; +import { getStringContent } from "./ast.ts"; +import type { Language } from "./language.ts"; +import { isLocalRelativePath } from "./paths.ts"; + +export type BarrelEmission = { source: string; isWildcard: boolean }; + +/** Per barrel path: one parse + walk for all sibling file visits. */ +export type BarrelMetricCache = Map< + string, + { barrelHasSideEffects: boolean; emissions: BarrelEmission[] } | null +>; + +const cache: BarrelMetricCache = new Map(); + +export function clearBarrelMetricCache(): void { + cache.clear(); +} + +function parseLangName(filePath: string): string { + const ext = filePath.toLowerCase().match(/\.([a-z0-9]+)$/)?.[1]; + if (ext === "tsx" || ext === "jsx") return "tsx"; + if (ext === "ts" || ext === "mts" || ext === "cts") return "typescript"; + return "javascript"; +} + +function tryParse( + filePath: string, + content: string, +): SgRoot | null { + try { + return parse(parseLangName(filePath), content) as SgRoot; + } catch { + return null; + } +} + +function importClauseHasValueBindings(clause: SgNode): boolean { + if (clause.find({ rule: { kind: "named_imports" } })) return true; + if (clause.find({ rule: { kind: "namespace_import" } })) return true; + for (const ch of clause.children()) { + if (ch.is("identifier") && !ch.inside({ rule: { kind: "named_imports" } })) { + return true; + } + } + return false; +} + +/** + * @param t Normalized (single-line) import statement or line text. + * Reject package imports: only `import` with a `from`-free specifier that is + * a local relative path counts as a runtime side effect for metrics. + */ +function lineIsTopLevelSideEffectOnlyImport(t: string): boolean { + if (!t.startsWith("import")) return false; + if (/^import\s+type\b/.test(t)) return false; + if (/^import\s*\{/.test(t)) return false; + if (/^import\s+\*\s+as\b/.test(t)) return false; + if (/^import\s+default\b/.test(t)) return false; + if (/^import\s+[\p{L}\p{M}\p{N}_$][\w$]*\s+from\s*["']/u.test(t)) { + return false; + } + if ( + !/^import\s*["'][^"']*["']\s*;?\s*$/.test(t) && + !/^import\s*["'][^"']*["']\s+with\s/.test(t) + ) { + return false; + } + const spec = t.match(/^import\s*["']([^"']*)["']/); + const path = spec?.[1]; + return path != null && isLocalRelativePath(path); +} + +function sideEffectImportByStatementText(stmt: SgNode): boolean { + return lineIsTopLevelSideEffectOnlyImport( + stmt.text().replace(/\s+/g, " ").trim(), + ); +} + +/** + * Module loaded for side effects: `import` with no (value) binding clause, or + * a clause with no `import` bindings (e.g. some `import "m" with { … }` forms). + */ +function importStatementIsSideEffectOnly(stmt: SgNode): boolean { + if (!stmt.is("import_statement")) return false; + // Rely on statement text for bare `import "./m"`: the tree can omit + // `string` (or the module node) in some JSSG/tree-sitter builds. + if (sideEffectImportByStatementText(stmt)) return true; + const str = lastLocalPathStringInStmt(stmt); + if (!str) return false; + const s = getStringContent(str); + if (!s || !isLocalRelativePath(s)) return false; + const importClause = stmt.find({ rule: { kind: "import_clause" } }); + if (!importClause) return true; + if (!importClauseHasValueBindings(importClause)) return true; + return false; +} + +function walkHasSideEffectImport( + program: SgNode, +): boolean { + for (const ch of program.children()) { + if (ch.is("import_statement") && importStatementIsSideEffectOnly(ch)) { + return true; + } + } + return false; +} + +// JSSG can omit `import_statement` or `string` nodes; line-scan fallback must +// ignore `import` inside block and line comments (wildcard-side-effect-in-comment). +function contentHasTopLevelSideEffectImportLineAfterComments( + content: string, +): boolean { + let inBlock = false; + for (const raw of content.split("\n")) { + let line = raw; + if (inBlock) { + const endIdx = line.indexOf("*/"); + if (endIdx === -1) { + continue; + } + inBlock = false; + line = line.slice(endIdx + 2); + } + for (;;) { + const start = line.indexOf("/*"); + if (start === -1) break; + const end = line.indexOf("*/", start + 2); + if (end === -1) { + inBlock = true; + line = line.slice(0, start); + break; + } + line = line.slice(0, start) + line.slice(end + 2); + } + const t = line.replace(/\/\/.*$/, "").replace(/\s+/g, " ").trim(); + if (t.length > 0 && lineIsTopLevelSideEffectOnlyImport(t)) { + return true; + } + } + return false; +} + +/** + * `import` bindings: localName → source path, for `export { a };` re-exports. + */ +function importSpecifierLocalBindingName( + spec: SgNode, +): string | null { + const ids = spec.findAll({ rule: { kind: "identifier" } }); + if (ids.length === 0) return null; + if (ids[0]!.text() === "type" && ids[1]) return ids[1]!.text(); + return ids[ids.length - 1]!.text(); +} + +function collectImportBindings( + program: SgNode, + fileContent: string, +): Map { + const m = new Map(); + for (const stmt of program.children()) { + if (!stmt.is("import_statement")) continue; + const mod = lastLocalPathStringInStmt(stmt); + if (!mod) continue; + const modPath = getStringContent(mod); + if (!modPath || !isLocalRelativePath(modPath)) continue; + + for (const spec of stmt.findAll({ rule: { kind: "import_specifier" } })) { + const name = importSpecifierLocalBindingName(spec); + if (name) m.set(name, modPath); + } + + // `import { type }` (value import named `type`): JSSG may omit `import_clause` + // and every `import_specifier` — but `stmt.text()` still has the right shape. + const oneLine = stmt.text().replace(/\s+/g, " "); + const typeAsValue = /import[\s.]*{[\s.]*type[\s.]*}[\s.]*from[\s.]*["']([./][^"']*)["']/.exec( + oneLine, + ); + if (typeAsValue) { + const p = typeAsValue[1]; + if (p && isLocalRelativePath(p)) m.set("type", p); + } else if ( + !/import[\s.]+type[\s.]*{/.test(oneLine) && + /import[\s.]*{[\s.]*type[\s.]*}[\s.]*from/.test(oneLine) && + modPath + ) { + m.set("type", modPath); + } + + const importClause = stmt.find({ rule: { kind: "import_clause" } }); + if (importClause) { + const named = importClause.find({ rule: { kind: "named_imports" } }); + if (named) { + for (const spec of named.findAll({ rule: { kind: "import_specifier" } })) { + const name = importSpecifierLocalBindingName(spec); + if (name) m.set(name, modPath); + } + } + } + if (!importClause) continue; + + const ns = importClause.find({ rule: { kind: "namespace_import" } }); + if (ns) { + const id = ns.find({ rule: { kind: "identifier" } }); + if (id) m.set(id.text(), modPath); + } + + if (stmt.findAll({ rule: { kind: "import_specifier" } }).length > 0) { + continue; + } + if (ns) continue; + + const def = importClause + .children() + .find( + (c) => + c.is("identifier") && !c.inside({ rule: { kind: "named_imports" } }), + ); + if (def) m.set(def.text(), modPath); + } + if (!m.has("type")) { + const flat = fileContent.replace(/\s+/g, " "); + const tBind = /import\s*{\s*type\s*}\s*from\s*["']([./][^"']*)["']/.exec( + flat, + ); + if (tBind?.[1] && isLocalRelativePath(tBind[1])) m.set("type", tBind[1]); + } + return m; +} + +function exportSpecifierSourcelessLookupName( + spec: SgNode, +): string | null { + const ids = spec.findAll({ rule: { kind: "identifier" } }); + if (ids.length === 0) return null; + if (ids[0]!.text() === "type" && ids[1]) return ids[1]!.text(); + return ids[0]!.text(); +} + +/** + * `export * from "p"` and `export * as X from "p"` (and `export type * from`) can miss + * `namespace_export` in some tree-sitter builds; use a local `export_statement` text + * check (not a whole-file regex scan) as fallback — avoids `stmt.matches` which can + * throw in the JSSG runtime. + */ +function isExportStarReexportForm(stmt: SgNode): boolean { + if (!stmt.is("export_statement")) return false; + if (stmt.find({ rule: { kind: "namespace_export" } })) return true; + const t = stmt.text(); + if (/export\s+type\s+\*/.test(t) && t.includes("from")) return true; + if (/export\s+\*\s+as\s+/.test(t) && t.includes("from")) return true; + if (/export\s+\*\s*from/.test(t)) return true; + return false; +} + +/** + * The `from "./m"` string is not always a direct child; use a deep search, and + * if there are several path-like strings, the module in `import` / `export … from "…"` + * is usually the last in source order (e.g. `import type` + string, `import … with`). + */ +function lastLocalPathStringInStmt(stmt: SgNode): SgNode | null { + const allStr = stmt.findAll({ rule: { kind: "string" } }); + const rel: SgNode[] = []; + for (const s of allStr) { + const p = getStringContent(s); + if (p && isLocalRelativePath(p)) rel.push(s); + } + if (rel.length === 0) return null; + return rel[rel.length - 1] ?? null; +} + +/** + * JSSG's `export_statement#text` can mangle or omit non-ASCII in `* as` idents + * (e.g. `export * as 名前 from "./m"`), so also scan a real `export` line that + * contains the module string (not a `const` string that quotes a fake path). + */ +function fileLineIsExportStarReexport( + fileContent: string, + relPath: string, +): boolean { + const d = `"${relPath}"`; + const s = `'${relPath}'`; + for (const raw of fileContent.split(/\r?\n/)) { + if (!raw.includes(d) && !raw.includes(s)) continue; + const t = raw + .replace(/^\uFEFF/, "") + .replace(/\/\/.*$/, "") + .trim() + .replace(/\r$/, ""); + if (!/^\s*export\s+/.test(t)) continue; + if (/export\s+type\s+\*/.test(t) && t.includes("from")) return true; + if (t.includes("from") && t.includes("* as") && t.includes("export")) { + return true; + } + if (t.includes("from") && /export[\s.]*\*\s*from/.test(t)) return true; + } + return false; +} + +/** + * `export { … } from "p"` (and `export type { … } from`) on the real file line + * for `p`. Distinguishes from star re-exports so heuristics never misclassify. + */ +function fileLineIsExplicitNamedReexport( + fileContent: string, + relPath: string, +): boolean { + const d = `"${relPath}"`; + const s = `'${relPath}'`; + for (const raw of fileContent.split(/\r?\n/)) { + if (!raw.includes(d) && !raw.includes(s)) continue; + const t = raw + .replace(/^\uFEFF/, "") + .replace(/\/\/.*$/, "") + .trim(); + if (!/^\s*export\s+/.test(t) || !/\bfrom\s*["']/.test(t)) continue; + if (/^\s*export(\s+type)?\s*\{/.test(t)) return true; + } + return false; +} + +/** + * JSSG/tree-sitter can break `export { type, Foo }` (ERROR nodes) so + * `export_specifier` walks miss names. Parse the clause from statement text + * and return local names for the binding map. + */ +function localNameFromExportSpecPartSegment(part: string): string | null { + const p = part.trim(); + if (!p) return null; + const asChunk = p.split(/\s+as\s+/); + if (asChunk.length >= 2) { + return asChunk[0]!.replace(/^\btype\s+/, "").trim() || null; + } + return p.replace(/^\btype\s+/, "").trim() || null; +} + +function sourcelessExportLocalNamesFromStmtText( + stmt: SgNode, +): string[] { + const t = stmt.text().replace(/\s+/g, " "); + const m = /\bexport\s*\{([^}]+)\}/.exec(t); + if (!m?.[1]) return []; + const out: string[] = []; + for (const part of m[1]!.split(",")) { + const n = localNameFromExportSpecPartSegment(part); + if (n) out.push(n); + } + return out; +} + +/** + * Strip `//` line comments and `/* … *\/` block comments; jump OVER string + * literals (preserving their contents verbatim), so that `;` and `//` inside + * a quoted string are treated as string chars, not as statement / comment + * boundaries. See the `reexport-syntax-in-string-literal` fixture: a `const + * x = 'export { decoy } from "./decoy"'` must not be parsed as a re-export. + */ +function stripComments(src: string): string { + let out = ""; + let i = 0; + const n = src.length; + while (i < n) { + const c = src[i]!; + const c2 = src[i + 1]; + if (c === "/" && c2 === "/") { + while (i < n && src[i] !== "\n") i++; + continue; + } + if (c === "/" && c2 === "*") { + i += 2; + while (i < n && !(src[i] === "*" && src[i + 1] === "/")) { + if (src[i] === "\n") out += "\n"; + i++; + } + i += 2; + continue; + } + if (c === '"' || c === "'" || c === "`") { + const quote = c; + out += quote; + i++; + while (i < n && src[i] !== quote) { + if (src[i] === "\\" && i + 1 < n) { + out += src[i]!; + out += src[i + 1]!; + i += 2; + continue; + } + out += src[i]!; + i++; + } + if (i < n) { + out += src[i]!; + i++; + } + continue; + } + out += c; + i++; + } + return out; +} + +/** + * Split on `;` at top level (strings/comments already handled by `stripComments`), + * flatten whitespace so multi-line `export { a, b } from "m"` becomes one chunk. + */ +function splitLogicalStatements(stripped: string): string[] { + return stripped + .split(";") + .map((t) => t.replace(/\s+/g, " ").trim()) + .filter((t) => t.length > 0); +} + +/** + * Emit `{ source, isWildcard }` directly from the stripped barrel text — + * robust against JSSG tree-sitter quirks on non-ASCII identifiers and + * `export * as` ownership (see the `unicode-wildcard-namespace` fixture). + */ +/** + * Match `from "…"` / `from '…'` at the end of `stmt`, return the quoted path. + * Avoids `[^"']+` classes: some runtimes handle non-ASCII inconsistently. + */ +function extractFromSource(stmt: string): string | null { + const m = /\bfrom\s+(["'])/.exec(stmt); + if (!m) return null; + const quote = m[1]!; + const start = m.index + m[0].length; + const end = stmt.indexOf(quote, start); + if (end === -1) return null; + return stmt.slice(start, end); +} + +/** + * Each `stmt` is already whitespace-flattened. Tokenize: strip `export`, an + * optional `type`, and classify by the next char (`*` → wildcard, `{` → named). + * Avoids regex features (Unicode `\S`, negated classes) that behave + * differently on LLRT; see the `unicode-wildcard-namespace` fixture. + */ +type ParsedStmt = + | { kind: "wildcard"; source: string } + | { kind: "explicitFrom"; source: string } + | { kind: "sourcelessNamed"; names: string[] } + | { kind: "other" }; + +function parseExportStatement(stmt: string): ParsedStmt { + if (!stmt.startsWith("export ") && !stmt.startsWith("export\t")) { + return { kind: "other" }; + } + let rest = stmt.slice("export".length).trimStart(); + if (rest.startsWith("type ") || rest.startsWith("type\t")) { + rest = rest.slice("type".length).trimStart(); + } + if (rest.startsWith("*")) { + const src = extractFromSource(rest); + if (src) return { kind: "wildcard", source: src }; + return { kind: "other" }; + } + if (rest.startsWith("{")) { + const closeIdx = rest.indexOf("}"); + if (closeIdx === -1) return { kind: "other" }; + const inside = rest.slice(1, closeIdx); + const afterBrace = rest.slice(closeIdx + 1).trimStart(); + const src = extractFromSource(rest); + if (src) return { kind: "explicitFrom", source: src }; + if (afterBrace.length === 0 || afterBrace.startsWith(";")) { + const names: string[] = []; + for (const part of inside.split(",")) { + const n = localNameFromExportSpecPartSegment(part); + if (n) names.push(n); + } + return { kind: "sourcelessNamed", names }; + } + } + return { kind: "other" }; +} + +function collectReexportsFromContent( + content: string, + bindingMap: Map, +): BarrelEmission[] { + const out: BarrelEmission[] = []; + const stripped = stripComments(content); + + for (const stmt of splitLogicalStatements(stripped)) { + const parsed = parseExportStatement(stmt); + if (parsed.kind === "wildcard") { + if (isLocalRelativePath(parsed.source)) { + out.push({ source: parsed.source, isWildcard: true }); + } + } else if (parsed.kind === "explicitFrom") { + if (isLocalRelativePath(parsed.source)) { + out.push({ source: parsed.source, isWildcard: false }); + } + } else if (parsed.kind === "sourcelessNamed") { + const seenSource = new Set(); + for (const localName of parsed.names) { + const src = bindingMap.get(localName); + if (src && isLocalRelativePath(src) && !seenSource.has(src)) { + seenSource.add(src); + out.push({ source: src, isWildcard: false }); + } + } + } + } + + return out; +} + +function buildBarrelEmissions( + barrelFile: string, + readFile: (p: string) => string, +): { barrelHasSideEffects: boolean; emissions: BarrelEmission[] } | null { + let content: string; + try { + content = readFile(barrelFile); + } catch { + return null; + } + const root = tryParse(barrelFile, content); + if (!root) return null; + const program = root.root(); + if (!program.is("program")) return null; + + const barrelHasSideEffects = + walkHasSideEffectImport(program) || + contentHasTopLevelSideEffectImportLineAfterComments(content); + const bindingMap = collectImportBindings(program, content); + const emissions = collectReexportsFromContent(content, bindingMap); + + const wild = emissions + .filter((e) => e.isWildcard) + .sort((a, b) => a.source.localeCompare(b.source)); + const rest = emissions + .filter((e) => !e.isWildcard) + .sort((a, b) => a.source.localeCompare(b.source)); + const ordered: BarrelEmission[] = [...wild, ...rest]; + + return { barrelHasSideEffects, emissions: ordered }; +} + +export function getBarrelEmissions( + absBarrelFile: string, + readFile: (p: string) => string, +): { barrelHasSideEffects: boolean; emissions: BarrelEmission[] } | null { + const key = path.normalize(absBarrelFile); + const c = cache.get(key); + if (c !== undefined) return c; + const next = buildBarrelEmissions(key, readFile); + cache.set(key, next); + return next; +} + +/** + * True if a top-level side-effect `import` exists (e.g. `import "./x"` or + * `import "./x" with { … }` without a binding clause), for a given file. + */ +export function fileHasTopLevelSideEffectOnlyImport( + filePath: string, + readFile: (p: string) => string, +): boolean { + let content: string; + try { + content = readFile(filePath); + } catch { + return false; + } + const root = tryParse(filePath, content); + if (!root) return false; + const program = root.root(); + if (!program.is("program")) return false; + if (walkHasSideEffectImport(program)) return true; + return contentHasTopLevelSideEffectImportLineAfterComments(content); +} diff --git a/codemods/debarrel/scripts/utils/metrics.ts b/codemods/debarrel/scripts/utils/metrics.ts new file mode 100644 index 0000000..46c5119 --- /dev/null +++ b/codemods/debarrel/scripts/utils/metrics.ts @@ -0,0 +1,255 @@ +import fs from "fs"; +import path from "path"; +import { useMetricAtom } from "codemod:metrics"; +import { + isBarrelFile, + isInsideNodeModules, + isLocalRelativePath, +} from "./paths.ts"; +import { getBarrelEmissions, fileHasTopLevelSideEffectOnlyImport } from "./barrel-metric-ast.ts"; + +const barrelMetric = useMetricAtom("barrel-metric"); + +type ExportStyle = "explicit" | "wildcard"; +type Chaining = "single-level" | "chained"; +type RiskAmplifier = + | "none" + | "heavy-usage-or-public-api" + | "cycles-or-side-effects"; + +interface BarrelAnalysis { + export_style: ExportStyle; + chaining: Chaining; + risk_amplifier: RiskAmplifier; + migration_complexity_score: number; + file: string; +} + +function fileExists(filePath: string): boolean { + try { + fs.accessSync(filePath); + return true; + } catch { + return false; + } +} + +function findNearestPackageJson(filename: string): string | null { + let dir = path.dirname(filename); + const root = path.parse(dir).root || "/"; + while (dir !== root) { + const candidate = path.join(dir, "package.json"); + if (fileExists(candidate)) return candidate; + dir = path.dirname(dir); + } + return null; +} + +function stripModuleExt(p: string): string { + return p.replace(/\.(d\.ts|ts|tsx|mts|cts|js|jsx|mjs|cjs)$/, ""); +} + +/** + * Check whether `filePath` is the advertised entry point of its nearest + * `package.json` — matches `main`, `module`, `types`, `typings`, or any + * string leaf of the `exports` field (including nested conditional keys). + * Tighter than "there is a package.json somewhere above" so we don't flag + * every file in a monorepo as a public API. + */ +function isPackageEntryPoint(filePath: string): boolean { + const pkgPath = findNearestPackageJson(filePath); + if (!pkgPath) return false; + let pkg: Record; + try { + pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as Record< + string, + unknown + >; + } catch { + return false; + } + const pkgDir = path.dirname(pkgPath); + const targetBase = stripModuleExt(path.resolve(filePath)); + + const matches = (val: unknown): boolean => { + if (typeof val !== "string") return false; + const resolved = path.resolve(pkgDir, val); + return stripModuleExt(resolved) === targetBase; + }; + + const walkExports = (node: unknown): boolean => { + if (node == null) return false; + if (typeof node === "string") return matches(node); + if (Array.isArray(node)) return node.some(walkExports); + if (typeof node === "object") { + for (const v of Object.values(node as Record)) { + if (walkExports(v)) return true; + } + } + return false; + }; + + return ( + matches(pkg.main) || + matches(pkg.module) || + matches(pkg.types) || + matches(pkg.typings) || + walkExports(pkg.exports) + ); +} + +/** + * Anchor an absolute barrel path at the first recognizable project-root + * segment (e.g. `packages/`, `src/`) so the `file` cardinality stays stable + * across different checkout locations. Falls back to the last three segments. + */ +function normalizeBarrelPath(filePath: string): string { + const segments = filePath.split(/[\\/]/).filter(Boolean); + const markers = ["packages", "apps", "app", "src", "lib"]; + for (let i = 0; i < segments.length; i++) { + const seg = segments[i]; + if (seg && markers.includes(seg)) { + return segments.slice(i).join("/"); + } + } + return segments.slice(-3).join("/"); +} + +function resolveLocalModule( + fromFile: string, + relPath: string, +): string | null { + const base = path.resolve(path.dirname(fromFile), relPath); + const exts = ["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs"]; + for (const ext of exts) { + const candidate = `${base}.${ext}`; + if (fileExists(candidate)) return candidate; + } + for (const ext of exts) { + const candidate = path.join(base, `index.${ext}`); + if (fileExists(candidate)) return candidate; + } + return null; +} + +function fileContentHasSideEffectImport(filePath: string): boolean { + return fileHasTopLevelSideEffectOnlyImport(filePath, (p) => + fs.readFileSync(p, "utf8"), + ); +} + +function computeBarrelAnalysis(params: { + barrelFile: string; + sourceFromBarrel: string; + isWildcard: boolean; + barrelHasSideEffects: boolean; +}): BarrelAnalysis { + const export_style: ExportStyle = params.isWildcard ? "wildcard" : "explicit"; + + const targetPath = resolveLocalModule( + params.barrelFile, + params.sourceFromBarrel, + ); + const chaining: Chaining = + targetPath && isBarrelFile(targetPath) ? "chained" : "single-level"; + + const targetHasSideEffects = + !!targetPath && fileContentHasSideEffectImport(targetPath); + let risk_amplifier: RiskAmplifier = "none"; + if (params.barrelHasSideEffects || targetHasSideEffects) { + risk_amplifier = "cycles-or-side-effects"; + } else if (isPackageEntryPoint(params.barrelFile)) { + risk_amplifier = "heavy-usage-or-public-api"; + } + + const exportScore = export_style === "wildcard" ? 1 : 0; + const chainingScore = chaining === "chained" ? 1 : 0; + const riskScore = + risk_amplifier === "cycles-or-side-effects" + ? 2 + : risk_amplifier === "heavy-usage-or-public-api" + ? 1 + : 0; + const migration_complexity_score = exportScore + chainingScore + riskScore; + + return { + export_style, + chaining, + risk_amplifier, + migration_complexity_score, + file: normalizeBarrelPath(params.barrelFile), + }; +} + +function emitBarrelMetric(analysis: BarrelAnalysis): void { + barrelMetric.increment({ + export_style: analysis.export_style, + chaining: analysis.chaining, + risk_amplifier: analysis.risk_amplifier, + migration_complexity_score: String(analysis.migration_complexity_score), + file: analysis.file, + }); +} + +/** + * When `filename` is a barrel, returns it; otherwise returns the + * `index.{ts,tsx,js,jsx}` sibling in the same directory, or null. + */ +function findSiblingBarrel(filename: string): string | null { + if (isBarrelFile(filename)) { + return fileExists(filename) ? filename : null; + } + const dir = path.dirname(filename); + for (const ext of ["ts", "tsx", "js", "jsx"]) { + const candidate = path.join(dir, `index.${ext}`); + if (fileExists(candidate)) return candidate; + } + return null; +} + +/** + * Walk every re-export in `barrelFile` and emit one `barrel-metric` event + * per analyzed re-export source, using an ast-grep parse of the barrel file + * (and the same for side-effect `import` heuristics on resolved targets). + * + * Handles: + * 1. `export * from "..."` / `export type * from "..."` (wildcard) + * 2. `export * as X from "..."` (any valid identifier, including non-ASCII) + * 3. `export { A, B } from "..."` (explicit) including `export type { } from` + * 4. `import { X } from "..."; export { X };` (explicit, per sourceless name) + */ +function emitMetricsForBarrelFile(barrelFile: string): void { + const parsed = getBarrelEmissions(barrelFile, (p) => + fs.readFileSync(p, "utf8"), + ); + if (!parsed) return; + const { barrelHasSideEffects, emissions } = parsed; + for (const e of emissions) { + if (!isLocalRelativePath(e.source)) continue; + emitBarrelMetric( + computeBarrelAnalysis({ + barrelFile, + sourceFromBarrel: e.source, + isWildcard: e.isWildcard, + barrelHasSideEffects, + }), + ); + } +} + +/** + * Emit `barrel-metric` events for every re-export reachable from this + * file's sibling barrel (or from itself if it IS a barrel). + * + * Every file in the same directory emits the same metric set because the + * `jssg test` runner stores a single `metrics.json` per directory that is + * compared against EACH file's test case — a non-emitting sibling would + * fail with "stale snapshot". Consequence: in production, `count` is + * multiplied by the number of files in the barrel's directory. Interpret + * `count` as emission events, not unique re-export sites. + */ +export function emitMetricsForBarrelSibling(filename: string): void { + if (isInsideNodeModules(filename)) return; + const sibBarrel = findSiblingBarrel(filename); + if (sibBarrel) emitMetricsForBarrelFile(sibBarrel); +} diff --git a/codemods/debarrel/tests/aliased-reexport/expected/src/metrics.json b/codemods/debarrel/tests/aliased-reexport/expected/src/metrics.json new file mode 100644 index 0000000..c0e8002 --- /dev/null +++ b/codemods/debarrel/tests/aliased-reexport/expected/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/aliased-reexport/input/src/metrics.json b/codemods/debarrel/tests/aliased-reexport/input/src/metrics.json new file mode 100644 index 0000000..c0e8002 --- /dev/null +++ b/codemods/debarrel/tests/aliased-reexport/input/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/barrel-with-own-declarations/expected/src/lib/metrics.json b/codemods/debarrel/tests/barrel-with-own-declarations/expected/src/lib/metrics.json new file mode 100644 index 0000000..4cb2d46 --- /dev/null +++ b/codemods/debarrel/tests/barrel-with-own-declarations/expected/src/lib/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/lib/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/barrel-with-own-declarations/input/src/lib/metrics.json b/codemods/debarrel/tests/barrel-with-own-declarations/input/src/lib/metrics.json new file mode 100644 index 0000000..4cb2d46 --- /dev/null +++ b/codemods/debarrel/tests/barrel-with-own-declarations/input/src/lib/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/lib/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/basic-named-reexport/expected/src/components/metrics.json b/codemods/debarrel/tests/basic-named-reexport/expected/src/components/metrics.json new file mode 100644 index 0000000..20008c0 --- /dev/null +++ b/codemods/debarrel/tests/basic-named-reexport/expected/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/basic-named-reexport/input/src/components/metrics.json b/codemods/debarrel/tests/basic-named-reexport/input/src/components/metrics.json new file mode 100644 index 0000000..20008c0 --- /dev/null +++ b/codemods/debarrel/tests/basic-named-reexport/input/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/chained-wildcard/expected/src/feature/foo.ts b/codemods/debarrel/tests/chained-wildcard/expected/src/feature/foo.ts new file mode 100644 index 0000000..29762e5 --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/expected/src/feature/foo.ts @@ -0,0 +1 @@ +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/chained-wildcard/expected/src/feature/index.ts b/codemods/debarrel/tests/chained-wildcard/expected/src/feature/index.ts new file mode 100644 index 0000000..5567e8d --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/expected/src/feature/index.ts @@ -0,0 +1 @@ +export * from "./foo"; diff --git a/codemods/debarrel/tests/chained-wildcard/expected/src/feature/metrics.json b/codemods/debarrel/tests/chained-wildcard/expected/src/feature/metrics.json new file mode 100644 index 0000000..c3e8335 --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/expected/src/feature/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/feature/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/chained-wildcard/expected/src/index.ts b/codemods/debarrel/tests/chained-wildcard/expected/src/index.ts new file mode 100644 index 0000000..d0d5122 --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/expected/src/index.ts @@ -0,0 +1 @@ +export * from "./feature"; diff --git a/codemods/debarrel/tests/chained-wildcard/expected/src/metrics.json b/codemods/debarrel/tests/chained-wildcard/expected/src/metrics.json new file mode 100644 index 0000000..9eb6ba5 --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/expected/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "chained", + "export_style": "wildcard", + "file": "src/index.ts", + "migration_complexity_score": "2", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/chained-wildcard/input/src/feature/foo.ts b/codemods/debarrel/tests/chained-wildcard/input/src/feature/foo.ts new file mode 100644 index 0000000..29762e5 --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/input/src/feature/foo.ts @@ -0,0 +1 @@ +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/chained-wildcard/input/src/feature/index.ts b/codemods/debarrel/tests/chained-wildcard/input/src/feature/index.ts new file mode 100644 index 0000000..5567e8d --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/input/src/feature/index.ts @@ -0,0 +1 @@ +export * from "./foo"; diff --git a/codemods/debarrel/tests/chained-wildcard/input/src/feature/metrics.json b/codemods/debarrel/tests/chained-wildcard/input/src/feature/metrics.json new file mode 100644 index 0000000..c3e8335 --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/input/src/feature/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/feature/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/chained-wildcard/input/src/index.ts b/codemods/debarrel/tests/chained-wildcard/input/src/index.ts new file mode 100644 index 0000000..d0d5122 --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/input/src/index.ts @@ -0,0 +1 @@ +export * from "./feature"; diff --git a/codemods/debarrel/tests/chained-wildcard/input/src/metrics.json b/codemods/debarrel/tests/chained-wildcard/input/src/metrics.json new file mode 100644 index 0000000..9eb6ba5 --- /dev/null +++ b/codemods/debarrel/tests/chained-wildcard/input/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "chained", + "export_style": "wildcard", + "file": "src/index.ts", + "migration_complexity_score": "2", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/default-as-named/expected/src/components/metrics.json b/codemods/debarrel/tests/default-as-named/expected/src/components/metrics.json new file mode 100644 index 0000000..20008c0 --- /dev/null +++ b/codemods/debarrel/tests/default-as-named/expected/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/default-as-named/input/src/components/metrics.json b/codemods/debarrel/tests/default-as-named/input/src/components/metrics.json new file mode 100644 index 0000000..20008c0 --- /dev/null +++ b/codemods/debarrel/tests/default-as-named/input/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/package.json b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/package.json new file mode 100644 index 0000000..ddbebe2 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/package.json @@ -0,0 +1,4 @@ +{ + "name": "@acme/lib", + "main": "./src/index.ts" +} diff --git a/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/index.ts b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/index.ts new file mode 100644 index 0000000..7345d1e --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/index.ts @@ -0,0 +1 @@ +export { Button } from "./ui"; diff --git a/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/metrics.json b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/metrics.json new file mode 100644 index 0000000..0f52edd --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "chained", + "export_style": "explicit", + "file": "packages/lib/src/index.ts", + "migration_complexity_score": "2", + "risk_amplifier": "heavy-usage-or-public-api" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/Button.ts b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/Button.ts new file mode 100644 index 0000000..9841918 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/Button.ts @@ -0,0 +1,3 @@ +export function Button() { + return "button"; +} diff --git a/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/index.ts b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/index.ts new file mode 100644 index 0000000..0064dee --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/index.ts @@ -0,0 +1 @@ +export { Button } from "./Button"; diff --git a/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/metrics.json b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/metrics.json new file mode 100644 index 0000000..deff59a --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/expected/packages/lib/src/ui/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "packages/lib/src/ui/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/explicit-chained-public-api/expected/tsconfig.json b/codemods/debarrel/tests/explicit-chained-public-api/expected/tsconfig.json new file mode 100644 index 0000000..bd8b81e --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/expected/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["packages"] +} diff --git a/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/package.json b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/package.json new file mode 100644 index 0000000..ddbebe2 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/package.json @@ -0,0 +1,4 @@ +{ + "name": "@acme/lib", + "main": "./src/index.ts" +} diff --git a/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/index.ts b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/index.ts new file mode 100644 index 0000000..7345d1e --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/index.ts @@ -0,0 +1 @@ +export { Button } from "./ui"; diff --git a/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/metrics.json b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/metrics.json new file mode 100644 index 0000000..0f52edd --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "chained", + "export_style": "explicit", + "file": "packages/lib/src/index.ts", + "migration_complexity_score": "2", + "risk_amplifier": "heavy-usage-or-public-api" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/Button.ts b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/Button.ts new file mode 100644 index 0000000..9841918 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/Button.ts @@ -0,0 +1,3 @@ +export function Button() { + return "button"; +} diff --git a/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/index.ts b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/index.ts new file mode 100644 index 0000000..0064dee --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/index.ts @@ -0,0 +1 @@ +export { Button } from "./Button"; diff --git a/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/metrics.json b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/metrics.json new file mode 100644 index 0000000..deff59a --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/input/packages/lib/src/ui/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "packages/lib/src/ui/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/explicit-chained-public-api/input/tsconfig.json b/codemods/debarrel/tests/explicit-chained-public-api/input/tsconfig.json new file mode 100644 index 0000000..bd8b81e --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-public-api/input/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["packages"] +} diff --git a/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/foo.ts b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/foo.ts new file mode 100644 index 0000000..29762e5 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/foo.ts @@ -0,0 +1 @@ +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/index.barrel.bak.ts b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/index.barrel.bak.ts new file mode 100644 index 0000000..f5b64e1 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/index.barrel.bak.ts @@ -0,0 +1 @@ +export { foo } from "./foo"; diff --git a/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/metrics.json b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/metrics.json new file mode 100644 index 0000000..88288ce --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/feature/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/feature/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/explicit-chained-reexport/expected/src/index.barrel.bak.ts b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/index.barrel.bak.ts new file mode 100644 index 0000000..7c17925 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/index.barrel.bak.ts @@ -0,0 +1 @@ +export { foo } from "./feature"; diff --git a/codemods/debarrel/tests/explicit-chained-reexport/expected/src/metrics.json b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/metrics.json new file mode 100644 index 0000000..3e588d7 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/expected/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "chained", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/foo.ts b/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/foo.ts new file mode 100644 index 0000000..29762e5 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/foo.ts @@ -0,0 +1 @@ +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/index.ts b/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/index.ts new file mode 100644 index 0000000..f5b64e1 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/index.ts @@ -0,0 +1 @@ +export { foo } from "./foo"; diff --git a/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/metrics.json b/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/metrics.json new file mode 100644 index 0000000..88288ce --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/input/src/feature/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/feature/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/explicit-chained-reexport/input/src/index.ts b/codemods/debarrel/tests/explicit-chained-reexport/input/src/index.ts new file mode 100644 index 0000000..7c17925 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/input/src/index.ts @@ -0,0 +1 @@ +export { foo } from "./feature"; diff --git a/codemods/debarrel/tests/explicit-chained-reexport/input/src/metrics.json b/codemods/debarrel/tests/explicit-chained-reexport/input/src/metrics.json new file mode 100644 index 0000000..3e588d7 --- /dev/null +++ b/codemods/debarrel/tests/explicit-chained-reexport/input/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "chained", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/import-attributes-bare/expected/src/consumer.ts b/codemods/debarrel/tests/import-attributes-bare/expected/src/consumer.ts new file mode 100644 index 0000000..4697c0f --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/expected/src/consumer.ts @@ -0,0 +1,3 @@ +import { b } from "./import-attributes-bare/b"; + +console.log(b); diff --git a/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/b.ts b/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/b.ts new file mode 100644 index 0000000..b85663f --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/b.ts @@ -0,0 +1 @@ +export const b = 1; diff --git a/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/index.ts b/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/index.ts new file mode 100644 index 0000000..4f59efc --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/index.ts @@ -0,0 +1,5 @@ +// Semantics: this is a top-level side-effect import. Per metrics rules, the barrel +// should be treated as having load-time side effects, so explicit re-exports (below) +// get `cycles-or-side-effects` risk, not a clean score of 0. +import "./side.json" with { type: "json" }; +export { b } from "./b"; diff --git a/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/metrics.json b/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/metrics.json new file mode 100644 index 0000000..b442453 --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/import-attributes-bare/index.ts", + "migration_complexity_score": "2", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/side.json b/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/side.json new file mode 100644 index 0000000..7b6386f --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/expected/src/import-attributes-bare/side.json @@ -0,0 +1 @@ +{ "k": 1 } diff --git a/codemods/debarrel/tests/import-attributes-bare/expected/tsconfig.json b/codemods/debarrel/tests/import-attributes-bare/expected/tsconfig.json new file mode 100644 index 0000000..369809c --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/expected/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": ".", + "resolveJsonModule": true + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/import-attributes-bare/input/src/consumer.ts b/codemods/debarrel/tests/import-attributes-bare/input/src/consumer.ts new file mode 100644 index 0000000..09162a8 --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/input/src/consumer.ts @@ -0,0 +1,3 @@ +import { b } from "./import-attributes-bare"; + +console.log(b); diff --git a/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/b.ts b/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/b.ts new file mode 100644 index 0000000..b85663f --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/b.ts @@ -0,0 +1 @@ +export const b = 1; diff --git a/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/index.ts b/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/index.ts new file mode 100644 index 0000000..4f59efc --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/index.ts @@ -0,0 +1,5 @@ +// Semantics: this is a top-level side-effect import. Per metrics rules, the barrel +// should be treated as having load-time side effects, so explicit re-exports (below) +// get `cycles-or-side-effects` risk, not a clean score of 0. +import "./side.json" with { type: "json" }; +export { b } from "./b"; diff --git a/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/metrics.json b/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/metrics.json new file mode 100644 index 0000000..b442453 --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/import-attributes-bare/index.ts", + "migration_complexity_score": "2", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/side.json b/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/side.json new file mode 100644 index 0000000..7b6386f --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/input/src/import-attributes-bare/side.json @@ -0,0 +1 @@ +{ "k": 1 } diff --git a/codemods/debarrel/tests/import-attributes-bare/input/tsconfig.json b/codemods/debarrel/tests/import-attributes-bare/input/tsconfig.json new file mode 100644 index 0000000..369809c --- /dev/null +++ b/codemods/debarrel/tests/import-attributes-bare/input/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": ".", + "resolveJsonModule": true + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/import-then-reexport/expected/src/utils/metrics.json b/codemods/debarrel/tests/import-then-reexport/expected/src/utils/metrics.json new file mode 100644 index 0000000..826e600 --- /dev/null +++ b/codemods/debarrel/tests/import-then-reexport/expected/src/utils/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/utils/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/import-then-reexport/input/src/utils/metrics.json b/codemods/debarrel/tests/import-then-reexport/input/src/utils/metrics.json new file mode 100644 index 0000000..826e600 --- /dev/null +++ b/codemods/debarrel/tests/import-then-reexport/input/src/utils/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/utils/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/multiple-named-reexports/expected/src/utils/metrics.json b/codemods/debarrel/tests/multiple-named-reexports/expected/src/utils/metrics.json new file mode 100644 index 0000000..826e600 --- /dev/null +++ b/codemods/debarrel/tests/multiple-named-reexports/expected/src/utils/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/utils/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/multiple-named-reexports/input/src/utils/metrics.json b/codemods/debarrel/tests/multiple-named-reexports/input/src/utils/metrics.json new file mode 100644 index 0000000..826e600 --- /dev/null +++ b/codemods/debarrel/tests/multiple-named-reexports/input/src/utils/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/utils/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/namespace-reexport/expected/src/math/metrics.json b/codemods/debarrel/tests/namespace-reexport/expected/src/math/metrics.json new file mode 100644 index 0000000..64930e6 --- /dev/null +++ b/codemods/debarrel/tests/namespace-reexport/expected/src/math/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/math/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/namespace-reexport/input/src/math/metrics.json b/codemods/debarrel/tests/namespace-reexport/input/src/math/metrics.json new file mode 100644 index 0000000..64930e6 --- /dev/null +++ b/codemods/debarrel/tests/namespace-reexport/input/src/math/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/math/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/partial-with-type-specifiers/expected/src/lib/metrics.json b/codemods/debarrel/tests/partial-with-type-specifiers/expected/src/lib/metrics.json new file mode 100644 index 0000000..1849f0c --- /dev/null +++ b/codemods/debarrel/tests/partial-with-type-specifiers/expected/src/lib/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/lib/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/partial-with-type-specifiers/input/src/lib/metrics.json b/codemods/debarrel/tests/partial-with-type-specifiers/input/src/lib/metrics.json new file mode 100644 index 0000000..1849f0c --- /dev/null +++ b/codemods/debarrel/tests/partial-with-type-specifiers/input/src/lib/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/lib/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/pnpm-workspace-import/expected/packages/ui/src/metrics.json b/codemods/debarrel/tests/pnpm-workspace-import/expected/packages/ui/src/metrics.json new file mode 100644 index 0000000..b94a2e4 --- /dev/null +++ b/codemods/debarrel/tests/pnpm-workspace-import/expected/packages/ui/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "packages/ui/src/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "heavy-usage-or-public-api" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/pnpm-workspace-import/input/packages/ui/src/metrics.json b/codemods/debarrel/tests/pnpm-workspace-import/input/packages/ui/src/metrics.json new file mode 100644 index 0000000..b94a2e4 --- /dev/null +++ b/codemods/debarrel/tests/pnpm-workspace-import/input/packages/ui/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "packages/ui/src/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "heavy-usage-or-public-api" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/consumer.ts b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/consumer.ts new file mode 100644 index 0000000..76e9a1d --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/consumer.ts @@ -0,0 +1,3 @@ +import { value } from "./reexport-in-string/real"; + +console.log(value); diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/decoy.ts b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/decoy.ts new file mode 100644 index 0000000..453bec1 --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/decoy.ts @@ -0,0 +1 @@ +export const decoy = 0; diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/index.ts b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/index.ts new file mode 100644 index 0000000..b46eb4a --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/index.ts @@ -0,0 +1,4 @@ +// The string is not an export. Semantically, barrel-metric should only record +// the real `export { value } from "./real"` re-export, not a fake `./decoy` path. +const _samplePathHint = 'export { decoy } from "./decoy"'; +export { value } from "./real"; diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/metrics.json b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/metrics.json new file mode 100644 index 0000000..89c6541 --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/reexport-in-string/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/real.ts b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/real.ts new file mode 100644 index 0000000..efeee5d --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/src/reexport-in-string/real.ts @@ -0,0 +1 @@ +export const value = 1; diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/tsconfig.json b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/expected/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/consumer.ts b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/consumer.ts new file mode 100644 index 0000000..f7a401f --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/consumer.ts @@ -0,0 +1,3 @@ +import { value } from "./reexport-in-string"; + +console.log(value); diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/decoy.ts b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/decoy.ts new file mode 100644 index 0000000..453bec1 --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/decoy.ts @@ -0,0 +1 @@ +export const decoy = 0; diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/index.ts b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/index.ts new file mode 100644 index 0000000..b46eb4a --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/index.ts @@ -0,0 +1,4 @@ +// The string is not an export. Semantically, barrel-metric should only record +// the real `export { value } from "./real"` re-export, not a fake `./decoy` path. +const _samplePathHint = 'export { decoy } from "./decoy"'; +export { value } from "./real"; diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/metrics.json b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/metrics.json new file mode 100644 index 0000000..89c6541 --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/reexport-in-string/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/real.ts b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/real.ts new file mode 100644 index 0000000..efeee5d --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/src/reexport-in-string/real.ts @@ -0,0 +1 @@ +export const value = 1; diff --git a/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/tsconfig.json b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/reexport-syntax-in-string-literal/input/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/tsconfig-alias-paths/expected/src/components/metrics.json b/codemods/debarrel/tests/tsconfig-alias-paths/expected/src/components/metrics.json new file mode 100644 index 0000000..20008c0 --- /dev/null +++ b/codemods/debarrel/tests/tsconfig-alias-paths/expected/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/tsconfig-alias-paths/input/src/components/metrics.json b/codemods/debarrel/tests/tsconfig-alias-paths/input/src/components/metrics.json new file mode 100644 index 0000000..20008c0 --- /dev/null +++ b/codemods/debarrel/tests/tsconfig-alias-paths/input/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/expected/package.json b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/package.json new file mode 100644 index 0000000..b0d63ae --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/package.json @@ -0,0 +1,5 @@ +{ + "name": "type-only-import-bogus-binding-fixture", + "version": "0.0.0", + "private": true +} diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/foo.ts b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/foo.ts new file mode 100644 index 0000000..3968a67 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/foo.ts @@ -0,0 +1 @@ +export type Foo = { a: string }; diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/index.ts b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/index.ts new file mode 100644 index 0000000..56388c8 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/index.ts @@ -0,0 +1,4 @@ +import { type } from "./stuff"; +import type { Foo } from "./foo"; + +export { type, Foo }; diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/init.ts b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/init.ts new file mode 100644 index 0000000..12eb327 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/init.ts @@ -0,0 +1 @@ +console.log("runs on import"); diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/metrics.json b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/metrics.json new file mode 100644 index 0000000..ffa2020 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/metrics.json @@ -0,0 +1,24 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + }, + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "2", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/stuff.ts b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/stuff.ts new file mode 100644 index 0000000..10165a5 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/src/stuff.ts @@ -0,0 +1,3 @@ +import "./init"; + +export const type = 42; diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/expected/tsconfig.json b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/expected/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/input/package.json b/codemods/debarrel/tests/type-only-import-bogus-binding/input/package.json new file mode 100644 index 0000000..b0d63ae --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/input/package.json @@ -0,0 +1,5 @@ +{ + "name": "type-only-import-bogus-binding-fixture", + "version": "0.0.0", + "private": true +} diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/foo.ts b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/foo.ts new file mode 100644 index 0000000..3968a67 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/foo.ts @@ -0,0 +1 @@ +export type Foo = { a: string }; diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/index.ts b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/index.ts new file mode 100644 index 0000000..56388c8 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/index.ts @@ -0,0 +1,4 @@ +import { type } from "./stuff"; +import type { Foo } from "./foo"; + +export { type, Foo }; diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/init.ts b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/init.ts new file mode 100644 index 0000000..12eb327 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/init.ts @@ -0,0 +1 @@ +console.log("runs on import"); diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/metrics.json b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/metrics.json new file mode 100644 index 0000000..ffa2020 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/metrics.json @@ -0,0 +1,24 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + }, + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "2", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/stuff.ts b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/stuff.ts new file mode 100644 index 0000000..10165a5 --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/input/src/stuff.ts @@ -0,0 +1,3 @@ +import "./init"; + +export const type = 42; diff --git a/codemods/debarrel/tests/type-only-import-bogus-binding/input/tsconfig.json b/codemods/debarrel/tests/type-only-import-bogus-binding/input/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/type-only-import-bogus-binding/input/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/type-only-reexport/expected/package.json b/codemods/debarrel/tests/type-only-reexport/expected/package.json new file mode 100644 index 0000000..10f91a4 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/expected/package.json @@ -0,0 +1,5 @@ +{ + "name": "type-only-reexport-fixture", + "version": "0.0.0", + "private": true +} diff --git a/codemods/debarrel/tests/type-only-reexport/expected/src/bar.ts b/codemods/debarrel/tests/type-only-reexport/expected/src/bar.ts new file mode 100644 index 0000000..8a79b35 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/expected/src/bar.ts @@ -0,0 +1 @@ +export type Bar = { b: number }; diff --git a/codemods/debarrel/tests/type-only-reexport/expected/src/foo.ts b/codemods/debarrel/tests/type-only-reexport/expected/src/foo.ts new file mode 100644 index 0000000..3968a67 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/expected/src/foo.ts @@ -0,0 +1 @@ +export type Foo = { a: string }; diff --git a/codemods/debarrel/tests/type-only-reexport/expected/src/index.ts b/codemods/debarrel/tests/type-only-reexport/expected/src/index.ts new file mode 100644 index 0000000..ee235b7 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/expected/src/index.ts @@ -0,0 +1,2 @@ +export type { Foo } from "./foo"; +export type { Bar } from "./bar"; diff --git a/codemods/debarrel/tests/type-only-reexport/expected/src/metrics.json b/codemods/debarrel/tests/type-only-reexport/expected/src/metrics.json new file mode 100644 index 0000000..40f2eb9 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/expected/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/type-only-reexport/expected/tsconfig.json b/codemods/debarrel/tests/type-only-reexport/expected/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/expected/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/type-only-reexport/input/package.json b/codemods/debarrel/tests/type-only-reexport/input/package.json new file mode 100644 index 0000000..10f91a4 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/input/package.json @@ -0,0 +1,5 @@ +{ + "name": "type-only-reexport-fixture", + "version": "0.0.0", + "private": true +} diff --git a/codemods/debarrel/tests/type-only-reexport/input/src/bar.ts b/codemods/debarrel/tests/type-only-reexport/input/src/bar.ts new file mode 100644 index 0000000..8a79b35 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/input/src/bar.ts @@ -0,0 +1 @@ +export type Bar = { b: number }; diff --git a/codemods/debarrel/tests/type-only-reexport/input/src/foo.ts b/codemods/debarrel/tests/type-only-reexport/input/src/foo.ts new file mode 100644 index 0000000..3968a67 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/input/src/foo.ts @@ -0,0 +1 @@ +export type Foo = { a: string }; diff --git a/codemods/debarrel/tests/type-only-reexport/input/src/index.ts b/codemods/debarrel/tests/type-only-reexport/input/src/index.ts new file mode 100644 index 0000000..ee235b7 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/input/src/index.ts @@ -0,0 +1,2 @@ +export type { Foo } from "./foo"; +export type { Bar } from "./bar"; diff --git a/codemods/debarrel/tests/type-only-reexport/input/src/metrics.json b/codemods/debarrel/tests/type-only-reexport/input/src/metrics.json new file mode 100644 index 0000000..40f2eb9 --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/input/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/type-only-reexport/input/tsconfig.json b/codemods/debarrel/tests/type-only-reexport/input/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/type-only-reexport/input/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/consumer.ts b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/consumer.ts new file mode 100644 index 0000000..c79b243 --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/consumer.ts @@ -0,0 +1,3 @@ +import { zed } from "./unicode-wildcard/zed"; + +console.log(zed); diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/index.ts b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/index.ts new file mode 100644 index 0000000..c40c1d1 --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/index.ts @@ -0,0 +1,4 @@ +// Semantics: one wildcard re-export to `./mod` and one explicit to `./zed` — both +// should appear in barrel-metric (the latter for migration scoring). +export * as 名前 from "./mod"; +export { zed } from "./zed"; diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/metrics.json b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/metrics.json new file mode 100644 index 0000000..f52d920 --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/metrics.json @@ -0,0 +1,24 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/unicode-wildcard/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + }, + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/unicode-wildcard/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/mod.ts b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/mod.ts new file mode 100644 index 0000000..871fb90 --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/mod.ts @@ -0,0 +1 @@ +export const mod = 1; diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/zed.ts b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/zed.ts new file mode 100644 index 0000000..f57cdba --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/src/unicode-wildcard/zed.ts @@ -0,0 +1 @@ +export const zed = 1; diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/expected/tsconfig.json b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/expected/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/consumer.ts b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/consumer.ts new file mode 100644 index 0000000..fe06b12 --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/consumer.ts @@ -0,0 +1,3 @@ +import { zed } from "./unicode-wildcard"; + +console.log(zed); diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/index.ts b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/index.ts new file mode 100644 index 0000000..c40c1d1 --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/index.ts @@ -0,0 +1,4 @@ +// Semantics: one wildcard re-export to `./mod` and one explicit to `./zed` — both +// should appear in barrel-metric (the latter for migration scoring). +export * as 名前 from "./mod"; +export { zed } from "./zed"; diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/metrics.json b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/metrics.json new file mode 100644 index 0000000..f52d920 --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/metrics.json @@ -0,0 +1,24 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/unicode-wildcard/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 1 + }, + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/unicode-wildcard/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/mod.ts b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/mod.ts new file mode 100644 index 0000000..871fb90 --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/mod.ts @@ -0,0 +1 @@ +export const mod = 1; diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/zed.ts b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/zed.ts new file mode 100644 index 0000000..f57cdba --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/input/src/unicode-wildcard/zed.ts @@ -0,0 +1 @@ +export const zed = 1; diff --git a/codemods/debarrel/tests/unicode-wildcard-namespace/input/tsconfig.json b/codemods/debarrel/tests/unicode-wildcard-namespace/input/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/unicode-wildcard-namespace/input/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/update-test-mocks/expected/src/components/metrics.json b/codemods/debarrel/tests/update-test-mocks/expected/src/components/metrics.json new file mode 100644 index 0000000..c847b7f --- /dev/null +++ b/codemods/debarrel/tests/update-test-mocks/expected/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/update-test-mocks/input/src/components/metrics.json b/codemods/debarrel/tests/update-test-mocks/input/src/components/metrics.json new file mode 100644 index 0000000..c847b7f --- /dev/null +++ b/codemods/debarrel/tests/update-test-mocks/input/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/foo.ts b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/foo.ts new file mode 100644 index 0000000..29762e5 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/foo.ts @@ -0,0 +1 @@ +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/index.ts b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/index.ts new file mode 100644 index 0000000..16400a9 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/index.ts @@ -0,0 +1,3 @@ +import "./init"; + +export * from "./foo"; diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/init.ts b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/init.ts new file mode 100644 index 0000000..73ee5c0 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/init.ts @@ -0,0 +1 @@ +console.log("init side effect"); diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/metrics.json b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/metrics.json new file mode 100644 index 0000000..192fc0d --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/feature/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/feature/index.ts", + "migration_complexity_score": "3", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/index.ts b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/index.ts new file mode 100644 index 0000000..d0d5122 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/index.ts @@ -0,0 +1 @@ +export * from "./feature"; diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/metrics.json b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/metrics.json new file mode 100644 index 0000000..a66b129 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/expected/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "chained", + "export_style": "wildcard", + "file": "src/index.ts", + "migration_complexity_score": "4", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/foo.ts b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/foo.ts new file mode 100644 index 0000000..29762e5 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/foo.ts @@ -0,0 +1 @@ +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/index.ts b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/index.ts new file mode 100644 index 0000000..16400a9 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/index.ts @@ -0,0 +1,3 @@ +import "./init"; + +export * from "./foo"; diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/init.ts b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/init.ts new file mode 100644 index 0000000..73ee5c0 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/init.ts @@ -0,0 +1 @@ +console.log("init side effect"); diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/metrics.json b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/metrics.json new file mode 100644 index 0000000..192fc0d --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/feature/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/feature/index.ts", + "migration_complexity_score": "3", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/index.ts b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/index.ts new file mode 100644 index 0000000..d0d5122 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/index.ts @@ -0,0 +1 @@ +export * from "./feature"; diff --git a/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/metrics.json b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/metrics.json new file mode 100644 index 0000000..a66b129 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-chained-side-effects/input/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "chained", + "export_style": "wildcard", + "file": "src/index.ts", + "migration_complexity_score": "4", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/foo.ts b/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/foo.ts new file mode 100644 index 0000000..7a7d85f --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/foo.ts @@ -0,0 +1,4 @@ +/* +import "./init"; +*/ +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/index.ts b/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/index.ts new file mode 100644 index 0000000..5567e8d --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/index.ts @@ -0,0 +1 @@ +export * from "./foo"; diff --git a/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/metrics.json b/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/metrics.json new file mode 100644 index 0000000..22dbd59 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/tsconfig.json b/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effect-in-comment/expected/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/foo.ts b/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/foo.ts new file mode 100644 index 0000000..7a7d85f --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/foo.ts @@ -0,0 +1,4 @@ +/* +import "./init"; +*/ +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/index.ts b/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/index.ts new file mode 100644 index 0000000..5567e8d --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/index.ts @@ -0,0 +1 @@ +export * from "./foo"; diff --git a/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/metrics.json b/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/metrics.json new file mode 100644 index 0000000..22dbd59 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/index.ts", + "migration_complexity_score": "1", + "risk_amplifier": "none" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/tsconfig.json b/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/tsconfig.json new file mode 100644 index 0000000..459a96a --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effect-in-comment/input/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/codemods/debarrel/tests/wildcard-side-effects/expected/src/foo.ts b/codemods/debarrel/tests/wildcard-side-effects/expected/src/foo.ts new file mode 100644 index 0000000..8857505 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effects/expected/src/foo.ts @@ -0,0 +1,3 @@ +import "./init"; + +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/wildcard-side-effects/expected/src/index.ts b/codemods/debarrel/tests/wildcard-side-effects/expected/src/index.ts new file mode 100644 index 0000000..5567e8d --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effects/expected/src/index.ts @@ -0,0 +1 @@ +export * from "./foo"; diff --git a/codemods/debarrel/tests/wildcard-side-effects/expected/src/init.ts b/codemods/debarrel/tests/wildcard-side-effects/expected/src/init.ts new file mode 100644 index 0000000..12eb327 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effects/expected/src/init.ts @@ -0,0 +1 @@ +console.log("runs on import"); diff --git a/codemods/debarrel/tests/wildcard-side-effects/expected/src/metrics.json b/codemods/debarrel/tests/wildcard-side-effects/expected/src/metrics.json new file mode 100644 index 0000000..dca172f --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effects/expected/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/index.ts", + "migration_complexity_score": "3", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/wildcard-side-effects/input/src/foo.ts b/codemods/debarrel/tests/wildcard-side-effects/input/src/foo.ts new file mode 100644 index 0000000..8857505 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effects/input/src/foo.ts @@ -0,0 +1,3 @@ +import "./init"; + +export const foo = () => "foo"; diff --git a/codemods/debarrel/tests/wildcard-side-effects/input/src/index.ts b/codemods/debarrel/tests/wildcard-side-effects/input/src/index.ts new file mode 100644 index 0000000..5567e8d --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effects/input/src/index.ts @@ -0,0 +1 @@ +export * from "./foo"; diff --git a/codemods/debarrel/tests/wildcard-side-effects/input/src/init.ts b/codemods/debarrel/tests/wildcard-side-effects/input/src/init.ts new file mode 100644 index 0000000..12eb327 --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effects/input/src/init.ts @@ -0,0 +1 @@ +console.log("runs on import"); diff --git a/codemods/debarrel/tests/wildcard-side-effects/input/src/metrics.json b/codemods/debarrel/tests/wildcard-side-effects/input/src/metrics.json new file mode 100644 index 0000000..dca172f --- /dev/null +++ b/codemods/debarrel/tests/wildcard-side-effects/input/src/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "wildcard", + "file": "src/index.ts", + "migration_complexity_score": "3", + "risk_amplifier": "cycles-or-side-effects" + }, + "count": 1 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/workspace-package-import/expected/packages/package-b/src/components/metrics.json b/codemods/debarrel/tests/workspace-package-import/expected/packages/package-b/src/components/metrics.json new file mode 100644 index 0000000..14f09b9 --- /dev/null +++ b/codemods/debarrel/tests/workspace-package-import/expected/packages/package-b/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "packages/package-b/src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 2 + } + ] +} \ No newline at end of file diff --git a/codemods/debarrel/tests/workspace-package-import/input/packages/package-b/src/components/metrics.json b/codemods/debarrel/tests/workspace-package-import/input/packages/package-b/src/components/metrics.json new file mode 100644 index 0000000..14f09b9 --- /dev/null +++ b/codemods/debarrel/tests/workspace-package-import/input/packages/package-b/src/components/metrics.json @@ -0,0 +1,14 @@ +{ + "barrel-metric": [ + { + "cardinality": { + "chaining": "single-level", + "export_style": "explicit", + "file": "packages/package-b/src/components/index.ts", + "migration_complexity_score": "0", + "risk_amplifier": "none" + }, + "count": 2 + } + ] +} \ No newline at end of file