UNPKG

@jcassidyav/generate-metadata-filter

Version:
478 lines (472 loc) 20 kB
#!/usr/bin/env node "use strict"; 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