Skip to content

feat(nuxt): allow configuring treeshakeable composables#19383

Merged
danielroe merged 21 commits into
mainfrom
feat/server-client-only-composables
Mar 7, 2023
Merged

feat(nuxt): allow configuring treeshakeable composables#19383
danielroe merged 21 commits into
mainfrom
feat/server-client-only-composables

Conversation

@harlan-zw

@harlan-zw harlan-zw commented Mar 2, 2023

Copy link
Copy Markdown
Contributor

🔗 Linked issue

Not an issue but to unblock me on #19302

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Background

Isolating composables to a specific environment is a useful way to optimise bundles. There are many instances of code that only need to run on a single environment that are introduced through modules.

However, there isn't an easy way for modules to opt-in to this treeshaking config.

For example:

  • Unhead - useServerHead, useServerSeoMeta, useServerHeadSafe
  • nuxt-schema-org - useSchemaOrg
  • nuxt-og-image - defineOgImageStatic, defineOgImageDynamic, defineOgImageScreenshot
  • nuxt-simple-robots - defineRobotMeta
  • ...etc

There is currently a way to handle this by wrapping code in env if blocks. This works okay, but it means the function arguments won't be tree-shaken (afaik).

You can wrap the composable call itself but this gets pretty verbose quickly and is not something module authors have control over.

export function useServerLog(input: any) {
  if (process.server) {
    console.log(input)
  }
}
<template lang="ts" setup>
// this is kept in the client build?
useServerLog({
  foo: 'bar'
  bar: 'foo',
  baz: 'bing',
  bong: 'bar',
})
// verbose!
if (process.server) {
  useServerLog({ /* */ })
} 
</template>

Solution

Make the treeShake config extendable by moving it to nuxt config.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@codesandbox

codesandbox Bot commented Mar 2, 2023

Copy link
Copy Markdown

CodeSandbox logoCodeSandbox logo  Open in CodeSandbox Web Editor | VS Code | VS Code Insiders

@harlan-zw harlan-zw added 3.x 🍰 p2-nice-to-have Nice to have, not blocking ✨ enhancement New feature or improvement to existing functionality labels Mar 2, 2023
@harlan-zw harlan-zw requested a review from danielroe March 2, 2023 07:34
@danielroe danielroe requested a review from antfu March 2, 2023 09:43
@antfu

antfu commented Mar 3, 2023

Copy link
Copy Markdown
Member

Interesting idea! However, I have a few questions:

  • Should we allow both .server and .client functions to explore the same name to have different functions in different env? If so, it might need some changes on the unimport side. (could also lead to problem of function signatures not matched)
  • Note: This will only work with composables when you aren't using their return value. - This pitfall feels a bit too much and may lead to confusion. And honestly might not be that useful as in many cases the return value is important. It was working for Vue because we are tree-shaking mostly the hooks.
  • The side-effects could also lead to confusion, as from a higher view, users would see the imports in .server being executed on the client side. It can be counterintuitive.

In that sense, I think I would push back for the current implementation as I see the trade-off is not worth it. People better use onMounted or if (process.server) or dynamic import to explicitly control the boundary at this moment.

@harlan-zw harlan-zw changed the title feat(nuxt): server/client only composables feat(nuxt): extendable treeShake config Mar 4, 2023
@harlan-zw

harlan-zw commented Mar 4, 2023

Copy link
Copy Markdown
Contributor Author

Interesting idea! However, I have a few questions:

  • Should we allow both .server and .client functions to explore the same name to have different functions in different env? If so, it might need some changes on the unimport side. (could also lead to problem of function signatures not matched)
  • Note: This will only work with composables when you aren't using their return value. - This pitfall feels a bit too much and may lead to confusion. And honestly might not be that useful as in many cases the return value is important. It was working for Vue because we are tree-shaking mostly the hooks.
  • The side-effects could also lead to confusion, as from a higher view, users would see the imports in .server being executed on the client side. It can be counterintuitive.

In that sense, I think I would push back for the current implementation as I see the trade-off is not worth it. People better use onMounted or if (process.server) or dynamic import to explicitly control the boundary at this moment.

Thanks for the detailed review! Good points

I agree that it could be a useful feature but the limitations open the scope for too many issues. I'd like to see it in Nuxt one day but sounds like some work needs to go into Unimport first.

I've minimised the scope of this PR to only make the treeShake config extendable which I think is more reasonable.

Comment thread test/fixtures/basic/composables/tree-shake.ts Outdated
Comment thread test/fixtures/basic/composables/tree-shake.ts Outdated
Comment thread test/fixtures/basic/composables/tree-shake.ts Outdated
Comment thread test/fixtures/basic/pages/tree-shake.vue
Comment thread packages/nuxt/src/core/nuxt.ts Outdated
Comment thread packages/schema/src/config/build.ts Outdated
Comment thread packages/schema/src/config/build.ts Outdated

@danielroe danielroe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! ❤️

@danielroe

Copy link
Copy Markdown
Member

Merging so @harlan-zw can go ahead with linked PR. But would particularly appreciate feedback @antfu on the schema change in this PR. If we do end up supporting something like this in unimport down the line, does it give you enough info? (it's in the preset format, so Record<packageName: string, imports: string[]>).

(Happy to change the schema before we release v3.3 if you have any suggestions for improvement.)

@danielroe danielroe changed the title feat(nuxt): extendable treeShake config feat(nuxt): allow configuring treeshakeable composables Mar 7, 2023
@danielroe danielroe merged commit bb61496 into main Mar 7, 2023
@danielroe danielroe deleted the feat/server-client-only-composables branch March 7, 2023 09:30
@danielroe danielroe mentioned this pull request Mar 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.x ✨ enhancement New feature or improvement to existing functionality 🍰 p2-nice-to-have Nice to have, not blocking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants