@platformos/platformos-graph 0.0.2
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.
- package/CHANGELOG.md +10 -0
- package/README.md +83 -0
- package/bin/jsconfig.json +18 -0
- package/bin/platformos-graph +107 -0
- package/dist/getWebComponentMap.d.ts +10 -0
- package/dist/getWebComponentMap.js +66 -0
- package/dist/getWebComponentMap.js.map +1 -0
- package/dist/graph/augment.d.ts +2 -0
- package/dist/graph/augment.js +22 -0
- package/dist/graph/augment.js.map +1 -0
- package/dist/graph/build.d.ts +3 -0
- package/dist/graph/build.js +31 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/module.d.ts +10 -0
- package/dist/graph/module.js +181 -0
- package/dist/graph/module.js.map +1 -0
- package/dist/graph/serialize.d.ts +2 -0
- package/dist/graph/serialize.js +18 -0
- package/dist/graph/serialize.js.map +1 -0
- package/dist/graph/test-helpers.d.ts +33 -0
- package/dist/graph/test-helpers.js +49 -0
- package/dist/graph/test-helpers.js.map +1 -0
- package/dist/graph/traverse.d.ts +14 -0
- package/dist/graph/traverse.js +458 -0
- package/dist/graph/traverse.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/toSourceCode.d.ts +8 -0
- package/dist/toSourceCode.js +76 -0
- package/dist/toSourceCode.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.js +47 -0
- package/dist/utils/index.js.map +1 -0
- package/docs/graph.png +0 -0
- package/docs/how-it-works.md +89 -0
- package/fixtures/skeleton/app/views/partials/child.liquid +9 -0
- package/fixtures/skeleton/app/views/partials/parent.liquid +9 -0
- package/fixtures/skeleton/assets/theme.css +0 -0
- package/fixtures/skeleton/assets/theme.js +7 -0
- package/fixtures/skeleton/blocks/_private.liquid +1 -0
- package/fixtures/skeleton/blocks/_static.liquid +10 -0
- package/fixtures/skeleton/blocks/group.liquid +27 -0
- package/fixtures/skeleton/blocks/render-static.liquid +22 -0
- package/fixtures/skeleton/blocks/text.liquid +14 -0
- package/fixtures/skeleton/jsconfig.json +9 -0
- package/fixtures/skeleton/layout/theme.liquid +14 -0
- package/fixtures/skeleton/sections/custom-section.liquid +6 -0
- package/fixtures/skeleton/sections/header-group.json +36 -0
- package/fixtures/skeleton/sections/header.liquid +1 -0
- package/fixtures/skeleton/templates/index.json +20 -0
- package/package.json +41 -0
- package/src/getWebComponentMap.ts +81 -0
- package/src/graph/augment.ts +34 -0
- package/src/graph/build.spec.ts +248 -0
- package/src/graph/build.ts +45 -0
- package/src/graph/module.ts +212 -0
- package/src/graph/serialize.spec.ts +62 -0
- package/src/graph/serialize.ts +20 -0
- package/src/graph/test-helpers.ts +57 -0
- package/src/graph/traverse.ts +639 -0
- package/src/index.ts +5 -0
- package/src/toSourceCode.ts +80 -0
- package/src/types.ts +213 -0
- package/src/utils/index.ts +51 -0
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asError,
|
|
3
|
+
toSourceCode as tcToSourceCode,
|
|
4
|
+
UriString,
|
|
5
|
+
} from '@platformos/platformos-check-common';
|
|
6
|
+
import { parse as acornParse, Program } from 'acorn';
|
|
7
|
+
import {
|
|
8
|
+
CssSourceCode,
|
|
9
|
+
FileSourceCode,
|
|
10
|
+
ImageSourceCode,
|
|
11
|
+
JsSourceCode,
|
|
12
|
+
SUPPORTED_ASSET_IMAGE_EXTENSIONS,
|
|
13
|
+
SvgSourceCode,
|
|
14
|
+
} from './types';
|
|
15
|
+
import { extname } from './utils';
|
|
16
|
+
|
|
17
|
+
export async function toCssSourceCode(uri: UriString, source: string): Promise<CssSourceCode> {
|
|
18
|
+
return {
|
|
19
|
+
type: 'css',
|
|
20
|
+
uri,
|
|
21
|
+
source,
|
|
22
|
+
ast: new Error('File parsing not implemented yet'), // Placeholder for CSS parsing
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function toSvgSourceCode(uri: UriString, source: string): Promise<SvgSourceCode> {
|
|
27
|
+
return {
|
|
28
|
+
type: 'svg',
|
|
29
|
+
uri,
|
|
30
|
+
source,
|
|
31
|
+
ast: new Error('File parsing not implemented yet'), // Placeholder for SVG parsing
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function toImageSourceCode(uri: UriString, source: string): Promise<ImageSourceCode> {
|
|
36
|
+
return {
|
|
37
|
+
type: 'image',
|
|
38
|
+
uri,
|
|
39
|
+
source,
|
|
40
|
+
ast: new Error('Image files are not parsed'),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function toJsSourceCode(uri: UriString, source: string): Promise<JsSourceCode> {
|
|
45
|
+
return {
|
|
46
|
+
type: 'javascript',
|
|
47
|
+
uri,
|
|
48
|
+
source,
|
|
49
|
+
ast: parseJs(source),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function parseJs(source: string): Program | Error {
|
|
54
|
+
try {
|
|
55
|
+
return acornParse(source, {
|
|
56
|
+
ecmaVersion: 'latest',
|
|
57
|
+
sourceType: 'module',
|
|
58
|
+
});
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return asError(error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function toSourceCode(uri: UriString, source: string): Promise<FileSourceCode> {
|
|
65
|
+
const extension = extname(uri);
|
|
66
|
+
|
|
67
|
+
if (extension === 'json' || extension === 'liquid' || extension === 'graphql') {
|
|
68
|
+
return tcToSourceCode(uri, source);
|
|
69
|
+
} else if (extension === 'js') {
|
|
70
|
+
return toJsSourceCode(uri, source);
|
|
71
|
+
} else if (extension === 'css') {
|
|
72
|
+
return toCssSourceCode(uri, source);
|
|
73
|
+
} else if (extension === 'svg') {
|
|
74
|
+
return toCssSourceCode(uri, source);
|
|
75
|
+
} else if (SUPPORTED_ASSET_IMAGE_EXTENSIONS.includes(extension)) {
|
|
76
|
+
return toImageSourceCode(uri, source);
|
|
77
|
+
} else {
|
|
78
|
+
throw new Error(`Unknown source code type for ${uri}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JSONSourceCode,
|
|
3
|
+
LiquidSourceCode,
|
|
4
|
+
Dependencies as ThemeCheckDependencies,
|
|
5
|
+
UriString,
|
|
6
|
+
Reference,
|
|
7
|
+
Location,
|
|
8
|
+
Range,
|
|
9
|
+
GraphQLSourceCode,
|
|
10
|
+
} from '@platformos/platformos-check-common';
|
|
11
|
+
import { Program } from 'acorn';
|
|
12
|
+
|
|
13
|
+
export interface IDependencies {
|
|
14
|
+
fs: ThemeCheckDependencies['fs'];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Asynchronously get the block schema for 'blocks/${name}.liquid'
|
|
18
|
+
* May return undefined when the theme isn't preloaded.
|
|
19
|
+
*/
|
|
20
|
+
getBlockSchema: NonNullable<ThemeCheckDependencies['getBlockSchema']>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Asynchronously get the section schema for 'section/${name}.liquid'
|
|
24
|
+
* May return undefined when the theme isn't preloaded or if there are none.
|
|
25
|
+
*/
|
|
26
|
+
getSectionSchema: NonNullable<ThemeCheckDependencies['getSectionSchema']>;
|
|
27
|
+
|
|
28
|
+
/** Optional perf improvement if you somehow have access to pre-computed source code info */
|
|
29
|
+
getSourceCode?: (uri: UriString) => Promise<FileSourceCode>;
|
|
30
|
+
|
|
31
|
+
/** A way to link <custom-element> to its window.customElements.define statement */
|
|
32
|
+
getWebComponentDefinitionReference: (
|
|
33
|
+
customElementName: string,
|
|
34
|
+
) => { assetName: string; range: Range } | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type Dependencies = Required<IDependencies>;
|
|
38
|
+
|
|
39
|
+
export type AugmentedDependencies = Dependencies & {
|
|
40
|
+
getThemeBlockNames: () => Promise<string[]>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export interface ThemeGraph {
|
|
44
|
+
rootUri: UriString;
|
|
45
|
+
entryPoints: ThemeModule[];
|
|
46
|
+
modules: Record<UriString, ThemeModule>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type ThemeModule =
|
|
50
|
+
| LiquidModule
|
|
51
|
+
| JsonModule
|
|
52
|
+
| JavaScriptModule
|
|
53
|
+
| CssModule
|
|
54
|
+
| SvgModule
|
|
55
|
+
| ImageModule;
|
|
56
|
+
|
|
57
|
+
export type FileSourceCode =
|
|
58
|
+
| LiquidSourceCode
|
|
59
|
+
| JSONSourceCode
|
|
60
|
+
| GraphQLSourceCode
|
|
61
|
+
| JsSourceCode
|
|
62
|
+
| CssSourceCode
|
|
63
|
+
| SvgSourceCode
|
|
64
|
+
| ImageSourceCode;
|
|
65
|
+
|
|
66
|
+
export interface SerializableGraph {
|
|
67
|
+
rootUri: UriString;
|
|
68
|
+
nodes: SerializableNode[];
|
|
69
|
+
edges: SerializableEdge[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface SerializableEdge {
|
|
73
|
+
source: Location;
|
|
74
|
+
target: Location;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type SerializableNode = Pick<ThemeModule, 'uri' | 'type' | 'kind' | 'exists'>;
|
|
78
|
+
|
|
79
|
+
export interface LiquidModule extends IThemeModule<ModuleType.Liquid> {
|
|
80
|
+
kind: LiquidModuleKind;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface JsonModule extends IThemeModule<ModuleType.Json> {
|
|
84
|
+
kind: JsonModuleKind;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface JavaScriptModule extends IThemeModule<ModuleType.JavaScript> {
|
|
88
|
+
kind: 'unused';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface CssModule extends IThemeModule<ModuleType.Css> {
|
|
92
|
+
kind: 'unused';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface SvgModule extends IThemeModule<ModuleType.Svg> {
|
|
96
|
+
kind: 'unused';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ImageModule extends IThemeModule<ModuleType.Image> {
|
|
100
|
+
kind: 'unused';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface IThemeModule<T extends ModuleType> {
|
|
104
|
+
/** Used as a discriminant in the ThemeNode union */
|
|
105
|
+
type: T;
|
|
106
|
+
|
|
107
|
+
/** Should be normalized. Used as key. */
|
|
108
|
+
uri: UriString;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Outgoing references to other modules. e.g. {% render 'child' %} from parent
|
|
112
|
+
*
|
|
113
|
+
* The source URI of all dependencies is this module.
|
|
114
|
+
*/
|
|
115
|
+
dependencies: Reference[];
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Ingoing references from other modules. e.g. {% render 'child' %} in parent
|
|
119
|
+
*
|
|
120
|
+
* The target URI of all dependencies is this module.
|
|
121
|
+
*/
|
|
122
|
+
references: Reference[];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Since you could have files that depend on files that don't exist,
|
|
126
|
+
*
|
|
127
|
+
* this property will be used to quickly identify those.
|
|
128
|
+
*/
|
|
129
|
+
exists?: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const enum ModuleType {
|
|
133
|
+
Liquid = 'Liquid',
|
|
134
|
+
JavaScript = 'JavaScript',
|
|
135
|
+
Json = 'JSON',
|
|
136
|
+
Css = 'CSS',
|
|
137
|
+
Svg = 'SVG',
|
|
138
|
+
Image = 'Image',
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const enum JsonModuleKind {
|
|
142
|
+
/** templates/*.json files */
|
|
143
|
+
Template = 'template',
|
|
144
|
+
|
|
145
|
+
/** sections/*.json files */
|
|
146
|
+
SectionGroup = 'section-group',
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const enum LiquidModuleKind {
|
|
150
|
+
/** layout/*.liquid files */
|
|
151
|
+
Layout = 'layout',
|
|
152
|
+
|
|
153
|
+
/** sections/*.liquid files */
|
|
154
|
+
Section = 'section',
|
|
155
|
+
|
|
156
|
+
/** blocks/*.liquid files */
|
|
157
|
+
Block = 'block',
|
|
158
|
+
|
|
159
|
+
/** app/views/partials/*.liquid files */
|
|
160
|
+
Partial = 'partial',
|
|
161
|
+
|
|
162
|
+
/** templates/*.liquid files (forgot those existed...) */
|
|
163
|
+
Template = 'template',
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const SUPPORTED_ASSET_IMAGE_EXTENSIONS = [
|
|
167
|
+
'jpg',
|
|
168
|
+
'jpeg',
|
|
169
|
+
'png',
|
|
170
|
+
'gif',
|
|
171
|
+
'webp',
|
|
172
|
+
'heic',
|
|
173
|
+
'ico',
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
export interface CssSourceCode {
|
|
177
|
+
type: 'css';
|
|
178
|
+
uri: UriString;
|
|
179
|
+
source: string;
|
|
180
|
+
ast: any | Error;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface SvgSourceCode {
|
|
184
|
+
type: 'svg';
|
|
185
|
+
uri: UriString;
|
|
186
|
+
source: string;
|
|
187
|
+
ast: any | Error;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface ImageSourceCode {
|
|
191
|
+
type: 'image';
|
|
192
|
+
uri: UriString;
|
|
193
|
+
source: string;
|
|
194
|
+
ast: any | Error;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface JsSourceCode {
|
|
198
|
+
type: 'javascript';
|
|
199
|
+
uri: UriString;
|
|
200
|
+
source: string;
|
|
201
|
+
ast: Program | Error;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export { Reference, Range, Location };
|
|
205
|
+
|
|
206
|
+
export type Void = void | Void[]; /** e.g. product-element, */
|
|
207
|
+
|
|
208
|
+
export type WebComponentMap = Map<WebComponentName, WebComponentDefinition>;
|
|
209
|
+
export type WebComponentName = string;
|
|
210
|
+
export type WebComponentDefinition = {
|
|
211
|
+
assetName: string; // Relative path to the asset file
|
|
212
|
+
range: [number, number]; // Start and end positions in the file
|
|
213
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { UriString } from '@platformos/platformos-check-common';
|
|
2
|
+
import { AbstractFileSystem } from '@platformos/platformos-common';
|
|
3
|
+
import { AugmentedDependencies } from '../types';
|
|
4
|
+
|
|
5
|
+
export function unique<T>(array: T[]): T[] {
|
|
6
|
+
return [...new Set(array)];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function assertNever(module: never) {
|
|
10
|
+
throw new Error(`Unknown module type ${module}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function unexpected(): Error {
|
|
14
|
+
return new Error('Unexpected code path encountered');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const identity = <T>(x: T): T => x;
|
|
18
|
+
|
|
19
|
+
export function isString(x: unknown): x is string {
|
|
20
|
+
return typeof x === 'string';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function extname(uri: UriString): string {
|
|
24
|
+
return uri.split('.').pop() || '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function exists(fs: AbstractFileSystem, uri: UriString): Promise<boolean> {
|
|
28
|
+
return fs
|
|
29
|
+
.stat(uri)
|
|
30
|
+
.then(() => true)
|
|
31
|
+
.catch(() => false);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function acceptsLocalBlocks(
|
|
35
|
+
sectionType: string,
|
|
36
|
+
deps: AugmentedDependencies,
|
|
37
|
+
): Promise<boolean | Error> {
|
|
38
|
+
const sectionSchema = await deps.getSectionSchema(sectionType).catch((_) => undefined);
|
|
39
|
+
if (!sectionSchema) {
|
|
40
|
+
return new Error('Section does not exist');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const validSchema = sectionSchema.validSchema;
|
|
44
|
+
if (validSchema instanceof Error) {
|
|
45
|
+
return validSchema;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (validSchema.blocks ?? []).some((block) => {
|
|
49
|
+
return block.type && 'name' in block && block.name;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"./src/**/*.ts",
|
|
5
|
+
"./src/**/*.json"
|
|
6
|
+
],
|
|
7
|
+
"exclude": [
|
|
8
|
+
"node_modules",
|
|
9
|
+
"src/**/*.spec.ts"
|
|
10
|
+
],
|
|
11
|
+
"references": [
|
|
12
|
+
{
|
|
13
|
+
"path": "../platformos-check-common/tsconfig.build.json"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "../liquid-html-parser/tsconfig.build.json"
|
|
17
|
+
},
|
|
18
|
+
{ "path": "../platformos-common/tsconfig.build.json" }
|
|
19
|
+
]
|
|
20
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"./src/**/*.ts",
|
|
5
|
+
"./src/**/*.json"
|
|
6
|
+
],
|
|
7
|
+
"exclude": [
|
|
8
|
+
"./dist"
|
|
9
|
+
],
|
|
10
|
+
"compilerOptions": {
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src",
|
|
13
|
+
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
|
|
14
|
+
"paths": {
|
|
15
|
+
"@platformos/liquid-html-parser": [
|
|
16
|
+
"../liquid-html-parser/src"
|
|
17
|
+
],
|
|
18
|
+
"@platformos/platformos-check-common": [
|
|
19
|
+
"../platformos-check-common/src"
|
|
20
|
+
],
|
|
21
|
+
"@platformos/platformos-common": [
|
|
22
|
+
"../platformos-common/src"
|
|
23
|
+
],
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"references": [
|
|
27
|
+
{
|
|
28
|
+
"path": "../platformos-check-common"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "../liquid-html-parser"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"path": "../platformos-common"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|