@platformos/platformos-graph 0.0.19 → 0.1.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 (141) hide show
  1. package/CHANGELOG.md +516 -0
  2. package/bin/platformos-graph +21 -81
  3. package/dist/cache-probes.spec.d.ts +1 -0
  4. package/dist/cache-probes.spec.js +62 -0
  5. package/dist/cache-probes.spec.js.map +1 -0
  6. package/dist/cli.d.ts +46 -0
  7. package/dist/cli.js +131 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/cli.spec.d.ts +1 -0
  10. package/dist/cli.spec.js +235 -0
  11. package/dist/cli.spec.js.map +1 -0
  12. package/dist/graph/app-index-resolution.spec.d.ts +1 -0
  13. package/dist/graph/app-index-resolution.spec.js +167 -0
  14. package/dist/graph/app-index-resolution.spec.js.map +1 -0
  15. package/dist/graph/asset-resolution.spec.d.ts +1 -0
  16. package/dist/graph/asset-resolution.spec.js +64 -0
  17. package/dist/graph/asset-resolution.spec.js.map +1 -0
  18. package/dist/graph/augment.js +3 -1
  19. package/dist/graph/augment.js.map +1 -1
  20. package/dist/graph/build.d.ts +15 -2
  21. package/dist/graph/build.js +54 -12
  22. package/dist/graph/build.js.map +1 -1
  23. package/dist/graph/build.spec.d.ts +1 -0
  24. package/dist/graph/build.spec.js +138 -0
  25. package/dist/graph/build.spec.js.map +1 -0
  26. package/dist/graph/edge-sources.d.ts +28 -0
  27. package/dist/graph/edge-sources.js +43 -0
  28. package/dist/graph/edge-sources.js.map +1 -0
  29. package/dist/graph/edge-sources.spec.d.ts +1 -0
  30. package/dist/graph/edge-sources.spec.js +125 -0
  31. package/dist/graph/edge-sources.spec.js.map +1 -0
  32. package/dist/graph/extract.spec.d.ts +1 -0
  33. package/dist/graph/extract.spec.js +163 -0
  34. package/dist/graph/extract.spec.js.map +1 -0
  35. package/dist/graph/module.d.ts +70 -2
  36. package/dist/graph/module.js +164 -29
  37. package/dist/graph/module.js.map +1 -1
  38. package/dist/graph/module.spec.d.ts +1 -0
  39. package/dist/graph/module.spec.js +74 -0
  40. package/dist/graph/module.spec.js.map +1 -0
  41. package/dist/graph/query.d.ts +72 -0
  42. package/dist/graph/query.js +140 -0
  43. package/dist/graph/query.js.map +1 -0
  44. package/dist/graph/query.spec.d.ts +1 -0
  45. package/dist/graph/query.spec.js +209 -0
  46. package/dist/graph/query.spec.js.map +1 -0
  47. package/dist/graph/serialize.spec.d.ts +1 -0
  48. package/dist/graph/serialize.spec.js +50 -0
  49. package/dist/graph/serialize.spec.js.map +1 -0
  50. package/dist/graph/structural.spec.d.ts +1 -0
  51. package/dist/graph/structural.spec.js +146 -0
  52. package/dist/graph/structural.spec.js.map +1 -0
  53. package/dist/graph/test-helpers.d.ts +2 -3
  54. package/dist/graph/test-helpers.js +5 -9
  55. package/dist/graph/test-helpers.js.map +1 -1
  56. package/dist/graph/traverse-edges.spec.d.ts +1 -0
  57. package/dist/graph/traverse-edges.spec.js +337 -0
  58. package/dist/graph/traverse-edges.spec.js.map +1 -0
  59. package/dist/graph/traverse.d.ts +63 -4
  60. package/dist/graph/traverse.js +364 -49
  61. package/dist/graph/traverse.js.map +1 -1
  62. package/dist/index.d.ts +5 -1
  63. package/dist/index.js +21 -4
  64. package/dist/index.js.map +1 -1
  65. package/dist/parsers.d.ts +35 -0
  66. package/dist/parsers.js +72 -0
  67. package/dist/parsers.js.map +1 -0
  68. package/dist/parsers.spec.d.ts +1 -0
  69. package/dist/parsers.spec.js +70 -0
  70. package/dist/parsers.spec.js.map +1 -0
  71. package/dist/toSourceCode.d.ts +14 -2
  72. package/dist/toSourceCode.js +23 -19
  73. package/dist/toSourceCode.js.map +1 -1
  74. package/dist/tsconfig.tsbuildinfo +1 -1
  75. package/dist/types.d.ts +114 -20
  76. package/dist/types.js.map +1 -1
  77. package/dist/utils/index.d.ts +1 -1
  78. package/fixtures/asset-edges/app/assets/logo.css +1 -0
  79. package/fixtures/asset-edges/app/assets/site/app.js +1 -0
  80. package/fixtures/asset-edges/app/views/pages/index.liquid +3 -0
  81. package/fixtures/background-edges/app/views/pages/broken.liquid +3 -0
  82. package/fixtures/background-edges/app/views/pages/index.liquid +3 -0
  83. package/fixtures/background-edges/app/views/partials/jobs/notify.liquid +3 -0
  84. package/fixtures/function-edges/app/lib/queries/list.liquid +3 -0
  85. package/fixtures/function-edges/app/views/pages/broken.liquid +3 -0
  86. package/fixtures/function-edges/app/views/pages/index.liquid +3 -0
  87. package/fixtures/graphql-edges/app/graphql/blog_posts/find.graphql +10 -0
  88. package/fixtures/graphql-edges/app/views/pages/broken.liquid +3 -0
  89. package/fixtures/graphql-edges/app/views/pages/index.liquid +3 -0
  90. package/fixtures/graphql-table/app/graphql/with_table.graphql +5 -0
  91. package/fixtures/graphql-table/app/graphql/without_table.graphql +3 -0
  92. package/fixtures/graphql-table/app/views/pages/index.liquid +4 -0
  93. package/fixtures/include-edges/app/views/pages/index.liquid +1 -0
  94. package/fixtures/include-edges/app/views/partials/shared/header.liquid +1 -0
  95. package/fixtures/layout-edges/app/views/layouts/theme.liquid +4 -0
  96. package/fixtures/layout-edges/app/views/pages/broken.liquid +5 -0
  97. package/fixtures/layout-edges/app/views/pages/index.liquid +5 -0
  98. package/fixtures/module-edges/app/views/pages/index.liquid +4 -0
  99. package/fixtures/module-edges/modules/my_module/public/lib/queries/get.liquid +3 -0
  100. package/fixtures/module-edges/modules/my_module/public/views/partials/card.liquid +1 -0
  101. package/fixtures/schema-nodes/app/schema/blog_post.yml +6 -0
  102. package/fixtures/schema-nodes/app/schema/no_name.yml +3 -0
  103. package/fixtures/schema-nodes/app/views/pages/index.liquid +1 -0
  104. package/fixtures/skeleton/app/assets/app.js +1 -0
  105. package/fixtures/skeleton/app/views/partials/child.liquid +2 -4
  106. package/fixtures/skeleton/app/views/partials/parent.liquid +2 -4
  107. package/fixtures/structural/app/views/pages/about.liquid +1 -0
  108. package/fixtures/structural/app/views/pages/blog/show.liquid +4 -0
  109. package/fixtures/structural/app/views/pages/index.liquid +6 -0
  110. package/fixtures/structural/app/views/pages/rich.liquid +8 -0
  111. package/fixtures/structural/app/views/partials/card.liquid +1 -0
  112. package/fixtures/structural/app/views/partials/documented.liquid +5 -0
  113. package/package.json +7 -5
  114. package/src/cli.spec.ts +271 -0
  115. package/src/cli.ts +167 -0
  116. package/src/graph/augment.ts +4 -2
  117. package/src/graph/build.spec.ts +888 -9
  118. package/src/graph/build.ts +68 -17
  119. package/src/graph/edge-sources.spec.ts +125 -0
  120. package/src/graph/edge-sources.ts +49 -0
  121. package/src/graph/module.spec.ts +168 -0
  122. package/src/graph/module.ts +167 -33
  123. package/src/graph/query.spec.ts +260 -0
  124. package/src/graph/query.ts +154 -0
  125. package/src/graph/serialize.spec.ts +4 -4
  126. package/src/graph/test-helpers.ts +5 -12
  127. package/src/graph/traverse.spec.ts +278 -0
  128. package/src/graph/traverse.ts +423 -54
  129. package/src/index.ts +16 -1
  130. package/src/parsers.spec.ts +85 -0
  131. package/src/parsers.ts +75 -0
  132. package/src/toSourceCode.ts +26 -26
  133. package/src/types.ts +120 -23
  134. package/src/utils/index.ts +1 -1
  135. package/bin/jsconfig.json +0 -18
  136. package/dist/getWebComponentMap.d.ts +0 -10
  137. package/dist/getWebComponentMap.js +0 -66
  138. package/dist/getWebComponentMap.js.map +0 -1
  139. package/fixtures/skeleton/assets/app.js +0 -7
  140. package/src/getWebComponentMap.ts +0 -81
  141. /package/fixtures/skeleton/{assets → app/assets}/app.css +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-sources.js","sourceRoot":"","sources":["../../src/graph/edge-sources.ts"],"names":[],"mappings":";;AAiBA,oCASC;AAiBD,oDAKC;AA/CD,qEAKuC;AAEvC;;;;;;;;GAQG;AACH,SAAgB,YAAY,CAAC,GAAc,EAAE,OAAkB;IAC7D,QAAQ,IAAA,+BAAW,EAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC;QAClC,KAAK,sCAAkB,CAAC,MAAM,CAAC;QAC/B,KAAK,sCAAkB,CAAC,IAAI,CAAC;QAC7B,KAAK,sCAAkB,CAAC,OAAO;YAC7B,OAAO,IAAI,CAAC;QACd;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACI,KAAK,UAAU,oBAAoB,CACxC,EAAsB,EACtB,OAAkB;IAElB,OAAO,IAAA,sCAAkB,EAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AAChF,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const platformos_check_common_1 = require("@platformos/platformos-check-common");
