Skip to content
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
22 changes: 22 additions & 0 deletions packages/plugin-rsc/e2e/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@ test.describe('build-default', () => {
defineTest(f)
})

test.describe('dev-non-optimized-cjs', () => {
test.beforeAll(async () => {
// remove explicitly added optimizeDeps.include
const editor = f.createEditor('vite.config.ts')
editor.edit((s) =>
s.replace(
`'@vitejs/test-dep-transitive-cjs > use-sync-external-store/shim/index.js',`,
``,
),
)
})

const f = useFixture({ root: 'examples/basic', mode: 'dev' })

test('show warning', async ({ page }) => {
await page.goto(f.url())
expect(f.proc().stderr()).toContain(
`[vite-rsc] found non-optimized CJS dependency in 'ssr' environment.`,
)
})
})

function defineTest(f: Fixture) {
test('basic', async ({ page }) => {
using _ = expectNoPageError(page)
Expand Down
17 changes: 15 additions & 2 deletions packages/plugin-rsc/e2e/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ function runCli(options: { command: string; label?: string } & SpawnOptions) {
const [name, ...args] = options.command.split(' ')
const child = x(name!, args, { nodeOptions: options }).process!
const label = `[${options.label ?? 'cli'}]`
let stdout = ''
let stderr = ''
child.stdout!.on('data', (data) => {
stdout += stripVTControlCharacters(String(data))
if (process.env.TEST_DEBUG) {
console.log(styleText('cyan', label), data.toString())
}
})
child.stderr!.on('data', (data) => {
stderr += stripVTControlCharacters(String(data))
console.log(styleText('magenta', label), data.toString())
})
const done = new Promise<void>((resolve) => {
Expand Down Expand Up @@ -48,7 +52,14 @@ function runCli(options: { command: string; label?: string } & SpawnOptions) {
}
}

return { proc: child, done, findPort, kill }
return {
proc: child,
done,
findPort,
kill,
stdout: () => stdout,
stderr: () => stderr,
}
}

export type Fixture = ReturnType<typeof useFixture>
Expand All @@ -64,12 +75,13 @@ export function useFixture(options: {
let baseURL!: string

const cwd = path.resolve(options.root)
let proc!: ReturnType<typeof runCli>

// TODO: `beforeAll` is called again on any test failure.
// https://playwright.dev/docs/test-retries
test.beforeAll(async () => {
if (options.mode === 'dev') {
const proc = runCli({
proc = runCli({
command: options.command ?? `pnpm dev`,
label: `${options.root}:dev`,
cwd,
Expand Down Expand Up @@ -144,6 +156,7 @@ export function useFixture(options: {
root: cwd,
url: (url: string = './') => new URL(url, baseURL).href,
createEditor,
proc: () => proc,
}
}

Expand Down
2 changes: 0 additions & 2 deletions packages/plugin-rsc/examples/basic/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,6 @@ export default { fetch: handler };
},
ssr: {
optimizeDeps: {
// TODO: this should be somehow auto inferred or at least show a warning
// to guide users to `optimizeDeps.include`
include: [
'@vitejs/test-dep-transitive-cjs > use-sync-external-store/shim/index.js',
],
Expand Down
37 changes: 35 additions & 2 deletions packages/plugin-rsc/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
normalizePath,
parseAstAsync,
} from 'vite'
import { crawlFrameworkPkgs } from 'vitefu'
import { crawlFrameworkPkgs, findClosestPkgJsonPath } from 'vitefu'
import vitePluginRscCore from './core/plugin'
import {
type TransformWrapExportFilter,
Expand Down Expand Up @@ -838,9 +838,42 @@ globalThis.AsyncLocalStorage = __viteRscAyncHooks.AsyncLocalStorage;
? [validateImportPlugin()]
: []),
scanBuildStripPlugin(),
detectNonOptimizedCjsPlugin(),
]
}

function detectNonOptimizedCjsPlugin(): Plugin {
return {
name: 'rsc:detect-non-optimized-cjs',
apply: 'serve',
async transform(code, id) {
if (
id.includes('/node_modules/') &&
!id.startsWith(this.environment.config.cacheDir) &&
/\b(require|exports)\b/.test(code)
) {
id = parseIdQuery(id).filename
let isEsm = id.endsWith('.mjs')
if (id.endsWith('.js')) {
const pkgJsonPath = await findClosestPkgJsonPath(path.dirname(id))
if (pkgJsonPath) {
const pkgJson = JSON.parse(
fs.readFileSync(pkgJsonPath, 'utf-8'),
) as { type?: string }
isEsm = pkgJson.type === 'module'
}
}
if (!isEsm) {
this.warn(
`[vite-rsc] found non-optimized CJS dependency in '${this.environment.name}' environment. ` +
`It is recommended to manually add the dependency to 'environments.${this.environment.name}.optimizeDeps.include'.`,
)
}
}
},
}
}

function scanBuildStripPlugin(): Plugin {
return {
name: 'rsc:scan-strip',
Expand Down Expand Up @@ -1900,7 +1933,7 @@ function evalValue<T = any>(rawValue: string): T {
// https://github.com/vitejs/vite-plugin-vue/blob/06931b1ea2b9299267374cb8eb4db27c0626774a/packages/plugin-vue/src/utils/query.ts#L13
function parseIdQuery(id: string) {
if (!id.includes('?')) return { filename: id, query: {} }
const [filename, rawQuery] = id.split(`?`, 2)
const [filename, rawQuery] = id.split(`?`, 2) as [string, string]
const query = Object.fromEntries(new URLSearchParams(rawQuery))
return { filename, query }
}
Expand Down
Loading