deepline 0.1.123 → 0.1.125

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.
@@ -125,6 +125,7 @@ export type BundledPlayFileSuccess = {
125
125
  sourceFiles: Record<string, string>;
126
126
  filePath: string;
127
127
  playName: string | null;
128
+ playDescription: string | null;
128
129
  compilerManifest?: PlayCompilerManifest;
129
130
  packagedFiles: PlayLocalFileReference[];
130
131
  unresolvedFileReferences: PlayLocalFileDiscoveryError[];
@@ -153,6 +154,7 @@ type SourceGraphAnalysis = {
153
154
  graphHash: string;
154
155
  importPolicy: PlayImportPolicy;
155
156
  playName: string | null;
157
+ playDescription: string | null;
156
158
  importedPlayDependencies: ImportedPlayDependency[];
157
159
  };
158
160
 
@@ -249,6 +251,107 @@ function stripCommentsToSpaces(source: string): string {
249
251
  );
250
252
  }
251
253
 
254
+ function previousSignificantToken(source: string, index: number): string {
255
+ let cursor = index - 1;
256
+ while (cursor >= 0 && /\s/.test(source[cursor]!)) cursor -= 1;
257
+ if (cursor < 0) return '';
258
+ const char = source[cursor]!;
259
+ if (!/[A-Za-z0-9_$]/.test(char)) return char;
260
+ let start = cursor;
261
+ while (start > 0 && /[A-Za-z0-9_$]/.test(source[start - 1]!)) start -= 1;
262
+ return source.slice(start, cursor + 1);
263
+ }
264
+
265
+ function canStartRegexLiteral(source: string, slashIndex: number): boolean {
266
+ const token = previousSignificantToken(source, slashIndex);
267
+ return (
268
+ !token ||
269
+ token === 'return' ||
270
+ token === 'throw' ||
271
+ token === 'case' ||
272
+ token === 'delete' ||
273
+ token === 'typeof' ||
274
+ token === 'void' ||
275
+ token === 'instanceof' ||
276
+ /^(?:[({[=,:;!&|?+\-*~^<>%]|\.\.\.)$/.test(token)
277
+ );
278
+ }
279
+
280
+ function maskSourceForStructure(source: string): string {
281
+ const chars = source.split('');
282
+ const maskRange = (start: number, end: number) => {
283
+ for (let cursor = start; cursor < end; cursor += 1) {
284
+ if (chars[cursor] !== '\n') chars[cursor] = ' ';
285
+ }
286
+ };
287
+
288
+ for (let index = 0; index < source.length; index += 1) {
289
+ const char = source[index]!;
290
+ const next = source[index + 1];
291
+ if (char === '/' && next === '/') {
292
+ let end = index + 2;
293
+ while (end < source.length && source[end] !== '\n') end += 1;
294
+ maskRange(index, end);
295
+ index = end;
296
+ continue;
297
+ }
298
+ if (char === '/' && next === '*') {
299
+ const close = source.indexOf('*/', index + 2);
300
+ const end = close >= 0 ? close + 2 : source.length;
301
+ maskRange(index, end);
302
+ index = end - 1;
303
+ continue;
304
+ }
305
+ if (char === '"' || char === "'" || char === '`') {
306
+ const quote = char;
307
+ let end = index + 1;
308
+ let escaped = false;
309
+ while (end < source.length) {
310
+ const current = source[end]!;
311
+ if (escaped) {
312
+ escaped = false;
313
+ } else if (current === '\\') {
314
+ escaped = true;
315
+ } else if (current === quote) {
316
+ end += 1;
317
+ break;
318
+ }
319
+ end += 1;
320
+ }
321
+ maskRange(index, end);
322
+ index = end - 1;
323
+ continue;
324
+ }
325
+ if (char === '/' && canStartRegexLiteral(source, index)) {
326
+ let end = index + 1;
327
+ let escaped = false;
328
+ let inCharacterClass = false;
329
+ while (end < source.length) {
330
+ const current = source[end]!;
331
+ if (escaped) {
332
+ escaped = false;
333
+ } else if (current === '\\') {
334
+ escaped = true;
335
+ } else if (current === '[') {
336
+ inCharacterClass = true;
337
+ } else if (current === ']') {
338
+ inCharacterClass = false;
339
+ } else if (current === '/' && !inCharacterClass) {
340
+ end += 1;
341
+ while (/[A-Za-z]/.test(source[end] ?? '')) end += 1;
342
+ break;
343
+ } else if (current === '\n') {
344
+ break;
345
+ }
346
+ end += 1;
347
+ }
348
+ maskRange(index, end);
349
+ index = end - 1;
350
+ }
351
+ }
352
+ return chars.join('');
353
+ }
354
+
252
355
  function lineAndColumnAt(
253
356
  source: string,
254
357
  index: number,
@@ -339,11 +442,15 @@ function unquoteStringLiteral(literal: string): string | null {
339
442
  const trimmed = literal.trim();
340
443
  const quote = trimmed[0];
341
444
  if (
342
- (quote !== '"' && quote !== "'") ||
445
+ (quote !== '"' && quote !== "'" && quote !== '`') ||
343
446
  trimmed[trimmed.length - 1] !== quote
344
447
  ) {
345
448
  return null;
346
449
  }
450
+ if (quote === '`') {
451
+ const value = trimmed.slice(1, -1);
452
+ return value.includes('${') ? null : value;
453
+ }
347
454
  try {
348
455
  return JSON.parse(
349
456
  quote === '"'
@@ -356,25 +463,10 @@ function unquoteStringLiteral(literal: string): string | null {
356
463
  }
357
464
 
358
465
  function findMatchingBrace(source: string, openIndex: number): number {
466
+ const structuralSource = maskSourceForStructure(source);
359
467
  let depth = 0;
360
- let quote: string | null = null;
361
- let escaped = false;
362
- for (let index = openIndex; index < source.length; index += 1) {
363
- const char = source[index]!;
364
- if (quote) {
365
- if (escaped) {
366
- escaped = false;
367
- } else if (char === '\\') {
368
- escaped = true;
369
- } else if (char === quote) {
370
- quote = null;
371
- }
372
- continue;
373
- }
374
- if (char === '"' || char === "'" || char === '`') {
375
- quote = char;
376
- continue;
377
- }
468
+ for (let index = openIndex; index < structuralSource.length; index += 1) {
469
+ const char = structuralSource[index]!;
378
470
  if (char === '{') depth += 1;
379
471
  if (char === '}') {
380
472
  depth -= 1;
@@ -384,39 +476,264 @@ function findMatchingBrace(source: string, openIndex: number): number {
384
476
  return -1;
385
477
  }
386
478
 
479
+ function findMatchingParen(source: string, openIndex: number): number {
480
+ const structuralSource = maskSourceForStructure(source);
481
+ let depth = 0;
482
+ for (let index = openIndex; index < structuralSource.length; index += 1) {
483
+ const char = structuralSource[index]!;
484
+ if (char === '(') depth += 1;
485
+ if (char === ')') {
486
+ depth -= 1;
487
+ if (depth === 0) return index;
488
+ }
489
+ }
490
+ return -1;
491
+ }
492
+
493
+ function splitTopLevelArguments(source: string): string[] {
494
+ const args: string[] = [];
495
+ const structuralSource = maskSourceForStructure(source);
496
+ let start = 0;
497
+ let parenDepth = 0;
498
+ let braceDepth = 0;
499
+ let bracketDepth = 0;
500
+ for (let index = 0; index < structuralSource.length; index += 1) {
501
+ const char = structuralSource[index]!;
502
+ if (char === '(') parenDepth += 1;
503
+ if (char === ')') parenDepth -= 1;
504
+ if (char === '{') braceDepth += 1;
505
+ if (char === '}') braceDepth -= 1;
506
+ if (char === '[') bracketDepth += 1;
507
+ if (char === ']') bracketDepth -= 1;
508
+ if (
509
+ char === ',' &&
510
+ parenDepth === 0 &&
511
+ braceDepth === 0 &&
512
+ bracketDepth === 0
513
+ ) {
514
+ args.push(source.slice(start, index).trim());
515
+ start = index + 1;
516
+ }
517
+ }
518
+ const finalArg = source.slice(start).trim();
519
+ if (finalArg) args.push(finalArg);
520
+ return args;
521
+ }
522
+
523
+ function extractStringProperty(
524
+ objectSource: string,
525
+ propertyName: string,
526
+ ): string | null {
527
+ for (const entry of splitTopLevelArguments(objectSource)) {
528
+ const structuralEntry = stripCommentsToSpaces(entry).trim();
529
+ const match = structuralEntry.match(
530
+ new RegExp(
531
+ `^(?:${propertyName}|['"]${propertyName}['"])\\s*:\\s*((['"\`])(?:\\\\.|(?!\\2)[\\s\\S])*\\2)\\s*$`,
532
+ ),
533
+ );
534
+ const value = match?.[1] ? unquoteStringLiteral(match[1]) : null;
535
+ if (value?.trim()) return value.trim();
536
+ }
537
+ return null;
538
+ }
539
+
387
540
  export function extractDefinedPlayName(sourceCode: string): string | null {
388
- const source = stripCommentsToSpaces(sourceCode);
541
+ for (const call of findDefinePlayCalls(sourceCode)) {
542
+ const name = nameFromDefinePlayArguments(sourceCode, call.arguments);
543
+ if (name) return name;
544
+ }
545
+ return null;
546
+ }
547
+
548
+ export function extractDefinedPlayNameForExport(
549
+ sourceCode: string,
550
+ exportName: string,
551
+ ): string | null {
552
+ return findDefinedPlayMetadata(
553
+ sourceCode,
554
+ exportName,
555
+ nameFromDefinePlayArguments,
556
+ );
557
+ }
558
+
559
+ export function extractDefinedPlayDescription(
560
+ sourceCode: string,
561
+ ): string | null {
562
+ return findDefinedPlayMetadata(
563
+ sourceCode,
564
+ null,
565
+ descriptionFromDefinePlayArguments,
566
+ );
567
+ }
568
+
569
+ export function extractDefinedPlayDescriptionForExport(
570
+ sourceCode: string,
571
+ exportName: string,
572
+ ): string | null {
573
+ return findDefinedPlayMetadata(
574
+ sourceCode,
575
+ exportName,
576
+ descriptionFromDefinePlayArguments,
577
+ );
578
+ }
579
+
580
+ function findDefinedPlayMetadata(
581
+ sourceCode: string,
582
+ exportName: string | null,
583
+ getMetadata: (sourceCode: string, args: string[]) => string | null,
584
+ ): string | null {
585
+ const resolvedExportName =
586
+ exportName === 'default'
587
+ ? (findDefaultExportIdentifier(sourceCode) ?? exportName)
588
+ : exportName;
589
+ for (const call of findDefinePlayCalls(sourceCode)) {
590
+ if (resolvedExportName && call.exportName !== resolvedExportName) {
591
+ continue;
592
+ }
593
+ const value = getMetadata(sourceCode, call.arguments);
594
+ if (value) return value;
595
+ }
596
+ return null;
597
+ }
598
+
599
+ type DefinePlayCallMetadata = {
600
+ arguments: string[];
601
+ exportName: string | null;
602
+ };
603
+
604
+ function findDefinePlayCalls(sourceCode: string): DefinePlayCallMetadata[] {
605
+ const source = maskSourceForStructure(sourceCode);
389
606
  const callPattern =
390
607
  /(?:\b[A-Za-z_$][\w$]*\s*\.\s*)?\b(?:definePlay|defineWorkflow)\s*\(/g;
608
+ const calls: DefinePlayCallMetadata[] = [];
391
609
  for (const match of source.matchAll(callPattern)) {
392
- const openParen = match.index! + match[0].length - 1;
393
- const firstArgStart = openParen + 1;
394
- const firstNonSpace = source.slice(firstArgStart).search(/\S/);
395
- if (firstNonSpace < 0) continue;
396
- const argIndex = firstArgStart + firstNonSpace;
397
- const quote = source[argIndex];
398
- if (quote === '"' || quote === "'") {
399
- const literalMatch = source
400
- .slice(argIndex)
401
- .match(/^(['"])(?:\\.|(?!\1)[\s\S])*\1/);
402
- const value = literalMatch ? unquoteStringLiteral(literalMatch[0]) : null;
403
- if (value?.trim()) return value.trim();
404
- }
405
- if (quote === '{') {
406
- const closeBrace = findMatchingBrace(source, argIndex);
407
- if (closeBrace < 0) continue;
408
- const objectSource = source.slice(argIndex + 1, closeBrace);
409
- const idMatch = objectSource.match(
410
- /(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/,
411
- );
412
- if (idMatch?.[2]?.trim()) {
413
- return idMatch[2].trim();
414
- }
415
- }
610
+ const callStart = match.index!;
611
+ const openParen = callStart + match[0].length - 1;
612
+ const closeParen = findMatchingParen(sourceCode, openParen);
613
+ if (closeParen < 0) continue;
614
+ calls.push({
615
+ arguments: splitTopLevelArguments(
616
+ sourceCode.slice(openParen + 1, closeParen),
617
+ ),
618
+ exportName: exportNameForDefinePlayCall(sourceCode, callStart),
619
+ });
620
+ }
621
+ return calls;
622
+ }
623
+
624
+ function findDefaultExportIdentifier(sourceCode: string): string | null {
625
+ const source = maskSourceForStructure(sourceCode);
626
+ const pattern = /\bexport\s+default\s+([A-Za-z_$][\w$]*)/g;
627
+ for (const match of source.matchAll(pattern)) {
628
+ const afterIdentifier = match.index! + match[0].length;
629
+ const nextNonSpace = source.slice(afterIdentifier).match(/\S/)?.[0] ?? '';
630
+ if (nextNonSpace !== '(') return match[1] ?? null;
416
631
  }
417
632
  return null;
418
633
  }
419
634
 
635
+ function nameFromDefinePlayArguments(
636
+ sourceCode: string,
637
+ args: string[],
638
+ ): string | null {
639
+ const firstArg = args[0]?.trim();
640
+ if (!firstArg) return null;
641
+ if (/^(['"`])/.test(firstArg))
642
+ return unquoteStringLiteral(firstArg)?.trim() ?? null;
643
+ const objectSource = objectLiteralBodyFromArgument(sourceCode, firstArg);
644
+ if (objectSource) {
645
+ return extractStringProperty(objectSource, 'id');
646
+ }
647
+ return null;
648
+ }
649
+
650
+ function descriptionFromDefinePlayArguments(
651
+ sourceCode: string,
652
+ args: string[],
653
+ ): string | null {
654
+ const firstArgDescription = descriptionFromDefinePlayArgument(
655
+ sourceCode,
656
+ args[0],
657
+ );
658
+ if (firstArgDescription) return firstArgDescription;
659
+ for (let index = args.length - 1; index >= 2; index -= 1) {
660
+ const description = descriptionFromDefinePlayArgument(
661
+ sourceCode,
662
+ args[index],
663
+ );
664
+ if (description) return description;
665
+ }
666
+ return null;
667
+ }
668
+
669
+ function descriptionFromDefinePlayArgument(
670
+ sourceCode: string,
671
+ arg: string | undefined,
672
+ ): string | null {
673
+ const trimmed = arg?.trim();
674
+ if (!trimmed) return null;
675
+ const objectSource = objectLiteralBodyFromArgument(sourceCode, trimmed);
676
+ if (!objectSource) return null;
677
+ return extractStringProperty(objectSource, 'description');
678
+ }
679
+
680
+ function objectLiteralBodyFromArgument(
681
+ sourceCode: string,
682
+ arg: string,
683
+ ): string | null {
684
+ const trimmed = arg.trim();
685
+ if (trimmed.startsWith('{')) {
686
+ const closeBrace = findMatchingBrace(trimmed, 0);
687
+ return closeBrace >= 0 ? trimmed.slice(1, closeBrace) : trimmed.slice(1);
688
+ }
689
+ if (!/^[A-Za-z_$][\w$]*$/.test(trimmed)) return null;
690
+ return findTopLevelObjectLiteralBody(sourceCode, trimmed);
691
+ }
692
+
693
+ function escapeRegExp(value: string): string {
694
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
695
+ }
696
+
697
+ function findTopLevelObjectLiteralBody(
698
+ sourceCode: string,
699
+ identifier: string,
700
+ ): string | null {
701
+ const source = maskSourceForStructure(sourceCode);
702
+ const pattern = new RegExp(
703
+ `(?:^|[;\\n])\\s*(?:export\\s+)?(?:const|let|var)\\s+${escapeRegExp(
704
+ identifier,
705
+ )}(?:\\s*:[^=;]+)?\\s*=\\s*{`,
706
+ 'g',
707
+ );
708
+ for (const match of source.matchAll(pattern)) {
709
+ const openBrace = source.indexOf('{', match.index);
710
+ if (openBrace < 0) continue;
711
+ const closeBrace = findMatchingBrace(sourceCode, openBrace);
712
+ if (closeBrace < 0) continue;
713
+ return sourceCode.slice(openBrace + 1, closeBrace);
714
+ }
715
+ return null;
716
+ }
717
+
718
+ function exportNameForDefinePlayCall(
719
+ sourceCode: string,
720
+ callStart: number,
721
+ ): string | null {
722
+ const source = maskSourceForStructure(sourceCode);
723
+ const structuralPrefix = source
724
+ .slice(Math.max(0, callStart - 500), callStart)
725
+ .trimEnd();
726
+ if (/\bexport\s+default\s*$/.test(structuralPrefix)) return 'default';
727
+ const variableMatch = structuralPrefix.match(
728
+ /\bexport\s+(?:declare\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)(?:\s*:[^=;]+)?\s*=\s*$/,
729
+ );
730
+ if (variableMatch?.[1]) return variableMatch[1];
731
+ const localVariableMatch = structuralPrefix.match(
732
+ /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)(?:\s*:[^=;]+)?\s*=\s*$/,
733
+ );
734
+ return localVariableMatch?.[1] ?? null;
735
+ }
736
+
420
737
  function canonicalizeRootPlayNameForWorkersRuntimeHash(
421
738
  sourceCode: string,
422
739
  ): string {
@@ -1016,6 +1333,7 @@ function resolvePackageImport(
1016
1333
  async function analyzeSourceGraph(
1017
1334
  entryFile: string,
1018
1335
  adapter: PlayBundlingAdapter,
1336
+ exportName: string,
1019
1337
  ): Promise<SourceGraphAnalysis> {
1020
1338
  const absoluteEntryFile = await normalizeLocalPath(entryFile);
1021
1339
  const workspace = createPlayWorkspace(absoluteEntryFile);
@@ -1139,7 +1457,14 @@ async function analyzeSourceGraph(
1139
1457
  .sort((left, right) => left.filePath.localeCompare(right.filePath)),
1140
1458
  }),
1141
1459
  );
1142
- const playName = extractDefinedPlayName(sourceCode);
1460
+ const playName =
1461
+ extractDefinedPlayNameForExport(sourceCode, exportName) ??
1462
+ (exportName === 'default' ? extractDefinedPlayName(sourceCode) : null);
1463
+ const playDescription =
1464
+ extractDefinedPlayDescriptionForExport(sourceCode, exportName) ??
1465
+ (exportName === 'default'
1466
+ ? extractDefinedPlayDescription(sourceCode)
1467
+ : null);
1143
1468
 
1144
1469
  return {
1145
1470
  sourceCode,
@@ -1158,6 +1483,7 @@ async function analyzeSourceGraph(
1158
1483
  .sort((left, right) => left.name.localeCompare(right.name)),
1159
1484
  },
1160
1485
  playName,
1486
+ playDescription,
1161
1487
  importedPlayDependencies: [...importedPlayDependencies.values()].sort(
1162
1488
  (left, right) => left.filePath.localeCompare(right.filePath),
1163
1489
  ),
@@ -1588,7 +1914,11 @@ export async function bundlePlayFile(
1588
1914
  adapter.warnAboutNonDevelopmentBundling?.(absolutePath);
1589
1915
 
1590
1916
  try {
1591
- const analysis = await analyzeSourceGraph(absolutePath, adapter);
1917
+ const analysis = await analyzeSourceGraph(
1918
+ absolutePath,
1919
+ adapter,
1920
+ exportName,
1921
+ );
1592
1922
  analysis.graphHash =
1593
1923
  target === PLAY_ARTIFACT_KINDS.esmWorkers
1594
1924
  ? buildWorkersRuntimeGraphHash({
@@ -1677,6 +2007,7 @@ export async function bundlePlayFile(
1677
2007
  sourceFiles: analysis.sourceFiles,
1678
2008
  filePath: absolutePath,
1679
2009
  playName: analysis.playName,
2010
+ playDescription: analysis.playDescription,
1680
2011
  packagedFiles: discoveredFiles.files,
1681
2012
  unresolvedFileReferences: discoveredFiles.unresolved,
1682
2013
  importedPlayDependencies: analysis.importedPlayDependencies,
@@ -1749,6 +2080,7 @@ export async function bundlePlayFile(
1749
2080
  sourceFiles: analysis.sourceFiles,
1750
2081
  filePath: absolutePath,
1751
2082
  playName: analysis.playName,
2083
+ playDescription: analysis.playDescription,
1752
2084
  packagedFiles: discoveredFiles.files,
1753
2085
  unresolvedFileReferences: discoveredFiles.unresolved,
1754
2086
  importedPlayDependencies: analysis.importedPlayDependencies,