Skip to content

Proposal: explicit named imports for non-JS/CSS assets #3722

Open
@gaearon

Description

@gaearon

Problem

We currently allow you to do this:

import logo from './logo.png';

After getting used to it, you’ll probably be comfortable with this giving you a URL.

But what about other types? For example, what should this return?

import doc from './doc.md';

Markdown source? Compiled HTML? An AST?

What about this?

import Icon from './icon.svg';

Should this give you a link? The SVG content? A React component?

The usual answer is “decide it for yourself in the configuration file”. However, that doesn’t work for CRA so we decided to treat all unknown extensions as URL imports. This is not ideal because in some cases it just doesn’t make any sense, and in others there are advanced (but still relatively common) use cases that aren’t satisfied.

Proposal

What if we allowed to user to pick what they want, from a limited supported subset per filetype?

import { url as logoUrl } from './logo.png';
import { html as docHtml } from './doc.md';
import { ReactComponent as Icon } from './icon.svg';

Named imports are checked by webpack so you’d get a compile error if you use an unsupported one.

Things that are unused will be tree shaken so if you only use e.g. HTML of Markdown files, their source won’t be bundled. Same for SVGs (whether you consume them as raw source, URLs, or React components).

Other zero-configuration tools can also adopt this approach.

Concerns

  • What do we do with the default import? Ideally I’d like to forbid it for anything other than JS/CSS because the intent is not clear for asset files (which version do you get?) We could do this with a lint rule.
  • If we make the breaking change, how do we update the consumers? We could write a codemod (it should be very simple).
  • It would be nice to coordinate this across at least a few other projects (e.g. @ndelangen Storybook). Maybe @KyleAMathews (Gatsby) @devongovett (Parcel) @rauchg (Next) would also be interested? I imagine we’ll need to write a multi-file Webpack loader for this, but I don’t see why other bundlers couldn’t adopt a similar convention.
  • Build performance: generating all possible content variations can be too slow. Ideally it would be nice if loaders had information about which import was used.

Thoughts?

Activity

gregberge

gregberge commented on Jan 9, 2018

@gregberge

I think it is a very good thing to adopt a convention on it.

Even without talking about zero-configuration, today we can't use several Webpack loaders safely. We had the problem with svgr + url-loader. I made a workaround to solve it but it should not be a workaround.

I think we should add @sokra in this discussion, Webpack could force it by design.

dmitriid

dmitriid commented on Jan 9, 2018

@dmitriid

What do we do with the default import? Ideally I’d like to forbid it for anything other than JS/CSS because the intent is not clear for asset files (which version do you get?)

We may want to define the rules for the default as and extension of "pick what they want, from a limited supported subset per filetype".

import { url } from './logo.png'

import Logo from './logo.png'

Logo.url === url

Will this work in practice though?

gaearon

gaearon commented on Jan 9, 2018

@gaearon
ContributorAuthor

I think we should add @sokra in this discussion, Webpack could force it by design.

I think this needs to be a grassroots effort because enforcing anything like this from webpack’s side is going to take a super long time, even if they do it.

gaearon

gaearon commented on Jan 9, 2018

@gaearon
ContributorAuthor

@dmitriid

My thinking is that your example shouldn't build because it has a default import (import Icon). I think that if we adopt this approach we should just forbid default import and ask people to be explicit about what they want.

We could maybe keep default import for compatibility reasons for a while.

Munter

Munter commented on Jan 9, 2018

@Munter

If you want to foster a culture of zero config tooling, then it would be nice to come up with a convention that doesn't break module imports by default. Using the import statement for anything non-module is already forcing a specific tool chain dependency and thus a default config requirement on the developer.

I'd really love to see a syntax that was actually JS compatible by not overloading the import statement

gaearon

gaearon commented on Jan 9, 2018

@gaearon
ContributorAuthor

@Munter I understand this sentiment but it’s not very practical in my experience. We’re trying to do the best with what we got. FWIW, my proposal is actually closer to no lock-in than syntax like raw!./file.md because at least it is possible to auto-generate file.md.js that contains those exports. So in practice it can even work on Node if you have a codegen step.

Let’s not derail this thread with a general discussion whether import for assets is a good idea. The fact is that it solves many real use cases, and people want to do it. So the question here is how to make that more ergonomic and less confusing.

gaearon

gaearon commented on Jan 9, 2018

@gaearon
ContributorAuthor

I think in order to adopt this we’ll have to keep supporting default import for a while. This will make it possible for tools like Storybook to catch up. Then we can decide whether we want to deprecate support for the default import or not.

dmitriid

dmitriid commented on Jan 9, 2018

@dmitriid

@gaearon

I think I agree with you in principle, but I think there are potential downsides. Though as I'm writing them down, I feel like I might be wrong about some of my assumptions.

  • import * as Icon from 'icon.svg'

In the absence of a default export devs will still attempt the above, which will lead to multiple questions similar to "Why import * as React from 'react' and not import React from 'react'".

Why would dev need * from an svg? Because sometimes they just do :)

  • assumption that a default export is just an object with exposed/exported properties

Even though this assumption is incorrect due to how import/loading works, many devs (including me :) ) will assume the following code to be equivalent:

import {prop} from 'something';

// many will assume it to be equivalent to

import Obj from 'something';
const prop = Obj.prop

It doesn't help that a lot of transpiled code looks that way.

80 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @devongovett@dmitriid@dmwyatt@stereobooster@lifeiscontent

        Issue actions

          Proposal: explicit named imports for non-JS/CSS assets · Issue #3722 · facebook/create-react-app