From 6ca9118c7c35a4015a9d9405cdd810d3b2872bca Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Sun, 31 May 2020 21:46:02 +0200 Subject: [PATCH] Directly resolve and load implicit dependants --- src/Graph.ts | 15 +- src/Module.ts | 1 - src/ModuleLoader.ts | 143 +++++++++++++----- src/utils/FileEmitter.ts | 23 +-- src/utils/error.ts | 29 ++++ .../deconflict-against-named-files/_config.js | 1 + .../emit-file/emit-chunk-facade/_config.js | 1 + .../emit-file/emit-chunk-filename/_config.js | 1 + .../emit-chunk-name-conflict/_config.js | 1 + .../emit-file/emit-chunk-named/_config.js | 1 + .../emit-chunk-with-importer/_config.js | 1 + .../emit-file/emit-chunk-worker/_config.js | 1 + .../emit-file/emit-chunk-worklet/_config.js | 1 + .../samples/emit-file/emit-chunk/_config.js | 1 + .../emit-file/emits-existing-chunk/_config.js | 1 + .../emit-file/named-user-chunks/_config.js | 1 + .../emit-file/sanitize-file-name/_config.js | 1 + .../single-dependency/_config.js | 1 + .../override-via-plugin/_config.js | 1 + .../_config.js | 1 + .../chunk-filename-not-available/_config.js | 1 + .../emit-file/chunk-not-found/_config.js | 1 + .../emit-file/invalid-chunk-id/_config.js | 1 + .../emit-file/modules-loaded/_config.js | 1 + .../samples/emit-file/no-input/_config.js | 1 + .../set-asset-source-chunk/_config.js | 1 + .../missing-dependant/_config.js | 24 +++ .../missing-dependant/dep.js | 1 + .../missing-dependant/main.js | 1 + 29 files changed, 186 insertions(+), 72 deletions(-) create mode 100644 test/function/samples/implicit-dependencies/missing-dependant/_config.js create mode 100644 test/function/samples/implicit-dependencies/missing-dependant/dep.js create mode 100644 test/function/samples/implicit-dependencies/missing-dependant/main.js diff --git a/src/Graph.ts b/src/Graph.ts index ea13b7914b3..ee87ce645c2 100644 --- a/src/Graph.ts +++ b/src/Graph.ts @@ -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'; @@ -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) @@ -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); } diff --git a/src/Module.ts b/src/Module.ts index 46060436904..6cba04350a5 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -197,7 +197,6 @@ export default class Module { exportsAll: { [name: string]: string } = Object.create(null); facadeChunk: Chunk | null = null; implicitlyLoadedAfter: Module[] = []; - implicitlyLoadedAfterIds = new Set(); implicitlyLoadedBefore = new Set(); importDescriptions: { [name: string]: ImportDescription } = Object.create(null); importers: string[] = []; diff --git a/src/ModuleLoader.ts b/src/ModuleLoader.ts index f238a5fa544..e82cc356a6a 100644 --- a/src/ModuleLoader.ts +++ b/src/ModuleLoader.ts @@ -3,6 +3,7 @@ import ExternalModule from './ExternalModule'; import Graph from './Graph'; import Module from './Module'; import { + EmittedChunk, GetManualChunk, HasModuleSideEffects, NormalizedInputOptions, @@ -15,10 +16,12 @@ import { errCannotAssignModuleToChunk, errEntryCannotBeExternal, errExternalSyntheticExports, + errImplicitDependantCannotBeExternal, errInternalIdCannotBeExternal, errNamespaceConflict, error, errUnresolvedEntry, + errUnresolvedImplicitDependant, errUnresolvedImport, errUnresolvedImportTreatedAsExternal } from './utils/error'; @@ -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; } @@ -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 @@ -69,41 +70,23 @@ export class ModuleLoader { this.nextEntryModuleIndex += unresolvedEntryModules.length; const loadNewEntryModulesPromise = Promise.all( unresolvedEntryModules.map( - ({ id, importer, implicitlyLoadedAfter }): Promise => - this.loadEntryModule(id, implicitlyLoadedAfter.length === 0, importer) + ({ id, importer }): Promise => 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 @@ -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( @@ -155,6 +138,29 @@ export class ModuleLoader { } } + async emitChunk({ + fileName, + id, + importer, + name, + implicitlyLoadedAfterOneOf, + preserveSignature + }: EmittedChunk): Promise { + 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, @@ -170,6 +176,32 @@ export class ModuleLoader { ); } + private addEntryWithImplicitDependants( + unresolvedModule: UnresolvedModule, + implicitlyLoadedAfter: string[] + ): Promise { + // 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; @@ -225,12 +257,8 @@ 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(loadNewModulesPromise: Promise): Promise { - this.latestLoadModulesPromise = Promise.all([ - loadNewModulesPromise, - this.latestLoadModulesPromise - ]); - const getCombinedPromise = async (): Promise => { const startingPromise = this.latestLoadModulesPromise; await startingPromise; @@ -238,10 +266,19 @@ export class ModuleLoader { return getCombinedPromise(); } }; + + this.extendLoadModulesPromise(loadNewModulesPromise); await getCombinedPromise(); return await loadNewModulesPromise; } + private extendLoadModulesPromise(loadNewModulesPromise: Promise): void { + this.latestLoadModulesPromise = Promise.all([ + loadNewModulesPromise, + this.latestLoadModulesPromise + ]); + } + private fetchAllDependencies(module: Module): Promise { return Promise.all([ ...Array.from(module.sources, async source => { @@ -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, @@ -379,7 +418,8 @@ export class ModuleLoader { private async loadEntryModule( unresolvedId: string, isEntry: boolean, - importer: string | undefined + importer: string | undefined, + implicitlyLoadedBefore: string | null ): Promise { const resolveIdResult = await resolveId( unresolvedId, @@ -392,7 +432,11 @@ 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; @@ -400,7 +444,11 @@ export class ModuleLoader { 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( @@ -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); + } + } +} diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index a633bf22a44..ba14de04ca5 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -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'; @@ -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 diff --git a/src/utils/error.ts b/src/utils/error.ts index 4483bfb4cc0..400a8c290e0 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -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, diff --git a/test/chunking-form/samples/emit-file/deconflict-against-named-files/_config.js b/test/chunking-form/samples/emit-file/deconflict-against-named-files/_config.js index a3622a8c983..09c05ef1a49 100644 --- a/test/chunking-form/samples/emit-file/deconflict-against-named-files/_config.js +++ b/test/chunking-form/samples/emit-file/deconflict-against-named-files/_config.js @@ -1,6 +1,7 @@ const { loader } = require('../../../../utils.js'); module.exports = { + // solo: true, description: 'deconflicts files against named files', options: { input: 'main', diff --git a/test/chunking-form/samples/emit-file/emit-chunk-facade/_config.js b/test/chunking-form/samples/emit-file/emit-chunk-facade/_config.js index 5434bb22502..943823c2531 100644 --- a/test/chunking-form/samples/emit-file/emit-chunk-facade/_config.js +++ b/test/chunking-form/samples/emit-file/emit-chunk-facade/_config.js @@ -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', diff --git a/test/chunking-form/samples/emit-file/emit-chunk-filename/_config.js b/test/chunking-form/samples/emit-file/emit-chunk-filename/_config.js index 391c8457fbc..1e75c50b538 100644 --- a/test/chunking-form/samples/emit-file/emit-chunk-filename/_config.js +++ b/test/chunking-form/samples/emit-file/emit-chunk-filename/_config.js @@ -2,6 +2,7 @@ const assert = require('assert'); let referenceId; module.exports = { + // solo: true, description: 'supports specifying a file name when emitting a chunk', options: { input: 'main', diff --git a/test/chunking-form/samples/emit-file/emit-chunk-name-conflict/_config.js b/test/chunking-form/samples/emit-file/emit-chunk-name-conflict/_config.js index ce14a9334cf..641f8667383 100644 --- a/test/chunking-form/samples/emit-file/emit-chunk-name-conflict/_config.js +++ b/test/chunking-form/samples/emit-file/emit-chunk-name-conflict/_config.js @@ -1,4 +1,5 @@ module.exports = { + // solo: true, description: 'deduplicates named chunks emitted more than once', options: { // For the second entry, a facade is created diff --git a/test/chunking-form/samples/emit-file/emit-chunk-named/_config.js b/test/chunking-form/samples/emit-file/emit-chunk-named/_config.js index f0c2828ab2e..86256a5742a 100644 --- a/test/chunking-form/samples/emit-file/emit-chunk-named/_config.js +++ b/test/chunking-form/samples/emit-file/emit-chunk-named/_config.js @@ -2,6 +2,7 @@ const assert = require('assert'); let referenceId; module.exports = { + // solo: true, description: 'allows naming emitted chunks', options: { input: 'main', diff --git a/test/chunking-form/samples/emit-file/emit-chunk-with-importer/_config.js b/test/chunking-form/samples/emit-file/emit-chunk-with-importer/_config.js index f8192ec4ea2..0143c20fe66 100644 --- a/test/chunking-form/samples/emit-file/emit-chunk-with-importer/_config.js +++ b/test/chunking-form/samples/emit-file/emit-chunk-with-importer/_config.js @@ -4,6 +4,7 @@ let mainReferenceId; let nestedReferenceId; module.exports = { + // solo: true, description: 'allows specifying an importer when resolving ids', options: { input: 'main', diff --git a/test/chunking-form/samples/emit-file/emit-chunk-worker/_config.js b/test/chunking-form/samples/emit-file/emit-chunk-worker/_config.js index 1be938dec5b..0023edd5865 100644 --- a/test/chunking-form/samples/emit-file/emit-chunk-worker/_config.js +++ b/test/chunking-form/samples/emit-file/emit-chunk-worker/_config.js @@ -4,6 +4,7 @@ let workerId; let proxyId; module.exports = { + // solo: true, description: 'allows adding additional chunks to be used in workers', options: { input: 'main', diff --git a/test/chunking-form/samples/emit-file/emit-chunk-worklet/_config.js b/test/chunking-form/samples/emit-file/emit-chunk-worklet/_config.js index 70bd5ddb829..2f03610e13c 100644 --- a/test/chunking-form/samples/emit-file/emit-chunk-worklet/_config.js +++ b/test/chunking-form/samples/emit-file/emit-chunk-worklet/_config.js @@ -1,6 +1,7 @@ const REGISTER_WORKLET = 'register-paint-worklet:'; module.exports = { + // solo: true, description: 'allows adding additional chunks to be used in worklets', options: { input: 'main', diff --git a/test/chunking-form/samples/emit-file/emit-chunk/_config.js b/test/chunking-form/samples/emit-file/emit-chunk/_config.js index 2c6897b8235..155792a4676 100644 --- a/test/chunking-form/samples/emit-file/emit-chunk/_config.js +++ b/test/chunking-form/samples/emit-file/emit-chunk/_config.js @@ -2,6 +2,7 @@ const assert = require('assert'); let referenceId; module.exports = { + // solo: true, description: 'allows adding additional chunks and retrieving their file name', options: { input: 'main', diff --git a/test/chunking-form/samples/emit-file/emits-existing-chunk/_config.js b/test/chunking-form/samples/emit-file/emits-existing-chunk/_config.js index 404461da95e..fed96986a07 100644 --- a/test/chunking-form/samples/emit-file/emits-existing-chunk/_config.js +++ b/test/chunking-form/samples/emit-file/emits-existing-chunk/_config.js @@ -1,4 +1,5 @@ module.exports = { + // solo: true, description: 'allows adding modules already in the graph as entry points', options: { input: { diff --git a/test/chunking-form/samples/emit-file/named-user-chunks/_config.js b/test/chunking-form/samples/emit-file/named-user-chunks/_config.js index 6807aad6a57..446451587cb 100644 --- a/test/chunking-form/samples/emit-file/named-user-chunks/_config.js +++ b/test/chunking-form/samples/emit-file/named-user-chunks/_config.js @@ -5,6 +5,7 @@ let referenceIdFileName1; let referenceIdFileName2; module.exports = { + // solo: true, description: 'deduplicates with named chunks defined by the user', options: { input: { mainChunk: 'main', mainChunkFacade: 'main' }, diff --git a/test/chunking-form/samples/emit-file/sanitize-file-name/_config.js b/test/chunking-form/samples/emit-file/sanitize-file-name/_config.js index 39a76de71d6..c0a5fe37d63 100644 --- a/test/chunking-form/samples/emit-file/sanitize-file-name/_config.js +++ b/test/chunking-form/samples/emit-file/sanitize-file-name/_config.js @@ -1,4 +1,5 @@ module.exports = { + // solo: true, description: 'allows adding additional chunks and retrieving their file name', options: { input: 'main', diff --git a/test/chunking-form/samples/implicit-dependencies/single-dependency/_config.js b/test/chunking-form/samples/implicit-dependencies/single-dependency/_config.js index 16eb619987b..3163c85c88c 100644 --- a/test/chunking-form/samples/implicit-dependencies/single-dependency/_config.js +++ b/test/chunking-form/samples/implicit-dependencies/single-dependency/_config.js @@ -10,6 +10,7 @@ const ID_DEP = path.join(__dirname, 'dep.js'); // TODO Lukas what if the original module is not included (empty)/executed (behind a missing dynamic import)? // TODO Lukas what about signatures? Even though we have the implicit dependency, signatures should probably still reflect what entry chunks do module.exports = { + // solo: true, description: 'supports implicit dependencies when emitting files', options: { preserveEntrySignatures: 'allow-extension', diff --git a/test/chunking-form/samples/preserve-entry-signatures/override-via-plugin/_config.js b/test/chunking-form/samples/preserve-entry-signatures/override-via-plugin/_config.js index d4888307bcf..c0bc048a2d8 100644 --- a/test/chunking-form/samples/preserve-entry-signatures/override-via-plugin/_config.js +++ b/test/chunking-form/samples/preserve-entry-signatures/override-via-plugin/_config.js @@ -1,4 +1,5 @@ module.exports = { + // solo: true, description: 'Allows overriding behaviour for emitted chunks', options: { input: [], diff --git a/test/chunking-form/samples/preserve-entry-signatures/undefined-with-tainted-plugin-chunk/_config.js b/test/chunking-form/samples/preserve-entry-signatures/undefined-with-tainted-plugin-chunk/_config.js index 4044ae8b0a7..b255d3892c7 100644 --- a/test/chunking-form/samples/preserve-entry-signatures/undefined-with-tainted-plugin-chunk/_config.js +++ b/test/chunking-form/samples/preserve-entry-signatures/undefined-with-tainted-plugin-chunk/_config.js @@ -1,4 +1,5 @@ module.exports = { + // solo: true, description: 'Does not warn if preserveEntrySignatures is not set and an empty facade is created for a plugin chunk', options: { diff --git a/test/function/samples/emit-file/chunk-filename-not-available/_config.js b/test/function/samples/emit-file/chunk-filename-not-available/_config.js index 48d5280a8f1..abd481ceba0 100644 --- a/test/function/samples/emit-file/chunk-filename-not-available/_config.js +++ b/test/function/samples/emit-file/chunk-filename-not-available/_config.js @@ -1,6 +1,7 @@ const path = require('path'); module.exports = { + // solo: true, description: 'Throws when accessing the filename before it has been generated', options: { input: 'main.js', diff --git a/test/function/samples/emit-file/chunk-not-found/_config.js b/test/function/samples/emit-file/chunk-not-found/_config.js index b136c0750e9..386b6fec678 100644 --- a/test/function/samples/emit-file/chunk-not-found/_config.js +++ b/test/function/samples/emit-file/chunk-not-found/_config.js @@ -1,6 +1,7 @@ const path = require('path'); module.exports = { + // solo: true, description: 'Throws if an emitted entry chunk cannot be resolved', options: { input: 'main.js', diff --git a/test/function/samples/emit-file/invalid-chunk-id/_config.js b/test/function/samples/emit-file/invalid-chunk-id/_config.js index ed73d4d4776..7d93fee6ca8 100644 --- a/test/function/samples/emit-file/invalid-chunk-id/_config.js +++ b/test/function/samples/emit-file/invalid-chunk-id/_config.js @@ -1,4 +1,5 @@ module.exports = { + // solo: true, description: 'throws for invalid chunk ids', options: { plugins: { diff --git a/test/function/samples/emit-file/modules-loaded/_config.js b/test/function/samples/emit-file/modules-loaded/_config.js index 3cd01bfcd83..61c4e591d27 100644 --- a/test/function/samples/emit-file/modules-loaded/_config.js +++ b/test/function/samples/emit-file/modules-loaded/_config.js @@ -1,4 +1,5 @@ module.exports = { + // solo: true, description: 'Throws when adding a chunk after the modules have finished loading', options: { input: 'main.js', diff --git a/test/function/samples/emit-file/no-input/_config.js b/test/function/samples/emit-file/no-input/_config.js index c35eb800405..cd308585126 100644 --- a/test/function/samples/emit-file/no-input/_config.js +++ b/test/function/samples/emit-file/no-input/_config.js @@ -1,4 +1,5 @@ module.exports = { + // solo: true, description: 'It is not necessary to provide an input if a dynamic entry is emitted', options: { input: undefined, diff --git a/test/function/samples/emit-file/set-asset-source-chunk/_config.js b/test/function/samples/emit-file/set-asset-source-chunk/_config.js index b13d837e6d2..873d099260c 100644 --- a/test/function/samples/emit-file/set-asset-source-chunk/_config.js +++ b/test/function/samples/emit-file/set-asset-source-chunk/_config.js @@ -1,6 +1,7 @@ const path = require('path'); module.exports = { + // solo: true, description: 'throws when trying to set the asset source of a chunk', options: { plugins: { diff --git a/test/function/samples/implicit-dependencies/missing-dependant/_config.js b/test/function/samples/implicit-dependencies/missing-dependant/_config.js new file mode 100644 index 00000000000..b43c6e1fafd --- /dev/null +++ b/test/function/samples/implicit-dependencies/missing-dependant/_config.js @@ -0,0 +1,24 @@ +const path = require('path'); + +module.exports = { + // solo: true, + description: 'throws when a module that is loaded before an emitted chunk does not exist', + options: { + plugins: { + name: 'test-plugin', + buildStart() { + this.emitFile({ + type: 'chunk', + id: 'dep.js', + implicitlyLoadedAfterOneOf: ['does-not-exist'] + }); + } + } + }, + error: { + code: 'UNRESOLVED_ENTRY', + message: + 'Module "does-not-exist" that should be implicitly loaded before "dep.js" could not be resolved.', + watchFiles: [path.resolve(__dirname, 'dep.js'), path.resolve(__dirname, 'main.js')] + } +}; diff --git a/test/function/samples/implicit-dependencies/missing-dependant/dep.js b/test/function/samples/implicit-dependencies/missing-dependant/dep.js new file mode 100644 index 00000000000..787bdab3ab0 --- /dev/null +++ b/test/function/samples/implicit-dependencies/missing-dependant/dep.js @@ -0,0 +1 @@ +export default 'dep'; diff --git a/test/function/samples/implicit-dependencies/missing-dependant/main.js b/test/function/samples/implicit-dependencies/missing-dependant/main.js new file mode 100644 index 00000000000..3d1d2b15c98 --- /dev/null +++ b/test/function/samples/implicit-dependencies/missing-dependant/main.js @@ -0,0 +1 @@ +export default 'main';