Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ... syntax to extend default tag and attributes #317

Merged
merged 1 commit into from Aug 25, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 14 additions & 47 deletions README.md
Expand Up @@ -75,11 +75,17 @@ Supported tags and attributes:
- the `href` attribute of the `link` tag (only for stylesheets)
- the `data` attribute of the `object` tag
- the `src` attribute of the `script` tag
- the `href` attribute of the `script` tag
- the `xlink:href` attribute of the `script` tag
- the `src` attribute of the `source` tag
- the `srcset` attribute of the `source` tag
- the `src` attribute of the `track` tag
- the `poster` attribute of the `video` tag
- the `src` attribute of the `video` tag
- the `xlink:href` attribute of the `image` tag
- the `href` attribute of the `image` tag
- the `xlink:href` attribute of the `use` tag
- the `href` attribute of the `use` tag

#### `Boolean`

Expand Down Expand Up @@ -107,6 +113,7 @@ module.exports = {
#### `Object`

Allows you to specify which tags and attributes to process, filter them, filter urls and process sources starts with `/`.

For example:

**webpack.config.js**
Expand All @@ -121,16 +128,8 @@ module.exports = {
options: {
attributes: {
list: [
{
tag: 'img',
attribute: 'src',
type: 'src',
},
{
tag: 'img',
attribute: 'srcset',
type: 'srcset',
},
// All default supported tags and attributes
'...',
{
tag: 'img',
attribute: 'data-src',
Expand All @@ -141,26 +140,6 @@ module.exports = {
attribute: 'data-srcset',
type: 'srcset',
},
{
tag: 'link',
attribute: 'href',
type: 'src',
filter: (tag, attribute, attributes) => {
if (!/stylesheet/i.test(attributes.rel)) {
return false;
}

if (
attributes.type &&
attributes.type.trim().toLowerCase() !== 'text/css'
) {
return false;
}

return true;
},
},
// More attributes
],
urlFilter: (attribute, value, resourcePath) => {
// The `attribute` argument contains a name of the HTML attribute.
Expand All @@ -185,10 +164,12 @@ module.exports = {
#### `list`

Type: `Array`
Default: https://github.com/webpack-contrib/html-loader#attributes
Default: [supported tags and attributes](#attributes).

Allows to setup which tags and attributes to process and how, and the ability to filter some of them.

Using `...` syntax allows you to extend [default supported tags and attributes](#attributes).

For example:

**webpack.config.js**
Expand All @@ -203,22 +184,8 @@ module.exports = {
options: {
attributes: {
list: [
{
// Tag name
tag: 'img',
// Attribute name
attribute: 'src',
// Type of processing, can be `src` or `scrset`
type: 'src',
},
{
// Tag name
tag: 'img',
// Attribute name
attribute: 'srcset',
// Type of processing, can be `src` or `scrset`
type: 'srcset',
},
// All default supported tags and attributes
'...',
{
tag: 'img',
attribute: 'data-src',
Expand Down
41 changes: 24 additions & 17 deletions src/options.json
Expand Up @@ -2,25 +2,32 @@
"type": "object",
"definitions": {
"Attribute": {
"type": "object",
"properties": {
"tag": {
"type": "string",
"minLength": 1
},
"attribute": {
"type": "string",
"minLength": 1
},
"type": {
"enum": ["src", "srcset"]
"anyOf": [
{
"type": "object",
"properties": {
"tag": {
"type": "string",
"minLength": 1
},
"attribute": {
"type": "string",
"minLength": 1
},
"type": {
"enum": ["src", "srcset"]
},
"filter": {
"instanceof": "Function"
}
},
"required": ["attribute", "type"],
"additionalProperties": false
},
"filter": {
"instanceof": "Function"
{
"enum": ["..."]
}
},
"required": ["attribute", "type"],
"additionalProperties": false
]
},
"AttributeList": {
"type": "array",
Expand Down
38 changes: 37 additions & 1 deletion src/utils.js
Expand Up @@ -562,6 +562,33 @@ const defaultAttributes = [
},
];

function smartMergeSources(array, factory) {
if (typeof array === 'undefined') {
return factory();
}

const newArray = [];

for (let i = 0; i < array.length; i++) {
const item = array[i];

if (item === '...') {
const items = factory();

if (typeof items !== 'undefined') {
// eslint-disable-next-line no-shadow
for (const item of items) {
newArray.push(item);
}
}
} else if (typeof newArray !== 'undefined') {
newArray.push(item);
}
}

return newArray;
}

function getAttributesOption(rawOptions) {
if (typeof rawOptions.attributes === 'undefined') {
return { list: defaultAttributes };
Expand All @@ -571,7 +598,16 @@ function getAttributesOption(rawOptions) {
return rawOptions.attributes === true ? { list: defaultAttributes } : false;
}

return { ...{ list: defaultAttributes }, ...rawOptions.attributes };
const sources = smartMergeSources(
rawOptions.attributes.list,
() => defaultAttributes
);

return {
list: sources,
urlFilter: rawOptions.attributes.urlFilter,
root: rawOptions.attributes.root,
};
}

export function normalizeOptions(rawOptions, loaderContext) {
Expand Down
411 changes: 411 additions & 0 deletions test/__snapshots__/attributes-option.test.js.snap

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions test/attributes-option.test.js
Expand Up @@ -35,6 +35,29 @@ describe("'attributes' option", () => {
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('should work with "..." syntax', async () => {
const compiler = getCompiler('simple.js', {
attributes: {
list: [
'...',
{
tag: 'flag-icon',
attribute: 'src',
type: 'src',
},
],
},
});
const stats = await compile(compiler);

expect(getModuleSource('./simple.html', stats)).toMatchSnapshot('module');
expect(
execute(readAsset('main.bundle.js', compiler, stats))
).toMatchSnapshot('result');
expect(getWarnings(stats)).toMatchSnapshot('warnings');
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it.skip('should handle the "include" tags', async () => {
const compiler = getCompiler('include.js', {
attributes: {
Expand Down
10 changes: 10 additions & 0 deletions test/validate-options.test.js
Expand Up @@ -47,6 +47,16 @@ describe('validate options', () => {
},
],
},
{
list: [
'...',
{
tag: 'img',
attribute: 'srcset',
type: 'srcset',
},
],
},
{ urlFilter: () => true },
{ root: '.' },
{
Expand Down