UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

90 lines (88 loc) 4.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.scan = exports.scanContext = void 0; const tslib_1 = require("tslib"); const ts_utils_1 = require("@neo-one/ts-utils"); const utils_1 = require("@neo-one/utils"); const typescript_1 = tslib_1.__importDefault(require("typescript")); const createContext_1 = require("./createContext"); const errors_1 = require("./errors"); exports.scanContext = (context) => { const smartContract = ts_utils_1.tsUtils.symbol.getDeclarations(context.builtins.getValueSymbol('SmartContract'))[0]; if (!typescript_1.default.isClassDeclaration(smartContract)) { throw new Error('Something went wrong!'); } const { contracts, dependencies } = ts_utils_1.tsUtils.class_ .getExtendors(context.program, context.languageService, smartContract) .reduce((acc, derived) => { if (!ts_utils_1.tsUtils.modifier.isAbstract(derived) && !ts_utils_1.tsUtils.file.isDeclarationFile(ts_utils_1.tsUtils.node.getSourceFile(derived))) { const filePath = ts_utils_1.tsUtils.file.getFilePath(ts_utils_1.tsUtils.node.getSourceFile(derived)); const name = ts_utils_1.tsUtils.node.getNameOrThrow(derived); const existing = acc.contracts[filePath]; if (existing !== undefined) { throw new errors_1.MultipleContractsInFileError(filePath); } const references = [ ...new Set(ts_utils_1.tsUtils.reference .findReferencesAsNodes(context.program, context.languageService, derived) .map((reference) => ts_utils_1.tsUtils.file.getFilePath(ts_utils_1.tsUtils.node.getSourceFile(reference)))), ]; const dependency = { filePath, name }; const dependenciesOut = references.reduce((innerAcc, reference) => { let filePathDependencies = innerAcc[reference]; if (filePathDependencies === undefined) { filePathDependencies = []; } return Object.assign(Object.assign({}, innerAcc), { [reference]: [...filePathDependencies, dependency] }); }, acc.dependencies); return { contracts: Object.assign(Object.assign({}, acc.contracts), { [filePath]: { filePath, name, dependencies: [], } }), dependencies: dependenciesOut, }; } return acc; }, { contracts: {}, dependencies: {} }); const unsortedContracts = Object.values(contracts).map((contract) => { const filePathDependencies = dependencies[contract.filePath]; return Object.assign(Object.assign({}, contract), { dependencies: filePathDependencies === undefined ? [] : filePathDependencies }); }); return topographicalSort(unsortedContracts); }; const topographicalSort = (contracts) => { const contractToDependencies = contracts.reduce((acc, contract) => (Object.assign(Object.assign({}, acc), { [contract.filePath]: new Set(contract.dependencies.map((dep) => dep.filePath)) })), {}); const mutableOut = []; const satisfied = contracts.filter((contract) => contract.dependencies.length === 0); let remaining = contracts.filter((contract) => contract.dependencies.length !== 0); while (satisfied.length > 0) { const node = satisfied.shift(); if (node === undefined) { break; } mutableOut.push(node); remaining = remaining .map((contract) => { const deps = contractToDependencies[contract.filePath]; deps.delete(node.filePath); if (deps.size === 0) { satisfied.push(contract); return undefined; } return contract; }) .filter(utils_1.utils.notNull); } if (mutableOut.length !== contracts.length) { throw new errors_1.CircularLinkedDependencyError(contracts.map((contract) => contract.name)); } return mutableOut; }; exports.scan = async (dir, host) => { const context = await createContext_1.createContextForDir(dir, host); return exports.scanContext(context); }; //# sourceMappingURL=scan.js.map