5
+ const platformos_common_1 = require("@platformos/platformos-common");
6
+ const test_1 = require("@platformos/platformos-check-common/dist/test");
7
+ const index_1 = require("../index");
8
+ /**
9
+ * TASK-9.17: `enumerateEdgeSources` is the SINGLE canonical "what are the
10
+ * edge-source liquid files under a project root" primitive. The supervisor's
11
+ * GraphCache consumes it for BOTH the fingerprint domain and the build's entry
12
+ * points, so its definition must never silently drift from the file-type
13
+ * classifier (`getFileType` ← `FILE_TYPE_DIRS`).
14
+ *
15
+ * The load-bearing guard is EQUIVALENCE-TO-THE-CLASSIFIER: the SCOPED walk (only
16
+ * `APP_SOURCE_SUBTREES`, the TASK-9.15 Phase-3A perf win) must gather the exact
17
+ * same set a WHOLE-TREE walk filtered by `isEdgeSource` would. If a partial
18
+ * location is added to the classifier but its subtree is not walkable, the
19
+ * whole-tree walk finds files the scoped walk misses → this test fails, catching
20
+ * the drift before it under-reports dependents.
21
+ */
22
+ (0, vitest_1.describe)('Unit: enumerateEdgeSources (canonical edge-source enumeration)', () => {
23
+ const rootUri = platformos_check_common_1.path.normalize('file:/');
24
+ const uri = (rel) => platformos_check_common_1.path.join(rootUri, rel);
25
+ // Edge sources under every canonical location a Page/Layout/Partial can live.
26
+ const EDGE_SOURCES = [
27
+ 'app/views/pages/home.liquid', // Page (modern app/ root)
28
+ 'app/views/layouts/application.liquid', // Layout
29
+ 'app/views/partials/card.liquid', // Partial (views/partials)
30
+ 'app/lib/helper.liquid', // Partial (lib)
31
+ 'marketplace_builder/views/pages/legacy.liquid', // Page (legacy root)
32
+ 'modules/shop/public/views/partials/widget.liquid', // Partial (top-level module, public)
33
+ 'modules/shop/private/lib/secret.liquid', // Partial (top-level module, PRIVATE access level)
34
+ 'app/modules/blog/private/views/pages/post.liquid', // Page (nested app/modules)
35
+ ];
36
+ // Files that are NOT edge sources: leaves, non-liquid, and a bundled
37
+ // non-platformOS sibling that must never be walked.
38
+ const NON_EDGE_SOURCES = {
39
+ 'app/graphql/get_posts.graphql': 'query { records { results { id } } }',
40
+ 'app/schema/blog_post.yml': 'name: blog_post',
41
+ 'app/assets/logo.css': 'body {}',
42
+ 'react-app/src/components/Widget.liquid': 'noise that is never a source',
43
+ 'README.md': '# project',
44
+ };
45
+ const makeFs = () => {
46
+ const files = { ...NON_EDGE_SOURCES };
47
+ for (const rel of EDGE_SOURCES)
48
+ files[rel] = '<div></div>';
49
+ return new test_1.MockFileSystem(files, rootUri);
50
+ };
51
+ const sorted = (uris) => [...uris].sort();
52
+ (0, vitest_1.it)('gathers exactly the edge sources across all source roots (nothing else)', async () => {
53
+ const fs = makeFs();
54
+ (0, vitest_1.expect)(sorted(await (0, index_1.enumerateEdgeSources)(fs, rootUri))).toEqual(sorted(EDGE_SOURCES.map(uri)));
55
+ });
56
+ (0, vitest_1.it)('the scoped walk equals a whole-tree walk filtered by the classifier (no drift)', async () => {
57
+ const fs = makeFs();
58
+ const scoped = sorted(await (0, index_1.enumerateEdgeSources)(fs, rootUri));
59
+ const wholeTree = sorted((await everyFileUnder(fs, rootUri)).filter((u) => (0, index_1.isEdgeSource)(u, rootUri)));
60
+ (0, vitest_1.expect)(scoped).toEqual(wholeTree);
61
+ });
62
+ (0, vitest_1.it)('isEdgeSource is exactly the Layout/Page/Partial classification', () => {
63
+ const EDGE_SOURCE_TYPES = [
64
+ platformos_common_1.PlatformOSFileType.Layout,
65
+ platformos_common_1.PlatformOSFileType.Page,
66
+ platformos_common_1.PlatformOSFileType.Partial,
67
+ ];
68
+ for (const rel of EDGE_SOURCES) {
69
+ const u = uri(rel);
70
+ (0, vitest_1.expect)((0, index_1.isEdgeSource)(u, rootUri)).toBe(true);
71
+ (0, vitest_1.expect)(EDGE_SOURCE_TYPES).toContain((0, platformos_common_1.getFileType)(u, rootUri));
72
+ }
73
+ for (const rel of Object.keys(NON_EDGE_SOURCES)) {
74
+ const u = uri(rel);
75
+ (0, vitest_1.expect)((0, index_1.isEdgeSource)(u, rootUri)).toBe(false);
76
+ (0, vitest_1.expect)(EDGE_SOURCE_TYPES).not.toContain((0, platformos_common_1.getFileType)(u, rootUri));
77
+ }
78
+ });
79
+ /**
80
+ * The reason the classification is anchored on the root. Unanchored, a directory
81
+ * named like a platformOS one ANYWHERE in the path classified: these three files
82
+ * are not part of the app, and counting them in the fingerprint domain made the
83
+ * supervisor's GraphCache rebuild on edits to files the app never reads.
84
+ */
85
+ (0, vitest_1.it)('does not admit a platformOS-looking path outside the app subtrees', async () => {
86
+ const outside = {
87
+ 'tmp/app/views/partials/scratch.liquid': '<div></div>',
88
+ 'seed/post_import/app/views/pages/fixture.liquid': '<div></div>',
89
+ 'node_modules/pkg/app/lib/helper.liquid': '<div></div>',
90
+ };
91
+ for (const rel of Object.keys(outside))
92
+ (0, vitest_1.expect)((0, index_1.isEdgeSource)(uri(rel), rootUri)).toBe(false);
93
+ // Control: the same three spellings ARE found when they sit at the real root, so
94
+ // the silence above is the anchoring, not the fixture or the walk.
95
+ const files = { ...outside };
96
+ for (const rel of EDGE_SOURCES)
97
+ files[rel] = '<div></div>';
98
+ const fs = new test_1.MockFileSystem(files, rootUri);
99
+ (0, vitest_1.expect)(sorted(await (0, index_1.enumerateEdgeSources)(fs, rootUri))).toEqual(sorted(EDGE_SOURCES.map(uri)));
100
+ });
101
+ (0, vitest_1.it)('never yields a non-platformOS sibling (a bundled react-app/ is skipped)', async () => {
102
+ const fs = makeFs();
103
+ const result = await (0, index_1.enumerateEdgeSources)(fs, rootUri);
104
+ (0, vitest_1.expect)(result).not.toContain(uri('react-app/src/components/Widget.liquid'));
105
+ });
106
+ });
107
+ /**
108
+ * Every file under `baseUri`, by brute-force recursion into every directory.
109
+ *
110
+ * Deliberately written HERE, and deliberately NOT `walkAppSourceFiles`: that is the walk
111
+ * `enumerateEdgeSources` is built on, so using it for the control too would compare the
112
+ * mechanism against itself and the equivalence assertion above would hold no matter how
113
+ * wrong `APP_SOURCE_SUBTREES` became. An independent walk is the entire value of that test.
114
+ *
115
+ * (It replaces check-common's `recursiveReadDirectory`, which master removed from
116
+ * production once the anchored walk superseded it. Its only remaining purpose was to be
117
+ * this control, and a control belongs next to the test that needs it rather than in a
118
+ * shipped package's public API.)
119
+ */
120
+ async function everyFileUnder(fs, baseUri) {
121
+ const entries = await fs.readDirectory(baseUri).catch(() => []);
122
+ const nested = await Promise.all(entries.map(async ([uri, type]) => type === platformos_common_1.FileType.Directory ? everyFileUnder(fs, uri) : [uri]));
123
+ return nested.flat();
124
+ }
125
+ //# sourceMappingURL=edge-sources.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-sources.spec.js","sourceRoot":"","sources":["../../src/graph/edge-sources.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const platformos_check_common_1 = require("@platformos/platformos-check-common");
4
+ const vitest_1 = require("vitest");
5
+ const index_1 = require("../index");
6
+ const toSourceCode_1 = require("../toSourceCode");
7
+ const test_helpers_1 = require("./test-helpers");
8
+ /**
9
+ * `extractFileReferences` is the per-file primitive a `validate_code`-style
10
+ * consumer uses: parse an in-flight buffer with `toSourceCode`, then resolve its
11
+ * outgoing dependency edges against the on-disk project — WITHOUT building the
12
+ * whole app graph. The buffer is never read from disk, so these specs pass the
13
+ * content inline (and even use a `sourceUri` for a file that does not exist).
14
+ */
15
+ const { fs } = (0, test_helpers_1.getDependencies)();
16
+ /** The exact source range of `snippet` within `source`. */
17
+ function rangeOf(source, snippet) {
18
+ const start = source.indexOf(snippet);
19
+ if (start < 0)
20
+ throw new Error(`snippet not found: ${snippet}`);
21
+ return [start, start + snippet.length];
22
+ }
23
+ function directRef(sourceUri, sourceRange, targetUri, kind, args) {
24
+ return {
25
+ source: { uri: sourceUri, range: sourceRange },
26
+ target: { uri: targetUri },
27
+ type: 'direct',
28
+ kind,
29
+ ...(args ? { args } : {}),
30
+ };
31
+ }
32
+ async function extract(rootUri, sourceUri, content) {
33
+ return (0, index_1.extractFileReferences)(rootUri, sourceUri, await (0, toSourceCode_1.toSourceCode)(sourceUri, content), { fs });
34
+ }
35
+ (0, vitest_1.describe)('extractFileReferences: resolves a single buffer against the project', () => {
36
+ const rootUri = platformos_check_common_1.path.join(test_helpers_1.fixturesRoot, 'function-edges');
37
+ const p = (part) => platformos_check_common_1.path.join(rootUri, ...part.split('/'));
38
+ (0, vitest_1.it)('resolves a {% function %} edge to the canonical lib URI', async () => {
39
+ const sourceUri = p('app/views/pages/draft.liquid'); // not on disk — in-flight buffer
40
+ const content = `{% liquid
41
+ function items = 'queries/list'
42
+ %}`;
43
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([
44
+ directRef(sourceUri, rangeOf(content, "function items = 'queries/list'"), p('app/lib/queries/list.liquid'), 'function'),
45
+ ]);
46
+ });
47
+ (0, vitest_1.it)('resolves a target that does not exist on disk (path-based resolution)', async () => {
48
+ const sourceUri = p('app/views/pages/draft.liquid');
49
+ const content = `{% liquid
50
+ function ghost = 'queries/missing'
51
+ %}`;
52
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([
53
+ directRef(sourceUri, rangeOf(content, "function ghost = 'queries/missing'"), p('app/lib/queries/missing.liquid'), 'function'),
54
+ ]);
55
+ });
56
+ (0, vitest_1.it)('captures the named-argument names at a render call site', async () => {
57
+ const sourceUri = p('app/views/pages/draft.liquid');
58
+ const content = "{% render 'card', title: page.title, count: 3 %}";
59
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([
60
+ directRef(sourceUri, rangeOf(content, "{% render 'card', title: page.title, count: 3 %}"), p('app/views/partials/card.liquid'), 'render', ['title', 'count']),
61
+ ]);
62
+ });
63
+ });
64
+ (0, vitest_1.describe)('extractFileReferences: kinds and resolution match the full graph build', () => {
65
+ (0, vitest_1.it)('tags include edges with kind "include"', async () => {
66
+ const rootUri = platformos_check_common_1.path.join(test_helpers_1.fixturesRoot, 'include-edges');
67
+ const p = (part) => platformos_check_common_1.path.join(rootUri, ...part.split('/'));
68
+ const sourceUri = p('app/views/pages/draft.liquid');
69
+ const content = "{% include 'shared/header' %}";
70
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([
71
+ directRef(sourceUri, rangeOf(content, "{% include 'shared/header' %}"), p('app/views/partials/shared/header.liquid'), 'include'),
72
+ ]);
73
+ });
74
+ (0, vitest_1.it)('resolves a {% graphql %} edge to the .graphql operation file', async () => {
75
+ const rootUri = platformos_check_common_1.path.join(test_helpers_1.fixturesRoot, 'graphql-edges');
76
+ const p = (part) => platformos_check_common_1.path.join(rootUri, ...part.split('/'));
77
+ const sourceUri = p('app/views/pages/draft.liquid');
78
+ const content = `{% liquid
79
+ graphql posts = 'blog_posts/find', id: '1'
80
+ %}`;
81
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([
82
+ directRef(sourceUri, rangeOf(content, "graphql posts = 'blog_posts/find', id: '1'"), p('app/graphql/blog_posts/find.graphql'), 'graphql', ['id']),
83
+ ]);
84
+ });
85
+ (0, vitest_1.it)('resolves module-namespaced render + function targets in declaration order', async () => {
86
+ const rootUri = platformos_check_common_1.path.join(test_helpers_1.fixturesRoot, 'module-edges');
87
+ const p = (part) => platformos_check_common_1.path.join(rootUri, ...part.split('/'));
88
+ const sourceUri = p('app/views/pages/draft.liquid');
89
+ const content = `{% liquid
90
+ function items = 'modules/my_module/queries/get'
91
+ %}
92
+ {% render 'modules/my_module/card' %}`;
93
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([
94
+ directRef(sourceUri, rangeOf(content, "function items = 'modules/my_module/queries/get'"), p('modules/my_module/public/lib/queries/get.liquid'), 'function'),
95
+ directRef(sourceUri, rangeOf(content, "{% render 'modules/my_module/card' %}"), p('modules/my_module/public/views/partials/card.liquid'), 'render'),
96
+ ]);
97
+ });
98
+ });
99
+ (0, vitest_1.describe)('extractFileReferences: only statically resolvable edges', () => {
100
+ const rootUri = platformos_check_common_1.path.join(test_helpers_1.fixturesRoot, 'function-edges');
101
+ const sourceUri = platformos_check_common_1.path.join(rootUri, 'app/views/pages/draft.liquid');
102
+ (0, vitest_1.it)('skips dynamic render targets', async () => {
103
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, '{% render partial_name %}')).toEqual([]);
104
+ });
105
+ (0, vitest_1.it)('returns nothing for an unparseable buffer instead of throwing', async () => {
106
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, '{% render %}{% endif %}')).toEqual([]);
107
+ });
108
+ (0, vitest_1.it)('returns nothing for a buffer with no references', async () => {
109
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, '<h1>{{ page.title }}</h1>')).toEqual([]);
110
+ });
111
+ });
112
+ (0, vitest_1.describe)('extractFileReferences: layout-association edges (frontmatter `layout:`)', () => {
113
+ const rootUri = platformos_check_common_1.path.join(test_helpers_1.fixturesRoot, 'function-edges'); // no layouts here → default path
114
+ const p = (part) => platformos_check_common_1.path.join(rootUri, ...part.split('/'));
115
+ const sourceUri = p('app/views/pages/draft.liquid');
116
+ // The whole frontmatter block: start through the closing `---` line + newline.
117
+ const fmRange = (content) => [
118
+ 0,
119
+ content.indexOf('\n', content.indexOf('---', 3)) + 1,
120
+ ];
121
+ (0, vitest_1.it)('emits a layout edge for an explicit frontmatter layout', async () => {
122
+ const content = `---
123
+ slug: draft
124
+ layout: theme
125
+ ---
126
+ <h1>x</h1>`;
127
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([
128
+ directRef(sourceUri, fmRange(content), p('app/views/layouts/theme.liquid'), 'layout'),
129
+ ]);
130
+ });
131
+ (0, vitest_1.it)('resolves a module-prefixed layout', async () => {
132
+ const content = `---
133
+ layout: modules/core/admin
134
+ ---
135
+ <h1>x</h1>`;
136
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([
137
+ directRef(sourceUri, fmRange(content), p('modules/core/public/views/layouts/admin.liquid'), 'layout'),
138
+ ]);
139
+ });
140
+ (0, vitest_1.it)('emits no edge for an explicitly empty layout', async () => {
141
+ const content = `---
142
+ slug: draft
143
+ layout: ''
144
+ ---
145
+ <h1>x</h1>`;
146
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([]);
147
+ });
148
+ (0, vitest_1.it)('emits no edge when layout is omitted (no implicit default)', async () => {
149
+ const content = `---
150
+ slug: draft
151
+ ---
152
+ <h1>x</h1>`;
153
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([]);
154
+ });
155
+ (0, vitest_1.it)('emits no edge for a dynamic layout value', async () => {
156
+ const content = `---
157
+ layout: "{{ context.constants.LAYOUT }}"
158
+ ---
159
+ <h1>x</h1>`;
160
+ (0, vitest_1.expect)(await extract(rootUri, sourceUri, content)).toEqual([]);
161
+ });
162
+ });
163
+ //# sourceMappingURL=extract.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.spec.js","sourceRoot":"","sources":["../../src/graph/extract.spec.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,75 @@
1
1
  import { UriString } from '@platformos/platformos-check-common';
