Skip to content

Commit 574b5f6

Browse files
authoredJun 17, 2020
refactor: wrapper option (#78)
1 parent c7f8799 commit 574b5f6

7 files changed

+245
-52
lines changed
 

‎README.md

+102-14
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717

1818
The imports loader allows you to use modules that depend on specific global variables.
1919

20-
This is useful for third-party modules that rely on global variables like `$` or `this` being the `window` object. The imports loader can add the necessary `require('whatever')` calls, so those modules work with webpack.
20+
This is useful for third-party modules that rely on global variables like `$` or `this` being the `window` object.
21+
The imports loader can add the necessary `require('whatever')` calls, so those modules work with webpack.
22+
23+
For further hints on compatibility issues, check out [Shimming](https://webpack.js.org/guides/shimming/) of the official docs.
2124

2225
> ⚠ By default loader generate ES module named syntax.
2326
@@ -39,7 +42,7 @@ Given you have this file:
3942
$('img').doSomeAwesomeJqueryPluginStuff();
4043
```
4144

42-
then you can inject the `jquery` value into the module by configuring the `imports-loader` using two approaches.
45+
Then you can inject the `jquery` value into the module by configuring the `imports-loader` using two approaches.
4346

4447
### Inline
4548

@@ -82,7 +85,9 @@ import(
8285
// import angular from "angular";
8386
//
8487
// (function () {
85-
// code from example.js
88+
// ...
89+
// Code
90+
// ...
8691
// }.call(window));
8792
```
8893

@@ -139,10 +144,12 @@ And run `webpack` via your preferred method.
139144

140145
## Options
141146

142-
| Name | Type | Default | Description |
143-
| :-----------------------: | :---------------------------------------: | :---------: | :-------------------------- |
144-
| **[`type`](#type)** | `{String}` | `module` | Format of generated imports |
145-
| **[`imports`](#imports)** | `{String\|Object\|Array<String\|Object>}` | `undefined` | List of imports |
147+
| Name | Type | Default | Description |
148+
| :-------------------------------------: | :---------------------------------------: | :---------: | :--------------------------------------------------------------------- |
149+
| **[`type`](#type)** | `{String}` | `module` | Format of generated imports |
150+
| **[`imports`](#imports)** | `{String\|Object\|Array<String\|Object>}` | `undefined` | List of imports |
151+
| **[`wrapper`](#wrapper)** | `{Boolean\|String\|Object}` | `undefined` | Closes the module code in a function (`(function () { ... }).call();`) |
152+
| **[`additionalCode`](#additionalcode)** | `{String}` | `undefined` | Adds custom code |
146153

147154
### `type`
148155

@@ -443,12 +450,16 @@ import 'lib_4';
443450
// ...
444451
```
445452

446-
### wrapper
453+
### `wrapper`
447454

448-
Type: `String|Array`
455+
Type: `Boolean|String|Object`
449456
Default: `undefined`
450457

451-
Closes the module code in a function with a given `this` (`(function () { ... }).call(window);`).
458+
Closes the module code in a function with a given `thisArg` and `args` (`(function () { ... }).call();`).
459+
460+
> ⚠ Do not use this option if source code contains ES module import(s)
461+
462+
#### `Boolean`
452463

453464
```js
454465
module.exports = {
@@ -464,7 +475,7 @@ module.exports = {
464475
moduleName: 'jquery',
465476
name: '$',
466477
},
467-
wrapper: ['window', 'document'],
478+
wrapper: true,
468479
},
469480
},
470481
],
@@ -483,12 +494,89 @@ import $ from 'jquery';
483494
// ...
484495
// Code
485496
// ...
486-
}.call(window, document));
497+
}.call());
487498
```
488499

489-
> ⚠ Do not use this option if source code contains ES module import(s)
500+
#### `String`
501+
502+
```js
503+
module.exports = {
504+
module: {
505+
rules: [
506+
{
507+
test: require.resolve('example.js'),
508+
use: [
509+
{
510+
loader: 'imports-loader',
511+
options: {
512+
imports: {
513+
moduleName: 'jquery',
514+
name: '$',
515+
},
516+
wrapper: 'window',
517+
},
518+
},
519+
],
520+
},
521+
],
522+
},
523+
};
524+
```
525+
526+
Generate output:
527+
528+
```js
529+
import $ from 'jquery';
530+
531+
(function () {
532+
// ...
533+
// Code
534+
// ...
535+
}.call(window));
536+
```
537+
538+
#### `Object`
539+
540+
```js
541+
module.exports = {
542+
module: {
543+
rules: [
544+
{
545+
test: require.resolve('example.js'),
546+
use: [
547+
{
548+
loader: 'imports-loader',
549+
options: {
550+
imports: {
551+
moduleName: 'jquery',
552+
name: '$',
553+
},
554+
wrapper: {
555+
thisArg: 'window',
556+
args: ['myVariable', 'myOtherVariable'],
557+
},
558+
},
559+
},
560+
],
561+
},
562+
],
563+
},
564+
};
565+
```
566+
567+
Generate output:
568+
569+
```js
570+
import $ from 'jquery';
571+
572+
(function (myVariable, myOtherVariable) {
573+
// ...
574+
// Code
575+
// ...
576+
}.call(window, myVariable, myOtherVariable));
577+
```
490578

491-
### additionalCode
579+
### `additionalCode`
492580

493581
Type: `String`
494582
Default: `undefined`

‎src/index.js

+18-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { SourceNode, SourceMapConsumer } from 'source-map';
7-
import { getOptions, getCurrentRequest } from 'loader-utils';
7+
import { getOptions } from 'loader-utils';
88
import validateOptions from 'schema-utils';
99

1010
import schema from './options.json';
@@ -54,8 +54,22 @@ export default function loader(content, sourceMap) {
5454
let codeAfterModule = '';
5555

5656
if (typeof options.wrapper !== 'undefined') {
57-
importsCode += '\n(function() {';
58-
codeAfterModule += `\n}.call(${options.wrapper.toString()}));\n`;
57+
let thisArg;
58+
let args;
59+
60+
if (typeof options.wrapper === 'boolean') {
61+
thisArg = '';
62+
args = '';
63+
} else if (typeof options.wrapper === 'string') {
64+
thisArg = options.wrapper;
65+
args = '';
66+
} else {
67+
({ thisArg, args } = options.wrapper);
68+
args = args.join(', ');
69+
}
70+
71+
importsCode += `\n(function(${args}) {`;
72+
codeAfterModule += `\n}.call(${thisArg}${args ? `, ${args}` : ''}));\n`;
5973
}
6074

6175
if (this.sourceMap && sourceMap) {
@@ -67,9 +81,7 @@ export default function loader(content, sourceMap) {
6781
node.prepend(`${importsCode}\n`);
6882
node.add(codeAfterModule);
6983

70-
const result = node.toStringWithSourceMap({
71-
file: getCurrentRequest(this),
72-
});
84+
const result = node.toStringWithSourceMap({ file: this.resourcePath });
7385

7486
callback(null, result.code, result.map.toJSON());
7587

‎src/options.json

+20-6
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,31 @@
6666
},
6767
"wrapper": {
6868
"anyOf": [
69+
{
70+
"type": "boolean"
71+
},
6972
{
7073
"type": "string",
7174
"minLength": 1
7275
},
7376
{
74-
"type": "array",
75-
"minItems": 1,
76-
"items": {
77-
"type": "string",
78-
"minLength": 1
79-
}
77+
"type": "object",
78+
"additionalProperties": false,
79+
"properties": {
80+
"thisArg": {
81+
"type": "string",
82+
"minLength": 1
83+
},
84+
"args": {
85+
"type": "array",
86+
"minItems": 1,
87+
"items": {
88+
"type": "string",
89+
"minLength": 1
90+
}
91+
}
92+
},
93+
"required": ["thisArg"]
8094
}
8195
]
8296
},

‎test/__snapshots__/loader.test.js.snap

+25-8
Original file line numberDiff line numberDiff line change
@@ -1103,9 +1103,9 @@ var someCode = {
11031103

11041104
exports[`loader should work with the "additionalCode" option: warnings 1`] = `Array []`;
11051105

1106-
exports[`loader should work with the "wrapper" option: errors 1`] = `Array []`;
1106+
exports[`loader should work with the "wrapper" option as a boolean notation: errors 1`] = `Array []`;
11071107

1108-
exports[`loader should work with the "wrapper" option: module 1`] = `
1108+
exports[`loader should work with the "wrapper" option as a boolean notation: module 1`] = `
11091109
"/*** IMPORTS FROM imports-loader ***/
11101110
11111111
(function() {
@@ -1114,15 +1114,15 @@ var someCode = {
11141114
object: { existingSubProperty: 123 }
11151115
};
11161116
1117-
}.call(window));
1117+
}.call());
11181118
"
11191119
`;
11201120

