@decohere/forge 0.0.1-dev.12 → 0.0.1-dev.121

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 (194) hide show
  1. package/README.md +141 -0
  2. package/commonjs/src/actions/build.action.js +8 -6
  3. package/commonjs/src/actions/clean.action.js +6 -8
  4. package/commonjs/src/actions/commit.action.js +4 -6
  5. package/commonjs/src/actions/copy.action.js +43 -12
  6. package/commonjs/src/actions/dependency-report.action.js +4 -6
  7. package/commonjs/src/actions/dependency-sync.action.js +3 -5
  8. package/commonjs/src/actions/docker.action.js +209 -0
  9. package/commonjs/src/actions/index.js +6 -2
  10. package/commonjs/src/actions/link.action.js +154 -0
  11. package/commonjs/src/actions/package.action.js +234 -0
  12. package/commonjs/src/actions/paths.action.js +109 -0
  13. package/commonjs/src/actions/publish.action.js +4 -6
  14. package/commonjs/src/actions/sync-lock-file.action.js +2 -4
  15. package/commonjs/src/actions/tag.action.js +6 -9
  16. package/commonjs/src/actions/typecheck.action.js +67 -0
  17. package/commonjs/src/actions/unlink.action.js +74 -0
  18. package/commonjs/src/actions/version.action.js +10 -14
  19. package/commonjs/src/actions/watch.action.js +3 -5
  20. package/commonjs/src/commands/build.command.js +4 -3
  21. package/commonjs/src/commands/dependency.command.js +1 -3
  22. package/commonjs/src/commands/docker.command.js +26 -0
  23. package/commonjs/src/commands/forge.command.js +1 -3
  24. package/commonjs/src/commands/index.js +5 -2
  25. package/commonjs/src/commands/link.command.js +90 -0
  26. package/commonjs/src/commands/package.command.js +36 -0
  27. package/commonjs/src/commands/paths.command.js +24 -0
  28. package/commonjs/src/commands/publish.command.js +1 -3
  29. package/commonjs/src/commands/tag.command.js +1 -3
  30. package/commonjs/src/commands/typecheck.command.js +24 -0
  31. package/commonjs/src/commands/version.command.js +1 -3
  32. package/commonjs/src/const.js +0 -2
  33. package/commonjs/src/create-workspace.js +42 -0
  34. package/commonjs/src/errors.js +2 -4
  35. package/commonjs/src/extensions/project.extensions.js +11 -13
  36. package/commonjs/src/factories/swc-config.factory.js +29 -9
  37. package/commonjs/src/forge.module.js +23 -22
  38. package/commonjs/src/index.js +11 -2
  39. package/commonjs/src/packaging/dependency-pinner.service.js +118 -0
  40. package/commonjs/src/packaging/deployable.js +44 -0
  41. package/commonjs/src/packaging/import-graph.service.js +137 -0
  42. package/commonjs/src/packaging/release-exports.js +90 -0
  43. package/commonjs/src/packaging/ui-assets.js +75 -0
  44. package/commonjs/src/semver/index.js +18 -0
  45. package/commonjs/src/semver/semver.js +92 -0
  46. package/commonjs/src/services/dependency-resolver.service.js +1 -3
  47. package/commonjs/src/services/file-writer.service.js +2 -11
  48. package/commonjs/src/services/incremental-watch.js +24 -0
  49. package/commonjs/src/services/output-cache.js +39 -0
  50. package/commonjs/src/services/swc-transpiler.js +85 -99
  51. package/commonjs/src/services/transpile-cache.js +44 -0
  52. package/commonjs/src/services/ts-program-host.js +4 -6
  53. package/commonjs/src/services/type-emission-program.js +24 -9
  54. package/commonjs/src/services/type-emitter.js +51 -16
  55. package/commonjs/src/types/build-target.type.js +0 -2
  56. package/commonjs/src/types/configuration.type.js +0 -2
  57. package/commonjs/src/types/dependency.types.js +0 -2
  58. package/commonjs/src/types/swc-config.type.js +0 -2
  59. package/commonjs/src/types/transform-response.model.js +3 -18
  60. package/commonjs/src/types/transformer.type.js +0 -2
  61. package/commonjs/src/types/workspace-config.type.js +0 -2
  62. package/commonjs/src/vitest/forge-build.plugin.js +28 -44
  63. package/commonjs/src/vitest/index.js +0 -2
  64. package/commonjs/src/workspace.config.js +39 -6
  65. package/es6/src/actions/build.action.js +7 -7
  66. package/es6/src/actions/clean.action.js +5 -8
  67. package/es6/src/actions/commit.action.js +3 -6
  68. package/es6/src/actions/copy.action.js +42 -12
  69. package/es6/src/actions/dependency-report.action.js +3 -7
  70. package/es6/src/actions/dependency-sync.action.js +2 -6
  71. package/es6/src/actions/docker.action.js +194 -0
  72. package/es6/src/actions/index.js +6 -2
  73. package/es6/src/actions/link.action.js +128 -0
  74. package/es6/src/actions/package.action.js +219 -0
  75. package/es6/src/actions/paths.action.js +86 -0
  76. package/es6/src/actions/publish.action.js +3 -6
  77. package/es6/src/actions/sync-lock-file.action.js +2 -4
  78. package/es6/src/actions/tag.action.js +5 -9
  79. package/es6/src/actions/typecheck.action.js +52 -0
  80. package/es6/src/actions/unlink.action.js +59 -0
  81. package/es6/src/actions/version.action.js +5 -10
  82. package/es6/src/actions/watch.action.js +2 -5
  83. package/es6/src/commands/build.command.js +4 -3
  84. package/es6/src/commands/dependency.command.js +1 -3
  85. package/es6/src/commands/docker.command.js +16 -0
  86. package/es6/src/commands/forge.command.js +1 -3
  87. package/es6/src/commands/index.js +5 -2
  88. package/es6/src/commands/link.command.js +67 -0
  89. package/es6/src/commands/package.command.js +26 -0
  90. package/es6/src/commands/paths.command.js +14 -0
  91. package/es6/src/commands/publish.command.js +1 -3
  92. package/es6/src/commands/tag.command.js +1 -3
  93. package/es6/src/commands/typecheck.command.js +14 -0
  94. package/es6/src/commands/version.command.js +1 -3
  95. package/es6/src/const.js +0 -2
  96. package/es6/src/create-workspace.js +30 -0
  97. package/es6/src/errors.js +1 -3
  98. package/es6/src/extensions/project.extensions.js +5 -7
  99. package/es6/src/factories/swc-config.factory.js +29 -9
  100. package/es6/src/forge.module.js +23 -22
  101. package/es6/src/index.js +3 -3
  102. package/es6/src/packaging/dependency-pinner.service.js +103 -0
  103. package/es6/src/packaging/deployable.js +21 -0
  104. package/es6/src/packaging/import-graph.service.js +122 -0
  105. package/es6/src/packaging/release-exports.js +80 -0
  106. package/es6/src/packaging/ui-assets.js +49 -0
  107. package/es6/src/semver/index.js +1 -0
  108. package/es6/src/semver/semver.js +71 -0
  109. package/es6/src/services/dependency-resolver.service.js +1 -3
  110. package/es6/src/services/file-writer.service.js +2 -11
  111. package/es6/src/services/incremental-watch.js +18 -0
  112. package/es6/src/services/output-cache.js +29 -0
  113. package/es6/src/services/swc-transpiler.js +85 -99
  114. package/es6/src/services/transpile-cache.js +29 -0
  115. package/es6/src/services/ts-program-host.js +4 -6
  116. package/es6/src/services/type-emission-program.js +24 -9
  117. package/es6/src/services/type-emitter.js +48 -16
  118. package/es6/src/types/build-target.type.js +0 -2
  119. package/es6/src/types/configuration.type.js +0 -2
  120. package/es6/src/types/dependency.types.js +0 -2
  121. package/es6/src/types/swc-config.type.js +0 -2
  122. package/es6/src/types/transform-response.model.js +0 -7
  123. package/es6/src/types/transformer.type.js +0 -2
  124. package/es6/src/types/workspace-config.type.js +0 -2
  125. package/es6/src/vitest/forge-build.plugin.js +27 -43
  126. package/es6/src/vitest/index.js +0 -2
  127. package/es6/src/workspace.config.js +39 -5
  128. package/package.json +6 -5
  129. package/types/src/actions/build.action.d.ts +5 -4
  130. package/types/src/actions/clean.action.d.ts +3 -3
  131. package/types/src/actions/commit.action.d.ts +3 -3
  132. package/types/src/actions/copy.action.d.ts +13 -3
  133. package/types/src/actions/dependency-report.action.d.ts +3 -3
  134. package/types/src/actions/dependency-sync.action.d.ts +3 -3
  135. package/types/src/actions/docker.action.d.ts +38 -0
  136. package/types/src/actions/index.d.ts +6 -0
  137. package/types/src/actions/link.action.d.ts +25 -0
  138. package/types/src/actions/package.action.d.ts +38 -0
  139. package/types/src/actions/paths.action.d.ts +28 -0
  140. package/types/src/actions/publish.action.d.ts +4 -4
  141. package/types/src/actions/sync-lock-file.action.d.ts +2 -2
  142. package/types/src/actions/tag.action.d.ts +3 -3
  143. package/types/src/actions/typecheck.action.d.ts +14 -0
  144. package/types/src/actions/unlink.action.d.ts +9 -0
  145. package/types/src/actions/version.action.d.ts +4 -4
  146. package/types/src/actions/watch.action.d.ts +3 -3
  147. package/types/src/commands/build.command.d.ts +3 -2
  148. package/types/src/commands/dependency.command.d.ts +2 -2
  149. package/types/src/commands/docker.command.d.ts +6 -0
  150. package/types/src/commands/forge.command.d.ts +2 -2
  151. package/types/src/commands/index.d.ts +5 -0
  152. package/types/src/commands/link.command.d.ts +29 -0
  153. package/types/src/commands/package.command.d.ts +11 -0
  154. package/types/src/commands/paths.command.d.ts +6 -0
  155. package/types/src/commands/publish.command.d.ts +1 -1
  156. package/types/src/commands/tag.command.d.ts +1 -1
  157. package/types/src/commands/typecheck.command.d.ts +6 -0
  158. package/types/src/commands/version.command.d.ts +1 -1
  159. package/types/src/create-workspace.d.ts +7 -0
  160. package/types/src/errors.d.ts +1 -1
  161. package/types/src/extensions/project.extensions.d.ts +2 -2
  162. package/types/src/factories/swc-config.factory.d.ts +3 -2
  163. package/types/src/forge.module.d.ts +16 -4
  164. package/types/src/index.d.ts +3 -1
  165. package/types/src/packaging/dependency-pinner.service.d.ts +20 -0
  166. package/types/src/packaging/deployable.d.ts +17 -0
  167. package/types/src/packaging/import-graph.service.d.ts +33 -0
  168. package/types/src/packaging/release-exports.d.ts +18 -0
  169. package/types/src/packaging/ui-assets.d.ts +21 -0
  170. package/types/src/semver/index.d.ts +1 -0
  171. package/types/src/semver/semver.d.ts +18 -0
  172. package/types/src/services/dependency-resolver.service.d.ts +1 -1
  173. package/types/src/services/file-writer.service.d.ts +1 -2
  174. package/types/src/services/incremental-watch.d.ts +13 -0
  175. package/types/src/services/output-cache.d.ts +10 -0
  176. package/types/src/services/swc-transpiler.d.ts +28 -15
  177. package/types/src/services/transpile-cache.d.ts +17 -0
  178. package/types/src/services/ts-program-host.d.ts +2 -2
  179. package/types/src/services/type-emission-program.d.ts +8 -3
  180. package/types/src/services/type-emitter.d.ts +21 -8
  181. package/types/src/types/configuration.type.d.ts +6 -1
  182. package/types/src/types/dependency.types.d.ts +1 -1
  183. package/types/src/types/swc-config.type.d.ts +7 -1
  184. package/types/src/types/transform-response.model.d.ts +2 -7
  185. package/types/src/types/transformer.type.d.ts +2 -11
  186. package/types/src/types/workspace-config.type.d.ts +3 -3
  187. package/types/src/vitest/forge-build.plugin.d.ts +0 -2
  188. package/types/src/workspace.config.d.ts +6 -0
  189. package/commonjs/src/mixins/caches-outputs.mixin.js +0 -44
  190. package/commonjs/src/mixins/incremental-transform.mixin.js +0 -36
  191. package/es6/src/mixins/caches-outputs.mixin.js +0 -34
  192. package/es6/src/mixins/incremental-transform.mixin.js +0 -26
  193. package/types/src/mixins/caches-outputs.mixin.d.ts +0 -7
  194. package/types/src/mixins/incremental-transform.mixin.d.ts +0 -13
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "Docker", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return Docker;
9
+ }
10
+ });
11
+ const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
12
+ const _decorators = require("@decohere/crucible/decorators");
13
+ const _workspace = require("@decohere/workspace");
14
+ const _cli = require("@decohere/devkit/cli");
15
+ const _interop = require("@decohere/devkit/interop");
16
+ const _io = require("@decohere/core/io");
17
+ const _scribe = require("@decohere/scribe");
18
+ const _errors = require("../errors.js");
19
+ const _deployable = require("../packaging/deployable.js");
20
+ function _interop_require_default(obj) {
21
+ return obj && obj.__esModule ? obj : {
22
+ default: obj
23
+ };
24
+ }
25
+ function _ts_decorate(decorators, target, key, desc) {
26
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
27
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
28
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
29
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
30
+ }
31
+ function _ts_metadata(k, v) {
32
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
+ }
34
+ class Docker {
35
+ workspace;
36
+ #logger = _scribe.scribe.logger(Docker.name);
37
+ docker = new _interop.Docker();
38
+ #defaultRegistry = process.env.FORGE_DOCKER_REGISTRY?.trim() || "ghcr.io";
39
+ constructor(workspace){
40
+ this.workspace = workspace;
41
+ }
42
+ async run(options) {
43
+ const projects = await this.getDockerProjects(this.workspace.getProjects(options.configuration));
44
+ if (projects.length === 0) {
45
+ this.#logger.info("No projects found with a Dockerfile — nothing to dockerize");
46
+ return _cli.ActionResponse.Success(Docker.name, []);
47
+ }
48
+ const workspacePackageJson = await this.workspace.getPackageJson();
49
+ const version = await this.workspace.getVersion();
50
+ const responses = await Promise.all(projects.map(async (project)=>await this.buildProject(project, workspacePackageJson, version, options)));
51
+ const failed = responses.filter((response)=>!response.succeeded);
52
+ if (failed.length > 0) {
53
+ return _cli.ActionResponse.Error(Docker.name, responses);
54
+ }
55
+ return _cli.ActionResponse.Success(Docker.name, responses);
56
+ }
57
+ async getDockerProjects(projects) {
58
+ const result = [];
59
+ for (const project of projects){
60
+ if (!await _deployable.deployable.isDeployable(project)) {
61
+ this.#logger.info(`Skipping ${project.name}: missing ${_deployable.deployable.dockerfileName(project)}`);
62
+ continue;
63
+ }
64
+ result.push(project);
65
+ }
66
+ return result;
67
+ }
68
+ /**
69
+ * A Dockerfile that copies `.package/` needs `forge package` to have run. Fail before the
70
+ * build with that instruction rather than with Docker's "not found" from deep in a layer.
71
+ */ async assertPackaged(project) {
72
+ const dockerfile = await _io.io.readFile(_deployable.deployable.dockerfilePath(project)).catch(()=>"");
73
+ if (!dockerfile.includes(_deployable.PACKAGE_DIR)) {
74
+ return;
75
+ }
76
+ const packaged = _nodepath.default.join(project.projectRoot, _deployable.PACKAGE_DIR, "package.json");
77
+ if (!await _io.io.fileExists(packaged)) {
78
+ throw new _errors.ForgeError(`${project.name}: its Dockerfile copies ${_deployable.PACKAGE_DIR}/, which does not exist. ` + `Run \`forge build --configuration release\` and \`forge package ${project.name}\` first.`);
79
+ }
80
+ }
81
+ async buildProject(project, workspacePackageJson, version, options) {
82
+ const dockerConfig = project.dockerConfig ?? {};
83
+ const envBuildArgs = this.parseJsonEnv("FORGE_DOCKER_BUILD_ARGS_JSON", {});
84
+ const envSecrets = this.parseJsonEnv("FORGE_DOCKER_SECRETS_JSON", {});
85
+ const envTags = this.parseJsonEnv("FORGE_DOCKER_EXTRA_TAGS_JSON", []);
86
+ const image = dockerConfig.image ?? this.defaultImageName(project.name, workspacePackageJson);
87
+ const dockerfile = dockerConfig.dockerfile ?? "Dockerfile";
88
+ const extraTags = this.extraTagsForImage(image, [
89
+ ...dockerConfig.tags ?? [],
90
+ ...envTags
91
+ ]);
92
+ const tags = [
93
+ `${image}:${version}`,
94
+ ...extraTags
95
+ ];
96
+ const buildArgs = this.interpolateBuildArgs({
97
+ ...dockerConfig.buildArgs,
98
+ ...envBuildArgs
99
+ }, version);
100
+ const secrets = {
101
+ ...dockerConfig.secrets,
102
+ ...envSecrets
103
+ };
104
+ this.#logger.info(`${options.dryRun ? "[dry-run] " : ""}Building ${project.name}`);
105
+ try {
106
+ await this.assertPackaged(project);
107
+ await this.docker.build(project.projectRoot, {
108
+ cwd: project.projectRoot,
109
+ dockerfile: _nodepath.default.join(project.projectRoot, dockerfile),
110
+ tags,
111
+ buildArgs,
112
+ secrets
113
+ });
114
+ const shouldPush = options.push || dockerConfig.push;
115
+ if (shouldPush && !options.dryRun) {
116
+ for (const tag of tags){
117
+ await this.docker.push(tag, {
118
+ cwd: project.projectRoot
119
+ });
120
+ }
121
+ } else if (shouldPush && options.dryRun) {
122
+ this.#logger.info(`[dry-run] Skipping push for ${project.name}`);
123
+ }
124
+ return {
125
+ projectName: project.name,
126
+ image,
127
+ tags,
128
+ succeeded: true,
129
+ message: options.dryRun ? `Built ${image} (not pushed)` : `Built ${image}`
130
+ };
131
+ } catch (reason) {
132
+ const message = (0, _errors.formatUnknownError)(reason);
133
+ return {
134
+ projectName: project.name,
135
+ image,
136
+ tags,
137
+ succeeded: false,
138
+ message,
139
+ error: `${project.name}: ${message}`
140
+ };
141
+ }
142
+ }
143
+ interpolateBuildArgs(buildArgs, version) {
144
+ const result = {};
145
+ const versionPlaceholder = "$" + "{version}";
146
+ for (const [key, value] of Object.entries(buildArgs ?? {})){
147
+ result[key] = value.split(versionPlaceholder).join(version);
148
+ }
149
+ return result;
150
+ }
151
+ /**
152
+ * Keep FORGE_DOCKER_EXTRA_TAGS_JSON scoped to this image so multi-image
153
+ * publishes do not stamp create:latest onto the app Dev Container (and vice versa).
154
+ */ extraTagsForImage(image, tags) {
155
+ const prefix = `${image}:`;
156
+ const seen = new Set();
157
+ const result = [];
158
+ for (const tag of tags){
159
+ if (tag === image || tag.startsWith(prefix)) {
160
+ if (!seen.has(tag)) {
161
+ seen.add(tag);
162
+ result.push(tag);
163
+ }
164
+ }
165
+ }
166
+ return result;
167
+ }
168
+ defaultImageName(projectName, workspacePackageJson) {
169
+ const repositoryPath = process.env.FORGE_DOCKER_NAMESPACE?.trim() || workspacePackageJson.getRepositoryFullName() || this.normalizeDockerRepository(projectName);
170
+ const projectSlug = this.normalizeProjectSlug(projectName);
171
+ if (repositoryPath.includes("/") && projectSlug !== "") {
172
+ return `${this.#defaultRegistry}/${repositoryPath}/${projectSlug}`;
173
+ }
174
+ return `${this.#defaultRegistry}/${repositoryPath}`;
175
+ }
176
+ normalizeDockerRepository(name) {
177
+ const repository = name.replace(/^@/, "").split("/").map((segment)=>segment.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "")).filter(Boolean).join("/");
178
+ if (repository === "") {
179
+ throw new _errors.ForgeError(`Could not derive a Docker repository from project name "${name}"`);
180
+ }
181
+ return repository;
182
+ }
183
+ normalizeProjectSlug(name) {
184
+ const unscoped = name.includes("/") ? name.split("/").at(-1) ?? name : name;
185
+ const slug = unscoped.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
186
+ if (slug === "") {
187
+ throw new _errors.ForgeError(`Could not derive a Docker project slug from project name "${name}"`);
188
+ }
189
+ return slug;
190
+ }
191
+ parseJsonEnv(name, fallback) {
192
+ const value = process.env[name];
193
+ if (value == null || value.trim() === "") {
194
+ return fallback;
195
+ }
196
+ try {
197
+ return JSON.parse(value);
198
+ } catch (reason) {
199
+ throw new _errors.ForgeError(`Invalid JSON in ${name}: ${(0, _errors.formatUnknownError)(reason)}`);
200
+ }
201
+ }
202
+ }
203
+ Docker = _ts_decorate([
204
+ (0, _decorators.injectable)(),
205
+ _ts_metadata("design:type", Function),
206
+ _ts_metadata("design:paramtypes", [
207
+ typeof _workspace.Workspace === "undefined" ? Object : _workspace.Workspace
208
+ ])
209
+ ], Docker);
@@ -8,9 +8,15 @@ _export_star(require("./commit.action.js"), exports);
8
8
  _export_star(require("./copy.action.js"), exports);
