From 11d21911b9eb9796bf0096c36dfa92aa336e04a8 Mon Sep 17 00:00:00 2001 From: patak Date: Sat, 18 Jun 2022 10:32:04 +0200 Subject: [PATCH] feat: support cjs noExternal in SSR dev, fix #2579 (#8430) --- packages/vite/src/node/build.ts | 2 +- packages/vite/src/node/optimizer/index.ts | 125 ++++++++++++++---- packages/vite/src/node/optimizer/optimizer.ts | 48 +++++-- .../vite/src/node/plugins/importAnalysis.ts | 9 +- .../src/node/plugins/importAnalysisBuild.ts | 5 +- .../vite/src/node/plugins/optimizedDeps.ts | 12 +- packages/vite/src/node/plugins/preAlias.ts | 10 +- packages/vite/src/node/plugins/resolve.ts | 22 +-- packages/vite/src/node/server/index.ts | 6 +- .../ssr-deps/__tests__/ssr-deps.spec.ts | 5 + playground/ssr-deps/no-external-cjs/index.js | 3 + .../ssr-deps/no-external-cjs/package.json | 6 + playground/ssr-deps/package.json | 3 +- playground/ssr-deps/server.js | 3 + playground/ssr-deps/src/app.js | 4 + pnpm-lock.yaml | 23 ++-- 16 files changed, 207 insertions(+), 79 deletions(-) create mode 100644 playground/ssr-deps/no-external-cjs/index.js create mode 100644 playground/ssr-deps/no-external-cjs/package.json diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index dcacd542d49e7e..d12542d35200f1 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -733,7 +733,7 @@ async function cjsSsrResolveExternal( ): Promise { // see if we have cached deps data available let knownImports: string[] | undefined - const dataPath = path.join(getDepsCacheDir(config), '_metadata.json') + const dataPath = path.join(getDepsCacheDir(config, false), '_metadata.json') try { const data = JSON.parse( fs.readFileSync(dataPath, 'utf-8') diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 4183c2a13ad66a..6043a0fcd9452a 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -6,8 +6,10 @@ import colors from 'picocolors' import type { BuildOptions as EsbuildBuildOptions } from 'esbuild' import { build } from 'esbuild' import { init, parse } from 'es-module-lexer' +import { createFilter } from '@rollup/pluginutils' import type { ResolvedConfig } from '../config' import { + arraify, createDebugger, emptyDir, flattenId, @@ -43,10 +45,13 @@ export type ExportsData = { } export interface DepsOptimizer { - metadata: DepOptimizationMetadata + metadata: (options: { ssr: boolean }) => DepOptimizationMetadata scanProcessing?: Promise - - registerMissingImport: (id: string, resolved: string) => OptimizedDepInfo + registerMissingImport: ( + id: string, + resolved: string, + ssr?: boolean + ) => OptimizedDepInfo run: () => void isOptimizedDepFile: (id: string) => boolean @@ -232,6 +237,53 @@ export async function optimizeDeps( return result.metadata } +export async function optimizeServerSsrDeps( + config: ResolvedConfig +): Promise { + const cachedMetadata = loadCachedDepOptimizationMetadata( + config, + config.optimizeDeps.force, + false, + true // ssr + ) + if (cachedMetadata) { + return cachedMetadata + } + + let alsoInclude: string[] | undefined + let noExternalFilter: ((id: unknown) => boolean) | undefined + + const noExternal = config.ssr?.noExternal + if (noExternal) { + alsoInclude = arraify(noExternal).filter( + (ne) => typeof ne === 'string' + ) as string[] + noExternalFilter = + noExternal === true + ? (dep: unknown) => false + : createFilter(noExternal, config.optimizeDeps?.exclude, { + resolve: false + }) + } + + const deps: Record = {} + + await addManuallyIncludedOptimizeDeps( + deps, + config, + alsoInclude, + noExternalFilter + ) + + const depsInfo = toDiscoveredDependencies(config, deps, true) + + const result = await runOptimizeDeps(config, depsInfo, true) + + await result.commit() + + return result.metadata +} + export function initDepsOptimizerMetadata( config: ResolvedConfig, timestamp?: string @@ -264,7 +316,8 @@ export function addOptimizedDepInfo( export function loadCachedDepOptimizationMetadata( config: ResolvedConfig, force = config.optimizeDeps.force, - asCommand = false + asCommand = false, + ssr = !!config.build.ssr ): DepOptimizationMetadata | undefined { const log = asCommand ? config.logger.info : debug @@ -274,7 +327,7 @@ export function loadCachedDepOptimizationMetadata( emptyDir(config.cacheDir) } - const depsCacheDir = getDepsCacheDir(config) + const depsCacheDir = getDepsCacheDir(config, ssr) if (!force) { let cachedMetadata: DepOptimizationMetadata | undefined @@ -341,6 +394,15 @@ export async function initialProjectDependencies( await addManuallyIncludedOptimizeDeps(deps, config) + return toDiscoveredDependencies(config, deps, !!config.build.ssr, timestamp) +} + +export function toDiscoveredDependencies( + config: ResolvedConfig, + deps: Record, + ssr: boolean, + timestamp?: string +): Record { const browserHash = getOptimizedBrowserHash( getDepHash(config), deps, @@ -351,7 +413,7 @@ export async function initialProjectDependencies( const src = deps[id] discovered[id] = { id, - file: getOptimizedDepPath(id, config), + file: getOptimizedDepPath(id, config, ssr), src, browserHash: browserHash, exportsData: extractExportsData(src, config) @@ -381,7 +443,8 @@ export function depsLogString(qualifiedIds: string[]): string { */ export async function runOptimizeDeps( resolvedConfig: ResolvedConfig, - depsInfo: Record + depsInfo: Record, + ssr: boolean = !!resolvedConfig.build.ssr ): Promise { const isBuild = resolvedConfig.command === 'build' const config: ResolvedConfig = { @@ -389,8 +452,8 @@ export async function runOptimizeDeps( command: 'build' } - const depsCacheDir = getDepsCacheDir(resolvedConfig) - const processingCacheDir = getProcessingDepsCacheDir(resolvedConfig) + const depsCacheDir = getDepsCacheDir(resolvedConfig, ssr) + const processingCacheDir = getProcessingDepsCacheDir(resolvedConfig, ssr) // Create a temporal directory so we don't need to delete optimized deps // until they have been processed. This also avoids leaving the deps cache @@ -526,7 +589,7 @@ export async function runOptimizeDeps( const id = path .relative(processingCacheDirOutputPath, o) .replace(jsExtensionRE, '') - const file = getOptimizedDepPath(id, resolvedConfig) + const file = getOptimizedDepPath(id, resolvedConfig, ssr) if ( !findOptimizedDepInfoInRecord( metadata.optimized, @@ -561,16 +624,18 @@ export async function findKnownImports( async function addManuallyIncludedOptimizeDeps( deps: Record, - config: ResolvedConfig + config: ResolvedConfig, + extra?: string[], + filter?: (id: string) => boolean ): Promise { - const include = config.optimizeDeps?.include + const include = [...(config.optimizeDeps?.include ?? []), ...(extra ?? [])] if (include) { const resolve = config.createResolver({ asSrc: false, scan: true }) for (const id of include) { // normalize 'foo >bar` as 'foo > bar' to prevent same id being added // and for pretty printing const normalizedId = normalizeId(id) - if (!deps[normalizedId]) { + if (!deps[normalizedId] && filter?.(normalizedId) !== false) { const entry = await resolve(id) if (entry) { deps[normalizedId] = entry @@ -603,14 +668,15 @@ export function depsFromOptimizedDepInfo( export function getOptimizedDepPath( id: string, - config: ResolvedConfig + config: ResolvedConfig, + ssr: boolean = !!config.build.ssr ): string { return normalizePath( - path.resolve(getDepsCacheDir(config), flattenId(id) + '.js') + path.resolve(getDepsCacheDir(config, ssr), flattenId(id) + '.js') ) } -function getDepsCacheSuffix(config: ResolvedConfig): string { +function getDepsCacheSuffix(config: ResolvedConfig, ssr: boolean): string { let suffix = '' if (config.command === 'build') { // Differentiate build caches depending on outDir to allow parallel builds @@ -618,34 +684,39 @@ function getDepsCacheSuffix(config: ResolvedConfig): string { const buildId = outDir.length > 8 || outDir.includes('/') ? getHash(outDir) : outDir suffix += `_build-${buildId}` - if (config.build.ssr) { - suffix += '_ssr' - } + } + if (ssr) { + suffix += '_ssr' } return suffix } -export function getDepsCacheDir(config: ResolvedConfig): string { - const dirName = 'deps' + getDepsCacheSuffix(config) - return normalizePath(path.resolve(config.cacheDir, dirName)) + +export function getDepsCacheDir(config: ResolvedConfig, ssr: boolean): string { + return getDepsCacheDirPrefix(config) + getDepsCacheSuffix(config, ssr) +} + +function getProcessingDepsCacheDir(config: ResolvedConfig, ssr: boolean) { + return ( + getDepsCacheDirPrefix(config) + getDepsCacheSuffix(config, ssr) + '_temp' + ) } -function getProcessingDepsCacheDir(config: ResolvedConfig) { - const dirName = 'deps' + getDepsCacheSuffix(config) + '_temp' - return normalizePath(path.resolve(config.cacheDir, dirName)) +export function getDepsCacheDirPrefix(config: ResolvedConfig): string { + return normalizePath(path.resolve(config.cacheDir, 'deps')) } export function isOptimizedDepFile( id: string, config: ResolvedConfig ): boolean { - return id.startsWith(getDepsCacheDir(config)) + return id.startsWith(getDepsCacheDirPrefix(config)) } export function createIsOptimizedDepUrl( config: ResolvedConfig ): (url: string) => boolean { const { root } = config - const depsCacheDir = getDepsCacheDir(config) + const depsCacheDir = getDepsCacheDirPrefix(config) // determine the url prefix of files inside cache directory const depsCacheDirRelative = normalizePath(path.relative(root, depsCacheDir)) diff --git a/packages/vite/src/node/optimizer/optimizer.ts b/packages/vite/src/node/optimizer/optimizer.ts index 8f6e7de61b2845..5592c438ece552 100644 --- a/packages/vite/src/node/optimizer/optimizer.ts +++ b/packages/vite/src/node/optimizer/optimizer.ts @@ -18,9 +18,11 @@ import { isOptimizedDepFile, loadCachedDepOptimizationMetadata, newDepOptimizationProcessing, + optimizeServerSsrDeps, runOptimizeDeps } from '.' import type { + DepOptimizationMetadata, DepOptimizationProcessing, DepsOptimizer, OptimizedDepInfo @@ -58,9 +60,18 @@ export async function initDepsOptimizer( let handle: NodeJS.Timeout | undefined + let ssrServerDepsMetadata: DepOptimizationMetadata + let _metadata = + cachedMetadata || initDepsOptimizerMetadata(config, sessionTimestamp) + const depsOptimizer: DepsOptimizer = { - metadata: - cachedMetadata || initDepsOptimizerMetadata(config, sessionTimestamp), + metadata: (options: { ssr: boolean }) => { + if (isBuild || !options.ssr) { + return _metadata + } else { + return ssrServerDepsMetadata + } + }, registerMissingImport, run: () => debouncedProcessing(0), isOptimizedDepFile: (id: string) => isOptimizedDepFile(id, config), @@ -75,6 +86,10 @@ export async function initDepsOptimizer( depsOptimizerMap.set(config, depsOptimizer) + if (!isBuild && config.ssr) { + ssrServerDepsMetadata = await optimizeServerSsrDeps(config) + } + let newDepsDiscovered = false let newDepsToLog: string[] = [] @@ -119,7 +134,7 @@ export async function initDepsOptimizer( config, sessionTimestamp ) - const { metadata } = depsOptimizer + const metadata = _metadata for (const depInfo of Object.values(discovered)) { addOptimizedDepInfo(metadata, 'discovered', { ...depInfo, @@ -137,7 +152,7 @@ export async function initDepsOptimizer( try { debug(colors.green(`scanning for dependencies...`)) - const { metadata } = depsOptimizer + const metadata = _metadata const discovered = await discoverProjectDependencies( config, @@ -183,7 +198,7 @@ export async function initDepsOptimizer( // Ensure that a rerun will not be issued for current discovered deps if (handle) clearTimeout(handle) - if (Object.keys(depsOptimizer.metadata.discovered).length === 0) { + if (Object.keys(_metadata.discovered).length === 0) { currentlyProcessing = false return } @@ -193,13 +208,13 @@ export async function initDepsOptimizer( // a succesful completion of the optimizeDeps rerun will end up // creating new bundled version of all current and discovered deps // in the cache dir and a new metadata info object assigned - // to optimizeDeps.metadata. A fullReload is only issued if - // the previous bundled dependencies have changed. + // to _metadata. A fullReload is only issued if the previous bundled + // dependencies have changed. - // if the rerun fails, optimizeDeps.metadata remains untouched, - // current discovered deps are cleaned, and a fullReload is issued + // if the rerun fails, _metadata remains untouched, current discovered + // deps are cleaned, and a fullReload is issued - let { metadata } = depsOptimizer + let metadata = _metadata // All deps, previous known and newly discovered are rebundled, // respect insertion order to keep the metadata file stable @@ -306,7 +321,7 @@ export async function initDepsOptimizer( ) } - metadata = depsOptimizer.metadata = newData + metadata = _metadata = newData resolveEnqueuedProcessingPromises() } @@ -400,7 +415,7 @@ export async function initDepsOptimizer( // debounce time to wait for new missing deps finished, issue a new // optimization of deps (both old and newly found) once the previous // optimizeDeps processing is finished - const deps = Object.keys(depsOptimizer.metadata.discovered) + const deps = Object.keys(_metadata.discovered) const depsString = depsLogString(deps) debug(colors.green(`new dependencies found: ${depsString}`)) runOptimizer() @@ -426,7 +441,12 @@ export async function initDepsOptimizer( 'Vite internal error: registering missing import before initial scanning is over' ) } - const { metadata } = depsOptimizer + if (!isBuild && ssr) { + config.logger.error( + `Error: ${id} is a missing dependency in SSR dev server, it needs to be added to optimizeDeps.include` + ) + } + const metadata = _metadata const optimized = metadata.optimized[id] if (optimized) { return optimized @@ -444,7 +464,7 @@ export async function initDepsOptimizer( newDepsDiscovered = true missing = addOptimizedDepInfo(metadata, 'discovered', { id, - file: getOptimizedDepPath(id, config), + file: getOptimizedDepPath(id, config, ssr), src: resolved, // Assing a browserHash to this missing dependency that is unique to // the current state of known + missing deps. If its optimizeDeps run diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index cbf38a473b464e..f0afb794060ef3 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -49,7 +49,7 @@ import { } from '../ssr/ssrExternal' import { transformRequest } from '../server/transformRequest' import { - getDepsCacheDir, + getDepsCacheDirPrefix, getDepsOptimizer, optimizedDepNeedsInterop } from '../optimizer' @@ -224,7 +224,8 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { // the dependency needs to be resolved starting from the original source location of the optimized file // because starting from node_modules/.vite will not find the dependency if it was not hoisted // (that is, if it is under node_modules directory in the package source of the optimized file) - for (const optimizedModule of depsOptimizer.metadata.depInfoList) { + for (const optimizedModule of depsOptimizer.metadata({ ssr }) + .depInfoList) { if (!optimizedModule.src) continue // Ignore chunks if (optimizedModule.file === importerModule.file) { importerFile = optimizedModule.src @@ -258,7 +259,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { // in root: infer short absolute path from root url = resolved.id.slice(root.length) } else if ( - resolved.id.startsWith(getDepsCacheDir(config)) || + resolved.id.startsWith(getDepsCacheDirPrefix(config)) || fs.existsSync(cleanUrl(resolved.id)) ) { // an optimized deps may not yet exists in the filesystem, or @@ -420,7 +421,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { const file = cleanUrl(resolvedId) // Remove ?v={hash} const needsInterop = await optimizedDepNeedsInterop( - depsOptimizer.metadata, + depsOptimizer.metadata({ ssr }), file, config ) diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index 0895bf5cb8f8a4..f56d2a4194ca84 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -172,7 +172,8 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { // the dependency needs to be resolved starting from the original source location of the optimized file // because starting from node_modules/.vite will not find the dependency if it was not hoisted // (that is, if it is under node_modules directory in the package source of the optimized file) - for (const optimizedModule of depsOptimizer.metadata.depInfoList) { + for (const optimizedModule of depsOptimizer.metadata({ ssr }) + .depInfoList) { if (!optimizedModule.src) continue // Ignore chunks if (optimizedModule.file === importer) { importerFile = optimizedModule.src @@ -263,7 +264,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { const file = cleanUrl(resolvedId) // Remove ?v={hash} const needsInterop = await optimizedDepNeedsInterop( - depsOptimizer.metadata, + depsOptimizer.metadata({ ssr }), file, config ) diff --git a/packages/vite/src/node/plugins/optimizedDeps.ts b/packages/vite/src/node/plugins/optimizedDeps.ts index b50294f9e64b9b..3a3f541adce3bc 100644 --- a/packages/vite/src/node/plugins/optimizedDeps.ts +++ b/packages/vite/src/node/plugins/optimizedDeps.ts @@ -27,10 +27,11 @@ export function optimizedDepsPlugin(config: ResolvedConfig): Plugin { // The logic to register an id to wait until it is processed // is in importAnalysis, see call to delayDepsOptimizerUntil - async load(id) { + async load(id, options) { + const ssr = options?.ssr ?? false const depsOptimizer = getDepsOptimizer(config) if (depsOptimizer?.isOptimizedDepFile(id)) { - const metadata = depsOptimizer?.metadata + const metadata = depsOptimizer?.metadata({ ssr }) if (metadata) { const file = cleanUrl(id) const versionMatch = id.match(DEP_VERSION_RE) @@ -54,7 +55,7 @@ export function optimizedDepsPlugin(config: ResolvedConfig): Plugin { throwProcessingError(id) return } - const newMetadata = depsOptimizer.metadata + const newMetadata = depsOptimizer.metadata({ ssr }) if (metadata !== newMetadata) { const currentInfo = optimizedDepInfoFromFile(newMetadata!, file) if (info.browserHash !== currentInfo?.browserHash) { @@ -100,9 +101,10 @@ export function optimizedDepsBuildPlugin(config: ResolvedConfig): Plugin { }) }, - async load(id) { + async load(id, options) { + const ssr = options?.ssr ?? false const depsOptimizer = getDepsOptimizer(config) - const metadata = depsOptimizer?.metadata + const metadata = depsOptimizer?.metadata({ ssr }) if (!metadata || !depsOptimizer?.isOptimizedDepFile(id)) { return } diff --git a/packages/vite/src/node/plugins/preAlias.ts b/packages/vite/src/node/plugins/preAlias.ts index 0d6076b03a329f..46bc2436b0686a 100644 --- a/packages/vite/src/node/plugins/preAlias.ts +++ b/packages/vite/src/node/plugins/preAlias.ts @@ -11,14 +11,10 @@ export function preAliasPlugin(config: ResolvedConfig): Plugin { return { name: 'vite:pre-alias', async resolveId(id, importer, options) { + const ssr = options?.ssr ?? false const depsOptimizer = getDepsOptimizer(config) - if ( - depsOptimizer && - !options?.ssr && - bareImportRE.test(id) && - !options?.scan - ) { - return await tryOptimizedResolve(depsOptimizer, id, importer) + if (depsOptimizer && bareImportRE.test(id) && !options?.scan) { + return await tryOptimizedResolve(depsOptimizer, ssr, id, importer) } } } diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 7ccc3a7eb5cb7a..f552b19a08a21a 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -89,7 +89,6 @@ export interface InternalResolveOptions extends ResolveOptions { export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { const { root, - isBuild, isProduction, asSrc, ssrConfig, @@ -187,7 +186,7 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { // Inject the current browserHash version if the path doesn't have one if (!normalizedFsPath.match(DEP_VERSION_RE)) { const browserHash = optimizedDepInfoFromFile( - depsOptimizer.metadata, + depsOptimizer.metadata({ ssr }), normalizedFsPath )?.browserHash if (browserHash) { @@ -266,9 +265,8 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { !external && asSrc && depsOptimizer && - (isBuild || !ssr) && !options.scan && - (res = await tryOptimizedResolve(depsOptimizer, id, importer)) + (res = await tryOptimizedResolve(depsOptimizer, ssr, id, importer)) ) { return res } @@ -542,6 +540,8 @@ export function tryNodeResolve( ): PartialResolvedId | undefined { const { root, dedupe, isBuild, preserveSymlinks, packageCache } = options + ssr ??= false + // split id by last '>' for nested selected packages, for example: // 'foo > bar > baz' => 'foo > bar' & 'baz' // 'foo' => '' & 'foo' @@ -687,7 +687,7 @@ export function tryNodeResolve( // otherwise we may introduce duplicated modules for externalized files // from pre-bundled deps. if (!isBuild) { - const versionHash = depsOptimizer.metadata.browserHash + const versionHash = depsOptimizer.metadata({ ssr }).browserHash if (versionHash && isJsType) { resolved = injectQuery(resolved, `v=${versionHash}`) } @@ -695,7 +695,7 @@ export function tryNodeResolve( } else { // this is a missing import, queue optimize-deps re-run and // get a resolved its optimized info - const optimizedInfo = depsOptimizer.registerMissingImport(id, resolved) + const optimizedInfo = depsOptimizer.registerMissingImport(id, resolved, ssr) resolved = depsOptimizer.getOptimizedDepId(optimizedInfo) } @@ -713,12 +713,18 @@ export function tryNodeResolve( export async function tryOptimizedResolve( depsOptimizer: DepsOptimizer, + ssr: boolean, id: string, importer?: string ): Promise { await depsOptimizer.scanProcessing - const depInfo = optimizedDepInfoFromId(depsOptimizer.metadata, id) + const metadata = depsOptimizer.metadata({ ssr }) + if (!metadata) { + return + } + + const depInfo = optimizedDepInfoFromId(metadata, id) if (depInfo) { return depsOptimizer.getOptimizedDepId(depInfo) } @@ -728,7 +734,7 @@ export async function tryOptimizedResolve( // further check if id is imported by nested dependency let resolvedSrc: string | undefined - for (const optimizedData of depsOptimizer.metadata.depInfoList) { + for (const optimizedData of metadata.depInfoList) { if (!optimizedData.src) continue // Ignore chunks const pkgPath = optimizedData.id diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index fc66b909a33989..169600b680fd99 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -749,13 +749,15 @@ async function restartServer(server: ViteDevServer) { async function updateCjsSsrExternals(server: ViteDevServer) { if (!server._ssrExternals) { + // We use the non-ssr optimized deps to find known imports let knownImports: string[] = [] const depsOptimizer = getDepsOptimizer(server.config) if (depsOptimizer) { await depsOptimizer.scanProcessing + const metadata = depsOptimizer.metadata({ ssr: false }) knownImports = [ - ...Object.keys(depsOptimizer.metadata.optimized), - ...Object.keys(depsOptimizer.metadata.discovered) + ...Object.keys(metadata.optimized), + ...Object.keys(metadata.discovered) ] } server._ssrExternals = cjsSsrResolveExternals(server.config, knownImports) diff --git a/playground/ssr-deps/__tests__/ssr-deps.spec.ts b/playground/ssr-deps/__tests__/ssr-deps.spec.ts index 75903a1c39c943..dbb169647fc603 100644 --- a/playground/ssr-deps/__tests__/ssr-deps.spec.ts +++ b/playground/ssr-deps/__tests__/ssr-deps.spec.ts @@ -72,3 +72,8 @@ test('msg from only object assigned exports', async () => { 'Hello World!' ) }) + +test('msg from no external cjs', async () => { + await page.goto(url) + expect(await page.textContent('.no-external-cjs-msg')).toMatch('Hello World!') +}) diff --git a/playground/ssr-deps/no-external-cjs/index.js b/playground/ssr-deps/no-external-cjs/index.js new file mode 100644 index 00000000000000..d4fc9da147c88f --- /dev/null +++ b/playground/ssr-deps/no-external-cjs/index.js @@ -0,0 +1,3 @@ +exports.hello = function () { + return 'Hello World!' +} diff --git a/playground/ssr-deps/no-external-cjs/package.json b/playground/ssr-deps/no-external-cjs/package.json new file mode 100644 index 00000000000000..799658987d7b2c --- /dev/null +++ b/playground/ssr-deps/no-external-cjs/package.json @@ -0,0 +1,6 @@ +{ + "name": "primitive-export", + "private": true, + "type": "commonjs", + "version": "0.0.0" +} diff --git a/playground/ssr-deps/package.json b/playground/ssr-deps/package.json index 74bbf77dd97bdd..4dd1ee49aea717 100644 --- a/playground/ssr-deps/package.json +++ b/playground/ssr-deps/package.json @@ -18,7 +18,8 @@ "primitive-export": "file:./primitive-export", "read-file-content": "file:./read-file-content", "require-absolute": "file:./require-absolute", - "ts-transpiled-exports": "file:./ts-transpiled-exports" + "ts-transpiled-exports": "file:./ts-transpiled-exports", + "no-external-cjs": "file:./no-external-cjs" }, "devDependencies": { "cross-env": "^7.0.3", diff --git a/playground/ssr-deps/server.js b/playground/ssr-deps/server.js index f205255320cfe2..f1707892ab6aca 100644 --- a/playground/ssr-deps/server.js +++ b/playground/ssr-deps/server.js @@ -32,6 +32,9 @@ export async function createServer(root = process.cwd(), hmrPort) { hmr: { port: hmrPort } + }, + ssr: { + noExternal: ['no-external-cjs'] } }) // use vite's connect instance as middleware diff --git a/playground/ssr-deps/src/app.js b/playground/ssr-deps/src/app.js index 9646cdcf2bf688..6cf0c32374913b 100644 --- a/playground/ssr-deps/src/app.js +++ b/playground/ssr-deps/src/app.js @@ -9,6 +9,7 @@ import definePropertiesExports from 'define-properties-exports' import definePropertyExports from 'define-property-exports' import onlyObjectAssignedExports from 'only-object-assigned-exports' import requireAbsolute from 'require-absolute' +import noExternalCjs from 'no-external-cjs' export async function render(url, rootDir) { let html = '' @@ -45,5 +46,8 @@ export async function render(url, rootDir) { const requireAbsoluteMessage = requireAbsolute.hello() html += `\n

