Skip to content

Commit e9e2179

Browse files
committed
chore(vite): Use vite plugin for context wrapping (#2058)
1 parent 6f86199 commit e9e2179

8 files changed

Lines changed: 202 additions & 96 deletions

File tree

packages/babel-config/src/api.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,10 @@ export const getApiSideBabelConfigPath = () => {
168168
}
169169
}
170170

171-
export const getApiSideBabelOverrides = ({ projectIsEsm = false } = {}) => {
171+
export const getApiSideBabelOverrides = ({
172+
projectIsEsm = false,
173+
forJest = false,
174+
} = {}) => {
172175
const overrides = [
173176
// Extract graphql options from the graphql function
174177
// NOTE: this must come before the context wrapping
@@ -177,8 +180,9 @@ export const getApiSideBabelOverrides = ({ projectIsEsm = false } = {}) => {
177180
test: /.+api(?:[\\|/])src(?:[\\|/])functions(?:[\\|/])graphql\.(?:js|ts)$/,
178181
plugins: [pluginCedarGraphqlOptionsExtract, pluginCedarGqlormInject],
179182
},
180-
// Apply context wrapping to all functions
181-
{
183+
// Apply context wrapping to all functions (Jest only; Vite uses
184+
// cedarContextWrappingPlugin instead)
185+
forJest && {
182186
// match */api/src/functions/*.js|ts
183187
test: /.+api(?:[\\|/])src(?:[\\|/])functions(?:[\\|/]).+.(?:js|ts)$/,
184188
plugins: [
@@ -200,11 +204,14 @@ export const getApiSideBabelOverrides = ({ projectIsEsm = false } = {}) => {
200204
return overrides as TransformOptions[]
201205
}
202206

203-
export const getApiSideDefaultBabelConfig = ({ projectIsEsm = false } = {}) => {
207+
export const getApiSideDefaultBabelConfig = ({
208+
projectIsEsm = false,
209+
forJest = false,
210+
} = {}) => {
204211
return {
205212
presets: getApiSideBabelPresets(),
206213
plugins: getApiSideBabelPlugins({ projectIsEsm }),
207-
overrides: getApiSideBabelOverrides({ projectIsEsm }),
214+
overrides: getApiSideBabelOverrides({ projectIsEsm, forJest }),
208215
extends: getApiSideBabelConfigPath(),
209216
babelrc: false,
210217
ignore: ['node_modules'],

packages/internal/src/build/api.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { getConfig, getPaths, projectSideIsEsm } from '@cedarjs/project-config'
1414

1515
import { findApiFiles } from '../files.js'
1616

17+
import { applyContextWrapping } from './esbuild-plugin-cedar-context-wrapping.js'
18+
1719
let BUILD_CTX: BuildContext | null = null
1820

1921
export const buildApi = async () => {
@@ -55,8 +57,20 @@ const runCedarBabelTransformsPlugin = {
5557
}),
5658
)
5759
if (transformedCode?.code) {
60+
// Apply the context-wrapping safeguard to API function handlers. This
61+
// is the standalone-esbuild equivalent of the Vite
62+
// cedarContextWrappingPlugin and the (Jest-only) babel plugin it
63+
// replaced.
64+
const functionsDir = normalizePath(
65+
path.join(getPaths().api.src, 'functions'),
66+
)
67+
const code = normalizePath(args.path).startsWith(functionsDir + '/')
68+
? (applyContextWrapping(transformedCode.code, {
69+
projectIsEsm: projectSideIsEsm('api'),
70+
}) ?? transformedCode.code)
71+
: transformedCode.code
5872
return {
59-
contents: transformedCode.code,
73+
contents: code,
6074
loader: 'js',
6175
}
6276
}
@@ -99,8 +113,16 @@ function createCedarViteApiPlugin(): Plugin {
99113
)
100114

101115
if (transformedCode?.code) {
116+
const functionsDir = normalizePath(
117+
path.join(cedarPaths.api.src, 'functions'),
118+
)
119+
const code = normalizePath(id).startsWith(functionsDir + '/')
120+
? (applyContextWrapping(transformedCode.code, {
121+
projectIsEsm: isEsm,
122+
}) ?? transformedCode.code)
123+
: transformedCode.code
102124
return {
103-
code: transformedCode.code,
125+
code,
104126
map: transformedCode.map ?? null,
105127
}
106128
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Context-wrapping safeguard for API function handlers. This mirrors the Vite
2+
// cedarContextWrappingPlugin (used by buildCedarApp) and the Jest-only babel
3+
// plugin it replaced. The legacy esbuild build and the standalone-Vite build
4+
// (buildApiWithVite) don't go through the Vite plugin pipeline, so they apply
5+
// it directly here. Keep this in sync with
6+
// packages/vite/src/plugins/vite-plugin-cedar-context-wrapping.ts.
7+
8+
export function applyContextWrapping(
9+
code: string,
10+
{ projectIsEsm = false }: { projectIsEsm?: boolean } = {},
11+
): string | null {
12+
const handlerRe =
13+
/^export\s+(?:const|let|var)\s+handler(?:[^=]|=>)*?=(?![>=])/m
14+
15+
const handlerMatch = handlerRe.exec(code)
16+
if (!handlerMatch) {
17+
return null
18+
}
19+
20+
const afterEquals = code
21+
.slice(handlerMatch.index + handlerMatch[0].length)
22+
.trimStart()
23+
const isAsync = /^async(?:\s*[\(\*]|\s+function)/.test(afterEquals)
24+
25+
const storePath = projectIsEsm
26+
? '@cedarjs/context/dist/store.js'
27+
: '@cedarjs/context/dist/store'
28+
29+
const importStatement = `import { getAsyncStoreInstance as __rw_getAsyncStoreInstance } from '${storePath}'\n`
30+
31+
const handlerStart = handlerMatch.index
32+
const before = code.slice(0, handlerStart)
33+
const after = code.slice(handlerStart)
34+
35+
const renamed = after.replace(handlerRe, 'const __rw_handler =')
36+
37+
const wrappedHandler =
38+
`\nexport const handler = ${isAsync ? 'async ' : ''}(__rw_event, __rw__context) => {\n` +
39+
` // The store will be undefined if no context isolation has been performed yet\n` +
40+
` const __rw_contextStore = __rw_getAsyncStoreInstance().getStore()\n` +
41+
` if (__rw_contextStore === undefined) {\n` +
42+
` return __rw_getAsyncStoreInstance().run(\n` +
43+
` new Map(),\n` +
44+
` __rw_handler,\n` +
45+
` __rw_event,\n` +
46+
` __rw__context\n` +
47+
` )\n` +
48+
` }\n` +
49+
` return __rw_handler(__rw_event, __rw__context)\n` +
50+
`}\n`
51+
52+
return before + importStatement + renamed + wrappedHandler
53+
}

packages/testing/src/config/jest/api/apiBabelConfig.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {
77
// Since configFile and babelrc is already passed a level up, cleaning up these keys here.
88
// babelrc can not reside inside "extend"ed
99
// Ref: packages/testing/config/jest/api/index.js
10-
const { babelrc: _b, ...defaultBabelConfig } = getApiSideDefaultBabelConfig()
10+
const { babelrc: _b, ...defaultBabelConfig } = getApiSideDefaultBabelConfig({
11+
forJest: true,
12+
})
1113

1214
type ConfigType = Omit<
1315
ReturnType<typeof getApiSideDefaultBabelConfig>,

packages/testing/src/config/jest/api/jest-preset.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getPaths } from '@cedarjs/project-config'
77

88
const rwjsPaths = getPaths()
99
const NODE_MODULES_PATH = path.join(rwjsPaths.base, 'node_modules')
10-
const { babelrc } = getApiSideDefaultBabelConfig()
10+
const { babelrc } = getApiSideDefaultBabelConfig({ forJest: true })
1111

1212
const config: Config = {
1313
// To make sure other config option which depends on rootDir use

packages/vite/src/buildApp.ts

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { findApiFiles } from '@cedarjs/internal/dist/files.js'
1414
import { getConfig, getPaths, projectSideIsEsm } from '@cedarjs/project-config'
1515

1616
import { getWorkspacePackageAliases } from './lib/workspacePackageAliases.js'
17+
import { cedarContextWrappingPlugin } from './plugins/vite-plugin-cedar-context-wrapping.js'
1718

1819
function resolveWithExtensions(id: string): string {
1920
if (fs.existsSync(id)) {
@@ -325,44 +326,48 @@ export async function buildCedarApp({
325326
return null
326327
},
327328
})
329+
}
328330

329-
if (babelPlugins) {
330-
plugins.push({
331-
name: 'cedar-vite-api-babel-transform',
332-
enforce: 'pre',
333-
async transform(code: string, id: string) {
334-
if (!/\.(js|ts|tsx|jsx)$/.test(id)) {
335-
return null
336-
}
331+
if (workspace.includes('api')) {
332+
plugins.push(
333+
cedarContextWrappingPlugin({ projectIsEsm: projectSideIsEsm('api') }),
334+
)
335+
}
337336

338-
if (id.includes('node_modules')) {
339-
return null
340-
}
337+
if (babelPlugins) {
338+
plugins.push({
339+
name: 'cedar-vite-api-babel-transform',
340+
enforce: 'pre',
341+
async transform(code: string, id: string) {
342+
if (!/\.(js|ts|tsx|jsx)$/.test(id)) {
343+
return null
344+
}
341345

342-
if (
343-
!normalizePath(id).startsWith(normalizePath(cedarPaths.api.base))
344-
) {
345-
return null
346-
}
346+
if (id.includes('node_modules')) {
347+
return null
348+
}
347349

348-
const transformedCode = await transformWithBabel(
349-
code,
350-
id,
351-
babelPlugins,
352-
true,
353-
)
350+
if (!normalizePath(id).startsWith(normalizePath(cedarPaths.api.base))) {
351+
return null
352+
}
354353

355-
if (transformedCode?.code) {
356-
return {
357-
code: transformedCode.code,
358-
map: transformedCode.map ?? null,
359-
}
354+
const transformedCode = await transformWithBabel(
355+
code,
356+
id,
357+
babelPlugins,
358+
true,
359+
)
360+
361+
if (transformedCode?.code) {
362+
return {
363+
code: transformedCode.code,
364+
map: transformedCode.map ?? null,
360365
}
366+
}
361367

362-
return null
363-
},
364-
})
365-
}
368+
return null
369+
},
370+
})
366371
}
367372

368373
const builder = await createBuilder({

packages/vite/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import { cedarSwapApolloProvider } from './plugins/vite-plugin-swap-apollo-provi
2525
export { cedarAutoImportsPlugin } from './plugins/vite-plugin-cedar-auto-import.js'
2626
export { cedarCjsCompatPlugin } from './plugins/vite-plugin-cedar-cjs-compat.js'
2727
export { cedarCellTransform } from './plugins/vite-plugin-cedar-cell.js'
28+
export { cedarContextWrappingPlugin } from './plugins/vite-plugin-cedar-context-wrapping.js'
29+
export { applyContextWrapping } from './plugins/vite-plugin-cedar-context-wrapping.js'
2830
export { cedarEntryInjectionPlugin } from './plugins/vite-plugin-cedar-entry-injection.js'
2931
export { cedarHtmlEnvPlugin } from './plugins/vite-plugin-cedar-html-env.js'
3032
export { cedarImportDirPlugin } from './plugins/vite-plugin-cedar-import-dir.js'

0 commit comments

Comments
 (0)