/
githubmirror
/
angular-cli
Обзор
Документация
Войти
/
githubmirror
/
angular-cli
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts
261 строка
9 KB
Charles Lyding
refactor(@angular/build): pre-warm transformer worker pool and pass static options via workerData
20 часов назад
20 часов назад
cabe95c
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import remapping, { type DecodedSourceMap, type EncodedSourceMap } from '@ampproject/remapping'; import { type PluginItem, transformAsync } from '@babel/core'; import { createRequire } from 'node:module'; import { workerData } from 'node:worker_threads'; import Piscina from 'piscina'; import { useBabelLinker } from '../../utils/environment-options.js'; import { findTrailingSourceMapComment, isTrailingSourceMapComment, loadInputSourceMap, loadInputSourceMapFromUrl, removeSourceMappingURL, } from '../../utils/source-map'; import { linkWithOxc } from '../angular/linker/oxc-linker.js'; import { transform as transformWithOxc } from '../oxc/oxc-transform.js'; import type { JavaScriptTransformerOptions } from './javascript-transformer'; interface JavaScriptTransformRequest { filename: string; data: string | Uint8Array; skipLinker?: boolean; sideEffects?: boolean; instrumentForCoverage?: boolean; } interface TransformOptions extends Omit<JavaScriptTransformRequest, 'filename' | 'data'> { inputSourceMap?: EncodedSourceMap; isAlreadyStripped?: boolean; } const { sourcemap = false, thirdPartySourcemaps = false, advancedOptimizations = false, jit = false, } = (workerData || {}) as Partial<JavaScriptTransformerOptions>; const textDecoder = new TextDecoder(); const textEncoder = new TextEncoder(); async function instrumentCoverage( filename: string, data: string, useInputSourcemap: boolean, ): Promise<{ code: string; map?: EncodedSourceMap }> { try { let resolvedPath = 'istanbul-lib-instrument'; try { const requireFn = createRequire(filename); resolvedPath = requireFn.resolve('istanbul-lib-instrument'); } catch { // Fallback to pool worker import traversal } const { createInstrumenter } = (await import( resolvedPath )) as typeof import('istanbul-lib-instrument'); const instrumenter = createInstrumenter({ produceSourceMap: useInputSourcemap, esModules: true, }); const inputSourceMap = useInputSourcemap ? loadInputSourceMap(filename, data) : undefined; const instrumentedCode = instrumenter.instrumentSync( data, filename, inputSourceMap as Parameters<typeof instrumenter.instrumentSync>[2], ); const lastMap = useInputSourcemap ? (instrumenter.lastSourceMap() as EncodedSourceMap) : undefined; return { code: instrumentedCode, map: lastMap ?? undefined, }; } catch (error) { throw new Error( `The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.`, { cause: error }, ); } } export default async function transformJavaScript( request: JavaScriptTransformRequest, ): Promise<unknown> { const { filename, data, ...options } = request; const useInputSourcemap = sourcemap && (!!thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename)); let textData: string; let inputSourceMap: EncodedSourceMap | undefined; let isAlreadyStripped = false; if (typeof data !== 'string') { const trailing = findTrailingSourceMapComment(data); if (trailing === null) { // 0 comments: fast path, no sourcemap to load or strip textData = textDecoder.decode(data); isAlreadyStripped = true; } else if (trailing !== undefined) { if (useInputSourcemap) { inputSourceMap = loadInputSourceMapFromUrl(filename, trailing.urlLine); if (inputSourceMap !== undefined) { // Valid trailing sourcemap comment confirmed: safe to slice code buffer for transformation passes. // Note: If no passes modify the code, the untouched original `data` buffer is returned below. textData = textDecoder.decode(trailing.code); isAlreadyStripped = true; } else { // Not a valid trailing sourcemap (e.g. inside template literal): fallback to full decode textData = textDecoder.decode(data); } } else if (isTrailingSourceMapComment(trailing.urlLine)) { // Valid trailing sourcemap comment confirmed: safe to slice code buffer textData = textDecoder.decode(trailing.code); isAlreadyStripped = true; } else { // Fallback to full decode and state-machine stripping textData = textDecoder.decode(data); } } else { // Multiple comments or comment not at line start: fall back to full decode and string parser textData = textDecoder.decode(data); } } else { textData = data; } const transformedData = await transformJavaScriptImpl(filename, textData, { ...options, inputSourceMap, isAlreadyStripped, }); // If no transformations modified the code, return the original untouched data buffer via `move`. // This preserves any original trailing sourcemap comment and avoids re-encoding. if (transformedData === textData && typeof data !== 'string') { return Piscina.move(data); } return Piscina.move(textEncoder.encode(transformedData)); } async function transformJavaScriptImpl( filename: string, data: string, options: TransformOptions, ): Promise<string> { const shouldLink = !options.skipLinker; const useInputSourcemap = sourcemap && (!!thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename)); let code = data; const maps: (DecodedSourceMap | EncodedSourceMap)[] = []; let coverageMap: EncodedSourceMap | undefined; if (options.instrumentForCoverage) { const result = await instrumentCoverage(filename, code, useInputSourcemap); code = result.code; coverageMap = result.map; } if (shouldLink) { if (useBabelLinker) { const { createEs2015LinkerPlugin } = await import('@angular/compiler-cli/linker/babel'); const { ConsoleLogger, LogLevel } = await import('@angular/compiler-cli'); const result = await transformAsync(code, { filename, inputSourceMap: false, sourceMaps: !!useInputSourcemap, compact: false, configFile: false, babelrc: false, browserslistConfigFile: false, plugins: [ createEs2015LinkerPlugin({ fileSystem: { exists: () => false, readFile: () => '', resolve: (...paths: string[]) => paths.join('/'), dirname: (path: string) => path.split('/').slice(0, -1).join('/'), relative: (_from: string, to: string) => to, } as never, logger: new ConsoleLogger(LogLevel.info), linkerJitMode: jit, // This is a workaround until https://github.com/angular/angular/issues/42769 is fixed. sourceMapping: false, }) as PluginItem, ], }); code = result?.code ?? code; if (result?.map) { maps.push(result.map as EncodedSourceMap); } } else { const result = linkWithOxc(filename, code, { sourcemap: useInputSourcemap, jit, skipCheck: true, }); code = result.code; if (result.map) { maps.push(result.map); } } } // Run advanced optimizations using our fast oxc-transform if (advancedOptimizations) { const sideEffectFree = options.sideEffects === false; const safeAngularPackage = sideEffectFree && /[\\/]node_modules[\\/]@angular[\\/]/.test(filename); const topLevelSafeMode = !safeAngularPackage; const result = transformWithOxc(filename, code, { sourcemap: useInputSourcemap, sideEffects: options.sideEffects, topLevelSafeMode, }); code = result.code; if (result.map) { maps.push(result.map); } } if (useInputSourcemap) { const baseMap = coverageMap ?? options.inputSourceMap ?? loadInputSourceMap(filename, data); if (maps.length > 0 || coverageMap) { if (!options.isAlreadyStripped) { code = removeSourceMappingURL(code); } const remappingChain: (DecodedSourceMap | EncodedSourceMap)[] = maps.reverse(); if (baseMap) { remappingChain.push(baseMap); } if (remappingChain.length > 0) { const finalMap = remapping(remappingChain, () => null).toString(); const base64Map = Buffer.from(finalMap).toString('base64'); code += `\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,${base64Map}`; } } return code; } // Strip sourcemaps if they should not be used return options.isAlreadyStripped ? code : removeSourceMappingURL(code); }