2
- import { AssetModule, AppGraph, AppModule, LiquidModule } from '../types';
2
+ import { AssetModule, AppGraph, AppModule, GraphQLModule, LiquidModule, SchemaModule } from '../types';
3
3
  export declare function getModule(appGraph: AppGraph, uri: UriString): AppModule | undefined;
4
+ /**
5
+ * Create (or fetch the cached) asset module for an already-resolved URI.
6
+ *
7
+ * Preferred over {@link getAssetModule} whenever the file is known, for the same
8
+ * reason as {@link getPartialModuleByUri}: it does not reconstruct a path from a
9
+ * name, so a nested or module asset keeps the location it actually has.
10
+ *
11
+ * Gated on {@link isSupportedAsset} HERE rather than at each call site, so a new
12
+ * caller cannot forget the gate and mint an Asset node for a value that is not an
13
+ * asset file.
14
+ */
15
+ export declare function getAssetModuleByUri(appGraph: AppGraph, uri: string): AssetModule | undefined;
16
+ /**
17
+ * Whether `nameOrUri` refers to a supported asset file (by extension).
18
+ *
19
+ * The single gate for admitting an asset, whether the subject is a reference (the
20
+ * string inside `{{ 'app.js' | asset_url }}`) or a resolved URI. It preserves the
21
+ * graph's historical behavior of ignoring an `asset_url`-family filter applied to
22
+ * a value that is not an asset file. Exported so the traversal can skip the
23
+ * filesystem probe for a non-asset value BEFORE resolving it; the module
24
+ * factories gate on it again, so neither layer depends on the other remembering.
25
+ */
26
+ export declare function isSupportedAsset(nameOrUri: string): boolean;
27
+ /**
28
+ * Create (or fetch the cached) asset module for an asset REFERENCE — the string
29
+ * inside `{{ 'app.js' | asset_url }}`, which may carry a `modules/<name>/` prefix.
30
+ *
31
+ * Assets live under the same roots as every other file type: `app/assets/` or
32
+ * `modules/<name>/{public,private}/assets/`. Those come from platformos-common, so the
33
+ * graph resolves a reference to the same place the linter and the platform do.
34
+ *
35
+ * The FIRST candidate is used as the canonical location: this is sync and has no
36
+ * filesystem, so it cannot tell which candidate exists. Prefer
37
+ * {@link getAssetModuleByUri} — the traversal resolves through `DocumentsLocator`,
38
+ * which can.
39
+ */
4
40
  export declare function getAssetModule(appGraph: AppGraph, asset: string): AssetModule | undefined;
