Skip to content

Commit dd8a6a6

Browse files
committed
feature #1070 New Encore method for adding multiple entries at once (shmolf)
This PR was squashed before being merged into the main branch. Discussion ---------- New Encore method for adding multiple entries at once Purpose: I dislike having to call `addEntry` multiple times, and would prefer the cleaner look of a single hash. Example Usage: ```js Encore.addEntries({ main: './path/to/main.js', secondary: './path/to/another/file.js', }); ``` This supports validation that any keys provided in the Object literal do not yet exist as an Entrypoint or Style Entrypoint. Tests have also been updated. --- Another new method (`validateNameIsNewEntry`) was also added to extract and consolidate logic related to checking for duplicate entrypoint names. Tests for this other function have also been added. Commits ------- 22a3ee1 New Encore method for adding multiple entries at once
2 parents ba177af + 22a3ee1 commit dd8a6a6

File tree

5 files changed

+185
-23
lines changed

5 files changed

+185
-23
lines changed

index.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,39 @@ class Encore {
235235
return this;
236236
}
237237

238+
/**
239+
* Adds a collection of JavaScript files that should be webpacked:
240+
*
241+
* ```
242+
* // final output file will be main.js in the output directory
243+
* Encore.addEntries({
244+
* main: './path/to/some_file.js',
245+
* secondary: './path/to/another_file.js',
246+
* });
247+
* ```
248+
*
249+
* If the JavaScript files imports/requires CSS/Sass/LESS files,
250+
* then a CSS file (e.g. main.css) will also be output.
251+
*
252+
* @param {Object.<string, string|array>} entries where the Keys are the
253+
* names (without extension) that will be used
254+
* as the output filename (e.g. app will become app.js)
255+
* in the output directory. The values are the path(s)
256+
* to the source file(s).
257+
* @returns {Encore}
258+
*/
259+
addEntries(entries) {
260+
webpackConfig.addEntries(entries);
261+
262+
return this;
263+
}
264+
238265
/**
239266
* Adds a CSS/SASS/LESS file that should be webpacked:
240267
*
241268
* ```
242269
* // final output file will be main.css in the output directory
243-
* Encore.addEntry('main', './path/to/some_file.css');
270+
* Encore.addStyleEntry('main', './path/to/some_file.css');
244271
* ```
245272
*
246273
* This is actually not something Webpack does natively, and you

lib/WebpackConfig.js

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -348,27 +348,27 @@ class WebpackConfig {
348348
}
349349

350350
addEntry(name, src) {
351-
if (this.entries.has(name)) {
352-
throw new Error(`Duplicate name "${name}" passed to addEntry(): entries must be unique.`);
353-
}
354-
355-
// also check for styleEntries duplicates
356-
if (this.styleEntries.has(name)) {
357-
throw new Error(`The "${name}" passed to addEntry conflicts with a name passed to addStyleEntry(). The entry names between addEntry() and addStyleEntry() must be unique.`);
358-
}
351+
this.validateNameIsNewEntry(name);
359352

360353
this.entries.set(name, src);
361354
}
362355

363-
addStyleEntry(name, src) {
364-
if (this.styleEntries.has(name)) {
365-
throw new Error(`Duplicate name "${name}" passed to addStyleEntry(): entries must be unique.`);
356+
/**
357+
* Provide a has of entries at once, as an alternative to calling `addEntry` several times.
358+
*
359+
* @param {Object.<string, string|string[]>} entries
360+
* @returns {Void}
361+
*/
362+
addEntries(entries = {}) {
363+
if (typeof entries !== 'object') {
364+
throw new Error('Argument 1 to addEntries() must be an object.');
366365
}
367366

368-
// also check for entries duplicates
369-
if (this.entries.has(name)) {
370-
throw new Error(`The "${name}" passed to addStyleEntry() conflicts with a name passed to addEntry(). The entry names between addEntry() and addStyleEntry() must be unique.`);
371-
}
367+
Object.entries(entries).forEach((entry) => this.addEntry(entry[0], entry[1]));
368+
}
369+
370+
addStyleEntry(name, src) {
371+
this.validateNameIsNewEntry(name);
372372

373373
this.styleEntries.set(name, src);
374374
}
@@ -1031,6 +1031,18 @@ class WebpackConfig {
10311031
isDevServer() {
10321032
return this.isDev() && this.runtimeConfig.useDevServer;
10331033
}
1034+
1035+
validateNameIsNewEntry(name) {
1036+
const entryNamesOverlapMsg = 'The entry names between addEntry(), addEntries(), and addStyleEntry() must be unique.';
1037+
1038+
if (this.entries.has(name)) {
1039+
throw new Error(`Duplicate name "${name}}" already exists as an Entrypoint. ${entryNamesOverlapMsg}`);
1040+
}
1041+
1042+
if (this.styleEntries.has(name)) {
1043+
throw new Error(`The "${name}" already exists as a Style Entrypoint. ${entryNamesOverlapMsg}`);
1044+
}
1045+
}
10341046
}
10351047

