Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions packages/plugin-rsc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"estree-walker": "^3.0.3",
"magic-string": "^0.30.19",
"periscopic": "^4.0.2",
"strip-literal": "^3.1.0",
"turbo-stream": "^3.1.0",
"vitefu": "^1.1.1"
},
Expand Down
16 changes: 10 additions & 6 deletions packages/plugin-rsc/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { scanBuildStripPlugin } from './plugins/scan'
import { validateImportPlugin } from './plugins/validate-import'
import { vitePluginFindSourceMapURL } from './plugins/find-source-map-url'
import { parseCssVirtual, toCssVirtual, parseIdQuery } from './plugins/shared'
import { stripLiteral } from 'strip-literal'

const isRolldownVite = 'rolldownVersion' in vite

Expand Down Expand Up @@ -702,10 +703,11 @@ export default function vitePluginRsc(
if (!code.includes('import.meta.viteRsc.loadModule')) return
const { server } = manager
const s = new MagicString(code)
for (const match of code.matchAll(
for (const match of stripLiteral(code).matchAll(
/import\.meta\.viteRsc\.loadModule\(([\s\S]*?)\)/dg,
)) {
const argCode = match[1]!.trim()
const [argStart, argEnd] = match.indices![1]!
const argCode = code.slice(argStart, argEnd).trim()
const [environmentName, entryName] = evalValue(`[${argCode}]`)
let replacement: string
if (
Expand Down Expand Up @@ -973,10 +975,11 @@ export default assetsManifest.bootstrapScriptContent;
assert(this.environment.name !== 'client')
const output = new MagicString(code)

for (const match of code.matchAll(
for (const match of stripLiteral(code).matchAll(
/import\s*\.\s*meta\s*\.\s*viteRsc\s*\.\s*loadBootstrapScriptContent\(([\s\S]*?)\)/dg,
)) {
const argCode = match[1]!.trim()
const [argStart, argEnd] = match.indices![1]!
const argCode = code.slice(argStart, argEnd).trim()
const entryName = evalValue(argCode)
assert(
entryName,
Expand Down Expand Up @@ -2087,11 +2090,12 @@ function vitePluginRscCss(
const output = new MagicString(code)
let importAdded = false

for (const match of code.matchAll(
for (const match of stripLiteral(code).matchAll(
/import\.meta\.viteRsc\.loadCss\(([\s\S]*?)\)/dg,
)) {
const [start, end] = match.indices![0]!
const argCode = match[1]!.trim()
const [argStart, argEnd] = match.indices![1]!
const argCode = code.slice(argStart, argEnd).trim()
let importer = id
if (argCode) {
const argValue = evalValue<string>(argCode)
Expand Down
125 changes: 125 additions & 0 deletions packages/plugin-rsc/src/plugins/strip-literal.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { describe, expect, it } from 'vitest'
import { stripLiteral } from 'strip-literal'

describe('stripLiteral for viteRsc API detection', () => {
it('should strip comments with import.meta.viteRsc.loadModule', () => {
const code = `
// This is a comment with import.meta.viteRsc.loadModule("test")
/* block comment import.meta.viteRsc.loadModule("test") */
import.meta.viteRsc.loadModule("actual");
`
const stripped = stripLiteral(code)
const matches = [
...stripped.matchAll(/import\.meta\.viteRsc\.loadModule\(([\s\S]*?)\)/dg),
]

// Should only match the actual call, not the ones in comments
expect(matches.length).toBe(1)
// Extract argument using indices from original code
const [argStart, argEnd] = matches[0]!.indices![1]!
const argCode = code.slice(argStart, argEnd).trim()
expect(argCode).toBe('"actual"')
})

it('should strip strings with import.meta.viteRsc.loadModule', () => {
const code = `
const x = "string with import.meta.viteRsc.loadModule('test')";
const y = 'another string import.meta.viteRsc.loadModule("test")';
import.meta.viteRsc.loadModule("actual");
`
const stripped = stripLiteral(code)
const matches = [
...stripped.matchAll(/import\.meta\.viteRsc\.loadModule\(([\s\S]*?)\)/dg),
]

// Should only match the actual call, not the ones in strings
expect(matches.length).toBe(1)
const [argStart, argEnd] = matches[0]!.indices![1]!
const argCode = code.slice(argStart, argEnd).trim()
expect(argCode).toBe('"actual"')
})

it('should strip comments with import.meta.viteRsc.loadCss', () => {
const code = `
// This is a comment with import.meta.viteRsc.loadCss("test")
/* block comment import.meta.viteRsc.loadCss("test") */
import.meta.viteRsc.loadCss("actual");
`
const stripped = stripLiteral(code)
const matches = [
...stripped.matchAll(/import\.meta\.viteRsc\.loadCss\(([\s\S]*?)\)/dg),
]

// Should only match the actual call, not the ones in comments
expect(matches.length).toBe(1)
const [argStart, argEnd] = matches[0]!.indices![1]!
const argCode = code.slice(argStart, argEnd).trim()
expect(argCode).toBe('"actual"')
})

it('should strip strings with import.meta.viteRsc.loadCss', () => {
const code = `
const x = "string with import.meta.viteRsc.loadCss('test')";
import.meta.viteRsc.loadCss("actual");
`
const stripped = stripLiteral(code)
const matches = [
...stripped.matchAll(/import\.meta\.viteRsc\.loadCss\(([\s\S]*?)\)/dg),
]

// Should only match the actual call, not the ones in strings
expect(matches.length).toBe(1)
const [argStart, argEnd] = matches[0]!.indices![1]!
const argCode = code.slice(argStart, argEnd).trim()
expect(argCode).toBe('"actual"')
})

it('should strip comments with import.meta.viteRsc.loadBootstrapScriptContent', () => {
const code = `
// This is a comment with import.meta.viteRsc.loadBootstrapScriptContent("test")
/* block import.meta.viteRsc.loadBootstrapScriptContent("test") */
import.meta.viteRsc.loadBootstrapScriptContent("actual");
`
const stripped = stripLiteral(code)
const matches = [
...stripped.matchAll(
/import\s*\.\s*meta\s*\.\s*viteRsc\s*\.\s*loadBootstrapScriptContent\(([\s\S]*?)\)/dg,
),
]

// Should only match the actual call, not the ones in comments
expect(matches.length).toBe(1)
const [argStart, argEnd] = matches[0]!.indices![1]!
const argCode = code.slice(argStart, argEnd).trim()
expect(argCode).toBe('"actual"')
})

it('should handle mixed comments and strings', () => {
const code = `
// Comment with import.meta.viteRsc.loadModule("comment")
const x = "string with import.meta.viteRsc.loadModule('string')";
/*
* Multi-line comment
* import.meta.viteRsc.loadModule("multiline")
*/
import.meta.viteRsc.loadModule("first-actual");
const y = \`template with import.meta.viteRsc.loadModule('template')\`;
import.meta.viteRsc.loadModule("second-actual");
`
const stripped = stripLiteral(code)
const matches = [
...stripped.matchAll(/import\.meta\.viteRsc\.loadModule\(([\s\S]*?)\)/dg),
]

// Should only match the actual calls
expect(matches.length).toBe(2)

const [argStart1, argEnd1] = matches[0]!.indices![1]!
const argCode1 = code.slice(argStart1, argEnd1).trim()
expect(argCode1).toBe('"first-actual"')

const [argStart2, argEnd2] = matches[1]!.indices![1]!
const argCode2 = code.slice(argStart2, argEnd2).trim()
expect(argCode2).toBe('"second-actual"')
})
})
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading