@jcassidyav/generate-metadata-filter
Version:
Generate a metadata filter from typescript
478 lines (472 loc) • 20 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
// package.json
var require_package = __commonJS({
"package.json"(exports, module2) {
module2.exports = {
name: "@jcassidyav/generate-metadata-filter",
version: "0.0.1-alpha",
description: "Generate a metadata filter from typescript",
keywords: [
"typescript",
"library",
"starter",
"template"
],
homepage: "https://github.com/jcassidyav/generate-metadata-filter",
bugs: "https://github.com/jcassidyav/generate-metadata-filter/issues",
author: "Jason Cassidy",
repository: "jcassidyav/generate-metadata-filter",
main: "./build/cjs/index.js",
module: "./build/esm/index.js",
types: "./build/index.d.ts",
license: "MIT",
bin: "./build/cjs/index.js",
engines: {
node: "> 14"
},
publishConfig: {
access: "public"
},
scripts: {
build: "npm run clean && npm run type-dts && npm run build-main",
"build-mainx": "esbuild --sourcemap --format=cjs --platform=node --outdir=dist ./src/index.ts",
pack: "npm pack && npm i -g jcassidyav-generate-metadata-filter-0.0.1-alpha.tgz",
"build-main": "node -r ./esbuild-hook ./scripts/build",
clean: "node -r ./esbuild-hook ./scripts/clean",
"type-dts": "tsc --emitDeclarationOnly",
"type-check": "tsc --noEmit",
format: 'prettier "src/**/*.ts" --write',
"format-check": 'prettier "src/**/*.ts" --check',
lint: "eslint src --ext .ts --fix",
"lint-check": "eslint src --ext .ts",
test: "ava --no-worker-threads",
"test-coverage": "nyc ava && nyc report --reporter=html",
"test-setup": "node -r ./esbuild-hook ./scripts/testSetup",
"spell-check": 'cspell "{README.md,CODE_OF_CONDUCT.md,CONTRIBUTING.md,.github/*.md,src/**/*.ts}"',
cz: "cz",
"semantic-release": "semantic-release"
},
devDependencies: {
"@ryansonshine/commitizen": "^4.2.8",
"@ryansonshine/cz-conventional-changelog": "^3.3.4",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/commit-analyzer": "^9.0.2",
"@semantic-release/github": "^8.0.4",
"@semantic-release/npm": "^9.0.1",
"@semantic-release/release-notes-generator": "^10.0.3",
"@types/node": "^18.0.0",
"@types/prompts": "^2.0.14",
"@typescript-eslint/eslint-plugin": "^5.27.1",
"@typescript-eslint/parser": "^5.27.1",
ava: "^4.3.0",
cspell: "^6.1.2",
esbuild: "^0.14.43",
eslint: "^8.17.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^2.7.1",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-import": "^2.26.0",
nyc: "^15.1.0",
prettier: "^2.6.2",
prompts: "^2.4.2",
"semantic-release": "^19.0.3",
slugify: "^1.6.5",
"source-map-support": "^0.5.21",
typescript: "^4.7.3",
"@types/minimatch": "^3.0.3",
"eslint-plugin-prettier": "^4.0.0"
},
volta: {
node: "16.15.1",
npm: "8.11.0"
},
overrides: {
"semver-regex": "3.1.4"
},
dependencies: {
"ts-morph": "^15.1.0",
minimatch: "5.1.0"
}
};
}
});
// src/androidGenerator.ts
var fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
var import_ts_morph = require("ts-morph");
var AndroidGenerator = class {
constructor(config2) {
this.config = config2;
}
result = new Array();
generate(scanResult2) {
for (const entry of scanResult2.entries()) {
let left;
let right;
console.log(entry[0]);
const info = entry[1];
console.log("name", info.name, "kind", info.declaration.kindName);
if (info.declaration.kind === import_ts_morph.ts.SyntaxKind.ClassDeclaration || info.declaration.kind === import_ts_morph.ts.SyntaxKind.EnumDeclaration || info.declaration.kind === import_ts_morph.ts.SyntaxKind.InterfaceDeclaration) {
right = info.name;
if (info.declaration.ancestors) {
const ancestorInfo = this.considerAncestorRules(info.declaration.ancestors);
left = ancestorInfo.left;
right = ancestorInfo.right.length > 0 ? ancestorInfo.right + "." + right : right;
this.result.push({ left, right });
}
} else if (info.declaration.kind === import_ts_morph.ts.SyntaxKind.PropertyDeclaration) {
} else if (info.declaration.kind === import_ts_morph.ts.SyntaxKind.MethodDeclaration) {
}
}
console.log("************ results *************");
this.result.forEach((r) => console.log("left", r.left, "right", r.right));
const outputResult = this.populateTemplate();
const outPath = this.config.output ?? "";
if (this.config.output)
fs.mkdirSync(this.config.output, { recursive: true });
fs.writeFileSync(import_path.default.join(outPath, "native-api-usage.json"), JSON.stringify(outputResult, null, 5));
}
populateTemplate() {
const result = /* @__PURE__ */ new Set();
this.result.forEach((r) => {
result.add(r.left + ":" + r.right);
});
if (this.config.mode == "app") {
const template = new AppTemplate();
template.whitelist = Array.from(result).sort();
return template;
} else if (this.config.mode == "plugin") {
const template = new PluginTemplate();
template.uses = Array.from(result).sort();
return template;
}
}
considerAncestorRules(ancestors) {
const theClasses = [];
let thePackage = "";
for (const ancestor of ancestors) {
if (ancestor.kindName === "ClassDeclaration") {
theClasses.push(ancestor.name);
} else if (ancestor.kindName === "ModuleDeclaration") {
thePackage = ancestor.fullName;
break;
}
}
return { left: thePackage, right: theClasses.reverse().join(".") };
}
addAncestorRules(ancestors) {
if (ancestors.length > 1) {
const base = ancestors[0];
const parent = ancestors[1];
if (base.kindName === "ClassDeclaration") {
this.result.push({ left: parent.fullName, right: base.name });
this.addAncestorRules(ancestors.slice(1));
}
}
}
};
var PluginTemplate = class {
uses = [];
};
var AppTemplate = class {
"whitelist-plugins-usages" = true;
whitelist = [];
blacklist = [];
};
// src/compile.ts
var import_path2 = __toESM(require("path"));
var import_minimatch = require("minimatch");
var import_ts_morph2 = require("ts-morph");
var Scanner = class {
constructor(config2) {
this.config = config2;
}
log = false;
identified = new ScanResults();
logDetails(node, symbol, declaration, parent) {
const checkParent = symbol?.getDeclarations()[0]?.getParent()?.getKindName();
const filePath = symbol?.getDeclarations()[0].getSourceFile().getFilePath();
const kind = symbol?.getDeclarations()[0].getKind();
let symbolName = symbol?.getFullyQualifiedName();
if (this.log) {
console.log("Parent is:", checkParent);
console.log("Symbol declared in:", filePath);
console.log("Symbol: symbol name", symbolName);
console.log("Symbol Type", symbol?.getDeclaredType().getText());
console.log("Location", node?.getSourceFile().getFilePath());
console.log("node Type", node?.getType().getText());
console.log("node Type 2", symbol?.getDeclarations()[0].getKindName());
}
if (symbol) {
if (kind === import_ts_morph2.ts.SyntaxKind.InterfaceDeclaration || kind === import_ts_morph2.ts.SyntaxKind.EnumDeclaration || kind === import_ts_morph2.ts.SyntaxKind.ClassDeclaration || kind === import_ts_morph2.ts.SyntaxKind.MethodDeclaration || kind === import_ts_morph2.ts.SyntaxKind.PropertyDeclaration) {
if (declaration.fullName == declaration.name && declaration.ancestors?.length) {
symbolName = declaration.name + declaration.ancestors[0].fullName;
}
this.identified.set(symbolName, { name: symbol?.getName(), declaration, parent });
}
}
}
getSanePath(thePath) {
return thePath.split(import_path2.default.sep).join(import_path2.default.posix.sep);
}
isTypeDefinitionNative(filePath) {
if (this.isTypeDefinitionAndroid(filePath)) {
return { isNative: true, isAndroid: true };
} else if (this.isTypeDefinitionIOS(filePath)) {
return { isNative: true, isIOS: true };
}
return { isNative: false };
}
isTypeDefinitionIOS(filePath) {
return filePath?.includes("@nativescript/types-ios") || !!this.config.typeSources?.ios?.find((pattern) => (0, import_minimatch.match)([filePath], pattern));
}
isTypeDefinitionAndroid(filePath) {
if (filePath?.includes("@nativescript/types-android")) {
return true;
}
if (this.config.typeSources?.android) {
for (let i = 0; i < this.config.typeSources?.android.length; ++i) {
const isMatch = (0, import_minimatch.match)([filePath], this.config.typeSources.android[0]);
if (isMatch.length > 0)
return true;
}
}
return false;
}
doScan() {
const project = new import_ts_morph2.Project({
tsConfigFilePath: this.config.tsconfig ?? "./tsconfig.json"
});
project.getSourceFiles().forEach((sourceFile) => {
sourceFile.getDescendants().forEach((node) => {
const symbol = node.getSymbol();
if (symbol) {
const decl = symbol?.getDeclarations();
if (decl && decl.length > 0) {
const declInfos = this.getInterestingDeclarationsInfo(symbol, node.getSourceFile().getFilePath());
for (const declInfo of declInfos) {
if (declInfo.isInteresting) {
this.logDetails(node, symbol, declInfo);
if (declInfo.kind === import_ts_morph2.ts.SyntaxKind.ClassDeclaration) {
for (const classDeclarationNode of symbol.getDeclarations()) {
if (classDeclarationNode.getKind() == import_ts_morph2.ts.SyntaxKind.ClassDeclaration) {
const classDeclaration = classDeclarationNode;
const extendedTypes = [];
const classExtended = classDeclaration.getExtends();
if (classExtended) {
extendedTypes.push(classExtended);
}
const interfaces = classDeclaration.getImplements();
if (interfaces && interfaces.length > 0) {
extendedTypes.push(...interfaces);
}
this.processExtended(extendedTypes);
}
}
}
if (declInfo.kind === import_ts_morph2.ts.SyntaxKind.MethodDeclaration) {
for (const methodDeclarationNode of symbol.getDeclarations()) {
const methodDeclaration = methodDeclarationNode;
const returnTypeSymbol = methodDeclaration.getReturnType().getSymbol();
const returnSymbolNode = methodDeclaration.getReturnTypeNode();
const returnDecl = this.processDeclarationNodeSymbol(returnTypeSymbol, returnSymbolNode);
if (returnDecl?.isInteresting) {
this.logDetails(returnSymbolNode, returnTypeSymbol, returnDecl);
}
methodDeclaration.getParameters().forEach((value) => {
const paramDecl = this.processDeclarationNodeSymbol(value.getType().getSymbol(), value.getTypeNode());
if (paramDecl?.isInteresting) {
this.logDetails(value.getTypeNode(), value.getType().getSymbol(), paramDecl);
}
});
}
} else if (declInfo.kind === import_ts_morph2.ts.SyntaxKind.PropertyDeclaration) {
for (const propertyDeclarationNode of symbol.getDeclarations()) {
const propertyDeclaration = propertyDeclarationNode;
if (propertyDeclaration) {
const returnTypeSymbol = propertyDeclaration.getType().getSymbol();
const returnSymbolNode = propertyDeclaration.getTypeNode();
const returnDecl = this.processDeclarationNodeSymbol(returnTypeSymbol, returnSymbolNode);
if (returnDecl?.isInteresting) {
this.logDetails(returnSymbolNode, returnTypeSymbol, returnDecl);
}
}
}
}
}
}
}
}
});
});
return this.identified;
}
processExtended(extendedTypes) {
if (extendedTypes && extendedTypes.length > 0) {
for (const extendedType of extendedTypes) {
const typeSymbol = extendedType.getType().getSymbol();
const returnDecl = this.processDeclarationNodeSymbol(typeSymbol, extendedType);
if (returnDecl?.isInteresting) {
this.logDetails(extendedType, typeSymbol, returnDecl);
}
const typeArgs = extendedType.getTypeArguments();
for (const typeArg of typeArgs) {
const returnDecl2 = this.processDeclarationNodeSymbol(typeArg.getSymbol(), typeArg);
if (returnDecl2?.isInteresting) {
this.logDetails(typeArg, typeArg.getSymbol(), returnDecl2);
}
}
}
}
}
processDeclarationNodeSymbol(typeSymbol, node) {
if (typeSymbol && node) {
const declInfo = this.getDeclarationsInfo(typeSymbol, "node.getSourceFile().getFilePath()");
if (declInfo.isInteresting) {
return declInfo;
}
}
}
walkToGetNamespaceClass(declaration) {
const interestingAncestors = [];
const ancestors = declaration.getAncestors();
for (const ancestor of ancestors) {
const ancestorKind = ancestor.getKind();
if (ancestorKind === import_ts_morph2.ts.SyntaxKind.ClassDeclaration) {
interestingAncestors.push({
fullName: ancestor.getSymbol()?.getFullyQualifiedName(),
name: ancestor.getSymbol()?.getName(),
isInteresting: true,
kindName: ancestor.getKindName(),
kind: ancestorKind
});
} else if (ancestorKind === import_ts_morph2.ts.SyntaxKind.ModuleDeclaration) {
const result = ancestor.getFirstAncestorByKind(import_ts_morph2.ts.SyntaxKind.ModuleDeclaration)?.getDescendants().find((node) => node.isKind(import_ts_morph2.ts.SyntaxKind.ClassDeclaration) && node.getName() === ancestor.getName());
if (result) {
interestingAncestors.push({
name: ancestor.getSymbol()?.getName(),
fullName: ancestor.getSymbol()?.getFullyQualifiedName(),
isInteresting: true,
kindName: "ClassDeclaration",
kind: import_ts_morph2.ts.SyntaxKind.ClassDeclaration
});
continue;
}
interestingAncestors.push({
name: ancestor.getSymbol()?.getName(),
fullName: ancestor.getSymbol()?.getFullyQualifiedName(),
isInteresting: true,
kindName: ancestor.getKindName(),
kind: ancestor.getKind()
});
break;
}
}
return interestingAncestors;
}
getDeclarationInfo(declaration, symbolFullName, anyKind = false) {
const filePath = this.getSanePath(import_path2.default.relative(this.getSanePath(process.cwd()), declaration.getSourceFile().getFilePath()));
const kind = declaration.getKind();
if (anyKind || kind === import_ts_morph2.ts.SyntaxKind.ClassDeclaration || kind === import_ts_morph2.ts.SyntaxKind.MethodDeclaration || kind === import_ts_morph2.ts.SyntaxKind.PropertyDeclaration || kind === import_ts_morph2.ts.SyntaxKind.InterfaceDeclaration || kind === import_ts_morph2.ts.SyntaxKind.TypeAliasDeclaration || kind === import_ts_morph2.ts.SyntaxKind.EnumDeclaration) {
const typeDefinition = this.isTypeDefinitionNative(filePath);
if (typeDefinition.isNative) {
const ancestors = this.walkToGetNamespaceClass(declaration);
return {
name: declaration.getName(),
fullName: symbolFullName,
typeClassification: typeDefinition,
kindName: declaration.getKindName(),
filePath,
isInteresting: true,
ancestors,
kind: declaration.getKind()
};
}
}
return { name: "", fullName: symbolFullName, isInteresting: false, kind: declaration.getKind() };
}
getInterestingDeclarationsInfo(symbol, sourceNodeFilePath) {
const interesting = [];
const symbolFullName = symbol.getFullyQualifiedName();
if (!sourceNodeFilePath.includes(".d.ts")) {
for (const declaration of symbol.getDeclarations()) {
const info = this.getDeclarationInfo(declaration, symbolFullName);
if (info.isInteresting) {
interesting.push(info);
}
}
}
return interesting;
}
getDeclarationsInfo(symbol, sourceNodeFilePath) {
const symbolFullName = symbol.getFullyQualifiedName();
if (!sourceNodeFilePath.includes(".d.ts")) {
for (const declaration of symbol.getDeclarations()) {
const info = this.getDeclarationInfo(declaration, symbolFullName);
if (info.isInteresting) {
return info;
}
}
}
return { name: "", fullName: symbolFullName, isInteresting: false, kind: import_ts_morph2.ts.SyntaxKind.Unknown };
}
};
var ScanResults = class {
ios = /* @__PURE__ */ new Map();
android = /* @__PURE__ */ new Map();
set(name, symbolInfo) {
if (symbolInfo.declaration.typeClassification?.isAndroid) {
this.android.set(name, symbolInfo);
} else if (symbolInfo.declaration.typeClassification?.isIOS) {
this.ios.set(name, symbolInfo);
}
}
};
// src/config.ts
var fs2 = __toESM(require("fs"));
var ConfigX = class {
static readConfig() {
if (fs2.existsSync(".generate-metadata-filter.json")) {
const configContents = fs2.readFileSync("./.generate-metadata-filter.json", { encoding: "utf8" });
return JSON.parse(configContents);
} else {
throw new Error("Could not find file `.generate-metadata-filter.json`");
}
}
};
// src/index.ts
var version = require_package().version;
console.log("Scanning Typescript for Native Types", version);
var config = ConfigX.readConfig();
var compile = new Scanner(config);
var scanResult = compile.doScan();
console.log("*********** Identified IOS*************");
scanResult.ios.forEach((value, key) => {
console.log(key, JSON.stringify(value));
});
console.log("*********** Identified Android *************");
scanResult.android.forEach((value, key) => {
console.log(key, JSON.stringify(value));
});
var generator = new AndroidGenerator(config);
generator.generate(scanResult.android);
//# sourceMappingURL=index.js.map