@teambit/dependency-resolver 1.0.108 → 1.0.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,719 +0,0 @@
1
- import isBuiltinModule from 'is-builtin-module';
2
- import path from 'path';
3
- import { uniq, compact, flatten, head, omit } from 'lodash';
4
- import { Stats } from 'fs';
5
- import fs from 'fs-extra';
6
- import resolveFrom from 'resolve-from';
7
- import { findCurrentBvmDir } from '@teambit/bvm.path';
8
- import { ComponentMap, Component, ComponentID, ComponentMain } from '@teambit/component';
9
- import { Logger } from '@teambit/logger';
10
- import { PathAbsolute } from '@teambit/legacy/dist/utils/path';
11
- import { BitError } from '@teambit/bit-error';
12
- import componentIdToPackageName from '@teambit/legacy/dist/utils/bit/component-id-to-package-name';
13
- import { EnvsMain } from '@teambit/envs';
14
- import { AspectLoaderMain, getCoreAspectName, getCoreAspectPackageName, getAspectDir } from '@teambit/aspect-loader';
15
- import {
16
- MainAspectNotLinkable,
17
- RootDirNotDefined,
18
- CoreAspectLinkError,
19
- NonAspectCorePackageLinkError,
20
- } from './exceptions';
21
- import { DependencyResolverMain } from './dependency-resolver.main.runtime';
22
-
23
- /**
24
- * context of the linking process.
25
- */
26
- export type DepLinkerContext = {
27
- inCapsule?: boolean;
28
- };
29
-
30
- export type LinkingOptions = {
31
- rewire?: boolean;
32
- /**
33
- * Whether to create link to @teambit/bit in the root node modules
34
- */
35
- linkTeambitBit?: boolean;
36
- /**
37
- * Whether to create links in the root dir node modules to all core aspects
38
- */
39
- linkCoreAspects?: boolean;
40
-
41
- linkNestedDepsInNM?: boolean;
42
-
43
- /**
44
- * link to another project, so that project could use components from this workspace.
45
- * similar to npm/yarn link
46
- */
47
- linkToDir?: string;
48
-
49
- /**
50
- * Link peer dependencies of the components to the target project.
51
- * Peer dependencies should be singletons, so the project should use the same
52
- * version of the peer dependency as the linked in components.
53
- */
54
- includePeers?: boolean;
55
-
56
- /**
57
- * whether link should import objects before linking
58
- */
59
- fetchObject?: boolean;
60
-
61
- /**
62
- * Link deps which should be linked to the env
63
- */
64
- linkDepsResolvedFromEnv?: boolean;
65
- };
66
-
67
- const DEFAULT_LINKING_OPTIONS: LinkingOptions = {
68
- rewire: false,
69
- linkTeambitBit: true,
70
- linkCoreAspects: true,
71
- linkDepsResolvedFromEnv: true,
72
- linkNestedDepsInNM: true,
73
- };
74
-
75
- export type LinkDetail = { packageName: string; from: string; to: string };
76
-
77
- export type CoreAspectLinkResult = {
78
- aspectId: string;
79
- linkDetail: LinkDetail;
80
- };
81
-
82
- export type DepsLinkedToEnvResult = {
83
- componentId: string;
84
- linksDetail: LinkDetail[];
85
- };
86
-
87
- export type NestedNMDepsLinksResult = {
88
- componentId: string;
89
- linksDetail: LinkDetail[];
90
- };
91
-
92
- export type LinkToDirResult = {
93
- componentId: string;
94
- linksDetail: LinkDetail;
95
- };
96
-
97
- export type LinkResults = {
98
- teambitBitLink?: CoreAspectLinkResult;
99
- coreAspectsLinks?: CoreAspectLinkResult[];
100
- harmonyLink?: LinkDetail;
101
- teambitLegacyLink?: LinkDetail;
102
- resolvedFromEnvLinks?: DepsLinkedToEnvResult[];
103
- nestedDepsInNmLinks?: NestedNMDepsLinksResult[];
104
- linkToDirResults?: LinkToDirResult[];
105
- };
106
-
107
- type NestedModuleFolderEntry = {
108
- moduleName: string;
109
- path: string;
110
- origPath?: string;
111
- };
112
-
113
- export class DependencyLinker {
114
- private _currentBitDir: string | null;
115
-
116
- constructor(
117
- private dependencyResolver: DependencyResolverMain,
118
-
119
- private aspectLoader: AspectLoaderMain,
120
-
121
- private componentAspect: ComponentMain,
122
-
123
- private envs: EnvsMain,
124
-
125
- private logger: Logger,
126
-
127
- private rootDir?: string | PathAbsolute,
128
-
129
- private linkingOptions?: LinkingOptions,
130
-
131
- private linkingContext: DepLinkerContext = {}
132
- ) {
133
- this._currentBitDir = findCurrentBvmDir();
134
- }
135
-
136
- async calculateLinkedDeps(
137
- rootDir: string | undefined,
138
- componentDirectoryMap: ComponentMap<string>,
139
- options: LinkingOptions = {}
140
- ): Promise<{ linkedRootDeps: Record<string, string>; linkResults: LinkResults }> {
141
- const linkResults = await this._calculateLinks(rootDir, componentDirectoryMap, options);
142
- const localLinks: Array<[string, string]> = [];
143
- if (linkResults.teambitBitLink) {
144
- localLinks.push(this.linkDetailToLocalDepEntry(linkResults.teambitBitLink.linkDetail));
145
- }
146
- if (linkResults.coreAspectsLinks) {
147
- linkResults.coreAspectsLinks.forEach((link) => {
148
- localLinks.push(this.linkDetailToLocalDepEntry(link.linkDetail));
149
- });
150
- }
151
- if (linkResults.harmonyLink) {
152
- localLinks.push(this.linkDetailToLocalDepEntry(linkResults.harmonyLink));
153
- }
154
- if (linkResults.teambitLegacyLink) {
155
- localLinks.push(this.linkDetailToLocalDepEntry(linkResults.teambitLegacyLink));
156
- }
157
- if (linkResults.resolvedFromEnvLinks) {
158
- linkResults.resolvedFromEnvLinks.forEach((link) => {
159
- link.linksDetail.forEach((linkDetail) => {
160
- localLinks.push(this.linkDetailToLocalDepEntry(linkDetail));
161
- });
162
- });
163
- }
164
- if (linkResults.linkToDirResults) {
165
- linkResults.linkToDirResults.forEach((link) => {
166
- localLinks.push(this.linkDetailToLocalDepEntry(link.linksDetail));
167
- });
168
- }
169
- return {
170
- linkedRootDeps: Object.fromEntries(localLinks.map(([key, value]) => [key, `link:${value}`])),
171
- linkResults,
172
- };
173
- }
174
-
175
- private linkDetailToLocalDepEntry(linkDetail: LinkDetail): [string, string] {
176
- return [linkDetail.packageName, linkDetail.from];
177
- }
178
-
179
- private async _calculateLinks(
180
- rootDir: string | undefined,
181
- componentDirectoryMap: ComponentMap<string>,
182
- options: LinkingOptions = {}
183
- ): Promise<LinkResults> {
184
- const outputMessage = this.linkingContext?.inCapsule
185
- ? `(capsule) linking components in root dir: ${rootDir || this.rootDir}`
186
- : 'linking components';
187
- if (!this.linkingContext?.inCapsule) {
188
- this.logger.setStatusLine(outputMessage);
189
- }
190
- this.logger.debug('linking components with options', omit(options, ['consumer']));
191
- const startTime = process.hrtime();
192
-
193
- let result: LinkResults = {};
194
- const finalRootDir = rootDir || this.rootDir;
195
- const linkingOpts = Object.assign({}, DEFAULT_LINKING_OPTIONS, this.linkingOptions || {}, options || {});
196
- if (!finalRootDir) {
197
- throw new RootDirNotDefined();
198
- }
199
- if (options.linkToDir) {
200
- const components = componentDirectoryMap.toArray().map(([component]) => component);
201
- const linkToDirResults = await this.linkToDir(finalRootDir, options.linkToDir, components);
202
- result.linkToDirResults = linkToDirResults;
203
- if (options.includePeers) {
204
- result.linkToDirResults.push(
205
- ...(await this._getLinksToPeers(componentDirectoryMap, { finalRootDir, linkToDir: options.linkToDir }))
206
- );
207
- }
208
- return result;
209
- }
210
-
211
- // Link deps which should be linked to the env
212
- if (linkingOpts.linkDepsResolvedFromEnv) {
213
- result.resolvedFromEnvLinks = await this.linkDepsResolvedFromEnv(componentDirectoryMap);
214
- }
215
- if (linkingOpts.linkNestedDepsInNM) {
216
- result.nestedDepsInNmLinks = this.addSymlinkFromComponentDirNMToWorkspaceDirNM(
217
- finalRootDir,
218
- componentDirectoryMap
219
- );
220
- }
221
-
222
- // We remove the version since it used in order to check if it's core aspects, and the core aspects arrived from aspect loader without versions
223
- const componentIds: ComponentID[] = [];
224
- componentDirectoryMap.map((_dir, comp) => {
225
- componentIds.push(comp.id);
226
- return undefined;
227
- });
228
- result = {
229
- ...result,
230
- ...(await this.linkCoreAspectsAndLegacy(finalRootDir, componentIds, linkingOpts)),
231
- };
232
- if (!this.linkingContext?.inCapsule) {
233
- this.logger.consoleSuccess(outputMessage, startTime);
234
- }
235
- return result;
236
- }
237
-
238
- async _getLinksToPeers(
239
- componentDirectoryMap: ComponentMap<string>,
240
- options: {
241
- finalRootDir: string;
242
- linkToDir: string;
243
- }
244
- ): Promise<LinkToDirResult[]> {
245
- const peers = new Set<string>();
246
- await Promise.all(
247
- componentDirectoryMap.toArray().map(async ([component]) => {
248
- const depList = await this.dependencyResolver.getDependencies(component);
249
- const peerList = depList.byLifecycle('peer');
250
- peerList.forEach((dependency) => {
251
- if (dependency.getPackageName) {
252
- peers.add(dependency.getPackageName());
253
- }
254
- });
255
- })
256
- );
257
- const fromDir = path.join(options.finalRootDir, 'node_modules');
258
- const toDir = path.join(options.linkToDir, 'node_modules');
259
- return Array.from(peers).map((packageName) => ({
260
- componentId: packageName,
261
- linksDetail: {
262
- packageName,
263
- from: path.join(fromDir, packageName),
264
- to: path.join(toDir, packageName),
265
- },
266
- }));
267
- }
268
-
269
- async linkCoreAspectsAndLegacy(
270
- rootDir: string,
271
- componentIds: ComponentID[] = [],
272
- options: Pick<LinkingOptions, 'linkTeambitBit' | 'linkCoreAspects'> = {}
273
- ) {
274
- const result: LinkResults = {};
275
- const componentIdsWithoutVersions: string[] = [];
276
- componentIds.map((id) => {
277
- componentIdsWithoutVersions.push(id.toString({ ignoreVersion: true }));
278
- return undefined;
279
- });
280
- const linkingOpts = Object.assign({}, DEFAULT_LINKING_OPTIONS, this.linkingOptions || {}, options || {});
281
- if (linkingOpts.linkTeambitBit && !this.isBitRepoWorkspace(rootDir)) {
282
- const bitLink = await this.linkBitAspectIfNotExist(
283
- path.join(rootDir, 'node_modules'),
284
- componentIdsWithoutVersions
285
- );
286
- result.teambitBitLink = bitLink;
287
- }
288
-
289
- let mainAspectPath = result.teambitBitLink?.linkDetail.from;
290
- if (!mainAspectPath && this.aspectLoader.mainAspect) {
291
- if (!this.aspectLoader.mainAspect.packageName) {
292
- throw new MainAspectNotLinkable();
293
- }
294
- mainAspectPath = path.join(rootDir, 'node_modules', this.aspectLoader.mainAspect.packageName);
295
- }
296
- if (linkingOpts.linkCoreAspects && !this.isBitRepoWorkspace(rootDir)) {
297
- const hasLocalInstallation = !linkingOpts.linkTeambitBit;
298
- if (mainAspectPath) {
299
- result.coreAspectsLinks = await this.linkNonExistingCoreAspects(componentIdsWithoutVersions, {
300
- targetModulesDir: path.join(rootDir, 'node_modules'),
301
- mainAspectPath,
302
- hasLocalInstallation,
303
- });
304
- } else {
305
- result.coreAspectsLinks = [];
306
- }
307
- }
308
-
309
- if (mainAspectPath) {
310
- result.teambitLegacyLink = this.linkNonAspectCorePackages(rootDir, 'legacy', mainAspectPath);
311
- result.harmonyLink = this.linkNonAspectCorePackages(rootDir, 'harmony', mainAspectPath);
312
- }
313
- return result;
314
- }
315
-
316
- private async linkToDir(rootDir: string, targetDir: string, components: Component[]): Promise<LinkToDirResult[]> {
317
- const results: LinkToDirResult[] = components.map((component) => {
318
- const componentPackageName = componentIdToPackageName(component.state._consumer);
319
- return {
320
- componentId: component.id.toString(),
321
- linksDetail: {
322
- packageName: componentPackageName,
323
- from: path.join(rootDir, 'node_modules', componentPackageName),
324
- to: path.join(targetDir, 'node_modules', componentPackageName),
325
- },
326
- };
327
- });
328
- return results;
329
- }
330
-
331
- /**
332
- * Add symlinks from the node_modules in the component's root-dir to the workspace node_modules
333
- * of the component. e.g.
334
- * <ws-root>/node_modules/comp1/node_modules/<dep> -> <ws-root>/components/comp1/node_modules/<dep>
335
- * This is needed because the component is compiled into the dist folder at <ws-root>/node_modules/comp1/dist,
336
- * so the files in the dist folder need to find the right dependencies of comp1.
337
- */
338
- private addSymlinkFromComponentDirNMToWorkspaceDirNM(
339
- rootDir: string,
340
- componentDirectoryMap: ComponentMap<string>
341
- ): NestedNMDepsLinksResult[] {
342
- const rootNodeModules = path.join(rootDir, 'node_modules');
343
- const getPackagesFoldersToLink = (dir: string, parent?: string): NestedModuleFolderEntry[] => {
344
- const folders = fs
345
- .readdirSync(dir, { withFileTypes: true })
346
- .filter((dirent) => {
347
- if (dirent.name.startsWith('.')) {
348
- return false;
349
- }
350
- return dirent.isDirectory() || dirent.isSymbolicLink();
351
- })
352
- .map((dirent) => {
353
- const dirPath = path.join(dir, dirent.name);
354
- const moduleName = parent ? `${parent}/${dirent.name}` : dirent.name;
355
- // If we have a folder with a name of built in module (like events)
356
- // the resolve from will resolve it from the core, so it will return something like 'events'
357
- // instead of the path.
358
- // adding a '/' at the end solve this
359
- const moduleNameToResolve = isBuiltinModule(moduleName) ? `${moduleName}/` : moduleName;
360
- // This is a scoped package, need to go inside
361
- if (dirent.name.startsWith('@')) {
362
- return getPackagesFoldersToLink(dirPath, dirent.name);
363
- }
364
-
365
- if (dirent.isSymbolicLink()) {
366
- const resolvedModuleFrom = resolveModuleFromDir(dir, moduleNameToResolve);
367
- if (!resolvedModuleFrom) {
368
- return {
369
- moduleName,
370
- path: dirPath,
371
- };
372
- }
373
- return {
374
- origPath: dirPath,
375
- moduleName,
376
- path: resolveModuleDirFromFile(resolvedModuleFrom, moduleName),
377
- };
378
- }
379
- return {
380
- moduleName,
381
- path: dirPath,
382
- };
383
- });
384
- return flatten(folders);
385
- };
386
- const linksOfAllComponents = componentDirectoryMap.toArray().map(([component, dir]) => {
387
- const compDirNM = path.join(dir, 'node_modules');
388
- if (!fs.existsSync(compDirNM)) return undefined;
389
- // TODO: support modules with scoped packages (start with @) - we need to make this logic 2 levels
390
-
391
- const componentPackageName = componentIdToPackageName(component.state._consumer);
392
- const innerNMofComponentInNM = path.join(rootNodeModules, componentPackageName);
393
- // If the folder itself is a symlink, do not try to symlink inside it
394
- if (isPathSymlink(innerNMofComponentInNM)) {
395
- return undefined;
396
- }
397
- const packagesFoldersToLink: NestedModuleFolderEntry[] = getPackagesFoldersToLink(compDirNM);
398
- fs.ensureDirSync(innerNMofComponentInNM);
399
-
400
- const oneComponentLinks: LinkDetail[] = packagesFoldersToLink.map((folderEntry: NestedModuleFolderEntry) => {
401
- const linkTarget = path.join(innerNMofComponentInNM, 'node_modules', folderEntry?.moduleName);
402
- const linkSrc = folderEntry.path;
403
- // This works as well, consider using it instead
404
- // const linkSrc = folderEntry.origPath || folderEntry.path;
405
- const origPath = folderEntry.origPath ? `(${folderEntry.origPath})` : '';
406
- const linkDetail: LinkDetail = {
407
- packageName: folderEntry.moduleName,
408
- from: `${linkSrc} ${origPath}`,
409
- to: linkTarget,
410
- };
411
- this.logger.info(
412
- `linking nested dependency ${folderEntry.moduleName} for component ${component}. link src: ${linkSrc} link target: ${linkTarget}`
413
- );
414
- return linkDetail;
415
- });
416
-
417
- const filteredLinks = compact(oneComponentLinks);
418
- return {
419
- componentId: component.id.toString(),
420
- linksDetail: filteredLinks,
421
- };
422
- });
423
- const filteredLinks = compact(linksOfAllComponents);
424
- return filteredLinks;
425
- }
426
-
427
- private async linkDepsResolvedFromEnv(
428
- componentDirectoryMap: ComponentMap<string>
429
- ): Promise<Array<DepsLinkedToEnvResult>> {
430
- const componentsNeedLinks: {
431
- component: Component;
432
- dir: string;
433
- env;
434
- resolvedFromEnv;
435
- envId?: string;
436
- envDir?: string;
437
- }[] = [];
438
-
439
- const componentsNeedLinksP = componentDirectoryMap.toArray().map(async ([component, dir]) => {
440
- const policy = await this.dependencyResolver.getPolicy(component);
441
- const resolvedFromEnv = policy.getResolvedFromEnv();
442
- // Nothing should be resolved from env, do nothing
443
- if (!resolvedFromEnv.length) {
444
- return;
445
- }
446
- const env = this.envs.getEnv(component);
447
- const componentNeedLink = {
448
- component,
449
- dir,
450
- env,
451
- resolvedFromEnv,
452
- };
453
- componentsNeedLinks.push(componentNeedLink);
454
- });
455
-
456
- await Promise.all(componentsNeedLinksP);
457
- // Stop if there are not components needs to be linked
458
- if (!componentsNeedLinks || !componentsNeedLinks.length) return [];
459
- const envsStringIds = componentsNeedLinks.map((obj) => obj.env.id);
460
- const uniqEnvIds = uniq(envsStringIds);
461
- const host = this.componentAspect.getHost();
462
- const resolvedEnvIds = await host.resolveMultipleComponentIds(uniqEnvIds);
463
- const resolvedAspects = await host.resolveAspects(undefined, resolvedEnvIds);
464
- const resolvedAspectsIndex = resolvedAspects.reduce((acc, curr) => {
465
- if (curr.getId) {
466
- acc[curr.getId] = curr;
467
- }
468
- return acc;
469
- }, {});
470
- const allLinksP = componentsNeedLinks.map(async (entry) => {
471
- const oneComponentLinksP: Array<LinkDetail | undefined> = entry.resolvedFromEnv.entries.map(async (depEntry) => {
472
- const linkTarget = path.join(entry.dir, 'node_modules', depEntry.dependencyId);
473
- const envDir = resolvedAspectsIndex[entry.env.id].aspectPath;
474
- const resolvedModule = resolveModuleFromDir(envDir, depEntry.dependencyId);
475
- if (!resolvedModule) {
476
- this.logger.console(`could not resolve ${depEntry.dependencyId} from env directory ${envDir}`);
477
- return undefined;
478
- }
479
- const linkSrc = resolveModuleDirFromFile(resolvedModule, depEntry.dependencyId);
480
- const linkDetail: LinkDetail = {
481
- packageName: depEntry.dependencyId,
482
- from: linkSrc,
483
- to: linkTarget,
484
- };
485
- this.logger.info(
486
- `linking dependency ${depEntry.dependencyId} from env directory ${envDir}. link src: ${linkSrc} link target: ${linkTarget}`
487
- );
488
-
489
- return linkDetail;
490
- });
491
- const oneComponentLinks = await Promise.all(oneComponentLinksP);
492
- const filteredLinks = compact(oneComponentLinks);
493
- const depsLinkedToEnvResult: DepsLinkedToEnvResult = {
494
- componentId: entry.component.id.toString(),
495
- linksDetail: filteredLinks,
496
- };
497
- return depsLinkedToEnvResult;
498
- });
499
- return Promise.all(allLinksP);
500
- }
501
-
502
- private async linkBitAspectIfNotExist(
503
- dir: string,
504
- componentIds: string[]
505
- ): Promise<CoreAspectLinkResult | undefined> {
506
- if (!this.aspectLoader.mainAspect) return undefined;
507
- const mainAspectId = this.aspectLoader.mainAspect.id;
508
- const existing = componentIds.find((id) => {
509
- return id === mainAspectId;
510
- });
511
-
512
- if (existing) {
513
- return undefined;
514
- }
515
- const linkDetail = await this.linkBit(dir);
516
- if (!linkDetail) return undefined;
517
- return {
518
- aspectId: mainAspectId,
519
- linkDetail,
520
- };
521
- }
522
-
523
- async linkBit(dir: string): Promise<LinkDetail | undefined> {
524
- if (!this.aspectLoader.mainAspect.packageName) {
525
- throw new MainAspectNotLinkable();
526
- }
527
- const target = path.join(dir, this.aspectLoader.mainAspect.packageName);
528
- const shouldSymlink = this.removeSymlinkTarget(target);
529
- if (!shouldSymlink) return undefined;
530
- const src =
531
- this._getPkgPathFromCurrentBitDir(this.aspectLoader.mainAspect.packageName) ?? this.aspectLoader.mainAspect.path;
532
- await fs.ensureDir(path.dirname(target));
533
- return { packageName: this.aspectLoader.mainAspect.packageName, from: src, to: target };
534
- }
535
-
536
- private async linkNonExistingCoreAspects(
537
- componentIds: string[],
538
- opts: {
539
- targetModulesDir: string;
540
- mainAspectPath: string;
541
- hasLocalInstallation?: boolean;
542
- }
543
- ): Promise<CoreAspectLinkResult[]> {
544
- const coreAspectsIds = this.aspectLoader.getCoreAspectIds();
545
- const filtered = coreAspectsIds.filter((aspectId) => {
546
- // Remove bit aspect
547
- if (aspectId === this.aspectLoader.mainAspect?.id) {
548
- return false;
549
- }
550
- // TODO: use the aspect id once default scope is resolved and the component dir map has the id with scope
551
- const name = getCoreAspectName(aspectId);
552
- const existing = componentIds.find((componentId) => {
553
- return componentId === name || componentId === aspectId;
554
- });
555
- if (existing) {
556
- return false;
557
- }
558
- return true;
559
- });
560
-
561
- this.logger.debug(`linkNonExistingCoreAspects: linking the following core aspects ${filtered.join()}`);
562
-
563
- const results = filtered.map((id) => this.linkCoreAspect(id, opts));
564
- return compact(results);
565
- }
566
-
567
- private isBitRepoWorkspace(dir: string) {
568
- // A special condition to not link core aspects in bit workspace itself
569
- if (this.aspectLoader.mainAspect?.path.startsWith(dir)) {
570
- return true;
571
- }
572
- return false;
573
- }
574
-
575
- private linkCoreAspect(
576
- id: string,
577
- {
578
- targetModulesDir,
579
- mainAspectPath,
580
- hasLocalInstallation,
581
- }: {
582
- targetModulesDir: string;
583
- mainAspectPath: string;
584
- hasLocalInstallation?: boolean;
585
- }
586
- ): CoreAspectLinkResult | undefined {
587
- const name = getCoreAspectName(id);
588
- const packageName = getCoreAspectPackageName(id);
589
- let aspectDir = path.join(mainAspectPath, 'dist', name);
590
- const target = path.join(targetModulesDir, packageName);
591
- const fromDir = this._getPkgPathFromCurrentBitDir(packageName);
592
- if (fromDir) {
593
- return { aspectId: id, linkDetail: { packageName, from: fromDir, to: target } };
594
- }
595
- const shouldSymlink = this.removeSymlinkTarget(target, hasLocalInstallation);
596
- if (!shouldSymlink) return undefined;
597
- const isAspectDirExist = fs.pathExistsSync(aspectDir);
598
- if (!isAspectDirExist) {
599
- this.logger.debug(`linkCoreAspect: aspectDir ${aspectDir} does not exist, linking it to ${target}`);
600
- aspectDir = getAspectDir(id);
601
- return { aspectId: id, linkDetail: { packageName, from: aspectDir, to: target } };
602
- }
603
-
604
- try {
605
- // eslint-disable-next-line global-require, import/no-dynamic-require
606
- const module = require(aspectDir);
607
- const aspectPath = path.resolve(path.join(module.path, '..', '..'));
608
- this.logger.debug(`linkCoreAspect: linking aspectPath ${aspectPath} to ${target}`);
609
- return { aspectId: id, linkDetail: { packageName, from: aspectPath, to: target } };
610
- } catch (err: any) {
611
- throw new CoreAspectLinkError(id, err);
612
- }
613
- }
614
-
615
- /**
616
- * returns true if it's safe to symlink it later.
617
- */
618
- private removeSymlinkTarget(targetPath: string, hasLocalInstallation = false): boolean {
619
- // TODO: change to fs.lstatSync(dest, {throwIfNoEntry: false});
620
- // TODO: this requires to upgrade node to v15.3.0 to have the throwIfNoEntry property (maybe upgrade fs-extra will work as well)
621
- // TODO: we don't use fs.pathExistsSync since it will return false in case the dest is a symlink which will result error on write
622
- let targetStat: Stats | undefined;
623
- try {
624
- targetStat = fs.lstatSync(targetPath);
625
- // eslint-disable-next-line no-empty
626
- } catch (e: any) {}
627
- if (targetStat && !hasLocalInstallation) {
628
- // Do not override links created by other means
629
- if (!targetStat.isSymbolicLink()) {
630
- this.logger.debug(`removing link target, target ${targetPath} already exist. skipping it`);
631
- return false;
632
- }
633
- return true;
634
- }
635
- return true;
636
- }
637
-
638
- private _getPkgPathFromCurrentBitDir(packageName: string): string | undefined {
639
- if (!this._currentBitDir) return undefined;
640
- return path.join(this._currentBitDir, 'node_modules', packageName);
641
- }
642
-
643
- private linkNonAspectCorePackages(rootDir: string, name: string, mainAspectPath: string): LinkDetail | undefined {
644
- const distDir = path.join(mainAspectPath, 'dist', name);
645
-
646
- const packageName = `@teambit/${name}`;
647
- const target = path.join(rootDir, 'node_modules', packageName);
648
- const fromDir = this._getPkgPathFromCurrentBitDir(packageName);
649
- if (fromDir) {
650
- return { packageName, from: fromDir, to: target };
651
- }
652
- const isDistDirExist = fs.pathExistsSync(distDir);
653
- if (!isDistDirExist) {
654
- const newDir = getDistDirForDevEnv(packageName);
655
- return { packageName, from: newDir, to: target };
656
- }
657
-
658
- try {
659
- // eslint-disable-next-line global-require, import/no-dynamic-require
660
- const module = require(distDir);
661
- const resolvedPath = path.resolve(path.join(module.path, '..', '..'));
662
- return { packageName, from: resolvedPath, to: target };
663
- } catch (err: any) {
664
- throw new NonAspectCorePackageLinkError(err, packageName);
665
- }
666
- }
667
- }
668
-
669
- /**
670
- * When running dev env (bd) we need to get the harmony/legacy folder from the node_modules of the clone
671
- */
672
- function getDistDirForDevEnv(packageName: string): string {
673
- let moduleDirectory = require.resolve(packageName);
674
- let dirPath;
675
- if (moduleDirectory.includes(packageName)) {
676
- dirPath = path.join(moduleDirectory, '../..'); // to remove the "index.js" at the end
677
- } else {
678
- // This is usually required for the @teambit/legacy, as we re inside the nm so we can't find it in the other way
679
- const nmDir = __dirname.substring(0, __dirname.indexOf('@teambit'));
680
- dirPath = path.join(nmDir, packageName);
681
- moduleDirectory = require.resolve(packageName, { paths: [nmDir] });
682
- }
683
- if (!fs.existsSync(dirPath)) {
684
- throw new BitError(`unable to find ${packageName} in ${dirPath}`);
685
- }
686
-
687
- return dirPath;
688
- }
689
-
690
- // TODO: extract to new component
691
- function resolveModuleFromDir(fromDir: string, moduleId: string, silent = true): string | undefined {
692
- if (silent) {
693
- return resolveFrom.silent(fromDir, moduleId);
694
- }
695
- return resolveFrom(fromDir, moduleId);
696
- }
697
-
698
- // TODO: extract to new component
699
- function resolveModuleDirFromFile(resolvedModulePath: string, moduleId: string): string {
700
- const NM = 'node_modules';
701
- if (resolvedModulePath.includes(NM)) {
702
- return path.join(resolvedModulePath.slice(0, resolvedModulePath.lastIndexOf(NM) + NM.length), moduleId);
703
- }
704
-
705
- const [start, end] = resolvedModulePath.split('@');
706
- if (!end) return path.basename(resolvedModulePath);
707
- const versionStr = head(end.split('/'));
708
- return `${start}@${versionStr}`;
709
- }
710
-
711
- function isPathSymlink(folderPath: string): boolean | undefined {
712
- // TODO: change to fs.lstatSync(dest, {throwIfNoEntry: false}); once upgrade fs-extra
713
- try {
714
- const stat = fs.lstatSync(folderPath);
715
- return stat.isSymbolicLink();
716
- } catch (e: any) {
717
- return undefined;
718
- }
719
- }