UNPKG

7.73 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6const broccoli_plugin_1 = __importDefault(require("broccoli-plugin"));
7const path_1 = require("path");
8const walk_sync_1 = __importDefault(require("walk-sync"));
9const fs_extra_1 = require("fs-extra");
10const fs_tree_diff_1 = __importDefault(require("fs-tree-diff"));
11const symlink_or_copy_1 = __importDefault(require("symlink-or-copy"));
12const uniqBy_1 = __importDefault(require("lodash/uniqBy"));
13const source_map_url_1 = require("./source-map-url");
14class Append extends broccoli_plugin_1.default {
15 constructor(upstreamTree, appendedTree, options) {
16 super([upstreamTree, appendedTree], {
17 annotation: 'ember-auto-import-analyzer',
18 persistentOutput: true
19 });
20 this.previousUpstreamTree = new fs_tree_diff_1.default();
21 this.previousAppendedTree = new fs_tree_diff_1.default();
22 // mappings maps entry points to maps that map file types to output files.
23 // reverseMappings maps output files back to entry points.
24 let reverseMappings = new Map();
25 for (let [key, map] of options.mappings.entries()) {
26 for (let value of map.values()) {
27 reverseMappings.set(value, key);
28 }
29 }
30 this.mappings = options.mappings;
31 this.reverseMappings = reverseMappings;
32 this.passthrough = options.passthrough;
33 }
34 get upstreamDir() {
35 return this.inputPaths[0];
36 }
37 get appendedDir() {
38 return this.inputPaths[1];
39 }
40 // returns the set of output files that should change based on changes to the
41 // appendedTree.
42 diffAppendedTree() {
43 let changed = new Set();
44 let { patchset, passthroughEntries } = this.appendedPatchset();
45 for (let [, relativePath] of patchset) {
46 let match = findByPrefix(relativePath, this.mappings);
47 if (match) {
48 let ext = path_1.extname(relativePath).slice(1);
49 if (match.mapsTo.has(ext)) {
50 changed.add(match.mapsTo.get(ext));
51 }
52 }
53 }
54 return { needsUpdate: changed, passthroughEntries };
55 }
56 build() {
57 // First note which output files should change due to changes in the
58 // appendedTree
59 let { needsUpdate, passthroughEntries } = this.diffAppendedTree();
60 // Then process all changes in the upstreamTree
61 for (let [operation, relativePath, entry] of this.upstreamPatchset(passthroughEntries)) {
62 let outputPath = path_1.join(this.outputPath, relativePath);
63 switch (operation) {
64 case 'unlink':
65 fs_extra_1.unlinkSync(outputPath);
66 break;
67 case 'rmdir':
68 fs_extra_1.rmdirSync(outputPath);
69 break;
70 case 'mkdir':
71 fs_extra_1.mkdirSync(outputPath);
72 break;
73 case 'change':
74 fs_extra_1.removeSync(outputPath);
75 // deliberate fallthrough
76 case 'create':
77 if (this.reverseMappings.has(relativePath)) {
78 // this is where we see the upstream original file being created or
79 // modified. We should always generate the complete appended file here.
80 this.handleAppend(relativePath);
81 // it no longer needs update once we've handled it here
82 needsUpdate.delete(relativePath);
83 }
84 else {
85 if (isPassthrough(entry)) {
86 symlink_or_copy_1.default.sync(path_1.join(this.appendedDir, entry.originalRelativePath), outputPath);
87 }
88 else {
89 symlink_or_copy_1.default.sync(path_1.join(this.upstreamDir, relativePath), outputPath);
90 }
91 }
92 }
93 }
94 // finally, any remaining things in `needsUpdate` are cases where the
95 // appendedTree changed but the corresponding file in the upstreamTree
96 // didn't. Those needs to get handled here.
97 for (let relativePath of needsUpdate.values()) {
98 this.handleAppend(relativePath);
99 }
100 }
101 upstreamPatchset(passthroughEntries) {
102 let input = walk_sync_1.default.entries(this.upstreamDir).concat(passthroughEntries);
103 // FSTree requires the entries to be sorted and uniq
104 input.sort(compareByRelativePath);
105 input = uniqBy_1.default(input, e => e.relativePath);
106 let previous = this.previousUpstreamTree;
107 let next = (this.previousUpstreamTree = fs_tree_diff_1.default.fromEntries(input));
108 return previous.calculatePatch(next);
109 }
110 appendedPatchset() {
111 let input = walk_sync_1.default.entries(this.appendedDir);
112 let passthroughEntries = input
113 .map(e => {
114 let match = findByPrefix(e.relativePath, this.passthrough);
115 if (match) {
116 let o = Object.create(e);
117 o.relativePath = e.relativePath.replace(new RegExp('^' + match.prefix), match.mapsTo);
118 o.isPassthrough = true;
119 o.originalRelativePath = e.relativePath;
120 return o;
121 }
122 }).filter(e => e && e.relativePath !== './');
123 let previous = this.previousAppendedTree;
124 let next = (this.previousAppendedTree = fs_tree_diff_1.default.fromEntries(input));
125 return { patchset: previous.calculatePatch(next), passthroughEntries };
126 }
127 handleAppend(relativePath) {
128 let upstreamPath = path_1.join(this.upstreamDir, relativePath);
129 let outputPath = path_1.join(this.outputPath, relativePath);
130 let ext = path_1.extname(relativePath);
131 if (!fs_extra_1.existsSync(upstreamPath)) {
132 fs_extra_1.removeSync(outputPath);
133 return;
134 }
135 let sourceDir = path_1.join(this.appendedDir, this.reverseMappings.get(relativePath));
136 if (!fs_extra_1.existsSync(sourceDir)) {
137 symlink_or_copy_1.default.sync(upstreamPath, outputPath);
138 return;
139 }
140 let appendedContent = fs_extra_1.readdirSync(sourceDir).map(name => {
141 if (name.endsWith(ext)) {
142 return fs_extra_1.readFileSync(path_1.join(sourceDir, name), 'utf8');
143 }
144 }).filter(Boolean).join(";\n");
145 let upstreamContent = fs_extra_1.readFileSync(upstreamPath, 'utf8');
146 if (appendedContent.length > 0) {
147 upstreamContent = source_map_url_1.insertBefore(upstreamContent, ";\n" + appendedContent);
148 }
149 fs_extra_1.writeFileSync(outputPath, upstreamContent, 'utf8');
150 }
151}
152exports.default = Append;
153function compareByRelativePath(entryA, entryB) {
154 let pathA = entryA.relativePath;
155 let pathB = entryB.relativePath;
156 if (pathA < pathB) {
157 return -1;
158 }
159 else if (pathA > pathB) {
160 return 1;
161 }
162 return 0;
163}
164function isPassthrough(entry) {
165 return entry.isPassthrough;
166}
167function findByPrefix(path, map) {
168 let parts = path.split('/');
169 for (let i = 1; i < parts.length; i++) {
170 let candidate = parts.slice(0, i).join('/');
171 if (map.has(candidate)) {
172 return {
173 prefix: candidate,
174 mapsTo: map.get(candidate)
175 };
176 }
177 }
178}
179//# sourceMappingURL=broccoli-append.js.map
\No newline at end of file