Skip to content

Conversation

@lydell
Copy link
Contributor

@lydell lydell commented Feb 13, 2014

The paths in the sources property of source maps may be relative. If
so, they are relative to the source map itself. When you apply a source
map A to another source map B, the source in map B that is associated
with map A will be replaced with sources from map A. If those sources
are relative, they need to be rewritten to be relative to map B instead
of map A. In order to do that, the applySourceMap method needs more
information: The missing piece between map B and map A. It is now
possible to give this information, using a third parameter: The path to
map A, relative to map B. (It can also be absolute, of course.)

This should be backwards compatible. If the third parameter is omitted,
the method works exactly like before: It assumes that both maps are in
the same directory, which makes all sources relative to both maps, which
in turn requires no rewriting.

Use case: `util.join('a', '../b')` should be `'b'`, not `'a/../b'`.
@fitzgen
Copy link
Contributor

fitzgen commented Feb 14, 2014

Thanks for the detailed description. I'll take a look at this tomorrow.

@lydell
Copy link
Contributor Author

lydell commented Feb 14, 2014

Crap. I just found a mistake, that somehow slipped through the tests. I'll fix it ASAP, hopefully this weekend.

@sokra
Copy link
Contributor

sokra commented Feb 14, 2014

I would favor to not have an object {from: ..., to: ...}, but only a relative directory where B is relative to A.

from to relative
/dir /dir . or omit
/dir/a /dir/b ../b
/dir /dir/b b
/dir/a/sub /dir ../..
/dir /dir/b/sub b/sub

Additionally an absolute url should be supported as 3rd argument.

@lydell
Copy link
Contributor Author

lydell commented Feb 14, 2014

I actually considered that first—but the other way around: A directory either absolute or relative from B to A. Isn't that what you meant? I don't understand how to construct the source paths otherwise.

The reason I didn't go that way was because using {to: ..., from: ...} we can get simpler paths in some cases:

Consider this directory structure:

  • dir/
    • foo.js
    • foo.js.map
    • a/
      • b/
        • foo.coffee
        • foo.min.js
        • foo.min.js.map

Using ../.. as the third parameter would produce ../../a/b/foo.coffee as a source in foo.min.js.map, while using {from: 'dir', to: 'dir/a/b'} would simply result in foo.coffee.

Or this directory structure:

  • temp/
    • foo.js
    • foo.js.map
  • public/
    • foo.coffee
    • foo.min.js
    • foo.min.js.map

../temp -> ../public/foo.coffee vs. {from: 'temp', to: 'public'} -> foo.coffee

But those kind of cases are the only cases I can think of that would produce less than optimal source paths: When you first "climb up" a bit, and then a source happens to "climb down" a bit again. And that doesn't feel like a common use case. A single path to the dirname of map A relative to map B would solve autoprefixer's use cases/issues as well as the test case of this PR exactly as well as {to: ..., from: ...}.

So now it feels like a silly optimization. @sokra's idea makes a much simpler and easier to understand API, and it would also solve the mistake I mentioned in a much simpler way than I had in mind.

@fitzgen, I'm gonna make this change, rebase and force push, so don't waste any time reviewing this right now (although a lot will of course be the same).

@lydell
Copy link
Contributor Author

lydell commented Feb 14, 2014

Done. Ready for feedback again :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't //b a protocol-relative url and should be threaded as absolute url? @fitzgen

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's possible. When I wrote these tests I played with the cd command and typing URLs in Firefox.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch @sokra. Yes, this should result in "/b". Can you add this assertion as well:

assert.equal(libUtil.join("http://example.com/foo", "//bar"), "http://example.com/bar"); 

Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

I actually should be this way:

assert.equal(libUtil.join('a/', '/b'), '/b'); assert.equal(libUtil.join("http://example.com/foo", "/bar"), "http://example.com/bar"); assert.equal(libUtil.join("http://example.com/foo", "//bar"), "http://bar"); assert.equal(libUtil.join("https://example.com/foo", "//bar.com/foo"), "https://bar.com/foo");
@fitzgen
Copy link
Contributor

fitzgen commented Feb 14, 2014

