Skip to content

Commit d46d45c

Browse files
committed
feat: requirejs indexer
1 parent cf7346b commit d46d45c

File tree

6 files changed

+192
-5
lines changed

6 files changed

+192
-5
lines changed

package-lock.json

Lines changed: 16 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@
427427
"@xml-tools/parser": "^1.0.11",
428428
"@xml-tools/simple-schema": "^3.0.5",
429429
"@xml-tools/validation": "^1.0.16",
430+
"acorn": "^8.14.1",
431+
"acorn-walk": "^8.3.4",
430432
"fast-xml-parser": "^4.5.1",
431433
"formik": "^2.4.6",
432434
"glob": "^11.0.1",

src/indexer/IndexManager.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@ import AclIndexer from './acl/AclIndexer';
1818
import { AclIndexData } from './acl/AclIndexData';
1919
import TemplateIndexer from './template/TemplateIndexer';
2020
import { TemplateIndexData } from './template/TemplateIndexData';
21+
import RequireJsIndexer from './require-js/RequireJsIndexer';
22+
import { RequireJsIndexData } from './require-js/RequireJsIndexData';
2123

2224
type IndexerInstance =
2325
| DiIndexer
2426
| ModuleIndexer
2527
| AutoloadNamespaceIndexer
2628
| EventsIndexer
2729
| AclIndexer
28-
| TemplateIndexer;
30+
| TemplateIndexer
31+
| RequireJsIndexer;
2932

3033
type IndexerDataMap = {
3134
[DiIndexer.KEY]: DiIndexData;
@@ -34,6 +37,7 @@ type IndexerDataMap = {
3437
[EventsIndexer.KEY]: EventsIndexData;
3538
[AclIndexer.KEY]: AclIndexData;
3639
[TemplateIndexer.KEY]: TemplateIndexData;
40+
[RequireJsIndexer.KEY]: RequireJsIndexData;
3741
};
3842

3943
class IndexManager {
@@ -48,6 +52,7 @@ class IndexManager {
4852
new EventsIndexer(),
4953
new AclIndexer(),
5054
new TemplateIndexer(),
55+
new RequireJsIndexer(),
5156
];
5257
this.indexStorage = new IndexStorage();
5358
}
@@ -181,6 +186,9 @@ class IndexManager {
181186
case TemplateIndexer.KEY:
182187
return new TemplateIndexData(data) as IndexerDataMap[T];
183188

189+
case RequireJsIndexer.KEY:
190+
return new RequireJsIndexData(data) as IndexerDataMap[T];
191+
184192
default:
185193
return undefined;
186194
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { AbstractIndexData } from 'indexer/AbstractIndexData';
2+
import RequireJsIndexer from './RequireJsIndexer';
3+
import { Map } from './types';
4+
5+
export class RequireJsIndexData extends AbstractIndexData<Map[]> {}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { RelativePattern, Uri } from 'vscode';
2+
import { Indexer } from 'indexer/Indexer';
3+
import { IndexerKey } from 'types/indexer';
4+
import { Map } from './types';
5+
import FileSystem from 'util/FileSystem';
6+
import {
7+
AssignmentProperty,
8+
Identifier,
9+
Literal,
10+
Node,
11+
ObjectExpression,
12+
Property,
13+
VariableDeclaration,
14+
VariableDeclarator,
15+
parse,
16+
} from 'acorn';
17+
import * as walk from 'acorn-walk';
18+
import { RequireJsConfig } from './types';
19+
20+
export default class RequireJsIndexer extends Indexer<RequireJsConfig> {
21+
public static readonly KEY = 'require-js';
22+
23+
public getVersion(): number {
24+
return 1;
25+
}
26+
27+
public getId(): IndexerKey {
28+
return RequireJsIndexer.KEY;
29+
}
30+
31+
public getName(): string {
32+
return 'requirejs-config.js';
33+
}
34+
35+
public getPattern(uri: Uri): RelativePattern {
36+
return new RelativePattern(uri, '**/view/**/requirejs-config.js');
37+
}
38+
39+
public async indexFile(uri: Uri): Promise<RequireJsConfig | undefined> {
40+
const js = await FileSystem.readFile(uri);
41+
const ast = parse(js, { ecmaVersion: 2020 });
42+
43+
// Find config identifier
44+
const configVariableDeclarator = await this.findConfigVariableDeclarator(ast);
45+
46+
if (!configVariableDeclarator) {
47+
return undefined;
48+
}
49+
50+
const config: RequireJsConfig = {
51+
map: {},
52+
paths: {},
53+
};
54+
55+
await this.processMaps(config, configVariableDeclarator);
56+
await this.processPaths(config, configVariableDeclarator);
57+
58+
return config;
59+
}
60+
61+
private async processMaps(config: RequireJsConfig, configVariableDeclarator: VariableDeclarator) {
62+
const mapProperty = await this.findProperty(configVariableDeclarator, 'map');
63+
64+
if (!mapProperty) {
65+
return config;
66+
}
67+
68+
const objectExpression = mapProperty.value as ObjectExpression;
69+
const properties = objectExpression.properties as Property[];
70+
71+
for (const property of properties) {
72+
const group = (property.key as Literal).value as string;
73+
const mappings = this.getGroupMappings(property);
74+
75+
config.map[group] = mappings;
76+
}
77+
}
78+
79+
private async processPaths(
80+
config: RequireJsConfig,
81+
configVariableDeclarator: VariableDeclarator
82+
) {
83+
const pathsProperty = await this.findProperty(configVariableDeclarator, 'paths');
84+
85+
if (!pathsProperty) {
86+
return config;
87+
}
88+
89+
const objectExpression = pathsProperty.value as ObjectExpression;
90+
const properties = objectExpression.properties as Property[];
91+
92+
for (const property of properties) {
93+
const key = (property.key as Literal).value as string;
94+
const value = (property.value as Literal).value as string;
95+
96+
config.paths[key] = value;
97+
}
98+
}
99+
100+
private getGroupMappings(property: Property): Record<string, string> {
101+
const objectExpression = property.value as ObjectExpression;
102+
const properties = objectExpression.properties as Property[];
103+
104+
const mappings: Record<string, string> = {};
105+
106+
for (const property of properties) {
107+
const key =
108+
property.key.type === 'Literal'
109+
? (property.key as Literal).value
110+
: (property.key as Identifier).name;
111+
const value =
112+
property.value.type === 'Literal'
113+
? (property.value as Literal).value
114+
: (property.value as Identifier).name;
115+
116+
mappings[key as string] = value as string;
117+
}
118+
119+
return mappings;
120+
}
121+
122+
private findConfigVariableDeclarator(ast: Node): Promise<VariableDeclarator | undefined> {
123+
return new Promise((resolve, reject) => {
124+
walk.simple(ast, {
125+
VariableDeclaration(node: VariableDeclaration) {
126+
node.declarations.forEach(declarator => {
127+
if (declarator.id.type === 'Identifier' && declarator.id.name === 'config') {
128+
resolve(declarator);
129+
}
130+
});
131+
},
132+
});
133+
134+
resolve(undefined);
135+
});
136+
}
137+
138+
private findProperty(
139+
variableDeclarator: VariableDeclarator,
140+
key: string
141+
): Promise<Property | AssignmentProperty | undefined> {
142+
return new Promise(resolve => {
143+
const objectExpression = variableDeclarator.init as ObjectExpression;
144+
145+
walk.simple(objectExpression, {
146+
Property(node: Property | AssignmentProperty) {
147+
if (node.key.type === 'Identifier' && node.key.name === key) {
148+
resolve(node);
149+
}
150+
},
151+
});
152+
153+
resolve(undefined);
154+
});
155+
}
156+
}

src/indexer/require-js/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface RequireJsConfig {
2+
map: Record<string, Record<string, string>>;
3+
paths: Record<string, string>;
4+
}

0 commit comments

Comments
 (0)