Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
29 changes: 28 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,39 @@ class Encore {
return this;
}

/**
* Adds a collection of JavaScript files that should be webpacked:
*
* ```
* // final output file will be main.js in the output directory
* Encore.addEntries({
* main: './path/to/some_file.js',
* secondary: './path/to/another_file.js',
* });
* ```
*
* If the JavaScript files imports/requires CSS/Sass/LESS files,
* then a CSS file (e.g. main.css) will also be output.
*
* @param {Object.<string, string|array>} entries where the Keys are the
* names (without extension) that will be used
* as the output filename (e.g. app will become app.js)
* in the output directory. The values are the path(s)
* to the source file(s).
* @returns {Encore}
*/
addEntries(entries) {
webpackConfig.addEntries(entries);

return this;
}

/**
* Adds a CSS/SASS/LESS file that should be webpacked:
*
* ```
* // final output file will be main.css in the output directory
* Encore.addEntry('main', './path/to/some_file.css');
* Encore.addStyleEntry('main', './path/to/some_file.css');
* ```
*
* This is actually not something Webpack does natively, and you
Expand Down
42 changes: 27 additions & 15 deletions lib/WebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,27 +346,27 @@ class WebpackConfig {
}

addEntry(name, src) {
if (this.entries.has(name)) {
throw new Error(`Duplicate name "${name}" passed to addEntry(): entries must be unique.`);
}

// also check for styleEntries duplicates
if (this.styleEntries.has(name)) {
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.`);
}
this.validateNameIsNewEntry(name);

this.entries.set(name, src);
}

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

// also check for entries duplicates
if (this.entries.has(name)) {
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.`);
}
Object.entries(entries).forEach((entry) => this.addEntry(entry[0], entry[1]));
}

addStyleEntry(name, src) {
this.validateNameIsNewEntry(name);

this.styleEntries.set(name, src);
}
Expand Down Expand Up @@ -1005,6 +1005,18 @@ class WebpackConfig {
isDevServer() {
return this.isDev() && this.runtimeConfig.useDevServer;
}

validateNameIsNewEntry(name) {
const entryNamesOverlapMsg = 'The entry names between addEntry(), addEntries(), and addStyleEntry() must be unique.';

if (this.entries.has(name)) {
throw new Error(`Duplicate name "${name}}" already exists as an Entrypoint. ${entryNamesOverlapMsg}`);
}

if (this.styleEntries.has(name)) {
throw new Error(`The "${name}" already exists as a Style Entrypoint. ${entryNamesOverlapMsg}`);
}
}
}

module.exports = WebpackConfig;
2 changes: 1 addition & 1 deletion lib/config/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Validator {
&& this.webpackConfig.styleEntries.size === 0
&& this.webpackConfig.copyFilesConfigs.length === 0
) {
throw new Error('No entries found! You must call addEntry() or addStyleEntry() or copyFiles() at least once - otherwise... there is nothing to webpack!');
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!');
}
}

Expand Down
88 changes: 82 additions & 6 deletions test/WebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,13 @@ describe('WebpackConfig object', () => {
});

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

expect(() => {
config.addEntry('entry_foo', './bar.js');
}).to.throw('Duplicate name');
}).to.throw('already exists as an Entrypoint');
});

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

expect(() => {
config.addEntry('main', './main.js');
}).to.throw('conflicts with a name passed to addStyleEntry');
}).to.throw('already exists as a Style Entrypoint');
});

it('Calling with a duplicate of addEntries', () => {
const config = createConfig();
config.addEntries({ main: './foo.js' });

expect(() => {
config.addEntry('main', './bar.js');
}).to.throw('already exists as an Entrypoint');
});
});

describe('addEntries', () => {
it('Calling with a duplicate entrypoint name throws an error', () => {
const config = createConfig();
config.addEntry('entry_foo', './foo.js');

expect(() => {
config.addEntries({ entry_foo: './bar.js' });
}).to.throw('already exists as an Entrypoint');
});

it('Calling with a duplicate of addStyleEntry', () => {
const config = createConfig();
config.addStyleEntry('main', './main.scss');

expect(() => {
config.addEntries({ main: './main.js' });
}).to.throw('already exists as a Style Entrypoint');
});

it('Calling with a duplicate of addEntries', () => {
const config = createConfig();
config.addEntries({ main: './foo.js' });

expect(() => {
config.addEntries({ main: './bar.js' });
}).to.throw('already exists as an Entrypoint');
});
});

describe('addStyleEntry', () => {
it('Calling with a duplicate name throws an error', () => {
it('Calling with a duplicate style entrypoint name throws an error', () => {
const config = createConfig();
config.addStyleEntry('entry_foo', './foo.css');

expect(() => {
config.addStyleEntry('entry_foo', './bar.css');
}).to.throw('Duplicate name');
}).to.throw('already exists as a Style Entrypoint');
});

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

expect(() => {
config.addStyleEntry('main', './main.js');
}).to.throw('conflicts with a name passed to addEntry');
}).to.throw('already exists as an Entrypoint');
});

it('Calling with a duplicate of addEntries', () => {
const config = createConfig();
config.addEntries({ main: './main.js' });

expect(() => {
config.addStyleEntry('main', './main.scss');
}).to.throw('already exists as an Entrypoint');
});
});

Expand Down Expand Up @@ -1513,4 +1560,33 @@ describe('WebpackConfig object', () => {
}).to.throw('"notExisting" is not a valid key for enableEslintLoader(). Valid keys: lintVue.');
});
});

describe('validateNameIsNewEntry', () => {
it('Providing a new name does not throw an error', () => {
const config = createConfig();
config.addEntry('entry_foo', './foo.js');

expect(() => {
config.validateNameIsNewEntry('unused_name');
}).to.not.throw;
});

it('Providing a name exists within Entries does throw an error', () => {
const config = createConfig();
config.addEntry('entry_foo', './foo.js');

expect(() => {
config.validateNameIsNewEntry('entry_foo');
}).to.throw('already exists as an Entrypoint');
});

it('Providing a name exists within Style Entries does throw an error', () => {
const config = createConfig();
config.addStyleEntry('entry_foo', './foo.js');

expect(() => {
config.validateNameIsNewEntry('entry_foo');
}).to.throw('already exists as a Style Entrypoint');
});
});
});
47 changes: 47 additions & 0 deletions test/config-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,53 @@ describe('The config-generator function', () => {
}));
});

it('addEntry and addEntries expectations are merged', () => {
const config = createConfig();
config.publicPath = '/';
config.outputPath = '/tmp';
config.addEntry('main', './main');
config.addEntries({ main2: './main2' });

const actualConfig = configGenerator(config);

expect(JSON.stringify(actualConfig.entry)).to.equal(JSON.stringify({
main: './main',
main2: './main2',
}));
});

it('addStyleEntry and addEntries expectations are merged', () => {
const config = createConfig();
config.publicPath = '/';
config.outputPath = '/tmp';
config.addStyleEntry('style', ['./bootstrap.css', './main.css']);
config.addEntries({ main: './main' });

const actualConfig = configGenerator(config);

expect(JSON.stringify(actualConfig.entry)).to.equal(JSON.stringify({
main: './main',
style: ['./bootstrap.css', './main.css'],
}));
});

it('addEntry, addStyleEntry and addEntries expectations are merged', () => {
const config = createConfig();
config.publicPath = '/';
config.outputPath = '/tmp';
config.addEntry('main', './main');
config.addStyleEntry('style', ['./bootstrap.css', './main.css']);
config.addEntries({ main2: './main2' });

const actualConfig = configGenerator(config);

expect(JSON.stringify(actualConfig.entry)).to.equal(JSON.stringify({
main: './main',
main2: './main2',
style: ['./bootstrap.css', './main.css'],
}));
});

it('basic output', () => {
const config = createConfig();

Expand Down