@enspirit/emb 0.0.9 → 0.1.0

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 (123) hide show
  1. package/README.md +50 -28
  2. package/dist/src/cli/abstract/BaseCommand.d.ts +6 -0
  3. package/dist/src/cli/abstract/BaseCommand.js +33 -0
  4. package/dist/src/cli/abstract/FlavouredCommand.d.ts +2 -1
  5. package/dist/src/cli/abstract/FlavouredCommand.js +4 -3
  6. package/dist/src/cli/abstract/index.d.ts +1 -0
  7. package/dist/src/cli/abstract/index.js +1 -0
  8. package/dist/src/cli/commands/clean.d.ts +2 -2
  9. package/dist/src/cli/commands/clean.js +7 -6
  10. package/dist/src/cli/commands/components/index.d.ts +5 -4
  11. package/dist/src/cli/commands/components/index.js +13 -15
  12. package/dist/src/cli/commands/config/print.d.ts +2 -2
  13. package/dist/src/cli/commands/containers/index.d.ts +2 -2
  14. package/dist/src/cli/commands/containers/index.js +3 -3
  15. package/dist/src/cli/commands/containers/prune.d.ts +2 -2
  16. package/dist/src/cli/commands/containers/prune.js +2 -2
  17. package/dist/src/cli/commands/images/delete.d.ts +2 -2
  18. package/dist/src/cli/commands/images/delete.js +3 -2
  19. package/dist/src/cli/commands/images/index.d.ts +2 -2
  20. package/dist/src/cli/commands/images/index.js +3 -3
  21. package/dist/src/cli/commands/images/prune.d.ts +2 -2
  22. package/dist/src/cli/commands/images/prune.js +3 -2
  23. package/dist/src/cli/commands/{components → resources}/build.d.ts +3 -3
  24. package/dist/src/cli/commands/{components → resources}/build.js +11 -10
  25. package/dist/src/cli/commands/resources/index.d.ts +9 -0
  26. package/dist/src/cli/commands/resources/index.js +28 -0
  27. package/dist/src/cli/commands/tasks/index.d.ts +2 -2
  28. package/dist/src/cli/commands/tasks/index.js +4 -4
  29. package/dist/src/cli/commands/tasks/run.d.ts +2 -2
  30. package/dist/src/cli/commands/tasks/run.js +12 -5
  31. package/dist/src/cli/commands/up.js +5 -4
  32. package/dist/src/cli/hooks/init.js +1 -26
  33. package/dist/src/cli/utils.d.ts +1 -0
  34. package/dist/src/cli/utils.js +26 -0
  35. package/dist/src/config/convert.d.ts +2 -4
  36. package/dist/src/config/convert.js +12 -35
  37. package/dist/src/config/index.d.ts +3 -4
  38. package/dist/src/config/index.js +2 -3
  39. package/dist/src/config/schema.d.ts +69 -48
  40. package/dist/src/config/schema.json +185 -99
  41. package/dist/src/config/types.d.ts +13 -46
  42. package/dist/src/config/types.js +1 -1
  43. package/dist/src/config/validation.d.ts +3 -3
  44. package/dist/src/config/validation.js +9 -9
  45. package/dist/src/context.d.ts +1 -1
  46. package/dist/src/context.js +1 -0
  47. package/dist/src/docker/images/index.d.ts +0 -1
  48. package/dist/src/docker/images/index.js +0 -1
  49. package/dist/src/docker/index.d.ts +1 -1
  50. package/dist/src/docker/index.js +1 -1
  51. package/dist/src/docker/operations/containers/ExecContainerOperation.js +2 -1
  52. package/dist/src/docker/operations/images/BuildImageOperation.d.ts +7 -8
  53. package/dist/src/docker/operations/images/BuildImageOperation.js +16 -10
  54. package/dist/src/docker/resources/DockerImageResource.js +56 -0
  55. package/dist/src/docker/resources/index.d.ts +1 -0
  56. package/dist/src/docker/resources/index.js +1 -0
  57. package/dist/src/docker/utils.d.ts +1 -7
  58. package/dist/src/docker/utils.js +3 -7
  59. package/dist/src/errors.d.ts +9 -6
  60. package/dist/src/errors.js +15 -9
  61. package/dist/src/index.d.ts +2 -0
  62. package/dist/src/index.js +2 -0
  63. package/dist/src/monorepo/component.d.ts +14 -19
  64. package/dist/src/monorepo/component.js +42 -58
  65. package/dist/src/monorepo/config.d.ts +13 -15
  66. package/dist/src/monorepo/config.js +15 -46
  67. package/dist/src/monorepo/index.d.ts +1 -0
  68. package/dist/src/monorepo/index.js +1 -0
  69. package/dist/src/monorepo/monorepo.d.ts +13 -10
  70. package/dist/src/monorepo/monorepo.js +78 -19
  71. package/dist/src/monorepo/operations/components/index.d.ts +0 -1
  72. package/dist/src/monorepo/operations/components/index.js +0 -1
  73. package/dist/src/monorepo/operations/fs/CreateFileOperation.d.ts +11 -0
  74. package/dist/src/monorepo/operations/fs/CreateFileOperation.js +31 -0
  75. package/dist/src/monorepo/operations/fs/index.d.ts +1 -0
  76. package/dist/src/monorepo/operations/fs/index.js +1 -0
  77. package/dist/src/monorepo/operations/index.d.ts +1 -0
  78. package/dist/src/monorepo/operations/index.js +1 -0
  79. package/dist/src/monorepo/operations/resources/BuildResourcesOperation.d.ts +27 -0
  80. package/dist/src/monorepo/operations/resources/BuildResourcesOperation.js +146 -0
  81. package/dist/src/monorepo/operations/tasks/RunTasksOperation.js +1 -1
  82. package/dist/src/monorepo/plugins/{ComponentDiscoverPlugin.d.ts → AutoDockerPlugin.d.ts} +4 -4
  83. package/dist/src/monorepo/plugins/AutoDockerPlugin.js +46 -0
  84. package/dist/src/monorepo/plugins/EmbfileLoaderPlugin.js +9 -2
  85. package/dist/src/monorepo/plugins/index.d.ts +1 -1
  86. package/dist/src/monorepo/plugins/index.js +3 -3
  87. package/dist/src/monorepo/resources/FileResource.d.ts +1 -0
  88. package/dist/src/monorepo/resources/FileResource.js +13 -0
  89. package/dist/src/monorepo/resources/ResourceFactory.d.ts +23 -0
  90. package/dist/src/monorepo/resources/ResourceFactory.js +16 -0
  91. package/dist/src/monorepo/resources/index.d.ts +1 -0
  92. package/dist/src/monorepo/resources/index.js +1 -0
  93. package/dist/src/monorepo/store/index.d.ts +1 -1
  94. package/dist/src/monorepo/store/index.js +10 -2
  95. package/dist/src/monorepo/taskManagerFactory.d.ts +2 -2
  96. package/dist/src/monorepo/taskManagerFactory.js +1 -2
  97. package/dist/src/monorepo/types.d.ts +18 -5
  98. package/dist/src/monorepo/utils/{findRunOrder.d.ts → EMBCollection.d.ts} +3 -9
  99. package/dist/src/monorepo/utils/EMBCollection.js +101 -0
  100. package/dist/src/monorepo/utils/graph.d.ts +15 -0
  101. package/dist/src/monorepo/utils/graph.js +84 -0
  102. package/dist/src/monorepo/utils/index.d.ts +7 -1
  103. package/dist/src/monorepo/utils/index.js +14 -1
  104. package/dist/src/monorepo/utils/types.d.ts +2 -0
  105. package/dist/src/monorepo/utils/types.js +1 -0
  106. package/dist/src/operations/abstract/AbstractOperation.d.ts +3 -1
  107. package/dist/src/operations/abstract/AbstractOperation.js +3 -0
  108. package/dist/src/operations/types.d.ts +1 -1
  109. package/dist/src/prerequisites/GitPrerequisitePlugin.js +2 -2
  110. package/dist/src/utils/TemplateExpander.d.ts +2 -1
  111. package/dist/src/utils/TemplateExpander.js +3 -1
  112. package/oclif.manifest.json +105 -68
  113. package/package.json +7 -2
  114. package/dist/src/docker/images/buildImage.d.ts +0 -19
  115. package/dist/src/docker/images/buildImage.js +0 -64
  116. package/dist/src/docker/types.d.ts +0 -14
  117. package/dist/src/monorepo/operations/components/BuildComponentsOperation.d.ts +0 -23
  118. package/dist/src/monorepo/operations/components/BuildComponentsOperation.js +0 -157
  119. package/dist/src/monorepo/plugins/ComponentDiscoverPlugin.js +0 -44
  120. package/dist/src/monorepo/project.d.ts +0 -6
  121. package/dist/src/monorepo/project.js +0 -8
  122. package/dist/src/monorepo/utils/findRunOrder.js +0 -165
  123. /package/dist/src/docker/{types.js → resources/DockerImageResource.d.ts} +0 -0
