Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrapped islands #9941

Draft
wants to merge 30 commits into
base: v2
Choose a base branch
from
Draft
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Different API for config loading
mischnic committed Aug 5, 2022
commit d0a812a96c28e72a04a5c1a205626d975ae0de47
166 changes: 122 additions & 44 deletions packages/core/core/src/PackagerRunner.js
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ import {
loadPluginConfig,
getConfigHash,
getConfigRequests,
type PluginWithLoadConfig,
type PluginWithLoadConfigGlobalInfo,
} from './requests/ConfigRequest';
import {
createDevDependency,
@@ -143,12 +143,20 @@ export default class PackagerRunner {
): Promise<PackageRequestResult> {
invalidateDevDeps(invalidDevDeps, this.options, this.config);

let configs = await this.loadConfigs(bundleGraph, bundle);
let {configs, globalInfos} = await this.loadConfigs(bundleGraph, bundle);
let bundleInfo =
(await this.getBundleInfoFromCache(bundleGraph, bundle, configs)) ??
(await this.getBundleInfo(bundle, bundleGraph, configs));

let configRequests = getConfigRequests([...configs.values()]);
(await this.getBundleInfoFromCache(
bundleGraph,
bundle,
configs,
globalInfos,
)) ??
(await this.getBundleInfo(bundle, bundleGraph, configs, globalInfos));

let configRequests = getConfigRequests([
...configs.values(),
...globalInfos.values(),
]);
let devDepRequests = getWorkerDevDepRequests([
...this.devDepRequests.values(),
]);
@@ -164,77 +172,116 @@ export default class PackagerRunner {
async loadConfigs(
bundleGraph: InternalBundleGraph,
bundle: InternalBundle,
): Promise<Map<string, Config>> {
): Promise<{|
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
|}> {
let configs = new Map();
let globalInfos = new Map();

await this.loadConfig(bundle, configs);
await this.loadConfig(bundleGraph, bundle, configs, globalInfos);
for (let inlineBundle of bundleGraph.getInlineBundles(bundle)) {
await this.loadConfig(inlineBundle, configs);
await this.loadConfig(bundleGraph, inlineBundle, configs, globalInfos);
}

return configs;
return {configs, globalInfos};
}

async loadConfig(
bundleGraph: InternalBundleGraph,
bundle: InternalBundle,
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
): Promise<void> {
let name = nullthrows(bundle.name);
let plugin = await this.config.getPackager(name);
await this.loadPluginConfig(plugin, configs);
await this.loadPluginConfig(
bundleGraph,
bundle,
plugin,
configs,
globalInfos,
);

let optimizers = await this.config.getOptimizers(name, bundle.pipeline);

for (let optimizer of optimizers) {
await this.loadPluginConfig(optimizer, configs);
await this.loadPluginConfig(
bundleGraph,
bundle,
optimizer,
configs,
globalInfos,
);
}
}

async loadPluginConfig<T: PluginWithLoadConfig>(
async loadPluginConfig<T: PluginWithLoadConfigGlobalInfo>(
bundleGraph: InternalBundleGraph,
bundle: InternalBundle,
plugin: LoadedPlugin<T>,
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
): Promise<void> {
if (configs.has(plugin.name)) {
return;
}
if (!configs.has(plugin.name)) {
// Only load config for a plugin once per build.
let existing = pluginConfigs.get(plugin.name);
if (existing != null) {
configs.set(plugin.name, existing);
} else {
if (plugin.plugin.loadConfig != null) {
let config = createConfig({
plugin: plugin.name,
searchPath: toProjectPathUnsafe('index'),
});

await loadPluginConfig(plugin, config, this.options);

for (let devDep of config.devDeps) {
let devDepRequest = await createDevDependency(
devDep,
this.previousDevDeps,
this.options,
);
let key = `${devDep.specifier}:${fromProjectPath(
this.options.projectRoot,
devDep.resolveFrom,
)}`;
this.devDepRequests.set(key, devDepRequest);
}

// Only load config for a plugin once per build.
let existing = pluginConfigs.get(plugin.name);
if (existing != null) {
configs.set(plugin.name, existing);
return;
pluginConfigs.set(plugin.name, config);
configs.set(plugin.name, config);
}
}
}

if (plugin.plugin.loadConfig != null) {
let loadGlobalInfo = plugin.plugin.loadGlobalInfo;
if (!globalInfos.has(plugin.name) && loadGlobalInfo != null) {
let config = createConfig({
plugin: plugin.name,
searchPath: toProjectPathUnsafe('index'),
});

await loadPluginConfig(plugin, config, this.options);

for (let devDep of config.devDeps) {
let devDepRequest = await createDevDependency(
devDep,
this.previousDevDeps,
config.result = await loadGlobalInfo({
bundle: NamedBundle.get(bundle, bundleGraph, this.options),
bundleGraph: new BundleGraph<NamedBundleType>(
bundleGraph,
NamedBundle.get.bind(NamedBundle),
this.options,
);
let key = `${devDep.specifier}:${fromProjectPath(
this.options.projectRoot,
devDep.resolveFrom,
)}`;
this.devDepRequests.set(key, devDepRequest);
}

pluginConfigs.set(plugin.name, config);
configs.set(plugin.name, config);
),
options: new PluginOptions(this.options),
logger: new PluginLogger({origin: plugin.name}),
});
// TODO expose config and let plugin set key?
config.cacheKey = hashString(JSON.stringify(config.result) ?? '');
globalInfos.set(plugin.name, config);
}
}

async getBundleInfoFromCache(
bundleGraph: InternalBundleGraph,
bundle: InternalBundle,
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
): Async<?BundleInfo> {
if (this.options.shouldDisableCache) {
return;
@@ -248,6 +295,7 @@ export default class PackagerRunner {
bundle,
bundleGraph,
configs,
globalInfos,
this.previousInvalidations,
);
let infoKey = PackagerRunner.getInfoKey(cacheKey);
@@ -258,17 +306,23 @@ export default class PackagerRunner {
bundle: InternalBundle,
bundleGraph: InternalBundleGraph,
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
): Promise<BundleInfo> {
let {type, contents, map} = await this.getBundleResult(
bundle,
bundleGraph,
configs,
globalInfos,
);

// Recompute cache keys as they may have changed due to dev dependencies.
let cacheKey = await this.getCacheKey(bundle, bundleGraph, configs, [
...this.invalidations.values(),
]);
let cacheKey = await this.getCacheKey(
bundle,
bundleGraph,
configs,
globalInfos,
[...this.invalidations.values()],
);
let cacheKeys = {
content: PackagerRunner.getContentKey(cacheKey),
map: PackagerRunner.getMapKey(cacheKey),
@@ -282,12 +336,18 @@ export default class PackagerRunner {
bundle: InternalBundle,
bundleGraph: InternalBundleGraph,
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
): Promise<{|
type: string,
contents: Blob,
map: ?string,
|}> {
let packaged = await this.package(bundle, bundleGraph, configs);
let packaged = await this.package(
bundle,
bundleGraph,
configs,
globalInfos,
);
let type = packaged.type ?? bundle.type;
let res = await this.optimize(
bundle,
@@ -296,6 +356,7 @@ export default class PackagerRunner {
packaged.contents,
packaged.map,
configs,
globalInfos,
);

let map =
@@ -323,6 +384,7 @@ export default class PackagerRunner {
internalBundle: InternalBundle,
bundleGraph: InternalBundleGraph,
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
): Promise<BundleResult> {
let bundle = NamedBundle.get(internalBundle, bundleGraph, this.options);
this.report({
@@ -336,6 +398,7 @@ export default class PackagerRunner {
try {
return await plugin.package({
config: configs.get(name)?.result,
globalInfo: globalInfos.get(name)?.result,
bundle,
bundleGraph: new BundleGraph<NamedBundleType>(
bundleGraph,
@@ -362,6 +425,7 @@ export default class PackagerRunner {
// $FlowFixMe
bundleGraphToInternalBundleGraph(bundleGraph),
configs,
globalInfos,
);

return {contents: res.contents};
@@ -399,6 +463,7 @@ export default class PackagerRunner {
contents: Blob,
map?: ?SourceMap,
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
): Promise<BundleResult> {
let bundle = NamedBundle.get(
internalBundle,
@@ -434,6 +499,7 @@ export default class PackagerRunner {
try {
let next = await optimizer.plugin.optimize({
config: configs.get(optimizer.name)?.result,
globalInfo: globalInfos.get(optimizer.name)?.result,
bundle,
bundleGraph,
contents: optimized.contents,
@@ -539,6 +605,7 @@ export default class PackagerRunner {
bundle: InternalBundle,
bundleGraph: InternalBundleGraph,
configs: Map<string, Config>,
globalInfos: Map<string, Config>,
invalidations: Array<RequestInvalidation>,
): Promise<string> {
let configResults = {};
@@ -551,6 +618,16 @@ export default class PackagerRunner {
);
}
}
let globalInfoResults = {};
for (let [pluginName, config] of globalInfos) {
if (config) {
globalInfoResults[pluginName] = await getConfigHash(
config,
pluginName,
this.options,
);
}
}

let devDepHashes = await this.getDevDepHashes(bundle);
for (let inlineBundle of bundleGraph.getInlineBundles(bundle)) {
@@ -569,6 +646,7 @@ export default class PackagerRunner {
bundle.target.publicUrl +
bundleGraph.getHash(bundle) +
JSON.stringify(configResults) +
JSON.stringify(globalInfoResults) +
this.options.mode,
);
}
8 changes: 4 additions & 4 deletions packages/core/core/src/ParcelConfig.js
Original file line number Diff line number Diff line change
@@ -253,7 +253,7 @@ export default class ParcelConfig {

async getPackager(
filePath: FilePath,
): Promise<LoadedPlugin<Packager<mixed>>> {
): Promise<LoadedPlugin<Packager<mixed, mixed>>> {
let packager = this.matchGlobMap(
toProjectPathUnsafe(filePath),
this.packagers,
@@ -265,7 +265,7 @@ export default class ParcelConfig {
'/packagers',
);
}
return this.loadPlugin<Packager<mixed>>(packager);
return this.loadPlugin<Packager<mixed, mixed>>(packager);
}

_getOptimizerNodes(
@@ -298,13 +298,13 @@ export default class ParcelConfig {
getOptimizers(
filePath: FilePath,
pipeline: ?string,
): Promise<Array<LoadedPlugin<Optimizer<mixed>>>> {
): Promise<Array<LoadedPlugin<Optimizer<mixed, mixed>>>> {
let optimizers = this._getOptimizerNodes(filePath, pipeline);
if (optimizers.length === 0) {
return Promise.resolve([]);
}

return this.loadPlugins<Optimizer<mixed>>(optimizers);
return this.loadPlugins<Optimizer<mixed, mixed>>(optimizers);
}

async getCompressors(
17 changes: 17 additions & 0 deletions packages/core/core/src/requests/ConfigRequest.js
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@ import type {
Config as IConfig,
PluginOptions as IPluginOptions,
PluginLogger as IPluginLogger,
NamedBundle as INamedBundle,
BundleGraph as IBundleGraph,
} from '@parcel/types';
import type {
Config,
@@ -32,6 +34,21 @@ export type PluginWithLoadConfig = {
...
};

export type PluginWithLoadConfigGlobalInfo = {
loadConfig?: ({|
config: IConfig,
options: IPluginOptions,
logger: IPluginLogger,
|}) => Async<mixed>,
loadGlobalInfo?: ({|
bundle: INamedBundle,
bundleGraph: IBundleGraph<INamedBundle>,
options: IPluginOptions,
logger: IPluginLogger,
|}) => Async<mixed>,
...
};

export type ConfigRequest = {
id: string,
invalidateOnFileChange: Set<ProjectPath>,
5 changes: 4 additions & 1 deletion packages/core/core/src/requests/WriteBundlesRequest.js
Original file line number Diff line number Diff line change
@@ -92,7 +92,10 @@ async function run({input, api, farm, options}: RunInput) {
bundleGraphReference: ref,
optionsRef,
});
let info = await api.runRequest(request);

// TODO remove?
// force to ensure that loadConfig runs for all packagers/optimizers
let info = await api.runRequest(request, {force: true});

bundleInfoMap[bundle.id] = info;
if (!info.hashReferences.length) {
4 changes: 2 additions & 2 deletions packages/core/plugin/src/PluginAPI.js
Original file line number Diff line number Diff line change
@@ -58,14 +58,14 @@ export class Validator {
}

export class Packager {
constructor<T>(opts: PackagerOpts<T>) {
constructor<T, U>(opts: PackagerOpts<T, U>) {
// $FlowFixMe
this[CONFIG] = opts;
}
}

export class Optimizer {
constructor<T>(opts: OptimizerOpts<T>) {
constructor<T, U>(opts: OptimizerOpts<T, U>) {
// $FlowFixMe
this[CONFIG] = opts;
}
22 changes: 18 additions & 4 deletions packages/core/types/index.js
Original file line number Diff line number Diff line change
@@ -1592,18 +1592,25 @@ export type Runtime<ConfigType> = {|
/**
* @section packager
*/
export type Packager<ConfigType> = {|
export type Packager<ConfigType, GlobalInfoType> = {|
loadConfig?: ({|
config: Config,
options: PluginOptions,
logger: PluginLogger,
|}) => Promise<ConfigType> | ConfigType,
|}) => Async<ConfigType>,
loadGlobalInfo?: ({|
bundle: NamedBundle,
bundleGraph: BundleGraph<NamedBundle>,
options: PluginOptions,
logger: PluginLogger,
|}) => Async<GlobalInfoType>,
package({|
bundle: NamedBundle,
bundleGraph: BundleGraph<NamedBundle>,
options: PluginOptions,
logger: PluginLogger,
config: ConfigType,
globalInfo: GlobalInfoType,
getInlineBundleContents: (
Bundle,
BundleGraph<NamedBundle>,
@@ -1615,12 +1622,18 @@ export type Packager<ConfigType> = {|
/**
* @section optimizer
*/
export type Optimizer<ConfigType> = {|
export type Optimizer<ConfigType, GlobalInfoType> = {|
loadConfig?: ({|
config: Config,
options: PluginOptions,
logger: PluginLogger,
|}) => Promise<ConfigType> | ConfigType,
|}) => Async<ConfigType>,
loadGlobalInfo?: ({|
bundle: NamedBundle,
bundleGraph: BundleGraph<NamedBundle>,
options: PluginOptions,
logger: PluginLogger,
|}) => Async<GlobalInfoType>,
optimize({|
bundle: NamedBundle,
bundleGraph: BundleGraph<NamedBundle>,
@@ -1629,6 +1642,7 @@ export type Optimizer<ConfigType> = {|
options: PluginOptions,
logger: PluginLogger,
config: ConfigType,
globalInfo: GlobalInfoType,
getSourceMapReference: (map: ?SourceMap) => Async<?string>,
|}): Async<BundleResult>,
|};
12 changes: 4 additions & 8 deletions packages/packagers/js/src/ScopeHoistingPackager.js
Original file line number Diff line number Diff line change
@@ -96,12 +96,14 @@ export class ScopeHoistingPackager {
islandRoot: Map<Asset, Asset> = new Map();
unwrappedAssets: Set<Asset> = new Set();
usedByUnwrappedEntries: Set<Asset> = new Set();
duplicatedAssets: Set<string>;

constructor(
options: PluginOptions,
bundleGraph: BundleGraph<NamedBundle>,
bundle: NamedBundle,
parcelRequireName: string,
duplicatedAssets: Array<string>,
) {
this.options = options;
this.bundleGraph = bundleGraph;
@@ -117,6 +119,7 @@ export class ScopeHoistingPackager {
this.bundle.bundleBehavior !== 'isolated';

this.globalNames = GLOBALS_BY_CONTEXT[bundle.env.context];
this.duplicatedAssets = new Set(duplicatedAssets);
}

async package(): Promise<{|contents: string, map: ?SourceMap|}> {
@@ -303,14 +306,7 @@ export class ScopeHoistingPackager {
.getIncomingDependencies(asset)
.some(dep => dep.meta.shouldWrap && dep.specifierType !== 'url') ||
// has to be dedupliated at runtime
// TODO invalidation
this.bundleGraph.getBundlesWithAsset(asset).some(
// TODO object equality checks fail for bundle and target objects
// TODO how to handle targets?
b =>
b.id !== this.bundle.id &&
b.target.name === this.bundle.target.name,
)
this.duplicatedAssets.has(asset.id)
) {
wrapped.add(asset);
this.unwrappedAssets.delete(asset);
28 changes: 27 additions & 1 deletion packages/packagers/js/src/index.js
Original file line number Diff line number Diff line change
@@ -22,12 +22,37 @@ export default (new Packager({
parcelRequireName: 'parcelRequire' + hashString(name).slice(-4),
};
},

loadGlobalInfo({bundle, bundleGraph}) {
let duplicatedAssets = [];
if (bundle.env.shouldScopeHoist) {
bundle.traverseAssets(asset => {
if (
bundleGraph.getBundlesWithAsset(asset).some(
// TODO object equality checks fail for bundle and target objects
// TODO how to handle targets?
b => b.id !== bundle.id && b.target.name === bundle.target.name,
)
) {
duplicatedAssets.push(asset.id);
}
});
}

// config.setCacheKey(hashString(JSON.stringify(duplicatedAssets)));

return {
duplicatedAssets,
};
},

async package({
bundle,
bundleGraph,
getInlineBundleContents,
getSourceMapReference,
config,
globalInfo,
options,
}) {
// If this is a non-module script, and there is only one asset with no dependencies,
@@ -50,7 +75,8 @@ export default (new Packager({
options,
bundleGraph,
bundle,
nullthrows(config).parcelRequireName,
config.parcelRequireName,
globalInfo.duplicatedAssets,
)
: new DevPackager(
options,