@teambit/builder 1.0.107 → 1.0.108

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.
Files changed (55) hide show
  1. package/build-pipe.ts +216 -0
  2. package/build-pipeline-order.ts +192 -0
  3. package/build-pipeline-result-list.ts +97 -0
  4. package/build-task.ts +151 -0
  5. package/build.cmd.ts +157 -0
  6. package/builder-env-type.ts +16 -0
  7. package/builder.aspect.ts +5 -0
  8. package/builder.graphql.ts +185 -0
  9. package/builder.main.runtime.ts +493 -0
  10. package/builder.route.ts +95 -0
  11. package/dist/artifact/artifact-definition.d.ts +2 -2
  12. package/dist/artifact/artifact-extractor.d.ts +3 -3
  13. package/dist/artifact/artifact-factory.d.ts +1 -1
  14. package/dist/artifact/artifact-factory.js +1 -2
  15. package/dist/artifact/artifact-factory.js.map +1 -1
  16. package/dist/artifact/artifact-list.d.ts +1 -1
  17. package/dist/artifact/artifact.d.ts +1 -1
  18. package/dist/artifact/artifacts.cmd.d.ts +1 -1
  19. package/dist/build-pipe.d.ts +3 -3
  20. package/dist/build-pipe.js +5 -12
  21. package/dist/build-pipe.js.map +1 -1
  22. package/dist/build-pipeline-result-list.d.ts +2 -2
  23. package/dist/build-pipeline-result-list.js +1 -2
  24. package/dist/build-pipeline-result-list.js.map +1 -1
  25. package/dist/build-task.d.ts +1 -1
  26. package/dist/build.cmd.d.ts +1 -1
  27. package/dist/builder.composition.d.ts +2 -2
  28. package/dist/builder.graphql.d.ts +17 -17
  29. package/dist/builder.graphql.js +4 -8
  30. package/dist/builder.graphql.js.map +1 -1
  31. package/dist/builder.main.runtime.d.ts +5 -5
  32. package/dist/builder.main.runtime.js +11 -14
  33. package/dist/builder.main.runtime.js.map +1 -1
  34. package/dist/builder.route.d.ts +1 -1
  35. package/dist/builder.route.js +3 -3
  36. package/dist/builder.route.js.map +1 -1
  37. package/dist/builder.service.d.ts +9 -9
  38. package/dist/builder.service.js +4 -4
  39. package/dist/builder.service.js.map +1 -1
  40. package/dist/pipeline.d.ts +1 -1
  41. package/dist/{preview-1703590665075.js → preview-1703647408454.js} +2 -2
  42. package/dist/storage/storage-resolver.d.ts +2 -2
  43. package/dist/task-results-list.js +2 -8
  44. package/dist/task-results-list.js.map +1 -1
  45. package/dist/tasks-queue.d.ts +1 -1
  46. package/dist/types.d.ts +2 -2
  47. package/index.ts +20 -0
  48. package/package.json +26 -33
  49. package/pipeline.ts +104 -0
  50. package/task-results-list.ts +68 -0
  51. package/task.ts +50 -0
  52. package/tasks-queue.ts +40 -0
  53. package/tsconfig.json +16 -21
  54. package/types/asset.d.ts +15 -3
  55. package/types.ts +37 -0
