@fuman/build 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +108 -0
  3. package/ci/github-actions.d.ts +3 -0
  4. package/ci/github-actions.js +28 -0
  5. package/ci/index.d.ts +1 -0
  6. package/cli/commands/_utils.d.ts +8 -0
  7. package/cli/commands/_utils.js +18 -0
  8. package/cli/commands/build.d.ts +20 -0
  9. package/cli/commands/build.js +45 -0
  10. package/cli/commands/bump-version.d.ts +18 -0
  11. package/cli/commands/bump-version.js +72 -0
  12. package/cli/commands/cr.d.ts +17 -0
  13. package/cli/commands/cr.js +76 -0
  14. package/cli/commands/find-changed-packages.d.ts +12 -0
  15. package/cli/commands/find-changed-packages.js +44 -0
  16. package/cli/commands/gen-changelog.d.ts +12 -0
  17. package/cli/commands/gen-changelog.js +49 -0
  18. package/cli/commands/gen-deps-graph.d.ts +15 -0
  19. package/cli/commands/gen-deps-graph.js +78 -0
  20. package/cli/commands/jsr.d.ts +6 -0
  21. package/cli/commands/jsr.js +79 -0
  22. package/cli/commands/publish.d.ts +48 -0
  23. package/cli/commands/publish.js +197 -0
  24. package/cli/commands/release.d.ts +34 -0
  25. package/cli/commands/release.js +226 -0
  26. package/cli/commands/validate-workspace-deps.d.ts +38 -0
  27. package/cli/commands/validate-workspace-deps.js +68 -0
  28. package/cli/index.d.ts +3 -0
  29. package/cli/main.d.ts +2 -0
  30. package/config.d.ts +32 -0
  31. package/fuman-build.d.ts +1 -0
  32. package/fuman-build.js +33 -0
  33. package/git/github.d.ts +14 -0
  34. package/git/github.js +48 -0
  35. package/git/index.d.ts +1 -0
  36. package/git/utils.d.ts +38 -0
  37. package/git/utils.js +110 -0
  38. package/index.d.ts +7 -0
  39. package/index.js +46 -0
  40. package/jsr/build-jsr.d.ts +7 -0
  41. package/jsr/build-jsr.js +145 -0
  42. package/jsr/config.d.ts +48 -0
  43. package/jsr/create-packages.d.ts +8 -0
  44. package/jsr/create-packages.js +46 -0
  45. package/jsr/deno-json.d.ts +19 -0
  46. package/jsr/deno-json.js +80 -0
  47. package/jsr/generate-workspace.d.ts +9 -0
  48. package/jsr/generate-workspace.js +174 -0
  49. package/jsr/index.d.ts +5 -0
  50. package/jsr/populate.d.ts +45 -0
  51. package/jsr/populate.js +132 -0
  52. package/jsr/utils/external-libs.d.ts +8 -0
  53. package/jsr/utils/external-libs.js +46 -0
  54. package/jsr/utils/index.d.ts +4 -0
  55. package/jsr/utils/jsr-api.d.ts +23 -0
  56. package/jsr/utils/jsr-api.js +115 -0
  57. package/jsr/utils/jsr-json.d.ts +10 -0
  58. package/jsr/utils/jsr-json.js +80 -0
  59. package/jsr/utils/jsr.d.ts +11 -0
  60. package/jsr/utils/jsr.js +74 -0
  61. package/jsr.d.ts +1 -0
  62. package/jsr.js +23 -0
  63. package/misc/_config.d.ts +1 -0
  64. package/misc/_config.js +18 -0
  65. package/misc/exec.d.ts +9 -0
  66. package/misc/exec.js +40 -0
  67. package/misc/fs.d.ts +4 -0
  68. package/misc/fs.js +32 -0
  69. package/misc/index.d.ts +4 -0
  70. package/misc/path.d.ts +1 -0
  71. package/misc/path.js +12 -0
  72. package/misc/publish-order.d.ts +3 -0
  73. package/misc/publish-order.js +56 -0
  74. package/misc/tsconfig.d.ts +2 -0
  75. package/misc/tsconfig.js +28 -0
  76. package/npm/index.d.ts +1 -0
  77. package/npm/npm-api.d.ts +7 -0
  78. package/npm/npm-api.js +18 -0
  79. package/package-json/collect-package-jsons.d.ts +8 -0
  80. package/package-json/collect-package-jsons.js +61 -0
  81. package/package-json/find-package-json.d.ts +7 -0
  82. package/package-json/find-package-json.js +28 -0
  83. package/package-json/index.d.ts +5 -0
  84. package/package-json/parse.d.ts +4 -0
  85. package/package-json/parse.js +40 -0
  86. package/package-json/process-package-json.d.ts +13 -0
  87. package/package-json/process-package-json.js +132 -0
  88. package/package-json/types.d.ts +56 -0
  89. package/package-json/types.js +57 -0
  90. package/package-json/utils.d.ts +4 -0
  91. package/package-json/utils.js +27 -0
  92. package/package.json +67 -0
  93. package/versioning/bump-version.d.ts +48 -0
  94. package/versioning/bump-version.js +129 -0
  95. package/versioning/collect-files.d.ts +22 -0
  96. package/versioning/collect-files.js +66 -0
  97. package/versioning/generate-changelog.d.ts +13 -0
  98. package/versioning/generate-changelog.js +81 -0
  99. package/versioning/types.d.ts +32 -0
  100. package/vite/build-plugin.d.ts +73 -0
  101. package/vite/build-plugin.js +170 -0
  102. package/vite/config.d.ts +34 -0
  103. package/vite/index.d.ts +2 -0
  104. package/vite.d.ts +1 -0
  105. package/vite.js +4 -0
