@neo-one/smart-contract-compiler
Version:
NEO•ONE TypeScript smart contract compiler.
90 lines (88 loc) • 4.32 kB
JavaScript
;
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