1 | const path = require('path');
|
2 | const fs = require('fs');
|
3 |
|
4 | const { getPackageJson, writePackageJson, shell, copyFile, end, getArg } = require('./utils');
|
5 | const { logInfo } = require('./log');
|
6 |
|
7 | const root = process.cwd();
|
8 |
|
9 | function sortDependencies(target, depsName) {
|
10 | if (!target[depsName]) { return; }
|
11 | const deps = target[depsName];
|
12 | target[depsName] = Object.keys(deps)
|
13 | .map(name => ({ name, version: deps[name] }))
|
14 | .sort((a, b) => a.name > b.name ? 1 : a.name < b.name ? -1 : 0)
|
15 | .reduce((newDeps, { name, version }) => ({
|
16 | ...newDeps,
|
17 | [name]: version,
|
18 | }), {});
|
19 | }
|
20 |
|
21 | function sortAllDependencies(targetPackageJson) {
|
22 | sortDependencies(targetPackageJson, 'dependencies');
|
23 | sortDependencies(targetPackageJson, 'devDependencies');
|
24 | sortDependencies(targetPackageJson, 'peerDependencies');
|
25 | }
|
26 |
|
27 | async function createTarballOf(packagePath, packageJson) {
|
28 | const tarballFileName = path.resolve(packagePath, 'package.tgz');
|
29 | const packageJsonFileName = path.resolve(packagePath, 'package.json');
|
30 | if (!fs.existsSync(packageJsonFileName)) { throw new Error(`Unable to find the package json file at this location: "${packagePath}`); }
|
31 |
|
32 |
|
33 |
|
34 | const { name } = packageJson || getPackageJson({ packagePath });
|
35 | logInfo(`Creating tarball of ${name}...`);
|
36 | const result = await shell('npm pack --loglevel error', { cwd: packagePath, stdout: false });
|
37 | const sourceTarballFileName = path.resolve(packagePath, result.stdout.trim());
|
38 | copyFile(sourceTarballFileName, tarballFileName);
|
39 | fs.unlinkSync(sourceTarballFileName);
|
40 | return tarballFileName;
|
41 | }
|
42 |
|
43 | async function loopThroughLinksFrom(packageJson, delegate) {
|
44 | const links = packageJson['links'] || packageJson['link'] || {};
|
45 | for (const name of Object.keys(links)) {
|
46 | const linkPath = links[name];
|
47 | await delegate(name, linkPath);
|
48 | }
|
49 | }
|
50 |
|
51 | async function makeTarballsOfLinks(tarballPackages) {
|
52 | await Promise.all(tarballPackages.map(({ packagePath }) => createTarballOf(packagePath)));
|
53 | }
|
54 |
|
55 | async function getTarballsOfLinks(packagePath, packageJson) {
|
56 | const tarballPackages = [];
|
57 | packageJson = packageJson || getPackageJson({ packagePath });
|
58 | await loopThroughLinksFrom(packageJson, async (name, linkPath) => {
|
59 | const resolvedLinkPath = path.resolve(packagePath, linkPath);
|
60 | if (!fs.existsSync(resolvedLinkPath)) { throw new Error(`Unable to install as the link for ${name} was invalid: ${linkPath} (resolves to: ${resolvedLinkPath})`); }
|
61 | const tarballFileName = path.join(resolvedLinkPath, './package.tgz');
|
62 | tarballPackages.push(...(await getTarballsOfLinks(resolvedLinkPath)));
|
63 | tarballPackages.push({ packagePath: resolvedLinkPath.toLowerCase(), tarballFileName, name, parentPackages: [packagePath.toLowerCase()] });
|
64 | });
|
65 | return tarballPackages;
|
66 | }
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | function removeDuplicatesFromTarballPackages(tarballPackages) {
|
73 | for (let index = tarballPackages.length - 1; index >= 0; index--) {
|
74 | const package = tarballPackages[index];
|
75 | const foundIndex = tarballPackages.findIndex(item => item.packagePath === package.packagePath);
|
76 | if (foundIndex === index) continue;
|
77 | const { parentPackages } = tarballPackages[foundIndex];
|
78 | tarballPackages.splice(index, 1);
|
79 | package.parentPackages.forEach(parentPackage => {
|
80 | if (parentPackages.includes(parentPackage)) return;
|
81 | parentPackages.push(parentPackage);
|
82 | });
|
83 | }
|
84 | }
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | function updatePackagesWithTarballLinks(tarballPackages) {
|
91 | const updates = {};
|
92 | tarballPackages.forEach(({ name, tarballFileName, parentPackages }) => parentPackages.forEach(parentPackage => {
|
93 | updates[parentPackage] = (updates[parentPackage] || []);
|
94 | updates[parentPackage].push({ name, tarballFileName });
|
95 | }));
|
96 | Object.entries(updates).forEach(([packagePath, details]) => {
|
97 | const packageJson = getPackageJson({ packagePath, throwErrorIfNotFound: true });
|
98 | sortAllDependencies(packageJson);
|
99 | details.forEach(({ name, tarballFileName }) => {
|
100 | ['dependencies', 'devDependencies'].forEach(location => {
|
101 | if (packageJson[location][name]) packageJson[location][name] = `file:${tarballFileName}`;
|
102 | });
|
103 | });
|
104 | writePackageJson(packageJson, packagePath);
|
105 | });
|
106 | }
|
107 |
|
108 | async function doInstall() {
|
109 | logInfo('Installing dependencies...');
|
110 | return shell('npm install --loglevel=error --color always');
|
111 | }
|
112 |
|
113 | async function removeLinksFromNodeModules(packagePath, packageJson) {
|
114 | packageJson = packageJson || getPackageJson({ packagePath });
|
115 | await loopThroughLinksFrom(packageJson, async name => {
|
116 | const nodeModulesLinkPath = path.resolve(packagePath, 'node_modules', name);
|
117 | if (!fs.existsSync(nodeModulesLinkPath)) { return; }
|
118 | await shell(`rm -rf ${nodeModulesLinkPath}`);
|
119 | });
|
120 | }
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 | module.exports = async function anuxInstall() {
|
152 | const packOnlyInstall = getArg(process.argv, '--pack-only', false, false)
|
153 | logInfo(`Installing...${packOnlyInstall ? ' (pack only)' : ''}`);
|
154 | const packageJson = getPackageJson();
|
155 | sortAllDependencies(packageJson);
|
156 | const tarballPackages = await getTarballsOfLinks(root, packageJson);
|
157 | removeDuplicatesFromTarballPackages(tarballPackages);
|
158 | makeTarballsOfLinks(tarballPackages);
|
159 | updatePackagesWithTarballLinks(tarballPackages);
|
160 | await removeLinksFromNodeModules(root, packageJson);
|
161 | writePackageJson(packageJson);
|
162 | if (packOnlyInstall === true) { return; }
|
163 | try {
|
164 | await doInstall();
|
165 | } catch ({ exitCode, stderr }) {
|
166 |
|
167 | console.error(stderr);
|
168 | end(1);
|
169 | }
|
170 |
|
171 | logInfo('Finished installing.');
|
172 | };
|