CloudCannon / bookshop

πŸ“š A component development workflow for static websites.
MIT License
249 stars 21 forks source link

Support astro:content inside Astro components in the Visual Editor #154

Closed oliverlynch closed 10 months ago

oliverlynch commented 1 year ago

I'm having an issue in the bookshop generate step when trying to use getCollection from astro:content inside astro components. My astro build succeeds, but bookshop generate fails.

I can reproduce this in the sendit astro template by adding the import inside any of the .jsx components.

import { getCollection } from "astro:content";

Bookshop version 3.6.5

Console output:

> npx @bookshop/generate
πŸ“š Generating Bookshop integrations

Looking for Bookshop component libraries...
Loading Bookshop from ./src
Loaded 14 components
Creating structures for all components...
Hydrating structures for nested components...
Loaded 1 Bookshop

Looking for output sites...
Found 1 site

Modifying output site at ./dist
Connected 0 component thumbnail(s)
Added live editing to 5 pages containing Bookshop components
 > src/shared/astro/Navigation.astro:2:30: error: Could not resolve "astro:content" (mark it as external to exclude it from the bundle)
    2 β”‚ import { getCollection } from "astro:content";
      β•΅                               ~~~~~~~~~~~~~~~

/Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:1493
  let error = new Error(`${text}${summary}`);
              ^

Error: Build failed with 1 error:
src/shared/astro/Navigation.astro:2:30: error: Could not resolve "astro:content" (mark it as external to exclude it from the bundle)
    at failureErrorWithLog (/Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:1493:15)
    at /Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:1151:28
    at runOnEndCallbacks (/Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:941:63)
    at buildResponseToResult (/Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:1149:7)
    at /Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:1258:14
    at /Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:629:9
    at handleIncomingPacket (/Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:726:9)
    at Socket.readFromStdout (/Users/oliverlynch/_millstream/clients/2022b.theoarhouse.au/node_modules/esbuild/lib/main.js:596:7)
    at Socket.emit (node:events:512:28)
    at addChunk (node:internal/streams/readable:324:12) {
  errors: [
    {
      detail: undefined,
      location: {
        column: 30,
        file: 'src/shared/astro/Navigation.astro',
        length: 15,
        line: 2,
        lineText: 'import { getCollection } from "astro:content";',
        namespace: '',
        suggestion: ''
      },
      notes: [],
      pluginName: '',
      text: 'Could not resolve "astro:content" (mark it as external to exclude it from the bundle)'
    }
  ],
  warnings: []
}

Node.js v19.7.0
bglw commented 1 year ago

Hey @oliverlynch πŸ‘‹

This is currently a limitation of the way Astro and Bookshop interact β€” though we should certainly display this error better. Since Bookshop previews in the browser without access to your source files, interacting with your content collections directly isn't possible from within a component.

We have some future plans to help support this β€” but there isn't anything in place just yet.

edmeehan commented 1 year ago

If we don't have a solution for this, does someone have a workaround alternative? I can't conditionally import a component file so not sure if there is a simple solution or if this needs to be complex.

oliverlynch commented 1 year ago

I managed to work around this by getting all of my collection information outside of the bookshop:live element, then passing it in; for example generating getStaticPaths() with getCollection then passing my static paths list as a prop allowing me to generate a navigation component inside bookshop:live.

edmeehan commented 11 months ago

I managed to work around this by getting all of my collection information outside of the bookshop:live element, then passing it in; for example generating getStaticPaths() with getCollection then passing my static paths list as a prop allowing me to generate a navigation component inside bookshop:live.

I used Astro middleware to pass collections to Astro.locals to get around this issue. Just posting this if others are running into the same issue.

silveltman commented 11 months ago

I managed to work around this by getting all of my collection information outside of the bookshop:live element, then passing it in; for example generating getStaticPaths() with getCollection then passing my static paths list as a prop allowing me to generate a navigation component inside bookshop:live.

I used Astro middleware to pass collections to Astro.locals to get around this issue. Just posting this if others are running into the same issue.

Hi @edmeehan , can you post you code on this please? I'm not familiar with astro middleware but this could really help me out.

Thanks! :)

silveltman commented 11 months ago

Update on this;

I get the error even when 'astro:content' is being used outside the bookshop:live component. I have a Layout wrapping it, which uses 'astro:content' and 'astro:transitions':

