@fuman/build 0.0.7 → 0.0.10
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/cli/commands/release.d.ts +2 -0
- package/cli/commands/release.js +34 -8
- package/cli/commands/validate-workspace-deps.d.ts +6 -0
- package/cli/commands/validate-workspace-deps.js +18 -4
- package/git/github.js +6 -4
- package/index.js +2 -1
- package/jsr/generate-workspace.js +25 -6
- package/jsr/index.d.ts +0 -1
- package/jsr.js +0 -2
- package/package-json/collect-package-jsons.d.ts +2 -0
- package/package-json/collect-package-jsons.js +8 -6
- package/package-json/parse.d.ts +9 -2
- package/package-json/parse.js +49 -8
- package/package-json/types.js +1 -1
- package/package.json +6 -5
- package/versioning/bump-version.js +1 -1
- package/versioning/types.d.ts +8 -0
- package/jsr/build-jsr.d.ts +0 -7
- package/jsr/build-jsr.js +0 -146
|
@@ -2,6 +2,7 @@ import { bc } from './_utils.js';
|
|
|
2
2
|
export declare const releaseCli: bc.Command<{
|
|
3
3
|
kind: "auto" | "major" | "minor" | "patch";
|
|
4
4
|
withGithubRelease: boolean;
|
|
5
|
+
gitExtraOrigins: string | undefined;
|
|
5
6
|
githubToken: string | undefined;
|
|
6
7
|
githubRepo: string | undefined;
|
|
7
8
|
githubApiUrl: string | undefined;
|
|
@@ -19,6 +20,7 @@ export declare const releaseCli: bc.Command<{
|
|
|
19
20
|
}, {
|
|
20
21
|
kind: "auto" | "major" | "minor" | "patch";
|
|
21
22
|
withGithubRelease: boolean;
|
|
23
|
+
gitExtraOrigins: string | undefined;
|
|
22
24
|
githubToken: string | undefined;
|
|
23
25
|
githubRepo: string | undefined;
|
|
24
26
|
githubApiUrl: string | undefined;
|
package/cli/commands/release.js
CHANGED
|
@@ -21,6 +21,8 @@ const releaseCli = bc.command({
|
|
|
21
21
|
options: {
|
|
22
22
|
kind: bc.string("kind").desc("release kind").enum("major", "minor", "patch", "auto").default("auto"),
|
|
23
23
|
withGithubRelease: bc.boolean("with-github-release").desc("whether to create a github release (requires GITHUB_TOKEN env var). if false, will only create a commit with the release notes").default(false),
|
|
24
|
+
gitExtraOrigins: bc.string("git-extra-origins").desc("extra git origins to push to (e.g. for mirrors). note that these origins will be force-pushed to"),
|
|
25
|
+
// ...because for some reason forgejo fails to properly push to the mirrors on push :/
|
|
24
26
|
githubToken: bc.string("github-token").desc("github token to use for creating a release (defaults to GITHUB_TOKEN env var)"),
|
|
25
27
|
githubRepo: bc.string("github-repo").desc("github repo to create a release for (defaults to GITHUB_REPOSITORY env var)"),
|
|
26
28
|
githubApiUrl: bc.string("github-api-url").desc("github api url to use for creating a release (for github-compatible apis)"),
|
|
@@ -58,6 +60,7 @@ const releaseCli = bc.command({
|
|
|
58
60
|
workspace: workspaceWithRoot,
|
|
59
61
|
since: prevTag ?? await getFirstCommit(root),
|
|
60
62
|
type: args.kind === "auto" ? void 0 : args.kind,
|
|
63
|
+
all: args.kind !== "auto",
|
|
61
64
|
cwd: root,
|
|
62
65
|
params: config?.versioning,
|
|
63
66
|
dryRun: args.dryRun,
|
|
@@ -90,6 +93,13 @@ const releaseCli = bc.command({
|
|
|
90
93
|
process.exit(1);
|
|
91
94
|
}
|
|
92
95
|
}
|
|
96
|
+
if (!changedPackages.some((pkg) => pkg.json.version === tagName.replace(/^v/, ""))) {
|
|
97
|
+
console.log(`❗ tag ${tagName} does not match any of the package versions. did the previous release complete successfully?`);
|
|
98
|
+
console.log("❗ if so, please verify versions in package.json, tag the commit release and try again");
|
|
99
|
+
if (!args.dryRun) {
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
93
103
|
console.log("");
|
|
94
104
|
console.log("📝 generating changelog...");
|
|
95
105
|
const changelog = prevTag != null ? await generateChangelog({
|
|
@@ -161,6 +171,7 @@ const releaseCli = bc.command({
|
|
|
161
171
|
"--quiet",
|
|
162
172
|
"--allow-dirty",
|
|
163
173
|
...args.dryRun ? ["--dry-run"] : [],
|
|
174
|
+
...args.jsrToken != null ? ["--token", args.jsrToken] : [],
|
|
164
175
|
...args.jsrPublishArgs?.split(" ") ?? []
|
|
165
176
|
], {
|
|
166
177
|
env: {
|
|
@@ -176,6 +187,7 @@ const releaseCli = bc.command({
|
|
|
176
187
|
if (args.dryRun) {
|
|
177
188
|
console.log("dry run, skipping release commit and tag");
|
|
178
189
|
} else {
|
|
190
|
+
await config?.versioning?.beforeReleaseCommit?.(workspaceWithRoot);
|
|
179
191
|
let message = `chore(release): ${tagName}`;
|
|
180
192
|
if (!args.withGithubRelease) {
|
|
181
193
|
message += `
|
|
@@ -187,11 +199,32 @@ ${changelog}`;
|
|
|
187
199
|
stdio: "inherit",
|
|
188
200
|
throwOnError: true
|
|
189
201
|
});
|
|
190
|
-
await exec(["git", "tag", tagName], {
|
|
202
|
+
await exec(["git", "tag", tagName, "-m", tagName], {
|
|
203
|
+
cwd: root,
|
|
204
|
+
stdio: "inherit",
|
|
205
|
+
throwOnError: true
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
if (!args.dryRun) {
|
|
209
|
+
await exec(["git", "push", "--follow-tags"], {
|
|
191
210
|
cwd: root,
|
|
192
211
|
stdio: "inherit",
|
|
193
212
|
throwOnError: true
|
|
194
213
|
});
|
|
214
|
+
if (args.gitExtraOrigins != null) {
|
|
215
|
+
for (const origin of args.gitExtraOrigins.split(",")) {
|
|
216
|
+
await exec(["git", "push", origin, "--force"], {
|
|
217
|
+
cwd: root,
|
|
218
|
+
stdio: "inherit",
|
|
219
|
+
throwOnError: true
|
|
220
|
+
});
|
|
221
|
+
await exec(["git", "push", origin, "--force", "--tags"], {
|
|
222
|
+
cwd: root,
|
|
223
|
+
stdio: "inherit",
|
|
224
|
+
throwOnError: true
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
195
228
|
}
|
|
196
229
|
if (args.withGithubRelease) {
|
|
197
230
|
if (args.dryRun) {
|
|
@@ -222,13 +255,6 @@ ${changelog}`;
|
|
|
222
255
|
console.log(`\x1B[;32m✅github release created: https://github.com/${repo}/releases/tag/${tagName}\x1B[;0m`);
|
|
223
256
|
}
|
|
224
257
|
}
|
|
225
|
-
if (!args.dryRun) {
|
|
226
|
-
await exec(["git", "push", "--follow-tags"], {
|
|
227
|
-
cwd: root,
|
|
228
|
-
stdio: "inherit",
|
|
229
|
-
throwOnError: true
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
258
|
console.log("");
|
|
233
259
|
console.log("🎉 done!");
|
|
234
260
|
}
|
|
@@ -32,13 +32,19 @@ export declare function validateWorkspaceDeps(params: {
|
|
|
32
32
|
* @default true
|
|
33
33
|
*/
|
|
34
34
|
skipWorkspaceDeps?: boolean;
|
|
35
|
+
/** whether to skip validating peer dependencies */
|
|
36
|
+
skipPeerDeps?: boolean;
|
|
35
37
|
}): Promise<WorkspaceDepsError[]>;
|
|
36
38
|
export declare const validateWorkspaceDepsCli: bc.Command<{
|
|
37
39
|
includeRoot: boolean;
|
|
38
40
|
noSkipWorkspaceDeps: boolean | undefined;
|
|
39
41
|
root: string | undefined;
|
|
42
|
+
withErrorCode: boolean | undefined;
|
|
43
|
+
skipPeerDeps: boolean | undefined;
|
|
40
44
|
}, {
|
|
41
45
|
includeRoot: boolean;
|
|
42
46
|
noSkipWorkspaceDeps: boolean | undefined;
|
|
43
47
|
root: string | undefined;
|
|
48
|
+
withErrorCode: boolean | undefined;
|
|
49
|
+
skipPeerDeps: boolean | undefined;
|
|
44
50
|
}>;
|
|
@@ -6,7 +6,8 @@ async function validateWorkspaceDeps(params) {
|
|
|
6
6
|
const {
|
|
7
7
|
workspaceRoot,
|
|
8
8
|
includeRoot = true,
|
|
9
|
-
skipWorkspaceDeps = true
|
|
9
|
+
skipWorkspaceDeps = true,
|
|
10
|
+
skipPeerDeps = true
|
|
10
11
|
} = params;
|
|
11
12
|
const pjs = await collectPackageJsons(workspaceRoot, includeRoot);
|
|
12
13
|
const workspacePackages = new Set(skipWorkspaceDeps ? pjs.map((pj) => pj.json.name) : []);
|
|
@@ -19,6 +20,7 @@ async function validateWorkspaceDeps(params) {
|
|
|
19
20
|
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
20
21
|
const deps = pj[field];
|
|
21
22
|
if (!deps) continue;
|
|
23
|
+
if (field === "peerDependencies" && skipPeerDeps) continue;
|
|
22
24
|
for (const [name, version] of Object.entries(deps)) {
|
|
23
25
|
if (workspacePackages.has(name)) continue;
|
|
24
26
|
if (version.startsWith("workspace:")) continue;
|
|
@@ -26,7 +28,13 @@ async function validateWorkspaceDeps(params) {
|
|
|
26
28
|
versions[name] = {};
|
|
27
29
|
}
|
|
28
30
|
for (const [pkgName, pkgDepVersions] of Object.entries(versions[name])) {
|
|
29
|
-
|
|
31
|
+
let ok = true;
|
|
32
|
+
if (pkgDepVersions.match(/^(?:https?:\/\/|catalog:)/)) {
|
|
33
|
+
ok = version === pkgDepVersions;
|
|
34
|
+
} else {
|
|
35
|
+
ok = satisfies(version, pkgDepVersions);
|
|
36
|
+
}
|
|
37
|
+
if (!ok) {
|
|
30
38
|
errors.push({
|
|
31
39
|
package: pj.name,
|
|
32
40
|
dependency: name,
|
|
@@ -49,19 +57,25 @@ const validateWorkspaceDepsCli = bc.command({
|
|
|
49
57
|
options: {
|
|
50
58
|
includeRoot: bc.boolean("include-root").desc("whether to also validate the root package.json").default(false),
|
|
51
59
|
noSkipWorkspaceDeps: bc.boolean("no-skip-workspace-deps").desc("whether to not skip validating dependencies of other workspace packages"),
|
|
52
|
-
root: bc.string().desc("path to the root of the workspace (default: cwd)")
|
|
60
|
+
root: bc.string().desc("path to the root of the workspace (default: cwd)"),
|
|
61
|
+
withErrorCode: bc.boolean("with-error-code").desc("whether to exit with a non-zero code if there are mismatches"),
|
|
62
|
+
skipPeerDeps: bc.boolean("skip-peer-deps").desc("whether to skip validating peer dependencies")
|
|
53
63
|
},
|
|
54
64
|
handler: async (args) => {
|
|
55
65
|
const errors = await validateWorkspaceDeps({
|
|
56
66
|
workspaceRoot: args.root ?? process.cwd(),
|
|
57
67
|
includeRoot: args.includeRoot,
|
|
58
|
-
skipWorkspaceDeps: !args.noSkipWorkspaceDeps
|
|
68
|
+
skipWorkspaceDeps: !args.noSkipWorkspaceDeps,
|
|
69
|
+
skipPeerDeps: args.skipPeerDeps
|
|
59
70
|
});
|
|
60
71
|
if (errors.length > 0) {
|
|
61
72
|
console.error("⚠️ Found external dependencies mismatch:");
|
|
62
73
|
for (const error of errors) {
|
|
63
74
|
console.error(` - at ${error.package}: ${error.at} has ${error.dependency}@${error.version}, but ${error.otherPackage} has @${error.otherVersion}`);
|
|
64
75
|
}
|
|
76
|
+
if (args.withErrorCode) {
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
65
79
|
} else {
|
|
66
80
|
console.log("✅ All external dependencies match!");
|
|
67
81
|
}
|
package/git/github.js
CHANGED
|
@@ -20,16 +20,18 @@ async function createGithubRelease(params) {
|
|
|
20
20
|
tag_name: params.tag,
|
|
21
21
|
name: params.name,
|
|
22
22
|
body: params.body,
|
|
23
|
-
draft: params.draft,
|
|
24
|
-
prerelease: params.prerelease
|
|
23
|
+
draft: params.draft ?? false,
|
|
24
|
+
prerelease: params.prerelease ?? false
|
|
25
25
|
},
|
|
26
26
|
validateResponse: (res) => res.status === 201
|
|
27
27
|
}).parsedJson(z.object({
|
|
28
|
-
id: z.number()
|
|
28
|
+
id: z.number(),
|
|
29
|
+
upload_url: z.string()
|
|
29
30
|
}));
|
|
31
|
+
const uploadUrl = release.upload_url.split("{")[0];
|
|
30
32
|
if (params.artifacts != null && params.artifacts.length > 0) {
|
|
31
33
|
await asyncPool(params.artifacts, async (file) => {
|
|
32
|
-
await ffetch(
|
|
34
|
+
await ffetch(uploadUrl, {
|
|
33
35
|
method: "POST",
|
|
34
36
|
query: { name: file.name },
|
|
35
37
|
headers: {
|
package/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { getTsconfigFiles, getTsconfigFor } from "./misc/tsconfig.js";
|
|
|
11
11
|
import { NPM_PACKAGE_NAME_REGEX, npmCheckVersion } from "./npm/npm-api.js";
|
|
12
12
|
import { collectPackageJsons, filterPackageJsonsForPublish } from "./package-json/collect-package-jsons.js";
|
|
13
13
|
import { findPackageJson } from "./package-json/find-package-json.js";
|
|
14
|
-
import { parsePackageJson, parsePackageJsonFile, parseWorkspaceRootPackageJson } from "./package-json/parse.js";
|
|
14
|
+
import { parsePackageJson, parsePackageJsonFile, parsePackageJsonFromDir, parseWorkspaceRootPackageJson } from "./package-json/parse.js";
|
|
15
15
|
import { processPackageJson } from "./package-json/process-package-json.js";
|
|
16
16
|
import { PackageJsonSchema } from "./package-json/types.js";
|
|
17
17
|
import { bumpVersion } from "./versioning/bump-version.js";
|
|
@@ -48,6 +48,7 @@ export {
|
|
|
48
48
|
parseConventionalCommit,
|
|
49
49
|
parsePackageJson,
|
|
50
50
|
parsePackageJsonFile,
|
|
51
|
+
parsePackageJsonFromDir,
|
|
51
52
|
parseWorkspaceRootPackageJson,
|
|
52
53
|
processPackageJson,
|
|
53
54
|
sortWorkspaceByPublishOrder,
|
|
@@ -2,6 +2,7 @@ import * as fsp from "node:fs/promises";
|
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { asyncPool } from "@fuman/utils";
|
|
5
|
+
import picomatch from "picomatch";
|
|
5
6
|
import { glob } from "tinyglobby";
|
|
6
7
|
import ts from "typescript";
|
|
7
8
|
import { loadBuildConfig } from "../misc/_config.js";
|
|
@@ -62,12 +63,18 @@ async function generateDenoWorkspace(params) {
|
|
|
62
63
|
const packageConfigJsr = packageConfig?.jsr;
|
|
63
64
|
const srcDir = join(packageRoot, normalizeFilePath(packageConfigJsr?.sourceDir ?? rootConfig?.sourceDir ?? ""));
|
|
64
65
|
const excludeFiles = mergeArrays(rootConfig?.exclude, packageConfigJsr?.exclude);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
const exludeFilesPico = picomatch(excludeFiles);
|
|
67
|
+
await fsp.cp(srcDir, packageOutRoot, {
|
|
68
|
+
recursive: true,
|
|
69
|
+
filter(source) {
|
|
70
|
+
if (exludeFilesPico(relative(srcDir, source))) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
70
75
|
});
|
|
76
|
+
const printer = ts.createPrinter();
|
|
77
|
+
const tsFiles = await glob("**/*.ts", { cwd: packageOutRoot });
|
|
71
78
|
await asyncPool(tsFiles, async (filename) => {
|
|
72
79
|
const fullFilePath = join(packageOutRoot, filename);
|
|
73
80
|
let fileContent = await fsp.readFile(fullFilePath, "utf8");
|
|
@@ -121,7 +128,7 @@ async function generateDenoWorkspace(params) {
|
|
|
121
128
|
}
|
|
122
129
|
});
|
|
123
130
|
const hookContext = {
|
|
124
|
-
outDir:
|
|
131
|
+
outDir: packageOutRoot,
|
|
125
132
|
packageDir: packageOutRoot,
|
|
126
133
|
packageName: pkg.json.name,
|
|
127
134
|
packageJson: pkg.json,
|
|
@@ -144,6 +151,8 @@ async function generateDenoWorkspace(params) {
|
|
|
144
151
|
packageJson.version = fixedVersion;
|
|
145
152
|
packageJsonOrig.version = fixedVersion;
|
|
146
153
|
}
|
|
154
|
+
hookContext.packageJson = packageJson;
|
|
155
|
+
await packageConfig?.finalizePackageJson?.(hookContext);
|
|
147
156
|
const denoJson = packageJsonToDeno({
|
|
148
157
|
packageJson,
|
|
149
158
|
packageJsonOrig,
|
|
@@ -152,6 +161,7 @@ async function generateDenoWorkspace(params) {
|
|
|
152
161
|
baseDir: relative(packageRoot, srcDir),
|
|
153
162
|
exclude: excludeFiles
|
|
154
163
|
});
|
|
164
|
+
packageConfig?.jsr?.finalizeDenoJson?.(hookContext, denoJson);
|
|
155
165
|
await fsp.writeFile(join(packageOutRoot, "deno.json"), JSON.stringify(denoJson, null, 4));
|
|
156
166
|
for (const file of mergeArrays(rootConfig?.copyRootFiles, packageConfig?.jsr?.copyRootFiles, ["LICENSE"])) {
|
|
157
167
|
await tryCopy(join(workspaceRoot, file), join(packageOutRoot, file), { recursive: true });
|
|
@@ -159,8 +169,17 @@ async function generateDenoWorkspace(params) {
|
|
|
159
169
|
for (const file of mergeArrays(rootConfig?.copyPackageFiles, packageConfig?.jsr?.copyPackageFiles, ["README.md"])) {
|
|
160
170
|
await tryCopy(join(packageRoot, file), join(packageOutRoot, file), { recursive: true });
|
|
161
171
|
}
|
|
172
|
+
await packageConfig?.jsr?.finalize?.(hookContext);
|
|
162
173
|
}
|
|
163
174
|
await fsp.writeFile(join(outDir, "deno.json"), JSON.stringify(rootDenoJson, null, 4));
|
|
175
|
+
await rootConfig?.finalize?.({
|
|
176
|
+
outDir,
|
|
177
|
+
packageDir: outDir,
|
|
178
|
+
packageName: "<jsr-root>",
|
|
179
|
+
packageJson: {},
|
|
180
|
+
jsr: true,
|
|
181
|
+
typedoc: false
|
|
182
|
+
});
|
|
164
183
|
if (rootConfig?.dryRun !== false || withDryRun) {
|
|
165
184
|
await exec(["deno", "publish", "--dry-run", "-q", "--allow-dirty"], {
|
|
166
185
|
cwd: outDir,
|
package/jsr/index.d.ts
CHANGED
package/jsr.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { runJsrBuild } from "./jsr/build-jsr.js";
|
|
2
1
|
import { packageJsonToDeno } from "./jsr/deno-json.js";
|
|
3
2
|
import { populateFromUpstream } from "./jsr/populate.js";
|
|
4
3
|
import { getModuleCacheDirectory, parseImportSpecifier, splitImportRequest } from "./jsr/utils/external-libs.js";
|
|
@@ -18,6 +17,5 @@ export {
|
|
|
18
17
|
parseImportSpecifier,
|
|
19
18
|
parseJsrJson,
|
|
20
19
|
populateFromUpstream,
|
|
21
|
-
runJsrBuild,
|
|
22
20
|
splitImportRequest
|
|
23
21
|
};
|
|
@@ -3,6 +3,8 @@ import { PackageJson } from './types.js';
|
|
|
3
3
|
export interface WorkspacePackage {
|
|
4
4
|
/** path to the package root */
|
|
5
5
|
path: string;
|
|
6
|
+
/** path to the package.json file (note that it might not be a .json file) */
|
|
7
|
+
packageJsonPath: string;
|
|
6
8
|
/** whether this is the root package */
|
|
7
9
|
root: boolean;
|
|
8
10
|
/** package.json of the package */
|
|
@@ -2,12 +2,12 @@ import * as path from "node:path";
|
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import { glob } from "tinyglobby";
|
|
4
4
|
import { normalizeFilePath } from "../misc/path.js";
|
|
5
|
-
import { parseWorkspaceRootPackageJson,
|
|
5
|
+
import { parseWorkspaceRootPackageJson, parsePackageJsonFromDir } from "./parse.js";
|
|
6
6
|
const maxDepth = process.env.FUMAN_BUILD_MAX_DEPTH !== void 0 ? Number(process.env.FUMAN_BUILD_MAX_DEPTH) : 5;
|
|
7
7
|
async function collectPackageJsons(workspaceRoot, includeRoot = false) {
|
|
8
8
|
workspaceRoot = normalizeFilePath(workspaceRoot);
|
|
9
9
|
const packageJsons = [];
|
|
10
|
-
const rootPackageJson = await parseWorkspaceRootPackageJson(workspaceRoot);
|
|
10
|
+
const { path: rootPackageJsonPath, json: rootPackageJson } = await parseWorkspaceRootPackageJson(workspaceRoot);
|
|
11
11
|
if (!rootPackageJson.workspaces) {
|
|
12
12
|
throw new Error("No workspaces found in package.json");
|
|
13
13
|
}
|
|
@@ -15,7 +15,8 @@ async function collectPackageJsons(workspaceRoot, includeRoot = false) {
|
|
|
15
15
|
packageJsons.push({
|
|
16
16
|
path: workspaceRoot,
|
|
17
17
|
root: true,
|
|
18
|
-
json: rootPackageJson
|
|
18
|
+
json: rootPackageJson,
|
|
19
|
+
packageJsonPath: rootPackageJsonPath
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
22
|
for (const dir of await glob({
|
|
@@ -26,14 +27,15 @@ async function collectPackageJsons(workspaceRoot, includeRoot = false) {
|
|
|
26
27
|
deep: maxDepth
|
|
27
28
|
})) {
|
|
28
29
|
try {
|
|
29
|
-
const
|
|
30
|
+
const { json, path: packageJsonPath } = await parsePackageJsonFromDir(path.join(workspaceRoot, dir));
|
|
30
31
|
packageJsons.push({
|
|
31
32
|
path: path.join(workspaceRoot, dir),
|
|
32
33
|
root: false,
|
|
33
|
-
|
|
34
|
+
packageJsonPath,
|
|
35
|
+
json
|
|
34
36
|
});
|
|
35
37
|
} catch (err) {
|
|
36
|
-
if (err.code === "ENOENT") ;
|
|
38
|
+
if (err.code === "ENOENT" || err.cause?.notFound === true) ;
|
|
37
39
|
else {
|
|
38
40
|
throw err;
|
|
39
41
|
}
|
package/package-json/parse.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { PackageJson } from './types.js';
|
|
2
2
|
/** parse a package.json from string */
|
|
3
|
-
export declare function parsePackageJson(packageJson: string): PackageJson;
|
|
3
|
+
export declare function parsePackageJson(packageJson: string, format?: 'json' | 'yaml'): PackageJson;
|
|
4
4
|
/** parse a package.json file */
|
|
5
5
|
export declare function parsePackageJsonFile(packageJsonPath: string | URL): Promise<PackageJson>;
|
|
6
|
+
export declare function parsePackageJsonFromDir(dir: string | URL): Promise<{
|
|
7
|
+
path: string;
|
|
8
|
+
json: PackageJson;
|
|
9
|
+
}>;
|
|
6
10
|
/** parse the package.json file at the root of the workspace */
|
|
7
|
-
export declare function parseWorkspaceRootPackageJson(workspaceRoot: string | URL): Promise<
|
|
11
|
+
export declare function parseWorkspaceRootPackageJson(workspaceRoot: string | URL): Promise<{
|
|
12
|
+
path: string;
|
|
13
|
+
json: PackageJson;
|
|
14
|
+
}>;
|
package/package-json/parse.js
CHANGED
|
@@ -1,18 +1,58 @@
|
|
|
1
1
|
import * as fsp from "node:fs/promises";
|
|
2
|
-
import path__default from "node:path";
|
|
2
|
+
import path__default, { extname } from "node:path";
|
|
3
3
|
import * as jsyaml from "js-yaml";
|
|
4
|
+
import * as json5_ from "json5";
|
|
5
|
+
import { fileExists } from "../misc/fs.js";
|
|
4
6
|
import { normalizeFilePath } from "../misc/path.js";
|
|
5
7
|
import { PackageJsonSchema } from "./types.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
let json5 = json5_;
|
|
9
|
+
if ("default" in json5_) {
|
|
10
|
+
json5 = json5_.default;
|
|
11
|
+
}
|
|
12
|
+
function parsePackageJson(packageJson, format = "json") {
|
|
13
|
+
let obj;
|
|
14
|
+
if (format === "json") {
|
|
15
|
+
obj = json5.parse(packageJson);
|
|
16
|
+
} else {
|
|
17
|
+
obj = jsyaml.load(packageJson);
|
|
18
|
+
}
|
|
19
|
+
return PackageJsonSchema.parse(obj);
|
|
8
20
|
}
|
|
9
21
|
async function parsePackageJsonFile(packageJsonPath) {
|
|
10
|
-
|
|
22
|
+
const path2 = normalizeFilePath(packageJsonPath);
|
|
23
|
+
const ext = extname(path2).slice(1);
|
|
24
|
+
let format;
|
|
25
|
+
if (ext === "json5" || ext === "jsonc" || ext === "json") format = "json";
|
|
26
|
+
else if (ext === "yml" || ext === "yaml") format = "yaml";
|
|
27
|
+
else throw new Error(`Unknown package.json extension: ${ext}`);
|
|
28
|
+
try {
|
|
29
|
+
return parsePackageJson(await fsp.readFile(normalizeFilePath(packageJsonPath), "utf8"), format);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
throw new Error(`Could not parse package.json at ${packageJsonPath}`, { cause: err });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const EXT_OPTIONS = ["json", "json5", "jsonc", "yml", "yaml"];
|
|
35
|
+
async function parsePackageJsonFromDir(dir) {
|
|
36
|
+
dir = normalizeFilePath(dir);
|
|
37
|
+
let packageJsonPath;
|
|
38
|
+
for (const ext of EXT_OPTIONS) {
|
|
39
|
+
const tmp = path__default.join(dir, `package.${ext}`);
|
|
40
|
+
if (await fileExists(tmp)) {
|
|
41
|
+
packageJsonPath = tmp;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (packageJsonPath == null) {
|
|
46
|
+
throw new Error(`Could not find package.json at ${dir}`, { cause: { notFound: true } });
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
path: packageJsonPath,
|
|
50
|
+
json: await parsePackageJsonFile(packageJsonPath)
|
|
51
|
+
};
|
|
11
52
|
}
|
|
12
53
|
async function parseWorkspaceRootPackageJson(workspaceRoot) {
|
|
13
54
|
workspaceRoot = normalizeFilePath(workspaceRoot);
|
|
14
|
-
const
|
|
15
|
-
const parsed = await parsePackageJsonFile(packageJsonPath);
|
|
55
|
+
const { path: pjPath, json: parsed } = await parsePackageJsonFromDir(workspaceRoot);
|
|
16
56
|
if (!parsed.workspaces) {
|
|
17
57
|
const pnpmWorkspacePath = path__default.join(workspaceRoot, "pnpm-workspace.yaml");
|
|
18
58
|
let yaml;
|
|
@@ -20,7 +60,7 @@ async function parseWorkspaceRootPackageJson(workspaceRoot) {
|
|
|
20
60
|
yaml = await fsp.readFile(pnpmWorkspacePath, "utf8");
|
|
21
61
|
} catch (e) {
|
|
22
62
|
if (e.code !== "ENOENT") throw e;
|
|
23
|
-
return parsed;
|
|
63
|
+
return { path: pjPath, json: parsed };
|
|
24
64
|
}
|
|
25
65
|
const workspace = jsyaml.load(yaml);
|
|
26
66
|
if (!workspace.packages) {
|
|
@@ -40,10 +80,11 @@ async function parseWorkspaceRootPackageJson(workspaceRoot) {
|
|
|
40
80
|
}
|
|
41
81
|
parsed.workspaces = workspace.packages;
|
|
42
82
|
}
|
|
43
|
-
return parsed;
|
|
83
|
+
return { path: pjPath, json: parsed };
|
|
44
84
|
}
|
|
45
85
|
export {
|
|
46
86
|
parsePackageJson,
|
|
47
87
|
parsePackageJsonFile,
|
|
88
|
+
parsePackageJsonFromDir,
|
|
48
89
|
parseWorkspaceRootPackageJson
|
|
49
90
|
};
|
package/package-json/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuman/build",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.10",
|
|
5
5
|
"description": "utils for building packages and managing monorepos",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"scripts": {},
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@drizzle-team/brocli": "^0.10.2",
|
|
10
|
-
"@fuman/fetch": "^0.0.
|
|
11
|
-
"@fuman/io": "^0.0.
|
|
12
|
-
"@fuman/node": "^0.0.
|
|
13
|
-
"@fuman/utils": "^0.0.
|
|
10
|
+
"@fuman/fetch": "^0.0.10",
|
|
11
|
+
"@fuman/io": "^0.0.10",
|
|
12
|
+
"@fuman/node": "^0.0.10",
|
|
13
|
+
"@fuman/utils": "^0.0.10",
|
|
14
14
|
"cross-spawn": "^7.0.5",
|
|
15
15
|
"detect-indent": "^7.0.1",
|
|
16
16
|
"js-yaml": "^4.1.0",
|
|
17
|
+
"json5": "^2.2.3",
|
|
17
18
|
"picomatch": "^4.0.2",
|
|
18
19
|
"semver": "^7.6.3",
|
|
19
20
|
"tinyglobby": "^0.2.6",
|
|
@@ -75,7 +75,6 @@ async function bumpVersion(params) {
|
|
|
75
75
|
const workspaceVersions = collectVersions(workspace);
|
|
76
76
|
const result = [];
|
|
77
77
|
for (const pkg of changedPackages) {
|
|
78
|
-
if (pkg.json.fuman?.ownVersioning) continue;
|
|
79
78
|
result.push({
|
|
80
79
|
package: pkg,
|
|
81
80
|
prevVersion: asNonNull(pkg.json.version)
|
|
@@ -117,6 +116,7 @@ async function bumpVersion(params) {
|
|
|
117
116
|
packagesToBump.push(findRootPackage(workspace));
|
|
118
117
|
}
|
|
119
118
|
for (const pkg of packagesToBump) {
|
|
119
|
+
if (pkg.json.fuman?.ownVersioning) continue;
|
|
120
120
|
if (!dryRun) {
|
|
121
121
|
const pkgJsonPath = join(pkg.path, "package.json");
|
|
122
122
|
const pkgJsonText = await fsp.readFile(pkgJsonPath, "utf8");
|
package/versioning/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MaybePromise } from '@fuman/utils';
|
|
2
2
|
import { CommitInfo, ConventionalCommit } from '../git/utils.js';
|
|
3
|
+
import { WorkspacePackage } from '../package-json/collect-package-jsons.js';
|
|
3
4
|
import { ProjectChangedFile } from './collect-files.js';
|
|
4
5
|
export interface ChangelogGeneratorParams {
|
|
5
6
|
onCommitParseFailed?: (commit: CommitInfo) => void;
|
|
@@ -30,4 +31,11 @@ export interface VersioningOptions {
|
|
|
30
31
|
*/
|
|
31
32
|
shouldInclude?: (file: ProjectChangedFile) => MaybePromise<boolean>;
|
|
32
33
|
changelog?: ChangelogGeneratorParams;
|
|
34
|
+
/**
|
|
35
|
+
* hook that is called after the versions were bumped and pushed to registries,
|
|
36
|
+
* but before the release commit is created and pushed to git.
|
|
37
|
+
*
|
|
38
|
+
* can be used to add something to the release commit
|
|
39
|
+
*/
|
|
40
|
+
beforeReleaseCommit?: (workspace: WorkspacePackage[]) => MaybePromise<void>;
|
|
33
41
|
}
|
package/jsr/build-jsr.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { WorkspacePackage } from '../package-json/collect-package-jsons.js';
|
|
2
|
-
import { JsrConfig } from './config.js';
|
|
3
|
-
export declare function runJsrBuild(params: {
|
|
4
|
-
packageName: string;
|
|
5
|
-
workspacePackages: WorkspacePackage[];
|
|
6
|
-
rootConfig?: JsrConfig;
|
|
7
|
-
}): Promise<void>;
|
package/jsr/build-jsr.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import * as fsp from "node:fs/promises";
|
|
2
|
-
import { join, relative } from "node:path";
|
|
3
|
-
import { asyncPool, asNonNull } from "@fuman/utils";
|
|
4
|
-
import { glob } from "tinyglobby";
|
|
5
|
-
import ts from "typescript";
|
|
6
|
-
import { loadBuildConfig } from "../misc/_config.js";
|
|
7
|
-
import { exec } from "../misc/exec.js";
|
|
8
|
-
import { tryCopy } from "../misc/fs.js";
|
|
9
|
-
import { normalizeFilePath } from "../misc/path.js";
|
|
10
|
-
import { processPackageJson } from "../package-json/process-package-json.js";
|
|
11
|
-
import { findPackageByName, findRootPackage, collectVersions } from "../package-json/utils.js";
|
|
12
|
-
import { packageJsonToDeno } from "./deno-json.js";
|
|
13
|
-
function mergeArrays(a, b, defaultValue = []) {
|
|
14
|
-
if (!a) return b ?? defaultValue;
|
|
15
|
-
if (!b) return a;
|
|
16
|
-
return [...a, ...b];
|
|
17
|
-
}
|
|
18
|
-
async function runJsrBuild(params) {
|
|
19
|
-
const {
|
|
20
|
-
packageName,
|
|
21
|
-
workspacePackages,
|
|
22
|
-
rootConfig
|
|
23
|
-
} = params;
|
|
24
|
-
const ourPackage = findPackageByName(workspacePackages, packageName);
|
|
25
|
-
const rootPackage = findRootPackage(workspacePackages);
|
|
26
|
-
const packageRoot = ourPackage.path;
|
|
27
|
-
const workspaceRoot = rootPackage.path;
|
|
28
|
-
const outDir = join(packageRoot, normalizeFilePath(rootConfig?.outputDir ?? "dist"));
|
|
29
|
-
const packageConfig = await loadBuildConfig(packageRoot);
|
|
30
|
-
const srcDir = join(packageRoot, normalizeFilePath(packageConfig?.jsr?.sourceDir ?? rootConfig?.sourceDir ?? ""));
|
|
31
|
-
const excludeFiles = mergeArrays(rootConfig?.exclude, packageConfig?.jsr?.exclude);
|
|
32
|
-
await fsp.rm(outDir, { recursive: true, force: true });
|
|
33
|
-
await fsp.mkdir(outDir, { recursive: true });
|
|
34
|
-
await asyncPool(await fsp.readdir(srcDir), async (file) => {
|
|
35
|
-
const src = join(srcDir, file);
|
|
36
|
-
if (src === outDir) return;
|
|
37
|
-
await fsp.cp(src, join(outDir, file), { recursive: true });
|
|
38
|
-
});
|
|
39
|
-
const printer = ts.createPrinter();
|
|
40
|
-
const tsFiles = await glob("**/*.ts", {
|
|
41
|
-
cwd: outDir,
|
|
42
|
-
ignore: excludeFiles
|
|
43
|
-
});
|
|
44
|
-
const badImports = [];
|
|
45
|
-
await asyncPool(tsFiles, async (filename) => {
|
|
46
|
-
const fullFilePath = join(outDir, filename);
|
|
47
|
-
let fileContent = await fsp.readFile(fullFilePath, "utf8");
|
|
48
|
-
let changed = false;
|
|
49
|
-
const file = ts.createSourceFile(filename, fileContent, ts.ScriptTarget.ESNext, true);
|
|
50
|
-
let changedTs = false;
|
|
51
|
-
for (const imp of file.statements) {
|
|
52
|
-
if (!ts.isImportDeclaration(imp) && !ts.isExportDeclaration(imp)) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (!imp.moduleSpecifier || !ts.isStringLiteral(imp.moduleSpecifier)) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
const mod = imp.moduleSpecifier.text;
|
|
59
|
-
if (mod[0] !== ".") {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (mod.endsWith(".js")) {
|
|
63
|
-
changedTs = true;
|
|
64
|
-
imp.moduleSpecifier = ts.factory.createStringLiteral(
|
|
65
|
-
mod.replace(/\.js$/, ".ts")
|
|
66
|
-
);
|
|
67
|
-
} else {
|
|
68
|
-
badImports.push(` from ${mod} at ${join(srcDir, filename)}`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (rootConfig?.transformAst?.(file)) {
|
|
72
|
-
changedTs = true;
|
|
73
|
-
}
|
|
74
|
-
if (packageConfig?.jsr?.transformAst?.(file)) {
|
|
75
|
-
changedTs = true;
|
|
76
|
-
}
|
|
77
|
-
if (changedTs) {
|
|
78
|
-
fileContent = printer.printFile(file);
|
|
79
|
-
changed = true;
|
|
80
|
-
}
|
|
81
|
-
if (rootConfig?.transformCode || packageConfig?.jsr?.transformCode) {
|
|
82
|
-
const origFileContent = fileContent;
|
|
83
|
-
if (rootConfig?.transformCode) {
|
|
84
|
-
fileContent = rootConfig.transformCode(filename, fileContent);
|
|
85
|
-
}
|
|
86
|
-
if (packageConfig?.jsr?.transformCode) {
|
|
87
|
-
fileContent = packageConfig.jsr.transformCode(filename, fileContent);
|
|
88
|
-
}
|
|
89
|
-
if (fileContent !== origFileContent) {
|
|
90
|
-
changed = true;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (changed) {
|
|
94
|
-
await fsp.writeFile(fullFilePath, fileContent);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
if (badImports.length > 0) {
|
|
98
|
-
throw new Error(`Found ${badImports.length} invalid imports (you must specify .js extension):
|
|
99
|
-
${badImports.join("\n")}`);
|
|
100
|
-
}
|
|
101
|
-
const hookContext = {
|
|
102
|
-
outDir: "",
|
|
103
|
-
packageDir: ourPackage.path,
|
|
104
|
-
packageName: asNonNull(ourPackage.json.name),
|
|
105
|
-
packageJson: ourPackage.json,
|
|
106
|
-
jsr: true,
|
|
107
|
-
typedoc: false
|
|
108
|
-
};
|
|
109
|
-
packageConfig?.preparePackageJson?.(hookContext);
|
|
110
|
-
const workspaceVersions = collectVersions(workspacePackages);
|
|
111
|
-
const { packageJson, packageJsonOrig } = processPackageJson({
|
|
112
|
-
packageJson: ourPackage.json,
|
|
113
|
-
rootPackageJson: rootPackage.json,
|
|
114
|
-
workspaceVersions,
|
|
115
|
-
// since there's no bundling, we can't drop any deps.
|
|
116
|
-
// we *could* copy them from node_modules and add to the import map,
|
|
117
|
-
// but maybe sometime later
|
|
118
|
-
bundledWorkspaceDeps: [],
|
|
119
|
-
rootFieldsToCopy: ["license"]
|
|
120
|
-
});
|
|
121
|
-
const denoJson = packageJsonToDeno({
|
|
122
|
-
packageJson,
|
|
123
|
-
packageJsonOrig,
|
|
124
|
-
workspaceVersions,
|
|
125
|
-
buildDirName: relative(packageRoot, outDir),
|
|
126
|
-
baseDir: relative(packageRoot, srcDir),
|
|
127
|
-
exclude: excludeFiles
|
|
128
|
-
});
|
|
129
|
-
await fsp.writeFile(join(outDir, "deno.json"), JSON.stringify(denoJson, null, 4));
|
|
130
|
-
for (const file of mergeArrays(rootConfig?.copyRootFiles, packageConfig?.jsr?.copyRootFiles, ["LICENSE"])) {
|
|
131
|
-
await tryCopy(join(workspaceRoot, file), join(outDir, file), { recursive: true });
|
|
132
|
-
}
|
|
133
|
-
for (const file of mergeArrays(rootConfig?.copyPackageFiles, packageConfig?.jsr?.copyPackageFiles, ["README.md"])) {
|
|
134
|
-
await tryCopy(join(packageRoot, file), join(outDir, file), { recursive: true });
|
|
135
|
-
}
|
|
136
|
-
if (!packageConfig?.jsr?.dryRun && !rootConfig?.dryRun) {
|
|
137
|
-
await exec(["deno", "publish", "--dry-run", "-q", "--allow-dirty"], {
|
|
138
|
-
cwd: outDir,
|
|
139
|
-
stdio: "inherit",
|
|
140
|
-
throwOnError: true
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
export {
|
|
145
|
-
runJsrBuild
|
|
146
|
-
};
|