[Feature Request]: Avoid duplicated clean dir
What problem does this feature solve?
Avoid duplicated clean dir when multiple outputs like this:
import { defineConfig, OutputOptions } from "rolldown"
import { dts } from "rolldown-plugin-dts"
const input = "src/xxx.ts"
const output: OutputOptions = {
dir: "out",
cleanDir: true,
sourcemap: true,
}
export default defineConfig([
{
plugins: [dts({ sourcemap: true })],
input,
output: { ...output, format: "esm" },
},
{
input,
output: {
...output,
format: "commonjs",
entryFileNames: "[name].cjs",
},
},
])
In that case, the second output process will clean the output of the first one.
Definitely, we can set the cleanDir option of the second output to false, which usually works. But that's not safe enough. Perhaps we can add another logic to prevent cleaning the same dir for multiple times.
What does the proposed API look like?
import {defineConfig} from "rolldown"
export default defineConfig({
...
output: {
cleanDir: true, // true | false | "force".
}
})
false- don't clean dir.true- clean dir but avoid duplicated cleans."force"- clean dir regardless of whether it had been cleaned during this build.
It's not difficult to implement such feature. But the question is whether such changes to source code structure and APIs are acceptable:
- API changes to the
cleanDirtype: frombooleantoboolean | "force". - Code structure changes to the
struct Bundler, which might have potential side effects.
pub struct Bundler {
...
// Append this new property to the Bundler struct.
pub(crate) cleaned_dirs: Vec<PathBuf>,
}
I don't see there's a better way to do this. Bundler itself isn't aware of multiple configs. We could do a option validation on nodejs side, emit a warning for such scenario.
Bundler itself isn't aware of multiple configs.
Yea, it's much complex than I thought... The Bundler is called by node side rather than rust side, that it's impossible to create something like a mutex to record what dir had been cleaned. Even we can't analysis and cancel the cleanDir config in node side before calling Bundler because there are no strict order on which one cleaned first.
A warning message by analyzing the output dirs before calling the rust side Bundler is definitely possible. But just a warning may not convenient enough for the developers.
How about clean the out dirs in the node side before the bundler is called:
- It's easy to avoid duplicated removal, because it won't delete anything during the lifecycle of the rust side bundler.
- Node side might a little bit slower than rust, but clean dirs are not performance sensitive operations.
- But it separate the clean process from the bundler, which might make the paths error prone.
Another idea, but might make it more complex:
- Keep original rust implementations, and add warning when might be duplicate cleanings.
- Add another input options called something like
safeCleanOutDirs, default to false. - Once
safeCleanOutDirsenabled, it will disable all rust side removals, and resolve everything in node side: detect all dirs to clean and resolve them before the rust side bundler is called using node scripts.
How about clean the out dirs in the node side before the bundler is called:
Won't consider about this direction due to design principle.
I also see users might want to control different output with separate cleanDir option. So Avoid duplicated clean dir isn't a always pefect behavior.
Things like only avoid removing the same dir when two options resolve to the same dir are too complex. I think keep a simple and intuitive behavior for users to predict is more important.
We could do a option validation on nodejs side, emit a warning for such scenario.
Due to so, raising warning won't be considered too.
Writing plugin is also not a good solution to avoid duplicated clean dir, because other plugins might modify the output options that the behavior is also not predictable. That's something only the bundler tool itself can do properly.
Perhaps the following code is a better choice for corresponding scenarios.
const outdirs = [ ... ]
for (const path of outdirs) {
mkdirSync(path, { recursive: true })
for (const n of readdirSync(path)) rmSync(join(path, n), { recursive: true })
}
export default defineConfig({
input: { ... },
output: { ... },
})
It's just 2 or 5 lines of code, clean and predicable, and easy to maintain in the codebase...
options get normalized/resolved from rust side, I don't think it could be done easily as you said so.
Yes, I'm not sure about that. If that's not something against current design principle, I'll try it later in another Issue&PR when I have enough time (consider that the rust side is more complex).
To be straight, I don't wanna kill your enthusiasm. But so far I don't see a solid reason for accepting the PR.
... Okay, I quit that idea. It's not a serious enthusiasm, I just wanna to solve a (not important) problem in my personal codebase, and there will be many other solutions. Thanks again for your kindness and thoughtful consideration.
Writing plugin is also not a good solution to avoid duplicated clean dir, because other plugins might modify the output options that the behavior is also not predictable.
I think you can know the final out dir from renderStart hook.