@fuman/build 0.0.13 → 0.0.15
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/README.md +15 -15
- package/cli/commands/_utils.d.ts +0 -1
- package/cli/commands/_utils.js +2 -3
- package/cli/commands/bump-version.d.ts +2 -4
- package/cli/commands/bump-version.js +2 -6
- package/cli/commands/find-changed-packages.d.ts +0 -2
- package/cli/commands/find-changed-packages.js +0 -2
- package/cli/commands/gen-changelog.d.ts +0 -2
- package/cli/commands/gen-changelog.js +0 -2
- package/cli/commands/lint/config.d.ts +30 -0
- package/cli/commands/lint/index.d.ts +8 -0
- package/cli/commands/lint/index.js +47 -0
- package/cli/commands/lint/validate-workspace-deps.d.ts +41 -0
- package/cli/commands/lint/validate-workspace-deps.js +94 -0
- package/cli/commands/release.d.ts +2 -2
- package/cli/commands/release.js +29 -18
- package/cli/index.d.ts +1 -1
- package/config.d.ts +3 -0
- package/fuman-build.js +2 -2
- package/git/utils.d.ts +3 -1
- package/git/utils.js +10 -2
- package/index.js +3 -2
- package/jsr/_deno-directives.d.ts +1 -0
- package/jsr/_deno-directives.js +23 -0
- package/jsr/config.d.ts +44 -0
- package/jsr/generate-workspace.js +4 -0
- package/misc/_config.d.ts +1 -1
- package/misc/_config.js +4 -3
- package/package-json/process-package-json.d.ts +6 -0
- package/package-json/process-package-json.js +14 -1
- package/package-json/types.d.ts +10 -1
- package/package-json/types.js +1 -0
- package/package.json +5 -17
- package/versioning/bump-version.d.ts +3 -1
- package/versioning/bump-version.js +28 -5
- package/versioning/collect-files.d.ts +1 -1
- package/versioning/types.d.ts +15 -0
- package/vite/build-plugin.d.ts +1 -1
- package/vite/build-plugin.js +4 -1
- package/cli/commands/validate-workspace-deps.d.ts +0 -50
- package/cli/commands/validate-workspace-deps.js +0 -87
package/README.md
CHANGED
|
@@ -16,12 +16,12 @@ this package works on some assumptions, allowing to abstract some of the complex
|
|
|
16
16
|
- `.exports` and `.bin` fields in package.json are build entrypoints, e.g.:
|
|
17
17
|
```json
|
|
18
18
|
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./src/index.ts"
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"fuman-build": "./src/cli.ts"
|
|
24
|
+
}
|
|
25
25
|
}
|
|
26
26
|
- `.main`, `.module`, `.types`, `.browser` are explicitly **not** supported. for browser-specific code, you should make a secondary entrypoint/package instead of relying on bundle-time magic
|
|
27
27
|
- package versioning is continous and semver-compliant (except "fixed version" releases, which all share the same version)
|
|
@@ -71,15 +71,15 @@ just put this in your `vite.config.js`:
|
|
|
71
71
|
import { fumanBuild } from '@fuman/build/vite'
|
|
72
72
|
|
|
73
73
|
export default {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
plugins: [
|
|
75
|
+
fumanBuild({
|
|
76
|
+
root: __dirname,
|
|
77
|
+
// if you're using `vite-plugin-dts`, make sure to add this option,
|
|
78
|
+
// otherwise additional entrypoints will not have proper types
|
|
79
|
+
// (and DO NOT add this option to the dts plugin itself)
|
|
80
|
+
insertTypesEntry: true,
|
|
81
|
+
}),
|
|
82
|
+
],
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
package/cli/commands/_utils.d.ts
CHANGED
package/cli/commands/_utils.js
CHANGED
|
@@ -3,12 +3,11 @@ import { loadBuildConfig } from "../../misc/_config.js";
|
|
|
3
3
|
async function loadConfig(params) {
|
|
4
4
|
const {
|
|
5
5
|
workspaceRoot,
|
|
6
|
-
configPath = "build.config.js",
|
|
7
6
|
require: require2 = true
|
|
8
7
|
} = params;
|
|
9
|
-
const config = await loadBuildConfig(workspaceRoot
|
|
8
|
+
const config = await loadBuildConfig(workspaceRoot);
|
|
10
9
|
if (!config && require2) {
|
|
11
|
-
throw new Error(`Config not found
|
|
10
|
+
throw new Error(`Config not found at ${workspaceRoot}`);
|
|
12
11
|
}
|
|
13
12
|
return config ?? null;
|
|
14
13
|
}
|
|
@@ -3,15 +3,13 @@ import { bc } from './_utils.js';
|
|
|
3
3
|
export declare function formatBumpVersionResult(result: BumpVersionResult, withReleaseType: boolean): string;
|
|
4
4
|
export declare const bumpVersionCli: bc.Command<{
|
|
5
5
|
root: string | undefined;
|
|
6
|
-
|
|
7
|
-
type: "auto" | "major" | "minor" | "patch";
|
|
6
|
+
type: "major" | "minor" | "patch" | "auto";
|
|
8
7
|
since: string | undefined;
|
|
9
8
|
dryRun: boolean | undefined;
|
|
10
9
|
quiet: boolean | undefined;
|
|
11
10
|
}, {
|
|
12
11
|
root: string | undefined;
|
|
13
|
-
|
|
14
|
-
type: "auto" | "major" | "minor" | "patch";
|
|
12
|
+
type: "major" | "minor" | "patch" | "auto";
|
|
15
13
|
since: string | undefined;
|
|
16
14
|
dryRun: boolean | undefined;
|
|
17
15
|
quiet: boolean | undefined;
|
|
@@ -13,8 +13,6 @@ function formatBumpVersionResult(result, withReleaseType) {
|
|
|
13
13
|
lines.push(` has new features: ${result.hasFeatures}`);
|
|
14
14
|
lines.push("");
|
|
15
15
|
}
|
|
16
|
-
lines.push(`next version: ${result.nextVersion}`);
|
|
17
|
-
lines.push("");
|
|
18
16
|
lines.push("list of changed packages:");
|
|
19
17
|
for (const { package: pkg, because, prevVersion } of result.changedPackages) {
|
|
20
18
|
let versionStr = prevVersion;
|
|
@@ -30,7 +28,6 @@ const bumpVersionCli = bc.command({
|
|
|
30
28
|
desc: "bump the version of changed packages",
|
|
31
29
|
options: {
|
|
32
30
|
root: bc.string().desc("path to the root of the workspace (default: process.cwd())"),
|
|
33
|
-
config: bc.string().desc("path to the build.config.js file"),
|
|
34
31
|
type: bc.string().desc("override type of release (major, minor, patch) (default: auto-detect)").enum("major", "minor", "patch", "auto").default("auto"),
|
|
35
32
|
since: bc.string().desc("starting point for the changelog (default: latest tag)"),
|
|
36
33
|
dryRun: bc.boolean("dry-run").desc("whether to only print the detected changes without actually modifying anything"),
|
|
@@ -42,7 +39,6 @@ const bumpVersionCli = bc.command({
|
|
|
42
39
|
const workspace = await collectPackageJsons(root);
|
|
43
40
|
const config = await loadConfig({
|
|
44
41
|
workspaceRoot: root,
|
|
45
|
-
configPath: args.config,
|
|
46
42
|
require: false
|
|
47
43
|
});
|
|
48
44
|
const since = args.since ?? await getLatestTag(root);
|
|
@@ -58,12 +54,12 @@ const bumpVersionCli = bc.command({
|
|
|
58
54
|
dryRun: args.dryRun
|
|
59
55
|
});
|
|
60
56
|
if (args.quiet) {
|
|
61
|
-
console.log(result.
|
|
57
|
+
console.log(JSON.stringify(result.nextVersions));
|
|
62
58
|
} else {
|
|
63
59
|
console.log(formatBumpVersionResult(result, releaseType == null));
|
|
64
60
|
}
|
|
65
61
|
if (isRunningInGithubActions()) {
|
|
66
|
-
writeGithubActionsOutput("
|
|
62
|
+
writeGithubActionsOutput("versions", JSON.stringify(result.nextVersions));
|
|
67
63
|
writeGithubActionsOutput("hasBreakingChanges", String(result.hasBreakingChanges));
|
|
68
64
|
writeGithubActionsOutput("hasFeatures", String(result.hasFeatures));
|
|
69
65
|
writeGithubActionsOutput("changedPackages", result.changedPackages.map((it) => it.package.json.name).join(","));
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { bc } from './_utils.js';
|
|
2
2
|
export declare const findChangedPackagesCli: bc.Command<{
|
|
3
3
|
root: string | undefined;
|
|
4
|
-
config: string | undefined;
|
|
5
4
|
since: string | undefined;
|
|
6
5
|
until: string | undefined;
|
|
7
6
|
}, {
|
|
8
7
|
root: string | undefined;
|
|
9
|
-
config: string | undefined;
|
|
10
8
|
since: string | undefined;
|
|
11
9
|
until: string | undefined;
|
|
12
10
|
}>;
|
|
@@ -9,7 +9,6 @@ const findChangedPackagesCli = bc.command({
|
|
|
9
9
|
desc: "find changed packages between two commits, and output a comma-separated list of package names",
|
|
10
10
|
options: {
|
|
11
11
|
root: bc.string().desc("path to the root of the workspace (default: process.cwd())"),
|
|
12
|
-
config: bc.string("config").desc("path to the build.config.js file"),
|
|
13
12
|
since: bc.string().desc("starting point for the changelog (default: latest tag)"),
|
|
14
13
|
until: bc.string().desc("ending point for the changelog (default: HEAD)")
|
|
15
14
|
},
|
|
@@ -17,7 +16,6 @@ const findChangedPackagesCli = bc.command({
|
|
|
17
16
|
const root = args.root ?? process.cwd();
|
|
18
17
|
const config = await loadConfig({
|
|
19
18
|
workspaceRoot: root,
|
|
20
|
-
configPath: args.config,
|
|
21
19
|
require: false
|
|
22
20
|
});
|
|
23
21
|
const since = args.since ?? await getLatestTag(root);
|
|
@@ -2,11 +2,9 @@ import { bc } from './_utils.js';
|
|
|
2
2
|
export declare const generateChangelogCli: bc.Command<{
|
|
3
3
|
only: string | undefined;
|
|
4
4
|
root: string | undefined;
|
|
5
|
-
config: string | undefined;
|
|
6
5
|
since: string | undefined;
|
|
7
6
|
}, {
|
|
8
7
|
only: string | undefined;
|
|
9
8
|
root: string | undefined;
|
|
10
|
-
config: string | undefined;
|
|
11
9
|
since: string | undefined;
|
|
12
10
|
}>;
|
|
@@ -11,14 +11,12 @@ const generateChangelogCli = bc.command({
|
|
|
11
11
|
options: {
|
|
12
12
|
only: bc.string().desc("comma-separated list of packages to include"),
|
|
13
13
|
root: bc.string().desc("path to the root of the workspace (default: process.cwd())"),
|
|
14
|
-
config: bc.string("config").desc("path to the build.config.js file"),
|
|
15
14
|
since: bc.string().desc("starting point for the changelog (default: latest tag)")
|
|
16
15
|
},
|
|
17
16
|
handler: async (args) => {
|
|
18
17
|
const root = args.root ?? process.cwd();
|
|
19
18
|
const config = await loadConfig({
|
|
20
19
|
workspaceRoot: root,
|
|
21
|
-
configPath: args.config,
|
|
22
20
|
require: false
|
|
23
21
|
});
|
|
24
22
|
let workspacePackages = await collectPackageJsons(root, false);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { WorkspacePackage } from '../../../package-json/collect-package-jsons.js';
|
|
2
|
+
export interface LintConfig {
|
|
3
|
+
/**
|
|
4
|
+
* whether to also validate the root package.json
|
|
5
|
+
*
|
|
6
|
+
* @default false
|
|
7
|
+
*/
|
|
8
|
+
includeRoot?: boolean;
|
|
9
|
+
/** validation of external dependencies */
|
|
10
|
+
externalDependencies?: {
|
|
11
|
+
/** @default true */
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* whether to skip validating peer dependencies
|
|
15
|
+
*
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
18
|
+
skipPeerDependencies?: boolean;
|
|
19
|
+
shouldSkip?: (ctx: {
|
|
20
|
+
/** package currently being validated */
|
|
21
|
+
package: WorkspacePackage;
|
|
22
|
+
/** name of the dependency */
|
|
23
|
+
dependency: string;
|
|
24
|
+
/** version of the dependency */
|
|
25
|
+
version: string;
|
|
26
|
+
/** field in which the dependency is declared */
|
|
27
|
+
field: 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies';
|
|
28
|
+
}) => boolean;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { loadConfig } from "../_utils.js";
|
|
3
|
+
import { validateWorkspaceDeps } from "./validate-workspace-deps.js";
|
|
4
|
+
import * as bc from "@drizzle-team/brocli";
|
|
5
|
+
const INTERNAL_MESSAGES = {
|
|
6
|
+
not_workspace_proto: "internal dependencies must be linked with workspace: protocol",
|
|
7
|
+
standalone_dep: "non-standalone packages cannot depend on standalone packages with workspace: protocol",
|
|
8
|
+
not_workspace_dep: "workspace: protocol is used to link to a package not found in the workspace"
|
|
9
|
+
};
|
|
10
|
+
const lintCli = bc.command({
|
|
11
|
+
name: "lint",
|
|
12
|
+
desc: "check the workspace for any issues",
|
|
13
|
+
options: {
|
|
14
|
+
workspace: bc.string().desc("path to the workspace root (default: cwd)"),
|
|
15
|
+
noErrorCode: bc.boolean("no-error-code").desc("whether to always exit with a zero code").default(false)
|
|
16
|
+
},
|
|
17
|
+
handler: async (args) => {
|
|
18
|
+
const workspaceRoot = args.workspace ?? process.cwd();
|
|
19
|
+
const config = (await loadConfig({ workspaceRoot }))?.lint;
|
|
20
|
+
const errors = await validateWorkspaceDeps({
|
|
21
|
+
workspaceRoot,
|
|
22
|
+
config
|
|
23
|
+
});
|
|
24
|
+
if (errors.length > 0) {
|
|
25
|
+
const externalErrors = errors.filter((it) => it.type === "external");
|
|
26
|
+
const internalErrors = errors.filter((it) => it.type === "internal");
|
|
27
|
+
if (externalErrors.length > 0) {
|
|
28
|
+
console.error("⚠️ Found external dependencies mismatch:");
|
|
29
|
+
for (const error of externalErrors) {
|
|
30
|
+
console.error(` - at ${error.package}: ${error.at} has ${error.dependency}@${error.version}, but ${error.otherPackage} has @${error.otherVersion}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (internalErrors.length > 0) {
|
|
34
|
+
console.error("⚠️ Found issues with internal dependencies:");
|
|
35
|
+
for (const error of internalErrors) {
|
|
36
|
+
console.error(` - at ${error.package}, dependency ${error.dependency}: ${INTERNAL_MESSAGES[error.subtype]}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!args.noErrorCode) {
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
export {
|
|
46
|
+
lintCli
|
|
47
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { WorkspacePackage } from '../../../package-json/collect-package-jsons.js';
|
|
2
|
+
import { LintConfig } from './config.js';
|
|
3
|
+
/** information about a mismatch between a package and its dependencies */
|
|
4
|
+
export interface ExternalDepsError {
|
|
5
|
+
type: 'external';
|
|
6
|
+
/** package name where the mismatch occurred */
|
|
7
|
+
package: string;
|
|
8
|
+
/** name of the mismatched dependency */
|
|
9
|
+
dependency: string;
|
|
10
|
+
/** version of the mismatched dependency */
|
|
11
|
+
version: string;
|
|
12
|
+
/** type of dependency (`dependencies`, `devDependencies`, etc) */
|
|
13
|
+
at: string;
|
|
14
|
+
/** package name where the dependency was originally declared */
|
|
15
|
+
otherPackage: string;
|
|
16
|
+
/** original version of the dependency */
|
|
17
|
+
otherVersion: string;
|
|
18
|
+
}
|
|
19
|
+
export interface InternalDepsError {
|
|
20
|
+
type: 'internal';
|
|
21
|
+
/** package name where the mismatch occurred */
|
|
22
|
+
package: string;
|
|
23
|
+
/** name of the mismatched dependency */
|
|
24
|
+
dependency: string;
|
|
25
|
+
/**
|
|
26
|
+
* sub-type of the error
|
|
27
|
+
* - not_workspace_proto: internal dependencies must be linked with workspace: protocol
|
|
28
|
+
* - standalone_dep: non-standalone packages cannot depend on standalone packages
|
|
29
|
+
* - not_workspace_dep: `workspace:` protocol is used to link to a package not found in the workspace
|
|
30
|
+
*/
|
|
31
|
+
subtype: 'not_workspace_proto' | 'standalone_dep' | 'not_workspace_dep';
|
|
32
|
+
}
|
|
33
|
+
export type WorkspaceDepsError = ExternalDepsError | InternalDepsError;
|
|
34
|
+
/**
|
|
35
|
+
* validate the external dependencies of a workspace
|
|
36
|
+
*/
|
|
37
|
+
export declare function validateWorkspaceDeps(params: {
|
|
38
|
+
workspaceRoot: string | URL;
|
|
39
|
+
packages?: WorkspacePackage[];
|
|
40
|
+
config?: LintConfig;
|
|
41
|
+
}): Promise<WorkspaceDepsError[]>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { valid, satisfies, validRange, subset } from "semver";
|
|
2
|
+
import { collectPackageJsons } from "../../../package-json/collect-package-jsons.js";
|
|
3
|
+
async function validateWorkspaceDeps(params) {
|
|
4
|
+
const {
|
|
5
|
+
workspaceRoot,
|
|
6
|
+
config: {
|
|
7
|
+
includeRoot,
|
|
8
|
+
externalDependencies: {
|
|
9
|
+
enabled: externalDependenciesEnabled = true,
|
|
10
|
+
skipPeerDependencies: externalDependenciesSkipPeerDependencies = false,
|
|
11
|
+
shouldSkip: externalDependenciesShouldSkip
|
|
12
|
+
} = {}
|
|
13
|
+
} = {}
|
|
14
|
+
} = params;
|
|
15
|
+
const packages = params.packages ?? await collectPackageJsons(workspaceRoot, includeRoot);
|
|
16
|
+
const packagesMap = new Map(packages.map((pj) => [pj.json.name, pj]));
|
|
17
|
+
const versions = {};
|
|
18
|
+
const errors = [];
|
|
19
|
+
for (const pkg of packages) {
|
|
20
|
+
const pj = pkg.json;
|
|
21
|
+
if (pj.name === void 0) {
|
|
22
|
+
throw new Error("package.json without name is not supported");
|
|
23
|
+
}
|
|
24
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
25
|
+
const deps = pj[field];
|
|
26
|
+
if (!deps) continue;
|
|
27
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
28
|
+
if (packagesMap.has(name)) {
|
|
29
|
+
const otherPkg = packagesMap.get(name);
|
|
30
|
+
const otherPkgStandalone = Boolean(otherPkg?.json.fuman?.standalone);
|
|
31
|
+
if (!otherPkgStandalone && !version.startsWith("workspace:")) {
|
|
32
|
+
errors.push({
|
|
33
|
+
type: "internal",
|
|
34
|
+
package: pj.name,
|
|
35
|
+
dependency: name,
|
|
36
|
+
subtype: "not_workspace_proto"
|
|
37
|
+
});
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!pj.fuman?.standalone && otherPkgStandalone && version.startsWith("workspace:")) {
|
|
41
|
+
errors.push({
|
|
42
|
+
type: "internal",
|
|
43
|
+
package: pj.name,
|
|
44
|
+
dependency: name,
|
|
45
|
+
subtype: "standalone_dep"
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (version.startsWith("workspace:")) {
|
|
51
|
+
errors.push({
|
|
52
|
+
type: "internal",
|
|
53
|
+
package: pj.name,
|
|
54
|
+
dependency: name,
|
|
55
|
+
subtype: "not_workspace_dep"
|
|
56
|
+
});
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!externalDependenciesEnabled) continue;
|
|
60
|
+
if (field === "peerDependencies" && externalDependenciesSkipPeerDependencies) continue;
|
|
61
|
+
if (externalDependenciesShouldSkip?.({ package: pkg, dependency: name, version, field })) continue;
|
|
62
|
+
if (versions[name] === void 0) {
|
|
63
|
+
versions[name] = {};
|
|
64
|
+
}
|
|
65
|
+
for (const [pkgName, pkgDepVersions] of Object.entries(versions[name])) {
|
|
66
|
+
let ok = true;
|
|
67
|
+
if (pkgDepVersions.match(/^(?:https?:\/\/|catalog:)/)) {
|
|
68
|
+
ok = version === pkgDepVersions;
|
|
69
|
+
} else if (valid(version) != null) {
|
|
70
|
+
ok = satisfies(version, pkgDepVersions);
|
|
71
|
+
} else if (validRange(version) != null) {
|
|
72
|
+
ok = subset(pkgDepVersions, version);
|
|
73
|
+
}
|
|
74
|
+
if (!ok) {
|
|
75
|
+
errors.push({
|
|
76
|
+
type: "external",
|
|
77
|
+
package: pj.name,
|
|
78
|
+
dependency: name,
|
|
79
|
+
version,
|
|
80
|
+
at: field,
|
|
81
|
+
otherPackage: pkgName,
|
|
82
|
+
otherVersion: pkgDepVersions
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
versions[name][pj.name] = version;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return errors;
|
|
91
|
+
}
|
|
92
|
+
export {
|
|
93
|
+
validateWorkspaceDeps
|
|
94
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { bc } from './_utils.js';
|
|
2
2
|
export declare const releaseCli: bc.Command<{
|
|
3
|
-
kind: "
|
|
3
|
+
kind: "major" | "minor" | "patch" | "auto";
|
|
4
4
|
withGithubRelease: boolean;
|
|
5
5
|
gitExtraOrigins: string | undefined;
|
|
6
6
|
githubToken: string | undefined;
|
|
@@ -18,7 +18,7 @@ export declare const releaseCli: bc.Command<{
|
|
|
18
18
|
npmRegistry: string | undefined;
|
|
19
19
|
dryRun: boolean | undefined;
|
|
20
20
|
}, {
|
|
21
|
-
kind: "
|
|
21
|
+
kind: "major" | "minor" | "patch" | "auto";
|
|
22
22
|
withGithubRelease: boolean;
|
|
23
23
|
gitExtraOrigins: string | undefined;
|
|
24
24
|
githubToken: string | undefined;
|
package/cli/commands/release.js
CHANGED
|
@@ -39,10 +39,9 @@ const releaseCli = bc.command({
|
|
|
39
39
|
dryRun: bc.boolean("dry-run").desc("whether to skip publishing and only print what is going to happen")
|
|
40
40
|
},
|
|
41
41
|
handler: async (args) => {
|
|
42
|
-
const root = process.cwd();
|
|
42
|
+
const root = process.env.FUMAN_ROOT ?? process.cwd();
|
|
43
43
|
const config = await loadConfig({
|
|
44
44
|
workspaceRoot: root,
|
|
45
|
-
configPath: "build.config.js",
|
|
46
45
|
require: false
|
|
47
46
|
});
|
|
48
47
|
const workspaceWithRoot = await collectPackageJsons(root, true);
|
|
@@ -79,28 +78,40 @@ const releaseCli = bc.command({
|
|
|
79
78
|
} else {
|
|
80
79
|
changedPackages = workspace;
|
|
81
80
|
}
|
|
81
|
+
const taggingSchema = config?.versioning?.taggingSchema ?? "semver";
|
|
82
82
|
let tagName;
|
|
83
|
-
if (
|
|
84
|
-
const versions = sort(
|
|
83
|
+
if (taggingSchema === "semver") {
|
|
84
|
+
const versions = sort(
|
|
85
|
+
workspace.filter((pkg) => !pkg.json.fuman?.ownVersioning && !pkg.json.fuman?.standalone).map((pkg) => asNonNull(pkg.json.version))
|
|
86
|
+
);
|
|
85
87
|
tagName = `v${versions[versions.length - 1]}`;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!args.dryRun) {
|
|
93
|
-
process.exit(1);
|
|
88
|
+
if (await gitTagExists(tagName, root)) {
|
|
89
|
+
console.log(`❗ tag ${tagName} already exists. did the previous release complete successfully?`);
|
|
90
|
+
console.log("❗ if so, please verify versions in package.json and try again");
|
|
91
|
+
if (!args.dryRun) {
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
if (!changedPackages.some((pkg) => pkg.json.version === tagName.replace(/^v/, ""))) {
|
|
96
|
+
console.log(`❗ tag ${tagName} does not match any of the package versions. did the previous release complete successfully?`);
|
|
97
|
+
console.log("❗ if so, please verify versions in package.json, tag the commit release and try again");
|
|
98
|
+
if (!args.dryRun) {
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
101
|
}
|
|
102
|
+
} else if (taggingSchema === "date") {
|
|
103
|
+
const date = /* @__PURE__ */ new Date();
|
|
104
|
+
const tagNamePrefix = `v${date.getFullYear()}.${(date.getMonth() + 1).toString().padStart(2, "0")}.${date.getDate().toString().padStart(2, "0")}`;
|
|
105
|
+
let currentLetter = "a";
|
|
106
|
+
do {
|
|
107
|
+
tagName = `${tagNamePrefix}${currentLetter}`;
|
|
108
|
+
currentLetter = String.fromCharCode(currentLetter.charCodeAt(0) + 1);
|
|
109
|
+
} while (await gitTagExists(tagName, root));
|
|
110
|
+
} else {
|
|
111
|
+
throw new Error(`Unknown tagging schema: ${taggingSchema}`);
|
|
102
112
|
}
|
|
103
113
|
console.log("");
|
|
114
|
+
console.log("🚀 next tag:", tagName);
|
|
104
115
|
console.log("📝 generating changelog...");
|
|
105
116
|
const changelog = prevTag != null ? await generateChangelog({
|
|
106
117
|
workspace: changedPackages,
|
package/cli/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { buildPackage } from './commands/build.js';
|
|
2
2
|
export { generateDocs } from './commands/docs.js';
|
|
3
3
|
export { generateDepsGraph } from './commands/gen-deps-graph.js';
|
|
4
|
-
export { validateWorkspaceDeps, type WorkspaceDepsError } from './commands/validate-workspace-deps.js';
|
|
4
|
+
export { validateWorkspaceDeps, type WorkspaceDepsError } from './commands/lint/validate-workspace-deps.js';
|
package/config.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AnyToNever } from '@fuman/utils';
|
|
2
2
|
import { TypeDocOptions } from 'typedoc';
|
|
3
|
+
import { LintConfig } from './cli/commands/lint/config.js';
|
|
3
4
|
import { JsrConfig } from './jsr/config.js';
|
|
4
5
|
import { PackageJson } from './package-json/types.js';
|
|
5
6
|
import { VersioningOptions } from './versioning/types.js';
|
|
@@ -49,6 +50,8 @@ export interface RootConfigObject {
|
|
|
49
50
|
*/
|
|
50
51
|
excludePackages?: string[];
|
|
51
52
|
}>;
|
|
53
|
+
/** `lint` command configuration */
|
|
54
|
+
lint?: LintConfig;
|
|
52
55
|
}
|
|
53
56
|
/** root configuration (either an object or a function that returns an object) */
|
|
54
57
|
export type RootConfig = RootConfigObject | (() => RootConfigObject);
|
package/fuman-build.js
CHANGED
|
@@ -9,11 +9,11 @@ import { findChangedPackagesCli } from "./cli/commands/find-changed-packages.js"
|
|
|
9
9
|
import { generateChangelogCli } from "./cli/commands/gen-changelog.js";
|
|
10
10
|
import { generateDepsGraphCli } from "./cli/commands/gen-deps-graph.js";
|
|
11
11
|
import { jsrCli } from "./cli/commands/jsr.js";
|
|
12
|
+
import { lintCli } from "./cli/commands/lint/index.js";
|
|
12
13
|
import { publishPackagesCli } from "./cli/commands/publish.js";
|
|
13
14
|
import { releaseCli } from "./cli/commands/release.js";
|
|
14
|
-
import { validateWorkspaceDepsCli } from "./cli/commands/validate-workspace-deps.js";
|
|
15
15
|
await bc.run([
|
|
16
|
-
|
|
16
|
+
lintCli,
|
|
17
17
|
generateDepsGraphCli,
|
|
18
18
|
buildPackageCli,
|
|
19
19
|
jsrCli,
|
package/git/utils.d.ts
CHANGED
|
@@ -70,7 +70,9 @@ export interface CommitInfo {
|
|
|
70
70
|
*/
|
|
71
71
|
export declare function getCommitsBetween(params: {
|
|
72
72
|
/** starting point for the diff */
|
|
73
|
-
since
|
|
73
|
+
since?: string;
|
|
74
|
+
/** only include commits that modified these files */
|
|
75
|
+
files?: string[];
|
|
74
76
|
/**
|
|
75
77
|
* ending point for the diff
|
|
76
78
|
* @default 'HEAD'
|
package/git/utils.js
CHANGED
|
@@ -50,13 +50,21 @@ async function findChangedFiles(params) {
|
|
|
50
50
|
return files;
|
|
51
51
|
}
|
|
52
52
|
async function getCommitsBetween(params) {
|
|
53
|
-
const { since, until = "HEAD", cwd } = params;
|
|
53
|
+
const { since, until = "HEAD", cwd, files } = params;
|
|
54
54
|
const delim = `---${randomUUID()}---`;
|
|
55
|
-
const res = await exec([
|
|
55
|
+
const res = await exec([
|
|
56
|
+
"git",
|
|
57
|
+
"log",
|
|
58
|
+
`--pretty=format:%H %s%n%an%n%ae%n%aI%n%cn%n%ce%n%cI%n%b%n${delim}`,
|
|
59
|
+
since != null ? `${since}..${until}` : until,
|
|
60
|
+
// eslint-disable-next-line ts/strict-boolean-expressions
|
|
61
|
+
...files?.length ? ["--", ...files] : []
|
|
62
|
+
], {
|
|
56
63
|
cwd,
|
|
57
64
|
throwOnError: true
|
|
58
65
|
});
|
|
59
66
|
const lines = res.stdout.trim().split("\n");
|
|
67
|
+
if (lines.length === 1 && lines[0] === "") return [];
|
|
60
68
|
const items = [];
|
|
61
69
|
let current = null;
|
|
62
70
|
for (let i = 0; i < lines.length; i++) {
|
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getGithubActionsInput, isRunningInGithubActions, writeGithubActionsOutp
|
|
|
2
2
|
import { buildPackage } from "./cli/commands/build.js";
|
|
3
3
|
import { generateDocs } from "./cli/commands/docs.js";
|
|
4
4
|
import { generateDepsGraph } from "./cli/commands/gen-deps-graph.js";
|
|
5
|
-
import { validateWorkspaceDeps } from "./cli/commands/validate-workspace-deps.js";
|
|
5
|
+
import { validateWorkspaceDeps } from "./cli/commands/lint/validate-workspace-deps.js";
|
|
6
6
|
import { findChangedFiles, getCommitsBetween, getCurrentBranch, getCurrentCommit, getFirstCommit, getLatestTag, gitTagExists, parseConventionalCommit } from "./git/utils.js";
|
|
7
7
|
import { ExecError, exec } from "./misc/exec.js";
|
|
8
8
|
import { normalizeFilePath } from "./misc/path.js";
|
|
@@ -12,7 +12,7 @@ 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
14
|
import { parsePackageJson, parsePackageJsonFile, parsePackageJsonFromDir, parseWorkspaceRootPackageJson } from "./package-json/parse.js";
|
|
15
|
-
import { processPackageJson } from "./package-json/process-package-json.js";
|
|
15
|
+
import { processPackageJson, removeCommonjsExports } 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";
|
|
18
18
|
import { findProjectChangedFiles, findProjectChangedPackages } from "./versioning/collect-files.js";
|
|
@@ -52,6 +52,7 @@ export {
|
|
|
52
52
|
parsePackageJsonFromDir,
|
|
53
53
|
parseWorkspaceRootPackageJson,
|
|
54
54
|
processPackageJson,
|
|
55
|
+
removeCommonjsExports,
|
|
55
56
|
sortWorkspaceByPublishOrder,
|
|
56
57
|
validateWorkspaceDeps,
|
|
57
58
|
writeGithubActionsOutput
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function applyDenoDirectives(code: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { asNonNull } from "@fuman/utils";
|
|
2
|
+
function applyDenoDirectives(code) {
|
|
3
|
+
if (!code.match("<deno-(insert|remove|tsignore)>")) return code;
|
|
4
|
+
let insertContent = code.match(/\/\/\s*<deno-insert>(.*?)<\/deno-insert>/s);
|
|
5
|
+
while (insertContent) {
|
|
6
|
+
code = code.slice(0, insertContent.index) + insertContent[1].replace(/\/\/\s*/g, "") + code.slice(asNonNull(insertContent.index) + insertContent[0].length);
|
|
7
|
+
insertContent = code.match(/\/\/\s*<deno-insert>(.*?)<\/deno-insert>/s);
|
|
8
|
+
}
|
|
9
|
+
let removeContent = code.match(/\/\/\s*<deno-remove>(.*?)<\/deno-remove>/s);
|
|
10
|
+
while (removeContent) {
|
|
11
|
+
code = code.slice(0, removeContent.index) + code.slice(asNonNull(removeContent.index) + removeContent[0].length);
|
|
12
|
+
removeContent = code.match(/\/\/\s*<deno-remove>(.*?)<\/deno-remove>/s);
|
|
13
|
+
}
|
|
14
|
+
let tsIgnoreContent = code.match(/\/\/\s*<deno-tsignore>/);
|
|
15
|
+
while (tsIgnoreContent) {
|
|
16
|
+
code = `${code.slice(0, tsIgnoreContent.index)}/* @ts-ignore */${code.slice(asNonNull(tsIgnoreContent.index) + tsIgnoreContent[0].length)}`;
|
|
17
|
+
tsIgnoreContent = code.match(/\/\/\s*<deno-tsignore>/);
|
|
18
|
+
}
|
|
19
|
+
return code;
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
applyDenoDirectives
|
|
23
|
+
};
|
package/jsr/config.d.ts
CHANGED
|
@@ -45,4 +45,48 @@ export interface JsrConfig {
|
|
|
45
45
|
transformAst?: (ast: ts.SourceFile) => boolean;
|
|
46
46
|
/** similar to transformAst, but for the code itself (runs after transformAst) */
|
|
47
47
|
transformCode?: (path: string, code: string) => string;
|
|
48
|
+
/**
|
|
49
|
+
* whether to enable pre-processor directives when processing typescript files.
|
|
50
|
+
* this is useful to provide deno-specific typings for stuff.
|
|
51
|
+
*
|
|
52
|
+
* **note**: this is run *before* the transformCode hook, but *after* the transformAst hook
|
|
53
|
+
*
|
|
54
|
+
* supported directives:
|
|
55
|
+
*
|
|
56
|
+
* `<deno-insert>`: inserts the given code at transform time. example:
|
|
57
|
+
* ```ts
|
|
58
|
+
* // <deno-insert>
|
|
59
|
+
* // declare type SharedWorker = never
|
|
60
|
+
* // </deno-insert>
|
|
61
|
+
* ```
|
|
62
|
+
* transforms to:
|
|
63
|
+
* ```ts
|
|
64
|
+
* declare type SharedWorker = never
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* `<deno-remove>`: remove the given code at transform time. example:
|
|
68
|
+
* ```ts
|
|
69
|
+
* // <deno-remove>
|
|
70
|
+
* if (self instanceof SharedWorkerGlobalScope) {
|
|
71
|
+
* // do something
|
|
72
|
+
* }
|
|
73
|
+
* // </deno-remove>
|
|
74
|
+
* ```
|
|
75
|
+
* transforms to: (nothing)
|
|
76
|
+
*
|
|
77
|
+
* `<deno-tsignore>`: insert a `// @ts-ignore` comment when building for deno at the given position
|
|
78
|
+
* example:
|
|
79
|
+
* ```ts
|
|
80
|
+
* // <deno-tsignore>
|
|
81
|
+
* const foo: string = 123
|
|
82
|
+
* ```
|
|
83
|
+
* transforms to:
|
|
84
|
+
* ```ts
|
|
85
|
+
* // @ts-ignore
|
|
86
|
+
* const foo = 123
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
91
|
+
enableDenoDirectives?: boolean;
|
|
48
92
|
}
|
|
@@ -12,6 +12,7 @@ import { normalizeFilePath } from "../misc/path.js";
|
|
|
12
12
|
import { collectPackageJsons, filterPackageJsonsForPublish } from "../package-json/collect-package-jsons.js";
|
|
13
13
|
import { processPackageJson } from "../package-json/process-package-json.js";
|
|
14
14
|
import { findRootPackage, collectVersions } from "../package-json/utils.js";
|
|
15
|
+
import { applyDenoDirectives } from "./_deno-directives.js";
|
|
15
16
|
import { packageJsonToDeno } from "./deno-json.js";
|
|
16
17
|
function mergeArrays(a, b, defaultValue = []) {
|
|
17
18
|
if (!a) return b ?? defaultValue;
|
|
@@ -111,6 +112,9 @@ async function generateDenoWorkspace(params) {
|
|
|
111
112
|
fileContent = printer.printFile(file);
|
|
112
113
|
changed = true;
|
|
113
114
|
}
|
|
115
|
+
if (rootConfig?.enableDenoDirectives) {
|
|
116
|
+
fileContent = applyDenoDirectives(fileContent);
|
|
117
|
+
}
|
|
114
118
|
if (rootConfig?.transformCode || packageConfigJsr?.transformCode) {
|
|
115
119
|
const origFileContent = fileContent;
|
|
116
120
|
if (rootConfig?.transformCode) {
|
package/misc/_config.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function loadBuildConfig<T>(packageRoot: string
|
|
1
|
+
export declare function loadBuildConfig<T>(packageRoot: string): Promise<T | undefined>;
|
package/misc/_config.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
|
|
2
|
+
const CONFIG_NAME = "build.config.js";
|
|
3
|
+
async function loadBuildConfig(packageRoot) {
|
|
3
4
|
try {
|
|
4
|
-
const mod = (await import(join(packageRoot,
|
|
5
|
+
const mod = (await import(join(packageRoot, CONFIG_NAME))).default;
|
|
5
6
|
if (typeof mod === "function") {
|
|
6
7
|
return mod();
|
|
7
8
|
} else {
|
|
@@ -9,7 +10,7 @@ async function loadBuildConfig(packageRoot, configName = "build.config.js") {
|
|
|
9
10
|
}
|
|
10
11
|
} catch (e) {
|
|
11
12
|
if (!(e instanceof Error && e.code === "ERR_MODULE_NOT_FOUND")) {
|
|
12
|
-
throw new Error(`Could not load ${
|
|
13
|
+
throw new Error(`Could not load ${CONFIG_NAME}`, { cause: e });
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
16
|
}
|
|
@@ -12,3 +12,9 @@ export declare function processPackageJson(params: {
|
|
|
12
12
|
packageJsonOrig: PackageJson;
|
|
13
13
|
entrypoints: Record<string, string>;
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* remove comonjs export definitions from package.json
|
|
17
|
+
*
|
|
18
|
+
* **note**: this function modifies the input object
|
|
19
|
+
*/
|
|
20
|
+
export declare function removeCommonjsExports(exports: Record<string, unknown>): void;
|
|
@@ -138,6 +138,19 @@ function processPackageJson(params) {
|
|
|
138
138
|
entrypoints
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
|
+
function removeCommonjsExports(exports) {
|
|
142
|
+
const keys = Object.keys(exports);
|
|
143
|
+
if (keys.includes("import")) {
|
|
144
|
+
delete exports.require;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
for (const key of keys) {
|
|
148
|
+
const value = exports[key];
|
|
149
|
+
if (value == null || typeof value !== "object") continue;
|
|
150
|
+
delete value.require;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
141
153
|
export {
|
|
142
|
-
processPackageJson
|
|
154
|
+
processPackageJson,
|
|
155
|
+
removeCommonjsExports
|
|
143
156
|
};
|
package/package-json/types.d.ts
CHANGED
|
@@ -45,12 +45,21 @@ export interface PackageJson {
|
|
|
45
45
|
*/
|
|
46
46
|
distOnlyFields?: Record<string, unknown>;
|
|
47
47
|
/**
|
|
48
|
-
* whether this package has its own versioning scheme
|
|
48
|
+
* whether this package has its own versioning scheme, not managed by @fuman/build
|
|
49
49
|
* (be careful with this option! this might break cross-release semver compatibility)
|
|
50
50
|
*/
|
|
51
51
|
ownVersioning?: boolean;
|
|
52
52
|
/** whether this package should not be published */
|
|
53
53
|
private?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* whether this is a "standalone" package
|
|
56
|
+
*
|
|
57
|
+
* standalone packages have a few differences from normal ones:
|
|
58
|
+
* - (bump-version) each standalone package has independent versioning
|
|
59
|
+
* - (lint) normal packages can't depend on standalone packages
|
|
60
|
+
* - (lint) unlike normal packages, standalone packages are allowed to depend on older versions of workspace packages
|
|
61
|
+
*/
|
|
62
|
+
standalone?: boolean;
|
|
54
63
|
};
|
|
55
64
|
[key: string]: any;
|
|
56
65
|
}
|
package/package-json/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuman/build",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.15",
|
|
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": "
|
|
11
|
-
"@fuman/io": "^0.0.
|
|
12
|
-
"@fuman/node": "^0.0.
|
|
13
|
-
"@fuman/utils": "^0.0.
|
|
10
|
+
"@fuman/fetch": "0.0.13",
|
|
11
|
+
"@fuman/io": "^0.0.15",
|
|
12
|
+
"@fuman/node": "^0.0.15",
|
|
13
|
+
"@fuman/utils": "^0.0.15",
|
|
14
14
|
"cross-spawn": "^7.0.5",
|
|
15
15
|
"detect-indent": "^7.0.1",
|
|
16
16
|
"js-yaml": "^4.1.0",
|
|
@@ -30,30 +30,18 @@
|
|
|
30
30
|
"import": {
|
|
31
31
|
"types": "./index.d.ts",
|
|
32
32
|
"default": "./index.js"
|
|
33
|
-
},
|
|
34
|
-
"require": {
|
|
35
|
-
"types": "./index.d.cts",
|
|
36
|
-
"default": "./index.cjs"
|
|
37
33
|
}
|
|
38
34
|
},
|
|
39
35
|
"./vite": {
|
|
40
36
|
"import": {
|
|
41
37
|
"types": "./vite.d.ts",
|
|
42
38
|
"default": "./vite.js"
|
|
43
|
-
},
|
|
44
|
-
"require": {
|
|
45
|
-
"types": "./vite.d.cts",
|
|
46
|
-
"default": "./vite.cjs"
|
|
47
39
|
}
|
|
48
40
|
},
|
|
49
41
|
"./jsr": {
|
|
50
42
|
"import": {
|
|
51
43
|
"types": "./jsr.d.ts",
|
|
52
44
|
"default": "./jsr.js"
|
|
53
|
-
},
|
|
54
|
-
"require": {
|
|
55
|
-
"types": "./jsr.d.cts",
|
|
56
|
-
"default": "./jsr.cjs"
|
|
57
45
|
}
|
|
58
46
|
}
|
|
59
47
|
},
|
|
@@ -17,7 +17,7 @@ export interface BumpVersionResult {
|
|
|
17
17
|
/** max version of all packages */
|
|
18
18
|
maxVersion: string;
|
|
19
19
|
/** next version */
|
|
20
|
-
|
|
20
|
+
nextVersions: Record<string, string>;
|
|
21
21
|
/** changed packages which will be bumped to `nextVersion` */
|
|
22
22
|
changedPackages: BumpVersionPackage[];
|
|
23
23
|
/** detected release type */
|
|
@@ -36,6 +36,8 @@ export interface BumpVersionResult {
|
|
|
36
36
|
export declare function bumpVersion(params: {
|
|
37
37
|
/** packages for which to generate the changelog */
|
|
38
38
|
workspace: WorkspacePackage[];
|
|
39
|
+
/** previous tag */
|
|
40
|
+
prevTag?: string | null;
|
|
39
41
|
/** whether to bump version of all packages, not just changed ones */
|
|
40
42
|
all?: boolean;
|
|
41
43
|
type?: ReleaseType;
|
|
@@ -4,7 +4,7 @@ import process from "node:process";
|
|
|
4
4
|
import { asNonNull } from "@fuman/utils";
|
|
5
5
|
import detectIndent from "detect-indent";
|
|
6
6
|
import { parse, inc, satisfies, gt } from "semver";
|
|
7
|
-
import { getCommitsBetween, parseConventionalCommit } from "../git/utils.js";
|
|
7
|
+
import { getCommitsBetween, parseConventionalCommit, getLatestTag } from "../git/utils.js";
|
|
8
8
|
import { collectVersions, findRootPackage } from "../package-json/utils.js";
|
|
9
9
|
import { findProjectChangedPackages } from "./collect-files.js";
|
|
10
10
|
async function bumpVersion(params) {
|
|
@@ -21,7 +21,7 @@ async function bumpVersion(params) {
|
|
|
21
21
|
for (const pkg of workspaceWithoutRoot) {
|
|
22
22
|
if (pkg.root) continue;
|
|
23
23
|
const version = asNonNull(pkg.json.version);
|
|
24
|
-
if (pkg.json.fuman?.ownVersioning) {
|
|
24
|
+
if (pkg.json.fuman?.ownVersioning || pkg.json.fuman?.standalone) {
|
|
25
25
|
continue;
|
|
26
26
|
}
|
|
27
27
|
if (maxVersion == null || gt(version, maxVersion)) {
|
|
@@ -89,6 +89,9 @@ async function bumpVersion(params) {
|
|
|
89
89
|
if (obj == null) continue;
|
|
90
90
|
if (obj[pkgName] == null || typeof obj[pkgName] !== "string") continue;
|
|
91
91
|
let expandedVersion = obj[pkgName];
|
|
92
|
+
if (!expandedVersion.startsWith("workspace:") && otherPkg.json.fuman?.standalone) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
92
95
|
if (expandedVersion === "workspace:^") expandedVersion = `^${workspaceVersions[pkgName]}`;
|
|
93
96
|
if (expandedVersion === "workspace:*") expandedVersion = workspaceVersions[pkgName];
|
|
94
97
|
if (!satisfies(nextVersion, expandedVersion)) {
|
|
@@ -115,22 +118,42 @@ async function bumpVersion(params) {
|
|
|
115
118
|
if (withRoot) {
|
|
116
119
|
packagesToBump.push(findRootPackage(workspace));
|
|
117
120
|
}
|
|
121
|
+
const nextVersions = {};
|
|
122
|
+
let prevTag = params.prevTag;
|
|
118
123
|
for (const pkg of packagesToBump) {
|
|
119
124
|
if (pkg.json.fuman?.ownVersioning) continue;
|
|
125
|
+
let newVersion = nextVersion;
|
|
126
|
+
if (pkg.json.fuman?.standalone) {
|
|
127
|
+
newVersion = asNonNull(pkg.json.version);
|
|
128
|
+
if (prevTag === void 0) {
|
|
129
|
+
prevTag = await getLatestTag(pkg.path);
|
|
130
|
+
}
|
|
131
|
+
if (prevTag != null) {
|
|
132
|
+
const commits = await getCommitsBetween({
|
|
133
|
+
until: prevTag,
|
|
134
|
+
cwd,
|
|
135
|
+
files: [join(pkg.path, "package.json")]
|
|
136
|
+
});
|
|
137
|
+
if (commits.length > 0) {
|
|
138
|
+
newVersion = asNonNull(inc(newVersion, type));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
120
142
|
if (!dryRun) {
|
|
121
143
|
const pkgJsonPath = join(pkg.path, "package.json");
|
|
122
144
|
const pkgJsonText = await fsp.readFile(pkgJsonPath, "utf8");
|
|
123
145
|
const indent = detectIndent(pkgJsonText).indent || " ";
|
|
124
146
|
const pkgJson = JSON.parse(pkgJsonText);
|
|
125
|
-
pkgJson.version =
|
|
147
|
+
pkgJson.version = newVersion;
|
|
126
148
|
await fsp.writeFile(pkgJsonPath, `${JSON.stringify(pkgJson, null, indent)}
|
|
127
149
|
`);
|
|
128
150
|
}
|
|
129
|
-
pkg.json.version =
|
|
151
|
+
pkg.json.version = newVersion;
|
|
152
|
+
nextVersions[asNonNull(pkg.json.name)] = newVersion;
|
|
130
153
|
}
|
|
131
154
|
return {
|
|
132
155
|
maxVersion,
|
|
133
|
-
|
|
156
|
+
nextVersions,
|
|
134
157
|
changedPackages: result,
|
|
135
158
|
releaseType: type,
|
|
136
159
|
hasBreakingChanges,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { VersioningOptions } from './types.js';
|
|
2
1
|
import { WorkspacePackage } from '../package-json/index.js';
|
|
2
|
+
import { VersioningOptions } from './types.js';
|
|
3
3
|
export interface ProjectChangedFile {
|
|
4
4
|
/** package to which the file belongs */
|
|
5
5
|
package: WorkspacePackage;
|
package/versioning/types.d.ts
CHANGED
|
@@ -12,6 +12,21 @@ export interface ChangelogGeneratorParams {
|
|
|
12
12
|
}
|
|
13
13
|
/** settings for versioning manager */
|
|
14
14
|
export interface VersioningOptions {
|
|
15
|
+
/**
|
|
16
|
+
* schema to use when tagging commits
|
|
17
|
+
* - `semver`: semver-compatible schema (e.g. `v1.2.3`), based on the max. version of the workspace
|
|
18
|
+
* - `date`: date-based schema (e.g. `v2023.01.01`), based on the date of the release
|
|
19
|
+
*
|
|
20
|
+
* unless your monorepo has standalone packages, you should probably use `semver` schema.
|
|
21
|
+
* `date` schema is primarily useful for repos where different packages have separate release cycles,
|
|
22
|
+
* to avoid conflicts when bumping versions.
|
|
23
|
+
*
|
|
24
|
+
* > note: this is **only** used for the `release` command, when tagging commits.
|
|
25
|
+
* > this does **not** affect the versioning of the packages themselves, they always use semver.
|
|
26
|
+
*
|
|
27
|
+
* @default 'semver'
|
|
28
|
+
*/
|
|
29
|
+
taggingSchema?: 'semver' | 'date';
|
|
15
30
|
/**
|
|
16
31
|
* globs of files changes to which to white-list (relative to package root)
|
|
17
32
|
*
|
package/vite/build-plugin.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { MaybeArray, MaybePromise } from '@fuman/utils';
|
|
1
2
|
import { PluginOption } from 'vite';
|
|
2
3
|
import { BuildHookContext } from '../config.js';
|
|
3
|
-
import { MaybeArray, MaybePromise } from '@fuman/utils';
|
|
4
4
|
export declare function fumanBuild(params: {
|
|
5
5
|
root: URL | string;
|
|
6
6
|
/**
|
package/vite/build-plugin.js
CHANGED
|
@@ -7,7 +7,7 @@ import { loadBuildConfig } from "../misc/_config.js";
|
|
|
7
7
|
import { tryCopy, fileExists, directoryExists } from "../misc/fs.js";
|
|
8
8
|
import { normalizeFilePath } from "../misc/path.js";
|
|
9
9
|
import { collectPackageJsons } from "../package-json/collect-package-jsons.js";
|
|
10
|
-
import { processPackageJson } from "../package-json/process-package-json.js";
|
|
10
|
+
import { processPackageJson, removeCommonjsExports } from "../package-json/process-package-json.js";
|
|
11
11
|
import { collectVersions } from "../package-json/utils.js";
|
|
12
12
|
async function fumanBuild(params) {
|
|
13
13
|
const {
|
|
@@ -113,6 +113,9 @@ async function fumanBuild(params) {
|
|
|
113
113
|
},
|
|
114
114
|
async closeBundle() {
|
|
115
115
|
if (isNoop) return;
|
|
116
|
+
if (!buildCjs) {
|
|
117
|
+
removeCommonjsExports(packageJson.exports);
|
|
118
|
+
}
|
|
116
119
|
await params.finalizePackageJson?.(hookContext);
|
|
117
120
|
await packageConfig?.finalizePackageJson?.(hookContext);
|
|
118
121
|
await fsp.writeFile(join(buildDir, "package.json"), JSON.stringify(packageJson, null, 4));
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { bc } from './_utils.js';
|
|
2
|
-
/** information about a mismatch between a package and its dependencies */
|
|
3
|
-
export interface WorkspaceDepsError {
|
|
4
|
-
/** package name where the mismatch occurred */
|
|
5
|
-
package: string;
|
|
6
|
-
/** name of the mismatched dependency */
|
|
7
|
-
dependency: string;
|
|
8
|
-
/** version of the mismatched dependency */
|
|
9
|
-
version: string;
|
|
10
|
-
/** type of dependency (`dependencies`, `devDependencies`, etc) */
|
|
11
|
-
at: string;
|
|
12
|
-
/** package name where the dependency was originally declared */
|
|
13
|
-
otherPackage: string;
|
|
14
|
-
/** original version of the dependency */
|
|
15
|
-
otherVersion: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* validate the external dependencies of a workspace
|
|
19
|
-
*/
|
|
20
|
-
export declare function validateWorkspaceDeps(params: {
|
|
21
|
-
/** path to the workspace root */
|
|
22
|
-
workspaceRoot: string | URL;
|
|
23
|
-
/**
|
|
24
|
-
* whether to also validate the root package.json
|
|
25
|
-
*
|
|
26
|
-
* @default true
|
|
27
|
-
*/
|
|
28
|
-
includeRoot?: boolean;
|
|
29
|
-
/**
|
|
30
|
-
* whether to skip validating dependencies of other workspace packages
|
|
31
|
-
*
|
|
32
|
-
* @default true
|
|
33
|
-
*/
|
|
34
|
-
skipWorkspaceDeps?: boolean;
|
|
35
|
-
/** whether to skip validating peer dependencies */
|
|
36
|
-
skipPeerDeps?: boolean;
|
|
37
|
-
}): Promise<WorkspaceDepsError[]>;
|
|
38
|
-
export declare const validateWorkspaceDepsCli: bc.Command<{
|
|
39
|
-
includeRoot: boolean;
|
|
40
|
-
noSkipWorkspaceDeps: boolean | undefined;
|
|
41
|
-
root: string | undefined;
|
|
42
|
-
withErrorCode: boolean | undefined;
|
|
43
|
-
skipPeerDeps: boolean | undefined;
|
|
44
|
-
}, {
|
|
45
|
-
includeRoot: boolean;
|
|
46
|
-
noSkipWorkspaceDeps: boolean | undefined;
|
|
47
|
-
root: string | undefined;
|
|
48
|
-
withErrorCode: boolean | undefined;
|
|
49
|
-
skipPeerDeps: boolean | undefined;
|
|
50
|
-
}>;
|
|
@@ -1,87 +0,0 @@
|
|
|
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(params) {
|
|
6
|
-
const {
|
|
7
|
-
workspaceRoot,
|
|
8
|
-
includeRoot = true,
|
|
9
|
-
skipWorkspaceDeps = true,
|
|
10
|
-
skipPeerDeps = true
|
|
11
|
-
} = params;
|
|
12
|
-
const pjs = await collectPackageJsons(workspaceRoot, includeRoot);
|
|
13
|
-
const workspacePackages = new Set(skipWorkspaceDeps ? pjs.map((pj) => pj.json.name) : []);
|
|
14
|
-
const versions = {};
|
|
15
|
-
const errors = [];
|
|
16
|
-
for (const { json: pj } of pjs) {
|
|
17
|
-
if (pj.name === void 0) {
|
|
18
|
-
throw new Error("package.json without name is not supported");
|
|
19
|
-
}
|
|
20
|
-
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
21
|
-
const deps = pj[field];
|
|
22
|
-
if (!deps) continue;
|
|
23
|
-
if (field === "peerDependencies" && skipPeerDeps) continue;
|
|
24
|
-
for (const [name, version] of Object.entries(deps)) {
|
|
25
|
-
if (workspacePackages.has(name)) continue;
|
|
26
|
-
if (version.startsWith("workspace:")) continue;
|
|
27
|
-
if (versions[name] === void 0) {
|
|
28
|
-
versions[name] = {};
|
|
29
|
-
}
|
|
30
|
-
for (const [pkgName, pkgDepVersions] of Object.entries(versions[name])) {
|
|
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) {
|
|
38
|
-
errors.push({
|
|
39
|
-
package: pj.name,
|
|
40
|
-
dependency: name,
|
|
41
|
-
version,
|
|
42
|
-
at: field,
|
|
43
|
-
otherPackage: pkgName,
|
|
44
|
-
otherVersion: pkgDepVersions
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
versions[name][pj.name] = version;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return errors;
|
|
53
|
-
}
|
|
54
|
-
const validateWorkspaceDepsCli = bc.command({
|
|
55
|
-
name: "validate-workspace-deps",
|
|
56
|
-
desc: "validate the external dependencies of a workspace",
|
|
57
|
-
options: {
|
|
58
|
-
includeRoot: bc.boolean("include-root").desc("whether to also validate the root package.json").default(false),
|
|
59
|
-
noSkipWorkspaceDeps: bc.boolean("no-skip-workspace-deps").desc("whether to not skip validating dependencies of other workspace packages"),
|
|
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")
|
|
63
|
-
},
|
|
64
|
-
handler: async (args) => {
|
|
65
|
-
const errors = await validateWorkspaceDeps({
|
|
66
|
-
workspaceRoot: args.root ?? process.cwd(),
|
|
67
|
-
includeRoot: args.includeRoot,
|
|
68
|
-
skipWorkspaceDeps: !args.noSkipWorkspaceDeps,
|
|
69
|
-
skipPeerDeps: args.skipPeerDeps
|
|
70
|
-
});
|
|
71
|
-
if (errors.length > 0) {
|
|
72
|
-
console.error("⚠️ Found external dependencies mismatch:");
|
|
73
|
-
for (const error of errors) {
|
|
74
|
-
console.error(` - at ${error.package}: ${error.at} has ${error.dependency}@${error.version}, but ${error.otherPackage} has @${error.otherVersion}`);
|
|
75
|
-
}
|
|
76
|
-
if (args.withErrorCode) {
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
console.log("✅ All external dependencies match!");
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
export {
|
|
85
|
-
validateWorkspaceDeps,
|
|
86
|
-
validateWorkspaceDepsCli
|
|
87
|
-
};
|