diff --git a/packages/docs/src/routes/demo/cookbook/glob-import/examples/example1.tsx b/packages/docs/src/routes/demo/cookbook/glob-import/examples/example1.tsx new file mode 100644 index 00000000000..4d6941122f5 --- /dev/null +++ b/packages/docs/src/routes/demo/cookbook/glob-import/examples/example1.tsx @@ -0,0 +1,9 @@ +import { component$ } from '@builder.io/qwik'; + +export default component$(() => { + return ( +

+ Hi 👋, I'm a component defined in /examples/example1.tsx +

+ ); +}); diff --git a/packages/docs/src/routes/demo/cookbook/glob-import/examples/example2.tsx b/packages/docs/src/routes/demo/cookbook/glob-import/examples/example2.tsx new file mode 100644 index 00000000000..af1f2158762 --- /dev/null +++ b/packages/docs/src/routes/demo/cookbook/glob-import/examples/example2.tsx @@ -0,0 +1,9 @@ +import { component$ } from '@builder.io/qwik'; + +export default component$(() => { + return ( +

+ Hey 👋, I'm a component defined in /examples/example2.tsx +

+ ); +}); diff --git a/packages/docs/src/routes/demo/cookbook/glob-import/examples/example3.tsx b/packages/docs/src/routes/demo/cookbook/glob-import/examples/example3.tsx new file mode 100644 index 00000000000..d1a900489fb --- /dev/null +++ b/packages/docs/src/routes/demo/cookbook/glob-import/examples/example3.tsx @@ -0,0 +1,9 @@ +import { component$ } from '@builder.io/qwik'; + +export default component$(() => { + return ( +

+ Hello 👋, I'm a component defined in /examples/example3.tsx +

+ ); +}); diff --git a/packages/docs/src/routes/demo/cookbook/glob-import/index.tsx b/packages/docs/src/routes/demo/cookbook/glob-import/index.tsx new file mode 100644 index 00000000000..fa90285ca31 --- /dev/null +++ b/packages/docs/src/routes/demo/cookbook/glob-import/index.tsx @@ -0,0 +1,38 @@ +import { + type Component, + component$, + useSignal, + useTask$, +} from '@builder.io/qwik'; +import { isDev } from '@builder.io/qwik/build'; + +const metaGlobComponents: Record = import.meta.glob( + '/src/routes/demo/cookbook/glob-import/examples/*', + { + import: 'default', + eager: isDev ? false : true, + } +); + +export default component$(() => { + return ( +
+ + + +
+ ); +}); + +export const MetaGlobExample = component$<{ name: string }>(({ name }) => { + const MetaGlobComponent = useSignal>(); + const componentPath = `/src/routes/demo/cookbook/glob-import/examples/${name}.tsx`; + + useTask$(async () => { + MetaGlobComponent.value = isDev + ? await metaGlobComponents[componentPath]() // We need to call `await metaGlobComponents[componentPath]()` in development as it is `eager:false` + : metaGlobComponents[componentPath]; // We need to directly access the `metaGlobComponents[componentPath]` expression in preview/production as it is `eager:true` + }); + + return <>{MetaGlobComponent.value && }; +}); diff --git a/packages/docs/src/routes/docs/cookbook/glob-import/index.mdx b/packages/docs/src/routes/docs/cookbook/glob-import/index.mdx new file mode 100644 index 00000000000..b66c173cae5 --- /dev/null +++ b/packages/docs/src/routes/docs/cookbook/glob-import/index.mdx @@ -0,0 +1,96 @@ +--- +title: Cookbook | Glob Import with import.meta.glob +contributors: + - maiieul +--- + +import CodeSandbox from '../../../../components/code-sandbox/index.tsx'; + +# Glob Import & Dynamic Import + +As you probably know, Qwik takes care of lazy-loading for you in order to make your app performant and scalable by default. + +As a consequence of this automatic optimization, you don't need to and shouldn't use vite's [dynamic imports](https://vitejs.dev/guide/features#dynamic-import) feature as it conflicts with the [optimizer](<../../(qwik)/advanced/optimizer/index.mdx>). + +But there are still some cases where you may need to import a lot of files from a directory and you may not want to type out all the file paths. For that kind of situation, you can use [`import.meta.glob`](https://vitejs.dev/guide/features#glob-import). + +## import.meta.glob + +The goal of using `import.meta.glob` is to allow you to create a wrapper component to which you can pass a name prop to chose which component you want to import: + +```tsx + + + +``` + +As written in the Vite documentation, `import.meta.glob` comes with a few features that allow you to specify how to import your files. + +By default you can simply use pattern matching to specify which files should be imported from which folder: + +```tsx +const metaGlobComponents: any = import.meta.glob('/src/components/*'); +``` + +But you can also pass in additional options like [`import`](https://vitejs.dev/guide/features#named-imports), [`as`](https://vitejs.dev/guide/features#glob-import-as), or `eager`: + +```tsx +const metaGlobComponents: any = import.meta.glob('/src/components/*', { + import: 'default', + as: 'raw', + eager: true, // defaults to false +}); +``` + +## How to + +The problem with `import.meta.glob` in Qwik, is that it currently either works in development and doesn't in preview/production, or it works in preview/production but gets slower and slower in development as the number of imported files increases. + +The reason for this behavior, is that `import.meta.glob` with `eager.false` breaks the production bundle as it creates lazy-loadable chunks that Qwik doesn't know how to handle. On the other hand, `eager:true` seemingly fixes the issue as it allows Qwik to normally bundle the files, but it also slows down the development server - especially when you import a lot of heavy components with it. + +As a workaround for now, you can use the build time `isDev` boolean from `"@builder.io/qwik/build"`: + + +```tsx +import { + type Component, + component$, + useSignal, + useTask$, +} from '@builder.io/qwik'; +import { isDev } from '@builder.io/qwik/build'; + +const metaGlobComponents: Record = import.meta.glob( + '/src/examples/*', + { + import: 'default', + eager: isDev ? false : true, + } +); + +export default component$(() => { + return ( +
+ + + +
+ ); +}); + +export const MetaGlobExample = component$<{ name: string }>(({ name }) => { + const MetaGlobComponent = useSignal>(); + const componentPath = `/src/examples/${name}.tsx`; + + useTask$(async () => { + MetaGlobComponent.value = isDev + ? await metaGlobComponents[componentPath]() + // We need to call `await metaGlobComponents[componentPath]()` in development as it is `eager:false` + : metaGlobComponents[componentPath]; + // We need to directly access the `metaGlobComponents[componentPath]` expression in preview/production as it is `eager:true` + }); + + return <>{MetaGlobComponent.value && }; +}); +``` +
diff --git a/packages/docs/src/routes/docs/cookbook/index.mdx b/packages/docs/src/routes/docs/cookbook/index.mdx index 0dc1963159d..3dce307e81d 100644 --- a/packages/docs/src/routes/docs/cookbook/index.mdx +++ b/packages/docs/src/routes/docs/cookbook/index.mdx @@ -4,6 +4,7 @@ contributors: - mhevery - fabiobiondi - n8sabes + - maiieul --- # Cookbook @@ -13,4 +14,5 @@ A cookbook contains a collection of useful patterns for solving common problems Examples: - [Modal Dialog Pop-Up](./portal/) - [Media Controller with iOS Support](./mediaController/) +- [Glob Import with `import.meta.glob`](./glob-import/) - [Theme Managment](./theme-management/) diff --git a/packages/docs/src/routes/docs/menu.md b/packages/docs/src/routes/docs/menu.md index a0f57cc045d..477d873287c 100644 --- a/packages/docs/src/routes/docs/menu.md +++ b/packages/docs/src/routes/docs/menu.md @@ -38,8 +38,9 @@ ## Cookbook - [Overview](/docs/cookbook/index.mdx) -- [Portal](/docs/cookbook/portal/index.mdx) +- [Glob Import](/docs/cookbook/glob-import/index.mdx) - [Media Controller](/docs/cookbook/mediaController/index.mdx) +- [Portal](/docs/cookbook/portal/index.mdx) ## Integrations