feat: TS v4, tslint -> eslint, add cspell support (#240)
Update all dependencies, migrate from tslint (deprecated) to typescript-eslint, and add support for cspell. BREAKING CHANGE: migrated from tslint (deprecated) to eslint.
This commit is contained in:
@@ -4,7 +4,6 @@ jobs:
|
||||
'node-10':
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
working_directory: ~/typescript-starter
|
||||
steps:
|
||||
- checkout
|
||||
# Download and cache dependencies
|
||||
@@ -21,10 +20,26 @@ jobs:
|
||||
- run: npm test
|
||||
- run: npm run cov:send
|
||||
- run: npm run cov:check
|
||||
'node-12':
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "package.json" }}
|
||||
- v1-dependencies-
|
||||
- run: npm install
|
||||
- save_cache:
|
||||
paths:
|
||||
- node_modules
|
||||
key: v1-dependencies-{{ checksum "package.json" }}
|
||||
- run: npm test
|
||||
- run: npm run cov:send
|
||||
- run: npm run cov:check
|
||||
'node-latest':
|
||||
docker:
|
||||
- image: circleci/node:latest
|
||||
working_directory: ~/typescript-starter
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
@@ -45,4 +60,5 @@ workflows:
|
||||
build:
|
||||
jobs:
|
||||
- 'node-10'
|
||||
- 'node-12'
|
||||
- 'node-latest'
|
||||
|
||||
33
.cspell.json
Normal file
33
.cspell.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"version": "0.1",
|
||||
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/master/cspell.schema.json",
|
||||
"language": "en",
|
||||
"words": [
|
||||
"bitjson",
|
||||
"bitauth",
|
||||
"cimg",
|
||||
"circleci",
|
||||
"codecov",
|
||||
"commitlint",
|
||||
"dependabot",
|
||||
"editorconfig",
|
||||
"execa",
|
||||
"exponentiate",
|
||||
"globby",
|
||||
"libauth",
|
||||
"mkdir",
|
||||
"prettierignore",
|
||||
"sandboxed",
|
||||
"transpiled",
|
||||
"typedoc",
|
||||
"untracked"
|
||||
],
|
||||
"flagWords": [],
|
||||
"ignorePaths": [
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"yarn.lock",
|
||||
"tsconfig.json",
|
||||
"node_modules/**"
|
||||
]
|
||||
}
|
||||
34
.eslintrc.json
Normal file
34
.eslintrc.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": { "project": "./tsconfig.json" },
|
||||
"env": { "es6": true },
|
||||
"ignorePatterns": ["node_modules", "build", "coverage"],
|
||||
"plugins": ["import", "eslint-comments", "functional"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:eslint-comments/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:import/typescript",
|
||||
"plugin:functional/lite",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"globals": { "BigInt": true, "console": true, "WebAssembly": true },
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"eslint-comments/disable-enable-pair": [
|
||||
"error",
|
||||
{ "allowWholeFile": true }
|
||||
],
|
||||
"eslint-comments/no-unused-disable": "error",
|
||||
"import/order": [
|
||||
"error",
|
||||
{ "newlines-between": "always", "alphabetize": { "order": "asc" } }
|
||||
],
|
||||
"sort-imports": [
|
||||
"error",
|
||||
{ "ignoreDeclarationSort": true, "ignoreCase": true }
|
||||
]
|
||||
}
|
||||
}
|
||||
16
.github/ISSUE_TEMPLATE.md
vendored
16
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,11 +1,9 @@
|
||||
* **I'm submitting a ...**
|
||||
[ ] bug report
|
||||
[ ] feature request
|
||||
[ ] question about the decisions made in the repository
|
||||
[ ] question about how to use this project
|
||||
- **I'm submitting a ...**
|
||||
[ ] bug report
|
||||
[ ] feature request
|
||||
[ ] question about the decisions made in the repository
|
||||
[ ] question about how to use this project
|
||||
|
||||
* **Summary**
|
||||
- **Summary**
|
||||
|
||||
|
||||
|
||||
* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
|
||||
- **Other information** (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
|
||||
|
||||
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,13 +1,7 @@
|
||||
* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
|
||||
- **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
|
||||
|
||||
- **What is the current behavior?** (You can also link to an open issue here)
|
||||
|
||||
- **What is the new behavior (if this is a feature change)?**
|
||||
|
||||
* **What is the current behavior?** (You can also link to an open issue here)
|
||||
|
||||
|
||||
|
||||
* **What is the new behavior (if this is a feature change)?**
|
||||
|
||||
|
||||
|
||||
* **Other information**:
|
||||
- **Other information**:
|
||||
|
||||
14
.npmignore
14
.npmignore
@@ -1,14 +0,0 @@
|
||||
src
|
||||
test
|
||||
tsconfig.json
|
||||
tsconfig.module.json
|
||||
tslint.json
|
||||
.travis.yml
|
||||
.github
|
||||
.prettierignore
|
||||
.vscode
|
||||
build/docs
|
||||
**/*.spec.*
|
||||
coverage
|
||||
.nyc_output
|
||||
*.log
|
||||
52
.vscode/debug-ts.js
vendored
52
.vscode/debug-ts.js
vendored
@@ -1,52 +0,0 @@
|
||||
'use strict';
|
||||
const meow = require('meow');
|
||||
const path = require('path');
|
||||
|
||||
const tsFile = getTSFile();
|
||||
const jsFile = TS2JS(tsFile);
|
||||
|
||||
replaceCLIArg(tsFile, jsFile);
|
||||
|
||||
// Ava debugger
|
||||
require('ava/profile');
|
||||
|
||||
/**
|
||||
* get ts file path from CLI args
|
||||
*
|
||||
* @return string path
|
||||
*/
|
||||
function getTSFile() {
|
||||
const cli = meow();
|
||||
return cli.input[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* get associated compiled js file path
|
||||
*
|
||||
* @param tsFile path
|
||||
* @return string path
|
||||
*/
|
||||
function TS2JS(tsFile) {
|
||||
const srcFolder = path.join(__dirname, '..', 'src');
|
||||
const distFolder = path.join(__dirname, '..', 'build', 'main');
|
||||
|
||||
const tsPathObj = path.parse(tsFile);
|
||||
|
||||
return path.format({
|
||||
dir: tsPathObj.dir.replace(srcFolder, distFolder),
|
||||
ext: '.js',
|
||||
name: tsPathObj.name,
|
||||
root: tsPathObj.root
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* replace a value in CLI args
|
||||
*
|
||||
* @param search value to search
|
||||
* @param replace value to replace
|
||||
* @return void
|
||||
*/
|
||||
function replaceCLIArg(search, replace) {
|
||||
process.argv[process.argv.indexOf(search)] = replace;
|
||||
}
|
||||
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"eamodio.gitlens",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
]
|
||||
}
|
||||
100
.vscode/launch.json
vendored
100
.vscode/launch.json
vendored
@@ -1,84 +1,44 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Project",
|
||||
// we test in `build` to make cleanup fast and easy
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
// Replace this with your project root. If there are multiple, you can
|
||||
// automatically run the currently visible file with: "program": ${file}"
|
||||
"program": "${workspaceFolder}/src/cli/cli.ts",
|
||||
// "args": ["--no-install"],
|
||||
"outFiles": ["${workspaceFolder}/build/main/**/*.js"],
|
||||
"skipFiles": [
|
||||
"<node_internals>/**/*.js",
|
||||
"${workspaceFolder}/node_modules/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "npm: build",
|
||||
"stopOnEntry": true,
|
||||
"smartStep": true,
|
||||
"runtimeArgs": ["--nolazy"],
|
||||
"env": {
|
||||
"TYPESCRIPT_STARTER_REPO_URL": "${workspaceFolder}"
|
||||
},
|
||||
"console": "externalTerminal"
|
||||
},
|
||||
"configurations": [
|
||||
// To debug, make sure a *.spec.ts file is active in the editor, then run a configuration
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Spec",
|
||||
"program": "${workspaceRoot}/.vscode/debug-ts.js",
|
||||
"args": ["${file}"],
|
||||
"name": "Debug Active Spec",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava",
|
||||
"runtimeArgs": ["debug", "--break", "--serial", "${file}"],
|
||||
"port": 9229,
|
||||
"outputCapture": "std",
|
||||
"skipFiles": ["<node_internals>/**/*.js"],
|
||||
// Consider using `npm run watch` or `yarn watch` for faster debugging
|
||||
// "preLaunchTask": "npm: build",
|
||||
// "smartStep": true,
|
||||
"runtimeArgs": ["--nolazy"]
|
||||
"preLaunchTask": "npm: build"
|
||||
// "smartStep": true
|
||||
},
|
||||
{
|
||||
// Use this one if you're already running `yarn watch`
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Active Spec (no build)",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava",
|
||||
"runtimeArgs": ["debug", "--break", "--serial", "${file}"],
|
||||
"port": 9229,
|
||||
"outputCapture": "std",
|
||||
"skipFiles": ["<node_internals>/**/*.js"]
|
||||
// "smartStep": true
|
||||
},
|
||||
// --- cut here ---
|
||||
// TODO: Simpler test debugging option. Discussion:
|
||||
// https://github.com/avajs/ava/issues/1505#issuecomment-370654427
|
||||
// {
|
||||
// "type": "node",
|
||||
// "request": "launch",
|
||||
// "name": "Debug Visible Compiled Spec",
|
||||
// "program": "${file}",
|
||||
// "outFiles": ["${workspaceFolder}/build/main/**/*.js"],
|
||||
// "skipFiles": ["<node_internals>/**/*.js"],
|
||||
// // Consider using `npm run watch` or `yarn watch` for faster debugging
|
||||
// // "preLaunchTask": "npm: build",
|
||||
// // "stopOnEntry": true,
|
||||
// // "smartStep": true,
|
||||
// "runtimeArgs": ["--nolazy"],
|
||||
// "env": {
|
||||
// "AVA_DEBUG_MODE": "1"
|
||||
// }
|
||||
// }
|
||||
// CLI:
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug CLI Unit Tests",
|
||||
"program": "${workspaceFolder}/node_modules/ava/profile.js",
|
||||
"args": ["${workspaceFolder}/build/main/cli/tests/cli.unit.spec.js"],
|
||||
"skipFiles": ["<node_internals>/**/*.js"],
|
||||
// "preLaunchTask": "npm: build",
|
||||
// "smartStep": true,
|
||||
"runtimeArgs": ["--nolazy"]
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug CLI Integration Tests",
|
||||
"program": "${workspaceFolder}/node_modules/ava/profile.js",
|
||||
"args": [
|
||||
"${workspaceFolder}/build/main/cli/tests/cli.integration.spec.js"
|
||||
],
|
||||
"skipFiles": ["<node_internals>/**/*.js"],
|
||||
// "preLaunchTask": "npm: build",
|
||||
// "smartStep": true,
|
||||
"runtimeArgs": ["--nolazy"]
|
||||
"name": "Try CLI",
|
||||
"program": "${workspaceFolder}/bin/typescript-starter",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"env": {
|
||||
"TYPESCRIPT_STARTER_REPO_URL": "${workspaceFolder}"
|
||||
},
|
||||
"args": ["debug"],
|
||||
"skipFiles": ["<node_internals>/**/*.js"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
// "typescript.implementationsCodeLens.enabled": true
|
||||
// "typescript.referencesCodeLens.enabled": true
|
||||
"cSpell.userWords": [], // only use words from .cspell.json
|
||||
"cSpell.enabled": true,
|
||||
"editor.formatOnSave": true,
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
|
||||
109
README.md
109
README.md
@@ -25,20 +25,20 @@ The interactive CLI will help you create and configure your project automaticall
|
||||
|
||||
# Features
|
||||
|
||||
* Write **standard, future javascript** – with stable ESNext features – today ([stage 3](https://github.com/tc39/proposals) or [finished](https://github.com/tc39/proposals/blob/master/finished-proposals.md) features)
|
||||
* [Optionally use typescript](https://medium.freecodecamp.org/its-time-to-give-typescript-another-chance-2caaf7fabe61) to improve tooling, linting, and documentation generation
|
||||
* Export as a [javascript module](http://jsmodules.io/), making your work **fully tree-shakable** for consumers capable of using [es6 imports](https://github.com/rollup/rollup/wiki/pkg.module) (like [Rollup](http://rollupjs.org/), [Webpack](https://webpack.js.org/), or [Parcel](https://parceljs.org/))
|
||||
* Export type declarations to improve your downstream development experience
|
||||
* Backwards compatibility for Node.js-style (CommonJS) imports
|
||||
* Both strict and flexible [typescript configurations](config/tsconfig.json) available
|
||||
- Write **standard, future javascript** – with stable ESNext features – today ([stage 3](https://github.com/tc39/proposals) or [finished](https://github.com/tc39/proposals/blob/master/finished-proposals.md) features)
|
||||
- [Optionally use typescript](https://medium.freecodecamp.org/its-time-to-give-typescript-another-chance-2caaf7fabe61) to improve tooling, linting, and documentation generation
|
||||
- Export as a [javascript module](http://jsmodules.io/), making your work **fully tree-shakable** for consumers capable of using [es6 imports](https://github.com/rollup/rollup/wiki/pkg.module) (like [Rollup](http://rollupjs.org/), [Webpack](https://webpack.js.org/), or [Parcel](https://parceljs.org/))
|
||||
- Export type declarations to improve your downstream development experience
|
||||
- Backwards compatibility for Node.js-style (CommonJS) imports
|
||||
- Both strict and flexible [typescript configurations](config/tsconfig.json) available
|
||||
|
||||
So we can have nice things:
|
||||
|
||||
* Generate API documentation (HTML or JSON) [without a mess of JSDoc tags](https://blog.cloudflare.com/generating-documentation-for-typescript-projects/) to maintain
|
||||
* Collocated, atomic, concurrent unit tests with [AVA](https://github.com/avajs/ava)
|
||||
* Source-mapped code coverage reports with [nyc](https://github.com/istanbuljs/nyc)
|
||||
* Configurable code coverage testing (for continuous integration)
|
||||
* Automatic linting and formatting using [TSLint](https://github.com/palantir/tslint) and [Prettier](https://prettier.io/)
|
||||
- Generate API documentation (HTML or JSON) [without a mess of JSDoc tags](https://blog.cloudflare.com/generating-documentation-for-typescript-projects/) to maintain
|
||||
- Collocated, atomic, concurrent unit tests with [AVA](https://github.com/avajs/ava)
|
||||
- Source-mapped code coverage reports with [nyc](https://github.com/istanbuljs/nyc)
|
||||
- Configurable code coverage testing (for continuous integration)
|
||||
- Automatic linting and formatting using [`typescript-eslint`](https://github.com/typescript-eslint/typescript-eslint) and [Prettier](https://prettier.io/)
|
||||
|
||||
## But first, a good editor
|
||||
|
||||
@@ -46,7 +46,7 @@ Before you start, consider using an [editor with good typescript support](https:
|
||||
|
||||
[VS Code](https://code.visualstudio.com/) (below) is a popular option. Editors with typescript support can provide helpful autocomplete, inline documentation, and code refactoring features.
|
||||
|
||||
Also consider installing editor extensions for [TSLint](https://github.com/Microsoft/vscode-tslint) and [Prettier](https://github.com/prettier/prettier-vscode). These extensions automatically format your code each time you save, and may quickly become invaluable.
|
||||
Also consider installing editor extensions for [ESLint](https://github.com/Microsoft/vscode-eslint) and [Prettier](https://github.com/prettier/prettier-vscode). These extensions automatically format your code each time you save, and may quickly become invaluable.
|
||||
|
||||
<p align="center">
|
||||
<img alt="Typescript Editor Support – vscode" width="600" src="https://cloud.githubusercontent.com/assets/904007/23042221/ccebd534-f465-11e6-838d-e2449899282c.png">
|
||||
@@ -79,6 +79,14 @@ To make getting started easier, the default `tsconfig.json` is using a very flex
|
||||
|
||||
To enable additional Typescript type checking features (a good idea for mission-critical or large projects), review the commented-out lines in your [typescript compiler options](./tsconfig.json).
|
||||
|
||||
## Auto-fix and format project
|
||||
|
||||
To automatically fix `eslint` and `prettier` formatting issues, run:
|
||||
|
||||
```
|
||||
npm run fix
|
||||
```
|
||||
|
||||
## View test coverage
|
||||
|
||||
To generate and view test coverage, run:
|
||||
@@ -154,11 +162,11 @@ npm run prepare-release
|
||||
|
||||
This command runs the following tasks:
|
||||
|
||||
* `reset`: cleans the repo by removing all untracked files and resetting `--hard` to the latest commit. (**Note: this could be destructive.**)
|
||||
* `test`: build and fully test the project
|
||||
* `docs:html`: generate the latest version of the documentation
|
||||
* `docs:publish`: publish the documentation to GitHub Pages
|
||||
* `version`: bump package.json version, update CHANGELOG.md, and git tag the release
|
||||
- `hard-reset`: cleans the repo by removing all untracked files and resetting `--hard` to the latest commit. (**Note: this could be destructive.**)
|
||||
- `test`: build and fully test the project
|
||||
- `docs:html`: generate the latest version of the documentation
|
||||
- `docs:publish`: publish the documentation to GitHub Pages
|
||||
- `version`: bump package.json version, update CHANGELOG.md, and git tag the release
|
||||
|
||||
When the script finishes, it will log the final command needed to push the release commit to the repo and publish the package on the `npm` registry:
|
||||
|
||||
@@ -174,7 +182,7 @@ You can also prepare a non-standard release:
|
||||
# Or a non-standard release:
|
||||
|
||||
# Reset the repo to the latest commit and build everything
|
||||
npm run reset && npm run test && npm run cov:check && npm run doc:html
|
||||
npm run hard-reset && npm run test && npm run cov:check && npm run doc:html
|
||||
|
||||
# Then version it with standard-version options. e.g.:
|
||||
# don't bump package.json version
|
||||
@@ -192,39 +200,6 @@ npm run version -- --first-release
|
||||
npm run doc:publish
|
||||
```
|
||||
|
||||
## Describe package scripts
|
||||
|
||||
You can run the `describe` script for a description of the available package scripts.
|
||||
|
||||
```
|
||||
npm run describe
|
||||
|
||||
> npm-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
|
||||
version:
|
||||
Bump package.json version, update CHANGELOG.md, tag release
|
||||
reset:
|
||||
Delete all untracked files and reset the repo to the last commit
|
||||
prepare-release:
|
||||
One-step: clean, build, test, publish docs, and prep a release
|
||||
```
|
||||
|
||||
# FAQs
|
||||
|
||||
## Why are there two builds? (`main` and `module`)
|
||||
@@ -237,13 +212,13 @@ Because Node.js LTS releases do not yet support the es6 module system, some proj
|
||||
|
||||
By convention, sample tests in this project are adjacent to the files they test.
|
||||
|
||||
* Such tests are easy to find.
|
||||
* You see at a glance if a part of your project lacks tests.
|
||||
* Nearby tests can reveal how a part works in context.
|
||||
* When you move the source (inevitable), you remember to move the test.
|
||||
* When you rename the source file (inevitable), you remember to rename the test file.
|
||||
- Such tests are easy to find.
|
||||
- You see at a glance if a part of your project lacks tests.
|
||||
- Nearby tests can reveal how a part works in context.
|
||||
- When you move the source (inevitable), you remember to move the test.
|
||||
- When you rename the source file (inevitable), you remember to rename the test file.
|
||||
|
||||
(Bullet points taken from [Angular's Testing Guide](https://angular.io/guide/testing#q-spec-file-location).)
|
||||
(Bullet points taken from [the Angular Testing Guide](https://angular.io/guide/testing#q-spec-file-location).)
|
||||
|
||||
## Can I move the tests?
|
||||
|
||||
@@ -309,15 +284,18 @@ console.log(sha256('test'));
|
||||
|
||||
This project uses [standard-version](https://github.com/conventional-changelog/standard-version) to automatically update the changelog based on commit messages since the last release. To do this, each relevant commit must be properly formatted.
|
||||
|
||||
To ensure all commits follow the proper conventions, you can use a package like [commitlint](https://github.com/marionebl/commitlint) with [Husky](https://github.com/typicode/husky). However, keep in mind that commit hooks can be confusing, especially for new contributors. They also interfere with some development tools and workflows.
|
||||
To ensure all commits follow the proper conventions, you can use a package like [`commitlint`](https://github.com/marionebl/commitlint) with [Husky](https://github.com/typicode/husky). However, keep in mind that commit hooks can be confusing, especially for new contributors. They also interfere with some development tools and workflows.
|
||||
|
||||
If your project is private, or will primarily receive contributions from long-running contributors, this may be a good fit. Otherwise, this setup may raise the barrier to one-off contributions slightly.
|
||||
|
||||
Note, as a maintainer, if you manage your project on GitHub or a similar website, you can now use the `Squash and Merge` option to add a properly formatted, descriptive commit messages when merging each pull request. This is likely to be more valuable than trying to force one-time contributors to adhere to commit conventions, since you can also maintain a more consistent language style. Because this is the best choice for the vast majority of projects, `typescript-starter` does not bundle any commit message validation.
|
||||
|
||||
# Contributing
|
||||
---
|
||||
|
||||
To work on the CLI, clone and build the repo, then use `npm link` to install it globally.
|
||||
<details>
|
||||
<summary><strong>Contributing</strong></summary>
|
||||
|
||||
Pull Requests welcome! To work on the CLI, clone and build the repo, then use `npm link` to install it globally.
|
||||
|
||||
```
|
||||
git clone https://github.com/bitjson/typescript-starter.git
|
||||
@@ -327,7 +305,7 @@ npm test
|
||||
npm link
|
||||
```
|
||||
|
||||
To manually test the CLI, you can use the `TYPESCRIPT_STARTER_REPO_URL` environment variable to test a clone from your local repo. Run `npm run watch` as you're developing, then in a different testing directory:
|
||||
To manually test the CLI, you can use the `TYPESCRIPT_STARTER_REPO_URL` environment variable to test a clone from your local repo. Run `npm run build:main -- -w` as you're developing, then in a different testing directory:
|
||||
|
||||
```
|
||||
mkdir typescript-starter-testing
|
||||
@@ -345,11 +323,4 @@ If `TYPESCRIPT_STARTER_REPO_BRANCH` is not provided, it will default to `master`
|
||||
|
||||
If you're using [VS Code](https://code.visualstudio.com/), the `Debug CLI` launch configuration also allows you to immediately build and step through execution of the CLI.
|
||||
|
||||
# In the wild
|
||||
|
||||
You can find more advanced configurations, usage examples, and inspiration from other projects using `typescript-starter`:
|
||||
|
||||
* [BitAuth](https://github.com/bitauth/) – A universal identity and authentication protocol, based on bitcoin
|
||||
* [s6: Super Simple Secrets \* Simple Secure Storage](https://gitlab.com/td7x/s6/) – An NPM library and tool to sprawl secrets with S3, ease, and encryption
|
||||
|
||||
Using `typescript-starter` for your project? Please send a pull request to add it to the list!
|
||||
</details>
|
||||
|
||||
@@ -2,6 +2,7 @@ environment:
|
||||
matrix:
|
||||
- nodejs_version: '10'
|
||||
- nodejs_version: '12'
|
||||
- nodejs_version: '14'
|
||||
|
||||
version: '{build}'
|
||||
|
||||
|
||||
9344
package-lock.json
generated
9344
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
124
package.json
124
package.json
@@ -20,7 +20,7 @@
|
||||
"typedoc",
|
||||
"ava",
|
||||
"nyc",
|
||||
"tslint",
|
||||
"eslint",
|
||||
"prettier",
|
||||
"standard-version",
|
||||
"conventional-changelog",
|
||||
@@ -29,96 +29,104 @@
|
||||
"appveyor"
|
||||
],
|
||||
"scripts": {
|
||||
"describe": "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 .",
|
||||
"fix:lint": "eslint . --ext .ts --fix",
|
||||
"test": "run-s build test:*",
|
||||
"test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different",
|
||||
"test:lint": "eslint . --ext .ts",
|
||||
"test:prettier": "prettier \"src/**/*.ts\" --list-different",
|
||||
"test:spelling": "cspell \"{README.md,.github/*.md,src/**/*.ts}\"",
|
||||
"test:unit": "nyc --silent ava",
|
||||
"watch": "run-s clean build:main && run-p \"build:main -- -w\" \"test:unit -- --watch\"",
|
||||
"cov": "run-s build test:unit cov:html && open-cli coverage/index.html",
|
||||
"cov": "run-s build test:unit cov:html cov:lcov && open-cli coverage/index.html",
|
||||
"cov:html": "nyc report --reporter=html",
|
||||
"cov:send": "nyc report --reporter=lcov && codecov",
|
||||
"cov:lcov": "nyc report --reporter=lcov",
|
||||
"cov:send": "run-s cov:lcov && codecov",
|
||||
"cov:check": "nyc report && nyc check-coverage --lines 100 --functions 100 --branches 100",
|
||||
"doc": "run-s doc:html && open-cli build/docs/index.html",
|
||||
"doc:html": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --out build/docs",
|
||||
"doc:json": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --json build/docs/typedoc.json",
|
||||
"doc:publish": "gh-pages -m \"[ci skip] Updates\" -d build/docs",
|
||||
"version": "standard-version",
|
||||
"reset": "git clean -dfx && git reset --hard && npm i",
|
||||
"reset-hard": "git clean -dfx && git reset --hard && npm i",
|
||||
"clean": "trash build test",
|
||||
"prepare-release": "run-s reset test cov:check doc:html version doc:publish"
|
||||
},
|
||||
"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",
|
||||
"version": "Bump package.json version, update CHANGELOG.md, tag release",
|
||||
"reset": "Delete all untracked files and reset the repo to the last commit",
|
||||
"prepare-release": "One-step: clean, build, test, publish docs, and prep a release"
|
||||
"prepare-release": "run-s reset-hard test cov:check doc:html version doc:publish"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9"
|
||||
"node": ">=10"
|
||||
},
|
||||
"NOTE": "These dependencies are for the CLI, and will be removed automatically.",
|
||||
"NOTE": "These dependencies are for the CLI and will not be included when you run 'npx typescript-starter'.",
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.2",
|
||||
"@bitauth/libauth": "^1.17.1",
|
||||
"chalk": "^4.1.0",
|
||||
"del": "^5.0.0",
|
||||
"execa": "^2.0.3",
|
||||
"execa": "^4.0.3",
|
||||
"github-username": "^5.0.1",
|
||||
"globby": "^10.0.1",
|
||||
"globby": "^11.0.1",
|
||||
"gradient-string": "^1.2.0",
|
||||
"inquirer": "^6.4.1",
|
||||
"meow": "^5.0.0",
|
||||
"ora": "^3.4.0",
|
||||
"project-version": "^1.2.0",
|
||||
"replace-in-file": "^4.1.1",
|
||||
"sha.js": "^2.4.11",
|
||||
"update-notifier": "^3.0.1",
|
||||
"inquirer": "^7.3.3",
|
||||
"meow": "^7.1.1",
|
||||
"ora": "^5.0.0",
|
||||
"replace-in-file": "^6.1.0",
|
||||
"update-notifier": "^4.1.1",
|
||||
"validate-npm-package-name": "^3.0.0"
|
||||
},
|
||||
"NOTE_2": "Many of these devDependencies are for the CLI and will not be included when you run 'npx typescript-starter'.",
|
||||
"devDependencies": {
|
||||
"@bitjson/npm-scripts-info": "^1.0.0",
|
||||
"@bitjson/typedoc": "^0.15.0-0",
|
||||
"@istanbuljs/nyc-config-typescript": "^0.1.3",
|
||||
"@types/inquirer": "^6.5.0",
|
||||
"@types/meow": "^5.0.0",
|
||||
"@types/nock": "^10.0.3",
|
||||
"@types/node": "^10",
|
||||
"@types/update-notifier": "^2.5.0",
|
||||
"ava": "2.2.0",
|
||||
"@ava/typescript": "^1.1.1",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@types/gradient-string": "^1.1.1",
|
||||
"@types/inquirer": "^7.3.1",
|
||||
"@types/md5-file": "^4.0.2",
|
||||
"@types/node": "^14.6.2",
|
||||
"@types/update-notifier": "^4.1.1",
|
||||
"@types/validate-npm-package-name": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.0.1",
|
||||
"@typescript-eslint/parser": "^4.0.1",
|
||||
"ava": "^3.12.1",
|
||||
"codecov": "^3.5.0",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"gh-pages": "^2.0.1",
|
||||
"md5-file": "^4.0.0",
|
||||
"nock": "^11.0.0-beta.13",
|
||||
"cspell": "^4.1.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^7.8.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-functional": "^3.0.2",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"gh-pages": "^3.1.0",
|
||||
"md5-file": "^5.0.0",
|
||||
"nock": "^13.0.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^14.1.1",
|
||||
"open-cli": "^5.0.0",
|
||||
"prettier": "^1.18.2",
|
||||
"standard-version": "^6.0.1",
|
||||
"nyc": "^15.1.0",
|
||||
"open-cli": "^6.0.1",
|
||||
"prettier": "^2.1.1",
|
||||
"standard-version": "^9.0.0",
|
||||
"trash-cli": "^3.0.0",
|
||||
"tslint": "^5.18.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-immutable": "^6.0.1",
|
||||
"typescript": "^3.5.3"
|
||||
"ts-node": "^9.0.0",
|
||||
"typedoc": "^0.19.0",
|
||||
"typescript": "^4.0.2"
|
||||
},
|
||||
"files": [
|
||||
"build/main",
|
||||
"build/module",
|
||||
"!**/*.spec.*",
|
||||
"!**/*.json",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"ava": {
|
||||
"failFast": true,
|
||||
"timeout": "20s",
|
||||
"typescript": {
|
||||
"rewritePaths": {
|
||||
"src/": "build/main/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"build/main/**/*.spec.js"
|
||||
],
|
||||
"sources": [
|
||||
"build/main/**/*.js"
|
||||
"!build/module/**"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// tslint:disable:no-console no-if-statement no-expression-statement
|
||||
|
||||
import meow from 'meow';
|
||||
import { Package, UpdateInfo, UpdateNotifier } from 'update-notifier';
|
||||
import { Package, UpdateNotifier } from 'update-notifier';
|
||||
|
||||
import { Runner, TypescriptStarterArgsOptions, validateName } from './utils';
|
||||
|
||||
export async function checkArgs(): Promise<TypescriptStarterArgsOptions> {
|
||||
@@ -23,8 +22,9 @@ export async function checkArgs(): Promise<TypescriptStarterArgsOptions> {
|
||||
--yarn use yarn (default: npm)
|
||||
|
||||
--no-circleci don't include CircleCI
|
||||
--no-cspell don't include cspell
|
||||
--no-editorconfig don't include .editorconfig
|
||||
--no-immutable don't enable tslint-immutable
|
||||
--no-functional don't enable eslint-plugin-functional
|
||||
--no-install skip yarn/npm install
|
||||
--no-vscode don't include VS Code debugging config
|
||||
|
||||
@@ -35,103 +35,107 @@ export async function checkArgs(): Promise<TypescriptStarterArgsOptions> {
|
||||
flags: {
|
||||
appveyor: {
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
circleci: {
|
||||
default: true,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
cspell: {
|
||||
default: true,
|
||||
type: 'boolean',
|
||||
},
|
||||
description: {
|
||||
alias: 'd',
|
||||
default: 'a typescript-starter project',
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
dom: {
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
editorconfig: {
|
||||
default: true,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
immutable: {
|
||||
functional: {
|
||||
default: true,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
install: {
|
||||
default: true,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
node: {
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
strict: {
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
travis: {
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
vscode: {
|
||||
default: true,
|
||||
type: 'boolean'
|
||||
type: 'boolean',
|
||||
},
|
||||
yarn: {
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
}
|
||||
}
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// immediately check for updates every time we run typescript-starter
|
||||
const updateInfo = await new Promise<UpdateInfo>((resolve, reject) => {
|
||||
const notifier = new UpdateNotifier({
|
||||
callback: (error, update) => {
|
||||
error ? reject(error) : resolve(update);
|
||||
},
|
||||
pkg: cli.pkg as Package
|
||||
});
|
||||
notifier.check();
|
||||
});
|
||||
if (updateInfo.type !== 'latest') {
|
||||
const info = await new UpdateNotifier({
|
||||
pkg: cli.pkg as Package,
|
||||
}).fetchInfo();
|
||||
if (info.type !== 'latest') {
|
||||
// eslint-disable-next-line functional/no-throw-statement
|
||||
throw new Error(`
|
||||
Your version of typescript-starter is outdated.
|
||||
Consider using 'npx typescript-starter' to always get the latest version.
|
||||
`);
|
||||
Your version of typescript-starter is outdated.
|
||||
Consider using 'npx typescript-starter' to always get the latest version.
|
||||
`);
|
||||
}
|
||||
|
||||
const version = cli.pkg.version as string;
|
||||
|
||||
const input = cli.input[0];
|
||||
if (!input) {
|
||||
// no project-name provided, return to collect options in interactive mode
|
||||
// note: we always return `install`, so --no-install always works
|
||||
// (important for test performance)
|
||||
/**
|
||||
* No project-name provided, return to collect options in interactive mode.
|
||||
* Note: we always return `install`, so --no-install always works
|
||||
* (important for test performance).
|
||||
*/
|
||||
return {
|
||||
install: cli.flags.install,
|
||||
starterVersion: cli.pkg.version
|
||||
starterVersion: version,
|
||||
};
|
||||
}
|
||||
const validOrMsg = await validateName(input);
|
||||
const validOrMsg = validateName(input);
|
||||
if (typeof validOrMsg === 'string') {
|
||||
// eslint-disable-next-line functional/no-throw-statement
|
||||
throw new Error(validOrMsg);
|
||||
}
|
||||
|
||||
return {
|
||||
appveyor: cli.flags.appveyor,
|
||||
circleci: cli.flags.circleci,
|
||||
cspell: cli.flags.cspell,
|
||||
description: cli.flags.description,
|
||||
domDefinitions: cli.flags.dom,
|
||||
editorconfig: cli.flags.editorconfig,
|
||||
immutable: cli.flags.immutable,
|
||||
functional: cli.flags.functional,
|
||||
install: cli.flags.install,
|
||||
nodeDefinitions: cli.flags.node,
|
||||
projectName: input,
|
||||
runner: cli.flags.yarn ? Runner.Yarn : Runner.Npm,
|
||||
starterVersion: cli.pkg.version,
|
||||
starterVersion: version,
|
||||
strict: cli.flags.strict,
|
||||
travis: cli.flags.travis,
|
||||
vscode: cli.flags.vscode
|
||||
vscode: cli.flags.vscode,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// tslint:disable:no-expression-statement no-console
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { checkArgs } from './args';
|
||||
import { inquire } from './inquire';
|
||||
import { addInferredOptions, LiveTasks } from './tasks';
|
||||
@@ -15,7 +15,7 @@ import { getIntro, hasCLIOptions, TypescriptStarterUserOptions } from './utils';
|
||||
console.log(getIntro(process.stdout.columns));
|
||||
return inquire();
|
||||
})()),
|
||||
...argInfo
|
||||
...argInfo,
|
||||
};
|
||||
const options = await addInferredOptions(userOptions);
|
||||
return typescriptStarter(options, LiveTasks);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DistinctQuestion, prompt } from 'inquirer';
|
||||
|
||||
import { Runner, TypescriptStarterCLIOptions, validateName } from './utils';
|
||||
|
||||
export async function inquire(): Promise<TypescriptStarterCLIOptions> {
|
||||
@@ -7,22 +8,21 @@ export async function inquire(): Promise<TypescriptStarterCLIOptions> {
|
||||
message: '📦 Enter the new package name:',
|
||||
name: 'projectName',
|
||||
type: 'input',
|
||||
validate: validateName
|
||||
validate: validateName,
|
||||
};
|
||||
|
||||
enum ProjectType {
|
||||
Node = 'node',
|
||||
Library = 'lib'
|
||||
Library = 'lib',
|
||||
}
|
||||
const projectTypeQuestion: DistinctQuestion = {
|
||||
// tslint:disable-next-line:readonly-array
|
||||
choices: [
|
||||
{ name: 'Node.js application', value: ProjectType.Node },
|
||||
{ name: 'Javascript library', value: ProjectType.Library }
|
||||
{ name: 'Javascript library', value: ProjectType.Library },
|
||||
],
|
||||
message: '🔨 What are you making?',
|
||||
name: 'type',
|
||||
type: 'list'
|
||||
type: 'list',
|
||||
};
|
||||
|
||||
const packageDescriptionQuestion: DistinctQuestion = {
|
||||
@@ -30,103 +30,106 @@ export async function inquire(): Promise<TypescriptStarterCLIOptions> {
|
||||
message: '💬 Enter the package description:',
|
||||
name: 'description',
|
||||
type: 'input',
|
||||
validate: (answer: string) => answer.length > 0
|
||||
validate: (answer: string) => answer.length > 0,
|
||||
};
|
||||
|
||||
const runnerQuestion: DistinctQuestion = {
|
||||
// tslint:disable-next-line:readonly-array
|
||||
choices: [
|
||||
{ name: 'npm', value: Runner.Npm },
|
||||
{ name: 'yarn', value: Runner.Yarn }
|
||||
{ name: 'yarn', value: Runner.Yarn },
|
||||
],
|
||||
message: '🚄 Will this project use npm or yarn?',
|
||||
name: 'runner',
|
||||
type: 'list'
|
||||
type: 'list',
|
||||
};
|
||||
|
||||
enum TypeDefinitions {
|
||||
none = 'none',
|
||||
node = 'node',
|
||||
dom = 'dom',
|
||||
nodeAndDom = 'both'
|
||||
nodeAndDom = 'both',
|
||||
}
|
||||
|
||||
const typeDefsQuestion: DistinctQuestion = {
|
||||
// tslint:disable-next-line:readonly-array
|
||||
choices: [
|
||||
{
|
||||
name: `None — the library won't use any globals or modules from Node.js or the DOM`,
|
||||
value: TypeDefinitions.none
|
||||
value: TypeDefinitions.none,
|
||||
},
|
||||
{
|
||||
name: `Node.js — parts of the library require access to Node.js globals or built-in modules`,
|
||||
value: TypeDefinitions.node
|
||||
value: TypeDefinitions.node,
|
||||
},
|
||||
{
|
||||
name: `DOM — parts of the library require access to the Document Object Model (DOM)`,
|
||||
value: TypeDefinitions.dom
|
||||
value: TypeDefinitions.dom,
|
||||
},
|
||||
{
|
||||
name: `Both Node.js and DOM — some parts of the library require Node.js, other parts require DOM access`,
|
||||
value: TypeDefinitions.nodeAndDom
|
||||
}
|
||||
value: TypeDefinitions.nodeAndDom,
|
||||
},
|
||||
],
|
||||
message: '📚 Which global type definitions do you want to include?',
|
||||
name: 'definitions',
|
||||
type: 'list',
|
||||
when: (answers: any) => answers.type === ProjectType.Library
|
||||
when: (answers) => answers.type === ProjectType.Library,
|
||||
};
|
||||
|
||||
enum Extras {
|
||||
appveyor = 'appveyor',
|
||||
circleci = 'circleci',
|
||||
cspell = 'cspell',
|
||||
editorconfig = 'editorconfig',
|
||||
immutable = 'immutable',
|
||||
functional = 'functional',
|
||||
strict = 'strict',
|
||||
travis = 'travis',
|
||||
vscode = 'vscode'
|
||||
vscode = 'vscode',
|
||||
}
|
||||
const extrasQuestion: DistinctQuestion = {
|
||||
// tslint:disable-next-line:readonly-array
|
||||
choices: [
|
||||
{
|
||||
name: 'Enable stricter type-checking',
|
||||
value: Extras.strict
|
||||
value: Extras.strict,
|
||||
},
|
||||
{
|
||||
checked: true,
|
||||
name: 'Enable tslint-immutable',
|
||||
value: Extras.immutable
|
||||
name: 'Enable eslint-plugin-functional',
|
||||
value: Extras.functional,
|
||||
},
|
||||
{
|
||||
checked: true,
|
||||
name: 'Include .editorconfig',
|
||||
value: Extras.editorconfig
|
||||
value: Extras.editorconfig,
|
||||
},
|
||||
{
|
||||
checked: true,
|
||||
name: 'Include cspell',
|
||||
value: Extras.cspell,
|
||||
},
|
||||
{
|
||||
checked: true,
|
||||
name: 'Include VS Code debugging config',
|
||||
value: Extras.vscode
|
||||
value: Extras.vscode,
|
||||
},
|
||||
{
|
||||
checked: true,
|
||||
name: 'Include CircleCI config',
|
||||
value: Extras.circleci
|
||||
value: Extras.circleci,
|
||||
},
|
||||
{
|
||||
checked: false,
|
||||
name: 'Include Appveyor (Windows-based CI) config',
|
||||
value: Extras.appveyor
|
||||
value: Extras.appveyor,
|
||||
},
|
||||
{
|
||||
checked: false,
|
||||
name: 'Include Travis CI config',
|
||||
value: Extras.travis
|
||||
}
|
||||
value: Extras.travis,
|
||||
},
|
||||
],
|
||||
message: '🚀 More fun stuff:',
|
||||
name: 'extras',
|
||||
type: 'checkbox'
|
||||
type: 'checkbox',
|
||||
};
|
||||
|
||||
return prompt([
|
||||
@@ -135,15 +138,15 @@ export async function inquire(): Promise<TypescriptStarterCLIOptions> {
|
||||
packageDescriptionQuestion,
|
||||
runnerQuestion,
|
||||
typeDefsQuestion,
|
||||
extrasQuestion
|
||||
]).then(answers => {
|
||||
extrasQuestion,
|
||||
]).then((answers) => {
|
||||
const {
|
||||
definitions,
|
||||
description,
|
||||
extras,
|
||||
projectName,
|
||||
runner,
|
||||
type
|
||||
type,
|
||||
} = answers as {
|
||||
readonly definitions?: TypeDefinitions;
|
||||
readonly description: string;
|
||||
@@ -155,6 +158,7 @@ export async function inquire(): Promise<TypescriptStarterCLIOptions> {
|
||||
return {
|
||||
appveyor: extras.includes(Extras.appveyor),
|
||||
circleci: extras.includes(Extras.circleci),
|
||||
cspell: extras.includes(Extras.cspell),
|
||||
description,
|
||||
domDefinitions: definitions
|
||||
? [TypeDefinitions.dom, TypeDefinitions.nodeAndDom].includes(
|
||||
@@ -162,7 +166,7 @@ export async function inquire(): Promise<TypescriptStarterCLIOptions> {
|
||||
)
|
||||
: false,
|
||||
editorconfig: extras.includes(Extras.editorconfig),
|
||||
immutable: extras.includes(Extras.immutable),
|
||||
functional: extras.includes(Extras.functional),
|
||||
install: true,
|
||||
nodeDefinitions: definitions
|
||||
? [TypeDefinitions.node, TypeDefinitions.nodeAndDom].includes(
|
||||
@@ -173,7 +177,7 @@ export async function inquire(): Promise<TypescriptStarterCLIOptions> {
|
||||
runner,
|
||||
strict: extras.includes(Extras.strict),
|
||||
travis: extras.includes(Extras.travis),
|
||||
vscode: extras.includes(Extras.vscode)
|
||||
vscode: extras.includes(Extras.vscode),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
// tslint:disable:no-console no-if-statement no-expression-statement
|
||||
import { join } from 'path';
|
||||
|
||||
import execa, { Options } from 'execa';
|
||||
import githubUsername from 'github-username';
|
||||
import { join } from 'path';
|
||||
|
||||
import {
|
||||
Runner,
|
||||
TypescriptStarterInferredOptions,
|
||||
TypescriptStarterOptions,
|
||||
TypescriptStarterUserOptions
|
||||
TypescriptStarterUserOptions,
|
||||
} from './utils';
|
||||
|
||||
export enum Placeholders {
|
||||
email = 'YOUR_EMAIL',
|
||||
name = 'YOUR_NAME',
|
||||
username = 'YOUR_GITHUB_USER_NAME'
|
||||
username = 'YOUR_GITHUB_USER_NAME',
|
||||
}
|
||||
|
||||
// We implement these as function factories to make unit testing easier.
|
||||
@@ -38,21 +39,23 @@ export const cloneRepo = (
|
||||
'--depth=1',
|
||||
`--branch=${repoInfo.branch}`,
|
||||
repoInfo.repo,
|
||||
dir
|
||||
dir,
|
||||
];
|
||||
try {
|
||||
await spawner('git', args, {
|
||||
cwd: workingDirectory,
|
||||
stdio: suppressOutput ? 'pipe' : 'inherit'
|
||||
stdio: suppressOutput ? 'pipe' : 'inherit',
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.exitCodeName === 'ENOENT') {
|
||||
// eslint-disable-next-line functional/no-throw-statement
|
||||
throw new Error(`
|
||||
Git is not installed on your PATH. Please install Git and try again.
|
||||
|
||||
For more information, visit: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
|
||||
`);
|
||||
} else {
|
||||
// eslint-disable-next-line functional/no-throw-statement
|
||||
throw new Error(`Git clone failed.`);
|
||||
}
|
||||
}
|
||||
@@ -60,19 +63,20 @@ export const cloneRepo = (
|
||||
const revParseResult = await spawner('git', ['rev-parse', 'HEAD'], {
|
||||
cwd: projectDir,
|
||||
encoding: 'utf8',
|
||||
// tslint:disable-next-line:readonly-array
|
||||
stdio: ['pipe', 'pipe', 'inherit']
|
||||
stdio: ['pipe', 'pipe', 'inherit'],
|
||||
});
|
||||
const commitHash = revParseResult.stdout;
|
||||
return { commitHash, gitHistoryDir };
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line functional/no-throw-statement
|
||||
throw new Error(`Git rev-parse failed.`);
|
||||
}
|
||||
};
|
||||
|
||||
export const getGithubUsername = (fetcher: any) => async (
|
||||
email: string | undefined
|
||||
): Promise<string> => {
|
||||
export const getGithubUsername = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fetcher: any
|
||||
) => async (email: string | undefined): Promise<string> => {
|
||||
if (email === Placeholders.email) {
|
||||
return Placeholders.username;
|
||||
}
|
||||
@@ -84,20 +88,19 @@ export const getGithubUsername = (fetcher: any) => async (
|
||||
export const getUserInfo = (spawner: typeof execa) => async () => {
|
||||
const opts: Options = {
|
||||
encoding: 'utf8',
|
||||
// tslint:disable-next-line:readonly-array
|
||||
stdio: ['pipe', 'pipe', 'inherit']
|
||||
stdio: ['pipe', 'pipe', 'inherit'],
|
||||
};
|
||||
try {
|
||||
const nameResult = await spawner('git', ['config', 'user.name'], opts);
|
||||
const emailResult = await spawner('git', ['config', 'user.email'], opts);
|
||||
return {
|
||||
gitEmail: emailResult.stdout,
|
||||
gitName: nameResult.stdout
|
||||
gitName: nameResult.stdout,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
gitEmail: Placeholders.email,
|
||||
gitName: Placeholders.name
|
||||
gitName: Placeholders.name,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -109,7 +112,7 @@ export const initialCommit = (spawner: typeof execa) => async (
|
||||
const opts: Options = {
|
||||
cwd: projectDir,
|
||||
encoding: 'utf8',
|
||||
stdio: 'pipe'
|
||||
stdio: 'pipe',
|
||||
};
|
||||
await spawner('git', ['init'], opts);
|
||||
await spawner('git', ['add', '-A'], opts);
|
||||
@@ -118,7 +121,7 @@ export const initialCommit = (spawner: typeof execa) => async (
|
||||
[
|
||||
'commit',
|
||||
'-m',
|
||||
`Initial commit\n\nCreated with bitjson/typescript-starter@${hash}`
|
||||
`Initial commit\n\nCreated with bitjson/typescript-starter@${hash}`,
|
||||
],
|
||||
opts
|
||||
);
|
||||
@@ -131,13 +134,14 @@ export const install = (spawner: typeof execa) => async (
|
||||
const opts: Options = {
|
||||
cwd: projectDir,
|
||||
encoding: 'utf8',
|
||||
stdio: 'inherit'
|
||||
stdio: 'inherit',
|
||||
};
|
||||
try {
|
||||
runner === Runner.Npm
|
||||
? spawner('npm', ['install'], opts)
|
||||
: spawner('yarn', opts);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line functional/no-throw-statement
|
||||
throw new Error(`Installation failed. You'll need to install manually.`);
|
||||
}
|
||||
};
|
||||
@@ -155,15 +159,15 @@ export const getRepoInfo = (starterVersion: string) => {
|
||||
branch: process.env.TYPESCRIPT_STARTER_REPO_BRANCH
|
||||
? process.env.TYPESCRIPT_STARTER_REPO_BRANCH
|
||||
: 'master',
|
||||
repo: process.env.TYPESCRIPT_STARTER_REPO_URL
|
||||
repo: process.env.TYPESCRIPT_STARTER_REPO_URL,
|
||||
}
|
||||
: {
|
||||
branch: `v${starterVersion}`,
|
||||
repo: 'https://github.com/bitjson/typescript-starter.git'
|
||||
repo: 'https://github.com/bitjson/typescript-starter.git',
|
||||
};
|
||||
};
|
||||
|
||||
export interface Tasks {
|
||||
export type Tasks = {
|
||||
readonly cloneRepo: (
|
||||
repoInfo: {
|
||||
readonly branch: string;
|
||||
@@ -178,12 +182,12 @@ export interface Tasks {
|
||||
name: string
|
||||
) => Promise<void>;
|
||||
readonly install: (runner: Runner, projectDir: string) => Promise<void>;
|
||||
}
|
||||
};
|
||||
|
||||
export const LiveTasks: Tasks = {
|
||||
cloneRepo: cloneRepo(execa),
|
||||
initialCommit: initialCommit(execa),
|
||||
install: install(execa)
|
||||
install: install(execa),
|
||||
};
|
||||
export const addInferredOptions = async (
|
||||
userOptions: TypescriptStarterUserOptions
|
||||
@@ -195,22 +199,23 @@ export const addInferredOptions = async (
|
||||
fullName: gitName,
|
||||
githubUsername: username,
|
||||
repoInfo: getRepoInfo(userOptions.starterVersion),
|
||||
workingDirectory: process.cwd()
|
||||
workingDirectory: process.cwd(),
|
||||
};
|
||||
return {
|
||||
...inferredOptions,
|
||||
appveyor: userOptions.appveyor,
|
||||
circleci: userOptions.circleci,
|
||||
cspell: userOptions.cspell,
|
||||
description: userOptions.description,
|
||||
domDefinitions: userOptions.domDefinitions,
|
||||
editorconfig: userOptions.editorconfig,
|
||||
immutable: userOptions.immutable,
|
||||
functional: userOptions.functional,
|
||||
install: userOptions.install,
|
||||
nodeDefinitions: userOptions.nodeDefinitions,
|
||||
projectName: userOptions.projectName,
|
||||
runner: userOptions.runner,
|
||||
strict: userOptions.strict,
|
||||
travis: userOptions.travis,
|
||||
vscode: userOptions.vscode
|
||||
vscode: userOptions.vscode,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
* `diff build/test-one/package.json build/test-two/package.json`
|
||||
*/
|
||||
|
||||
// tslint:disable:no-expression-statement
|
||||
import { join, relative } from 'path';
|
||||
|
||||
import test, { ExecutionContext } from 'ava';
|
||||
import del from 'del';
|
||||
import execa from 'execa';
|
||||
import globby from 'globby';
|
||||
import md5File from 'md5-file';
|
||||
import meow from 'meow';
|
||||
import { join, relative } from 'path';
|
||||
|
||||
import { cloneRepo, Placeholders, Tasks } from '../tasks';
|
||||
import { typescriptStarter } from '../typescript-starter';
|
||||
import { normalizePath, Runner } from '../utils';
|
||||
@@ -37,17 +38,17 @@ const branch = execa.sync('git', [
|
||||
'rev-parse',
|
||||
'--symbolic-full-name',
|
||||
'--abbrev-ref',
|
||||
'HEAD'
|
||||
'HEAD',
|
||||
]).stdout;
|
||||
const repoInfo = {
|
||||
// if local repo is in a detached HEAD state, providing --branch to `git clone` will fail.
|
||||
branch: branch === 'HEAD' ? '.' : branch,
|
||||
repo: process.cwd()
|
||||
repo: process.cwd(),
|
||||
};
|
||||
const buildDir = join(process.cwd(), 'build');
|
||||
const env = {
|
||||
TYPESCRIPT_STARTER_REPO_BRANCH: repoInfo.branch,
|
||||
TYPESCRIPT_STARTER_REPO_URL: repoInfo.repo
|
||||
TYPESCRIPT_STARTER_REPO_URL: repoInfo.repo,
|
||||
};
|
||||
|
||||
enum TestDirectories {
|
||||
@@ -56,7 +57,7 @@ enum TestDirectories {
|
||||
three = 'test-3',
|
||||
four = 'test-4',
|
||||
five = 'test-5',
|
||||
six = 'test-6'
|
||||
six = 'test-6',
|
||||
}
|
||||
|
||||
// If the tests all pass, the TestDirectories will automatically be cleaned up.
|
||||
@@ -67,23 +68,23 @@ test.after(async () => {
|
||||
`./build/${TestDirectories.three}`,
|
||||
`./build/${TestDirectories.four}`,
|
||||
`./build/${TestDirectories.five}`,
|
||||
`./build/${TestDirectories.six}`
|
||||
`./build/${TestDirectories.six}`,
|
||||
]);
|
||||
});
|
||||
|
||||
test('returns version', async t => {
|
||||
test('returns version', async (t) => {
|
||||
const expected = meow('').pkg.version;
|
||||
t.truthy(typeof expected === 'string');
|
||||
const { stdout } = await execa(`./bin/typescript-starter`, ['--version']);
|
||||
t.is(stdout, expected);
|
||||
});
|
||||
|
||||
test('returns help/usage', async t => {
|
||||
test('returns help/usage', async (t) => {
|
||||
const { stdout } = await execa(`./bin/typescript-starter`, ['--help']);
|
||||
t.regex(stdout, /Usage/);
|
||||
});
|
||||
|
||||
test('errors if project name collides with an existing path', async t => {
|
||||
test('errors if project name collides with an existing path', async (t) => {
|
||||
const existingDir = 'build';
|
||||
const error = await t.throwsAsync<execa.ExecaError>(
|
||||
execa(`./bin/typescript-starter`, [existingDir])
|
||||
@@ -91,7 +92,7 @@ test('errors if project name collides with an existing path', async t => {
|
||||
t.regex(error.stderr, /"build" path already exists/);
|
||||
});
|
||||
|
||||
test('errors if project name is not in kebab-case', async t => {
|
||||
test('errors if project name is not in kebab-case', async (t) => {
|
||||
const error = await t.throwsAsync<execa.ExecaError>(
|
||||
execa(`./bin/typescript-starter`, ['name with spaces'])
|
||||
);
|
||||
@@ -106,24 +107,17 @@ async function hashAllTheThings(
|
||||
const rawFilePaths: ReadonlyArray<string> = await globby(
|
||||
[projectDir, `!${projectDir}/.git`],
|
||||
{
|
||||
dot: true
|
||||
dot: true,
|
||||
}
|
||||
);
|
||||
const filePaths = sandboxed
|
||||
? rawFilePaths
|
||||
: rawFilePaths.filter(
|
||||
path =>
|
||||
(path) =>
|
||||
// When not sandboxed, these files will change based on git config
|
||||
!['LICENSE', 'package.json'].includes(relative(projectDir, path))
|
||||
);
|
||||
const hashAll = filePaths.map<Promise<string>>(
|
||||
path =>
|
||||
new Promise<string>((resolve, reject) => {
|
||||
md5File(path, (err: Error, result: string) => {
|
||||
err ? reject(err) : resolve(result);
|
||||
});
|
||||
})
|
||||
);
|
||||
const hashAll = filePaths.map<Promise<string>>((path) => md5File(path));
|
||||
const hashes = await Promise.all(hashAll);
|
||||
return hashes.reduce<{ readonly [filename: string]: string }>(
|
||||
(acc, hash, i) => {
|
||||
@@ -132,7 +126,7 @@ async function hashAllTheThings(
|
||||
);
|
||||
return {
|
||||
...acc,
|
||||
[trimmedNormalizedFilePath]: hash
|
||||
[trimmedNormalizedFilePath]: hash,
|
||||
};
|
||||
},
|
||||
{}
|
||||
@@ -140,19 +134,19 @@ async function hashAllTheThings(
|
||||
}
|
||||
|
||||
/**
|
||||
* Since we're using Greenkeeper to keep dependencies fresh, including
|
||||
* Since we're using Dependabot to keep dependencies fresh, including
|
||||
* `package.json` in these file fingerprint tests guarantees that every
|
||||
* Greenkeeper PR will trigger a false-positive test failure.
|
||||
* Dependabot PR will trigger a false-positive test failure.
|
||||
*
|
||||
* Here we trade complete assurance that `package.json` is correct for much less
|
||||
* noisy build results.
|
||||
*/
|
||||
const ignorePackageJson = (map: { readonly [file: string]: string }) =>
|
||||
Object.entries(map)
|
||||
.filter(entry => !entry[0].includes('package.json'))
|
||||
.filter((entry) => !entry[0].includes('package.json'))
|
||||
.reduce((ret, [path, hash]) => ({ ...ret, [path]: hash }), {});
|
||||
|
||||
test(`${TestDirectories.one}: parses CLI arguments, handles default options`, async t => {
|
||||
test(`${TestDirectories.one}: parses CLI arguments, handles default options`, async (t) => {
|
||||
const description = 'example description 1';
|
||||
const { stdout } = await execa(
|
||||
`../bin/typescript-starter`,
|
||||
@@ -160,40 +154,40 @@ test(`${TestDirectories.one}: parses CLI arguments, handles default options`, as
|
||||
`${TestDirectories.one}`,
|
||||
// (user entered `-d='example description 1'`)
|
||||
`-d=${description}`,
|
||||
'--no-install'
|
||||
'--no-install',
|
||||
],
|
||||
{
|
||||
cwd: buildDir,
|
||||
env
|
||||
env,
|
||||
}
|
||||
);
|
||||
t.regex(stdout, new RegExp(`Created ${TestDirectories.one} 🎉`));
|
||||
const map = await hashAllTheThings(TestDirectories.one);
|
||||
t.deepEqual(map, {
|
||||
'test-1/.circleci/config.yml': 'd29447823209aa9a230f08af7e7bfcc7',
|
||||
'test-1/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba',
|
||||
'test-1/.cspell.json': '5b2e0ad337bc05ef2ce5635270d983fd',
|
||||
'test-1/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4',
|
||||
'test-1/.eslintrc.json': '4e74756d24eaccb7f28d4999e4bd6f0d',
|
||||
'test-1/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08',
|
||||
'test-1/.github/ISSUE_TEMPLATE.md': '82d1b99b29f32d851627b317195e73d2',
|
||||
'test-1/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e',
|
||||
'test-1/.github/PULL_REQUEST_TEMPLATE.md':
|
||||
'710eb5973a8cda83fc568cb1bbe7c026',
|
||||
'70f4b97f3914e2f399bcc5868e072c29',
|
||||
'test-1/.gitignore': '892227b7f662b74410e9bf6fb2ae887f',
|
||||
'test-1/.npmignore': '49c9375c9a1b4a1b74076f62379b0297',
|
||||
'test-1/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683',
|
||||
'test-1/.vscode/debug-ts.js': '23eb6ab10faaa25a95f5bd3325d0455c',
|
||||
'test-1/.vscode/launch.json': '669e4d1dda91c781177c6adae7aa7e00',
|
||||
'test-1/.vscode/settings.json': '10c634c5fef6ecd298b6e41bf159f2cc',
|
||||
'test-1/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139',
|
||||
'test-1/.vscode/launch.json': '140e17d591e03b8850c456ade3aefb1f',
|
||||
'test-1/.vscode/settings.json': '1671948882faee285f18d7491f0fc913',
|
||||
'test-1/README.md': '7a9f4efa9213266c3800f3cc82a53ba7',
|
||||
'test-1/src/index.ts': '5025093b4dc30524d349fd1cc465ed30',
|
||||
'test-1/src/lib/number.spec.ts': '6a9a00630b10e7d57a79678c74a0e4df',
|
||||
'test-1/src/lib/number.ts': '43756f90e6ac0b1c4ee6c81d8ab969c7',
|
||||
'test-1/src/types/example.d.ts': '4221812f6f0434eec77ccb1fba1e3759',
|
||||
'test-1/src/lib/number.spec.ts': '0592001d71aa3b3e6bf72f4cd95dc1b9',
|
||||
'test-1/src/lib/number.ts': 'dcbcc98fea337d07e81728c1a6526a1e',
|
||||
'test-1/src/types/example.d.ts': '76642861732b16754b0110fb1de49823',
|
||||
'test-1/tsconfig.json': '0e04adfce2f26c6473f079f6dabd108a',
|
||||
'test-1/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d',
|
||||
'test-1/tslint.json': '7ac167ffbcb724a6c270e8dc4e747067'
|
||||
});
|
||||
});
|
||||
|
||||
test(`${TestDirectories.two}: parses CLI arguments, handles all options`, async t => {
|
||||
test(`${TestDirectories.two}: parses CLI arguments, handles all options`, async (t) => {
|
||||
const description = 'example description 2';
|
||||
const { stdout } = await execa(
|
||||
`../bin/typescript-starter`,
|
||||
@@ -205,40 +199,40 @@ test(`${TestDirectories.two}: parses CLI arguments, handles all options`, async
|
||||
'--yarn',
|
||||
'--node',
|
||||
'--dom',
|
||||
'--no-install'
|
||||
'--no-install',
|
||||
],
|
||||
{
|
||||
cwd: buildDir,
|
||||
env
|
||||
env,
|
||||
}
|
||||
);
|
||||
t.regex(stdout, new RegExp(`Created ${TestDirectories.two} 🎉`));
|
||||
const map = await hashAllTheThings(TestDirectories.two);
|
||||
t.deepEqual(map, {
|
||||
'test-2/.circleci/config.yml': 'd29447823209aa9a230f08af7e7bfcc7',
|
||||
'test-2/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba',
|
||||
'test-2/.cspell.json': '5b2e0ad337bc05ef2ce5635270d983fd',
|
||||
'test-2/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4',
|
||||
'test-2/.eslintrc.json': '4e74756d24eaccb7f28d4999e4bd6f0d',
|
||||
'test-2/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08',
|
||||
'test-2/.github/ISSUE_TEMPLATE.md': '82d1b99b29f32d851627b317195e73d2',
|
||||
'test-2/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e',
|
||||
'test-2/.github/PULL_REQUEST_TEMPLATE.md':
|
||||
'710eb5973a8cda83fc568cb1bbe7c026',
|
||||
'70f4b97f3914e2f399bcc5868e072c29',
|
||||
'test-2/.gitignore': 'af817565c661f1b15514584c8ea9e469',
|
||||
'test-2/.npmignore': '49c9375c9a1b4a1b74076f62379b0297',
|
||||
'test-2/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683',
|
||||
'test-2/.vscode/debug-ts.js': '23eb6ab10faaa25a95f5bd3325d0455c',
|
||||
'test-2/.vscode/launch.json': '669e4d1dda91c781177c6adae7aa7e00',
|
||||
'test-2/.vscode/settings.json': '10c634c5fef6ecd298b6e41bf159f2cc',
|
||||
'test-2/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139',
|
||||
'test-2/.vscode/launch.json': '140e17d591e03b8850c456ade3aefb1f',
|
||||
'test-2/.vscode/settings.json': '1671948882faee285f18d7491f0fc913',
|
||||
'test-2/README.md': 'ddaf27da4cc4ca5225785f0ac8f4da58',
|
||||
'test-2/src/index.ts': 'fbc67c2cbf3a7d37e4e02583bf06eec9',
|
||||
'test-2/src/lib/async.spec.ts': '1f51a721fffe53832fb289429baba971',
|
||||
'test-2/src/lib/async.ts': '9012c267bb25fa98ad2561929de3d4e2',
|
||||
'test-2/src/lib/hash.spec.ts': '11589e1960ddd75e7597c9de6854cd08',
|
||||
'test-2/src/lib/hash.ts': 'a4c552897f25da5963f410e375264bd1',
|
||||
'test-2/src/lib/number.spec.ts': '6a9a00630b10e7d57a79678c74a0e4df',
|
||||
'test-2/src/lib/number.ts': '43756f90e6ac0b1c4ee6c81d8ab969c7',
|
||||
'test-2/src/types/example.d.ts': '4221812f6f0434eec77ccb1fba1e3759',
|
||||
'test-2/src/lib/async.spec.ts': '65b10546885ebad41c098318b896f23c',
|
||||
'test-2/src/lib/async.ts': '926732fef1285cb0abb5c6e287ed24df',
|
||||
'test-2/src/lib/hash.spec.ts': '06f6bfc1b03f893a16448bb6d6806ea2',
|
||||
'test-2/src/lib/hash.ts': 'cf3659937ff3162bc525c8bfac18b7cf',
|
||||
'test-2/src/lib/number.spec.ts': '0592001d71aa3b3e6bf72f4cd95dc1b9',
|
||||
'test-2/src/lib/number.ts': 'dcbcc98fea337d07e81728c1a6526a1e',
|
||||
'test-2/src/types/example.d.ts': '76642861732b16754b0110fb1de49823',
|
||||
'test-2/tsconfig.json': '8a55379f60e4e6d4fad1f0b2318b74c4',
|
||||
'test-2/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d',
|
||||
'test-2/tslint.json': '7ac167ffbcb724a6c270e8dc4e747067'
|
||||
});
|
||||
});
|
||||
|
||||
@@ -246,7 +240,7 @@ const down = '\x1B\x5B\x42';
|
||||
const up = '\x1B\x5B\x41';
|
||||
const enter = '\x0D';
|
||||
const ms = (milliseconds: number) =>
|
||||
new Promise<void>(resolve => setTimeout(resolve, milliseconds));
|
||||
new Promise<void>((resolve) => setTimeout(resolve, milliseconds));
|
||||
|
||||
async function testInteractive(
|
||||
projectName: string,
|
||||
@@ -255,18 +249,18 @@ async function testInteractive(
|
||||
const typeDefs = entry[3] !== '';
|
||||
const proc = execa(`../bin/typescript-starter`, ['--no-install'], {
|
||||
cwd: buildDir,
|
||||
env
|
||||
env,
|
||||
});
|
||||
|
||||
// TODO: missing in Node.js type definition's ChildProcess.stdin?
|
||||
// https://nodejs.org/api/process.html#process_process_stdin
|
||||
// proc.stdin.setEncoding('utf8');
|
||||
|
||||
const type = (input: string) => proc.stdin.write(input);
|
||||
const type = (input: string) => proc.stdin?.write(input);
|
||||
|
||||
// wait for first chunk to be emitted
|
||||
await new Promise(resolve => {
|
||||
proc.stdout.once('data', resolve);
|
||||
await new Promise((resolve) => {
|
||||
proc.stdout?.once('data', resolve);
|
||||
});
|
||||
await ms(200);
|
||||
type(`${projectName}${enter}`);
|
||||
@@ -277,7 +271,6 @@ async function testInteractive(
|
||||
await ms(200);
|
||||
type(`${entry[2][0]}${enter}`);
|
||||
await ms(200);
|
||||
// tslint:disable-next-line:no-if-statement
|
||||
if (typeDefs) {
|
||||
type(`${entry[3][0]}${enter}`);
|
||||
await ms(200);
|
||||
@@ -287,84 +280,87 @@ async function testInteractive(
|
||||
return proc;
|
||||
}
|
||||
|
||||
test(`${TestDirectories.three}: interactive mode: javascript library`, async t => {
|
||||
test(`${TestDirectories.three}: interactive mode: javascript library`, async (t) => {
|
||||
const proc = await testInteractive(`${TestDirectories.three}`, [
|
||||
[`${down}${up}${down}`, `Javascript library`],
|
||||
`integration test 3 description`,
|
||||
[`${down}${up}${down}${enter}`, `yarn`],
|
||||
[`${down}${down}${down}${enter}`, `Both Node.js and DOM`],
|
||||
[' ', 'stricter type-checking[\\s\\S]*tslint-immutable[\\s\\S]*VS Code']
|
||||
[
|
||||
' ',
|
||||
'stricter type-checking[\\s\\S]*eslint-plugin-functional[\\s\\S]*VS Code',
|
||||
],
|
||||
]);
|
||||
await proc;
|
||||
const map = await hashAllTheThings(TestDirectories.three);
|
||||
t.deepEqual(map, {
|
||||
'test-3/.circleci/config.yml': 'd29447823209aa9a230f08af7e7bfcc7',
|
||||
'test-3/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba',
|
||||
'test-3/.cspell.json': '5b2e0ad337bc05ef2ce5635270d983fd',
|
||||
'test-3/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4',
|
||||
'test-3/.eslintrc.json': '4e74756d24eaccb7f28d4999e4bd6f0d',
|
||||
'test-3/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08',
|
||||
'test-3/.github/ISSUE_TEMPLATE.md': '82d1b99b29f32d851627b317195e73d2',
|
||||
'test-3/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e',
|
||||
'test-3/.github/PULL_REQUEST_TEMPLATE.md':
|
||||
'710eb5973a8cda83fc568cb1bbe7c026',
|
||||
'70f4b97f3914e2f399bcc5868e072c29',
|
||||
'test-3/.gitignore': 'af817565c661f1b15514584c8ea9e469',
|
||||
'test-3/.npmignore': '49c9375c9a1b4a1b74076f62379b0297',
|
||||
'test-3/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683',
|
||||
'test-3/.vscode/debug-ts.js': '23eb6ab10faaa25a95f5bd3325d0455c',
|
||||
'test-3/.vscode/launch.json': '669e4d1dda91c781177c6adae7aa7e00',
|
||||
'test-3/.vscode/settings.json': '10c634c5fef6ecd298b6e41bf159f2cc',
|
||||
'test-3/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139',
|
||||
'test-3/.vscode/launch.json': '140e17d591e03b8850c456ade3aefb1f',
|
||||
'test-3/.vscode/settings.json': '1671948882faee285f18d7491f0fc913',
|
||||
'test-3/README.md': 'c52631ebf78f6b030af9a109b769b647',
|
||||
'test-3/src/index.ts': 'fbc67c2cbf3a7d37e4e02583bf06eec9',
|
||||
'test-3/src/lib/async.spec.ts': '1f51a721fffe53832fb289429baba971',
|
||||
'test-3/src/lib/async.ts': '9012c267bb25fa98ad2561929de3d4e2',
|
||||
'test-3/src/lib/hash.spec.ts': '11589e1960ddd75e7597c9de6854cd08',
|
||||
'test-3/src/lib/hash.ts': 'a4c552897f25da5963f410e375264bd1',
|
||||
'test-3/src/lib/number.spec.ts': '6a9a00630b10e7d57a79678c74a0e4df',
|
||||
'test-3/src/lib/number.ts': '43756f90e6ac0b1c4ee6c81d8ab969c7',
|
||||
'test-3/src/types/example.d.ts': '4221812f6f0434eec77ccb1fba1e3759',
|
||||
'test-3/src/lib/async.spec.ts': '65b10546885ebad41c098318b896f23c',
|
||||
'test-3/src/lib/async.ts': '926732fef1285cb0abb5c6e287ed24df',
|
||||
'test-3/src/lib/hash.spec.ts': '06f6bfc1b03f893a16448bb6d6806ea2',
|
||||
'test-3/src/lib/hash.ts': 'cf3659937ff3162bc525c8bfac18b7cf',
|
||||
'test-3/src/lib/number.spec.ts': '0592001d71aa3b3e6bf72f4cd95dc1b9',
|
||||
'test-3/src/lib/number.ts': 'dcbcc98fea337d07e81728c1a6526a1e',
|
||||
'test-3/src/types/example.d.ts': '76642861732b16754b0110fb1de49823',
|
||||
'test-3/tsconfig.json': '43817952d399db9e44977b3703edd7cf',
|
||||
'test-3/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d',
|
||||
'test-3/tslint.json': '7ac167ffbcb724a6c270e8dc4e747067'
|
||||
});
|
||||
});
|
||||
|
||||
test(`${TestDirectories.four}: interactive mode: node.js application`, async t => {
|
||||
test(`${TestDirectories.four}: interactive mode: node.js application`, async (t) => {
|
||||
const proc = await testInteractive(`${TestDirectories.four}`, [
|
||||
[`${down}${up}`, `Node.js application`],
|
||||
`integration test 4 description`,
|
||||
[`${down}${up}${enter}`, `npm`],
|
||||
'',
|
||||
[`${down} `, 'VS Code']
|
||||
[`${down} `, 'VS Code'],
|
||||
]);
|
||||
await proc;
|
||||
const map = await hashAllTheThings(TestDirectories.four);
|
||||
t.deepEqual(map, {
|
||||
'test-4/.circleci/config.yml': 'd29447823209aa9a230f08af7e7bfcc7',
|
||||
'test-4/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba',
|
||||
'test-4/.cspell.json': '5b2e0ad337bc05ef2ce5635270d983fd',
|
||||
'test-4/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4',
|
||||
'test-4/.eslintrc.json': '941448b089cd055bbe476a84c8f96cfe',
|
||||
'test-4/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08',
|
||||
'test-4/.github/ISSUE_TEMPLATE.md': '82d1b99b29f32d851627b317195e73d2',
|
||||
'test-4/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e',
|
||||
'test-4/.github/PULL_REQUEST_TEMPLATE.md':
|
||||
'710eb5973a8cda83fc568cb1bbe7c026',
|
||||
'70f4b97f3914e2f399bcc5868e072c29',
|
||||
'test-4/.gitignore': '892227b7f662b74410e9bf6fb2ae887f',
|
||||
'test-4/.npmignore': '49c9375c9a1b4a1b74076f62379b0297',
|
||||
'test-4/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683',
|
||||
'test-4/.vscode/debug-ts.js': '23eb6ab10faaa25a95f5bd3325d0455c',
|
||||
'test-4/.vscode/launch.json': '669e4d1dda91c781177c6adae7aa7e00',
|
||||
'test-4/.vscode/settings.json': '10c634c5fef6ecd298b6e41bf159f2cc',
|
||||
'test-4/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139',
|
||||
'test-4/.vscode/launch.json': '140e17d591e03b8850c456ade3aefb1f',
|
||||
'test-4/.vscode/settings.json': '1671948882faee285f18d7491f0fc913',
|
||||
'test-4/README.md': 'a3e0699b39498df4843c9dde95f1e000',
|
||||
'test-4/src/index.ts': 'fbc67c2cbf3a7d37e4e02583bf06eec9',
|
||||
'test-4/src/lib/async.spec.ts': '1f51a721fffe53832fb289429baba971',
|
||||
'test-4/src/lib/async.ts': '9012c267bb25fa98ad2561929de3d4e2',
|
||||
'test-4/src/lib/hash.spec.ts': '11589e1960ddd75e7597c9de6854cd08',
|
||||
'test-4/src/lib/hash.ts': 'a4c552897f25da5963f410e375264bd1',
|
||||
'test-4/src/lib/number.spec.ts': '6a9a00630b10e7d57a79678c74a0e4df',
|
||||
'test-4/src/lib/number.ts': '43756f90e6ac0b1c4ee6c81d8ab969c7',
|
||||
'test-4/src/types/example.d.ts': '4221812f6f0434eec77ccb1fba1e3759',
|
||||
'test-4/src/lib/async.spec.ts': '65b10546885ebad41c098318b896f23c',
|
||||
'test-4/src/lib/async.ts': '926732fef1285cb0abb5c6e287ed24df',
|
||||
'test-4/src/lib/hash.spec.ts': '06f6bfc1b03f893a16448bb6d6806ea2',
|
||||
'test-4/src/lib/hash.ts': 'cf3659937ff3162bc525c8bfac18b7cf',
|
||||
'test-4/src/lib/number.spec.ts': '0592001d71aa3b3e6bf72f4cd95dc1b9',
|
||||
'test-4/src/lib/number.ts': 'dcbcc98fea337d07e81728c1a6526a1e',
|
||||
'test-4/src/types/example.d.ts': '76642861732b16754b0110fb1de49823',
|
||||
'test-4/tsconfig.json': 'e41d08f0aca16cb05430b61e4b6286db',
|
||||
'test-4/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d',
|
||||
'test-4/tslint.json': '99f6f8fa763bfc2a32377739b3e5dd5c'
|
||||
});
|
||||
});
|
||||
|
||||
const sandboxTasks = (
|
||||
t: ExecutionContext<{}>,
|
||||
t: ExecutionContext<unknown>,
|
||||
commit: boolean,
|
||||
install: boolean
|
||||
): Tasks => {
|
||||
@@ -375,7 +371,7 @@ const sandboxTasks = (
|
||||
},
|
||||
install: async () => {
|
||||
install ? t.pass() : t.fail();
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -383,108 +379,109 @@ const sandboxOptions = {
|
||||
description: 'this is an example description',
|
||||
githubUsername: 'SOME_GITHUB_USERNAME',
|
||||
repoInfo,
|
||||
workingDirectory: buildDir
|
||||
workingDirectory: buildDir,
|
||||
};
|
||||
|
||||
const silenceConsole = (console: any) => {
|
||||
// tslint:disable-next-line:no-object-mutation no-console
|
||||
const silenceConsole = (console: Console) => {
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
console.log = () => {
|
||||
// mock console.log to silence it
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
test(`${TestDirectories.five}: Sandboxed: npm install, initial commit`, async t => {
|
||||
test(`${TestDirectories.five}: Sandboxed: npm install, initial commit`, async (t) => {
|
||||
t.plan(3);
|
||||
const options = {
|
||||
...sandboxOptions,
|
||||
appveyor: false,
|
||||
circleci: false,
|
||||
cspell: false,
|
||||
domDefinitions: false,
|
||||
editorconfig: false,
|
||||
email: 'email@example.com',
|
||||
// cspell: disable-next-line
|
||||
fullName: 'Satoshi Nakamoto',
|
||||
immutable: true,
|
||||
functional: true,
|
||||
install: true,
|
||||
nodeDefinitions: false,
|
||||
projectName: TestDirectories.five,
|
||||
runner: Runner.Npm,
|
||||
strict: true,
|
||||
travis: false,
|
||||
vscode: false
|
||||
vscode: false,
|
||||
};
|
||||
silenceConsole(console);
|
||||
await typescriptStarter(options, sandboxTasks(t, true, true));
|
||||
const map = await hashAllTheThings(TestDirectories.five, true);
|
||||
t.deepEqual(ignorePackageJson(map), {
|
||||
'test-5/.eslintrc.json': '4e74756d24eaccb7f28d4999e4bd6f0d',
|
||||
'test-5/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08',
|
||||
'test-5/.github/ISSUE_TEMPLATE.md': '82d1b99b29f32d851627b317195e73d2',
|
||||
'test-5/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e',
|
||||
'test-5/.github/PULL_REQUEST_TEMPLATE.md':
|
||||
'710eb5973a8cda83fc568cb1bbe7c026',
|
||||
'70f4b97f3914e2f399bcc5868e072c29',
|
||||
'test-5/.gitignore': '892227b7f662b74410e9bf6fb2ae887f',
|
||||
'test-5/.npmignore': '49c9375c9a1b4a1b74076f62379b0297',
|
||||
'test-5/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683',
|
||||
'test-5/LICENSE': '317693126d229a3cdd19725a624a56fc',
|
||||
'test-5/README.md': '8fc7ecb21d7d47289e4b2469eea4db39',
|
||||
'test-5/src/index.ts': '5025093b4dc30524d349fd1cc465ed30',
|
||||
'test-5/src/lib/number.spec.ts': '6a9a00630b10e7d57a79678c74a0e4df',
|
||||
'test-5/src/lib/number.ts': '43756f90e6ac0b1c4ee6c81d8ab969c7',
|
||||
'test-5/src/types/example.d.ts': '4221812f6f0434eec77ccb1fba1e3759',
|
||||
'test-5/src/lib/number.spec.ts': '0592001d71aa3b3e6bf72f4cd95dc1b9',
|
||||
'test-5/src/lib/number.ts': 'dcbcc98fea337d07e81728c1a6526a1e',
|
||||
'test-5/src/types/example.d.ts': '76642861732b16754b0110fb1de49823',
|
||||
'test-5/tsconfig.json': 'f36dc6407fc898f41a23cb620b2f4884',
|
||||
'test-5/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d',
|
||||
'test-5/tslint.json': '7ac167ffbcb724a6c270e8dc4e747067'
|
||||
});
|
||||
});
|
||||
|
||||
test(`${TestDirectories.six}: Sandboxed: yarn, no initial commit`, async t => {
|
||||
test(`${TestDirectories.six}: Sandboxed: yarn, no initial commit`, async (t) => {
|
||||
t.plan(2);
|
||||
const options = {
|
||||
...sandboxOptions,
|
||||
appveyor: true,
|
||||
circleci: true,
|
||||
cspell: false,
|
||||
domDefinitions: true,
|
||||
editorconfig: true,
|
||||
email: Placeholders.email,
|
||||
fullName: Placeholders.name,
|
||||
immutable: true,
|
||||
functional: true,
|
||||
install: true,
|
||||
nodeDefinitions: true,
|
||||
projectName: TestDirectories.six,
|
||||
runner: Runner.Yarn,
|
||||
strict: false,
|
||||
travis: true,
|
||||
vscode: true
|
||||
vscode: true,
|
||||
};
|
||||
silenceConsole(console);
|
||||
await typescriptStarter(options, sandboxTasks(t, false, true));
|
||||
const map = await hashAllTheThings(TestDirectories.six, true);
|
||||
t.deepEqual(ignorePackageJson(map), {
|
||||
'test-6/.circleci/config.yml': 'd29447823209aa9a230f08af7e7bfcc7',
|
||||
'test-6/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba',
|
||||
'test-6/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4',
|
||||
'test-6/.eslintrc.json': '4e74756d24eaccb7f28d4999e4bd6f0d',
|
||||
'test-6/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08',
|
||||
'test-6/.github/ISSUE_TEMPLATE.md': '82d1b99b29f32d851627b317195e73d2',
|
||||
'test-6/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e',
|
||||
'test-6/.github/PULL_REQUEST_TEMPLATE.md':
|
||||
'710eb5973a8cda83fc568cb1bbe7c026',
|
||||
'70f4b97f3914e2f399bcc5868e072c29',
|
||||
'test-6/.gitignore': 'af817565c661f1b15514584c8ea9e469',
|
||||
'test-6/.npmignore': '49c9375c9a1b4a1b74076f62379b0297',
|
||||
'test-6/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683',
|
||||
'test-6/.travis.yml': 'b56cf7194d8ff58d6cf51c34b0c645e0',
|
||||
'test-6/.vscode/debug-ts.js': '23eb6ab10faaa25a95f5bd3325d0455c',
|
||||
'test-6/.vscode/launch.json': '669e4d1dda91c781177c6adae7aa7e00',
|
||||
'test-6/.vscode/settings.json': '10c634c5fef6ecd298b6e41bf159f2cc',
|
||||
'test-6/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139',
|
||||
'test-6/.vscode/launch.json': '140e17d591e03b8850c456ade3aefb1f',
|
||||
'test-6/.vscode/settings.json': 'f70eb64341e74d24d901055a26dc8242',
|
||||
'test-6/LICENSE': '03ffa741a4f7e356b69353efa4937d2b',
|
||||
'test-6/README.md': 'd809bcbf240f44b51b575a3d49936232',
|
||||
'test-6/appveyor.yml': '27c787d8e288f89c71357b1ac32b42e8',
|
||||
'test-6/appveyor.yml': '214e043a9baa2a9d2579a0af0a5621a3',
|
||||
'test-6/src/index.ts': 'fbc67c2cbf3a7d37e4e02583bf06eec9',
|
||||
'test-6/src/lib/async.spec.ts': '1f51a721fffe53832fb289429baba971',
|
||||
'test-6/src/lib/async.ts': '9012c267bb25fa98ad2561929de3d4e2',
|
||||
'test-6/src/lib/hash.spec.ts': '11589e1960ddd75e7597c9de6854cd08',
|
||||
'test-6/src/lib/hash.ts': 'a4c552897f25da5963f410e375264bd1',
|
||||
'test-6/src/lib/number.spec.ts': '6a9a00630b10e7d57a79678c74a0e4df',
|
||||
'test-6/src/lib/number.ts': '43756f90e6ac0b1c4ee6c81d8ab969c7',
|
||||
'test-6/src/types/example.d.ts': '4221812f6f0434eec77ccb1fba1e3759',
|
||||
'test-6/src/lib/async.spec.ts': '65b10546885ebad41c098318b896f23c',
|
||||
'test-6/src/lib/async.ts': '926732fef1285cb0abb5c6e287ed24df',
|
||||
'test-6/src/lib/hash.spec.ts': '06f6bfc1b03f893a16448bb6d6806ea2',
|
||||
'test-6/src/lib/hash.ts': 'cf3659937ff3162bc525c8bfac18b7cf',
|
||||
'test-6/src/lib/number.spec.ts': '0592001d71aa3b3e6bf72f4cd95dc1b9',
|
||||
'test-6/src/lib/number.ts': 'dcbcc98fea337d07e81728c1a6526a1e',
|
||||
'test-6/src/types/example.d.ts': '76642861732b16754b0110fb1de49823',
|
||||
'test-6/tsconfig.json': '8a55379f60e4e6d4fad1f0b2318b74c4',
|
||||
'test-6/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d',
|
||||
'test-6/tslint.json': '7ac167ffbcb724a6c270e8dc4e747067'
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// tslint:disable:no-expression-statement
|
||||
import test from 'ava';
|
||||
import execa from 'execa';
|
||||
import meow from 'meow';
|
||||
import nock from 'nock';
|
||||
|
||||
import { checkArgs } from '../args';
|
||||
import {
|
||||
cloneRepo,
|
||||
@@ -11,11 +11,11 @@ import {
|
||||
getUserInfo,
|
||||
initialCommit,
|
||||
install,
|
||||
Placeholders
|
||||
Placeholders,
|
||||
} from '../tasks';
|
||||
import { getIntro, Runner, validateName } from '../utils';
|
||||
|
||||
test('errors if outdated', async t => {
|
||||
test('errors if outdated', async (t) => {
|
||||
nock.disableNetConnect();
|
||||
nock('https://registry.npmjs.org:443')
|
||||
.get('/typescript-starter')
|
||||
@@ -24,9 +24,9 @@ test('errors if outdated', async t => {
|
||||
name: 'typescript-starter',
|
||||
versions: {
|
||||
'9000.0.1': {
|
||||
version: '9000.0.1'
|
||||
}
|
||||
}
|
||||
version: '9000.0.1',
|
||||
},
|
||||
},
|
||||
});
|
||||
const error = await t.throwsAsync(checkArgs);
|
||||
t.regex(error.message, /is outdated/);
|
||||
@@ -41,20 +41,20 @@ const pretendLatestVersionIs = (version: string) => {
|
||||
name: 'typescript-starter',
|
||||
versions: {
|
||||
[version]: {
|
||||
version
|
||||
}
|
||||
}
|
||||
version,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
test("doesn't error if not outdated", async t => {
|
||||
const currentVersion = meow('').pkg.version;
|
||||
test("doesn't error if not outdated", async (t) => {
|
||||
const currentVersion = meow('').pkg.version as string;
|
||||
t.truthy(typeof currentVersion === 'string');
|
||||
pretendLatestVersionIs(currentVersion);
|
||||
await t.notThrows(checkArgs);
|
||||
});
|
||||
|
||||
test('errors if update-notifier fails', async t => {
|
||||
test('errors if update-notifier fails', async (t) => {
|
||||
nock.disableNetConnect();
|
||||
nock('https://registry.npmjs.org:443')
|
||||
.get('/typescript-starter')
|
||||
@@ -63,35 +63,38 @@ test('errors if update-notifier fails', async t => {
|
||||
t.regex(error.message, /could not be found/);
|
||||
});
|
||||
|
||||
test('checkArgs returns the right options', async t => {
|
||||
test('checkArgs returns the right options', async (t) => {
|
||||
pretendLatestVersionIs('1.0.0');
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
process.argv = [
|
||||
'path/to/node',
|
||||
'path/to/typescript-starter',
|
||||
'example-project',
|
||||
'--appveyor',
|
||||
`-description "example description"`,
|
||||
'--description',
|
||||
'"example description"',
|
||||
'--dom',
|
||||
'--node',
|
||||
'--strict',
|
||||
'--travis',
|
||||
'--yarn',
|
||||
'--no-circleci',
|
||||
'--no-cspell',
|
||||
'--no-editorconfig',
|
||||
'--no-immutable',
|
||||
'--no-functional',
|
||||
'--no-install',
|
||||
'--no-vscode'
|
||||
'--no-vscode',
|
||||
];
|
||||
const opts = await checkArgs();
|
||||
const currentVersion = meow('').pkg.version;
|
||||
const currentVersion = meow('').pkg.version as string;
|
||||
t.deepEqual(opts, {
|
||||
appveyor: true,
|
||||
circleci: false,
|
||||
description: '',
|
||||
cspell: false,
|
||||
description: 'example description',
|
||||
domDefinitions: true,
|
||||
editorconfig: false,
|
||||
immutable: false,
|
||||
functional: false,
|
||||
install: false,
|
||||
nodeDefinitions: true,
|
||||
projectName: 'example-project',
|
||||
@@ -99,75 +102,78 @@ test('checkArgs returns the right options', async t => {
|
||||
starterVersion: currentVersion,
|
||||
strict: true,
|
||||
travis: true,
|
||||
vscode: false
|
||||
vscode: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('checkArgs always returns a TypescriptStarterRequiredConfig, even in interactive mode', async t => {
|
||||
test('checkArgs always returns a TypescriptStarterRequiredConfig, even in interactive mode', async (t) => {
|
||||
pretendLatestVersionIs('1.0.0');
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
process.argv = ['path/to/node', 'path/to/typescript-starter'];
|
||||
const opts = await checkArgs();
|
||||
t.true(typeof opts.install === 'boolean');
|
||||
t.true(typeof opts.starterVersion === 'string');
|
||||
});
|
||||
|
||||
test('only accepts valid package names', async t => {
|
||||
test('only accepts valid package names', async (t) => {
|
||||
t.true(validateName('package-name'));
|
||||
t.true(validateName('package-name-2'));
|
||||
t.true(validateName('@example/package-name-2'));
|
||||
});
|
||||
|
||||
test('ascii art shows if stdout has 85+ columns', async t => {
|
||||
test('ascii art shows if stdout has 85+ columns', async (t) => {
|
||||
const jumbo = getIntro(100);
|
||||
const snippet = `| __| | | | '_ \\ / _ \\/ __|/ __| '__| | '_ \\|`;
|
||||
t.regex(jumbo, new RegExp(snippet));
|
||||
});
|
||||
|
||||
test('small ascii art shows if stdout has 74-84 columns', async t => {
|
||||
test('small ascii art shows if stdout has 74-84 columns', async (t) => {
|
||||
const jumbo = getIntro(80);
|
||||
const snippet = `| _| || | '_ \\/ -_|_-</ _| '_| | '_ \\ _|`;
|
||||
t.regex(jumbo, new RegExp(snippet));
|
||||
});
|
||||
|
||||
const mockErr = (code: number = 1, name: string = 'ERR') =>
|
||||
const mockErr = (code = 1, name = 'ERR') =>
|
||||
((() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const err: any = new Error();
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
err.exitCode = code;
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
err.exitCodeName = name;
|
||||
// eslint-disable-next-line functional/no-throw-statement
|
||||
throw err;
|
||||
}) as any) as typeof execa;
|
||||
}) as unknown) as typeof execa;
|
||||
|
||||
test('cloneRepo: errors when Git is not installed on PATH', async t => {
|
||||
test('cloneRepo: errors when Git is not installed on PATH', async (t) => {
|
||||
const error = await t.throwsAsync(
|
||||
cloneRepo(mockErr(1, 'ENOENT'))({ repo: 'r', branch: '.' }, 'd', 'p')
|
||||
);
|
||||
t.regex(error.message, /Git is not installed on your PATH/);
|
||||
});
|
||||
|
||||
test('cloneRepo: throws when clone fails', async t => {
|
||||
test('cloneRepo: throws when clone fails', async (t) => {
|
||||
const error = await t.throwsAsync(
|
||||
cloneRepo(mockErr(128))({ repo: 'r', branch: 'b' }, 'd', 'p')
|
||||
);
|
||||
t.regex(error.message, /Git clone failed./);
|
||||
});
|
||||
|
||||
test('cloneRepo: throws when rev-parse fails', async t => {
|
||||
// tslint:disable-next-line:prefer-const no-let
|
||||
test('cloneRepo: throws when rev-parse fails', async (t) => {
|
||||
// eslint-disable-next-line functional/no-let
|
||||
let calls = 0;
|
||||
const mock = ((async () => {
|
||||
calls++;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return calls === 1 ? {} : (mockErr(128) as any)();
|
||||
}) as any) as typeof execa;
|
||||
}) as unknown) as typeof execa;
|
||||
const error = await t.throwsAsync(
|
||||
cloneRepo(mock)({ repo: 'r', branch: 'b' }, 'd', 'p')
|
||||
);
|
||||
t.regex(error.message, /Git rev-parse failed./);
|
||||
});
|
||||
|
||||
test('getGithubUsername: returns found users', async t => {
|
||||
test('getGithubUsername: returns found users', async (t) => {
|
||||
const mockFetcher = async (email: string) => email.split('@')[0];
|
||||
const username: string = await getGithubUsername(mockFetcher)(
|
||||
'bitjson@github.com'
|
||||
@@ -175,7 +181,7 @@ test('getGithubUsername: returns found users', async t => {
|
||||
t.is(username, 'bitjson');
|
||||
});
|
||||
|
||||
test("getGithubUsername: returns placeholder if user doesn't have Git user.email set", async t => {
|
||||
test("getGithubUsername: returns placeholder if user doesn't have Git user.email set", async (t) => {
|
||||
const mockFetcher = async () => t.fail();
|
||||
const username: string = await getGithubUsername(mockFetcher)(
|
||||
Placeholders.email
|
||||
@@ -183,8 +189,9 @@ test("getGithubUsername: returns placeholder if user doesn't have Git user.email
|
||||
t.is(username, Placeholders.username);
|
||||
});
|
||||
|
||||
test('getGithubUsername: returns placeholder if not found', async t => {
|
||||
test('getGithubUsername: returns placeholder if not found', async (t) => {
|
||||
const mockFetcher = async () => {
|
||||
// eslint-disable-next-line functional/no-throw-statement
|
||||
throw new Error();
|
||||
};
|
||||
const username: string = await getGithubUsername(mockFetcher)(
|
||||
@@ -193,72 +200,72 @@ test('getGithubUsername: returns placeholder if not found', async t => {
|
||||
t.is(username, Placeholders.username);
|
||||
});
|
||||
|
||||
test('getUserInfo: suppresses errors and returns empty strings', async t => {
|
||||
test('getUserInfo: suppresses errors and returns empty strings', async (t) => {
|
||||
const result = await getUserInfo(mockErr(1))();
|
||||
t.deepEqual(result, {
|
||||
gitEmail: Placeholders.email,
|
||||
gitName: Placeholders.name
|
||||
gitName: Placeholders.name,
|
||||
});
|
||||
});
|
||||
|
||||
test('getUserInfo: returns results properly', async t => {
|
||||
test('getUserInfo: returns results properly', async (t) => {
|
||||
const mock = ((async () => {
|
||||
return {
|
||||
stdout: 'result'
|
||||
stdout: 'result',
|
||||
};
|
||||
}) as any) as typeof execa;
|
||||
}) as unknown) as typeof execa;
|
||||
const result = await getUserInfo(mock)();
|
||||
t.deepEqual(result, {
|
||||
gitEmail: 'result',
|
||||
gitName: 'result'
|
||||
gitName: 'result',
|
||||
});
|
||||
});
|
||||
|
||||
test('initialCommit: throws generated errors', async t => {
|
||||
test('initialCommit: throws generated errors', async (t) => {
|
||||
const error = await t.throwsAsync<execa.ExecaError>(
|
||||
initialCommit(mockErr(1))('deadbeef', 'fail')
|
||||
);
|
||||
t.is(error.exitCode, 1);
|
||||
});
|
||||
|
||||
test('initialCommit: spawns 3 times', async t => {
|
||||
test('initialCommit: spawns 3 times', async (t) => {
|
||||
t.plan(4);
|
||||
const mock = ((async () => {
|
||||
t.pass();
|
||||
}) as any) as typeof execa;
|
||||
}) as unknown) as typeof execa;
|
||||
await t.notThrowsAsync(initialCommit(mock)('commit', 'dir'));
|
||||
});
|
||||
|
||||
test('install: uses the correct runner', async t => {
|
||||
test('install: uses the correct runner', async (t) => {
|
||||
const mock = ((async (runner: Runner) => {
|
||||
runner === Runner.Yarn ? t.pass() : t.fail();
|
||||
}) as any) as typeof execa;
|
||||
}) as unknown) as typeof execa;
|
||||
await install(mock)(Runner.Yarn, 'pass');
|
||||
});
|
||||
|
||||
test('install: throws pretty error on failure', async t => {
|
||||
test('install: throws pretty error on failure', async (t) => {
|
||||
const error = await t.throwsAsync(install(mockErr())(Runner.Npm, 'fail'));
|
||||
t.is(error.message, "Installation failed. You'll need to install manually.");
|
||||
});
|
||||
|
||||
test("getRepoInfo: returns defaults when TYPESCRIPT_STARTER_REPO_URL/BRANCH aren't set", async t => {
|
||||
test("getRepoInfo: returns defaults when TYPESCRIPT_STARTER_REPO_URL/BRANCH aren't set", async (t) => {
|
||||
const thisRelease = '9000.0.1';
|
||||
t.deepEqual(getRepoInfo(thisRelease), {
|
||||
branch: `v${thisRelease}`,
|
||||
repo: 'https://github.com/bitjson/typescript-starter.git'
|
||||
repo: 'https://github.com/bitjson/typescript-starter.git',
|
||||
});
|
||||
const url = 'https://another/repo';
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
process.env.TYPESCRIPT_STARTER_REPO_URL = url;
|
||||
t.deepEqual(getRepoInfo(thisRelease), {
|
||||
branch: `master`,
|
||||
repo: url
|
||||
repo: url,
|
||||
});
|
||||
const branch = 'test';
|
||||
// tslint:disable-next-line:no-object-mutation
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
process.env.TYPESCRIPT_STARTER_REPO_BRANCH = branch;
|
||||
t.deepEqual(getRepoInfo(thisRelease), {
|
||||
branch,
|
||||
repo: url
|
||||
repo: url,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,24 +1,36 @@
|
||||
// tslint:disable:no-console no-if-statement no-expression-statement
|
||||
import { readFileSync, renameSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import del from 'del';
|
||||
import { readFileSync, renameSync, writeFileSync } from 'fs';
|
||||
import ora from 'ora';
|
||||
import { join } from 'path';
|
||||
import replace from 'replace-in-file';
|
||||
import { replaceInFile } from 'replace-in-file';
|
||||
|
||||
import { Placeholders, Tasks } from './tasks';
|
||||
import { normalizePath, Runner, TypescriptStarterOptions } from './utils';
|
||||
|
||||
const readPackageJson = (path: string) =>
|
||||
JSON.parse(readFileSync(path, 'utf8'));
|
||||
|
||||
const writePackageJson = (path: string, pkg: unknown) => {
|
||||
// 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';
|
||||
return writeFileSync(path, stringified);
|
||||
};
|
||||
|
||||
export async function typescriptStarter(
|
||||
{
|
||||
appveyor,
|
||||
circleci,
|
||||
cspell,
|
||||
description,
|
||||
domDefinitions,
|
||||
editorconfig,
|
||||
email,
|
||||
fullName,
|
||||
githubUsername,
|
||||
immutable,
|
||||
functional,
|
||||
install,
|
||||
nodeDefinitions,
|
||||
projectName,
|
||||
@@ -27,7 +39,7 @@ export async function typescriptStarter(
|
||||
strict,
|
||||
travis,
|
||||
vscode,
|
||||
workingDirectory
|
||||
workingDirectory,
|
||||
}: TypescriptStarterOptions,
|
||||
tasks: Tasks
|
||||
): Promise<void> {
|
||||
@@ -47,29 +59,35 @@ export async function typescriptStarter(
|
||||
const pkgPath = join(projectPath, 'package.json');
|
||||
|
||||
const keptDevDeps: ReadonlyArray<string> = [
|
||||
'@bitjson/npm-scripts-info',
|
||||
'@bitjson/typedoc',
|
||||
'@ava/typescript',
|
||||
'@istanbuljs/nyc-config-typescript',
|
||||
'@typescript-eslint/eslint-plugin',
|
||||
'@typescript-eslint/parser',
|
||||
'ava',
|
||||
'codecov',
|
||||
'cspell',
|
||||
'cz-conventional-changelog',
|
||||
'eslint',
|
||||
'eslint-config-prettier',
|
||||
'eslint-plugin-eslint-comments',
|
||||
...(functional ? ['eslint-plugin-functional'] : []),
|
||||
'eslint-plugin-import',
|
||||
'gh-pages',
|
||||
'npm-run-all',
|
||||
'npm-scripts-info',
|
||||
'nyc',
|
||||
'open-cli',
|
||||
'prettier',
|
||||
'standard-version',
|
||||
'trash-cli',
|
||||
'tslint',
|
||||
'tslint-config-prettier',
|
||||
'tslint-immutable',
|
||||
'ts-node',
|
||||
'typedoc',
|
||||
'typescript'
|
||||
'typescript',
|
||||
];
|
||||
|
||||
// dependencies to retain for Node.js applications
|
||||
const nodeKeptDeps: ReadonlyArray<string> = ['sha.js'];
|
||||
/**
|
||||
* dependencies to retain for Node.js applications
|
||||
*/
|
||||
const nodeKeptDeps: ReadonlyArray<string> = ['@bitauth/libauth'];
|
||||
|
||||
const filterAllBut = (
|
||||
keep: ReadonlyArray<string>,
|
||||
@@ -90,7 +108,6 @@ export async function typescriptStarter(
|
||||
: {},
|
||||
description,
|
||||
devDependencies: filterAllBut(keptDevDeps, pkg.devDependencies),
|
||||
// tslint:disable-next-line:readonly-array
|
||||
keywords: [],
|
||||
name: projectName,
|
||||
repository: `https://github.com/${githubUsername}/${projectName}`,
|
||||
@@ -98,40 +115,43 @@ export async function typescriptStarter(
|
||||
runner === Runner.Yarn
|
||||
? {
|
||||
...pkg.scripts,
|
||||
preinstall: `node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('${projectName} must be installed with Yarn: https://yarnpkg.com/')\"`
|
||||
'reset-hard': `git clean -dfx && git reset --hard && yarn`,
|
||||
}
|
||||
: { ...pkg.scripts },
|
||||
version: '1.0.0'
|
||||
version: '1.0.0',
|
||||
};
|
||||
|
||||
// tslint:disable:no-delete no-object-mutation
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
delete newPkg.bin;
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
delete newPkg.NOTE;
|
||||
// tslint:enable:no-delete no-object-mutation
|
||||
// eslint-disable-next-line functional/immutable-data
|
||||
delete newPkg.NOTE_2;
|
||||
|
||||
writePackageJson(pkgPath, newPkg);
|
||||
spinnerPackage.succeed();
|
||||
|
||||
const spinnerGitignore = ora('Updating .gitignore').start();
|
||||
if (runner === Runner.Yarn) {
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, '.gitignore'),
|
||||
from: 'yarn.lock',
|
||||
to: 'package-lock.json'
|
||||
to: 'package-lock.json',
|
||||
});
|
||||
}
|
||||
spinnerGitignore.succeed();
|
||||
|
||||
const spinnerLicense = ora('Updating LICENSE').start();
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'LICENSE'),
|
||||
// cspell: disable-next-line
|
||||
from: 'Jason Dreyzehner',
|
||||
to: fullName
|
||||
to: fullName,
|
||||
});
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'LICENSE'),
|
||||
from: '2017',
|
||||
to: new Date().getUTCFullYear().toString()
|
||||
to: new Date().getUTCFullYear().toString(),
|
||||
});
|
||||
spinnerLicense.succeed();
|
||||
|
||||
@@ -143,7 +163,6 @@ export async function typescriptStarter(
|
||||
normalizePath(join(projectPath, 'package-lock.json')),
|
||||
normalizePath(join(projectPath, 'bin')),
|
||||
normalizePath(join(projectPath, 'src', 'cli')),
|
||||
normalizePath(join(projectPath, 'src', 'types', 'cli.d.ts'))
|
||||
]);
|
||||
if (!appveyor) {
|
||||
del([normalizePath(join(projectPath, 'appveyor.yml'))]);
|
||||
@@ -151,6 +170,21 @@ export async function typescriptStarter(
|
||||
if (!circleci) {
|
||||
del([normalizePath(join(projectPath, '.circleci'))]);
|
||||
}
|
||||
if (!cspell) {
|
||||
del([normalizePath(join(projectPath, '.cspell.json'))]);
|
||||
if (vscode) {
|
||||
await replaceInFile({
|
||||
files: join(projectPath, '.vscode', 'settings.json'),
|
||||
from: ` "cSpell.userWords": [], // only use words from .cspell.json\n`,
|
||||
to: '',
|
||||
});
|
||||
await replaceInFile({
|
||||
files: join(projectPath, '.vscode', 'settings.json'),
|
||||
from: ` "cSpell.enabled": true,\n`,
|
||||
to: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!travis) {
|
||||
del([normalizePath(join(projectPath, '.travis.yml'))]);
|
||||
}
|
||||
@@ -163,16 +197,16 @@ export async function typescriptStarter(
|
||||
spinnerDelete.succeed();
|
||||
|
||||
const spinnerTsconfigModule = ora('Removing traces of the CLI').start();
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'tsconfig.module.json'),
|
||||
from: /,\s+\/\/ typescript-starter:[\s\S]*"src\/cli\/\*\*\/\*\.ts"/,
|
||||
to: ''
|
||||
to: '',
|
||||
});
|
||||
if (vscode) {
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, '.vscode', 'launch.json'),
|
||||
from: /,[\s]*\/\/ --- cut here ---[\s\S]*]/,
|
||||
to: ']'
|
||||
to: ']',
|
||||
});
|
||||
}
|
||||
spinnerTsconfigModule.succeed();
|
||||
@@ -182,67 +216,74 @@ export async function typescriptStarter(
|
||||
join(projectPath, 'README-starter.md'),
|
||||
join(projectPath, 'README.md')
|
||||
);
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'README.md'),
|
||||
from: '[package-name]',
|
||||
to: projectName
|
||||
to: projectName,
|
||||
});
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'README.md'),
|
||||
from: '[description]',
|
||||
to: description
|
||||
to: description,
|
||||
});
|
||||
spinnerReadme.succeed();
|
||||
|
||||
if (!strict) {
|
||||
const spinnerStrict = ora(`tsconfig: disable strict`).start();
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'tsconfig.json'),
|
||||
from: '"strict": true',
|
||||
to: '// "strict": true'
|
||||
to: '// "strict": true',
|
||||
});
|
||||
spinnerStrict.succeed();
|
||||
}
|
||||
|
||||
if (!domDefinitions) {
|
||||
const spinnerDom = ora(`tsconfig: don't include "dom" lib`).start();
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'tsconfig.json'),
|
||||
from: '"lib": ["es2017", "dom"]',
|
||||
to: '"lib": ["es2017"]'
|
||||
to: '"lib": ["es2017"]',
|
||||
});
|
||||
spinnerDom.succeed();
|
||||
}
|
||||
|
||||
if (!nodeDefinitions) {
|
||||
const spinnerNode = ora(`tsconfig: don't include "node" types`).start();
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'tsconfig.json'),
|
||||
from: '"types": ["node"]',
|
||||
to: '"types": []'
|
||||
to: '"types": []',
|
||||
});
|
||||
await replace({
|
||||
await replaceInFile({
|
||||
files: join(projectPath, 'src', 'index.ts'),
|
||||
from: /^export[\S\s]*hash';\s*/,
|
||||
to: ''
|
||||
to: '',
|
||||
});
|
||||
await del([
|
||||
normalizePath(join(projectPath, 'src', 'lib', 'hash.ts')),
|
||||
normalizePath(join(projectPath, 'src', 'lib', 'hash.spec.ts')),
|
||||
normalizePath(join(projectPath, 'src', 'lib', 'async.ts')),
|
||||
normalizePath(join(projectPath, 'src', 'lib', 'async.spec.ts'))
|
||||
normalizePath(join(projectPath, 'src', 'lib', 'async.spec.ts')),
|
||||
]);
|
||||
spinnerNode.succeed();
|
||||
}
|
||||
|
||||
if (!immutable) {
|
||||
const spinnerTslint = ora(`tslint: disable tslint-immutable`).start();
|
||||
await replace({
|
||||
files: join(projectPath, 'tslint.json'),
|
||||
from: /,[\s]*\/\* tslint-immutable rules \*\/[\s\S]*\/\* end tslint-immutable rules \*\//,
|
||||
to: ''
|
||||
if (!functional) {
|
||||
const spinnerEslint = ora(
|
||||
`eslint: disable eslint-plugin-functional`
|
||||
).start();
|
||||
await replaceInFile({
|
||||
files: join(projectPath, '.eslintrc.json'),
|
||||
from: '"plugins": ["import", "eslint-comments", "functional"]',
|
||||
to: '"plugins": ["import", "eslint-comments"]',
|
||||
});
|
||||
spinnerTslint.succeed();
|
||||
await replaceInFile({
|
||||
files: join(projectPath, '.eslintrc.json'),
|
||||
from: '"plugin:functional/lite",\n',
|
||||
to: '',
|
||||
});
|
||||
spinnerEslint.succeed();
|
||||
}
|
||||
|
||||
if (install) {
|
||||
@@ -261,13 +302,3 @@ export async function typescriptStarter(
|
||||
|
||||
console.log(`\n${chalk.blue.bold(`Created ${projectName} 🎉`)}\n`);
|
||||
}
|
||||
|
||||
const readPackageJson = (path: string) =>
|
||||
JSON.parse(readFileSync(path, 'utf8'));
|
||||
|
||||
const 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';
|
||||
return writeFileSync(path, stringified);
|
||||
};
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import chalk from 'chalk';
|
||||
import { existsSync } from 'fs';
|
||||
import gradient from 'gradient-string';
|
||||
import { normalize } from 'path';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import gradient from 'gradient-string';
|
||||
import validateNpmPackageName from 'validate-npm-package-name';
|
||||
export enum Runner {
|
||||
Npm = 'npm',
|
||||
Yarn = 'yarn'
|
||||
Yarn = 'yarn',
|
||||
}
|
||||
|
||||
export interface TypescriptStarterCLIOptions {
|
||||
export type TypescriptStarterCLIOptions = {
|
||||
readonly appveyor: boolean;
|
||||
readonly circleci: boolean;
|
||||
readonly cspell: boolean;
|
||||
readonly description: string;
|
||||
readonly domDefinitions: boolean;
|
||||
readonly editorconfig: boolean;
|
||||
readonly immutable: boolean;
|
||||
readonly functional: boolean;
|
||||
readonly install: boolean;
|
||||
readonly nodeDefinitions: boolean;
|
||||
readonly projectName: string;
|
||||
@@ -22,12 +24,12 @@ export interface TypescriptStarterCLIOptions {
|
||||
readonly strict: boolean;
|
||||
readonly travis: boolean;
|
||||
readonly vscode: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export interface TypescriptStarterRequiredConfig {
|
||||
export type TypescriptStarterRequiredConfig = {
|
||||
readonly starterVersion: string;
|
||||
readonly install: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type TypescriptStarterUserOptions = TypescriptStarterCLIOptions &
|
||||
TypescriptStarterRequiredConfig;
|
||||
@@ -36,7 +38,7 @@ export type TypescriptStarterArgsOptions =
|
||||
| TypescriptStarterUserOptions
|
||||
| TypescriptStarterRequiredConfig;
|
||||
|
||||
export interface TypescriptStarterInferredOptions {
|
||||
export type TypescriptStarterInferredOptions = {
|
||||
readonly githubUsername: string;
|
||||
readonly fullName: string;
|
||||
readonly email: string;
|
||||
@@ -45,13 +47,12 @@ export interface TypescriptStarterInferredOptions {
|
||||
readonly branch: string;
|
||||
};
|
||||
readonly workingDirectory: string;
|
||||
}
|
||||
};
|
||||
|
||||
export interface TypescriptStarterOptions
|
||||
extends TypescriptStarterCLIOptions,
|
||||
TypescriptStarterInferredOptions {
|
||||
// readonly starterVersion?: string;
|
||||
}
|
||||
export type TypescriptStarterOptions = TypescriptStarterCLIOptions &
|
||||
TypescriptStarterInferredOptions & {
|
||||
// readonly starterVersion?: string;
|
||||
};
|
||||
|
||||
export function hasCLIOptions(
|
||||
opts: TypescriptStarterArgsOptions
|
||||
@@ -96,5 +97,5 @@ _ _ _ _ _
|
||||
* On Windows, normalize returns "\\" as the path separator.
|
||||
* This method normalizes with POSIX.
|
||||
*/
|
||||
export const normalizePath = (path: string) =>
|
||||
export const normalizePath = (path: string): string =>
|
||||
normalize(path).replace(/\\/g, '/');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// tslint:disable:no-expression-statement
|
||||
import test from 'ava';
|
||||
|
||||
import { asyncABC } from './async';
|
||||
|
||||
test('getABC', async t => {
|
||||
test('getABC', async (t) => {
|
||||
t.deepEqual(await asyncABC(), ['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* A sample async function (to demo Typescript's es7 async/await downleveling).
|
||||
* A sample async function (to demo Typescript's es7 async/await down-leveling).
|
||||
*
|
||||
* ### Example (es imports)
|
||||
* ```js
|
||||
@@ -15,18 +15,18 @@
|
||||
* // => ['a','b','c']
|
||||
* ```
|
||||
*
|
||||
* @returns a Promise which should contain `['a','b','c']`
|
||||
* @returns a Promise which should contain `['a','b','c']`
|
||||
*/
|
||||
export async function asyncABC(): Promise<ReadonlyArray<string>> {
|
||||
function somethingSlow(index: 0 | 1 | 2): Promise<string> {
|
||||
export const asyncABC = async () => {
|
||||
const somethingSlow = (index: 0 | 1 | 2) => {
|
||||
const storage = 'abc'.charAt(index);
|
||||
return new Promise<string>(resolve =>
|
||||
return new Promise<string>((resolve) =>
|
||||
// later...
|
||||
resolve(storage)
|
||||
);
|
||||
}
|
||||
};
|
||||
const a = await somethingSlow(0);
|
||||
const b = await somethingSlow(1);
|
||||
const c = await somethingSlow(2);
|
||||
return [a, b, c];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// tslint:disable:no-expression-statement no-object-mutation
|
||||
import test from 'ava';
|
||||
|
||||
import { sha256, sha256Native } from './hash';
|
||||
|
||||
test(
|
||||
'sha256',
|
||||
(t, input: string, expected: string) => {
|
||||
t.is(sha256(input), expected);
|
||||
async (t, input: string, expected: string) => {
|
||||
t.is(await sha256(input), expected);
|
||||
t.is(sha256Native(input), expected);
|
||||
},
|
||||
'test',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createHash } from 'crypto';
|
||||
import shaJs from 'sha.js';
|
||||
|
||||
import { binToHex, instantiateSha256, utf8ToBin } from '@bitauth/libauth';
|
||||
|
||||
/**
|
||||
* Calculate the sha256 digest of a string.
|
||||
@@ -7,32 +8,34 @@ import shaJs from 'sha.js';
|
||||
* ### Example (es imports)
|
||||
* ```js
|
||||
* import { sha256 } from 'typescript-starter'
|
||||
* sha256('test')
|
||||
* // => '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
|
||||
*
|
||||
* (async () => {
|
||||
* console.log(await sha256('test'));
|
||||
* // => '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param message - the string to hash
|
||||
* @returns sha256 message digest
|
||||
*/
|
||||
export function sha256(message: string): string {
|
||||
return shaJs('sha256')
|
||||
.update(message)
|
||||
.digest('hex');
|
||||
}
|
||||
export const sha256 = async (message: string) => {
|
||||
const sha256 = await instantiateSha256();
|
||||
return binToHex(sha256.hash(utf8ToBin(message)));
|
||||
};
|
||||
|
||||
/**
|
||||
* A faster implementation of [[sha256]] which requires the native Node.js module. Browser consumers should use [[sha256]], instead.
|
||||
* A synchronous implementation of `sha256` which uses the native Node.js
|
||||
* module. (Browser consumers should use the `sha256` method.)
|
||||
*
|
||||
* ### Example (es imports)
|
||||
* ```js
|
||||
* import { sha256Native as sha256 } from 'typescript-starter'
|
||||
* sha256('test')
|
||||
* console.log(sha256('test'));
|
||||
* // => '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
|
||||
* ```
|
||||
*
|
||||
* @param message - the string to hash
|
||||
* @returns sha256 message digest
|
||||
*/
|
||||
export function sha256Native(message: string): string {
|
||||
return createHash('sha256')
|
||||
.update(message)
|
||||
.digest('hex');
|
||||
}
|
||||
export const sha256Native = (message: string) => {
|
||||
return createHash('sha256').update(message).digest('hex');
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// tslint:disable:no-expression-statement
|
||||
import test from 'ava';
|
||||
|
||||
import { double, power } from './number';
|
||||
|
||||
test('double', t => {
|
||||
test('double', (t) => {
|
||||
t.is(double(2), 4);
|
||||
});
|
||||
|
||||
test('power', t => {
|
||||
test('power', (t) => {
|
||||
t.is(power(2, 4), 16);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Multiplies a value by 2. (Also a full example of Typedoc's functionality.)
|
||||
* Multiplies a value by 2. (Also a full example of TypeDoc's functionality.)
|
||||
*
|
||||
* ### Example (es module)
|
||||
* ```js
|
||||
@@ -15,16 +15,17 @@
|
||||
* // => 8
|
||||
* ```
|
||||
*
|
||||
* @param value Comment describing the `value` parameter.
|
||||
* @returns Comment describing the return type.
|
||||
* @anotherNote Some other value.
|
||||
* @param value - Comment describing the `value` parameter.
|
||||
* @returns Comment describing the return type.
|
||||
* @anotherNote Some other value.
|
||||
*/
|
||||
export function double(value: number): number {
|
||||
export const double = (value: number) => {
|
||||
return value * 2;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Raise the value of the first parameter to the power of the second using the es7 `**` operator.
|
||||
* Raise the value of the first parameter to the power of the second using the
|
||||
* es7 exponentiation operator (`**`).
|
||||
*
|
||||
* ### Example (es module)
|
||||
* ```js
|
||||
@@ -39,8 +40,12 @@ export function double(value: number): number {
|
||||
* console.log(power(2,3))
|
||||
* // => 8
|
||||
* ```
|
||||
* @param base - the base to exponentiate
|
||||
* @param exponent - the power to which to raise the base
|
||||
*/
|
||||
export function power(base: number, exponent: number): number {
|
||||
// This is a proposed es7 operator, which should be transpiled by Typescript
|
||||
export const power = (base: number, exponent: number) => {
|
||||
/**
|
||||
* This es7 exponentiation operator is transpiled by TypeScript
|
||||
*/
|
||||
return base ** exponent;
|
||||
}
|
||||
};
|
||||
|
||||
8
src/types/cli.d.ts
vendored
8
src/types/cli.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
// We develop typescript-starter with the `strict` compiler option to ensure it
|
||||
// works out of the box for downstream users. This file is deleted by the CLI,
|
||||
// so its purpose is just to squelch noImplicitAny errors.
|
||||
declare module 'github-username';
|
||||
declare module 'gradient-string';
|
||||
declare module 'md5-file';
|
||||
declare module 'replace-in-file';
|
||||
declare module 'validate-npm-package-name';
|
||||
30
src/types/example.d.ts
vendored
30
src/types/example.d.ts
vendored
@@ -10,25 +10,15 @@
|
||||
* for the package, you may want to declare your own. (If you're using the
|
||||
* `noImplicitAny` compiler options, you'll be required to declare it.)
|
||||
*
|
||||
* This is an example type definition for the `sha.js` package, used in hash.ts.
|
||||
*
|
||||
* (This definition was primarily extracted from:
|
||||
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v8/index.d.ts
|
||||
* This is an example type definition which allows import from `module-name`,
|
||||
* e.g.:
|
||||
* ```ts
|
||||
* import something from 'module-name';
|
||||
* something();
|
||||
* ```
|
||||
*/
|
||||
declare module 'sha.js' {
|
||||
export default function shaJs(algorithm: string): Hash;
|
||||
|
||||
type Utf8AsciiLatin1Encoding = 'utf8' | 'ascii' | 'latin1';
|
||||
type HexBase64Latin1Encoding = 'latin1' | 'hex' | 'base64';
|
||||
|
||||
export interface Hash extends NodeJS.ReadWriteStream {
|
||||
// tslint:disable:no-method-signature
|
||||
update(
|
||||
data: string | Buffer | DataView,
|
||||
inputEncoding?: Utf8AsciiLatin1Encoding
|
||||
): Hash;
|
||||
digest(): Buffer;
|
||||
digest(encoding: HexBase64Latin1Encoding): string;
|
||||
// tslint:enable:no-method-signature
|
||||
}
|
||||
declare module 'module-name' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const whatever: any;
|
||||
export = whatever;
|
||||
}
|
||||
|
||||
34
tslint.json
34
tslint.json
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"extends": ["tslint:latest", "tslint-config-prettier", "tslint-immutable"],
|
||||
"rules": {
|
||||
"interface-name": [true, "never-prefix"],
|
||||
// TODO: allow devDependencies only in **/*.spec.ts files:
|
||||
// waiting on https://github.com/palantir/tslint/pull/3708
|
||||
"no-implicit-dependencies": [true, "dev"],
|
||||
|
||||
/* tslint-immutable rules */
|
||||
// Recommended built-in rules
|
||||
"no-var-keyword": true,
|
||||
"no-parameter-reassignment": true,
|
||||
"typedef": [true, "call-signature"],
|
||||
|
||||
// Immutability rules
|
||||
"readonly-keyword": true,
|
||||
"readonly-array": true,
|
||||
"no-let": true,
|
||||
"no-object-mutation": true,
|
||||
"no-delete": true,
|
||||
"no-method-signature": true,
|
||||
|
||||
// Functional style rules
|
||||
"no-this": true,
|
||||
"no-class": true,
|
||||
"no-mixed-interface": true,
|
||||
"no-expression-statement": [
|
||||
true,
|
||||
{ "ignore-prefix": ["console.", "process.exit"] }
|
||||
],
|
||||
"no-if-statement": true
|
||||
/* end tslint-immutable rules */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user