diff --git a/baseline-export-bench.txt b/baseline-export-bench.txt new file mode 100644 index 00000000..938246b4 --- /dev/null +++ b/baseline-export-bench.txt @@ -0,0 +1,37 @@ +ÔùÅ Validation Warning: + + Unknown option "reporters" with value ["default"] was found. + This is probably a typing mistake. Fixing it will remove this message. + + Configuration Documentation: + https://jestjs.io/docs/configuration + + console.log + [Export Management > Export detection] should detect exports x 56,768 ops/sec ┬▒2.09% (48553 runs sampled) + + at Object. (tools/tinybench-utils.ts:1323:17) + + console.log + [Export Management > Export addition] should add exports x 61,458 ops/sec ┬▒1.81% (54844 runs sampled) + + at Object. (tools/tinybench-utils.ts:1323:17) + + console.log + [Export Management > Export removal] should remove exports x 56,077 ops/sec ┬▒1.82% (50026 runs sampled) + + at Object. (tools/tinybench-utils.ts:1323:17) + +PASS benchmarks packages/workspace/src/generators/move-file/benchmarks/export-management.bench.ts + Export Management + Export detection + ÔêÜ should detect exports (25 ms) + Export addition + ÔêÜ should add exports (1 ms) + Export removal + ÔêÜ should remove exports + +Test Suites: 1 passed, 1 total +Tests: 3 passed, 3 total +Snapshots: 0 total +Time: 4.658 s, estimated 5 s +Ran all test suites matching /export-management/i. diff --git a/packages/workspace/src/generators/move-file/export-management/index-exports-cache.ts b/packages/workspace/src/generators/move-file/export-management/index-exports-cache.ts new file mode 100644 index 00000000..b62b1f47 --- /dev/null +++ b/packages/workspace/src/generators/move-file/export-management/index-exports-cache.ts @@ -0,0 +1,62 @@ +import type { Tree } from '@nx/devkit'; +import { treeReadCache } from '../tree-cache'; + +/** + * Cached export information for index (entrypoint) files to avoid reparsing. + */ +export interface IndexExports { + exports: Set; // direct exports (file paths without extension) + reexports: Set; // re-exported modules (file paths without extension) +} + +interface CachedIndexExports extends IndexExports { + content: string; // content snapshot used to build this cache entry +} + +// Internal cache keyed by normalized index file path +const indexExportsCache = new Map(); + +/** Clears all cached index export data. */ +export function clearIndexExportsCache(): void { + indexExportsCache.clear(); +} + +/** Invalidates a single index file from the cache (e.g., after write). */ +export function invalidateIndexExportsCacheEntry(indexPath: string): void { + indexExportsCache.delete(indexPath); +} + +/** + * Get (and cache) export info for an index/entrypoint file. + * Lightweight regex based extraction – sufficient for current export patterns. + */ +export function getIndexExports(tree: Tree, indexPath: string): IndexExports { + const content = treeReadCache.read(tree, indexPath, 'utf-8') || ''; + + const cached = indexExportsCache.get(indexPath); + if (cached && cached.content === content) return cached; + + const exports = new Set(); // local exports (currently none parsed) + const reexports = new Set(); // export ... from / export * from specifiers + + // Match: export * from './path'; OR export { ... } from './path'; OR export {default as X} from './path'; + const reExportPattern = + /export\s+(?:\*|\{[^}]+\})\s+from\s+['"](\.\.?\/[^'";]+)['"];?/g; + // Match: export * from './path'; specially capture star exports for potential future distinction + // Simple capture group for path without extension processing here + + let match: RegExpExecArray | null; + while ((match = reExportPattern.exec(content))) { + const spec = match[1]; + reexports.add(spec); + } + + // Note: We do NOT add reexports to the exports set to avoid conflating concepts. + // exports holds normalized local export specifiers (if/when we add direct export collection logic). + // For current patterns (only re-exports), exports remains empty; callers may consult reexports directly. + // Future enhancement: parse local declarations (e.g. export { foo, bar }; without 'from'). + + const result: CachedIndexExports = { exports, reexports, content }; + indexExportsCache.set(indexPath, result); + return result; +} diff --git a/packages/workspace/src/generators/move-file/export-management/is-file-exported.ts b/packages/workspace/src/generators/move-file/export-management/is-file-exported.ts index 5d842ca6..7c911da9 100644 --- a/packages/workspace/src/generators/move-file/export-management/is-file-exported.ts +++ b/packages/workspace/src/generators/move-file/export-management/is-file-exported.ts @@ -2,8 +2,9 @@ import type { Tree } from '@nx/devkit'; import type { ProjectConfiguration } from '@nx/devkit'; import { getProjectEntryPointPaths } from '../project-analysis/get-project-entry-point-paths'; import { removeSourceFileExtension } from '../path-utils/remove-source-file-extension'; -import { escapeRegex } from '../security-utils/escape-regex'; + import { treeReadCache } from '../tree-cache'; +import { getIndexExports } from './index-exports-cache'; /** * Checks if a file is exported from the project's entrypoint. @@ -31,7 +32,6 @@ export function isFileExported( const indexPaths = getProjectEntryPointPaths(tree, project); const fileWithoutExt = removeSourceFileExtension(file); - const escapedFile = escapeRegex(fileWithoutExt); return indexPaths.some((indexPath) => { if (!cachedTreeExists(tree, indexPath)) { @@ -41,12 +41,10 @@ export function isFileExported( if (!content) { return false; } - // Support: export ... from "path" - // Support: export * from "path" - // Support: export { Something } from "path" - const exportPattern = new RegExp( - `export\\s+(?:\\*|\\{[^}]+\\}|.+)\\s+from\\s+['"]\\.?\\.?/.*${escapedFile}['"]`, - ); - return exportPattern.test(content); + // Use cached export analysis for index file + const indexExports = getIndexExports(tree, indexPath); + // Compare against file path without extension (as stored) + // Since local exports are not yet collected, rely on reexports for detection. + return indexExports.reexports.has(`./${fileWithoutExt}`); }); } diff --git a/updated-export-bench.txt b/updated-export-bench.txt new file mode 100644 index 00000000..d3a43755 --- /dev/null +++ b/updated-export-bench.txt @@ -0,0 +1,37 @@ +ÔùÅ Validation Warning: + + Unknown option "reporters" with value ["default"] was found. + This is probably a typing mistake. Fixing it will remove this message. + + Configuration Documentation: + https://jestjs.io/docs/configuration + + console.log + [Export Management > Export detection] should detect exports x 62,156 ops/sec ┬▒1.66% (55877 runs sampled) + + at Object. (tools/tinybench-utils.ts:1323:17) + + console.log + [Export Management > Export addition] should add exports x 61,446 ops/sec ┬▒1.30% (54407 runs sampled) + + at Object. (tools/tinybench-utils.ts:1323:17) + + console.log + [Export Management > Export removal] should remove exports x 62,213 ops/sec ┬▒4.02% (56818 runs sampled) + + at Object. (tools/tinybench-utils.ts:1323:17) + +PASS benchmarks packages/workspace/src/generators/move-file/benchmarks/export-management.bench.ts + Export Management + Export detection + ÔêÜ should detect exports (13 ms) + Export addition + ÔêÜ should add exports (1 ms) + Export removal + ÔêÜ should remove exports (1 ms) + +Test Suites: 1 passed, 1 total +Tests: 3 passed, 3 total +Snapshots: 0 total +Time: 4.375 s, estimated 5 s +Ran all test suites matching /export-management/i.