1121-
exports[`loader should work with the "wrapper" option: warnings 1`] = `Array []`;
1121+
exports[`loader should work with the "wrapper" option as a boolean notation: warnings 1`] = `Array []`;
11221122

1123-
exports[`loader should work with the "wrapper" options and arguments: errors 1`] = `Array []`;
1123+
exports[`loader should work with the "wrapper" option as a string notation: errors 1`] = `Array []`;
11241124

1125-
exports[`loader should work with the "wrapper" options and arguments: module 1`] = `
1125+
exports[`loader should work with the "wrapper" option as a string notation: module 1`] = `
11261126
"/*** IMPORTS FROM imports-loader ***/
11271127
11281128
(function() {
@@ -1131,8 +1131,25 @@ var someCode = {
11311131
object: { existingSubProperty: 123 }
11321132
};
11331133
1134-
}.call(window,document));
1134+
}.call(window));
1135+
"
1136+
`;
1137+
1138+
exports[`loader should work with the "wrapper" option as a string notation: warnings 1`] = `Array []`;
1139+
1140+
exports[`loader should work with the "wrapper" options as an object notation: errors 1`] = `Array []`;
1141+
1142+
exports[`loader should work with the "wrapper" options as an object notation: module 1`] = `
1143+
"/*** IMPORTS FROM imports-loader ***/
1144+
1145+
(function(myGlobalVariable, myOtherGlobalVariable) {
1146+
var someCode = {
1147+
number: 123,
1148+
object: { existingSubProperty: 123 }
1149+
};
1150+
1151+
}.call(window, myGlobalVariable, myOtherGlobalVariable));
11351152
"
11361153
`;
11371154

1138-
exports[`loader should work with the "wrapper" options and arguments: warnings 1`] = `Array []`;
1155+
exports[`loader should work with the "wrapper" options as an object notation: warnings 1`] = `Array []`;

‎test/__snapshots__/validate-options.test.js.snap

+44-13
Original file line numberDiff line numberDiff line change
@@ -229,32 +229,63 @@ exports[`validate options should throw an error on the "unknown" option with "tr
229229
* options misses the property 'additionalCode' | should be any non-object."
230230
`;
231231
232-
exports[`validate options should throw an error on the "wrapper" option with "[""]" value 1`] = `
232+
exports[`validate options should throw an error on the "wrapper" option with "/test/" value 1`] = `
233233
"Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
234-
- options.wrapper[0] should be an non-empty string."
234+
- options.wrapper misses the property 'thisArg'. Should be:
235+
non-empty string"
235236
`;
236237
237-
exports[`validate options should throw an error on the "wrapper" option with "[]" value 1`] = `
238+
exports[`validate options should throw an error on the "wrapper" option with "[""]" value 1`] = `
238239
"Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
239-
- options.wrapper should be an non-empty array."
240+
- options.wrapper should be one of these:
241+
boolean | non-empty string | object { thisArg, args? }
242+
Details:
243+
* options.wrapper should be a boolean.
244+
* options.wrapper should be a non-empty string.
245+
* options.wrapper should be an object:
246+
object { thisArg, args? }"
240247
`;
241248
242-
exports[`validate options should throw an error on the "wrapper" option with "false" value 1`] = `
249+
exports[`validate options should throw an error on the "wrapper" option with "[]" value 1`] = `
243250
"Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
244251
- options.wrapper should be one of these:
245-
non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
252+
boolean | non-empty string | object { thisArg, args? }
246253
Details:
254+
* options.wrapper should be a boolean.
247255
* options.wrapper should be a non-empty string.
248-
* options.wrapper should be an array:
249-
[non-empty string, ...] (should not have fewer than 1 item)"
256+
* options.wrapper should be an object:
257+
object { thisArg, args? }"
258+
`;
259+
260+
exports[`validate options should throw an error on the "wrapper" option with "{"thisArg":"window","args":[1,"bar"]}" value 1`] = `
261+
"Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
262+
- options.wrapper.args[0] should be a non-empty string."
250263
`;
251264
252-
exports[`validate options should throw an error on the "wrapper" option with "true" value 1`] = `
265+
exports[`validate options should throw an error on the "wrapper" option with "{"thisArg":"window","args":true}" value 1`] = `
266+
"Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
267+
- options.wrapper.args should be an array:
268+
[non-empty string, ...] (should not have fewer than 1 item)"
269+
`;
270+
271+
exports[`validate options should throw an error on the "wrapper" option with "{"thisArg":1}" value 1`] = `
272+
"Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
273+
- options.wrapper.thisArg should be a non-empty string."
274+
`;
275+
276+
exports[`validate options should throw an error on the "wrapper" option with "{"unknown":true}" value 1`] = `
253277
"Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
254278
- options.wrapper should be one of these:
255-
non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
279+
boolean | non-empty string | object { thisArg, args? }
256280
Details:
257-
* options.wrapper should be a non-empty string.
258-
* options.wrapper should be an array:
259-
[non-empty string, ...] (should not have fewer than 1 item)"
281+
* options.wrapper has an unknown property 'unknown'. These properties are valid:
282+
object { thisArg, args? }
283+
* options.wrapper misses the property 'thisArg'. Should be:
284+
non-empty string"
285+
`;
286+
287+
exports[`validate options should throw an error on the "wrapper" option with "{}" value 1`] = `
288+
"Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
289+
- options.wrapper misses the property 'thisArg'. Should be:
290+
non-empty string"
260291
`;

‎test/loader.test.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,20 @@ describe('loader', () => {
217217
expect(getWarnings(stats)).toMatchSnapshot('warnings');
218218
});
219219

220-
it('should work with the "wrapper" option', async () => {
220+
it('should work with the "wrapper" option as a boolean notation', async () => {
221+
const compiler = getCompiler('some-library.js', {
222+
wrapper: true,
223+
});
224+
const stats = await compile(compiler);
225+
226+
expect(getModuleSource('./some-library.js', stats)).toMatchSnapshot(
227+
'module'
228+
);
229+
expect(getErrors(stats)).toMatchSnapshot('errors');
230+
expect(getWarnings(stats)).toMatchSnapshot('warnings');
231+
});
232+
233+
it('should work with the "wrapper" option as a string notation', async () => {
221234
const compiler = getCompiler('some-library.js', {
222235
wrapper: 'window',
223236
});
@@ -230,9 +243,12 @@ describe('loader', () => {
230243
expect(getWarnings(stats)).toMatchSnapshot('warnings');
231244
});
232245

233-
it('should work with the "wrapper" options and arguments', async () => {
246+
it('should work with the "wrapper" options as an object notation', async () => {
234247
const compiler = getCompiler('some-library.js', {
235-
wrapper: ['window', 'document'],
248+
wrapper: {
249+
thisArg: 'window',
250+
args: ['myGlobalVariable', 'myOtherGlobalVariable'],
251+
},
236252
});
237253
const stats = await compile(compiler);
238254

‎test/validate-options.test.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,23 @@ describe('validate options', () => {
8484
],
8585
},
8686
wrapper: {
87-
success: ['window', ['window', 'document']],
88-
failure: [false, true, [], ['']],
87+
success: [
88+
true,
89+
false,
90+
'window',
91+
{ thisArg: 'window' },
92+
{ thisArg: 'window', args: ['foo', 'bar'] },
93+
],
94+
failure: [
95+
[],
96+
[''],
97+
/test/,
98+
{},
99+
{ unknown: true },
100+
{ thisArg: 1 },
101+
{ thisArg: 'window', args: true },
102+
{ thisArg: 'window', args: [1, 'bar'] },
103+
],
89104
},
90105
additionalCode: {
91106
success: ['var x = 2;'],

0 commit comments

Comments
 (0)
Please sign in to comment.