Skip to content

Commit

Permalink
Merge pull request #65 from webpack/add/get-options
Browse files Browse the repository at this point in the history
Add/get options
  • Loading branch information
jhnns authored Feb 20, 2017
2 parents 8933f86 + ff86dd6 commit 6f4472a
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 133 deletions.
63 changes: 26 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,46 @@

## Methods

### `getLoaderConfig`
### getOptions

Recommended way to retrieve the loader config:
Recommended way to retrieve the options of a loader invocation:

```javascript
// inside your loader
config = loaderUtils.getLoaderConfig(this, "myLoader");
const options = loaderUtils.getOptions(this);
```

Tries to read the loader config from the `webpack.config.js` under the given property name (`"myLoader"` in this case) and merges the result with the loader query. For example, if your `webpack.config.js` had this property...
**Please note:** The returned `options` object is *read-only*. It may be re-used across multiple invocations.
If you pass it on to another library, make sure to make a *deep copy* of it:

```javascript
cheesecakeLoader: {
type: "delicious",
slices: 4
}
const options = Object.assign({}, loaderUtils.getOptions(this));
// don't forget nested objects or arrays
options.obj = Object.assign({}, options.obj);
options.arr = options.arr.slice();
someLibrary(options);
```

...and your loader was called with `?slices=8`, `getLoaderConfig(this, "cheesecakeLoader")` would return
[assign-deep](https://www.npmjs.com/package/assign-deep) is a good library to make a deep copy of the options.

```javascript
{
type: "delicious",
slices: 8
}
```

It is recommended that you use the camelCased loader name as your default config property name.
#### Options as query strings

### `parseQuery`

``` javascript
var query = loaderUtils.parseQuery(this.query);
assert(typeof query == "object");
if(query.flag)
// ...
```
If the loader options have been passed as loader query string (`loader?some&params`), the string is parsed like this:

``` text
null -> {}
? -> {}
?flag -> { flag: true }
?+flag -> { flag: true }
?-flag -> { flag: false }
?xyz=test -> { xyz: "test" }
?xyz[]=a -> { xyz: ["a"] }
?flag1&flag2 -> { flag1: true, flag2: true }
?+flag1,-flag2 -> { flag1: true, flag2: false }
?xyz[]=a,xyz[]=b -> { xyz: ["a", "b"] }
?a%2C%26b=c%2C%26d -> { "a,&b": "c,&d" }
?{json:5,data:{a:1}} -> { json: 5, data: { a: 1 } }
null -> {}
? -> {}
?flag -> { flag: true }
?+flag -> { flag: true }
?-flag -> { flag: false }
?xyz=test -> { xyz: "test" }
?xyz=1 -> { xyz: "1" }
?xyz[]=a -> { xyz: ["a"] }
?flag1&flag2 -> { flag1: true, flag2: true }
?+flag1,-flag2 -> { flag1: true, flag2: false }
?xyz[]=a,xyz[]=b -> { xyz: ["a", "b"] }
?a%2C%26b=c%2C%26d -> { "a,&b": "c,&d" }
?{data:{a:1},isJSON5:true} -> { data: { a: 1 }, isJSON5: true }
```

### `stringifyRequest`
Expand Down
17 changes: 0 additions & 17 deletions lib/getLoaderConfig.js

This file was deleted.

13 changes: 13 additions & 0 deletions lib/getOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"use strict";

const parseQuery = require("./parseQuery");

function getOptions(loaderContext) {
const query = loaderContext.query;
if(typeof query === "string") {
return parseQuery(loaderContext.query);
}
return query;
}

module.exports = getOptions;
6 changes: 2 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use strict";

const parseQuery = require("./parseQuery");
const getLoaderConfig = require("./getLoaderConfig");
const getOptions = require("./getOptions");
const stringifyRequest = require("./stringifyRequest");
const getRemainingRequest = require("./getRemainingRequest");
const getCurrentRequest = require("./getCurrentRequest");
Expand All @@ -11,8 +10,7 @@ const parseString = require("./parseString");
const getHashDigest = require("./getHashDigest");
const interpolateName = require("./interpolateName");

exports.parseQuery = parseQuery;
exports.getLoaderConfig = getLoaderConfig;
exports.getOptions = getOptions;
exports.stringifyRequest = stringifyRequest;
exports.getRemainingRequest = getRemainingRequest;
exports.getCurrentRequest = getCurrentRequest;
Expand Down
17 changes: 3 additions & 14 deletions lib/parseQuery.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,22 @@
"use strict";

const JSON5 = require("json5");
const util = require("util");
const os = require("os");

const parseQueryDeprecationWarning = util.deprecate(() => {},
"loaderUtils.parseQuery() received a non-string value which can be problematic, " +
"see https://github.com/webpack/loader-utils/issues/56" + os.EOL +
"parseQuery() will be replaced with getOptions() in the next major version of loader-utils."
);
const specialValues = {
"null": null,
"true": true,
"false": false
};

function parseQuery(query) {
if(!query) return {};
if(typeof query !== "string") {
parseQueryDeprecationWarning();
return query;
if(query.substr(0, 1) !== "?") {
throw new Error("A valid query string passed to parseQuery should begin with '?'");
}
if(query.substr(0, 1) !== "?")
throw new Error("a valid query string passed to parseQuery should begin with '?'");
query = query.substr(1);
if(query.substr(0, 1) === "{" && query.substr(-1) === "}") {
return JSON5.parse(query);
}
const queryArgs = query.split(/[,\&]/g);
const queryArgs = query.split(/[,&]/g);
const result = {};
queryArgs.forEach(arg => {
const idx = arg.indexOf("=");
Expand Down
23 changes: 0 additions & 23 deletions test/getLoaderConfig.test.js

This file was deleted.

112 changes: 112 additions & 0 deletions test/getOptions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"use strict";

const assert = require("assert");
const loaderUtils = require("../lib");

describe("getOptions()", () => {
describe("when loaderContext.query is a string", () => {
[{
it: "should parse query params",
query: "?name=cheesecake&slices=8&delicious&warm=false",
expected: {
delicious: true,
name: "cheesecake",
slices: "8", // numbers are still strings with query params
warm: false
}
},
{
it: "should parse query params with arrays",
query: "?ingredients[]=flour&ingredients[]=sugar",
expected: {
ingredients: ["flour", "sugar"]
}
},
{
it: "should parse query params in JSON format",
query: "?" + JSON.stringify({
delicious: true,
name: "cheesecake",
slices: 8,
warm: false
}),
expected: {
delicious: true,
name: "cheesecake",
slices: 8,
warm: false
}
},
{
it: "should use decodeURIComponent",
query: "?%3d",
expected: { "=": true }
},
{
it: "should recognize params starting with + as boolean params with the value true",
query: "?+%3d",
expected: { "=": true }
},
{
it: "should recognize params starting with - as boolean params with the value false",
query: "?-%3d",
expected: { "=": false }
},
{
it: "should not confuse regular equal signs and encoded equal signs",
query: "?%3d=%3D",
expected: { "=": "=" }
}].forEach(test => {
it(test.it, () => {
assert.deepEqual(
loaderUtils.getOptions({
query: test.query
}),
test.expected
);
});
});
describe("and the query string does not start with ?", () => {
it("should throw an error", () => {
assert.throws(
() => loaderUtils.getOptions({ query: "a" }),
"A valid query string passed to parseQuery should begin with '?'"
);
});
});
});
describe("when loaderContext.query is an object", () => {
it("should just return the object", () => {
const query = {};
assert.strictEqual(
loaderUtils.getOptions({
query
}),
query
);
});
});
describe("when loaderContext.query is anything else", () => {
it("should just return it", () => {
const query = [];
assert.strictEqual(
loaderUtils.getOptions({
query
}),
query
);
assert.strictEqual(
loaderUtils.getOptions({
query: undefined
}),
undefined
);
assert.strictEqual(
loaderUtils.getOptions({
query: null
}),
null
);
});
});
});
38 changes: 0 additions & 38 deletions test/parseQuery.test.js

This file was deleted.

0 comments on commit 6f4472a

Please sign in to comment.