5
- export declare function getPartialModule(appGraph: AppGraph, partial: string): LiquidModule;
41
+ /**
42
+ * Create (or fetch the cached) Liquid Partial module for an ALREADY-RESOLVED
43
+ * full URI — used for `{% render %}` / `{% include %}` / `{% function %}` /
44
+ * `{% background %}` targets whose URI is resolved canonically by
45
+ * `DocumentsLocator` (which handles lib paths, module prefixes, and extensions).
46
+ * It keys the node by that URI verbatim (normalized), never reconstructing a path
47
+ * from a name: partials have two search roots (`views/partials`, `lib`) and two
48
+ * extensions, so a name-derived path is a guess, and guessing split a `lib/`
49
+ * partial into two nodes. Commands/queries/lib helpers are all `Partial` kind,
50
+ * consistent with check-common's file-type classification.
51
+ */
52
+ export declare function getPartialModuleByUri(appGraph: AppGraph, uri: string): LiquidModule;
53
+ /**
54
+ * Create (or fetch the cached) GraphQL module for an already-resolved
55
+ * `.graphql` URI — used for `{% graphql op = 'name' %}` targets resolved by
56
+ * `DocumentsLocator`. A leaf node (no outgoing edges).
57
+ */
58
+ export declare function getGraphQLModuleByUri(appGraph: AppGraph, uri: string): GraphQLModule;
59
+ /**
60
+ * Create (or fetch the cached) Schema module for an already-resolved
61
+ * `custom_model_type`/schema URI — discovered during a full `buildAppGraph`.
62
+ * A leaf node; its `table` (the model `name:`) is populated during traversal.
63
+ */
64
+ export declare function getSchemaModule(appGraph: AppGraph, uri: string): SchemaModule;
6
65
  export declare function getLayoutModule(appGraph: AppGraph, layoutUri: string | false | undefined): LiquidModule | undefined;
