@velarscript/cli 0.12.1 → 0.13.0

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.
Files changed (140) hide show
  1. package/README.md +4 -12
  2. package/dist/browser-process-owner.d.ts +23 -0
  3. package/dist/browser-process-owner.d.ts.map +1 -1
  4. package/dist/browser-process-owner.js +46 -2
  5. package/dist/browser-process-owner.js.map +1 -1
  6. package/dist/browser-test-runner.d.ts.map +1 -1
  7. package/dist/browser-test-runner.js +110 -14
  8. package/dist/browser-test-runner.js.map +1 -1
  9. package/dist/cli.js +131 -68
  10. package/dist/cli.js.map +1 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +28 -4
  13. package/dist/config.js.map +1 -1
  14. package/dist/dev-server.d.ts +18 -0
  15. package/dist/dev-server.d.ts.map +1 -1
  16. package/dist/dev-server.js +168 -8
  17. package/dist/dev-server.js.map +1 -1
  18. package/dist/extension-metadata.d.ts +6 -0
  19. package/dist/extension-metadata.d.ts.map +1 -1
  20. package/dist/extension-metadata.js +70 -4
  21. package/dist/extension-metadata.js.map +1 -1
  22. package/dist/format-guard.d.ts +20 -0
  23. package/dist/format-guard.d.ts.map +1 -0
  24. package/dist/format-guard.js +13 -0
  25. package/dist/format-guard.js.map +1 -0
  26. package/dist/framework-host.d.ts.map +1 -1
  27. package/dist/framework-host.js +15 -2
  28. package/dist/framework-host.js.map +1 -1
  29. package/dist/language-server-bundle-entry.js +0 -2
  30. package/dist/language-server-bundle-entry.js.map +1 -1
  31. package/dist/language-server-tool.d.ts.map +1 -1
  32. package/dist/language-server-tool.js +0 -2
  33. package/dist/language-server-tool.js.map +1 -1
  34. package/dist/language-server.d.ts +1 -1
  35. package/dist/language-server.d.ts.map +1 -1
  36. package/dist/language-server.js +87 -293
  37. package/dist/language-server.js.map +1 -1
  38. package/dist/local-request-guard.d.ts +18 -0
  39. package/dist/local-request-guard.d.ts.map +1 -0
  40. package/dist/local-request-guard.js +75 -0
  41. package/dist/local-request-guard.js.map +1 -0
  42. package/dist/mechanical-fixer.d.ts.map +1 -1
  43. package/dist/mechanical-fixer.js +116 -2
  44. package/dist/mechanical-fixer.js.map +1 -1
  45. package/dist/module-assets.d.ts +23 -0
  46. package/dist/module-assets.d.ts.map +1 -1
  47. package/dist/module-assets.js +216 -14
  48. package/dist/module-assets.js.map +1 -1
  49. package/dist/node-application.d.ts +7 -2
  50. package/dist/node-application.d.ts.map +1 -1
  51. package/dist/node-application.js +44 -14
  52. package/dist/node-application.js.map +1 -1
  53. package/dist/node-runtime-dependencies.d.ts +2 -0
  54. package/dist/node-runtime-dependencies.d.ts.map +1 -0
  55. package/dist/node-runtime-dependencies.js +24 -0
  56. package/dist/node-runtime-dependencies.js.map +1 -0
  57. package/dist/npm.d.ts +29 -0
  58. package/dist/npm.d.ts.map +1 -1
  59. package/dist/npm.js +39 -2
  60. package/dist/npm.js.map +1 -1
  61. package/dist/ownership-graph.d.ts.map +1 -1
  62. package/dist/ownership-graph.js +155 -38
  63. package/dist/ownership-graph.js.map +1 -1
  64. package/dist/package-manager.d.ts.map +1 -1
  65. package/dist/package-manager.js +4 -1
  66. package/dist/package-manager.js.map +1 -1
  67. package/dist/preview-server.d.ts.map +1 -1
  68. package/dist/preview-server.js +12 -4
  69. package/dist/preview-server.js.map +1 -1
  70. package/dist/production-build.d.ts.map +1 -1
  71. package/dist/production-build.js +24 -6
  72. package/dist/production-build.js.map +1 -1
  73. package/dist/production-verifier.js +3 -0
  74. package/dist/production-verifier.js.map +1 -1
  75. package/dist/project-check.d.ts +14 -1
  76. package/dist/project-check.d.ts.map +1 -1
  77. package/dist/project-check.js +17 -4
  78. package/dist/project-check.js.map +1 -1
  79. package/dist/project-format.d.ts +9 -0
  80. package/dist/project-format.d.ts.map +1 -1
  81. package/dist/project-format.js +13 -0
  82. package/dist/project-format.js.map +1 -1
  83. package/dist/project-semantic.d.ts.map +1 -1
  84. package/dist/project-semantic.js +15 -4
  85. package/dist/project-semantic.js.map +1 -1
  86. package/dist/project.d.ts +25 -0
  87. package/dist/project.d.ts.map +1 -1
  88. package/dist/project.js +341 -42
  89. package/dist/project.js.map +1 -1
  90. package/dist/reproduction.d.ts +18 -0
  91. package/dist/reproduction.d.ts.map +1 -1
  92. package/dist/reproduction.js +47 -6
  93. package/dist/reproduction.js.map +1 -1
  94. package/dist/stable-order.d.ts +17 -0
  95. package/dist/stable-order.d.ts.map +1 -0
  96. package/dist/stable-order.js +19 -0
  97. package/dist/stable-order.js.map +1 -0
  98. package/dist/standard-modules.d.ts +2 -2
  99. package/dist/standard-modules.d.ts.map +1 -1
  100. package/dist/standard-modules.js +2 -2
  101. package/dist/standard-modules.js.map +1 -1
  102. package/dist/static-deployment.d.ts.map +1 -1
  103. package/dist/static-deployment.js +27 -0
  104. package/dist/static-deployment.js.map +1 -1
  105. package/dist/test-runner.d.ts.map +1 -1
  106. package/dist/test-runner.js +196 -100
  107. package/dist/test-runner.js.map +1 -1
  108. package/dist/test-worker.d.ts +64 -0
  109. package/dist/test-worker.d.ts.map +1 -0
  110. package/dist/test-worker.js +161 -0
  111. package/dist/test-worker.js.map +1 -0
  112. package/dist/typescript-declarations.d.ts +1 -1
  113. package/dist/typescript-declarations.d.ts.map +1 -1
  114. package/dist/typescript-declarations.js +233 -21
  115. package/dist/typescript-declarations.js.map +1 -1
  116. package/dist/unowned-errors.d.ts +15 -2
  117. package/dist/unowned-errors.d.ts.map +1 -1
  118. package/dist/unowned-errors.js +13 -2
  119. package/dist/unowned-errors.js.map +1 -1
  120. package/dist/version.d.ts +1 -1
  121. package/dist/version.js +1 -1
  122. package/dist/workspace-index.d.ts.map +1 -1
  123. package/dist/workspace-index.js +42 -17
  124. package/dist/workspace-index.js.map +1 -1
  125. package/package.json +7 -8
  126. package/skill/ai-skill-node.md +74 -9
  127. package/skill/ai-skill-web.md +51 -1
  128. package/skill/ai-skill.md +36 -13
  129. package/dist/official-script-language-service.d.ts +0 -2
  130. package/dist/official-script-language-service.d.ts.map +0 -1
  131. package/dist/official-script-language-service.js +0 -10
  132. package/dist/official-script-language-service.js.map +0 -1
  133. package/dist/official-tool-assets.d.ts +0 -3
  134. package/dist/official-tool-assets.d.ts.map +0 -1
  135. package/dist/official-tool-assets.js +0 -60
  136. package/dist/official-tool-assets.js.map +0 -1
  137. package/dist/script-language-service.d.ts +0 -82
  138. package/dist/script-language-service.d.ts.map +0 -1
  139. package/dist/script-language-service.js +0 -18
  140. package/dist/script-language-service.js.map +0 -1
