@absolutejs/changelog 0.7.0 → 0.7.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/CHANGELOG.md CHANGED
@@ -6,6 +6,13 @@ This file is generated by `absolute-changelog` from the entries in
6
6
  `changelog/`. Edit an entry, not this file — and add new ones under
7
7
  `changelog/unreleased/`.
8
8
 
9
+ ## 0.7.1 — 2026-10-07
10
+
11
+ ### Fixed
12
+
13
+ - **Quote style, comments and line wrapping no longer read as API changes** (`diffSurface`)
14
+ Two builds of the same API can print a declaration differently: a formatter swaps 'single' for "double" quotes, wraps a parameter list and adds a trailing comma, or a doc comment is reworded. The release gate and the upgrade advisor reported each of those as a changed export. Declarations are now compared token by token, with string literals in one form and comments, trailing commas and spacing ignored; a different string, type or parameter is still a change. The declarations shown in a report keep their own text.
15
+
9
16
  ## 0.7.0 — 2026-09-10
10
17
 
11
18
  ### Added
package/changelog.json CHANGED
@@ -2,6 +2,20 @@
2
2
  "contract": 1,
3
3
  "name": "@absolutejs/changelog",
4
4
  "releases": [
5
+ {
6
+ "changes": [
7
+ {
8
+ "detail": "Two builds of the same API can print a declaration differently: a formatter swaps 'single' for \"double\" quotes, wraps a parameter list and adds a trailing comma, or a doc comment is reworded. The release gate and the upgrade advisor reported each of those as a changed export. Declarations are now compared token by token, with string literals in one form and comments, trailing commas and spacing ignored; a different string, type or parameter is still a change. The declarations shown in a report keep their own text.",
9
+ "kind": "fixed",
10
+ "summary": "Quote style, comments and line wrapping no longer read as API changes",
11
+ "symbols": [
12
+ "diffSurface"
13
+ ]
14
+ }
15
+ ],
16
+ "date": "2026-10-07",
17
+ "version": "0.7.1"
18
+ },
5
19
  {
6
20
  "changes": [
7
21
  {
package/dist/cli.js CHANGED
@@ -337,6 +337,38 @@ var readTar = (archive, keep, limits = {}) => {
337
337
  var DECLARATION = /\.d\.[cm]?ts$/u;
338
338
  var parse = (source, name) => ts.createSourceFile(name, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
339
339
  var normalize = (text) => text.replace(/\s+/gu, " ").trim();
340
+ var CLOSERS = new Set(["}", ")", "]", ">"]);
341
+ var wordy = (token) => /^[\w$#@]/u.test(token) || /[\w$]$/u.test(token);
342
+ var LAST2 = -1;
343
+ var tokenText = (scanner, scanned, state) => {
344
+ const closesTemplate = scanned === ts.SyntaxKind.CloseBraceToken && state.templates.at(LAST2) === state.depth;
345
+ const token = closesTemplate ? scanner.reScanTemplateToken(false) : scanned;
346
+ if (closesTemplate && token === ts.SyntaxKind.TemplateTail)
347
+ state.templates.pop();
348
+ if (closesTemplate)
349
+ return scanner.getTokenText();
350
+ if (token === ts.SyntaxKind.StringLiteral)
351
+ return JSON.stringify(scanner.getTokenValue());
352
+ if (token === ts.SyntaxKind.TemplateHead)
353
+ state.templates.push(state.depth);
354
+ if (token === ts.SyntaxKind.OpenBraceToken)
355
+ state.depth += 1;
356
+ if (token === ts.SyntaxKind.CloseBraceToken)
357
+ state.depth -= 1;
358
+ return scanner.getTokenText();
359
+ };
360
+ var canonical = (text) => {
361
+ const scanner = ts.createScanner(ts.ScriptTarget.Latest, true, ts.LanguageVariant.Standard, text);
362
+ const state = { depth: 0, templates: [] };
363
+ const tokens = [];
364
+ for (let token = scanner.scan();token !== ts.SyntaxKind.EndOfFileToken; token = scanner.scan()) {
365
+ const value = tokenText(scanner, token, state);
366
+ if (CLOSERS.has(value) && tokens.at(LAST2) === ",")
367
+ tokens.pop();
368
+ tokens.push(value);
369
+ }
370
+ return tokens.reduce((joined, token, index) => index > 0 && wordy(tokens[index - 1] ?? "") && wordy(token) ? `${joined} ${token}` : `${joined}${token}`, "");
371
+ };
340
372
  var resolveFrom = (files, from, specifier) => {
341
373
  if (!specifier.startsWith("."))
342
374
  return;
@@ -502,7 +534,7 @@ var SHAPE = {
502
534
  types: "types",
503
535
  unknown: "unknown"
504
536
  };
505
- var isStub = (declaration) => declaration.startsWith("export {");
537
+ var isStub = (declaration) => /^export\s*\{/u.test(declaration);
506
538
  var signatureOf = (declaration) => {
507
539
  const file = parse(`${declaration}
508
540
  `, "declaration.d.ts");
@@ -518,7 +550,7 @@ var parametersOf = (declaration) => {
518
550
  const found = [];
519
551
  const visit = (node) => {
520
552
  if (ts.isFunctionTypeNode(node) || ts.isFunctionDeclaration(node) || ts.isMethodSignature(node) || ts.isMethodDeclaration(node) || ts.isConstructSignatureDeclaration(node) || ts.isConstructorDeclaration(node))
521
- found.push(node.parameters.map((parameter) => normalize(parameter.getText())).join(", "));
553
+ found.push(node.parameters.map((parameter) => canonical(parameter.getText())).join(", "));
522
554
  ts.forEachChild(node, visit);
523
555
  };
524
556
  visit(statement);
@@ -560,7 +592,7 @@ var diffSurface = (before, after) => {
560
592
  const lost = was.entries.filter((entry) => !now.entries.includes(entry));
561
593
  if (lost.length > 0)
562
594
  withdrawn.push({ from: lost, name });
563
- if (now.declaration !== was.declaration)
595
+ if (canonical(now.declaration) !== canonical(was.declaration))
564
596
  changed.push({
565
597
  after: now.declaration,
566
598
  before: was.declaration,
@@ -581,9 +613,9 @@ var diffSurface = (before, after) => {
581
613
  var notable = (diff) => diff.changed.filter((change) => change.shape === "parameters" || change.shape === "members");
582
614
 
583
615
  // src/entries.ts
584
- var loadEntry = async (path2) => {
616
+ var loadEntry = async (path) => {
585
617
  try {
586
- const module = await import(pathToFileURL(path2).href);
618
+ const module = await import(pathToFileURL(path).href);
587
619
  const named = typeof module === "object" && module !== null && "change" in module ? module.change : undefined;
588
620
  const exported = named ?? (typeof module === "object" && module !== null && "default" in module ? module.default : undefined);
589
621
  const read = { exported };
@@ -601,10 +633,10 @@ var OUTPUT_DIRECTORIES = ["dist", "build", "lib", "types", "out"];
601
633
  var newestUnder = async (root) => {
602
634
  const found = await readdir(root, { withFileTypes: true }).catch(() => []);
603
635
  const times = await Promise.all(found.map(async (entry) => {
604
- const path2 = join(root, entry.name);
636
+ const path = join(root, entry.name);
605
637
  if (entry.isDirectory())
606
- return newestUnder(path2);
607
- return stat(path2).then((stats) => stats.mtimeMs, () => 0);
638
+ return newestUnder(path);
639
+ return stat(path).then((stats) => stats.mtimeMs, () => 0);
608
640
  }));
609
641
  return Math.max(0, ...times);
610
642
  };
@@ -660,8 +692,8 @@ var loadUnreleased = async (packageRoot) => {
660
692
  }
661
693
  return { entries, problems };
662
694
  };
663
- var readJsonFile = async (path2) => {
664
- const source = await readFile(path2, "utf8").catch(() => {
695
+ var readJsonFile = async (path) => {
696
+ const source = await readFile(path, "utf8").catch(() => {
665
697
  return;
666
698
  });
667
699
  if (source === undefined)
@@ -684,9 +716,9 @@ var indentOf = (source) => {
684
716
  const found = /\n([\t ]+)\S/u.exec(source)?.[1];
685
717
  return found === undefined || found === "" ? "\t" : found;
686
718
  };
687
- var writeJsonLike = async (path2, document) => {
688
- const source = await readFile2(path2, "utf8").catch(() => "");
689
- await writeFile(path2, `${JSON.stringify(document, null, indentOf(source))}
719
+ var writeJsonLike = async (path, document) => {
720
+ const source = await readFile2(path, "utf8").catch(() => "");
721
+ await writeFile(path, `${JSON.stringify(document, null, indentOf(source))}
690
722
  `);
691
723
  };
692
724
 
@@ -852,7 +884,7 @@ var publishedChangelog = async (name, version, options = {}) => {
852
884
  const archive = await packageArchive(name, version, options);
853
885
  if (!archive)
854
886
  return;
855
- const file = readTar(archive, (path2) => path2 === CHANGELOG_FILE).get(CHANGELOG_FILE);
887
+ const file = readTar(archive, (path) => path === CHANGELOG_FILE).get(CHANGELOG_FILE);
856
888
  if (!file)
857
889
  return;
858
890
  try {
@@ -1192,7 +1224,7 @@ var check = async (root, flags) => {
1192
1224
  failures.push(`the registry did not answer for ${manifest.name}, so nothing was compared \u2014 pass --offline to skip the comparison deliberately`);
1193
1225
  if (versions !== undefined && versions.published.length === 0)
1194
1226
  console.warn(` (${manifest.name} has never been published, so there was nothing to compare)`);
1195
- const baseline = (versions?.published ?? []).filter((version) => compareVersions(version, manifest.version) <= 0).sort(compareVersions).at(LAST2);
1227
+ const baseline = (versions?.published ?? []).filter((version) => compareVersions(version, manifest.version) <= 0).sort(compareVersions).at(LAST3);
1196
1228
  if (versions !== undefined && versions.published.length > 0 && baseline === undefined && !offline)
1197
1229
  console.warn(` (nothing published at or below ${manifest.version}, so there was nothing to compare)`);
1198
1230
  if (built !== undefined && baseline !== undefined) {
@@ -1216,7 +1248,7 @@ var check = async (root, flags) => {
1216
1248
  `));
1217
1249
  console.log(`${manifest.name}@${manifest.version}: changelog holds (${entries.length} pending, ${current.releases.length} released)`);
1218
1250
  };
1219
- var LAST2 = -1;
1251
+ var LAST3 = -1;
1220
1252
  var subpathsOf = (manifest) => {
1221
1253
  const { exports } = manifest;
1222
1254
  if (typeof exports !== "object" || exports === null)
@@ -1224,13 +1256,13 @@ var subpathsOf = (manifest) => {
1224
1256
  return new Set(Object.keys(exports).map((key) => key === "." ? manifest.name ?? "" : `${manifest.name}${key.slice(1)}`));
1225
1257
  };
1226
1258
  var CHECKING_CONFIGS = ["tsconfig.json", "tsconfig.eslint.json"];
1227
- var readTsconfig = async (path2) => {
1228
- const source = await readFile3(path2, "utf8").catch(() => {
1259
+ var readTsconfig = async (path) => {
1260
+ const source = await readFile3(path, "utf8").catch(() => {
1229
1261
  return;
1230
1262
  });
1231
1263
  if (source === undefined)
1232
1264
  return;
1233
- const read = ts2.parseConfigFileTextToJson(path2, source);
1265
+ const read = ts2.parseConfigFileTextToJson(path, source);
1234
1266
  if (read.error !== undefined)
1235
1267
  return;
1236
1268
  const { config } = read;
@@ -1240,8 +1272,8 @@ var readTsconfig = async (path2) => {
1240
1272
  var coversEntries = (include) => Array.isArray(include) && include.some((entry) => typeof entry === "string" && entry.startsWith("changelog"));
1241
1273
  var includeOf = (config) => typeof config === "object" && config !== null ? new Map(Object.entries(config)).get("include") : undefined;
1242
1274
  var includeEntriesIn = async (root, file) => {
1243
- const path2 = join2(root, file);
1244
- const read = await readTsconfig(path2);
1275
+ const path = join2(root, file);
1276
+ const read = await readTsconfig(path);
1245
1277
  if (read === undefined)
1246
1278
  return false;
1247
1279
  const include = includeOf(read.config);
@@ -1251,7 +1283,7 @@ var includeEntriesIn = async (root, file) => {
1251
1283
  if (opened === null)
1252
1284
  return false;
1253
1285
  const opens = opened.index + opened[0].length;
1254
- await writeFile2(path2, `${read.source.slice(0, opens)}
1286
+ await writeFile2(path, `${read.source.slice(0, opens)}
1255
1287
  "changelog/**/*",${read.source.slice(opens)}`);
1256
1288
  return true;
1257
1289
  };
@@ -1260,8 +1292,8 @@ var includeEntries = async (root) => {
1260
1292
  return wired.some(Boolean) ? undefined : "no tsconfig include list to wire \u2014 make sure changelog/ is compiled by something";
1261
1293
  };
1262
1294
  var ignoreGenerated = async (root) => {
1263
- const path2 = join2(root, ".prettierignore");
1264
- const existing = await readFile3(path2, "utf8").catch(() => "");
1295
+ const path = join2(root, ".prettierignore");
1296
+ const existing = await readFile3(path, "utf8").catch(() => "");
1265
1297
  const wanted = [CHANGELOG_FILE, CHANGELOG_MARKDOWN].filter((name) => !existing.split(`
1266
1298
  `).includes(name));
1267
1299
  if (wanted.length === 0)
@@ -1269,7 +1301,7 @@ var ignoreGenerated = async (root) => {
1269
1301
  const head = existing.trim() === "" ? `# Generated by absolute-changelog. Edit the entries, not these.
1270
1302
  ` : `${existing.trimEnd()}
1271
1303
  `;
1272
- await writeFile2(path2, `${head}${wanted.join(`
1304
+ await writeFile2(path, `${head}${wanted.join(`
1273
1305
  `)}
1274
1306
  `);
1275
1307
  };
@@ -1333,5 +1365,5 @@ await main().catch((error) => {
1333
1365
  process.exit(1);
1334
1366
  });
1335
1367
 
1336
- //# debugId=D18444F7EA93A50564756E2164756E21
1368
+ //# debugId=D76B4516336E7D1B64756E2164756E21
1337
1369
  //# sourceMappingURL=cli.js.map