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

add the ability to rewrite modules in-place #1922

Merged
merged 2 commits into from
Mar 14, 2018

Conversation

kellyselden
Copy link
Contributor

@kellyselden kellyselden commented Jan 28, 2018

Closes #1878.

This turned out to be remarkably simpler once @guybedford laid the foundational work with dynamic imports and chunking. I essentially tapped into the dynamic imports system and made every import behave like a dynamic import.

To try this out, all you have to do is add the experimentalPreserveModules to your existing experimentalCodeSplitting setup. Then, the directory tree should be preserved in your output dir.

@kellyselden kellyselden changed the title add the ability to rewrite modules in-place WIP: add the ability to rewrite modules in-place Jan 29, 2018
@kellyselden
Copy link
Contributor Author

Turns out the the folders are not preserved: "app/index.js" => "index.js". I will work on that next.

@kellyselden
Copy link
Contributor Author

Added another option to enable original directory structure.

Copy link
Contributor

@guybedford guybedford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've included some feedback here, let me know your thoughts.

src/Graph.ts Outdated
curEntry.isEntryPoint = true;
curEntryHash = randomUint8Array(10);
visit(curEntry);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this is done to ensure that no chunking happens right?

I think it can be done more easily by altering the visit function itself to do something like:

if (!preserveModules)
  Uint8ArrayXor(module.entryPointsHash, curEntryHash);
else if (isZero(module.entryPointsHash))
  module.entryPointsHash = randomUint8Array(10);

then I think that would be all there is too it without any extra loops or need to track modules.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! It got a lot simpler with this change.

