diff --git a/.azure-pipelines-steps.yml b/.azure-pipelines-steps.yml index 063cc72d..c08e5a42 100644 --- a/.azure-pipelines-steps.yml +++ b/.azure-pipelines-steps.yml @@ -28,7 +28,7 @@ steps: condition: succeededOrFailed() # Check types - - script: yarn lint:types + - script: yarn types displayName: "Check types" condition: succeededOrFailed() diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 0b08320f..8b89fa7e 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -5,6 +5,21 @@ trigger: tags: include: - latest + paths: + exclude: + - .github/* + - bin/* + - docs/* + - "*.md" + - .codeclimate.yml + - .eslintrc.js + - .gitignore + - .npmignore + - .prettierignore + - aws-spot-price.code-workspace + - commitlint.config.js + - webpack.config.js + - yarn.lock jobs: - job: Release diff --git a/.github/workflows/ec2-gen.yml b/.github/workflows/ec2-gen.yml index dfc229a3..2e610e52 100644 --- a/.github/workflows/ec2-gen.yml +++ b/.github/workflows/ec2-gen.yml @@ -27,7 +27,7 @@ jobs: run: yarn lint - name: Check types - run: yarn lint:types + run: yarn types - name: Run tests and update snapshots run: yarn test -u diff --git a/.gitignore b/.gitignore index 610217f6..f61153bf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ coverage dist junit.xml node_modules +types diff --git a/.npmignore b/.npmignore index a78dc97f..7e1d3000 100644 --- a/.npmignore +++ b/.npmignore @@ -14,11 +14,11 @@ docs/ junit.xml node_modules/ patches/ +scripts/ sonar-project.properties src/ stats.json test/ tsconfig*.json -util/ webpack.config.js yarn.lock \ No newline at end of file diff --git a/README.md b/README.md index 16d29df0..4b595232 100644 --- a/README.md +++ b/README.md @@ -8,87 +8,163 @@ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=hoonoh_aws-spot-price&metric=coverage)](https://sonarcloud.io/component_measures?id=hoonoh_aws-spot-price&metric=coverage&view=list) [![Greenkeeper badge](https://badges.greenkeeper.io/hoonoh/aws-spot-price.svg)](https://greenkeeper.io/) -CLI utility to list current global AWS EC2 Spot Instance prices. +Lists current global AWS EC2 Spot Instance prices. -## Example +Supports CLI and module usage. + +## CLI + +### Example ![Example](https://raw.githubusercontent.com/hoonoh/aws-spot-price/master/docs/preview.svg?sanitize=true) -## Installation +### Installation -### npm +#### npm `npm -g i aws-spot-price` -### yarn +#### yarn `yarn global add aws-spot-price` -### run with npx +#### run with npx `npx aws-spot-price` -## Usage +### Usage `aws-spot-run [options]` If no options are applied, it will fetch all recent pricing data from default regions and show top 20 cheapest instances. -### Credentials +#### Credentials This CLI utility uses AWS-SDK and requires AWS Access & Secret keys. If environment variables pair `AWS_ACCESS_KEY_ID` & `AWS_SECRET_ACCESS_KEY` or `~/.aws/credentials` is available it will use it. Otherwise, you will need to supply credentials through CLI options [`--accessKeyId`](#accessKeyId) and [`--secretAccessKey`](#secretAccessKey). -### Options +#### Options -#### --ui +##### --ui Start with UI mode. -#### --region | -r +##### --region | -r AWS region to fetch data from. Accepts multiple string values. Defaults to all available AWS region which does not require opt-in. -#### --family +##### --family EC2 instance families to filter. Will be translated to `--familyType` and `--size` values. Accepts multiple string values. Choose from: `general`, `compute`, `memory`, `storage`, `acceleratedComputing` -#### --instanceType | -i +##### --instanceType | -i Type of EC2 instance to filter. Accepts multiple string values. Enter valid EC2 instance type name. e.g. `-i t3.nano t3a.nano` -#### --familyType | -f +##### --familyType | -f EC2 Family type (`c4`, `c5`, etc..). Accepts multiple string values. -#### --size | -s +##### --size | -s EC2 size (`large`, `xlarge`, etc..). Accepts multiple string values. -#### --priceMax | -p +##### --priceMax | -p Maximum price. -#### --productDescription | -d +##### --productDescription | -d Instance product description to filter. Accepts multiple string values. You can use `linux` or `windows` (all in lowercase) as wildcard. -#### --limit | -l +##### --limit | -l Limits list of price information items to be returned. -#### --json | -j +##### --json | -j Outputs in JSON format. This option will silence any progress output. -#### --accessKeyId +##### --accessKeyId Specific AWS Access Key ID. Requires `--secretAccessKey` option to be used together. -#### --secretAccessKey +##### --secretAccessKey Specific AWS Secret Access Key. Requires `--accessKeyId` option to be used together. + +## Module + +### Installation + +#### npm + +`npm i aws-spot-price` + +#### yarn + +`yarn add aws-spot-price` + +### Example + +#### Code + +```javascript +import { getGlobalSpotPrices } from 'aws-spot-price'; + +(async () => { + const results = await getGlobalSpotPrices({ + regions: ['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2'], + familyTypes: ['c3', 'c4', 'c5'], + sizes: ['large', 'medium', 'xlarge'], + limit: 5, + }); + console.log(JSON.stringify(results, null, 2)); +})(); +``` + +#### Results + +```json +[ + { + "AvailabilityZone": "us-east-2a", + "InstanceType": "c4.large", + "ProductDescription": "Linux/UNIX", + "SpotPrice": "0.018100", + "Timestamp": "2019-11-05T03:07:19.000Z" + }, + { + "AvailabilityZone": "us-east-2c", + "InstanceType": "c4.large", + "ProductDescription": "Linux/UNIX", + "SpotPrice": "0.018100", + "Timestamp": "2019-11-05T03:07:19.000Z" + }, + { + "AvailabilityZone": "us-east-2a", + "InstanceType": "c5.large", + "ProductDescription": "Linux/UNIX", + "SpotPrice": "0.019000", + "Timestamp": "2019-11-04T14:51:42.000Z" + }, + { + "AvailabilityZone": "us-east-2c", + "InstanceType": "c5.large", + "ProductDescription": "Linux/UNIX", + "SpotPrice": "0.019000", + "Timestamp": "2019-11-04T14:51:42.000Z" + }, + { + "AvailabilityZone": "us-east-2b", + "InstanceType": "c5.large", + "ProductDescription": "Linux/UNIX", + "SpotPrice": "0.019300", + "Timestamp": "2019-11-04T14:51:42.000Z" + } +] +``` diff --git a/bin/aws-spot-price b/bin/aws-spot-price index 20a0b1f0..d1c523a9 100755 --- a/bin/aws-spot-price +++ b/bin/aws-spot-price @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../dist/aws-spot-price.bundle'); +require('../dist/cli'); diff --git a/package.json b/package.json index 14743e37..268003d7 100644 --- a/package.json +++ b/package.json @@ -22,18 +22,20 @@ "name": "hoonoh" }, "bin": "bin/aws-spot-price", + "main": "dist/module.js", + "types": "types/module.d.ts", "license": "MIT", "scripts": { - "prepublishOnly": "yarn clean && yarn build", - "clean": "rm -rf dist && rm -rf coverage", + "prepublishOnly": "yarn clean && yarn build && yarn types", + "clean": "rm -rf dist && rm -rf types && rm -rf coverage", "build:ec2-types": "ts-node -T scripts/generate-ec2-types.ts", "build": "patch-package && webpack", "changelog": "conventional-changelog -i CHANGELOG.md -s -p angular", "test": "patch-package && jest --runInBand --verbose", "test:coverage": "yarn test --coverage", - "test:ci": "yarn test:coverage --ci --reporters=jest-junit --coverageReporters lcov --coverageReporters cobertura", + "test:ci": "yarn test:coverage --reporters=default jest-junit --coverageReporters lcov --coverageReporters cobertura", "lint": "eslint **/*.ts", - "lint:types": "tsc", + "types": "tsc", "semantic-release": "semantic-release" }, "config": { @@ -47,7 +49,8 @@ } }, "eslintIgnore": [ - "dist" + "dist", + "types" ], "prettier": { "printWidth": 100, @@ -129,6 +132,7 @@ "prettier": "^1.18.2", "prompts": "^2.2.1", "semantic-release": "^15.13.27", + "string-replace-loader": "^2.2.0", "table": "^5.4.6", "ts-jest": "^24.1.0", "ts-loader": "^6.2.1", diff --git a/scripts/generate-ec2-types.ts b/scripts/generate-ec2-types.ts index 46236151..ef560250 100644 --- a/scripts/generate-ec2-types.ts +++ b/scripts/generate-ec2-types.ts @@ -1,8 +1,8 @@ import { writeFileSync } from 'fs'; import { resolve } from 'path'; -import * as prettier from 'prettier'; +import prettier from 'prettier'; -import { getGlobalSpotPrices } from '../src/lib/lib'; +import { getGlobalSpotPrices } from '../src/lib/core'; const familyGeneral = ['a', 't', 'm']; @@ -92,7 +92,7 @@ const sortInstances = (i1: string, i2: string): number => { const getEc2Types = async (): Promise => { let prices; try { - prices = await getGlobalSpotPrices({ silent: true }); + prices = await getGlobalSpotPrices(); } catch (error) { console.log(`getGlobalSpotPrices error: ${error}`); process.exit(1); diff --git a/scripts/generate-spot-prices-mock-data.ts b/scripts/generate-spot-prices-mock-data.ts index 357d6928..a346dab3 100644 --- a/scripts/generate-spot-prices-mock-data.ts +++ b/scripts/generate-spot-prices-mock-data.ts @@ -1,8 +1,8 @@ -import * as EC2 from 'aws-sdk/clients/ec2'; +import EC2 from 'aws-sdk/clients/ec2'; import { readFileSync, writeFileSync } from 'fs'; import { find, uniqWith, xorWith } from 'lodash'; import { resolve } from 'path'; -import * as yargs from 'yargs'; +import yargs from 'yargs'; import { defaultRegions, Region } from '../src/constants/regions'; diff --git a/src/cli.spec.ts b/src/cli.spec.ts index c3b34584..46699123 100644 --- a/src/cli.spec.ts +++ b/src/cli.spec.ts @@ -160,7 +160,7 @@ describe('cli', () => { }); describe('test by spawnSync', () => { - const cliJsPath = resolve(__dirname, '../dist/aws-spot-price.bundle.js'); + const cliJsPath = resolve(__dirname, '../dist/cli.js'); it('should stdout help screen', () => { const s = spawnSync('node', [cliJsPath, '--help'], { encoding: 'utf-8' }); expect(s.stdout).toMatchSnapshot(); diff --git a/src/cli.ts b/src/cli.ts index 906845df..529e230e 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,28 +1,33 @@ +import ora from 'ora'; import { sep } from 'path'; -import * as yargs from 'yargs'; +import { table } from 'table'; +import yargs from 'yargs'; +import { ui } from './lib/ui'; import { allInstances, + allProductDescriptions, + allRegions, + AuthError, + awsCredentialsCheck, + defaults, + Ec2SpotPriceError, + generateTypeSizeSetsFromFamily, + getGlobalSpotPrices, instanceFamily, InstanceFamily, InstanceFamilyType, instanceFamilyTypes, + instanceOfProductDescription, InstanceSize, instanceSizes, InstanceType, -} from './constants/ec2-types'; -import { - allProductDescriptions, - instanceOfProductDescription, ProductDescription, productDescriptionWildcards, ProductDescriptionWildcards, -} from './constants/product-description'; -import { allRegions, Region } from './constants/regions'; -import { AuthError, awsCredentialsCheck } from './lib/credential'; -import { defaults, getGlobalSpotPrices } from './lib/lib'; -import { ui } from './lib/ui'; -import { generateTypeSizeSetsFromFamily } from './lib/utils'; + Region, + regionNames, +} from './module'; export const main = (argvInput?: string[]): Promise => new Promise((res, rej): void => { @@ -198,6 +203,37 @@ export const main = (argvInput?: string[]): Promise => const familyTypeSetArray = Array.from(familyTypeSet); const sizeSetArray = Array.from(sizeSet); + let spinnerText: string | undefined; + let spinner: ora.Ora | undefined; + let onRegionFetch: ((reg: Region) => void) | undefined; + let onRegionFetchFail: ((error: Ec2SpotPriceError) => void) | undefined; + let onFetchComplete: (() => void) | undefined; + + if (!json && process.env.NODE_ENV !== 'test') { + spinner = ora({ + text: 'Waiting for data to be retrieved...', + discardStdin: false, + }).start(); + + onRegionFetch = (reg: Region): void => { + spinnerText = `Retrieved data from ${reg}...`; + if (spinner) spinner.text = spinnerText; + }; + onRegionFetchFail = (error: Ec2SpotPriceError): void => { + if (spinner) + spinner.fail(`Failed to retrieve data from ${error.region}. (${error.code})`); + let text = spinnerText; + if (!text && spinner) text = spinner.text; + spinner = ora({ + text, + discardStdin: false, + }).start(); + }; + onFetchComplete = (): void => { + if (spinner) spinner.succeed('All data retrieved!').stop(); + }; + } + const results = await getGlobalSpotPrices({ regions: region as Region[], instanceTypes: instanceType as InstanceType[], @@ -210,10 +246,36 @@ export const main = (argvInput?: string[]): Promise => : undefined, accessKeyId, secretAccessKey, - silent: json, + onRegionFetch, + onRegionFetchFail, + onFetchComplete, }); - if (json) console.log(JSON.stringify(results, null, 2)); + if (json) { + console.log(JSON.stringify(results, null, 2)); + } else if (results.length > 0) { + console.log( + table( + results.reduce( + (list, price) => { + list.push([ + price.InstanceType, + price.SpotPrice, + price.ProductDescription, + price.AvailabilityZone, + price.AvailabilityZone + ? regionNames[price.AvailabilityZone.slice(0, -1) as Region] + : /* istanbul ignore next */ undefined, + ]); + return list; + }, + [] as (string | undefined)[][], + ), + ), + ); + } else { + console.log('no matching records found'); + } res(); } catch (error) { diff --git a/src/lib/lib.spec.ts b/src/lib/core.spec.ts similarity index 97% rename from src/lib/lib.spec.ts rename to src/lib/core.spec.ts index 9239c7e5..cdf36063 100644 --- a/src/lib/lib.spec.ts +++ b/src/lib/core.spec.ts @@ -1,7 +1,7 @@ import { SpotPrice } from 'aws-sdk/clients/ec2'; import mockConsole, { RestoreConsole } from 'jest-mock-console'; import { filter } from 'lodash'; -import * as nock from 'nock'; +import nock from 'nock'; import { mockAwsCredentials, mockAwsCredentialsClear } from '../../test/mock-credential-endpoints'; import { @@ -12,7 +12,7 @@ import { consoleMockCallJoin } from '../../test/utils'; import { InstanceFamilyType, InstanceSize } from '../constants/ec2-types'; import { ProductDescription } from '../constants/product-description'; import { Region } from '../constants/regions'; -import { getGlobalSpotPrices } from './lib'; +import { getGlobalSpotPrices } from './core'; describe('lib', () => { describe('getGlobalSpotPrices', () => { @@ -50,7 +50,6 @@ describe('lib', () => { sizes, productDescriptions, limit: 20, - silent: true, }); }); @@ -91,7 +90,6 @@ describe('lib', () => { results = await getGlobalSpotPrices({ familyTypes, limit: 20, - silent: true, }); }); @@ -125,7 +123,6 @@ describe('lib', () => { results = await getGlobalSpotPrices({ sizes, limit: 20, - silent: true, }); }); @@ -161,7 +158,6 @@ describe('lib', () => { instanceTypes: ['c5.2xlarge'], productDescriptions: ['Linux/UNIX'], limit: 200, - silent: true, }); }); @@ -192,7 +188,6 @@ describe('lib', () => { results = await getGlobalSpotPrices({ priceMax, - silent: true, }); }); diff --git a/src/lib/lib.ts b/src/lib/core.ts similarity index 79% rename from src/lib/lib.ts rename to src/lib/core.ts index e721b6b7..b723c04a 100644 --- a/src/lib/lib.ts +++ b/src/lib/core.ts @@ -1,10 +1,8 @@ -import * as EC2 from 'aws-sdk/clients/ec2'; -import * as ora from 'ora'; -import { table } from 'table'; +import EC2 from 'aws-sdk/clients/ec2'; import { InstanceFamilyType, InstanceSize, InstanceType } from '../constants/ec2-types'; import { ProductDescription } from '../constants/product-description'; -import { defaultRegions, Region, regionNames } from '../constants/regions'; +import { defaultRegions, Region } from '../constants/regions'; import { generateInstantTypesFromFamilyTypeSize } from './utils'; const sortSpotPrice = (p1: EC2.SpotPrice, p2: EC2.SpotPrice): number => { @@ -29,7 +27,7 @@ const sortSpotPrice = (p1: EC2.SpotPrice, p2: EC2.SpotPrice): number => { return rtn; }; -class Ec2SpotPriceError extends Error { +export class Ec2SpotPriceError extends Error { constructor(message: string, region: Region, code: string) { super(message) /* istanbul ignore next */; this.name = 'Ec2SpotPriceError'; @@ -118,9 +116,11 @@ export const getGlobalSpotPrices = async (options?: { instanceTypes?: InstanceType[]; productDescriptions?: ProductDescription[]; limit?: number; - silent?: boolean; accessKeyId?: string; secretAccessKey?: string; + onRegionFetch?: (region: Region) => void; + onRegionFetchFail?: (error: Ec2SpotPriceError) => void; + onFetchComplete?: Function; }): Promise => { const { familyTypes, @@ -128,9 +128,11 @@ export const getGlobalSpotPrices = async (options?: { priceMax, productDescriptions, limit, - silent, accessKeyId, secretAccessKey, + onRegionFetch, + onRegionFetchFail, + onFetchComplete, } = options || { limit: defaults.limit, }; @@ -151,16 +153,6 @@ export const getGlobalSpotPrices = async (options?: { } } - let spinner: ora.Ora | undefined; - let spinnerText: string | undefined; - /* istanbul ignore if */ - if (!silent && process.env.NODE_ENV !== 'test') { - spinner = ora({ - text: 'Waiting for data to be retrieved...', - discardStdin: false, - }).start(); - } - const rtn: EC2.SpotPrice[] = await Promise.all( regions.map(async region => { try { @@ -172,20 +164,13 @@ export const getGlobalSpotPrices = async (options?: { secretAccessKey, }); /* istanbul ignore if */ - if (spinner) { - spinnerText = `Retrieved data from ${region}...`; - spinner.text = spinnerText; - } + if (onRegionFetch) onRegionFetch(region); return regionsPrices; } catch (error) { /* istanbul ignore if */ if (error instanceof Ec2SpotPriceError) { - if (spinner) { - spinner.fail(`Failed to retrieve data from ${error.region}. (${error.code})`); - spinner = ora({ - text: spinnerText || spinner.text, - discardStdin: false, - }).start(); + if (onRegionFetchFail) { + onRegionFetchFail(error); } else { throw error; } @@ -197,7 +182,7 @@ export const getGlobalSpotPrices = async (options?: { }), ).then(results => { /* istanbul ignore if */ - if (spinner) spinner.succeed('All data retrieved!').stop(); + if (onFetchComplete) onFetchComplete(); return results .reduce( (finalList: EC2.SpotPrice[], curList: EC2.SpotPrice[]) => { @@ -254,32 +239,5 @@ export const getGlobalSpotPrices = async (options?: { // limit output if (limit && rtn.length > limit) rtn.splice(limit); - // log output - if (!silent) { - if (rtn.length > 0) { - console.log( - table( - rtn.reduce( - (list, price) => { - list.push([ - price.InstanceType, - price.SpotPrice, - price.ProductDescription, - price.AvailabilityZone, - price.AvailabilityZone - ? regionNames[price.AvailabilityZone.slice(0, -1) as Region] - : /* istanbul ignore next */ undefined, - ]); - return list; - }, - [] as (string | undefined)[][], - ), - ), - ); - } else { - console.log('no matching records found'); - } - } - return rtn; }; diff --git a/src/lib/credential.spec.ts b/src/lib/credential.spec.ts index 693be636..8dd4f740 100644 --- a/src/lib/credential.spec.ts +++ b/src/lib/credential.spec.ts @@ -1,5 +1,5 @@ import mockConsole, { RestoreConsole } from 'jest-mock-console'; -import * as nock from 'nock'; +import nock from 'nock'; import { mockAwsCredentials, mockAwsCredentialsClear } from '../../test/mock-credential-endpoints'; import { consoleMockCallJoin } from '../../test/utils'; diff --git a/src/lib/credential.ts b/src/lib/credential.ts index f7bf8e80..2b986d17 100644 --- a/src/lib/credential.ts +++ b/src/lib/credential.ts @@ -1,4 +1,4 @@ -import STS = require('aws-sdk/clients/sts'); +import STS from 'aws-sdk/clients/sts'; type AuthErrorCode = 'CredentialsNotFound' | 'UnAuthorized'; diff --git a/src/module.ts b/src/module.ts new file mode 100644 index 00000000..fadea04f --- /dev/null +++ b/src/module.ts @@ -0,0 +1,7 @@ +// export { getGlobalSpotPrices } from './lib/core'; +export * from './lib/core'; +export * from './lib/credential'; +export * from './lib/utils'; +export * from './constants/ec2-types'; +export * from './constants/product-description'; +export * from './constants/regions'; diff --git a/test/__snapshots__/lib/lib.spec.ts.snap b/test/__snapshots__/lib/core.spec.ts.snap similarity index 100% rename from test/__snapshots__/lib/lib.spec.ts.snap rename to test/__snapshots__/lib/core.spec.ts.snap diff --git a/test/mock-credential-endpoints.ts b/test/mock-credential-endpoints.ts index 81a8aa8c..3de86c46 100644 --- a/test/mock-credential-endpoints.ts +++ b/test/mock-credential-endpoints.ts @@ -1,6 +1,6 @@ import { config } from 'aws-sdk'; -import * as fs from 'fs'; -import * as nock from 'nock'; +import fs from 'fs'; +import nock from 'nock'; import { sep } from 'path'; let readFileSyncMock: jest.SpyInstance; diff --git a/test/mock-ec2-endpoints.ts b/test/mock-ec2-endpoints.ts index 2885f6cc..9ef53a0e 100644 --- a/test/mock-ec2-endpoints.ts +++ b/test/mock-ec2-endpoints.ts @@ -1,7 +1,7 @@ import { SpotPrice } from 'aws-sdk/clients/ec2'; import { readFileSync } from 'fs'; import { filter } from 'lodash'; -import * as nock from 'nock'; +import nock from 'nock'; import { resolve } from 'path'; import { parse } from 'querystring'; diff --git a/tsconfig.json b/tsconfig.json index fff0241f..88ae9eb0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,23 +1,29 @@ { "compilerOptions": { - "module": "commonjs", - "target": "es5", - "lib": ["es5"], + "module": "esnext", + "target": "esnext", + "lib": ["esnext"], "moduleResolution": "node", "strict": true, "strictPropertyInitialization": false, "sourceMap": true, "downlevelIteration": true, "rootDir": "src", - "noEmit": true + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "types", + "esModuleInterop": true }, "files": [ "src/cli.ts", + "src/module.ts", "src/constants/ec2-types.ts", "src/constants/product-description.ts", "src/constants/regions.ts", - "src/lib/lib.ts", - "src/lib/ui.ts" + "src/lib/core.ts", + "src/lib/credential.ts", + "src/lib/ui.ts", + "src/lib/utils.ts" ], "exclude": ["node_modules"] } diff --git a/webpack.config.js b/webpack.config.js index 0be276cb..b514a9f3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,14 +2,16 @@ const path = require('path'); module.exports = { mode: 'production', - entry: path.resolve(__dirname, 'src/cli.ts'), + entry: { + cli: path.resolve(__dirname, 'src/cli.ts'), + module: path.resolve(__dirname, 'src/module.ts'), + }, resolve: { extensions: ['.js', '.json', '.ts'], }, output: { libraryTarget: 'commonjs', path: path.join(__dirname, 'dist'), - filename: 'aws-spot-price.bundle.js', }, stats: { warningsFilter: [/node_modules\/yargs/], @@ -17,6 +19,14 @@ module.exports = { target: 'node', module: { rules: [ + { + test: /cli\.ts$/, + loader: 'string-replace-loader', + options: { + search: "from './module'", + replace: "from '../dist/module'", + }, + }, { test: /\.tsx?$/, use: [ @@ -30,4 +40,5 @@ module.exports = { }, ], }, + externals: [/dist\/module$/], }; diff --git a/yarn.lock b/yarn.lock index 051dbc4c..135c68c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8432,6 +8432,14 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" +string-replace-loader@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-2.2.0.tgz#0a0e6543fcec783d85c353a3e96a23872d45a94f" + integrity sha512-Ukt4ZC8+xVWdBRut3/iwnPJCNL1yV8AbVKXn8UcWdYrHgtuW4UDDAbBSi/J/CQDEWQBt824AJvPYahF23eJLRg== + dependencies: + loader-utils "^1.2.3" + schema-utils "^1.0.0" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"