Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"tailwindcss": "npm:tailwindcss@^4.1.10",
"postcss": "npm:postcss@8.5.6",

"ts-morph": "npm:ts-morph@^26.0.0",
"ts-morph": "npm:ts-morph@^27.0.2",

"@std/front-matter": "jsr:@std/front-matter@^1.0.5",
"github-slugger": "npm:github-slugger@^2.0.0",
Expand Down
16 changes: 8 additions & 8 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 39 additions & 17 deletions packages/update/src/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as JSONC from "@std/jsonc";
import * as tsmorph from "ts-morph";
import * as colors from "@std/fmt/colors";
import { ProgressBar } from "@std/cli/unstable-progress-bar";
import { walk } from "@std/fs/walk";

export const SyntaxKind = tsmorph.ts.SyntaxKind;

Expand All @@ -12,6 +13,10 @@ export const PREACT_SIGNALS_VERSION = "2.5.0";

// Function to filter out node_modules and vendor directories from logs
const HIDE_FILES = /[\\/]+(node_modules|vendor)[\\/]+/;
// Directories we should completely skip when walking the tree. This keeps us
// from loading vendored dependencies (often thousands of files) into ts-morph.
const SKIP_DIRS =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between HIDE_FILES and SKIP_DIRS? Aren't both used for the same thing? Let's unify that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, done.

/(?:[\\/]\.[^\\/]+(?:[\\/]|$)|[\\/](node_modules|vendor|docs|\.git|\.next|\.turbo|_fresh|dist|build|target|\.cache)(?:[\\/]|$))/;
export interface DenoJson {
lock?: boolean;
tasks?: Record<string, string>;
Expand Down Expand Up @@ -170,17 +175,28 @@ export async function updateProject(dir: string) {

// Update routes folder
const project = new tsmorph.Project();
const sfs = project.addSourceFilesAtPaths(
path.join(dir, "**", "*.{js,jsx,ts,tsx}"),
);

// Filter out node_modules and vendor files for user display
const userFiles = sfs.filter((sf) => !HIDE_FILES.test(sf.getFilePath()));
// First pass: collect file paths so we can size the progress bar and avoid
// walking the tree twice.
const filesToProcess: string[] = [];
let userFileCount = 0;
for await (
const entry of walk(dir, {
includeDirs: false,
includeFiles: true,
exts: ["js", "jsx", "ts", "tsx"],
skip: [SKIP_DIRS],
})
) {
filesToProcess.push(entry.path);
if (!HIDE_FILES.test(entry.path)) userFileCount++;
}
const totalFiles = filesToProcess.length;

// deno-lint-ignore no-console
console.log(colors.cyan(`📁 Found ${userFiles.length} files to process`));
console.log(colors.cyan(`📁 Found ${userFileCount} files to process`));

if (sfs.length === 0) {
if (totalFiles === 0) {
// deno-lint-ignore no-console
console.log(colors.green("🎉 Migration completed successfully!"));
return;
Expand All @@ -195,30 +211,34 @@ export async function updateProject(dir: string) {

// Create a progress bar
const bar = new ProgressBar({
max: sfs.length,
max: totalFiles,
formatter(x) {
return `[${x.styledTime}] [${x.progressBar}] [${x.value}/${x.max} files]`;
},
});

// process files sequentially to show proper progress
await Promise.all(sfs.map(async (sourceFile) => {
// Second pass: process each file one-by-one to keep memory flat. We add a
// SourceFile, transform it, then immediately forget it so ts-morph releases
// the AST from memory.
for (const filePath of filesToProcess) {
const sourceFile = project.addSourceFileAtPath(filePath);
try {
const wasModified = await updateFile(sourceFile);
if (wasModified) {
modifiedFilesList.push(sourceFile.getFilePath());
}

return wasModified;
} catch (err) {
// deno-lint-ignore no-console
console.error(`Could not process ${sourceFile.getFilePath()}`);
throw err;
} finally {
completedFiles++;
bar.value = completedFiles;
// Drop the AST to avoid unbounded memory growth
sourceFile.forget();
project.removeSourceFile(sourceFile);
}
}));
}

// Clear the progress line and add a newline
await bar.stop();
Expand All @@ -227,19 +247,21 @@ export async function updateProject(dir: string) {
const modifiedFilesToShow = modifiedFilesList.filter((filePath) =>
!HIDE_FILES.test(filePath)
);
const unmodifiedCount = Math.max(
userFileCount - modifiedFilesToShow.length,
0,
);

// add migration summary
// deno-lint-ignore no-console
console.log("\n" + colors.bold("📊 Migration Summary:"));
// deno-lint-ignore no-console
console.log(` Total files processed: ${userFiles.length}`);
console.log(` Total files processed: ${userFileCount}`);
// deno-lint-ignore no-console
console.log(` Successfully modified: ${modifiedFilesToShow.length}`);
// deno-lint-ignore no-console
console.log(
` Unmodified (no changes needed): ${
userFiles.length - modifiedFilesToShow.length
}`,
` Unmodified (no changes needed): ${unmodifiedCount}`,
);

// Display modified files list (filtered)
Expand Down
5 changes: 3 additions & 2 deletions packages/update/src/update_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,11 +714,12 @@ export default function Foo(props: PageProps) {

const files = await readFiles(dir);

// Should leave dependency trees untouched
expect(files["/node_modules/foo/bar.ts"]).toEqual(
`import { IS_BROWSER } from "fresh/runtime";`,
`import { IS_BROWSER } from "$fresh/runtime.ts";`,
);
expect(files["/vendor/foo/bar.ts"]).toEqual(
`import { IS_BROWSER } from "fresh/runtime";`,
`import { IS_BROWSER } from "$fresh/runtime.ts";`,
);
expect(files["/routes/index.tsx"]).toEqual(
`import { PageProps } from "fresh";
Expand Down
Loading