66
+ /**
67
+ * Create (or fetch the cached) Layout module for an already-resolved layout URI
68
+ * — used for the frontmatter `layout:` association edge, whose target is
69
+ * resolved by `DocumentsLocator` (`'layout'` type: `app/views/layouts`, module
70
+ * prefixes, `.html.liquid`/`.liquid`). Distinct from {@link getLayoutModule} only
71
+ * in that the latter accepts the `string | false | undefined` a frontmatter read
72
+ * yields; both normalize, so both key the same node.
73
+ */
74
+ export declare function getLayoutModuleByUri(appGraph: AppGraph, uri: string): LiquidModule;
7
75
  export declare function getPageModule(appGraph: AppGraph, pageUri: string): LiquidModule;
@@ -1,11 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getModule = getModule;
4
+ exports.getAssetModuleByUri = getAssetModuleByUri;
5
+ exports.isSupportedAsset = isSupportedAsset;
4
6
  exports.getAssetModule = getAssetModule;
5
- exports.getPartialModule = getPartialModule;
7
+ exports.getPartialModuleByUri = getPartialModuleByUri;
8
+ exports.getGraphQLModuleByUri = getGraphQLModuleByUri;
9
+ exports.getSchemaModule = getSchemaModule;
6
10
  exports.getLayoutModule = getLayoutModule;
