question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[RFC] Import query params

See original GitHub issue

Problem Statement

Currently in Parcel v2, dependencies are resolved to files and the resulting file paths are matched against the configured globs in the .parcelrc file to see which pipeline the file should be transformed by. This works for most cases, but sometimes you might want to force a dependency down a specific pipeline. For instance, let’s say you are building documentation for a React component. By default, importing the React component will bundle the component as a JavaScript module, but what if you additionaly want the component’s source as plain text? Maybe you also want to run some transformer over the component that extracts what prop types it expects and bundle that as well.

A very naive solution that would work for any and all tooling would be to just create files for the source text and prop types in a prebuild step and then you can import three individual files. The downside to this approach is that you would then have two build steps, which means you have two build commands that you have to string together and two separate watchers if you want to build in watch mode. Imagine in this scenario you updated MyComponent.js. Both the prebuild and the bundle watchers would pick up the change and start rebuilding. Then when the prebuild step finishes, the derived files are updated, which causes the bundler to rebuild yet again. It makes for a much better developer experience if this can all be handled in one process.

With webpack, this is usually solved by using inline loaders.

import MyComponent from './MyComponent';
import MyComponentSrc from '!!raw-loader!./MyComponent';
import MyComponentPropTypes from `!!extract-react-types-loader!./MyComponent`;

One downside to this approach is that the detail that you are using webpack leaks into your source code. Ideally this could be done in a more bundler agnostic way.

Proposed Solution: Import Query Params

The ECMAScript modules standard (ESM for short) is now supported across all modern browsers and will soon be supported by Node.js as well. One neat thing about ESM is that it essentially treats all module specifiers as URLs, which means adding query parameters is perfectly valid syntax and not something bespoke to a specific bundler. This seems like a great way to specify how you would like a dependency loaded:

import MyComponent from './MyComponent.js';
import MyComponentSrc from './MyComponent.js?as=raw-text';
import MyComponentPropTypes from './MyComponent.js?as=react-types';

So what would it take for Parcel to support this? The most straightforward approach requires no changes to Parcel’s configuration so it’s got that going for it. All you would have to do to force a file to be transformed by a different pipeline is add a query param to the module specifier and configure a pipeline in your .parcelrc with a glob that matches it.

{
  "transforms": {
    "**/*.js\?as=react-types": ["parcel-transformer-extract-react-types"]
  }
}

The only change we would need to make in Parcel would be to slice off the query params from the module specifiers to resolve the file and then tack them back on to resolve the transformation pipeline. Perhaps it would be nice to have a special glob matcher that makes it easier to write globs of this type. Query params could also be passed on to transformers as well.

import profilePic from './profile.png?width=300&height=300';

Drawbacks

  • Since this solution is based on ESM it makes it pretty JavaScript centric. Some other languages and module formats support module specifiers as URLs, but what about the ones that don’t? Parcel will either end up encouraging developers to write non standard code that only works because it’s run with Parcel or this behavior may not end up being supported in all types of files.
  • Text matching can get hairy. It seems possible that this could cause Parcel config files to balloon especially considering there could be multiple query parameters that might influence what pipeline a file should be transformed by.
  • This is more of a drawback for the transformation configuration model in general, but one thing it doesn’t accommodate is influencing what pipeline to use based on the file that defined the dependency. For instance you might want something different when importing as=url from a JavaScript file vs a CSS file. This could be solved by using a more specific parameter like as=js-url, but the problem still remains for imports without query parameters. If we decided to change the configuration to allow for this type of thing then it could potentially lead to a cleaner/smaller/simpler config.

Alternatives

One alternative would be to make a change to Parcel’s configuration. I do like how simple Parcel’s configuration is at the moment, but I could imagine this not working perfectly for all cases. I’m not aware of any of these cases at the moment, but that’s what the RFC is for. If no one brings up a compelling case that proves that a change to configuration would either be necessary or would be a much cleaner solution, then this seems like a reasonable place to start.

Another alternative could be to use a special extension like extensions leading with a certain number of underscores.

import MyComponent from './MyComponent.js';
import MyComponentSrc from './MyComponent.js.__raw';
import MyComponentPropTypes from './MyComponent.js.__react-types';

Parcel could split the module specifier like it would have to with query parameters and use the parts before the special extension to resolve the file and tack the special extension back on to resolve the pipeline. You could also have a prebuild step that actually creates these files which means the imports would still work with other tooling. One potential issue is that the special extension might conflict with legitimate extensions.

Open Questions

  • ESM treats module specifiers as URLs but CommonJS does not. Should Parcel only support this feature on module specifiers that are known to be URLs or should it be less strict?
  • Should this be supported in node_modules or only source code?
  • What happens when the configured transformer changes the type of the asset? Do we use the query parameters to match the next pipeline or do they only apply to the first pipeline? (Seems like you would probably have to use them to match if you wanted any control over whether to jump to a new pipeline or not)
  • Any examples where this type of configuration doesn’t cut it?

cc @devongovett @wbinnssmith @jamiebuilds

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:16
  • Comments:26 (20 by maintainers)

github_iconTop GitHub Comments

7reactions
svr93commented, Jan 8, 2020

I think it will be good to use a single syntax for the whole JS ecosystem so I suggest combining different solutions into one - https://github.com/tc39/proposal-module-attributes/issues/22

5reactions
jakearchibaldcommented, Sep 2, 2019

My opinions here may not fit with what Parcel is trying to achieve, so feel free to ignore.

I don’t think build systems should hide. If a file is to be transformed in an uncommon way, it should be obvious from the import that something special is happening.

import text from './whatever.js?as=text';

The above doesn’t really look special. It’s just a regular URL.

If the above is passed to a system that doesn’t recognise the ‘as’ query parameter, it’ll interpret the module as JavaScript, which feels like a bad fallback. If the transform doesn’t have an ‘acceptable’ fallback (if a file fails to minify, the result still works), the import should fail as soon as possible.

Other ideas:

import text from 'as-text:./whatever.js';

The as-text scheme is invalid in module identifiers, so it’s obvious something special is happening. Without the transform, this will fail at the parse stage.

import text from './whatever.js/text';

This treats whatever.js as a directory, when it isn’t. Transforms would define what a file could ‘contain’. Without the transform, this will fail at the fetch stage. That might be too late, and the special nature of the import may not be obvious enough.

  • Should this be supported in node_modules or only source code?

I’m not sure if files within node_modules should support this syntax. However, I think files outside of node_modules should be able to use it on files within node_modules.

Eg:

import text from 'whatever-package?as=text';
Read more comments on GitHub >

github_iconTop Results From Across the Web

How to pass import parameters to sap RFC
I am new to MuleSoft , I want to pass Query string value to Import parameter of SAP RFC. <http:listener config-ref="HTTP_Listener_Configuration" ...
Read more >
Multiple Values in one Import Parameter of RFC.
I have a requirement in which I have to pass RANDOM and MULTIPLE values to one import parameter of RFC.
Read more >
RFC 8040: RESTCONF Protocol
1. The "content" Query Parameter The "content" query parameter controls how descendant nodes of the requested data nodes will be processed in the...
Read more >
ios - How can I build a URL with query parameters containing ...
It can add the QueryItem to your existing URL. extension URL { func appending(_ queryItem: String, value ...
Read more >
Working With URLComponents In Swift - Cocoacasts
Remove the contents of the playground and add an import statement for the ... The keys and values of the query parameters need...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found