@platformos/platformos-graph 0.0.19 → 0.0.20

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 (53) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/platformos-graph +21 -81
  3. package/dist/cli.d.ts +46 -0
  4. package/dist/cli.js +126 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/graph/augment.js +0 -1
  7. package/dist/graph/augment.js.map +1 -1
  8. package/dist/graph/module.d.ts +16 -1
  9. package/dist/graph/module.js +39 -0
  10. package/dist/graph/module.js.map +1 -1
  11. package/dist/graph/test-helpers.d.ts +2 -3
  12. package/dist/graph/test-helpers.js +2 -6
  13. package/dist/graph/test-helpers.js.map +1 -1
  14. package/dist/graph/traverse.d.ts +31 -3
  15. package/dist/graph/traverse.js +146 -41
  16. package/dist/graph/traverse.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +3 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/tsconfig.tsbuildinfo +1 -1
  21. package/dist/types.d.ts +14 -19
  22. package/dist/types.js.map +1 -1
  23. package/fixtures/background-edges/app/views/pages/broken.liquid +3 -0
  24. package/fixtures/background-edges/app/views/pages/index.liquid +3 -0
  25. package/fixtures/background-edges/app/views/partials/jobs/notify.liquid +3 -0
  26. package/fixtures/function-edges/app/lib/queries/list.liquid +3 -0
  27. package/fixtures/function-edges/app/views/pages/broken.liquid +3 -0
  28. package/fixtures/function-edges/app/views/pages/index.liquid +3 -0
  29. package/fixtures/graphql-edges/app/graphql/blog_posts/find.graphql +10 -0
  30. package/fixtures/graphql-edges/app/views/pages/broken.liquid +3 -0
  31. package/fixtures/graphql-edges/app/views/pages/index.liquid +3 -0
  32. package/fixtures/include-edges/app/views/pages/index.liquid +1 -0
  33. package/fixtures/include-edges/app/views/partials/shared/header.liquid +1 -0
  34. package/fixtures/module-edges/app/views/pages/index.liquid +4 -0
  35. package/fixtures/module-edges/modules/my_module/public/lib/queries/get.liquid +3 -0
  36. package/fixtures/module-edges/modules/my_module/public/views/partials/card.liquid +1 -0
  37. package/fixtures/skeleton/app/views/partials/child.liquid +2 -4
  38. package/fixtures/skeleton/app/views/partials/parent.liquid +2 -4
  39. package/fixtures/skeleton/assets/app.js +1 -7
  40. package/package.json +5 -5
  41. package/src/cli.spec.ts +265 -0
  42. package/src/cli.ts +151 -0
  43. package/src/graph/augment.ts +0 -2
  44. package/src/graph/build.spec.ts +19 -5
  45. package/src/graph/extract.spec.ts +155 -0
  46. package/src/graph/module.ts +40 -0
  47. package/src/graph/test-helpers.ts +2 -9
  48. package/src/graph/traverse-edges.spec.ts +278 -0
  49. package/src/graph/traverse.ts +177 -49
  50. package/src/index.ts +1 -1
  51. package/src/types.ts +16 -18
  52. package/bin/jsconfig.json +0 -18
  53. package/src/getWebComponentMap.ts +0 -81
package/src/types.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  Dependencies as CheckDependencies,
5
5
  UriString,
6
6
  Reference,
7
+ ReferenceKind,
7
8
  Location,
8
9
  Range,
9
10
  GraphQLSourceCode,
@@ -15,11 +16,6 @@ export interface IDependencies {
15
16
 
16
17
  /** Optional perf improvement if you somehow have access to pre-computed source code info */
17
18
  getSourceCode?: (uri: UriString) => Promise<FileSourceCode>;
18
-
19
- /** A way to link <custom-element> to its window.customElements.define statement */
20
- getWebComponentDefinitionReference: (
21
- customElementName: string,
22
- ) => { assetName: string; range: Range } | undefined;
23
19
  }
24
20
 
25
21
  export type Dependencies = Required<IDependencies>;
@@ -32,7 +28,7 @@ export interface AppGraph {
32
28
  modules: Record<UriString, AppModule>;
33
29
  }
34
30
 
35
- export type AppModule = LiquidModule | AssetModule;
31
+ export type AppModule = LiquidModule | AssetModule | GraphQLModule;
36
32
 
37
33
  export type FileSourceCode =
38
34
  | LiquidSourceCode
@@ -47,10 +43,9 @@ export interface SerializableGraph {
47
43
  edges: SerializableEdge[];
48
44
  }
49
45
 
50
- export interface SerializableEdge {
51
- source: Location;
52
- target: Location;
53
- }
46
+ // Serialized edges are the modules' dependency `Reference`s verbatim, so they
47
+ // carry `type` and `kind` in addition to `source`/`target`.
48
+ export type SerializableEdge = Reference;
54
49
 
55
50
  export type SerializableNode = Pick<AppModule, 'uri' | 'type' | 'kind' | 'exists'>;
56
51
 
@@ -62,6 +57,15 @@ export interface AssetModule extends IAppModule<ModuleType.Asset> {
62
57
  kind: 'unused';
63
58
  }
