fix(core): throw error if build folder already exists on initial clean#9112
Conversation
✅ [V2]Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify site configuration. |
⚡️ Lighthouse report for the deploy preview of this PR
|
| // More context: https://github.com/facebook/docusaurus/pull/1839 | ||
|
|
||
| import path from 'path'; | ||
| import * as fs from 'fs'; |
There was a problem hiding this comment.
Use fs-extra and await fs.pathExists(); you can find other examples.
There was a problem hiding this comment.
@Josh-Cena I'm pushing a commit that use await pathExists instead of sync func
|
@thedevwonder Using the sync version is fine here, since we'd better leave the method sync. It shouldn't be too much of a problem. |
sorry I didn't see the notification and already did it. Should I revert back to sync version? |
| return; | ||
| } | ||
|
|
||
| if (await fs.pathExists(this.outputPath)) { |
There was a problem hiding this comment.
The outputPath is supposed to exist—this method is used to clean the output. What's problematic is if it's not a directory. You also need to test if it's a file.
There was a problem hiding this comment.
Right, I will add a validation for file as well
There was a problem hiding this comment.
@Josh-Cena I have a confusion here, pathExistsSync will still return true when build is a file. Then why do we need an additional validation?
There was a problem hiding this comment.
There are three cases here:
outputPathdoesn't exist -> Always good;removeFilesdoes nothingoutputPathis a directory -> This is why we have this function; we want to clean its content and prepare for the next build outputoutputPathis a file -> This is what Build error Thecwdoption must be a path to a directory when a file calledbuildexists #9110 is reporting. In this casedelSyncfails becausecwdis a file, not a directory. Logically, we can't clear anything from this file, so we fail.
We don't want to touch the behavior in cases 1 and 2, only give a better error in 3.
There was a problem hiding this comment.
I see, I misunderstood earlier. We do not need to change the behaviour when this.outputPath is a directory. So the check that I've put should only validate files and not directories.
There was a problem hiding this comment.
@Josh-Cena Reading the original issue, I came across another observation. the error message should occur in case-insensitive filesystems as well. To check if there are no existing files with same name as build but different case, I created a private validator that returns boolean. checks for both files and directories with duplicate name. This will solve:
- case where root dir may have diff variations of same file such as
BUILD,build,buiLD. - diff variations of same directories.
There was a problem hiding this comment.
tested the code in local env.
…ase-insensitive systems
| private checkIfFileNameIsDuplicate(): boolean { | ||
| const dir = path.dirname(this.outputPath); | ||
| const baseName = path.basename(this.outputPath).toLowerCase(); | ||
| // eslint-disable-next-line no-restricted-properties | ||
| const duplicateFiles = fs | ||
| .readdirSync(dir) | ||
| .filter((fileName) => fileName.toLowerCase() === baseName); | ||
| if (duplicateFiles.length > 0) { | ||
| return true; | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
I think this should be enough
if (
(await fs.pathExists(outputPath)) &&
(await fs.stat(outputPath)).isFile()
) {
throw new Error("...");
}On:
- case insensitive FS: this should throw for files like
BUILDorbuild - case-sensitive FS: if a file named
BUILDexists, I think it's allowed to create a folder namedbuild? 🤔
I only have access to a case insensitive file-system. Can someone test my assumption on case sensitive file-system?
There was a problem hiding this comment.
@slorber, I think yours is better because it doesn't break the abstraction of whether the FS is case-sensitive or not :)
There was a problem hiding this comment.
I think this should be enough
if ( (await fs.pathExists(outputPath)) && (await fs.stat(outputPath)).isFile() ) { throw new Error("..."); }On:
- case insensitive FS: this should throw for files like
BUILDorbuild- case-sensitive FS: if a file named
BUILDexists, I think it's allowed to create a folder namedbuild? 🤔I only have access to a case insensitive file-system. Can someone test my assumption on case sensitive file-system?
I used gitpod.io to test this usecase. It is a case-sensitive system. I changed the code because I thought someone using case-sensitive FS may create a BUILD file and someone not using this FS may find this file throwing an error during the build
There was a problem hiding this comment.
What do you think about this, any suggestions? @slorber @Josh-Cena
There was a problem hiding this comment.
I think it's fine. Running the same project on different systems can often run into inconsistencies, like some imports that work on case-insensitive systems suddenly fail in case-sensitive ones.
There was a problem hiding this comment.
@Josh-Cena I see, keeping it simple then!
There was a problem hiding this comment.
@Josh-Cena @slorber I have used the sync version of pathExists and stat functions and fixed the error message. Let me know if this looks good.
There was a problem hiding this comment.
I have also tested the code in my local env. Works fine:)
|
|
||
| if (this.checkIfFileNameIsDuplicate()) { | ||
| throw new Error( | ||
| `Output directory ${this.outputPath} already exists. Docusaurus needs this directory to save the build output. Either remove the directory or choose a different build directory via '--out-dir'.`, |
There was a problem hiding this comment.
If it's a directory the role of this plugin is to delete. As @Josh-Cena said the problem is only if a file already exists, so this error message is not good.
There was a problem hiding this comment.
@slorber this makes sense, I'll do the change
slorber
left a comment
There was a problem hiding this comment.
Thanks, LGTM.
Unfortunate that we need to use sync IO but not a big deal.
@slorber is there some way we can check the performance diff in introducing sync I/O functions here? Just curious. Probably build time could be one parameter |
oh I see, build performance is literally one of the checks here, my bad |
|
That's fine, this CI fails for other reasons 👍 |
Pre-flight checklist
Motivation
Fix #9110
Test Plan
local tests
Test links
Deploy preview: https://deploy-preview-9112--docusaurus-2.netlify.app/
Related issues/PRs