11
+ exports.getLayoutModuleByUri = getLayoutModuleByUri;
7
12
  exports.getPageModule = getPageModule;
8
13
  const platformos_check_common_1 = require("@platformos/platformos-check-common");
14
+ const platformos_common_1 = require("@platformos/platformos-common");
9
15
  const types_1 = require("../types");
10
16
  const utils_1 = require("../utils");
11
17
  /**
@@ -28,48 +34,153 @@ function getModule(appGraph, uri) {
28
34
  if (cache.has(uri)) {
29
35
  return cache.get(uri);
30
36
  }
31
- const relativePath = platformos_check_common_1.path.relative(uri, appGraph.rootUri);
32
- switch (true) {
33
- case (0, platformos_check_common_1.isLayout)(uri):
37
+ // One anchored classification, not four unanchored ones: a file's type is its
38
+ // position relative to the project root, and the graph has that root.
39
+ switch ((0, platformos_common_1.getFileType)(uri, appGraph.rootUri)) {
40
+ case platformos_common_1.PlatformOSFileType.Layout:
34
41
  return getLayoutModule(appGraph, uri);
35
- case (0, platformos_check_common_1.isPage)(uri):
42
+ case platformos_common_1.PlatformOSFileType.Page:
36
43
  return getPageModule(appGraph, uri);
37
- case (0, platformos_check_common_1.isPartial)(uri):
38
- return getPartialModule(appGraph, platformos_check_common_1.path.basename(uri, '.liquid'));
39
- case relativePath.startsWith('assets') || relativePath.startsWith('modules'):
40
- return getAssetModule(appGraph, platformos_check_common_1.path.basename(uri));
44
+ case platformos_common_1.PlatformOSFileType.Partial:
45
+ // The URI is already resolved, so it is used as-is. Reducing it to a
46
+ // `basename` and rebuilding a path from that loses any subdirectory and any
47
+ // module prefix — `app/views/partials/ui/card.liquid` came back as
48
+ // `app/views/partials/card.liquid`, a node for a file that need not exist,
49
+ // and `app/lib/can/x.liquid` was split from the same file resolved as an
50
+ // edge target (which comes through getPartialModuleByUri).
51
+ return getPartialModuleByUri(appGraph, uri);
52
+ case platformos_common_1.PlatformOSFileType.Asset:
53
+ return getAssetModuleByUri(appGraph, uri);
41
54
  }
42
55
  }
56
+ /**
57
+ * Create (or fetch the cached) asset module for an already-resolved URI.
58
+ *
59
+ * Preferred over {@link getAssetModule} whenever the file is known, for the same
60
+ * reason as {@link getPartialModuleByUri}: it does not reconstruct a path from a
61
+ * name, so a nested or module asset keeps the location it actually has.
62
+ *
63
+ * Gated on {@link isSupportedAsset} HERE rather than at each call site, so a new
64
+ * caller cannot forget the gate and mint an Asset node for a value that is not an
65
+ * asset file.
66
+ */
67
+ function getAssetModuleByUri(appGraph, uri) {
68
+ if (!isSupportedAsset(uri))
69
+ return undefined;
70
+ return module(appGraph, {
71
+ type: "Asset" /* ModuleType.Asset */,
72
+ kind: 'unused',
73
+ // Normalize to forward slashes — see getPartialModuleByUri.
74
+ uri: platformos_check_common_1.path.normalize(uri),
75
+ dependencies: [],
76
+ references: [],
77
+ });
78
+ }
79
+ /** File extensions the graph treats as assets (the `asset_url` edge gate). */
80
+ const SUPPORTED_ASSET_EXTENSIONS = [
81
+ ...types_1.SUPPORTED_ASSET_IMAGE_EXTENSIONS,
82
+ 'js',
83
+ 'css',
84
+ 'svg',
85
+ 'pdf',
86
+ 'woff',
87
+ 'woff2',
88
+ 'ttf',
89
+ 'eot',
90
+ ];
91
+ /**
92
+ * Whether `nameOrUri` refers to a supported asset file (by extension).
93
+ *
94
+ * The single gate for admitting an asset, whether the subject is a reference (the
95
+ * string inside `{{ 'app.js' | asset_url }}`) or a resolved URI. It preserves the
96
+ * graph's historical behavior of ignoring an `asset_url`-family filter applied to
97
+ * a value that is not an asset file. Exported so the traversal can skip the
98
+ * filesystem probe for a non-asset value BEFORE resolving it; the module
99
+ * factories gate on it again, so neither layer depends on the other remembering.
100
+ */
101
+ function isSupportedAsset(nameOrUri) {
102
+ return SUPPORTED_ASSET_EXTENSIONS.includes((0, utils_1.extname)(nameOrUri));
103
+ }
104
+ /**
105
+ * Create (or fetch the cached) asset module for an asset REFERENCE — the string
106
+ * inside `{{ 'app.js' | asset_url }}`, which may carry a `modules/<name>/` prefix.
107
+ *
108
+ * Assets live under the same roots as every other file type: `app/assets/` or
109
+ * `modules/<name>/{public,private}/assets/`. Those come from platformos-common, so the
110
+ * graph resolves a reference to the same place the linter and the platform do.
111
+ *
112
+ * The FIRST candidate is used as the canonical location: this is sync and has no
113
+ * filesystem, so it cannot tell which candidate exists. Prefer
114
+ * {@link getAssetModuleByUri} — the traversal resolves through `DocumentsLocator`,
115
+ * which can.
116
+ */
43
117
  function getAssetModule(appGraph, asset) {
44
- const extension = (0, utils_1.extname)(asset);
45
- const SUPPORTED_ASSET_EXTENSIONS = [
46
- ...types_1.SUPPORTED_ASSET_IMAGE_EXTENSIONS,
47
- 'js',
48
- 'css',
49
- 'svg',
50
- 'pdf',
51
- 'woff',
52
- 'woff2',
53
- 'ttf',
54
- 'eot',
55
- ];
56
- if (!SUPPORTED_ASSET_EXTENSIONS.includes(extension)) {
118
+ if (!isSupportedAsset(asset))
119
+ return undefined;
120
+ const [canonical] = (0, platformos_common_1.nameToPaths)(platformos_common_1.PlatformOSFileType.Asset, asset);
121
+ if (!canonical)
57
122
  return undefined;
58
- }
59
123
  return module(appGraph, {
60
124
  type: "Asset" /* ModuleType.Asset */,
61
125
  kind: 'unused',
126
+ uri: platformos_check_common_1.path.join(appGraph.rootUri, canonical),
62
127
  dependencies: [],
63
128
  references: [],
64
- uri: platformos_check_common_1.path.join(appGraph.rootUri, 'assets', asset),
65
129
  });
66
130
  }
