Skip to content

Commit 2ed7db0

Browse files
feat: added support msapplication-task in meta.content (#356)
1 parent cc556fe commit 2ed7db0

File tree

10 files changed

+444
-287
lines changed

10 files changed

+444
-287
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Supported tags and attributes:
8888
- the `href` attribute of the `link` tag when the `rel` attribute contains `stylesheet`, `icon`, `shortcut icon`, `mask-icon`, `apple-touch-icon`, `apple-touch-icon-precomposed`, `apple-touch-startup-image`, `manifest`, `prefetch`, `preload` or when the `itemprop` attribute is `image`, `logo`, `screenshot`, `thumbnailurl`, `contenturl`, `downloadurl`, `duringmedia`, `embedurl`, `installurl`, `layoutimage`
8989
- the `imagesrcset` attribute of the `link` tag when the `rel` attribute contains `stylesheet`, `icon`, `shortcut icon`, `mask-icon`, `apple-touch-icon`, `apple-touch-icon-precomposed`, `apple-touch-startup-image`, `manifest`, `prefetch`, `preload`
9090
- the `content` attribute of the `meta` tag when the `name` attribute is `msapplication-tileimage`, `msapplication-square70x70logo`, `msapplication-square150x150logo`, `msapplication-wide310x150logo`, `msapplication-square310x310logo`, `msapplication-config`, `twitter:image` or when the `property` attribute is `og:image`, `og:image:url`, `og:image:secure_url`, `og:audio`, `og:audio:secure_url`, `og:video`, `og:video:secure_url`, `vk:image` or when the `itemprop` attribute is `image`, `logo`, `screenshot`, `thumbnailurl`, `contenturl`, `downloadurl`, `duringmedia`, `embedurl`, `installurl`, `layoutimage`
91+
- the `icon-uri` value component in `content` attribute of the `meta` tag when the `name` attribute is `msapplication-task`
9192

9293
#### `Boolean`
9394

src/plugins/sources-plugin.js

Lines changed: 34 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import SAXParser from 'parse5-sax-parser';
22

3-
import HtmlSourceError from '../HtmlSourceError';
43
import {
54
getFilter,
6-
parseSrc,
7-
parseSrcset,
85
normalizeUrl,
96
requestify,
107
isUrlRequestable,
11-
c0ControlCodesExclude,
128
stringifyRequest,
9+
typeSrc,
10+
typeSrcset,
1311
} from '../utils';
1412

1513
export default (options) =>
@@ -46,7 +44,7 @@ export default (options) =>
4644
const { tagName, attrs, sourceCodeLocation } = node;
4745

4846
attrs.forEach((attribute) => {
49-
const { value, prefix } = attribute;
47+
const { prefix } = attribute;
5048
let { name } = attribute;
5149

5250
name = prefix ? `${prefix}:${name}` : name;
@@ -72,96 +70,49 @@ export default (options) =>
7270
target[target.length - 1] !== '"' &&
7371
target[target.length - 1] !== "'";
7472

73+
const result = [];
74+
7575
// eslint-disable-next-line default-case
7676
switch (type) {
7777
case 'src': {
78-
let source;
79-
80-
try {
81-
source = parseSrc(value);
82-
} catch (error) {
83-
options.errors.push(
84-
new HtmlSourceError(
85-
`Bad value for attribute "${attribute.name}" on element "${tagName}": ${error.message}`,
86-
sourceCodeLocation.attrs[name].startOffset,
87-
sourceCodeLocation.attrs[name].endOffset,
88-
html
89-
)
90-
);
91-
92-
return;
93-
}
94-
95-
source = c0ControlCodesExclude(source);
96-
97-
if (!isUrlRequestable(source.value)) {
98-
return;
99-
}
100-
101-
const startOffset =
102-
sourceCodeLocation.attrs[name].startOffset +
103-
target.indexOf(source.value, name.length);
104-
105-
sources.push({
106-
name,
107-
value: source.value,
108-
unquoted,
109-
startIndex: startOffset,
110-
endIndex: startOffset + source.value.length,
111-
});
112-
78+
typeSrc({ name, attribute, node, target, html, options }).forEach(
79+
(i) => {
80+
result.push(i);
81+
}
82+
);
11383
break;
11484
}
11585

11686
case 'srcset': {
117-
let sourceSet;
118-
119-
try {
120-
sourceSet = parseSrcset(value);
121-
} catch (error) {
122-
options.errors.push(
123-
new HtmlSourceError(
124-
`Bad value for attribute "${attribute.name}" on element "${tagName}": ${error.message}`,
125-
sourceCodeLocation.attrs[name].startOffset,
126-
sourceCodeLocation.attrs[name].endOffset,
127-
html
128-
)
129-
);
130-
131-
return;
132-
}
133-
134-
sourceSet = sourceSet.map((item) => {
135-
return {
136-
source: c0ControlCodesExclude(item.source),
137-
};
87+
typeSrcset({
88+
name,
89+
attribute,
90+
node,
91+
target,
92+
html,
93+
options,
94+
}).forEach((i) => {
95+
result.push(i);
13896
});
97+
break;
98+
}
13999

140-
let searchFrom = name.length;
141-
142-
sourceSet.forEach((sourceItem) => {
143-
const { source } = sourceItem;
144-
145-
if (!isUrlRequestable(source.value)) {
146-
return;
100+
default: {
101+
type({ name, attribute, node, target, html, options }).forEach(
102+
(i) => {
103+
result.push(i);
147104
}
105+
);
106+
}
107+
}
148108

149-
const startOffset =
150-
sourceCodeLocation.attrs[name].startOffset +
151-
target.indexOf(source.value, searchFrom);
152-
153-
searchFrom = target.indexOf(source.value, searchFrom) + 1;
154-
155-
sources.push({
156-
name,
157-
value: source.value,
158-
unquoted,
159-
startIndex: startOffset,
160-
endIndex: startOffset + source.value.length,
161-
});
109+
for (const i of result) {
110+
if (i) {
111+
sources.push({
112+
...i,
113+
name,
114+
unquoted,
162115
});
163-
164-
break;
165116
}
166117
}
167118
});

src/utils.js

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import path from 'path';
22

3+
import HtmlSourceError from './HtmlSourceError';
4+
35
function isASCIIWhitespace(character) {
46
return (
57
// Horizontal tab
@@ -608,6 +610,7 @@ const META = new Map([
608610
'layoutimage',
609611
]),
610612
],
613+
['name', new Set(['msapplication-task'])],
611614
]);
612615

613616
function linkItempropFilter(tag, attribute, attributes) {
@@ -658,6 +661,183 @@ function metaContentFilter(tag, attribute, attributes) {
658661
return false;
659662
}
660663

664+
export function typeSrc({ name, attribute, node, target, html, options }) {
665+
const { tagName, sourceCodeLocation } = node;
666+
const { value } = attribute;
667+
const result = [];
668+
let source;
669+
670+
try {
671+
source = parseSrc(value);
672+
} catch (error) {
673+
options.errors.push(
674+
new HtmlSourceError(
675+
`Bad value for attribute "${attribute.name}" on element "${tagName}": ${error.message}`,
676+
sourceCodeLocation.attrs[name].startOffset,
677+
sourceCodeLocation.attrs[name].endOffset,
678+
html
679+
)
680+
);
681+
682+
return result;
683+
}
684+
685+
source = c0ControlCodesExclude(source);
686+
687+
if (!isUrlRequestable(source.value)) {
688+
return result;
689+
}
690+
691+
const startOffset =
692+
sourceCodeLocation.attrs[name].startOffset +
693+
target.indexOf(source.value, name.length);
694+
695+
result.push({
696+
value: source.value,
697+
startIndex: startOffset,
698+
endIndex: startOffset + source.value.length,
699+
});
700+
701+
return result;
702+
}
703+
704+
export function typeSrcset({ name, attribute, node, target, html, options }) {
705+
const { tagName, sourceCodeLocation } = node;
706+
const { value } = attribute;
707+
const result = [];
708+
let sourceSet;
709+
710+
try {
711+
sourceSet = parseSrcset(value);
712+
} catch (error) {
713+
options.errors.push(
714+
new HtmlSourceError(
715+
`Bad value for attribute "${attribute.name}" on element "${tagName}": ${error.message}`,
716+
sourceCodeLocation.attrs[name].startOffset,
717+
sourceCodeLocation.attrs[name].endOffset,
718+
html
719+
)
720+
);
721+
722+
return result;
723+
}
724+
725+
sourceSet = sourceSet.map((item) => {
726+
return {
727+
source: c0ControlCodesExclude(item.source),
728+
};
729+
});
730+
731+
let searchFrom = name.length;
732+
733+
sourceSet.forEach((sourceItem) => {
734+
const { source } = sourceItem;
735+
736+
if (!isUrlRequestable(source.value)) {
737+
return false;
738+
}
739+
740+
const startOffset =
741+
sourceCodeLocation.attrs[name].startOffset +
742+
target.indexOf(source.value, searchFrom);
743+
744+
searchFrom = target.indexOf(source.value, searchFrom) + 1;
745+
746+
result.push({
747+
value: source.value,
748+
startIndex: startOffset,
749+
endIndex: startOffset + source.value.length,
750+
});
751+
752+
return false;
753+
});
754+
755+
return result;
756+
}
757+
758+
function typeMsapplicationTask({
759+
name,
760+
attribute,
761+
node,
762+
target,
763+
html,
764+
options,
765+
}) {
766+
const { tagName, sourceCodeLocation } = node;
767+
const [content] = typeSrc({ name, attribute, node, target, html, options });
768+
const result = [];
769+
770+
if (!content) {
771+
return result;
772+
}
773+
774+
let startIndex = 0;
775+
let endIndex = 0;
776+
let foundIconUri;
777+
let source;
778+
779+
content.value.split(';').forEach((i) => {
780+
if (foundIconUri) {
781+
return;
782+
}
783+
784+
if (!i.includes('icon-uri')) {
785+
// +1 because of ";"
786+
startIndex += i.length + 1;
787+
return;
788+
}
789+
790+
foundIconUri = true;
791+
792+
const [, aValue] = i.split('=');
793+
794+
try {
795+
source = parseSrc(aValue);
796+
} catch (error) {
797+
options.errors.push(
798+
new HtmlSourceError(
799+
`Bad value for attribute "icon-uri" on element "${tagName}": ${error.message}`,
800+
sourceCodeLocation.attrs[name].startOffset,
801+
sourceCodeLocation.attrs[name].endOffset,
802+
html
803+
)
804+
);
805+
806+
return;
807+
}
808+
809+
// +1 because of "="
810+
startIndex += i.indexOf('=') + source.startIndex + 1;
811+
endIndex = startIndex + source.value.length;
812+
});
813+
814+
if (!source) {
815+
return result;
816+
}
817+
818+
result.push({
819+
...content,
820+
startIndex: content.startIndex + startIndex,
821+
endIndex: content.startIndex + endIndex,
822+
name: 'icon-uri',
823+
value: source.value,
824+
});
825+
826+
return result;
827+
}
828+
829+
function metaContentType({ name, attribute, node, target, html, options }) {
830+
const isMsapplicationTask = node.attrs.filter(
831+
(i) =>
832+
i.name.toLowerCase() === 'name' &&
833+
i.value.toLowerCase() === 'msapplication-task'
834+
);
835+
836+
return isMsapplicationTask.length === 0
837+
? typeSrc({ name, attribute, node, target, html, options })
838+
: typeMsapplicationTask({ name, attribute, node, target, html, options });
839+
}
840+
661841
const defaultAttributes = [
662842
{
663843
tag: 'audio',
@@ -699,7 +879,7 @@ const defaultAttributes = [
699879
{
700880
tag: 'meta',
701881
attribute: 'content',
702-
type: 'src',
882+
type: metaContentType,
703883
filter: metaContentFilter,
704884
},
705885
{

test/__snapshots__/esModule-option.test.js.snap

Lines changed: 33 additions & 33 deletions
Large diffs are not rendered by default.

test/__snapshots__/loader.test.js.snap

Lines changed: 11 additions & 11 deletions
Large diffs are not rendered by default.

test/__snapshots__/minimize-option.test.js.snap

Lines changed: 42 additions & 42 deletions
Large diffs are not rendered by default.

test/__snapshots__/sources-option.test.js.snap

Lines changed: 125 additions & 112 deletions
Large diffs are not rendered by default.

test/fixtures/sources.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,15 @@
276276
<link rel="preload" as="image" imagesrcset="image.png 400w, image.png 800w, image.png 1600w" imagesizes="100vw">
277277
<link rel="preload" as="image" imagesrcset="image.png 400w" imagesizes="100vw">
278278
<link rel="preload" as="image" imagesrcset="" imagesizes="100vw">
279+
280+
<meta content="name=Check Order Status;
281+
action-uri=./orderStatus.aspx?src=IE9;
282+
icon-uri= ./image.png" name="msapplication-task">
283+
284+
<meta content="name=Check Order Status;
285+
icon-uri= ./image.png;
286+
action-uri=./orderStatus.aspx?src=IE9" name="msapplication-task">
287+
288+
<meta content="name=Check Order Status;
289+
action-uri=./orderStatus.aspx?src=IE9;
290+
icon-uri=" name="msapplication-task">

test/helpers/getCompiler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default (fixture, loaderOptions = {}, config = {}) => {
3434
type: 'asset/inline',
3535
},
3636
{
37-
test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2|ogg|pdf|vtt|webp|xml|webmanifest|mp3|mp4)$/i,
37+
test: /\.(png|jpg|gif|svg|ico|eot|ttf|woff|woff2|ogg|pdf|vtt|webp|xml|webmanifest|mp3|mp4)$/i,
3838
resourceQuery: /^(?!.*\?url).*$/,
3939
type: 'asset/resource',
4040
},

0 commit comments

Comments
 (0)