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

custom paths for entrypoints, chunks, and assets in outdir #224

Closed
nitsky opened this issue Jul 5, 2020 · 15 comments
Closed

custom paths for entrypoints, chunks, and assets in outdir #224

nitsky opened this issue Jul 5, 2020 · 15 comments

Comments

@nitsky
Copy link
Contributor

nitsky commented Jul 5, 2020

I have been unable to build my project with esbuild because the structure of the output directory is not customizable. For example, esbuild fails for multiple entrypoints with the same basename, such as src/a/page.tsx and src/b/page.tsx, because esbuild wants to output both to <outdir>/page.js. Rollup's input, entryFileNames, chunkFileNames, and assetFileNames options give users a good deal of control over where output files are placed. Would you consider similar features for esbuild?

Perhaps something like this...

esbuild --outdir=dist --entryFileNames="pages/[name].js" "pageA=src/a/page.tsx" "pageB=src/b/page.tsx"

...which would output dist/pages/pageA.js and dist/pages/pageB.js.

Below are links to Rollup's documentation for the relevant options:

http://rollupjs.org/guide/en/#input
http://rollupjs.org/guide/en/#outputentryfilenames
http://rollupjs.org/guide/en/#outputchunkfilenames
http://rollupjs.org/guide/en/#outputassetfilenames

@photonstorm
Copy link
Contributor

Just adding my support for having this feature.

I, too, cannot do a full build because the entry files are all lumped in the outdir together, so lots of them overwrite each other.

I don't mind, because I can still do a full release build with Rollup and use esbuild for all my testing, but it would be great to have this feature.

@photonstorm
Copy link
Contributor

Spent a good while today playing with the esbuild JS API, hoping to maybe emulate this feature via Transform, but I just don't think it's possible.

I don't need to be able to configure the output chunks, I just need them to mirror the structure of the input entry files.

Fingers crossed this lands in a forth-coming release :)

@matthewmueller
Copy link

matthewmueller commented Jul 10, 2020

(Merging in from #244)

Problem

Given the following file structure:

.
├── a
│   └── index.js
└── b
    └── index.js

If you run:

esbuild --bundle --outdir=dist --format=esm a/index.js b/index.js

You get the following error:

error: Two output files share the same path: dist/index.js

Possible Solution

I think I'd expect the bundle output to look like this:

dist
├── a
│   └── index.js
└── b
    └── index.js

So it seems like there'd need to be a base directory (defaulting to working directory) so you could do:

const rel = path.relative(basedir, entry)
const outpath = path.join(outdir, rel)

I'd suggest steering this discussion to the minimal changes required to solve this problem. I agree that customizing the names of chunks, assets, and entrypoints is nice to have (I use these features in rollup too), but even without any of those options, rollup and tsc handle the issue of two entrypoints in different directories with the same name.

@evanw
Copy link
Owner

evanw commented Jul 10, 2020

Just did a survey of different tools to see what they do.

It looks like Parcel and tsc avoid collisions by generating nested folders (so dist/a/index.js and dist/b/index.js). That approach will work for esbuild.

Rollup avoids collisions by adding a number after the file (so dist/index.js and dist/index2.js) but I don't want to use that solution for esbuild because it seems error-prone. The meaning of the path dist/index.js could unexpectedly change if one of the entry points is later removed such that there is no longer a collision.

@evanw
Copy link
Owner

evanw commented Jul 11, 2020

This should be fixed as of version 0.6.0.

@photonstorm
Copy link
Contributor

Superb, thank you! I will test this on my codebase on Monday and open an issue if I find any. It should be a good test, as there are over 1200 entry points for the API (which is why I'm desperate to move away from Rollup as it takes over 6mins for a full build)

@matthewmueller
Copy link

matthewmueller commented Jul 11, 2020

Works beautifully. Thanks Evan!

Entrypoints

- views/users/edit.js      => dist/users/edit.js
- views/posts/index.js     => dist/posts/index.js
- views/posts/show.js      => dist/posts/show.js
- views/posts/new.js       => dist/posts/new.js
- views/posts/edit.js      => dist/posts/edit.js
- views/layouts/default.js => dist/layouts/default.js
- views/users/index.js     => dist/users/index.js
- views/users/show.js      => dist/users/show.js
- views/users/new.js       => dist/users/new.js

Additional Chunks

- dist/chunk.mivFoCKm.js
- dist/chunk.-3gnZy6S.js
- dist/chunk._Q398Reo.js
- dist/chunk.ryjutOKl.js
- dist/chunk.nkB2WGXm.js
- dist/chunk.a4CBmrWb.js

@evanw
Copy link
Owner

evanw commented Jul 11, 2020

Superb, thank you! I will test this on my codebase on Monday and open an issue if I find any. It should be a good test, as there are over 1200 entry points for the API (which is why I'm desperate to move away from Rollup as it takes over 6mins for a full build)

How long does it take now? Just curious.

Works beautifully. Thanks Evan!

Thanks for reporting back. That's great to hear!

@photonstorm
Copy link
Contributor

@evanw thought you may like to know that I finished migrating my build scripts over from Rollup to esbuild (I had to code my own functions for things like clearing dist folder, copying files, etc but this is trivial in node.js). Here is the build log now:

✔ Cleared target folder (1083 ms)
✔ Copied dist files (8 ms)
✔ Built Phaser 4 v0.1.9 - 1151 modules (21 ms)
✔ TypeScript defs complete (10615 ms)
✔ Build complete in 11.733 secs

Just to be clear, it used to take Rollup over 6 minutes to do the exact same thing. The esbuild portion of this log is just 21 ms for, currently, 1,151 entry points.

Lovely :)

@petterek
Copy link

Is there a way to "turn this feature off" and have esbuild put all files in the same folder?
Or can I write a plugin that from the entry point resolves the "exit point" / target file

@pft
Copy link

pft commented Jun 11, 2022

Is there a way to "turn this feature off" and have esbuild put all files in the same folder? Or can I write a plugin that from the entry point resolves the "exit point" / target file

I'd like this too. One of my entrypoints is a dynamic temporary file, while the others may be just in the source directory. This leads to a path for the temp file output starting with a bunch of directories called _.._. Working around it by making the other entrypoints temp files too might solve this, but seems like a lot of hassle for something that could just work. Note I do not see a collision hazard as I use the hash in the output filenames.

@pft
Copy link

pft commented Jun 11, 2022

Answering my own comment: when using the object form for entryPoints, esbuild will refrain from using the lowest common ancestor. For example:

entryPoints: {
 main: '/tmp/djjd73e.js',
 other: '/home/me/proj/lib/x.js'
}

@loicnestler
Copy link

Is there a way to "turn this feature off" and have esbuild put all files in the same folder?
Or can I write a plugin that from the entry point resolves the "exit point" / target file

Any updates on this? I want to bundle my code for a Shopify theme however as their guidelines define themes aren't currently allowed to have nested directories (but only one single assets/ dir that needs to contain all bundled files).

@MurmeltierS
Copy link

@loicnestler lol - same

@loicnestler
Copy link

For anyone still struggling with this problem:

  1. Load your entryPoints as an array of file paths (e.g. by using fast-glob)
  2. Transform your file paths array into an array tuples of 2 strings containing the base file name and the full path to the file.
  3. Translate said map into an Object using Object.fromEntries and pass that into esbuild's entryPoints option.
import { parse } from 'path'

// Load all the files
const filePaths = glob.sync('src/**/*.ts')

// Transform into a map of name => path
const mapped = new Map(filePaths.map(path => [
    parse(path).name,
    path
]))

const entryPoints = Object.fromEntries(mapped)

// Esbuild will output all files flat into the outdir
build({
    entryPoints,
    // ...,
    outdir: 'assets/'
})

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

8 participants