package/build.cmd.ts ADDED
@@ -0,0 +1,157 @@
1
+ import { Command, CommandOptions } from '@teambit/cli';
2
+ import { Logger } from '@teambit/logger';
3
+ import prettyTime from 'pretty-time';
4
+ import { OutsideWorkspaceError, Workspace } from '@teambit/workspace';
5
+ import { COMPONENT_PATTERN_HELP } from '@teambit/legacy/dist/constants';
6
+ import chalk from 'chalk';
7
+ import { BuilderMain } from './builder.main.runtime';
8
+
9
+ type BuildOpts = {
10
+ all: boolean; // deprecated. use unmodified
11
+ unmodified?: boolean;
12
+ dev: boolean;
13
+ rebuild: boolean;
14
+ install: boolean;
15
+ cachePackagesOnCapsulesRoot: boolean;
16
+ reuseCapsules: boolean;
17
+ tasks: string;
18
+ listTasks?: string;
19
+ skipTests?: boolean;
20
+ failFast?: boolean;
21
+ includeSnap?: boolean;
22
+ includeTag?: boolean;
23
+ };
24
+
25
+ export class BuilderCmd implements Command {
26
+ name = 'build [component-pattern]';
27
+ description = 'run set of tasks for build.';
28
+ extendedDescription = 'by default, only new and modified components are built';
29
+ arguments = [{ name: 'component-pattern', description: COMPONENT_PATTERN_HELP }];
30
+ helpUrl = 'reference/build-pipeline/builder-overview';
31
+ alias = '';
32
+ group = 'development';
33
+ options = [
34
+ ['a', 'all', 'DEPRECATED. use --unmodified'],
35
+ ['u', 'unmodified', 'include unmodified components (by default, only new and modified components are built)'],
36
+ ['d', 'dev', 'run the pipeline in dev mode'],
37
+ ['', 'install', 'install core aspects in capsules'],
38
+ ['', 'reuse-capsules', 'avoid deleting the capsules root-dir before starting the build'],
39
+ [
40
+ '',
41
+ 'tasks <string>',
42
+ `build the specified task(s) only. for multiple tasks, separate by a comma and wrap with quotes.
43
+ specify the task-name (e.g. "TypescriptCompiler") or the task-aspect-id (e.g. teambit.compilation/compiler)`,
44
+ ],
45
+ ['', 'cache-packages-on-capsule-root', 'set the package-manager cache on the capsule root'],
46
+ [
47
+ '',
48
+ 'list-tasks <string>',
49
+ 'list tasks of an env or a component-id for each one of the pipelines: build, tag and snap',
50
+ ],
51
+ ['', 'skip-tests', 'skip running component tests during build process'],
52
+ [
53
+ '',
54
+ 'fail-fast',
55
+ 'stop pipeline execution on the first failed task (by default a task is skipped only when its dependency failed)',
56
+ ],
57
+ [
58
+ '',
59
+ 'include-snap',
60
+ 'EXPERIMENTAL. include snap pipeline tasks. Warning: this may deploy/publish if you have such tasks',
61
+ ],
62
+ [
63
+ '',
64
+ 'include-tag',
65
+ 'EXPERIMENTAL. include tag pipeline tasks. Warning: this may deploy/publish if you have such tasks',
66
+ ],
67
+ ] as CommandOptions;
68
+
69
+ constructor(private builder: BuilderMain, private workspace: Workspace, private logger: Logger) {}
70
+
71
+ async report(
72
+ [pattern]: [string],
73
+ {
74
+ all = false,
75
+ unmodified = false,
76
+ dev = false,
77
+ install = false,
78
+ cachePackagesOnCapsulesRoot = false,
79
+ reuseCapsules = false,
80
+ tasks,
81
+ listTasks,
82
+ skipTests,
83
+ failFast,
84
+ includeSnap,
85
+ includeTag,
86
+ }: BuildOpts
87
+ ): Promise<string> {
88
+ if (!this.workspace) throw new OutsideWorkspaceError();
89
+ if (all) {
90
+ this.logger.consoleWarning(`--all is deprecated, please use --unmodified instead`);
91
+ unmodified = true;
92
+ }
93
+ if (listTasks) {
94
+ return this.getListTasks(listTasks);
95
+ }
96
+
97
+ this.logger.setStatusLine('build');
98
+ const start = process.hrtime();
99
+ const components = await this.workspace.getComponentsByUserInput(unmodified, pattern, true);
100
+ if (!components.length) {
101
+ return chalk.bold(
102
+ `no components found to build. use "--unmodified" flag to build all components or specify the ids to build, otherwise, only new and modified components will be built`
103
+ );
104
+ }
105
+
106
+ const envsExecutionResults = await this.builder.build(
107
+ components,
108
+ {
109
+ installOptions: {
110
+ installTeambitBit: install,
111
+ packageManagerConfigRootDir: this.workspace.path,
112
+ },
113
+ linkingOptions: { linkTeambitBit: !install },
114
+ emptyRootDir: !reuseCapsules,
115
+ getExistingAsIs: reuseCapsules,
116
+ cachePackagesOnCapsulesRoot,
117
+ },
118
+ {
119
+ dev,
120
+ tasks: tasks ? tasks.split(',').map((task) => task.trim()) : [],
121
+ skipTests,
122
+ exitOnFirstFailedTask: failFast,
123
+ },
124
+ {
125
+ includeSnap,
126
+ includeTag,
127
+ }
128
+ );
129
+ this.logger.console(`build output can be found in path: ${envsExecutionResults.capsuleRootDir}`);
130
+ const duration = prettyTime(process.hrtime(start));
131
+ const succeedOrFailed = envsExecutionResults.hasErrors() ? 'failed' : 'succeeded';
132
+ const msg = `build ${succeedOrFailed}. completed in ${duration}.`;
133
+ if (envsExecutionResults.hasErrors()) {
134
+ this.logger.consoleFailure(msg);
135
+ }
136
+ envsExecutionResults.throwErrorsIfExist();
137
+ return chalk.green(msg);
138
+ }
139
+
140
+ private async getListTasks(componentIdStr: string): Promise<string> {
141
+ const compId = await this.workspace.resolveComponentId(componentIdStr);
142
+ const component = await this.workspace.get(compId);
143
+ const results = this.builder.listTasks(component);
144
+ return `${chalk.green('Task List')}
145
+ id: ${results.id.toString()}
146
+ envId: ${results.envId}
147
+
148
+ ${chalk.bold('Build Pipeline Tasks:')}
149
+ ${results.buildTasks.join('\n')}
150
+
151
+ ${chalk.bold('Tag Pipeline Tasks:')}
152
+ ${results.tagTasks.join('\n')}
153
+
154
+ ${chalk.bold('Snap Pipeline Tasks:')}
155
+ ${results.snapTasks.join('\n') || '<N/A>'}`;
156
+ }
157
+ }
@@ -0,0 +1,16 @@
1
+ import { Pipeline } from './pipeline';
2
+
3
+ export interface BuilderEnv {
4
+ /**
5
+ * return a build pipeline instance.
6
+ */
7
+ build(): Pipeline;
8
+ /**
9
+ * return a snap pipeline instance.
10
+ */
11
+ snap(): Pipeline;
12
+ /**
13
+ * return a tag pipeline instance.
14
+ */
15
+ tag(): Pipeline;
16
+ }
@@ -0,0 +1,5 @@
1
+ import { Aspect } from '@teambit/harmony';
2
+
3
+ export const BuilderAspect = Aspect.create({
4
+ id: 'teambit.pipelines/builder',
5
+ });
@@ -0,0 +1,185 @@
1
+ import { Component, ComponentID } from '@teambit/component';
2
+ import gql from 'graphql-tag';
3
+ import { Logger } from '@teambit/logger';
4
+ import isBinaryPath from 'is-binary-path';
5
+ import { BuilderMain } from './builder.main.runtime';
6
+ import { PipelineReport } from './build-pipeline-result-list';
7
+
8
+ type ArtifactGQLFile = {
9
+ /**
10
+ * same as the path - used for GQL caching
11
+ */
12
+ id: string;
13
+ /**
14
+ * name of the artifact file
15
+ */
16
+ name: string;
17
+ /**
18
+ * path of the artifact file
19
+ */
20
+ path: string;
21
+ /**
22
+ * artifact file content (only for text files). Use /api/<component-id>/~aspect/builder/<extension-id>/~<path> to fetch binary file data
23
+ */
24
+ content?: string;
25
+ /**
26
+ * REST endpoint to fetch artifact data from. /api/<component-id>/~aspect/builder/<extension-id>/~<pat
27
+ */
28
+ downloadUrl?: string;
29
+ /**
30
+ * Remote storage url to resolve artifact file from
31
+ */
32
+ externalUrl?: string;
33
+ /**
34
+ * Size in bytes
35
+ */
36
+ size: number;
37
+ };
38
+
39
+ type ArtifactGQLData = {
40
+ name: string;
41
+ description?: string;
42
+ storage?: string;
43
+ generatedBy: string;
44
+ files: ArtifactGQLFile[];
45
+ };
46
+ type TaskReport = PipelineReport & {
47
+ artifact?: ArtifactGQLData;
48
+ componentId: ComponentID;
49
+ };
50
+
51
+ export function builderSchema(builder: BuilderMain, logger: Logger) {
52
+ return {
53
+ typeDefs: gql`
54
+ type TaskReport {
55
+ # for GQL caching - taskId + taskName
56
+ id: String!
57
+ taskId: String!
58
+ taskName: String!
59
+ description: String
60
+ startTime: String
61
+ endTime: String
62
+ errors: [String!]
63
+ warnings: [String!]
64
+ artifact(path: String): Artifact
65
+ }
66
+
67
+ type ArtifactFile {
68
+ # for GQL caching - same as the path
69
+ id: String!
70
+ # name of the artifact file
71
+ name: String
72
+ # path of the artifact file
73
+ path: String!
74
+ # artifact file content (only for text files). Use /api/<component-id>/~aspect/builder/<extension-id>/~<path> to fetch binary file data
75
+ content: String
76
+ # REST endpoint to fetch artifact data from. /api/<component-id>/~aspect/builder/<extension-id>/~<path>
77
+ downloadUrl: String
78
+ # Remote storage url to resolve artifact file from
79
+ externalUrl: String
80
+ # size in bytes
81
+ size: Int!
82
+ }
83
+
84
+ type Artifact {
85
+ # for GQL caching - PipelineId + Artifact Name
86
+ id: String!
87
+ # artifact name
88
+ name: String!
89
+ description: String
90
+ storage: String
91
+ generatedBy: String
92
+ files: [ArtifactFile!]!
93
+ }
94
+
95
+ extend type Component {
96
+ pipelineReport(taskId: String): [TaskReport!]!
97
+ }
98
+ `,
99
+
100
+ resolvers: {
101
+ Component: {
102
+ pipelineReport: async (component: Component, { taskId }: { taskId?: string }) => {
103
+ try {
104
+ const builderData = builder.getBuilderData(component);
105
+ const pipeline = builderData?.pipeline || [];
106
+ const artifacts = taskId
107
+ ? builder.getArtifactsByAspect(component, taskId)
108
+ : builder.getArtifacts(component);
109
+ const artifactsWithVinyl = await Promise.all(
110
+ artifacts.map(async (artifact) => {
111
+ const id = artifact.task.aspectId;
112
+ const name = artifact.task.name as string;
113
+ try {
114
+ const artifactFiles = (await builder.getArtifactsVinylByAspectAndTaskName(component, id, name)).map(
115
+ (vinyl) => {
116
+ const { basename, path, contents } = vinyl || {};
117
+ const isBinary = path && isBinaryPath(path);
118
+ const content = !isBinary ? contents?.toString('utf-8') : undefined;
119
+ const size = contents.byteLength;
120
+ const downloadUrl = encodeURI(
121
+ builder.getDownloadUrlForArtifact(component.id, artifact.task.aspectId, path)
122
+ );
123
+ const externalUrl = vinyl.url;
124
+ return { id: path, name: basename, path, content, downloadUrl, externalUrl, size };
125
+ }
126
+ );
127
+ return {
128
+ id: `${id}-${name}-${artifact.name}`,
129
+ name: artifact.name,
130
+ description: artifact.description,
131
+ task: artifact.task,
132
+ storage: artifact.storage,
133
+ generatedBy: artifact.generatedBy,
134
+ files: artifactFiles,
135
+ };
136
+ } catch (e: any) {
137
+ logger.error(e.toString());
138
+ return {
139
+ id: `${id}-${name}-${artifact.name}`,
140
+ name: artifact.name,
141
+ description: artifact.description,
142
+ task: artifact.task,
143
+ storage: artifact.storage,
144
+ generatedBy: artifact.generatedBy,
145
+ files: [],
146
+ };
147
+ }
148
+ })
149
+ );
150
+
151
+ const result = pipeline
152
+ .filter((task) => !taskId || task.taskId === taskId)
153
+ .map((task) => ({
154
+ ...task,
155
+ id: `filter-${taskId || ''}`,
156
+ artifact: artifactsWithVinyl.find(
157
+ (data) => data.task.aspectId === task.taskId && data.task.name === task.taskName
158
+ ),
159
+ }));
160
+
161
+ return result;
162
+ } catch (e: any) {
163
+ logger.error(e.toString());
164
+ return [];
165
+ }
166
+ },
167
+ },
168
+ TaskReport: {
169
+ id: (taskReport: TaskReport & { id?: string }) =>
170
+ `${(taskReport.id && `${taskReport.id}-`) || ''}${taskReport.taskId}-${taskReport.taskName}`,
171
+ description: (taskReport: TaskReport) => taskReport.taskDescription,
172
+ errors: (taskReport: TaskReport) => taskReport.errors?.map((e) => e.toString()) || [],
173
+ warnings: (taskReport: TaskReport) => taskReport.warnings || [],
174
+ artifact: async (taskReport: TaskReport, { path: pathFilter }: { path?: string }) => {
175
+ if (!taskReport.artifact) return undefined;
176
+ return {
177
+ id: `${taskReport.taskId}-${taskReport.taskName}-${taskReport.artifact?.name}-${pathFilter || ''}`,
178
+ ...taskReport.artifact,
179
+ files: taskReport.artifact.files.filter((file) => !pathFilter || file.path === pathFilter),
180
+ };
181
+ },
182
+ },
183
+ },
184
+ };
185
+ }