1 | "use strict";
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | return new (P || (P = Promise))(function (resolve, reject) {
|
4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
7 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
8 | });
|
9 | };
|
10 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
11 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
12 | };
|
13 | Object.defineProperty(exports, "__esModule", { value: true });
|
14 | const debug_1 = __importDefault(require("debug"));
|
15 | const util_1 = require("./util");
|
16 | const lodash_1 = require("lodash");
|
17 | const enhanced_resolve_1 = require("enhanced-resolve");
|
18 | const pkg_up_1 = __importDefault(require("pkg-up"));
|
19 | const path_1 = require("path");
|
20 | const debug = debug_1.default('ember-auto-import:splitter');
|
21 | const resolver = enhanced_resolve_1.ResolverFactory.createResolver({
|
22 |
|
23 | fileSystem: new enhanced_resolve_1.CachedInputFileSystem(new enhanced_resolve_1.NodeJsInputFileSystem(), 4000),
|
24 | extensions: ['.js', '.json'],
|
25 | mainFields: ['browser', 'module', 'main']
|
26 | });
|
27 | class Splitter {
|
28 | constructor(options) {
|
29 | this.options = options;
|
30 | this.lastDeps = null;
|
31 | this.packageVersions = new Map();
|
32 | }
|
33 | deps() {
|
34 | return __awaiter(this, void 0, void 0, function* () {
|
35 | if (this.importsChanged()) {
|
36 | this.lastDeps = yield this.computeDeps(this.options.analyzers);
|
37 | debug('output %s', new LazyPrintDeps(this.lastDeps));
|
38 | }
|
39 | return this.lastDeps;
|
40 | });
|
41 | }
|
42 | importsChanged() {
|
43 | let imports = [...this.options.analyzers.keys()].map(analyzer => analyzer.imports);
|
44 | if (!this.lastImports || !util_1.shallowEqual(this.lastImports, imports)) {
|
45 | this.lastImports = imports;
|
46 | return true;
|
47 | }
|
48 | return false;
|
49 | }
|
50 | computeTargets(analyzers) {
|
51 | return __awaiter(this, void 0, void 0, function* () {
|
52 | let specifiers = new Map();
|
53 | let imports = lodash_1.flatten([...analyzers.keys()].map(analyzer => analyzer.imports));
|
54 | yield Promise.all(imports.map((imp) => __awaiter(this, void 0, void 0, function* () {
|
55 | if (imp.specifier[0] === '.' || imp.specifier[0] === '/') {
|
56 |
|
57 |
|
58 | return;
|
59 | }
|
60 | let aliasedSpecifier = imp.package.aliasFor(imp.specifier);
|
61 | let parts = aliasedSpecifier.split('/');
|
62 | let packageName;
|
63 | if (aliasedSpecifier[0] === '@') {
|
64 | packageName = `${parts[0]}/${parts[1]}`;
|
65 | }
|
66 | else {
|
67 | packageName = parts[0];
|
68 | }
|
69 | if (imp.package.excludesDependency(packageName)) {
|
70 |
|
71 | return;
|
72 | }
|
73 | if (!imp.package.hasDependency(packageName) ||
|
74 | imp.package.isEmberAddonDependency(packageName)) {
|
75 | return;
|
76 | }
|
77 | imp.package.assertAllowedDependency(packageName);
|
78 | let entrypoint = yield resolveEntrypoint(aliasedSpecifier, imp.package);
|
79 | let seenAlready = specifiers.get(imp.specifier);
|
80 | if (seenAlready) {
|
81 | yield this.assertSafeVersion(seenAlready, imp, entrypoint);
|
82 | seenAlready.importedBy.push(imp);
|
83 | }
|
84 | else {
|
85 | specifiers.set(imp.specifier, {
|
86 | specifier: imp.specifier,
|
87 | entrypoint,
|
88 | importedBy: [imp]
|
89 | });
|
90 | }
|
91 | })));
|
92 | return specifiers;
|
93 | });
|
94 | }
|
95 | versionOfPackage(entrypoint) {
|
96 | return __awaiter(this, void 0, void 0, function* () {
|
97 | if (this.packageVersions.has(entrypoint)) {
|
98 | return this.packageVersions.get(entrypoint);
|
99 | }
|
100 | let pkgPath = yield pkg_up_1.default(path_1.dirname(entrypoint));
|
101 | let version = null;
|
102 | if (pkgPath) {
|
103 | let pkg = require(pkgPath);
|
104 | version = pkg.version;
|
105 | }
|
106 | this.packageVersions.set(entrypoint, version);
|
107 | return version;
|
108 | });
|
109 | }
|
110 | assertSafeVersion(have, nextImport, entrypoint) {
|
111 | return __awaiter(this, void 0, void 0, function* () {
|
112 | if (have.entrypoint === entrypoint) {
|
113 |
|
114 |
|
115 | return;
|
116 | }
|
117 | let [haveVersion, nextVersion] = yield Promise.all([
|
118 | this.versionOfPackage(have.entrypoint),
|
119 | this.versionOfPackage(entrypoint)
|
120 | ]);
|
121 | if (haveVersion !== nextVersion) {
|
122 | throw new Error(`${nextImport.package.name} and ${have.importedBy[0].package.name} are using different versions of ${have.specifier} (${nextVersion} located at ${entrypoint} vs ${haveVersion} located at ${have.entrypoint})`);
|
123 | }
|
124 | });
|
125 | }
|
126 | computeDeps(analyzers) {
|
127 | return __awaiter(this, void 0, void 0, function* () {
|
128 | let targets = yield this.computeTargets(analyzers);
|
129 | let deps = new Map();
|
130 | this.options.bundles.names.forEach(bundleName => {
|
131 | deps.set(bundleName, { staticImports: [], dynamicImports: [] });
|
132 | });
|
133 | for (let target of targets.values()) {
|
134 | let [dynamicUses, staticUses] = lodash_1.partition(target.importedBy, imp => imp.isDynamic);
|
135 | if (staticUses.length > 0) {
|
136 | let bundleName = this.chooseBundle(staticUses);
|
137 | deps.get(bundleName).staticImports.push(target);
|
138 | }
|
139 | if (dynamicUses.length > 0) {
|
140 | let bundleName = this.chooseBundle(dynamicUses);
|
141 | deps.get(bundleName).dynamicImports.push(target);
|
142 | }
|
143 | }
|
144 | this.sortDependencies(deps);
|
145 | return deps;
|
146 | });
|
147 | }
|
148 | sortDependencies(deps) {
|
149 | for (const bundle of deps.values()) {
|
150 | this.sortBundle(bundle);
|
151 | }
|
152 | }
|
153 | sortBundle(bundle) {
|
154 | for (const imports of lodash_1.values(bundle)) {
|
155 | imports.sort((a, b) => a.specifier.localeCompare(b.specifier));
|
156 | }
|
157 | }
|
158 |
|
159 |
|
160 | chooseBundle(importedBy) {
|
161 | let usedInBundles = {};
|
162 | importedBy.forEach(usage => {
|
163 | usedInBundles[this.bundleForPath(usage)] = true;
|
164 | });
|
165 | return this.options.bundles.names.find(bundle => usedInBundles[bundle]);
|
166 | }
|
167 | bundleForPath(usage) {
|
168 | let bundleName = this.options.bundles.bundleForPath(usage.path);
|
169 | if (this.options.bundles.names.indexOf(bundleName) === -1) {
|
170 | throw new Error(`bundleForPath("${usage.path}") returned ${bundleName}" but the only configured bundle names are ${this.options.bundles.names.join(',')}`);
|
171 | }
|
172 | debug('bundleForPath("%s")=%s', usage.path, bundleName);
|
173 | return bundleName;
|
174 | }
|
175 | }
|
176 | exports.default = Splitter;
|
177 | function resolveEntrypoint(specifier, pkg) {
|
178 | return __awaiter(this, void 0, void 0, function* () {
|
179 | return new Promise((resolvePromise, reject) => {
|
180 |
|
181 | resolver.resolve({}, pkg.root, specifier, {}, (err, path) => {
|
182 | if (err) {
|
183 | reject(err);
|
184 | }
|
185 | else {
|
186 | resolvePromise(path);
|
187 | }
|
188 | });
|
189 | });
|
190 | });
|
191 | }
|
192 | class LazyPrintDeps {
|
193 | constructor(deps) {
|
194 | this.deps = deps;
|
195 | }
|
196 | describeResolvedImport(imp) {
|
197 | return {
|
198 | specifier: imp.specifier,
|
199 | entrypoint: imp.entrypoint,
|
200 | importedBy: imp.importedBy.map(this.describeImport.bind(this))
|
201 | };
|
202 | }
|
203 | describeImport(imp) {
|
204 | return {
|
205 | package: imp.package.name,
|
206 | path: imp.path,
|
207 | isDynamic: imp.isDynamic
|
208 | };
|
209 | }
|
210 | toString() {
|
211 | let output = {};
|
212 | for (let [bundle, { staticImports, dynamicImports }] of this.deps.entries()) {
|
213 | output[bundle] = {
|
214 | static: staticImports.map(this.describeResolvedImport.bind(this)),
|
215 | dynamic: dynamicImports.map(this.describeResolvedImport.bind(this))
|
216 | };
|
217 | }
|
218 | return JSON.stringify(output, null, 2);
|
219 | }
|
220 | }
|
221 |
|
\ | No newline at end of file |