@mxpicture/gcp-functions-code 0.2.1 → 0.2.3

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.
@@ -1,7 +1,7 @@
1
1
  import { DocumentData } from "@mxpicture/gcp-functions-common/types";
2
2
  import { GitChangesCommit } from "../git/GitChanges.js";
3
3
  import { Changelog } from "./Changelog.js";
4
- import { PackageVersion } from "./changelog.util.js";
4
+ import { PackageVersion } from "../common/code.common.js";
5
5
  export declare enum ChangeWatchType {
6
6
  changelog = "changelog",
7
7
  gitHistory = "gitHistory"
@@ -1,10 +1,8 @@
1
1
  import { GitChanges } from "../git/GitChanges.js";
2
- import { ChangelogEntry, PackageVersion } from "./changelog.util.js";
2
+ import { ChangelogEntry } from "./changelog.util.js";
3
+ import { PackageVersion } from "../common/code.common.js";
3
4
  export declare class Changelog {
4
5
  protected readonly git: GitChanges;
5
- protected readonly changelogPath: string;
6
- protected readonly packageJsonPath: string;
7
- protected readonly rootDir: string;
8
6
  constructor(git: GitChanges);
9
7
  run(): Promise<ChangelogEntry | null>;
10
8
  readChangelogEntries(sinceVersion?: PackageVersion): Promise<ChangelogEntry[]>;
@@ -1,35 +1,28 @@
1
1
  import { readFile, writeFile } from "node:fs/promises";
2
- import { dirname, resolve } from "node:path";
3
- import { changelogEntriesSchema, changelogEntrySchema, resolveBaselineCommit, readChangedTsFiles, mapChangedPackages, mapAffectedPackages, readMinPackageVersion, splitVersion, compareVersionsGT, } from "./changelog.util.js";
2
+ import { changelogEntriesSchema, changelogEntrySchema, readChangedTsFiles, mapChangedPackages, mapAffectedPackages, resolveBaselineCommit, } from "./changelog.util.js";
4
3
  import pkg from "json5";
5
- import { fileURLToPath } from "node:url";
6
4
  import { lastCommitHash } from "../git/git.util.js";
7
- import { formatJson2Spaces } from "../common/code.common.js";
5
+ import { compareVersionsGT, formatJson2Spaces, splitVersion, } from "../common/code.common.js";
6
+ import { readMinPackageVersion } from "../vscode/workspaceAsync.vscode.js";
7
+ import { basePaths, changelogPath, packageJsonPath, WsPathType, } from "@mxpicture/gcp-functions-common/path";
8
8
  const changelogLimit = 10;
9
- const __dirname = dirname(fileURLToPath(import.meta.url));
10
9
  export class Changelog {
11
10
  git;
12
- changelogPath;
13
- packageJsonPath;
14
- rootDir;
15
11
  constructor(git) {
16
12
  this.git = git;
17
- this.changelogPath = resolve(__dirname, "../../changelog.json");
18
- this.packageJsonPath = resolve(__dirname, "../../package.json");
19
- this.rootDir = resolve(__dirname, "../../../..");
20
13
  }
21
14
  async run() {
22
15
  const existingEntries = await this.readChangelogEntries();
23
- const currentCommitHash = await lastCommitHash(this.rootDir);
16
+ const currentCommitHash = await lastCommitHash(basePaths.root);
24
17
  if (!currentCommitHash)
25
18
  throw new Error("No current git commit hash found");
26
- const baselineCommit = await resolveBaselineCommit(existingEntries, this.git.rootDir, this.packageJsonPath);
19
+ const baselineCommit = await resolveBaselineCommit(existingEntries, this.git.rootDir, packageJsonPath(WsPathType.code));
27
20
  const changedFiles = await readChangedTsFiles(baselineCommit, this.git);
28
21
  if (changedFiles.length === 0)
29
22
  return null;
30
23
  const changedPackages = mapChangedPackages(changedFiles);
31
24
  const affectedPackages = mapAffectedPackages(changedPackages);
32
- const version = await readMinPackageVersion(this.rootDir);
25
+ const version = await readMinPackageVersion(basePaths.root);
33
26
  if (!version)
34
27
  throw new Error("No version found");
35
28
  const entry = {
@@ -46,12 +39,12 @@ export class Changelog {
46
39
  if (!condenseResult.hasChanged)
47
40
  return null;
48
41
  await changelogEntriesSchema.parseAsync(condenseResult.entries);
49
- await writeFile(this.changelogPath, await formatJson2Spaces(pkg.stringify(condenseResult.entries)));
42
+ await writeFile(changelogPath, await formatJson2Spaces(pkg.stringify(condenseResult.entries)));
50
43
  return entry;
51
44
  }
52
45
  async readChangelogEntries(sinceVersion) {
53
46
  try {
54
- const content = await readFile(this.changelogPath, "utf8");
47
+ const content = await readFile(changelogPath, "utf8");
55
48
  const parsed = pkg.parse(content);
56
49
  const entries = await changelogEntriesSchema.parseAsync(parsed);
57
50
  if (!sinceVersion)
@@ -1,45 +1,11 @@
1
1
  import { z } from "zod";
2
2
  import { GitChanges } from "../git/GitChanges.js";
3
+ import { PackageVersion } from "../common/code.common.js";
4
+ import { WsPathType } from "@mxpicture/gcp-functions-common/path";
3
5
  export type ProcessMode = "fix" | "restore";
4
- export interface PackageVersion {
5
- major: number;
6
- minor: number | null;
7
- patch: number | null;
8
- prefix: string | null;
9
- }
10
- export declare const packageVersionSchema: z.ZodObject<{
11
- major: z.ZodNumber;
12
- minor: z.ZodXor<readonly [z.ZodNumber, z.ZodNull]>;
13
- patch: z.ZodXor<readonly [z.ZodNumber, z.ZodNull]>;
14
- prefix: z.ZodXor<readonly [z.ZodString, z.ZodNull]>;
15
- }, z.core.$strip>;
16
- export interface PnpmWorkspace {
17
- packages: string[];
18
- }
19
- export interface PackageJson {
20
- name: string;
21
- version: string;
22
- dependencies?: Record<string, string>;
23
- devDependencies?: Record<string, string>;
24
- peerDependencies?: Record<string, string>;
25
- [key: string]: unknown;
26
- }
27
- export interface PackageEntry {
28
- jsonPath: string;
29
- repoPath: string;
30
- wsName: string;
31
- content: PackageJson;
32
- modified: boolean;
33
- }
34
- export interface MapEntry {
35
- fromPkg: PackageEntry;
36
- toPkg: PackageEntry;
37
- relPath: string;
38
- }
39
- export declare const readPackageJson: (dirOrFilepath: string) => Promise<PackageJson | null>;
40
- export declare const changelogPackageNames: readonly ["common", "backend", "frontend", "code"];
6
+ export declare const changelogPackageNames: WsPathType[];
41
7
  export type ChangelogPackageName = (typeof changelogPackageNames)[number];
42
- export declare const changelogDependents: Record<ChangelogPackageName, string[]>;
8
+ export declare const changelogDependents: Record<ChangelogPackageName, WsPathType[]>;
43
9
  export interface ChangelogEntry {
44
10
  version: string;
45
11
  versionParts: PackageVersion;
@@ -77,18 +43,7 @@ export declare const changelogEntriesSchema: z.ZodArray<z.ZodObject<{
77
43
  affectedPackages: z.ZodArray<z.ZodString>;
78
44
  changedFiles: z.ZodArray<z.ZodString>;
79
45
  }, z.core.$strip>>;
80
- export declare const splitVersion: (version: string) => PackageVersion;
81
- export declare const concatVersion: (version: PackageVersion) => string;
82
- export declare const compareVersions: (a: PackageVersion | string, b: PackageVersion | string) => number;
83
- export declare const compareVersionsGT: (a: PackageVersion | string, b: PackageVersion | string) => boolean;
84
- export declare const readWorkspaceYaml: (repoRoot: string) => Promise<PnpmWorkspace>;
85
- export declare const readPackageJsons: (repoRoot: string) => Promise<PackageEntry[]>;
86
- export declare const writePackageJsons: (entries: PackageEntry[]) => Promise<void[]>;
87
- export declare const buildMapEntries: (pkgEntries: PackageEntry[]) => MapEntry[];
88
46
  export declare const resolveBaselineCommit: (entries: ChangelogEntry[], rootDir: string, packageJsonPath: string) => Promise<string>;
89
- export declare const ensureCommitRef: (ref: string, fallback: string, rootDir: string) => Promise<string>;
90
47
  export declare const readChangedTsFiles: (baselineCommit: string, git: GitChanges) => Promise<string[]>;
91
48
  export declare const mapChangedPackages: (files: string[]) => string[];
92
49
  export declare const mapAffectedPackages: (changedPackages: string[]) => string[];
93
- export declare const readPackageVersions: (rootDir: string) => Promise<Record<string, string>>;
94
- export declare const readMinPackageVersion: (rootDir: string) => Promise<string | null>;
@@ -1,40 +1,23 @@
1
- import { readdir, readFile, writeFile } from "node:fs/promises";
2
- import { join, relative, resolve } from "node:path";
3
- import pkg from "json5";
4
1
  import { z } from "zod";
5
- import { parse } from "yaml";
6
- import { verifiedGit } from "../git/git.util.js";
7
- export const packageVersionSchema = z.object({
8
- major: z.number().min(0),
9
- minor: z.xor([z.number(), z.null()]),
10
- patch: z.xor([z.number(), z.null()]),
11
- prefix: z.xor([z.string(), z.null()]),
12
- });
13
- export const readPackageJson = async (dirOrFilepath) => {
14
- try {
15
- const pkgPath = dirOrFilepath.endsWith("/package.json")
16
- ? dirOrFilepath
17
- : join(dirOrFilepath, "package.json");
18
- const content = await readFile(pkgPath, "utf8");
19
- return pkg.parse(content);
20
- }
21
- catch {
22
- return null;
23
- }
24
- };
25
- // todo get dynamically
26
- export const changelogPackageNames = [
27
- "common",
28
- "backend",
29
- "frontend",
30
- "code",
31
- ];
32
- // todo get dynamically
2
+ import { ensureCommitRef, verifiedGit } from "../git/git.util.js";
3
+ import { packageVersionSchema } from "../common/code.common.js";
4
+ import { readPackageJson } from "../vscode/workspaceAsync.vscode.js";
5
+ import { WsPathType } from "@mxpicture/gcp-functions-common/path";
6
+ export const changelogPackageNames = Object.values(WsPathType);
33
7
  export const changelogDependents = {
34
- common: ["common", "backend", "frontend", "code"],
35
- backend: ["backend", "code"],
36
- frontend: ["frontend", "code"],
37
- code: ["code"],
8
+ [WsPathType.backend]: [WsPathType.backend, WsPathType.generator],
9
+ [WsPathType.code]: [WsPathType.fs, WsPathType.generator],
10
+ [WsPathType.common]: [
11
+ WsPathType.common,
12
+ WsPathType.backend,
13
+ WsPathType.code,
14
+ WsPathType.frontend,
15
+ WsPathType.fs,
16
+ WsPathType.generator,
17
+ ],
18
+ [WsPathType.frontend]: [WsPathType.frontend, WsPathType.generator],
19
+ [WsPathType.fs]: [WsPathType.fs],
20
+ [WsPathType.generator]: [WsPathType.generator],
38
21
  };
39
22
  export const changelogEntrySchema = z.object({
40
23
  version: z.string().min(1),
@@ -46,110 +29,6 @@ export const changelogEntrySchema = z.object({
46
29
  changedFiles: z.array(z.string()),
47
30
  });
48
31
  export const changelogEntriesSchema = z.array(changelogEntrySchema);
49
- export const splitVersion = (version) => {
50
- const parts = version.split(".");
51
- let major = parts.shift();
52
- const prefix = major.startsWith("v") ? "v" : null;
53
- if (major.startsWith("v"))
54
- major = major.substring(1);
55
- const minor = parts.shift();
56
- const patch = parts.shift();
57
- return {
58
- major: Number(major),
59
- minor: minor ? Number(minor) : null,
60
- patch: patch ? Number(patch) : null,
61
- prefix,
62
- };
63
- };
64
- export const concatVersion = (version) => {
65
- let result = version.major.toString();
66
- if (version.prefix)
67
- result = version.prefix + result;
68
- if (version.minor)
69
- result += "." + version.minor.toString();
70
- if (version.patch)
71
- result += "." + version.patch.toString();
72
- return result;
73
- };
74
- export const compareVersions = (a, b) => {
75
- a = typeof a === "string" ? splitVersion(a) : a;
76
- b = typeof b === "string" ? splitVersion(b) : b;
77
- if (a.major !== b.major)
78
- return a.major - b.major;
79
- if (a.minor === null || b.minor === null)
80
- return 0;
81
- if (a.minor !== b.minor)
82
- return a.minor - b.minor;
83
- if (a.patch === null || b.patch === null)
84
- return 0;
85
- if (a.patch !== b.patch)
86
- return a.patch - b.patch;
87
- return 0;
88
- };
89
- export const compareVersionsGT = (a, b) => compareVersions(a, b) > 0;
90
- export const readWorkspaceYaml = async (repoRoot) => {
91
- const workspaceFilePath = resolve(repoRoot, "pnpm-workspace.yaml");
92
- const fileContent = await readFile(workspaceFilePath, "utf8");
93
- const parsed = parse(fileContent);
94
- if (!parsed || !parsed.packages || !Array.isArray(parsed.packages))
95
- throw new Error("No 'packages' field found in pnpm-workspace.yaml");
96
- const packageColletions = await Promise.all(parsed.packages.map(async (p) => {
97
- if (!p.endsWith("/*"))
98
- return [p];
99
- const base = p.substring(0, p.length - 2);
100
- const pkgs = await readdir(join(repoRoot, base));
101
- return pkgs
102
- .filter((pkg) => !pkg.startsWith("."))
103
- .map((pkg) => join(base, pkg));
104
- }));
105
- const packages = [];
106
- for (const packageColletion of packageColletions)
107
- packages.push(...packageColletion);
108
- return { packages };
109
- };
110
- export const readPackageJsons = async (repoRoot) => {
111
- try {
112
- const pnpmWS = await readWorkspaceYaml(repoRoot);
113
- return Promise.all(pnpmWS.packages.map(async (repoPath) => {
114
- const jsonPath = join(repoRoot, repoPath, "package.json");
115
- const content = JSON.parse(await readFile(jsonPath, "utf8"));
116
- return {
117
- jsonPath,
118
- repoPath,
119
- content,
120
- modified: false,
121
- wsName: repoPath.startsWith("packages/")
122
- ? repoPath.substring(9)
123
- : repoPath,
124
- };
125
- }));
126
- }
127
- catch {
128
- return [];
129
- }
130
- };
131
- export const writePackageJsons = async (entries) => Promise.all(entries.map(async (entry) => {
132
- if (entry.modified) {
133
- await writeFile(entry.jsonPath, JSON.stringify(entry.content, null, 2) + "\n", "utf8");
134
- console.log(` ✅ Updated ${entry.content.name}\n`);
135
- }
136
- else {
137
- console.log(` ⏭️ No workspace dependencies to fix. ${entry.content.name}\n`);
138
- }
139
- }));
140
- // Build a map of package names to their versions
141
- export const buildMapEntries = (pkgEntries) => {
142
- const versionEntries = [];
143
- for (const fromPkg of pkgEntries)
144
- for (const toPkg of pkgEntries)
145
- if (fromPkg.content.name !== toPkg.content.name)
146
- versionEntries.push({
147
- fromPkg,
148
- toPkg,
149
- relPath: relative(fromPkg.repoPath, toPkg.repoPath),
150
- });
151
- return versionEntries;
152
- };
153
32
  export const resolveBaselineCommit = async (entries, rootDir, packageJsonPath) => {
154
33
  if (entries.length > 0 &&
155
34
  entries[0].gitCommitHashs.length > 0 &&
@@ -166,16 +45,6 @@ export const resolveBaselineCommit = async (entries, rootDir, packageJsonPath) =
166
45
  }
167
46
  return ensureCommitRef("HEAD~1", "HEAD", rootDir);
168
47
  };
169
- export const ensureCommitRef = async (ref, fallback, rootDir) => {
170
- try {
171
- await verifiedGit(rootDir, ref);
172
- return ref;
173
- }
174
- catch {
175
- await verifiedGit(rootDir, fallback);
176
- return fallback;
177
- }
178
- };
179
48
  export const readChangedTsFiles = async (baselineCommit, git) => {
180
49
  git.sinceCommit = baselineCommit;
181
50
  const [result, simplegit] = await Promise.all([
@@ -211,18 +80,3 @@ export const mapAffectedPackages = (changedPackages) => {
211
80
  }
212
81
  return [...affected].sort();
213
82
  };
214
- export const readPackageVersions = async (rootDir) => {
215
- const versions = {};
216
- const packageJsons = await readPackageJsons(rootDir);
217
- for (const packageJson of packageJsons)
218
- versions[packageJson.repoPath] = packageJson.content.version;
219
- return versions;
220
- };
221
- export const readMinPackageVersion = async (rootDir) => {
222
- const versions = await readPackageVersions(rootDir);
223
- const splitVersions = Object.values(versions).map(splitVersion);
224
- if (splitVersions.length === 0)
225
- return null;
226
- splitVersions.sort(compareVersions);
227
- return concatVersion(splitVersions[0]);
228
- };
@@ -1,5 +1,6 @@
1
1
  import { MetaFileExtension, MetaFileType } from "@mxpicture/gcp-functions-common/meta";
2
2
  import { ExtractFileParts, SourceFile } from "./types.common.js";
3
+ import { z } from "zod";
3
4
  export declare const isTemplateFile: (filename: string) => boolean;
4
5
  export declare const isTsFile: (filename: string, type: MetaFileType) => boolean;
5
6
  export declare const isAnyFile: (filename: string, type?: MetaFileType, ext?: MetaFileExtension) => boolean;
@@ -15,3 +16,19 @@ export declare const formatCode: (...lines: string[]) => Promise<string>;
15
16
  export declare const formatJson: (...lines: string[]) => Promise<string>;
16
17
  export declare const formatJson2Spaces: (...lines: string[]) => Promise<string>;
17
18
  export declare const createSourceFiles: (filePaths: string[]) => SourceFile[];
19
+ export interface PackageVersion {
20
+ major: number;
21
+ minor: number | null;
22
+ patch: number | null;
23
+ prefix: string | null;
24
+ }
25
+ export declare const packageVersionSchema: z.ZodObject<{
26
+ major: z.ZodNumber;
27
+ minor: z.ZodXor<readonly [z.ZodNumber, z.ZodNull]>;
28
+ patch: z.ZodXor<readonly [z.ZodNumber, z.ZodNull]>;
29
+ prefix: z.ZodXor<readonly [z.ZodString, z.ZodNull]>;
30
+ }, z.core.$strip>;
31
+ export declare const splitVersion: (version: string) => PackageVersion;
32
+ export declare const concatVersion: (version: PackageVersion) => string;
33
+ export declare const compareVersions: (a: PackageVersion | string, b: PackageVersion | string) => number;
34
+ export declare const compareVersionsGT: (a: PackageVersion | string, b: PackageVersion | string) => boolean;
@@ -1,6 +1,7 @@
1
1
  import ts from "typescript";
2
2
  import { format } from "prettier";
3
3
  import { guardMetaFileExtension, guardMetaFileType, MetaFileExtension, MetaFileType, } from "@mxpicture/gcp-functions-common/meta";
4
+ import { z } from "zod";
4
5
  export const isTemplateFile = (filename) => isTsFile(filename, MetaFileType.template);
5
6
  export const isTsFile = (filename, type) => isAnyFile(filename, type, MetaFileExtension.ts);
6
7
  export const isAnyFile = (filename, type, ext) => {
@@ -63,3 +64,50 @@ export const createSourceFiles = (filePaths) => {
63
64
  }
64
65
  return results;
65
66
  };
67
+ export const packageVersionSchema = z.object({
68
+ major: z.number().min(0),
69
+ minor: z.xor([z.number(), z.null()]),
70
+ patch: z.xor([z.number(), z.null()]),
71
+ prefix: z.xor([z.string(), z.null()]),
72
+ });
73
+ export const splitVersion = (version) => {
74
+ const parts = version.split(".");
75
+ let major = parts.shift();
76
+ const prefix = major.startsWith("v") ? "v" : null;
77
+ if (major.startsWith("v"))
78
+ major = major.substring(1);
79
+ const minor = parts.shift();
80
+ const patch = parts.shift();
81
+ return {
82
+ major: Number(major),
83
+ minor: minor ? Number(minor) : null,
84
+ patch: patch ? Number(patch) : null,
85
+ prefix,
86
+ };
87
+ };
88
+ export const concatVersion = (version) => {
89
+ let result = version.major.toString();
90
+ if (version.prefix)
91
+ result = version.prefix + result;
92
+ if (version.minor)
93
+ result += "." + version.minor.toString();
94
+ if (version.patch)
95
+ result += "." + version.patch.toString();
96
+ return result;
97
+ };
98
+ export const compareVersions = (a, b) => {
99
+ a = typeof a === "string" ? splitVersion(a) : a;
100
+ b = typeof b === "string" ? splitVersion(b) : b;
101
+ if (a.major !== b.major)
102
+ return a.major - b.major;
103
+ if (a.minor === null || b.minor === null)
104
+ return 0;
105
+ if (a.minor !== b.minor)
106
+ return a.minor - b.minor;
107
+ if (a.patch === null || b.patch === null)
108
+ return 0;
109
+ if (a.patch !== b.patch)
110
+ return a.patch - b.patch;
111
+ return 0;
112
+ };
113
+ export const compareVersionsGT = (a, b) => compareVersions(a, b) > 0;
@@ -1,5 +1,4 @@
1
1
  import ts from "typescript";
2
- import type { PackageJson } from "type-fest";
3
2
  import { MetaFileExtension, MetaFileType, MetaTargetType } from "@mxpicture/gcp-functions-common/meta";
4
3
  export interface ExtractFileParts {
5
4
  filename: string;
@@ -18,3 +17,26 @@ export interface TargetPackage {
18
17
  filePath: string;
19
18
  pkg: PackageJson;
20
19
  }
20
+ export interface PnpmWorkspace {
21
+ packages: string[];
22
+ }
23
+ export interface PackageJson {
24
+ name: string;
25
+ version: string;
26
+ dependencies?: Record<string, string>;
27
+ devDependencies?: Record<string, string>;
28
+ peerDependencies?: Record<string, string>;
29
+ [key: string]: unknown;
30
+ }
31
+ export interface PackageEntry {
32
+ jsonPath: string;
33
+ repoPath: string;
34
+ wsName: string;
35
+ content: PackageJson;
36
+ modified: boolean;
37
+ }
38
+ export interface MapEntry {
39
+ fromPkg: PackageEntry;
40
+ toPkg: PackageEntry;
41
+ relPath: string;
42
+ }
@@ -1,4 +1,5 @@
1
- import { MapEntry, ProcessMode, PackageEntry } from "../changelog/changelog.util.js";
1
+ import { ProcessMode } from "../changelog/changelog.util.js";
2
+ import { MapEntry, PackageEntry } from "../common/types.common.js";
2
3
  export interface FixWorkspaceDepsMain {
3
4
  mapEntries: MapEntry[];
4
5
  consumingPkg: PackageEntry;
@@ -1,4 +1,4 @@
1
- import { buildMapEntries, readPackageJsons, writePackageJsons, } from "../changelog/changelog.util.js";
1
+ import { readPackageJsons, buildMapEntries, writePackageJsons, } from "../vscode/workspaceAsync.vscode.js";
2
2
  export class IFixWorkspaceDeps {
3
3
  repoRoot;
4
4
  mode;
@@ -7,3 +7,4 @@ import { GitCommitHash } from "./types.git.js";
7
7
  export declare const verifiedGit: (rootDir: string, commitRef?: string) => Promise<SimpleGit>;
8
8
  export declare const lastCommitHash: (rootDir: string) => Promise<string | null>;
9
9
  export declare const commitHashs: (rootDir: string) => Promise<GitCommitHash[]>;
10
+ export declare const ensureCommitRef: (ref: string, fallback: string, rootDir: string) => Promise<string>;
@@ -31,3 +31,13 @@ export const commitHashs = async (rootDir) => (await (await verifiedGit(rootDir)
31
31
  hash: a.hash,
32
32
  diff: a.diff,
33
33
  }));
34
+ export const ensureCommitRef = async (ref, fallback, rootDir) => {
35
+ try {
36
+ await verifiedGit(rootDir, ref);
37
+ return ref;
38
+ }
39
+ catch {
40
+ await verifiedGit(rootDir, fallback);
41
+ return fallback;
42
+ }
43
+ };
@@ -1,2 +1,11 @@
1
1
  import { WorkspaceFile } from "./types.vscode.js";
2
+ import { MapEntry, PackageEntry, PackageJson, PnpmWorkspace } from "../common/types.common.js";
2
3
  export declare const findWorkspaceRootAsync: (startPath?: string) => Promise<WorkspaceFile>;
4
+ export declare const readWorkspaceYaml: (repoRoot: string) => Promise<PnpmWorkspace>;
5
+ export declare const readPackageJson: (dirOrFilepath: string) => Promise<PackageJson | null>;
6
+ export declare const readPackageJsonThrow: (dirOrFilepath: string) => Promise<PackageJson>;
7
+ export declare const readPackageJsons: (repoRoot: string) => Promise<PackageEntry[]>;
8
+ export declare const writePackageJsons: (entries: PackageEntry[]) => Promise<void[]>;
9
+ export declare const buildMapEntries: (pkgEntries: PackageEntry[]) => MapEntry[];
10
+ export declare const readPackageVersions: (rootDir: string) => Promise<Record<string, string>>;
11
+ export declare const readMinPackageVersion: (rootDir: string) => Promise<string | null>;
@@ -1,14 +1,17 @@
1
- import { dirname, join } from "path";
1
+ import { dirname, join, relative, resolve } from "path";
2
+ import { parse } from "yaml";
2
3
  import { fileURLToPath } from "url";
4
+ import pkg from "json5";
3
5
  import { WorkspaceFileType } from "./types.vscode.js";
4
6
  import { getFileType } from "./common.vscode.js";
5
- import { readdir } from "fs/promises";
7
+ import { readdir, readFile, writeFile } from "fs/promises";
8
+ import { compareVersions, concatVersion, splitVersion, } from "../common/code.common.js";
6
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
10
  let __workspaceRoot = null;
8
- export const findWorkspaceRootAsync = async (startPath) => __workspaceRoot ??
9
- (__workspaceRoot = await _findWorkspaceRoot(startPath ?? __dirname));
10
- const _findWorkspaceRoot = async (dirPath) => {
11
- let path = dirPath;
11
+ export const findWorkspaceRootAsync = async (startPath) => {
12
+ if (__workspaceRoot)
13
+ return __workspaceRoot;
14
+ let path = startPath ?? __dirname;
12
15
  let level = 0;
13
16
  const promises = [];
14
17
  while (path.length > 0) {
@@ -50,5 +53,98 @@ const _findWorkspaceRoot = async (dirPath) => {
50
53
  results.find((r) => r.value.type === WorkspaceFileType.package)?.value;
51
54
  if (!result)
52
55
  throw new Error("findWorkspaceRoot: no file found");
56
+ __workspaceRoot = result;
53
57
  return result;
54
58
  };
59
+ export const readWorkspaceYaml = async (repoRoot) => {
60
+ const workspaceFilePath = resolve(repoRoot, "pnpm-workspace.yaml");
61
+ const fileContent = await readFile(workspaceFilePath, "utf8");
62
+ const parsed = parse(fileContent);
63
+ if (!parsed || !parsed.packages || !Array.isArray(parsed.packages))
64
+ throw new Error("No 'packages' field found in pnpm-workspace.yaml");
65
+ const packageColletions = await Promise.all(parsed.packages.map(async (p) => {
66
+ if (!p.endsWith("/*"))
67
+ return [p];
68
+ const base = p.substring(0, p.length - 2);
69
+ const pkgs = await readdir(join(repoRoot, base));
70
+ return pkgs
71
+ .filter((pkg) => !pkg.startsWith("."))
72
+ .map((pkg) => join(base, pkg));
73
+ }));
74
+ const packages = [];
75
+ for (const packageColletion of packageColletions)
76
+ packages.push(...packageColletion);
77
+ return { packages };
78
+ };
79
+ export const readPackageJson = async (dirOrFilepath) => {
80
+ try {
81
+ return readPackageJsonThrow(dirOrFilepath);
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ };
87
+ export const readPackageJsonThrow = async (dirOrFilepath) => {
88
+ const pkgPath = dirOrFilepath.endsWith("/package.json")
89
+ ? dirOrFilepath
90
+ : join(dirOrFilepath, "package.json");
91
+ return pkg.parse(await readFile(pkgPath, "utf8"));
92
+ };
93
+ export const readPackageJsons = async (repoRoot) => {
94
+ try {
95
+ const pnpmWS = await readWorkspaceYaml(repoRoot);
96
+ return Promise.all(pnpmWS.packages.map(async (repoPath) => {
97
+ const jsonPath = join(repoRoot, repoPath, "package.json");
98
+ const content = await readPackageJsonThrow(jsonPath);
99
+ return {
100
+ jsonPath,
101
+ repoPath,
102
+ content,
103
+ modified: false,
104
+ wsName: repoPath.startsWith("packages/")
105
+ ? repoPath.substring(9)
106
+ : repoPath,
107
+ };
108
+ }));
109
+ }
110
+ catch {
111
+ return [];
112
+ }
113
+ };
114
+ export const writePackageJsons = async (entries) => Promise.all(entries.map(async (entry) => {
115
+ if (entry.modified) {
116
+ await writeFile(entry.jsonPath, JSON.stringify(entry.content, null, 2) + "\n", "utf8");
117
+ console.log(` ✅ Updated ${entry.content.name}\n`);
118
+ }
119
+ else {
120
+ console.log(` ⏭️ No workspace dependencies to fix. ${entry.content.name}\n`);
121
+ }
122
+ }));
123
+ // Build a map of package names to their versions
124
+ export const buildMapEntries = (pkgEntries) => {
125
+ const versionEntries = [];
126
+ for (const fromPkg of pkgEntries)
127
+ for (const toPkg of pkgEntries)
128
+ if (fromPkg.content.name !== toPkg.content.name)
129
+ versionEntries.push({
130
+ fromPkg,
131
+ toPkg,
132
+ relPath: relative(fromPkg.repoPath, toPkg.repoPath),
133
+ });
134
+ return versionEntries;
135
+ };
136
+ export const readPackageVersions = async (rootDir) => {
137
+ const versions = {};
138
+ const packageJsons = await readPackageJsons(rootDir);
139
+ for (const packageJson of packageJsons)
140
+ versions[packageJson.repoPath] = packageJson.content.version;
141
+ return versions;
142
+ };
143
+ export const readMinPackageVersion = async (rootDir) => {
144
+ const versions = await readPackageVersions(rootDir);
145
+ const splitVersions = Object.values(versions).map(splitVersion);
146
+ if (splitVersions.length === 0)
147
+ return null;
148
+ splitVersions.sort(compareVersions);
149
+ return concatVersion(splitVersions[0]);
150
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mxpicture/gcp-functions-code",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Tools for google cloud functions",
5
5
  "type": "module",
6
6
  "author": "MXPicture",
@@ -26,27 +26,24 @@
26
26
  "scripts": {
27
27
  "clean": "rm -rf dist .tsbuildinfo tsconfig.tsbuildinfo node_modules",
28
28
  "lint": "eslint \"src/**/*.{ts,tsx}\" --ext .ts,.tsx",
29
- "build": "tsc -b .",
30
- "test:clean": "rm -rf src/4testing/generator-test-*",
31
- "run:test": "pnpm exec tsx src/scripts/test.mts"
29
+ "build": "tsc -b ."
32
30
  },
33
31
  "publishConfig": {
34
32
  "access": "public"
35
33
  },
36
34
  "dependencies": {
37
- "@mxpicture/gcp-functions-common": "^0.2.1",
38
- "json5": "^1.0.2",
35
+ "@mxpicture/gcp-functions-common": "^0.2.3",
36
+ "json5": "^2.2.3",
39
37
  "micromatch": "^4.0.8",
40
38
  "prettier": "^3.8.1",
41
39
  "simple-git": "^3.31.1",
42
- "type-fest": "^5.4.4",
43
40
  "yaml": "^2.8.2",
44
41
  "zod": "^4.3.6"
45
42
  },
46
43
  "devDependencies": {
47
44
  "@types/micromatch": "^4.0.10",
48
- "@types/node": "^25.2.0",
49
- "eslint": "^9.39.2",
45
+ "@types/node": "^25.2.3",
46
+ "eslint": "^10.0.2",
50
47
  "typescript": "^5.9.3"
51
48
  }
52
49
  }
@@ -1 +0,0 @@
1
- export {};
@@ -1,81 +0,0 @@
1
- import { basename, dirname, join } from "node:path";
2
- import { readdir, writeFile } from "node:fs/promises";
3
- import { VSCodeWorkspace } from "../vscode/VSCodeWorkspace.js";
4
- // ─── Configuration ───────────────────────────────────────────────────────────
5
- // /** File patterns to exclude from barrel exports */
6
- const EXCLUDED_PATTERNS = [
7
- /\.test\.ts$/,
8
- /\.spec\.ts$/,
9
- /\.d\.ts$/,
10
- /index\.ts$/,
11
- ];
12
- // /** Header comment added to every generated barrel file */
13
- const HEADER = "// This file is auto-generated. Do not edit manually.\n";
14
- const isExcluded = (fileName) => EXCLUDED_PATTERNS.some((pattern) => pattern.test(fileName));
15
- // ─── Generator ───────────────────────────────────────────────────────────────
16
- const createBarrels = async () => {
17
- const rootDir = (await VSCodeWorkspace.loadAsync()).root;
18
- const packagesDir = join(rootDir, "packages");
19
- const packages = (await readdir(packagesDir)).map((p) => join(packagesDir, p));
20
- const promises = [];
21
- for (const pkg of packages) {
22
- try {
23
- const baseDirPath = join(pkg, "src");
24
- const items = (await readdir(baseDirPath, { recursive: true })).sort();
25
- let currentGroup;
26
- for (const item of items) {
27
- const filename = basename(item);
28
- if (isExcluded(filename) || !filename.endsWith(".ts"))
29
- continue;
30
- const dirPath = join(baseDirPath, dirname(item));
31
- if (dirPath !== currentGroup?.dirPath) {
32
- if (currentGroup)
33
- promises.push(persistBarrel({ ...currentGroup }));
34
- currentGroup = { dirPath, files: [] };
35
- }
36
- currentGroup.files.push(filename);
37
- }
38
- if (currentGroup)
39
- promises.push(persistBarrel({ ...currentGroup }));
40
- }
41
- catch {
42
- continue;
43
- }
44
- }
45
- await Promise.all(promises);
46
- };
47
- const persistBarrel = async (group) => {
48
- if (group.files.length === 0)
49
- return;
50
- const content = `${HEADER}${group.files
51
- .sort()
52
- .map((f) => `export * from "./${basename(f, ".ts")}.js";`)
53
- .join("\n")}\n`;
54
- const barrelPath = join(group.dirPath, "index.ts");
55
- return writeFile(barrelPath, content);
56
- };
57
- // ─── Entry Point ─────────────────────────────────────────────────────────────
58
- const run = async () => {
59
- await createBarrels();
60
- // // Allow overriding paths via CLI arguments:
61
- // // npx tsx scripts/generate-barrels.ts ./src/hooks ./src/types
62
- // const cliPaths = process.argv.slice(2);
63
- // const paths = cliPaths.length > 0 ? cliPaths : BARREL_PATHS;
64
- // console.log("🛢️ Generating barrel files...\n");
65
- // console.log(
66
- // `📂 Target directories:\n${paths
67
- // .map((p) => ` - ${resolve(p)}`)
68
- // .join("\n")}\n`,
69
- // );
70
- // let generated = 0;
71
- // for (const targetPath of paths) {
72
- // const resolved = resolve(targetPath);
73
- // if (await generateBarrel(resolved)) {
74
- // generated++;
75
- // }
76
- // }
77
- // console.log(
78
- // `\n🎉 Done! Barrels generated for ${generated}/${paths.length} directories.`,
79
- // );
80
- };
81
- run().catch(console.error);
@@ -1,3 +0,0 @@
1
- export * from "./fix-workspace-deps.js";
2
- export * from "./generate-barrels.js";
3
- export * from "./generate-changelog.js";
@@ -1,4 +0,0 @@
1
- // This file is auto-generated. Do not edit manually.
2
- export * from "./fix-workspace-deps.js";
3
- export * from "./generate-barrels.js";
4
- export * from "./generate-changelog.js";