@@ -67,7 +67,7 @@ export async function loadTypeScriptDeclarations(source, importerPath) {
67
67
  return null;
68
68
  }
69
69
  try {
70
- const bridge = await loadTypeScriptDeclarationGraph(root, path, packageName);
70
+ const bridge = await loadTypeScriptDeclarationGraph(root, path, packageName, source);
71
71
  return { ...bridge, dependencies: [...unique([manifestPath, ...bridge.dependencies])].sort() };
72
72
  }
73
73
  catch {
@@ -113,10 +113,16 @@ function declarationFileCandidates(path) {
113
113
  function isDeclarationFile(path) {
114
114
  return path.endsWith(".d.ts") || path.endsWith(".d.mts") || path.endsWith(".d.cts");
115
115
  }
116
- async function loadTypeScriptDeclarationGraph(root, entry, packageSource) {
116
+ async function loadTypeScriptDeclarationGraph(root, entry, packageSource, importedSpecifier = packageSource) {
117
117
  const [rootPath, entryPath] = await Promise.all([realpath(root), realpath(entry)]);
118
118
  if (!insideRoot(rootPath, entryPath))
119
119
  throw new Error("TypeScript declaration entry escapes its package root");
120
+ // The specifiers a `declare module "…"` block may legitimately declare: the
121
+ // package this graph belongs to, in the legacy ambient spelling. A subpath
122
+ // import declares its own specifier, so `pkg/sub.d.ts` writing
123
+ // `declare module "pkg/sub" { … }` is that subpath's own contract and must
124
+ // not be excised as somebody else's ambient block.
125
+ const ownModules = new Set([importedSpecifier, packageSource, packageNameOf(packageSource)]);
120
126
  const cache = new Map();
121
127
  const visiting = new Set();
122
128
  const counted = new Set();
@@ -150,7 +156,7 @@ async function loadTypeScriptDeclarationGraph(root, entry, packageSource) {
150
156
  const importedTypes = new Map();
151
157
  const importedRegistry = new Map();
152
158
  const importWarnings = [];
153
- for (const declarationImport of declarationImports(source)) {
159
+ for (const declarationImport of declarationImports(source, ownModules)) {
154
160
  if (declarationImport.unsupported) {
155
161
  importWarnings.push(`Namespace declaration import from '${declarationImport.source}' is outside the VelarScript declaration bridge and was kept as unknown`);
156
162
  continue;
@@ -176,7 +182,7 @@ async function loadTypeScriptDeclarationGraph(root, entry, packageSource) {
176
182
  }
177
183
  }
178
184
  }
179
- const local = parseTypeScriptDeclarations(source, path, origin, true, importedTypes, importedRegistry);
185
+ const local = parseTypeScriptDeclarations(source, path, origin, true, importedTypes, importedRegistry, ownModules);
180
186
  const exports = new Map(local.exports);
181
187
  const typeExports = new Map(local.typeExports);
182
188
  const classes = new Map(local.classes);
@@ -184,7 +190,7 @@ async function loadTypeScriptDeclarationGraph(root, entry, packageSource) {
184
190
  const warnings = [...importWarnings, ...local.warnings];
185
191
  const explicit = new Set(exports.keys());
186
192
  const explicitTypes = new Set(typeExports.keys());
187
- const reexports = declarationReexports(source);
193
+ const reexports = declarationReexports(source, ownModules);
188
194
  for (const reexport of reexports.filter((item) => item.names !== null)) {
189
195
  const childPath = await resolveDeclarationReexport(rootPath, path, reexport.source);
190
196
  if (!childPath) {
@@ -298,8 +304,8 @@ async function loadTypeScriptDeclarationGraph(root, entry, packageSource) {
298
304
  function emptyDeclarationBridge(path, warning) {
299
305
  return { path, dependencies: [path], exports: new Map(), typeExports: new Map(), classes: new Map(), classRegistry: new Map(), warnings: [warning] };
300
306
  }
301
- function declarationReexports(source) {
302
- const text = source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/\/\/[^\n]*/gu, "");
307
+ function declarationReexports(source, ownModules) {
308
+ const text = excludeAmbientBlocks(stripDeclarationComments(source), [], ownModules);
303
309
  const output = [];
304
310
  const named = /export\s+(type\s+)?\{([\s\S]*?)\}\s*from\s*["']([^"']+)["']\s*;?/gu;
305
311
  for (const match of text.matchAll(named)) {
@@ -317,8 +323,8 @@ function declarationReexports(source) {
317
323
  output.push({ source: match[1], names: null });
318
324
  return output;
319
325
  }
320
- function declarationImports(source) {
321
- const text = source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/\/\/[^\n]*/gu, "");
326
+ function declarationImports(source, ownModules) {
327
+ const text = excludeAmbientBlocks(stripDeclarationComments(source), [], ownModules);
322
328
  const output = [];
323
329
  const pattern = /^[ \t]*import\s+(?:type\s+)?([\s\S]*?)\s+from\s*["']([^"']+)["']\s*;?/gmu;
324
330
  for (const match of text.matchAll(pattern)) {
@@ -394,12 +400,12 @@ function renameDeclarationExport(type, name) {
394
400
  function renameDeclarationType(type, name) {
395
401
  return type.kind === "class" ? { ...type, name } : type;
396
402
  }
397
- export function parseTypeScriptDeclarations(source, path = "<types>", moduleSource = path, reexportsHandled = false, importedTypes = new Map(), importedClassRegistry = new Map()) {
403
+ export function parseTypeScriptDeclarations(source, path = "<types>", moduleSource = path, reexportsHandled = false, importedTypes = new Map(), importedClassRegistry = new Map(), ownModules = new Set()) {
398
404
  if (Buffer.byteLength(source, "utf8") > MAX_TYPESCRIPT_DECLARATION_BYTES) {
399
405
  throw new RangeError(`${path} exceeds the 2 MiB TypeScript declaration bridge limit`);
400
406
  }
401
- const text = source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/\/\/[^\n]*/gu, "");
402
407
  const warnings = [];
408
+ const text = excludeAmbientBlocks(stripDeclarationComments(source), warnings, ownModules);
403
409
  const aliases = new Map();
404
410
  const interfaces = new Map();
405
411
  const invalidInterfaces = new Set();
@@ -890,7 +896,7 @@ function parseClassDeclaration(declaration, parse, warnings, moduleSource, class
890
896
  target.set(accessor.name, contract);
891
897
  continue;
892
898
  }
893
- const property = /^((?:(?:public|static|readonly|declare)\s+)*)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/u.exec(member);
899
+ const property = /^((?:(?:public|static|readonly|declare)\s+)*)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/su.exec(member);
894
900
  if (property) {
895
901
  const modifiers = new Set((property[1] ?? "").trim().split(/\s+/u).filter(Boolean));
896
902
  const target = modifiers.has("static") ? staticFields : fields;
@@ -1004,6 +1010,15 @@ function parseRestElementType(source, parse) {
1004
1010
  const generic = /^(?:Array|ReadonlyArray)\s*<([\s\S]+)>$/u.exec(value);
1005
1011
  return generic && splitTopLevel(generic[1] ?? "", ",").length === 1 ? parse(generic[1] ?? "unknown") : null;
1006
1012
  }
1013
+ /**
1014
+ * Drops TypeScript's `this` pseudo-parameter. It only types the receiver, it
1015
+ * carries no runtime argument, and it is legal only in first position — so
1016
+ * counting it as positional gives every correct call site an arity error with
1017
+ * no value the author could supply.
1018
+ */
1019
+ function withoutThisParameter(parts) {
1020
+ return parts.length > 0 && /^this\s*:/u.test(parts[0]) ? parts.slice(1) : parts;
1021
+ }
1007
1022
  function parseParameters(source, parse, warnings) {
1008
1023
  if (!source.trim())
1009
1024
  return { types: [], required: 0 };
@@ -1012,9 +1027,9 @@ function parseParameters(source, parse, warnings) {
1012
1027
  let optionalSeen = false;
1013
1028
  let invalid = false;
1014
1029
  let rest;
1015
- const parts = splitTopLevel(source, ",");
1030
+ const parts = withoutThisParameter(splitTopLevel(source, ","));
1016
1031
  for (const [index, part] of parts.entries()) {
1017
- const match = /^(\.\.\.)?[A-Za-z_$][\w$]*(\?)?\s*:\s*(.+)$/u.exec(part.trim());
1032
+ const match = /^(\.\.\.)?[A-Za-z_$][\w$]*(\?)?\s*:\s*(.+)$/su.exec(part.trim());
1018
1033
  if (!match) {
1019
1034
  warnings.push(`Unsupported parameter declaration '${part.trim()}' was kept as unknown`);
1020
1035
  types.push(unsupportedType);
@@ -1058,9 +1073,9 @@ function parseClassConstructorParameters(source, parse, warnings) {
1058
1073
  let required = 0;
1059
1074
  let optionalSeen = false;
1060
1075
  let rest;
1061
- const parts = splitTopLevel(source, ",");
1076
+ const parts = withoutThisParameter(splitTopLevel(source, ","));
1062
1077
  for (const [index, part] of parts.entries()) {
1063
- const match = /^((?:(?:public|private|protected|readonly)\s+)*)(\.\.\.)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/u.exec(part.trim());
1078
+ const match = /^((?:(?:public|private|protected|readonly)\s+)*)(\.\.\.)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/su.exec(part.trim());
1064
1079
  if (!match) {
1065
1080
  warnings.push(`Unsupported constructor parameter declaration '${part.trim()}' was kept as unknown`);
1066
1081
  types.push(unsupportedType);
@@ -1277,7 +1292,7 @@ function objectType(source, aliases, warnings, stack, classTypes, direction) {
1277
1292
  const optionalFields = new Set();
1278
1293
  let invalidInputShape = false;
1279
1294
  for (const part of splitFields(source)) {
1280
- const match = /^(readonly\s+)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/u.exec(part.trim());
1295
+ const match = /^(readonly\s+)?([A-Za-z_$][\w$]*)(\?)?\s*:\s*(.+)$/su.exec(part.trim());
1281
1296
  if (match) {
1282
1297
  const name = match[2];
1283
1298
  const type = parseTsType(match[4] ?? "unknown", aliases, warnings, stack, classTypes, match[1] ? direction : "invariant");
@@ -1405,6 +1420,108 @@ function skipWhitespace(source, start) {
1405
1420
  cursor += 1;
1406
1421
  return cursor;
1407
1422
  }
1423
+ /**
1424
+ * Finds the closing quote of the string literal that opens at `start`, or the
1425
+ * last character it can belong to when the literal is never closed.
1426
+ */
1427
+ function closingQuote(source, start) {
1428
+ const quote = source[start];
1429
+ for (let index = start + 1; index < source.length; index += 1) {
1430
+ const character = source[index];
1431
+ if (character === "\\") {
1432
+ index += 1;
1433
+ continue;
1434
+ }
1435
+ if (character === quote)
1436
+ return index;
1437
+ if (quote !== "`" && character === "\n")
1438
+ return index - 1;
1439
+ }
1440
+ return source.length - 1;
1441
+ }
1442
+ /**
1443
+ * Removes comments from a declaration file without disturbing string literal
1444
+ * types. A `.d.ts` routinely writes `/*`, `*` + `/` or `//` inside a string
1445
+ * literal type — URL literals, glob literals, template literal types — and a
1446
+ * regex stripper eats every real declaration between two of them with no word
1447
+ * to the author. Comment bytes become spaces so that every offset recorded
1448
+ * against the result still addresses the same character of `source`.
1449
+ */
1450
+ function stripDeclarationComments(source) {
1451
+ let output = "";
1452
+ let index = 0;
1453
+ while (index < source.length) {
1454
+ const character = source[index];
1455
+ if (character === "\"" || character === "'" || character === "`") {
1456
+ const end = closingQuote(source, index);
1457
+ output += source.slice(index, end + 1);
1458
+ index = end + 1;
1459
+ continue;
1460
+ }
1461
+ if (character === "/" && source[index + 1] === "*") {
1462
+ const close = source.indexOf("*/", index + 2);
1463
+ const end = close < 0 ? source.length : close + 2;
1464
+ output += " ".repeat(end - index);
1465
+ index = end;
1466
+ continue;
1467
+ }
1468
+ if (character === "/" && source[index + 1] === "/") {
1469
+ let end = index;
1470
+ while (end < source.length && source[end] !== "\n")
1471
+ end += 1;
1472
+ output += " ".repeat(end - index);
1473
+ index = end;
1474
+ continue;
1475
+ }
1476
+ output += character;
1477
+ index += 1;
1478
+ }
1479
+ return output;
1480
+ }
1481
+ const ambientBlockPattern = /(?:^|[;{}\r\n])[\t ]*((?:export\s+)?declare\s+(?:global|namespace\s+[A-Za-z_$][\w$.]*|module\s+(?:"[^"\r\n]*"|'[^'\r\n]*'|[A-Za-z_$][\w$.]*))|(?:export\s+)?(?:namespace|module)\s+[A-Za-z_$][\w$.]*)\s*\{/gu;
1482
+ /**
1483
+ * Blanks `declare global`, `declare namespace X` and `declare module "x"`
1484
+ * bodies. The declaration sweeps have no concept of a nested declaration
1485
+ * scope, so without this an `export` inside one of these blocks is read as a
1486
+ * module export the package does not have — a clean `velar check` that links
1487
+ * to a host `SyntaxError` at run time. The span becomes spaces rather than
1488
+ * disappearing so recorded offsets stay valid, and each excised block is
1489
+ * reported.
1490
+ *
1491
+ * `ownModules` names the specifiers this file is the declaration for. A
1492
+ * `declare module "self"` block is that package's own contract in the legacy
1493
+ * ambient spelling, so only its head and braces are blanked and its members
1494
+ * stay module members.
1495
+ */
1496
+ function excludeAmbientBlocks(source, warnings, ownModules = new Set()) {
1497
+ let text = source;
1498
+ let blankedUntil = 0;
1499
+ for (const match of source.matchAll(ambientBlockPattern)) {
1500
+ const head = match[1];
1501
+ const start = match.index + match[0].indexOf(head);
1502
+ if (start < blankedUntil)
1503
+ continue;
1504
+ const open = match.index + match[0].length - 1;
1505
+ const close = matchingDelimiter(source, open, "{", "}");
1506
+ const end = close < 0 ? source.length : close + 1;
1507
+ const label = head.replace(/\s+/gu, " ");
1508
+ const declaredModule = /^(?:export\s+)?declare\s+module\s+["'](.+)["']$/u.exec(label);
1509
+ if (declaredModule && ownModules.has(declaredModule[1]) && close >= 0) {
1510
+ // The kept body is swept again rather than left to this loop, because a
1511
+ // block nested on the same line as this one has no statement start in
1512
+ // front of it once the outer match has consumed the opening brace.
1513
+ text = `${text.slice(0, start)}${" ".repeat(open + 1 - start)}${excludeAmbientBlocks(text.slice(open + 1, close), warnings, ownModules)} ${text.slice(close + 1)}`;
1514
+ blankedUntil = close + 1;
1515
+ continue;
1516
+ }
1517
+ warnings.push(close < 0
1518
+ ? `Ambient '${label}' block has an unclosed body, so it and the rest of the declaration file were ignored`
1519
+ : `Ambient '${label}' block is outside the VelarScript declaration bridge and its declarations were ignored`);
1520
+ text = text.slice(0, start) + " ".repeat(end - start) + text.slice(end);
1521
+ blankedUntil = end;
1522
+ }
1523
+ return text;
1524
+ }
1408
1525
  function matchingDelimiter(source, start, open, close) {
1409
1526
  let depth = 0;
1410
1527
  let quote = "";
@@ -1466,19 +1583,110 @@ function topLevelTerminator(source, start, terminator) {
1466
1583
  }
1467
1584
  return -1;
1468
1585
  }
1586
+ /**
1587
+ * Text that starts a fresh interface, object type, or class member. A body may
1588
+ * terminate its members with semicolons, with newlines, or with a mix of the
1589
+ * two, so a newline ends a member only when what follows opens a new one
1590
+ * rather than continuing the type that is being written.
1591
+ */
1592
+ const memberHeadPattern = /^(?:(?:readonly|public|private|protected|static|override|abstract|declare)\s+)*(?:new\s*\(|constructor\s*\(|(?:get|set)\s+[A-Za-z_$][\w$]*\s*\(|\[|\(|<|[A-Za-z_$][\w$]*\s*\??\s*[:(<]|["'][^"'\r\n]*["']\s*\??\s*[:(])/u;
1593
+ /**
1594
+ * Reports whether a member that reads as `pending` is plainly unfinished, so a
1595
+ * newline inside it is a wrap rather than a terminator.
1596
+ */
1597
+ function memberContinues(pending) {
1598
+ const trimmed = pending.trimEnd();
1599
+ if (!trimmed)
1600
+ return true;
1601
+ if (trimmed.endsWith("=>"))
1602
+ return true;
1603
+ if (/\b(?:extends|keyof|typeof|infer|in|is|new|readonly)$/u.test(trimmed))
1604
+ return true;
1605
+ return /[:,|&=<([{?+\-.]$/u.test(trimmed);
1606
+ }
1607
+ /**
1608
+ * Splits a declaration body into members on its real terminators: a top-level
1609
+ * `;` or `,`, or a top-level newline that separates a finished member from the
1610
+ * head of the next one. Choosing one separator for the whole body glues the
1611
+ * member after a semicolon-less method onto it, which deletes that member from
1612
+ * the package's contract without a word.
1613
+ */
1469
1614
  function splitFields(source) {
1470
- const semicolons = splitTopLevel(source, ";");
1471
- return topLevelTerminator(source, 0, ";") >= 0 ? semicolons : source.split(/\n+/u);
1615
+ const output = [];
1616
+ let start = 0;
1617
+ let angle = 0;
1618
+ let round = 0;
1619
+ let square = 0;
1620
+ let brace = 0;
1621
+ let quote = "";
1622
+ const take = (end) => {
1623
+ const member = source.slice(start, end).trim();
1624
+ if (member)
1625
+ output.push(member);
1626
+ start = end + 1;
1627
+ };
1628
+ for (let index = 0; index < source.length; index += 1) {
1629
+ const character = source[index];
1630
+ if (quote) {
1631
+ if (character === "\\")
1632
+ index += 1;
1633
+ else if (character === quote)
1634
+ quote = "";
1635
+ continue;
1636
+ }
1637
+ if (character === "\"" || character === "'" || character === "`")
1638
+ quote = character;
1639
+ else if (character === "<")
1640
+ angle += 1;
1641
+ else if (character === ">")
1642
+ angle = Math.max(0, angle - 1);
1643
+ else if (character === "(")
1644
+ round += 1;
1645
+ else if (character === ")")
1646
+ round = Math.max(0, round - 1);
1647
+ else if (character === "[")
1648
+ square += 1;
1649
+ else if (character === "]")
1650
+ square = Math.max(0, square - 1);
1651
+ else if (character === "{")
1652
+ brace += 1;
1653
+ else if (character === "}")
1654
+ brace = Math.max(0, brace - 1);
1655
+ else if (angle === 0 && round === 0 && square === 0 && brace === 0) {
1656
+ if (character === ";" || character === ",")
1657
+ take(index);
1658
+ else if (character === "\n"
1659
+ && !memberContinues(source.slice(start, index))
1660
+ && memberHeadPattern.test(source.slice(index + 1).trimStart())) {
1661
+ take(index);
1662
+ }
1663
+ }
1664
+ }
1665
+ const last = source.slice(start).trim();
1666
+ if (last)
1667
+ output.push(last);
1668
+ return output;
1472
1669
  }
1473
1670
  function splitTopLevel(source, separator) {
1474
1671
  const output = [];
1475
1672
  let start = 0;
1476
1673
  let angle = 0;
1477
1674
  let round = 0;
1675
+ let square = 0;
1478
1676
  let brace = 0;
1677
+ let quote = "";
1479
1678
  for (let index = 0; index < source.length; index += 1) {
1480
1679
  const character = source[index];
1481
- if (character === "<")
1680
+ if (quote) {
1681
+ if (character === "\\")
1682
+ index += 1;
1683
+ else if (character === quote)
1684
+ quote = "";
1685
+ continue;
1686
+ }
1687
+ if (character === "\"" || character === "'" || character === "`")
1688
+ quote = character;
1689
+ else if (character === "<")
1482
1690
  angle += 1;
1483
1691
  else if (character === ">")
1484
1692
  angle = Math.max(0, angle - 1);
@@ -1486,11 +1694,15 @@ function splitTopLevel(source, separator) {
1486
1694
  round += 1;
1487
1695
  else if (character === ")")
1488
1696
  round = Math.max(0, round - 1);
1697
+ else if (character === "[")
1698
+ square += 1;
1699
+ else if (character === "]")
1700
+ square = Math.max(0, square - 1);
1489
1701
  else if (character === "{")
1490
1702
  brace += 1;
1491
1703
  else if (character === "}")
1492
1704
  brace = Math.max(0, brace - 1);
1493
- else if (character === separator && angle === 0 && round === 0 && brace === 0) {
1705
+ else if (character === separator && angle === 0 && round === 0 && square === 0 && brace === 0) {
1494
1706
  output.push(source.slice(start, index).trim());
1495
1707
  start = index + 1;
1496
1708
  }