Thanks for the PR :) Will merge it when you've updated the patch based on comments above.

The paths in the `sources` property of source maps may be relative. If so, they are relative to the source map itself. When you apply a source map A to another source map B, the source in map B that is associated with map A will be replaced with sources from map A. If those sources are relative, they need to be rewritten to be relative to map B instead of map A. In order to do that, the `applySourceMap` method needs more information: The missing piece between map B and map A. It is now possible to give this information, using a third parameter: The path to map A, relative to map B. (It can also be absolute, of course.) This should be backwards compatible. If the third parameter is omitted, the method works exactly like before: It assumes that both maps are in the same directory, which makes all sources relative to both maps, which in turn requires no rewriting.
@lydell
Copy link
Contributor Author

lydell commented Feb 15, 2014

I've amended fixes for the nitpicks/code style stuff. Starting to work on util.join() fixes now.

- If aPath is a URL or a data URI, aPath is returned, unless aPath is a scheme-relative URL: Then the scheme of aRoot, if any, is prepended first. - Otherwise aPath is a path. If aRoot is a URL, then its path portion is updated with the result and aRoot is returned. Otherwise the result is returned. - If aPath is absolute, the result is aPath. - Otherwise the two paths are joined with a slash. - Joining for example 'http://' and 'www.example.com' is also supported.
@lydell
Copy link
Contributor Author

lydell commented Feb 15, 2014

There we go, I've addressed everything except #93 (comment) which needs clarification and discussion. Ready for another round of feedback :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should probably be simplified to if (aSourceMapPath).

@lydell
Copy link
Contributor Author

lydell commented Feb 17, 2014

I might have addressed #93 (comment) now.

@lydell
Copy link
Contributor Author

lydell commented Feb 17, 2014

lib/source-map/util.js is possibly a bit long now; Should we move urlParse, urlGenerate, normalize, join and relative into its own file (called perhaps url.js or path.js)?

@fitzgen
Copy link
Contributor

fitzgen commented Feb 17, 2014

Just a note: I probably won't be able to get to this pull request this week because I'm at a team offsite all week.

@lydell
Copy link
Contributor Author

lydell commented Feb 23, 2014

Hmm ... it would really be a lot easier if we could use the Node.js 'url' module. It’s on npm: https://www.npmjs.org/package/url. Is this out of question?

@lydell
Copy link
Contributor Author

lydell commented Feb 25, 2014

Really, it would be cool if we could use require("url") in Node.js, use resolve-url in the browser and Components.utils.import(foo) inside Firefox (I hope there is such a thing). Or am I dreaming? All of those do what we want: Resolve urls. Either case util.join should probably be called util.resolve. Currently, util.join is more of a mix between path.join and url.resolve. For example, util.join("../temp/app.js.map", "app.coffee") should be "../temp/app.coffee", not "../temp/app.js.map/app.coffee". This would simplify the description of the third parameter of .applySourceMap() as well: Instead of being the dirname of the path to the source map, it’d just be the path to the source map.

@lydell
Copy link
Contributor Author

lydell commented Feb 26, 2014

Thinking a bit more about it, I think we should do one of the following:

  • Rename util.join to util.resolve, and adjust it slightly so that it resolves like browsers (like Node.js url.resolve does). Pros: Mostly done. Cons: Breaks the unix philosophy, forces us to work on stuff we don’t care about the most (resolving urls instead of source maps).
  • Remove util.join and use some other library. Pros: Moves url resolving out of the way, more likely to get a robust, well-defined resolver. Cons: How to deal with different environments and building and stuff?

And then we should adjust the third argument of the .applySourceMap method (that this PR is really about) to be a path to a source map instead of the dirname of a source map. Much more sensible, in my opinion.

@fitzgen fitzgen merged commit 4dce61e into mozilla:master Feb 26, 2014
@fitzgen
Copy link
Contributor

fitzgen commented Feb 26, 2014

It would make sense to have a pluggable join/resolve thing going on, but it would need to be integrated with the build system. Feel free to file an issue to track progress.

@lydell lydell mentioned this pull request Mar 1, 2014
@lydell lydell deleted the apply-source-map branch June 6, 2014 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants