Repository navigation
Expand file tree
/
Copy paththeme-graph
More file actions
executable file
·127 lines (110 loc) · 3.57 KB
/
Copy paththeme-graph
File metadata and controls
executable file
·127 lines (110 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env node
const { buildThemeGraph, serializeThemeGraph, toSourceCode } = require('@shopify/theme-graph');
const {
memoize,
toSchema,
path: pathUtils,
recursiveReadDirectory,
FileType,
} = require('@shopify/theme-check-common');
const fs = require('node:fs/promises');
const path = require('path');
const { URI } = require('vscode-uri');
const usage = `
Usage:
theme-graph <path-to-theme-directory>
Example:
theme-graph horizon > graph.json
`;
const identity = (/** @type {any} */ x) => x;
/** @type {import('@shopify/theme-check-common').AbstractFileSystem} */
const NodeFileSystem = {
async readFile(uri) {
return fs.readFile(pathUtils.fsPath(uri), 'utf8');
},
async readDirectory(uri) {
const files = await fs.readdir(pathUtils.fsPath(uri), { withFileTypes: true });
return files.map((file) => {
return [pathUtils.join(uri, file.name), file.isDirectory() ? FileType.Directory : FileType.File];
});
},
async stat(uri) {
const stats = await fs.stat(pathUtils.fsPath(uri));
return {
type: stats.isDirectory() ? FileType.Directory : FileType.File,
size: stats.size,
};
},
};
/**
* Outputs the JSON representing the theme graph for a Shopify theme directory.
*
* @param {string} root
*/
async function main(root) {
if (!root) {
console.error(usage);
process.exit(1);
}
if (!path.isAbsolute(root)) {
root = path.resolve(process.cwd(), root);
}
const getSourceCode = makeGetSourceCode();
// Preload files to get a sense of what is slow
const rootUri = URI.file(root).toString(true);
const filesToPreload = await recursiveReadDirectory(NodeFileSystem, rootUri, ([uri]) =>
['.json', '.liquid'].some((ext) => uri.endsWith(ext)),
);
await bench('Preloading files', async () => {
await Promise.all(
filesToPreload.map(async (uri) => {
await getSourceCode(uri);
}),
);
});
/** @type {import('@shopify/theme-graph').WebComponentMap} */
const webComponentDefs = new Map();
/** @type {import('@shopify/theme-graph').Dependencies} */
const dependencies = {
fs: NodeFileSystem,
// @ts-ignore
getSectionSchema: memoize(async (name) => {
const uri = pathUtils.join(rootUri, 'sections', `${name}.liquid`);
const sourceCode = await getSourceCode(uri);
return toSchema('theme', uri, /** @type {any} */ (sourceCode), async () => true);
}, identity),
// @ts-ignore
getBlockSchema: memoize(async (name) => {
const uri = pathUtils.join(rootUri, 'blocks', `${name}.liquid`);
const sourceCode = await getSourceCode(uri);
return toSchema('theme', uri, /** @type {any} */ (sourceCode), async () => true);
}, identity),
getSourceCode,
getWebComponentDefinitionReference: (customElementName) =>
webComponentDefs.get(customElementName),
};
// Build graph
const graph = await bench('Build graph', () => buildThemeGraph(rootUri, dependencies));
// Serialize into JSON
const serializedGraph = serializeThemeGraph(graph);
// Print result to STDOUT
console.log(JSON.stringify(serializedGraph));
}
function makeGetSourceCode() {
return memoize(async function getSourceCode(uri) {
const source = await NodeFileSystem.readFile(uri);
return await toSourceCode(URI.file(uri).toString(), source);
}, identity);
}
/**
* @param {string} name
* @param {(...args: any[]) => Promise<any>} fn
*/
async function bench(name, fn) {
const start = performance.now();
const result = await fn();
const end = performance.now();
console.error(`${name} took ${end - start}ms`);
return result;
}
main(process.argv[2]);