message from require-absolute: ${requireAbsoluteMessage}

` + const noExternalCjsMessage = noExternalCjs.hello() + html += `\n

message from no-external-cjs: ${noExternalCjsMessage}

` + return html + '\n' } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c72f8028f1b324..50bf6b16a92af4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -811,6 +811,7 @@ importers: define-property-exports: file:./define-property-exports express: ^4.18.1 forwarded-export: file:./forwarded-export + no-external-cjs: file:./no-external-cjs object-assigned-exports: file:./object-assigned-exports only-object-assigned-exports: file:./only-object-assigned-exports primitive-export: file:./primitive-export @@ -822,6 +823,7 @@ importers: define-properties-exports: file:playground/ssr-deps/define-properties-exports define-property-exports: file:playground/ssr-deps/define-property-exports forwarded-export: file:playground/ssr-deps/forwarded-export + no-external-cjs: file:playground/ssr-deps/no-external-cjs object-assigned-exports: file:playground/ssr-deps/object-assigned-exports only-object-assigned-exports: file:playground/ssr-deps/only-object-assigned-exports primitive-export: file:playground/ssr-deps/primitive-export @@ -844,6 +846,9 @@ importers: dependencies: object-assigned-exports: file:playground/ssr-deps/object-assigned-exports + playground/ssr-deps/no-external-cjs: + specifiers: {} + playground/ssr-deps/object-assigned-exports: specifiers: {} @@ -3808,7 +3813,7 @@ packages: dev: true /encodeurl/1.0.2: - resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=} + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} dev: true @@ -4468,7 +4473,7 @@ packages: engines: {node: '>=6'} /escape-html/1.0.3: - resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=} + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} dev: true /escape-string-regexp/1.0.5: @@ -6021,7 +6026,7 @@ packages: engines: {node: '>= 8'} /methods/1.1.2: - resolution: {integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=} + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} dev: true @@ -6388,10 +6393,6 @@ packages: engines: {node: '>= 6'} dev: false - /object-inspect/1.12.0: - resolution: {integrity: sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==} - dev: true - /object-inspect/1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} dev: true @@ -7544,7 +7545,7 @@ packages: dependencies: call-bind: 1.0.2 get-intrinsic: 1.1.1 - object-inspect: 1.12.0 + object-inspect: 1.12.2 dev: true /signal-exit/3.0.7: @@ -8795,6 +8796,12 @@ packages: object-assigned-exports: file:playground/ssr-deps/object-assigned-exports dev: false + file:playground/ssr-deps/no-external-cjs: + resolution: {directory: playground/ssr-deps/no-external-cjs, type: directory} + name: primitive-export + version: 0.0.0 + dev: false + file:playground/ssr-deps/object-assigned-exports: resolution: {directory: playground/ssr-deps/object-assigned-exports, type: directory} name: object-assigned-exports