64
59
 
60
+ /**
61
+ * A `.graphql` operation file (referenced by `{% graphql op = 'name' %}`).
62
+ * A leaf node — GraphQL documents have no outgoing platformOS dependencies —
63
+ * so it is not traversed, only existence-checked (like {@link AssetModule}).
64
+ */
65
+ export interface GraphQLModule extends IAppModule<ModuleType.GraphQL> {
66
+ kind: 'graphql';
67
+ }
68
+
65
69
  export interface IAppModule<T extends ModuleType> {
66
70
  /** Used as a discriminant in the AppModule union */
67
71
  type: T;
@@ -94,6 +98,7 @@ export interface IAppModule<T extends ModuleType> {
94
98
  export const enum ModuleType {
95
99
  Liquid = 'Liquid',
96
100
  Asset = 'Asset',
101
+ GraphQL = 'GraphQL',
97
102
  }
98
103
 
99
104
  export const enum LiquidModuleKind {
@@ -124,13 +129,6 @@ export interface AssetSourceCode {
124
129
  ast: any | Error;
125
130
  }
126
131
 
127
- export { Reference, Range, Location };
132
+ export { Reference, ReferenceKind, Range, Location };
128
133
 
129
134
  export type Void = void | Void[];
130
-
131
- export type WebComponentMap = Map<WebComponentName, WebComponentDefinition>;
132
- export type WebComponentName = string;
133
- export type WebComponentDefinition = {
134
- assetName: string; // Relative path to the asset file
135
- range: [number, number]; // Start and end positions in the file
136
- };
package/bin/jsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "commonjs",
5
- "checkJs": true,
6
- "strict": true,
7
- "baseUrl": ".",
8
- "paths": {
9
- "@platformos/platformos-graph": ["../src"],
10
- "@platformos/platformos-check-common": ["../../platformos-check-common/src"]
11
- },
12
- "allowJs": true,
13
- "noEmit": true,
14
- "esModuleInterop": true,
15
- "skipLibCheck": true
16
- },
17
- "files": ["./platformos-graph"]
18
- }
@@ -1,81 +0,0 @@
1
- import { path, recursiveReadDirectory } from '@platformos/platformos-check-common';
2
- import { ancestor as visit } from 'acorn-walk';
3
- import { Dependencies, Void, WebComponentMap } from './types';
4
- import { CallExpression } from 'acorn';
5
-
6
- /**
7
- * Regular expression for web component names
8
- *
9
- * Based on the HTML specification for valid custom element names.
10
- *
11
- * https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
12
- */
13
- const wcre =
14
- /^[a-z][-.\d_a-z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]*([-][-.\d_a-z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]*)$/u;
15
-
16
- /**
17
- * Find all the web component definitions from the JavaScript files in the
18
- * assets directory.
19
- *
20
- * From those, we'll be able to map `<custom-element-name>` to the definition in
21
- * the corresponding asset file.
22
- */
23
- export async function getWebComponentMap(
24
- rootUri: string,
25
- { fs, getSourceCode }: Pick<Dependencies, 'fs' | 'getSourceCode'>,
26
- ): Promise<WebComponentMap> {
27
- const webComponentDefs: WebComponentMap = new Map();
28
- const assetRoot = path.join(rootUri, 'assets');
29
- const jsFiles = await recursiveReadDirectory(fs, assetRoot, ([fileName]) =>
30
- fileName.endsWith('.js'),
31
- );
32
-
33
- await Promise.all(
34
- jsFiles.map((uri) =>
35
- findWebComponentReferences(uri, assetRoot, getSourceCode, webComponentDefs),
36
- ),
37
- );
38
-
39
- return webComponentDefs;
40
- }
41
-
42
- export async function findWebComponentReferences(
43
- uri: string,
44
- assetRoot: string,
45
- getSourceCode: Dependencies['getSourceCode'],
46
- result: WebComponentMap,
47
- ): Promise<Void> {
48
- const sourceCode = await getSourceCode(uri);
49
- if (!uri.endsWith('.js')) {
50
- return;
51
- }
52
-
53
- const ast = sourceCode.ast;
54
- if (ast instanceof Error) {
55
- return;
56
- }
57
-
58
- for (const node of ast.body) {
59
- visit(node, {
60
- Literal(node, _state, ancestors) {
61
- if (typeof node.value === 'string' && wcre.test(node.value)) {
62
- // Making sure we're looking at customElements.define calls
63
- const parentNode = ancestors.at(-2);
64
- if (!parentNode) return;
65
- if (parentNode.type !== 'CallExpression') return;
66
- const callee = (parentNode as CallExpression).callee;
67
- if (callee.type !== 'MemberExpression') return;
68
- const property = callee.property;
69
- if (property.type !== 'Identifier') return;
70
- if (property.name !== 'define') return;
71
-
72
- result.set(node.value, {
73
- assetName: path.relative(uri, assetRoot),
74
- range: [property.start, node.end],
75
- });
76
- }
77
- return null;
78
- },
79
- });
80
- }
81
- }