Repository: dai-shi/waku (ts)

Issue:
Remove prettier format step from generation and add formatter ignore comments
remove prettier formatting from: https://github.com/dai-shi/waku/blob/main/packages/waku/src/lib/plugins/vite-plugin-fs-router-typegen.ts#L73-L75

Ignore recommendations for:

- [deno fmt](https://docs.deno.com/runtime/reference/cli/fmt)
- [Prettier](https://prettier.io/)
- ~[Biome](https://biomejs.dev/formatter)~
- ~[dprint](https://dprint.dev/)~

plan for fixing: https://github.com/dai-shi/waku/issues/1148#issuecomment-2599867558


Pull request description:
fix(typegen): skip parsing errors and remove formatting
fixes: [discord comment](https://discord.com/channels/627656437971288081/1083774812129857566/1330093730354561034)

fixes #1148

related to #1163

Patch:
diff --git a/packages/waku/src/lib/plugins/vite-plugin-fs-router-typegen.ts b/packages/waku/src/lib/plugins/vite-plugin-fs-router-typegen.ts
index cf0213ca..2a53254c 100644
--- a/packages/waku/src/lib/plugins/vite-plugin-fs-router-typegen.ts
+++ b/packages/waku/src/lib/plugins/vite-plugin-fs-router-typegen.ts
@@ -58,7 +58,7 @@ export const fsRouterTypegenPlugin = (opts: { srcDir: string }): Plugin => {
   let entriesFilePossibilities: string[] | undefined;
   let pagesDir: string | undefined;
   let outputFile: string | undefined;
-  let formatter = (s: string): Promise<string> => Promise.resolve(s);
+
   return {
     name: 'vite-plugin-fs-router-typegen',
     apply: 'serve',
@@ -68,17 +68,6 @@ export const fsRouterTypegenPlugin = (opts: { srcDir: string }): Plugin => {
         joinPath(config.root, opts.srcDir, SRC_ENTRIES + ext),
       );
       outputFile = joinPath(config.root, opts.srcDir, 'pages.gen.ts');
-
-      try {
-        const prettier = await import('prettier');
-        // Get user's prettier config
-        const config = await prettier.resolveConfig(outputFile);
-
-        formatter = (s) =>
-          prettier.format(s, { ...config, parser: 'typescript' });
-      } catch {
-        // ignore
-      }
     },
     configureServer(server) {
       if (
@@ -149,7 +138,7 @@ export const fsRouterTypegenPlugin = (opts: { srcDir: string }): Plugin => {
         });
       };
 
-      const generateFile = (filePaths: string[]): string => {
+      const generateFile = (filePaths: string[]): string | null => {
         const fileInfo: { path: string; src: string; hasGetConfig: boolean }[] =
           [];
         const moduleNames = getImportModuleNames(filePaths);
@@ -157,7 +146,12 @@ export const fsRouterTypegenPlugin = (opts: { srcDir: string }): Plugin => {
         for (const filePath of filePaths) {
           // where to import the component from
           const src = filePath.replace(/^\//, '');
-          const hasGetConfig = fileExportsGetConfig(filePath);
+          let hasGetConfig = false;
+          try {
+            hasGetConfig = fileExportsGetConfig(filePath);
+          } catch {
+            return null;
+          }
 
           if (filePath.endsWith('/_layout.tsx')) {
             continue;
@@ -177,36 +171,43 @@ export const fsRouterTypegenPlugin = (opts: { srcDir: string }): Plugin => {
           }
         }
 
-        let result = `import type { PathsForPages, GetConfigResponse } from 'waku/router';\n\n`;
+        let result = `// deno-fmt-ignore-file
+// biome-ignore format: generated types do not need formatting
+/* eslint-disable */
+// prettier-ignore
+import type { PathsForPages, GetConfigResponse } from 'waku/router';\n\n`;
 
         for (const file of fileInfo) {
           const moduleName = moduleNames[file.src];
           if (file.hasGetConfig) {
-            result += `import type { getConfig as ${moduleName}_getConfig } from './${SRC_PAGES}/${file.src.replace('.tsx', '')}';\n`;
+            result += `// prettier-ignore\nimport type { getConfig as ${moduleName}_getConfig } from './${SRC_PAGES}/${file.src.replace('.tsx', '')}';\n`;
           }
         }
 
-        result += `\ntype Page =\n`;
+        result += `\n// prettier-ignore\ntype Page =\n`;
 
         for (const file of fileInfo) {
           const moduleName = moduleNames[file.src];
           if (file.hasGetConfig) {
-            result += `| ({path: '${file.path}'} & GetConfigResponse<typeof ${moduleName}_getConfig>)\n`;
+            result += `| ({ path: '${file.path}' } & GetConfigResponse<typeof ${moduleName}_getConfig>)\n`;
           } else {
-            result += `| {path: '${file.path}'; render: 'dynamic'}\n`;
+            result += `| { path: '${file.path}'; render: 'dynamic' }\n`;
           }
         }
 
-        result += `;
+        result =
+          result.slice(0, -1) +
+          `;
 
-  declare module 'waku/router' {
-    interface RouteConfig {
-      paths: PathsForPages<Page>;
-    }
-    interface CreatePagesConfig {
-      pages: Page;
-    }
+// prettier-ignore
+declare module 'waku/router' {
+  interface RouteConfig {
+    paths: PathsForPages<Page>;
+  }
+  interface CreatePagesConfig {
+    pages: Page;
   }
+}
   `;
 
         return result;
@@ -220,8 +221,12 @@ export const fsRouterTypegenPlugin = (opts: { srcDir: string }): Plugin => {
         if (!files.length) {
           return;
         }
-        const formatted = await formatter(generateFile(files));
-        await writeFile(outputFile, formatted, 'utf-8');
+        const generation = generateFile(files);
+        if (!generation) {
+          // skip failures
+          return;
+        }
+        await writeFile(outputFile, generation, 'utf-8');
       };
 
       server.watcher.on('change', async (file) => {


Test patch:
diff --git a/e2e/hot-reload.spec.ts b/e2e/hot-reload.spec.ts
index 7e24b316..54bb0e36 100644
--- a/e2e/hot-reload.spec.ts
+++ b/e2e/hot-reload.spec.ts
@@ -112,5 +112,20 @@ test.describe('hot reload', () => {
     await expect(page.getByText('About2 Page')).toBeVisible();
     await page.getByTestId('home').click();
     await expect(page.getByText('Edited Page')).toBeVisible();
+    // Modify with a JSX syntax error
+    await modifyFile(
+      standaloneDir,
+      'src/pages/index.tsx',
+      '<p>Edited Page</p>',
+      '<pEdited Page</p>',
+    );
+    await page.waitForTimeout(500); // need to wait for possible crash
+    await modifyFile(
+      standaloneDir,
+      'src/pages/index.tsx',
+      '<pEdited Page</p>',
+      '<p>Fixed Page</p>',
+    );
+    await expect(page.getByText('Fixed Page')).toBeVisible();
   });
 });
diff --git a/packages/waku/tests/vite-plugin-fs-router-typegen.test.ts b/packages/waku/tests/vite-plugin-fs-router-typegen.test.ts
index 21d7168e..cfa843ba 100644
--- a/packages/waku/tests/vite-plugin-fs-router-typegen.test.ts
+++ b/packages/waku/tests/vite-plugin-fs-router-typegen.test.ts
@@ -86,12 +86,19 @@ describe('vite-plugin-fs-router-typegen', () => {
   test('creates the expected imports the generated entries file', async () => {
     await runTest(
       root,
-      `import type { getConfig as CategoryTagsIndex_getConfig } from './pages/[category]/[...tags]/index';
+      `// prettier-ignore
+import type { getConfig as CategoryTagsIndex_getConfig } from './pages/[category]/[...tags]/index';
+// prettier-ignore
 import type { getConfig as Root_getConfig } from './pages/_root';
+// prettier-ignore
 import type { getConfig as Index_getConfig } from './pages/index';
+// prettier-ignore
 import type { getConfig as OneTwoThree_getConfig } from './pages/one-two-three';
+// prettier-ignore
 import type { getConfig as OneTwoThree_1_getConfig } from './pages/one__two_three';
+// prettier-ignore
 import type { getConfig as OneTwoThree_2_getConfig } from './pages/one_two_three';
+// prettier-ignore
 import type { getConfig as ØnéTwoThree_getConfig } from './pages/øné_two_three';`,
     );
   });