Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Route files replaced with "Hello /route" boilerplate #2151

Open
timoxley opened this issue Aug 19, 2024 · 2 comments
Open

Route files replaced with "Hello /route" boilerplate #2151

timoxley opened this issue Aug 19, 2024 · 2 comments

Comments

@timoxley
Copy link

timoxley commented Aug 19, 2024

Using vite with @tanstack/router-plugin/vite.
Route files with real content are getting clobbered with boilerplate. i.e.

import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/myroute')({
  component: () => <div>Hello /myroute!</div>
})

This has popped up a number of times, but doesn't happen consistently. Normally I just revert the file in my IDE.

But this time, at some point between testing some changes and committing the file it did the clobbering and I managed to lose all the changes and actually committed the boilerplate to source control. (yes i should have checked the diff)

Only just noticed a day later when I tried to demo the new route so I look like a doofus and the original content is not recoverable. 😿😿😿

Platform

  • OS: Windows
  • Browser: Chrome
  • IDE: Webstorm
  • Version:
    "@tanstack/react-router": "^1.48.1",
    "@tanstack/router-cli": "^1.47.0",
    "@tanstack/router-devtools": "^1.48.1",
    "@tanstack/router-plugin": "^1.47.0",

I haven't tested with latest version but I did only update a few days ago.

My guess as to what's happening

Maybe git or the ide does something funny with the file which triggers the plugin into thinking the file is empty (and it might have been, briefly) and async operations between that tool and the route boilerplater get a bit tangled.

// on file changed...
if (await isFileEmpty()) { // it's empty (at this point)
   // but file becomes no longer empty between these two operations
	await writeBoilerplate()
}
sequenceDiagram
   
    participant G as Git (or IDE)
    participant F as File
    participant T as Tan

    G ->> F: "Truncate File"
    activate G
    F -->> T: "Report File Changed!"
    activate T
    
    T ->> F: "Is File Empty?"
    Note right of T: if (await IsFileEmpty()) {<br />...  
    F -->> T: "File is Empty"
    Note right of G: Meanwhile...
    G ->> F: "Write New Content"
    deactivate G
    Note right of F: File is no longer empty...
  
    Note right of T: await WriteBoilerplate()
    T ->> F: "Write Boilerplate"
    deactivate T
    Note right of F: Clobbered New Content!
Loading
@SeanCassiere
Copy link
Member

I haven't experienced this for a while now. That being said, we've done as much as we can to stop this from happening whilst the dev server is running.

These are the checks we've added have so far:

Checking that the routeCode is empty.

const routeCode = fs.readFileSync(node.fullPath, 'utf-8')
const escapedRoutePath = removeTrailingUnderscores(
node.routePath?.replaceAll('$', '$$') ?? '',
)
let replaced = routeCode
if (!routeCode) {

Only updating the written values for routes instead of pasting the full template.

} else {
replaced = routeCode
.replace(
/(FileRoute\(\s*['"])([^\s]*)(['"],?\s*\))/g,
(match, p1, p2, p3) => `${p1}${escapedRoutePath}${p3}`,
)
.replace(
/(import\s*\{.*)(create(Lazy)?FileRoute)(.*\}\s*from\s*['"]@tanstack\/react-router['"])/gs,
(match, p1, p2, p3, p4) =>
`${p1}${node.isLazy ? 'createLazyFileRoute' : 'createFileRoute'}${p4}`,
)
.replace(
/create(Lazy)?FileRoute(\(\s*['"])([^\s]*)(['"],?\s*\))/g,
(match, p1, p2, p3, p4) =>
`${node.isLazy ? 'createLazyFileRoute' : 'createFileRoute'}${p2}${escapedRoutePath}${p4}`,
)
}

Running a block when the generator is running to not double-run it.

const generate = async () => {
if (checkLock()) {
return
}
setLock(true)
try {
await generator(userConfig)
} catch (err) {
console.error(err)
console.info()
} finally {
setLock(false)
}
}

@TruDan
Copy link

TruDan commented Sep 4, 2024

Happening to us too. Funnily enough, also when we are trying to demo our app to our boss! 😂 Workaround being to rollback using either Git, or webstorm's Local History

We use a mix of Linux + Windows environments, also using git (with frequent updates on both ends) and Webstorm 2024.1.4

Node version: 20.11.0

    "@tanstack/react-form": "^0.23.3",
    "@tanstack/react-query": "^5.45.1",
    "@tanstack/react-router": "^1.38.1",
    "@tanstack/react-virtual": "^3.7.0",

My insights on the issue (albiet not as detailed as OP)

It seems like some sort of race condition between ((git || webstorm) && tanstack) where both are trying to write to the file at the same time. where (git || webstorm) empties the file completely before writing perhaps so tanstack see's it as empty and therefore writes its boilerplate.

I have witnessed once or twice the 'reset' files actually being a mash-up of the boilerplate + the real code (before reset). But most of the time when it does happen its just like the OP described.

for example:

import {createFileRoute} from '@tanstack/react-router'
import {PageHeader} from "@/components/PageHeader.tsx";
import {useSuspenseQuery} from "@tanstack/react-query";
import {Fab, List, MenuItem, Paper} from "@mui/material";
import {getProjects} from "@/api/queryOptions.ts";
import {HeroLink} from "@/components/HeroLink.tsx";
import {FilterList} from "@mui/icons-material";
import {useAppLayout} from "@/hooks/useAppLayout.tsx";

export const Route = createFileRoute('/_authenticated/projects')({
    component: Projects
})

function Projects() {
    const {data: projects} = useSuspenseQuery(getProjects());

    const {bottomNavigationHeight} = useAppLayout();

    return (
        <>
            <PageHeader title="Projects" />

            <Paper>
                <List>
                    {projects.map(p => (<MenuItem key={p.id} component={HeroLink} to={'/projects/$projectId'} params={{projectId: p.id}}>{p.projectName}</MenuItem>))}
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/_authenticated/projects')({
  component: () => <div>Hello /_authenticated/projects!</div>
})

or the tanstack stuff at the start and then half of the real code after it - this makes me believe both are writing at the same time (for the record i'm the one on Windows)

Another thing that may or may not be relevant, since both members of our team use different OS's, our line endings are different of course and each time a change is made to a route page we see that the routeTree.gen.ts updates but only line endings, shouldn't be a problem, but perhaps this causes some updates to files or something? or perhaps if some route pages are LF and others CRLF the checking for a route's existence doesnt work correctly?

Does the generator use file-locks to encase the read + update of the file?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants