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

Export complete files instead of having shared ones #85

Open
2 tasks
nik312123 opened this issue Sep 5, 2024 · 5 comments
Open
2 tasks

Export complete files instead of having shared ones #85

nik312123 opened this issue Sep 5, 2024 · 5 comments
Labels
enhancement New feature or request

Comments

@nik312123
Copy link

nik312123 commented Sep 5, 2024

Feature request

Basically, let us say my project contains the following typescript files:

  • a.ts
  • b.ts
  • c.ts

a.ts and b.ts both depend on c.ts.

If my package.json has this:

"main": "src/a.ts",
"exports": {
    "src/a.ts": "./dist/a.js"
},

It will export one a.js file, which includes the contents of the c.ts compiled to JS.

If my package.json has this:

"main": "src/a.ts",
"exports": {
    "src/a.ts": "./dist/a.js",
    "src/b.ts": "./dist/b.js"
},

It will export the following files:

  • a.js
  • b.js
  • c-[arbitrary-string-of-characters].js

a.js and b.js will both import from c-[arbitrary-string-of-characters].js.

Is there a way to simply export only a.js and b.js with both having the full contents of c.js within them?

Motivations

I came across this when trying to create multiple full exported JS files using pkgroll.

Alternatives

No response

Additional context

No response

Bugs are expected to be fixed by those affected by it

  • I'm interested in working on this issue

Compensating engineering work financially will speed up resolution

  • I'm willing to offer financial support
@nik312123 nik312123 added the enhancement New feature or request label Sep 5, 2024
@privatenumber
Copy link
Owner

privatenumber commented Sep 5, 2024

Interesting FR. Although I haven't needed it myself, I can definitely see it being useful.

FYI seems like it was requested in Rollup as well but it's been open for a long time:
rollup/rollup#2756

The workaround shared there is to create multiple builds, but to avoid the duplicated processing overhead, I'm curious if we can make a plugin that duplicates every shared module with a unique ID right before the bundling step so Rollup thinks none of the dependencies are shared.

Feel free to play around with implementation ideas and open a PR.

@nik312123
Copy link
Author

nik312123 commented Sep 5, 2024

Hmm... It looks like the plugin idea was already mentioned in the issue here.

To prevent collisions, which could be possible (though rare) in the solution linked above, something could probably be done like this:

import path from 'path';

let nextId = -1;

const disableChunks = (targets = []) => {
    /** @type{import('rollup').Plugin} */
    const plugin = {
        name: 'disable-chunks',
        async resolveId(source, importer, options) {
            const resolved = await this.resolve(source, importer, options);
            if(resolved && targets.some(file => resolved.id.includes(file))) {
                ++nextId;
                return `${resolved.id}?unique=${nextId.toString(16)}`;
            }
        },
        load(id) {
            const regex = /(\?unique=[a-zA-Z0-9]*)$/;
            if(regex.test(id)) {
                return this.load({id: id.replace(regex, '')});
            }
        },
        generateBundle(options, bundle) {
            const nonEntryChunks = Object.values(bundle).filter(({isEntry}) => !isEntry);
            if(nonEntryChunks.length > 0) {
                const moduleIds = new Set(
                    nonEntryChunks.map(({moduleIds}) => moduleIds)
                        .flat()
                        .map(absPath => path.relative('.', absPath))
                );
                throw new Error(`Found non-entry chunks: ${Array.from(moduleIds).join(', ')}`);
            }
        }
    };
    
    return plugin;
};

Then, if some option (e.g. command line or other) is included in pkgroll with the files to exclude as chunks, it can be included as an option in the rollup configs (get-rollup-configs.ts?) to disable chunk generation for specific files similar to this:

// rollup.config.mjs
export default defineConfig([
    {
        input: ['src/index1.ts', 'src/index2.ts'],
        output: {
            dir: 'dist'
        },
        plugins: [
            disableChunks(['src/shared.ts']),
            typescript({module: 'ESNext'})
        ]
    }
]);

I am finding it a bit hard to directly implement this in get-rollup-configs.ts or where this would be required. Any advice/guidance would be much appreciated, @privatenumber. 😊

@privatenumber
Copy link
Owner

Oh good catch! I missed that.

For configuration, I'm thinking we can adopt a naming convention similar to *.min.js where .min indicates it's minified. Likewise, maybe .bundled.js can indicate it's a bundled entry.

The custom plugin should be added in here:
https://github.com/privatenumber/pkgroll/tree/master/src/utils/rollup-plugins

And added in this array if there's an entry point with bundled.js:
https://github.com/privatenumber/pkgroll/blob/master/src/utils/get-rollup-configs.ts#L145

Instead of the incremental unique counter, we can simply have a namespace=dist/entry-file.bundled.js parameter that's added to everything resolved by the bundled.js entry point. This way, we won't interfere with chunking in entrypoints that don't have bundled.js in it. That said, we just need the resolveId and load hooks (no generateBundle).

@nik312123
Copy link
Author

@privatenumber Hmm... Trying to work through this but having trouble figuring out how to get the targets array if passing in via the plugins array at 145. Any advice?

@privatenumber
Copy link
Owner

You can accept it as a parameter in getConfig.app()

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

No branches or pull requests

2 participants