@quenty/nevermore-cli 4.18.0 → 4.19.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 (68) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/commands/tools-command/ci-post-test-results.d.ts.map +1 -1
  3. package/dist/commands/tools-command/ci-post-test-results.js +18 -0
  4. package/dist/commands/tools-command/ci-post-test-results.js.map +1 -1
  5. package/dist/commands/tools-command/strip-sourcemap-jest-command.d.ts.map +1 -1
  6. package/dist/commands/tools-command/strip-sourcemap-jest-command.js.map +1 -1
  7. package/dist/utils/linting/parsers/moonwave-parser.d.ts.map +1 -1
  8. package/dist/utils/linting/parsers/moonwave-parser.js +2 -6
  9. package/dist/utils/linting/parsers/moonwave-parser.js.map +1 -1
  10. package/dist/utils/sourcemap/index.d.ts +4 -0
  11. package/dist/utils/sourcemap/index.d.ts.map +1 -0
  12. package/dist/utils/sourcemap/index.js +3 -0
  13. package/dist/utils/sourcemap/index.js.map +1 -0
  14. package/dist/utils/sourcemap/sourcemap-loader.d.ts +10 -0
  15. package/dist/utils/sourcemap/sourcemap-loader.d.ts.map +1 -0
  16. package/dist/utils/sourcemap/sourcemap-loader.js +29 -0
  17. package/dist/utils/sourcemap/sourcemap-loader.js.map +1 -0
  18. package/dist/utils/sourcemap/sourcemap-resolver.d.ts +30 -0
  19. package/dist/utils/sourcemap/sourcemap-resolver.d.ts.map +1 -0
  20. package/dist/utils/sourcemap/sourcemap-resolver.js +58 -0
  21. package/dist/utils/sourcemap/sourcemap-resolver.js.map +1 -0
  22. package/dist/utils/sourcemap/sourcemap-resolver.test.d.ts +2 -0
  23. package/dist/utils/sourcemap/sourcemap-resolver.test.d.ts.map +1 -0
  24. package/dist/utils/sourcemap/sourcemap-resolver.test.js +106 -0
  25. package/dist/utils/sourcemap/sourcemap-resolver.test.js.map +1 -0
  26. package/dist/utils/sourcemap/sourcemap-types.d.ts +13 -0
  27. package/dist/utils/sourcemap/sourcemap-types.d.ts.map +1 -0
  28. package/dist/utils/sourcemap/sourcemap-types.js +2 -0
  29. package/dist/utils/sourcemap/sourcemap-types.js.map +1 -0
  30. package/dist/utils/testing/parsers/index.d.ts +3 -0
  31. package/dist/utils/testing/parsers/index.d.ts.map +1 -0
  32. package/dist/utils/testing/parsers/index.js +3 -0
  33. package/dist/utils/testing/parsers/index.js.map +1 -0
  34. package/dist/utils/testing/parsers/jest-lua-parser.d.ts +41 -0
  35. package/dist/utils/testing/parsers/jest-lua-parser.d.ts.map +1 -0
  36. package/dist/utils/testing/parsers/jest-lua-parser.js +222 -0
  37. package/dist/utils/testing/parsers/jest-lua-parser.js.map +1 -0
  38. package/dist/utils/testing/parsers/jest-lua-parser.test.d.ts +2 -0
  39. package/dist/utils/testing/parsers/jest-lua-parser.test.d.ts.map +1 -0
  40. package/dist/utils/testing/parsers/jest-lua-parser.test.js +297 -0
  41. package/dist/utils/testing/parsers/jest-lua-parser.test.js.map +1 -0
  42. package/dist/utils/testing/parsers/roblox-path-resolver.d.ts +24 -0
  43. package/dist/utils/testing/parsers/roblox-path-resolver.d.ts.map +1 -0
  44. package/dist/utils/testing/parsers/roblox-path-resolver.js +71 -0
  45. package/dist/utils/testing/parsers/roblox-path-resolver.js.map +1 -0
  46. package/dist/utils/testing/parsers/roblox-path-resolver.test.d.ts +2 -0
  47. package/dist/utils/testing/parsers/roblox-path-resolver.test.d.ts.map +1 -0
  48. package/dist/utils/testing/parsers/roblox-path-resolver.test.js +76 -0
  49. package/dist/utils/testing/parsers/roblox-path-resolver.test.js.map +1 -0
  50. package/dist/utils/testing/test-log-parser.d.ts.map +1 -1
  51. package/dist/utils/testing/test-log-parser.js +2 -1
  52. package/dist/utils/testing/test-log-parser.js.map +1 -1
  53. package/package.json +6 -6
  54. package/src/commands/tools-command/ci-post-test-results.ts +26 -0
  55. package/src/commands/tools-command/strip-sourcemap-jest-command.ts +1 -7
  56. package/src/utils/linting/parsers/moonwave-parser.ts +2 -7
  57. package/src/utils/sourcemap/index.ts +3 -0
  58. package/src/utils/sourcemap/sourcemap-loader.ts +33 -0
  59. package/src/utils/sourcemap/sourcemap-resolver.test.ts +150 -0
  60. package/src/utils/sourcemap/sourcemap-resolver.ts +75 -0
  61. package/src/utils/sourcemap/sourcemap-types.ts +12 -0
  62. package/src/utils/testing/parsers/index.ts +2 -0
  63. package/src/utils/testing/parsers/jest-lua-parser.test.ts +350 -0
  64. package/src/utils/testing/parsers/jest-lua-parser.ts +279 -0
  65. package/src/utils/testing/parsers/roblox-path-resolver.test.ts +129 -0
  66. package/src/utils/testing/parsers/roblox-path-resolver.ts +85 -0
  67. package/src/utils/testing/test-log-parser.ts +3 -1
  68. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,129 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { resolveRobloxTestPath } from './roblox-path-resolver.js';
