diff --git a/__fixtures__/amm/src/contract.js b/__fixtures__/amm/src/contract.js new file mode 100644 index 0000000..83a95b6 --- /dev/null +++ b/__fixtures__/amm/src/contract.js @@ -0,0 +1,197 @@ +import { getBalance, sendCoins } from 'bank'; + +const store = (storeDefinition) => { + return (target, state) => { + Object.entries(storeDefinition).forEach(([key, value]) => { + if (typeof value === 'function') { + // For functions, create a getter and a setter + Object.defineProperty(target, key, { + get() { + return (...args) => state.get([key, ...args].join('/')) ?? value(undefined); // TODO: support nested mappings + }, + enumerable: true, + }); + + const setterName = `set${key.charAt(0).toUpperCase() + key.slice(1)}`; + Object.defineProperty(target, setterName, { + value: (...args) => { + const newValue = args[args.length - 1]; + const keys = args.slice(0, -1); + state.set([key, ...keys].join('/'), newValue); + }, + enumerable: false, + }); + } else { + // For non-functions, create a getter and a setter + Object.defineProperty(target, key, { + get() { + return state.get(key) ?? value; + }, + set(newValue) { + state.set(key, newValue); + }, + enumerable: true, + }); + + const setterName = `set${key.charAt(0).toUpperCase() + key.slice(1)}`; + Object.defineProperty(target, setterName, { + value: (newValue) => { + state.set(key, newValue); + }, + enumerable: false, + }); + } + }); + }; +}; + +////////////////////////////////////////////////////////////////////////////////// + +const useStore = store({ + totalSupply: 0, + balance:address => 0, + reserves: [0, 0], +}); +// should construct +// this.totalSupply // getter +// this.setTotalSupply(value) // setter +// this.balance(address) // getter +// this.setBalance(address, value) // setter +// this.reserves // getter +// this.setReserves(value) // setter +export default class AMMContract { + constructor(state, {msg, address}) { + this.msg = msg; + this.address = address; + + useStore(this, state); + } + + token0 = 'USDC'; + token1 = 'ATOM'; + + getTotalSupply() { + return this.totalSupply; + } + + getBalance(address) { + return this.balance(address); + } + + getReserves() { + return this.reserves; + } + + #getBankBalance(address, token) { + return getBalance(address, token); + } + + #mint(to, amount) { + const balance = this.balance(to); + this.setBalance(to, balance + amount); + this.totalSupply += amount; + } + + #burn(from, amount) { + const balance = this.balance(from); + if (balance < amount) { + throw Error('insufficient balance'); + } + this.setBalance(from, balance - amount); + this.totalSupply -= amount; + } + + #update(amount0, amount1) { + const [reserve0, reserve1] = this.reserves; + this.reserves = [ + reserve0 + amount0, + reserve1 + amount1, + ]; + } + + swap({tokenIn, amountIn}) { + const isToken0 = tokenIn == this.token0; + const isToken1 = tokenIn == this.token1; + + if (!isToken0 && !isToken1) { + throw Error('invalid token'); + } + + const [reserve0, reserve1] = this.reserves; + let tokenOut, reserveIn, reserveOut; + + [tokenIn, tokenOut, reserveIn, reserveOut] = + isToken0 + ? [this.token0, this.token1, reserve0, reserve1] + : [this.token1, this.token0, reserve1, reserve0]; + + sendCoins(this.msg.sender, this.address, { + [tokenIn]: amountIn, + }); + + const amountInWithFee = amountIn * 997 / 1000; + const amountOut = (reserveOut * amountInWithFee) / (reserveIn + amountInWithFee); + + sendCoins(this.address, this.msg.sender, { + [tokenOut]: amountOut, + }); + + this.#update( + this.#getBankBalance(this.address, this.token0).amount, + this.#getBankBalance(this.address, this.token1).amount, + ); + + return amountOut; + } + + addLiquidity({amount0, amount1}) { + sendCoins(this.msg.sender, this.address, { + [this.token0]: amount0, + [this.token1]: amount1, + }); + + const [reserve0, reserve1] = this.reserves; + + if (reserve0 > 0 || reserve1 > 0) { + if (reserve0 * amount1 != reserve1 * amount0) { + throw Error('invalid liquidity'); + } + } + + let shares = 0; + if (this.totalSupply > 0) { + shares = Math.sqrt(amount0 * amount1); + } else { + shares = Math.min( + (amount0 * this.totalSupply) / reserve0, + (amount1 * this.totalSupply) / reserve1, + ); + } + + this.#mint(this.msg.sender, shares); + + this.#update( + this.#getBankBalance(this.address, this.token0).amount, + this.#getBankBalance(this.address, this.token1).amount, + ); + + return shares; + } + + removeLiquidity({shares}) { + const bal0 = this.#getBankBalance(this.address, this.token0); + const bal1 = this.#getBankBalance(this.address, this.token1); + const totalSupply = this.totalSupply; + + const amount0 = bal0 * shares / totalSupply; + const amount1 = bal1 * shares / totalSupply; + this.#burn(this.msg.sender, shares); + this.#update(bal0 - amount0, bal1 - amount1); + sendCoins(this.address, this.msg.sender, { + [this.token0]: amount0, + [this.token1]: amount1, + }); + + return [amount0, amount1]; + } +} \ No newline at end of file diff --git a/__fixtures__/amm/src/index.js b/__fixtures__/amm/src/index.js new file mode 100644 index 0000000..12c7040 --- /dev/null +++ b/__fixtures__/amm/src/index.js @@ -0,0 +1,53 @@ +// top level middleware for JSD contracts + +import * as contract from './contract.js'; + +function contractNamespaceObject() { + // iterate over all properties of the contract object + // ESM modules are not enumerable, use getOwnPropertyNames + const exports = {}; + + for (const key of Object.getOwnPropertyNames(contract)) { + if (key === 'default') { + continue; + } + + exports[key] = contract[key]; + } + + return exports; +} + +function callClassContract(state, functionName, body) { + const instance = new contract.default(state, {msg: {sender: state.sender}, address: state.self}); + + return instance[functionName](body); +} + +export default function(state, functionName, body) { + if (typeof functionName !== 'string') { + return new Error('contract function name must be a string, got ' + typeof functionName); + } + + // module 'contract' exports contract as a class via the default export + // TODO: check if contract is a class or just a function + if (contract.default && typeof contract.default === 'function') { + return callClassContract(state, functionName, body); + } + + const entry = contractNamespaceObject()[functionName]; + + if (typeof entry === 'undefined') { + return new Error('contract function ' + functionName + ' not found: got undefined'); + } + + if (typeof entry !== 'function' && !body) { + return entry; + } + + if (typeof entry === 'function') { + return entry(state, body); + } + + return new Error('contract function' + functionName + ' not found: got ' + typeof entry); +} \ No newline at end of file diff --git a/__output__/amm/bundle.js b/__output__/amm/bundle.js new file mode 100644 index 0000000..a70ee20 --- /dev/null +++ b/__output__/amm/bundle.js @@ -0,0 +1,201 @@ +var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; + +// ../../__fixtures__/amm/src/contract.js +var contract_exports = {}; +__export(contract_exports, { + default: () => AMMContract +}); +import { getBalance, sendCoins } from "bank"; +var store = (storeDefinition) => { + return (target, state) => { + Object.entries(storeDefinition).forEach(([key, value]) => { + if (typeof value === "function") { + Object.defineProperty(target, key, { + get() { + return (...args) => state.get([key, ...args].join("/")) ?? value(void 0); + }, + enumerable: true + }); + const setterName = `set${key.charAt(0).toUpperCase() + key.slice(1)}`; + Object.defineProperty(target, setterName, { + value: (...args) => { + const newValue = args[args.length - 1]; + const keys = args.slice(0, -1); + state.set([key, ...keys].join("/"), newValue); + }, + enumerable: false + }); + } else { + Object.defineProperty(target, key, { + get() { + return state.get(key) ?? value; + }, + set(newValue) { + state.set(key, newValue); + }, + enumerable: true + }); + const setterName = `set${key.charAt(0).toUpperCase() + key.slice(1)}`; + Object.defineProperty(target, setterName, { + value: (newValue) => { + state.set(key, newValue); + }, + enumerable: false + }); + } + }); + }; +}; +var useStore = store({ + totalSupply: 0, + balance: (address) => 0, + reserves: [0, 0] +}); +var AMMContract = class { + constructor(state, { msg, address }) { + this.msg = msg; + this.address = address; + useStore(this, state); + } + token0 = "USDC"; + token1 = "ATOM"; + getTotalSupply() { + return this.totalSupply; + } + getBalance(address) { + return this.balance(address); + } + getReserves() { + return this.reserves; + } + #getBankBalance(address, token) { + return getBalance(address, token); + } + #mint(to, amount) { + const balance = this.balance(to); + this.setBalance(to, balance + amount); + this.totalSupply += amount; + } + #burn(from, amount) { + const balance = this.balance(from); + if (balance < amount) { + throw Error("insufficient balance"); + } + this.setBalance(from, balance - amount); + this.totalSupply -= amount; + } + #update(amount0, amount1) { + const [reserve0, reserve1] = this.reserves; + this.reserves = [ + reserve0 + amount0, + reserve1 + amount1 + ]; + } + swap({ tokenIn, amountIn }) { + const isToken0 = tokenIn == this.token0; + const isToken1 = tokenIn == this.token1; + if (!isToken0 && !isToken1) { + throw Error("invalid token"); + } + const [reserve0, reserve1] = this.reserves; + let tokenOut, reserveIn, reserveOut; + [tokenIn, tokenOut, reserveIn, reserveOut] = isToken0 ? [this.token0, this.token1, reserve0, reserve1] : [this.token1, this.token0, reserve1, reserve0]; + sendCoins(this.msg.sender, this.address, { + [tokenIn]: amountIn + }); + const amountInWithFee = amountIn * 997 / 1e3; + const amountOut = reserveOut * amountInWithFee / (reserveIn + amountInWithFee); + sendCoins(this.address, this.msg.sender, { + [tokenOut]: amountOut + }); + this.#update( + this.#getBankBalance(this.address, this.token0).amount, + this.#getBankBalance(this.address, this.token1).amount + ); + return amountOut; + } + addLiquidity({ amount0, amount1 }) { + sendCoins(this.msg.sender, this.address, { + [this.token0]: amount0, + [this.token1]: amount1 + }); + const [reserve0, reserve1] = this.reserves; + if (reserve0 > 0 || reserve1 > 0) { + if (reserve0 * amount1 != reserve1 * amount0) { + throw Error("invalid liquidity"); + } + } + let shares = 0; + if (this.totalSupply > 0) { + shares = Math.sqrt(amount0 * amount1); + } else { + shares = Math.min( + amount0 * this.totalSupply / reserve0, + amount1 * this.totalSupply / reserve1 + ); + } + this.#mint(this.msg.sender, shares); + this.#update( + this.#getBankBalance(this.address, this.token0).amount, + this.#getBankBalance(this.address, this.token1).amount + ); + return shares; + } + removeLiquidity({ shares }) { + const bal0 = this.#getBankBalance(this.address, this.token0); + const bal1 = this.#getBankBalance(this.address, this.token1); + const totalSupply = this.totalSupply; + const amount0 = bal0 * shares / totalSupply; + const amount1 = bal1 * shares / totalSupply; + this.#burn(this.msg.sender, shares); + this.#update(bal0 - amount0, bal1 - amount1); + sendCoins(this.address, this.msg.sender, { + [this.token0]: amount0, + [this.token1]: amount1 + }); + return [amount0, amount1]; + } +}; + +// ../../__fixtures__/amm/src/index.js +function contractNamespaceObject() { + const exports = {}; + for (const key of Object.getOwnPropertyNames(contract_exports)) { + if (key === "default") { + continue; + } + exports[key] = contract_exports[key]; + } + return exports; +} +function callClassContract(state, functionName, body) { + const instance = new AMMContract(state, { msg: { sender: state.sender }, address: state.self }); + return instance[functionName](body); +} +function src_default(state, functionName, body) { + if (typeof functionName !== "string") { + return new Error("contract function name must be a string, got " + typeof functionName); + } + if (AMMContract && typeof AMMContract === "function") { + return callClassContract(state, functionName, body); + } + const entry = contractNamespaceObject()[functionName]; + if (typeof entry === "undefined") { + return new Error("contract function " + functionName + " not found: got undefined"); + } + if (typeof entry !== "function" && !body) { + return entry; + } + if (typeof entry === "function") { + return entry(state, body); + } + return new Error("contract function" + functionName + " not found: got " + typeof entry); +} +export { + src_default as default +}; +//# sourceMappingURL=bundle.js.map diff --git a/__output__/amm/bundle.js.map b/__output__/amm/bundle.js.map new file mode 100644 index 0000000..94c9871 --- /dev/null +++ b/__output__/amm/bundle.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../__fixtures__/amm/src/contract.js", "../../__fixtures__/amm/src/index.js"], + "sourcesContent": ["import { getBalance, sendCoins } from 'bank';\n\nconst store = (storeDefinition) => {\n return (target, state) => {\n Object.entries(storeDefinition).forEach(([key, value]) => {\n if (typeof value === 'function') {\n // For functions, create a getter and a setter\n Object.defineProperty(target, key, {\n get() {\n return (...args) => state.get([key, ...args].join('/')) ?? value(undefined); // TODO: support nested mappings\n },\n enumerable: true,\n });\n \n const setterName = `set${key.charAt(0).toUpperCase() + key.slice(1)}`;\n Object.defineProperty(target, setterName, {\n value: (...args) => {\n const newValue = args[args.length - 1];\n const keys = args.slice(0, -1);\n state.set([key, ...keys].join('/'), newValue);\n },\n enumerable: false,\n });\n } else {\n // For non-functions, create a getter and a setter\n Object.defineProperty(target, key, {\n get() {\n return state.get(key) ?? value;\n },\n set(newValue) {\n state.set(key, newValue);\n },\n enumerable: true,\n });\n\n const setterName = `set${key.charAt(0).toUpperCase() + key.slice(1)}`;\n Object.defineProperty(target, setterName, {\n value: (newValue) => {\n state.set(key, newValue);\n },\n enumerable: false,\n });\n }\n });\n };\n};\n\n//////////////////////////////////////////////////////////////////////////////////\n\nconst useStore = store({\n totalSupply: 0,\n balance:address => 0,\n reserves: [0, 0],\n});\n// should construct\n// this.totalSupply // getter\n// this.setTotalSupply(value) // setter\n// this.balance(address) // getter\n// this.setBalance(address, value) // setter\n// this.reserves // getter\n// this.setReserves(value) // setter\nexport default class AMMContract {\n constructor(state, {msg, address}) {\n this.msg = msg;\n this.address = address;\n\n useStore(this, state);\n }\n\n token0 = 'USDC';\n token1 = 'ATOM';\n\n getTotalSupply() {\n return this.totalSupply;\n }\n\n getBalance(address) {\n return this.balance(address);\n }\n\n getReserves() {\n return this.reserves;\n }\n\n #getBankBalance(address, token) {\n return getBalance(address, token);\n }\n\n #mint(to, amount) {\n const balance = this.balance(to);\n this.setBalance(to, balance + amount);\n this.totalSupply += amount;\n }\n\n #burn(from, amount) {\n const balance = this.balance(from);\n if (balance < amount) {\n throw Error('insufficient balance');\n }\n this.setBalance(from, balance - amount);\n this.totalSupply -= amount;\n }\n\n #update(amount0, amount1) {\n const [reserve0, reserve1] = this.reserves;\n this.reserves = [\n reserve0 + amount0,\n reserve1 + amount1,\n ];\n }\n\n swap({tokenIn, amountIn}) {\n const isToken0 = tokenIn == this.token0;\n const isToken1 = tokenIn == this.token1;\n\n if (!isToken0 && !isToken1) {\n throw Error('invalid token');\n }\n\n const [reserve0, reserve1] = this.reserves;\n let tokenOut, reserveIn, reserveOut;\n\n [tokenIn, tokenOut, reserveIn, reserveOut] = \n isToken0 \n ? [this.token0, this.token1, reserve0, reserve1]\n : [this.token1, this.token0, reserve1, reserve0];\n\n sendCoins(this.msg.sender, this.address, {\n [tokenIn]: amountIn,\n });\n\n const amountInWithFee = amountIn * 997 / 1000;\n const amountOut = (reserveOut * amountInWithFee) / (reserveIn + amountInWithFee);\n\n sendCoins(this.address, this.msg.sender, {\n [tokenOut]: amountOut,\n });\n\n this.#update(\n this.#getBankBalance(this.address, this.token0).amount, \n this.#getBankBalance(this.address, this.token1).amount,\n );\n\n return amountOut;\n }\n\n addLiquidity({amount0, amount1}) {\n sendCoins(this.msg.sender, this.address, {\n [this.token0]: amount0,\n [this.token1]: amount1,\n });\n\n const [reserve0, reserve1] = this.reserves;\n\n if (reserve0 > 0 || reserve1 > 0) {\n if (reserve0 * amount1 != reserve1 * amount0) {\n throw Error('invalid liquidity');\n }\n }\n\n let shares = 0;\n if (this.totalSupply > 0) {\n shares = Math.sqrt(amount0 * amount1);\n } else {\n shares = Math.min(\n (amount0 * this.totalSupply) / reserve0,\n (amount1 * this.totalSupply) / reserve1,\n );\n }\n\n this.#mint(this.msg.sender, shares);\n\n this.#update(\n this.#getBankBalance(this.address, this.token0).amount,\n this.#getBankBalance(this.address, this.token1).amount,\n );\n\n return shares;\n }\n\n removeLiquidity({shares}) {\n const bal0 = this.#getBankBalance(this.address, this.token0);\n const bal1 = this.#getBankBalance(this.address, this.token1);\n const totalSupply = this.totalSupply;\n\n const amount0 = bal0 * shares / totalSupply;\n const amount1 = bal1 * shares / totalSupply;\n this.#burn(this.msg.sender, shares);\n this.#update(bal0 - amount0, bal1 - amount1);\n sendCoins(this.address, this.msg.sender, {\n [this.token0]: amount0,\n [this.token1]: amount1,\n });\n\n return [amount0, amount1];\n }\n}", "// top level middleware for JSD contracts\n\nimport * as contract from './contract.js';\n\nfunction contractNamespaceObject() {\n // iterate over all properties of the contract object\n // ESM modules are not enumerable, use getOwnPropertyNames\n const exports = {};\n\n for (const key of Object.getOwnPropertyNames(contract)) {\n if (key === 'default') {\n continue;\n }\n\n exports[key] = contract[key];\n }\n\n return exports;\n}\n\nfunction callClassContract(state, functionName, body) {\n const instance = new contract.default(state, {msg: {sender: state.sender}, address: state.self});\n\n return instance[functionName](body);\n}\n\nexport default function(state, functionName, body) {\n if (typeof functionName !== 'string') {\n return new Error('contract function name must be a string, got ' + typeof functionName);\n }\n\n // module 'contract' exports contract as a class via the default export\n // TODO: check if contract is a class or just a function\n if (contract.default && typeof contract.default === 'function') {\n return callClassContract(state, functionName, body);\n }\n\n const entry = contractNamespaceObject()[functionName];\n\n if (typeof entry === 'undefined') {\n return new Error('contract function ' + functionName + ' not found: got undefined');\n }\n\n if (typeof entry !== 'function' && !body) {\n return entry;\n }\n\n if (typeof entry === 'function') {\n return entry(state, body);\n }\n\n return new Error('contract function' + functionName + ' not found: got ' + typeof entry);\n}"], + "mappings": "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", + "names": [] +} diff --git a/__output__/first/bundle.js b/__output__/first/bundle.js index 0ba87ca..16c1fc1 100644 --- a/__output__/first/bundle.js +++ b/__output__/first/bundle.js @@ -1,5 +1,3 @@ -"use strict"; - // ../../__fixtures__/first/src/greet.ts function greet(name) { return `Hello, ${name}!`; diff --git a/__output__/first/bundle.js.map b/__output__/first/bundle.js.map index 6139683..7901ca5 100644 --- a/__output__/first/bundle.js.map +++ b/__output__/first/bundle.js.map @@ -2,6 +2,6 @@ "version": 3, "sources": ["../../__fixtures__/first/src/greet.ts", "../../__fixtures__/first/src/farewell.ts", "../../__fixtures__/first/src/index.ts"], "sourcesContent": ["export function greet(name: string): string {\n return `Hello, ${name}!`;\n}\n", "export function farewell(name: string): string {\n myname();\n return `Goodbye, ${name}!`;\n}\n\nfunction myname() {\n console.log('hello');\n}\n", "import { greet } from './greet';\nimport { farewell } from './farewell';\n\nfunction myname() {\n console.log('hello');\n}\n\nconsole.log(greet('World'));\nconsole.log(farewell('World'));\nmyname();\n"], - "mappings": ";;;AAAO,SAAS,MAAM,MAAsB;AAC1C,SAAO,UAAU,IAAI;AACvB;;;ACFO,SAAS,SAAS,MAAsB;AAC7C,SAAO;AACP,SAAO,YAAY,IAAI;AACzB;AAEA,SAAS,SAAS;AAChB,UAAQ,IAAI,OAAO;AACrB;;;ACJA,SAASA,UAAS;AAChB,UAAQ,IAAI,OAAO;AACrB;AAEA,QAAQ,IAAI,MAAM,OAAO,CAAC;AAC1B,QAAQ,IAAI,SAAS,OAAO,CAAC;AAC7BA,QAAO;", + "mappings": ";AAAO,SAAS,MAAM,MAAsB;AAC1C,SAAO,UAAU,IAAI;AACvB;;;ACFO,SAAS,SAAS,MAAsB;AAC7C,SAAO;AACP,SAAO,YAAY,IAAI;AACzB;AAEA,SAAS,SAAS;AAChB,UAAQ,IAAI,OAAO;AACrB;;;ACJA,SAASA,UAAS;AAChB,UAAQ,IAAI,OAAO;AACrB;AAEA,QAAQ,IAAI,MAAM,OAAO,CAAC;AAC1B,QAAQ,IAAI,SAAS,OAAO,CAAC;AAC7BA,QAAO;", "names": ["myname"] } diff --git a/__output__/imports/bundle.js b/__output__/imports/bundle.js index 55b4a3e..bfcdf5e 100644 --- a/__output__/imports/bundle.js +++ b/__output__/imports/bundle.js @@ -1,36 +1,12 @@ -"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 __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( - // If the importer is in node compatibility mode or this is not an ESM - // file that has been converted to a CommonJS file using a Babel- - // compatible transform (i.e. "__esModule" has not been set), then set - // "default" to the CommonJS "module.exports" for node compatibility. - isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, - mod -)); - // ../../__fixtures__/imports/src/greet.ts -var yolo = __toESM(require("~somepackage")); +import * as yolo from "~somepackage"; function greet(name) { console.log(yolo); return `Hello, ${name}!`; } // ../../__fixtures__/imports/src/farewell.ts -var yolo2 = __toESM(require("otherpackage")); +import * as yolo2 from "otherpackage"; function farewell(name) { myname(); console.log(yolo2); diff --git a/__output__/imports/bundle.js.map b/__output__/imports/bundle.js.map index 301f32d..a968ec2 100644 --- a/__output__/imports/bundle.js.map +++ b/__output__/imports/bundle.js.map @@ -2,6 +2,6 @@ "version": 3, "sources": ["../../__fixtures__/imports/src/greet.ts", "../../__fixtures__/imports/src/farewell.ts", "../../__fixtures__/imports/src/index.ts"], "sourcesContent": ["import * as yolo from '~somepackage';\n\nexport function greet(name: string): string {\n console.log(yolo);\n return `Hello, ${name}!`;\n}\n", "import * as yolo from 'otherpackage';\n\nexport function farewell(name: string): string {\n myname();\n console.log(yolo);\n return `Goodbye, ${name}!`;\n}\n\nfunction myname() {\n console.log('hello');\n}\n", "import { greet } from './greet';\nimport { farewell } from './farewell';\n\nfunction myname() {\n console.log('hello');\n}\n\nconsole.log(greet('World'));\nconsole.log(farewell('World'));\nmyname();\n"], - "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,WAAsB;AAEf,SAAS,MAAM,MAAsB;AAC1C,UAAQ,IAAI,IAAI;AAChB,SAAO,UAAU,IAAI;AACvB;;;ACLA,IAAAA,QAAsB;AAEf,SAAS,SAAS,MAAsB;AAC7C,SAAO;AACP,UAAQ,IAAIA,KAAI;AAChB,SAAO,YAAY,IAAI;AACzB;AAEA,SAAS,SAAS;AAChB,UAAQ,IAAI,OAAO;AACrB;;;ACPA,SAASC,UAAS;AAChB,UAAQ,IAAI,OAAO;AACrB;AAEA,QAAQ,IAAI,MAAM,OAAO,CAAC;AAC1B,QAAQ,IAAI,SAAS,OAAO,CAAC;AAC7BA,QAAO;", + "mappings": ";AAAA,YAAY,UAAU;AAEf,SAAS,MAAM,MAAsB;AAC1C,UAAQ,IAAI,IAAI;AAChB,SAAO,UAAU,IAAI;AACvB;;;ACLA,YAAYA,WAAU;AAEf,SAAS,SAAS,MAAsB;AAC7C,SAAO;AACP,UAAQ,IAAIA,KAAI;AAChB,SAAO,YAAY,IAAI;AACzB;AAEA,SAAS,SAAS;AAChB,UAAQ,IAAI,OAAO;AACrB;;;ACPA,SAASC,UAAS;AAChB,UAAQ,IAAI,OAAO;AACrB;AAEA,QAAQ,IAAI,MAAM,OAAO,CAAC;AAC1B,QAAQ,IAAI,SAAS,OAAO,CAAC;AAC7BA,QAAO;", "names": ["yolo", "myname"] } diff --git a/packages/build/__tests__/amm.test.ts b/packages/build/__tests__/amm.test.ts new file mode 100644 index 0000000..c7262a6 --- /dev/null +++ b/packages/build/__tests__/amm.test.ts @@ -0,0 +1,33 @@ +import fs from 'fs/promises'; +import { join, resolve } from 'path'; + +import { InterwebBuild, InterwebBuildOptions } from '../src/index'; + +const fixtureDir = resolve(join(__dirname, '/../../../__fixtures__/', 'amm')); +const outputDir = resolve(join(__dirname, '/../../../__output__/', 'amm')); + +describe('InterwebBuild', () => { + it('builds the AMM fixture project successfully', async () => { + const outfile = join(outputDir, 'bundle.js'); + + const options: Partial = { + entryPoints: [join(fixtureDir, 'src/index.js')], + outfile, + external: ['bank'] + }; + + await InterwebBuild.build(options); + + // Check if the output file exists + const outfileExists = await fs.access(outfile) + .then(() => true) + .catch(() => false); + + expect(outfileExists).toBe(true); + + // Optionally, you can read the contents of the file and perform additional checks + const bundleContent = await fs.readFile(outfile, 'utf-8'); + //expect(bundleContent).toContain('function greet'); + //expect(bundleContent).toContain('function farewell'); + }); +}); \ No newline at end of file diff --git a/packages/build/src/index.ts b/packages/build/src/index.ts index 26a5027..d602097 100644 --- a/packages/build/src/index.ts +++ b/packages/build/src/index.ts @@ -9,10 +9,15 @@ export const defaultOptions: InterwebBuildOptions = { bundle: true, minify: false, outfile: 'dist/bundle.js', - platform: 'node', + platform: 'neutral', sourcemap: true, - target: 'ESNext', - logLevel: 'info' + target: 'es2022', + logLevel: 'info', + format: 'esm', + minifyIdentifiers: false, + minifySyntax: false, + minifyWhitespace: false, + treeShaking: false, }; export const InterwebBuild = {