@jay-framework/compiler-analyze-exported-types 0.5.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.
- package/dist/index.d.ts +10 -0
- package/dist/index.js +180 -0
- package/package.json +52 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { JayType } from '@jay-framework/compiler-shared';
|
|
2
|
+
|
|
3
|
+
interface ResolveTsConfigOptions {
|
|
4
|
+
relativePath?: string;
|
|
5
|
+
}
|
|
6
|
+
declare function resolveTsConfig(options: ResolveTsConfigOptions): string;
|
|
7
|
+
|
|
8
|
+
declare function analyzeExportedTypes(filename: string, options?: ResolveTsConfigOptions): JayType[];
|
|
9
|
+
|
|
10
|
+
export { type ResolveTsConfigOptions, analyzeExportedTypes, resolveTsConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const ts = require("typescript");
|
|
6
|
+
const compilerShared = require("@jay-framework/compiler-shared");
|
|
7
|
+
function resolveTsConfig(options) {
|
|
8
|
+
const tsConfigPath = path.resolve(process.cwd(), options.relativePath || "tsconfig.json");
|
|
9
|
+
if (!ts.sys.fileExists(tsConfigPath)) {
|
|
10
|
+
if (options.relativePath) {
|
|
11
|
+
throw new Error(`Could not find specified tsconfig.json at ${tsConfigPath}`);
|
|
12
|
+
} else {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return tsConfigPath;
|
|
17
|
+
}
|
|
18
|
+
function getTypeName(typeName) {
|
|
19
|
+
if (ts.isIdentifier(typeName))
|
|
20
|
+
return typeName.text;
|
|
21
|
+
else if (ts.isQualifiedName(typeName))
|
|
22
|
+
return `${getTypeName(typeName.left)}.${typeName.right.text}`;
|
|
23
|
+
}
|
|
24
|
+
function getJayType(typeNode, types) {
|
|
25
|
+
let propType = compilerShared.resolvePrimitiveType(typeNode.getText());
|
|
26
|
+
if (propType === compilerShared.JayUnknown && ts.isTypeReferenceNode(typeNode)) {
|
|
27
|
+
const typeName = getTypeName(typeNode.typeName);
|
|
28
|
+
if (typeName === "Array" && typeNode.typeArguments.length > 0)
|
|
29
|
+
propType = new compilerShared.JayArrayType(getJayType(typeNode.typeArguments[0], types));
|
|
30
|
+
else
|
|
31
|
+
propType = types.find((_) => _.name === getTypeName(typeNode.typeName)) ?? compilerShared.JayUnknown;
|
|
32
|
+
}
|
|
33
|
+
return propType;
|
|
34
|
+
}
|
|
35
|
+
function getInterfaceJayType(interfaceDeclaration, types) {
|
|
36
|
+
let props = {};
|
|
37
|
+
const jayObjectType = new compilerShared.JayObjectType(interfaceDeclaration.name.text, props);
|
|
38
|
+
interfaceDeclaration.members.filter((member) => ts.isPropertySignature(member) && ts.isIdentifier(member.name)).forEach((member) => {
|
|
39
|
+
let property = member;
|
|
40
|
+
let name = member.name;
|
|
41
|
+
let propKey = name.text;
|
|
42
|
+
props[propKey] = getJayType(property.type, [...types, jayObjectType]);
|
|
43
|
+
});
|
|
44
|
+
return jayObjectType;
|
|
45
|
+
}
|
|
46
|
+
function getElementConstructorType(name, typeName) {
|
|
47
|
+
return new compilerShared.JayElementConstructorType(name, typeName);
|
|
48
|
+
}
|
|
49
|
+
function getComponentType(tsTypeChecker, name, componentType) {
|
|
50
|
+
let properties = componentType.getProperties();
|
|
51
|
+
let componentAPIs = [];
|
|
52
|
+
for (let property of properties) {
|
|
53
|
+
let type = tsTypeChecker.getTypeAtLocation(property.getDeclarations()[0]);
|
|
54
|
+
if (JayComponentProperties[property.getName()])
|
|
55
|
+
;
|
|
56
|
+
else {
|
|
57
|
+
if (type.getSymbol().getName() === "EventEmitter")
|
|
58
|
+
componentAPIs.push(new compilerShared.JayComponentApiMember(property.getName(), true));
|
|
59
|
+
else
|
|
60
|
+
componentAPIs.push(new compilerShared.JayComponentApiMember(property.getName(), false));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return new compilerShared.JayComponentType(name, componentAPIs);
|
|
64
|
+
}
|
|
65
|
+
function autoAddExtension(filename) {
|
|
66
|
+
if (fs.existsSync(filename + ".ts"))
|
|
67
|
+
return filename + ".ts";
|
|
68
|
+
else if (fs.existsSync(filename + ".tsx"))
|
|
69
|
+
return filename + ".tsx";
|
|
70
|
+
else if (fs.existsSync(filename + ".d.ts"))
|
|
71
|
+
return filename + ".d.ts";
|
|
72
|
+
else if (fs.existsSync(filename))
|
|
73
|
+
return filename;
|
|
74
|
+
else
|
|
75
|
+
throw new Error(`File not found. Tried ${filename}, ${filename}.ts and ${filename}.d.ts`);
|
|
76
|
+
}
|
|
77
|
+
function isOrSubclassOf(type, ofClass) {
|
|
78
|
+
if (type.symbol?.name === ofClass)
|
|
79
|
+
return true;
|
|
80
|
+
for (let baseType of type.getBaseTypes() || [])
|
|
81
|
+
if (baseType.symbol?.name === ofClass)
|
|
82
|
+
return true;
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const JayComponentProperties = {
|
|
86
|
+
element: true,
|
|
87
|
+
update: true,
|
|
88
|
+
mount: true,
|
|
89
|
+
unmount: true,
|
|
90
|
+
addEventListener: true,
|
|
91
|
+
removeEventListener: true
|
|
92
|
+
};
|
|
93
|
+
function loadTSCompilerOptions(options = {}) {
|
|
94
|
+
let tsConfigPath = resolveTsConfig(options);
|
|
95
|
+
let loadedTSConfig = fs.readFileSync(tsConfigPath, "utf8");
|
|
96
|
+
let tsConfig = ts.parseConfigFileTextToJson("tsconfig.json", loadedTSConfig);
|
|
97
|
+
return ts.convertCompilerOptionsFromJson(tsConfig.config.compilerOptions, tsConfigPath).options;
|
|
98
|
+
}
|
|
99
|
+
function isExportedStatement(statement) {
|
|
100
|
+
return Boolean(
|
|
101
|
+
statement.modifiers && statement.modifiers.find(
|
|
102
|
+
(_) => _.kind === ts.SyntaxKind.ExportKeyword
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
const SYMBOLS = {
|
|
107
|
+
MAKE_JAY_COMPONENT: { module: compilerShared.JAY_COMPONENT, namedImport: compilerShared.MAKE_JAY_COMPONENT }
|
|
108
|
+
};
|
|
109
|
+
function findImportedSymbol(module2, namedImport) {
|
|
110
|
+
return Object.values(SYMBOLS).find(
|
|
111
|
+
(importedSymbol) => importedSymbol.module === module2 && importedSymbol.namedImport == namedImport
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
function mapImportedSymbols(statements, tsTypeChecker) {
|
|
115
|
+
statements.filter(ts.isImportDeclaration).forEach((importDeclaration) => {
|
|
116
|
+
if (ts.isStringLiteral(importDeclaration.moduleSpecifier) && importDeclaration.importClause?.namedBindings) {
|
|
117
|
+
const module2 = importDeclaration.moduleSpecifier.text;
|
|
118
|
+
if (ts.isNamedImports(importDeclaration.importClause.namedBindings)) {
|
|
119
|
+
importDeclaration.importClause.namedBindings.elements.forEach((namedImport) => {
|
|
120
|
+
const name = namedImport.name.text;
|
|
121
|
+
const importedSymbol = findImportedSymbol(module2, name);
|
|
122
|
+
if (importedSymbol)
|
|
123
|
+
importedSymbol.symbol = tsTypeChecker.getTypeAtLocation(namedImport).symbol;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return SYMBOLS;
|
|
129
|
+
}
|
|
130
|
+
function analyzeExportedTypes(filename, options = {}) {
|
|
131
|
+
let compilerOptions = loadTSCompilerOptions(options);
|
|
132
|
+
filename = autoAddExtension(filename);
|
|
133
|
+
const program = ts.createProgram([filename], compilerOptions);
|
|
134
|
+
const sourceFile = program.getSourceFile(filename);
|
|
135
|
+
const tsTypeChecker = program.getTypeChecker();
|
|
136
|
+
const types = [];
|
|
137
|
+
const { MAKE_JAY_COMPONENT: MAKE_JAY_COMPONENT2 } = mapImportedSymbols(sourceFile.statements, tsTypeChecker);
|
|
138
|
+
for (const statement of sourceFile.statements.filter(isExportedStatement)) {
|
|
139
|
+
if (ts.isInterfaceDeclaration(statement)) {
|
|
140
|
+
types.push(getInterfaceJayType(statement, types));
|
|
141
|
+
} else if (ts.isFunctionDeclaration(statement)) {
|
|
142
|
+
const functionType = tsTypeChecker.getTypeAtLocation(statement.type);
|
|
143
|
+
if (functionType.symbol || functionType.aliasSymbol) {
|
|
144
|
+
const typeName = functionType.symbol?.name;
|
|
145
|
+
const functionName = statement?.name.text;
|
|
146
|
+
const aliasName = functionType.aliasSymbol ? functionType.aliasSymbol.name : typeName;
|
|
147
|
+
if (functionName && isOrSubclassOf(functionType, "JayElement")) {
|
|
148
|
+
types.push(getElementConstructorType(functionName, aliasName));
|
|
149
|
+
} else if (isOrSubclassOf(functionType, "JayComponent")) {
|
|
150
|
+
types.push(getComponentType(tsTypeChecker, functionName, functionType));
|
|
151
|
+
} else
|
|
152
|
+
types.push(compilerShared.JayUnknown);
|
|
153
|
+
}
|
|
154
|
+
} else if (ts.isTypeAliasDeclaration(statement)) {
|
|
155
|
+
if (ts.isTypeReferenceNode(statement.type) && ts.isIdentifier(statement.type.typeName) && statement.type.typeName.text === "JayElement")
|
|
156
|
+
types.push(new compilerShared.JayElementType(statement.name.text));
|
|
157
|
+
} else if (ts.isVariableStatement(statement)) {
|
|
158
|
+
statement.declarationList.declarations.forEach((declaration) => {
|
|
159
|
+
if (ts.isCallExpression(declaration.initializer) && ts.isIdentifier(declaration.name)) {
|
|
160
|
+
const functionType = tsTypeChecker.getTypeAtLocation(
|
|
161
|
+
declaration.initializer.expression
|
|
162
|
+
);
|
|
163
|
+
const name = declaration.name.text;
|
|
164
|
+
if (functionType.symbol === MAKE_JAY_COMPONENT2.symbol) {
|
|
165
|
+
const callMakeJayComponentSignature = tsTypeChecker.getResolvedSignature(
|
|
166
|
+
declaration.initializer
|
|
167
|
+
);
|
|
168
|
+
const componentConstructorSignature = callMakeJayComponentSignature.getReturnType().getCallSignatures();
|
|
169
|
+
let componentType = componentConstructorSignature[0].getReturnType();
|
|
170
|
+
types.push(getComponentType(tsTypeChecker, name, componentType));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
} else
|
|
175
|
+
types.push(compilerShared.JayUnknown);
|
|
176
|
+
}
|
|
177
|
+
return types;
|
|
178
|
+
}
|
|
179
|
+
exports.analyzeExportedTypes = analyzeExportedTypes;
|
|
180
|
+
exports.resolveTsConfig = resolveTsConfig;
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jay-framework/compiler-analyze-exported-types",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"directories": {
|
|
8
|
+
"lib": "lib"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"readme.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "npm run build:js && npm run build:types",
|
|
16
|
+
"build:watch": "npm run build:js -- --watch & npm run build:types -- --watch",
|
|
17
|
+
"build:js": "vite build",
|
|
18
|
+
"build:types": "tsup lib/index.ts --dts-only --format cjs",
|
|
19
|
+
"build:check-types": "tsc",
|
|
20
|
+
"clean": "rimraf dist",
|
|
21
|
+
"confirm": "npm run clean && npm run build && npm run build:check-types && npm run test",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest"
|
|
24
|
+
},
|
|
25
|
+
"author": "",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@jay-framework/compiler-shared": "workspace:^",
|
|
28
|
+
"@jay-framework/component": "workspace:^",
|
|
29
|
+
"@jay-framework/runtime": "workspace:^",
|
|
30
|
+
"@jay-framework/secure": "workspace:^",
|
|
31
|
+
"@types/js-yaml": "^4.0.9",
|
|
32
|
+
"change-case": "^4.1.2",
|
|
33
|
+
"js-yaml": "^4.1.0",
|
|
34
|
+
"node-html-parser": "^6.1.12",
|
|
35
|
+
"pluralize": "^8.0.0",
|
|
36
|
+
"typescript": "^5.3.3"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@caiogondim/strip-margin": "^1.0.0",
|
|
40
|
+
"@jay-framework/dev-environment": "workspace:^",
|
|
41
|
+
"@testing-library/jest-dom": "^6.2.0",
|
|
42
|
+
"@types/js-beautify": "^1",
|
|
43
|
+
"@types/node": "^20.11.5",
|
|
44
|
+
"jest-diff": "^29.7.0",
|
|
45
|
+
"jest-matcher-utils": "^29.7.0",
|
|
46
|
+
"js-beautify": "^1.14.11",
|
|
47
|
+
"rimraf": "^5.0.5",
|
|
48
|
+
"tsup": "^8.0.1",
|
|
49
|
+
"vite": "^5.0.11",
|
|
50
|
+
"vitest": "^1.2.1"
|
|
51
|
+
}
|
|
52
|
+
}
|