<Layout>
  <RenderBookshop
    bookshop:live
    settings={settings}
    preview={preview}
    collections={collections}
  />
</Layout>

For me it causes bookshop:live to bring more problems than benefit unfortunately.

oliverlynch commented 11 months ago

I used Astro middleware to pass collections to Astro.locals to get around this issue. Just posting this if others are running into the same issue.

Great idea to use middleware - I've moved my navigation generation to middleware which means I no longer need to pass everything down to my site navigation component. This is probably the best workaround for now, although astro can't seem to read the type declarations I've set for local in env.d.ts...

can you post you code on this please? I'm not familiar with astro middleware but this could really help me out.

The basic example on astro docs for middleware works pretty much out of the box with bookshop and the docs go more depth than I can add here, but in my middleware I am getting the "pages" collection to generate navigation, and attaching it to locals so it can be accessed anywhere. I am also attaching all of the collections to locals so that components can reference other pages without using astro:content.

import { defineMiddleware } from "astro:middleware";
import { getCollection, type CollectionEntry } from "astro:content";
import { collections } from "src/content/config";

export const onRequest = defineMiddleware(
  async (ctx: Record<string, any>, next) => {
    const pages = await getCollection("pages", (page) => {
      return page.data.status === "online";
    });

    ctx.locals.tree = makeTree(pages, ctx.params.slug);

    ctx.locals.collections = await getAllCollections();

    return next();
  }
);

The code inside the defineMiddleware function is run for each page that is generated, and anything that you attach to the locals will be available at Astro.locals.

I get the error even when 'astro:content' is being used outside the bookshop:live component. I have a Layout wrapping it, which uses 'astro:content' and 'astro:transitions':

Thats strange, I've just tried adding both astro:content and astro:transitions into my layout, organised similarly to your setup:

<Layout {...props} {id} {paths} {currentPath}>
  <Page bookshop:live contentBlocks={props.content_blocks} />
</Layout>

...and bookshop seems happy enough adding live editing. The only thing I could think of at the top of my head that might cause it is if you are passing the collections as a promise to the bookshop:live component, but I quickly tested it and bookshop didn't seem to mind.

silveltman commented 11 months ago

@oliverlynch Thanks for the testing! Seems weird that I'm running into this issue then.

Something I also ran into when trying out the middleware appreach is that Astro.locals seems to be undefined in the CloudCannon live editing. No build errors this time, but I could not actually use the data. Have you also experienced this @oliverlynch ?

oliverlynch commented 11 months ago

Something I also ran into when trying out the middleware appreach is that Astro.locals seems to be undefined in the CloudCannon live editing. No build errors this time, but I could not actually use the data. Have you also experienced this

I've tested a bunch of stuff out and it looks like you're right - both passing into the bookshop:live element with props and middleware result in undefined variables in the live editor...

It seems to me that in live mode only props defined within bookshop:live or in the component's __.bookshop.yml file are actually set. I thought the middleware setup had worked as bookshop compiled correctly and the navigation appeared in the live editor, but accessing Astro.locals inside bookshop:live is undefined as you have stated. I assumed passing props worked because it appears to work with the standard component properties normally, and I hadn't used any extra props - This should possibly be mentioned in the bookshop:live documentation.

This limitation is frustrating as centralising my navigation to middleware was partially to help create a page index component, but it won't work inside the live editor. For now I'll probably just use the ENV_BOOKSHOP_LIVE to display a placeholder during live editing, but obviously this is suboptimal. Hopefully we can get support for middleware and/or passing props within bookshop:live in the future.

Tate-CC commented 10 months ago

Hi @silveltman, @oliverlynch, and @edmeehan πŸ‘‹

Yesterday we released Bookshop 3.9.0 which includes support for using astro:content inside Bookshop components in the visual editor. There is a small caveat that the collection item's content and render functions are just placeholders since we can't access the actual file in the visual editor. Otherwise all the collection item's data/frontmatter will be available and any build issues should be resolved.

silveltman commented 10 months ago

Hi @Tate-CC ,

Sounds awesome! However, I just tried it out and my components are not rendering. Just to be sure, do the component actually work with 'astro:content' OR does the live editor skip these components and render the rest?