@codefast/cli 0.3.12 → 0.3.13-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -51
- package/dist/{bin.js → bin.mjs} +4 -1
- package/dist/commands/arrange.mjs +107 -0
- package/dist/commands/mirror.mjs +66 -0
- package/dist/commands/tag.mjs +58 -0
- package/dist/lib/arrange/application/analyze.mjs +92 -0
- package/dist/lib/arrange/application/group-file.mjs +113 -0
- package/dist/lib/arrange/application/run-target.mjs +65 -0
- package/dist/lib/arrange/domain/ast/ast-helpers.mjs +115 -0
- package/dist/lib/arrange/domain/ast/collectors-cn.mjs +61 -0
- package/dist/lib/arrange/domain/ast/collectors-jsx.mjs +20 -0
- package/dist/lib/arrange/domain/ast/collectors-tv.mjs +170 -0
- package/dist/lib/arrange/domain/ast/targets.mjs +106 -0
- package/dist/lib/arrange/domain/constants.mjs +169 -0
- package/dist/lib/arrange/domain/errors.mjs +19 -0
- package/dist/lib/arrange/domain/grouping.mjs +235 -0
- package/dist/lib/arrange/domain/imports.mjs +54 -0
- package/dist/lib/arrange/domain/tokenizer.mjs +216 -0
- package/dist/lib/arrange/index.mjs +15 -0
- package/dist/lib/arrange/infra/walk.mjs +33 -0
- package/dist/lib/arrange/presentation/formatters.mjs +52 -0
- package/dist/lib/arrange/presentation/report.mjs +24 -0
- package/dist/lib/config/domain/schema.mjs +25 -0
- package/dist/lib/config/index.mjs +3 -0
- package/dist/lib/config/infra/loader.mjs +78 -0
- package/dist/lib/infra/caught-unknown-message.mjs +20 -0
- package/dist/lib/infra/config-reporter.mjs +10 -0
- package/dist/lib/infra/node-io.mjs +30 -0
- package/dist/lib/infra/workspace/repo-root.mjs +22 -0
- package/dist/lib/mirror/application/engine.mjs +248 -0
- package/dist/lib/mirror/application/sync.mjs +162 -0
- package/dist/lib/mirror/domain/constants.mjs +12 -0
- package/dist/lib/mirror/domain/errors.mjs +15 -0
- package/dist/lib/mirror/domain/types.mjs +1 -0
- package/dist/lib/mirror/index.mjs +2 -0
- package/dist/lib/mirror/infra/dirent-list.mjs +14 -0
- package/dist/lib/mirror/infra/package-filter.mjs +26 -0
- package/dist/lib/mirror/infra/path-normalizer.mjs +7 -0
- package/dist/lib/mirror/infra/update-pkg.mjs +105 -0
- package/dist/lib/mirror/infra/workspace-packages.mjs +159 -0
- package/dist/lib/mirror/presentation/reporter.mjs +111 -0
- package/dist/lib/tag/application/engine.mjs +287 -0
- package/dist/lib/tag/domain/types.mjs +1 -0
- package/dist/lib/tag/index.mjs +4 -0
- package/dist/lib/tag/infra/target-resolver.mjs +170 -0
- package/dist/lib/tag/presentation/colors.mjs +12 -0
- package/dist/lib/tag/presentation/tag-presenter.mjs +63 -0
- package/dist/program.mjs +34 -0
- package/package.json +11 -11
- package/dist/bin.d.ts +0 -3
- package/dist/bin.d.ts.map +0 -1
- package/dist/commands/arrange.d.ts +0 -3
- package/dist/commands/arrange.d.ts.map +0 -1
- package/dist/commands/arrange.js +0 -114
- package/dist/commands/mirror.d.ts +0 -4
- package/dist/commands/mirror.d.ts.map +0 -1
- package/dist/commands/mirror.js +0 -63
- package/dist/lib/arrange/analyze.d.ts +0 -4
- package/dist/lib/arrange/analyze.d.ts.map +0 -1
- package/dist/lib/arrange/analyze.js +0 -105
- package/dist/lib/arrange/ast/collectors-cn.d.ts +0 -10
- package/dist/lib/arrange/ast/collectors-cn.d.ts.map +0 -1
- package/dist/lib/arrange/ast/collectors-cn.js +0 -84
- package/dist/lib/arrange/ast/collectors-jsx.d.ts +0 -4
- package/dist/lib/arrange/ast/collectors-jsx.d.ts.map +0 -1
- package/dist/lib/arrange/ast/collectors-jsx.js +0 -18
- package/dist/lib/arrange/ast/collectors-tv.d.ts +0 -13
- package/dist/lib/arrange/ast/collectors-tv.d.ts.map +0 -1
- package/dist/lib/arrange/ast/collectors-tv.js +0 -273
- package/dist/lib/arrange/ast/targets.d.ts +0 -8
- package/dist/lib/arrange/ast/targets.d.ts.map +0 -1
- package/dist/lib/arrange/ast/targets.js +0 -143
- package/dist/lib/arrange/ast/utils.d.ts +0 -36
- package/dist/lib/arrange/ast/utils.d.ts.map +0 -1
- package/dist/lib/arrange/ast/utils.js +0 -138
- package/dist/lib/arrange/constants.d.ts +0 -68
- package/dist/lib/arrange/constants.d.ts.map +0 -1
- package/dist/lib/arrange/constants.js +0 -179
- package/dist/lib/arrange/errors.d.ts +0 -14
- package/dist/lib/arrange/errors.d.ts.map +0 -1
- package/dist/lib/arrange/errors.js +0 -16
- package/dist/lib/arrange/formatters.d.ts +0 -16
- package/dist/lib/arrange/formatters.d.ts.map +0 -1
- package/dist/lib/arrange/formatters.js +0 -57
- package/dist/lib/arrange/group-file.d.ts +0 -4
- package/dist/lib/arrange/group-file.d.ts.map +0 -1
- package/dist/lib/arrange/group-file.js +0 -115
- package/dist/lib/arrange/grouping.d.ts +0 -41
- package/dist/lib/arrange/grouping.d.ts.map +0 -1
- package/dist/lib/arrange/grouping.js +0 -280
- package/dist/lib/arrange/imports.d.ts +0 -6
- package/dist/lib/arrange/imports.d.ts.map +0 -1
- package/dist/lib/arrange/imports.js +0 -74
- package/dist/lib/arrange/report.d.ts +0 -4
- package/dist/lib/arrange/report.d.ts.map +0 -1
- package/dist/lib/arrange/report.js +0 -37
- package/dist/lib/arrange/run-target.d.ts +0 -4
- package/dist/lib/arrange/run-target.d.ts.map +0 -1
- package/dist/lib/arrange/run-target.js +0 -28
- package/dist/lib/arrange/tokenizer.d.ts +0 -37
- package/dist/lib/arrange/tokenizer.d.ts.map +0 -1
- package/dist/lib/arrange/tokenizer.js +0 -390
- package/dist/lib/arrange/types.d.ts +0 -104
- package/dist/lib/arrange/types.d.ts.map +0 -1
- package/dist/lib/arrange/types.js +0 -7
- package/dist/lib/arrange/walk.d.ts +0 -4
- package/dist/lib/arrange/walk.d.ts.map +0 -1
- package/dist/lib/arrange/walk.js +0 -34
- package/dist/lib/arrange.d.ts +0 -41
- package/dist/lib/arrange.d.ts.map +0 -1
- package/dist/lib/arrange.js +0 -38
- package/dist/lib/infra/fs-contract.d.ts +0 -28
- package/dist/lib/infra/fs-contract.d.ts.map +0 -1
- package/dist/lib/infra/node-io.d.ts +0 -4
- package/dist/lib/infra/node-io.d.ts.map +0 -1
- package/dist/lib/infra/node-io.js +0 -27
- package/dist/lib/mirror/config.d.ts +0 -7
- package/dist/lib/mirror/config.d.ts.map +0 -1
- package/dist/lib/mirror/config.js +0 -146
- package/dist/lib/mirror/constants.d.ts +0 -10
- package/dist/lib/mirror/constants.d.ts.map +0 -1
- package/dist/lib/mirror/constants.js +0 -13
- package/dist/lib/mirror/engine.d.ts +0 -12
- package/dist/lib/mirror/engine.d.ts.map +0 -1
- package/dist/lib/mirror/engine.js +0 -248
- package/dist/lib/mirror/errors.d.ts +0 -10
- package/dist/lib/mirror/errors.d.ts.map +0 -1
- package/dist/lib/mirror/errors.js +0 -12
- package/dist/lib/mirror/package-filter.d.ts +0 -8
- package/dist/lib/mirror/package-filter.d.ts.map +0 -1
- package/dist/lib/mirror/package-filter.js +0 -33
- package/dist/lib/mirror/reporter.d.ts +0 -24
- package/dist/lib/mirror/reporter.d.ts.map +0 -1
- package/dist/lib/mirror/reporter.js +0 -126
- package/dist/lib/mirror/sync.d.ts +0 -4
- package/dist/lib/mirror/sync.d.ts.map +0 -1
- package/dist/lib/mirror/sync.js +0 -135
- package/dist/lib/mirror/types.d.ts +0 -81
- package/dist/lib/mirror/types.d.ts.map +0 -1
- package/dist/lib/mirror/update-pkg.d.ts +0 -13
- package/dist/lib/mirror/update-pkg.d.ts.map +0 -1
- package/dist/lib/mirror/update-pkg.js +0 -39
- package/dist/lib/mirror/workspace-packages.d.ts +0 -26
- package/dist/lib/mirror/workspace-packages.d.ts.map +0 -1
- package/dist/lib/mirror/workspace-packages.js +0 -160
- package/dist/lib/mirror.d.ts +0 -7
- package/dist/lib/mirror.d.ts.map +0 -1
- package/dist/lib/mirror.js +0 -5
- package/dist/lib/repo-root.d.ts +0 -7
- package/dist/lib/repo-root.d.ts.map +0 -1
- package/dist/lib/repo-root.js +0 -23
- package/dist/lib/shared/utils.d.ts +0 -9
- package/dist/lib/shared/utils.d.ts.map +0 -1
- package/dist/lib/shared/utils.js +0 -12
- package/dist/program.d.ts +0 -4
- package/dist/program.d.ts.map +0 -1
- package/dist/program.js +0 -38
- /package/dist/lib/{infra/fs-contract.js → arrange/domain/types.mjs} +0 -0
- /package/dist/lib/{mirror/types.js → infra/fs-contract.mjs} +0 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { DTS_EXTENSION, PACKAGE_JSON_EXPORT, VALID_JS_EXTENSIONS } from "../domain/constants.mjs";
|
|
2
|
+
import { isDirentList } from "../infra/dirent-list.mjs";
|
|
3
|
+
import { normalizePath } from "../infra/path-normalizer.mjs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
//#region src/lib/mirror/application/engine.ts
|
|
6
|
+
function resolvePackageScopedConfig(configMap, pkgMeta) {
|
|
7
|
+
if (!configMap) return;
|
|
8
|
+
return configMap[pkgMeta.packageName];
|
|
9
|
+
}
|
|
10
|
+
async function scanDirectoryFiles(fs, dir, baseDir = dir) {
|
|
11
|
+
try {
|
|
12
|
+
const raw = await fs.readdir(dir, {
|
|
13
|
+
recursive: true,
|
|
14
|
+
withFileTypes: true
|
|
15
|
+
});
|
|
16
|
+
if (!isDirentList(raw)) return [];
|
|
17
|
+
return raw.filter((dirent) => dirent.isFile()).map((dirent) => {
|
|
18
|
+
const fullPath = path.join(dirent.parentPath, dirent.name);
|
|
19
|
+
return normalizePath(path.relative(baseDir, fullPath));
|
|
20
|
+
});
|
|
21
|
+
} catch (caughtError) {
|
|
22
|
+
if (caughtError && typeof caughtError === "object" && "code" in caughtError) {
|
|
23
|
+
if (caughtError.code === "ENOENT" || caughtError.code === "EACCES") return [];
|
|
24
|
+
}
|
|
25
|
+
throw caughtError;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function isDirectoryCssOnly(fs, distDir, dirPath) {
|
|
29
|
+
try {
|
|
30
|
+
const raw = await fs.readdir(path.join(distDir, dirPath), { withFileTypes: true });
|
|
31
|
+
if (!isDirentList(raw)) return false;
|
|
32
|
+
if (raw.length === 0) return true;
|
|
33
|
+
return raw.every((dirent) => dirent.isFile() && dirent.name.endsWith(".css"));
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function groupFilesByModule(files) {
|
|
39
|
+
const modules = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
let ext;
|
|
42
|
+
let modulePath;
|
|
43
|
+
if (file.endsWith(".d.ts")) {
|
|
44
|
+
ext = DTS_EXTENSION;
|
|
45
|
+
modulePath = file.slice(0, -DTS_EXTENSION.length);
|
|
46
|
+
} else {
|
|
47
|
+
ext = path.extname(file);
|
|
48
|
+
if (!VALID_JS_EXTENSIONS.has(ext)) continue;
|
|
49
|
+
modulePath = file.slice(0, -ext.length);
|
|
50
|
+
}
|
|
51
|
+
if (!path.basename(modulePath)) continue;
|
|
52
|
+
let distModule = modules.get(modulePath);
|
|
53
|
+
if (!distModule) {
|
|
54
|
+
distModule = {
|
|
55
|
+
path: modulePath,
|
|
56
|
+
files: {
|
|
57
|
+
js: null,
|
|
58
|
+
mjs: null,
|
|
59
|
+
cjs: null,
|
|
60
|
+
dts: null
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
modules.set(modulePath, distModule);
|
|
64
|
+
}
|
|
65
|
+
if (ext === ".js") distModule.files.js = file;
|
|
66
|
+
else if (ext === ".mjs") distModule.files.mjs = file;
|
|
67
|
+
else if (ext === ".cjs") distModule.files.cjs = file;
|
|
68
|
+
else if (ext === ".d.ts") distModule.files.dts = file;
|
|
69
|
+
}
|
|
70
|
+
return modules;
|
|
71
|
+
}
|
|
72
|
+
function toExportPath(distPath) {
|
|
73
|
+
if (distPath === "index") return ".";
|
|
74
|
+
if (distPath.endsWith("/index")) return `./${distPath.slice(0, -6)}`;
|
|
75
|
+
return `./${distPath}`;
|
|
76
|
+
}
|
|
77
|
+
const GROUP_ORDER = {
|
|
78
|
+
components: 100,
|
|
79
|
+
hooks: 200,
|
|
80
|
+
primitives: 300,
|
|
81
|
+
core: 400,
|
|
82
|
+
loaders: 500,
|
|
83
|
+
utils: 600,
|
|
84
|
+
adapters: 700,
|
|
85
|
+
script: 750,
|
|
86
|
+
css: 900
|
|
87
|
+
};
|
|
88
|
+
function getExportGroup(exportPath, pathTransform) {
|
|
89
|
+
if (exportPath === ".") return [
|
|
90
|
+
".",
|
|
91
|
+
"",
|
|
92
|
+
0,
|
|
93
|
+
0
|
|
94
|
+
];
|
|
95
|
+
const cleanPath = exportPath.startsWith("./") ? exportPath.slice(2) : exportPath;
|
|
96
|
+
if (cleanPath.endsWith("/*")) {
|
|
97
|
+
const dirName = cleanPath.slice(0, -2);
|
|
98
|
+
return [
|
|
99
|
+
dirName,
|
|
100
|
+
"",
|
|
101
|
+
dirName === "css" ? 900 : 100,
|
|
102
|
+
0
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
const parts = cleanPath.split("/");
|
|
106
|
+
if (parts.length === 1) {
|
|
107
|
+
const name = parts[0];
|
|
108
|
+
if (name in GROUP_ORDER) return [
|
|
109
|
+
name,
|
|
110
|
+
"",
|
|
111
|
+
GROUP_ORDER[name] ?? 0,
|
|
112
|
+
0
|
|
113
|
+
];
|
|
114
|
+
if (pathTransform) return [
|
|
115
|
+
"components",
|
|
116
|
+
name,
|
|
117
|
+
100,
|
|
118
|
+
1
|
|
119
|
+
];
|
|
120
|
+
return [
|
|
121
|
+
"",
|
|
122
|
+
name,
|
|
123
|
+
800,
|
|
124
|
+
1
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
const group = parts[0] ?? "";
|
|
128
|
+
return [
|
|
129
|
+
group,
|
|
130
|
+
parts.slice(1).join("/"),
|
|
131
|
+
GROUP_ORDER[group] ?? 700,
|
|
132
|
+
1
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
function createPathTransform(config, pkgMeta) {
|
|
136
|
+
const pathConfig = resolvePackageScopedConfig(config?.pathTransformations, pkgMeta);
|
|
137
|
+
if (!pathConfig) return null;
|
|
138
|
+
const { removePrefix } = pathConfig;
|
|
139
|
+
if (!removePrefix) return null;
|
|
140
|
+
return (exportPath) => {
|
|
141
|
+
if (!exportPath.startsWith(removePrefix)) return exportPath;
|
|
142
|
+
const result = exportPath.slice(removePrefix.length);
|
|
143
|
+
if (result && result !== "." && !result.startsWith("./")) return `./${result}`;
|
|
144
|
+
return result;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function getSortTuple(exportPath, pathTransform) {
|
|
148
|
+
const [group, subpath, order, isIndex] = getExportGroup(exportPath, pathTransform);
|
|
149
|
+
return [
|
|
150
|
+
order,
|
|
151
|
+
group,
|
|
152
|
+
isIndex,
|
|
153
|
+
subpath
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
function compareTuples(left, right) {
|
|
157
|
+
if (left[0] !== right[0]) return left[0] - right[0];
|
|
158
|
+
if (left[1] !== right[1]) return left[1] < right[1] ? -1 : 1;
|
|
159
|
+
if (left[2] !== right[2]) return left[2] - right[2];
|
|
160
|
+
if (left[3] !== right[3]) return left[3] < right[3] ? -1 : 1;
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
async function generateCssExports(fs, distDir, cssConfig) {
|
|
164
|
+
if (cssConfig === false) return {};
|
|
165
|
+
if (cssConfig === true) cssConfig = { enabled: true };
|
|
166
|
+
if (!cssConfig || cssConfig.enabled === false) return {};
|
|
167
|
+
const cssFiles = (await scanDirectoryFiles(fs, distDir)).filter((relativeDistPath) => relativeDistPath.endsWith(".css"));
|
|
168
|
+
if (!cssFiles.length) return {};
|
|
169
|
+
const cssExports = { ...cssConfig.customExports };
|
|
170
|
+
const cssByDir = /* @__PURE__ */ new Map();
|
|
171
|
+
const rootCss = [];
|
|
172
|
+
for (const file of cssFiles) {
|
|
173
|
+
const dirName = normalizePath(path.dirname(file));
|
|
174
|
+
if (dirName === ".") rootCss.push(file);
|
|
175
|
+
else {
|
|
176
|
+
const arr = cssByDir.get(dirName) || [];
|
|
177
|
+
arr.push(file);
|
|
178
|
+
cssByDir.set(dirName, arr);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const file of rootCss) {
|
|
182
|
+
const exportPath = `./${file}`;
|
|
183
|
+
if (!(exportPath in cssExports)) cssExports[exportPath] = `./dist/${file}`;
|
|
184
|
+
}
|
|
185
|
+
for (const [dirName, dirFiles] of cssByDir.entries()) if (await isDirectoryCssOnly(fs, distDir, dirName) && !cssConfig.forceExportFiles) {
|
|
186
|
+
const wildcardExport = `./${dirName}/*`;
|
|
187
|
+
if (!(wildcardExport in cssExports)) cssExports[wildcardExport] = `./dist/${dirName}/*`;
|
|
188
|
+
} else for (const file of dirFiles) {
|
|
189
|
+
const exportPath = `./${file}`;
|
|
190
|
+
if (!(exportPath in cssExports)) cssExports[exportPath] = `./dist/${file}`;
|
|
191
|
+
}
|
|
192
|
+
return cssExports;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Compute `package.json#exports` from a built `dist/` tree. No logging; no writes.
|
|
196
|
+
*/
|
|
197
|
+
async function generateExports(fs, distDir, pathTransform, cssConfig, customExports) {
|
|
198
|
+
const files = await scanDirectoryFiles(fs, distDir);
|
|
199
|
+
if (!files.length) return {
|
|
200
|
+
exports: { [PACKAGE_JSON_EXPORT]: PACKAGE_JSON_EXPORT },
|
|
201
|
+
originalPathBySpecifier: { [PACKAGE_JSON_EXPORT]: PACKAGE_JSON_EXPORT },
|
|
202
|
+
jsCount: 0,
|
|
203
|
+
cssCount: 0
|
|
204
|
+
};
|
|
205
|
+
const modules = groupFilesByModule(files);
|
|
206
|
+
const validModules = Array.from(modules.values()).filter((moduleEntry) => (moduleEntry.files.js || moduleEntry.files.mjs) && moduleEntry.files.dts);
|
|
207
|
+
if (!validModules.length) return {
|
|
208
|
+
exports: { [PACKAGE_JSON_EXPORT]: PACKAGE_JSON_EXPORT },
|
|
209
|
+
originalPathBySpecifier: { [PACKAGE_JSON_EXPORT]: PACKAGE_JSON_EXPORT },
|
|
210
|
+
jsCount: 0,
|
|
211
|
+
cssCount: 0
|
|
212
|
+
};
|
|
213
|
+
const exportsObj = {};
|
|
214
|
+
const originalPathBySpecifier = {};
|
|
215
|
+
for (const distModuleEntry of validModules) {
|
|
216
|
+
const originalExportPath = toExportPath(distModuleEntry.path);
|
|
217
|
+
let exportPath = originalExportPath;
|
|
218
|
+
if (pathTransform) exportPath = pathTransform(exportPath);
|
|
219
|
+
const distPath = `./dist/${distModuleEntry.path}`;
|
|
220
|
+
const entry = { types: `${distPath}.d.ts` };
|
|
221
|
+
if (distModuleEntry.files.mjs) entry.import = `${distPath}.mjs`;
|
|
222
|
+
else if (distModuleEntry.files.js) entry.import = `${distPath}.js`;
|
|
223
|
+
if (distModuleEntry.files.cjs) entry.require = `${distPath}.cjs`;
|
|
224
|
+
exportsObj[exportPath] = entry;
|
|
225
|
+
originalPathBySpecifier[exportPath] = originalExportPath;
|
|
226
|
+
}
|
|
227
|
+
let sortedKeys = Object.keys(exportsObj).sort((pathA, pathB) => compareTuples(getSortTuple(pathA, pathTransform), getSortTuple(pathB, pathTransform)));
|
|
228
|
+
const sortedExports = {};
|
|
229
|
+
for (const exportKey of sortedKeys) sortedExports[exportKey] = exportsObj[exportKey];
|
|
230
|
+
const cssExports = await generateCssExports(fs, distDir, cssConfig ?? { enabled: true });
|
|
231
|
+
Object.assign(sortedExports, cssExports);
|
|
232
|
+
for (const [specifier, mappedPath] of Object.entries(customExports || {})) if (specifier !== "./package.json") sortedExports[specifier] = mappedPath;
|
|
233
|
+
for (const cssSpecifier of Object.keys(cssExports)) if (!(cssSpecifier in originalPathBySpecifier)) originalPathBySpecifier[cssSpecifier] = cssSpecifier;
|
|
234
|
+
for (const customSpecifier of Object.keys(customExports || {})) if (customSpecifier !== "./package.json" && !(customSpecifier in originalPathBySpecifier)) originalPathBySpecifier[customSpecifier] = customSpecifier;
|
|
235
|
+
sortedKeys = Object.keys(sortedExports).filter((exportKey) => exportKey !== PACKAGE_JSON_EXPORT).sort((pathA, pathB) => compareTuples(getSortTuple(pathA, pathTransform), getSortTuple(pathB, pathTransform)));
|
|
236
|
+
const finalExports = {};
|
|
237
|
+
for (const exportKey of sortedKeys) finalExports[exportKey] = sortedExports[exportKey];
|
|
238
|
+
finalExports[PACKAGE_JSON_EXPORT] = PACKAGE_JSON_EXPORT;
|
|
239
|
+
originalPathBySpecifier[PACKAGE_JSON_EXPORT] = PACKAGE_JSON_EXPORT;
|
|
240
|
+
return {
|
|
241
|
+
exports: finalExports,
|
|
242
|
+
originalPathBySpecifier,
|
|
243
|
+
jsCount: Object.keys(exportsObj).length,
|
|
244
|
+
cssCount: Object.keys(cssExports).length
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
export { createPathTransform, generateExports };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { messageFromCaughtUnknown } from "../../infra/caught-unknown-message.mjs";
|
|
2
|
+
import { DIST_DIR, PACKAGE_JSON } from "../domain/constants.mjs";
|
|
3
|
+
import { createPathTransform, generateExports } from "./engine.mjs";
|
|
4
|
+
import { resolvePackageFilterUnderRoot } from "../infra/package-filter.mjs";
|
|
5
|
+
import { configureMirrorColors, logPackageError, logPackageSuccess, logPrunedStaleExport, logSkippedWorkspacePackage, mirrorBanner, mirrorFatalError, mirrorNoPackages, mirrorProcessingMode, mirrorSummary, mirrorSummarySeparator } from "../presentation/reporter.mjs";
|
|
6
|
+
import { resolvePackageDisplayName, writePackageJsonExportsAtomic } from "../infra/update-pkg.mjs";
|
|
7
|
+
import { findWorkspacePackageRelPaths } from "../infra/workspace-packages.mjs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
//#region src/lib/mirror/application/sync.ts
|
|
10
|
+
function resolvePackageScopedConfig(configMap, pkgMeta) {
|
|
11
|
+
if (!configMap) return;
|
|
12
|
+
return configMap[pkgMeta.packageName];
|
|
13
|
+
}
|
|
14
|
+
function isPackageSkipped(skipPackagesArray, pkgMeta) {
|
|
15
|
+
if (!skipPackagesArray) return false;
|
|
16
|
+
return skipPackagesArray.includes(pkgMeta.packageName);
|
|
17
|
+
}
|
|
18
|
+
async function syncExportsForWorkspacePackage(fs, logger, rootDir, packagePathStr, index, total, config, verbose, stats) {
|
|
19
|
+
const packageDir = path.resolve(rootDir, packagePathStr);
|
|
20
|
+
const distDir = path.join(packageDir, DIST_DIR);
|
|
21
|
+
const packageJsonPath = path.join(packageDir, PACKAGE_JSON);
|
|
22
|
+
const folderBasename = path.basename(packageDir);
|
|
23
|
+
const pkgStats = {
|
|
24
|
+
name: folderBasename,
|
|
25
|
+
path: packageDir,
|
|
26
|
+
jsModules: 0,
|
|
27
|
+
cssExports: 0,
|
|
28
|
+
customExports: 0,
|
|
29
|
+
totalExports: 0,
|
|
30
|
+
hasTransform: false,
|
|
31
|
+
cssConfigStatus: "",
|
|
32
|
+
skipped: false,
|
|
33
|
+
skipReason: "",
|
|
34
|
+
error: null
|
|
35
|
+
};
|
|
36
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
37
|
+
pkgStats.skipped = true;
|
|
38
|
+
pkgStats.skipReason = "package.json not found";
|
|
39
|
+
stats.packagesSkipped++;
|
|
40
|
+
logSkippedWorkspacePackage(logger, index, total, folderBasename, pkgStats.skipReason);
|
|
41
|
+
return pkgStats;
|
|
42
|
+
}
|
|
43
|
+
let packageJsonParseError;
|
|
44
|
+
try {
|
|
45
|
+
const pkgContent = await fs.readFile(packageJsonPath, "utf8");
|
|
46
|
+
const raw = JSON.parse(pkgContent);
|
|
47
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new SyntaxError("package.json root must be a JSON object");
|
|
48
|
+
pkgStats.name = resolvePackageDisplayName(raw, folderBasename);
|
|
49
|
+
} catch (caughtError) {
|
|
50
|
+
pkgStats.name = folderBasename;
|
|
51
|
+
packageJsonParseError = caughtError;
|
|
52
|
+
}
|
|
53
|
+
const pkgMeta = { packageName: pkgStats.name };
|
|
54
|
+
if (isPackageSkipped(config.skipPackages, pkgMeta)) {
|
|
55
|
+
pkgStats.skipped = true;
|
|
56
|
+
pkgStats.skipReason = "configured to skip";
|
|
57
|
+
stats.packagesSkipped++;
|
|
58
|
+
logSkippedWorkspacePackage(logger, index, total, pkgStats.name, pkgStats.skipReason);
|
|
59
|
+
return pkgStats;
|
|
60
|
+
}
|
|
61
|
+
if (packageJsonParseError !== void 0) {
|
|
62
|
+
if (!fs.existsSync(distDir)) {
|
|
63
|
+
pkgStats.skipped = true;
|
|
64
|
+
pkgStats.skipReason = "dist/ not found";
|
|
65
|
+
stats.packagesSkipped++;
|
|
66
|
+
logSkippedWorkspacePackage(logger, index, total, pkgStats.name, pkgStats.skipReason);
|
|
67
|
+
return pkgStats;
|
|
68
|
+
}
|
|
69
|
+
pkgStats.error = messageFromCaughtUnknown(packageJsonParseError);
|
|
70
|
+
stats.packagesErrored++;
|
|
71
|
+
logPackageError(logger, index, total, pkgStats.name, packageJsonParseError, verbose);
|
|
72
|
+
return pkgStats;
|
|
73
|
+
}
|
|
74
|
+
if (!fs.existsSync(distDir)) {
|
|
75
|
+
pkgStats.skipped = true;
|
|
76
|
+
pkgStats.skipReason = "dist/ not found";
|
|
77
|
+
stats.packagesSkipped++;
|
|
78
|
+
logSkippedWorkspacePackage(logger, index, total, pkgStats.name, pkgStats.skipReason);
|
|
79
|
+
return pkgStats;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const pathTransform = createPathTransform(config, pkgMeta);
|
|
83
|
+
pkgStats.hasTransform = !!pathTransform;
|
|
84
|
+
const cssConfig = resolvePackageScopedConfig(config.cssExports, pkgMeta);
|
|
85
|
+
if (cssConfig === false) pkgStats.cssConfigStatus = "disabled";
|
|
86
|
+
else if (cssConfig !== void 0) pkgStats.cssConfigStatus = "configured";
|
|
87
|
+
const customExports = resolvePackageScopedConfig(config.customExports, pkgMeta) || {};
|
|
88
|
+
const generatedExports = await generateExports(fs, distDir, pathTransform, cssConfig, customExports);
|
|
89
|
+
const { prunedKeys } = await writePackageJsonExportsAtomic(fs, packageJsonPath, {
|
|
90
|
+
generatedExports: generatedExports.exports,
|
|
91
|
+
managedExportSpecifiers: Object.keys(generatedExports.exports),
|
|
92
|
+
originalPathBySpecifier: generatedExports.originalPathBySpecifier
|
|
93
|
+
});
|
|
94
|
+
for (const exportSpecifier of prunedKeys) logPrunedStaleExport(logger, exportSpecifier);
|
|
95
|
+
pkgStats.jsModules = generatedExports.jsCount;
|
|
96
|
+
pkgStats.cssExports = generatedExports.cssCount;
|
|
97
|
+
pkgStats.customExports = Object.keys(customExports).length;
|
|
98
|
+
pkgStats.totalExports = Object.keys(generatedExports.exports).length;
|
|
99
|
+
stats.packagesProcessed++;
|
|
100
|
+
stats.totalExports += pkgStats.totalExports;
|
|
101
|
+
stats.totalJsModules += pkgStats.jsModules;
|
|
102
|
+
stats.totalCssExports += pkgStats.cssExports;
|
|
103
|
+
logPackageSuccess(logger, index, total, pkgStats, generatedExports, verbose);
|
|
104
|
+
} catch (caughtError) {
|
|
105
|
+
pkgStats.error = messageFromCaughtUnknown(caughtError);
|
|
106
|
+
stats.packagesErrored++;
|
|
107
|
+
logPackageError(logger, index, total, pkgStats.name, caughtError, verbose);
|
|
108
|
+
}
|
|
109
|
+
return pkgStats;
|
|
110
|
+
}
|
|
111
|
+
/** @returns Process exit code (0 or 1). */
|
|
112
|
+
async function runMirrorSync(opts) {
|
|
113
|
+
const { fs, logger } = opts;
|
|
114
|
+
const config = opts.config ?? {};
|
|
115
|
+
configureMirrorColors(!!opts.noColor);
|
|
116
|
+
mirrorBanner(logger);
|
|
117
|
+
const startTime = performance.now();
|
|
118
|
+
const verbose = !!opts.verbose;
|
|
119
|
+
try {
|
|
120
|
+
const stats = {
|
|
121
|
+
packagesFound: 0,
|
|
122
|
+
packagesProcessed: 0,
|
|
123
|
+
packagesSkipped: 0,
|
|
124
|
+
packagesErrored: 0,
|
|
125
|
+
totalExports: 0,
|
|
126
|
+
totalJsModules: 0,
|
|
127
|
+
totalCssExports: 0,
|
|
128
|
+
packageDetails: []
|
|
129
|
+
};
|
|
130
|
+
let targetPackages = [];
|
|
131
|
+
if (opts.packageFilter) {
|
|
132
|
+
targetPackages = [resolvePackageFilterUnderRoot(opts.rootDir, opts.packageFilter)];
|
|
133
|
+
mirrorProcessingMode(logger, { kind: "single" });
|
|
134
|
+
} else {
|
|
135
|
+
const { relPaths, multiSource } = await findWorkspacePackageRelPaths(opts.rootDir, fs, logger);
|
|
136
|
+
targetPackages = relPaths;
|
|
137
|
+
mirrorProcessingMode(logger, {
|
|
138
|
+
kind: "multi",
|
|
139
|
+
source: multiSource
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
stats.packagesFound = targetPackages.length;
|
|
143
|
+
if (targetPackages.length === 0) {
|
|
144
|
+
mirrorNoPackages(logger);
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
let nextPackageOrdinal = 1;
|
|
148
|
+
for (const pkgPath of targetPackages) {
|
|
149
|
+
const pkgStats = await syncExportsForWorkspacePackage(fs, logger, opts.rootDir, pkgPath, nextPackageOrdinal++, targetPackages.length, config, verbose, stats);
|
|
150
|
+
stats.packageDetails.push(pkgStats);
|
|
151
|
+
}
|
|
152
|
+
const elapsed = (performance.now() - startTime) / 1e3;
|
|
153
|
+
mirrorSummarySeparator(logger);
|
|
154
|
+
mirrorSummary(logger, stats, elapsed);
|
|
155
|
+
return stats.packagesErrored > 0 ? 1 : 0;
|
|
156
|
+
} catch (caughtError) {
|
|
157
|
+
mirrorFatalError(logger, caughtError);
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
export { runMirrorSync };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/lib/mirror/domain/constants.ts
|
|
2
|
+
const DIST_DIR = "dist";
|
|
3
|
+
const PACKAGE_JSON = "package.json";
|
|
4
|
+
const VALID_JS_EXTENSIONS = new Set([
|
|
5
|
+
".js",
|
|
6
|
+
".mjs",
|
|
7
|
+
".cjs"
|
|
8
|
+
]);
|
|
9
|
+
const DTS_EXTENSION = ".d.ts";
|
|
10
|
+
const PACKAGE_JSON_EXPORT = "./package.json";
|
|
11
|
+
//#endregion
|
|
12
|
+
export { DIST_DIR, DTS_EXTENSION, PACKAGE_JSON, PACKAGE_JSON_EXPORT, VALID_JS_EXTENSIONS };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/lib/mirror/domain/errors.ts
|
|
2
|
+
const MirrorErrorCode = {
|
|
3
|
+
FATAL: "FATAL",
|
|
4
|
+
PACKAGE_WRITE: "PACKAGE_WRITE"
|
|
5
|
+
};
|
|
6
|
+
var MirrorError = class extends Error {
|
|
7
|
+
code;
|
|
8
|
+
constructor(code, message, options) {
|
|
9
|
+
super(message, options);
|
|
10
|
+
this.name = "MirrorError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { MirrorError, MirrorErrorCode };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/lib/mirror/infra/dirent-list.ts
|
|
2
|
+
/**
|
|
3
|
+
* Narrow `fs.promises.readdir` overload result to `Dirent[]` when `withFileTypes: true`.
|
|
4
|
+
* Accepts both historical checks (`isFile` / `isDirectory`) in one guard.
|
|
5
|
+
* Returning `true` for an empty array is intentional: callers treat empty lists
|
|
6
|
+
* the same regardless of the element type, so `[]` is safely accepted as `Dirent[]`.
|
|
7
|
+
*/
|
|
8
|
+
function isDirentList(x) {
|
|
9
|
+
if (x.length === 0) return true;
|
|
10
|
+
const first = x[0];
|
|
11
|
+
return typeof first === "object" && first !== null && "isFile" in first && "isDirectory" in first;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { isDirentList };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { normalizePath } from "./path-normalizer.mjs";
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/lib/mirror/infra/package-filter.ts
|
|
5
|
+
function tryRealpath(entryPath) {
|
|
6
|
+
try {
|
|
7
|
+
return realpathSync.native(entryPath);
|
|
8
|
+
} catch {
|
|
9
|
+
return path.resolve(entryPath);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve `packageFilter` to a POSIX-ish path relative to `rootDir`, without using
|
|
14
|
+
* `process.cwd()`. Use for programmatic `runMirrorSync` calls.
|
|
15
|
+
*
|
|
16
|
+
* @throws If the resolved path is not a strict subdirectory of `rootDir`.
|
|
17
|
+
*/
|
|
18
|
+
function resolvePackageFilterUnderRoot(rootDir, packageFilter) {
|
|
19
|
+
const rootReal = tryRealpath(path.resolve(rootDir));
|
|
20
|
+
const resolved = path.isAbsolute(packageFilter) ? tryRealpath(path.resolve(packageFilter)) : tryRealpath(path.join(rootReal, packageFilter));
|
|
21
|
+
const normalized = normalizePath(path.relative(rootReal, resolved));
|
|
22
|
+
if (normalized.startsWith("..") || path.isAbsolute(normalized) || normalized === "" || normalized === ".") throw new Error(`Package path must be a subdirectory under monorepo root: ${rootDir}`);
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { resolvePackageFilterUnderRoot };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { messageFromCaughtUnknown } from "../../infra/caught-unknown-message.mjs";
|
|
2
|
+
import { PACKAGE_JSON_EXPORT } from "../domain/constants.mjs";
|
|
3
|
+
import { MirrorError, MirrorErrorCode } from "../domain/errors.mjs";
|
|
4
|
+
//#region src/lib/mirror/infra/update-pkg.ts
|
|
5
|
+
function containsDistTarget(value) {
|
|
6
|
+
if (typeof value === "string") return value.startsWith("./dist/");
|
|
7
|
+
if (Array.isArray(value)) return value.some((entryValue) => containsDistTarget(entryValue));
|
|
8
|
+
if (value && typeof value === "object") return Object.values(value).some((entryValue) => containsDistTarget(entryValue));
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Keep `./package.json` at the very end for aesthetics by default.
|
|
13
|
+
* If a catch-all `./*` exists, move `./package.json` before wildcards so it remains reachable.
|
|
14
|
+
*/
|
|
15
|
+
function exportSpecifierRank(exportSpecifier, hasCatchAllWildcard) {
|
|
16
|
+
if (exportSpecifier === ".") return 0;
|
|
17
|
+
if (exportSpecifier === "./package.json") return hasCatchAllWildcard ? 2 : 4;
|
|
18
|
+
if (exportSpecifier.includes("*")) return 3;
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
function compareExportSpecifiers(leftSpecifier, rightSpecifier, originalPathBySpecifier, hasCatchAllWildcard) {
|
|
22
|
+
const leftRank = exportSpecifierRank(leftSpecifier, hasCatchAllWildcard);
|
|
23
|
+
const rightRank = exportSpecifierRank(rightSpecifier, hasCatchAllWildcard);
|
|
24
|
+
if (leftRank !== rightRank) return leftRank - rightRank;
|
|
25
|
+
const leftOriginalPath = originalPathBySpecifier[leftSpecifier] ?? leftSpecifier;
|
|
26
|
+
const rightOriginalPath = originalPathBySpecifier[rightSpecifier] ?? rightSpecifier;
|
|
27
|
+
const originalPathComparison = leftOriginalPath.localeCompare(rightOriginalPath);
|
|
28
|
+
if (originalPathComparison !== 0) return originalPathComparison;
|
|
29
|
+
return leftSpecifier.localeCompare(rightSpecifier);
|
|
30
|
+
}
|
|
31
|
+
/** Shared rule: show `package.json#name` when it is a non-empty string; else folder basename. */
|
|
32
|
+
function resolvePackageDisplayName(packageJson, folderBasename) {
|
|
33
|
+
const declaredName = packageJson.name;
|
|
34
|
+
return typeof declaredName === "string" && declaredName.length > 0 ? declaredName : folderBasename;
|
|
35
|
+
}
|
|
36
|
+
async function readPackageJsonDisplayName(fs, packageJsonPath, folderBasename) {
|
|
37
|
+
if (!fs.existsSync(packageJsonPath)) return folderBasename;
|
|
38
|
+
try {
|
|
39
|
+
const raw = await fs.readFile(packageJsonPath, "utf8");
|
|
40
|
+
return resolvePackageDisplayName(JSON.parse(raw), folderBasename);
|
|
41
|
+
} catch {
|
|
42
|
+
return folderBasename;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Writes `exports` into `package.json` via a temp file + rename (atomic on same volume).
|
|
47
|
+
* Preserves a trailing newline after JSON.
|
|
48
|
+
*/
|
|
49
|
+
async function writePackageJsonExportsAtomic(fs, packageJsonPath, mergeInput) {
|
|
50
|
+
const raw = await fs.readFile(packageJsonPath, "utf8");
|
|
51
|
+
const packageJson = JSON.parse(raw);
|
|
52
|
+
const existingExports = packageJson.exports;
|
|
53
|
+
const existingExportMap = existingExports && typeof existingExports === "object" && !Array.isArray(existingExports) ? existingExports : {};
|
|
54
|
+
const managedSpecifiers = new Set(mergeInput.managedExportSpecifiers);
|
|
55
|
+
const originalPathBySpecifier = { ...mergeInput.originalPathBySpecifier };
|
|
56
|
+
const mergedExportMap = {};
|
|
57
|
+
const prunedKeys = [];
|
|
58
|
+
for (const [exportSpecifier, exportValue] of Object.entries(existingExportMap)) {
|
|
59
|
+
if (exportSpecifier === "./package.json") {
|
|
60
|
+
mergedExportMap[exportSpecifier] = exportValue;
|
|
61
|
+
if (!(exportSpecifier in originalPathBySpecifier)) originalPathBySpecifier[exportSpecifier] = exportSpecifier;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!managedSpecifiers.has(exportSpecifier)) if (containsDistTarget(exportValue)) prunedKeys.push(exportSpecifier);
|
|
65
|
+
else {
|
|
66
|
+
mergedExportMap[exportSpecifier] = exportValue;
|
|
67
|
+
if (!(exportSpecifier in originalPathBySpecifier)) originalPathBySpecifier[exportSpecifier] = exportSpecifier;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const [exportSpecifier, exportValue] of Object.entries(mergeInput.generatedExports)) mergedExportMap[exportSpecifier] = exportValue;
|
|
71
|
+
if (!("./package.json" in mergedExportMap)) mergedExportMap[PACKAGE_JSON_EXPORT] = PACKAGE_JSON_EXPORT;
|
|
72
|
+
originalPathBySpecifier[PACKAGE_JSON_EXPORT] = PACKAGE_JSON_EXPORT;
|
|
73
|
+
const rootExport = mergedExportMap["."];
|
|
74
|
+
if (rootExport && typeof rootExport === "object" && !Array.isArray(rootExport)) {
|
|
75
|
+
const rootEntry = rootExport;
|
|
76
|
+
const requirePath = typeof rootEntry.require === "string" ? rootEntry.require : void 0;
|
|
77
|
+
const importPath = typeof rootEntry.import === "string" ? rootEntry.import : void 0;
|
|
78
|
+
const typesPath = typeof rootEntry.types === "string" ? rootEntry.types : void 0;
|
|
79
|
+
if (requirePath || importPath) packageJson.main = requirePath ?? importPath;
|
|
80
|
+
if (importPath && importPath.endsWith(".mjs")) packageJson.module = importPath;
|
|
81
|
+
if (typesPath) packageJson.types = typesPath;
|
|
82
|
+
}
|
|
83
|
+
const filesField = packageJson.files;
|
|
84
|
+
if (Array.isArray(filesField)) {
|
|
85
|
+
const normalizedFiles = filesField.filter((entry) => typeof entry === "string");
|
|
86
|
+
if (!normalizedFiles.includes("dist")) normalizedFiles.push("dist");
|
|
87
|
+
packageJson.files = normalizedFiles;
|
|
88
|
+
} else packageJson.files = ["dist"];
|
|
89
|
+
const sortedExportMap = {};
|
|
90
|
+
const hasCatchAllWildcard = "./*" in mergedExportMap;
|
|
91
|
+
for (const exportSpecifier of Object.keys(mergedExportMap).sort((leftSpecifier, rightSpecifier) => compareExportSpecifiers(leftSpecifier, rightSpecifier, originalPathBySpecifier, hasCatchAllWildcard))) sortedExportMap[exportSpecifier] = mergedExportMap[exportSpecifier];
|
|
92
|
+
packageJson.exports = sortedExportMap;
|
|
93
|
+
const serialized = JSON.stringify(packageJson, null, 2) + "\n";
|
|
94
|
+
const tmpPath = `${packageJsonPath}.tmp`;
|
|
95
|
+
await fs.writeFile(tmpPath, serialized, "utf8");
|
|
96
|
+
try {
|
|
97
|
+
await fs.rename(tmpPath, packageJsonPath);
|
|
98
|
+
} catch (caughtRenameError) {
|
|
99
|
+
await fs.unlink(tmpPath).catch(() => {});
|
|
100
|
+
throw new MirrorError(MirrorErrorCode.PACKAGE_WRITE, `Failed to atomically update ${packageJsonPath}: ${messageFromCaughtUnknown(caughtRenameError)}`, caughtRenameError instanceof Error ? { cause: caughtRenameError } : void 0);
|
|
101
|
+
}
|
|
102
|
+
return { prunedKeys };
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
export { readPackageJsonDisplayName, resolvePackageDisplayName, writePackageJsonExportsAtomic };
|