-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Implement support for classic scripts and automatic module/nomodule #6247
Conversation
This pull request has been linked to and will mark 2 tasks as "Done" when merged:
|
}); | ||
|
||
let convertLoc = loc => ({ | ||
filePath: relativePath, | ||
start: { | ||
line: loc.start_line, | ||
line: loc.start_line + Number(asset.meta.startLine ?? 1) - 1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of a hack. Should we do something in core for this?
packages/transformers/js/src/lib.rs
Outdated
decls: &decls, | ||
used_env: &mut result.used_env | ||
}, | ||
config.source_type != SourceType::Script |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should process.env be supported in scripts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it would be nice? But we could also add that later if it comes up. Was there a reason you disabled it?
The only thing we can't support if course is the process
Node global replacement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mainly I was just trying to make scripts as close to real browser JS as possible, and not enable any CommonJS/Node features.
1221253
to
ddf378a
Compare
# Conflicts: # packages/core/core/src/Environment.js # packages/core/core/test/Environment.test.js # packages/core/core/test/TargetRequest.test.js # packages/transformers/html/src/dependencies.js # packages/transformers/js/core/src/global_replacer.rs # packages/transformers/js/src/JSTransformer.js
delete attrs.type; | ||
attrs.nomodule = ''; | ||
attrs.src = asset.addURLDependency(attrs.src, { | ||
// Keep in the same bundle group as the HTML. | ||
priority: 'parallel', | ||
env: { | ||
sourceType, | ||
outputFormat: 'global', | ||
loc, | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these statements do anything? attrs
is a shallow clone and it was already merged into copy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attrs
is referenced by copy
, so this is modifying the attrs of the copy.
@@ -6,7 +6,7 @@ import {createEnvironment} from '../src/Environment'; | |||
describe('Environment', () => { | |||
it('assigns a default environment with nothing passed', () => { | |||
assert.deepEqual(createEnvironment(), { | |||
id: '1051b306b4fe6e64', | |||
id: '2163092a3dd83a5f', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😞
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be neat if we used/made something along the lines of Jest's stringMatching matcher: https://jestjs.io/docs/expect#expectstringmatchingstring--regexp
@@ -428,7 +428,7 @@ describe('sourcemaps', function() { | |||
source: inputs[0], | |||
generated: raw, | |||
str: 'const local', | |||
generatedStr: 'const o', | |||
generatedStr: 'const t', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😞
@@ -70,6 +70,7 @@ impl<'a> Fold for GlobalReplacer<'a> { | |||
attributes: None, | |||
is_optional: false, | |||
is_helper: false, | |||
source_type: Some(SourceType::Module), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be None
as in fs.rs
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
file dependencies don't need a source type, but in this case it's referring to a require that should be treated as a module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -148,7 +148,7 @@ export class CJSOutputFormat implements OutputFormat { | |||
return [res, lines]; | |||
} | |||
|
|||
buildBundlePostlude(): string { | |||
return ''; | |||
buildBundlePostlude(): [string, number] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Maybe a type alias or a comment explaining what these values in the return type mean?
Found my way here from the new "Browser scripts cannot have imports or exports" error. Why does this restriction exist, and can it be suppressed if we know that Parcel is going to bundle away all of the import statements? |
You just need to add |
What if I only want to produce one bundle for all browsers? If everything's being bundled together, what does it matter if the bundle is being loaded as a module or not? Seems unusually opinionated for Parcel to force there to be two essentially identical versions of the bundle; what are we getting for this? |
The main reason is to ship less code to most browsers. 92.64% of global web traffic is via a browser that supports ES modules natively. This also means that we can ship a lot more modern JS features like classes etc. without transpilation which results in reduced bundle sizes. Basically, unless you have to support IE 11 still, you will get one bundle anyway, and if you do, then most of your users will need to download less code, and only IE 11 users will need to download the larger legacy bundle. |
Hmm, okay. I still don't see why Parcel couldn't just support script tags without the module type by doing exactly what it does to generate a nomodule bundle, or what transpilation of other JS features has to do with modules and imports/exports (which are completely eliminated in bundles regardless of what features the browser supports), but I can try targeting modules and seeing if that causes any problems for me. |
Yeah it's because scripts behave differently in other ways as well. Whereas modules are self contained, scripts run in the global context meaning any variables created become global. This might be seen as a bad idea but there are lots of legacy libraries like jquery that rely on this assumption. The goal of Parcel is to work exactly like if you ran your code natively in a browser but also make your code portable to older browsers as well if needed. Treating scripts as modules in Parcel v1 was a mistake that we're trying to correct in v2. |
Ah, so this is a way to support <script src="jquery.js"></script>
<script type="module" src="app.js"></script> such that on old browsers, jquery.js is loaded as-is but app.js is wrapped in an IIFE or something to keep it from adding to the global scope? |
Yeah exactly. It solves two separate problems that we've had for a long time - legacy script support, and automatic modern/legacy builds for reduced bundle sizes. It'll be a transition for everyone to get used to for sure. If you think there's an improvement we can make to the error message, we'd love to hear suggestions. Maybe we can add a link to the docs with some more info or something. |
This is awesome! I have classic imports but they still seem to be wrapped as modules.
which results in:
|
Yes
Can you open an issue for that with a full code sample (not sure if there's more needed that you already posted)? |
This implements #5398 on top of the new SWC-based JS transformer. It takes a slightly different approach: rather than adding support for multiple contexts to the environment, it adds a
sourceType
field, which can either bescript
for classic scripts, ormodule
for ES modules.The other difference from the original implementation is the way scripts hoist their top-level variables into the global scope. In the previous implementation, this was done by attempting to find all top-level vars and assign them to the global object. In this implementation, the code for the entry asset of script bundles itself is moved outside the bundle wrapper. Any
require
calls that the entry asset might do (e.g. for runtimes) are replaced with aparcelRequire
call.Otherwise, everything is the same as the previous PR. See there for details.
The posthtml parser fork for adding location information will be removed once posthtml/posthtml-parser#63 is merged and released.DONEClose T-136. Fixes #333. Closes #3011.