Skip to content

Commit

Permalink
Directly resolve and load implicit dependants
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed May 31, 2020
1 parent 498f54d commit 6ca9118
Show file tree
Hide file tree
Showing 29 changed files with 186 additions and 72 deletions.
15 changes: 3 additions & 12 deletions src/Graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
} from './rollup/types';
import { BuildPhase } from './utils/buildPhase';
import { getChunkAssignments } from './utils/chunkAssignment';
import { error } from './utils/error';
import { analyseModuleExecution, sortByExecutionOrder } from './utils/executionOrder';
import { PluginDriver } from './utils/PluginDriver';
import relativeId from './utils/relativeId';
Expand Down Expand Up @@ -198,7 +197,9 @@ export default class Graph {
hasModuleSideEffects: foundModule.moduleSideEffects,
id: foundModule.id,
implicitlyLoadedAfterOneOf:
foundModule instanceof Module ? [...foundModule.implicitlyLoadedAfterIds] : [],
foundModule instanceof Module
? Array.from(foundModule.implicitlyLoadedAfter, module => module.id)
: [],
implicitlyLoadedBefore:
foundModule instanceof Module
? Array.from(foundModule.implicitlyLoadedBefore, module => module.id)
Expand Down Expand Up @@ -227,16 +228,6 @@ export default class Graph {
for (const module of this.modulesById.values()) {
if (module instanceof Module) {
this.modules.push(module);
for (const dependantId of module.implicitlyLoadedAfterIds) {
const dependant = this.modulesById.get(dependantId) as Module;
if (!dependant) {
return error({
message: `A plugin has specified that "${dependantId}" is an implicit dependant of "${module.id}" but the dependant does not correspond to a known module id.`
});
}
module.implicitlyLoadedAfter.push(dependant);
dependant.implicitlyLoadedBefore.add(module);
}
} else {
this.externalModules.push(module);
}
Expand Down
1 change: 0 additions & 1 deletion src/Module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ export default class Module {
exportsAll: { [name: string]: string } = Object.create(null);
facadeChunk: Chunk | null = null;
implicitlyLoadedAfter: Module[] = [];
implicitlyLoadedAfterIds = new Set<string>();
implicitlyLoadedBefore = new Set<Module>();
importDescriptions: { [name: string]: ImportDescription } = Object.create(null);
importers: string[] = [];
Expand Down
143 changes: 104 additions & 39 deletions src/ModuleLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ExternalModule from './ExternalModule';
import Graph from './Graph';
import Module from './Module';
import {
EmittedChunk,
GetManualChunk,
HasModuleSideEffects,
NormalizedInputOptions,
Expand All @@ -15,10 +16,12 @@ import {
errCannotAssignModuleToChunk,
errEntryCannotBeExternal,
errExternalSyntheticExports,
errImplicitDependantCannotBeExternal,
errInternalIdCannotBeExternal,
errNamespaceConflict,
error,
errUnresolvedEntry,
errUnresolvedImplicitDependant,
errUnresolvedImport,
errUnresolvedImportTreatedAsExternal
} from './utils/error';
Expand All @@ -33,7 +36,6 @@ import transform from './utils/transform';
export interface UnresolvedModule {
fileName: string | null;
id: string;
implicitlyLoadedAfter: string[];
importer: string | undefined;
name: string | null;
}
Expand All @@ -56,7 +58,6 @@ export class ModuleLoader {
: () => true;
}

// TODO Lukas do not use this for non-entry chunks but rather just extract file name logic
async addEntryModules(
unresolvedEntryModules: UnresolvedModule[],
isUserDefined: boolean
Expand All @@ -69,41 +70,23 @@ export class ModuleLoader {
this.nextEntryModuleIndex += unresolvedEntryModules.length;
const loadNewEntryModulesPromise = Promise.all(
unresolvedEntryModules.map(
({ id, importer, implicitlyLoadedAfter }): Promise<Module> =>
this.loadEntryModule(id, implicitlyLoadedAfter.length === 0, importer)
({ id, importer }): Promise<Module> => this.loadEntryModule(id, true, importer, null)
)
).then(entryModules => {
let moduleIndex = firstEntryModuleIndex;
for (let index = 0; index < entryModules.length; index++) {
const { fileName, implicitlyLoadedAfter, name } = unresolvedEntryModules[index];
const entryModule = entryModules[index];
if (fileName !== null) {
entryModule.chunkFileNames.add(fileName);
} else if (name !== null) {
if (entryModule.chunkName === null) {
entryModule.chunkName = name;
}
if (isUserDefined) {
entryModule.userChunkNames.add(name);
}
}
if (implicitlyLoadedAfter.length > 0) {
for (const dependant of implicitlyLoadedAfter) {
entryModule.implicitlyLoadedAfterIds.add(dependant);
}
entryModule.isUserDefinedEntryPoint = entryModule.isUserDefinedEntryPoint || isUserDefined;
addChunkNamesToModule(entryModule, unresolvedEntryModules[index], isUserDefined);
const existingIndexedModule = this.indexedEntryModules.find(
indexedModule => indexedModule.module === entryModule
);
if (!existingIndexedModule) {
this.indexedEntryModules.push({ module: entryModule, index: moduleIndex });
} else {
entryModule.isUserDefinedEntryPoint =
entryModule.isUserDefinedEntryPoint || isUserDefined;
const existingIndexedModule = this.indexedEntryModules.find(
indexedModule => indexedModule.module === entryModule
);
if (!existingIndexedModule) {
this.indexedEntryModules.push({ module: entryModule, index: moduleIndex });
} else {
existingIndexedModule.index = Math.min(existingIndexedModule.index, moduleIndex);
}
moduleIndex++;
existingIndexedModule.index = Math.min(existingIndexedModule.index, moduleIndex);
}
moduleIndex++;
}
this.indexedEntryModules.sort(({ index: indexA }, { index: indexB }) =>
indexA > indexB ? 1 : -1
Expand All @@ -128,7 +111,7 @@ export class ModuleLoader {
}
return this.awaitLoadModulesPromise(
Promise.all(
unresolvedManualChunks.map(({ id }) => this.loadEntryModule(id, false, undefined))
unresolvedManualChunks.map(({ id }) => this.loadEntryModule(id, false, undefined, null))
).then(manualChunkModules => {
for (let index = 0; index < manualChunkModules.length; index++) {
this.addModuleToManualChunk(
Expand All @@ -155,6 +138,29 @@ export class ModuleLoader {
}
}

async emitChunk({
fileName,
id,
importer,
name,
implicitlyLoadedAfterOneOf,
preserveSignature
}: EmittedChunk): Promise<Module> {
const unresolvedModule: UnresolvedModule = {
fileName: fileName || null,
id,
importer,
name: name || null
};
const module = implicitlyLoadedAfterOneOf
? await this.addEntryWithImplicitDependants(unresolvedModule, implicitlyLoadedAfterOneOf)
: (await this.addEntryModules([unresolvedModule], false)).newEntryModules[0];
if (preserveSignature != null) {
module.preserveSignature = preserveSignature;
}
return module;
}

async resolveId(
source: string,
importer: string | undefined,
Expand All @@ -170,6 +176,32 @@ export class ModuleLoader {
);
}

private addEntryWithImplicitDependants(
unresolvedModule: UnresolvedModule,
implicitlyLoadedAfter: string[]
): Promise<Module> {
// TODO Lukas do everything in parallel?
const loadModulePromise = this.loadEntryModule(
unresolvedModule.id,
false,
unresolvedModule.importer,
null
).then(async entryModule => {
addChunkNamesToModule(entryModule, unresolvedModule, false);
entryModule.implicitlyLoadedAfter = await Promise.all(
implicitlyLoadedAfter.map(id =>
this.loadEntryModule(id, false, unresolvedModule.importer, entryModule.id)
)
);
for (const dependant of entryModule.implicitlyLoadedAfter) {
dependant.implicitlyLoadedBefore.add(entryModule);
}
return entryModule;
});
this.extendLoadModulesPromise(loadModulePromise);
return loadModulePromise;
}

private async addModuleSource(id: string, importer: string | undefined, module: Module) {
timeStart('load modules', 3);
let source: string | SourceDescription;
Expand Down Expand Up @@ -225,23 +257,28 @@ export class ModuleLoader {
this.manualChunkModules[alias].push(module);
}

// TODO Lukas remove parameter but rather replace this with getCombinedPromise and use this together with extendLoadModulesPromise
private async awaitLoadModulesPromise<T>(loadNewModulesPromise: Promise<T>): Promise<T> {
this.latestLoadModulesPromise = Promise.all([
loadNewModulesPromise,
this.latestLoadModulesPromise
]);

const getCombinedPromise = async (): Promise<void> => {
const startingPromise = this.latestLoadModulesPromise;
await startingPromise;
if (this.latestLoadModulesPromise !== startingPromise) {
return getCombinedPromise();
}
};

this.extendLoadModulesPromise(loadNewModulesPromise);
await getCombinedPromise();
return await loadNewModulesPromise;
}

private extendLoadModulesPromise(loadNewModulesPromise: Promise<any>): void {
this.latestLoadModulesPromise = Promise.all([
loadNewModulesPromise,
this.latestLoadModulesPromise
]);
}

private fetchAllDependencies(module: Module): Promise<unknown> {
return Promise.all([
...Array.from(module.sources, async source => {
Expand Down Expand Up @@ -277,6 +314,8 @@ export class ModuleLoader {
]);
}

// TODO Lukas check usages: How do we add the flags if a module is already part of the graph?
// TODO Lukas do we want to resolve implicit dependants, possibly using the importer if it exists?
private async fetchModule(
id: string,
importer: string | undefined,
Expand Down Expand Up @@ -379,7 +418,8 @@ export class ModuleLoader {
private async loadEntryModule(
unresolvedId: string,
isEntry: boolean,
importer: string | undefined
importer: string | undefined,
implicitlyLoadedBefore: string | null
): Promise<Module> {
const resolveIdResult = await resolveId(
unresolvedId,
Expand All @@ -392,15 +432,23 @@ export class ModuleLoader {
resolveIdResult === false ||
(resolveIdResult && typeof resolveIdResult === 'object' && resolveIdResult.external)
) {
return error(errEntryCannotBeExternal(unresolvedId));
return error(
implicitlyLoadedBefore === null
? errEntryCannotBeExternal(unresolvedId)
: errImplicitDependantCannotBeExternal(unresolvedId, implicitlyLoadedBefore)
);
}
const id =
resolveIdResult && typeof resolveIdResult === 'object' ? resolveIdResult.id : resolveIdResult;

if (typeof id === 'string') {
return this.fetchModule(id, undefined, true, false, isEntry);
}
return error(errUnresolvedEntry(unresolvedId));
return error(
implicitlyLoadedBefore === null
? errUnresolvedEntry(unresolvedId)
: errUnresolvedImplicitDependant(unresolvedId, implicitlyLoadedBefore)
);
}

private normalizeResolveIdResult(
Expand Down Expand Up @@ -491,3 +539,20 @@ function normalizeRelativeExternalId(source: string, importer: string | undefine
: resolve(source)
: source;
}

function addChunkNamesToModule(
module: Module,
{ fileName, name }: UnresolvedModule,
isUserDefined: boolean
) {
if (fileName !== null) {
module.chunkFileNames.add(fileName);
} else if (name !== null) {
if (module.chunkName === null) {
module.chunkName = name;
}
if (isUserDefined) {
module.userChunkNames.add(name);
}
}
}
23 changes: 3 additions & 20 deletions src/utils/FileEmitter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Graph from '../Graph';
import Module from '../Module';
import {
EmittedChunk,
FilePlaceholder,
NormalizedInputOptions,
OutputBundleWithPlaceholders,
PreserveEntrySignaturesOption,
WarningHandler
} from '../rollup/types';
import { BuildPhase } from './buildPhase';
Expand Down Expand Up @@ -300,25 +300,8 @@ export class FileEmitter {
type: 'chunk'
};
this.graph.moduleLoader
.addEntryModules(
[
{
fileName: emittedChunk.fileName || null,
id: emittedChunk.id,
implicitlyLoadedAfter: (emittedChunk.implicitlyLoadedAfterOneOf as string[]) || [],
importer: emittedChunk.importer as string | undefined,
name: emittedChunk.name || null
}
],
false
)
.then(({ newEntryModules: [module] }) => {
consumedChunk.module = module;
const preserveSignature = emittedChunk.preserveSignature;
if (preserveSignature || preserveSignature === false) {
module.preserveSignature = preserveSignature as PreserveEntrySignaturesOption;
}
})
.emitChunk((emittedChunk as unknown) as EmittedChunk)
.then(module => (consumedChunk.module = module))
.catch(() => {
// Avoid unhandled Promise rejection as the error will be thrown later
// once module loading has finished
Expand Down
29 changes: 29 additions & 0 deletions src/utils/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,35 @@ export function errUnresolvedEntry(unresolvedId: string) {
};
}

// TODO Lukas test
export function errImplicitDependantCannotBeExternal(
unresolvedId: string,
implicitlyLoadedBefore: string
) {
return {
code: Errors.UNRESOLVED_ENTRY,
message: `Module "${relativeId(
unresolvedId
)}" that should be implicitly loaded before "${relativeId(
implicitlyLoadedBefore
)}" cannot be external.`
};
}

export function errUnresolvedImplicitDependant(
unresolvedId: string,
implicitlyLoadedBefore: string
) {
return {
code: Errors.UNRESOLVED_ENTRY,
message: `Module "${relativeId(
unresolvedId
)}" that should be implicitly loaded before "${relativeId(
implicitlyLoadedBefore
)}" could not be resolved.`
};
}

export function errUnresolvedImport(source: string, importer: string) {
return {
code: Errors.UNRESOLVED_IMPORT,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { loader } = require('../../../../utils.js');

module.exports = {
// solo: true,
description: 'deconflicts files against named files',
options: {
input: 'main',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const assert = require('assert');
let referenceId;

module.exports = {
// solo: true,
description: 'retrieves the correct name of an emitted chunk in case a facade is created',
options: {
input: 'main',
Expand Down

0 comments on commit 6ca9118

Please sign in to comment.