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

fix: throw missing name error only when 'umd' or 'iife' are used #9886

Merged
merged 1 commit into from Nov 8, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 98 additions & 1 deletion packages/vite/src/node/__tests__/build.spec.ts
@@ -1,9 +1,11 @@
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import type { Logger } from 'vite'
import { describe, expect, test } from 'vitest'
import { describe, expect, test, vi } from 'vitest'
import type { OutputOptions } from 'rollup'
import type { LibraryFormats, LibraryOptions } from '../build'
import { resolveBuildOutputs, resolveLibFilename } from '../build'
import { createLogger } from '../logger'

const __dirname = resolve(fileURLToPath(import.meta.url), '..')

Expand All @@ -13,6 +15,101 @@ const baseLibOptions: LibraryOptions = {
entry: 'mylib.js'
}

describe('resolveBuildOutputs', () => {
test('resolves outputs correctly', () => {
const logger = createLogger()
const libOptions: LibraryOptions = { ...baseLibOptions }
const outputs: OutputOptions[] = [{ format: 'es' }]
const resolvedOutputs = resolveBuildOutputs(outputs, libOptions, logger)

expect(resolvedOutputs).toEqual([
{
format: 'es'
}
])
})

test('resolves outputs from lib options', () => {
const logger = createLogger()
const libOptions: LibraryOptions = { ...baseLibOptions, name: 'lib' }
const resolvedOutputs = resolveBuildOutputs(void 0, libOptions, logger)

expect(resolvedOutputs).toEqual([
{
format: 'es'
},
{
format: 'umd'
}
])
})

test('does not change outputs when lib options are missing', () => {
const logger = createLogger()
const outputs: OutputOptions[] = [{ format: 'es' }]
const resolvedOutputs = resolveBuildOutputs(outputs, false, logger)

expect(resolvedOutputs).toEqual(outputs)
})

test('logs a warning when outputs is an array and formats are specified', () => {
const logger = createLogger()
const loggerSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {})
const libOptions: LibraryOptions = {
...baseLibOptions,
formats: ['iife']
}
const outputs: OutputOptions[] = [{ format: 'es' }]

resolveBuildOutputs(outputs, libOptions, logger)

expect(loggerSpy).toHaveBeenCalledWith(
expect.stringContaining('"build.lib.formats" will be ignored because')
)
})

test('throws an error when lib.name is missing on iife format', () => {
const logger = createLogger()
const libOptions: LibraryOptions = {
...baseLibOptions,
formats: ['iife']
}
const resolveBuild = () => resolveBuildOutputs(void 0, libOptions, logger)

expect(resolveBuild).toThrowError(/Option "build\.lib\.name" is required/)
})

test('throws an error when lib.name is missing on umd format', () => {
const logger = createLogger()
const libOptions: LibraryOptions = { ...baseLibOptions, formats: ['umd'] }
const resolveBuild = () => resolveBuildOutputs(void 0, libOptions, logger)

expect(resolveBuild).toThrowError(/Option "build\.lib\.name" is required/)
})

test('throws an error when output.name is missing on iife format', () => {
const logger = createLogger()
const libOptions: LibraryOptions = { ...baseLibOptions }
const outputs: OutputOptions[] = [{ format: 'iife' }]
const resolveBuild = () => resolveBuildOutputs(outputs, libOptions, logger)

expect(resolveBuild).toThrowError(
/Entries in "build\.rollupOptions\.output" must specify "name"/
)
})

test('throws an error when output.name is missing on umd format', () => {
const logger = createLogger()
const libOptions: LibraryOptions = { ...baseLibOptions }
const outputs: OutputOptions[] = [{ format: 'umd' }]
const resolveBuild = () => resolveBuildOutputs(outputs, libOptions, logger)

expect(resolveBuild).toThrowError(
/Entries in "build\.rollupOptions\.output" must specify "name"/
)
})
})

describe('resolveLibFilename', () => {
test('custom filename function', () => {
const filename = resolveLibFilename(
Expand Down
54 changes: 31 additions & 23 deletions packages/vite/src/node/build.ts
Expand Up @@ -783,41 +783,49 @@ export function resolveBuildOutputs(
logger: Logger
): OutputOptions | OutputOptions[] | undefined {
if (libOptions) {
const hasMultipleEntries =
const libHasMultipleEntries =
typeof libOptions.entry !== 'string' &&
Object.values(libOptions.entry).length > 1
const libFormats =
libOptions.formats ||
(libHasMultipleEntries ? ['es', 'cjs'] : ['es', 'umd'])

if (!Array.isArray(outputs)) {
if (libFormats.includes('umd') || libFormats.includes('iife')) {
if (libHasMultipleEntries) {
throw new Error(
'Multiple entry points are not supported when output formats include "umd" or "iife".'
)
}

const formats =
libOptions.formats || (hasMultipleEntries ? ['es', 'cjs'] : ['es', 'umd'])

if (formats.includes('umd') || formats.includes('iife')) {
if (hasMultipleEntries) {
throw new Error(
`Multiple entry points are not supported when output formats include "umd" or "iife".`
)
if (!libOptions.name) {
throw new Error(
'Option "build.lib.name" is required when output formats include "umd" or "iife".'
)
}
}

if (!libOptions.name) {
throw new Error(
`Option "build.lib.name" is required when output formats ` +
`include "umd" or "iife".`
)
}
return libFormats.map((format) => ({ ...outputs, format }))
}
if (!outputs) {
return formats.map((format) => ({ format }))
} else if (!Array.isArray(outputs)) {
return formats.map((format) => ({ ...outputs, format }))
} else if (libOptions.formats) {
// user explicitly specifying own output array

// By this point, we know "outputs" is an Array.
if (libOptions.formats) {
logger.warn(
colors.yellow(
`"build.lib.formats" will be ignored because ` +
`"build.rollupOptions.output" is already an array format`
'"build.lib.formats" will be ignored because "build.rollupOptions.output" is already an array format.'
)
)
}

outputs.forEach((output) => {
if (['umd', 'iife'].includes(output.format!) && !output.name) {
throw new Error(
'Entries in "build.rollupOptions.output" must specify "name" when the format is "umd" or "iife".'
)
}
})
}

return outputs
}

Expand Down