@@ -0,0 +1,68 @@
1
+ import process from "node:process";
2
+ import { satisfies } from "semver";
3
+ import { collectPackageJsons } from "../../package-json/collect-package-jsons.js";
4
+ import * as bc from "@drizzle-team/brocli";
5
+ async function validateWorkspaceDeps(workspaceRoot, params) {
6
+ const { includeRoot = true, skipWorkspaceDeps = true } = params || {};
7
+ const pjs = await collectPackageJsons(workspaceRoot, includeRoot);
8
+ const workspacePackages = new Set(skipWorkspaceDeps ? pjs.map((pj) => pj.json.name) : []);
9
+ const versions = {};
10
+ const errors = [];
11
+ for (const { json: pj } of pjs) {
12
+ if (pj.name === void 0) {
13
+ throw new Error("package.json without name is not supported");
14
+ }
15
+ for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
16
+ const deps = pj[field];
17
+ if (!deps) continue;
18
+ for (const [name, version] of Object.entries(deps)) {
19
+ if (workspacePackages.has(name)) continue;
20
+ if (version.startsWith("workspace:")) continue;
21
+ if (versions[name] === void 0) {
22
+ versions[name] = {};
23
+ }
24
+ for (const [pkgName, pkgDepVersions] of Object.entries(versions[name])) {
25
+ if (!satisfies(version, pkgDepVersions)) {
26
+ errors.push({
27
+ package: pj.name,
28
+ dependency: name,
29
+ version,
30
+ at: field,
31
+ otherPackage: pkgName,
32
+ otherVersion: pkgDepVersions
33
+ });
34
+ }
35
+ }
36
+ versions[name][pj.name] = version;
37
+ }
38
+ }
39
+ }
40
+ return errors;
41
+ }
42
+ const validateWorkspaceDepsCli = bc.command({
43
+ name: "validate-workspace-deps",
44
+ desc: "validate the external dependencies of a workspace",
45
+ options: {
46
+ includeRoot: bc.boolean("include-root").desc("whether to also validate the root package.json").default(false),
47
+ noSkipWorkspaceDeps: bc.boolean("no-skip-workspace-deps").desc("whether to not skip validating dependencies of other workspace packages"),
48
+ root: bc.string().desc("path to the root of the workspace (default: cwd)")
49
+ },
50
+ handler: async (args) => {
51
+ const errors = await validateWorkspaceDeps(args.root ?? process.cwd(), {
52
+ includeRoot: args.includeRoot,
53
+ skipWorkspaceDeps: !args.noSkipWorkspaceDeps
54
+ });
55
+ if (errors.length > 0) {
56
+ console.error("⚠️ Found external dependencies mismatch:");
57
+ for (const error of errors) {
58
+ console.error(` - at ${error.package}: ${error.at} has ${error.dependency}@${error.version}, but ${error.otherPackage} has @${error.otherVersion}`);
59
+ }
60
+ } else {
61
+ console.log("✅ All external dependencies match!");
62
+ }
63
+ }
64
+ });
65
+ export {
66
+ validateWorkspaceDeps,
67
+ validateWorkspaceDepsCli
68
+ };
package/cli/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { buildPackage } from './commands/build.js';
2
+ export { generateDepsGraph } from './commands/gen-deps-graph.js';
3
+ export { validateWorkspaceDeps } from './commands/validate-workspace-deps.js';
package/cli/main.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/config.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { JsrConfig } from './jsr/config.js';
2
+ import { PackageJson } from './package-json/types.js';
3
+ import { VersioningOptions } from './versioning/types.js';
4
+ export interface BuildHookContext {
5
+ /** full path to the output directory */
6
+ outDir: string;
7
+ /** full path to the package directory */
8
+ packageDir: string;
9
+ /** name of the package being build */
10
+ packageName: string;
11
+ /**
12
+ * package.json of the package being build
13
+ * should not be modified unless allowed in the hook docs
14
+ */
15
+ packageJson: PackageJson;
16
+ /** whether this is a jsr build */
17
+ jsr: boolean;
18
+ }
19
+ export interface RootConfigObject {
20
+ /**
21
+ * path to vite config to use when building using fuman-build cli,
22
+ * relative to the workspace root
23
+ *
24
+ * @default "vite.config.js"
25
+ */
26
+ viteConfig?: string;
27
+ /** jsr-specific configuration */
28
+ jsr?: JsrConfig;
29
+ /** configuration for the changelog generator */
30
+ versioning?: VersioningOptions;
31
+ }
32
+ export type RootConfig = RootConfigObject | (() => RootConfigObject);
@@ -0,0 +1 @@
1
+ export * from "./cli/main.js"
package/fuman-build.js ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ import process from "node:process";
3
+ import * as bc from "@drizzle-team/brocli";
4
+ import { buildPackageCli } from "./cli/commands/build.js";
5
+ import { bumpVersionCli } from "./cli/commands/bump-version.js";
6
+ import { runContinuousReleaseCli } from "./cli/commands/cr.js";
7
+ import { findChangedPackagesCli } from "./cli/commands/find-changed-packages.js";
8
+ import { generateChangelogCli } from "./cli/commands/gen-changelog.js";
9
+ import { generateDepsGraphCli } from "./cli/commands/gen-deps-graph.js";
10
+ import { jsrCli } from "./cli/commands/jsr.js";
11
+ import { publishPackagesCli } from "./cli/commands/publish.js";
12
+ import { releaseCli } from "./cli/commands/release.js";
13
+ import { validateWorkspaceDepsCli } from "./cli/commands/validate-workspace-deps.js";
14
+ await bc.run([
15
+ validateWorkspaceDepsCli,
16
+ generateDepsGraphCli,
17
+ buildPackageCli,
18
+ jsrCli,
19
+ generateChangelogCli,
20
+ findChangedPackagesCli,
21
+ bumpVersionCli,
22
+ publishPackagesCli,
23
+ releaseCli,
24
+ runContinuousReleaseCli
25
+ ], {
26
+ theme: (event) => {
27
+ if (event.type === "error" && event.violation === "unknown_error") {
28
+ console.error(event.error);
29
+ process.exit(1);
30
+ }
31
+ return false;
32
+ }
33
+ });
@@ -0,0 +1,14 @@
1
+ export declare function createGithubRelease(params: {
2
+ token: string;
3
+ repo: string;
4
+ tag: string;
5
+ name: string;
6
+ body: string;
7
+ draft?: boolean;
8
+ prerelease?: boolean;
9
+ artifacts?: {
10
+ name: string;
11
+ type: string;
12
+ body: BodyInit;
13
+ }[];
14
+ }): Promise<number>;
package/git/github.js ADDED
@@ -0,0 +1,48 @@
1
+ import { ffetchBase, ffetchAddons } from "@fuman/fetch";
2
+ import { ffetchZodAdapter } from "@fuman/fetch/zod";
3
+ import { asyncPool } from "@fuman/utils";
4
+ import { z } from "zod";
5
+ async function createGithubRelease(params) {
6
+ const ffetch = ffetchBase.extend({
7
+ baseUrl: "https://api.github.com",
8
+ addons: [
9
+ ffetchAddons.retry(),
10
+ ffetchAddons.parser(ffetchZodAdapter())
11
+ ],
12
+ headers: {
13
+ "Accept": "application/vnd.github+json",
14
+ "User-Agent": "@fuman/build",
15
+ "X-GitHub-Api-Version": "2022-11-28",
16
+ "Authorization": `Bearer ${params.token}`
17
+ }
18
+ });
19
+ const release = await ffetch.post(`/repos/${params.repo}/releases`, {
20
+ json: {
21
+ tag_name: params.tag,
22
+ name: params.name,
23
+ body: params.body,
24
+ draft: params.draft,
25
+ prerelease: params.prerelease
26
+ },
27
+ validateResponse: (res) => res.status === 201
28
+ }).parsedJson(z.object({
29
+ id: z.number()
30
+ }));
31
+ if (params.artifacts != null && params.artifacts.length > 0) {
32
+ await asyncPool(params.artifacts, async (file) => {
33
+ await ffetch(`/repos/${params.repo}/releases/${release.id}/assets`, {
34
+ method: "POST",
35
+ query: { name: file.name },
36
+ headers: {
37
+ "Content-Type": file.type
38
+ },
39
+ body: file.body,
40
+ validateResponse: (res) => res.status === 201
41
+ });
42
+ });
43
+ }
44
+ return release.id;
45
+ }
46
+ export {
47
+ createGithubRelease
48
+ };
package/git/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './utils.js';
package/git/utils.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ export declare function getLatestTag(cwd?: string | URL): Promise<string | null>;
2
+ export declare function getFirstCommit(cwd?: string | URL): Promise<string>;
3
+ export declare function getCurrentCommit(cwd?: string | URL): Promise<string>;
4
+ export declare function getCurrentBranch(cwd?: string | URL): Promise<string>;
5
+ export declare function findChangedFiles(params: {
6
+ since: string;
7
+ /** @default 'HEAD' */
8
+ until?: string;
9
+ cwd?: string | URL;
10
+ }): Promise<string[]>;
11
+ export interface CommitInfo {
12
+ hash: string;
13
+ author: {
14
+ name: string;
15
+ email: string;
16
+ date: Date;
17
+ };
18
+ committer: {
19
+ name: string;
20
+ email: string;
21
+ date: Date;
22
+ };
23
+ message: string;
24
+ description: string;
25
+ }
26
+ export declare function getCommitsBetween(params: {
27
+ since: string;
28
+ /** @default 'HEAD' */
29
+ until?: string;
30
+ cwd?: string | URL;
31
+ }): Promise<CommitInfo[]>;
32
+ export interface ConventionalCommit {
33
+ type: string;
34
+ scope?: string;
35
+ breaking: boolean;
36
+ subject: string;
37
+ }
38
+ export declare function parseConventionalCommit(msg: string): ConventionalCommit | null;
package/git/utils.js ADDED
@@ -0,0 +1,110 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { exec } from "../misc/exec.js";
3
+ async function getLatestTag(cwd) {
4
+ const res = await exec(["git", "describe", "--abbrev=0", "--tags"], { cwd });
5
+ if (res.exitCode !== 0) {
6
+ if (res.stderr.match(/^fatal: (?:No names found|No tags can describe)/i)) {
7
+ return null;
8
+ }
9
+ throw new Error(`git describe failed: ${res.stderr}`);
10
+ }
11
+ return res.stdout.trim();
12
+ }
13
+ async function getFirstCommit(cwd) {
14
+ return (await exec(["git", "rev-list", "--max-parents=0", "HEAD"], {
15
+ cwd,
16
+ throwOnError: true
17
+ })).stdout.trim();
18
+ }
19
+ async function getCurrentCommit(cwd) {
20
+ const res = await exec(["git", "rev-parse", "HEAD"], {
21
+ cwd,
22
+ throwOnError: true
23
+ });
24
+ return res.stdout.trim();
25
+ }
26
+ async function getCurrentBranch(cwd) {
27
+ const res = await exec(["git", "rev-parse", "--abbrev-ref", "HEAD"], {
28
+ cwd,
29
+ throwOnError: true
30
+ });
31
+ return res.stdout.trim();
32
+ }
33
+ async function findChangedFiles(params) {
34
+ const { since, until = "HEAD", cwd } = params;
35
+ const res = await exec(["git", "diff", "--name-only", since, until], {
36
+ cwd,
37
+ throwOnError: true
38
+ });
39
+ const files = res.stdout.trim().split("\n");
40
+ if (files.length === 1 && files[0] === "") {
41
+ return [];
42
+ }
43
+ return files;
44
+ }
45
+ async function getCommitsBetween(params) {
46
+ const { since, until = "HEAD", cwd } = params;
47
+ const delim = `---${randomUUID()}---`;
48
+ const res = await exec(["git", "log", `--pretty=format:%H %s%n%an%n%ae%n%aI%n%cn%n%ce%n%cI%n%b%n${delim}`, `${since}..${until}`], {
49
+ cwd,
50
+ throwOnError: true
51
+ });
52
+ const lines = res.stdout.trim().split("\n");
53
+ const items = [];
54
+ let current = null;
55
+ for (let i = 0; i < lines.length; i++) {
56
+ const line = lines[i];
57
+ if (line === delim) {
58
+ if (current) items.push(current);
59
+ current = null;
60
+ } else if (current) {
61
+ if (current.description) current.description += "\n";
62
+ current.description += line;
63
+ } else {
64
+ const [hash, ...msg] = line.split(" ");
65
+ const authorName = lines[++i];
66
+ const authorEmail = lines[++i];
67
+ const authorDate = lines[++i];
68
+ const committerName = lines[++i];
69
+ const committerEmail = lines[++i];
70
+ const committerDate = lines[++i];
71
+ current = {
72
+ hash,
73
+ author: {
74
+ name: authorName,
75
+ email: authorEmail,
76
+ date: new Date(authorDate)
77
+ },
78
+ committer: {
79
+ name: committerName,
80
+ email: committerEmail,
81
+ date: new Date(committerDate)
82
+ },
83
+ message: msg.join(" "),
84
+ description: ""
85
+ };
86
+ }
87
+ }
88
+ if (current) items.push(current);
89
+ return items.reverse();
90
+ }
91
+ function parseConventionalCommit(msg) {
92
+ const match = msg.match(/^(\w+)(?:\(([^)]+)\))?(!?): (.+)$/);
93
+ if (!match) return null;
94
+ const [, type, scope, breaking, subject] = match;
95
+ return {
96
+ type,
97
+ scope,
98
+ breaking: Boolean(breaking),
99
+ subject
100
+ };
101
+ }
102
+ export {
103
+ findChangedFiles,
104
+ getCommitsBetween,
105
+ getCurrentBranch,
106
+ getCurrentCommit,
107
+ getFirstCommit,
108
+ getLatestTag,
109
+ parseConventionalCommit
110
+ };
package/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './ci/index.js';
2
+ export * from './cli/index.js';
3
+ export * from './config.js';
4
+ export * from './git/index.js';
5
+ export * from './misc/index.js';
6
+ export * from './npm/index.js';
7
+ export * from './package-json/index.js';
package/index.js ADDED
@@ -0,0 +1,46 @@
1
+ import { getGithubActionsInput, isRunningInGithubActions, writeGithubActionsOutput } from "./ci/github-actions.js";
2
+ import { buildPackage } from "./cli/commands/build.js";
3
+ import { generateDepsGraph } from "./cli/commands/gen-deps-graph.js";
4
+ import { validateWorkspaceDeps } from "./cli/commands/validate-workspace-deps.js";
5
+ import { findChangedFiles, getCommitsBetween, getCurrentBranch, getCurrentCommit, getFirstCommit, getLatestTag, parseConventionalCommit } from "./git/utils.js";
6
+ import { exec } from "./misc/exec.js";
7
+ import { normalizeFilePath } from "./misc/path.js";
8
+ import { determinePublishOrder, sortWorkspaceByPublishOrder } from "./misc/publish-order.js";
9
+ import { getTsconfigFiles, getTsconfigFor } from "./misc/tsconfig.js";
10
+ import { NPM_PACKAGE_NAME_REGEX, npmCheckVersion } from "./npm/npm-api.js";
11
+ import { collectPackageJsons, filterPackageJsonsForPublish } from "./package-json/collect-package-jsons.js";
12
+ import { findPackageJson } from "./package-json/find-package-json.js";
13
+ import { parsePackageJson, parsePackageJsonFile, parseWorkspaceRootPackageJson } from "./package-json/parse.js";
14
+ import { processPackageJson } from "./package-json/process-package-json.js";
15
+ import { PackageJsonSchema } from "./package-json/types.js";
16
+ export {
17
+ NPM_PACKAGE_NAME_REGEX,
18
+ PackageJsonSchema,
19
+ buildPackage,
20
+ collectPackageJsons,
21
+ determinePublishOrder,
22
+ exec,
23
+ filterPackageJsonsForPublish,
24
+ findChangedFiles,
25
+ findPackageJson,
26
+ generateDepsGraph,
27
+ getCommitsBetween,
28
+ getCurrentBranch,
29
+ getCurrentCommit,
30
+ getFirstCommit,
31
+ getGithubActionsInput,
32
+ getLatestTag,
33
+ getTsconfigFiles,
34
+ getTsconfigFor,
35
+ isRunningInGithubActions,
36
+ normalizeFilePath,
37
+ npmCheckVersion,
38
+ parseConventionalCommit,
39
+ parsePackageJson,
40
+ parsePackageJsonFile,
41
+ parseWorkspaceRootPackageJson,
42
+ processPackageJson,
43
+ sortWorkspaceByPublishOrder,
44
+ validateWorkspaceDeps,
45
+ writeGithubActionsOutput
46
+ };
@@ -0,0 +1,7 @@
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>;
@@ -0,0 +1,145 @@
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
+ };
108
+ packageConfig?.preparePackageJson?.(hookContext);
109
+ const workspaceVersions = collectVersions(workspacePackages);
110
+ const { packageJson, packageJsonOrig } = processPackageJson({
111
+ packageJson: ourPackage.json,
112
+ rootPackageJson: rootPackage.json,
113
+ workspaceVersions,
114
+ // since there's no bundling, we can't drop any deps.
115
+ // we *could* copy them from node_modules and add to the import map,
116
+ // but maybe sometime later
117
+ bundledWorkspaceDeps: [],
118
+ rootFieldsToCopy: ["license"]
119
+ });
120
+ const denoJson = packageJsonToDeno({
121
+ packageJson,
122
+ packageJsonOrig,
123
+ workspaceVersions,
124
+ buildDirName: relative(packageRoot, outDir),
125
+ baseDir: relative(packageRoot, srcDir),
126
+ exclude: excludeFiles
127
+ });
128
+ await fsp.writeFile(join(outDir, "deno.json"), JSON.stringify(denoJson, null, 4));
129
+ for (const file of mergeArrays(rootConfig?.copyRootFiles, packageConfig?.jsr?.copyRootFiles, ["LICENSE"])) {
130
+ await tryCopy(join(workspaceRoot, file), join(outDir, file), { recursive: true });
131
+ }
132
+ for (const file of mergeArrays(rootConfig?.copyPackageFiles, packageConfig?.jsr?.copyPackageFiles, ["README.md"])) {
133
+ await tryCopy(join(packageRoot, file), join(outDir, file), { recursive: true });
134
+ }
135
+ if (!packageConfig?.jsr?.dryRun && !rootConfig?.dryRun) {
136
+ await exec(["deno", "publish", "--dry-run", "-q", "--allow-dirty"], {
137
+ cwd: outDir,
138
+ stdio: "inherit",
139
+ throwOnError: true
140
+ });
141
+ }
142
+ }
143
+ export {
144
+ runJsrBuild
145
+ };
@@ -0,0 +1,48 @@
1
+ import { MaybePromise } from '@fuman/utils';
2
+ import { default as ts } from 'typescript';
3
+ import { BuildHookContext } from '../config.js';
4
+ import { WorkspacePackage } from '../package-json/collect-package-jsons.js';
5
+ export interface JsrConfig {
6
+ /**
7
+ * jsr build output dir (relative to workspace root)
8
+ *
9
+ * @default "dist"
10
+ */
11
+ outputDir?: string;
12
+ /**
13
+ * files to copy from the workspace root directory to the package build directory
14
+ *
15
+ * @default ['LICENSE']
16
+ */
17
+ copyRootFiles?: string[];
18
+ /**
19
+ * files to copy from the package root directory to the package build directory
20
+ *
21
+ * @default ['README.md']
22
+ */
23
+ copyPackageFiles?: string[];
24
+ /**
25
+ * source dir (relative to package root)
26
+ * anything outside this dir will be copied as-is
27
+ * @default "" (i.e. the package root itself)
28
+ */
29
+ sourceDir?: string;
30
+ /** globs to exclude from publishing */
31
+ exclude?: string[];
32
+ /** custom predicate for including packages */
33
+ includePackage?: (pkg: WorkspacePackage) => boolean;
34
+ /**
35
+ * whether to run `deno publish --dry-run` after generating the workspace
36
+ * to make sure everything is ok
37
+ * @default true
38
+ */
39
+ dryRun?: boolean;
40
+ /** hook to run after the deno.json file is generated but before it is written to disk */
41
+ finalizeDenoJson?: (ctx: BuildHookContext, jsr: any) => void;
42
+ /** hook to run *after* the build is done, time to do any final modifications to the package contents */
43
+ finalize?: (ctx: BuildHookContext) => MaybePromise<void>;
44
+ /** hook that will be run on each file as it is being processed, allowing you to change ast as you wish */
45
+ transformAst?: (ast: ts.SourceFile) => boolean;
46
+ /** similar to transformAst, but for the code itself (runs after transformAst) */
47
+ transformCode?: (path: string, code: string) => string;
48
+ }
@@ -0,0 +1,8 @@
1
+ import { WorkspacePackage } from '../package-json/collect-package-jsons.js';
2
+ export declare function jsrCreatePackages(params: {
3
+ workspaceRoot: string;
4
+ workspacePackages?: WorkspacePackage[];
5
+ registry?: string;
6
+ token?: string;
7
+ githubRepo?: string;
8
+ }): Promise<boolean>;
@@ -0,0 +1,46 @@
1
+ import process from "node:process";
2
+ import { filterPackageJsonsForPublish, collectPackageJsons } from "../package-json/collect-package-jsons.js";
3
+ import { jsrMaybeCreatePackage, jsrSetGithubRepo } from "./utils/jsr-api.js";
4
+ import { jsrCheckVersion } from "./utils/jsr.js";
5
+ async function jsrCreatePackages(params) {
6
+ const {
7
+ workspaceRoot,
8
+ registry = process.env.JSR_URL ?? "https://jsr.io",
9
+ token,
10
+ githubRepo
11
+ } = params;
12
+ const workspace = filterPackageJsonsForPublish(params.workspacePackages ?? await collectPackageJsons(workspaceRoot, false), "jsr");
13
+ let hasFailed = false;
14
+ for (const pkg of workspace) {
15
+ if (pkg.json.name == null) continue;
16
+ if (await jsrCheckVersion({ registry, package: pkg.json.name })) {
17
+ continue;
18
+ }
19
+ const [scope_, packageName] = pkg.json.name.split("/");
20
+ const scope = scope_.slice(1);
21
+ if (token == null) {
22
+ console.log(`ℹ️ to create \x1B[;33m${pkg.json.name}\x1B[;0m follow this link: \x1B[;34m${new URL(`/create?scope=${scope}&package=${packageName}`, registry).href}\x1B[;0m`);
23
+ hasFailed = true;
24
+ continue;
25
+ }
26
+ await jsrMaybeCreatePackage({
27
+ name: pkg.json.name,
28
+ registry,
29
+ token
30
+ });
31
+ if (githubRepo != null) {
32
+ const [owner, repo] = githubRepo.split("/");
33
+ await jsrSetGithubRepo({
34
+ registry,
35
+ name: pkg.json.name,
36
+ token,
37
+ owner,
38
+ repo
39
+ });
40
+ }
41
+ }
42
+ return hasFailed;
43
+ }
44
+ export {
45
+ jsrCreatePackages
46
+ };