2024-06-20 19:40:14 +02:00
|
|
|
import type { Plugin } from 'vite'
|
|
|
|
|
import * as babel from '@babel/core'
|
|
|
|
|
import type { PluginItem } from '@babel/core'
|
|
|
|
|
|
2024-06-21 18:52:58 +02:00
|
|
|
import {
|
|
|
|
|
TUONO_MAIN_PACKAGE,
|
|
|
|
|
TUONO_DYNAMIC_FN_ID,
|
|
|
|
|
REACT_LAZY_FN_ID,
|
|
|
|
|
} from './constants'
|
|
|
|
|
|
2024-06-20 19:40:14 +02:00
|
|
|
import * as t from '@babel/types'
|
|
|
|
|
|
|
|
|
|
import type {
|
|
|
|
|
Identifier,
|
|
|
|
|
ImportDeclaration,
|
|
|
|
|
CallExpression,
|
|
|
|
|
ArrowFunctionExpression,
|
|
|
|
|
StringLiteral,
|
|
|
|
|
} from '@babel/types'
|
|
|
|
|
|
|
|
|
|
/**
|
2024-06-21 18:52:58 +02:00
|
|
|
* This plugin just removes the `dynamic` imported function from any tuono import
|
2024-06-20 19:40:14 +02:00
|
|
|
*/
|
|
|
|
|
const RemoveTuonoLazyImport: PluginItem = {
|
|
|
|
|
name: 'remove-tuono-lazy-import-plugin',
|
|
|
|
|
visitor: {
|
|
|
|
|
ImportSpecifier: (path) => {
|
2024-06-21 18:52:58 +02:00
|
|
|
if ((path.node.imported as Identifier).name === TUONO_DYNAMIC_FN_ID) {
|
2024-06-20 19:40:14 +02:00
|
|
|
if (
|
2024-06-21 18:52:58 +02:00
|
|
|
(path.parentPath.node as ImportDeclaration).source.value ===
|
|
|
|
|
TUONO_MAIN_PACKAGE
|
2024-06-20 19:40:14 +02:00
|
|
|
) {
|
|
|
|
|
path.remove()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-06-21 18:52:58 +02:00
|
|
|
* This plugin adds: "Import { lazy } from 'react'"
|
|
|
|
|
* and translate dynamic call into a React.lazy call
|
2024-06-20 19:40:14 +02:00
|
|
|
*/
|
|
|
|
|
const ImportReactLazy: PluginItem = {
|
|
|
|
|
name: 'import-react-lazy-plugin',
|
|
|
|
|
visitor: {
|
2024-06-21 18:52:58 +02:00
|
|
|
// Add the import statement
|
2024-06-20 19:40:14 +02:00
|
|
|
Program: (path: any) => {
|
|
|
|
|
let isReactImported = false
|
|
|
|
|
|
|
|
|
|
path.node.body.forEach((val: any) => {
|
|
|
|
|
if (val.type === 'ImportDeclaration' && val.source.value === 'react') {
|
|
|
|
|
isReactImported = true
|
|
|
|
|
// TODO: Handle also here case of already imported react
|
|
|
|
|
// Right now works just for the main routes file
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
|
|
|
if (!isReactImported) {
|
|
|
|
|
const importDeclaration = t.importDeclaration(
|
2024-06-21 18:52:58 +02:00
|
|
|
[
|
|
|
|
|
t.importSpecifier(
|
|
|
|
|
t.identifier(REACT_LAZY_FN_ID),
|
|
|
|
|
t.identifier(REACT_LAZY_FN_ID),
|
|
|
|
|
),
|
|
|
|
|
],
|
2024-06-20 19:40:14 +02:00
|
|
|
t.stringLiteral('react'),
|
|
|
|
|
)
|
|
|
|
|
path.unshiftContainer('body', importDeclaration)
|
|
|
|
|
}
|
|
|
|
|
},
|
2024-06-21 18:52:58 +02:00
|
|
|
// Update lazy function name from `dynamic` to `lazy`
|
|
|
|
|
CallExpression: (path: any) => {
|
|
|
|
|
if (path.node.callee?.name === TUONO_DYNAMIC_FN_ID) {
|
|
|
|
|
path.node.callee.name = REACT_LAZY_FN_ID
|
|
|
|
|
}
|
|
|
|
|
},
|
2024-06-20 19:40:14 +02:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* For the server side we need to statically import the lazy loaded components
|
|
|
|
|
*/
|
2024-06-21 18:52:58 +02:00
|
|
|
const TurnLazyIntoStaticImport: PluginItem = {
|
|
|
|
|
name: 'turn-lazy-into-static-import-plugin',
|
2024-06-20 19:40:14 +02:00
|
|
|
visitor: {
|
|
|
|
|
VariableDeclaration: (path) => {
|
|
|
|
|
path.node.declarations.forEach((el) => {
|
|
|
|
|
const init = el.init as CallExpression
|
2024-06-21 18:52:58 +02:00
|
|
|
if ((init.callee as Identifier).name === TUONO_DYNAMIC_FN_ID) {
|
2024-06-20 19:40:14 +02:00
|
|
|
const importName = (el.id as Identifier).name
|
|
|
|
|
const importPath = (
|
|
|
|
|
(
|
|
|
|
|
(init.arguments[0] as ArrowFunctionExpression)
|
|
|
|
|
.body as CallExpression
|
|
|
|
|
).arguments[0] as StringLiteral
|
|
|
|
|
).value
|
|
|
|
|
|
|
|
|
|
if (importName && importPath) {
|
|
|
|
|
const importDeclaration = t.importDeclaration(
|
|
|
|
|
[t.importDefaultSpecifier(t.identifier(importName))],
|
|
|
|
|
t.stringLiteral(importPath),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
path.replaceWith(importDeclaration)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function LazyLoadingPlugin(): Plugin {
|
|
|
|
|
return {
|
|
|
|
|
name: 'vite-plugin-tuono-lazy-loading',
|
|
|
|
|
enforce: 'pre',
|
|
|
|
|
transform(code, _id, opts): string | undefined | null {
|
2024-06-21 18:52:58 +02:00
|
|
|
if (
|
|
|
|
|
code.includes(TUONO_DYNAMIC_FN_ID) &&
|
|
|
|
|
code.includes(TUONO_MAIN_PACKAGE)
|
|
|
|
|
) {
|
2024-06-20 19:40:14 +02:00
|
|
|
const res = babel.transformSync(code, {
|
|
|
|
|
plugins: [
|
|
|
|
|
['@babel/plugin-syntax-jsx', {}],
|
|
|
|
|
['@babel/plugin-syntax-typescript', { isTSX: true }],
|
|
|
|
|
[RemoveTuonoLazyImport],
|
|
|
|
|
[!opts?.ssr ? ImportReactLazy : []],
|
2024-06-21 18:52:58 +02:00
|
|
|
[opts?.ssr ? TurnLazyIntoStaticImport : []],
|
2024-06-20 19:40:14 +02:00
|
|
|
],
|
|
|
|
|
sourceMaps: true,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return res?.code
|
|
|
|
|
}
|
|
|
|
|
return code
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|