|
1 | 1 | import { promises as fs } from 'fs' |
2 | | -import { join, relative } from 'path' |
| 2 | +import { join } from 'path' |
3 | 3 |
|
4 | 4 | import commonPathPrefix from 'common-path-prefix' |
5 | 5 | import { v4 as uuidv4 } from 'uuid' |
6 | 6 |
|
7 | 7 | import { importMapSpecifier } from '../shared/consts.js' |
8 | 8 |
|
9 | | -import { DenoBridge, DenoOptions, OnAfterDownloadHook, OnBeforeDownloadHook } from './bridge.js' |
| 9 | +import { |
| 10 | + DenoBridge, |
| 11 | + DenoOptions, |
| 12 | + OnAfterDownloadHook, |
| 13 | + OnBeforeDownloadHook, |
| 14 | + LEGACY_DENO_VERSION_RANGE, |
| 15 | +} from './bridge.js' |
10 | 16 | import type { Bundle } from './bundle.js' |
11 | 17 | import { FunctionConfig, getFunctionConfig } from './config.js' |
12 | 18 | import { Declaration, mergeDeclarations } from './declaration.js' |
13 | 19 | import { load as loadDeployConfig } from './deploy_config.js' |
14 | 20 | import { EdgeFunction } from './edge_function.js' |
15 | 21 | import { FeatureFlags, getFlags } from './feature_flags.js' |
16 | 22 | import { findFunctions } from './finder.js' |
17 | | -import { bundle as bundleESZIP, extension as eszipExtension, extract as extractESZIP } from './formats/eszip.js' |
| 23 | +import { bundle as bundleESZIP } from './formats/eszip.js' |
18 | 24 | import { bundle as bundleTarball } from './formats/tarball.js' |
19 | 25 | import { ImportMap } from './import_map.js' |
20 | 26 | import { getLogger, LogFunction, Logger } from './logger.js' |
21 | 27 | import { writeManifest } from './manifest.js' |
22 | 28 | import { vendorNPMSpecifiers } from './npm_dependencies.js' |
23 | 29 | import { ensureLatestTypes } from './types.js' |
24 | 30 | import { nonNullable } from './utils/non_nullable.js' |
25 | | -import { BundleError } from './bundle_error.js' |
| 31 | +import { getPathInHome } from './home_path.js' |
26 | 32 |
|
27 | 33 | export interface BundleOptions { |
28 | 34 | basePath?: string |
@@ -172,15 +178,11 @@ export const bundle = async ( |
172 | 178 | // The final file name of the bundles contains a SHA256 hash of the contents, |
173 | 179 | // which we can only compute now that the files have been generated. So let's |
174 | 180 | // rename the bundles to their permanent names. |
175 | | - const bundlePaths = await createFinalBundles(bundles, distDirectory, buildID) |
176 | | - const eszipPath = bundlePaths.find((path) => path.endsWith(eszipExtension)) |
| 181 | + await createFinalBundles(bundles, distDirectory, buildID) |
177 | 182 |
|
178 | 183 | const { internalFunctions: internalFunctionsWithConfig, userFunctions: userFunctionsWithConfig } = |
179 | 184 | await getFunctionConfigs({ |
180 | | - basePath, |
181 | 185 | deno, |
182 | | - eszipPath, |
183 | | - featureFlags, |
184 | 186 | importMap, |
185 | 187 | internalFunctions, |
186 | 188 | log: logger, |
@@ -224,81 +226,65 @@ export const bundle = async ( |
224 | 226 | } |
225 | 227 |
|
226 | 228 | interface GetFunctionConfigsOptions { |
227 | | - basePath: string |
228 | 229 | deno: DenoBridge |
229 | | - eszipPath?: string |
230 | | - featureFlags?: FeatureFlags |
231 | 230 | importMap: ImportMap |
232 | 231 | internalFunctions: EdgeFunction[] |
233 | 232 | log: Logger |
234 | 233 | userFunctions: EdgeFunction[] |
235 | 234 | } |
236 | 235 |
|
237 | 236 | const getFunctionConfigs = async ({ |
238 | | - basePath, |
239 | 237 | deno, |
240 | | - eszipPath, |
241 | | - featureFlags, |
242 | 238 | importMap, |
243 | 239 | log, |
244 | 240 | internalFunctions, |
245 | 241 | userFunctions, |
246 | 242 | }: GetFunctionConfigsOptions) => { |
247 | | - try { |
248 | | - const internalConfigPromises = internalFunctions.map( |
249 | | - async (func) => [func.name, await getFunctionConfig({ functionPath: func.path, importMap, deno, log })] as const, |
250 | | - ) |
251 | | - const userConfigPromises = userFunctions.map( |
252 | | - async (func) => [func.name, await getFunctionConfig({ functionPath: func.path, importMap, deno, log })] as const, |
253 | | - ) |
254 | | - |
255 | | - // Creating a hash of function names to configuration objects. |
256 | | - const internalFunctionsWithConfig = Object.fromEntries(await Promise.all(internalConfigPromises)) |
257 | | - const userFunctionsWithConfig = Object.fromEntries(await Promise.all(userConfigPromises)) |
258 | | - |
259 | | - return { |
260 | | - internalFunctions: internalFunctionsWithConfig, |
261 | | - userFunctions: userFunctionsWithConfig, |
262 | | - } |
263 | | - } catch (err) { |
264 | | - if (!(err instanceof Error && err.cause === 'IMPORT_ASSERT') || !eszipPath || !featureFlags?.edge_bundler_deno_v2) { |
265 | | - throw err |
266 | | - } |
| 243 | + const functions = [...internalFunctions, ...userFunctions] |
| 244 | + const results = await Promise.allSettled( |
| 245 | + functions.map(async (func) => { |
| 246 | + return [func.name, await getFunctionConfig({ functionPath: func.path, importMap, deno, log })] as const |
| 247 | + }), |
| 248 | + ) |
| 249 | + const legacyDeno = new DenoBridge({ |
| 250 | + cacheDirectory: getPathInHome('deno-cli-v1'), |
| 251 | + useGlobal: false, |
| 252 | + versionRange: LEGACY_DENO_VERSION_RANGE, |
| 253 | + }) |
267 | 254 |
|
268 | | - log.user( |
269 | | - 'WARNING: Import assertions are deprecated and will be removed soon. Refer to https://ntl.fyi/import-assert for more information.', |
270 | | - ) |
| 255 | + for (let i = 0; i < results.length; i++) { |
| 256 | + const result = results[i] |
| 257 | + const func = functions[i] |
| 258 | + |
| 259 | + // We offer support for some features of Deno 1.x that have been removed |
| 260 | + // from 2.x, such as import assertions and the `window` global. When we |
| 261 | + // see that we failed to extract a config due to those edge cases, re-run |
| 262 | + // the script with Deno 1.x so we can extract the config. |
| 263 | + if ( |
| 264 | + result.status === 'rejected' && |
| 265 | + result.reason instanceof Error && |
| 266 | + (result.reason.cause === 'IMPORT_ASSERT' || result.reason.cause === 'WINDOW_GLOBAL') |
| 267 | + ) { |
| 268 | + try { |
| 269 | + const fallbackConfig = await getFunctionConfig({ functionPath: func.path, importMap, deno: legacyDeno, log }) |
271 | 270 |
|
272 | | - try { |
273 | | - // We failed to extract the configuration because there is an import assert |
274 | | - // in the function code, a deprecated feature that we used to support with |
275 | | - // Deno 1.x. To avoid a breaking change, we treat this error as a special |
276 | | - // case, using the generated ESZIP to extract the configuration. This works |
277 | | - // because import asserts are transpiled to import attributes. |
278 | | - const extractedESZIP = await extractESZIP(deno, eszipPath) |
279 | | - const configs = await Promise.all( |
280 | | - [...internalFunctions, ...userFunctions].map(async (func) => { |
281 | | - const relativePath = relative(basePath, func.path) |
282 | | - const functionPath = join(extractedESZIP.path, relativePath) |
| 271 | + results[i] = { status: 'fulfilled', value: [func.name, fallbackConfig] } |
| 272 | + } catch { |
| 273 | + throw result.reason |
| 274 | + } |
| 275 | + } |
| 276 | + } |
283 | 277 |
|
284 | | - return [func.name, await getFunctionConfig({ functionPath, importMap, deno, log })] as const |
285 | | - }), |
286 | | - ) |
| 278 | + const failure = results.find((result) => result.status === 'rejected') |
| 279 | + if (failure) { |
| 280 | + throw failure.reason |
| 281 | + } |
287 | 282 |
|
288 | | - await extractedESZIP.cleanup() |
| 283 | + const configs = results.map((config) => (config as PromiseFulfilledResult<[string, FunctionConfig]>).value) |
289 | 284 |
|
290 | | - return { |
291 | | - internalFunctions: Object.fromEntries(configs.slice(0, internalFunctions.length)), |
292 | | - userFunctions: Object.fromEntries(configs.slice(internalFunctions.length)), |
293 | | - } |
294 | | - } catch (err) { |
295 | | - throw new BundleError( |
296 | | - new Error( |
297 | | - 'An error occurred while building an edge function that uses an import assertion. Refer to https://ntl.fyi/import-assert for more information.', |
298 | | - ), |
299 | | - { cause: err }, |
300 | | - ) |
301 | | - } |
| 285 | + return { |
| 286 | + internalFunctions: Object.fromEntries(configs.slice(0, internalFunctions.length)), |
| 287 | + userFunctions: Object.fromEntries(configs.slice(internalFunctions.length)), |
302 | 288 | } |
303 | 289 | } |
304 | 290 |
|
|
0 commit comments