1
0
mirror of synced 2025-11-08 12:57:47 +00:00

feat(CLI): create CLI, begin work on v2

This commit is contained in:
Jason Dreyzehner
2018-02-22 02:20:38 -05:00
parent a211a54c1f
commit 76336c88db
25 changed files with 11069 additions and 5189 deletions

2
.gitignore vendored
View File

@@ -6,3 +6,5 @@ src/**.js
coverage
.nyc_output
*.log
yarn.lock

View File

@@ -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
View File

@@ -0,0 +1 @@
package.json

View File

@@ -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
View File

@@ -0,0 +1 @@
# package-name

View File

@@ -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))
});
}

View File

@@ -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()
]
}

View File

@@ -1,9 +0,0 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "../../build/module",
"rootDir": "../../src",
"module": "es6",
"declaration": false
}
}

View File

@@ -1,5 +0,0 @@
{
"compilerOptions": {
"strictNullChecks": true
}
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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
}
}

View File

@@ -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()
}

View File

@@ -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';

View File

@@ -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']);
});

View File

@@ -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];
}

View File

@@ -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'
);

View File

@@ -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');
}

View File

@@ -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);
});

View File

@@ -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
View 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);
}

View File

@@ -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
View File

@@ -0,0 +1,7 @@
{
"extends": "./tsconfig",
"compilerOptions": {
"outDir": "build/module",
"module": "ESNext"
}
}

View File

@@ -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"]
}
}

4911
yarn.lock

File diff suppressed because it is too large Load Diff