Skip to content

Commit

Permalink
refactor: override
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Jun 24, 2020
1 parent 748798b commit 46bd532
Show file tree
Hide file tree
Showing 12 changed files with 676 additions and 128 deletions.
76 changes: 59 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# expose-loader

The `expose-loader` loader allow you to expose a module (in whole or in part) to `global` scope (`self`, `window` and `global`).
The `expose-loader` loader allows to expose a module (in whole or in part) to global object (`self`, `window` and `global`).

For further hints on compatibility issues, check out [Shimming](https://webpack.js.org/guides/shimming/) of the official docs.

Expand All @@ -30,33 +30,37 @@ Then you can use the `expose-loader` using two approaches.

## Inline

The `|` or `%20` (space) separate command parts.

> `%20` is space in a query string, because you can't use spaces in URLs
**src/index.js**

```js
import $ from 'expose-loader?exposes[]=$&exposes[]=jQuery!jquery';
//
// Adds the `jquery` to the `global` object under the names `$` and `jQuery`
// Adds the `jquery` to the global object under the names `$` and `jQuery`
```

**src/index.js**

```js
import { concat } from 'expose-loader?exposes=_.concat!lodash/concat';
//
// Adds the `lodash/concat` to the `global` object under the name `_.concat`
// Adds the `lodash/concat` to the global object under the name `_.concat`
```

**src/index.js**

```js
import {
map,
reduce,
} from 'expose-loader?exposes[]=_.map|map&exposes[]=_.reduce|reduce!underscore';
//
// Adds the `map` and `reduce` method from `underscore` to the `global` object under the name `_.map` and `_.reduce`
// Adds the `map` and `reduce` method from `underscore` to the global object under the name `_.map` and `_.reduce`
```

The `|` or `%20` (space) allow to separate the export name of the module and the name in the global object.

> `%20` is space in a query string, because you can't use spaces in URLs
Description of string values can be found in the documentation below.

## Using Configuration
Expand Down Expand Up @@ -125,12 +129,13 @@ List of exposes.

Allows to use a string to describe an expose.

String syntax - `[[globalName] [moduleLocalName]]` or `[[globalName]|[moduleLocalName]]`, where:
String syntax - `[[globalName] [moduleLocalName] [override]]` or `[[globalName]|[moduleLocalName]|[override]]`, where:

- `globalName` - the name under which the value will be available in the global scope, for example `windows.$` for a browser environment (**required**)
- `moduleLocalName` - the name of method or variable (module should export it) (**may be omitted**)
- `globalName` - the name in the global object, for example `window.$` for a browser environment (**required**)
- `moduleLocalName` - the name of method/variable/etc of the module (the module must export it) (**may be omitted**)
- `override` - allows to override existing value in the global object (**may be omitted**)

If no `moduleLocalName` is specified, it exposes the entire module to global scope, otherwise it exposes only the `moduleLocalName` value.
If `moduleLocalName` is not specified, it exposes the entire module to the global object, otherwise it exposes only the value of `moduleLocalName`.

**src/index.js**

Expand Down Expand Up @@ -166,7 +171,7 @@ Allows to use an object to describe an expose.
Type: `String|Array<String>`
Default: `undefined`

Name of an exposed value in `global` scope (**required**).
The name in the global object. (**required**).

**src/index.js**

Expand Down Expand Up @@ -201,9 +206,8 @@ module.exports = {
Type: `String`
Default: `undefined`

Name of method or variable (module should export it).

If the `moduleLocalName` option is specified, it exposes only the `moduleLocalName` value.
The name of method/variable/etc of the module (the module must export it).
If `moduleLocalName` is specified, it exposes only the value of `moduleLocalName`.

**src/index.js**

Expand Down Expand Up @@ -232,6 +236,44 @@ module.exports = {
};
```

##### `override`

Type: `Boolean`
Default: `false`

By default loader does not override the existing value in the global object, because it is unsafe.
In `development` mode, we throw an error if the value already present in the global object.
But you can configure loader to override the existing value in the global object using this option.

To force override the value that is already present in the global object you can set the `override` option to the `true` value.

**src/index.js**

```js
import $ from 'jquery';
```

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: require.resolve('jquery'),
loader: 'expose-loader',
options: {
exposes: {
globalName: '$',
override: true,
},
},
},
],
},
};
```

#### `Array`

**src/index.js**
Expand Down Expand Up @@ -268,7 +310,7 @@ module.exports = {
};
```

It will expose **only** `map`, `filter` and `find` (under `myNameForFind` name) methods in global scope.
It will expose **only** `map`, `filter` and `find` (under `myNameForFind` name) methods to the global object.

In a browser these methods will be available under `windows._.map(..args)`, `windows._.filter(...args)` and `windows._.myNameForFind(...args)` methods.

Expand Down
18 changes: 15 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ export default function loader() {
this,
require.resolve('./runtime/getGlobalThis.js')
)});\n`;
code += `var ___EXPOSE_LOADER_GLOBAL_THIS___ = ___EXPOSE_LOADER_GET_GLOBAL_THIS___();\n`;
code += `var ___EXPOSE_LOADER_GLOBAL_THIS___ = ___EXPOSE_LOADER_GET_GLOBAL_THIS___;\n`;

for (const expose of exposes) {
const { globalName, moduleLocalName } = expose;
const { globalName, moduleLocalName, override } = expose;
const globalNameInterpolated = globalName.map((item) =>
interpolateName(this, item, {})
);
Expand All @@ -72,16 +72,28 @@ export default function loader() {

for (let i = 0; i < globalName.length; i++) {
if (i > 0) {
code += `if (!${propertyString}) ${propertyString} = {};\n`;
code += `if (typeof ${propertyString} === 'undefined') ${propertyString} = {};\n`;
}

propertyString += `[${JSON.stringify(globalNameInterpolated[i])}]`;
}

if (!override) {
code += `if (typeof ${propertyString} === 'undefined') `;
}

code +=
typeof moduleLocalName !== 'undefined'
? `${propertyString} = ___EXPOSE_LOADER_IMPORT_MODULE_LOCAL_NAME___;\n`
: `${propertyString} = ___EXPOSE_LOADER_IMPORT___;\n`;

if (!override) {
if (this.mode === 'development') {
code += `else throw new Error('[exposes-loader] The "${globalName.join(

This comment has been minimized.

Copy link
@ogonkov

ogonkov Feb 9, 2021

Typo

-        code += `else throw new Error('[exposes-loader] The "${globalName.join(
+        code += `else throw new Error('[expose-loader] The "${globalName.join(

This comment has been minimized.

Copy link
@alexander-akait

alexander-akait Feb 9, 2021

Member

PR welcome

'.'
)}" value exists in the global scope, it may not be safe to overwrite it, use the "override" option')\n`;
}
}
}

code += `module.exports = ___EXPOSE_LOADER_IMPORT___;\n`;
Expand Down
3 changes: 3 additions & 0 deletions src/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
"moduleLocalName": {
"type": "string",
"minLength": 1
},
"override": {
"type": "boolean"
}
},
"required": ["globalName"]
Expand Down
36 changes: 24 additions & 12 deletions src/runtime/getGlobalThis.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
module.exports = function getGlobalThis() {
if (typeof globalThis !== 'undefined') {
// eslint-disable-next-line func-names
module.exports = (function () {
if (typeof globalThis === 'object') {
return globalThis;
}

if (typeof self !== 'undefined') {
return self;
}
let g;

if (typeof window !== 'undefined') {
return window;
}
try {
// This works if eval is allowed (see CSP)
// eslint-disable-next-line no-new-func
g = this || new Function('return this')();
} catch (e) {
// This works if the window reference is available
if (typeof window === 'object') {
return window;
}

// This works if the self reference is available
if (typeof self === 'object') {
return self;
}

if (typeof global !== 'undefined') {
return global;
// This works if the global reference is available
if (typeof global !== 'undefined') {
return global;
}
}

throw new Error('unable to locate global object');
};
return g;
})();
22 changes: 21 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,39 @@ function splitCommand(command) {
return result;
}

function parseBoolean(string, defaultValue = null) {
if (typeof string === 'undefined') {
return defaultValue;
}

switch (string.toLowerCase()) {
case 'true':
return true;
case 'false':
return false;
default:
return defaultValue;
}
}

function resolveExposes(item) {
let result;

if (typeof item === 'string') {
const splittedItem = splitCommand(item.trim());

if (splittedItem.length > 2) {
if (splittedItem.length > 3) {
throw new Error(`Invalid "${item}" for exposes`);
}

result = {
globalName: splittedItem[0],
moduleLocalName: splittedItem[1],
override:
typeof splittedItem[2] !== 'undefined'
? parseBoolean(splittedItem[2], false)
: // eslint-disable-next-line no-undefined
undefined,
};
} else {
result = item;
Expand Down
Loading

0 comments on commit 46bd532

Please sign in to comment.