1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | import File = require('vinyl');
|
16 | import {ResolvedUrl} from 'polymer-analyzer';
|
17 | import {Bundler, Options, BundleManifest, generateShellMergeStrategy} from 'polymer-bundler';
|
18 | import {ProjectConfig} from 'polymer-project-config';
|
19 |
|
20 | import {BuildAnalyzer} from './analyzer';
|
21 | import {FileMapUrlLoader} from './file-map-url-loader';
|
22 | import {pathFromUrl, urlFromPath, LocalFsPath} from './path-transformers';
|
23 | import {AsyncTransformStream} from './streams';
|
24 |
|
25 | export {Options} from 'polymer-bundler';
|
26 |
|
27 | export class BuildBundler extends AsyncTransformStream<File, File> {
|
28 | config: ProjectConfig;
|
29 |
|
30 | private _buildAnalyzer: BuildAnalyzer;
|
31 | private _bundler: Bundler;
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 | files = new Map<ResolvedUrl, File>();
|
38 |
|
39 | constructor(
|
40 | config: ProjectConfig, buildAnalyzer: BuildAnalyzer,
|
41 | options: Options = {}) {
|
42 | super({objectMode: true});
|
43 |
|
44 | this.config = config;
|
45 |
|
46 | this._buildAnalyzer = buildAnalyzer;
|
47 |
|
48 | const bundlerOptions = {...options};
|
49 |
|
50 | const urlLoader = new FileMapUrlLoader(
|
51 | this.files, bundlerOptions.analyzer || buildAnalyzer.analyzer);
|
52 | bundlerOptions.analyzer =
|
53 | (bundlerOptions.analyzer || buildAnalyzer.analyzer)._fork({urlLoader});
|
54 |
|
55 | if (bundlerOptions.strategy === undefined &&
|
56 | this.config.shell !== undefined) {
|
57 | bundlerOptions.strategy = generateShellMergeStrategy(
|
58 | bundlerOptions.analyzer.resolveUrl(urlFromPath(
|
59 | this.config.root as LocalFsPath,
|
60 | this.config.shell as LocalFsPath))!);
|
61 | }
|
62 |
|
63 | this._bundler = new Bundler(bundlerOptions);
|
64 | }
|
65 |
|
66 | protected async *
|
67 | _transformIter(files: AsyncIterable<File>): AsyncIterable<File> {
|
68 | for await (const file of files) {
|
69 | this._mapFile(file);
|
70 | }
|
71 | await this._buildBundles();
|
72 | for (const file of this.files.values()) {
|
73 | yield file;
|
74 | }
|
75 | }
|
76 |
|
77 | private async _buildBundles() {
|
78 |
|
79 |
|
80 | await this._bundler.analyzer.filesChanged(
|
81 | this._getFilesChangedSinceInitialAnalysis());
|
82 |
|
83 | const {documents, manifest} =
|
84 | await this._bundler.bundle(await this._generateBundleManifest());
|
85 |
|
86 | this._unmapBundledFiles(manifest);
|
87 |
|
88 |
|
89 | for (const [url, document] of documents) {
|
90 | this._mapFile(new File({
|
91 | path: pathFromUrl(
|
92 | this.config.root as LocalFsPath,
|
93 | this._bundler.analyzer.urlResolver.relative(url)),
|
94 | contents: Buffer.from(document.content),
|
95 | }));
|
96 | }
|
97 | }
|
98 |
|
99 | private async _generateBundleManifest(): Promise<BundleManifest> {
|
100 | const entrypoints = this.config.allFragments.map(
|
101 | (e) => this._bundler.analyzer.resolveUrl(
|
102 | urlFromPath(this.config.root as LocalFsPath, e as LocalFsPath))!);
|
103 | return this._bundler.generateManifest(entrypoints);
|
104 | }
|
105 |
|
106 | private _getFilesChangedSinceInitialAnalysis(): string[] {
|
107 | const filesChanged = [];
|
108 | for (const [url, originalFile] of this._buildAnalyzer.files) {
|
109 | const downstreamFile =
|
110 | this.files.get(this._buildAnalyzer.analyzer.resolveUrl(url)!);
|
111 | if (downstreamFile == null) {
|
112 | throw new Error(
|
113 | `Internal error: could not find downstreamFile at ${url}`);
|
114 | }
|
115 | if (downstreamFile.contents!.toString() !==
|
116 | originalFile.contents!.toString()) {
|
117 | filesChanged.push(url);
|
118 | }
|
119 | }
|
120 | return filesChanged;
|
121 | }
|
122 |
|
123 | private _mapFile(file: File) {
|
124 | this.files.set(
|
125 | this._buildAnalyzer.analyzer.resolveUrl(urlFromPath(
|
126 | this.config.root as LocalFsPath, file.path as LocalFsPath))!,
|
127 | file);
|
128 | }
|
129 |
|
130 | |
131 |
|
132 |
|
133 | private _unmapBundledFiles(manifest: BundleManifest) {
|
134 | for (const {
|
135 | files,
|
136 | inlinedHtmlImports,
|
137 | inlinedScripts,
|
138 | inlinedStyles,
|
139 | } of manifest.bundles.values()) {
|
140 | for (const url
|
141 | of [...files,
|
142 | ...inlinedHtmlImports,
|
143 | ...inlinedScripts,
|
144 | ...inlinedStyles]) {
|
145 |
|
146 | if (!manifest.bundles.has(url)) {
|
147 | this.files.delete(url);
|
148 | }
|
149 | }
|
150 | }
|
151 | }
|
152 | }
|