diff --git a/.circleci/config.yml b/.circleci/config.yml index e29b46f..14a26b8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,6 +18,7 @@ jobs: - node_modules key: v1-dependencies-{{ checksum "package.json" }} - run: npm test + - run: npm run check-integration-tests - run: npm run cov:send - run: npm run cov:check 'node-12': @@ -35,6 +36,7 @@ jobs: - node_modules key: v1-dependencies-{{ checksum "package.json" }} - run: npm test + - run: npm run check-integration-tests - run: npm run cov:send - run: npm run cov:check 'node-latest': @@ -52,6 +54,7 @@ jobs: - node_modules key: v1-dependencies-{{ checksum "package.json" }} - run: npm test + - run: npm run check-integration-tests - run: npm run cov:send - run: npm run cov:check diff --git a/.cspell.json b/.cspell.json index 0c738c7..59c785d 100644 --- a/.cspell.json +++ b/.cspell.json @@ -11,6 +11,7 @@ "commitlint", "dependabot", "editorconfig", + "esnext", "execa", "exponentiate", "globby", diff --git a/.gitignore b/.gitignore index f14f6bc..c0e9c45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.cache +diff .idea/* .nyc_output build diff --git a/README.md b/README.md index 7719cb7..3c430a3 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,8 @@ npm test npm link ``` +### Manual testing + 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: ``` @@ -327,6 +329,24 @@ TYPESCRIPT_STARTER_REPO_URL='https://github.com/YOUR_USERNAME/typescript-starter If `TYPESCRIPT_STARTER_REPO_BRANCH` is not provided, it will default to `master`. +### Debug in VS Code + 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. +### Integration Test Result Diffs + +You can compare the integration test results before and after a change by running `check-cli` before and after applying your changes: + +```sh +npm run check-cli +``` + +Each time you run `check-cli`, the test results will be committed to the `diff` directory, allowing you to easily review the differences with `git diff HEAD` or an interactive Git client like [GitHub for Desktop](https://desktop.github.com/) or [SourceTree](https://www.sourcetreeapp.com/). + +If you already have changes in the working directory, try: + +```sh +git stash && npm run check-cli && git stash pop && npm run check-cli +``` + diff --git a/package.json b/package.json index 53a6c17..8c08133 100644 --- a/package.json +++ b/package.json @@ -34,12 +34,21 @@ "build:module": "tsc -p tsconfig.module.json", "fix": "run-s fix:*", "fix:prettier": "prettier \"src/**/*.ts\" --write", - "fix:lint": "eslint . --ext .ts --fix", + "fix:lint": "eslint src --ext .ts --fix", "test": "run-s build test:*", "test:lint": "eslint src --ext .ts", "test:prettier": "prettier \"src/**/*.ts\" --list-different", "test:spelling": "cspell \"{README.md,.github/*.md,src/**/*.ts}\"", "test:unit": "nyc --silent ava", + "check-cli": "run-s test diff-integration-tests check-integration-tests", + "check-integration-tests": "run-s check-integration-test:*", + "check-integration-test:1": "cd test/test-1 && npm i && npm run build && npm run test", + "check-integration-test:2": "cd test/test-2 && yarn && yarn build && yarn test", + "check-integration-test:3": "cd test/test-3 && yarn && yarn build && yarn test", + "check-integration-test:4": "cd test/test-4 && npm i && npm run build && npm run test", + "check-integration-test:5": "cd test/test-5 && npm i && npm run build && npm run test", + "check-integration-test:6": "cd test/test-6 && yarn && yarn build && yarn test", + "diff-integration-tests": "mkdir -p diff && rm -rf diff/test && cp -r test diff/test && rm -rf diff/test/test-*/.git && cd diff && git init --quiet && git add -A && git commit --quiet --no-verify --allow-empty -m 'WIP' && echo '\\n\\nCommitted most recent integration test output in the \"diff\" directory. Review the changes with \"cd diff && git diff HEAD\" or your preferred git diff viewer.'", "watch:build": "tsc -p tsconfig.json -w", "watch:test": "nyc --silent ava --watch", "cov": "run-s build test:unit cov:html cov:lcov && open-cli coverage/index.html", @@ -125,10 +134,12 @@ } }, "files": [ + "!diff", "!test", "!build/module/**" ], "ignoredByWatcher": [ + "diff", "test" ] }, diff --git a/src/cli/tests/cli.integration.spec.ts b/src/cli/tests/cli.integration.spec.ts index 82b3ffe..b62d40c 100644 --- a/src/cli/tests/cli.integration.spec.ts +++ b/src/cli/tests/cli.integration.spec.ts @@ -158,14 +158,14 @@ test(`${TestDirectories.one}: parses CLI arguments, handles default options`, as const map = await hashAllTheThings(TestDirectories.one); t.deepEqual(map, { 'test-1/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba', - 'test-1/.cspell.json': '5b2e0ad337bc05ef2ce5635270d983fd', + 'test-1/.cspell.json': '61bbd8ed98375a94cbd1063ab498959f', 'test-1/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4', 'test-1/.eslintrc.json': '4e74756d24eaccb7f28d4999e4bd6f0d', 'test-1/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08', 'test-1/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e', 'test-1/.github/PULL_REQUEST_TEMPLATE.md': '70f4b97f3914e2f399bcc5868e072c29', - 'test-1/.gitignore': '96abf7aadc9155ba2a5bba1e05563ff0', + 'test-1/.gitignore': 'a053cd9512af2ebf8ffac400b14034eb', 'test-1/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683', 'test-1/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139', 'test-1/.vscode/launch.json': '140e17d591e03b8850c456ade3aefb1f', @@ -175,7 +175,7 @@ test(`${TestDirectories.one}: parses CLI arguments, handles default options`, as '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': '741e6fa9f78712a6f276ba33fc2c798b', + 'test-1/tsconfig.json': '4228782ef56faca96f8123a88bf26dc2', 'test-1/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d', }); }); @@ -203,14 +203,14 @@ test(`${TestDirectories.two}: parses CLI arguments, handles all options`, async const map = await hashAllTheThings(TestDirectories.two); t.deepEqual(map, { 'test-2/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba', - 'test-2/.cspell.json': '5b2e0ad337bc05ef2ce5635270d983fd', + 'test-2/.cspell.json': '61bbd8ed98375a94cbd1063ab498959f', 'test-2/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4', 'test-2/.eslintrc.json': '4e74756d24eaccb7f28d4999e4bd6f0d', 'test-2/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08', 'test-2/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e', 'test-2/.github/PULL_REQUEST_TEMPLATE.md': '70f4b97f3914e2f399bcc5868e072c29', - 'test-2/.gitignore': '2b90e204ba76e30ec95f946ac7c9787e', + 'test-2/.gitignore': '703beedaf3cabd7f7fd7d3f57408ec61', 'test-2/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683', 'test-2/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139', 'test-2/.vscode/launch.json': '140e17d591e03b8850c456ade3aefb1f', @@ -224,7 +224,7 @@ test(`${TestDirectories.two}: parses CLI arguments, handles all options`, async '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': 'eadf0f5640c5000ceabf242d157861e4', + 'test-2/tsconfig.json': '7a9481f033cb07985ee1b903ad42b167', 'test-2/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d', }); }); @@ -288,14 +288,14 @@ test(`${TestDirectories.three}: interactive mode: javascript library`, async (t) const map = await hashAllTheThings(TestDirectories.three); t.deepEqual(map, { 'test-3/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba', - 'test-3/.cspell.json': '5b2e0ad337bc05ef2ce5635270d983fd', + 'test-3/.cspell.json': '61bbd8ed98375a94cbd1063ab498959f', 'test-3/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4', 'test-3/.eslintrc.json': '4e74756d24eaccb7f28d4999e4bd6f0d', 'test-3/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08', 'test-3/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e', 'test-3/.github/PULL_REQUEST_TEMPLATE.md': '70f4b97f3914e2f399bcc5868e072c29', - 'test-3/.gitignore': '2b90e204ba76e30ec95f946ac7c9787e', + 'test-3/.gitignore': '703beedaf3cabd7f7fd7d3f57408ec61', 'test-3/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683', 'test-3/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139', 'test-3/.vscode/launch.json': '140e17d591e03b8850c456ade3aefb1f', @@ -309,7 +309,7 @@ test(`${TestDirectories.three}: interactive mode: javascript library`, async (t) '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': '25e6e53cdc0b9194c5ba862b275afc37', + 'test-3/tsconfig.json': '472791a7b88cee5b9369207216fe7f98', 'test-3/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d', }); }); @@ -326,28 +326,26 @@ test(`${TestDirectories.four}: interactive mode: node.js application`, async (t) const map = await hashAllTheThings(TestDirectories.four); t.deepEqual(map, { 'test-4/.circleci/config.yml': '44d2fd424c93d381d41030f789efabba', - 'test-4/.cspell.json': '5b2e0ad337bc05ef2ce5635270d983fd', + 'test-4/.cspell.json': '61bbd8ed98375a94cbd1063ab498959f', 'test-4/.editorconfig': '44a3e6c69d9267b0f756986fd970a8f4', 'test-4/.eslintrc.json': '941448b089cd055bbe476a84c8f96cfe', 'test-4/.github/CONTRIBUTING.md': '5f0dfa7fdf9bf828e3a3aa8fcaeece08', 'test-4/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e', 'test-4/.github/PULL_REQUEST_TEMPLATE.md': '70f4b97f3914e2f399bcc5868e072c29', - 'test-4/.gitignore': '96abf7aadc9155ba2a5bba1e05563ff0', + 'test-4/.gitignore': 'a053cd9512af2ebf8ffac400b14034eb', 'test-4/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683', '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/index.ts': '5991bedc40ac87a01d880c6db16fe349', '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': '0256ae9ca8952a1125c461392b69ce06', + 'test-4/tsconfig.json': 'b4786d3cf1c3e6bd51253c036bfc6e8a', 'test-4/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d', }); }); @@ -413,7 +411,7 @@ test(`${TestDirectories.five}: Sandboxed: npm install, initial commit`, async (t 'test-5/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e', 'test-5/.github/PULL_REQUEST_TEMPLATE.md': '70f4b97f3914e2f399bcc5868e072c29', - 'test-5/.gitignore': '96abf7aadc9155ba2a5bba1e05563ff0', + 'test-5/.gitignore': 'a053cd9512af2ebf8ffac400b14034eb', 'test-5/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683', 'test-5/LICENSE': '317693126d229a3cdd19725a624a56fc', 'test-5/README.md': '8fc7ecb21d7d47289e4b2469eea4db39', @@ -421,7 +419,7 @@ test(`${TestDirectories.five}: Sandboxed: npm install, initial commit`, async (t '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': '24a7b8aff2651d9a1920e33459d8c346', + 'test-5/tsconfig.json': '5ea2a8356e00e9407dd4641007fd3940', 'test-5/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d', }); }); @@ -457,7 +455,7 @@ test(`${TestDirectories.six}: Sandboxed: yarn, no initial commit`, async (t) => 'test-6/.github/ISSUE_TEMPLATE.md': 'e70a0b70402765682b1a961af855040e', 'test-6/.github/PULL_REQUEST_TEMPLATE.md': '70f4b97f3914e2f399bcc5868e072c29', - 'test-6/.gitignore': '2b90e204ba76e30ec95f946ac7c9787e', + 'test-6/.gitignore': '703beedaf3cabd7f7fd7d3f57408ec61', 'test-6/.prettierignore': '1da1ce4fdb868f0939608fafd38f9683', 'test-6/.travis.yml': '91976af7b86cffb6960db8c35b27b7d0', 'test-6/.vscode/extensions.json': '2d26a716ba181656faac4cd2d38ec139', @@ -474,7 +472,7 @@ test(`${TestDirectories.six}: Sandboxed: yarn, no initial commit`, async (t) => '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': 'eadf0f5640c5000ceabf242d157861e4', + 'test-6/tsconfig.json': '7a9481f033cb07985ee1b903ad42b167', 'test-6/tsconfig.module.json': '2fda4c8760c6cfa3462b40df0645850d', }); }); diff --git a/src/cli/typescript-starter.ts b/src/cli/typescript-starter.ts index f70cf3e..a99f147 100644 --- a/src/cli/typescript-starter.ts +++ b/src/cli/typescript-starter.ts @@ -101,6 +101,23 @@ export async function typescriptStarter( ); const pkg = readPackageJson(pkgPath); + const removeCliScripts = { + 'check-integration-test': undefined, + 'check-integration-test:1': undefined, + 'check-integration-test:2': undefined, + 'check-integration-test:3': undefined, + 'check-integration-test:4': undefined, + 'check-integration-test:5': undefined, + 'check-integration-test:6': undefined, + }; + const scripts = { + ...pkg.scripts, + ...removeCliScripts, + ...(runner === Runner.Yarn + ? { 'reset-hard': `git clean -dfx && git reset --hard && yarn` } + : {}), + ...(cspell ? {} : { 'test:spelling': undefined }), + }; const newPkg = { ...pkg, dependencies: nodeDefinitions @@ -111,14 +128,13 @@ export async function typescriptStarter( keywords: [], name: projectName, repository: `https://github.com/${githubUsername}/${projectName}`, - scripts: - runner === Runner.Yarn - ? { - ...pkg.scripts, - 'reset-hard': `git clean -dfx && git reset --hard && yarn`, - } - : { ...pkg.scripts }, + scripts, version: '1.0.0', + ava: { + ...pkg.ava, + files: ['!build/module/**'], + ignoredByWatcher: undefined, + }, }; // eslint-disable-next-line functional/immutable-data @@ -132,6 +148,11 @@ export async function typescriptStarter( spinnerPackage.succeed(); const spinnerGitignore = ora('Updating .gitignore').start(); + await replaceInFile({ + files: join(projectPath, '.gitignore'), + from: 'diff\n', + to: '', + }); if (runner === Runner.Yarn) { await replaceInFile({ files: join(projectPath, '.gitignore'), @@ -169,6 +190,12 @@ export async function typescriptStarter( } if (!circleci) { del([normalizePath(join(projectPath, '.circleci'))]); + } else { + await replaceInFile({ + files: join(projectPath, '.circleci', 'config.yml'), + from: / {6}- run: npm run check-integration-tests\n/g, + to: '', + }); } if (!cspell) { del([normalizePath(join(projectPath, '.cspell.json'))]); @@ -245,6 +272,15 @@ export async function typescriptStarter( from: '"lib": ["es2017", "dom"]', to: '"lib": ["es2017"]', }); + await replaceInFile({ + files: join(projectPath, 'src', 'index.ts'), + from: `export * from './lib/hash';\n`, + to: '', + }); + await del([ + normalizePath(join(projectPath, 'src', 'lib', 'hash.ts')), + normalizePath(join(projectPath, 'src', 'lib', 'hash.spec.ts')), + ]); spinnerDom.succeed(); } @@ -257,7 +293,12 @@ export async function typescriptStarter( }); await replaceInFile({ files: join(projectPath, 'src', 'index.ts'), - from: /^export[\S\s]*hash';\s*/, + from: `export * from './lib/async';\n`, + to: '', + }); + await replaceInFile({ + files: join(projectPath, 'src', 'index.ts'), + from: `export * from './lib/hash';\n`, to: '', }); await del([ diff --git a/tsconfig.json b/tsconfig.json index 34f17e6..0548ce0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,7 @@ "declaration": true, "inlineSourceMap": true, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + "resolveJsonModule": true /* Include modules imported with .json extension. */, "strict": true /* Enable all strict type-checking options. */,