@teambit/builder 0.0.813 → 0.0.816
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.
- package/artifact/artifact-extractor.ts +31 -30
- package/artifact/artifact-factory.ts +10 -17
- package/artifact/artifact-list.ts +61 -19
- package/artifact/artifact.ts +27 -18
- package/artifact/fs-artifact.ts +21 -0
- package/artifact/index.ts +1 -0
- package/dist/artifact/artifact-extractor.js +43 -26
- package/dist/artifact/artifact-extractor.js.map +1 -1
- package/dist/artifact/artifact-factory.d.ts +4 -4
- package/dist/artifact/artifact-factory.js +9 -10
- package/dist/artifact/artifact-factory.js.map +1 -1
- package/dist/artifact/artifact-list.d.ts +15 -12
- package/dist/artifact/artifact-list.js +68 -13
- package/dist/artifact/artifact-list.js.map +1 -1
- package/dist/artifact/artifact.d.ts +8 -26
- package/dist/artifact/artifact.js +33 -14
- package/dist/artifact/artifact.js.map +1 -1
- package/dist/artifact/fs-artifact.d.ts +18 -0
- package/dist/artifact/fs-artifact.js +35 -0
- package/dist/artifact/fs-artifact.js.map +1 -0
- package/dist/artifact/index.d.ts +1 -0
- package/dist/artifact/index.js +16 -0
- package/dist/artifact/index.js.map +1 -1
- package/dist/build-pipe.d.ts +2 -2
- package/dist/build-pipe.js.map +1 -1
- package/dist/build-pipeline-result-list.js +2 -2
- package/dist/build-pipeline-result-list.js.map +1 -1
- package/dist/build-task.d.ts +5 -0
- package/dist/build-task.js.map +1 -1
- package/dist/builder.graphql.d.ts +76 -4
- package/dist/builder.graphql.js +114 -16
- package/dist/builder.graphql.js.map +1 -1
- package/dist/builder.main.runtime.d.ts +30 -16
- package/dist/builder.main.runtime.js +88 -41
- package/dist/builder.main.runtime.js.map +1 -1
- package/dist/builder.route.d.ts +20 -0
- package/dist/builder.route.js +145 -0
- package/dist/builder.route.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/storage/default-resolver.d.ts +2 -2
- package/dist/storage/default-resolver.js.map +1 -1
- package/dist/storage/storage-resolver.d.ts +3 -3
- package/dist/storage/storage-resolver.js.map +1 -1
- package/package-tar/teambit-builder-0.0.816.tgz +0 -0
- package/package.json +20 -14
- package/{preview-1660188657912.js → preview-1660447667955.js} +2 -2
- package/storage/default-resolver.ts +2 -2
- package/storage/storage-resolver.ts +3 -3
- package/package-tar/teambit-builder-0.0.813.tgz +0 -0
|
@@ -5,9 +5,11 @@ import { ScopeMain } from '@teambit/scope';
|
|
|
5
5
|
import { ComponentID } from '@teambit/component-id';
|
|
6
6
|
import pMapSeries from 'p-map-series';
|
|
7
7
|
import minimatch from 'minimatch';
|
|
8
|
-
import { ArtifactFiles
|
|
8
|
+
import { ArtifactFiles } from '@teambit/legacy/dist/consumer/component/sources/artifact-files';
|
|
9
9
|
import { BuilderMain } from '../builder.main.runtime';
|
|
10
10
|
import { ArtifactsOpts } from './artifacts.cmd';
|
|
11
|
+
import { ArtifactList } from './artifact-list';
|
|
12
|
+
import { Artifact } from './artifact';
|
|
11
13
|
|
|
12
14
|
export type ExtractorResult = {
|
|
13
15
|
id: ComponentID;
|
|
@@ -26,9 +28,9 @@ export type ExtractorResultGrouped = {
|
|
|
26
28
|
artifacts: { [aspectId: string]: ExtractorArtifactResult[] };
|
|
27
29
|
};
|
|
28
30
|
|
|
29
|
-
type
|
|
31
|
+
type ArtifactListPerId = {
|
|
30
32
|
id: ComponentID;
|
|
31
|
-
artifacts:
|
|
33
|
+
artifacts: ArtifactList<Artifact>;
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
export class ArtifactExtractor {
|
|
@@ -42,16 +44,16 @@ export class ArtifactExtractor {
|
|
|
42
44
|
async list(): Promise<ExtractorResult[]> {
|
|
43
45
|
const ids = await this.scope.idsByPattern(this.pattern);
|
|
44
46
|
const components = await this.scope.loadMany(ids);
|
|
45
|
-
const
|
|
47
|
+
const artifactListPerId: ArtifactListPerId[] = components.map((component) => {
|
|
46
48
|
return {
|
|
47
49
|
id: component.id,
|
|
48
|
-
artifacts: this.builder.getArtifacts(component)
|
|
50
|
+
artifacts: this.builder.getArtifacts(component),
|
|
49
51
|
};
|
|
50
52
|
});
|
|
51
|
-
this.filterByOptions(
|
|
52
|
-
await this.saveFilesInFileSystemIfAsked(
|
|
53
|
+
this.filterByOptions(artifactListPerId);
|
|
54
|
+
await this.saveFilesInFileSystemIfAsked(artifactListPerId);
|
|
53
55
|
|
|
54
|
-
return this.artifactsObjectsToExtractorResults(
|
|
56
|
+
return this.artifactsObjectsToExtractorResults(artifactListPerId);
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
groupResultsByAspect(extractorResult: ExtractorResult[]) {
|
|
@@ -64,32 +66,27 @@ export class ArtifactExtractor {
|
|
|
64
66
|
});
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
private async saveFilesInFileSystemIfAsked(
|
|
69
|
+
private async saveFilesInFileSystemIfAsked(artifactListPerId: ArtifactListPerId[]) {
|
|
68
70
|
const outDir = this.options.outDir;
|
|
69
71
|
if (!outDir) {
|
|
70
72
|
return;
|
|
71
73
|
}
|
|
72
74
|
// @todo: optimize this to first import all missing hashes.
|
|
73
|
-
await pMapSeries(
|
|
74
|
-
const vinyls = await
|
|
75
|
-
artifacts.map((artifactObject) =>
|
|
76
|
-
artifactObject.files.getVinylsAndImportIfMissing(id._legacy, this.scope.legacyScope)
|
|
77
|
-
)
|
|
78
|
-
);
|
|
79
|
-
const flattenedVinyls = vinyls.flat();
|
|
75
|
+
await pMapSeries(artifactListPerId, async ({ id, artifacts }) => {
|
|
76
|
+
const vinyls = await artifacts.getVinylsAndImportIfMissing(id._legacy, this.scope.legacyScope);
|
|
80
77
|
// make sure the component-dir is just one dir. without this, every slash in the component-id will create a new dir.
|
|
81
78
|
const idAsFilename = filenamify(id.toStringWithoutVersion(), { replacement: '_' });
|
|
82
79
|
const compPath = path.join(outDir, idAsFilename);
|
|
83
|
-
await Promise.all(
|
|
80
|
+
await Promise.all(vinyls.map((vinyl) => fs.outputFile(path.join(compPath, vinyl.path), vinyl.contents)));
|
|
84
81
|
});
|
|
85
82
|
}
|
|
86
83
|
|
|
87
|
-
private artifactsObjectsToExtractorResults(
|
|
88
|
-
return
|
|
84
|
+
private artifactsObjectsToExtractorResults(artifactListPerId: ArtifactListPerId[]): ExtractorResult[] {
|
|
85
|
+
return artifactListPerId.map(({ id, artifacts }) => {
|
|
89
86
|
const results: ExtractorArtifactResult[] = artifacts.map((artifact) => {
|
|
90
87
|
return {
|
|
91
88
|
artifactName: artifact.name,
|
|
92
|
-
aspectId: artifact.task.
|
|
89
|
+
aspectId: artifact.task.aspectId,
|
|
93
90
|
taskName: artifact.task.name || artifact.generatedBy,
|
|
94
91
|
files: artifact.files.refs.map((ref) => ref.relativePath),
|
|
95
92
|
};
|
|
@@ -101,22 +98,26 @@ export class ArtifactExtractor {
|
|
|
101
98
|
});
|
|
102
99
|
}
|
|
103
100
|
|
|
104
|
-
private filterByOptions(
|
|
101
|
+
private filterByOptions(artifactListPerId: ArtifactListPerId[]) {
|
|
105
102
|
const { aspect, task, files } = this.options;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
let filteredArtifacts: Artifact[] = [];
|
|
104
|
+
|
|
105
|
+
artifactListPerId.forEach((item) => {
|
|
106
|
+
filteredArtifacts = item.artifacts.filter((artifact) => {
|
|
107
|
+
if (aspect && aspect !== artifact.task.aspectId) return false;
|
|
109
108
|
if (task && task !== artifact.task.name) return false;
|
|
110
109
|
return true;
|
|
111
110
|
});
|
|
112
111
|
if (files) {
|
|
113
|
-
item.artifacts
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
filteredArtifacts = item.artifacts
|
|
113
|
+
.map((artifact) => {
|
|
114
|
+
const refs = artifact.files.refs.filter((ref) => minimatch(ref.relativePath, files));
|
|
115
|
+
return new Artifact(artifact.def, new ArtifactFiles([], [], refs), artifact.task);
|
|
116
|
+
})
|
|
117
|
+
// remove artifacts with no files
|
|
118
|
+
.filter((artifact) => !artifact.isEmpty());
|
|
119
119
|
}
|
|
120
|
+
item.artifacts = ArtifactList.fromArray(filteredArtifacts);
|
|
120
121
|
});
|
|
121
122
|
}
|
|
122
123
|
}
|
|
@@ -6,13 +6,13 @@ import { Component, ComponentMap } from '@teambit/component';
|
|
|
6
6
|
import { ArtifactDefinition } from './artifact-definition';
|
|
7
7
|
import { DefaultResolver } from '../storage';
|
|
8
8
|
import { ArtifactList } from './artifact-list';
|
|
9
|
-
import { Artifact } from './artifact';
|
|
10
9
|
import type { BuildContext, BuildTask } from '../build-task';
|
|
11
10
|
import { CapsuleNotFound } from '../exceptions';
|
|
11
|
+
import { FsArtifact } from './fs-artifact';
|
|
12
12
|
|
|
13
13
|
export const DEFAULT_CONTEXT = 'component';
|
|
14
14
|
|
|
15
|
-
export type ArtifactMap = ComponentMap<ArtifactList
|
|
15
|
+
export type ArtifactMap = ComponentMap<ArtifactList<FsArtifact>>;
|
|
16
16
|
|
|
17
17
|
export class ArtifactFactory {
|
|
18
18
|
resolvePaths(root: string, def: ArtifactDefinition): string[] {
|
|
@@ -41,27 +41,26 @@ export class ArtifactFactory {
|
|
|
41
41
|
component: Component,
|
|
42
42
|
def: ArtifactDefinition,
|
|
43
43
|
task: BuildTask
|
|
44
|
-
):
|
|
45
|
-
const storageResolver = this.getStorageResolver(def);
|
|
44
|
+
): FsArtifact | undefined {
|
|
46
45
|
const contextPath = this.getArtifactContextPath(context, component, def);
|
|
47
46
|
const rootDir = this.getRootDir(contextPath, def);
|
|
48
47
|
const paths = this.resolvePaths(rootDir, def);
|
|
49
48
|
if (!paths || !paths.length) {
|
|
50
49
|
return undefined;
|
|
51
50
|
}
|
|
52
|
-
return new
|
|
51
|
+
return new FsArtifact(def, new ArtifactFiles(paths), task, rootDir);
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
private getStorageResolver(def: ArtifactDefinition) {
|
|
56
55
|
return def.storageResolver || new DefaultResolver();
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
private toComponentMap(context: BuildContext, artifactMap: [string,
|
|
60
|
-
return ComponentMap.as<ArtifactList
|
|
58
|
+
private toComponentMap(context: BuildContext, artifactMap: [string, FsArtifact][]) {
|
|
59
|
+
return ComponentMap.as<ArtifactList<FsArtifact>>(context.components, (component) => {
|
|
61
60
|
const id = component.id.toString();
|
|
62
61
|
const artifacts = artifactMap.filter(([targetId]) => targetId === id).map(([, artifact]) => artifact);
|
|
63
62
|
|
|
64
|
-
return
|
|
63
|
+
return ArtifactList.fromArray(artifacts);
|
|
65
64
|
});
|
|
66
65
|
}
|
|
67
66
|
|
|
@@ -73,8 +72,8 @@ export class ArtifactFactory {
|
|
|
73
72
|
/**
|
|
74
73
|
* generate artifacts from a build context according to the spec defined in the artifact definitions.
|
|
75
74
|
*/
|
|
76
|
-
generate(context: BuildContext, defs: ArtifactDefinition[], task: BuildTask): ComponentMap<ArtifactList
|
|
77
|
-
const tupleArr: [string,
|
|
75
|
+
generate(context: BuildContext, defs: ArtifactDefinition[], task: BuildTask): ComponentMap<ArtifactList<FsArtifact>> {
|
|
76
|
+
const tupleArr: [string, FsArtifact][] = [];
|
|
78
77
|
|
|
79
78
|
defs.forEach((def) => {
|
|
80
79
|
const artifactContext = this.getArtifactContext(def);
|
|
@@ -83,13 +82,7 @@ export class ArtifactFactory {
|
|
|
83
82
|
const rootDir = this.getRootDir(capsuleDir, def);
|
|
84
83
|
const paths = this.resolvePaths(rootDir, def);
|
|
85
84
|
if (paths && paths.length) {
|
|
86
|
-
const artifact = new
|
|
87
|
-
def,
|
|
88
|
-
this.getStorageResolver(def),
|
|
89
|
-
new ArtifactFiles(this.resolvePaths(rootDir, def)),
|
|
90
|
-
rootDir,
|
|
91
|
-
task
|
|
92
|
-
);
|
|
85
|
+
const artifact = new FsArtifact(def, new ArtifactFiles(this.resolvePaths(rootDir, def)), task, rootDir);
|
|
93
86
|
|
|
94
87
|
return context.components.forEach((component) => {
|
|
95
88
|
tupleArr.push([component.id.toString(), artifact]);
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Component } from '@teambit/component';
|
|
2
2
|
import type { ArtifactObject } from '@teambit/legacy/dist/consumer/component/sources/artifact-files';
|
|
3
|
-
import
|
|
3
|
+
import { BitId } from '@teambit/legacy/dist/bit-id';
|
|
4
|
+
import { Scope } from '@teambit/legacy/dist/scope';
|
|
5
|
+
import { ArtifactVinyl } from '@teambit/legacy/dist/consumer/component/sources/artifact';
|
|
6
|
+
import { FsArtifact } from './fs-artifact';
|
|
7
|
+
import { Artifact } from './artifact';
|
|
4
8
|
import {
|
|
5
9
|
ArtifactStorageResolver,
|
|
6
10
|
FileStorageResolver,
|
|
@@ -8,24 +12,47 @@ import {
|
|
|
8
12
|
DefaultResolver,
|
|
9
13
|
} from '../storage';
|
|
10
14
|
|
|
11
|
-
export type ResolverMap = { [key: string]:
|
|
15
|
+
export type ResolverMap<T extends Artifact> = { [key: string]: T[] };
|
|
12
16
|
|
|
13
|
-
export class ArtifactList {
|
|
14
|
-
|
|
17
|
+
export class ArtifactList<T extends Artifact> extends Array<T> {
|
|
18
|
+
byAspectNameAndName(aspectName?: string, name?: string): ArtifactList<T> {
|
|
19
|
+
const filtered = this.filter((artifact) => {
|
|
20
|
+
let cond = true;
|
|
21
|
+
if (aspectName) {
|
|
22
|
+
cond = cond && artifact.task.aspectId === aspectName;
|
|
23
|
+
}
|
|
24
|
+
if (name) {
|
|
25
|
+
cond = cond && artifact.name === name;
|
|
26
|
+
}
|
|
27
|
+
return cond;
|
|
28
|
+
});
|
|
29
|
+
return ArtifactList.fromArray(filtered);
|
|
30
|
+
}
|
|
15
31
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
byAspectNameAndTaskName(aspectName?: string, name?: string): ArtifactList<T> {
|
|
33
|
+
const filtered = this.filter((artifact) => {
|
|
34
|
+
let cond = true;
|
|
35
|
+
if (aspectName) {
|
|
36
|
+
cond = cond && artifact.task.aspectId === aspectName;
|
|
37
|
+
}
|
|
38
|
+
if (name) {
|
|
39
|
+
cond = cond && artifact.task.name === name;
|
|
40
|
+
}
|
|
41
|
+
return cond;
|
|
42
|
+
});
|
|
43
|
+
return ArtifactList.fromArray(filtered);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
isEmpty(): boolean {
|
|
47
|
+
return this.every((artifact) => artifact.files.isEmpty());
|
|
21
48
|
}
|
|
22
49
|
|
|
23
50
|
/**
|
|
24
51
|
* group artifacts by the storage resolver.
|
|
25
52
|
*/
|
|
26
|
-
groupByResolver(): ResolverMap {
|
|
27
|
-
const resolverMap: ResolverMap = {};
|
|
28
|
-
this.
|
|
53
|
+
groupByResolver(): ResolverMap<T> {
|
|
54
|
+
const resolverMap: ResolverMap<T> = {};
|
|
55
|
+
this.forEach((artifact) => {
|
|
29
56
|
const storageResolver = artifact.storageResolver;
|
|
30
57
|
const resolverArray = resolverMap[storageResolver.name];
|
|
31
58
|
if (!resolverArray) {
|
|
@@ -41,17 +68,23 @@ export class ArtifactList {
|
|
|
41
68
|
}
|
|
42
69
|
|
|
43
70
|
toObject(): ArtifactObject[] {
|
|
44
|
-
return this.
|
|
71
|
+
return this.map((artifact) => artifact.toObject());
|
|
45
72
|
}
|
|
46
73
|
|
|
47
74
|
groupByTaskId() {
|
|
48
|
-
return this.
|
|
75
|
+
return this.reduce((acc: { [key: string]: T }, artifact) => {
|
|
49
76
|
const taskId = artifact.task.aspectId;
|
|
50
77
|
acc[taskId] = artifact;
|
|
51
78
|
return acc;
|
|
52
79
|
}, {});
|
|
53
80
|
}
|
|
54
81
|
|
|
82
|
+
async getVinylsAndImportIfMissing(id: BitId, scope: Scope): Promise<ArtifactVinyl[]> {
|
|
83
|
+
if (this.isEmpty()) return [];
|
|
84
|
+
const vinyls = await Promise.all(this.map((artifact) => artifact.files.getVinylsAndImportIfMissing(id, scope)));
|
|
85
|
+
return vinyls.flat();
|
|
86
|
+
}
|
|
87
|
+
|
|
55
88
|
/**
|
|
56
89
|
* store all artifacts using the configured storage resolvers.
|
|
57
90
|
*/
|
|
@@ -61,8 +94,8 @@ export class ArtifactList {
|
|
|
61
94
|
const artifacts = byResolvers[key];
|
|
62
95
|
if (!artifacts.length) return;
|
|
63
96
|
const storageResolver = artifacts[0].storageResolver;
|
|
64
|
-
const artifactList =
|
|
65
|
-
const artifactPromises = artifactList.
|
|
97
|
+
const artifactList = ArtifactList.fromArray(artifacts);
|
|
98
|
+
const artifactPromises = artifactList.map(async (artifact) => {
|
|
66
99
|
return this.storeArtifact(storageResolver, artifact, component);
|
|
67
100
|
});
|
|
68
101
|
await Promise.all(artifactPromises);
|
|
@@ -75,7 +108,7 @@ export class ArtifactList {
|
|
|
75
108
|
// For now we are always storing also using the default resolver
|
|
76
109
|
if (storageResolver.name !== 'default') {
|
|
77
110
|
const defaultResolver = new DefaultResolver();
|
|
78
|
-
await defaultResolver.store(component, artifact);
|
|
111
|
+
await defaultResolver.store(component, artifact as FsArtifact);
|
|
79
112
|
}
|
|
80
113
|
// @ts-ignore
|
|
81
114
|
if (storageResolver.store && typeof storageResolver.store === 'function') {
|
|
@@ -95,7 +128,7 @@ export class ArtifactList {
|
|
|
95
128
|
artifact: Artifact,
|
|
96
129
|
component: Component
|
|
97
130
|
) {
|
|
98
|
-
const results = await storageResolver.store(component, artifact);
|
|
131
|
+
const results = await storageResolver.store(component, artifact as FsArtifact);
|
|
99
132
|
if (!results) return;
|
|
100
133
|
artifact.files.vinyls.map(async (file) => {
|
|
101
134
|
const url = results[file.relative];
|
|
@@ -113,11 +146,20 @@ export class ArtifactList {
|
|
|
113
146
|
*/
|
|
114
147
|
private storeArtifactFilesByResolver(storageResolver: FileStorageResolver, artifact: Artifact, component: Component) {
|
|
115
148
|
const promises = artifact.files.vinyls.map(async (file) => {
|
|
116
|
-
const url = await storageResolver.storeFile(component, artifact, file);
|
|
149
|
+
const url = await storageResolver.storeFile(component, artifact as FsArtifact, file);
|
|
117
150
|
if (url) {
|
|
118
151
|
file.url = url;
|
|
119
152
|
}
|
|
120
153
|
});
|
|
121
154
|
return Promise.all(promises);
|
|
122
155
|
}
|
|
156
|
+
|
|
157
|
+
static fromArtifactObjects(artifactObjects: ArtifactObject[]): ArtifactList<Artifact> {
|
|
158
|
+
const artifacts = artifactObjects.map((object) => Artifact.fromArtifactObject(object));
|
|
159
|
+
return ArtifactList.fromArray(artifacts);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static fromArray<T extends Artifact>(artifacts: T[]) {
|
|
163
|
+
return new ArtifactList(...artifacts);
|
|
164
|
+
}
|
|
123
165
|
}
|
package/artifact/artifact.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ArtifactFiles, ArtifactObject } from '@teambit/legacy/dist/consumer/component/sources/artifact-files';
|
|
2
|
-
import type {
|
|
3
|
-
import type { ArtifactStorageResolver } from '../storage';
|
|
2
|
+
import type { TaskDescriptor } from '../build-task';
|
|
4
3
|
import type { ArtifactDefinition } from './artifact-definition';
|
|
4
|
+
import { DefaultResolver } from '../storage/default-resolver';
|
|
5
5
|
|
|
6
6
|
export class Artifact {
|
|
7
7
|
constructor(
|
|
@@ -10,25 +10,11 @@ export class Artifact {
|
|
|
10
10
|
*/
|
|
11
11
|
readonly def: ArtifactDefinition,
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* storage resolver. can be used to replace where artifacts are stored.
|
|
15
|
-
*/
|
|
16
|
-
readonly storageResolver: ArtifactStorageResolver,
|
|
17
|
-
|
|
18
13
|
readonly files: ArtifactFiles,
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* join this with `this.paths` to get the absolute paths
|
|
22
|
-
*/
|
|
23
|
-
readonly rootDir: string,
|
|
24
|
-
|
|
25
14
|
/**
|
|
26
15
|
* the declaring task.
|
|
27
|
-
* todo: change this to taskDescriptor that has only the metadata of the task, so it could be
|
|
28
|
-
* saved into the model.
|
|
29
16
|
*/
|
|
30
|
-
readonly task:
|
|
31
|
-
|
|
17
|
+
readonly task: TaskDescriptor,
|
|
32
18
|
/**
|
|
33
19
|
* timestamp of the artifact creation.
|
|
34
20
|
*/
|
|
@@ -36,7 +22,11 @@ export class Artifact {
|
|
|
36
22
|
) {}
|
|
37
23
|
|
|
38
24
|
get storage() {
|
|
39
|
-
return this.storageResolver;
|
|
25
|
+
return this.storageResolver.name;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get storageResolver() {
|
|
29
|
+
return this.def.storageResolver || new DefaultResolver();
|
|
40
30
|
}
|
|
41
31
|
|
|
42
32
|
/**
|
|
@@ -60,6 +50,10 @@ export class Artifact {
|
|
|
60
50
|
return this.def.generatedBy || this.task.aspectId;
|
|
61
51
|
}
|
|
62
52
|
|
|
53
|
+
isEmpty(): boolean {
|
|
54
|
+
return this.files.isEmpty();
|
|
55
|
+
}
|
|
56
|
+
|
|
63
57
|
/**
|
|
64
58
|
* archive all artifact files into a tar.
|
|
65
59
|
*/
|
|
@@ -78,4 +72,19 @@ export class Artifact {
|
|
|
78
72
|
files: this.files,
|
|
79
73
|
};
|
|
80
74
|
}
|
|
75
|
+
|
|
76
|
+
static fromArtifactObject(object: ArtifactObject): Artifact {
|
|
77
|
+
const artifactDef: ArtifactDefinition = {
|
|
78
|
+
name: object.name,
|
|
79
|
+
generatedBy: object.generatedBy,
|
|
80
|
+
description: object.description,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const task: TaskDescriptor = {
|
|
84
|
+
aspectId: object.task.id,
|
|
85
|
+
name: object.task.name,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return new Artifact(artifactDef, object.files, task);
|
|
89
|
+
}
|
|
81
90
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ArtifactFiles } from '@teambit/legacy/dist/consumer/component/sources/artifact-files';
|
|
2
|
+
import type { ArtifactDefinition } from './artifact-definition';
|
|
3
|
+
import { Artifact } from './artifact';
|
|
4
|
+
import { TaskDescriptor } from '../build-task';
|
|
5
|
+
|
|
6
|
+
export class FsArtifact extends Artifact {
|
|
7
|
+
constructor(
|
|
8
|
+
/**
|
|
9
|
+
* definition of the artifact.
|
|
10
|
+
*/
|
|
11
|
+
readonly def: ArtifactDefinition,
|
|
12
|
+
|
|
13
|
+
readonly files: ArtifactFiles,
|
|
14
|
+
|
|
15
|
+
readonly task: TaskDescriptor,
|
|
16
|
+
|
|
17
|
+
readonly rootDir: string
|
|
18
|
+
) {
|
|
19
|
+
super(def, files, task);
|
|
20
|
+
}
|
|
21
|
+
}
|
package/artifact/index.ts
CHANGED
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
4
|
|
|
5
|
-
require("core-js/modules/es.array.flat.js");
|
|
6
|
-
|
|
7
5
|
require("core-js/modules/es.array.iterator.js");
|
|
8
6
|
|
|
9
|
-
require("core-js/modules/es.array.unscopables.flat.js");
|
|
10
|
-
|
|
11
7
|
require("core-js/modules/es.promise.js");
|
|
12
8
|
|
|
13
9
|
Object.defineProperty(exports, "__esModule", {
|
|
@@ -75,6 +71,26 @@ function _artifactFiles() {
|
|
|
75
71
|
return data;
|
|
76
72
|
}
|
|
77
73
|
|
|
74
|
+
function _artifactList() {
|
|
75
|
+
const data = require("./artifact-list");
|
|
76
|
+
|
|
77
|
+
_artifactList = function () {
|
|
78
|
+
return data;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return data;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function _artifact() {
|
|
85
|
+
const data = require("./artifact");
|
|
86
|
+
|
|
87
|
+
_artifact = function () {
|
|
88
|
+
return data;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return data;
|
|
92
|
+
}
|
|
93
|
+
|
|
78
94
|
class ArtifactExtractor {
|
|
79
95
|
constructor(scope, builder, pattern, options) {
|
|
80
96
|
this.scope = scope;
|
|
@@ -86,15 +102,15 @@ class ArtifactExtractor {
|
|
|
86
102
|
async list() {
|
|
87
103
|
const ids = await this.scope.idsByPattern(this.pattern);
|
|
88
104
|
const components = await this.scope.loadMany(ids);
|
|
89
|
-
const
|
|
105
|
+
const artifactListPerId = components.map(component => {
|
|
90
106
|
return {
|
|
91
107
|
id: component.id,
|
|
92
|
-
artifacts: this.builder.getArtifacts(component)
|
|
108
|
+
artifacts: this.builder.getArtifacts(component)
|
|
93
109
|
};
|
|
94
110
|
});
|
|
95
|
-
this.filterByOptions(
|
|
96
|
-
await this.saveFilesInFileSystemIfAsked(
|
|
97
|
-
return this.artifactsObjectsToExtractorResults(
|
|
111
|
+
this.filterByOptions(artifactListPerId);
|
|
112
|
+
await this.saveFilesInFileSystemIfAsked(artifactListPerId);
|
|
113
|
+
return this.artifactsObjectsToExtractorResults(artifactListPerId);
|
|
98
114
|
}
|
|
99
115
|
|
|
100
116
|
groupResultsByAspect(extractorResult) {
|
|
@@ -112,7 +128,7 @@ class ArtifactExtractor {
|
|
|
112
128
|
});
|
|
113
129
|
}
|
|
114
130
|
|
|
115
|
-
async saveFilesInFileSystemIfAsked(
|
|
131
|
+
async saveFilesInFileSystemIfAsked(artifactListPerId) {
|
|
116
132
|
const outDir = this.options.outDir;
|
|
117
133
|
|
|
118
134
|
if (!outDir) {
|
|
@@ -120,12 +136,11 @@ class ArtifactExtractor {
|
|
|
120
136
|
} // @todo: optimize this to first import all missing hashes.
|
|
121
137
|
|
|
122
138
|
|
|
123
|
-
await (0, _pMapSeries().default)(
|
|
139
|
+
await (0, _pMapSeries().default)(artifactListPerId, async ({
|
|
124
140
|
id,
|
|
125
141
|
artifacts
|
|
126
142
|
}) => {
|
|
127
|
-
const vinyls = await
|
|
128
|
-
const flattenedVinyls = vinyls.flat(); // make sure the component-dir is just one dir. without this, every slash in the component-id will create a new dir.
|
|
143
|
+
const vinyls = await artifacts.getVinylsAndImportIfMissing(id._legacy, this.scope.legacyScope); // make sure the component-dir is just one dir. without this, every slash in the component-id will create a new dir.
|
|
129
144
|
|
|
130
145
|
const idAsFilename = (0, _filenamify().default)(id.toStringWithoutVersion(), {
|
|
131
146
|
replacement: '_'
|
|
@@ -133,19 +148,19 @@ class ArtifactExtractor {
|
|
|
133
148
|
|
|
134
149
|
const compPath = _path().default.join(outDir, idAsFilename);
|
|
135
150
|
|
|
136
|
-
await Promise.all(
|
|
151
|
+
await Promise.all(vinyls.map(vinyl => _fsExtra().default.outputFile(_path().default.join(compPath, vinyl.path), vinyl.contents)));
|
|
137
152
|
});
|
|
138
153
|
}
|
|
139
154
|
|
|
140
|
-
artifactsObjectsToExtractorResults(
|
|
141
|
-
return
|
|
155
|
+
artifactsObjectsToExtractorResults(artifactListPerId) {
|
|
156
|
+
return artifactListPerId.map(({
|
|
142
157
|
id,
|
|
143
158
|
artifacts
|
|
144
159
|
}) => {
|
|
145
160
|
const results = artifacts.map(artifact => {
|
|
146
161
|
return {
|
|
147
162
|
artifactName: artifact.name,
|
|
148
|
-
aspectId: artifact.task.
|
|
163
|
+
aspectId: artifact.task.aspectId,
|
|
149
164
|
taskName: artifact.task.name || artifact.generatedBy,
|
|
150
165
|
files: artifact.files.refs.map(ref => ref.relativePath)
|
|
151
166
|
};
|
|
@@ -157,27 +172,29 @@ class ArtifactExtractor {
|
|
|
157
172
|
});
|
|
158
173
|
}
|
|
159
174
|
|
|
160
|
-
filterByOptions(
|
|
175
|
+
filterByOptions(artifactListPerId) {
|
|
161
176
|
const {
|
|
162
177
|
aspect,
|
|
163
178
|
task,
|
|
164
179
|
files
|
|
165
180
|
} = this.options;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
181
|
+
let filteredArtifacts = [];
|
|
182
|
+
artifactListPerId.forEach(item => {
|
|
183
|
+
filteredArtifacts = item.artifacts.filter(artifact => {
|
|
184
|
+
if (aspect && aspect !== artifact.task.aspectId) return false;
|
|
169
185
|
if (task && task !== artifact.task.name) return false;
|
|
170
186
|
return true;
|
|
171
187
|
});
|
|
172
188
|
|
|
173
189
|
if (files) {
|
|
174
|
-
item.artifacts.
|
|
190
|
+
filteredArtifacts = item.artifacts.map(artifact => {
|
|
175
191
|
const refs = artifact.files.refs.filter(ref => (0, _minimatch().default)(ref.relativePath, files));
|
|
176
|
-
artifact.
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
item.artifacts = item.artifacts.filter(artifact => artifact.files.refs.length);
|
|
192
|
+
return new (_artifact().Artifact)(artifact.def, new (_artifactFiles().ArtifactFiles)([], [], refs), artifact.task);
|
|
193
|
+
}) // remove artifacts with no files
|
|
194
|
+
.filter(artifact => !artifact.isEmpty());
|
|
180
195
|
}
|
|
196
|
+
|
|
197
|
+
item.artifacts = _artifactList().ArtifactList.fromArray(filteredArtifacts);
|
|
181
198
|
});
|
|
182
199
|
}
|
|
183
200
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["ArtifactExtractor","constructor","scope","builder","pattern","options","list","ids","idsByPattern","components","loadMany","
|
|
1
|
+
{"version":3,"names":["ArtifactExtractor","constructor","scope","builder","pattern","options","list","ids","idsByPattern","components","loadMany","artifactListPerId","map","component","id","artifacts","getArtifacts","filterByOptions","saveFilesInFileSystemIfAsked","artifactsObjectsToExtractorResults","groupResultsByAspect","extractorResult","result","reduce","acc","current","aspectId","push","outDir","pMapSeries","vinyls","getVinylsAndImportIfMissing","_legacy","legacyScope","idAsFilename","filenamify","toStringWithoutVersion","replacement","compPath","path","join","Promise","all","vinyl","fs","outputFile","contents","results","artifact","artifactName","name","task","taskName","generatedBy","files","refs","ref","relativePath","aspect","filteredArtifacts","forEach","item","filter","minimatch","Artifact","def","ArtifactFiles","isEmpty","ArtifactList","fromArray"],"sources":["artifact-extractor.ts"],"sourcesContent":["import path from 'path';\nimport filenamify from 'filenamify';\nimport fs from 'fs-extra';\nimport { ScopeMain } from '@teambit/scope';\nimport { ComponentID } from '@teambit/component-id';\nimport pMapSeries from 'p-map-series';\nimport minimatch from 'minimatch';\nimport { ArtifactFiles } from '@teambit/legacy/dist/consumer/component/sources/artifact-files';\nimport { BuilderMain } from '../builder.main.runtime';\nimport { ArtifactsOpts } from './artifacts.cmd';\nimport { ArtifactList } from './artifact-list';\nimport { Artifact } from './artifact';\n\nexport type ExtractorResult = {\n id: ComponentID;\n artifacts: ExtractorArtifactResult[];\n};\n\nexport type ExtractorArtifactResult = {\n artifactName: string;\n aspectId: string;\n taskName: string;\n files: string[];\n};\n\nexport type ExtractorResultGrouped = {\n id: ComponentID;\n artifacts: { [aspectId: string]: ExtractorArtifactResult[] };\n};\n\ntype ArtifactListPerId = {\n id: ComponentID;\n artifacts: ArtifactList<Artifact>;\n};\n\nexport class ArtifactExtractor {\n constructor(\n private scope: ScopeMain,\n private builder: BuilderMain,\n private pattern: string,\n private options: ArtifactsOpts\n ) {}\n\n async list(): Promise<ExtractorResult[]> {\n const ids = await this.scope.idsByPattern(this.pattern);\n const components = await this.scope.loadMany(ids);\n const artifactListPerId: ArtifactListPerId[] = components.map((component) => {\n return {\n id: component.id,\n artifacts: this.builder.getArtifacts(component),\n };\n });\n this.filterByOptions(artifactListPerId);\n await this.saveFilesInFileSystemIfAsked(artifactListPerId);\n\n return this.artifactsObjectsToExtractorResults(artifactListPerId);\n }\n\n groupResultsByAspect(extractorResult: ExtractorResult[]) {\n return extractorResult.map((result) => {\n const artifacts = result.artifacts.reduce((acc, current) => {\n (acc[current.aspectId] ||= []).push(current);\n return acc;\n }, {});\n return { id: result.id, artifacts };\n });\n }\n\n private async saveFilesInFileSystemIfAsked(artifactListPerId: ArtifactListPerId[]) {\n const outDir = this.options.outDir;\n if (!outDir) {\n return;\n }\n // @todo: optimize this to first import all missing hashes.\n await pMapSeries(artifactListPerId, async ({ id, artifacts }) => {\n const vinyls = await artifacts.getVinylsAndImportIfMissing(id._legacy, this.scope.legacyScope);\n // make sure the component-dir is just one dir. without this, every slash in the component-id will create a new dir.\n const idAsFilename = filenamify(id.toStringWithoutVersion(), { replacement: '_' });\n const compPath = path.join(outDir, idAsFilename);\n await Promise.all(vinyls.map((vinyl) => fs.outputFile(path.join(compPath, vinyl.path), vinyl.contents)));\n });\n }\n\n private artifactsObjectsToExtractorResults(artifactListPerId: ArtifactListPerId[]): ExtractorResult[] {\n return artifactListPerId.map(({ id, artifacts }) => {\n const results: ExtractorArtifactResult[] = artifacts.map((artifact) => {\n return {\n artifactName: artifact.name,\n aspectId: artifact.task.aspectId,\n taskName: artifact.task.name || artifact.generatedBy,\n files: artifact.files.refs.map((ref) => ref.relativePath),\n };\n });\n return {\n id,\n artifacts: results,\n };\n });\n }\n\n private filterByOptions(artifactListPerId: ArtifactListPerId[]) {\n const { aspect, task, files } = this.options;\n let filteredArtifacts: Artifact[] = [];\n\n artifactListPerId.forEach((item) => {\n filteredArtifacts = item.artifacts.filter((artifact) => {\n if (aspect && aspect !== artifact.task.aspectId) return false;\n if (task && task !== artifact.task.name) return false;\n return true;\n });\n if (files) {\n filteredArtifacts = item.artifacts\n .map((artifact) => {\n const refs = artifact.files.refs.filter((ref) => minimatch(ref.relativePath, files));\n return new Artifact(artifact.def, new ArtifactFiles([], [], refs), artifact.task);\n })\n // remove artifacts with no files\n .filter((artifact) => !artifact.isEmpty());\n }\n item.artifacts = ArtifactList.fromArray(filteredArtifacts);\n });\n }\n}\n"],"mappings":"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"}
|