feat(CLI): create CLI, begin work on v2
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@ src/**.js
|
||||
coverage
|
||||
.nyc_output
|
||||
*.log
|
||||
|
||||
yarn.lock
|
||||
@@ -1,14 +1,14 @@
|
||||
src
|
||||
config
|
||||
examples
|
||||
test
|
||||
tsconfig.json
|
||||
tsconfig.module.json
|
||||
tslint.json
|
||||
.travis.yml
|
||||
.github
|
||||
build/temp
|
||||
.prettierignore
|
||||
build/docs
|
||||
|
||||
**/*.spec.*
|
||||
coverage
|
||||
.nyc_output
|
||||
*.log
|
||||
examples
|
||||
|
||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
package.json
|
||||
@@ -2,7 +2,5 @@ sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- 8
|
||||
- 6
|
||||
- 4
|
||||
after_success:
|
||||
- yarn send-coverage
|
||||
- npm run send-coverage
|
||||
|
||||
1
README-starter.md
Normal file
1
README-starter.md
Normal file
@@ -0,0 +1 @@
|
||||
# package-name
|
||||
@@ -1,53 +0,0 @@
|
||||
// this script watches the tests exported by typescript, copies them to the test directories, and modifies the require("PKG.NAME") statements to test each build
|
||||
const cpx = require("cpx");
|
||||
const path = require("path");
|
||||
const Transform = require("stream").Transform;
|
||||
const pkg = require('../../package');
|
||||
|
||||
const req = (path) => `require("${path}")`;
|
||||
|
||||
// replace instances of pkg.name with the proper route to the build being tested
|
||||
const makeTransform = (rootDir, buildPath, specPath) => {
|
||||
const specDir = path.dirname(specPath)
|
||||
const testDir = specDir.replace(path.join(rootDir, 'build'), path.join(rootDir, 'test'))
|
||||
|
||||
const newRequire = path.relative(testDir, buildPath)
|
||||
.split(path.sep).join('/')
|
||||
|
||||
return new Transform({
|
||||
transform(chunk, encoding, done) {
|
||||
const str = chunk
|
||||
.toString()
|
||||
.replace(req(pkg.name), req(newRequire))
|
||||
this.push(str);
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// copy, then watch for changes to the tests
|
||||
const testsFromRoot = 'build/main/**/*.spec.js';
|
||||
const watchMode = process.argv.indexOf('-w') !== -1 ? true : false;
|
||||
const browserTests = process.argv.indexOf('--no-browser') !== -1 ? true : false;
|
||||
const task = watchMode ? cpx.watch : cpx.copy;
|
||||
|
||||
const rootDir = path.resolve('.');
|
||||
const mainBuildPath = path.resolve(pkg.main);
|
||||
|
||||
task(testsFromRoot, 'test/main', {
|
||||
transform: (specPath) => makeTransform(
|
||||
rootDir,
|
||||
mainBuildPath,
|
||||
path.resolve(specPath))
|
||||
});
|
||||
|
||||
if (!browserTests) {
|
||||
const browserBuildPath = path.resolve(pkg.browser);
|
||||
|
||||
task(testsFromRoot, 'test/browser', {
|
||||
transform: (specPath) => makeTransform(
|
||||
rootDir,
|
||||
browserBuildPath.replace('.js', '.cjs.js'),
|
||||
path.resolve(specPath))
|
||||
});
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// rollup.config.js
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
import alias from 'rollup-plugin-alias';
|
||||
import json from 'rollup-plugin-json';
|
||||
|
||||
const substituteModulePaths = {
|
||||
'crypto': 'build/module/adapters/crypto.browser.js',
|
||||
'hash.js': 'build/temp/hash.js'
|
||||
}
|
||||
|
||||
export default {
|
||||
entry: 'build/module/index.js',
|
||||
sourceMap: true,
|
||||
plugins: [
|
||||
alias(substituteModulePaths),
|
||||
nodeResolve({
|
||||
browser: true
|
||||
}),
|
||||
commonjs(),
|
||||
json()
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../build/module",
|
||||
"rootDir": "../../src",
|
||||
"module": "es6",
|
||||
"declaration": false
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny" : true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
}
|
||||
}
|
||||
10484
package-lock.json
generated
10484
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
167
package.json
167
package.json
@@ -1,86 +1,15 @@
|
||||
{
|
||||
"name": "typescript-starter",
|
||||
"version": "1.4.1",
|
||||
"version": "2.0.0",
|
||||
"description": "A typescript starter for building javascript libraries and projects",
|
||||
"bin": {
|
||||
"typescript-starter": "./build/main/typescript-starter.js"
|
||||
},
|
||||
"main": "build/main/index.js",
|
||||
"typings": "build/main/index.d.ts",
|
||||
"module": "build/module/index.js",
|
||||
"browser": "build/browser/index.js",
|
||||
"repository": "https://github.com/bitjson/typescript-starter",
|
||||
"author": "Jason Dreyzehner <jason@dreyzehner.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"info": "npm-scripts-info",
|
||||
"build": "trash build && yarn build:main && yarn build:module && yarn build:browser-deps && yarn build:browser && yarn build:browser-cjs && yarn build:resolve-sourcemaps",
|
||||
"build:main": "tsc -p tsconfig.json",
|
||||
"build:module": "tsc -p config/exports/tsconfig.module.json",
|
||||
"build:browser-deps": "mkdirp build/temp && browserify node_modules/hash.js/lib/hash.js --standalone hash -o build/temp/hash.js",
|
||||
"build:browser": "rollup -c config/exports/rollup.config.js -f es -o build/browser/index.js",
|
||||
"build:browser-cjs": "rollup -c config/exports/rollup.config.js -f cjs -o build/browser/index.cjs.js",
|
||||
"build:resolve-sourcemaps": "sorcery -i build/browser/index.js && sorcery -i build/browser/index.cjs.js",
|
||||
"build:tests": "trash test && node config/exports/build-tests.js",
|
||||
"lint": "tslint --project . --type-check src/**/*.ts",
|
||||
"unit": "yarn build && yarn build:tests && nyc ava",
|
||||
"check-coverage": "nyc check-coverage --lines 100 --functions 100 --branches 100",
|
||||
"test": "yarn lint && yarn unit && yarn check-coverage",
|
||||
"watch": "yarn build && yarn build:tests -- --no-browser && concurrently -r --kill-others \"npm run --silent build:main -- -w\" \"npm run --silent build:tests -- -w --no-browser\" \"sleepms 2000 && ava --watch\"",
|
||||
"cov": "yarn unit && yarn html-coverage && opn coverage/index.html",
|
||||
"html-coverage": "nyc report --reporter=html",
|
||||
"send-coverage": "nyc report --reporter=lcov > coverage.lcov && codecov",
|
||||
"docs": "yarn docs:html && opn build/docs/index.html",
|
||||
"docs:html": "typedoc src/index.ts --excludePrivate --mode file --theme minimal --out build/docs",
|
||||
"docs:json": "typedoc --mode file --json build/docs/typedoc.json src/index.ts",
|
||||
"docs:publish": "yarn docs:html && gh-pages -d build/docs",
|
||||
"changelog": "standard-version",
|
||||
"release": "yarn reset && yarn test && yarn docs:publish && yarn changelog",
|
||||
"reset": "git clean -dfx && git reset --hard && yarn"
|
||||
},
|
||||
"scripts-info": {
|
||||
"info": "Display information about the scripts",
|
||||
"build": "(Trash and re)build the library",
|
||||
"lint": "Lint all typescript source files",
|
||||
"unit": "Build the library and run unit tests",
|
||||
"test": "Lint, build, and test the library",
|
||||
"watch": "Watch source files, rebuild library on changes, rerun relevant tests",
|
||||
"cov": "Run tests, generate the HTML coverage report, and open it in a browser",
|
||||
"docs": "Generate HTML API documentation and open it in a browser",
|
||||
"docs:publish": "Generate HTML API documentation and push it to GitHub Pages",
|
||||
"docs:json": "Generate API documentation in typedoc JSON format",
|
||||
"changelog": "Bump package.json version, update CHANGELOG.md, tag a release",
|
||||
"reset": "Delete all untracked files and reset the repo to the last commit",
|
||||
"release": "Clean, build, test, publish docs, and prepare release (a one-step publish process)"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^8.0.4",
|
||||
"ava": "^0.21.0",
|
||||
"browserify": "^14.1.0",
|
||||
"codecov": "^2.2.0",
|
||||
"concurrently": "^3.4.0",
|
||||
"cpx": "^1.5.0",
|
||||
"gh-pages": "^1.0.0",
|
||||
"hash.js": "^1.0.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"npm-scripts-info": "^0.3.6",
|
||||
"nyc": "^11.0.3",
|
||||
"opn-cli": "^3.1.0",
|
||||
"rollup": "^0.44.0",
|
||||
"rollup-plugin-alias": "^1.2.0",
|
||||
"rollup-plugin-commonjs": "^8.0.2",
|
||||
"rollup-plugin-json": "^2.3.0",
|
||||
"rollup-plugin-node-resolve": "^3.0.0",
|
||||
"rollup-watch": "^4.0.0",
|
||||
"sleep-ms": "^2.0.1",
|
||||
"sorcery": "^0.10.0",
|
||||
"standard-version": "^4.0.0",
|
||||
"trash-cli": "^1.4.0",
|
||||
"tslint": "^5.4.3",
|
||||
"tslint-config-standard": "^6.0.1",
|
||||
"typedoc": "^0.8.0",
|
||||
"typescript": "^2.4.1"
|
||||
},
|
||||
"keywords": [
|
||||
"async",
|
||||
"ava",
|
||||
@@ -102,20 +31,92 @@
|
||||
"typescript",
|
||||
"typings"
|
||||
],
|
||||
"scripts": {
|
||||
"info": "npm-scripts-info",
|
||||
"build": "run-s clean && run-p build:*",
|
||||
"build:main": "tsc -p tsconfig.json",
|
||||
"build:module": "tsc -p tsconfig.module.json",
|
||||
"fix": "run-s fix:*",
|
||||
"fix:prettier": "prettier 'src/**/*.ts' --write",
|
||||
"fix:tslint": "tslint --fix --project . src/**/*.ts",
|
||||
"test": "run-s test:*",
|
||||
"test:lint": "tslint --project . src/**/*.ts && prettier 'src/**/*.ts' --list-different",
|
||||
"test:unit": "nyc ava",
|
||||
"test:coverage": "nyc check-coverage --lines 100 --functions 100 --branches 100",
|
||||
"watch": "run-s clean && run-p 'build:* -- -w' 'test:unit -- --watch'",
|
||||
"cov": "run-s build test:unit cov:html && opn coverage/index.html",
|
||||
"cov:html": "nyc report --reporter=html",
|
||||
"cov:send": "nyc report --reporter=lcov > coverage.lcov && codecov",
|
||||
"doc": "run-s doc:html && opn build/docs/index.html",
|
||||
"doc:html": "typedoc src/index.ts --excludePrivate --mode file --theme minimal --out build/docs",
|
||||
"doc:json": "typedoc --mode file --json build/docs/typedoc.json src/index.ts",
|
||||
"docs:publish": "gh-pages -d build/docs",
|
||||
"changelog": "standard-version",
|
||||
"reset": "git clean -dfx && git reset --hard && npm i",
|
||||
"clean": "trash build test",
|
||||
"release": "run-s reset test docs:html docs:publish changelog"
|
||||
},
|
||||
"scripts-info": {
|
||||
"info": "Display information about the package scripts",
|
||||
"build": "Clean and Rebuild the project",
|
||||
"fix": "Try to automatically fix any linting problems",
|
||||
"test": "Lint and unit test the project",
|
||||
"watch": "Watch and rebuild the project on save, then rerun relevant tests",
|
||||
"cov": "Rebuild, run tests, then create and open the coverage report",
|
||||
"doc": "Generate HTML API documentation and open it in a browser",
|
||||
"doc:json": "Generate API documentation in typedoc JSON format",
|
||||
"changelog": "Bump package.json version, update CHANGELOG.md, tag release",
|
||||
"reset": "Delete all untracked files and reset the repo to the last commit",
|
||||
"release": "One-step: clean, build, test, publish docs, and prep a release"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^2.3.1",
|
||||
"cross-spawn": "^6.0.4",
|
||||
"github-username": "^4.1.0",
|
||||
"gradient-string": "^1.0.0",
|
||||
"inquirer": "^5.1.0",
|
||||
"ora": "^2.0.0",
|
||||
"project-version": "^1.0.0",
|
||||
"replace-in-file": "^3.1.1",
|
||||
"sha.js": "^2.4.10",
|
||||
"sorted-object": "^2.0.1",
|
||||
"trash": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^8.9.4",
|
||||
"ava": "^1.0.0-beta.3",
|
||||
"codecov": "^3.0.0",
|
||||
"gh-pages": "^1.0.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"npm-scripts-info": "^0.3.6",
|
||||
"nyc": "^11.5.0",
|
||||
"opn-cli": "^3.1.0",
|
||||
"prettier": "^1.10.2",
|
||||
"standard-version": "^4.0.0",
|
||||
"trash-cli": "^1.4.0",
|
||||
"tslint": "^5.4.3",
|
||||
"tslint-config-prettier": "^1.8.0",
|
||||
"typedoc": "^0.10.0",
|
||||
"typescript": "^2.4.1"
|
||||
},
|
||||
"nyc": {
|
||||
"exclude": [
|
||||
"**/*.spec.js",
|
||||
"build/browser/**"
|
||||
"**/*.spec.js"
|
||||
]
|
||||
},
|
||||
"ava": {
|
||||
"source": [
|
||||
"test/**/*.js",
|
||||
"build/**/*.js",
|
||||
"!build/**/*.spec.js"
|
||||
"failFast": true,
|
||||
"files": [
|
||||
"build/main/**/*.spec.js"
|
||||
],
|
||||
"sources": [
|
||||
"build/main/**/*.js"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^1.6.0"
|
||||
"prettier": {
|
||||
"singleQuote": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// Must first be built by browserify.
|
||||
// https://github.com/rollup/rollup-plugin-commonjs/issues/105#issuecomment-281917166
|
||||
import hash from 'hash.js'
|
||||
|
||||
/**
|
||||
* Simulate the Node.js crypto.createHash function using hash.js' implementation.
|
||||
* @internal
|
||||
* @hidden (TypeDoc currently doesn't understand @internal)
|
||||
*/
|
||||
export function createHash (algorithm: 'sha256') {
|
||||
return hash.sha256()
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './lib/async'
|
||||
export * from './lib/hash'
|
||||
export * from './lib/number'
|
||||
export * from './lib/async';
|
||||
export * from './lib/hash';
|
||||
export * from './lib/number';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { test } from 'ava'
|
||||
import { asyncABC } from 'typescript-starter'
|
||||
import { test } from 'ava';
|
||||
import { asyncABC } from './async';
|
||||
|
||||
test('getABC', async t => {
|
||||
t.deepEqual(await asyncABC(), ['a','b', 'c'])
|
||||
})
|
||||
t.deepEqual(await asyncABC(), ['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
@@ -17,16 +17,16 @@
|
||||
*
|
||||
* @returns a Promise which should contain `['a','b','c']`
|
||||
*/
|
||||
export async function asyncABC () {
|
||||
function somethingSlow (index: 0 | 1 | 2) {
|
||||
let storage = 'abc'.charAt(index)
|
||||
export async function asyncABC() {
|
||||
function somethingSlow(index: 0 | 1 | 2) {
|
||||
const storage = 'abc'.charAt(index);
|
||||
return new Promise<string>(resolve => {
|
||||
// here we pretend to wait on the network
|
||||
setTimeout(() => resolve(storage), 0)
|
||||
})
|
||||
setTimeout(() => resolve(storage), 0);
|
||||
});
|
||||
}
|
||||
let a = await somethingSlow(0)
|
||||
let b = await somethingSlow(1)
|
||||
let c = await somethingSlow(2)
|
||||
return [a, b, c]
|
||||
const a = await somethingSlow(0);
|
||||
const b = await somethingSlow(1);
|
||||
const c = await somethingSlow(2);
|
||||
return [a, b, c];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import { test } from 'ava'
|
||||
import { sha256 } from 'typescript-starter'
|
||||
import { Macro, test } from 'ava';
|
||||
import { sha256, sha256Native } from './hash';
|
||||
|
||||
test('sha256', t => {
|
||||
t.deepEqual(sha256('test'), '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08')
|
||||
})
|
||||
const hash: Macro = (t, input: string, expected: string) => {
|
||||
t.is(sha256(input), expected);
|
||||
t.is(sha256Native(input), expected);
|
||||
};
|
||||
|
||||
hash.title = (providedTitle: string, input: string, expected: string) =>
|
||||
`${providedTitle}: ${input} => ${expected}`;
|
||||
|
||||
test(
|
||||
'sha256',
|
||||
hash,
|
||||
'test',
|
||||
'9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createHash } from 'crypto'
|
||||
import { createHash } from 'crypto';
|
||||
import shaJs from 'sha.js';
|
||||
|
||||
/**
|
||||
* Calculate the sha256 digest of a string. On Node.js, this will use the native module, in the browser, it will fall back to a pure javascript implementation.
|
||||
* Calculate the sha256 digest of a string.
|
||||
*
|
||||
* ### Example (es imports)
|
||||
* ```js
|
||||
@@ -12,6 +13,19 @@ import { createHash } from 'crypto'
|
||||
*
|
||||
* @returns sha256 message digest
|
||||
*/
|
||||
export function sha256 (message: string) {
|
||||
return createHash('sha256').update(message).digest('hex')
|
||||
export function sha256(message: string) {
|
||||
return shaJs('sha256')
|
||||
.update(message)
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* A faster implementation of [[sha256]] which requires the native Node.js module. Browser consumers should use [[sha256]], instead.
|
||||
*
|
||||
* @returns sha256 message digest
|
||||
*/
|
||||
export function sha256Native(message: string) {
|
||||
return createHash('sha256')
|
||||
.update(message)
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { test } from 'ava'
|
||||
import { double, power } from 'typescript-starter'
|
||||
import { test } from 'ava';
|
||||
import { double, power } from './number';
|
||||
|
||||
test('double', t => {
|
||||
t.deepEqual(double(2), 4)
|
||||
})
|
||||
t.deepEqual(double(2), 4);
|
||||
});
|
||||
|
||||
test('power', t => {
|
||||
t.deepEqual(power(2,4), 16)
|
||||
})
|
||||
t.deepEqual(power(2, 4), 16);
|
||||
});
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
* @returns Comment describing the return type.
|
||||
* @anotherNote Some other value.
|
||||
*/
|
||||
export function double (value: number) {
|
||||
return value * 2
|
||||
export function double(value: number) {
|
||||
return value * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +40,7 @@ export function double (value: number) {
|
||||
* // => 8
|
||||
* ```
|
||||
*/
|
||||
export function power (base: number, exponent: number) {
|
||||
export function power(base: number, exponent: number) {
|
||||
// This is a proposed es7 operator, which should be transpiled by Typescript
|
||||
return base ** exponent
|
||||
return base ** exponent;
|
||||
}
|
||||
|
||||
402
src/typescript-starter.ts
Normal file
402
src/typescript-starter.ts
Normal file
@@ -0,0 +1,402 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// tslint:disable:no-console
|
||||
import chalk from 'chalk';
|
||||
import spawn from 'cross-spawn';
|
||||
import { existsSync, readFileSync, renameSync, writeFileSync } from 'fs';
|
||||
import githubUsername from 'github-username';
|
||||
import gradient from 'gradient-string';
|
||||
import { prompt } from 'inquirer';
|
||||
import ora from 'ora';
|
||||
import { join } from 'path';
|
||||
import replace from 'replace-in-file';
|
||||
import sortedObject from 'sorted-object';
|
||||
import trash from 'trash';
|
||||
|
||||
enum ProjectType {
|
||||
Node,
|
||||
Library
|
||||
}
|
||||
|
||||
enum Runner {
|
||||
Npm,
|
||||
Yarn
|
||||
}
|
||||
|
||||
enum TypeDefinitions {
|
||||
none,
|
||||
Node,
|
||||
DOM,
|
||||
NodeAndDOM
|
||||
}
|
||||
|
||||
const ascii = `
|
||||
_ _ _ _ _
|
||||
| |_ _ _ _ __ ___ ___ ___ _ __(_)_ __ | |_ ___| |_ __ _ _ __| |_ ___ _ __
|
||||
| __| | | | '_ \\ / _ \\/ __|/ __| '__| | '_ \\| __|____/ __| __/ _\` | '__| __/ _ \\ '__|
|
||||
| |_| |_| | |_) | __/\\__ \\ (__| | | | |_) | ||_____\\__ \\ || (_| | | | || __/ |
|
||||
\\__|\\__, | .__/ \\___||___/\\___|_| |_| .__/ \\__| |___/\\__\\__,_|_| \\__\\___|_|
|
||||
|___/|_| |_|
|
||||
`;
|
||||
|
||||
const repo =
|
||||
process.env.TYPESCRIPT_STARTER_REPO_URL ||
|
||||
'https://github.com/bitjson/typescript-starter.git';
|
||||
|
||||
(async () => {
|
||||
if (process.argv.some(a => a === '-v' || a === '--version')) {
|
||||
console.log(
|
||||
JSON.parse(readFileSync(`${__dirname}/../../package.json`, 'utf8'))
|
||||
.version
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
if (process.stdout.columns && process.stdout.columns >= 85) {
|
||||
console.log(chalk.bold(gradient.mind(ascii)));
|
||||
} else {
|
||||
console.log(`\n${chalk.cyan.bold.underline('typescript-starter')}\n`);
|
||||
}
|
||||
const { definitions, description, name, runner } = await collectOptions();
|
||||
const commitHash = await cloneRepo(name);
|
||||
const nodeDefinitions =
|
||||
definitions === TypeDefinitions.Node ||
|
||||
definitions === TypeDefinitions.NodeAndDOM
|
||||
? true
|
||||
: false;
|
||||
const domDefinitions =
|
||||
definitions === TypeDefinitions.DOM ||
|
||||
definitions === TypeDefinitions.NodeAndDOM
|
||||
? true
|
||||
: false;
|
||||
console.log(`${chalk.dim(`Cloned at commit:${commitHash}`)}\n`);
|
||||
|
||||
const { gitName, gitEmail } = await getUserInfo();
|
||||
const username = await githubUsername(gitEmail).catch(err => {
|
||||
// if username isn't found, just return a placeholder
|
||||
return 'YOUR_USER_NAME';
|
||||
});
|
||||
|
||||
const spinner1 = ora('Updating package.json').start();
|
||||
const projectPath = join(process.cwd(), name);
|
||||
const pkgPath = join(projectPath, 'package.json');
|
||||
const pkg = readPackageJson(pkgPath);
|
||||
pkg.name = name;
|
||||
pkg.version = '1.0.0';
|
||||
pkg.description = description;
|
||||
delete pkg.bin;
|
||||
pkg.repository = `https://github.com/${username}/${name}`;
|
||||
pkg.keywords = [];
|
||||
|
||||
// dependencies to retain for Node.js applications
|
||||
const nodeKeptDeps = ['sha.js'];
|
||||
pkg.dependencies = nodeDefinitions
|
||||
? nodeKeptDeps.reduce((all, dep) => {
|
||||
all[dep] = pkg.dependencies[dep];
|
||||
return all;
|
||||
}, {})
|
||||
: {};
|
||||
|
||||
if (runner === Runner.Yarn) {
|
||||
pkg.scripts.preinstall = `node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('${name} must be installed with Yarn: https://yarnpkg.com/')\"`;
|
||||
}
|
||||
|
||||
writePackageJson(pkgPath, pkg);
|
||||
spinner1.succeed();
|
||||
|
||||
const spinner2 = ora('Updating .gitignore').start();
|
||||
if (runner === Runner.Yarn) {
|
||||
await replace({
|
||||
files: join(projectPath, '.gitignore'),
|
||||
from: 'yarn.lock',
|
||||
to: 'package-lock.json'
|
||||
});
|
||||
}
|
||||
spinner2.succeed();
|
||||
|
||||
const spinner3 = ora('Updating .npmignore').start();
|
||||
await replace({
|
||||
files: join(projectPath, '.npmignore'),
|
||||
from: 'examples\n',
|
||||
to: ''
|
||||
});
|
||||
spinner3.succeed();
|
||||
|
||||
const spinner4 = ora('Updating LICENSE').start();
|
||||
await replace({
|
||||
files: join(projectPath, 'LICENSE'),
|
||||
from: 'Jason Dreyzehner',
|
||||
to: gitName
|
||||
});
|
||||
spinner4.succeed();
|
||||
|
||||
const spinner5 = ora('Deleting unnecessary files').start();
|
||||
await trash([
|
||||
join(projectPath, 'examples'),
|
||||
join(projectPath, 'CHANGELOG.md'),
|
||||
join(projectPath, 'README.md'),
|
||||
join(projectPath, 'package-lock.json'),
|
||||
join(projectPath, 'src', 'typescript-starter.ts')
|
||||
]);
|
||||
spinner5.succeed();
|
||||
|
||||
const spinner6 = ora('Updating README.md').start();
|
||||
renameSync(
|
||||
join(projectPath, 'README-starter.md'),
|
||||
join(projectPath, 'README.md')
|
||||
);
|
||||
await replace({
|
||||
files: join(projectPath, 'README.md'),
|
||||
from: 'package-name',
|
||||
to: name
|
||||
});
|
||||
spinner6.succeed();
|
||||
|
||||
if (!domDefinitions) {
|
||||
const spinner6A = ora(`tsconfig: don't include "dom" lib`).start();
|
||||
await replace({
|
||||
files: join(projectPath, 'tsconfig.json'),
|
||||
from: '"lib": ["es2017", "dom"]',
|
||||
to: '"lib": ["es2017"]'
|
||||
});
|
||||
spinner6A.succeed();
|
||||
}
|
||||
|
||||
if (!nodeDefinitions) {
|
||||
const spinner6B = ora(`tsconfig: don't include "node" types`).start();
|
||||
await replace({
|
||||
files: join(projectPath, 'tsconfig.json'),
|
||||
from: '"types": ["node"]',
|
||||
to: '"types": []'
|
||||
});
|
||||
await replace({
|
||||
files: join(projectPath, 'src', 'index.ts'),
|
||||
from: `export * from './lib/hash';\n`,
|
||||
to: ''
|
||||
});
|
||||
await trash([
|
||||
join(projectPath, 'src', 'lib', 'hash.ts'),
|
||||
join(projectPath, 'src', 'lib', 'hash.spec.ts')
|
||||
join(projectPath, 'src', 'lib', 'async.ts'),
|
||||
join(projectPath, 'src', 'lib', 'async.spec.ts')
|
||||
]);
|
||||
spinner6B.succeed();
|
||||
}
|
||||
|
||||
console.log(`\n${chalk.green.bold('Installing dependencies...')}\n`);
|
||||
await install(runner, projectPath);
|
||||
console.log();
|
||||
|
||||
const spinner7 = ora(`Initializing git repository`).start();
|
||||
await initialCommit(commitHash, projectPath);
|
||||
spinner7.succeed();
|
||||
|
||||
console.log(`\n${chalk.blue.bold(`Created ${name} 🎉`)}\n`);
|
||||
// TODO:
|
||||
// readme: add how to work on this file
|
||||
// `npm link`, `npm run watch`, and in a test directory `TYPESCRIPT_STARTER_REPO_URL='/local/path/to/typescript-starter' typescript-starter`
|
||||
})();
|
||||
|
||||
async function collectOptions() {
|
||||
const packageName = {
|
||||
filter: (answer: string) => answer.trim(),
|
||||
message: 'Enter the new package name:',
|
||||
name: 'name',
|
||||
type: 'input',
|
||||
validate: (answer: string) =>
|
||||
!/^\s*[a-zA-Z]+(-[a-zA-Z]+)*\s*$/.test(answer)
|
||||
? 'Name should be in-kebab-case'
|
||||
: existsSync(answer)
|
||||
? `The ${answer} path already exists in this directory.`
|
||||
: true
|
||||
};
|
||||
|
||||
const node = 'Node.js application';
|
||||
const lib = 'Javascript library';
|
||||
const projectType = {
|
||||
choices: [node, lib],
|
||||
filter: val => (val === node ? ProjectType.Node : ProjectType.Library),
|
||||
message: 'What are you making?',
|
||||
name: 'type',
|
||||
type: 'list'
|
||||
};
|
||||
|
||||
const packageDescription = {
|
||||
filter: answer => answer.trim(),
|
||||
message: 'Enter the package description:',
|
||||
name: 'description',
|
||||
type: 'input',
|
||||
validate: (answer: string) => answer.length > 0
|
||||
};
|
||||
|
||||
const runnerChoices = ['npm', 'yarn'];
|
||||
const runner = {
|
||||
choices: runnerChoices,
|
||||
filter: val => runnerChoices.indexOf(val),
|
||||
message: 'Will this project use npm or yarn?',
|
||||
name: 'runner',
|
||||
type: 'list'
|
||||
};
|
||||
|
||||
const typeDefChoices = [
|
||||
`None — the library won't use any globals or modules from Node.js or the DOM`,
|
||||
`Node.js — parts of the library require access to Node.js globals or built-in modules`,
|
||||
`DOM — parts of the library require access to the Document Object Model (DOM)`,
|
||||
`Both Node.js and DOM — some parts of the library require Node.js, other parts require DOM access`
|
||||
];
|
||||
const typeDefs = {
|
||||
choices: typeDefChoices,
|
||||
filter: val => typeDefChoices.indexOf(val),
|
||||
message: 'Which global type definitions do you want to include?',
|
||||
name: 'definitions',
|
||||
type: 'list',
|
||||
when: answers => answers.type === ProjectType.Library
|
||||
};
|
||||
|
||||
return (prompt([
|
||||
packageName,
|
||||
projectType,
|
||||
packageDescription,
|
||||
runner,
|
||||
typeDefs
|
||||
]) as Promise<{
|
||||
name: string;
|
||||
type: ProjectType;
|
||||
description: string;
|
||||
runner: Runner;
|
||||
definitions?: TypeDefinitions;
|
||||
}>).then(answers => {
|
||||
return {
|
||||
definitions:
|
||||
answers.definitions === undefined
|
||||
? TypeDefinitions.Node
|
||||
: answers.definitions,
|
||||
description: answers.description,
|
||||
name: answers.name,
|
||||
runner: answers.runner,
|
||||
type: answers.type
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function cloneRepo(dir: string) {
|
||||
console.log();
|
||||
const cwd = process.cwd();
|
||||
const projectDir = join(cwd, dir);
|
||||
const gitHistoryDir = join(projectDir, '.git');
|
||||
const clone = spawn.sync('git', ['clone', '--depth=1', repo, dir], {
|
||||
cwd,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
if (clone.error && clone.error.code === 'ENOENT') {
|
||||
console.error(
|
||||
chalk.red(
|
||||
`\nGit is not installed on your PATH. Please install Git and try again.`
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
chalk.dim(
|
||||
`\nFor more information, visit: ${chalk.bold.underline(
|
||||
'https://git-scm.com/book/en/v2/Getting-Started-Installing-Git'
|
||||
)}\n`
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
} else if (clone.status !== 0) {
|
||||
abort(chalk.red(`Git clone failed. Correct the issue and try again.`));
|
||||
}
|
||||
console.log();
|
||||
const revParse = spawn.sync('git', ['rev-parse', 'HEAD'], {
|
||||
cwd: projectDir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', process.stderr]
|
||||
});
|
||||
if (revParse.status !== 0) {
|
||||
abort(chalk.red(`Git rev-parse failed.`));
|
||||
}
|
||||
const commitHash = revParse.stdout.trim();
|
||||
await trash([gitHistoryDir]);
|
||||
return commitHash;
|
||||
}
|
||||
|
||||
async function getUserInfo() {
|
||||
const gitNameProc = spawn.sync('git', ['config', 'user.name'], {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', process.stderr]
|
||||
});
|
||||
if (gitNameProc.status !== 0) {
|
||||
abort(chalk.red(`Couldn't get name from Git config.`));
|
||||
}
|
||||
const gitName = gitNameProc.stdout.trim();
|
||||
const gitEmailProc = spawn.sync('git', ['config', 'user.email'], {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', process.stderr]
|
||||
});
|
||||
if (gitEmailProc.status !== 0) {
|
||||
abort(chalk.red(`Couldn't get email from Git config.`));
|
||||
}
|
||||
const gitEmail = gitEmailProc.stdout.trim();
|
||||
return {
|
||||
gitEmail,
|
||||
gitName
|
||||
};
|
||||
}
|
||||
|
||||
function readPackageJson(path: string) {
|
||||
return JSON.parse(readFileSync(path, 'utf8'));
|
||||
}
|
||||
|
||||
function writePackageJson(path: string, pkg: any) {
|
||||
// write using the same format as npm:
|
||||
// https://github.com/npm/npm/blob/latest/lib/install/update-package-json.js#L48
|
||||
const stringified = JSON.stringify(pkg, null, 2) + '\n';
|
||||
writeFileSync(path, stringified);
|
||||
}
|
||||
|
||||
async function install(runner: Runner, projectDir: string) {
|
||||
const opts = {
|
||||
cwd: projectDir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'inherit', process.stderr]
|
||||
};
|
||||
const runnerProc =
|
||||
runner === Runner.Npm
|
||||
? spawn.sync('npm', ['install'], opts)
|
||||
: spawn.sync('yarn', opts);
|
||||
|
||||
if (runnerProc.status !== 0) {
|
||||
abort(chalk.red(`Installation failed. You'll need to install manually.`));
|
||||
}
|
||||
}
|
||||
|
||||
async function initialCommit(hash: string, projectDir: string) {
|
||||
const opts = {
|
||||
cwd: projectDir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'ignore', process.stderr]
|
||||
};
|
||||
const init = spawn.sync('git', ['init'], opts);
|
||||
if (init.status !== 0) {
|
||||
abort(chalk.red(`Git repo initialization failed.`));
|
||||
}
|
||||
const add = spawn.sync('git', ['add', '-A'], opts);
|
||||
if (add.status !== 0) {
|
||||
abort(chalk.red(`Could not stage initial commit.`));
|
||||
}
|
||||
const commit = spawn.sync(
|
||||
'git',
|
||||
[
|
||||
'commit',
|
||||
'-m',
|
||||
`Initial commit\n\nCreated with typescript-starter@${hash}`
|
||||
],
|
||||
opts
|
||||
);
|
||||
if (commit.status !== 0) {
|
||||
abort(chalk.red(`Initial commit failed.`));
|
||||
}
|
||||
}
|
||||
|
||||
function abort(msg: string) {
|
||||
console.error(`\n${msg}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -1,33 +1,43 @@
|
||||
{
|
||||
"extends": "./config/tsconfig.flexible", // also available: "./config/tsconfig.strict"
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"target": "es2017",
|
||||
"outDir": "build/main",
|
||||
"rootDir": "src",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"importHelpers": true,
|
||||
"inlineSourceMap": true,
|
||||
"listFiles": false,
|
||||
"traceResolution": false,
|
||||
"pretty": true,
|
||||
"lib" : [
|
||||
"es6"
|
||||
],
|
||||
"types" : [
|
||||
"node"
|
||||
],
|
||||
"baseUrl": ".", // required for "paths"
|
||||
"paths": {
|
||||
"typescript-starter": ["src/index.ts"] // write tests without relative paths
|
||||
}
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
|
||||
// "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
// "strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
|
||||
// "strictNullChecks": true /* Enable strict null checks. */,
|
||||
// "strictFunctionTypes": true /* Enable strict checking of function types. */,
|
||||
// "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
|
||||
// "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
|
||||
// "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true /* Report errors on unused locals. */,
|
||||
// "noUnusedParameters": true /* Report errors on unused parameters. */,
|
||||
// "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
|
||||
// "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
|
||||
|
||||
/* Debugging Options */
|
||||
"traceResolution": false /* Report module resolution log messages. */,
|
||||
"listEmittedFiles": false /* Print names of generated files part of the compilation. */,
|
||||
"listFiles": false /* Print names of files part of the compilation. */,
|
||||
"pretty": true /* Stylize errors and messages using color and context. */,
|
||||
|
||||
"lib": ["es2017", "dom"],
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules/**"
|
||||
],
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules/**"],
|
||||
"compileOnSave": false
|
||||
}
|
||||
|
||||
7
tsconfig.module.json
Normal file
7
tsconfig.module.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "build/module",
|
||||
"module": "ESNext"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
{
|
||||
"extends": "tslint-config-standard"
|
||||
"extends": ["tslint:latest", "tslint-config-prettier"],
|
||||
"rules": {
|
||||
"interface-name": [true, "never-prefix"],
|
||||
// TODO: allow devDependencies only in **/*.spec.ts files:
|
||||
// https://github.com/palantir/tslint/pull/3708
|
||||
"no-implicit-dependencies": [true, "dev"]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user