├── .eslintignore
├── .eslintrc.json
├── .github
├── release-please.yml
├── release-trigger.yml
└── workflows
│ ├── ci.yaml
│ └── release.yaml
├── .gitignore
├── .mocharc.json
├── .pre-commit-hooks.yaml
├── .prettierrc.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── js-green-licenses.json
├── package-lock.json
├── package.json
├── renovate.json
├── src
├── clean.ts
├── cli.ts
├── index.ts
├── init.ts
└── util.ts
├── template
└── index.ts
├── test
├── fixtures
│ └── kitchen
│ │ ├── package.json
│ │ └── src
│ │ ├── samples.js
│ │ └── server.ts
├── kitchen.ts
├── test-clean.ts
├── test-init.ts
└── test-util.ts
├── tsconfig-google.json
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | test/fixtures/
3 | template/
4 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint:recommended", "plugin:n/recommended", "prettier"],
3 | "plugins": ["n", "prettier"],
4 | "rules": {
5 | "prettier/prettier": "error",
6 | "block-scoped-var": "error",
7 | "eqeqeq": "error",
8 | "no-var": "error",
9 | "prefer-const": "error",
10 | "eol-last": "error",
11 | "prefer-arrow-callback": "error",
12 | "no-trailing-spaces": "error",
13 | "quotes": ["warn", "single", {"avoidEscape": true}],
14 | "no-restricted-properties": [
15 | "error",
16 | {
17 | "object": "describe",
18 | "property": "only"
19 | },
20 | {
21 | "object": "it",
22 | "property": "only"
23 | }
24 | ]
25 | },
26 | "overrides": [
27 | {
28 | "files": ["**/*.ts", "**/*.tsx"],
29 | "parser": "@typescript-eslint/parser",
30 | "extends": ["plugin:@typescript-eslint/recommended"],
31 | "rules": {
32 | "@typescript-eslint/ban-ts-comment": "warn",
33 | "@typescript-eslint/no-floating-promises": "error",
34 | "@typescript-eslint/no-non-null-assertion": "off",
35 | "@typescript-eslint/no-use-before-define": "off",
36 | "@typescript-eslint/no-warning-comments": "off",
37 | "@typescript-eslint/no-empty-function": "off",
38 | "@typescript-eslint/no-var-requires": "off",
39 | "@typescript-eslint/explicit-function-return-type": "off",
40 | "@typescript-eslint/explicit-module-boundary-types": "off",
41 | "@typescript-eslint/ban-types": "off",
42 | "@typescript-eslint/camelcase": "off",
43 | "n/no-missing-import": "off",
44 | "n/no-empty-function": "off",
45 | "n/no-unsupported-features/es-syntax": "off",
46 | "n/no-missing-require": "off",
47 | "n/shebang": "off",
48 | "no-dupe-class-members": "off",
49 | "require-atomic-updates": "off"
50 | },
51 | "parserOptions": {
52 | "ecmaVersion": 2018,
53 | "project": "./tsconfig.json",
54 | "sourceType": "module"
55 | }
56 | }
57 | ]
58 | }
59 |
--------------------------------------------------------------------------------
/.github/release-please.yml:
--------------------------------------------------------------------------------
1 | releaseType: node
2 | handleGHRelease: true
3 | primaryBranch: main
4 |
--------------------------------------------------------------------------------
/.github/release-trigger.yml:
--------------------------------------------------------------------------------
1 | enabled: true
2 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches: [$default-branch]
4 | pull_request:
5 | name: ci
6 | jobs:
7 | test:
8 | runs-on: ubuntu-latest
9 | strategy:
10 | fail-fast: false
11 | matrix:
12 | node: [18, 20, 22]
13 | steps:
14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
15 | - uses: actions/setup-node@v4
16 | with:
17 | node-version: ${{ matrix.node }}
18 | - run: npm i --production --engine-strict --ignore-scripts
19 | - run: node --version
20 | - run: npm ci
21 | - run: npm test
22 | - name: coverage
23 | uses: codecov/codecov-action@v4
24 | with:
25 | name: actions ${{ matrix.node }}
26 | windows:
27 | runs-on: windows-latest
28 | steps:
29 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
30 | - uses: actions/setup-node@v4
31 | with:
32 | node-version: 18
33 | - run: npm ci
34 | - run: npm run system-test
35 | system_test:
36 | runs-on: ubuntu-latest
37 | steps:
38 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
39 | - uses: actions/setup-node@v4
40 | with:
41 | node-version: 18
42 | - run: npm ci
43 | - run: npm run system-test
44 | lint:
45 | runs-on: ubuntu-latest
46 | steps:
47 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
48 | - uses: actions/setup-node@v4
49 | with:
50 | node-version: 18
51 | - run: npm ci
52 | - run: npm run lint
53 | license_check:
54 | runs-on: ubuntu-latest
55 | steps:
56 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
57 | - uses: actions/setup-node@v4
58 | with:
59 | node-version: 18
60 | - run: npm ci
61 | - run: npm run license-check
62 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | release:
3 | types: [published]
4 | name: release
5 | jobs:
6 | release-please:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
10 | - uses: actions/setup-node@v4
11 | with:
12 | node-version: 18
13 | registry-url: 'https://wombat-dressing-room.appspot.com'
14 | - run: npm ci
15 | - run: npm publish
16 | env:
17 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .nyc_output
3 | .vscode
4 | build
5 | coverage
6 | node_modules
7 | npm-debug.log
8 | yarn-error.log
9 | yarn.lock
10 | __pycache__
11 |
--------------------------------------------------------------------------------
/.mocharc.json:
--------------------------------------------------------------------------------
1 | {
2 | "enable-source-maps": true,
3 | "timeout": 240000,
4 | "throw-deprecation": true
5 | }
6 |
--------------------------------------------------------------------------------
/.pre-commit-hooks.yaml:
--------------------------------------------------------------------------------
1 | - id: gts
2 | name: gts
3 | description: 'gts: TypeScript style guide, formatter, and linter.'
4 | entry: gts lint
5 | language: node
6 | language_version: system
7 | minimum_pre_commit_version: 2.9.2
8 | require_serial: true
9 | types_or: [javascript, jsx, ts, tsx]
10 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": false,
3 | "singleQuote": true,
4 | "trailingComma": "all",
5 | "arrowParens": "avoid"
6 | }
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [6.0.2](https://github.com/google/gts/compare/v6.0.1...v6.0.2) (2024-10-25)
4 |
5 |
6 | ### Bug Fixes
7 |
8 | * TS `lib` Support for Node 18+ ([#913](https://github.com/google/gts/issues/913)) ([82aa8e3](https://github.com/google/gts/commit/82aa8e33869b9d91ac426afbe3027e6b6f7b90b3))
9 |
10 | ## [6.0.1](https://github.com/google/gts/compare/v6.0.0...v6.0.1) (2024-10-22)
11 |
12 |
13 | ### Bug Fixes
14 |
15 | * Allow `typescript` v5+ as a peer dependency ([#909](https://github.com/google/gts/issues/909)) ([996aaec](https://github.com/google/gts/commit/996aaece999033e2686300477bdae21debf62368))
16 |
17 | ## [6.0.0](https://github.com/google/gts/compare/v5.3.1...v6.0.0) (2024-10-10)
18 |
19 |
20 | ### ⚠ BREAKING CHANGES
21 |
22 | * Update '.prettierrc.json' to include `trailingComma: "all"` to match internal Google config ([#822](https://github.com/google/gts/issues/822))
23 | * Update `typescript` and other dependencies ([#902](https://github.com/google/gts/issues/902))
24 | * Set `no-floating-promises` to `error` ([#901](https://github.com/google/gts/issues/901))
25 | * Set `composite: true` in `tsconfig-google.json` ([#899](https://github.com/google/gts/issues/899))
26 | * Set `stripInternal` in `tsconfig-google.json` ([#900](https://github.com/google/gts/issues/900))
27 | * Support Node 18+ ([#896](https://github.com/google/gts/issues/896))
28 |
29 | ### Features
30 |
31 | * Set `composite: true` in `tsconfig-google.json` ([#899](https://github.com/google/gts/issues/899)) ([71972dc](https://github.com/google/gts/commit/71972dc0e5611fbbe8b3885eb18d19563545cd4b))
32 | * Set `no-floating-promises` to `error` ([#901](https://github.com/google/gts/issues/901)) ([1d28f92](https://github.com/google/gts/commit/1d28f92df53b9efd3924e4dbf11dc71dd8091d36))
33 | * Set `stripInternal` in `tsconfig-google.json` ([#900](https://github.com/google/gts/issues/900)) ([9b37243](https://github.com/google/gts/commit/9b37243804dcca9a69b00142706f60f36b89e405))
34 | * Support Node 18+ ([#896](https://github.com/google/gts/issues/896)) ([f011fa3](https://github.com/google/gts/commit/f011fa3ae9628ed72eeec6095cf8b9c0813317e7))
35 | * Update '.prettierrc.json' to include `trailingComma: "all"` to match internal Google config ([#822](https://github.com/google/gts/issues/822)) ([27d0d93](https://github.com/google/gts/commit/27d0d93cdeedd885fa494c875957edf6399202e0))
36 | * Update `typescript` and other dependencies ([#902](https://github.com/google/gts/issues/902)) ([1c18b3a](https://github.com/google/gts/commit/1c18b3ae1a54bbcbb587518dede9bfaf8fd5e5dd))
37 |
38 |
39 | ### Bug Fixes
40 |
41 | * **deps:** update dependency eslint to v8.57.1 ([#903](https://github.com/google/gts/issues/903)) ([23da8ef](https://github.com/google/gts/commit/23da8ef208a8ba957c2fc5fead4c72b98448d35d))
42 | * **deps:** update dependency eslint-plugin-prettier to v5.2.1 ([#894](https://github.com/google/gts/issues/894)) ([a6d5e6a](https://github.com/google/gts/commit/a6d5e6a24037e4ed3b2a56861ef7b7e1e20982e4))
43 | * **deps:** update dependency prettier to v3.3.3 ([#883](https://github.com/google/gts/issues/883)) ([52dca7c](https://github.com/google/gts/commit/52dca7c40808eda19e12a840a058581097dd2c9f))
44 |
45 | ## [5.3.1](https://github.com/google/gts/compare/v5.3.0...v5.3.1) (2024-04-10)
46 |
47 |
48 | ### Bug Fixes
49 |
50 | * **deps:** replace dependency eslint-plugin-node with eslint-plugin-n ([#865](https://github.com/google/gts/issues/865)) ([efbe3a8](https://github.com/google/gts/commit/efbe3a838f40959188190dc04b4f11f45dd1aa87))
51 | * **deps:** update dependency eslint to v8.57.0 ([#833](https://github.com/google/gts/issues/833)) ([0c0a45c](https://github.com/google/gts/commit/0c0a45c83832034ed96c19c0f7956180587991ed))
52 | * **deps:** update dependency prettier to v3.2.5 ([#846](https://github.com/google/gts/issues/846)) ([7e60e38](https://github.com/google/gts/commit/7e60e3878b877865b3536ea66dc3607d9243cee4))
53 |
54 |
55 | ### Performance Improvements
56 |
57 | * Supercharge Performance & Efficiency: Leveraging `Promise.all` for Resource-Friendly Tasks 🚤 ([#838](https://github.com/google/gts/issues/838)) ([7424fe1](https://github.com/google/gts/commit/7424fe19f822dc152315c1b1eb5f874512a88b55))
58 |
59 | ## [5.3.0](https://github.com/google/gts/compare/v5.2.0...v5.3.0) (2024-03-21)
60 |
61 |
62 | ### Features
63 |
64 | * adding pre-commit-hooks.yaml ([#858](https://github.com/google/gts/issues/858)) ([b17994d](https://github.com/google/gts/commit/b17994d2f26f0cacaf3e7956dc01bc644a32b5ae))
65 |
66 |
67 | ### Bug Fixes
68 |
69 | * **deps:** update dependency eslint to v8.51.0 ([#812](https://github.com/google/gts/issues/812)) ([ae913c1](https://github.com/google/gts/commit/ae913c17ae4460d0f76aad16b96cb3e1f23a5b89))
70 | * **deps:** update dependency eslint to v8.52.0 ([#821](https://github.com/google/gts/issues/821)) ([50b3ce5](https://github.com/google/gts/commit/50b3ce56190a5f785c52b00b8da56769255caaa1))
71 | * **deps:** update dependency eslint to v8.53.0 ([#829](https://github.com/google/gts/issues/829)) ([7d9ffed](https://github.com/google/gts/commit/7d9ffed35d0ee076bda063e5189573eeba82ac0a))
72 | * **deps:** update dependency eslint-config-prettier to v9.1.0 ([#836](https://github.com/google/gts/issues/836)) ([9105ebb](https://github.com/google/gts/commit/9105ebb83516746503f8714914fc62da0e7fe1a6))
73 | * **deps:** update dependency eslint-plugin-prettier to v5.0.1 ([#817](https://github.com/google/gts/issues/817)) ([89b8955](https://github.com/google/gts/commit/89b8955576d1997d2bc587b79bf57d3c4d07cdda))
74 | * **deps:** update dependency eslint-plugin-prettier to v5.1.2 ([#839](https://github.com/google/gts/issues/839)) ([b5ab5c4](https://github.com/google/gts/commit/b5ab5c495e6da286ac57dc285586806f612913b8))
75 | * **deps:** update dependency eslint-plugin-prettier to v5.1.3 ([#845](https://github.com/google/gts/issues/845)) ([6e13e12](https://github.com/google/gts/commit/6e13e12b9d4f82fc43ef015980d5345fb47b9a41))
76 | * **deps:** update dependency prettier to v3.1.0 ([#832](https://github.com/google/gts/issues/832)) ([faf6d7e](https://github.com/google/gts/commit/faf6d7e60e7a382077de8fd7c8c5a9ec065259a5))
77 | * **deps:** update dependency prettier to v3.1.1 ([#837](https://github.com/google/gts/issues/837)) ([6de3e3b](https://github.com/google/gts/commit/6de3e3b9a741e6ff0e34996848a2ac1f8957bb32))
78 |
79 | ## [5.2.0](https://github.com/google/gts/compare/v5.1.1...v5.2.0) (2023-10-04)
80 |
81 |
82 | ### Features
83 |
84 | * warn for ts-ignore comments for ESM ([#810](https://github.com/google/gts/issues/810)) ([350fbf0](https://github.com/google/gts/commit/350fbf0486c7470123eef19a8ab1816d1d05a6ee))
85 |
86 | ## [5.1.1](https://github.com/google/gts/compare/v5.1.0...v5.1.1) (2023-10-04)
87 |
88 |
89 | ### Bug Fixes
90 |
91 | * revert feat: no-floating-promises ([44de7f7](https://github.com/google/gts/commit/44de7f705cea94d4781e4eb4b2d71a4ee4f0e89d))
92 |
93 | ## [5.1.0](https://github.com/google/gts/compare/v5.0.1...v5.1.0) (2023-09-29)
94 |
95 |
96 | ### Features
97 |
98 | * no-floating-promises ([#756](https://github.com/google/gts/issues/756)) ([c93e733](https://github.com/google/gts/commit/c93e73316164137e29daa7bea8a48083f7d7c1da))
99 |
100 |
101 | ### Bug Fixes
102 |
103 | * **deps:** update dependency eslint to v8.49.0 ([#784](https://github.com/google/gts/issues/784)) ([5e2a05c](https://github.com/google/gts/commit/5e2a05c12886dc63a60219e0c0830846de7c24b9))
104 | * **deps:** update dependency eslint to v8.50.0 ([#802](https://github.com/google/gts/issues/802)) ([794abf3](https://github.com/google/gts/commit/794abf30e4ee1d4655436ad9efa11ee031027993))
105 | * **deps:** update dependency eslint-config-prettier to v8.10.0 ([#785](https://github.com/google/gts/issues/785)) ([5391d89](https://github.com/google/gts/commit/5391d89de2b8af68b63954b01b90f88015258406))
106 | * **deps:** update dependency eslint-config-prettier to v9 ([#777](https://github.com/google/gts/issues/777)) ([470977a](https://github.com/google/gts/commit/470977a2bc7b29db0e4abb36c362920ec16381c5))
107 | * **deps:** update dependency prettier to v3.0.3 ([#782](https://github.com/google/gts/issues/782)) ([5a04e76](https://github.com/google/gts/commit/5a04e76c74cb4984cbf9c7f7fbee4e800b33ca52))
108 | * run eslint from PATH ([#654](https://github.com/google/gts/issues/654)) ([5dc2a76](https://github.com/google/gts/commit/5dc2a76aae06e5e46b6b623447837c77b58cd757))
109 |
110 | ## [5.0.1](https://github.com/google/gts/compare/v5.0.0...v5.0.1) (2023-08-21)
111 |
112 |
113 | ### Bug Fixes
114 |
115 | * line up linting with owlbot post-processing linting ([#778](https://github.com/google/gts/issues/778)) ([a731fe9](https://github.com/google/gts/commit/a731fe9aef6d0003fe229627522ab8250a9222d9))
116 |
117 | ## [5.0.0](https://github.com/google/gts/compare/v4.0.1...v5.0.0) (2023-07-26)
118 |
119 |
120 | ### ⚠ BREAKING CHANGES
121 |
122 | * Upgrade to node 14 as the minimum version ([#771](https://github.com/google/gts/issues/771))
123 |
124 | ### Bug Fixes
125 |
126 | * **deps:** update dependency prettier to ~2.8.0 ([#743](https://github.com/google/gts/issues/743)) ([7582516](https://github.com/google/gts/commit/75825165ea32bb9bcd5013223d5e5dff2efa731c))
127 | * update eslint-prettier ([43d4a06](https://github.com/google/gts/commit/43d4a06a27565b7d3839432c6f8267d254f6a002))
128 |
129 |
130 | ### Miscellaneous Chores
131 |
132 | * Upgrade to node 14 as the minimum version ([#771](https://github.com/google/gts/issues/771)) ([6301178](https://github.com/google/gts/commit/6301178c859361ddf8dfd678f94fc80ad5b7e38f))
133 |
134 | ## [4.0.1](https://github.com/google/gts/compare/v4.0.0...v4.0.1) (2023-01-09)
135 |
136 |
137 | ### Bug Fixes
138 |
139 | * **deps:** bump json5 from 2.2.1 to 2.2.2 addressing CVE-2022-46175 ([2a1fd61](https://github.com/google/gts/commit/2a1fd614620f0dc26cc6f12c3b05a3088409b923))
140 |
141 | ## [4.0.0](https://github.com/google/gts/compare/v3.1.0...v4.0.0) (2022-07-04)
142 |
143 |
144 | ### ⚠ BREAKING CHANGES
145 |
146 | * drop support for node.js 10.x (#686)
147 |
148 | ### Features
149 |
150 | * generate .editorconfig ([#500](https://github.com/google/gts/issues/500)) ([81397e0](https://github.com/google/gts/commit/81397e0fd9a7f141c00b52f47c3c5d9a921292ad))
151 |
152 |
153 | ### Bug Fixes
154 |
155 | * **deps:** update dependency eslint-config-prettier to v8 ([#624](https://github.com/google/gts/issues/624)) ([23a4abb](https://github.com/google/gts/commit/23a4abb50a539a93a7d1043669b2e42a887415fa))
156 | * **deps:** update dependency eslint-plugin-prettier to v4 ([#657](https://github.com/google/gts/issues/657)) ([5408bfe](https://github.com/google/gts/commit/5408bfeda4eb9cc22fb948442cda4fb6da631ed7))
157 | * **deps:** update dependency prettier to ~2.5.0 ([#660](https://github.com/google/gts/issues/660)) ([8789fd4](https://github.com/google/gts/commit/8789fd42388aead5cb572a543ae218563b21ac94))
158 | * **deps:** update dependency prettier to ~2.6.0 ([#670](https://github.com/google/gts/issues/670)) ([2feba2c](https://github.com/google/gts/commit/2feba2cdf8884420349256420c8b5ccc9cb858c8))
159 | * **deps:** update dependency prettier to ~2.7.0 ([#696](https://github.com/google/gts/issues/696)) ([3c677fd](https://github.com/google/gts/commit/3c677fdbad4772aea0be25c1cdd8149a88b0d735))
160 | * **deps:** update dependency write-file-atomic to v4 ([#687](https://github.com/google/gts/issues/687)) ([f16a3e1](https://github.com/google/gts/commit/f16a3e1a1101bc5dab90ac3ab1437dd7758adf4e))
161 | * **deps:** update typescript-eslint monorepo to v5 ([#688](https://github.com/google/gts/issues/688)) ([ed2fd0c](https://github.com/google/gts/commit/ed2fd0ce1be4826239b97bc1c8fdae5c61c50e62))
162 | * **deps:** upgrade to eslint 8.x ([#693](https://github.com/google/gts/issues/693)) ([7ae5c8b](https://github.com/google/gts/commit/7ae5c8b827abb41844ba6f533821bf3d0a7f302b))
163 | * drop update notifier ([#706](https://github.com/google/gts/issues/706)) ([cab7704](https://github.com/google/gts/commit/cab7704389c2ba7e8e426da08397af47991d8596))
164 | * pin prettier to 2.3.x ([#641](https://github.com/google/gts/issues/641)) ([323fb4a](https://github.com/google/gts/commit/323fb4acacc9bfc1fcba06b27135a77acc54b15a))
165 |
166 |
167 | ### Build System
168 |
169 | * drop support for node.js 10.x ([#686](https://github.com/google/gts/issues/686)) ([12cd913](https://github.com/google/gts/commit/12cd913b6e9eb97e52b1cf3a275aadfa4517fdcb))
170 |
171 | ## [3.1.0](https://www.github.com/google/gts/compare/v3.0.3...v3.1.0) (2021-01-11)
172 |
173 |
174 | ### Features
175 |
176 | * support comments in JSON ([#571](https://www.github.com/google/gts/issues/571)) ([cb6d2ca](https://www.github.com/google/gts/commit/cb6d2cacb5de7bcc9c8e82dd47e14fc5bf9596a3))
177 |
178 |
179 | ### Bug Fixes
180 |
181 | * **deps:** update dependency eslint-config-prettier to v7 ([#601](https://www.github.com/google/gts/issues/601)) ([6e26681](https://www.github.com/google/gts/commit/6e266812da4b90b18e2abead9b2b5a1ca0c6654b))
182 | * **deps:** upgrade to latest version of meow ([#616](https://www.github.com/google/gts/issues/616)) ([634bad9](https://www.github.com/google/gts/commit/634bad9bbbdb4d397bba101dc38ab14881172a30))
183 |
184 | ### [3.0.3](https://www.github.com/google/gts/compare/v3.0.2...v3.0.3) (2020-12-03)
185 |
186 |
187 | ### Bug Fixes
188 |
189 | * **deps:** update dependency execa to v5 ([#600](https://www.github.com/google/gts/issues/600)) ([4e5f1e5](https://www.github.com/google/gts/commit/4e5f1e54facf53588bbb3b025b5240edbd7f3c8a))
190 | * **deps:** update dependency meow to v8 ([#591](https://www.github.com/google/gts/issues/591)) ([c7e223e](https://www.github.com/google/gts/commit/c7e223e6a2ff605fabad2f8359a0385033f8de66))
191 |
192 | ### [3.0.2](https://www.github.com/google/gts/compare/v3.0.1...v3.0.2) (2020-10-26)
193 |
194 |
195 | ### Bug Fixes
196 |
197 | * **deps:** loosen ts peer dependency ([#589](https://www.github.com/google/gts/issues/589)) ([8f1d381](https://www.github.com/google/gts/commit/8f1d381d7b166a510c42786c4a337e81b7222c84))
198 |
199 | ### [3.0.1](https://www.github.com/google/gts/compare/v3.0.0...v3.0.1) (2020-10-12)
200 |
201 |
202 | ### Bug Fixes
203 |
204 | * **rule:** turn off @typescript-eslint/no-var-requires ([#578](https://www.github.com/google/gts/issues/578)) ([3b37229](https://www.github.com/google/gts/commit/3b37229c45969a3c53af123c69bb749578ee6b0b))
205 |
206 | ## [3.0.0](https://www.github.com/google/gts/compare/v2.0.2...v3.0.0) (2020-10-08)
207 |
208 |
209 | ### ⚠ BREAKING CHANGES
210 |
211 | * change default `check` to `lint` (#570)
212 | * **deps:** require TypeScript 4.x (#565)
213 |
214 | ### Features
215 |
216 | * Add TypeScript v4 support ([#551](https://www.github.com/google/gts/issues/551)) ([0883956](https://www.github.com/google/gts/commit/08839565a1d2b4b39d532c9b0b596f01b18856fe))
217 | * change default `check` to `lint` ([#570](https://www.github.com/google/gts/issues/570)) ([c527b66](https://www.github.com/google/gts/commit/c527b66be1ef6a78ea14b3d29225a8d7fb7097bd))
218 | * generate .eslintignore when running init ([#521](https://www.github.com/google/gts/issues/521)) ([8bce036](https://www.github.com/google/gts/commit/8bce0368767f0c2ad7d0700deb839962bc928d16))
219 |
220 |
221 | ### Bug Fixes
222 |
223 | * add build/.eslintrc.json to files field ([#553](https://www.github.com/google/gts/issues/553)) ([3b516ad](https://www.github.com/google/gts/commit/3b516ad5e9f0d58201dde469461db7c6ed1c1b78))
224 | * **deps:** require TypeScript 4.x ([#565](https://www.github.com/google/gts/issues/565)) ([cbc5267](https://www.github.com/google/gts/commit/cbc5267579ef24e8c8ceaa2ef794df3ef54ea56a))
225 | * **deps:** update dependency update-notifier to v5 ([#574](https://www.github.com/google/gts/issues/574)) ([9a882bf](https://www.github.com/google/gts/commit/9a882bf4ac30ad06e7b91a65ad5721d8e8b41c4b))
226 | * **deps:** update typescript-eslint monorepo to v2.34.0 ([#509](https://www.github.com/google/gts/issues/509)) ([998a4ac](https://www.github.com/google/gts/commit/998a4ac9b75c97f04d8e5db37563f32d31652f23))
227 | * **deps:** update typescript-eslint monorepo to v3 (major) ([#528](https://www.github.com/google/gts/issues/528)) ([e22e173](https://www.github.com/google/gts/commit/e22e17338db2ddb7eb829c821037c2f4e77ff869))
228 | * **deps:** update typescript-eslint monorepo to v4 ([#556](https://www.github.com/google/gts/issues/556)) ([54148df](https://www.github.com/google/gts/commit/54148dfbd8b5f8b36a0f44f901c5db933393a661))
229 | * better error message for broken tsconfig.json ([#501](https://www.github.com/google/gts/issues/501)) ([0c17a76](https://www.github.com/google/gts/commit/0c17a76c6650eee1d8abaff11a897a432eeaa65f))
230 | * prohibit calls for it.only and describe.only ([#499](https://www.github.com/google/gts/issues/499)) ([071c33c](https://www.github.com/google/gts/commit/071c33ceef0e3765166aaebf6ed4698167ac0f98))
231 |
232 | ### [2.0.2](https://www.github.com/google/gts/compare/v2.0.1...v2.0.2) (2020-05-11)
233 |
234 |
235 | ### Bug Fixes
236 |
237 | * Revert 'update dependency eslint to v7'" ([#507](https://www.github.com/google/gts/issues/507)) ([0f9950b](https://www.github.com/google/gts/commit/0f9950b273329dbcce5f3cc20864c3dcd076f08c))
238 | * **deps:** pin release of eslint-typescript ([#508](https://www.github.com/google/gts/issues/508)) ([bd86b42](https://www.github.com/google/gts/commit/bd86b42e2bb904d3765dee82262e4691a11b9958))
239 | * **deps:** update dependency eslint to v7 ([#504](https://www.github.com/google/gts/issues/504)) ([6aee159](https://www.github.com/google/gts/commit/6aee1595d0486ae2c7fd68d16b1b59c4c4015753))
240 |
241 | ### [2.0.1](https://www.github.com/google/gts/compare/v2.0.0...v2.0.1) (2020-05-07)
242 |
243 |
244 | ### Bug Fixes
245 |
246 | * throw an error if running with an unsupported version of nodejs ([#493](https://www.github.com/google/gts/issues/493)) ([94fdf1e](https://www.github.com/google/gts/commit/94fdf1eaed634aa73c3e44c7a3d9f1325f773b07))
247 | * **deps:** update dependency meow to v7 ([#502](https://www.github.com/google/gts/issues/502)) ([cf91cda](https://www.github.com/google/gts/commit/cf91cda1afab25759427511d3c97d0037d61c649))
248 |
249 | ## [2.0.0](https://www.github.com/google/gts/compare/v1.1.2...v2.0.0) (2020-04-02)
250 |
251 | ### ⚠ BREAKING CHANGES ⚠
252 | This is a major rewrite of the tool. Based on community guidance, we've switched from using [tslint](https://palantir.github.io/tslint/) to [eslint](https://eslint.org/). *Please read all of the steps below to upgrade*.
253 |
254 | #### Configuring `eslint`
255 | With the shift to `eslint`, `gts` now will format and lint JavaScript *as well* as TypeScript. Upgrading will require a number of manual steps. To format JavaScript and TypeScript, you can run:
256 |
257 | ```
258 | $ npx gts fix
259 | ```
260 |
261 | To specify only TypeScript:
262 |
263 | ```
264 | $ npx gts fix '**/*.ts'
265 | ```
266 |
267 | #### Delete `tslint.json`
268 | This file is no longer used, and can lead to confusion.
269 |
270 | #### Create a `.eslintrc.json`
271 | Now that we're using eslint, you need to extend the eslint configuration baked into the module. Create a new file named `.eslintrc.json`, and paste the following:
272 | ```js
273 | {
274 | "extends": "./node_modules/gts"
275 | }
276 | ```
277 |
278 | #### Create a `.eslintignore`
279 | The `.eslintignore` file lets you ignore specific directories. This tool now lints and formats JavaScript, so it's _really_ important to ignore your build directory! Here is an example of a `.eslintignore` file:
280 |
281 | ```
282 | **/node_modules
283 | build/
284 | ```
285 |
286 | #### Rule changes
287 | The underlying linter was changed, so naturally there are going to be a variety of rule changes along the way. To see the full list, check out [.eslintrc.json](https://github.com/google/gts/blob/main/.eslintrc.json).
288 |
289 | #### Require Node.js 10.x and up
290 | Node.js 8.x is now end of life - this module now requires Ndoe.js 10.x and up.
291 |
292 | ### Features
293 |
294 | * add the eol-last rule ([#425](https://www.github.com/google/gts/issues/425)) ([50ebd4d](https://www.github.com/google/gts/commit/50ebd4dbaf063615f4c025f567ca28076a734223))
295 | * allow eslintrc to run over tsx files ([#469](https://www.github.com/google/gts/issues/469)) ([a21db94](https://www.github.com/google/gts/commit/a21db94601def563952d677cb0980a12b6730f4c))
296 | * disable global rule for checking TODO comments ([#459](https://www.github.com/google/gts/issues/459)) ([96aa84a](https://www.github.com/google/gts/commit/96aa84a0a42181046daa248750cc8fef0c320619))
297 | * override require-atomic-updates ([#468](https://www.github.com/google/gts/issues/468)) ([8105c93](https://www.github.com/google/gts/commit/8105c9334ee5104b05f6b1b2f150e51419637262))
298 | * prefer single quotes if possible ([#475](https://www.github.com/google/gts/issues/475)) ([39a2705](https://www.github.com/google/gts/commit/39a2705e51b4b6329a70f91f8293a2d7a363bf5d))
299 | * use eslint instead of tslint ([#400](https://www.github.com/google/gts/issues/400)) ([b3096fb](https://www.github.com/google/gts/commit/b3096fbd5076d302d93c2307bf627e12c423e726))
300 |
301 |
302 | ### Bug Fixes
303 |
304 | * use .prettierrc.js ([#437](https://www.github.com/google/gts/issues/437)) ([06efa84](https://www.github.com/google/gts/commit/06efa8444cdf1064b64f3e8d61ebd04f45d90b4c))
305 | * **deps:** update dependency chalk to v4 ([#477](https://www.github.com/google/gts/issues/477)) ([061d64e](https://www.github.com/google/gts/commit/061d64e29d37b93ce55228937cc100e05ddef352))
306 | * **deps:** update dependency eslint-plugin-node to v11 ([#426](https://www.github.com/google/gts/issues/426)) ([a394b7c](https://www.github.com/google/gts/commit/a394b7c1f80437f25017ca5c500b968ebb789ece))
307 | * **deps:** update dependency execa to v4 ([#427](https://www.github.com/google/gts/issues/427)) ([f42ef36](https://www.github.com/google/gts/commit/f42ef36709251553342e655e287e889df72ee3e3))
308 | * **deps:** update dependency prettier to v2 ([#464](https://www.github.com/google/gts/issues/464)) ([20ef43d](https://www.github.com/google/gts/commit/20ef43d566df17d3c93949ef7db3b72ee9123ca3))
309 | * disable no-use-before-define ([#431](https://www.github.com/google/gts/issues/431)) ([dea2c22](https://www.github.com/google/gts/commit/dea2c223d1d3a60a1786aa820eebb93be27016a7))
310 | * **deps:** update dependency update-notifier to v4 ([#403](https://www.github.com/google/gts/issues/403)) ([57393b7](https://www.github.com/google/gts/commit/57393b74c6cf299e8ae09311f0382226b8baa3e3))
311 | * **deps:** upgrade to meow 6.x ([#423](https://www.github.com/google/gts/issues/423)) ([8f93d00](https://www.github.com/google/gts/commit/8f93d0049337a832d9a22b6ae4e86fd41140ec56))
312 | * align back to the google style guide ([#440](https://www.github.com/google/gts/issues/440)) ([8bd78c4](https://www.github.com/google/gts/commit/8bd78c4c78526a72400f618a95a987d2a7c1a8db))
313 | * disable empty-function check ([#467](https://www.github.com/google/gts/issues/467)) ([6455d7a](https://www.github.com/google/gts/commit/6455d7a9d227320d3ffe1b00c9c739b846f339a8))
314 | * drop support for node 8 ([#422](https://www.github.com/google/gts/issues/422)) ([888c686](https://www.github.com/google/gts/commit/888c68692079065f38ce66ec84472f1f3311a050))
315 | * emit .prettierrc.js with init ([#462](https://www.github.com/google/gts/issues/462)) ([b114614](https://www.github.com/google/gts/commit/b114614d22ab5560d2d1dd5cb6695968cc80027b))
316 | * enable trailing comma ([#470](https://www.github.com/google/gts/issues/470)) ([6518f58](https://www.github.com/google/gts/commit/6518f5843d3093e3beb7d3371b56d9aecedf3924))
317 | * include *.tsx and *.jsx in default fix command ([#473](https://www.github.com/google/gts/issues/473)) ([0509780](https://www.github.com/google/gts/commit/050978005ad089d9b3b5d8895b25ea1175d75db2))
318 |
319 | ### [1.1.2](https://www.github.com/google/gts/compare/v1.1.1...v1.1.2) (2019-11-20)
320 |
321 |
322 | ### Bug Fixes
323 |
324 | * **deps:** update to newest prettier (with support for optional chain) ([#396](https://www.github.com/google/gts/issues/396)) ([ce8ad06](https://www.github.com/google/gts/commit/ce8ad06c8489c44a9e2ed5292382637b3ebb7601))
325 |
326 | ### [1.1.1](https://www.github.com/google/gts/compare/v1.1.0...v1.1.1) (2019-11-11)
327 |
328 |
329 | ### Bug Fixes
330 |
331 | * **deps:** update dependency chalk to v3 ([#389](https://www.github.com/google/gts/issues/389)) ([1ce0f45](https://www.github.com/google/gts/commit/1ce0f450677e143a27efc39def617d13c66503e8))
332 | * **deps:** update dependency inquirer to v7 ([#377](https://www.github.com/google/gts/issues/377)) ([bf2c349](https://www.github.com/google/gts/commit/bf2c349b2208ac63e551542599ac9cd27b461338))
333 | * **deps:** update dependency rimraf to v3 ([#374](https://www.github.com/google/gts/issues/374)) ([2058eaa](https://www.github.com/google/gts/commit/2058eaa682f4baae978b469fd708d1f866e7da74))
334 | * **deps:** update dependency write-file-atomic to v3 ([#353](https://www.github.com/google/gts/issues/353)) ([59e6aa8](https://www.github.com/google/gts/commit/59e6aa8580a2f8e9457d2d2b6fa9e18e86347592))
335 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project,
4 | and in the interest of fostering an open and welcoming community,
5 | we pledge to respect all people who contribute through reporting issues,
6 | posting feature requests, updating documentation,
7 | submitting pull requests or patches, and other activities.
8 |
9 | We are committed to making participation in this project
10 | a harassment-free experience for everyone,
11 | regardless of level of experience, gender, gender identity and expression,
12 | sexual orientation, disability, personal appearance,
13 | body size, race, ethnicity, age, religion, or nationality.
14 |
15 | Examples of unacceptable behavior by participants include:
16 |
17 | * The use of sexualized language or imagery
18 | * Personal attacks
19 | * Trolling or insulting/derogatory comments
20 | * Public or private harassment
21 | * Publishing other's private information,
22 | such as physical or electronic
23 | addresses, without explicit permission
24 | * Other unethical or unprofessional conduct.
25 |
26 | Project maintainers have the right and responsibility to remove, edit, or reject
27 | comments, commits, code, wiki edits, issues, and other contributions
28 | that are not aligned to this Code of Conduct.
29 | By adopting this Code of Conduct,
30 | project maintainers commit themselves to fairly and consistently
31 | applying these principles to every aspect of managing this project.
32 | Project maintainers who do not follow or enforce the Code of Conduct
33 | may be permanently removed from the project team.
34 |
35 | This code of conduct applies both within project spaces and in public spaces
36 | when an individual is representing the project or its community.
37 |
38 | Instances of abusive, harassing, or otherwise unacceptable behavior
39 | may be reported by opening an issue
40 | or contacting one or more of the project maintainers.
41 |
42 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
43 | available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
44 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2013 Google Inc.
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gts
2 |
3 | > Google TypeScript Style
4 |
5 | [![NPM Version][npm-image]][npm-url]
6 | [![GitHub Actions][github-image]][github-url]
7 | [![Known Vulnerabilities][snyk-image]][snyk-url]
8 | [![codecov][codecov-image]][codecov-url]
9 | [![TypeScript Style Guide][gts-image]][gts-url]
10 |
11 | [gts][npm-url] is Google's TypeScript style guide, and the configuration for our formatter, linter, and automatic code fixer. No lint rules to edit, no configuration to update, no more bike shedding over syntax.
12 |
13 | To borrow from [standardjs][standardjs-url]:
14 |
15 | - **No configuration**. The easiest way to enforce consistent style in your project. Just drop it in.
16 | - **Automatically format code**. Just run `gts fix` and say goodbye to messy or inconsistent code.
17 | - **Catch style issues & programmer errors early**. Save precious code review time by eliminating back-and-forth between reviewer & contributor.
18 | - **Opinionated, but not to a fault**. We recommend you use the default configuration, but if you _need_ to customize compiler or linter config, you can.
19 |
20 | Under the covers, we use [eslint][eslint-url] to enforce the style guide and provide automated fixes, and [prettier][prettier-url] to re-format code.
21 |
22 | ## Getting Started
23 |
24 | The easiest way to get started is to run:
25 |
26 | ```sh
27 | npx gts init
28 | ```
29 |
30 | ## How it works
31 |
32 | When you run the `npx gts init` command, it's going to do a few things for you:
33 |
34 | - Adds an opinionated `tsconfig.json` file to your project that uses the Google TypeScript Style.
35 | - Adds the necessary devDependencies to your `package.json`.
36 | - Adds scripts to your `package.json`:
37 | - `lint`: Lints and checks for formatting problems.
38 | - `fix`: Automatically fixes formatting and linting problems (if possible).
39 | - `clean`: Removes output files.
40 | - `compile`: Compiles the source code using TypeScript compiler.
41 | - `pretest`, `posttest` and `prepare`: convenience integrations.
42 | - If a source folder is not already present it will add a default template project.
43 |
44 | ### Individual files
45 |
46 | The commands above will all run in the scope of the current folder. Some commands can be run on individual files:
47 |
48 | ```sh
49 | gts lint index.ts
50 | gts lint one.ts two.ts three.ts
51 | gts lint *.ts
52 | ```
53 |
54 | ### Working with eslint
55 |
56 | Under the covers, we use [eslint][eslint-url] to enforce the style guide and provide automated fixes, and [prettier][prettier-url] to re-format code. To use the shared `eslint` configuration, create an `.eslintrc` in your project directory, and extend the shared config:
57 |
58 | ```yml
59 | ---
60 | extends:
61 | - './node_modules/gts'
62 | ```
63 |
64 | If you don't want to use the `gts` CLI, you can drop down to using the module as a basic `eslint` config, and just use the `eslint` cli:
65 |
66 | ```
67 | $ eslint --fix
68 | ```
69 |
70 | This opens the ability to use the vast `eslint` ecosystem including custom rules, and tools like the VSCode plugin for eslint:
71 |
72 | - https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint
73 |
74 | ## Badge
75 |
76 | Show your love for `gts` and include a badge!
77 |
78 | [](https://github.com/google/gts)
79 |
80 | ```md
81 | [](https://github.com/google/gts)
82 | ```
83 |
84 | ## Supported Node.js Versions
85 |
86 | Our client libraries follow the [Node.js release schedule](https://nodejs.org/en/about/releases/). Libraries are compatible with all current _active_ and _maintenance_ versions of Node.js.
87 |
88 | ## Can I use *gts* with the [pre-commit](https://pre-commit.com/) framework?
89 |
90 | Yes! You can put the following in your `.pre-commit-config.yaml` file:
91 |
92 | ```yaml
93 | repos:
94 | - repo: https://github.com/google/gts
95 | rev: '' # Use the sha / tag you want to point at
96 | hooks:
97 | - id: gts
98 | ```
99 |
100 | ## License
101 |
102 | [Apache-2.0](LICENSE)
103 |
104 | ---
105 |
106 | Made with ❤️ by the Google Node.js team.
107 |
108 | > **_NOTE: This is not an official Google product._**
109 |
110 | [github-image]: https://github.com/google/gts/workflows/ci/badge.svg
111 | [github-url]: https://github.com/google/gts/actions
112 | [prettier-url]: https://prettier.io/
113 | [codecov-image]: https://codecov.io/gh/google/gts/branch/main/graph/badge.svg
114 | [codecov-url]: https://codecov.io/gh/google/gts
115 | [david-image]: https://david-dm.org/google/gts.svg
116 | [david-url]: https://david-dm.org/google/gts
117 | [gts-image]: https://img.shields.io/badge/code%20style-google-blueviolet.svg
118 | [gts-url]: https://github.com/google/gts
119 | [npm-image]: https://img.shields.io/npm/v/gts.svg
120 | [npm-url]: https://npmjs.org/package/gts
121 | [snyk-image]: https://snyk.io/test/github/google/gts/badge.svg
122 | [snyk-url]: https://snyk.io/test/github/google/gts
123 | [standardjs-url]: https://www.npmjs.com/package/standard
124 | [eslint-url]: https://eslint.org/
125 |
--------------------------------------------------------------------------------
/js-green-licenses.json:
--------------------------------------------------------------------------------
1 | {
2 | "packageAllowlist": ["argparse"]
3 | }
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gts",
3 | "version": "6.0.2",
4 | "description": "Google TypeScript Style",
5 | "repository": "google/gts",
6 | "main": "build/src/index.js",
7 | "bin": {
8 | "gts": "build/src/cli.js"
9 | },
10 | "files": [
11 | "CHANGELOG.md",
12 | "build/src",
13 | "build/template",
14 | "build/.eslintrc.json",
15 | ".prettierrc.json",
16 | "tsconfig-google.json",
17 | "tsconfig.json",
18 | ".eslintrc.json"
19 | ],
20 | "scripts": {
21 | "build": "npm run compile",
22 | "clean": "rimraf ./build/",
23 | "compile": "tsc",
24 | "postcompile": "ncp template build/template",
25 | "lint": "eslint '**/*.ts'",
26 | "prepare": "npm run compile",
27 | "test": "c8 mocha build/test/test-*.js",
28 | "system-test": "c8 mocha build/test/kitchen.js",
29 | "pretest": "npm run compile",
30 | "presystem-test": "npm run compile",
31 | "license-check": "jsgl --local .",
32 | "fix": "eslint --fix '**/*.ts'"
33 | },
34 | "engines": {
35 | "node": ">=18"
36 | },
37 | "keywords": [
38 | "typescript",
39 | "linter",
40 | "formatter",
41 | "google"
42 | ],
43 | "author": "Google Inc.",
44 | "license": "Apache-2.0",
45 | "dependencies": {
46 | "@typescript-eslint/eslint-plugin": "5.62.0",
47 | "@typescript-eslint/parser": "5.62.0",
48 | "chalk": "^4.1.2",
49 | "eslint": "8.57.1",
50 | "eslint-config-prettier": "9.1.0",
51 | "eslint-plugin-n": "15.7.0",
52 | "eslint-plugin-prettier": "5.2.1",
53 | "execa": "^5.0.0",
54 | "inquirer": "^7.3.3",
55 | "json5": "^2.1.3",
56 | "meow": "^9.0.0",
57 | "ncp": "^2.0.0",
58 | "prettier": "3.3.3",
59 | "rimraf": "3.0.2",
60 | "write-file-atomic": "^4.0.0"
61 | },
62 | "devDependencies": {
63 | "@npm/types": "^2.0.0",
64 | "@types/cross-spawn": "^6.0.2",
65 | "@types/eslint": "^8.0.0",
66 | "@types/fs-extra": "^11.0.0",
67 | "@types/inquirer": "^8.0.0",
68 | "@types/json5": "2.2.0",
69 | "@types/mocha": "^10.0.0",
70 | "@types/ncp": "^2.0.4",
71 | "@types/node": "^22.7.5",
72 | "@types/rimraf": "^3.0.0",
73 | "@types/sinon": "^17.0.0",
74 | "@types/tmp": "^0.2.0",
75 | "@types/write-file-atomic": "^4.0.0",
76 | "c8": "^10.1.2",
77 | "cross-spawn": "^7.0.3",
78 | "fs-extra": "^11.0.0",
79 | "inline-fixtures": "^1.1.0",
80 | "js-green-licenses": "^4.0.0",
81 | "mocha": "^10.0.0",
82 | "sinon": "^19.0.0",
83 | "tmp": "0.2.3",
84 | "typescript": "^5.6.3"
85 | },
86 | "peerDependencies": {
87 | "typescript": ">=5"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base",
4 | "docker:disable",
5 | ":disableDependencyDashboard"
6 | ],
7 | "pinVersions": false,
8 | "rebaseStalePrs": true,
9 | "lockFileMaintenance": {
10 | "enabled": true,
11 | "recreateClosed": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/clean.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import chalk = require('chalk');
17 | import * as ts from 'typescript';
18 |
19 | import {Options} from './cli';
20 | import {getTSConfig, rimrafp} from './util';
21 |
22 | interface TSConfig {
23 | compilerOptions: ts.CompilerOptions;
24 | }
25 |
26 | /**
27 | * Remove files generated by the build.
28 | */
29 | export async function clean(options: Options): Promise {
30 | const tsconfig = (await getTSConfig(options.targetRootDir)) as TSConfig;
31 | if (tsconfig.compilerOptions && tsconfig.compilerOptions.outDir) {
32 | const outDir = tsconfig.compilerOptions.outDir;
33 | if (outDir === '.') {
34 | options.logger.error(
35 | `${chalk.red('ERROR:')} ${chalk.gray('compilerOptions.outDir')} ` +
36 | 'cannot use the value ".". That would delete all of our sources.',
37 | );
38 | return false;
39 | }
40 | const message = `${chalk.red('Removing')} ${outDir} ...`;
41 | options.logger.log(message);
42 | await rimrafp(outDir);
43 | return true;
44 | } else {
45 | options.logger.error(
46 | `${chalk.red('ERROR:')} The ${chalk.gray('clean')} command` +
47 | ` requires ${chalk.gray('compilerOptions.outDir')} to be defined in ` +
48 | 'tsconfig.json.',
49 | );
50 | return false;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Copyright 2017 Google Inc. All Rights Reserved.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import * as path from 'path';
20 | import * as meow from 'meow';
21 | import {init} from './init';
22 | import {clean} from './clean';
23 | import {isYarnUsed} from './util';
24 | import * as execa from 'execa';
25 |
26 | export interface Logger {
27 | log: (...args: Array<{}>) => void;
28 | error: (...args: Array<{}>) => void;
29 | dir: (obj: {}, options?: {}) => void;
30 | }
31 |
32 | export interface Options {
33 | dryRun: boolean;
34 | gtsRootDir: string;
35 | targetRootDir: string;
36 | yes: boolean;
37 | no: boolean;
38 | logger: Logger;
39 | yarn?: boolean;
40 | }
41 |
42 | export type VerbFilesFunction = (
43 | options: Options,
44 | files: string[],
45 | fix?: boolean,
46 | ) => Promise;
47 |
48 | const logger: Logger = console;
49 |
50 | const cli = meow({
51 | help: `
52 | Usage
53 | $ gts [...] [options]
54 |
55 | Verb can be:
56 | init Adds default npm scripts to your package.json.
57 | lint Checks code for formatting and lint issues.
58 | check Alias for lint. Kept for backward compatibility.
59 | fix Fixes formatting and linting issues (if possible).
60 | clean Removes all files generated by the build.
61 |
62 | Options
63 | --help Prints this help message.
64 | -y, --yes Assume a yes answer for every prompt.
65 | -n, --no Assume a no answer for every prompt.
66 | --dry-run Don't make any actual changes.
67 | --yarn Use yarn instead of npm.
68 |
69 | Examples
70 | $ gts init -y
71 | $ gts lint
72 | $ gts fix
73 | $ gts fix src/file1.ts src/file2.ts
74 | $ gts clean`,
75 | flags: {
76 | help: {type: 'boolean'},
77 | yes: {type: 'boolean', alias: 'y'},
78 | no: {type: 'boolean', alias: 'n'},
79 | dryRun: {type: 'boolean'},
80 | yarn: {type: 'boolean'},
81 | },
82 | });
83 |
84 | /**
85 | * Get the current version of node.js being run.
86 | * Exported purely for stubbing purposes.
87 | * @private
88 | */
89 | export function getNodeVersion() {
90 | return process.version;
91 | }
92 |
93 | function usage(msg?: string): void {
94 | if (msg) {
95 | logger.error(msg);
96 | }
97 | cli.showHelp(1);
98 | }
99 |
100 | export async function run(verb: string, files: string[]): Promise {
101 | // throw if running on an old version of nodejs
102 | const nodeMajorVersion = Number(getNodeVersion().slice(1).split('.')[0]);
103 | console.log(`version: ${nodeMajorVersion}`);
104 | if (nodeMajorVersion < 10) {
105 | throw new Error(
106 | `gts requires node.js 10.x or up. You are currently running
107 | ${process.version}, which is not supported. Please upgrade to
108 | a safe, secure version of nodejs!`,
109 | );
110 | }
111 |
112 | const options = {
113 | dryRun: cli.flags.dryRun || false,
114 | // Paths are relative to the transpiled output files.
115 | gtsRootDir: path.resolve(__dirname, '../..'),
116 | targetRootDir: process.cwd(),
117 | yes: cli.flags.yes || cli.flags.y || false,
118 | no: cli.flags.no || cli.flags.n || false,
119 | logger,
120 | yarn: cli.flags.yarn || isYarnUsed(),
121 | } as Options;
122 | // Linting/formatting depend on typescript. We don't want to load the
123 | // typescript module during init, since it might not exist.
124 | // See: https://github.com/google/gts/issues/48
125 | if (verb === 'init') {
126 | return init(options);
127 | }
128 |
129 | const flags = Object.assign([], files);
130 | if (flags.length === 0) {
131 | flags.push(
132 | '**/*.ts',
133 | '**/*.js',
134 | '**/*.tsx',
135 | '**/*.jsx',
136 | '--no-error-on-unmatched-pattern',
137 | );
138 | }
139 |
140 | switch (verb) {
141 | case 'lint':
142 | case 'check': {
143 | try {
144 | await execa('eslint', flags, {stdio: 'inherit'});
145 | return true;
146 | } catch (e) {
147 | return false;
148 | }
149 | }
150 | case 'fix': {
151 | const fixFlag = options.dryRun ? '--fix-dry-run' : '--fix';
152 | try {
153 | await execa('eslint', [fixFlag, ...flags], {stdio: 'inherit'});
154 | return true;
155 | } catch (e) {
156 | console.error(e);
157 | return false;
158 | }
159 | }
160 | case 'clean':
161 | return clean(options);
162 | default:
163 | usage(`Unknown verb: ${verb}`);
164 | return false;
165 | }
166 | }
167 |
168 | if (cli.input.length < 1) {
169 | usage();
170 | }
171 |
172 | run(cli.input[0], cli.input.slice(1))
173 | .then(success => {
174 | if (!success) {
175 | // eslint-disable-next-line n/no-process-exit
176 | process.exit(1);
177 | }
178 | })
179 | .catch(e => {
180 | console.error(e);
181 | // eslint-disable-next-line n/no-process-exit
182 | process.exit(1);
183 | });
184 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2019 LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import * as cfg from '../.eslintrc.json';
18 | module.exports = cfg;
19 |
--------------------------------------------------------------------------------
/src/init.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import * as cp from 'child_process';
17 | import * as fs from 'fs';
18 | import * as inquirer from 'inquirer';
19 | import * as path from 'path';
20 | import {ncp} from 'ncp';
21 | import * as util from 'util';
22 | import * as writeFileAtomic from 'write-file-atomic';
23 |
24 | import {
25 | getPkgManagerCommand,
26 | readFilep as read,
27 | readJsonp as readJson,
28 | Bag,
29 | DefaultPackage,
30 | } from './util';
31 |
32 | import {Options} from './cli';
33 | import {PackageJSON} from '@npm/types';
34 | import chalk = require('chalk');
35 |
36 | // eslint-disable-next-line @typescript-eslint/no-var-requires
37 | const pkg = require('../../package.json');
38 |
39 | const ncpp = util.promisify(ncp);
40 |
41 | const DEFAULT_PACKAGE_JSON: PackageJSON = {
42 | name: '',
43 | version: '0.0.0',
44 | description: '',
45 | main: 'build/src/index.js',
46 | types: 'build/src/index.d.ts',
47 | files: ['build/src'],
48 | license: 'Apache-2.0',
49 | keywords: [],
50 | scripts: {test: 'echo "Error: no test specified" && exit 1'},
51 | };
52 |
53 | async function query(
54 | message: string,
55 | question: string,
56 | defaultVal: boolean,
57 | options: Options,
58 | ): Promise {
59 | if (options.yes) {
60 | return true;
61 | } else if (options.no) {
62 | return false;
63 | }
64 |
65 | if (message) {
66 | options.logger.log(message);
67 | }
68 |
69 | const answers: inquirer.Answers = await inquirer.prompt({
70 | type: 'confirm',
71 | name: 'query',
72 | message: question,
73 | default: defaultVal,
74 | });
75 | return answers.query;
76 | }
77 |
78 | export async function addScripts(
79 | packageJson: PackageJSON,
80 | options: Options,
81 | ): Promise {
82 | let edits = false;
83 | const pkgManager = getPkgManagerCommand(options.yarn);
84 | const scripts: Bag = {
85 | lint: 'gts lint',
86 | clean: 'gts clean',
87 | compile: 'tsc',
88 | fix: 'gts fix',
89 | prepare: `${pkgManager} run compile`,
90 | pretest: `${pkgManager} run compile`,
91 | posttest: `${pkgManager} run lint`,
92 | };
93 |
94 | if (!packageJson.scripts) {
95 | packageJson.scripts = {};
96 | }
97 |
98 | for (const script of Object.keys(scripts)) {
99 | let install = true;
100 | const existing = packageJson.scripts[script];
101 | const target = scripts[script];
102 |
103 | if (existing !== target) {
104 | if (existing) {
105 | const message =
106 | `package.json already has a script for ${chalk.bold(script)}:\n` +
107 | `-${chalk.red(existing)}\n+${chalk.green(target)}`;
108 | install = await query(message, 'Replace', false, options);
109 | }
110 |
111 | if (install) {
112 | // eslint-disable-next-line require-atomic-updates
113 | packageJson.scripts[script] = scripts[script];
114 | edits = true;
115 | }
116 | }
117 | }
118 | return edits;
119 | }
120 |
121 | export async function addDependencies(
122 | packageJson: PackageJSON,
123 | options: Options,
124 | ): Promise {
125 | let edits = false;
126 | const deps: DefaultPackage = {
127 | gts: `^${pkg.version}`,
128 | typescript: pkg.devDependencies.typescript,
129 | '@types/node': pkg.devDependencies['@types/node'],
130 | };
131 |
132 | if (!packageJson.devDependencies) {
133 | packageJson.devDependencies = {};
134 | }
135 |
136 | for (const dep of Object.keys(deps)) {
137 | let install = true;
138 | const existing = packageJson.devDependencies[dep];
139 | const target = deps[dep];
140 |
141 | if (existing !== target) {
142 | if (existing) {
143 | const message =
144 | `Already have devDependency for ${chalk.bold(dep)}:\n` +
145 | `-${chalk.red(existing)}\n+${chalk.green(target)}`;
146 | install = await query(message, 'Overwrite', false, options);
147 | }
148 |
149 | if (install) {
150 | // eslint-disable-next-line require-atomic-updates
151 | packageJson.devDependencies[dep] = deps[dep];
152 | edits = true;
153 | }
154 | }
155 | }
156 |
157 | return edits;
158 | }
159 |
160 | function formatJson(object: {}) {
161 | const json = JSON.stringify(object, null, ' ');
162 | return `${json}\n`;
163 | }
164 |
165 | async function writePackageJson(
166 | packageJson: PackageJSON,
167 | options: Options,
168 | ): Promise {
169 | options.logger.log('Writing package.json...');
170 | if (!options.dryRun) {
171 | await writeFileAtomic('./package.json', formatJson(packageJson));
172 | }
173 | const preview = {
174 | scripts: packageJson.scripts,
175 | devDependencies: packageJson.devDependencies,
176 | };
177 | options.logger.dir(preview);
178 | }
179 |
180 | export const ESLINT_CONFIG = {
181 | extends: './node_modules/gts/',
182 | };
183 |
184 | export const ESLINT_IGNORE = 'build/\n';
185 |
186 | async function generateConfigFile(
187 | options: Options,
188 | filename: string,
189 | contents: string,
190 | ) {
191 | let existing;
192 | try {
193 | existing = await read(filename, 'utf8');
194 | } catch (e) {
195 | const err = e as Error & {code?: string};
196 | if (err.code === 'ENOENT') {
197 | /* not found, create it. */
198 | } else {
199 | throw new Error(`Unknown error reading ${filename}: ${err.message}`);
200 | }
201 | }
202 |
203 | let writeFile = true;
204 | if (existing && existing === contents) {
205 | options.logger.log(`No edits needed in ${filename}`);
206 | return;
207 | } else if (existing) {
208 | writeFile = await query(
209 | `${chalk.bold(filename)} already exists`,
210 | 'Overwrite',
211 | false,
212 | options,
213 | );
214 | }
215 |
216 | if (writeFile) {
217 | options.logger.log(`Writing ${filename}...`);
218 | if (!options.dryRun) {
219 | await writeFileAtomic(filename, contents);
220 | }
221 | options.logger.log(contents);
222 | }
223 | }
224 |
225 | async function generateESLintConfig(options: Options): Promise {
226 | return generateConfigFile(
227 | options,
228 | './.eslintrc.json',
229 | formatJson(ESLINT_CONFIG),
230 | );
231 | }
232 |
233 | async function generateESLintIgnore(options: Options): Promise {
234 | return generateConfigFile(options, './.eslintignore', ESLINT_IGNORE);
235 | }
236 |
237 | async function generateTsConfig(options: Options): Promise {
238 | const config = formatJson({
239 | extends: './node_modules/gts/tsconfig-google.json',
240 | compilerOptions: {rootDir: '.', outDir: 'build'},
241 | include: ['src/**/*.ts', 'test/**/*.ts'],
242 | });
243 | return generateConfigFile(options, './tsconfig.json', config);
244 | }
245 |
246 | async function generatePrettierConfig(options: Options): Promise {
247 | const style = `module.exports = {
248 | ...require('gts/.prettierrc.json')
249 | }
250 | `;
251 | return generateConfigFile(options, './.prettierrc.js', style);
252 | }
253 |
254 | async function generateEditorConfig(options: Options): Promise {
255 | const config = `root = true
256 |
257 | [*]
258 | indent_style = space
259 | indent_size = 2
260 | end_of_line = lf
261 | charset = utf-8
262 | insert_final_newline = true
263 | `;
264 | return generateConfigFile(options, './.editorconfig', config);
265 | }
266 |
267 | export async function installDefaultTemplate(
268 | options: Options,
269 | ): Promise {
270 | const cwd = process.cwd();
271 | const sourceDirName = path.join(__dirname, '../template');
272 | const targetDirName = path.join(cwd, 'src');
273 |
274 | try {
275 | fs.mkdirSync(targetDirName);
276 | } catch (e) {
277 | const err = e as Error & {code?: string};
278 | if (err.code !== 'EEXIST') {
279 | throw err;
280 | }
281 | // Else, continue and populate files into the existing directory.
282 | }
283 |
284 | // Only install the template if no ts files exist in target directory.
285 | const files = fs.readdirSync(targetDirName);
286 | const tsFiles = files.filter(file => file.toLowerCase().endsWith('.ts'));
287 | if (tsFiles.length !== 0) {
288 | options.logger.log(
289 | 'Target src directory already has ts files. ' +
290 | 'Template files not installed.',
291 | );
292 | return false;
293 | }
294 | await ncpp(sourceDirName, targetDirName);
295 | options.logger.log('Default template installed.');
296 | return true;
297 | }
298 |
299 | export async function init(options: Options): Promise {
300 | let generatedPackageJson = false;
301 | let packageJson;
302 | try {
303 | packageJson = await readJson('./package.json');
304 | } catch (e) {
305 | const err = e as Error & {code?: string};
306 | if (err.code !== 'ENOENT') {
307 | throw new Error(`Unable to open package.json file: ${err.message}`);
308 | }
309 | const generate = await query(
310 | `${chalk.bold('package.json')} does not exist.`,
311 | 'Generate',
312 | true,
313 | options,
314 | );
315 |
316 | if (!generate) {
317 | options.logger.log('Please run from a directory with your package.json.');
318 | return false;
319 | }
320 |
321 | packageJson = DEFAULT_PACKAGE_JSON;
322 | generatedPackageJson = true;
323 | }
324 |
325 | const [addedDeps, addedScripts] = await Promise.all([
326 | addDependencies(packageJson, options),
327 | addScripts(packageJson, options),
328 | ]);
329 | if (generatedPackageJson || addedDeps || addedScripts) {
330 | await writePackageJson(packageJson, options);
331 | } else {
332 | options.logger.log('No edits needed in package.json.');
333 | }
334 | await Promise.all([
335 | generateTsConfig(options),
336 | generateESLintConfig(options),
337 | generateESLintIgnore(options),
338 | generatePrettierConfig(options),
339 | generateEditorConfig(options),
340 | ]);
341 | await installDefaultTemplate(options);
342 |
343 | // Run `npm install` after initial setup so `npm run lint` works right away.
344 | if (!options.dryRun) {
345 | // --ignore-scripts so that compilation doesn't happen because there's no
346 | // source files yet.
347 |
348 | cp.spawnSync(
349 | getPkgManagerCommand(options.yarn),
350 | ['install', '--ignore-scripts'],
351 | {stdio: 'inherit'},
352 | );
353 | }
354 |
355 | return true;
356 | }
357 |
--------------------------------------------------------------------------------
/src/util.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import * as fs from 'fs';
18 | import * as path from 'path';
19 | import * as rimraf from 'rimraf';
20 | import {promisify} from 'util';
21 | import * as ncp from 'ncp';
22 | import * as JSON5 from 'json5';
23 |
24 | export const readFilep = promisify(fs.readFile);
25 | export const rimrafp = promisify(rimraf);
26 | export const ncpp = promisify(ncp.ncp);
27 |
28 | export interface Bag {
29 | [script: string]: T;
30 | }
31 |
32 | export interface DefaultPackage extends Bag {
33 | gts: string;
34 | typescript: string;
35 | '@types/node': string;
36 | }
37 |
38 | export async function readJsonp(jsonPath: string) {
39 | const contents = await readFilep(jsonPath, {encoding: 'utf8'});
40 | return JSON5.parse(contents);
41 | }
42 |
43 | export interface ReadFileP {
44 | (path: string, encoding: string): Promise;
45 | }
46 |
47 | export function nop() {
48 | /* empty */
49 | }
50 |
51 | /**
52 | * Recursively iterate through the dependency chain until we reach the end of
53 | * the dependency chain or encounter a circular reference
54 | * @param filePath Filepath of file currently being read
55 | * @param customReadFilep The file reading function being used
56 | * @param readFiles an array of the previously read files so we can check for
57 | * circular references
58 | * returns a ConfigFile object containing the data from all the dependencies
59 | */
60 | async function getBase(
61 | filePath: string,
62 | customReadFilep: ReadFileP,
63 | readFiles: Set,
64 | currentDir: string,
65 | ): Promise {
66 | customReadFilep = customReadFilep || readFilep;
67 |
68 | filePath = path.resolve(currentDir, filePath);
69 |
70 | // An error is thrown if there is a circular reference as specified by the
71 | // TypeScript doc
72 | if (readFiles.has(filePath)) {
73 | throw new Error(`Circular reference in ${filePath}`);
74 | }
75 | readFiles.add(filePath);
76 | try {
77 | const json = await customReadFilep(filePath, 'utf8');
78 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
79 | let contents: any;
80 | try {
81 | contents = JSON5.parse(json);
82 | } catch (e) {
83 | const err = e as Error;
84 | err.message = `Unable to parse ${filePath}!\n${err.message}`;
85 | throw err;
86 | }
87 |
88 | if (contents.extends) {
89 | const nextFile = await getBase(
90 | contents.extends,
91 | customReadFilep,
92 | readFiles,
93 | path.dirname(filePath),
94 | );
95 | contents = combineTSConfig(nextFile, contents);
96 | }
97 |
98 | return contents;
99 | } catch (e) {
100 | const err = e as Error;
101 | err.message = `Error: ${filePath}\n${err.message}`;
102 | throw err;
103 | }
104 | }
105 |
106 | /**
107 | * Takes in 2 config files
108 | * @param base is loaded first
109 | * @param inherited is then loaded and overwrites base
110 | */
111 | function combineTSConfig(base: ConfigFile, inherited: ConfigFile): ConfigFile {
112 | const result: ConfigFile = {compilerOptions: {}};
113 |
114 | Object.assign(result, base, inherited);
115 | Object.assign(
116 | result.compilerOptions!,
117 | base.compilerOptions!,
118 | inherited.compilerOptions!,
119 | );
120 | delete result.extends;
121 | return result;
122 | }
123 |
124 | /**
125 | * An interface containing the top level data fields present in Config Files
126 | */
127 | export interface ConfigFile {
128 | files?: string[];
129 | compilerOptions?: {};
130 | include?: string[];
131 | exclude?: string[];
132 | extends?: string[];
133 | }
134 |
135 | /**
136 | * Automatically defines npm or yarn is going to be used:
137 | * - If only yarn.lock exists, use yarn
138 | * - If only package-lock.json or both exist, use npm
139 | */
140 | export function isYarnUsed(existsSync = fs.existsSync): boolean {
141 | if (existsSync('package-lock.json')) {
142 | return false;
143 | }
144 | return existsSync('yarn.lock');
145 | }
146 |
147 | export function getPkgManagerCommand(isYarnUsed?: boolean): string {
148 | return (
149 | (isYarnUsed ? 'yarn' : 'npm') + (process.platform === 'win32' ? '.cmd' : '')
150 | );
151 | }
152 |
153 | /**
154 | * Find the tsconfig.json, read it, and return parsed contents.
155 | * @param rootDir Directory where the tsconfig.json should be found.
156 | * If the tsconfig.json file has an "extends" field hop down the dependency tree
157 | * until it ends or a circular reference is found in which case an error will be
158 | * thrown
159 | */
160 | export async function getTSConfig(
161 | rootDir: string,
162 | customReadFilep?: ReadFileP,
163 | ): Promise {
164 | customReadFilep = (customReadFilep || readFilep) as ReadFileP;
165 | const readArr = new Set();
166 | return getBase('tsconfig.json', customReadFilep, readArr, rootDir);
167 | }
168 |
--------------------------------------------------------------------------------
/template/index.ts:
--------------------------------------------------------------------------------
1 | console.log("Try npm run lint/fix!");
2 |
3 | const longString = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ut aliquet diam.';
4 |
5 | const trailing = 'Semicolon'
6 |
7 | const why={am:'I tabbed?'};
8 |
9 | const iWish = "I didn't have a trailing space...";
10 |
11 | const sicilian = true;;
12 |
13 | const vizzini = (!!sicilian) ? !!!sicilian : sicilian;
14 |
15 | const re = /foo bar/;
16 |
17 | export function doSomeStuff(withThis: string, andThat: string, andThose: string[]) {
18 | //function on one line
19 | if(!Boolean(andThose.length)) {return false;}
20 | console.log(withThis);
21 | console.log(andThat);
22 | console.dir(andThose);
23 | console.log(longString, trailing, why, iWish, vizzini, re);
24 | return;
25 | }
26 | // TODO: more examples
27 |
--------------------------------------------------------------------------------
/test/fixtures/kitchen/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kitchen",
3 | "version": "1.0.0",
4 | "devDependencies": {
5 | "typescript": "^5.4.3",
6 | "gts": "file:../gts.tgz"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/test/fixtures/kitchen/src/samples.js:
--------------------------------------------------------------------------------
1 | let isASample = true;
2 | console.log(isASample);
3 |
--------------------------------------------------------------------------------
/test/fixtures/kitchen/src/server.ts:
--------------------------------------------------------------------------------
1 | const isThisTypeScript = true
2 |
--------------------------------------------------------------------------------
/test/kitchen.ts:
--------------------------------------------------------------------------------
1 | import chalk = require('chalk');
2 | import * as cp from 'child_process';
3 | import * as fs from 'fs-extra';
4 | import * as tmp from 'tmp';
5 | import * as assert from 'assert';
6 | import * as path from 'path';
7 | import {describe, it, before, after} from 'mocha';
8 |
9 | import spawn = require('cross-spawn');
10 | import execa = require('execa');
11 | // eslint-disable-next-line @typescript-eslint/no-var-requires
12 | const pkg = require('../../package.json');
13 | const keep = !!process.env.GTS_KEEP_TEMPDIRS;
14 | const stagingDir = tmp.dirSync({keep, unsafeCleanup: true});
15 | const stagingPath = stagingDir.name;
16 | const execOpts = {
17 | cwd: `${stagingPath}${path.sep}kitchen`,
18 | encoding: 'utf8' as BufferEncoding,
19 | };
20 |
21 | const action = process.platform !== 'win32' ? describe : describe.skip;
22 |
23 | action('🚰 kitchen sink', () => {
24 | const fixturesPath = path.join('test', 'fixtures');
25 | const gtsPath = path.join('node_modules', '.bin', 'gts');
26 | const kitchenPath = path.join(stagingPath, 'kitchen');
27 |
28 | // Create a staging directory with temp fixtures used to test on a fresh application.
29 | before(() => {
30 | console.log(`${chalk.blue(`${__filename} staging area: ${stagingPath}`)}`);
31 | cp.execSync('npm pack');
32 | const tarball = `${pkg.name}-${pkg.version}.tgz`;
33 | fs.renameSync(tarball, 'gts.tgz');
34 | const targetPath = path.resolve(stagingPath, 'gts.tgz');
35 | console.log('moving packed tar to ', targetPath);
36 | fs.moveSync('gts.tgz', targetPath);
37 | fs.copySync(fixturesPath, path.join(stagingPath, path.sep));
38 | });
39 | // CLEAN UP - remove the staging directory when done.
40 | after('cleanup staging', () => {
41 | if (!keep) {
42 | stagingDir.removeCallback();
43 | }
44 | });
45 |
46 | it('it should run init', () => {
47 | const args = [
48 | '-p',
49 | path.resolve(stagingPath, 'gts.tgz'),
50 | 'gts',
51 | 'init',
52 | // It's important to use `-n` here because we don't want to overwrite
53 | // the version of gts installed, as it will trigger the npm install.
54 | '-n',
55 | ];
56 |
57 | const res = spawn.sync('npx', args, execOpts);
58 | console.log('out: ', res.stdout + '');
59 | console.log('error: ', res.stderr + '');
60 |
61 | // Ensure config files got generated.
62 | fs.accessSync(path.join(kitchenPath, 'tsconfig.json'));
63 | fs.accessSync(path.join(kitchenPath, '.eslintrc.json'));
64 | fs.accessSync(path.join(kitchenPath, '.eslintignore'));
65 | fs.accessSync(path.join(kitchenPath, '.prettierrc.js'));
66 | fs.accessSync(path.join(kitchenPath, '.editorconfig'));
67 | console.log('ensured config files existed');
68 |
69 | // Compilation shouldn't have happened. Hence no `build` directory.
70 | const dirContents = fs.readdirSync(kitchenPath);
71 | console.log(`read dirContents length = ${dirContents.length}`);
72 | assert.strictEqual(dirContents.indexOf('build'), -1);
73 | });
74 |
75 | it('should use as a non-locally installed module', () => {
76 | // Use from a directory different from where we have locally installed. This
77 | // simulates use as a globally installed module.
78 | const GTS = path.resolve(stagingPath, 'kitchen/node_modules/.bin/gts');
79 | const tmpDir = tmp.dirSync({keep, unsafeCleanup: true});
80 | const opts = {cwd: path.join(tmpDir.name, 'kitchen')};
81 |
82 | // Copy test files.
83 | fs.copySync(fixturesPath, tmpDir.name);
84 | // Test package.json expects a gts tarball from ../gts.tgz.
85 | fs.copySync(
86 | path.join(stagingPath, 'gts.tgz'),
87 | path.join(tmpDir.name, 'gts.tgz'),
88 | );
89 | // It's important to use `-n` here because we don't want to overwrite
90 | // the version of gts installed, as it will trigger the npm install.
91 | spawn.sync(GTS, ['init', '-n'], opts);
92 |
93 | // The `extends` field must use the local gts path.
94 | const tsconfigJson = fs.readFileSync(
95 | path.join(tmpDir.name, 'kitchen', 'tsconfig.json'),
96 | 'utf8',
97 | );
98 | const tsconfig = JSON.parse(tsconfigJson);
99 | assert.deepStrictEqual(
100 | tsconfig.extends,
101 | './node_modules/gts/tsconfig-google.json',
102 | );
103 |
104 | // server.ts has a lint error. Should error.
105 | assert.throws(() => cp.execSync(`${GTS} lint src/server.ts`, opts));
106 |
107 | if (!keep) {
108 | tmpDir.removeCallback();
109 | }
110 | });
111 |
112 | it('should terminate generated files with newline', () => {
113 | const GTS = path.resolve(stagingPath, gtsPath);
114 | spawn.sync(GTS, ['init', '-y'], execOpts);
115 | assert.ok(
116 | fs
117 | .readFileSync(path.join(kitchenPath, 'package.json'), 'utf8')
118 | .endsWith('\n'),
119 | );
120 | assert.ok(
121 | fs
122 | .readFileSync(path.join(kitchenPath, 'tsconfig.json'), 'utf8')
123 | .endsWith('\n'),
124 | );
125 | assert.ok(
126 | fs
127 | .readFileSync(path.join(kitchenPath, '.eslintrc.json'), 'utf8')
128 | .endsWith('\n'),
129 | );
130 | assert.ok(
131 | fs
132 | .readFileSync(path.join(kitchenPath, '.eslintignore'), 'utf8')
133 | .endsWith('\n'),
134 | );
135 | assert.ok(
136 | fs
137 | .readFileSync(path.join(kitchenPath, '.prettierrc.js'), 'utf8')
138 | .endsWith('\n'),
139 | );
140 | });
141 |
142 | it('should lint before fix', async () => {
143 | const res = await execa(
144 | 'npm',
145 | ['run', 'lint'],
146 | Object.assign({}, {reject: false}, execOpts),
147 | );
148 | assert.strictEqual(res.exitCode, 1);
149 | assert.ok(res.stdout.includes('assigned a value but'));
150 | });
151 |
152 | it('should fix', () => {
153 | const preFix = fs
154 | .readFileSync(path.join(kitchenPath, 'src', 'server.ts'), 'utf8')
155 | .split(/[\n\r]+/);
156 |
157 | cp.execSync('npm run fix', execOpts);
158 | const postFix = fs
159 | .readFileSync(path.join(kitchenPath, 'src', 'server.ts'), 'utf8')
160 | .split(/[\n\r]+/);
161 | assert.strictEqual(preFix[0].trim() + ';', postFix[0]); // fix should have added a semi-colon
162 | });
163 |
164 | it('should lint after fix', () => {
165 | cp.execSync('npm run lint', execOpts);
166 | });
167 |
168 | it('should build', () => {
169 | cp.execSync('npm run compile', execOpts);
170 | fs.accessSync(path.join(kitchenPath, 'build', 'src', 'server.js'));
171 | fs.accessSync(path.join(kitchenPath, 'build', 'src', 'server.js.map'));
172 | fs.accessSync(path.join(kitchenPath, 'build', 'src', 'server.d.ts'));
173 | });
174 |
175 | // Verify the `gts clean` command actually removes the output dir
176 | it('should clean', () => {
177 | cp.execSync('npm run clean', execOpts);
178 | assert.throws(() => fs.accessSync(path.join(kitchenPath, 'build')));
179 | });
180 | });
181 |
--------------------------------------------------------------------------------
/test/test-clean.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import * as assert from 'assert';
18 | import * as fs from 'fs';
19 | import * as path from 'path';
20 |
21 | import {clean} from '../src/clean';
22 | import {nop} from '../src/util';
23 |
24 | import {withFixtures} from 'inline-fixtures';
25 | import {describe, it} from 'mocha';
26 |
27 | describe('clean', () => {
28 | const OPTIONS = {
29 | gtsRootDir: path.resolve(__dirname, '../..'),
30 | targetRootDir: './',
31 | dryRun: false,
32 | yes: false,
33 | no: false,
34 | logger: {log: nop, error: nop, dir: nop},
35 | };
36 |
37 | it('should gracefully error if tsconfig is missing', () => {
38 | return assert.rejects(() =>
39 | withFixtures({}, async () => {
40 | await clean(OPTIONS);
41 | }),
42 | );
43 | });
44 |
45 | it('should gracefully error if tsconfig does not have valid outDir', () => {
46 | return withFixtures({'tsconfig.json': JSON.stringify({})}, async () => {
47 | const deleted = await clean(OPTIONS);
48 | assert.strictEqual(deleted, false);
49 | });
50 | });
51 |
52 | it('should gracefully handle JSON with comments', () => {
53 | const invalidJson = `
54 | {
55 | // hah, comments in JSON, what a world
56 | compilerOptions: {outDir: '.'}
57 | }`;
58 | return withFixtures({'tsconfig.json': invalidJson}, async () => {
59 | await clean(OPTIONS);
60 | });
61 | });
62 |
63 | it('should gracefully error if tsconfig has invalid JSON', () => {
64 | const invalidJson = "silly bear, this isn't JSON!";
65 | return withFixtures({'tsconfig.json': invalidJson}, async () => {
66 | await assert.rejects(clean(OPTIONS), /Unable to parse/);
67 | });
68 | });
69 |
70 | it('should avoid deleting .', () => {
71 | return withFixtures(
72 | {'tsconfig.json': JSON.stringify({compilerOptions: {outDir: '.'}})},
73 | async () => {
74 | const deleted = await clean(OPTIONS);
75 | assert.strictEqual(deleted, false);
76 | },
77 | );
78 | });
79 |
80 | it('should ensure that outDir is local to targetRoot', () => {
81 | return assert.rejects(() =>
82 | withFixtures(
83 | {
84 | 'tsconfig.json': JSON.stringify({
85 | compilerOptions: {outDir: '../out'},
86 | }),
87 | },
88 | async () => {
89 | const deleted = await clean(OPTIONS);
90 | assert.strictEqual(deleted, false);
91 | },
92 | ),
93 | );
94 | });
95 |
96 | it('should remove outDir', () => {
97 | const OUT = 'outputDirectory';
98 | return withFixtures(
99 | {
100 | 'tsconfig.json': JSON.stringify({compilerOptions: {outDir: OUT}}),
101 | [OUT]: {},
102 | },
103 | async dir => {
104 | const outputPath = path.join(dir, OUT);
105 | // make sure the output directory exists.
106 | fs.accessSync(outputPath);
107 | const deleted = await clean(OPTIONS);
108 | assert.strictEqual(deleted, true);
109 | // make sure the directory has been deleted.
110 | assert.throws(() => {
111 | fs.accessSync(outputPath);
112 | });
113 | },
114 | );
115 | });
116 | });
117 |
--------------------------------------------------------------------------------
/test/test-init.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google LLC.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import * as sinon from 'sinon';
18 | import * as cp from 'child_process';
19 | import * as assert from 'assert';
20 | import * as fs from 'fs';
21 | import * as path from 'path';
22 | import {accessSync} from 'fs';
23 | import {PackageJSON} from '@npm/types';
24 | import {withFixtures, Fixtures} from 'inline-fixtures';
25 | import {describe, it, beforeEach, afterEach} from 'mocha';
26 |
27 | import {nop, readJsonp as readJson, DefaultPackage} from '../src/util';
28 | import {Options} from '../src/cli';
29 | import * as init from '../src/init';
30 |
31 | const OPTIONS: Options = {
32 | gtsRootDir: path.resolve(__dirname, '../..'),
33 | targetRootDir: './',
34 | dryRun: false,
35 | yes: false,
36 | no: false,
37 | logger: {log: nop, error: nop, dir: nop},
38 | };
39 | const OPTIONS_YES = Object.assign({}, OPTIONS, {yes: true});
40 | const OPTIONS_NO = Object.assign({}, OPTIONS, {no: true});
41 | const OPTIONS_YARN = Object.assign({}, OPTIONS_YES, {yarn: true});
42 | const MINIMAL_PACKAGE_JSON = {name: 'name', version: 'v1.1.1'};
43 |
44 | function hasExpectedScripts(packageJson: PackageJSON): boolean {
45 | return (
46 | !!packageJson.scripts &&
47 | ['lint', 'clean', 'compile', 'fix', 'prepare', 'pretest', 'posttest'].every(
48 | s => !!packageJson.scripts![s],
49 | )
50 | );
51 | }
52 |
53 | function hasExpectedDependencies(packageJson: PackageJSON): boolean {
54 | return (
55 | !!packageJson.devDependencies &&
56 | ['gts', 'typescript'].every(d => !!packageJson.devDependencies![d])
57 | );
58 | }
59 |
60 | describe('init', () => {
61 | const sandbox = sinon.createSandbox();
62 |
63 | beforeEach(function () {
64 | this.spawnSyncStub = sandbox.stub(cp, 'spawnSync');
65 | });
66 |
67 | afterEach(function () {
68 | this.spawnSyncStub.restore();
69 | });
70 |
71 | it('addScripts should add a scripts section if none exists', async () => {
72 | const pkg: PackageJSON = {...MINIMAL_PACKAGE_JSON};
73 | const result = await init.addScripts(pkg, OPTIONS);
74 | assert.strictEqual(result, true); // made edits.
75 | assert.ok(pkg.scripts);
76 | assert.strictEqual(hasExpectedScripts(pkg), true);
77 | });
78 |
79 | it('addScripts should not edit existing scripts on no', async () => {
80 | const SCRIPTS = {
81 | lint: 'fake lint',
82 | clean: 'fake clean',
83 | compile: 'fake tsc',
84 | fix: 'fake fix',
85 | prepare: 'fake run compile',
86 | pretest: 'fake run compile',
87 | posttest: 'fake run lint',
88 | };
89 | const pkg: PackageJSON = {
90 | ...MINIMAL_PACKAGE_JSON,
91 | scripts: {...SCRIPTS},
92 | };
93 | const result = await init.addScripts(pkg, OPTIONS_NO);
94 | assert.strictEqual(result, false); // no edits.
95 | assert.deepStrictEqual(pkg.scripts, SCRIPTS);
96 | });
97 |
98 | it('addScripts should edit existing scripts on yes', async () => {
99 | const SCRIPTS = {
100 | lint: 'fake lint',
101 | clean: 'fake clean',
102 | compile: 'fake tsc',
103 | fix: 'fake fix',
104 | prepare: 'fake run compile',
105 | pretest: 'fake run compile',
106 | posttest: 'fake run lint',
107 | };
108 | const pkg: PackageJSON = {
109 | ...MINIMAL_PACKAGE_JSON,
110 | scripts: {...SCRIPTS},
111 | };
112 | const result = await init.addScripts(pkg, OPTIONS_YES);
113 | assert.strictEqual(result, true); // made edits.
114 | assert.notDeepStrictEqual(pkg.scripts, SCRIPTS);
115 | });
116 |
117 | it('addDependencies should add a deps section if none exists', async () => {
118 | const pkg: PackageJSON = {...MINIMAL_PACKAGE_JSON};
119 | const result = await init.addDependencies(pkg, OPTIONS);
120 | assert.strictEqual(result, true); // made edits.
121 | assert.ok(pkg.devDependencies);
122 | });
123 |
124 | it('addDependencies should not edit existing deps on no', async () => {
125 | const DEPS: DefaultPackage = {
126 | gts: 'something',
127 | typescript: 'or the other',
128 | '@types/node': 'or another',
129 | };
130 | const pkg: PackageJSON = {
131 | ...MINIMAL_PACKAGE_JSON,
132 | devDependencies: {...DEPS},
133 | };
134 | const OPTIONS_NO = Object.assign({}, OPTIONS, {no: true});
135 | const result = await init.addDependencies(pkg, OPTIONS_NO);
136 | assert.strictEqual(result, false); // no edits.
137 | assert.deepStrictEqual(pkg.devDependencies, DEPS);
138 | });
139 |
140 | it('addDependencies should edit existing deps on yes', async () => {
141 | const DEPS = {gts: 'something', typescript: 'or the other'};
142 | const pkg: PackageJSON = {
143 | ...MINIMAL_PACKAGE_JSON,
144 | devDependencies: {...DEPS},
145 | };
146 | const result = await init.addDependencies(pkg, OPTIONS_YES);
147 | assert.strictEqual(result, true); // made edits.
148 | assert.notDeepStrictEqual(pkg.devDependencies, DEPS);
149 | });
150 |
151 | // init
152 | it('init should read local package.json', () => {
153 | const originalContents = {some: 'property'};
154 | return withFixtures(
155 | {'package.json': JSON.stringify(originalContents)},
156 | async () => {
157 | const result = await init.init(OPTIONS_YES);
158 | assert.strictEqual(result, true);
159 | const contents = await readJson('./package.json');
160 |
161 | assert.notStrictEqual(
162 | contents,
163 | originalContents,
164 | 'the file should have been modified',
165 | );
166 | assert.strictEqual(
167 | contents.some,
168 | originalContents.some,
169 | 'unrelated property should have preserved',
170 | );
171 | },
172 | );
173 | });
174 |
175 | it('init should handle missing package.json', () => {
176 | return withFixtures({}, async () => {
177 | const result = await init.init(OPTIONS_YES);
178 | assert.strictEqual(result, true);
179 | const contents = await readJson('./package.json');
180 | assert.strictEqual(hasExpectedScripts(contents), true);
181 | assert.strictEqual(hasExpectedDependencies(contents), true);
182 | });
183 | });
184 |
185 | it('init should support yarn', () => {
186 | return withFixtures(
187 | {
188 | 'package.json': JSON.stringify({name: 'test'}),
189 | 'yarn.lock': '',
190 | },
191 | async () => {
192 | const result = await init.init(OPTIONS_YARN);
193 | assert.strictEqual(result, true);
194 |
195 | const contents = await readJson('./package.json');
196 | const cmd = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';
197 | assert.strictEqual(contents.scripts.prepare, cmd + ' run compile');
198 | },
199 | );
200 | });
201 |
202 | it('should install a default template if the source directory do not exists', () => {
203 | return withFixtures({}, async dir => {
204 | const indexPath = path.join(dir, 'src', 'index.ts');
205 | await init.init(OPTIONS_YES);
206 | assert.doesNotThrow(() => {
207 | accessSync(indexPath);
208 | });
209 | });
210 | });
211 |
212 | it('should install template copy if src directory already exists and is empty', () => {
213 | const FIXTURES = {
214 | src: {},
215 | };
216 | return withFixtures(FIXTURES, async dir => {
217 | const dirPath = path.join(dir, 'src');
218 | const created = await init.installDefaultTemplate(OPTIONS_YES);
219 | assert.strictEqual(created, true);
220 | assert.doesNotThrow(() => {
221 | accessSync(path.join(dirPath, 'index.ts'));
222 | });
223 | });
224 | });
225 |
226 | it('should install template copy if src directory already exists and contains files other than ts', () => {
227 | const FIXTURES = {
228 | src: {
229 | 'README.md': '# Read this',
230 | },
231 | };
232 | return withFixtures(FIXTURES, async dir => {
233 | const dirPath = path.join(dir, 'src');
234 | const created = await init.installDefaultTemplate(OPTIONS_YES);
235 | assert.strictEqual(created, true);
236 | assert.doesNotThrow(() => {
237 | // Both old and new files should exist.
238 | accessSync(path.join(dirPath, 'README.md'));
239 | accessSync(path.join(dirPath, 'index.ts'));
240 | });
241 | });
242 | });
243 |
244 | it('should copy the template with correct contents', () => {
245 | const FIXTURES = {
246 | src: {},
247 | };
248 | return withFixtures(FIXTURES, async dir => {
249 | const destDir = path.join(dir, 'src');
250 | const created = await init.installDefaultTemplate(OPTIONS_YES);
251 | assert.strictEqual(created, true);
252 |
253 | // make sure the target directory exists.
254 | accessSync(destDir);
255 |
256 | // make sure the copied file exists and has the same content.
257 | const srcFilename = path.join(__dirname, '../template/index.ts');
258 | const destFilename = path.join(destDir, 'index.ts');
259 | const content = fs.readFileSync(destFilename, 'utf8');
260 | assert.strictEqual(content, fs.readFileSync(srcFilename, 'utf8'));
261 | });
262 | });
263 |
264 | it('should not install the default template if the source directory already exists and does contain ts files', () => {
265 | const EXISTING = 'src';
266 | const FIXTURES: Fixtures = {
267 | [EXISTING]: {
268 | 'main.ts': '42;',
269 | },
270 | };
271 | return withFixtures(FIXTURES, async dir => {
272 | const newPath = path.join(dir, 'src');
273 | const created = await init.installDefaultTemplate(OPTIONS_YES);
274 | assert.strictEqual(created, false);
275 | assert.doesNotThrow(() => {
276 | accessSync(newPath);
277 | });
278 | });
279 | });
280 | });
281 |
--------------------------------------------------------------------------------
/test/test-util.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import * as assert from 'assert';
17 | import * as path from 'path';
18 | import {PathLike} from 'fs';
19 | import {describe, it} from 'mocha';
20 | import {
21 | ConfigFile,
22 | getTSConfig,
23 | isYarnUsed,
24 | getPkgManagerCommand,
25 | } from '../src/util';
26 |
27 | /**
28 | * Creates a fake promisified readFile function from a map
29 | * @param myMap contains a filepath as the key and a ConfigFile object as the
30 | * value.
31 | * The returned function has the same interface as fs.readFile
32 | */
33 | function createFakeReadFilep(myMap: Map) {
34 | return (configPath: string) => {
35 | const configFile = myMap.get(configPath);
36 | if (configFile) {
37 | return Promise.resolve(JSON.stringify(configFile));
38 | } else {
39 | return Promise.reject(`${configPath} Not Found`);
40 | }
41 | };
42 | }
43 |
44 | function makeFakeFsExistsSync(
45 | expected: PathLike[],
46 | ): (path: PathLike) => boolean {
47 | return (path: PathLike) => expected.some(item => item === path);
48 | }
49 |
50 | const FAKE_DIRECTORY = '/some/fake/directory';
51 | const PATH_TO_TSCONFIG = path.resolve(FAKE_DIRECTORY, 'tsconfig.json');
52 | const PATH_TO_CONFIG2 = path.resolve(FAKE_DIRECTORY, 'FAKE_CONFIG2');
53 | const PATH_TO_CONFIG3 = path.resolve(FAKE_DIRECTORY, 'FAKE_CONFIG3');
54 |
55 | describe('util', () => {
56 | it('get should parse the correct tsconfig file', async () => {
57 | const FAKE_CONFIG1 = {files: ['b']};
58 |
59 | function fakeReadFilep(
60 | configPath: string,
61 | encoding: string,
62 | ): Promise {
63 | assert.strictEqual(configPath, PATH_TO_TSCONFIG);
64 | assert.strictEqual(encoding, 'utf8');
65 | return Promise.resolve(JSON.stringify(FAKE_CONFIG1));
66 | }
67 | const contents = await getTSConfig(FAKE_DIRECTORY, fakeReadFilep);
68 |
69 | assert.deepStrictEqual(contents, FAKE_CONFIG1);
70 | });
71 |
72 | it('should throw an error if it finds a circular reference', () => {
73 | const FAKE_CONFIG1 = {files: ['b'], extends: 'FAKE_CONFIG2'};
74 | const FAKE_CONFIG2 = {extends: 'FAKE_CONFIG3'};
75 | const FAKE_CONFIG3 = {extends: 'tsconfig.json'};
76 | const myMap = new Map();
77 | myMap.set(PATH_TO_TSCONFIG, FAKE_CONFIG1);
78 | myMap.set(PATH_TO_CONFIG2, FAKE_CONFIG2);
79 | myMap.set(PATH_TO_CONFIG3, FAKE_CONFIG3);
80 |
81 | // eslint-disable-next-line n/no-unsupported-features/node-builtins
82 | return assert.rejects(
83 | () => getTSConfig(FAKE_DIRECTORY, createFakeReadFilep(myMap)),
84 | Error,
85 | 'Circular Reference Detected',
86 | );
87 | });
88 |
89 | it('should follow dependency chain caused by extends files', async () => {
90 | const FAKE_CONFIG1 = {
91 | compilerOptions: {a: 'n'},
92 | files: ['b'],
93 | extends: 'FAKE_CONFIG2',
94 | };
95 | const FAKE_CONFIG2 = {include: ['/stuff/*'], extends: 'FAKE_CONFIG3'};
96 | const FAKE_CONFIG3 = {exclude: ['doesnt/look/like/anything/to/me']};
97 | const combinedConfig = {
98 | compilerOptions: {a: 'n'},
99 | files: ['b'],
100 | include: ['/stuff/*'],
101 | exclude: ['doesnt/look/like/anything/to/me'],
102 | };
103 |
104 | const myMap = new Map();
105 | myMap.set(PATH_TO_TSCONFIG, FAKE_CONFIG1);
106 | myMap.set(PATH_TO_CONFIG2, FAKE_CONFIG2);
107 | myMap.set(PATH_TO_CONFIG3, FAKE_CONFIG3);
108 |
109 | const contents = await getTSConfig(
110 | FAKE_DIRECTORY,
111 | createFakeReadFilep(myMap),
112 | );
113 | assert.deepStrictEqual(contents, combinedConfig);
114 | });
115 |
116 | it('when a file contains an extends field, the base file is loaded first then overridden by the inherited files', async () => {
117 | const FAKE_CONFIG1 = {files: ['b'], extends: 'FAKE_CONFIG2'};
118 | const FAKE_CONFIG2 = {files: ['c'], extends: 'FAKE_CONFIG3'};
119 | const FAKE_CONFIG3 = {files: ['d']};
120 | const combinedConfig = {compilerOptions: {}, files: ['b']};
121 | const myMap = new Map();
122 | myMap.set(PATH_TO_TSCONFIG, FAKE_CONFIG1);
123 | myMap.set(PATH_TO_CONFIG2, FAKE_CONFIG2);
124 | myMap.set(PATH_TO_CONFIG3, FAKE_CONFIG3);
125 |
126 | const contents = await getTSConfig(
127 | FAKE_DIRECTORY,
128 | createFakeReadFilep(myMap),
129 | );
130 | assert.deepStrictEqual(contents, combinedConfig);
131 | });
132 |
133 | it('when reading a file, all filepaths should be relative to the config file currently being read', async () => {
134 | const FAKE_CONFIG1 = {files: ['b'], extends: './foo/FAKE_CONFIG2'};
135 | const FAKE_CONFIG2 = {include: ['c'], extends: './bar/FAKE_CONFIG3'};
136 | const FAKE_CONFIG3 = {exclude: ['d']};
137 | const combinedConfig = {
138 | compilerOptions: {},
139 | exclude: ['d'],
140 | files: ['b'],
141 | include: ['c'],
142 | };
143 | const myMap = new Map();
144 | myMap.set(PATH_TO_TSCONFIG, FAKE_CONFIG1);
145 | myMap.set(path.resolve(FAKE_DIRECTORY, './foo/FAKE_CONFIG2'), FAKE_CONFIG2);
146 | myMap.set(
147 | path.resolve(FAKE_DIRECTORY, './foo/bar/FAKE_CONFIG3'),
148 | FAKE_CONFIG3,
149 | );
150 |
151 | const contents = await getTSConfig(
152 | FAKE_DIRECTORY,
153 | createFakeReadFilep(myMap),
154 | );
155 | assert.deepStrictEqual(contents, combinedConfig);
156 | });
157 |
158 | it('function throws an error when reading a file that does not exist', () => {
159 | const myMap = new Map();
160 |
161 | // eslint-disable-next-line n/no-unsupported-features/node-builtins
162 | return assert.rejects(
163 | () => getTSConfig(FAKE_DIRECTORY, createFakeReadFilep(myMap)),
164 | Error,
165 | `${FAKE_DIRECTORY}/tsconfig.json Not Found`,
166 | );
167 | });
168 |
169 | it("isYarnUsed returns true if there's yarn.lock file only", () => {
170 | const existsSync = makeFakeFsExistsSync(['yarn.lock']);
171 | assert.strictEqual(isYarnUsed(existsSync), true);
172 | });
173 |
174 | it("isYarnUsed returns false if there's package-lock.json file only", () => {
175 | const existsSync = makeFakeFsExistsSync(['package-lock.json']);
176 | assert.strictEqual(isYarnUsed(existsSync), false);
177 | });
178 |
179 | it("isYarnUsed returns false if there're yarn.lock and package-lock.json files", () => {
180 | const existsSync = makeFakeFsExistsSync(['package-lock.json', 'yarn.lock']);
181 | assert.strictEqual(isYarnUsed(existsSync), false);
182 | });
183 |
184 | const npmCmd = process.platform !== 'win32' ? 'npm' : 'npm.cmd';
185 | const yarnCmd = process.platform !== 'win32' ? 'yarn' : 'yarn.cmd';
186 | it('getPkgManagerCommand returns npm by default', () => {
187 | assert.strictEqual(getPkgManagerCommand(), npmCmd);
188 | assert.strictEqual(getPkgManagerCommand(), getPkgManagerCommand(false));
189 | });
190 |
191 | it('getPkgManagerCommand returns yarn', () => {
192 | assert.strictEqual(getPkgManagerCommand(true), yarnCmd);
193 | });
194 | });
195 |
--------------------------------------------------------------------------------
/tsconfig-google.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowUnreachableCode": false,
4 | "allowUnusedLabels": false,
5 | "composite": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "lib": ["ES2023"],
8 | "module": "commonjs",
9 | "noEmitOnError": true,
10 | "noFallthroughCasesInSwitch": true,
11 | "noImplicitReturns": true,
12 | "pretty": true,
13 | "sourceMap": true,
14 | "stripInternal": true,
15 | "strict": true,
16 | "target": "ES2022"
17 | },
18 | "exclude": ["node_modules"]
19 | }
20 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-google.json",
3 | "compilerOptions": {
4 | "rootDir": ".",
5 | "outDir": "build",
6 | "resolveJsonModule": true
7 | },
8 | "include": [".eslintrc.json", "src/**/*.ts", "test/**/*.ts"],
9 | "exclude": ["test/fixtures/**/*.*", "template/**/*.*"]
10 | }
11 |
--------------------------------------------------------------------------------