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

Preserve correctness of import.meta.url in bundled modules. #300

Merged
merged 10 commits into from
May 3, 2018

Conversation

usergenic
Copy link
Contributor

Note that in the case where an imported module's URL is identical to the bundle URL, no modification to the module code is needed before bundling it, but for all other cases where import.meta occurs somewhere in the document contents, we construct a module-scoped stand-in variable and swap other references to import.meta to use the stand-in, which is declared as an object-literal like {...import.meta, url: new URL(relativeUrlToModule, import.meta.url)}.

@usergenic
Copy link
Contributor Author

Fixes #294

const aUrl = analyzer.resolveUrl('a.js')!;
const bUrl = analyzer.resolveUrl('subfolder/b.js')!;
const cUrl = analyzer.resolveUrl('c.js')!;
const dUrl = analyzer.resolveUrl('d.js')!;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is dUrl 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.

No, thanks! I was adding another test case and then gave up; forgot that was there. Removed.

moduleFile: babel.File,
relativeUrl: FileRelativeUrl): babel.File {
// Generate a stand-in for any local references to import.meta...
// const __bundledImportMeta = {...__importMeta, url: __bundledImportURL};
Copy link
Contributor

Choose a reason for hiding this comment

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

What is __importMeta 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.

I previously had a local variable stand-in just for import.meta that is now gone. Old comment. Fixed to import.meta! Thanks!

traverse(newModuleFile, {
noScope: true,
MetaProperty: {
enter(path: NodePath) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can avoid the cast below by doing: enter(page: NodePath<babel.MetaProperty>) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AH! Thanks! I have several of these in bundler code to fix! Will do now.

// URL has changed as a result of bundling.
const relativeUrl =
ensureLeadingDot(this.bundler.analyzer.urlResolver.relative(
this.bundle.url, id as ResolvedUrl));
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the cast of id needed? It looks like it's a ResolvedUrl already

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. Fixed.

ensureLeadingDot(this.bundler.analyzer.urlResolver.relative(
this.bundle.url, id as ResolvedUrl));
const newAst = this._rewriteImportMetaToBundleMeta(
document.parsedDocument.ast as babel.File, relativeUrl);
Copy link
Contributor

Choose a reason for hiding this comment

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

We could put this constraint into the type system if we added a type checking function that checks that document is a Document<ParsedJavascriptDocument>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right and in fact this code probably should have more guarantees/verification of the type of document it is loading anyways. I'll add a TODO here for this.

// Generate a stand-in for any local references to import.meta...
// const __bundledImportMeta = {...__importMeta, url: __bundledImportURL};
const bundledImportMetaName = '__bundledImportMeta';
const bundledImportMetaDeclaration =
Copy link
Member

Choose a reason for hiding this comment

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

Could maybe use the @babel/template ast template string literal tag here instead of building it manually with the API? (Example: https://github.com/Polymer/tools/blob/master/packages/build/src/babel-plugin-dynamic-import-amd.ts#L64)

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 love this idea. I even made a personal TODO note earlier today "Look up to see if there's a template string builder that would make this code cleaner."

I would like to consider bringing that approach to all of the ast production in bundler, but I'm going to hold off on a separate PR to bring that machinery in here. Adding TODO. It's a great idea and would make this code a lot better.

I should note that as of right now though, Bundler does not have any babel parsing in it and relies purely on Analyzer. Use of the template literal stuff would essentially require ensuring I either find a nice way to clone the Analyzer's babel parsing stack or rebuild it locally.

Copy link
Member

Choose a reason for hiding this comment

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

SGTM

babel.identifier('import'), babel.identifier('meta'))),
babel.objectProperty(
babel.identifier('url'),
babel.newExpression(
Copy link
Contributor

Choose a reason for hiding this comment

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

We want to get the .href property of this new url, as import.meta.url is a string

Copy link
Contributor Author

Choose a reason for hiding this comment

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

DERP! Really good catch. Fixed.

@usergenic usergenic requested review from rictic and aomarks May 3, 2018 05:49
@usergenic
Copy link
Contributor Author

@rictic & @aomarks comments addressed

@usergenic
Copy link
Contributor Author

Awww nuts. Something probably in the package-lock has updated causing something to break during gulp-typescript... https://ci.appveyor.com/project/Polymer/tools/build/1.0.858#L127

@usergenic
Copy link
Contributor Author

Bunch of things like Property '_transform' in type 'GulpTransform' is not assignable to the same property in base type 'Transform'.

@usergenic
Copy link
Contributor Author

Ah this failure is unrelated to branch. Created a new PR to start to fix the TS errors related to updating package locks at #309

// Generate a stand-in for any local references to import.meta...
// const __bundledImportMeta = {...import.meta, url: __bundledImportURL};
// TODO(usergenic): Consider migrating this AST production mishmash into the
// `ast` tagged template literal available like this:
Copy link
Contributor

Choose a reason for hiding this comment

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

+1. Much easier to read and write.

// TODO(usergenic): Consider migrating this AST production mishmash into the
// `ast` tagged template literal available like this:
// https://github.com/Polymer/tools/blob/master/packages/build/src/babel-plugin-dynamic-import-amd.ts#L64
const bundledImportMetaName = '__bundledImportMeta';
Copy link
Contributor

Choose a reason for hiding this comment

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

We should generate this name to be conflict free with identifiers at import.meta use sites.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added issue. #310

babel.newExpression(
babel.identifier('URL'),
[
//
Copy link
Contributor

Choose a reason for hiding this comment

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

remove? Or if it's for formatting, move to previous line?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

clang-format went crazy so I put this in here to get it to behave. I can annotate further. This of course goes away after I switch to ast template literal...

babel.identifier('href')))
]))
]);
const newModuleFile = clone(moduleFile);
Copy link
Contributor

Choose a reason for hiding this comment

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

why do you have to clone it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because I modify in-place from document returned by analyzer and I'm trying to be a good citizen.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1. Modifying documents returned by analyzer will lead to the worst kind of weird bugs with spooky action at a distance.

return;
}
const bundledImportMeta = babel.identifier(bundledImportMetaName);
rewriteObject(metaProperty, bundledImportMeta);
Copy link
Contributor

Choose a reason for hiding this comment

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

use path.replaceWith instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay. That makes sense here. Replace'd

babel.identifier('href')))
]))
]);
const newModuleFile = clone(moduleFile);
Copy link
Contributor

Choose a reason for hiding this comment

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

+1. Modifying documents returned by analyzer will lead to the worst kind of weird bugs with spooky action at a distance.

@usergenic usergenic merged commit f313b16 into master May 3, 2018
@usergenic usergenic deleted the bundler-import-meta-fix branch May 3, 2018 19:02
@keanulee
Copy link
Contributor

I'm seeing this transform present on the es6-bundled build output which doesn't make since it uses transformModulesToAmd and makes import undefined/an invalid keyword in FF. Is this expected, or is there some other JS config I need in that build configuration @usergenic ?

const bundledImportMeta = { ...import.meta,
  url: new URL('...', import.meta.url).href
};

@usergenic
Copy link
Contributor Author

@keanulee the fix was to pin a specific dependency (that was suddenly causing errors after a release a couple days ago) until babel's minify preset is more stable #380

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

Successfully merging this pull request may close these issues.

6 participants