@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.
@@ -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;
@@ -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
- if (!satisfies(version, pkgDepVersions)) {
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(`https://uploads.github.com/repos/${params.repo}/releases/${release.id}/assets`, {
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
- await fsp.cp(srcDir, packageOutRoot, { recursive: true });
66
- const printer = ts.createPrinter();
67
- const tsFiles = await glob("**/*.ts", {
68
- cwd: packageOutRoot,
69
- ignore: excludeFiles
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
@@ -1,4 +1,3 @@
1
- export * from './build-jsr.js';
2
1
  export * from './config.js';
3
2
  export * from './deno-json.js';
4
3
  export * from './populate.js';
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, parsePackageJsonFile } from "./parse.js";
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 packageJson = await parsePackageJsonFile(path.join(workspaceRoot, dir, "package.json"));
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
- json: packageJson
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
  }
@@ -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<PackageJson>;
11
+ export declare function parseWorkspaceRootPackageJson(workspaceRoot: string | URL): Promise<{
12
+ path: string;
13
+ json: PackageJson;
14
+ }>;
@@ -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
- function parsePackageJson(packageJson) {
7
- return PackageJsonSchema.parse(JSON.parse(packageJson));
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
- return parsePackageJson(await fsp.readFile(normalizeFilePath(packageJsonPath), "utf8"));
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 packageJsonPath = path__default.join(workspaceRoot, "package.json");
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
  };
@@ -34,7 +34,7 @@ const PackageJsonSchema = z.object({
34
34
  engines: z.record(z.string()),
35
35
  pnpm: z.object({
36
36
  overrides: z.record(z.string())
37
- }),
37
+ }).partial(),
38
38
  fuman: z.object({
39
39
  jsr: z.union([
40
40
  z.literal("skip"),
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "@fuman/build",
3
3
  "type": "module",
4
- "version": "0.0.7",
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.7",
11
- "@fuman/io": "^0.0.4",
12
- "@fuman/node": "^0.0.4",
13
- "@fuman/utils": "^0.0.4",
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");
@@ -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
  }
@@ -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
- };