Skip to content
46 changes: 46 additions & 0 deletions packages/vite/src/node/__tests__/plugins/completeAmdWrap.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { describe, expect, test } from 'vitest'
import { completeAmdWrapPlugin } from '../../plugins/completeAmdWrap'

async function createCompleteAmdWrapPluginRenderChunk() {
const instance = completeAmdWrapPlugin()

return async (code: string) => {
// @ts-expect-error transform.handler should exist
const result = await instance.renderChunk.call(instance, code, 'foo.ts', {
format: 'amd',
})
return result?.code || result
}
}

describe('completeAmdWrapPlugin', async () => {
const renderChunk = await createCompleteAmdWrapPluginRenderChunk()

describe('adds require parameter', async () => {
test('without other dependencies', async () => {
expect(
await renderChunk('define((function() { } ))'),
).toMatchInlineSnapshot(`"define(["require"], (function(require) { } ))"`)
})

test('with other dependencies', async () => {
expect(
await renderChunk(
'define(["vue", "vue-router"], function(vue, vueRouter) { } ))',
),
).toMatchInlineSnapshot(
`"define(["require", "vue", "vue-router"], (function(require, vue, vueRouter) { } ))"`,
)
})

test("only if require isn't injected already", async () => {
expect(
await renderChunk('define(["require"], function(require) { } ))'),
).toMatchInlineSnapshot(`"define(["require"], (function(require) { } ))"`)

expect(
await renderChunk(`define(['require'], function(require) { } ))`),
).toMatchInlineSnapshot(`"define(['require'], (function(require) { } ))"`)
})
})
})
2 changes: 2 additions & 0 deletions packages/vite/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
resolveChokidarOptions,
resolveEmptyOutDir,
} from './watch'
import { completeAmdWrapPlugin } from './plugins/completeAmdWrap'
import { completeSystemWrapPlugin } from './plugins/completeSystemWrap'
import { webWorkerPostPlugin } from './plugins/worker'
import { getHookHandler } from './plugins'
Expand Down Expand Up @@ -466,6 +467,7 @@ export async function resolveBuildPlugins(config: ResolvedConfig): Promise<{
}> {
return {
pre: [
completeAmdWrapPlugin(),
completeSystemWrapPlugin(),
...(!config.isWorker ? [prepareOutDirPlugin()] : []),
perEnvironmentPlugin('commonjs', (environment) => {
Expand Down
30 changes: 30 additions & 0 deletions packages/vite/src/node/plugins/completeAmdWrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { Plugin } from '../plugin'

/**
* ensure amd bundles request `require` to be injected
*/
export function completeAmdWrapPlugin(): Plugin {
const AmdWrapRE =
/\bdefine\((?:\s*\[([^\]]*)\],)?\s*(?:\(\s*)?function\s*\(([^)]*)\)\s*\{/g

return {
name: 'vite:force-amd-wrap-require',
renderChunk(code, _chunk, opts) {
if (opts.format !== 'amd') return

return {
code: code.replace(AmdWrapRE, (_, deps, params) => {
if (deps?.includes(`"require"`) || deps?.includes(`'require'`)) {
return `define([${deps}], (function(${params}) {`
}

const newDeps = deps ? `"require", ${deps}` : '"require"'
const newParams = params.trim() ? `require, ${params}` : 'require'

return `define([${newDeps}], (function(${newParams}) {`
}),
map: null, // no need to generate sourcemap as no mapping exists for the wrapper
}
},
}
}