diff --git a/README.md b/README.md index 8288703b76736f535a5a6387be2aeb765c7728a7..7fa20361478dd763480b4ee677ae61fe23c60deb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # create-tsdev -> Using npx to generate a TS dev env. +> Using npx to generate a latest (2021) Typescript development environment for node.js side + +This setup is using [esbuild](https://esbuild.github.io/getting-started/) (claim to be 3 times faster than webpack / babel) and [ava.js](https://github.com/avajs/) a real modern testing suite for node.js (My options ava.js is way way way better than this [joker](https://jestjs.io)) ## Installation @@ -20,6 +22,19 @@ Please note you need to init your project before you can use this tool. By default it will init the TS dev env inside your project root. Or you can pass `--to /where/your/project/root`. So it will switch over to that folder. +You can also pass `--skipInstall` so it won't run the `npm install` in the end. + +## Credits + +The idea is based on this [blog post](https://www.metachris.com/2021/04/starting-a-typescript-project-in-2021/#:~:text=In%20tsconfig.json%2C%20add%20DOM%20to%20the%20list%20of,can%20attach%20custom%20properties%20to%20window%20like%20this%3A) + +## TODOS + +- Add options to add github / gitlab CI actions +- Option to setup postCSS and browser env +- for ava.js, work out a way to use esbuild instead of `node-ts/register` (just like using `esm`) + + --- Joel Chu (c) 2021 diff --git a/clean.js b/clean.js new file mode 100644 index 0000000000000000000000000000000000000000..07e21db3e246910c815bb58cde46ff62d129f1ad --- /dev/null +++ b/clean.js @@ -0,0 +1,9 @@ +// use fs-extra to clean folder instead of command line makes it cross platform + +const fsx = require('fs-extra') +const { join } = require('path') +const dirs = ['dist', 'build'] + +dirs.forEach(dir => { + fsx.removeSync(join(__dirname, dir)) +}) diff --git a/dist/custom-error.d.ts b/dist/custom-error.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..92cba5d2353f844af05df09bec217a4f45f9f6b1 --- /dev/null +++ b/dist/custom-error.d.ts @@ -0,0 +1,4 @@ +export declare class CustomError extends Error { + parent: Error; + constructor(parent: any); +} diff --git a/dist/custom-error.js b/dist/custom-error.js new file mode 100644 index 0000000000000000000000000000000000000000..82e8c62b5037a64af520aa3209b9e08a2147b0b0 --- /dev/null +++ b/dist/custom-error.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CustomError = void 0; +class CustomError extends Error { + constructor(parent) { + super(parent.message); + this.parent = parent; + } +} +exports.CustomError = CustomError; +//# sourceMappingURL=custom-error.js.map \ No newline at end of file diff --git a/dist/custom-error.js.map b/dist/custom-error.js.map new file mode 100644 index 0000000000000000000000000000000000000000..6489aaffa9785a2b9937eb10f965eed47fcb288b --- /dev/null +++ b/dist/custom-error.js.map @@ -0,0 +1 @@ +{"version":3,"file":"custom-error.js","sourceRoot":"","sources":["../src/custom-error.ts"],"names":[],"mappings":";;;AACA,MAAa,WAAY,SAAQ,KAAK;IAGrC,YAAY,MAAW;QACtB,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACrB,CAAC;CACD;AAPD,kCAOC"} \ No newline at end of file diff --git a/dist/lib.d.ts b/dist/lib.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..c849675ac5d92656da594abbc35d9dc7fa014826 --- /dev/null +++ b/dist/lib.d.ts @@ -0,0 +1,36 @@ +import { CustomError } from './custom-error'; +declare type configObj = { + to?: string; + skipInstall?: boolean; +}; +export { CustomError }; +/** + * @param {array} arg -- process.argv + * @return {promise} resolve nothing + */ +export declare function processArg(argv: Array): Promise; +/** + * pass the `to` prop and switch over to that directory + * @param {string} where to + * @return {array} [ pkgFile, json ] + */ +export declare function changeAndGetPkg(where: string): any; +/** + * copy over the properties + * @param {object} pkg + * @return {object} + */ +export declare function copyProps(pkg: any): any; +/** + * just overwrite the existing package.json + * @param {string} pkgFile path to package.json + * @param {object} pkg content of the json + * @return {promise} not throw error that means success + */ +export declare function overwritePkgJson(pkgFile: string, pkg: any): Promise; +/** + * Execute a npm install if they didn't supply the --noInstall + * @param {object} args from command line + * @return {promise} + */ +export declare function runInstall(args: any): Promise; diff --git a/dist/lib.js b/dist/lib.js new file mode 100644 index 0000000000000000000000000000000000000000..5ef06a5efe979c7c9d760c5b958175dd339cd1e6 --- /dev/null +++ b/dist/lib.js @@ -0,0 +1,114 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.runInstall = exports.overwritePkgJson = exports.copyProps = exports.changeAndGetPkg = exports.processArg = exports.CustomError = void 0; +const tslib_1 = require("tslib"); +// lib.ts libraries of functions +const path_1 = require("path"); +const child_process_1 = require("child_process"); +const fs_extra_1 = tslib_1.__importDefault(require("fs-extra")); +const custom_error_1 = require("./custom-error"); +Object.defineProperty(exports, "CustomError", { enumerable: true, get: function () { return custom_error_1.CustomError; } }); +const PKG_FILE = 'package.json'; +/** + * @param {array} arg -- process.argv + * @return {promise} resolve nothing + */ +function processArg(argv) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + return Promise.resolve(argv) + .then(args => { + return args.reduce((a, arg) => { + if (a.to !== undefined) { + a.to = arg; + } + else if (arg.toLowerCase() === '--to') { + a.to = ''; // placeholder + } + else if (arg === '--skipInstall') { + a.skipInstall = true; + } + return a; + }, {}); + }); + }); +} +exports.processArg = processArg; +/** + * pass the `to` prop and switch over to that directory + * @param {string} where to + * @return {array} [ pkgFile, json ] + */ +function changeAndGetPkg(where) { + if (fs_extra_1.default.existsSync(where)) { + process.chdir(where); + const dir = process.cwd(); + const pkgFile = path_1.join(dir, PKG_FILE); + if (fs_extra_1.default.existsSync(pkgFile)) { + // return a tuple instead + return [ + pkgFile, + fs_extra_1.default.readJsonSync(pkgFile) + ]; + } + } + // just for f**king around with Typescript + throw new custom_error_1.CustomError(new TypeError(`${where} does not exist`)); +} +exports.changeAndGetPkg = changeAndGetPkg; +/** + * copy over the properties + * @param {object} pkg + * @return {object} + */ +function copyProps(pkg) { + const pathToPkg = path_1.resolve(__dirname, '..', PKG_FILE); + const myPkg = fs_extra_1.default.readJsonSync(pathToPkg); + // first merge the devDependencies + pkg.devDependencies = Object.assign(pkg.devDependencies || {}, myPkg.devDependencies); + // next add the ava options + pkg.ava = myPkg.ava; + // finally add some of the scripts + const keys = ["test", "lint", "build", "clean", "ts-node", "docs"]; + pkg.scripts = keys.reduce((obj, key) => (Object.assign(obj, { [key]: myPkg.scripts[key] })), pkg.scripts || {}); + pkg.dependencies = Object.assign(pkg.dependencies || {}, myPkg.dependencies); + return pkg; +} +exports.copyProps = copyProps; +/** + * just overwrite the existing package.json + * @param {string} pkgFile path to package.json + * @param {object} pkg content of the json + * @return {promise} not throw error that means success + */ +function overwritePkgJson(pkgFile, pkg) { + return fs_extra_1.default.writeJson(pkgFile, pkg); +} +exports.overwritePkgJson = overwritePkgJson; +/** + * Execute a npm install if they didn't supply the --noInstall + * @param {object} args from command line + * @return {promise} + */ +function runInstall(args) { + return new Promise((resolver, rejecter) => { + if (args.skipInstall !== true && process.env.NODE_ENV !== 'test') { + child_process_1.exec("npm install", { cwd: process.cwd() }, (error, stdout, stderr) => { + if (error) { + console.error(`ERROR:`, error); + return rejecter(false); + } + console.log(`stdout`, stdout); + if (stderr) { + console.error(`stderr`, stderr); + } + resolver(true); + }); + } + else { + console.log(`All done nothing to do`); + resolver(true); + } + }); +} +exports.runInstall = runInstall; +//# sourceMappingURL=lib.js.map \ No newline at end of file diff --git a/dist/lib.js.map b/dist/lib.js.map new file mode 100644 index 0000000000000000000000000000000000000000..ff8fea7058fe0438f21d559d8665f2c79169b471 --- /dev/null +++ b/dist/lib.js.map @@ -0,0 +1 @@ +{"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/main.d.ts b/dist/main.d.ts index 35eb8ed29c577e91e1d036601205903fb7ef5126..6b6038191351d709693832ec219eda0cd59729bd 100644 --- a/dist/main.d.ts +++ b/dist/main.d.ts @@ -1,10 +1,7 @@ -declare type configObj = { - to?: String; - skipInstall?: Boolean; -}; /** - * @param {array} arg -- process.argv - * @return {promise} resolve nothing + * Top level API + * @param {Array} _args from process.argv + * @return {void} + * @public */ -export declare function processArg(argv: Array): Promise; -export {}; +export declare function main(_args: Array): Promise; diff --git a/dist/main.js b/dist/main.js index 38ba8c5ddb375cbb7f592cf8e17ea8fb6a00f341..ef797076e0078183d4c405b8179fb6b8d3834333 100644 --- a/dist/main.js +++ b/dist/main.js @@ -1,29 +1,27 @@ "use strict"; +// just create a top level main function Object.defineProperty(exports, "__esModule", { value: true }); -exports.processArg = void 0; +exports.main = void 0; const tslib_1 = require("tslib"); +const lib_1 = require("./lib"); /** - * @param {array} arg -- process.argv - * @return {promise} resolve nothing + * Top level API + * @param {Array} _args from process.argv + * @return {void} + * @public */ -function processArg(argv) { +function main(_args) { return tslib_1.__awaiter(this, void 0, void 0, function* () { - return Promise.resolve(argv.slice(2)) + // there is no point of accepting input anyway + return lib_1.processArg(_args) .then(args => { - return args.reduce((a, arg) => { - if (a.to !== undefined) { - a.to = arg; - } - else if (arg.toLowerCase() === '--to') { - a.to = ''; // placeholder - } - else if (arg === '--skipInstall') { - a.skipInstall = true; - } - return a; - }, {}); - }); + const [pkgFile, _pkg] = lib_1.changeAndGetPkg(args.to || process.cwd()); + const pkg = lib_1.copyProps(_pkg); + return { args, pkg, pkgFile }; + }) + .then(({ args, pkg, pkgFile }) => (lib_1.overwritePkgJson(pkgFile, pkg) + .then(() => lib_1.runInstall(args)))); }); } -exports.processArg = processArg; +exports.main = main; //# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map index ddca3865afb3c72a50c9285b11066e8a2d9395bd..b2988be08453e848b48a5f2661294a8daad46b74 100644 --- a/dist/main.js.map +++ b/dist/main.js.map @@ -1 +1 @@ -{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;AAOA;;;GAGG;AACH,SAAsB,UAAU,CAAC,IAAmB;;QAClD,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;aAClC,IAAI,CAAC,IAAI,CAAC,EAAE;YACX,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAY,EAAE,GAAW,EAAE,EAAE;gBAC/C,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,EAAE;oBACtB,CAAC,CAAC,EAAE,GAAG,GAAG,CAAA;iBACX;qBACI,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE;oBACrC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAA,CAAC,cAAc;iBACzB;qBACI,IAAI,GAAG,KAAK,eAAe,EAAE;oBAChC,CAAC,CAAC,WAAW,GAAG,IAAI,CAAA;iBACrB;gBACD,OAAO,CAAC,CAAA;YACV,CAAC,EAAE,EAAE,CAAC,CAAA;QACR,CAAC,CAAC,CAAA;IACN,CAAC;CAAA;AAhBD,gCAgBC"} \ No newline at end of file +{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAAA,wCAAwC;;;;AAExC,+BAMc;AAEd;;;;;GAKG;AACH,SAAsB,IAAI,CAAC,KAAiB;;QAC1C,8CAA8C;QAC9C,OAAO,gBAAU,CAAC,KAAK,CAAC;aACrB,IAAI,CAAC,IAAI,CAAC,EAAE;YACX,MAAM,CAAE,OAAO,EAAE,IAAI,CAAE,GAAG,qBAAe,CAAC,IAAI,CAAC,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;YACnE,MAAM,GAAG,GAAG,eAAS,CAAC,IAAI,CAAC,CAAA;YAE3B,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAA;QAC/B,CAAC,CAAC;aACD,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAChC,sBAAgB,CAAC,OAAO,EAAE,GAAG,CAAC;aAC3B,IAAI,CAAC,GAAG,EAAE,CAAC,gBAAU,CAAC,IAAI,CAAC,CAAC,CAChC,CAAC,CAAA;IACN,CAAC;CAAA;AAbD,oBAaC"} \ No newline at end of file diff --git a/index.js b/index.js index a75cc9de7026d631daace9d43fc42e9989353fe2..0653195a356dcaaa1216d61a6d59cfe0e99c62ef 100644 --- a/index.js +++ b/index.js @@ -1,26 +1,5 @@ #!/usr/bin/env node // this is just the entry point where it gets call -const { processArg } = require('./dist/main.js') -const fsx = require('fs-extra') -const { join } = require('path') +const { main } = require('./dist/main.js') -processArg(process.argv) - .then(config => { - console.log(`Hello LOL`, config) - - // little test - if (config.to && fsx.existsSync(config.to)) { - process.chdir(config.to) - - const dir = process.cwd() - - console.log(dir) - - const pkgFile = join(dir, 'package.json') - console.log(pkgFile) - const pkg = fsx.readJsonSync(pkgFile) - console.log(pkg.name, pkg.version) - } - - - }) +main(process.argv.slice(2)) diff --git a/package.json b/package.json index 2049ebf8b44fcd2c84261593035019dabac0069d..2909a9ea03a3ef3beac357b103835b30772e4f76 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,15 @@ "main": "index.js", "files": [ "index.js", - "dist" + "dist", + "tsconfig.json" ], "scripts": { "test": "ava", "cli": "node ./index.js", "lint": "eslint src/ --ext .js,.jsx,.ts,.tsx", "build": "tsc -p tsconfig.json", - "clean": "rm -rf dist build", + "clean": "node ./clean.js", "ts-node": "ts-node", "docs": "typedoc --entryPoints src/main.ts", "build-all": "npm run clean && npm run build && npm run esbuild-node", diff --git a/src/lib.ts b/src/lib.ts new file mode 100644 index 0000000000000000000000000000000000000000..08e35358510f2c6851ddf2913509fb6a39bf79ab --- /dev/null +++ b/src/lib.ts @@ -0,0 +1,119 @@ +// lib.ts libraries of functions +import { join, resolve } from 'path' +import { exec } from 'child_process' +import fsx from 'fs-extra' +import { CustomError } from './custom-error' + +// the main method +type configObj = { + to?: string, + skipInstall? : boolean +} +const PKG_FILE: string = 'package.json' + +// re-export +export { CustomError } + +/** + * @param {array} arg -- process.argv + * @return {promise} resolve nothing + */ +export async function processArg(argv: Array): Promise { + return Promise.resolve(argv) + .then(args => { + return args.reduce((a: configObj, arg: string) => { + if (a.to !== undefined) { + a.to = arg + } + else if (arg.toLowerCase() === '--to') { + a.to = '' // placeholder + } + else if (arg === '--skipInstall') { + a.skipInstall = true + } + return a + }, {}) + }) +} + +/** + * pass the `to` prop and switch over to that directory + * @param {string} where to + * @return {array} [ pkgFile, json ] + */ +export function changeAndGetPkg(where: string): any { + if (fsx.existsSync(where)) { + process.chdir(where) + const dir = process.cwd() + const pkgFile = join(dir, PKG_FILE) + if (fsx.existsSync(pkgFile)) { + // return a tuple instead + return [ + pkgFile, + fsx.readJsonSync(pkgFile) + ] + } + } + // just for f**king around with Typescript + throw new CustomError(new TypeError(`${where} does not exist`)) +} + +/** + * copy over the properties + * @param {object} pkg + * @return {object} + */ +export function copyProps(pkg: any): any { + const pathToPkg = resolve(__dirname, '..', PKG_FILE) + const myPkg = fsx.readJsonSync(pathToPkg) + // first merge the devDependencies + pkg.devDependencies = Object.assign(pkg.devDependencies || {}, myPkg.devDependencies) + // next add the ava options + pkg.ava = myPkg.ava + // finally add some of the scripts + const keys = ["test", "lint", "build", "clean", "ts-node", "docs"] + pkg.scripts = keys.reduce((obj: any, key: string) => ( + Object.assign(obj, {[key]: myPkg.scripts[key]}) + ), pkg.scripts || {}) + pkg.dependencies = Object.assign(pkg.dependencies || {}, myPkg.dependencies) + + return pkg +} + +/** + * just overwrite the existing package.json + * @param {string} pkgFile path to package.json + * @param {object} pkg content of the json + * @return {promise} not throw error that means success + */ +export function overwritePkgJson(pkgFile: string, pkg: any): Promise { + return fsx.writeJson(pkgFile, pkg) +} + +/** + * Execute a npm install if they didn't supply the --noInstall + * @param {object} args from command line + * @return {promise} + */ +export function runInstall(args: any): Promise { + return new Promise((resolver, rejecter) => { + if (args.skipInstall !== true && process.env.NODE_ENV !== 'test') { + exec("npm install", + {cwd: process.cwd()}, + (error, stdout, stderr) => { + if (error) { + console.error(`ERROR:`, error) + return rejecter(false) + } + console.log(`stdout`, stdout) + if (stderr) { + console.error(`stderr`, stderr) + } + resolver(true) + }) + } else { + console.log(`All done nothing to do`) + resolver(true) + } + }) +} diff --git a/src/main.ts b/src/main.ts index 290c60f5f14cb54dd0e23d35e21a37145b4e5ebd..61b017f6652ffbdfefb7ee300ec1207c6a5674f1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,54 +1,30 @@ -// main.ts -import fsx from 'fs-extra' -import { join } from 'path' -import { CustomError } from './custom-error' +// just create a top level main function -// the main method -type configObj = { - to?: String, - skipInstall? : Boolean -} -const PKG_FILE: string = 'package.json' - -// re-export -export { CustomError } +import { + processArg, + changeAndGetPkg, + copyProps, + overwritePkgJson, + runInstall +} from './lib' /** - * @param {array} arg -- process.argv - * @return {promise} resolve nothing + * Top level API + * @param {Array} _args from process.argv + * @return {void} + * @public */ -export async function processArg(argv: Array): Promise { - return Promise.resolve(argv.slice(2)) +export async function main(_args: Array) { + // there is no point of accepting input anyway + return processArg(_args) .then(args => { - return args.reduce((a: configObj, arg: String) => { - if (a.to !== undefined) { - a.to = arg - } - else if (arg.toLowerCase() === '--to') { - a.to = '' // placeholder - } - else if (arg === '--skipInstall') { - a.skipInstall = true - } - return a - }, {}) - }) -} + const [ pkgFile, _pkg ] = changeAndGetPkg(args.to || process.cwd()) + const pkg = copyProps(_pkg) -/** - * pass the `to` prop and switch over to that directory - * @param {string} where to - * @return {*} - */ -export function changeAndGetPkg(where: string): any { - if (fsx.existsSync(where)) { - process.chdir(where) - const dir = process.cwd() - const pkgFile = join(dir, PKG_FILE) - if (fsx.existsSync(pkgFile)) { - return fsx.readJsonSync(pkgFile) - } - } - // just for f**king around with Typescript - throw new CustomError(new TypeError(`${where} does not exist`)) + return { args, pkg, pkgFile } + }) + .then(({ args, pkg, pkgFile }) => ( + overwritePkgJson(pkgFile, pkg) + .then(() => runInstall(args)) + )) } diff --git a/tests/fixtures/package-tpl.json b/tests/fixtures/package-tpl.json new file mode 100644 index 0000000000000000000000000000000000000000..53350f736738a83d88a534e47ab5bb8d93e6a576 --- /dev/null +++ b/tests/fixtures/package-tpl.json @@ -0,0 +1,21 @@ +{ + "name": "create-tsdev", + "version": "0.0.1", + "description": "Using npx to generate a TS dev env", + "main": "index.js", + "scripts": { + "hello": "echo \"hello\"" + }, + "repository": { + "type": "git", + "url": "git@gitee.com:NEWBRAN/create-tsdev.git" + }, + "keywords": [ + "Node", + "TS", + "Typescript", + "ava.js" + ], + "author": "NOBODY", + "license": "MIT" +} diff --git a/tests/lib.test.ts b/tests/lib.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd095c191b9d9f5fd4964bcffd724026051d5d34 --- /dev/null +++ b/tests/lib.test.ts @@ -0,0 +1,47 @@ +// main test file +import test from 'ava' +import { copySync, removeSync, readJsonSync } from 'fs-extra' +import { join } from 'path' +import { + processArg, + changeAndGetPkg, + CustomError, + copyProps +} from '../src/lib' + + +const fixtures: string = join(__dirname, 'fixtures') +const pkgTpl: string = join(fixtures, 'package-tpl.json') +const dest: string = join(fixtures, 'package.json' ) + +test.before(() => { + copySync(pkgTpl, dest) +}) + +test.after(() => { + removeSync(dest) +}) + + +test(`Expect to able to get the right properties`, async t => { + const p = '/home/joel/Projects/create-t1sts' + const result = await processArg(['_', '_', '--to', p]) + + t.is(result.to, p) +}) + +test(`Expect to fail if there is no package.json`, t => { + const myTestFunc = () => changeAndGetPkg("/path/to/no/where") + + const err = t.throws(myTestFunc) + t.is(err.parent.name, 'TypeError') +}) + +test(`Expect to copy over the necessary properties to the package.json`, t => { + const myPkg = readJsonSync(dest) + const result = copyProps(myPkg) + + t.true(result.dependencies !== undefined) + t.true(result.scripts !== undefined) + t.is(result.scripts.test, "ava") +}) diff --git a/tests/main.test.ts b/tests/main.test.ts index ab0d0fa6d3fb5f6d8f375ebde1a8f6653c6f3861..da2539d3e600c7966a1921f61a272a19774f8763 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -1,20 +1,30 @@ -// main test file import test from 'ava' -import { processArg, changeAndGetPkg, CustomError } from '../src/main' +import { removeSync, copySync, existsSync, readJsonSync } from 'fs-extra' +import { join } from 'path' -test(`Expect to able to get the right properties`, async t => { - const p = '/home/joel/Projects/create-t1sts' - const result = await processArg(['_', '_', '--to', p]) +import { main } from '../src/main' - t.is(result.to, p) -}) +const from = join(__dirname, 'fixtures', 'package-tpl.json') +const to = join(__dirname, 'tmp') +const pkgFile = join(to, 'package.json') +test.before(() => { + copySync(from , pkgFile) +}) -test(`Expect to fail if there is no package.json`, t => { - const myTestFunc = () => changeAndGetPkg("/path/to/no/where") - - const err = t.throws(myTestFunc) - t.is(err.parent.name, 'TypeError') +test.after(() => { + removeSync(to) }) -test.todo(`Expect to copy over the necessary properties to the package.json`) + +test(`End to end test`, async t => { + const res = await main(['--to', to]) + t.true(res) + t.true(existsSync(pkgFile)) + + const pkg = readJsonSync(pkgFile) + + t.true(pkg.dependencies !== undefined) + t.true(pkg.scripts !== undefined) + t.is(pkg.scripts.test, "ava") +})