Skip to content
Open
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
117 changes: 29 additions & 88 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import alias from '@rollup/plugin-alias'
import { entries } from './scripts/aliases.js'
import { inlineEnums } from './scripts/inline-enums.js'
import { minify as minifySwc } from '@swc/core'
import {
resolveCJSIgnores,
resolveDefines,
resolveEntryFile,
resolveExternal as resolveExternalShared,
} from './scripts/build-shared.js'

/**
* @template T
Expand All @@ -32,7 +38,6 @@ const require = createRequire(import.meta.url)
const __dirname = fileURLToPath(new URL('.', import.meta.url))

const masterVersion = require('./package.json').version
const consolidatePkg = require('@vue/consolidate/package.json')

const privatePackages = fs.readdirSync('packages-private')
const pkgBase = privatePackages.includes(process.env.TARGET)
Expand Down Expand Up @@ -156,54 +161,19 @@ function createConfig(format, output, plugins = []) {
output.name = packageOptions.name
}

let entryFile = /runtime$/.test(format) ? `src/runtime.ts` : `src/index.ts`

// the compat build needs both default AND named exports. This will cause
// Rollup to complain for non-ESM targets, so we use separate entries for
// esm vs. non-esm builds.
if (isCompatPackage && (isBrowserESMBuild || isBundlerESMBuild)) {
entryFile = /runtime$/.test(format)
? `src/esm-runtime.ts`
: `src/esm-index.ts`
}
// Use shared function for entry file resolution
const entryFile = resolveEntryFile(format, isCompatPackage)

function resolveDefine() {
/** @type {Record<string, string>} */
const replacements = {
__COMMIT__: `"${process.env.COMMIT}"`,
__VERSION__: `"${masterVersion}"`,
// this is only used during Vue's internal tests
__TEST__: `false`,
// If the build is expected to run directly in the browser (global / esm builds)
__BROWSER__: String(isBrowserBuild),
__GLOBAL__: String(isGlobalBuild),
__ESM_BUNDLER__: String(isBundlerESMBuild),
__ESM_BROWSER__: String(isBrowserESMBuild),
// is targeting Node (SSR)?
__CJS__: String(isCJSBuild),
// need SSR-specific branches?
__SSR__: String(!isGlobalBuild),

// 2.x compat build
__COMPAT__: String(isCompatBuild),

// feature flags
__FEATURE_SUSPENSE__: `true`,
__FEATURE_OPTIONS_API__: isBundlerESMBuild
? `__VUE_OPTIONS_API__`
: `true`,
__FEATURE_PROD_DEVTOOLS__: isBundlerESMBuild
? `__VUE_PROD_DEVTOOLS__`
: `false`,
__FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__: isBundlerESMBuild
? `__VUE_PROD_HYDRATION_MISMATCH_DETAILS__`
: `false`,
}

if (!isBundlerESMBuild) {
// hard coded dev/prod builds
replacements.__DEV__ = String(!isProductionBuild)
}
// Use shared function for base defines
const replacements = resolveDefines({
pkg,
format,
target: process.env.TARGET,
prod: isProductionBuild,
version: masterVersion,
commit: process.env.COMMIT || 'dev',
})

// allow inline overrides like
//__RUNTIME_COMPILE__=true pnpm build runtime-core
Expand Down Expand Up @@ -255,50 +225,21 @@ function createConfig(format, output, plugins = []) {
}

function resolveExternal() {
const treeShakenDeps = [
'source-map-js',
'@babel/parser',
'estree-walker',
'entities/lib/decode.js',
]

if (isGlobalBuild || isBrowserESMBuild || isCompatPackage) {
if (!packageOptions.enableNonBrowserBranches) {
// normal browser builds - non-browser only imports are tree-shaken,
// they are only listed here to suppress warnings.
return treeShakenDeps
}
} else {
// Node / esm-bundler builds.
// externalize all direct deps unless it's the compat build.
return [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
// for @vue/compiler-sfc / server-renderer
...['path', 'url', 'stream'],
// somehow these throw warnings for runtime-* package builds
...treeShakenDeps,
]
}
// Use shared function for external resolution
return resolveExternalShared({
pkg,
format,
target: process.env.TARGET,
isGlobalBuild,
isBrowserESMBuild,
isCompatPackage,
packageOptions,
})
}