3
+ import { SourcemapResolver } from '../../sourcemap/index.js';
4
+ import type { SourcemapNode } from '../../sourcemap/index.js';
5
+
6
+ describe('resolveRobloxTestPath', () => {
7
+ it('resolves a standard ServerScriptService path', () => {
8
+ expect(
9
+ resolveRobloxTestPath(
10
+ 'ServerScriptService.observablecollection.Shared.ObservableList.spec'
11
+ )
12
+ ).toBe('src/observablecollection/src/Shared/ObservableList.spec.lua');
13
+ });
14
+
15
+ it('resolves a nested subdirectory path', () => {
16
+ expect(
17
+ resolveRobloxTestPath(
18
+ 'ServerScriptService.maid.Shared.Maid.spec'
19
+ )
20
+ ).toBe('src/maid/src/Shared/Maid.spec.lua');
21
+ });
22
+
23
+ it('resolves a deeply nested path', () => {
24
+ expect(
25
+ resolveRobloxTestPath(
26
+ 'ServerScriptService.blend.Client.Blend.Spring.SpringObject.spec'
27
+ )
28
+ ).toBe('src/blend/src/Client/Blend/Spring/SpringObject.spec.lua');
29
+ });
30
+
31
+ it('handles missing ServerScriptService prefix as fallback', () => {
32
+ expect(
33
+ resolveRobloxTestPath('observablecollection.Shared.ObservableList.spec')
34
+ ).toBe('src/observablecollection/src/Shared/ObservableList.spec.lua');
35
+ });
36
+
37
+ it('strips :LINE suffix', () => {
38
+ expect(
39
+ resolveRobloxTestPath(
40
+ 'ServerScriptService.observablecollection.Shared.ObservableList.spec:45'
41
+ )
42
+ ).toBe('src/observablecollection/src/Shared/ObservableList.spec.lua');
43
+ });
44
+
45
+ it('handles path without ServerScriptService prefix and with :LINE suffix', () => {
46
+ expect(
47
+ resolveRobloxTestPath('maid.Shared.Maid.spec:23')
48
+ ).toBe('src/maid/src/Shared/Maid.spec.lua');
49
+ });
50
+
51
+ it('handles a bare package slug', () => {
52
+ expect(
53
+ resolveRobloxTestPath('ServerScriptService.maid')
54
+ ).toBe('src/maid/src');
55
+ });
56
+
57
+ it('handles a single-level spec path', () => {
58
+ expect(
59
+ resolveRobloxTestPath('ServerScriptService.maid.Maid.spec')
60
+ ).toBe('src/maid/src/Maid.spec.lua');
61
+ });
62
+
63
+ describe('with sourcemap resolver', () => {
64
+ const repoRoot = '/repo';
65
+
66
+ const sourcemap: SourcemapNode = {
67
+ name: 'Nevermore',
68
+ className: 'DataModel',
69
+ children: [
70
+ {
71
+ name: 'mypkg',
72
+ className: 'Folder',
73
+ filePaths: [`${repoRoot}/src/mypkg/default.project.json`],
74
+ children: [
75
+ {
76
+ name: 'Shared',
77
+ className: 'Folder',
78
+ children: [
79
+ {
80
+ name: 'MyModule',
81
+ className: 'ModuleScript',
82
+ filePaths: [`${repoRoot}/src/mypkg/src/Shared/MyModule.lua`],
83
+ children: [
84
+ {
85
+ name: 'MyModule.spec',
86
+ className: 'ModuleScript',
87
+ filePaths: [
88
+ `${repoRoot}/src/mypkg/src/Shared/MyModule.spec.lua`,
89
+ ],
90
+ },
91
+ ],
92
+ },
93
+ ],
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ };
99
+
100
+ const resolver = SourcemapResolver.fromSourcemap(sourcemap, repoRoot);
101
+
102
+ it('uses sourcemap when path is found', () => {
103
+ expect(
104
+ resolveRobloxTestPath(
105
+ 'ServerScriptService.mypkg.Shared.MyModule.MyModule.spec',
106
+ resolver
107
+ )
108
+ ).toBe('src/mypkg/src/Shared/MyModule.spec.lua');
109
+ });
110
+
111
+ it('falls back to heuristic when sourcemap has no mapping', () => {
112
+ expect(
113
+ resolveRobloxTestPath(
114
+ 'ServerScriptService.unknownpkg.Shared.Foo.spec',
115
+ resolver
116
+ )
117
+ ).toBe('src/unknownpkg/src/Shared/Foo.spec.lua');
118
+ });
119
+
120
+ it('strips :LINE suffix with sourcemap', () => {
121
+ expect(
122
+ resolveRobloxTestPath(
123
+ 'ServerScriptService.mypkg.Shared.MyModule.MyModule.spec:42',
124
+ resolver
125
+ )
126
+ ).toBe('src/mypkg/src/Shared/MyModule.spec.lua');
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Converts Roblox instance paths from Jest-lua output back to
3
+ * repo-relative local filesystem paths.
4
+ *
5
+ * Two strategies:
6
+ *
7
+ * 1. **Sourcemap** (preferred) — Uses the authoritative `sourcemap.json`
8
+ * mapping from `rojo sourcemap --absolute`. Exact and handles all
9
+ * project layouts.
10
+ *
11
+ * 2. **Heuristic** (fallback) — Assumes `src/{slug}/src/` layout and
12
+ * hard-codes `.spec`/`.story` as known dotted suffixes. Works when no
13
+ * sourcemap is available.
14
+ */
15
+
16
+ import type { SourcemapResolver } from '../../sourcemap/index.js';
17
+
18
+ const SSS_PREFIX = 'ServerScriptService.';
19
+
20
+ /**
21
+ * Resolve a Roblox instance path (from Jest-lua or a stack trace) to a
22
+ * repo-relative filesystem path.
23
+ *
24
+ * When a `sourcemapResolver` is provided, it is tried first. Falls back to
25
+ * the heuristic if the resolver doesn't have a mapping.
26
+ */
27
+ export function resolveRobloxTestPath(
28
+ instancePath: string,
29
+ sourcemapResolver?: SourcemapResolver
30
+ ): string {
31
+ if (sourcemapResolver) {
32
+ const resolved = sourcemapResolver.resolve(instancePath);
33
+ if (resolved) return resolved;
34
+ }
35
+
36
+ return _resolveHeuristic(instancePath);
37
+ }
38
+
39
+ /**
40
+ * Heuristic fallback: assumes `src/{slug}/src/` layout.
41
+ *
42
+ * Handles optional `:LINE` suffixes and missing `ServerScriptService.` prefix.
43
+ */
44
+ function _resolveHeuristic(instancePath: string): string {
45
+ // Strip :LINE suffix if present
46
+ let path = instancePath.replace(/:\d+$/, '');
47
+
48
+ // Strip ServerScriptService. prefix
49
+ if (path.startsWith(SSS_PREFIX)) {
50
+ path = path.slice(SSS_PREFIX.length);
51
+ }
52
+
53
+ // First dot-separated segment is the package slug
54
+ const dotIndex = path.indexOf('.');
55
+ if (dotIndex === -1) {
56
+ // Just a package name with no sub-path — return the src dir
57
+ return `src/${path}/src`;
58
+ }
59
+
60
+ const packageSlug = path.slice(0, dotIndex);
61
+ const remaining = path.slice(dotIndex + 1);
62
+
63
+ // Split into segments. In Rojo, dots in filenames (e.g. "Maid.spec.lua"
64
+ // becomes instance "Maid.spec") are ambiguous with the hierarchy separator.
65
+ // Rejoin known filename suffixes like ".spec" and ".story" with a dot
66
+ // instead of a slash.
67
+ const segments = remaining.split('.');
68
+ const rejoined: string[] = [];
69
+
70
+ for (let i = 0; i < segments.length; i++) {
71
+ const seg = segments[i];
72
+ // If this segment is a known file suffix, merge it with the previous
73
+ if (
74
+ i > 0 &&
75
+ (seg === 'spec' || seg === 'story')
76
+ ) {
77
+ rejoined[rejoined.length - 1] += `.${seg}`;
78
+ } else {
79
+ rejoined.push(seg);
80
+ }
81
+ }
82
+
83
+ const subPath = rejoined.join('/');
84
+ return `src/${packageSlug}/src/${subPath}.lua`;
85
+ }
@@ -1,3 +1,5 @@
1
+ import { OutputHelper } from '@quenty/cli-output-helpers';
2
+
1
3
  export interface ParsedTestLogs {
2
4
  success: boolean;
3
5
  logs: string;
@@ -8,7 +10,7 @@ export interface ParsedTestLogs {
8
10
  * Shared by both Open Cloud log fetching and local run-in-roblox output.
9
11
  */
10
12
  export function parseTestLogs(rawOutput: string): ParsedTestLogs {
11
- const cleanLogs = rawOutput.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
13
+ const cleanLogs = OutputHelper.stripAnsi(rawOutput);
12
14
 
13
15
  // Check for Jest-style test failures
14
16
  const failedSuites = cleanLogs.match(/Test Suites:\s*(\d+)\s+failed/);