2021-12-20 14:01:35 +01:00
|
|
|
import * as acorn from 'next/dist/compiled/acorn'
|
2021-10-26 18:50:56 +02:00
|
|
|
import { getRawPageExtensions } from '../../utils'
|
|
|
|
|
|
|
|
function isClientComponent(importSource: string, pageExtensions: string[]) {
|
|
|
|
return new RegExp(`\\.client(\\.(${pageExtensions.join('|')}))?`).test(
|
|
|
|
importSource
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-11-30 23:54:47 +01:00
|
|
|
function isServerComponent(importSource: string, pageExtensions: string[]) {
|
|
|
|
return new RegExp(`\\.server(\\.(${pageExtensions.join('|')}))?`).test(
|
|
|
|
importSource
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-10-26 18:50:56 +02:00
|
|
|
function isNextComponent(importSource: string) {
|
|
|
|
return (
|
|
|
|
importSource.includes('next/link') || importSource.includes('next/image')
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isImageImport(importSource: string) {
|
|
|
|
// TODO: share extension with next/image
|
|
|
|
// TODO: add other static assets, jpeg -> jpg
|
|
|
|
return ['jpg', 'jpeg', 'png', 'webp', 'avif'].some((imageExt) =>
|
|
|
|
importSource.endsWith('.' + imageExt)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
async function parseImportsInfo(
|
|
|
|
source: string,
|
|
|
|
imports: Array<string>,
|
|
|
|
isClientCompilation: boolean,
|
|
|
|
pageExtensions: string[]
|
|
|
|
): Promise<string> {
|
|
|
|
const { body } = acorn.parse(source, {
|
2021-11-05 04:27:02 +01:00
|
|
|
ecmaVersion: 11,
|
2021-10-26 18:50:56 +02:00
|
|
|
sourceType: 'module',
|
|
|
|
}) as any
|
|
|
|
|
|
|
|
let transformedSource = ''
|
|
|
|
let lastIndex = 0
|
|
|
|
|
|
|
|
for (let i = 0; i < body.length; i++) {
|
|
|
|
const node = body[i]
|
|
|
|
switch (node.type) {
|
|
|
|
case 'ImportDeclaration':
|
|
|
|
const importSource = node.source.value
|
|
|
|
|
|
|
|
if (!isClientCompilation) {
|
2021-11-03 03:14:14 +01:00
|
|
|
if (
|
|
|
|
!(
|
|
|
|
isClientComponent(importSource, pageExtensions) ||
|
|
|
|
isNextComponent(importSource) ||
|
|
|
|
isImageImport(importSource)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
continue
|
|
|
|
}
|
2021-10-26 18:50:56 +02:00
|
|
|
transformedSource += source.substr(
|
|
|
|
lastIndex,
|
|
|
|
node.source.start - lastIndex
|
|
|
|
)
|
|
|
|
transformedSource += JSON.stringify(`${node.source.value}?flight`)
|
2021-11-30 23:54:47 +01:00
|
|
|
} else {
|
|
|
|
// For the client compilation, we skip all modules imports but
|
|
|
|
// always keep client components in the bundle. All client components
|
|
|
|
// have to be imported from either server or client components.
|
|
|
|
if (
|
|
|
|
!(
|
|
|
|
isClientComponent(importSource, pageExtensions) ||
|
|
|
|
isServerComponent(importSource, pageExtensions) ||
|
|
|
|
// Special cases for Next.js APIs that are considered as client
|
|
|
|
// components:
|
|
|
|
isNextComponent(importSource) ||
|
|
|
|
isImageImport(importSource)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
continue
|
|
|
|
}
|
2021-10-26 18:50:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex = node.source.end
|
|
|
|
imports.push(`require(${JSON.stringify(importSource)})`)
|
|
|
|
continue
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isClientCompilation) {
|
|
|
|
transformedSource += source.substr(lastIndex)
|
|
|
|
}
|
|
|
|
|
|
|
|
return transformedSource
|
|
|
|
}
|
|
|
|
|
|
|
|
export default async function transformSource(
|
|
|
|
this: any,
|
|
|
|
source: string
|
|
|
|
): Promise<string> {
|
|
|
|
const { client: isClientCompilation, pageExtensions: pageExtensionsJson } =
|
2021-11-02 16:13:15 +01:00
|
|
|
this.getOptions()
|
2021-10-26 18:50:56 +02:00
|
|
|
const { resourcePath } = this
|
|
|
|
const pageExtensions = JSON.parse(pageExtensionsJson)
|
|
|
|
|
|
|
|
if (typeof source !== 'string') {
|
|
|
|
throw new Error('Expected source to have been transformed to a string.')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resourcePath.includes('/node_modules/')) {
|
|
|
|
return source
|
|
|
|
}
|
|
|
|
|
|
|
|
const imports: string[] = []
|
|
|
|
const transformed = await parseImportsInfo(
|
|
|
|
source,
|
|
|
|
imports,
|
|
|
|
isClientCompilation,
|
|
|
|
getRawPageExtensions(pageExtensions)
|
|
|
|
)
|
|
|
|
|
|
|
|
const noop = `\nexport const __rsc_noop__=()=>{${imports.join(';')}}`
|
|
|
|
const defaultExportNoop = isClientCompilation
|
|
|
|
? `\nexport default function Comp(){}\nComp.__next_rsc__=1`
|
|
|
|
: ''
|
2021-11-03 03:14:14 +01:00
|
|
|
|
2021-10-26 18:50:56 +02:00
|
|
|
return transformed + noop + defaultExportNoop
|
|
|
|
}
|