mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
feat(nuxt): support custom keyed composables (#19490)
This commit is contained in:
parent
faeffcb963
commit
60d07df4cc
@ -132,6 +132,27 @@ export default defineUntypedSchema({
|
|||||||
* Build time optimization configuration.
|
* Build time optimization configuration.
|
||||||
*/
|
*/
|
||||||
optimization: {
|
optimization: {
|
||||||
|
/**
|
||||||
|
* Functions to inject a key for.
|
||||||
|
*
|
||||||
|
* As long as the number of arguments passed to the function is less than `argumentLength`, an
|
||||||
|
* additional magic string will be injected that can be used to deduplicate requests between server
|
||||||
|
* and client. You will need to take steps to handle this additional key.
|
||||||
|
*
|
||||||
|
* The key will be unique based on the location of the function being invoked within the file.
|
||||||
|
*
|
||||||
|
* @type {Array<{ name: string, argumentLength: number }>}
|
||||||
|
*/
|
||||||
|
keyedComposables: {
|
||||||
|
$resolve: (val) => [
|
||||||
|
{ name: 'useState', argumentLength: 2 },
|
||||||
|
{ name: 'useFetch', argumentLength: 3 },
|
||||||
|
{ name: 'useAsyncData', argumentLength: 3 },
|
||||||
|
{ name: 'useLazyAsyncData', argumentLength: 3 },
|
||||||
|
{ name: 'useLazyFetch', argumentLength: 3 },
|
||||||
|
].concat(val).filter(Boolean)
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tree shake code from specific builds.
|
* Tree shake code from specific builds.
|
||||||
*/
|
*/
|
||||||
|
@ -7,20 +7,24 @@ import MagicString from 'magic-string'
|
|||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
import type { CallExpression } from 'estree'
|
import type { CallExpression } from 'estree'
|
||||||
import { parseQuery, parseURL } from 'ufo'
|
import { parseQuery, parseURL } from 'ufo'
|
||||||
|
import escapeRE from 'escape-string-regexp'
|
||||||
import { findStaticImports, parseStaticImport } from 'mlly'
|
import { findStaticImports, parseStaticImport } from 'mlly'
|
||||||
|
|
||||||
export interface ComposableKeysOptions {
|
export interface ComposableKeysOptions {
|
||||||
sourcemap: boolean
|
sourcemap: boolean
|
||||||
rootDir: string
|
rootDir: string
|
||||||
|
composables: Array<{ name: string, argumentLength: number }>
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyedFunctions = [
|
|
||||||
'useState', 'useFetch', 'useAsyncData', 'useLazyAsyncData', 'useLazyFetch'
|
|
||||||
]
|
|
||||||
const KEYED_FUNCTIONS_RE = new RegExp(`(${keyedFunctions.join('|')})`)
|
|
||||||
const stringTypes = ['Literal', 'TemplateLiteral']
|
const stringTypes = ['Literal', 'TemplateLiteral']
|
||||||
|
|
||||||
export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptions) => {
|
export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptions) => {
|
||||||
|
const composableMeta = Object.fromEntries(options.composables.map(({ name, ...meta }) => [name, meta]))
|
||||||
|
|
||||||
|
const maxLength = Math.max(...options.composables.map(({ argumentLength }) => argumentLength))
|
||||||
|
const keyedFunctions = new Set(options.composables.map(({ name }) => name))
|
||||||
|
const KEYED_FUNCTIONS_RE = new RegExp(`\\b(${[...keyedFunctions].map(f => escapeRE(f)).join('|')})\\b`)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:composable-keys',
|
name: 'nuxt:composable-keys',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
@ -44,24 +48,28 @@ export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptio
|
|||||||
if (_node.type !== 'CallExpression' || (_node as CallExpression).callee.type !== 'Identifier') { return }
|
if (_node.type !== 'CallExpression' || (_node as CallExpression).callee.type !== 'Identifier') { return }
|
||||||
const node: CallExpression = _node as CallExpression
|
const node: CallExpression = _node as CallExpression
|
||||||
const name = 'name' in node.callee && node.callee.name
|
const name = 'name' in node.callee && node.callee.name
|
||||||
if (!name || !keyedFunctions.includes(name) || node.arguments.length >= 4) { return }
|
if (!name || !keyedFunctions.has(name) || node.arguments.length >= maxLength) { return }
|
||||||
|
|
||||||
imports = imports || detectImportNames(script)
|
imports = imports || detectImportNames(script)
|
||||||
if (imports.has(name)) { return }
|
if (imports.has(name)) { return }
|
||||||
|
|
||||||
|
const meta = composableMeta[name]
|
||||||
|
|
||||||
|
if (node.arguments.length >= meta.argumentLength) { return }
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'useState':
|
case 'useState':
|
||||||
if (node.arguments.length >= 2 || stringTypes.includes(node.arguments[0]?.type)) { return }
|
if (stringTypes.includes(node.arguments[0]?.type)) { return }
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'useFetch':
|
case 'useFetch':
|
||||||
case 'useLazyFetch':
|
case 'useLazyFetch':
|
||||||
if (node.arguments.length >= 3 || stringTypes.includes(node.arguments[1]?.type)) { return }
|
if (stringTypes.includes(node.arguments[1]?.type)) { return }
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'useAsyncData':
|
case 'useAsyncData':
|
||||||
case 'useLazyAsyncData':
|
case 'useLazyAsyncData':
|
||||||
if (node.arguments.length >= 3 || stringTypes.includes(node.arguments[0]?.type) || stringTypes.includes(node.arguments[node.arguments.length - 1]?.type)) { return }
|
if (stringTypes.includes(node.arguments[0]?.type) || stringTypes.includes(node.arguments[node.arguments.length - 1]?.type)) { return }
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,11 @@ export async function bundle (nuxt: Nuxt) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
composableKeysPlugin.vite({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client, rootDir: nuxt.options.rootDir }),
|
composableKeysPlugin.vite({
|
||||||
|
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
|
||||||
|
rootDir: nuxt.options.rootDir,
|
||||||
|
composables: nuxt.options.optimization.keyedComposables
|
||||||
|
}),
|
||||||
replace({
|
replace({
|
||||||
...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])),
|
...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])),
|
||||||
preventAssignment: true
|
preventAssignment: true
|
||||||
|
@ -46,7 +46,8 @@ export async function bundle (nuxt: Nuxt) {
|
|||||||
}
|
}
|
||||||
config.plugins!.push(composableKeysPlugin.webpack({
|
config.plugins!.push(composableKeysPlugin.webpack({
|
||||||
sourcemap: nuxt.options.sourcemap[config.name as 'client' | 'server'],
|
sourcemap: nuxt.options.sourcemap[config.name as 'client' | 'server'],
|
||||||
rootDir: nuxt.options.rootDir
|
rootDir: nuxt.options.rootDir,
|
||||||
|
composables: nuxt.options.optimization.keyedComposables
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Create compiler
|
// Create compiler
|
||||||
|
8
test/fixtures/basic/nuxt.config.ts
vendored
8
test/fixtures/basic/nuxt.config.ts
vendored
@ -49,6 +49,14 @@ export default defineNuxtConfig({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
optimization: {
|
||||||
|
keyedComposables: [
|
||||||
|
{
|
||||||
|
name: 'useKeyedComposable',
|
||||||
|
argumentLength: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
baseURL: '',
|
baseURL: '',
|
||||||
baseAPIToken: '',
|
baseAPIToken: '',
|
||||||
|
@ -32,6 +32,11 @@ const { data: useFetchTest2 } = await useLocalFetch()
|
|||||||
const useLocalLazyFetch = () => useLazyFetch(() => '/api/counter')
|
const useLocalLazyFetch = () => useLazyFetch(() => '/api/counter')
|
||||||
const { data: useLazyFetchTest1 } = await useLocalLazyFetch()
|
const { data: useLazyFetchTest1 } = await useLocalLazyFetch()
|
||||||
const { data: useLazyFetchTest2 } = await useLocalLazyFetch()
|
const { data: useLazyFetchTest2 } = await useLocalLazyFetch()
|
||||||
|
|
||||||
|
const useKeyedComposable = (arg?: string) => arg
|
||||||
|
const useLocalKeyedComposable = () => useKeyedComposable()
|
||||||
|
const useMyAsyncDataTest1 = useLocalKeyedComposable()
|
||||||
|
const useMyAsyncDataTest2 = useLocalKeyedComposable()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -41,6 +46,7 @@ const { data: useLazyFetchTest2 } = await useLocalLazyFetch()
|
|||||||
{{ useLazyAsyncDataTest1 === useLazyAsyncDataTest2 }}
|
{{ useLazyAsyncDataTest1 === useLazyAsyncDataTest2 }}
|
||||||
{{ useFetchTest1 === useFetchTest2 }}
|
{{ useFetchTest1 === useFetchTest2 }}
|
||||||
{{ useLazyFetchTest1 === useLazyFetchTest2 }}
|
{{ useLazyFetchTest1 === useLazyFetchTest2 }}
|
||||||
|
{{ !!useMyAsyncDataTest1 && useMyAsyncDataTest1 === useMyAsyncDataTest2 }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user