Skip to content

Commit 4d6381b

Browse files
committed
minor #115 Add Encore.configureRuntimeEnvironment() method to the public API (Lyrkan)
This PR was squashed before being merged into the master branch (closes #115). Discussion ---------- Add Encore.configureRuntimeEnvironment() method to the public API This PR fixes #109 by adding an `Encore.configureRuntimeEnvironment` and an `Encore.clearRuntimeEnvironment` method to the public API. --- Basically, until now if you tried requiring `@symfony/webpack-encore` without using `./node_modules/.bin/encore`, it would throw an `Are you trying to require index.js directly?` error. This prevented retrieving the Webpack configuration object easily, which can be needed in some cases (e.g. if you use [karma-webpack](https://github.com/webpack-contrib/karma-webpack#usage)). With these changes, requiring `index.js` doesn't create a `WebpackConfig` instance anymore if the runtime environment isn't available, which removes the need of having a check at that time. In order to deal with methods of the public API that need the `WebpackConfig` to be available, a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) of the API is exported instead of the real object. This proxy only allows calls to `configureRuntimeEnvironment` and `clearRuntimeEnvironment` if the `WebpackConfig` isn't initialized, or act as a passthrough if it is. This also fixes #55 since it allows to add a `try { ... } catch { ... }` quite easily around all the methods of the API. The `Encore.clearRuntimeEnvironment()` (that nullifies `runtimeConfig` and `webpackConfig`) was needed to write tests but since I doubt it'll really be useful outside of that case let me know if you see another way to do it. --- **Examples:** ```js const Encore = require('@symfony/webpack-encore'); Encore .setOutputPath('build/') .setPublicPath('/') .addEntry('main', './src/index.ts'); ``` If executed directly would result in the following error: ``` Error: Encore.setOutputPath() cannot be called yet because the runtime environment doesn't appear to be configured. Try calling Encore.configureRuntimeEnvironment() first. ``` Whereas the following would work: ```js const Encore = require('@symfony/webpack-encore'); Encore .configureRuntimeEnvironment('dev-server') .setOutputPath('build/') .setPublicPath('/') .addEntry('main', './src/index.ts'); ``` It is also possible to pass the same options that are available using the usual CLI tool using the second argument of `configureRuntimeEnvironment` and camel-cased names: ```js const Encore = require('@symfony/webpack-encore'); Encore .configureRuntimeEnvironment('dev-server', { keepPublicPath: true, https: true, }) .setOutputPath('build/') .setPublicPath('/') .addEntry('main', './src/index.ts'); ``` Commits ------- f760e52 Remove some tests related to the public API proxy and improve some texts/comments ce6e7d3 Add Encore.configureRuntimeEnvironment() and Encore.clearRuntimeEnvironment() methods
2 parents c4b6e04 + f760e52 commit 4d6381b

File tree

2 files changed

+137
-20
lines changed

2 files changed

+137
-20
lines changed

index.js

Lines changed: 111 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,27 @@ const WebpackConfig = require('./lib/WebpackConfig');
1313
const configGenerator = require('./lib/config-generator');
1414
const validator = require('./lib/config/validator');
1515
const PrettyError = require('pretty-error');
16-
const runtimeConfig = require('./lib/context').runtimeConfig;
1716
const logger = require('./lib/logger');
17+
const parseRuntime = require('./lib/config/parse-runtime');
1818

19-
// at this time, the encore executable should have set the runtimeConfig
20-
if (!runtimeConfig) {
21-
throw new Error('Are you trying to require index.js directly?');
19+
let webpackConfig = null;
20+
let runtimeConfig = require('./lib/context').runtimeConfig;
21+
22+
function initializeWebpackConfig() {
23+
if (runtimeConfig.verbose) {
24+
logger.verbose();
25+
}
26+
27+
webpackConfig = new WebpackConfig(runtimeConfig);
2228
}
2329

24-
let webpackConfig = new WebpackConfig(runtimeConfig);
25-
if (runtimeConfig.verbose) {
26-
logger.verbose();
30+
// If runtimeConfig is already set webpackConfig can directly
31+
// be initialized here.
32+
if (runtimeConfig) {
33+
initializeWebpackConfig();
2734
}
2835

29-
module.exports = {
36+
const publicApi = {
3037
/**
3138
* The directory where your files should be output.
3239
*
@@ -493,17 +500,9 @@ module.exports = {
493500
* @returns {*}
494501
*/
495502
getWebpackConfig() {
496-
try {
497-
validator(webpackConfig);
498-
499-
return configGenerator(webpackConfig);
500-
} catch (error) {
501-
// prettifies errors thrown by our library
502-
const pe = new PrettyError();
503+
validator(webpackConfig);
503504

504-
console.log(pe.render(error));
505-
process.exit(1); // eslint-disable-line
506-
}
505+
return configGenerator(webpackConfig);
507506
},
508507

509508
/**
@@ -516,5 +515,98 @@ module.exports = {
516515
*/
517516
reset() {
518517
webpackConfig = new WebpackConfig(runtimeConfig);
519-
}
518+
},
519+
520+
/**
521+
* Initialize the runtime environment.
522+
*
523+
* This can be used to configure the Encore runtime if you're
524+
* using Encore without executing the "./node_module/.bin/encore"
525+
* utility (e.g. with karma-webpack).
526+
*
527+
* Encore.configureRuntimeEnvironment(
528+
* // Environment to use (dev, dev-server, production)
529+
* 'dev-server',
530+
*
531+
* // Same options you would use with the
532+
* // CLI utility with their name in
533+
* // camelCase.
534+
* {
535+
* https: true,
536+
* keepPublicPath: true
537+
* }
538+
* )
539+
*
540+
* Be aware than using this method will also reset the current
541+
* webpack configuration.
542+
*
543+
* @param {string} environment
544+
* @param {object} options
545+
* @returns {exports}
546+
*/
547+
configureRuntimeEnvironment(environment, options = {}) {
548+
runtimeConfig = parseRuntime(
549+
Object.assign(
550+
{},
551+
require('yargs/yargs')([environment]).argv,
552+
options
553+
),
554+
process.cwd()
555+
);
556+
557+
initializeWebpackConfig();
558+
559+
return this;
560+
},
561+
562+
/**
563+
* Clear the runtime environment.
564+
*
565+
* Be aware than using this method will also reset the
566+
* current webpack configuration.
567+
*
568+
* @returns {void}
569+
*/
570+
clearRuntimeEnvironment() {
571+
runtimeConfig = null;
572+
webpackConfig = null;
573+
},
520574
};
575+
576+
// Proxy the API in order to prevent calls to most of its methods
577+
// if the webpackConfig object hasn't been initialized yet.
578+
const publicApiProxy = new Proxy(publicApi, {
579+
get: (target, prop) => {
580+
if (typeof target[prop] === 'function') {
581+
// These methods of the public API can be called even if the
582+
// webpackConfig object hasn't been initialized yet.
583+
const safeMethods = [
584+
'configureRuntimeEnvironment',
585+
'clearRuntimeEnvironment',
586+
];
587+
588+
if (!webpackConfig && (safeMethods.indexOf(prop) === -1)) {
589+
throw new Error(`Encore.${prop}() cannot be called yet because the runtime environment doesn't appear to be configured. Make sure you're using the encore executable or call Encore.configureRuntimeEnvironment() first if you're purposely not calling Encore directly.`);
590+
}
591+
592+
// Either a safe method has been called or the webpackConfig
593+
// object is already available. In this case act as a passthrough.
594+
return (...parameters) => {
595+
try {
596+
const res = target[prop](...parameters);
597+
return (res === target) ? publicApiProxy : res;
598+
} catch (error) {
599+
// prettifies errors thrown by our library
600+
const pe = new PrettyError();
601+
602+
console.log(pe.render(error));
603+
process.exit(1); // eslint-disable-line
604+
}
605+
};
606+
}
607+
608+
return target[prop];
609+
}
610+
});
611+
612+
module.exports = publicApiProxy;

test/index.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
'use strict';
1111

1212
const expect = require('chai').expect;
13-
require('../lib/context').runtimeConfig = {};
1413
const api = require('../index');
1514

1615
describe('Public API', () => {
16+
beforeEach(() => {
17+
api.configureRuntimeEnvironment('dev');
18+
});
1719

1820
describe('setOutputPath', () => {
1921

@@ -230,4 +232,27 @@ describe('Public API', () => {
230232
});
231233

232234
});
235+
236+
describe('configureRuntimeEnvironment', () => {
237+
238+
it('should return the API object', () => {
239+
const returnedValue = api.configureRuntimeEnvironment('dev');
240+
expect(returnedValue).to.equal(api);
241+
});
242+
243+
});
244+
245+
describe('Runtime environment proxy', () => {
246+
beforeEach(() => {
247+
api.clearRuntimeEnvironment();
248+
});
249+
250+
it('safe methods should be callable even if the runtime environment has not been configured', () => {
251+
expect(() => api.clearRuntimeEnvironment()).to.not.throw();
252+
});
253+
254+
it('unsafe methods should NOT be callable if the runtime environment has not been configured', () => {
255+
expect(() => api.setOutputPath('/')).to.throw('Encore.setOutputPath() cannot be called yet');
256+
});
257+
});
233258
});

0 commit comments

Comments
 (0)