@emeryld/manager 0.6.4 → 0.6.5
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/dist/colors-shared.js +11 -0
- package/dist/packages/manifest-utils.js +145 -0
- package/dist/sync-version.mjs +63 -0
- package/package.json +1 -1
- package/tsconfig.base.json +1 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
const DEFAULT_COLOR_PALETTE = ['cyan', 'green', 'yellow', 'magenta', 'red'];
|
|
5
|
+
const IGNORED_DIRS = new Set([
|
|
6
|
+
'node_modules',
|
|
7
|
+
'.git',
|
|
8
|
+
'.turbo',
|
|
9
|
+
'.next',
|
|
10
|
+
'dist',
|
|
11
|
+
'build',
|
|
12
|
+
'.cache',
|
|
13
|
+
'coverage',
|
|
14
|
+
]);
|
|
15
|
+
export function manifestFilePath(rootDir) {
|
|
16
|
+
return path.join(rootDir, 'scripts', 'packages.mjs');
|
|
17
|
+
}
|
|
18
|
+
export function normalizeManifestPath(rootDir, value) {
|
|
19
|
+
const absolute = path.resolve(rootDir, value || '');
|
|
20
|
+
let relative = path.relative(rootDir, absolute);
|
|
21
|
+
if (!relative)
|
|
22
|
+
return '.';
|
|
23
|
+
relative = relative.replace(/\\/g, '/');
|
|
24
|
+
return relative.replace(/^(?:\.\/)+/, '');
|
|
25
|
+
}
|
|
26
|
+
export async function findPackageJsonFiles(rootDir) {
|
|
27
|
+
const results = new Set();
|
|
28
|
+
const queue = [rootDir];
|
|
29
|
+
while (queue.length) {
|
|
30
|
+
const current = queue.shift();
|
|
31
|
+
let entries;
|
|
32
|
+
try {
|
|
33
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
if (entry.isFile() && entry.name === 'package.json') {
|
|
40
|
+
results.add(path.join(current, entry.name));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (!entry.isDirectory())
|
|
45
|
+
continue;
|
|
46
|
+
if (entry.isSymbolicLink())
|
|
47
|
+
continue;
|
|
48
|
+
if (IGNORED_DIRS.has(entry.name))
|
|
49
|
+
continue;
|
|
50
|
+
queue.push(path.join(current, entry.name));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [...results].sort();
|
|
54
|
+
}
|
|
55
|
+
export async function loadWorkspaceManifest(rootDir) {
|
|
56
|
+
const manifestPath = manifestFilePath(rootDir);
|
|
57
|
+
try {
|
|
58
|
+
const manifestModule = await import(pathToFileURL(manifestPath).href);
|
|
59
|
+
if (Array.isArray(manifestModule?.PACKAGE_MANIFEST)) {
|
|
60
|
+
return manifestModule.PACKAGE_MANIFEST;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (isManifestMissing(error))
|
|
65
|
+
return undefined;
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
export async function inferManifestFromWorkspace(rootDir) {
|
|
71
|
+
const manifest = [];
|
|
72
|
+
const pkgJsonPaths = await findPackageJsonFiles(rootDir);
|
|
73
|
+
for (const pkgJsonPath of pkgJsonPaths) {
|
|
74
|
+
try {
|
|
75
|
+
const raw = await readFile(pkgJsonPath, 'utf8');
|
|
76
|
+
const json = JSON.parse(raw);
|
|
77
|
+
const pkgDir = path.dirname(pkgJsonPath);
|
|
78
|
+
const pkgName = (json.name || path.basename(pkgDir)).trim() || 'package';
|
|
79
|
+
manifest.push({
|
|
80
|
+
name: pkgName,
|
|
81
|
+
path: normalizeManifestPath(rootDir, path.relative(rootDir, pkgDir)),
|
|
82
|
+
color: colorFromSeed(pkgName),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return manifest;
|
|
90
|
+
}
|
|
91
|
+
export function mergeManifestEntries(rootDir, inferred, overrides) {
|
|
92
|
+
const normalizedOverrides = new Map();
|
|
93
|
+
overrides?.forEach((entry) => {
|
|
94
|
+
const normalized = normalizeManifestPath(rootDir, entry.path);
|
|
95
|
+
if (!normalized)
|
|
96
|
+
return;
|
|
97
|
+
normalizedOverrides.set(normalized, { ...entry, path: normalized });
|
|
98
|
+
});
|
|
99
|
+
const merged = [];
|
|
100
|
+
for (const baseEntry of inferred) {
|
|
101
|
+
const normalized = normalizeManifestPath(rootDir, baseEntry.path);
|
|
102
|
+
const override = normalizedOverrides.get(normalized);
|
|
103
|
+
if (override) {
|
|
104
|
+
normalizedOverrides.delete(normalized);
|
|
105
|
+
const name = override.name || baseEntry.name;
|
|
106
|
+
const color = override.color ?? baseEntry.color ?? colorFromSeed(name);
|
|
107
|
+
merged.push({ name, path: normalized, color });
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
merged.push({ ...baseEntry, path: normalized });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
normalizedOverrides.forEach((entry) => {
|
|
114
|
+
const name = entry.name || path.basename(entry.path) || 'package';
|
|
115
|
+
const color = entry.color ?? colorFromSeed(name);
|
|
116
|
+
merged.push({ name, path: entry.path, color });
|
|
117
|
+
});
|
|
118
|
+
return merged;
|
|
119
|
+
}
|
|
120
|
+
export function colorFromSeed(seed) {
|
|
121
|
+
const normalized = `${seed}`.trim() || 'package';
|
|
122
|
+
let hash = 0;
|
|
123
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
124
|
+
hash = (hash * 31 + normalized.charCodeAt(i)) >>> 0;
|
|
125
|
+
}
|
|
126
|
+
return DEFAULT_COLOR_PALETTE[hash % DEFAULT_COLOR_PALETTE.length];
|
|
127
|
+
}
|
|
128
|
+
export function deriveSubstitute(name) {
|
|
129
|
+
const trimmed = (name || '').trim();
|
|
130
|
+
if (!trimmed)
|
|
131
|
+
return '';
|
|
132
|
+
const segments = trimmed.split(/[@\/\-]/).filter(Boolean);
|
|
133
|
+
const transformed = segments
|
|
134
|
+
.map((segment) => segment)
|
|
135
|
+
.filter(Boolean)
|
|
136
|
+
.join(' ');
|
|
137
|
+
return transformed || trimmed;
|
|
138
|
+
}
|
|
139
|
+
function isManifestMissing(error) {
|
|
140
|
+
if (typeof error !== 'object' || error === null)
|
|
141
|
+
return false;
|
|
142
|
+
const code = error.code;
|
|
143
|
+
return code === 'ERR_MODULE_NOT_FOUND' || code === 'ENOENT';
|
|
144
|
+
}
|
|
145
|
+
export { DEFAULT_COLOR_PALETTE };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { colorFromSeed, inferManifestFromWorkspace, loadWorkspaceManifest, mergeManifestEntries, } from './packages/manifest-utils.js';
|
|
6
|
+
import { colors } from './colors-shared.js';
|
|
7
|
+
const rootDir = process.cwd();
|
|
8
|
+
const registerPackageEntry = (map, pkgPath, entry) => {
|
|
9
|
+
if (!map.has(pkgPath)) {
|
|
10
|
+
map.set(pkgPath, entry);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
async function collectPackageEntries() {
|
|
14
|
+
const records = new Map();
|
|
15
|
+
const [workspaceManifest, inferred] = await Promise.all([
|
|
16
|
+
loadWorkspaceManifest(rootDir),
|
|
17
|
+
inferManifestFromWorkspace(rootDir),
|
|
18
|
+
]);
|
|
19
|
+
const manifestEntries = mergeManifestEntries(rootDir, inferred, workspaceManifest);
|
|
20
|
+
for (const manifestEntry of manifestEntries) {
|
|
21
|
+
if (!manifestEntry.path)
|
|
22
|
+
continue;
|
|
23
|
+
const pkgPath = path.join(rootDir, manifestEntry.path, 'package.json');
|
|
24
|
+
registerPackageEntry(records, pkgPath, {
|
|
25
|
+
name: manifestEntry.name,
|
|
26
|
+
pkgPath,
|
|
27
|
+
color: manifestEntry.color ?? colorFromSeed(manifestEntry.name),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return Array.from(records.values());
|
|
31
|
+
}
|
|
32
|
+
async function main() {
|
|
33
|
+
const version = process.argv[2];
|
|
34
|
+
if (!version) {
|
|
35
|
+
console.error('Usage: pnpm sync <version>');
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const semverPattern = /^\d+\.\d+\.\d+(?:[-+].*)?$/;
|
|
40
|
+
if (!semverPattern.test(version)) {
|
|
41
|
+
console.error(`Invalid version "${version}". Expected semver (e.g. 1.6.0).`);
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const packageEntries = await collectPackageEntries();
|
|
46
|
+
await Promise.all(packageEntries.map(async (entry) => {
|
|
47
|
+
if (!existsSync(entry.pkgPath)) {
|
|
48
|
+
console.warn(`${colors[entry.color ?? 'cyan']('●')} ${colors.yellow(`Skipping missing package file: ${path.relative(rootDir, entry.pkgPath)}`)}`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const raw = await readFile(entry.pkgPath, 'utf8');
|
|
52
|
+
const json = JSON.parse(raw);
|
|
53
|
+
const previousVersion = json.version ?? 'unknown';
|
|
54
|
+
json.version = version;
|
|
55
|
+
const formatted = `${JSON.stringify(json, null, 2)}\n`;
|
|
56
|
+
await writeFile(entry.pkgPath, formatted);
|
|
57
|
+
console.log(`${colors[entry.color ?? 'cyan']('●')} ${colors.cyan('[sync]')} ${path.relative(rootDir, entry.pkgPath)} ${colors.dim(`${previousVersion} -> ${version}`)}`);
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
main().catch((error) => {
|
|
61
|
+
console.error(error);
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
});
|
package/package.json
CHANGED