├── .editorconfig
├── .eslintignore
├── .gitattributes
├── .github
└── workflows
│ ├── ci.yml
│ ├── codeql.yml
│ ├── dependency-review.yml
│ └── scorecards.yml
├── .gitignore
├── .prettierrc.yml
├── LICENSE
├── README.md
├── __tests__
├── app.js
├── boilerplate.js
├── cli.js
├── editorconfig.js
├── eslint.js
├── git.js
└── readme.js
├── generators
├── app
│ └── index.js
├── boilerplate
│ ├── index.js
│ └── templates
│ │ └── index.js
├── cli
│ ├── index.js
│ └── templates
│ │ └── cli.js
├── editorconfig
│ ├── index.js
│ └── templates
│ │ └── editorconfig
├── eslint
│ ├── index.js
│ └── templates
│ │ └── eslintignore
├── git
│ ├── index.js
│ └── templates
│ │ ├── gitattributes
│ │ └── gitignore
└── readme
│ ├── index.js
│ └── templates
│ └── README.md
├── index.js
├── package-lock.json
└── package.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 | generators/*/templates
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | permissions:
12 | contents: read
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 |
18 | strategy:
19 | matrix:
20 | # TODO: We need to re-introduce Node 6 and 7 once we have a way to run tests on them.
21 | node-version: [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
22 | continue-on-error: true
23 | steps:
24 | - name: Checkout repository
25 | uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
26 |
27 | - name: Set up Node.js ${{ matrix.node-version }}
28 | uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
29 | with:
30 | node-version: ${{ matrix.node-version }}
31 |
32 | - name: Install dependencies
33 | run: npm install
34 |
35 | - name: Run lint
36 | run: npm run pretest
37 |
38 | - name: Run tests
39 | run: npm test
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: ["main"]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: ["main"]
20 | schedule:
21 | - cron: "0 0 * * 1"
22 |
23 | permissions:
24 | contents: read
25 |
26 | jobs:
27 | analyze:
28 | name: Analyze
29 | runs-on: ubuntu-latest
30 | permissions:
31 | actions: read
32 | contents: read
33 | security-events: write
34 |
35 | strategy:
36 | fail-fast: false
37 | matrix:
38 | language: ["javascript"]
39 | # CodeQL supports [ $supported-codeql-languages ]
40 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
41 |
42 | steps:
43 | - name: Checkout repository
44 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
45 |
46 | # Initializes the CodeQL tools for scanning.
47 | - name: Initialize CodeQL
48 | uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
49 | with:
50 | languages: ${{ matrix.language }}
51 | # If you wish to specify custom queries, you can do so here or in a config file.
52 | # By default, queries listed here will override any specified in a config file.
53 | # Prefix the list here with "+" to use these queries and those in the config file.
54 |
55 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
56 | # If this step fails, then you should remove it and run the build manually (see below)
57 | - name: Autobuild
58 | uses: github/codeql-action/autobuild@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
59 |
60 | # ℹ️ Command-line programs to run using the OS shell.
61 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
62 |
63 | # If the Autobuild fails above, remove it and uncomment the following three lines.
64 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
65 |
66 | # - run: |
67 | # echo "Run, Build Application using script"
68 | # ./location_of_script_within_repo/buildscript.sh
69 |
70 | - name: Perform CodeQL Analysis
71 | uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
72 | with:
73 | category: "/language:${{matrix.language}}"
74 |
--------------------------------------------------------------------------------
/.github/workflows/dependency-review.yml:
--------------------------------------------------------------------------------
1 | # Dependency Review Action
2 | #
3 | # This Action will scan dependency manifest files that change as part of a Pull Request,
4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR.
5 | # Once installed, if the workflow run is marked as required,
6 | # PRs introducing known-vulnerable packages will be blocked from merging.
7 | #
8 | # Source repository: https://github.com/actions/dependency-review-action
9 | name: 'Dependency Review'
10 | on: [pull_request]
11 |
12 | permissions:
13 | contents: read
14 |
15 | jobs:
16 | dependency-review:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: 'Checkout Repository'
20 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
21 | - name: 'Dependency Review'
22 | uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
23 |
--------------------------------------------------------------------------------
/.github/workflows/scorecards.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub. They are provided
2 | # by a third-party and are governed by separate terms of service, privacy
3 | # policy, and support documentation.
4 |
5 | name: Scorecard supply-chain security
6 | on:
7 | # For Branch-Protection check. Only the default branch is supported. See
8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
9 | branch_protection_rule:
10 | # To guarantee Maintained check is occasionally updated. See
11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
12 | schedule:
13 | - cron: '20 7 * * 2'
14 | push:
15 | branches: ["main"]
16 |
17 | # Declare default permissions as read only.
18 | permissions: read-all
19 |
20 | jobs:
21 | analysis:
22 | name: Scorecard analysis
23 | runs-on: ubuntu-latest
24 | permissions:
25 | # Needed to upload the results to code-scanning dashboard.
26 | security-events: write
27 | # Needed to publish results and get a badge (see publish_results below).
28 | id-token: write
29 | contents: read
30 | actions: read
31 | # To allow GraphQL ListCommits to work
32 | issues: read
33 | pull-requests: read
34 | # To detect SAST tools
35 | checks: read
36 |
37 | steps:
38 | - name: "Checkout code"
39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
40 | with:
41 | persist-credentials: false
42 |
43 | - name: "Run analysis"
44 | uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
45 | with:
46 | results_file: results.sarif
47 | results_format: sarif
48 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
49 | # - you want to enable the Branch-Protection check on a *public* repository, or
50 | # - you are installing Scorecards on a *private* repository
51 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
52 | # repo_token: ${{ secrets.SCORECARD_TOKEN }}
53 |
54 | # Public repositories:
55 | # - Publish results to OpenSSF REST API for easy access by consumers
56 | # - Allows the repository to include the Scorecard badge.
57 | # - See https://github.com/ossf/scorecard-action#publishing-results.
58 | # For private repositories:
59 | # - `publish_results` will always be set to `false`, regardless
60 | # of the value entered here.
61 | publish_results: true
62 |
63 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
64 | # format to the repository Actions tab.
65 | - name: "Upload artifact"
66 | uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
67 | with:
68 | name: SARIF file
69 | path: results.sarif
70 | retention-days: 5
71 |
72 | # Upload the results to GitHub's code scanning dashboard.
73 | - name: "Upload to code-scanning"
74 | uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
75 | with:
76 | sarif_file: results.sarif
77 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 |
--------------------------------------------------------------------------------
/.prettierrc.yml:
--------------------------------------------------------------------------------
1 | singleQuote: true
2 | printWidth: 90
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2015
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Node Generator [](https://github.com/yeoman/generator-node/actions/workflows/ci.yml) [](https://gitter.im/yeoman/yeoman) [](https://opencollective.com/yeoman#support)
2 |
3 | `generator-node` creates a base template to start a new Node.js module.
4 |
5 | It is also easily composed into your own generators so you can only target your efforts at your generator's specific features.
6 |
7 |
8 | ## Install
9 |
10 | ```
11 | $ npm install --global generator-node
12 | ```
13 |
14 |
15 | ## Usage
16 |
17 | ```
18 | $ yo node
19 | ```
20 |
21 | *Note that this template will generate files in the current directory, so be sure to change to a new directory first if you don't want to overwrite existing files.*
22 |
23 | That'll generate a project with all the common tools setup. This includes:
24 |
25 | - Filled `package.json` file
26 | - [jest](https://facebook.github.io/jest/) unit test and code coverage (optionally tracked on [Coveralls](https://coveralls.io/))
27 | - [ESLint](http://eslint.org/) linting and code style checking
28 | - [Travis CI](https://travis-ci.com/) continuous integration (optional)
29 | - [License](https://spdx.org/licenses/)
30 |
31 |
32 | ### Running tests
33 |
34 | Once the project is scaffolded, inside the project folder run:
35 |
36 | ```
37 | $ npm test
38 | ```
39 |
40 | You can also directly use jest to run test on single files:
41 |
42 | ```
43 | $ npm -g install jest-cli
44 | $ jest --watch
45 | ```
46 |
47 |
48 | ### Publishing your code
49 |
50 | Once your tests are passing (ideally with a Travis CI green run), you might be ready to publish your code to npm. We recommend you using [npm version](https://docs.npmjs.com/cli/version) to tag release correctly.
51 |
52 | ```
53 | $ npm version major
54 | $ git push --follow-tags
55 | # ATTENTION: There is no turning back here.
56 | $ npm publish
57 | ```
58 |
59 |
60 | ## Extend this generator
61 |
62 | First of all, make sure you're comfortable with [Yeoman composability](http://yeoman.io/authoring/composability.html) feature. Then in your own generator:
63 |
64 | ```js
65 | var Generator = require('yeoman-generator');
66 |
67 | module.exports = class extends Generator({
68 | default() {
69 | this.composeWith(require.resolve('generator-node/generators/app'), {
70 | /* provide the options you want */
71 | });
72 | }
73 | });
74 | ```
75 |
76 |
77 | ### Options
78 |
79 | Here's a list of our supported options:
80 |
81 | - `boilerplate` (Boolean, default true) include or not the boilerplate files (`lib/index.js`, `test/index.js`).
82 | - `cli` (Boolean, default false) include or not a `lib/cli.js` file.
83 | - `editorconfig` (Boolean, default true) include or not a `.editorconfig` file.
84 | - `git` (Boolean, default true) include or not the git files (`.gitattributes`, `.gitignore`).
85 | - `license` (Boolean, default true) include or not a `LICENSE` file.
86 | - `travis` (Boolean, default true) include or not a `.travis.yml` file.
87 | - `githubAccount` (String) Account name for GitHub repo location.
88 | - `readme` (String) content of the `README.md` file. Given this option, generator-node will still generate the title (with badges) and the license section.
89 |
90 |
91 | ### Sub generators
92 |
93 | If you don't need all the features provided by the main generator, you can still use a limited set of features by composing with our sub generators directly.
94 |
95 | Remember you can see the options of each sub generators by running `yo node:sub --help`.
96 |
97 | - `node:boilerplate`
98 | - `node:cli`
99 | - `node:editorconfig`
100 | - `node:eslint`
101 | - `node:git`
102 | - `node:readme`
103 |
104 |
105 | ## Backers
106 | Love Yeoman work and community? Help us keep it alive by donating funds to cover project expenses!
107 | [[Become a backer](https://opencollective.com/yeoman#support)]
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 | ## License
201 |
202 | MIT © Yeoman team (http://yeoman.io)
203 |
--------------------------------------------------------------------------------
/__tests__/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const _ = require('lodash');
3 | const assert = require('yeoman-assert');
4 | const helpers = require('yeoman-test');
5 |
6 | jest.mock('npm-name', () => {
7 | return () => Promise.resolve(true);
8 | });
9 |
10 | jest.mock('github-username', () => {
11 | return () => Promise.resolve('unicornUser');
12 | });
13 |
14 | jest.mock('generator-license/app', () => {
15 | const helpers = require('yeoman-test');
16 | return helpers.createDummyGenerator();
17 | });
18 |
19 | describe('node:app', () => {
20 | describe('running on new project', () => {
21 | it('scaffold a full project', () => {
22 | const answers = {
23 | name: 'generator-node',
24 | description: 'A node generator',
25 | homepage: 'http://yeoman.io',
26 | githubAccount: 'yeoman',
27 | authorName: 'The Yeoman Team',
28 | authorEmail: 'hi@yeoman.io',
29 | authorUrl: 'http://yeoman.io',
30 | keywords: ['foo', 'bar'],
31 | includeCoveralls: true,
32 | node: 'v10.4.1,v10'
33 | };
34 | return helpers
35 | .run(require.resolve('../generators/app'))
36 | .withPrompts(answers)
37 | .then(() => {
38 | assert.file([
39 | '.travis.yml',
40 | '.editorconfig',
41 | '.gitignore',
42 | '.gitattributes',
43 | 'README.md',
44 | 'lib/index.js',
45 | 'lib/__tests__/generatorNode.test.js'
46 | ]);
47 |
48 | assert.file('package.json');
49 | assert.jsonFileContent('package.json', {
50 | name: 'generator-node',
51 | version: '0.0.0',
52 | description: answers.description,
53 | homepage: answers.homepage,
54 | repository: 'yeoman/generator-node',
55 | author: {
56 | name: answers.authorName,
57 | email: answers.authorEmail,
58 | url: answers.authorUrl
59 | },
60 | files: ['lib'],
61 | keywords: answers.keywords,
62 | main: 'lib/index.js'
63 | });
64 |
65 | assert.file('README.md');
66 | assert.fileContent(
67 | 'README.md',
68 | "const generatorNode = require('generator-node');"
69 | );
70 | assert.fileContent('README.md', '> A node generator');
71 | assert.fileContent('README.md', '$ npm install --save generator-node');
72 | assert.fileContent('README.md', '© [The Yeoman Team](http://yeoman.io)');
73 | assert.fileContent(
74 | 'README.md',
75 | '[travis-image]: https://travis-ci.com/yeoman/generator-node.svg?branch=master'
76 | );
77 | assert.fileContent('README.md', 'coveralls');
78 |
79 | assert.fileContent('.travis.yml', '| coveralls');
80 | assert.fileContent('.travis.yml', '- v10.4.1');
81 | assert.fileContent('.travis.yml', '- v10');
82 | });
83 | });
84 | });
85 |
86 | describe('running on existing project', () => {
87 | it('Keeps current Readme and extend package.json fields', () => {
88 | const pkg = {
89 | version: '1.0.34',
90 | description: 'lots of fun',
91 | homepage: 'http://yeoman.io',
92 | repository: 'yeoman/generator-node',
93 | author: 'The Yeoman Team',
94 | files: ['lib'],
95 | keywords: ['bar']
96 | };
97 | return helpers
98 | .run(require.resolve('../generators/app'))
99 | .withPrompts({ name: 'generator-node' })
100 | .on('ready', gen => {
101 | gen.fs.writeJSON(gen.destinationPath('package.json'), pkg);
102 | gen.fs.write(gen.destinationPath('README.md'), 'foo');
103 | })
104 | .then(() => {
105 | const newPkg = _.extend({ name: 'generator-node' }, pkg);
106 | assert.jsonFileContent('package.json', newPkg);
107 | assert.fileContent('README.md', 'foo');
108 | });
109 | });
110 | });
111 |
112 | describe('--name', () => {
113 | it('allows scopes in names', () => {
114 | return helpers
115 | .run(require.resolve('../generators/app'))
116 | .withOptions({
117 | name: '@some-scope/generator-node',
118 | githubAccount: 'yeoman'
119 | })
120 | .then(() => {
121 | assert.file('lib/__tests__/someScopeGeneratorNode.test.js');
122 |
123 | assert.file('package.json');
124 | assert.jsonFileContent('package.json', {
125 | name: '@some-scope/generator-node',
126 | repository: 'yeoman/generator-node'
127 | });
128 |
129 | assert.file('README.md');
130 | assert.fileContent(
131 | 'README.md',
132 | "const someScopeGeneratorNode = require('@some-scope/generator-node');"
133 | );
134 | assert.fileContent(
135 | 'README.md',
136 | '$ npm install --save @some-scope/generator-node'
137 | );
138 | assert.fileContent(
139 | 'README.md',
140 | '[travis-image]: https://travis-ci.com/yeoman/generator-node.svg?branch=master'
141 | );
142 | assert.fileContent(
143 | '.git/config',
144 | '[remote "origin"]\n url = git@github.com:yeoman/generator-node.git'
145 | );
146 | });
147 | });
148 |
149 | it('throws when an invalid name is supplied', async () => {
150 | await expect(
151 | helpers.run(require.resolve('../generators/app')).withOptions({
152 | name: '@/invalid-name',
153 | githubAccount: 'yeoman'
154 | })
155 | ).rejects.toMatchInlineSnapshot(
156 | `[Error: name can only contain URL-friendly characters]`
157 | );
158 |
159 | await expect(
160 | helpers.run(require.resolve('../generators/app')).withOptions({
161 | name: 'invalid@name',
162 | githubAccount: 'yeoman'
163 | })
164 | ).rejects.toMatchInlineSnapshot(
165 | `[Error: name can only contain URL-friendly characters]`
166 | );
167 | });
168 | });
169 |
170 | describe('--repository-name', () => {
171 | it('can be set separately from --name', () => {
172 | return helpers
173 | .run(require.resolve('../generators/app'))
174 | .withOptions({
175 | name: 'generator-node',
176 | githubAccount: 'yeoman',
177 | repositoryName: 'not-generator-node'
178 | })
179 | .then(() => {
180 | assert.file('package.json');
181 | assert.jsonFileContent('package.json', {
182 | repository: 'yeoman/not-generator-node'
183 | });
184 |
185 | assert.file('README.md');
186 | assert.fileContent(
187 | 'README.md',
188 | '[travis-image]: https://travis-ci.com/yeoman/not-generator-node.svg?branch=master'
189 | );
190 | assert.fileContent(
191 | '.git/config',
192 | '[remote "origin"]\n url = git@github.com:yeoman/not-generator-node.git'
193 | );
194 | });
195 | });
196 | });
197 |
198 | describe('--no-travis', () => {
199 | it('skip .travis.yml', () => {
200 | return helpers
201 | .run(require.resolve('../generators/app'))
202 | .withOptions({ travis: false })
203 | .then(() => assert.noFile('.travis.yml'));
204 | });
205 | });
206 |
207 | describe('--projectRoot', () => {
208 | it('include the raw files', () => {
209 | return helpers
210 | .run(require.resolve('../generators/app'))
211 | .withOptions({ projectRoot: 'generators' })
212 | .then(() => {
213 | assert.jsonFileContent('package.json', {
214 | files: ['generators'],
215 | main: 'generators/index.js'
216 | });
217 | });
218 | });
219 | });
220 |
221 | describe('--no-editorconfig', () => {
222 | it('include the raw files', () => {
223 | return helpers
224 | .run(require.resolve('../generators/app'))
225 | .withOptions({ editorconfig: false })
226 | .then(() => assert.noFile('.editorconfig'));
227 | });
228 | });
229 | });
230 |
--------------------------------------------------------------------------------
/__tests__/boilerplate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const assert = require('yeoman-assert');
3 | const helpers = require('yeoman-test');
4 |
5 | describe('node:boilerplate', () => {
6 | beforeEach(() => {
7 | return helpers
8 | .run(require.resolve('../generators/boilerplate'))
9 | .withOptions({ name: 'my-module' });
10 | });
11 |
12 | it('creates boilerplate files', () => {
13 | assert.file('lib/index.js');
14 | assert.file('lib/__tests__/myModule.test.js');
15 | assert.fileContent('lib/index.js', 'module.exports = {};');
16 | assert.fileContent('lib/__tests__/myModule.test.js', 'const myModule');
17 | assert.fileContent('lib/__tests__/myModule.test.js', "describe('myModule'");
18 | });
19 | });
20 |
21 | describe('node:boilerplate', () => {
22 | beforeEach(() => {
23 | return helpers
24 | .run(require.resolve('../generators/boilerplate'))
25 | .withOptions({ name: 'my-module', generateInto: 'other/' });
26 | });
27 |
28 | it('creates boilerplate files using another path', () => {
29 | assert.file('other/lib/index.js');
30 | assert.file('other/lib/__tests__/myModule.test.js');
31 | assert.fileContent('other/lib/index.js', 'module.exports = {};');
32 | assert.fileContent('other/lib/__tests__/myModule.test.js', 'const myModule');
33 | assert.fileContent('other/lib/__tests__/myModule.test.js', "describe('myModule'");
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/__tests__/cli.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const assert = require('yeoman-assert');
3 | const helpers = require('yeoman-test');
4 |
5 | describe('node:cli', () => {
6 | beforeEach(() => {
7 | return helpers.run(require.resolve('../generators/cli')).on('ready', generator => {
8 | generator.fs.write(generator.destinationPath('package.json'), '{"name": "my-lib"}');
9 | });
10 | });
11 |
12 | it('creates cli.js', () => {
13 | assert.file('lib/cli.js');
14 | assert.fileContent('lib/cli.js', "const meow = require('meow')");
15 | assert.fileContent('lib/cli.js', "const myLib = require('./')");
16 | });
17 |
18 | it('Extends package.json', () => {
19 | assert.fileContent('package.json', '"bin": "lib/cli.js"');
20 | assert.fileContent('package.json', '"meow"');
21 | assert.fileContent('package.json', /"lec": "\^/);
22 | assert.fileContent('package.json', '"prepare": "lec lib/cli.js -c LF"');
23 | });
24 | });
25 |
26 | describe('node:cli', () => {
27 | beforeEach(() => {
28 | return helpers
29 | .run(require.resolve('../generators/cli'))
30 | .withOptions({ generateInto: 'other/' })
31 | .on('ready', generator => {
32 | generator.fs.write(
33 | generator.destinationPath('other/package.json'),
34 | '{"name": "my-lib"}'
35 | );
36 | });
37 | });
38 |
39 | it('creates cli.js with path option', () => {
40 | assert.file('other/lib/cli.js');
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/__tests__/editorconfig.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const assert = require('yeoman-assert');
3 | const helpers = require('yeoman-test');
4 |
5 | describe('node:editorconfig', () => {
6 | it('creates .editorconfig', () => {
7 | return helpers
8 | .run(require.resolve('../generators/editorconfig'))
9 | .then(() => assert.file('.editorconfig'));
10 | });
11 |
12 | it('respect --generate-into option', () => {
13 | return helpers
14 | .run(require.resolve('../generators/editorconfig'))
15 | .withOptions({ generateInto: 'other/' })
16 | .then(() => assert.file('other/.editorconfig'));
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/__tests__/eslint.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const assert = require('yeoman-assert');
3 | const helpers = require('yeoman-test');
4 |
5 | describe('node:eslint', () => {
6 | it('fill package.json', () => {
7 | return helpers.run(require.resolve('../generators/eslint')).then(() => {
8 | assert.fileContent('package.json', /"eslint-config-xo":/);
9 | assert.jsonFileContent('package.json', {
10 | eslintConfig: {
11 | extends: ['xo', 'prettier'],
12 | env: {
13 | jest: true
14 | }
15 | },
16 | scripts: {
17 | pretest: 'eslint .'
18 | }
19 | });
20 | assert.file('.eslintignore');
21 | });
22 | });
23 |
24 | it('respect --generate-into option as the root of the scaffolding', () => {
25 | return helpers
26 | .run(require.resolve('../generators/eslint'))
27 | .withOptions({ generateInto: 'other/' })
28 | .then(() => {
29 | assert.fileContent('other/package.json', /"eslint-config-xo":/);
30 | assert.jsonFileContent('other/package.json', {
31 | eslintConfig: {
32 | extends: ['xo', 'prettier']
33 | }
34 | });
35 | assert.file('other/.eslintignore');
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/__tests__/git.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const assert = require('yeoman-assert');
3 | const helpers = require('yeoman-test');
4 |
5 | describe('node:git', () => {
6 | it('creates the git config files and init the repository', () => {
7 | return helpers
8 | .run(require.resolve('../generators/git'))
9 | .withOptions({
10 | githubAccount: 'yeoman',
11 | repositoryName: 'generator-node'
12 | })
13 | .then(() => {
14 | assert.file('.gitignore');
15 | assert.file('.gitattributes');
16 | assert.file('.git');
17 |
18 | assert.file('package.json');
19 | assert.jsonFileContent('package.json', {
20 | repository: 'yeoman/generator-node'
21 | });
22 |
23 | assert.fileContent(
24 | '.git/config',
25 | '[remote "origin"]\n url = git@github.com:yeoman/generator-node.git'
26 | );
27 | });
28 | });
29 |
30 | it('respects --generate-into option', () => {
31 | return helpers
32 | .run(require.resolve('../generators/git'))
33 | .withOptions({
34 | githubAccount: 'other-account',
35 | repositoryName: 'other-name',
36 | generateInto: 'other/'
37 | })
38 | .then(() => {
39 | assert.file('other/.gitignore');
40 | assert.file('other/.gitattributes');
41 | assert.file('other/.git');
42 |
43 | assert.file('other/package.json');
44 | assert.jsonFileContent('other/package.json', {
45 | repository: 'other-account/other-name'
46 | });
47 |
48 | assert.fileContent(
49 | 'other/.git/config',
50 | '[remote "origin"]\n url = git@github.com:other-account/other-name.git'
51 | );
52 | });
53 | });
54 |
55 | it("doesn't add remote `origin` when `githubAccount` isn't passed", () => {
56 | return helpers
57 | .run(require.resolve('../generators/git'))
58 | .withOptions({
59 | repositoryName: 'other-name'
60 | })
61 | .then(() => {
62 | assert.file('.gitignore');
63 | assert.file('.gitattributes');
64 | assert.file('.git');
65 | assert.file('package.json');
66 |
67 | assert.noFileContent('package.json', '"repository"');
68 | assert.noFileContent('.git/config', '[remote "origin"]');
69 | });
70 | });
71 |
72 | it("doesn't add remote `origin` when `repositoryName` isn't passed", () => {
73 | return helpers
74 | .run(require.resolve('../generators/git'))
75 | .withOptions({
76 | githubAccount: 'other-account'
77 | })
78 | .then(() => {
79 | assert.file('.gitignore');
80 | assert.file('.gitattributes');
81 | assert.file('.git');
82 | assert.file('package.json');
83 |
84 | assert.noFileContent('package.json', '"repository"');
85 | assert.noFileContent('.git/config', '[remote "origin"]');
86 | });
87 | });
88 | });
89 |
--------------------------------------------------------------------------------
/__tests__/readme.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const assert = require('yeoman-assert');
3 | const helpers = require('yeoman-test');
4 |
5 | describe('node:readme', () => {
6 | beforeEach(() => {
7 | return helpers
8 | .run(require.resolve('../generators/readme'))
9 | .withOptions({
10 | name: 'my-project',
11 | description: 'a cool project',
12 | githubAccount: 'yeoman',
13 | authorName: 'Yeoman',
14 | authorUrl: 'http://yeoman.io',
15 | coveralls: true
16 | })
17 | .on('ready', gen => {
18 | gen.fs.writeJSON(gen.destinationPath('package.json'), {
19 | license: 'MIT'
20 | });
21 | });
22 | });
23 |
24 | it('creates and fill contents in README.md', () => {
25 | assert.file('README.md');
26 | assert.fileContent('README.md', "const myProject = require('my-project');");
27 | assert.fileContent('README.md', '> a cool project');
28 | assert.fileContent('README.md', '$ npm install --save my-project');
29 | assert.fileContent('README.md', 'MIT © [Yeoman](http://yeoman.io)');
30 | assert.fileContent(
31 | 'README.md',
32 | '[travis-image]: https://travis-ci.com/yeoman/my-project.svg?branch=master'
33 | );
34 | assert.fileContent('README.md', 'coveralls');
35 | });
36 | });
37 |
38 | describe('node:readme --content', () => {
39 | beforeEach(() => {
40 | return helpers
41 | .run(require.resolve('../generators/readme'))
42 | .withOptions({
43 | name: 'my-project',
44 | description: 'a cool project',
45 | githubAccount: 'yeoman',
46 | authorName: 'Yeoman',
47 | authorUrl: 'http://yeoman.io',
48 | coveralls: true,
49 | content: 'My custom content'
50 | })
51 | .on('ready', gen => {
52 | gen.fs.writeJSON(gen.destinationPath('package.json'), {
53 | license: 'MIT'
54 | });
55 | });
56 | });
57 |
58 | it('fill custom contents in README.md', () => {
59 | assert.file('README.md');
60 | assert.fileContent('README.md', 'My custom content');
61 | assert.fileContent('README.md', 'MIT © [Yeoman](http://yeoman.io)');
62 | assert.fileContent(
63 | 'README.md',
64 | '[travis-image]: https://travis-ci.com/yeoman/my-project.svg?branch=master'
65 | );
66 | assert.fileContent('README.md', 'coveralls');
67 | });
68 | });
69 |
70 | describe('node:readme --no-coveralls', () => {
71 | beforeEach(() => {
72 | return helpers
73 | .run(require.resolve('../generators/readme'))
74 | .withOptions({
75 | name: 'my-project',
76 | description: 'a cool project',
77 | githubAccount: 'yeoman',
78 | authorName: 'Yeoman',
79 | authorUrl: 'http://yeoman.io',
80 | coveralls: false
81 | })
82 | .on('ready', gen => {
83 | gen.fs.writeJSON(gen.destinationPath('package.json'), {
84 | license: 'MIT'
85 | });
86 | });
87 | });
88 |
89 | it('does not include coveralls badge README.md', () => {
90 | assert.noFileContent('README.md', 'coveralls');
91 | });
92 | });
93 |
94 | describe('node:readme --generate-into', () => {
95 | beforeEach(() => {
96 | return helpers
97 | .run(require.resolve('../generators/readme'))
98 | .withOptions({
99 | name: 'my-project',
100 | description: 'a cool project',
101 | githubAccount: 'yeoman',
102 | authorName: 'Yeoman',
103 | authorUrl: 'http://yeoman.io',
104 | coveralls: true,
105 | generateInto: 'other/'
106 | })
107 | .on('ready', gen => {
108 | gen.fs.writeJSON(gen.destinationPath('other/package.json'), {
109 | license: 'MIT'
110 | });
111 | });
112 | });
113 |
114 | it('creates and fill contents in README.md', () => {
115 | assert.file('other/README.md');
116 | assert.fileContent('other/README.md', "const myProject = require('my-project');");
117 | assert.fileContent('other/README.md', '> a cool project');
118 | assert.fileContent('other/README.md', '$ npm install --save my-project');
119 | assert.fileContent('other/README.md', 'MIT © [Yeoman](http://yeoman.io)');
120 | assert.fileContent(
121 | 'other/README.md',
122 | '[travis-image]: https://travis-ci.com/yeoman/my-project.svg?branch=master'
123 | );
124 | assert.fileContent('other/README.md', 'coveralls');
125 | });
126 | });
127 |
128 | describe('node:readme --content and --generate-into', () => {
129 | beforeEach(() => {
130 | return helpers
131 | .run(require.resolve('../generators/readme'))
132 | .withOptions({
133 | name: 'my-project',
134 | description: 'a cool project',
135 | githubAccount: 'yeoman',
136 | authorName: 'Yeoman',
137 | authorUrl: 'http://yeoman.io',
138 | coveralls: true,
139 | content: 'My custom content',
140 | generateInto: 'other/'
141 | })
142 | .on('ready', gen => {
143 | gen.fs.writeJSON(gen.destinationPath('other/package.json'), {
144 | license: 'MIT'
145 | });
146 | });
147 | });
148 |
149 | it('fill custom contents in README.md', () => {
150 | assert.file('other/README.md');
151 | assert.fileContent('other/README.md', 'My custom content');
152 | assert.fileContent('other/README.md', 'MIT © [Yeoman](http://yeoman.io)');
153 | assert.fileContent(
154 | 'other/README.md',
155 | '[travis-image]: https://travis-ci.com/yeoman/my-project.svg?branch=master'
156 | );
157 | assert.fileContent('other/README.md', 'coveralls');
158 | });
159 | });
160 |
161 | describe('node:readme --no-coveralls and --generate-into', () => {
162 | beforeEach(() => {
163 | return helpers
164 | .run(require.resolve('../generators/readme'))
165 | .withOptions({
166 | name: 'my-project',
167 | description: 'a cool project',
168 | githubAccount: 'yeoman',
169 | authorName: 'Yeoman',
170 | authorUrl: 'http://yeoman.io',
171 | coveralls: false,
172 | generateInto: 'other/'
173 | })
174 | .on('ready', gen => {
175 | gen.fs.writeJSON(gen.destinationPath('other/package.json'), {
176 | license: 'MIT'
177 | });
178 | });
179 | });
180 |
181 | it('does not include coveralls badge README.md', () => {
182 | assert.noFileContent('other/README.md', 'coveralls');
183 | });
184 | });
185 |
186 | describe('node:readme --yarn', () => {
187 | beforeEach(() => {
188 | return helpers
189 | .run(require.resolve('../generators/readme'))
190 | .withOptions({
191 | name: 'my-project',
192 | description: 'a cool project',
193 | githubAccount: 'yeoman',
194 | authorName: 'Yeoman',
195 | authorUrl: 'http://yeoman.io',
196 | yarn: true,
197 | coveralls: false
198 | })
199 | .on('ready', gen => {
200 | gen.fs.writeJSON(gen.destinationPath('package.json'), {
201 | license: 'MIT'
202 | });
203 | });
204 | });
205 |
206 | it('creates and fills contents in README.md', () => {
207 | assert.file('README.md');
208 | assert.fileContent('README.md', '$ yarn add my-project');
209 | });
210 | });
211 |
--------------------------------------------------------------------------------
/generators/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const _ = require('lodash');
3 | const extend = _.merge;
4 | const Generator = require('yeoman-generator');
5 | const parseAuthor = require('parse-author');
6 | const githubUsername = require('github-username');
7 | const path = require('path');
8 | const askName = require('inquirer-npm-name');
9 | const chalk = require('chalk');
10 | const validatePackageName = require('validate-npm-package-name');
11 | const pkgJson = require('../../package.json');
12 |
13 | module.exports = class extends Generator {
14 | constructor(args, options) {
15 | super(args, options);
16 |
17 | this.option('travis', {
18 | type: Boolean,
19 | required: false,
20 | default: true,
21 | desc: 'Include travis config'
22 | });
23 |
24 | this.option('boilerplate', {
25 | type: Boolean,
26 | required: false,
27 | default: true,
28 | desc: 'Include boilerplate files'
29 | });
30 |
31 | this.option('cli', {
32 | type: Boolean,
33 | required: false,
34 | default: false,
35 | desc: 'Add a CLI'
36 | });
37 |
38 | this.option('coveralls', {
39 | type: Boolean,
40 | required: false,
41 | desc: 'Include coveralls config'
42 | });
43 |
44 | this.option('editorconfig', {
45 | type: Boolean,
46 | required: false,
47 | default: true,
48 | desc: 'Include a .editorconfig file'
49 | });
50 |
51 | this.option('license', {
52 | type: Boolean,
53 | required: false,
54 | default: true,
55 | desc: 'Include a license'
56 | });
57 |
58 | this.option('name', {
59 | type: String,
60 | required: false,
61 | desc: 'Project name'
62 | });
63 |
64 | this.option('githubAccount', {
65 | type: String,
66 | required: false,
67 | desc: 'GitHub username or organization'
68 | });
69 |
70 | this.option('repositoryName', {
71 | type: String,
72 | required: false,
73 | desc: 'Name of the GitHub repository'
74 | });
75 |
76 | this.option('projectRoot', {
77 | type: String,
78 | required: false,
79 | default: 'lib',
80 | desc: 'Relative path to the project code root'
81 | });
82 |
83 | this.option('readme', {
84 | type: String,
85 | required: false,
86 | desc: 'Content to insert in the README.md file'
87 | });
88 | }
89 |
90 | initializing() {
91 | this.pkg = this.fs.readJSON(this.destinationPath('package.json'), {});
92 |
93 | // Pre set the default props from the information we have at this point
94 | this.props = {
95 | name: this.pkg.name,
96 | description: this.pkg.description,
97 | version: this.pkg.version,
98 | homepage: this.pkg.homepage,
99 | repositoryName: this.options.repositoryName
100 | };
101 |
102 | if (this.options.name) {
103 | const name = this.options.name;
104 | const packageNameValidity = validatePackageName(name);
105 |
106 | if (packageNameValidity.validForNewPackages) {
107 | this.props.name = name;
108 | } else {
109 | this.emit(
110 | 'error',
111 | new Error(
112 | _.get(packageNameValidity, 'errors.0') ||
113 | 'The name option is not a valid npm package name.'
114 | )
115 | );
116 | }
117 | }
118 |
119 | if (_.isObject(this.pkg.author)) {
120 | this.props.authorName = this.pkg.author.name;
121 | this.props.authorEmail = this.pkg.author.email;
122 | this.props.authorUrl = this.pkg.author.url;
123 | } else if (_.isString(this.pkg.author)) {
124 | const info = parseAuthor(this.pkg.author);
125 | this.props.authorName = info.name;
126 | this.props.authorEmail = info.email;
127 | this.props.authorUrl = info.url;
128 | }
129 | }
130 |
131 | _getModuleNameParts(name) {
132 | const moduleName = {
133 | name,
134 | repositoryName: this.props.repositoryName
135 | };
136 |
137 | if (moduleName.name.startsWith('@')) {
138 | const nameParts = moduleName.name.slice(1).split('/');
139 |
140 | Object.assign(moduleName, {
141 | scopeName: nameParts[0],
142 | localName: nameParts[1]
143 | });
144 | } else {
145 | moduleName.localName = moduleName.name;
146 | }
147 |
148 | if (!moduleName.repositoryName) {
149 | moduleName.repositoryName = moduleName.localName;
150 | }
151 |
152 | return moduleName;
153 | }
154 |
155 | _askForModuleName() {
156 | let askedName;
157 |
158 | if (this.props.name) {
159 | askedName = Promise.resolve({
160 | name: this.props.name
161 | });
162 | } else {
163 | askedName = askName(
164 | {
165 | name: 'name',
166 | default: path.basename(process.cwd())
167 | },
168 | this
169 | );
170 | }
171 |
172 | return askedName.then(answer => {
173 | const moduleNameParts = this._getModuleNameParts(answer.name);
174 |
175 | Object.assign(this.props, moduleNameParts);
176 | });
177 | }
178 |
179 | _askFor() {
180 | const prompts = [
181 | {
182 | name: 'description',
183 | message: 'Description',
184 | when: !this.props.description
185 | },
186 | {
187 | name: 'homepage',
188 | message: 'Project homepage url',
189 | when: !this.props.homepage
190 | },
191 | {
192 | name: 'authorName',
193 | message: "Author's Name",
194 | when: !this.props.authorName,
195 | default: this.user.git.name(),
196 | store: true
197 | },
198 | {
199 | name: 'authorEmail',
200 | message: "Author's Email",
201 | when: !this.props.authorEmail,
202 | default: this.user.git.email(),
203 | store: true
204 | },
205 | {
206 | name: 'authorUrl',
207 | message: "Author's Homepage",
208 | when: !this.props.authorUrl,
209 | store: true
210 | },
211 | {
212 | name: 'keywords',
213 | message: 'Package keywords (comma to split)',
214 | when: !this.pkg.keywords,
215 | filter(words) {
216 | return words.split(/\s*,\s*/g);
217 | }
218 | },
219 | {
220 | name: 'includeCoveralls',
221 | type: 'confirm',
222 | message: 'Send coverage reports to coveralls',
223 | when: this.options.coveralls === undefined
224 | }
225 | ];
226 |
227 | return this.prompt(prompts).then(props => {
228 | this.props = extend(this.props, props);
229 | });
230 | }
231 |
232 | _askForTravis() {
233 | const prompts = [
234 | {
235 | name: 'node',
236 | message: 'Enter Node versions (comma separated)',
237 | when: this.options.travis
238 | }
239 | ];
240 |
241 | return this.prompt(prompts).then(props => {
242 | this.props = extend(this.props, props);
243 | });
244 | }
245 |
246 | _askForGithubAccount() {
247 | if (this.options.githubAccount) {
248 | this.props.githubAccount = this.options.githubAccount;
249 | return Promise.resolve();
250 | }
251 |
252 | let usernamePromise;
253 | if (this.props.scopeName) {
254 | usernamePromise = Promise.resolve(this.props.scopeName);
255 | } else {
256 | usernamePromise = githubUsername(this.props.authorEmail).then(
257 | username => username,
258 | () => ''
259 | );
260 | }
261 |
262 | return usernamePromise.then(username => {
263 | return this.prompt({
264 | name: 'githubAccount',
265 | message: 'GitHub username or organization',
266 | default: username
267 | }).then(prompt => {
268 | this.props.githubAccount = prompt.githubAccount;
269 | });
270 | });
271 | }
272 |
273 | prompting() {
274 | return this._askForModuleName()
275 | .then(this._askFor.bind(this))
276 | .then(this._askForTravis.bind(this))
277 | .then(this._askForGithubAccount.bind(this));
278 | }
279 |
280 | writing() {
281 | // Re-read the content at this point because a composed generator might modify it.
282 | const currentPkg = this.fs.readJSON(this.destinationPath('package.json'), {});
283 |
284 | const pkg = extend(
285 | {
286 | name: this.props.name,
287 | version: '0.0.0',
288 | description: this.props.description,
289 | homepage: this.props.homepage,
290 | author: {
291 | name: this.props.authorName,
292 | email: this.props.authorEmail,
293 | url: this.props.authorUrl
294 | },
295 | files: [this.options.projectRoot],
296 | main: path.join(this.options.projectRoot, 'index.js').replace(/\\/g, '/'),
297 | keywords: [],
298 | devDependencies: {},
299 | engines: {
300 | npm: '>= 4.0.0'
301 | }
302 | },
303 | currentPkg
304 | );
305 |
306 | if (this.props.includeCoveralls) {
307 | pkg.devDependencies.coveralls = pkgJson.devDependencies.coveralls;
308 | }
309 |
310 | // Combine the keywords
311 | if (this.props.keywords && this.props.keywords.length) {
312 | pkg.keywords = _.uniq(this.props.keywords.concat(pkg.keywords));
313 | }
314 |
315 | // Let's extend package.json so we're not overwriting user previous fields
316 | this.fs.writeJSON(this.destinationPath('package.json'), pkg);
317 | }
318 |
319 | default() {
320 | if (this.options.travis) {
321 | let options = { config: {} };
322 |
323 | if (this.props.node) {
324 | // eslint-disable-next-line camelcase
325 | options.config.node_js = this.props.node.split(',');
326 | }
327 |
328 | if (this.props.includeCoveralls) {
329 | options.config.after_script = 'cat ./coverage/lcov.info | coveralls'; // eslint-disable-line camelcase
330 | }
331 |
332 | this.composeWith(require.resolve('generator-travis/generators/app'), options);
333 | }
334 |
335 | if (this.options.editorconfig) {
336 | this.composeWith(require.resolve('../editorconfig'));
337 | }
338 |
339 | this.composeWith(require.resolve('../eslint'));
340 |
341 | this.composeWith(require.resolve('../git'), {
342 | name: this.props.name,
343 | githubAccount: this.props.githubAccount,
344 | repositoryName: this.props.repositoryName
345 | });
346 |
347 | this.composeWith(require.resolve('generator-jest/generators/app'), {
348 | testEnvironment: 'node',
349 | coveralls: false
350 | });
351 |
352 | if (this.options.boilerplate) {
353 | this.composeWith(require.resolve('../boilerplate'), {
354 | name: this.props.name
355 | });
356 | }
357 |
358 | if (this.options.cli) {
359 | this.composeWith(require.resolve('../cli'));
360 | }
361 |
362 | if (this.options.license && !this.pkg.license) {
363 | this.composeWith(require.resolve('generator-license/app'), {
364 | name: this.props.authorName,
365 | email: this.props.authorEmail,
366 | website: this.props.authorUrl
367 | });
368 | }
369 |
370 | if (!this.fs.exists(this.destinationPath('README.md'))) {
371 | this.composeWith(require.resolve('../readme'), {
372 | name: this.props.name,
373 | description: this.props.description,
374 | githubAccount: this.props.githubAccount,
375 | repositoryName: this.props.repositoryName,
376 | authorName: this.props.authorName,
377 | authorUrl: this.props.authorUrl,
378 | coveralls: this.props.includeCoveralls,
379 | content: this.options.readme
380 | });
381 | }
382 | }
383 |
384 | installing() {
385 | this.npmInstall();
386 | }
387 |
388 | end() {
389 | this.log('Thanks for using Yeoman.');
390 |
391 | if (this.options.travis) {
392 | let travisUrl = chalk.cyan(
393 | `https://travis-ci.com/profile/${this.props.githubAccount || ''}`
394 | );
395 | this.log(`- Enable Travis integration at ${travisUrl}`);
396 | }
397 |
398 | if (this.props.includeCoveralls) {
399 | let coverallsUrl = chalk.cyan('https://coveralls.io/repos/new');
400 | this.log(`- Enable Coveralls integration at ${coverallsUrl}`);
401 | }
402 | }
403 | };
404 |
--------------------------------------------------------------------------------
/generators/boilerplate/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const _ = require('lodash');
3 | const Generator = require('yeoman-generator');
4 |
5 | module.exports = class extends Generator {
6 | constructor(args, options) {
7 | super(args, options);
8 |
9 | this.option('generateInto', {
10 | type: String,
11 | required: false,
12 | default: '',
13 | desc: 'Relocate the location of the generated files.'
14 | });
15 |
16 | this.option('name', {
17 | type: String,
18 | required: true,
19 | desc: 'The new module name.'
20 | });
21 | }
22 |
23 | writing() {
24 | const filepath = this.destinationPath(this.options.generateInto, 'lib/index.js');
25 |
26 | this.fs.copyTpl(this.templatePath('index.js'), filepath);
27 |
28 | this.composeWith(require.resolve('generator-jest/generators/test'), {
29 | arguments: [filepath],
30 | componentName: _.camelCase(this.options.name)
31 | });
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/generators/boilerplate/templates/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {};
4 |
--------------------------------------------------------------------------------
/generators/cli/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const _ = require('lodash');
3 | const extend = _.merge;
4 | const Generator = require('yeoman-generator');
5 |
6 | module.exports = class extends Generator {
7 | constructor(args, options) {
8 | super(args, options);
9 |
10 | this.option('generateInto', {
11 | type: String,
12 | required: false,
13 | defaults: '',
14 | desc: 'Relocate the location of the generated files.'
15 | });
16 | }
17 |
18 | writing() {
19 | const pkg = this.fs.readJSON(
20 | this.destinationPath(this.options.generateInto, 'package.json'),
21 | {}
22 | );
23 |
24 | extend(pkg, {
25 | bin: 'lib/cli.js',
26 | dependencies: {
27 | meow: '^3.7.0'
28 | },
29 | devDependencies: {
30 | lec: '^1.0.1'
31 | },
32 | scripts: {
33 | prepare: 'lec lib/cli.js -c LF'
34 | }
35 | });
36 |
37 | this.fs.writeJSON(
38 | this.destinationPath(this.options.generateInto, 'package.json'),
39 | pkg
40 | );
41 |
42 | this.fs.copyTpl(
43 | this.templatePath('cli.js'),
44 | this.destinationPath(this.options.generateInto, 'lib/cli.js'),
45 | {
46 | pkgName: pkg.name,
47 | pkgSafeName: _.camelCase(pkg.name)
48 | }
49 | );
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/generators/cli/templates/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict';
3 | const meow = require('meow');
4 | const <%= pkgSafeName %> = require('./');
5 |
6 | const cli = meow(`
7 | Usage
8 | $ <%= pkgName %> [input]
9 |
10 | Options
11 | --foo Lorem ipsum. [Default: false]
12 |
13 | Examples
14 | $ <%= pkgName %>
15 | unicorns
16 | $ <%= pkgName %> rainbows
17 | unicorns & rainbows
18 | `);
19 |
--------------------------------------------------------------------------------
/generators/editorconfig/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Generator = require('yeoman-generator');
3 |
4 | module.exports = class extends Generator {
5 | constructor(args, options) {
6 | super(args, options);
7 |
8 | this.option('generateInto', {
9 | type: String,
10 | required: false,
11 | defaults: '',
12 | desc: 'Relocate the location of the generated files.'
13 | });
14 | }
15 |
16 | initializing() {
17 | this.fs.copy(
18 | this.templatePath('editorconfig'),
19 | this.destinationPath(this.options.generateInto, '.editorconfig')
20 | );
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/generators/editorconfig/templates/editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
--------------------------------------------------------------------------------
/generators/eslint/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Generator = require('yeoman-generator');
3 | const rootPkg = require('../../package.json');
4 |
5 | module.exports = class extends Generator {
6 | constructor(args, options) {
7 | super(args, options);
8 |
9 | this.option('generateInto', {
10 | type: String,
11 | required: false,
12 | default: '',
13 | desc: 'Relocate the location of the generated files.'
14 | });
15 | }
16 |
17 | writing() {
18 | const pkgJson = {
19 | devDependencies: {
20 | eslint: rootPkg.devDependencies.eslint,
21 | prettier: rootPkg.devDependencies.prettier,
22 | husky: rootPkg.devDependencies.husky,
23 | 'lint-staged': rootPkg.devDependencies['lint-staged'],
24 | 'eslint-config-prettier': rootPkg.devDependencies['eslint-config-prettier'],
25 | 'eslint-plugin-prettier': rootPkg.devDependencies['eslint-plugin-prettier'],
26 | 'eslint-config-xo': rootPkg.devDependencies['eslint-config-xo']
27 | },
28 | 'lint-staged': rootPkg['lint-staged'],
29 | husky: rootPkg.husky,
30 | eslintConfig: rootPkg.eslintConfig,
31 | scripts: {
32 | pretest: rootPkg.scripts.pretest,
33 | precommit: rootPkg.scripts.precommit
34 | }
35 | };
36 |
37 | this.fs.extendJSON(
38 | this.destinationPath(this.options.generateInto, 'package.json'),
39 | pkgJson
40 | );
41 |
42 | this.fs.copy(
43 | this.templatePath('eslintignore'),
44 | this.destinationPath(this.options.generateInto, '.eslintignore')
45 | );
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/generators/eslint/templates/eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 |
--------------------------------------------------------------------------------
/generators/git/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Generator = require('yeoman-generator');
3 | const originUrl = require('git-remote-origin-url');
4 |
5 | module.exports = class extends Generator {
6 | constructor(args, opts) {
7 | super(args, opts);
8 | this.option('generateInto', {
9 | type: String,
10 | required: false,
11 | defaults: '',
12 | desc: 'Relocate the location of the generated files.'
13 | });
14 |
15 | this.option('githubAccount', {
16 | type: String,
17 | required: true,
18 | desc: 'GitHub username or organization'
19 | });
20 |
21 | this.option('repositoryName', {
22 | type: String,
23 | required: true,
24 | desc: 'Name of the GitHub repository'
25 | });
26 | }
27 |
28 | initializing() {
29 | this.fs.copy(
30 | this.templatePath('gitattributes'),
31 | this.destinationPath(this.options.generateInto, '.gitattributes')
32 | );
33 |
34 | this.fs.copy(
35 | this.templatePath('gitignore'),
36 | this.destinationPath(this.options.generateInto, '.gitignore')
37 | );
38 |
39 | return originUrl(this.destinationPath(this.options.generateInto)).then(
40 | function(url) {
41 | this.originUrl = url;
42 | }.bind(this),
43 | function() {
44 | this.originUrl = '';
45 | }.bind(this)
46 | );
47 | }
48 |
49 | _readPkg() {
50 | return this.fs.readJSON(
51 | this.destinationPath(this.options.generateInto, 'package.json'),
52 | {}
53 | );
54 | }
55 |
56 | writing() {
57 | const pkg = this._readPkg();
58 |
59 | let repository;
60 | if (this.originUrl) {
61 | repository = this.originUrl;
62 | } else if (this.options.githubAccount && this.options.repositoryName) {
63 | repository = this.options.githubAccount + '/' + this.options.repositoryName;
64 | }
65 |
66 | pkg.repository = pkg.repository || repository;
67 |
68 | this.fs.writeJSON(
69 | this.destinationPath(this.options.generateInto, 'package.json'),
70 | pkg
71 | );
72 | }
73 |
74 | end() {
75 | const pkg = this._readPkg();
76 |
77 | this.spawnCommandSync('git', ['init', '--quiet'], {
78 | cwd: this.destinationPath(this.options.generateInto)
79 | });
80 |
81 | if (pkg.repository && !this.originUrl) {
82 | let repoSSH = pkg.repository;
83 | if (pkg.repository && pkg.repository.indexOf('.git') === -1) {
84 | repoSSH = 'git@github.com:' + pkg.repository + '.git';
85 | }
86 |
87 | this.spawnCommandSync('git', ['remote', 'add', 'origin', repoSSH], {
88 | cwd: this.destinationPath(this.options.generateInto)
89 | });
90 | }
91 | }
92 | };
93 |
--------------------------------------------------------------------------------
/generators/git/templates/gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/generators/git/templates/gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 |
--------------------------------------------------------------------------------
/generators/readme/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const _ = require('lodash');
3 | const Generator = require('yeoman-generator');
4 | const querystring = require('querystring');
5 |
6 | module.exports = class extends Generator {
7 | constructor(args, options) {
8 | super(args, options);
9 |
10 | this.option('generateInto', {
11 | type: String,
12 | required: false,
13 | defaults: '',
14 | desc: 'Relocate the location of the generated files.'
15 | });
16 |
17 | this.option('name', {
18 | type: String,
19 | required: true,
20 | desc: 'Project name'
21 | });
22 |
23 | this.option('description', {
24 | type: String,
25 | required: true,
26 | desc: 'Project description'
27 | });
28 |
29 | this.option('githubAccount', {
30 | type: String,
31 | required: true,
32 | desc: 'GitHub username or organization'
33 | });
34 |
35 | this.option('repositoryName', {
36 | type: String,
37 | required: true,
38 | desc: 'Name of the GitHub repository'
39 | });
40 |
41 | this.option('authorName', {
42 | type: String,
43 | required: true,
44 | desc: 'Author name'
45 | });
46 |
47 | this.option('authorUrl', {
48 | type: String,
49 | required: true,
50 | desc: 'Author url'
51 | });
52 |
53 | this.option('coveralls', {
54 | type: Boolean,
55 | required: true,
56 | desc: 'Include coveralls badge'
57 | });
58 |
59 | this.option('content', {
60 | type: String,
61 | required: false,
62 | desc: 'Readme content'
63 | });
64 | }
65 |
66 | writing() {
67 | const pkg = this.fs.readJSON(
68 | this.destinationPath(this.options.generateInto, 'package.json'),
69 | {}
70 | );
71 | this.fs.copyTpl(
72 | this.templatePath('README.md'),
73 | this.destinationPath(this.options.generateInto, 'README.md'),
74 | {
75 | projectName: this.options.name,
76 | safeProjectName: _.camelCase(this.options.name),
77 | escapedProjectName: querystring.escape(this.options.name),
78 | repositoryName: this.options.repositoryName || this.options.name,
79 | description: this.options.description,
80 | githubAccount: this.options.githubAccount,
81 | author: {
82 | name: this.options.authorName,
83 | url: this.options.authorUrl
84 | },
85 | license: pkg.license,
86 | includeCoveralls: this.options.coveralls,
87 | yarn: this.options.yarn,
88 | content: this.options.content
89 | }
90 | );
91 | }
92 | };
93 |
--------------------------------------------------------------------------------
/generators/readme/templates/README.md:
--------------------------------------------------------------------------------
1 | # <%= projectName %> [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url]<%
2 | if (includeCoveralls) { %> [![Coverage percentage][coveralls-image]][coveralls-url]<% } -%>
3 |
4 | > <%= description %>
5 |
6 | <% if (!content) { -%>
7 | ## Installation
8 |
9 | ```sh
10 | <% if (yarn) { -%>
11 | $ yarn add <%= projectName %>
12 | <% } else { -%>
13 | $ npm install --save <%= projectName %>
14 | <% } -%>
15 | ```
16 |
17 | ## Usage
18 |
19 | ```js
20 | const <%= safeProjectName %> = require('<%= projectName %>');
21 |
22 | <%= safeProjectName %>('Rainbow');
23 | ```
24 | <% } else { -%>
25 | <%= content %>
26 | <% } -%>
27 | ## License
28 |
29 | <%= license %> © [<%= author.name %>](<%= author.url %>)
30 |
31 |
32 | [npm-image]: https://badge.fury.io/js/<%= escapedProjectName %>.svg
33 | [npm-url]: https://npmjs.org/package/<%= projectName %>
34 | [travis-image]: https://travis-ci.com/<%= githubAccount %>/<%= repositoryName %>.svg?branch=master
35 | [travis-url]: https://travis-ci.com/<%= githubAccount %>/<%= repositoryName %>
36 | [daviddm-image]: https://david-dm.org/<%= githubAccount %>/<%= repositoryName %>.svg?theme=shields.io
37 | [daviddm-url]: https://david-dm.org/<%= githubAccount %>/<%= repositoryName %>
38 | <% if (includeCoveralls) { -%>
39 | [coveralls-image]: https://coveralls.io/repos/<%= githubAccount %>/<%= repositoryName %>/badge.svg
40 | [coveralls-url]: https://coveralls.io/r/<%= githubAccount %>/<%= repositoryName %>
41 | <% } -%>
42 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | app: require.resolve('./generators/app'),
5 | boilerplate: require.resolve('./generators/boilerplate'),
6 | cli: require.resolve('./generators/cli'),
7 | editorconfig: require.resolve('./generators/editorconfig'),
8 | eslint: require.resolve('./generators/eslint'),
9 | git: require.resolve('./generators/git'),
10 | readme: require.resolve('./generators/readme')
11 | };
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-node",
3 | "version": "2.8.0",
4 | "description": "Create a Node.js module",
5 | "homepage": "https://github.com/yeoman/generator-node",
6 | "author": "Yeoman team",
7 | "files": [
8 | "index.js",
9 | "generators"
10 | ],
11 | "main": "index.js",
12 | "keywords": [
13 | "yeoman-generator",
14 | "scaffold",
15 | "node",
16 | "module",
17 | "cli"
18 | ],
19 | "devDependencies": {
20 | "coveralls": "^3.0.7",
21 | "eslint": "^6.6.0",
22 | "eslint-config-prettier": "^6.6.0",
23 | "eslint-config-xo": "^0.27.2",
24 | "eslint-plugin-prettier": "^3.1.1",
25 | "husky": "^3.0.9",
26 | "jest": "^24.9.0",
27 | "lint-staged": "^9.4.3",
28 | "prettier": "^1.19.1",
29 | "yeoman-assert": "^3.1.1",
30 | "yeoman-test": "^2.0.0"
31 | },
32 | "repository": "yeoman/generator-node",
33 | "scripts": {
34 | "pretest": "eslint .",
35 | "test": "jest"
36 | },
37 | "dependencies": {
38 | "chalk": "^3.0.0",
39 | "generator-jest": "^1.7.0",
40 | "generator-license": "^5.2.0",
41 | "generator-travis": "^1.9.0",
42 | "git-remote-origin-url": "^3.0.0",
43 | "github-username": "^5.0.1",
44 | "inquirer-npm-name": "^3.0.0",
45 | "lodash": "^4.17.15",
46 | "parse-author": "^2.0.0",
47 | "validate-npm-package-name": "^3.0.0",
48 | "yeoman-generator": "^4.2.0"
49 | },
50 | "eslintConfig": {
51 | "extends": [
52 | "xo",
53 | "prettier"
54 | ],
55 | "env": {
56 | "jest": true,
57 | "node": true
58 | },
59 | "rules": {
60 | "prettier/prettier": "error"
61 | },
62 | "plugins": [
63 | "prettier"
64 | ]
65 | },
66 | "lint-staged": {
67 | "*.js": [
68 | "eslint --fix",
69 | "git add"
70 | ],
71 | "*.json": [
72 | "prettier --write",
73 | "git add"
74 | ]
75 | },
76 | "license": "MIT",
77 | "jest": {
78 | "testEnvironment": "node",
79 | "collectCoverage": true,
80 | "coverageReporters": [
81 | "lcov",
82 | "text",
83 | "text-summary"
84 | ]
85 | },
86 | "engines": {
87 | "npm": ">= 6.0.0"
88 | },
89 | "husky": {
90 | "hooks": {
91 | "pre-commit": "lint-staged"
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------