@ucdjs/release-scripts 0.1.0-beta.24 → 0.1.0-beta.25
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/eta-BV8TCRDW.mjs +481 -0
- package/dist/index.d.mts +5 -1
- package/dist/index.mjs +600 -43
- package/package.json +13 -15
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path$2 from "node:path";
|
|
3
|
+
|
|
4
|
+
//#region node_modules/.pnpm/eta@4.5.1/node_modules/eta/dist/index.mjs
|
|
5
|
+
var EtaError = class extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "Eta Error";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var EtaParseError = class extends EtaError {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "EtaParser Error";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var EtaRuntimeError = class extends EtaError {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "EtaRuntime Error";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var EtaFileResolutionError = class extends EtaError {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "EtaFileResolution Error";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var EtaNameResolutionError = class extends EtaError {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "EtaNameResolution Error";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Throws an EtaError with a nicely formatted error and message showing where in the template the error occurred.
|
|
37
|
+
*/
|
|
38
|
+
function ParseErr(message, str, indx) {
|
|
39
|
+
const whitespace = str.slice(0, indx).split(/\n/);
|
|
40
|
+
const lineNo = whitespace.length;
|
|
41
|
+
const colNo = whitespace[lineNo - 1].length + 1;
|
|
42
|
+
message += " at line " + lineNo + " col " + colNo + ":\n\n " + str.split(/\n/)[lineNo - 1] + "\n " + Array(colNo).join(" ") + "^";
|
|
43
|
+
throw new EtaParseError(message);
|
|
44
|
+
}
|
|
45
|
+
function RuntimeErr(originalError, str, lineNo, path$1) {
|
|
46
|
+
const lines = str.split("\n");
|
|
47
|
+
const start = Math.max(lineNo - 3, 0);
|
|
48
|
+
const end = Math.min(lines.length, lineNo + 3);
|
|
49
|
+
const filename = path$1;
|
|
50
|
+
const context = lines.slice(start, end).map((line, i) => {
|
|
51
|
+
const curr = i + start + 1;
|
|
52
|
+
return (curr === lineNo ? " >> " : " ") + curr + "| " + line;
|
|
53
|
+
}).join("\n");
|
|
54
|
+
const err = new EtaRuntimeError((filename ? filename + ":" + lineNo + "\n" : "line " + lineNo + "\n") + context + "\n\n" + originalError.message);
|
|
55
|
+
err.name = originalError.name;
|
|
56
|
+
err.cause = originalError;
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
function readFile(path$1) {
|
|
60
|
+
let res = "";
|
|
61
|
+
try {
|
|
62
|
+
res = fs.readFileSync(path$1, "utf8");
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (err?.code === "ENOENT") throw new EtaFileResolutionError(`Could not find template: ${path$1}`);
|
|
65
|
+
else throw err;
|
|
66
|
+
}
|
|
67
|
+
return res;
|
|
68
|
+
}
|
|
69
|
+
function resolvePath(templatePath, options) {
|
|
70
|
+
let resolvedFilePath = "";
|
|
71
|
+
const views = this.config.views;
|
|
72
|
+
if (!views) throw new EtaFileResolutionError("Views directory is not defined");
|
|
73
|
+
const baseFilePath = options?.filepath;
|
|
74
|
+
const defaultExtension = this.config.defaultExtension === void 0 ? ".eta" : this.config.defaultExtension;
|
|
75
|
+
const cacheIndex = JSON.stringify({
|
|
76
|
+
filename: baseFilePath,
|
|
77
|
+
path: templatePath,
|
|
78
|
+
views: this.config.views
|
|
79
|
+
});
|
|
80
|
+
templatePath += path$2.extname(templatePath) ? "" : defaultExtension;
|
|
81
|
+
if (baseFilePath) {
|
|
82
|
+
if (this.config.cacheFilepaths && this.filepathCache[cacheIndex]) return this.filepathCache[cacheIndex];
|
|
83
|
+
if (absolutePathRegExp.exec(templatePath)?.length) {
|
|
84
|
+
const formattedPath = templatePath.replace(/^\/*|^\\*/, "");
|
|
85
|
+
resolvedFilePath = path$2.join(views, formattedPath);
|
|
86
|
+
} else resolvedFilePath = path$2.join(path$2.dirname(baseFilePath), templatePath);
|
|
87
|
+
} else resolvedFilePath = path$2.join(views, templatePath);
|
|
88
|
+
if (dirIsChild(views, resolvedFilePath)) {
|
|
89
|
+
if (baseFilePath && this.config.cacheFilepaths) this.filepathCache[cacheIndex] = resolvedFilePath;
|
|
90
|
+
return resolvedFilePath;
|
|
91
|
+
} else throw new EtaFileResolutionError(`Template '${templatePath}' is not in the views directory`);
|
|
92
|
+
}
|
|
93
|
+
function dirIsChild(parent, dir) {
|
|
94
|
+
const relative = path$2.relative(parent, dir);
|
|
95
|
+
return relative && !relative.startsWith("..") && !path$2.isAbsolute(relative);
|
|
96
|
+
}
|
|
97
|
+
const absolutePathRegExp = /^\\|^\//;
|
|
98
|
+
/* istanbul ignore next */
|
|
99
|
+
const AsyncFunction = (async () => {}).constructor;
|
|
100
|
+
/**
|
|
101
|
+
* Takes a template string and returns a template function that can be called with (data, config)
|
|
102
|
+
*
|
|
103
|
+
* @param str - The template string
|
|
104
|
+
* @param config - A custom configuration object (optional)
|
|
105
|
+
*/
|
|
106
|
+
function compile(str, options) {
|
|
107
|
+
const config = this.config;
|
|
108
|
+
const ctor = options?.async ? AsyncFunction : Function;
|
|
109
|
+
try {
|
|
110
|
+
return new ctor(config.varName, "options", this.compileToString.call(this, str, options));
|
|
111
|
+
} catch (e) {
|
|
112
|
+
if (e instanceof SyntaxError) throw new EtaParseError("Bad template syntax\n\n" + e.message + "\n" + Array(e.message.length + 1).join("=") + "\n" + this.compileToString.call(this, str, options) + "\n");
|
|
113
|
+
else throw e;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Compiles a template string to a function string. Most often users just use `compile()`, which calls `compileToString` and creates a new function using the result
|
|
118
|
+
*/
|
|
119
|
+
function compileToString(str, options) {
|
|
120
|
+
const config = this.config;
|
|
121
|
+
const isAsync = options?.async;
|
|
122
|
+
const compileBody$1 = this.compileBody;
|
|
123
|
+
const buffer = this.parse.call(this, str);
|
|
124
|
+
let res = `${config.functionHeader}
|
|
125
|
+
let include = (__eta_t, __eta_d) => this.render(__eta_t, {...${config.varName}, ...(__eta_d ?? {})}, options);
|
|
126
|
+
let includeAsync = (__eta_t, __eta_d) => this.renderAsync(__eta_t, {...${config.varName}, ...(__eta_d ?? {})}, options);
|
|
127
|
+
|
|
128
|
+
let __eta = {res: "", e: this.config.escapeFunction, f: this.config.filterFunction${config.debug ? ", line: 1, templateStr: \"" + str.replace(/\\|"/g, "\\$&").replace(/\r\n|\n|\r/g, "\\n") + "\"" : ""}};
|
|
129
|
+
|
|
130
|
+
function layout(path, data) {
|
|
131
|
+
__eta.layout = path;
|
|
132
|
+
__eta.layoutData = data;
|
|
133
|
+
}${config.debug ? "try {" : ""}${config.useWith ? "with(" + config.varName + "||{}){" : ""}
|
|
134
|
+
|
|
135
|
+
function ${config.outputFunctionName}(s){__eta.res+=s;}
|
|
136
|
+
|
|
137
|
+
${compileBody$1.call(this, buffer)}
|
|
138
|
+
if (__eta.layout) {
|
|
139
|
+
__eta.res = ${isAsync ? "await includeAsync" : "include"} (__eta.layout, {...${config.varName}, body: __eta.res, ...__eta.layoutData});
|
|
140
|
+
}
|
|
141
|
+
${config.useWith ? "}" : ""}${config.debug ? "} catch (e) { this.RuntimeErr(e, __eta.templateStr, __eta.line, options.filepath) }" : ""}
|
|
142
|
+
return __eta.res;
|
|
143
|
+
`;
|
|
144
|
+
if (config.plugins) for (let i = 0; i < config.plugins.length; i++) {
|
|
145
|
+
const plugin = config.plugins[i];
|
|
146
|
+
if (plugin.processFnString) res = plugin.processFnString(res, config);
|
|
147
|
+
}
|
|
148
|
+
return res;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Loops through the AST generated by `parse` and transform each item into JS calls
|
|
152
|
+
*
|
|
153
|
+
* **Example**
|
|
154
|
+
*
|
|
155
|
+
* ```js
|
|
156
|
+
* let templateAST = ['Hi ', { val: 'it.name', t: 'i' }]
|
|
157
|
+
* compileBody.call(Eta, templateAST)
|
|
158
|
+
* // => "__eta.res+='Hi '\n__eta.res+=__eta.e(it.name)\n"
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
function compileBody(buff) {
|
|
162
|
+
const config = this.config;
|
|
163
|
+
let i = 0;
|
|
164
|
+
const buffLength = buff.length;
|
|
165
|
+
let returnStr = "";
|
|
166
|
+
for (; i < buffLength; i++) {
|
|
167
|
+
const currentBlock = buff[i];
|
|
168
|
+
if (typeof currentBlock === "string") returnStr += "__eta.res+='" + currentBlock + "';\n";
|
|
169
|
+
else {
|
|
170
|
+
const type = currentBlock.t;
|
|
171
|
+
let content = currentBlock.val || "";
|
|
172
|
+
if (config.debug) returnStr += "__eta.line=" + currentBlock.lineNo + "\n";
|
|
173
|
+
if (type === "r") {
|
|
174
|
+
if (config.autoFilter) content = "__eta.f(" + content + ")";
|
|
175
|
+
returnStr += "__eta.res+=" + content + ";\n";
|
|
176
|
+
} else if (type === "i") {
|
|
177
|
+
if (config.autoFilter) content = "__eta.f(" + content + ")";
|
|
178
|
+
if (config.autoEscape) content = "__eta.e(" + content + ")";
|
|
179
|
+
returnStr += "__eta.res+=" + content + ";\n";
|
|
180
|
+
} else if (type === "e") returnStr += content + "\n";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return returnStr;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Takes a string within a template and trims it, based on the preceding tag's whitespace control and `config.autoTrim`
|
|
187
|
+
*/
|
|
188
|
+
function trimWS(str, config, wsLeft, wsRight) {
|
|
189
|
+
let leftTrim;
|
|
190
|
+
let rightTrim;
|
|
191
|
+
if (Array.isArray(config.autoTrim)) {
|
|
192
|
+
leftTrim = config.autoTrim[1];
|
|
193
|
+
rightTrim = config.autoTrim[0];
|
|
194
|
+
} else leftTrim = rightTrim = config.autoTrim;
|
|
195
|
+
if (wsLeft || wsLeft === false) leftTrim = wsLeft;
|
|
196
|
+
if (wsRight || wsRight === false) rightTrim = wsRight;
|
|
197
|
+
if (!rightTrim && !leftTrim) return str;
|
|
198
|
+
if (leftTrim === "slurp" && rightTrim === "slurp") return str.trim();
|
|
199
|
+
if (leftTrim === "_" || leftTrim === "slurp") str = str.trimStart();
|
|
200
|
+
else if (leftTrim === "-" || leftTrim === "nl") str = str.replace(/^(?:\r\n|\n|\r)/, "");
|
|
201
|
+
if (rightTrim === "_" || rightTrim === "slurp") str = str.trimEnd();
|
|
202
|
+
else if (rightTrim === "-" || rightTrim === "nl") str = str.replace(/(?:\r\n|\n|\r)$/, "");
|
|
203
|
+
return str;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* A map of special HTML characters to their XML-escaped equivalents
|
|
207
|
+
*/
|
|
208
|
+
const escMap = {
|
|
209
|
+
"&": "&",
|
|
210
|
+
"<": "<",
|
|
211
|
+
">": ">",
|
|
212
|
+
"\"": """,
|
|
213
|
+
"'": "'"
|
|
214
|
+
};
|
|
215
|
+
function replaceChar(s) {
|
|
216
|
+
return escMap[s];
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* XML-escapes an input value after converting it to a string
|
|
220
|
+
*
|
|
221
|
+
* @param str - Input value (usually a string)
|
|
222
|
+
* @returns XML-escaped string
|
|
223
|
+
*/
|
|
224
|
+
function XMLEscape(str) {
|
|
225
|
+
const newStr = String(str);
|
|
226
|
+
if (/[&<>"']/.test(newStr)) return newStr.replace(/[&<>"']/g, replaceChar);
|
|
227
|
+
else return newStr;
|
|
228
|
+
}
|
|
229
|
+
/** Eta's base (global) configuration */
|
|
230
|
+
const defaultConfig = {
|
|
231
|
+
autoEscape: true,
|
|
232
|
+
autoFilter: false,
|
|
233
|
+
autoTrim: [false, "nl"],
|
|
234
|
+
cache: false,
|
|
235
|
+
cacheFilepaths: true,
|
|
236
|
+
debug: false,
|
|
237
|
+
escapeFunction: XMLEscape,
|
|
238
|
+
filterFunction: (val) => String(val),
|
|
239
|
+
outputFunctionName: "output",
|
|
240
|
+
functionHeader: "",
|
|
241
|
+
parse: {
|
|
242
|
+
exec: "",
|
|
243
|
+
interpolate: "=",
|
|
244
|
+
raw: "~"
|
|
245
|
+
},
|
|
246
|
+
plugins: [],
|
|
247
|
+
rmWhitespace: false,
|
|
248
|
+
tags: ["<%", "%>"],
|
|
249
|
+
useWith: false,
|
|
250
|
+
varName: "it",
|
|
251
|
+
defaultExtension: ".eta"
|
|
252
|
+
};
|
|
253
|
+
const templateLitReg = /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g;
|
|
254
|
+
const singleQuoteReg = /'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g;
|
|
255
|
+
const doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;
|
|
256
|
+
/** Escape special regular expression characters inside a string */
|
|
257
|
+
function escapeRegExp(string) {
|
|
258
|
+
return string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
|
|
259
|
+
}
|
|
260
|
+
function getLineNo(str, index) {
|
|
261
|
+
return str.slice(0, index).split("\n").length;
|
|
262
|
+
}
|
|
263
|
+
function parse(str) {
|
|
264
|
+
const config = this.config;
|
|
265
|
+
let buffer = [];
|
|
266
|
+
let trimLeftOfNextStr = false;
|
|
267
|
+
let lastIndex = 0;
|
|
268
|
+
const parseOptions = config.parse;
|
|
269
|
+
if (config.plugins) for (let i = 0; i < config.plugins.length; i++) {
|
|
270
|
+
const plugin = config.plugins[i];
|
|
271
|
+
if (plugin.processTemplate) str = plugin.processTemplate(str, config);
|
|
272
|
+
}
|
|
273
|
+
if (config.rmWhitespace) str = str.replace(/[\r\n]+/g, "\n").replace(/^\s+|\s+$/gm, "");
|
|
274
|
+
templateLitReg.lastIndex = 0;
|
|
275
|
+
singleQuoteReg.lastIndex = 0;
|
|
276
|
+
doubleQuoteReg.lastIndex = 0;
|
|
277
|
+
function pushString(strng, shouldTrimRightOfString) {
|
|
278
|
+
if (strng) {
|
|
279
|
+
strng = trimWS(strng, config, trimLeftOfNextStr, shouldTrimRightOfString);
|
|
280
|
+
if (strng) {
|
|
281
|
+
strng = strng.replace(/\\|'/g, "\\$&").replace(/\r\n|\n|\r/g, "\\n");
|
|
282
|
+
buffer.push(strng);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const prefixes = [
|
|
287
|
+
parseOptions.exec,
|
|
288
|
+
parseOptions.interpolate,
|
|
289
|
+
parseOptions.raw
|
|
290
|
+
].reduce((accumulator, prefix) => {
|
|
291
|
+
if (accumulator && prefix) return accumulator + "|" + escapeRegExp(prefix);
|
|
292
|
+
else if (prefix) return escapeRegExp(prefix);
|
|
293
|
+
else return accumulator;
|
|
294
|
+
}, "");
|
|
295
|
+
const parseOpenReg = new RegExp(escapeRegExp(config.tags[0]) + "(-|_)?\\s*(" + prefixes + ")?\\s*", "g");
|
|
296
|
+
const parseCloseReg = new RegExp("'|\"|`|\\/\\*|(\\s*(-|_)?" + escapeRegExp(config.tags[1]) + ")", "g");
|
|
297
|
+
let m;
|
|
298
|
+
while (m = parseOpenReg.exec(str)) {
|
|
299
|
+
const precedingString = str.slice(lastIndex, m.index);
|
|
300
|
+
lastIndex = m[0].length + m.index;
|
|
301
|
+
const wsLeft = m[1];
|
|
302
|
+
const prefix = m[2] || "";
|
|
303
|
+
pushString(precedingString, wsLeft);
|
|
304
|
+
parseCloseReg.lastIndex = lastIndex;
|
|
305
|
+
let closeTag;
|
|
306
|
+
let currentObj = false;
|
|
307
|
+
while (closeTag = parseCloseReg.exec(str)) if (closeTag[1]) {
|
|
308
|
+
const content = str.slice(lastIndex, closeTag.index);
|
|
309
|
+
parseOpenReg.lastIndex = lastIndex = parseCloseReg.lastIndex;
|
|
310
|
+
trimLeftOfNextStr = closeTag[2];
|
|
311
|
+
currentObj = {
|
|
312
|
+
t: prefix === parseOptions.exec ? "e" : prefix === parseOptions.raw ? "r" : prefix === parseOptions.interpolate ? "i" : "",
|
|
313
|
+
val: content
|
|
314
|
+
};
|
|
315
|
+
break;
|
|
316
|
+
} else {
|
|
317
|
+
const char = closeTag[0];
|
|
318
|
+
if (char === "/*") {
|
|
319
|
+
const commentCloseInd = str.indexOf("*/", parseCloseReg.lastIndex);
|
|
320
|
+
if (commentCloseInd === -1) ParseErr("unclosed comment", str, closeTag.index);
|
|
321
|
+
parseCloseReg.lastIndex = commentCloseInd;
|
|
322
|
+
} else if (char === "'") {
|
|
323
|
+
singleQuoteReg.lastIndex = closeTag.index;
|
|
324
|
+
if (singleQuoteReg.exec(str)) parseCloseReg.lastIndex = singleQuoteReg.lastIndex;
|
|
325
|
+
else ParseErr("unclosed string", str, closeTag.index);
|
|
326
|
+
} else if (char === "\"") {
|
|
327
|
+
doubleQuoteReg.lastIndex = closeTag.index;
|
|
328
|
+
if (doubleQuoteReg.exec(str)) parseCloseReg.lastIndex = doubleQuoteReg.lastIndex;
|
|
329
|
+
else ParseErr("unclosed string", str, closeTag.index);
|
|
330
|
+
} else if (char === "`") {
|
|
331
|
+
templateLitReg.lastIndex = closeTag.index;
|
|
332
|
+
if (templateLitReg.exec(str)) parseCloseReg.lastIndex = templateLitReg.lastIndex;
|
|
333
|
+
else ParseErr("unclosed string", str, closeTag.index);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (currentObj) {
|
|
337
|
+
if (config.debug) currentObj.lineNo = getLineNo(str, m.index);
|
|
338
|
+
buffer.push(currentObj);
|
|
339
|
+
} else ParseErr("unclosed tag", str, m.index);
|
|
340
|
+
}
|
|
341
|
+
pushString(str.slice(lastIndex, str.length), false);
|
|
342
|
+
if (config.plugins) for (let i = 0; i < config.plugins.length; i++) {
|
|
343
|
+
const plugin = config.plugins[i];
|
|
344
|
+
if (plugin.processAST) buffer = plugin.processAST(buffer, config);
|
|
345
|
+
}
|
|
346
|
+
return buffer;
|
|
347
|
+
}
|
|
348
|
+
function handleCache(template, options) {
|
|
349
|
+
const templateStore = options?.async ? this.templatesAsync : this.templatesSync;
|
|
350
|
+
if (this.resolvePath && this.readFile && !template.startsWith("@")) {
|
|
351
|
+
const templatePath = options.filepath;
|
|
352
|
+
const cachedTemplate = templateStore.get(templatePath);
|
|
353
|
+
if (this.config.cache && cachedTemplate) return cachedTemplate;
|
|
354
|
+
else {
|
|
355
|
+
const templateString = this.readFile(templatePath);
|
|
356
|
+
const templateFn = this.compile(templateString, options);
|
|
357
|
+
if (this.config.cache) templateStore.define(templatePath, templateFn);
|
|
358
|
+
return templateFn;
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
const cachedTemplate = templateStore.get(template);
|
|
362
|
+
if (cachedTemplate) return cachedTemplate;
|
|
363
|
+
else throw new EtaNameResolutionError(`Failed to get template '${template}'`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function render(template, data, meta) {
|
|
367
|
+
let templateFn;
|
|
368
|
+
const options = {
|
|
369
|
+
...meta,
|
|
370
|
+
async: false
|
|
371
|
+
};
|
|
372
|
+
if (typeof template === "string") {
|
|
373
|
+
if (this.resolvePath && this.readFile && !template.startsWith("@")) options.filepath = this.resolvePath(template, options);
|
|
374
|
+
templateFn = handleCache.call(this, template, options);
|
|
375
|
+
} else templateFn = template;
|
|
376
|
+
return templateFn.call(this, data, options);
|
|
377
|
+
}
|
|
378
|
+
function renderAsync(template, data, meta) {
|
|
379
|
+
let templateFn;
|
|
380
|
+
const options = {
|
|
381
|
+
...meta,
|
|
382
|
+
async: true
|
|
383
|
+
};
|
|
384
|
+
if (typeof template === "string") {
|
|
385
|
+
if (this.resolvePath && this.readFile && !template.startsWith("@")) options.filepath = this.resolvePath(template, options);
|
|
386
|
+
templateFn = handleCache.call(this, template, options);
|
|
387
|
+
} else templateFn = template;
|
|
388
|
+
const res = templateFn.call(this, data, options);
|
|
389
|
+
return Promise.resolve(res);
|
|
390
|
+
}
|
|
391
|
+
function renderString(template, data) {
|
|
392
|
+
const templateFn = this.compile(template, { async: false });
|
|
393
|
+
return render.call(this, templateFn, data);
|
|
394
|
+
}
|
|
395
|
+
function renderStringAsync(template, data) {
|
|
396
|
+
const templateFn = this.compile(template, { async: true });
|
|
397
|
+
return renderAsync.call(this, templateFn, data);
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Handles storage and accessing of values
|
|
401
|
+
*
|
|
402
|
+
* In this case, we use it to store compiled template functions
|
|
403
|
+
* Indexed by their `name` or `filename`
|
|
404
|
+
*/
|
|
405
|
+
var Cacher = class {
|
|
406
|
+
constructor(cache) {
|
|
407
|
+
this.cache = cache;
|
|
408
|
+
}
|
|
409
|
+
define(key, val) {
|
|
410
|
+
this.cache[key] = val;
|
|
411
|
+
}
|
|
412
|
+
get(key) {
|
|
413
|
+
return this.cache[key];
|
|
414
|
+
}
|
|
415
|
+
remove(key) {
|
|
416
|
+
delete this.cache[key];
|
|
417
|
+
}
|
|
418
|
+
reset() {
|
|
419
|
+
this.cache = {};
|
|
420
|
+
}
|
|
421
|
+
load(cacheObj) {
|
|
422
|
+
this.cache = {
|
|
423
|
+
...this.cache,
|
|
424
|
+
...cacheObj
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
var Eta$1 = class {
|
|
429
|
+
constructor(customConfig) {
|
|
430
|
+
if (customConfig) this.config = {
|
|
431
|
+
...defaultConfig,
|
|
432
|
+
...customConfig
|
|
433
|
+
};
|
|
434
|
+
else this.config = { ...defaultConfig };
|
|
435
|
+
}
|
|
436
|
+
config;
|
|
437
|
+
RuntimeErr = RuntimeErr;
|
|
438
|
+
compile = compile;
|
|
439
|
+
compileToString = compileToString;
|
|
440
|
+
compileBody = compileBody;
|
|
441
|
+
parse = parse;
|
|
442
|
+
render = render;
|
|
443
|
+
renderAsync = renderAsync;
|
|
444
|
+
renderString = renderString;
|
|
445
|
+
renderStringAsync = renderStringAsync;
|
|
446
|
+
filepathCache = {};
|
|
447
|
+
templatesSync = new Cacher({});
|
|
448
|
+
templatesAsync = new Cacher({});
|
|
449
|
+
resolvePath = null;
|
|
450
|
+
readFile = null;
|
|
451
|
+
configure(customConfig) {
|
|
452
|
+
this.config = {
|
|
453
|
+
...this.config,
|
|
454
|
+
...customConfig
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
withConfig(customConfig) {
|
|
458
|
+
return {
|
|
459
|
+
...this,
|
|
460
|
+
config: {
|
|
461
|
+
...this.config,
|
|
462
|
+
...customConfig
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
loadTemplate(name, template, options) {
|
|
467
|
+
if (typeof template === "string") (options?.async ? this.templatesAsync : this.templatesSync).define(name, this.compile(template, options));
|
|
468
|
+
else {
|
|
469
|
+
let templates = this.templatesSync;
|
|
470
|
+
if (template.constructor.name === "AsyncFunction" || options?.async) templates = this.templatesAsync;
|
|
471
|
+
templates.define(name, template);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
var Eta = class extends Eta$1 {
|
|
476
|
+
readFile = readFile;
|
|
477
|
+
resolvePath = resolvePath;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
//#endregion
|
|
481
|
+
export { Eta as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -24,16 +24,20 @@ interface ReleaseScriptsOptionsInput {
|
|
|
24
24
|
};
|
|
25
25
|
types?: Record<string, {
|
|
26
26
|
title: string;
|
|
27
|
+
color?: string;
|
|
27
28
|
}>;
|
|
28
29
|
changelog?: {
|
|
29
30
|
enabled?: boolean;
|
|
30
31
|
template?: string;
|
|
31
32
|
emojis?: boolean;
|
|
32
33
|
};
|
|
34
|
+
npm?: {
|
|
35
|
+
otp?: string;
|
|
36
|
+
provenance?: boolean;
|
|
37
|
+
};
|
|
33
38
|
}
|
|
34
39
|
//#endregion
|
|
35
40
|
//#region src/services/workspace.service.d.ts
|
|
36
|
-
|
|
37
41
|
declare const WorkspacePackageSchema: Schema.Struct<{
|
|
38
42
|
name: typeof Schema.String;
|
|
39
43
|
version: typeof Schema.String;
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,139 @@
|
|
|
1
|
+
import { t as Eta } from "./eta-BV8TCRDW.mjs";
|
|
1
2
|
import { Console, Context, Data, Effect, Layer, Schema } from "effect";
|
|
3
|
+
import path from "node:path";
|
|
2
4
|
import { Command, CommandExecutor } from "@effect/platform";
|
|
3
5
|
import { NodeCommandExecutor, NodeFileSystem } from "@effect/platform-node";
|
|
4
6
|
import * as CommitParser from "commit-parser";
|
|
5
7
|
import process from "node:process";
|
|
6
8
|
import semver from "semver";
|
|
7
9
|
import fs from "node:fs/promises";
|
|
8
|
-
import path from "node:path";
|
|
9
10
|
|
|
11
|
+
//#region src/utils/changelog-formatters.ts
|
|
12
|
+
const eta$1 = new Eta();
|
|
13
|
+
/**
|
|
14
|
+
* Pure function to parse commits into changelog entries
|
|
15
|
+
*/
|
|
16
|
+
function parseCommits(commits) {
|
|
17
|
+
return commits.filter((commit) => commit.isConventional).map((commit) => ({
|
|
18
|
+
type: commit.type || "other",
|
|
19
|
+
scope: commit.scope,
|
|
20
|
+
description: commit.description,
|
|
21
|
+
breaking: commit.isBreaking || false,
|
|
22
|
+
hash: commit.hash,
|
|
23
|
+
shortHash: commit.shortHash,
|
|
24
|
+
references: commit.references.map((ref) => ({
|
|
25
|
+
type: ref.type,
|
|
26
|
+
value: ref.value
|
|
27
|
+
}))
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Pure function to group changelog entries by type
|
|
32
|
+
*/
|
|
33
|
+
function groupByType(entries) {
|
|
34
|
+
const groups = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
const type = entry.breaking ? "breaking" : entry.type;
|
|
37
|
+
if (!groups.has(type)) groups.set(type, []);
|
|
38
|
+
groups.get(type).push(entry);
|
|
39
|
+
}
|
|
40
|
+
return groups;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Changelog template for Eta rendering
|
|
44
|
+
*/
|
|
45
|
+
const CHANGELOG_TEMPLATE = `# <%= it.packageName %> v<%= it.version %>
|
|
46
|
+
|
|
47
|
+
**Previous version**: \`<%= it.previousVersion %>\`
|
|
48
|
+
**New version**: \`<%= it.version %>\`
|
|
49
|
+
|
|
50
|
+
<% if (it.entries.length === 0) { %>
|
|
51
|
+
*No conventional commits found.*
|
|
52
|
+
<% } else { %>
|
|
53
|
+
<% const groups = it.groupedEntries; %>
|
|
54
|
+
<% const typeOrder = ["breaking", "feat", "fix", "perf", "docs", "style", "refactor", "test", "build", "ci", "chore"]; %>
|
|
55
|
+
<% const typeLabels = {
|
|
56
|
+
breaking: "💥 Breaking Changes",
|
|
57
|
+
feat: "✨ Features",
|
|
58
|
+
fix: "🐛 Bug Fixes",
|
|
59
|
+
perf: "⚡ Performance",
|
|
60
|
+
docs: "📝 Documentation",
|
|
61
|
+
style: "💄 Styling",
|
|
62
|
+
refactor: "♻️ Refactoring",
|
|
63
|
+
test: "✅ Tests",
|
|
64
|
+
build: "📦 Build",
|
|
65
|
+
ci: "👷 CI",
|
|
66
|
+
chore: "🔧 Chores"
|
|
67
|
+
}; %>
|
|
68
|
+
|
|
69
|
+
<% for (const type of typeOrder) { %>
|
|
70
|
+
<% const entries = groups.get(type); %>
|
|
71
|
+
<% if (entries && entries.length > 0) { %>
|
|
72
|
+
## <%= typeLabels[type] || type.charAt(0).toUpperCase() + type.slice(1) %>
|
|
73
|
+
|
|
74
|
+
<% for (const entry of entries) { %>
|
|
75
|
+
- <% if (entry.scope) { %>**<%= entry.scope %>**: <% } %><%= entry.description %><% if (entry.references.length > 0) { %> (<%= entry.references.map(r => "#" + r.value).join(", ") %>)<% } %> (\`<%= entry.shortHash %>\`)
|
|
76
|
+
<% } %>
|
|
77
|
+
|
|
78
|
+
<% } %>
|
|
79
|
+
<% } %>
|
|
80
|
+
|
|
81
|
+
<% for (const [type, entries] of groups) { %>
|
|
82
|
+
<% if (!typeOrder.includes(type)) { %>
|
|
83
|
+
## <%= type.charAt(0).toUpperCase() + type.slice(1) %>
|
|
84
|
+
|
|
85
|
+
<% for (const entry of entries) { %>
|
|
86
|
+
- <% if (entry.scope) { %>**<%= entry.scope %>**: <% } %><%= entry.description %> (\`<%= entry.shortHash %>\`)
|
|
87
|
+
<% } %>
|
|
88
|
+
|
|
89
|
+
<% } %>
|
|
90
|
+
<% } %>
|
|
91
|
+
<% } %>`;
|
|
92
|
+
/**
|
|
93
|
+
* Pure function to format changelog as markdown
|
|
94
|
+
*/
|
|
95
|
+
function formatChangelogMarkdown(changelog) {
|
|
96
|
+
const groups = groupByType(changelog.entries);
|
|
97
|
+
return eta$1.renderString(CHANGELOG_TEMPLATE, {
|
|
98
|
+
packageName: changelog.packageName,
|
|
99
|
+
version: changelog.version,
|
|
100
|
+
previousVersion: changelog.previousVersion,
|
|
101
|
+
entries: changelog.entries,
|
|
102
|
+
groupedEntries: groups
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Pure function to create a changelog object
|
|
107
|
+
*/
|
|
108
|
+
function createChangelog(packageName, version, previousVersion, commits) {
|
|
109
|
+
return {
|
|
110
|
+
packageName,
|
|
111
|
+
version,
|
|
112
|
+
previousVersion,
|
|
113
|
+
entries: parseCommits(commits)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/services/changelog.service.ts
|
|
119
|
+
var ChangelogService = class extends Effect.Service()("@ucdjs/release-scripts/ChangelogService", {
|
|
120
|
+
effect: Effect.gen(function* () {
|
|
121
|
+
function generateChangelog(pkg, newVersion, commits) {
|
|
122
|
+
return Effect.gen(function* () {
|
|
123
|
+
const changelog = createChangelog(pkg.name, newVersion, pkg.version, commits);
|
|
124
|
+
return {
|
|
125
|
+
changelog,
|
|
126
|
+
markdown: formatChangelogMarkdown(changelog),
|
|
127
|
+
filePath: `${pkg.path}/CHANGELOG.md`
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return { generateChangelog };
|
|
132
|
+
}),
|
|
133
|
+
dependencies: []
|
|
134
|
+
}) {};
|
|
135
|
+
|
|
136
|
+
//#endregion
|
|
10
137
|
//#region src/services/dependency-graph.service.ts
|
|
11
138
|
var DependencyGraphService = class extends Effect.Service()("@ucdjs/release-scripts/DependencyGraphService", {
|
|
12
139
|
effect: Effect.gen(function* () {
|
|
@@ -81,21 +208,42 @@ var WorkspaceError = class extends Data.TaggedError("WorkspaceError") {};
|
|
|
81
208
|
var GitHubError = class extends Data.TaggedError("GitHubError") {};
|
|
82
209
|
var VersionCalculationError = class extends Data.TaggedError("VersionCalculationError") {};
|
|
83
210
|
var OverridesLoadError = class extends Data.TaggedError("OverridesLoadError") {};
|
|
211
|
+
var NPMError = class extends Data.TaggedError("NPMError") {};
|
|
212
|
+
var PublishError = class extends Data.TaggedError("PublishError") {};
|
|
213
|
+
var TagError = class extends Data.TaggedError("TagError") {};
|
|
84
214
|
|
|
85
215
|
//#endregion
|
|
86
216
|
//#region src/options.ts
|
|
87
217
|
const DEFAULT_PR_BODY_TEMPLATE = `## Summary\n\nThis PR contains the following changes:\n\n- Updated package versions\n- Updated changelogs\n\n## Packages\n\nThe following packages will be released:\n\n{{packages}}`;
|
|
88
218
|
const DEFAULT_CHANGELOG_TEMPLATE = `# Changelog\n\n{{releases}}`;
|
|
89
219
|
const DEFAULT_TYPES = {
|
|
90
|
-
feat: {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
220
|
+
feat: {
|
|
221
|
+
title: "🚀 Features",
|
|
222
|
+
color: "green"
|
|
223
|
+
},
|
|
224
|
+
fix: {
|
|
225
|
+
title: "🐞 Bug Fixes",
|
|
226
|
+
color: "red"
|
|
227
|
+
},
|
|
228
|
+
refactor: {
|
|
229
|
+
title: "🔧 Code Refactoring",
|
|
230
|
+
color: "blue"
|
|
231
|
+
},
|
|
232
|
+
perf: {
|
|
233
|
+
title: "🏎 Performance",
|
|
234
|
+
color: "orange"
|
|
235
|
+
},
|
|
236
|
+
docs: {
|
|
237
|
+
title: "📚 Documentation",
|
|
238
|
+
color: "purple"
|
|
239
|
+
},
|
|
240
|
+
style: {
|
|
241
|
+
title: "🎨 Styles",
|
|
242
|
+
color: "pink"
|
|
243
|
+
}
|
|
96
244
|
};
|
|
97
245
|
function normalizeReleaseScriptsOptions(options) {
|
|
98
|
-
const { workspaceRoot = process.cwd(), githubToken = "", repo: fullRepo, packages = true, branch = {}, globalCommitMode = "dependencies", pullRequest = {}, changelog = {}, types = {}, dryRun = false } = options;
|
|
246
|
+
const { workspaceRoot = process.cwd(), githubToken = "", repo: fullRepo, packages = true, branch = {}, globalCommitMode = "dependencies", pullRequest = {}, changelog = {}, types = {}, dryRun = false, npm = {} } = options;
|
|
99
247
|
const token = githubToken.trim();
|
|
100
248
|
if (!token) throw new Error("GitHub token is required. Pass it in via options.");
|
|
101
249
|
if (!fullRepo || !fullRepo.trim() || !fullRepo.includes("/")) throw new Error("Repository (repo) is required. Specify in 'owner/repo' format (e.g., 'octocat/hello-world').");
|
|
@@ -129,7 +277,11 @@ function normalizeReleaseScriptsOptions(options) {
|
|
|
129
277
|
types: options.types ? {
|
|
130
278
|
...DEFAULT_TYPES,
|
|
131
279
|
...types
|
|
132
|
-
} : DEFAULT_TYPES
|
|
280
|
+
} : DEFAULT_TYPES,
|
|
281
|
+
npm: {
|
|
282
|
+
otp: npm.otp,
|
|
283
|
+
provenance: npm.provenance ?? true
|
|
284
|
+
}
|
|
133
285
|
};
|
|
134
286
|
}
|
|
135
287
|
var ReleaseScriptsOptions = class extends Context.Tag("@ucdjs/release-scripts/ReleaseScriptsOptions")() {};
|
|
@@ -176,6 +328,9 @@ var GitService = class extends Effect.Service()("@ucdjs/release-scripts/GitServi
|
|
|
176
328
|
function checkoutBranch(branch) {
|
|
177
329
|
return execGitCommand(["checkout", branch]);
|
|
178
330
|
}
|
|
331
|
+
function rebaseBranch(onto) {
|
|
332
|
+
return execGitCommandIfNotDry(["rebase", onto]);
|
|
333
|
+
}
|
|
179
334
|
function stageChanges(files) {
|
|
180
335
|
return Effect.gen(function* () {
|
|
181
336
|
if (files.length === 0) return yield* Effect.fail(/* @__PURE__ */ new Error("No files to stage."));
|
|
@@ -196,6 +351,14 @@ var GitService = class extends Effect.Service()("@ucdjs/release-scripts/GitServi
|
|
|
196
351
|
branch
|
|
197
352
|
]);
|
|
198
353
|
}
|
|
354
|
+
function forcePushChanges(branch, remote = "origin") {
|
|
355
|
+
return execGitCommandIfNotDry([
|
|
356
|
+
"push",
|
|
357
|
+
"--force-with-lease",
|
|
358
|
+
remote,
|
|
359
|
+
branch
|
|
360
|
+
]);
|
|
361
|
+
}
|
|
199
362
|
function readFile(filePath, ref = "HEAD") {
|
|
200
363
|
return execGitCommand(["show", `${ref}:${filePath}`]);
|
|
201
364
|
}
|
|
@@ -215,6 +378,32 @@ var GitService = class extends Effect.Service()("@ucdjs/release-scripts/GitServi
|
|
|
215
378
|
})));
|
|
216
379
|
}));
|
|
217
380
|
}
|
|
381
|
+
function createTag(name, message) {
|
|
382
|
+
return execGitCommandIfNotDry(message ? [
|
|
383
|
+
"tag",
|
|
384
|
+
"-a",
|
|
385
|
+
name,
|
|
386
|
+
"-m",
|
|
387
|
+
message
|
|
388
|
+
] : ["tag", name]).pipe(Effect.mapError((err) => new TagError({
|
|
389
|
+
message: `Failed to create tag "${name}"`,
|
|
390
|
+
tagName: name,
|
|
391
|
+
operation: "create",
|
|
392
|
+
cause: err
|
|
393
|
+
})));
|
|
394
|
+
}
|
|
395
|
+
function pushTag(name, remote = "origin") {
|
|
396
|
+
return execGitCommandIfNotDry([
|
|
397
|
+
"push",
|
|
398
|
+
remote,
|
|
399
|
+
name
|
|
400
|
+
]).pipe(Effect.mapError((err) => new TagError({
|
|
401
|
+
message: `Failed to push tag "${name}" to ${remote}`,
|
|
402
|
+
tagName: name,
|
|
403
|
+
operation: "push",
|
|
404
|
+
cause: err
|
|
405
|
+
})));
|
|
406
|
+
}
|
|
218
407
|
function getCommits(options) {
|
|
219
408
|
return Effect.tryPromise({
|
|
220
409
|
try: async () => CommitParser.getCommits({
|
|
@@ -264,16 +453,22 @@ var GitService = class extends Effect.Service()("@ucdjs/release-scripts/GitServi
|
|
|
264
453
|
exists: doesBranchExist,
|
|
265
454
|
create: createBranch,
|
|
266
455
|
checkout: checkoutBranch,
|
|
456
|
+
rebase: rebaseBranch,
|
|
267
457
|
get: getBranch
|
|
268
458
|
},
|
|
269
459
|
commits: {
|
|
270
460
|
stage: stageChanges,
|
|
271
461
|
write: writeCommit,
|
|
272
462
|
push: pushChanges,
|
|
463
|
+
forcePush: forcePushChanges,
|
|
273
464
|
get: getCommits,
|
|
274
465
|
filesChangesBetweenRefs
|
|
275
466
|
},
|
|
276
|
-
tags: {
|
|
467
|
+
tags: {
|
|
468
|
+
mostRecentForPackage: getMostRecentPackageTag,
|
|
469
|
+
create: createTag,
|
|
470
|
+
push: pushTag
|
|
471
|
+
},
|
|
277
472
|
workspace: {
|
|
278
473
|
readFile,
|
|
279
474
|
isWithinRepository,
|
|
@@ -287,6 +482,7 @@ var GitService = class extends Effect.Service()("@ucdjs/release-scripts/GitServi
|
|
|
287
482
|
|
|
288
483
|
//#endregion
|
|
289
484
|
//#region src/services/github.service.ts
|
|
485
|
+
const eta = new Eta();
|
|
290
486
|
const PullRequestSchema = Schema.Struct({
|
|
291
487
|
number: Schema.Number,
|
|
292
488
|
title: Schema.String,
|
|
@@ -370,7 +566,128 @@ var GitHubService = class extends Effect.Service()("@ucdjs/release-scripts/GitHu
|
|
|
370
566
|
cause: e.cause
|
|
371
567
|
})));
|
|
372
568
|
}
|
|
373
|
-
|
|
569
|
+
function setCommitStatus(sha, status) {
|
|
570
|
+
return makeRequest(`statuses/${sha}`, Schema.Unknown, {
|
|
571
|
+
method: "POST",
|
|
572
|
+
body: JSON.stringify(status)
|
|
573
|
+
}).pipe(Effect.map(() => status), Effect.catchAll((e) => Effect.fail(new GitHubError({
|
|
574
|
+
message: e.message,
|
|
575
|
+
operation: "setCommitStatus",
|
|
576
|
+
cause: e.cause
|
|
577
|
+
}))));
|
|
578
|
+
}
|
|
579
|
+
function updatePullRequest(number, options) {
|
|
580
|
+
return makeRequest(`pulls/${number}`, PullRequestSchema, {
|
|
581
|
+
method: "PATCH",
|
|
582
|
+
body: JSON.stringify(options)
|
|
583
|
+
}).pipe(Effect.mapError((e) => new GitHubError({
|
|
584
|
+
message: e.message,
|
|
585
|
+
operation: "updatePullRequest",
|
|
586
|
+
cause: e.cause
|
|
587
|
+
})));
|
|
588
|
+
}
|
|
589
|
+
const prBodyTemplate = `## Release Summary
|
|
590
|
+
|
|
591
|
+
This PR prepares the release of <%= it.count %> package<%= it.count === 1 ? "" : "s" %>:
|
|
592
|
+
|
|
593
|
+
<% for (const release of it.releases) { %>
|
|
594
|
+
- **<%= release.packageName %>**: \`<%= release.previousVersion %>\` → \`<%= release.version %>\`
|
|
595
|
+
<% } %>
|
|
596
|
+
|
|
597
|
+
## Changes
|
|
598
|
+
|
|
599
|
+
See individual package changelogs for details.
|
|
600
|
+
`;
|
|
601
|
+
function generateReleasePRBody(releases) {
|
|
602
|
+
return Effect.gen(function* () {
|
|
603
|
+
return eta.renderString(prBodyTemplate, {
|
|
604
|
+
count: releases.length,
|
|
605
|
+
releases
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
return {
|
|
610
|
+
getPullRequestByBranch,
|
|
611
|
+
setCommitStatus,
|
|
612
|
+
updatePullRequest,
|
|
613
|
+
generateReleasePRBody
|
|
614
|
+
};
|
|
615
|
+
}),
|
|
616
|
+
dependencies: []
|
|
617
|
+
}) {};
|
|
618
|
+
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/services/npm.service.ts
|
|
621
|
+
const PackumentSchema = Schema.Struct({
|
|
622
|
+
"name": Schema.String,
|
|
623
|
+
"dist-tags": Schema.Record({
|
|
624
|
+
key: Schema.String,
|
|
625
|
+
value: Schema.String
|
|
626
|
+
}),
|
|
627
|
+
"versions": Schema.Record({
|
|
628
|
+
key: Schema.String,
|
|
629
|
+
value: Schema.Struct({
|
|
630
|
+
name: Schema.String,
|
|
631
|
+
version: Schema.String,
|
|
632
|
+
description: Schema.optional(Schema.String),
|
|
633
|
+
dist: Schema.Struct({
|
|
634
|
+
tarball: Schema.String,
|
|
635
|
+
shasum: Schema.String,
|
|
636
|
+
integrity: Schema.optional(Schema.String)
|
|
637
|
+
})
|
|
638
|
+
})
|
|
639
|
+
})
|
|
640
|
+
});
|
|
641
|
+
var NPMService = class extends Effect.Service()("@ucdjs/release-scripts/NPMService", {
|
|
642
|
+
effect: Effect.gen(function* () {
|
|
643
|
+
const executor = yield* CommandExecutor.CommandExecutor;
|
|
644
|
+
const config = yield* ReleaseScriptsOptions;
|
|
645
|
+
const fetchPackument = (packageName) => Effect.tryPromise({
|
|
646
|
+
try: async () => {
|
|
647
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}`);
|
|
648
|
+
if (response.status === 404) return null;
|
|
649
|
+
if (!response.ok) throw new Error(`Failed to fetch packument: ${response.statusText}`);
|
|
650
|
+
return await response.json();
|
|
651
|
+
},
|
|
652
|
+
catch: (error) => {
|
|
653
|
+
return new NPMError({
|
|
654
|
+
message: error instanceof Error ? error.message : String(error),
|
|
655
|
+
operation: "fetchPackument"
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
}).pipe(Effect.flatMap((data) => {
|
|
659
|
+
if (data === null) return Effect.succeed(null);
|
|
660
|
+
return Schema.decodeUnknown(PackumentSchema)(data).pipe(Effect.mapError((error) => new NPMError({
|
|
661
|
+
message: `Failed to parse packument: ${error}`,
|
|
662
|
+
operation: "fetchPackument"
|
|
663
|
+
})));
|
|
664
|
+
}));
|
|
665
|
+
const versionExists = (packageName, version) => fetchPackument(packageName).pipe(Effect.map((packument) => {
|
|
666
|
+
if (!packument) return false;
|
|
667
|
+
return version in packument.versions;
|
|
668
|
+
}));
|
|
669
|
+
const getLatestVersion = (packageName) => fetchPackument(packageName).pipe(Effect.map((packument) => {
|
|
670
|
+
if (!packument) return null;
|
|
671
|
+
return packument["dist-tags"].latest || null;
|
|
672
|
+
}));
|
|
673
|
+
const publish = (options) => Effect.gen(function* () {
|
|
674
|
+
const args = ["publish"];
|
|
675
|
+
if (options.tagName) args.push("--tag", options.tagName);
|
|
676
|
+
if (options.otp) args.push("--otp", options.otp);
|
|
677
|
+
if (options.provenance !== false) args.push("--provenance");
|
|
678
|
+
if (options.dryRun ?? config.dryRun) args.push("--dry-run");
|
|
679
|
+
const command = Command.make("pnpm", ...args).pipe(Command.workingDirectory(options.packagePath));
|
|
680
|
+
return (yield* executor.string(command).pipe(Effect.mapError((err) => new PublishError({
|
|
681
|
+
message: `Failed to publish package at ${options.packagePath}: ${err.message}`,
|
|
682
|
+
cause: err
|
|
683
|
+
})))).trim();
|
|
684
|
+
});
|
|
685
|
+
return {
|
|
686
|
+
fetchPackument,
|
|
687
|
+
versionExists,
|
|
688
|
+
getLatestVersion,
|
|
689
|
+
publish
|
|
690
|
+
};
|
|
374
691
|
}),
|
|
375
692
|
dependencies: []
|
|
376
693
|
}) {};
|
|
@@ -839,8 +1156,257 @@ function findCommitRange(packages) {
|
|
|
839
1156
|
return [oldestCommit.hash, newestCommit.hash];
|
|
840
1157
|
}
|
|
841
1158
|
|
|
1159
|
+
//#endregion
|
|
1160
|
+
//#region src/prepare.ts
|
|
1161
|
+
function constructPrepareProgram(config) {
|
|
1162
|
+
return Effect.gen(function* () {
|
|
1163
|
+
const changelog = yield* ChangelogService;
|
|
1164
|
+
const git = yield* GitService;
|
|
1165
|
+
const github = yield* GitHubService;
|
|
1166
|
+
const dependencyGraph = yield* DependencyGraphService;
|
|
1167
|
+
const packageUpdater = yield* PackageUpdaterService;
|
|
1168
|
+
const versionCalculator = yield* VersionCalculatorService;
|
|
1169
|
+
const workspace = yield* WorkspaceService;
|
|
1170
|
+
yield* git.workspace.assertWorkspaceReady;
|
|
1171
|
+
const releasePullRequest = yield* github.getPullRequestByBranch(config.branch.release);
|
|
1172
|
+
if (!releasePullRequest || !releasePullRequest.head) return yield* Effect.fail(/* @__PURE__ */ new Error(`Release pull request for branch "${config.branch.release}" does not exist.`));
|
|
1173
|
+
yield* Console.log(`✅ Release pull request #${releasePullRequest.number} exists.`);
|
|
1174
|
+
if ((yield* git.branches.get) !== config.branch.release) {
|
|
1175
|
+
yield* git.branches.checkout(config.branch.release);
|
|
1176
|
+
yield* Console.log(`✅ Checked out to release branch "${config.branch.release}".`);
|
|
1177
|
+
}
|
|
1178
|
+
yield* Console.log(`🔄 Rebasing "${config.branch.release}" onto "${config.branch.default}"...`);
|
|
1179
|
+
yield* git.branches.rebase(config.branch.default);
|
|
1180
|
+
yield* Console.log(`✅ Rebase complete.`);
|
|
1181
|
+
const overrides = yield* loadOverrides({
|
|
1182
|
+
sha: config.branch.default,
|
|
1183
|
+
overridesPath: ".github/ucdjs-release.overrides.json"
|
|
1184
|
+
});
|
|
1185
|
+
if (Object.keys(overrides).length > 0) yield* Console.log("📋 Loaded version overrides:", overrides);
|
|
1186
|
+
const originalBranch = yield* git.branches.get;
|
|
1187
|
+
yield* git.branches.checkout(config.branch.default);
|
|
1188
|
+
const packages = yield* workspace.discoverWorkspacePackages.pipe(Effect.flatMap(mergePackageCommitsIntoPackages), Effect.flatMap((pkgs) => mergeCommitsAffectingGloballyIntoPackage(pkgs, config.globalCommitMode)));
|
|
1189
|
+
yield* Console.log(`📦 Discovered ${packages.length} packages with commits.`);
|
|
1190
|
+
const releases = yield* versionCalculator.calculateBumps(packages, overrides);
|
|
1191
|
+
yield* dependencyGraph.topologicalOrder(packages);
|
|
1192
|
+
const releasesCount = releases.length;
|
|
1193
|
+
yield* Console.log(`📊 ${releasesCount} package${releasesCount === 1 ? "" : "s"} will be released.`);
|
|
1194
|
+
yield* git.branches.checkout(originalBranch);
|
|
1195
|
+
yield* Console.log("✏️ Updating package.json files...");
|
|
1196
|
+
yield* packageUpdater.applyReleases(packages, releases);
|
|
1197
|
+
yield* Console.log("✅ package.json files updated.");
|
|
1198
|
+
yield* Console.log("📝 Generating changelogs...");
|
|
1199
|
+
const changelogFiles = [];
|
|
1200
|
+
for (const release of releases) {
|
|
1201
|
+
const pkg = packages.find((p) => p.name === release.package.name);
|
|
1202
|
+
if (!pkg || !pkg.commits) continue;
|
|
1203
|
+
const result = yield* changelog.generateChangelog(pkg, release.newVersion, pkg.commits);
|
|
1204
|
+
yield* Effect.tryPromise({
|
|
1205
|
+
try: async () => {
|
|
1206
|
+
await (await import("node:fs/promises")).writeFile(result.filePath, result.markdown, "utf-8");
|
|
1207
|
+
},
|
|
1208
|
+
catch: (e) => /* @__PURE__ */ new Error(`Failed to write changelog: ${String(e)}`)
|
|
1209
|
+
});
|
|
1210
|
+
changelogFiles.push(result.filePath);
|
|
1211
|
+
}
|
|
1212
|
+
yield* Console.log(`✅ Generated ${changelogFiles.length} changelog file${changelogFiles.length === 1 ? "" : "s"}.`);
|
|
1213
|
+
const filesToStage = [...releases.map((r) => `${r.package.path}/package.json`), ...changelogFiles];
|
|
1214
|
+
yield* Console.log(`📌 Staging ${filesToStage.length} file${filesToStage.length === 1 ? "" : "s"}...`);
|
|
1215
|
+
yield* git.commits.stage(filesToStage);
|
|
1216
|
+
const commitMessage = `chore(release): prepare release
|
|
1217
|
+
|
|
1218
|
+
${releasesCount} package${releasesCount === 1 ? "" : "s"} updated:
|
|
1219
|
+
${releases.map((r) => ` - ${r.package.name}@${r.newVersion}`).join("\n")}`;
|
|
1220
|
+
yield* Console.log("💾 Creating commit...");
|
|
1221
|
+
yield* git.commits.write(commitMessage);
|
|
1222
|
+
yield* Console.log("✅ Commit created.");
|
|
1223
|
+
yield* Console.log(`⬆️ Force pushing to "${config.branch.release}"...`);
|
|
1224
|
+
yield* git.commits.forcePush(config.branch.release);
|
|
1225
|
+
yield* Console.log("✅ Force push complete.");
|
|
1226
|
+
yield* Console.log("📄 Updating pull request...");
|
|
1227
|
+
const prBody = yield* github.generateReleasePRBody(releases.map((r) => ({
|
|
1228
|
+
packageName: r.package.name,
|
|
1229
|
+
version: r.newVersion,
|
|
1230
|
+
previousVersion: r.package.version
|
|
1231
|
+
})));
|
|
1232
|
+
yield* github.updatePullRequest(releasePullRequest.number, { body: prBody });
|
|
1233
|
+
yield* Console.log("✅ Pull request updated.");
|
|
1234
|
+
yield* Console.log(`\n🎉 Release preparation complete! View PR: #${releasePullRequest.number}`);
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
//#endregion
|
|
1239
|
+
//#region src/publish.ts
|
|
1240
|
+
function isPrerelease(version) {
|
|
1241
|
+
const parsed = semver.parse(version);
|
|
1242
|
+
return parsed !== null && parsed.prerelease.length > 0;
|
|
1243
|
+
}
|
|
1244
|
+
function getDistTag(version) {
|
|
1245
|
+
return isPrerelease(version) ? "next" : "latest";
|
|
1246
|
+
}
|
|
1247
|
+
function buildPackage(packagePath) {
|
|
1248
|
+
return Effect.gen(function* () {
|
|
1249
|
+
const executor = yield* CommandExecutor.CommandExecutor;
|
|
1250
|
+
const command = Command.make("pnpm", "run", "build").pipe(Command.workingDirectory(packagePath));
|
|
1251
|
+
return (yield* executor.string(command).pipe(Effect.mapError((err) => /* @__PURE__ */ new Error(`Failed to build package at ${packagePath}: ${err.message}`)))).trim();
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
function constructPublishProgram(config) {
|
|
1255
|
+
return Effect.gen(function* () {
|
|
1256
|
+
const git = yield* GitService;
|
|
1257
|
+
const npm = yield* NPMService;
|
|
1258
|
+
const workspace = yield* WorkspaceService;
|
|
1259
|
+
const dependencyGraph = yield* DependencyGraphService;
|
|
1260
|
+
yield* git.workspace.assertWorkspaceReady;
|
|
1261
|
+
const currentBranch = yield* git.branches.get;
|
|
1262
|
+
if (currentBranch !== config.branch.default) return yield* Effect.fail(/* @__PURE__ */ new Error(`Publish must be run on the default branch "${config.branch.default}". Current branch: "${currentBranch}"`));
|
|
1263
|
+
yield* Console.log(`✅ On default branch "${config.branch.default}".`);
|
|
1264
|
+
const publicPackages = (yield* workspace.discoverWorkspacePackages).filter((pkg) => !pkg.packageJson.private);
|
|
1265
|
+
yield* Console.log(`📦 Found ${publicPackages.length} public package${publicPackages.length === 1 ? "" : "s"} to check.`);
|
|
1266
|
+
const orderedPackages = yield* dependencyGraph.topologicalOrder(publicPackages);
|
|
1267
|
+
const results = [];
|
|
1268
|
+
for (const updateOrder of orderedPackages) {
|
|
1269
|
+
const pkg = updateOrder.package;
|
|
1270
|
+
const version = pkg.version;
|
|
1271
|
+
const tagName = `${pkg.name}@${version}`;
|
|
1272
|
+
if (yield* npm.versionExists(pkg.name, version)) {
|
|
1273
|
+
yield* Console.log(`⏭️ Skipping ${pkg.name}@${version} - already published.`);
|
|
1274
|
+
results.push({
|
|
1275
|
+
packageName: pkg.name,
|
|
1276
|
+
version,
|
|
1277
|
+
status: "skipped",
|
|
1278
|
+
reason: "Already published to npm"
|
|
1279
|
+
});
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
yield* Console.log(`🔨 Building ${pkg.name}...`);
|
|
1283
|
+
yield* buildPackage(pkg.path);
|
|
1284
|
+
yield* Console.log(`✅ Build complete for ${pkg.name}.`);
|
|
1285
|
+
const distTag = getDistTag(version);
|
|
1286
|
+
yield* Console.log(`🚀 Publishing ${pkg.name}@${version} with tag "${distTag}"...`);
|
|
1287
|
+
const publishResult = yield* npm.publish({
|
|
1288
|
+
packagePath: pkg.path,
|
|
1289
|
+
tagName: distTag,
|
|
1290
|
+
otp: config.npm.otp,
|
|
1291
|
+
provenance: config.npm.provenance,
|
|
1292
|
+
dryRun: config.dryRun
|
|
1293
|
+
}).pipe(Effect.map(() => ({ success: true })), Effect.catchAll((err) => Effect.succeed({
|
|
1294
|
+
success: false,
|
|
1295
|
+
error: err
|
|
1296
|
+
})));
|
|
1297
|
+
if (publishResult.success) {
|
|
1298
|
+
yield* Console.log(`✅ Published ${pkg.name}@${version}.`);
|
|
1299
|
+
if (!config.dryRun) {
|
|
1300
|
+
yield* Console.log(`🏷️ Creating tag ${tagName}...`);
|
|
1301
|
+
yield* git.tags.create(tagName, `Release ${tagName}`);
|
|
1302
|
+
yield* git.tags.push(tagName);
|
|
1303
|
+
yield* Console.log(`✅ Tag ${tagName} created and pushed.`);
|
|
1304
|
+
} else yield* Console.log(`🏷️ [Dry Run] Would create and push tag ${tagName}.`);
|
|
1305
|
+
results.push({
|
|
1306
|
+
packageName: pkg.name,
|
|
1307
|
+
version,
|
|
1308
|
+
status: "published"
|
|
1309
|
+
});
|
|
1310
|
+
} else {
|
|
1311
|
+
const error = publishResult.error;
|
|
1312
|
+
yield* Console.log(`❌ Failed to publish ${pkg.name}@${version}: ${error.message}`);
|
|
1313
|
+
results.push({
|
|
1314
|
+
packageName: pkg.name,
|
|
1315
|
+
version,
|
|
1316
|
+
status: "failed",
|
|
1317
|
+
reason: error.message
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
const published = results.filter((r) => r.status === "published");
|
|
1322
|
+
const skipped = results.filter((r) => r.status === "skipped");
|
|
1323
|
+
const failed = results.filter((r) => r.status === "failed");
|
|
1324
|
+
yield* Console.log("\n📊 Publish Summary:");
|
|
1325
|
+
yield* Console.log(` Published: ${published.length}`);
|
|
1326
|
+
yield* Console.log(` Skipped: ${skipped.length}`);
|
|
1327
|
+
yield* Console.log(` Failed: ${failed.length}`);
|
|
1328
|
+
if (failed.length > 0) {
|
|
1329
|
+
yield* Console.log("\n❌ Failed packages:");
|
|
1330
|
+
for (const f of failed) yield* Console.log(` - ${f.packageName}@${f.version}: ${f.reason}`);
|
|
1331
|
+
return yield* Effect.fail(/* @__PURE__ */ new Error("Some packages failed to publish."));
|
|
1332
|
+
}
|
|
1333
|
+
if (published.length === 0 && skipped.length > 0) yield* Console.log("\n✅ All packages were already published.");
|
|
1334
|
+
else if (published.length > 0) yield* Console.log("\n🎉 Publish complete!");
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
|
|
842
1338
|
//#endregion
|
|
843
1339
|
//#region src/verify.ts
|
|
1340
|
+
function satisfiesRange(range, version) {
|
|
1341
|
+
return semver.satisfies(version, range, { includePrerelease: true });
|
|
1342
|
+
}
|
|
1343
|
+
function snapshotPackageJson(pkg, ref) {
|
|
1344
|
+
return Effect.gen(function* () {
|
|
1345
|
+
return yield* (yield* GitService).workspace.readFile(`${pkg.path}/package.json`, ref).pipe(Effect.flatMap((content) => Effect.try({
|
|
1346
|
+
try: () => JSON.parse(content),
|
|
1347
|
+
catch: (e) => /* @__PURE__ */ new Error(`Failed to parse package.json for ${pkg.name} at ${ref}: ${String(e)}`)
|
|
1348
|
+
})));
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
function findDrift(packages, releases, branchSnapshots) {
|
|
1352
|
+
const releaseVersionByName = /* @__PURE__ */ new Map();
|
|
1353
|
+
for (const rel of releases) releaseVersionByName.set(rel.package.name, rel.newVersion);
|
|
1354
|
+
const reasons = [];
|
|
1355
|
+
for (const pkg of packages) {
|
|
1356
|
+
const snapshot = branchSnapshots.get(pkg.name);
|
|
1357
|
+
if (snapshot == null) {
|
|
1358
|
+
reasons.push({
|
|
1359
|
+
packageName: pkg.name,
|
|
1360
|
+
reason: "package.json missing on release branch"
|
|
1361
|
+
});
|
|
1362
|
+
continue;
|
|
1363
|
+
}
|
|
1364
|
+
if (snapshot instanceof Error) {
|
|
1365
|
+
reasons.push({
|
|
1366
|
+
packageName: pkg.name,
|
|
1367
|
+
reason: snapshot.message
|
|
1368
|
+
});
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
const expectedVersion = releaseVersionByName.get(pkg.name) ?? pkg.version;
|
|
1372
|
+
const branchVersion = typeof snapshot.version === "string" ? snapshot.version : void 0;
|
|
1373
|
+
if (!branchVersion) {
|
|
1374
|
+
reasons.push({
|
|
1375
|
+
packageName: pkg.name,
|
|
1376
|
+
reason: "package.json on release branch lacks version"
|
|
1377
|
+
});
|
|
1378
|
+
continue;
|
|
1379
|
+
}
|
|
1380
|
+
if (branchVersion !== expectedVersion) reasons.push({
|
|
1381
|
+
packageName: pkg.name,
|
|
1382
|
+
reason: `version mismatch: expected ${expectedVersion}, found ${branchVersion}`
|
|
1383
|
+
});
|
|
1384
|
+
for (const section of [
|
|
1385
|
+
"dependencies",
|
|
1386
|
+
"devDependencies",
|
|
1387
|
+
"peerDependencies"
|
|
1388
|
+
]) {
|
|
1389
|
+
const deps = snapshot[section];
|
|
1390
|
+
if (!deps || typeof deps !== "object") continue;
|
|
1391
|
+
for (const [depName, range] of Object.entries(deps)) {
|
|
1392
|
+
const bumpedVersion = releaseVersionByName.get(depName);
|
|
1393
|
+
if (!bumpedVersion) continue;
|
|
1394
|
+
if (typeof range !== "string") {
|
|
1395
|
+
reasons.push({
|
|
1396
|
+
packageName: pkg.name,
|
|
1397
|
+
reason: `${section}.${depName} is not a string range`
|
|
1398
|
+
});
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
if (!satisfiesRange(range, bumpedVersion)) reasons.push({
|
|
1402
|
+
packageName: pkg.name,
|
|
1403
|
+
reason: `${section}.${depName} does not include ${bumpedVersion}`
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return reasons;
|
|
1409
|
+
}
|
|
844
1410
|
function constructVerifyProgram(config) {
|
|
845
1411
|
return Effect.gen(function* () {
|
|
846
1412
|
const git = yield* GitService;
|
|
@@ -867,6 +1433,26 @@ function constructVerifyProgram(config) {
|
|
|
867
1433
|
const ordered = yield* dependencyGraph.topologicalOrder(packages);
|
|
868
1434
|
yield* Console.log("Calculated releases:", releases);
|
|
869
1435
|
yield* Console.log("Release order:", ordered);
|
|
1436
|
+
const releaseHeadSha = releasePullRequest.head.sha;
|
|
1437
|
+
const branchSnapshots = /* @__PURE__ */ new Map();
|
|
1438
|
+
for (const pkg of packages) {
|
|
1439
|
+
const snapshot = yield* snapshotPackageJson(pkg, releaseHeadSha).pipe(Effect.catchAll((err) => Effect.succeed(err instanceof Error ? err : new Error(String(err)))));
|
|
1440
|
+
branchSnapshots.set(pkg.name, snapshot);
|
|
1441
|
+
}
|
|
1442
|
+
const drift = findDrift(packages, releases, branchSnapshots);
|
|
1443
|
+
if (drift.length === 0) yield* Console.log("✅ Release branch is in sync with expected releases.");
|
|
1444
|
+
else yield* Console.log("❌ Release branch is out of sync:", drift);
|
|
1445
|
+
const status = drift.length === 0 ? {
|
|
1446
|
+
state: "success",
|
|
1447
|
+
description: "Release artifacts in sync",
|
|
1448
|
+
context: "release/verify"
|
|
1449
|
+
} : {
|
|
1450
|
+
state: "failure",
|
|
1451
|
+
description: "Release branch out of sync",
|
|
1452
|
+
context: "release/verify"
|
|
1453
|
+
};
|
|
1454
|
+
yield* github.setCommitStatus(releaseHeadSha, status);
|
|
1455
|
+
if (drift.length > 0) return yield* Effect.fail(/* @__PURE__ */ new Error("Release branch is out of sync."));
|
|
870
1456
|
});
|
|
871
1457
|
}
|
|
872
1458
|
|
|
@@ -874,7 +1460,7 @@ function constructVerifyProgram(config) {
|
|
|
874
1460
|
//#region src/index.ts
|
|
875
1461
|
async function createReleaseScripts(options) {
|
|
876
1462
|
const config = normalizeReleaseScriptsOptions(options);
|
|
877
|
-
const AppLayer = Layer.succeed(ReleaseScriptsOptions, config).pipe(Layer.provide(NodeCommandExecutor.layer), Layer.provide(NodeFileSystem.layer), Layer.provide(GitService.Default), Layer.provide(GitHubService.Default), Layer.provide(DependencyGraphService.Default), Layer.provide(PackageUpdaterService.Default), Layer.provide(VersionCalculatorService.Default), Layer.provide(WorkspaceService.Default));
|
|
1463
|
+
const AppLayer = Layer.succeed(ReleaseScriptsOptions, config).pipe(Layer.provide(NodeCommandExecutor.layer), Layer.provide(NodeFileSystem.layer), Layer.provide(ChangelogService.Default), Layer.provide(GitService.Default), Layer.provide(GitHubService.Default), Layer.provide(DependencyGraphService.Default), Layer.provide(NPMService.Default), Layer.provide(PackageUpdaterService.Default), Layer.provide(VersionCalculatorService.Default), Layer.provide(WorkspaceService.Default));
|
|
878
1464
|
const runProgram = (program) => {
|
|
879
1465
|
const provided = program.pipe(Effect.provide(AppLayer));
|
|
880
1466
|
return Effect.runPromise(provided);
|
|
@@ -894,39 +1480,10 @@ async function createReleaseScripts(options) {
|
|
|
894
1480
|
return runProgram(constructVerifyProgram(config));
|
|
895
1481
|
},
|
|
896
1482
|
async prepare() {
|
|
897
|
-
return runProgram(
|
|
898
|
-
const git = yield* GitService;
|
|
899
|
-
const github = yield* GitHubService;
|
|
900
|
-
const dependencyGraph = yield* DependencyGraphService;
|
|
901
|
-
const packageUpdater = yield* PackageUpdaterService;
|
|
902
|
-
const versionCalculator = yield* VersionCalculatorService;
|
|
903
|
-
const workspace = yield* WorkspaceService;
|
|
904
|
-
yield* safeguardProgram;
|
|
905
|
-
const releasePullRequest = yield* github.getPullRequestByBranch(config.branch.release);
|
|
906
|
-
if (!releasePullRequest || !releasePullRequest.head) return yield* Effect.fail(/* @__PURE__ */ new Error(`Release pull request for branch "${config.branch.release}" does not exist.`));
|
|
907
|
-
yield* Console.log(`✅ Release pull request #${releasePullRequest.number} exists.`);
|
|
908
|
-
if ((yield* git.branches.get) !== config.branch.default) {
|
|
909
|
-
yield* git.branches.checkout(config.branch.default);
|
|
910
|
-
yield* Console.log(`✅ Checked out to default branch "${config.branch.default}".`);
|
|
911
|
-
}
|
|
912
|
-
const overrides = yield* loadOverrides({
|
|
913
|
-
sha: releasePullRequest.head.sha,
|
|
914
|
-
overridesPath: ".github/ucdjs-release.overrides.json"
|
|
915
|
-
});
|
|
916
|
-
yield* Console.log("Loaded overrides:", overrides);
|
|
917
|
-
const packages = yield* workspace.discoverWorkspacePackages.pipe(Effect.flatMap(mergePackageCommitsIntoPackages), Effect.flatMap((pkgs) => mergeCommitsAffectingGloballyIntoPackage(pkgs, config.globalCommitMode)));
|
|
918
|
-
yield* Console.log("Discovered packages with commits and global commits:", packages);
|
|
919
|
-
const releases = yield* versionCalculator.calculateBumps(packages, overrides);
|
|
920
|
-
const ordered = yield* dependencyGraph.topologicalOrder(packages);
|
|
921
|
-
yield* Console.log("Calculated releases:", releases);
|
|
922
|
-
yield* Console.log("Release order:", ordered);
|
|
923
|
-
yield* packageUpdater.applyReleases(packages, releases);
|
|
924
|
-
}));
|
|
1483
|
+
return runProgram(constructPrepareProgram(config));
|
|
925
1484
|
},
|
|
926
1485
|
async publish() {
|
|
927
|
-
return runProgram(
|
|
928
|
-
return yield* Effect.fail(/* @__PURE__ */ new Error("Not implemented yet."));
|
|
929
|
-
}));
|
|
1486
|
+
return runProgram(constructPublishProgram(config));
|
|
930
1487
|
},
|
|
931
1488
|
packages: {
|
|
932
1489
|
async list() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ucdjs/release-scripts",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.25",
|
|
4
4
|
"description": "@ucdjs release scripts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,37 +15,35 @@
|
|
|
15
15
|
".": "./dist/index.mjs",
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
|
-
"main": "./dist/index.mjs",
|
|
19
|
-
"module": "./dist/index.mjs",
|
|
20
18
|
"types": "./dist/index.d.mts",
|
|
21
19
|
"files": [
|
|
22
20
|
"dist"
|
|
23
21
|
],
|
|
24
22
|
"dependencies": {
|
|
25
|
-
"@effect/platform": "0.
|
|
26
|
-
"@effect/platform-node": "0.
|
|
27
|
-
"@luxass/utils": "2.7.
|
|
23
|
+
"@effect/platform": "0.94.4",
|
|
24
|
+
"@effect/platform-node": "0.104.1",
|
|
25
|
+
"@luxass/utils": "2.7.3",
|
|
28
26
|
"commit-parser": "1.3.0",
|
|
29
|
-
"effect": "3.19.
|
|
27
|
+
"effect": "3.19.16",
|
|
30
28
|
"farver": "1.0.0-beta.1",
|
|
31
29
|
"mri": "1.2.0",
|
|
32
30
|
"prompts": "2.4.2",
|
|
33
|
-
"semver": "7.7.
|
|
31
|
+
"semver": "7.7.4",
|
|
34
32
|
"tinyexec": "1.0.2"
|
|
35
33
|
},
|
|
36
34
|
"devDependencies": {
|
|
37
|
-
"@effect/language-service": "^0.
|
|
35
|
+
"@effect/language-service": "^0.73.1",
|
|
38
36
|
"@effect/vitest": "0.27.0",
|
|
39
|
-
"@luxass/eslint-config": "
|
|
37
|
+
"@luxass/eslint-config": "7.2.0",
|
|
40
38
|
"@types/node": "22.18.12",
|
|
41
39
|
"@types/prompts": "2.4.9",
|
|
42
40
|
"@types/semver": "7.7.1",
|
|
43
|
-
"eslint": "
|
|
44
|
-
"eta": "4.
|
|
45
|
-
"tsdown": "0.
|
|
41
|
+
"eslint": "10.0.0",
|
|
42
|
+
"eta": "4.5.1",
|
|
43
|
+
"tsdown": "0.20.3",
|
|
46
44
|
"typescript": "5.9.3",
|
|
47
|
-
"vitest": "4.0.
|
|
48
|
-
"vitest-testdirs": "4.
|
|
45
|
+
"vitest": "4.0.18",
|
|
46
|
+
"vitest-testdirs": "4.4.2"
|
|
49
47
|
},
|
|
50
48
|
"scripts": {
|
|
51
49
|
"build": "tsdown",
|