@@ -1,3 +1,4 @@
1
+ import jsonpatch from 'fast-json-patch';
1
2
  import { join } from 'node:path';
2
3
  import { TemplateExpander } from '../utils/index.js';
3
4
  import { Component } from './component.js';
@@ -5,16 +6,15 @@ import { MonorepoConfig } from './config.js';
5
6
  import { getPlugin } from './plugins/index.js';
6
7
  import { EMBStore } from './store/index.js';
7
8
  export class Monorepo {
9
+ defaultFlavor;
8
10
  _config;
9
11
  _store;
10
12
  initialized = false;
11
- constructor(config) {
13
+ constructor(config, defaultFlavor = 'default') {
14
+ this.defaultFlavor = defaultFlavor;
15
+ console.log('WEIRD BECAUSE, adnsod', defaultFlavor);
12
16
  this._config = new MonorepoConfig(config);
13
17
  }
14
- // TODO: cache/improve
15
- get components() {
16
- return this._config.components.map((c) => new Component(c, this));
17
- }
18
18
  get config() {
19
19
  return this._config.toJSON();
20
20
  }
@@ -22,7 +22,7 @@ export class Monorepo {
22
22
  return this._config.defaults;
23
23
  }
24
24
  get flavors() {
25
- return this._config.flavors.map((f) => f.name);
25
+ return this._config.flavors;
26
26
  }
27
27
  get name() {
28
28
  return this._config.project.name;
@@ -31,28 +31,37 @@ export class Monorepo {
31
31
  return this._config.project.rootDir;
32
32
  }
33
33
  get currentFlavor() {
34
- return this._config.currentFlavor;
34
+ return this.defaultFlavor;
35
35
  }
36
36
  get store() {
37
37
  return this._store;
38
38
  }
39
+ get components() {
40
+ return Object.entries(this._config.components).map(([name, c]) => new Component(name, c, this));
41
+ }
42
+ component(name) {
43
+ return new Component(name, this._config.component(name), this);
44
+ }
39
45
  get tasks() {
40
- const globalTasks = (this._config.tasks || [])?.map((t) => {
46
+ const globalTasks = Object.entries(this._config.tasks || {}).map(([name, task]) => {
41
47
  return {
42
- ...t,
43
- id: `global:${t.name}`,
48
+ ...task,
49
+ name,
50
+ id: `global:${name}`,
44
51
  };
45
52
  });
46
53
  return this.components.reduce((tasks, cmp) => {
47
- return [...tasks, ...cmp.tasks];
54
+ const cmpTasks = Object.entries(cmp.tasks || {}).map(([name, task]) => {
55
+ return {
56
+ ...task,
57
+ name,
58
+ component: cmp.name,
59
+ id: `${cmp.name}:${name}`,
60
+ };
61
+ });
62
+ return [...tasks, ...cmpTasks];
48
63
  }, globalTasks);
49
64
  }
50
- get vars() {
51
- return this._config.vars;
52
- }
53
- component(name) {
54
- return new Component(this._config.component(name), this);
55
- }
56
65
  task(nameOrId) {
57
66
  const byId = this.tasks.find((t) => t.id === nameOrId);
58
67
  if (byId) {
@@ -67,6 +76,35 @@ export class Monorepo {
67
76
  }
68
77
  return found[0];
69
78
  }
79
+ get resources() {
80
+ return this.components.reduce((resources, cmp) => {
81
+ const cmpResources = Object.entries(cmp.resources || {}).map(([name, task]) => {
82
+ return {
83
+ ...task,
84
+ name,
85
+ id: `${cmp.name}:${name}`,
86
+ };
87
+ });
88
+ return [...resources, ...cmpResources];
89
+ }, []);
90
+ }
91
+ resource(nameOrId) {
92
+ const byId = this.resources.find((t) => t.id === nameOrId);
93
+ if (byId) {
94
+ return byId;
95
+ }
96
+ const found = this.resources.filter((t) => t.name === nameOrId);
97
+ if (found.length > 1) {
98
+ throw new Error(`Resource name ambigous, found multiple matches: ${nameOrId}`);
99
+ }
100
+ if (found.length === 0) {
101
+ throw new Error(`Resource not found: ${nameOrId}`);
102
+ }
103
+ return found[0];
104
+ }
105
+ get vars() {
106
+ return this._config.vars;
107
+ }
70
108
  async expand(strOrRecord, expander = new TemplateExpander()) {
71
109
  const options = {
72
110
  default: 'vars',
@@ -125,8 +163,29 @@ export class Monorepo {
125
163
  async run(operation, args) {
126
164
  return operation.run(args);
127
165
  }
128
- async withFlavor(name) {
129
- const repo = new Monorepo(this._config.withFlavor(name));
166
+ async withFlavor(flavorName) {
167
+ const patches = this._config.flavor(flavorName).patches || [];
168
+ const original = this._config.toJSON();
169
+ const errors = jsonpatch.validate(patches || [], original);
170
+ if (errors) {
171
+ throw new Error('Invalid patch(es) detected');
172
+ }
173
+ const withComponentPatches = this.components.reduce((config, cmp) => {
174
+ const componentPatches = cmp.flavor(flavorName, false)?.patches || [];
175
+ const errors = jsonpatch.validate(componentPatches || [], config.components[cmp.name]);
176
+ if (errors) {
177
+ throw new Error('Invalid patch(es) detected');
178
+ }
179
+ config.components[cmp.name] = componentPatches.reduce((doc, patch, index) => {
180
+ return jsonpatch.applyReducer(doc, patch, index);
181
+ }, config.components[cmp.name]);
182
+ return config;
183
+ }, original);
184
+ const withGlobalPatches = patches.reduce((doc, patch, index) => {
185
+ return jsonpatch.applyReducer(doc, patch, index);
186
+ }, withComponentPatches);
187
+ const newConfig = new MonorepoConfig(withGlobalPatches);
188
+ const repo = new Monorepo(newConfig, flavorName);
130
189
  await repo.installStore();
131
190
  await repo.installEnv();
132
191
  return repo;
@@ -1,2 +1 @@
1
- export * from './BuildComponentsOperation.js';
2
1
  export * from './GetComponentContainerOperation.js';
@@ -1,2 +1 @@
1
- export * from './BuildComponentsOperation.js';
2
1
  export * from './GetComponentContainerOperation.js';
@@ -0,0 +1,11 @@
1
+ import * as z from 'zod';
2
+ import { AbstractOperation } from '../../../operations/index.js';
3
+ declare const schema: z.ZodObject<{
4
+ path: z.ZodString;
5
+ force: z.ZodOptional<z.ZodBoolean>;
6
+ }, z.core.$strip>;
7
+ export declare class CreateFileOperation extends AbstractOperation<typeof schema, unknown> {
8
+ constructor();
9
+ protected _run(input: z.input<typeof schema>): Promise<void>;
10
+ }
11
+ export {};
@@ -0,0 +1,31 @@
1
+ import { open, statfs, utimes } from 'node:fs/promises';
2
+ import * as z from 'zod';
3
+ import { AbstractOperation } from '../../../operations/index.js';
4
+ const schema = z.object({
5
+ //
6
+ path: z.string().describe('Path to the file to create'),
7
+ force: z
8
+ .boolean()
9
+ .optional()
10
+ .describe("Update 'atime' and 'mtime' if the file already exists"),
11
+ });
12
+ export class CreateFileOperation extends AbstractOperation {
13
+ constructor() {
14
+ super(schema);
15
+ }
16
+ async _run(input) {
17
+ try {
18
+ await statfs(input.path);
19
+ if (input.force) {
20
+ await utimes(input.path, Date.now(), Date.now());
21
+ }
22
+ }
23
+ catch (error) {
24
+ if (error?.code === 'ENOENT') {
25
+ const fn = await open(input.path, 'a');
26
+ return fn.close();
27
+ }
28
+ throw error;
29
+ }
30
+ }
31
+ }
@@ -0,0 +1 @@
1
+ export * from './CreateFileOperation.js';
@@ -0,0 +1 @@
1
+ export * from './CreateFileOperation.js';
@@ -1,3 +1,4 @@
1
1
  export * from './components/index.js';
2
+ export * from './fs/index.js';
2
3
  export * from './shell/index.js';
3
4
  export * from './tasks/index.js';
@@ -1,3 +1,4 @@
1
1
  export * from './components/index.js';
2
+ export * from './fs/index.js';
2
3
  export * from './shell/index.js';
3
4
  export * from './tasks/index.js';
@@ -0,0 +1,27 @@
1
+ import * as z from 'zod';
2
+ import { ResourceInfo } from '../../index.js';
3
+ import { ResourceBuilderInfo } from '../../resources/ResourceFactory.js';
4
+ import { AbstractOperation } from '../../../operations/index.js';
5
+ export type BuildResourceMeta = {
6
+ dryRun?: boolean;
7
+ force?: boolean;
8
+ resource?: ResourceInfo;
9
+ builder?: ResourceBuilderInfo<unknown, unknown>;
10
+ sentinelData?: unknown;
11
+ cacheHit?: boolean;
12
+ };
13
+ declare const schema: z.ZodObject<{
14
+ resources: z.ZodOptional<z.ZodArray<z.ZodString>>;
15
+ dryRun: z.ZodOptional<z.ZodBoolean>;
16
+ silent: z.ZodOptional<z.ZodBoolean>;
17
+ force: z.ZodOptional<z.ZodBoolean>;
18
+ }, z.core.$strip>;
19
+ export declare class BuildResourcesOperation extends AbstractOperation<typeof schema, Record<string, BuildResourceMeta>> {
20
+ constructor();
21
+ protected _run(input: z.input<typeof schema>): Promise<Record<string, BuildResourceMeta>>;
22
+ private buildResource;
23
+ private sentinelFilePath;
24
+ private storeSentinelData;
25
+ private readSentinelFile;
26
+ }
27
+ export {};
@@ -0,0 +1,146 @@
1
+ import * as z from 'zod';
2
+ import { EMBCollection, findRunOrder, taskManagerFactory, } from '../../index.js';
3
+ import { ResourceFactory, } from '../../resources/ResourceFactory.js';
4
+ import { AbstractOperation } from '../../../operations/index.js';
5
+ const schema = z.object({
6
+ resources: z
7
+ .array(z.string())
8
+ .describe('The list of resources to build')
9
+ .optional(),
10
+ dryRun: z
11
+ .boolean()
12
+ .optional()
13
+ .describe('Do not build but return the config that would be used to build the resources'),
14
+ silent: z
15
+ .boolean()
16
+ .optional()
17
+ .describe('Do not produce any output on the terminal'),
18
+ force: z
19
+ .boolean()
20
+ .optional()
21
+ .describe('Bypass the cache and force the build'),
22
+ });
23
+ export class BuildResourcesOperation extends AbstractOperation {
24
+ constructor() {
25
+ super(schema);
26
+ }
27
+ async _run(input) {
28
+ const { monorepo } = this.context;
29
+ const manager = taskManagerFactory();
30
+ const collection = new EMBCollection(monorepo.resources, {
31
+ idField: 'id',
32
+ depField: 'dependencies',
33
+ forbidIdNameCollision: true,
34
+ });
35
+ const ordered = findRunOrder(input.resources || [], collection);
36
+ const tasks = ordered.map((resource) => {
37
+ return {
38
+ task: async (context, task) => {
39
+ return this.buildResource(resource, task, context, {
40
+ dryRun: input.dryRun,
41
+ force: input.force,
42
+ });
43
+ },
44
+ title: `Building ${resource.id}`,
45
+ };
46
+ });
47
+ return manager.run([
48
+ {
49
+ title: 'Build resources',
50
+ async task(ctx, task) {
51
+ return task.newListr(tasks, {
52
+ rendererOptions: {
53
+ collapseSubtasks: false,
54
+ collapseSkips: true,
55
+ },
56
+ });
57
+ },
58
+ },
59
+ ], {
60
+ silentRendererCondition() {
61
+ return Boolean(input.silent);
62
+ },
63
+ rendererOptions: {
64
+ collapseSkips: true,
65
+ collapseSubtasks: true,
66
+ },
67
+ ctx: {},
68
+ });
69
+ }
70
+ async buildResource(resource, parentTask, parentContext, options) {
71
+ const list = parentTask.newListr([
72
+ {
73
+ title: 'Prepare build context',
74
+ task: async (ctx) => {
75
+ // Extend the context for this specific resource build chain
76
+ Object.assign(ctx, options, { resource });
77
+ const { monorepo } = this.context;
78
+ ctx.builder = await ResourceFactory.factor(resource.type, {
79
+ monorepo,
80
+ config: resource,
81
+ component: monorepo.component(resource.component),
82
+ });
83
+ },
84
+ },
85
+ // Actual build
86
+ {
87
+ title: `Build ${resource.id}`,
88
+ /** Skip the build if the builder knows it can be skipped */
89
+ task: async (ctx, task) => {
90
+ if (ctx.builder?.mustBuild) {
91
+ const previousSentinelData = await this.readSentinelFile(resource);
92
+ ctx.sentinelData =
93
+ await ctx.builder.mustBuild(previousSentinelData);
94
+ ctx.cacheHit = !ctx.sentinelData;
95
+ }
96
+ if (!ctx.force && (ctx.dryRun || ctx.cacheHit)) {
97
+ const prefix = ctx.dryRun ? '[dry run]' : '[cache hit]';
98
+ parentTask.title = `${prefix} ${resource.id}`;
99
+ task.skip();
100
+ return parentTask.skip();
101
+ }
102
+ return ctx.builder.operation.run(ctx.builder?.input);
103
+ },
104
+ },
105
+ {
106
+ // Return build meta data and dump
107
+ // cache data into sentinel file
108
+ task: async (ctx) => {
109
+ // TODO: clean this
110
+ if (ctx.builder?.mustBuild) {
111
+ delete ctx.builder.mustBuild;
112
+ }
113
+ if (ctx.builder?.operation) {
114
+ // @ts-expect-error duynno
115
+ delete ctx.builder.operation;
116
+ }
117
+ //
118
+ parentContext[resource.id] = ctx;
119
+ if (ctx.sentinelData && !ctx.dryRun) {
120
+ await this.storeSentinelData(resource, ctx.sentinelData);
121
+ }
122
+ },
123
+ },
124
+ ], {
125
+ ctx: {
126
+ ...options,
127
+ },
128
+ rendererOptions: {
129
+ collapseSubtasks: true,
130
+ },
131
+ });
132
+ return list;
133
+ }
134
+ sentinelFilePath(resource) {
135
+ const { monorepo } = this.context;
136
+ return `sentinels/flavors/${monorepo.currentFlavor}/${resource.component}/${resource.name}.built`;
137
+ }
138
+ async storeSentinelData(resource, data) {
139
+ await this.context.monorepo.store.writeFile(this.sentinelFilePath(resource), JSON.stringify(data));
140
+ }
141
+ async readSentinelFile(resource) {
142
+ const path = this.sentinelFilePath(resource);
143
+ const data = await this.context.monorepo.store.readFile(path, false);
144
+ return data ? JSON.parse(data) : undefined;
145
+ }
146
+ }
@@ -64,7 +64,7 @@ export class RunTasksOperation {
64
64
  async runLocal(task) {
65
65
  const { monorepo } = getContext();
66
66
  const cwd = task.component
67
- ? monorepo.component(task.component).rootdir
67
+ ? monorepo.component(task.component).rootDir
68
68
  : monorepo.rootDir;
69
69
  return monorepo.run(new ExecuteLocalCommandOperation(), {
70
70
  script: task.script,
@@ -1,15 +1,15 @@
1
1
  import { Monorepo, MonorepoConfig } from '../index.js';
2
2
  import { AbstractPlugin } from './plugin.js';
3
- export type ComponentDiscoverPluginOptions = {
3
+ export type AutoDockerPluginOptions = {
4
4
  glob?: string;
5
5
  ignore?: string | string[];
6
6
  };
7
- export declare const ComponentDiscoverPluginDefaultOptions: {
7
+ export declare const AutoDockerPluginDefaultOptions: {
8
8
  glob: string;
9
9
  };
10
- export declare class ComponentDiscoverPlugin extends AbstractPlugin<ComponentDiscoverPluginOptions> {
10
+ export declare class AutoDockerPlugin extends AbstractPlugin<AutoDockerPluginOptions> {
11
11
  protected monorepo: Monorepo;
12
12
  static name: string;
13
- constructor(config: Partial<ComponentDiscoverPluginOptions>, monorepo: Monorepo);
13
+ constructor(config: Partial<AutoDockerPluginOptions>, monorepo: Monorepo);
14
14
  extendConfig(config: MonorepoConfig): Promise<MonorepoConfig>;
15
15
  }
@@ -0,0 +1,46 @@
1
+ import deepmerge from '@fastify/deepmerge';
2
+ import { glob } from 'glob';
3
+ import { dirname } from 'node:path';
4
+ import { MonorepoConfig } from '../index.js';
5
+ import { AbstractPlugin } from './plugin.js';
6
+ export const AutoDockerPluginDefaultOptions = {
7
+ glob: '*/Dockerfile',
8
+ };
9
+ export class AutoDockerPlugin extends AbstractPlugin {
10
+ monorepo;
11
+ static name = 'autodocker';
12
+ constructor(config, monorepo) {
13
+ super({
14
+ ...AutoDockerPluginDefaultOptions,
15
+ ...config,
16
+ }, monorepo);
17
+ this.monorepo = monorepo;
18
+ }
19
+ async extendConfig(config) {
20
+ const files = await glob(this.config.glob || AutoDockerPluginDefaultOptions.glob, {
21
+ ...this.config,
22
+ cwd: config.project.rootDir,
23
+ });
24
+ const overrides = files.reduce((cmps, path) => {
25
+ const name = dirname(path);
26
+ const component = config.components[name];
27
+ const cfg = {
28
+ resources: {
29
+ image: {
30
+ type: 'docker/image',
31
+ params: {},
32
+ },
33
+ },
34
+ };
35
+ cmps[name] = component ? deepmerge()(component, cfg) : cfg;
36
+ return cmps;
37
+ }, {});
38
+ return new MonorepoConfig({
39
+ ...config,
40
+ components: {
41
+ ...config.components,
42
+ ...overrides,
43
+ },
44
+ });
45
+ }
46
+ }
@@ -1,5 +1,6 @@
1
+ import deepmerge from '@fastify/deepmerge';
1
2
  import { glob } from 'glob';
2
- import { join } from 'node:path';
3
+ import { basename, dirname, join } from 'node:path';
3
4
  import { validateEmbfile } from '../../config/index.js';
4
5
  import { AbstractPlugin } from './plugin.js';
5
6
  export const EmbfileLoaderPluginDefaultOptions = {
@@ -26,10 +27,16 @@ export class EmbfileLoaderPlugin extends AbstractPlugin {
26
27
  });
27
28
  const newConfig = await files.reduce(async (pConfig, path) => {
28
29
  const config = await pConfig;
30
+ const rootDir = dirname(path);
31
+ const name = basename(rootDir);
29
32
  const embfile = await join(config.project.rootDir, path);
30
33
  const component = await validateEmbfile(embfile);
34
+ const original = config.components[name];
35
+ const newComponent = deepmerge()(original || {}, component);
31
36
  return config.with({
32
- components: [component],
37
+ components: {
38
+ [name]: newComponent,
39
+ },
33
40
  });
34
41
  }, Promise.resolve(config));
35
42
  return newConfig;
@@ -1,5 +1,5 @@
1
1
  import { AbstractPlugin } from './plugin.js';
2
- export * from './ComponentDiscoverPlugin.js';
2
+ export * from './AutoDockerPlugin.js';
3
3
  export * from './DotEnvPlugin.js';
4
4
  export * from './EmbfileLoaderPlugin.js';
5
5
  import { Monorepo } from '../index.js';
@@ -1,7 +1,7 @@
1
- export * from './ComponentDiscoverPlugin.js';
1
+ export * from './AutoDockerPlugin.js';
2
2
  export * from './DotEnvPlugin.js';
3
3
  export * from './EmbfileLoaderPlugin.js';
4
- import { ComponentDiscoverPlugin } from './ComponentDiscoverPlugin.js';
4
+ import { AutoDockerPlugin } from './AutoDockerPlugin.js';
5
5
  import { DotEnvPlugin } from './DotEnvPlugin.js';
6
6
  import { EmbfileLoaderPlugin } from './EmbfileLoaderPlugin.js';
7
7
  const PluginRegistry = new Map();
@@ -18,6 +18,6 @@ export const getPlugin = (name) => {
18
18
  return PluginRegistry.get(name);
19
19
  };
20
20
  /** Not sure why we need casting */
21
- registerPlugin(ComponentDiscoverPlugin);
21
+ registerPlugin(AutoDockerPlugin);
22
22
  registerPlugin(DotEnvPlugin);
23
23
  registerPlugin(EmbfileLoaderPlugin);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import { CreateFileOperation } from '../index.js';
2
+ import { ResourceFactory } from './ResourceFactory.js';
3
+ // Bring better abstraction and register as part of the plugin initialization
4
+ ResourceFactory.register('file', async ({ config, component }) => {
5
+ const fromConfig = (config.params || {});
6
+ const input = {
7
+ path: component.join(fromConfig?.path || config.name),
8
+ };
9
+ return {
10
+ input,
11
+ operation: new CreateFileOperation(),
12
+ };
13
+ });
@@ -0,0 +1,23 @@
1
+ import { Component, Monorepo, ResourceInfo } from '../../index.js';
2
+ import { IOperation } from '../../operations/types.js';
3
+ export type ResourceBuildContext = {
4
+ config: ResourceInfo;
5
+ component: Component;
6
+ monorepo: Monorepo;
7
+ };
8
+ export type SentinelData<T = void> = {
9
+ mtime: number;
10
+ data: T;
11
+ };
12
+ export type ResourceBuilderInfo<I, O, D = unknown> = {
13
+ input: I;
14
+ operation: IOperation<I, O>;
15
+ mustBuild?: (previousSentinelData: SentinelData<D> | undefined) => Promise<undefined | unknown>;
16
+ };
17
+ export type ResourceFactoryOutput<I, O> = Promise<ResourceBuilderInfo<I, O>>;
18
+ export type ResourceOperationFactory<I, O> = (context: ResourceBuildContext) => ResourceFactoryOutput<I, O>;
19
+ export declare class ResourceFactory {
20
+ protected static types: Record<string, ResourceOperationFactory<unknown, unknown>>;
21
+ static register<I, O>(type: string, opFactory: ResourceOperationFactory<I, O>): void;
22
+ static factor<I, O>(type: string, context: ResourceBuildContext): ResourceFactoryOutput<I, O>;
23
+ }
@@ -0,0 +1,16 @@
1
+ export class ResourceFactory {
2
+ static types = {};
3
+ static register(type, opFactory) {
4
+ if (this.types[type]) {
5
+ throw new Error(`Resource type \`${type}\` already registered`);
6
+ }
7
+ this.types[type] = opFactory;
8
+ }
9
+ static factor(type, context) {
10
+ const opFactory = this.types[type];
11
+ if (!opFactory) {
12
+ throw new Error(`Unknown resource type \`${type}\``);
13
+ }
14
+ return opFactory(context);
15
+ }
16
+ }
@@ -0,0 +1 @@
1
+ import './FileResource.js';
@@ -0,0 +1 @@
1
+ import './FileResource.js';
@@ -14,7 +14,7 @@ export declare class EMBStore {
14
14
  init(): Promise<void>;
15
15
  join(path: string): string;
16
16
  mkdirp(path: string): Promise<void>;
17
- readFile(path: string): Promise<Buffer<ArrayBufferLike>>;
17
+ readFile(path: string, mustExist?: boolean): Promise<string | undefined>;
18
18
  trash(): Promise<void>;
19
19
  writeFile(path: string, data: string): Promise<void>;
20
20
  }
@@ -54,8 +54,16 @@ export class EMBStore {
54
54
  const normalized = normalize(join('/', path));
55
55
  await mkdir(this.join(normalized), { recursive: true });
56
56
  }
57
- async readFile(path) {
58
- return readFile(this.join(path));
57
+ async readFile(path, mustExist = true) {
58
+ try {
59
+ return (await readFile(this.join(path))).toString();
60
+ }
61
+ catch (error) {
62
+ if (error.code === 'ENOENT' && !mustExist) {
63
+ return;
64
+ }
65
+ throw error;
66
+ }
59
67
  }
60
68
  async trash() {
61
69
  return rm(this.path, { force: true, recursive: true });
@@ -1,3 +1,3 @@
1
1
  import { Manager } from '@listr2/manager';
2
- import { ListrBaseClassOptions } from 'listr2';
3
- export declare function taskManagerFactory<T extends Record<PropertyKey, unknown>>(override?: ListrBaseClassOptions): Manager<T>;
2
+ import { ListrContext, ListrRendererValue, ListrSecondaryRendererValue } from 'listr2';
3
+ export declare function taskManagerFactory<Ctx = ListrContext, FallbackRenderer extends ListrRendererValue = ListrSecondaryRendererValue>(): Manager<Ctx, ListrRendererValue, FallbackRenderer>;
@@ -1,6 +1,6 @@
1
1
  import { Manager } from '@listr2/manager';
2
2
  import { ListrDefaultRendererLogLevels, PRESET_TIMER, } from 'listr2';
3
- export function taskManagerFactory(override) {
3
+ export function taskManagerFactory() {
4
4
  return new Manager({
5
5
  collectErrors: 'minimal',
6
6
  concurrent: false,
@@ -16,6 +16,5 @@ export function taskManagerFactory(override) {
16
16
  ...PRESET_TIMER,
17
17
  },
18
18
  },
19
- ...override,
20
19
  });
21
20
  }