9
9
  _export_star(require("./dependency-report.action.js"), exports);
10
10
  _export_star(require("./dependency-sync.action.js"), exports);
11
+ _export_star(require("./docker.action.js"), exports);
12
+ _export_star(require("./link.action.js"), exports);
13
+ _export_star(require("./package.action.js"), exports);
14
+ _export_star(require("./paths.action.js"), exports);
11
15
  _export_star(require("./publish.action.js"), exports);
12
16
  _export_star(require("./sync-lock-file.action.js"), exports);
13
17
  _export_star(require("./tag.action.js"), exports);
18
+ _export_star(require("./typecheck.action.js"), exports);
19
+ _export_star(require("./unlink.action.js"), exports);
14
20
  _export_star(require("./version.action.js"), exports);
15
21
  _export_star(require("./watch.action.js"), exports);
16
22
  function _export_star(from, to) {
@@ -26,5 +32,3 @@ function _export_star(from, to) {
26
32
  });
27
33
  return from;
28
34
  }
29
-
30
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL3J1bm5lci93b3JrL2RlY29oZXJlL2RlY29oZXJlL3Byb2plY3RzL2ZvcmdlL3NyYy9hY3Rpb25zL2luZGV4LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCAqIGZyb20gXCIuL2J1aWxkLmFjdGlvbi5qc1wiO1xuZXhwb3J0ICogZnJvbSBcIi4vY2xlYW4uYWN0aW9uLmpzXCI7XG5leHBvcnQgKiBmcm9tIFwiLi9jb21taXQuYWN0aW9uLmpzXCI7XG5leHBvcnQgKiBmcm9tIFwiLi9jb3B5LmFjdGlvbi5qc1wiO1xuZXhwb3J0ICogZnJvbSBcIi4vZGVwZW5kZW5jeS1yZXBvcnQuYWN0aW9uLmpzXCI7XG5leHBvcnQgKiBmcm9tIFwiLi9kZXBlbmRlbmN5LXN5bmMuYWN0aW9uLmpzXCI7XG5leHBvcnQgKiBmcm9tIFwiLi9wdWJsaXNoLmFjdGlvbi5qc1wiO1xuZXhwb3J0ICogZnJvbSBcIi4vc3luYy1sb2NrLWZpbGUuYWN0aW9uLmpzXCI7XG5leHBvcnQgKiBmcm9tIFwiLi90YWcuYWN0aW9uLmpzXCI7XG5leHBvcnQgKiBmcm9tIFwiLi92ZXJzaW9uLmFjdGlvbi5qc1wiO1xuZXhwb3J0ICogZnJvbSBcIi4vd2F0Y2guYWN0aW9uLmpzXCI7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztxQkFBYztxQkFDQTtxQkFDQTtxQkFDQTtxQkFDQTtxQkFDQTtxQkFDQTtxQkFDQTtxQkFDQTtxQkFDQTtxQkFDQSJ9
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get DEFAULT_LINK_CONFIGURATION () {
13
+ return DEFAULT_LINK_CONFIGURATION;
14
+ },
15
+ get LINK_MANIFEST () {
16
+ return LINK_MANIFEST;
17
+ },
18
+ get Link () {
19
+ return Link;
20
+ }
21
+ });
22
+ const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
23
+ const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
24
+ const _decorators = require("@decohere/crucible/decorators");
25
+ const _workspace = require("@decohere/workspace");
26
+ const _cli = require("@decohere/devkit/cli");
27
+ const _scribe = require("@decohere/scribe");
28
+ require("../extensions/project.extensions.js");
29
+ const _errors = require("../errors.js");
30
+ function _interop_require_default(obj) {
31
+ return obj && obj.__esModule ? obj : {
32
+ default: obj
33
+ };
34
+ }
35
+ function _ts_decorate(decorators, target, key, desc) {
36
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
37
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
38
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
39
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
40
+ }
41
+ function _ts_metadata(k, v) {
42
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
+ }
44
+ const LINK_MANIFEST = ".forge-link.json";
45
+ const DEFAULT_LINK_CONFIGURATION = "link";
46
+ class Link {
47
+ workspace;
48
+ #logger = _scribe.scribe.logger(Link.name);
49
+ constructor(workspace){
50
+ this.workspace = workspace;
51
+ }
52
+ run = async (options)=>{
53
+ const targetDir = _nodepath.default.resolve(options.targetDir);
54
+ const nodeModulesDir = _nodepath.default.join(targetDir, "node_modules");
55
+ const stat = await _promises.default.stat(nodeModulesDir).catch(()=>null);
56
+ if (stat == null) {
57
+ throw new _errors.ForgeError(`node_modules not found in "${targetDir}". Run pnpm install first.`);
58
+ }
59
+ const projects = this.workspace.getProjects(options.configuration);
60
+ if (projects.length === 0) {
61
+ throw new _errors.ForgeError(`No projects found for configuration "${options.configuration}"`);
62
+ }
63
+ // Load any existing manifest so originalPath values are preserved on re-link
64
+ const manifestPath = _nodepath.default.join(targetDir, LINK_MANIFEST);
65
+ const existingManifestText = await _promises.default.readFile(manifestPath, "utf8").catch(()=>null);
66
+ const existingManifest = existingManifestText != null ? JSON.parse(existingManifestText) : null;
67
+ const manifest = {
68
+ workspaceRoot: this.workspace.rootDir,
69
+ links: []
70
+ };
71
+ for (const project of projects){
72
+ const pkg = await project.getPackageJson();
73
+ const packageName = pkg.name;
74
+ if (!packageName) {
75
+ this.#logger.trace(`Skipping project "${project.name}" — no package name`);
76
+ continue;
77
+ }
78
+ // Resolve the build output directory (no target arg — debug config is not packed)
79
+ const outDir = project.getOutDir();
80
+ const absoluteOutDir = _nodepath.default.resolve(outDir);
81
+ // Pre-create the output dir so the symlink is valid even before a build completes.
82
+ await _promises.default.mkdir(absoluteOutDir, {
83
+ recursive: true
84
+ });
85
+ // Compute symlink path inside target node_modules
86
+ const symlinkPath = _nodepath.default.join(nodeModulesDir, ...packageName.split("/"));
87
+ const symlinkParent = _nodepath.default.dirname(symlinkPath);
88
+ // Capture any existing entry so we can restore it on unlink.
89
+ // If a manifest already exists for this package, preserve its originalPath —
90
+ // the current symlink is our own and must not be treated as the original.
91
+ let originalPath = null;
92
+ const existingStat = await _promises.default.lstat(symlinkPath).catch(()=>null);
93
+ if (existingStat != null) {
94
+ const existingEntry = existingManifest?.links.find((l)=>l.packageName === packageName);
95
+ if (existingEntry != null) {
96
+ // Already linked — preserve the recorded originalPath and replace the symlink
97
+ originalPath = existingEntry.originalPath;
98
+ if (existingStat.isSymbolicLink()) {
99
+ await _promises.default.unlink(symlinkPath);
100
+ }
101
+ } else if (existingStat.isSymbolicLink()) {
102
+ originalPath = await _promises.default.readlink(symlinkPath);
103
+ await _promises.default.unlink(symlinkPath);
104
+ } else {
105
+ // Real directory — back it up by renaming
106
+ originalPath = `${symlinkPath}.__forge_backup__`;
107
+ await _promises.default.rename(symlinkPath, originalPath);
108
+ }
109
+ }
110
+ await _promises.default.mkdir(symlinkParent, {
111
+ recursive: true
112
+ });
113
+ await _promises.default.symlink(absoluteOutDir, symlinkPath, "dir");
114
+ manifest.links.push({
115
+ packageName,
116
+ symlinkPath,
117
+ originalPath
118
+ });
119
+ this.#logger.info(`Linked ${packageName} → ${absoluteOutDir}`);
120
+ }
121
+ // Create a peer node_modules at the build output root so Node can resolve
122
+ // bare specifiers from the real (non-symlink) paths of linked packages.
123
+ if (manifest.links.length > 0) {
124
+ const buildRoot = _nodepath.default.resolve(projects[0].getRootOut());
125
+ const peerNodeModulesDir = _nodepath.default.join(buildRoot, "node_modules");
126
+ await _promises.default.rm(peerNodeModulesDir, {
127
+ recursive: true,
128
+ force: true
129
+ });
130
+ for (const project of projects){
131
+ const pkg = await project.getPackageJson();
132
+ if (!pkg.name) continue;
133
+ const outDir = _nodepath.default.resolve(project.getOutDir());
134
+ const peerSymlink = _nodepath.default.join(peerNodeModulesDir, ...pkg.name.split("/"));
135
+ await _promises.default.mkdir(_nodepath.default.dirname(peerSymlink), {
136
+ recursive: true
137
+ });
138
+ await _promises.default.symlink(outDir, peerSymlink, "dir");
139
+ this.#logger.trace(`Peer-linked ${pkg.name} → ${outDir}`);
140
+ }
141
+ manifest.peerNodeModulesDir = peerNodeModulesDir;
142
+ this.#logger.info(`Created peer node_modules at ${peerNodeModulesDir}`);
143
+ }
144
+ await _promises.default.writeFile(_nodepath.default.join(targetDir, LINK_MANIFEST), JSON.stringify(manifest, null, 2));
145
+ return _cli.ActionResponse.Success(Link.name);
146
+ };
147
+ }
148
+ Link = _ts_decorate([
149
+ (0, _decorators.injectable)(),
150
+ _ts_metadata("design:type", Function),
151
+ _ts_metadata("design:paramtypes", [
152
+ typeof _workspace.Workspace === "undefined" ? Object : _workspace.Workspace
153
+ ])
154
+ ], Link);
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "PackageDeployable", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return PackageDeployable;
9
+ }
10
+ });
11
+ const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
12
+ const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
13
+ const _decorators = require("@decohere/crucible/decorators");
14
+ const _workspace = require("@decohere/workspace");
15
+ const _cli = require("@decohere/devkit/cli");
16
+ const _interop = require("@decohere/devkit/interop");
17
+ const _io = require("@decohere/core/io");
18
+ const _scribe = require("@decohere/scribe");
19
+ const _errors = require("../errors.js");
20
+ const _dependencypinnerservice = require("../packaging/dependency-pinner.service.js");
21
+ const _deployable = require("../packaging/deployable.js");
22
+ const _importgraphservice = require("../packaging/import-graph.service.js");
23
+ const _releaseexports = require("../packaging/release-exports.js");
24
+ const _uiassets = require("../packaging/ui-assets.js");
25
+ const _workspaceconfig = require("../workspace.config.js");
26
+ function _interop_require_default(obj) {
27
+ return obj && obj.__esModule ? obj : {
28
+ default: obj
29
+ };
30
+ }
31
+ function _ts_decorate(decorators, target, key, desc) {
32
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
33
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
34
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
35
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36
+ }
37
+ function _ts_metadata(k, v) {
38
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
39
+ }
40
+ class PackageDeployable {
41
+ workspace;
42
+ importGraph;
43
+ pinner;
44
+ #logger = _scribe.scribe.logger(PackageDeployable.name);
45
+ npm = new _interop.Npm();
46
+ constructor(workspace, importGraph, pinner){
47
+ this.workspace = workspace;
48
+ this.importGraph = importGraph;
49
+ this.pinner = pinner;
50
+ }
51
+ async run(options) {
52
+ const projects = this.workspace.getProjects(_workspaceconfig.Release.name);
53
+ const selected = await this.select(projects, options.project);
54
+ if (typeof selected === "string") {
55
+ return _cli.ActionResponse.Error(PackageDeployable.name, selected);
56
+ }
57
+ if (selected.length === 0) {
58
+ this.#logger.info("No projects found with a Dockerfile — nothing to package");
59
+ return _cli.ActionResponse.Success(PackageDeployable.name, []);
60
+ }
61
+ const releaseProjects = await this.releaseProjects(projects);
62
+ const responses = [];
63
+ for (const project of selected){
64
+ responses.push(await this.packageProject(project, releaseProjects, projects));
65
+ }
66
+ return responses.some((response)=>!response.succeeded) ? _cli.ActionResponse.Error(PackageDeployable.name, responses) : _cli.ActionResponse.Success(PackageDeployable.name, responses);
67
+ }
68
+ /** The deployables to package, or the reason a named project cannot be one. */ async select(projects, requested) {
69
+ if (requested == null || requested === "") {
70
+ const found = [];
71
+ for (const project of projects){
72
+ if (await _deployable.deployable.isDeployable(project)) {
73
+ found.push(project);
74
+ }
75
+ }
76
+ return found;
77
+ }
78
+ const project = projects.find((candidate)=>candidate.name === requested);
79
+ if (project == null) {
80
+ return `${requested} is not a workspace project. Known projects: ${projects.map((p)=>p.name).join(", ")}`;
81
+ }
82
+ if (!await _deployable.deployable.isDeployable(project)) {
83
+ return `${requested} has no ${_deployable.deployable.dockerfileName(project)}, so it is a library, not a deployable. ` + "Libraries are packaged into the deployables that import them.";
84
+ }
85
+ return [
86
+ project
87
+ ];
88
+ }
89
+ async releaseProjects(projects) {
90
+ const entries = await Promise.all(projects.map(async (project)=>{
91
+ const pkg = await project.getPackageJson();
92
+ return [
93
+ project.name,
94
+ {
95
+ name: project.name,
96
+ pkg,
97
+ projectRoot: _nodepath.default.resolve(project.projectRoot),
98
+ releaseDir: _nodepath.default.resolve(project.getOutDir())
99
+ }
100
+ ];
101
+ }));
102
+ return new Map(entries);
103
+ }
104
+ async packageProject(project, releaseProjects, projects) {
105
+ const outDir = _nodepath.default.join(_nodepath.default.resolve(project.projectRoot), _deployable.PACKAGE_DIR);
106
+ const base = {
107
+ projectName: project.name,
108
+ outDir,
109
+ workspacePackages: [],
110
+ dependencies: {}
111
+ };
112
+ try {
113
+ const entry = releaseProjects.get(project.name);
114
+ if (!await _io.io.directoryExists(_nodepath.default.join(entry.releaseDir, "es6"))) {
115
+ throw new _errors.ForgeError(`No release build for ${project.name}. Run \`forge build --configuration release\` first.`);
116
+ }
117
+ const ui = await _uiassets.uiAssets.resolve(project, projects);
118
+ const reached = await this.importGraph.walk(entry, releaseProjects);
119
+ const libraries = [
120
+ ...reached.workspace
121
+ ].filter((name)=>name !== project.name).sort().map((name)=>releaseProjects.get(name));
122
+ const dependencies = await this.pinner.pin(reached.thirdParty, [
123
+ entry,
124
+ ...libraries
125
+ ], releaseProjects, {
126
+ dir: _nodepath.default.resolve(this.workspace.rootDir),
127
+ pkg: await this.workspace.getPackageJson()
128
+ });
129
+ await this.write(entry, libraries, dependencies, outDir, ui);
130
+ const workspacePackages = [
131
+ ...reached.workspace
132
+ ].sort();
133
+ this.#logger.info(`Packaged ${project.name} to ${outDir}`, {
134
+ workspace: workspacePackages,
135
+ thirdParty: Object.keys(dependencies)
136
+ });
137
+ return {
138
+ projectName: project.name,
139
+ outDir,
140
+ workspacePackages,
141
+ dependencies,
142
+ succeeded: true,
143
+ message: `Packaged ${project.name} to ${outDir}`
144
+ };
145
+ } catch (reason) {
146
+ const message = (0, _errors.formatUnknownError)(reason);
147
+ return {
148
+ ...base,
149
+ succeeded: false,
150
+ message,
151
+ error: `${project.name}: ${message}`
152
+ };
153
+ }
154
+ }
155
+ async write(entry, libraries, dependencies, outDir, ui) {
156
+ await _promises.default.rm(outDir, {
157
+ recursive: true,
158
+ force: true
159
+ });
160
+ await _promises.default.mkdir(outDir, {
161
+ recursive: true
162
+ });
163
+ await this.writeJson(_nodepath.default.join(outDir, "package.json"), {
164
+ name: entry.name.replace(/^@/, "").replace("/", "-"),
165
+ version: entry.pkg.version,
166
+ private: true,
167
+ type: "module",
168
+ scripts: {
169
+ start: `node es6/${_releaseexports.releaseExports.toJs(entry.pkg.main ?? "src/index.ts")}`
170
+ },
171
+ dependencies
172
+ });
173
+ // npm writes real folders. It also removes anything in node_modules it does not know,
174
+ // so the workspace libraries are copied in only after it has run.
175
+ if (Object.keys(dependencies).length > 0) {
176
+ await this.npm.installProduction({
177
+ cwd: outDir
178
+ });
179
+ }
180
+ await this.copyTree(_nodepath.default.join(entry.releaseDir, "es6"), _nodepath.default.join(outDir, "es6"));
181
+ const config = _nodepath.default.join(_nodepath.default.resolve(this.workspace.rootDir), "config");
182
+ if (await _io.io.directoryExists(config)) {
183
+ await this.copyTree(config, _nodepath.default.join(outDir, "config"));
184
+ }
185
+ if (ui != null) {
186
+ await this.copyTree(ui.from, _nodepath.default.join(outDir, ui.to));
187
+ }
188
+ for (const library of libraries){
189
+ const target = _nodepath.default.join(outDir, "node_modules", ...library.name.split("/"));
190
+ await this.copyTree(_nodepath.default.join(library.releaseDir, "es6"), _nodepath.default.join(target, "es6"));
191
+ await this.writeJson(_nodepath.default.join(target, "package.json"), {
192
+ name: library.name,
193
+ version: library.pkg.version,
194
+ type: "module",
195
+ exports: _releaseexports.releaseExports.build(library.pkg)
196
+ });
197
+ }
198
+ await this.assertNoSymlinks(outDir);
199
+ }
200
+ /** Copies file contents, never links: a symlink in the output would break outside the image. */ async copyTree(from, to) {
201
+ await _promises.default.cp(from, to, {
202
+ recursive: true,
203
+ dereference: true
204
+ });
205
+ }
206
+ async writeJson(filePath, value) {
207
+ await _io.io.writeFile(filePath, `${JSON.stringify(value, null, "\t")}\n`, {
208
+ mkdir: true,
209
+ encoding: "utf8"
210
+ });
211
+ }
212
+ async assertNoSymlinks(dir) {
213
+ for (const entry of (await _promises.default.readdir(dir, {
214
+ withFileTypes: true
215
+ }))){
216
+ const entryPath = _nodepath.default.join(dir, entry.name);
217
+ if (entry.isSymbolicLink()) {
218
+ throw new _errors.ForgeError(`The packaged output must hold no symlinks, but found ${entryPath}`);
219
+ }
220
+ if (entry.isDirectory()) {
221
+ await this.assertNoSymlinks(entryPath);
222
+ }
223
+ }
224
+ }
225
+ }
226
+ PackageDeployable = _ts_decorate([
227
+ (0, _decorators.injectable)(),
228
+ _ts_metadata("design:type", Function),
229
+ _ts_metadata("design:paramtypes", [
230
+ typeof _workspace.Workspace === "undefined" ? Object : _workspace.Workspace,
231
+ typeof _importgraphservice.ImportGraph === "undefined" ? Object : _importgraphservice.ImportGraph,
232
+ typeof _dependencypinnerservice.DependencyPinner === "undefined" ? Object : _dependencypinnerservice.DependencyPinner
233
+ ])
234
+ ], PackageDeployable);