10361048
module.exports = WebpackConfig;

lib/config/validator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class Validator {
4444
&& this.webpackConfig.styleEntries.size === 0
4545
&& this.webpackConfig.copyFilesConfigs.length === 0
4646
) {
47-
throw new Error('No entries found! You must call addEntry() or addStyleEntry() or copyFiles() at least once - otherwise... there is nothing to webpack!');
47+
throw new Error('No entries found! You must call addEntry() or addEntries() or addStyleEntry() or copyFiles() at least once - otherwise... there is nothing to webpack!');
4848
}
4949
}
5050

test/WebpackConfig.js

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -347,13 +347,13 @@ describe('WebpackConfig object', () => {
347347
});
348348

349349
describe('addEntry', () => {
350-
it('Calling with a duplicate name throws an error', () => {
350+
it('Calling with a duplicate entrypoint name throws an error', () => {
351351
const config = createConfig();
352352
config.addEntry('entry_foo', './foo.js');
353353

354354
expect(() => {
355355
config.addEntry('entry_foo', './bar.js');
356-
}).to.throw('Duplicate name');
356+
}).to.throw('already exists as an Entrypoint');
357357
});
358358

359359
it('Calling with a duplicate of addStyleEntry', () => {
@@ -362,18 +362,56 @@ describe('WebpackConfig object', () => {
362362

363363
expect(() => {
364364
config.addEntry('main', './main.js');
365-
}).to.throw('conflicts with a name passed to addStyleEntry');
365+
}).to.throw('already exists as a Style Entrypoint');
366+
});
367+
368+
it('Calling with a duplicate of addEntries', () => {
369+
const config = createConfig();
370+
config.addEntries({ main: './foo.js' });
371+
372+
expect(() => {
373+
config.addEntry('main', './bar.js');
374+
}).to.throw('already exists as an Entrypoint');
375+
});
376+
});
377+
378+
describe('addEntries', () => {
379+
it('Calling with a duplicate entrypoint name throws an error', () => {
380+
const config = createConfig();
381+
config.addEntry('entry_foo', './foo.js');
382+
383+
expect(() => {
384+
config.addEntries({ entry_foo: './bar.js' });
385+
}).to.throw('already exists as an Entrypoint');
386+
});
387+
388+
it('Calling with a duplicate of addStyleEntry', () => {
389+
const config = createConfig();
390+
config.addStyleEntry('main', './main.scss');
391+
392+
expect(() => {
393+
config.addEntries({ main: './main.js' });
394+
}).to.throw('already exists as a Style Entrypoint');
395+
});
396+
397+
it('Calling with a duplicate of addEntries', () => {
398+
const config = createConfig();
399+
config.addEntries({ main: './foo.js' });
400+
401+
expect(() => {
402+
config.addEntries({ main: './bar.js' });
403+
}).to.throw('already exists as an Entrypoint');
366404
});
367405
});
368406

369407
describe('addStyleEntry', () => {
370-
it('Calling with a duplicate name throws an error', () => {
408+
it('Calling with a duplicate style entrypoint name throws an error', () => {
371409
const config = createConfig();
372410
config.addStyleEntry('entry_foo', './foo.css');
373411

374412
expect(() => {
375413
config.addStyleEntry('entry_foo', './bar.css');
376-
}).to.throw('Duplicate name');
414+
}).to.throw('already exists as a Style Entrypoint');
377415
});
378416

379417
it('Calling with a duplicate of addEntry', () => {
@@ -382,7 +420,16 @@ describe('WebpackConfig object', () => {
382420

383421
expect(() => {
384422
config.addStyleEntry('main', './main.js');
385-
}).to.throw('conflicts with a name passed to addEntry');
423+
}).to.throw('already exists as an Entrypoint');
424+
});
425+
426+
it('Calling with a duplicate of addEntries', () => {
427+
const config = createConfig();
428+
config.addEntries({ main: './main.js' });
429+
430+
expect(() => {
431+
config.addStyleEntry('main', './main.scss');
432+
}).to.throw('already exists as an Entrypoint');
386433
});
387434
});
388435

@@ -1532,4 +1579,33 @@ describe('WebpackConfig object', () => {
15321579
}).to.throw('Encore.enableEslintPlugin() can not be called when Encore.enableEslintLoader() has been called.');
15331580
});
15341581
});
1582+
1583+
describe('validateNameIsNewEntry', () => {
1584+
it('Providing a new name does not throw an error', () => {
1585+
const config = createConfig();
1586+
config.addEntry('entry_foo', './foo.js');
1587+
1588+
expect(() => {
1589+
config.validateNameIsNewEntry('unused_name');
1590+
}).to.not.throw;
1591+
});
1592+
1593+
it('Providing a name exists within Entries does throw an error', () => {
1594+
const config = createConfig();
1595+
config.addEntry('entry_foo', './foo.js');
1596+
1597+
expect(() => {
1598+
config.validateNameIsNewEntry('entry_foo');
1599+
}).to.throw('already exists as an Entrypoint');
1600+
});
1601+
1602+
it('Providing a name exists within Style Entries does throw an error', () => {
1603+
const config = createConfig();
1604+
config.addStyleEntry('entry_foo', './foo.js');
1605+
1606+
expect(() => {
1607+
config.validateNameIsNewEntry('entry_foo');
1608+
}).to.throw('already exists as a Style Entrypoint');
1609+
});
1610+
});
15351611
});

test/config-generator.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,53 @@ describe('The config-generator function', () => {
105105
}));
106106
});
107107

108+
it('addEntry and addEntries expectations are merged', () => {
109+
const config = createConfig();
110+
config.publicPath = '/';
111+
config.outputPath = '/tmp';
112+
config.addEntry('main', './main');
113+
config.addEntries({ main2: './main2' });
114+
115+
const actualConfig = configGenerator(config);
116+
117+
expect(JSON.stringify(actualConfig.entry)).to.equal(JSON.stringify({
118+
main: './main',
119+
main2: './main2',
120+
}));
121+
});
122+
123+
it('addStyleEntry and addEntries expectations are merged', () => {
124+
const config = createConfig();
125+
config.publicPath = '/';
126+
config.outputPath = '/tmp';
127+
config.addStyleEntry('style', ['./bootstrap.css', './main.css']);
128+
config.addEntries({ main: './main' });
129+
130+
const actualConfig = configGenerator(config);
131+
132+
expect(JSON.stringify(actualConfig.entry)).to.equal(JSON.stringify({
133+
main: './main',
134+
style: ['./bootstrap.css', './main.css'],
135+
}));
136+
});
137+
138+
it('addEntry, addStyleEntry and addEntries expectations are merged', () => {
139+
const config = createConfig();
140+
config.publicPath = '/';
141+
config.outputPath = '/tmp';
142+
config.addEntry('main', './main');
143+
config.addStyleEntry('style', ['./bootstrap.css', './main.css']);
144+
config.addEntries({ main2: './main2' });
145+
146+
const actualConfig = configGenerator(config);
147+
148+
expect(JSON.stringify(actualConfig.entry)).to.equal(JSON.stringify({
149+
main: './main',
150+
main2: './main2',
151+
style: ['./bootstrap.css', './main.css'],
152+
}));
153+
});
154+
108155
it('basic output', () => {
109156
const config = createConfig();
110157

0 commit comments

Comments
 (0)