Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSC: Refactor node-loader and some vite plugins #10046

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 46 additions & 52 deletions packages/vite/src/react-server-dom-webpack/node-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ function transformServerModule(
newSrc += '$$bound: { value: null }'
newSrc += '});\n'
})

return newSrc
}

Expand Down Expand Up @@ -272,7 +273,6 @@ function resolveClientImport(
// This resolution algorithm will not necessarily have the same configuration
// as the actual client loader. It should mostly work and if it doesn't you can
// always convert to explicit exported names instead.
const conditions = ['node', 'import']

if (stashedResolve === null) {
throw new Error(
Expand All @@ -283,14 +283,17 @@ function resolveClientImport(
return stashedResolve(
specifier,
{
conditions,
conditions: ['node', 'import'],
parentURL,
},
stashedResolve
)
}

async function parseExportNamesInto(
/**
* Parses `body` for exports and stores them in `names` (the second argument)
*/
async function parseExportNamesIntoNames(
body: any,
names: Array<string>,
parentURL: string,
Expand All @@ -305,31 +308,26 @@ async function parseExportNamesInto(
addExportNames(names, node.exported)
continue
} else {
const _await$resolveClientI = await resolveClientImport(
node.source.value,
parentURL
),
url = _await$resolveClientI.url

const _await$loader = await loader(
url,
{
format: 'module',
conditions: [],
importAssertions: {},
},
loader
),
source = _await$loader.source

if (typeof source !== 'string') {
const clientImport = await resolveClientImport(
node.source.value,
parentURL
)
const url = clientImport.url
const loadContext = {
format: 'module',
conditions: [],
importAssertions: {},
}
const mod = await loader(url, loadContext, loader)

if (typeof mod.source !== 'string') {
throw new Error('Expected the transformed source to be a string.')
}

let childBody

try {
childBody = acorn.parse(source, {
childBody = acorn.parse(mod.source, {
ecmaVersion: '2024',
sourceType: 'module',
}).body
Expand All @@ -338,7 +336,8 @@ async function parseExportNamesInto(
continue
}

await parseExportNamesInto(childBody, names, url, loader)
await parseExportNamesIntoNames(childBody, names, url, loader)

continue
}

Expand Down Expand Up @@ -378,7 +377,10 @@ async function transformClientModule(
loader: LoadFunction
): Promise<string> {
const names: Array<string> = []
await parseExportNamesInto(body, names, url, loader)

// This will insert the names into the `names` array
await parseExportNamesIntoNames(body, names, url, loader)

let newSrc =
"const CLIENT_REFERENCE = Symbol.for('react.client.reference');\n"

Expand Down Expand Up @@ -433,29 +435,27 @@ async function loadClientImport(
throw new Error(
'Expected getSource to have been called before transformSource'
)
} // TODO: Validate that this is another module by calling getFormat.
}

const _await$stashedGetSour = await stashedGetSource(
url,
{
format: 'module',
},
stashedGetSource
),
source = _await$stashedGetSour.source

const result = await defaultTransformSource(
source,
{
format: 'module',
url,
},
defaultTransformSource
// TODO: Validate that this is another module by calling getFormat.

const getSourceContext = { format: 'module' }
const { source } = await stashedGetSource(
url,
getSourceContext,
stashedGetSource
)
return {
const transformContext = {
format: 'module',
source: result.source,
url,
}
const { source: transformedSource } = await defaultTransformSource(
source,
transformContext,
defaultTransformSource
)

return { format: 'module', source: transformedSource }
}

async function transformModuleIfNeeded(
Expand All @@ -465,10 +465,7 @@ async function transformModuleIfNeeded(
): Promise<string> {
// Do a quick check for the exact string. If it doesn't exist, don't
// bother parsing.
if (
source.indexOf('use client') === -1 &&
source.indexOf('use server') === -1
) {
if (!source.includes('use client') && !source.includes('use server')) {
return source
}

Expand Down Expand Up @@ -545,9 +542,8 @@ export async function transformSource(
return loadClientImport(url, defaultTransformSource)
}
)
return {
source: newSrc,
}

return { source: newSrc }
}

return transformed
Expand Down Expand Up @@ -579,5 +575,3 @@ export async function load(

return result
}

// export { getSource, load, resolve, transformSource }
32 changes: 23 additions & 9 deletions packages/vite/src/rsc/rscVitePlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ export function rscIndexPlugin(): Plugin {
export function rscTransformPlugin(): Plugin {
return {
name: 'rsc-transform-plugin',
// TODO(RSC): Seems like resolveId() is never called. Can we remove it?
async resolveId(id, importer, options) {
console.log(
'rscVitePlugins - rscTransformPlugin::resolveId()',
id,
options
)
if (!id.endsWith('.js')) {
return id
}
Expand Down Expand Up @@ -80,8 +86,21 @@ export function rscTransformPlugin(): Plugin {
return { url }
}

const context = {
conditions: ['react-server'],
parentURL: '',
}

// Calling `resolve` here stashes the resolve function for use with
// `RSDWNodeLoader.load()` below
RSDWNodeLoader.resolve('', context, resolve)

const load = async (url: string) => {
let source = url === id ? code : (await this.load({ id: url })).code
let source: string | null = code

if (url !== id) {
source = (await this.load({ id: url })).code
}

if (!source) {
throw new Error(`Failed to load ${url}`)
Expand All @@ -92,18 +111,13 @@ export function rscTransformPlugin(): Plugin {
/^(import {.*?} from ".*?";)\s*"use (client|server)";/,
'"use $2";$1'
)

return { format: 'module', source }
}

RSDWNodeLoader.resolve(
'',
{ conditions: ['react-server'], parentURL: '' },
resolve
)

const source = (await RSDWNodeLoader.load(id, null, load)).source
const mod = await RSDWNodeLoader.load(id, null, load)

return source
return mod.source
},
}
}
Expand Down
Loading