src/Graph.ts Outdated
preservedModules.forEach(module => {
if (entryModules.indexOf(module) === -1)
entryModules.push(module);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just "all modules" right? If so it should be possible to generate from Object.values(this.graph) + this.externals?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed with your other suggested change.

src/Graph.ts Outdated
@@ -396,7 +421,7 @@ export default class Graph {
chunkList.forEach(chunk => {
// generate the imports and exports for the output chunk file
if (chunk.entryModule) {
const entryName = generateChunkName(chunk.entryModule.id, chunkNames, true);
const entryName = generateChunkName(chunk.entryModule.id, chunkNames, true, inputRelativeDir);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a mandatory option then? Is there not a way to deduce the lowest base by default if this is not provided?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to rely on the package commondir to calculate given the entry modules. It is used in a subsequent feature request here to make it something other than common base path, but I will move the option into that branch instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since commondir is not widely maintained and a small piece of code, do you think we could just inline the implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could do that. Are you sure I should just copy paste? I would argue that we depend on it until we need to fix something, then copy. @lukastaegert care to tie-break?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must admit I did not read everything here. Looking at commondir, it relies on nodes path.resolve which we do not have available in a browser build—even though we have our own polyfill for that (damn, we REALLY need a test for this. You should see a warning about "path" being external when building rollup with this setup—this is always BAD). Thus I fear this needs to go into our own code base somehow.

However I am happy for every bit of functionality we can extract from Graphs.ts and put into other files and for me, this is a clear candidate for the "utils" folder.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ported into the utils folder.

@guybedford
Copy link
Contributor

It's pretty awesome how small this feature has got. And it definitely preserves the original pathnames ok with directory creation etc?

@kellyselden
Copy link
Contributor Author

From my testing, yes. It does get weird if your entry file imports from '../../../../../x', then your output dir will look pretty strange, but it should still work and be "correct".

@kellyselden kellyselden force-pushed the preserve-modules-2 branch 3 times, most recently from c9647d3 to 803d043 Compare February 15, 2018 06:39
Copy link
Contributor

@guybedford guybedford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good.

@lukastaegert would be interested to hear your thoughts here as well.

@@ -0,0 +1,21 @@
// ported from https://github.com/substack/node-commondir
export default function commondir (relfiles: string[]) {
var files = relfiles;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps just name the argument files/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

export default function commondir (relfiles: string[]) {
var files = relfiles;

var res = files.slice(1).reduce(function (ps, file) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const and arrow function can be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

var res = files.slice(1).reduce(function (ps, file) {
if (!file.match(/^([A-Za-z]:)?\/|\\/)) {
throw new Error('relative path without a basedir');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we know that our inputs will pass this, it may be possible to remove this check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

throw new Error('relative path without a basedir');
}

var xs = file.split(/\/+|\\+/);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

for (
var i = 0;
ps[i] === xs[i] && i < Math.min(ps.length, xs.length);
i++
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tailing semicolon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compiler didn't like that

name = path.relative(inputRelativeDir, id).replace(/\\/g, '/');
} else {
name = path.basename(id);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check indentation here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I observed that I think we should definitely improve is the fact that if a module imports something from outside the common directory of the entry points (i.e. if the entry point imports something that starts with ../), new files and folders are created outside the intended target directory. This can definitely leave a mess on an unsuspecting user's system. In my opinion there are two possible solutions

  1. throw an error if something like this would happen (probably not ideal), or
  2. find the common root directory not among the entry points but among the generated chunk file names.

To achieve the latter, we could do the following:

  • use generateChunkName only for the non-path-preserving case (and remove the last argument)
  • if we preserve paths/modules, we use the completed chunks object to build a new object with correct paths (possibly extract a helper method here)

This is just a rough idea, some fine-tuning may be necessary.

@lukastaegert
Copy link
Member

Sure thing. Was a little busy the last days to get my own PR in order. Will probably take a look at this evening or tomorrow. I think this could become on of the core features of 0.57.0!

@lukastaegert
Copy link
Member

BTW some of the chunking form tests seem to be broken. And this should probably be rebased against the current master soon.

@kellyselden
Copy link
Contributor Author

@lukastaegert I discovered a bug, so I added more test cases. I'm trying to diagnose it now.

@kellyselden kellyselden force-pushed the preserve-modules-2 branch 2 times, most recently from e061cc2 to 3849e93 Compare February 16, 2018 05:28
@kellyselden
Copy link
Contributor Author

@lukastaegert @guybedford Turns out it might not be a bug, but an understanding issue. It has to do with what's going on in \test\chunking-form\samples\chunking-reexport. The way it's collapsing reexports there is unexpected to me, and probably not desirable for this new feature. I would love some pointers.

@kellyselden kellyselden changed the title WIP: add the ability to rewrite modules in-place add the ability to rewrite modules in-place Feb 16, 2018
@guybedford
Copy link
Contributor

@kellyselden can you link me to the test case that is failing? I just re-ran the CI here and it seems nothing is breaking specifically. If I can follow the exact simplified scenario I may be able to suggest further.

@kellyselden
Copy link
Contributor Author

kellyselden commented Feb 16, 2018

@guybedford https://github.com/rollup/rollup/pull/1922/files#diff-c7e8e1a25cc0ed7f80f7f0df33f956c7R7
I skipped it because it appears to be working as you intended after comparing with \test\chunking-form\samples\chunking-reexport, I just don't understand why it is working that way, and if I should change it for this new feature. The _expected folder in my skipped test is what I would expect though.

@kellyselden kellyselden reopened this Feb 16, 2018
@kellyselden
Copy link
Contributor Author

@guybedford I added the test for export aliases. Since there are still two commits, I added the test to the first one, and export aliases worked as expected. Then I had to modify the test to remove the aliases once your commit was added on top. You probably already knew this, I just wanted to point it out in case you didn't.

Copy link
Member

@lukastaegert lukastaegert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for not getting to this sooner. This works nicely now! As this is an experimental feature anyway, I'll merge this into the next release, we can always fledge out any last details later.

@lukastaegert lukastaegert merged commit f837ca7 into rollup:master Mar 14, 2018
@kellyselden kellyselden deleted the preserve-modules-2 branch March 14, 2018 17:09
@tunnckoCore
Copy link
Contributor

This option is still not documented on the site? It seems that i just should try it ;/

@guybedford
Copy link
Contributor

I think we should document this now actually. A documentation PR is more than welcome!

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

Successfully merging this pull request may close these issues.

None yet

4 participants