1 | import * as dom5 from 'dom5/lib/index-next';
|
2 | import {predicates as p} from 'dom5/lib/index-next';
|
3 | import * as parse5 from 'parse5';
|
4 | import * as url from 'url';
|
5 |
|
6 | import File = require('vinyl');
|
7 | import {AsyncTransformStream, getFileContents} from './streams';
|
8 |
|
9 | const attrValueMatches = (attrName: string, regex: RegExp) => {
|
10 | return (node: parse5.ASTNode) => {
|
11 | const attrValue = dom5.getAttribute(node, attrName);
|
12 | return attrValue != null && regex.test(attrValue);
|
13 | };
|
14 | };
|
15 |
|
16 | const webcomponentsLoaderRegex = /\bwebcomponents\-(loader|lite|bundle)\.js\b/;
|
17 | const webcomponentsLoaderMatcher = p.AND(
|
18 | p.hasTagName('script'), attrValueMatches('src', webcomponentsLoaderRegex));
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | export class CustomElementsEs5AdapterInjector extends
|
24 | AsyncTransformStream<File, File> {
|
25 | constructor() {
|
26 | super({objectMode: true});
|
27 | }
|
28 |
|
29 | protected async *
|
30 | _transformIter(files: AsyncIterable<File>): AsyncIterable<File> {
|
31 | for await (const file of files) {
|
32 | if (file.contents === null || file.extname !== '.html') {
|
33 | yield file;
|
34 | continue;
|
35 | }
|
36 | const contents = await getFileContents(file);
|
37 | const updatedContents = addCustomElementsEs5Adapter(contents);
|
38 | if (contents === updatedContents) {
|
39 | yield file;
|
40 | } else {
|
41 | const updatedFile = file.clone();
|
42 | updatedFile.contents = Buffer.from(updatedContents, 'utf-8');
|
43 | yield updatedFile;
|
44 | }
|
45 | }
|
46 | }
|
47 | }
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 | export function addCustomElementsEs5Adapter(html: string): string {
|
60 |
|
61 |
|
62 |
|
63 | if (!webcomponentsLoaderRegex.test(html)) {
|
64 | return html;
|
65 | }
|
66 | const parsed = parse5.parse(html, {locationInfo: true});
|
67 | const script = dom5.query(parsed, webcomponentsLoaderMatcher);
|
68 | if (!script) {
|
69 | return html;
|
70 | }
|
71 |
|
72 |
|
73 | const loaderScriptUrl = dom5.getAttribute(script, 'src')!;
|
74 | const adapterScriptUrl =
|
75 | url.resolve(loaderScriptUrl, 'custom-elements-es5-adapter.js');
|
76 | const es5AdapterFragment = parse5.parseFragment(`
|
77 | <script>if (!window.customElements) { document.write('<!--'); }</script>
|
78 | <script type="text/javascript" src="${adapterScriptUrl}"></script>
|
79 | <!--! do not remove -->
|
80 | `);
|
81 |
|
82 | dom5.insertBefore(script.parentNode!, script, es5AdapterFragment);
|
83 | return parse5.serialize(parsed);
|
84 | }
|