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