67
- function getPartialModule(appGraph, partial) {
68
- const uri = platformos_check_common_1.path.join(appGraph.rootUri, 'app/views/partials', `${partial}.liquid`);
131
+ /**
132
+ * Create (or fetch the cached) Liquid Partial module for an ALREADY-RESOLVED
133
+ * full URI — used for `{% render %}` / `{% include %}` / `{% function %}` /
134
+ * `{% background %}` targets whose URI is resolved canonically by
135
+ * `DocumentsLocator` (which handles lib paths, module prefixes, and extensions).
136
+ * It keys the node by that URI verbatim (normalized), never reconstructing a path
137
+ * from a name: partials have two search roots (`views/partials`, `lib`) and two
138
+ * extensions, so a name-derived path is a guess, and guessing split a `lib/`
139
+ * partial into two nodes. Commands/queries/lib helpers are all `Partial` kind,
140
+ * consistent with check-common's file-type classification.
141
+ */
142
+ function getPartialModuleByUri(appGraph, uri) {
69
143
  return module(appGraph, {
70
144
  type: "Liquid" /* ModuleType.Liquid */,
71
145
  kind: "partial" /* LiquidModuleKind.Partial */,
72
- uri: uri,
146
+ // Normalize to forward slashes so module keys match the rest of the graph
147
+ // (getAssetModule builds its URI via path.join, which normalizes).
148
+ // DocumentsLocator returns `Utils.joinPath(...).toString()` unnormalized,
149
+ // which on Windows keeps backslashes and breaks key/edge matching against the
150
+ // normalized URIs everywhere else.
151
+ uri: platformos_check_common_1.path.normalize(uri),
152
+ dependencies: [],
153
+ references: [],
154
+ });
155
+ }
156
+ /**
157
+ * Create (or fetch the cached) GraphQL module for an already-resolved
158
+ * `.graphql` URI — used for `{% graphql op = 'name' %}` targets resolved by
159
+ * `DocumentsLocator`. A leaf node (no outgoing edges).
160
+ */
161
+ function getGraphQLModuleByUri(appGraph, uri) {
162
+ return module(appGraph, {
163
+ type: "GraphQL" /* ModuleType.GraphQL */,
164
+ kind: 'graphql',
165
+ // Normalize to forward slashes — see getPartialModuleByUri.
166
+ uri: platformos_check_common_1.path.normalize(uri),
167
+ dependencies: [],
168
+ references: [],
169
+ // Always present (empty = no table declared); populated during traversal.
170
+ tables: [],
171
+ });
172
+ }
173
+ /**
174
+ * Create (or fetch the cached) Schema module for an already-resolved
175
+ * `custom_model_type`/schema URI — discovered during a full `buildAppGraph`.
176
+ * A leaf node; its `table` (the model `name:`) is populated during traversal.
177
+ */
178
+ function getSchemaModule(appGraph, uri) {
179
+ return module(appGraph, {
180
+ type: "Schema" /* ModuleType.Schema */,
181
+ kind: 'schema',
182
+ // Normalize to forward slashes — see getPartialModuleByUri.
183
+ uri: platformos_check_common_1.path.normalize(uri),
73
184
  dependencies: [],
74
185
  references: [],
75
186
  });
@@ -80,7 +191,29 @@ function getLayoutModule(appGraph, layoutUri) {
80
191
  return module(appGraph, {
81
192
  type: "Liquid" /* ModuleType.Liquid */,
82
193
  kind: "layout" /* LiquidModuleKind.Layout */,
83
- uri: layoutUri,
194
+ // Normalize so a layout discovered as an entry point keys identically to the
195
+ // same file resolved as a frontmatter `layout:` edge target (which comes
196
+ // through getLayoutModuleByUri, also normalized) — one node, never a split
197
+ // identity. See getPartialModuleByUri for why DocumentsLocator/fs URIs must
198
+ // be normalized.
199
+ uri: platformos_check_common_1.path.normalize(layoutUri),
200
+ dependencies: [],
201
+ references: [],
202
+ });
203
+ }
204
+ /**
205
+ * Create (or fetch the cached) Layout module for an already-resolved layout URI
206
+ * — used for the frontmatter `layout:` association edge, whose target is
207
+ * resolved by `DocumentsLocator` (`'layout'` type: `app/views/layouts`, module
208
+ * prefixes, `.html.liquid`/`.liquid`). Distinct from {@link getLayoutModule} only
209
+ * in that the latter accepts the `string | false | undefined` a frontmatter read
210
+ * yields; both normalize, so both key the same node.
211
+ */
212
+ function getLayoutModuleByUri(appGraph, uri) {
213
+ return module(appGraph, {
214
+ type: "Liquid" /* ModuleType.Liquid */,
215
+ kind: "layout" /* LiquidModuleKind.Layout */,
216
+ uri: platformos_check_common_1.path.normalize(uri),
84
217
  dependencies: [],
85
218
  references: [],
86
219
  });
@@ -89,7 +222,9 @@ function getPageModule(appGraph, pageUri) {
89
222
  return module(appGraph, {
90
223
  type: "Liquid" /* ModuleType.Liquid */,
91
224
  kind: "page" /* LiquidModuleKind.Page */,
92
- uri: pageUri,
225
+ // Normalize for the same node-identity reason as getLayoutModule / the other
226
+ // factories (see getPartialModuleByUri).
227
+ uri: platformos_check_common_1.path.normalize(pageUri),
93
228
  dependencies: [],
94
229
  references: [],
95
230
  });