UNPKG

@parcel/core

Version:
187 lines (168 loc) • 5.04 kB
// @flow strict-local import type {ContentKey} from '@parcel/graph'; import type {Async} from '@parcel/types'; import type {StaticRunOpts} from '../RequestTracker'; import type { AssetRequestInput, AssetRequestResult, DevDepRequest, TransformationRequest, } from '../types'; import type {ConfigAndCachePath} from './ParcelConfigRequest'; import type {TransformationResult} from '../Transformation'; import nullthrows from 'nullthrows'; import ThrowableDiagnostic from '@parcel/diagnostic'; import {hashString} from '@parcel/hash'; import createParcelConfigRequest from './ParcelConfigRequest'; import {runDevDepRequest} from './DevDepRequest'; import {runConfigRequest} from './ConfigRequest'; import {fromProjectPath, fromProjectPathRelative} from '../projectPath'; import {report} from '../ReporterRunner'; type RunInput<TResult> = {| input: AssetRequestInput, ...StaticRunOpts<TResult>, |}; export type AssetRequest = {| id: ContentKey, +type: 'asset_request', run: (RunInput<AssetRequestResult>) => Async<AssetRequestResult>, input: AssetRequestInput, |}; export default function createAssetRequest( input: AssetRequestInput, ): AssetRequest { return { type: 'asset_request', id: getId(input), run, input, }; } const type = 'asset_request'; function getId(input: AssetRequestInput) { // eslint-disable-next-line no-unused-vars let {optionsRef, ...hashInput} = input; return hashString( type + fromProjectPathRelative(input.filePath) + input.env.id + String(input.isSource) + String(input.sideEffects) + (input.code ?? '') + ':' + (input.pipeline ?? '') + ':' + (input.query ?? ''), ); } async function run({input, api, farm, invalidateReason, options}) { report({ type: 'buildProgress', phase: 'transforming', filePath: fromProjectPath(options.projectRoot, input.filePath), }); api.invalidateOnFileUpdate(input.filePath); let start = Date.now(); let {optionsRef, ...rest} = input; let {cachePath} = nullthrows( await api.runRequest<null, ConfigAndCachePath>(createParcelConfigRequest()), ); let previousDevDepRequests = new Map( await Promise.all( api .getSubRequests() .filter(req => req.type === 'dev_dep_request') .map(async req => [ req.id, nullthrows(await api.getRequestResult<DevDepRequest>(req.id)), ]), ), ); let request: TransformationRequest = { ...rest, invalidateReason, // Add invalidations to the request if a node already exists in the graph. // These are used to compute the cache key for assets during transformation. invalidations: api.getInvalidations().filter(invalidation => { // Filter out invalidation node for the input file itself. return ( invalidation.type !== 'file' || invalidation.filePath !== input.filePath ); }), devDeps: new Map( [...previousDevDepRequests.entries()] .filter(([id]) => api.canSkipSubrequest(id)) .map(([, req]) => [ `${req.specifier}:${fromProjectPathRelative(req.resolveFrom)}`, req.hash, ]), ), invalidDevDeps: await Promise.all( [...previousDevDepRequests.entries()] .filter(([id]) => !api.canSkipSubrequest(id)) .flatMap(([, req]) => { return [ { specifier: req.specifier, resolveFrom: req.resolveFrom, }, ...(req.additionalInvalidations ?? []).map(i => ({ specifier: i.specifier, resolveFrom: i.resolveFrom, })), ]; }), ), }; let { assets, configRequests, error, invalidations, invalidateOnFileCreate, devDepRequests, } = (await farm.createHandle( 'runTransform', input.isSingleChangeRebuild, )({ configCachePath: cachePath, optionsRef, request, }): TransformationResult); let time = Date.now() - start; if (assets) { for (let asset of assets) { asset.stats.time = time; } } for (let invalidation of invalidateOnFileCreate) { api.invalidateOnFileCreate(invalidation); } for (let invalidation of invalidations) { switch (invalidation.type) { case 'file': api.invalidateOnFileUpdate(invalidation.filePath); api.invalidateOnFileDelete(invalidation.filePath); break; case 'env': api.invalidateOnEnvChange(invalidation.key); break; case 'option': api.invalidateOnOptionChange(invalidation.key); break; default: throw new Error(`Unknown invalidation type: ${invalidation.type}`); } } for (let devDepRequest of devDepRequests) { await runDevDepRequest(api, devDepRequest); } for (let configRequest of configRequests) { await runConfigRequest(api, configRequest); } if (error != null) { throw new ThrowableDiagnostic({diagnostic: error}); } else { return nullthrows(assets); } }