function resolveNodePlugins() {
// we are bundling forked consolidate.js in compiler-sfc which dynamically
// requires a ton of template engines which should be ignored.
/** @type {ReadonlyArray<string>} */
let cjsIgnores = []
if (pkg.name === '@vue/compiler-sfc') {
cjsIgnores = [
...Object.keys(consolidatePkg.devDependencies),
'vm',
'crypto',
'react-dom/server',
'teacup/lib/express',
'arc-templates/dist/es5',
'then-pug',
'then-jade',
]
}
// Use shared function for CJS ignores
const cjsIgnores = resolveCJSIgnores(process.env.TARGET)

const nodePlugins =
(format === 'cjs' && Object.keys(pkg.devDependencies || {}).length) ||
Expand Down
206 changes: 206 additions & 0 deletions scripts/build-shared.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// @ts-check
import { createRequire } from 'node:module'

const require = createRequire(import.meta.url)

/**
* @param {Object} options
* @param {any} options.pkg - Package.json object
* @param {string} options.format - Build format
* @param {string|undefined} options.target - Target package name
* @param {boolean} options.isGlobalBuild - Whether this is a global build
* @param {boolean} options.isBrowserESMBuild - Whether this is a browser ESM build
* @param {boolean} options.isCompatPackage - Whether this is the compat package
* @param {any} options.packageOptions - Package build options
* @returns {string[]}
*/
export function resolveExternal({
pkg,
format,
target = '',
isGlobalBuild = false,
isBrowserESMBuild = false,
isCompatPackage = false,
packageOptions = {},
}) {
const treeShakenDeps = [
'source-map-js',
'@babel/parser',
'estree-walker',
'entities/lib/decode.js',
]

// Global and browser builds inline everything
if (isGlobalBuild || isBrowserESMBuild || isCompatPackage) {
if (!packageOptions.enableNonBrowserBranches) {
return treeShakenDeps
}
}

// Base externals for Node/bundler builds
let external = []

// For CJS and ESM-bundler formats, externalize dependencies
if (format === 'cjs' || format.includes('esm-bundler')) {
external = [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
'path',
'url',
'stream',
]
}

// Special handling for compiler-sfc
if (target === 'compiler-sfc') {
const consolidateDeps = getConsolidateDeps()
external = [
...external,
...consolidateDeps,
'fs',
'vm',
'crypto',
'react-dom/server',
'teacup/lib/express',
'arc-templates/dist/es5',
'then-pug',
'then-jade',
]
}

// Add tree-shaken deps to suppress warnings
if (external.length) {
external = [...external, ...treeShakenDeps]
}

return external
}

/**
* Get consolidate package dependencies
* @returns {string[]}
*/
export function getConsolidateDeps() {
try {
const consolidatePkg = require('@vue/consolidate/package.json')
return Object.keys(consolidatePkg.devDependencies || {})
} catch {
return []
}
}

/**
* Resolve compiler ignore list for CommonJS
* @param {string|undefined} target - Target package name
* @returns {string[]}
*/
export function resolveCJSIgnores(target) {
if (target === 'compiler-sfc') {
return [
...getConsolidateDeps(),
'vm',
'crypto',
'react-dom/server',
'teacup/lib/express',
'arc-templates/dist/es5',
'then-pug',
'then-jade',
]
}
return []
}

/**
* Resolve define/replace values for build
* @param {Object} options
* @param {any} options.pkg - Package.json object
* @param {string} options.format - Build format
* @param {string | undefined} options.target - Target package name
* @param {boolean} options.prod - Whether this is a production build
* @param {string} [options.version] - Version override
* @param {string} [options.commit] - Commit hash
* @returns {Record<string, string>}
*/
export function resolveDefines({
pkg,
format,
target = '',
prod,
version,
commit = 'dev',
}) {
const isBundlerESMBuild = format.includes('esm-bundler')
const isBrowserESMBuild = format.includes('esm-browser')
const isGlobalBuild = format.includes('global')
const isCJSBuild = format === 'cjs'
const isCompatBuild = target === 'vue-compat' || pkg.buildOptions?.compat

// Determine if this is a browser build
const isBrowserBuild =
(isGlobalBuild || isBrowserESMBuild || isBundlerESMBuild) &&
!pkg.buildOptions?.enableNonBrowserBranches

const defines = {
__COMMIT__: `"${commit}"`,
__VERSION__: `"${version || pkg.version}"`,
__DEV__: prod ? `false` : `true`,
__TEST__: `false`,
__BROWSER__: String(isBrowserBuild),
__GLOBAL__: String(isGlobalBuild),
__ESM_BUNDLER__: String(isBundlerESMBuild),
__ESM_BROWSER__: String(isBrowserESMBuild),
__CJS__: String(isCJSBuild),
__SSR__: String(!isGlobalBuild),
__COMPAT__: String(isCompatBuild),
__FEATURE_SUSPENSE__: `true`,
__FEATURE_OPTIONS_API__: isBundlerESMBuild ? `__VUE_OPTIONS_API__` : `true`,
__FEATURE_PROD_DEVTOOLS__: isBundlerESMBuild
? `__VUE_PROD_DEVTOOLS__`
: `false`,
__FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__: isBundlerESMBuild
? `__VUE_PROD_HYDRATION_MISMATCH_DETAILS__`
: `false`,
}

return defines
}

/**
* Resolve output format for different build types
* @param {string} format - Build format string
* @returns {'iife' | 'cjs' | 'esm'}
*/
export function resolveOutputFormat(format) {
if (format.startsWith('global')) return 'iife'
if (format === 'cjs') return 'cjs'
return 'esm'
}

/**
* Resolve output file postfix
* @param {string} format - Build format string
* @returns {string}
*/
export function resolvePostfix(format) {
return format.endsWith('-runtime')
? `runtime.${format.replace(/-runtime$/, '')}`
: format
}

/**
* Resolve entry file based on format and package
* @param {string} format - Build format
* @param {boolean} isCompatPackage - Whether this is compat package
* @returns {string}
*/
export function resolveEntryFile(format, isCompatPackage = false) {
const isRuntime = /runtime$/.test(format)
const isBrowserESMBuild = format.includes('esm-browser')
const isBundlerESMBuild = format.includes('esm-bundler')

if (isCompatPackage && (isBrowserESMBuild || isBundlerESMBuild)) {
return isRuntime ? `src/esm-runtime.ts` : `src/esm-index.ts`
}

return isRuntime ? `src/runtime.ts` : `src/index.ts`
}
Loading