shoelace-style / shoelace

A collection of professionally designed, every day UI components built on Web standards. SHOELACE IS BECOMING WEB AWESOME 👇👇👇
https://webawesome.com
MIT License
12.86k stars 821 forks source link

Rollup: Missing Export: 'setAssetPath' is not exported #183

Closed sslotsky closed 4 years ago

sslotsky commented 4 years ago

Describe the bug

Using 2.0.0-beta.15

Following the instructions for important components from a Rollup app, I'm unable to build my Stencil project as soon as I attempt to import setAssetPath.

[ ERROR ]  Rollup: Missing Export: ./src/components/live-jazz/live-jazz.tsx:5:9
           'setAssetPath' is not exported by
           ./node_modules/@shoelace-style/shoelace/dist/collection/index.js, imported by
           ./src/components/live-jazz/live-jazz.tsx

      L4:  import { setAssetPath, SlButton } from "@shoelace-style/shoelace";
      L6:  setAssetPath((document.currentScript as HTMLScriptElement).src);

Inspecting the file node_modules/@shoelace-style/shoelace/dist/collection/index.js reveals that it's completely empty. On the other hand, setAssetPath does seem to be exported form node_modules/@shoelace-style/shoelace/dist/custom-elements/index.js. So perhaps something is just off with the build configuration for Shoelace.

To Reproduce

From another Stencil project:

  1. Use npm to install shoelace at 2.0.0-beta.15
  2. Modify stencil.config.ts according to the shoelace installation docs for rollup
  3. From one of the project's components, try to import and use setAsset path as shown in the docs
  4. Run the build
  5. Observe Missing Export error

Expected behavior

The compiler should be able to resolve imports into shoelace and build successfully.

Snippets

Stencil config:

import { Config } from "@stencil/core";
import { postcss } from "@stencil/postcss";
import copy from "rollup-plugin-copy";
import { resolve } from "path";
import postCSSPresetEnv from "postcss-preset-env";

export const config: Config = {
  namespace: "live-jazz",
  plugins: [
    postcss({
      plugins: [
        postCSSPresetEnv({
          features: {
            "custom-media-queries": true,
            "nesting-rules": true,
          },
        }),
      ],
    }),
    copy({
      targets: [
        {
          src: resolve(
            __dirname,
            "node_modules/@shoelace-style/shoelace/dist/shoelace/icons"
          ),
          dest: resolve(__dirname, "dist"),
        },
      ],
    }),
  ],
  outputTargets: [
    {
      type: "dist",
      esmLoaderPath: "../loader",
    },
    {
      type: "docs-readme",
    },
    {
      type: "www",
      serviceWorker: null, // disable service workers
    },
  ],
};

Sample component:

import { Component, h, State, getAssetPath } from "@stencil/core";
import Soundfont from "soundfont-player";
import "@shoelace-style/shoelace/dist/shoelace/shoelace.css";
import { setAssetPath, SlButton } from "@shoelace-style/shoelace";

setAssetPath((document.currentScript as HTMLScriptElement).src);
customElements.define("sl-button", SlButton);

import { keys } from "../../utils";

function getNote(n: number) {
  return {
    note: keys[n % 12].note,
    octave: Math.floor(n / 12) - 1,
  };
}

@Component({
  tag: "live-jazz",
  styleUrls: ["../styles.css", "styles.css"],
  assetsDir: "../../assets",
  shadow: true,
})
export class LiveJazz {
  @State() access: WebMidi.MIDIAccess;
  @State() inputId: string;
  @State() activeNotes: string[] = [];
  @State() starting: boolean;

  ctx: AudioContext;
  player: Soundfont.Player;

  start = async () => {
    const t = setTimeout(() => {
      this.starting = true;
    }, 500);

    this.ctx = new AudioContext();
    this.player = await Soundfont.instrument(this.ctx, "electric_piano_2");
    this.access = await navigator.requestMIDIAccess({
      sysex: true,
    });

    clearTimeout(t);
    this.starting = false;
  };

  setInput = (id: string) => () => {
    this.inputId = id;
    this.player.listenToMidi(this.access.inputs.get(this.inputId));
    this.player.on("start", (...args: number[]) => {
      const { note, octave } = getNote(args[1]);
      this.activeNotes = this.activeNotes.concat(`${note}${octave}`);
    });

    this.player.on("stop", (...args: number[]) => {
      const { note, octave } = getNote(args[1]);
      this.activeNotes = this.activeNotes.filter(
        (n) => n !== `${note}${octave}`
      );
    });
  };

  render() {
    if (this.starting) {
      return (
        <img
          alt="Spinning Notes"
          class="spin"
          src={getAssetPath("../../assets/notes.png")}
        />
      );
    }

    if (this.inputId) {
      return (
        <scott-free octaves={5} activeNotes={this.activeNotes}></scott-free>
      );
    }

    if (navigator.requestMIDIAccess) {
      if (this.access) {
        if (this.access.inputs.size === 0) {
          return <p>No MIDI inputs detected</p>;
        } else {
          return (
            <div class="inputs">
              {[...this.access.inputs.values()].map((input) => [
                <button type="button" onClick={this.setInput(input.id)}>
                  Use {input.name}
                </button>,
              ])}
            </div>
          );
        }
      } else {
        return (
          <sl-button type="button" onClick={this.start}>
            Start
          </sl-button>
        );
      }
    }

    return [
      <p class="notice">Your browser does not support Web MIDI.</p>,
      <p class="notice">
        <a target="_blank" href="https://www.google.com/chrome/">
          Please try Chrome browser instead.
        </a>
      </p>,
    ];
  }
}
claviska commented 4 years ago

I didn't realize you were using Stencil when I tweeted you last night. The instructions are a bit different in this case.

Stencil is aware of the library because of the collection properties in package.json:

  "collection": "dist/collection/collection-manifest.json",
  "collection:main": "dist/collection/index.js",

This provides Stencil with some first-class insight into the package, so you can import the library like this:

import '@shoelace-style/shoelace';

And then just attach the supporting stylesheet to supply design tokens from:

@shoelace-style/shoelace/dist/shoelace/shoelace.css

Doing this, assets are loading as expected (icon is the only component that uses icons right now):

image

That said, I'm not sure why that import is failing, but I've tested it to work in webpack and Rollup independently and it works outside the context of a Stencil app.

sslotsky commented 4 years ago

Success!!! Thank you :bow:

image