create-juisy 2.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/index.js +162 -0
  2. package/package.json +63 -0
  3. package/template/.env.example +4 -0
  4. package/template/CHANGELOG.md +0 -0
  5. package/template/bin/cli/cli.js +27 -0
  6. package/template/bin/cli/cmds/index.js +13 -0
  7. package/template/bin/cli/cmds/private/changelog.js +44 -0
  8. package/template/bin/cli/cmds/private/docs/generate-api.js +22 -0
  9. package/template/bin/cli/cmds/private/docs/generate-cli.js +11 -0
  10. package/template/bin/cli/cmds/private/docs/generate-readme.js +11 -0
  11. package/template/bin/cli/cmds/private/docs/index.js +20 -0
  12. package/template/bin/cli/cmds/private/docs/lint.js +42 -0
  13. package/template/bin/cli/cmds/private/git-hooks/index.js +20 -0
  14. package/template/bin/cli/cmds/private/git-hooks/reset.js +48 -0
  15. package/template/bin/cli/cmds/private/git-hooks/sync.js +19 -0
  16. package/template/bin/cli/cmds/private/release.js +223 -0
  17. package/template/bin/cli/cmds/private/test.js +33 -0
  18. package/template/bin/cli/index.js +7 -0
  19. package/template/bin/cli/lib/docs/generate-api-doc.js +33 -0
  20. package/template/bin/cli/lib/release/generate-release-note.js +3 -0
  21. package/template/bin/cli/lib/version/update-version.js +51 -0
  22. package/template/bin/scripts/commit-msg.js +42 -0
  23. package/template/bin/scripts/pre-commit.js +32 -0
  24. package/template/dev.config.js +34 -0
  25. package/template/dev.config.json +27 -0
  26. package/template/dev.config.ts +28 -0
  27. package/template/docs/api/docs.config.js +10 -0
  28. package/template/docs/readme/config.js +22 -0
  29. package/template/docs/readme/readme.js +70 -0
  30. package/template/docs/readme/template.md +53 -0
  31. package/template/package.json +25 -0
  32. package/template/project.globals.js +33 -0
package/index.js ADDED
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Create a new Juisy project
5
+ * @author Hervé Perchec
6
+ * @see https://github.com/hperchec/juisy
7
+ */
8
+
9
+ import fs from 'node:fs'
10
+ import path, { dirname } from 'node:path'
11
+ import { fileURLToPath } from 'node:url'
12
+ import mri from 'mri'
13
+ import { eject } from 'juisy'
14
+ import { InterfaceUtils, OutputUtils } from 'juisy/cli'
15
+
16
+ const __filename = fileURLToPath(import.meta.url)
17
+ const __dirname = dirname(__filename)
18
+
19
+ const {
20
+ run,
21
+ prompts
22
+ } = InterfaceUtils
23
+
24
+ const {
25
+ $style,
26
+ log,
27
+ step,
28
+ substep
29
+ } = OutputUtils
30
+
31
+ // Use `mri` library to parse command line arguments
32
+ // Usage: npm create juisy <project-name> -- [--template|-t <template>] [--force|-f]
33
+ const argv = mri(process.argv.slice(2), {
34
+ alias: { t: 'template', f: 'force' },
35
+ boolean: [ 'force' ],
36
+ string: [ 'template' ]
37
+ })
38
+
39
+ const cwd = process.cwd()
40
+
41
+ // Get juisy version from this package.json file
42
+ const { version: JUISY_VERSION } = JSON.parse(
43
+ fs.readFileSync(path.resolve(__dirname, './package.json'), 'utf-8')
44
+ )
45
+
46
+ const defaultTargetDir = 'juisy-project'
47
+
48
+ const rmTrailingSlashes = str => str.trim().replace(/\/+$/g, '') // remove trailing slashes
49
+
50
+ async function main () {
51
+ // Get target dir argument (optional)
52
+ let targetDir = argv._[0]
53
+ ? rmTrailingSlashes(String(argv._[0])) // remove trailing slashes
54
+ : undefined
55
+
56
+ let devConfigFormat = '.ts' // default is typescript config
57
+
58
+ // Project name (& directory name)
59
+ if (!targetDir) {
60
+ const promptsResult = await prompts([
61
+ {
62
+ type: 'text',
63
+ message: 'Project name (directory):',
64
+ initial: defaultTargetDir,
65
+ name: 'projectName'
66
+ }
67
+ ])
68
+ targetDir = rmTrailingSlashes(promptsResult.projectName)
69
+ }
70
+
71
+ // Dev config format
72
+ const promptsResult = await prompts([
73
+ {
74
+ type: 'select',
75
+ message: 'Dev config format:',
76
+ choices: [
77
+ { title: 'TypeScript', description: 'dev.config.ts', value: '.ts' },
78
+ { title: 'JavaScript', description: 'dev.config.js', value: '.js' },
79
+ { title: 'JSON', description: 'dev.config.json', value: '.json' }
80
+ ],
81
+ initial: 0,
82
+ name: 'devConfigFormat'
83
+ }
84
+ ])
85
+ devConfigFormat = promptsResult.devConfigFormat
86
+
87
+ /**
88
+ * Eject files from template
89
+ */
90
+ step('Copying files')
91
+
92
+ await eject(path.resolve(__dirname, './template'), '**/*', {
93
+ force: argv.force,
94
+ targetDir: path.resolve(cwd, targetDir),
95
+ ignoreFiles: [
96
+ 'dev.config.ts',
97
+ 'dev.config.js',
98
+ 'dev.config.json'
99
+ ].filter(identifier => !identifier.endsWith(devConfigFormat)), // don't ignore dev config file of chosen format
100
+ logLevel: 'warn',
101
+ processor: (content, identifier) => {
102
+ if (identifier === 'package.json') {
103
+ return content.replace(/__JUISY_VERSION__/g, JUISY_VERSION)
104
+ } else {
105
+ return content
106
+ }
107
+ }
108
+ })
109
+
110
+ substep($style.green(`✔ Successfuly copied`), { last: true })
111
+ log() // Blank line
112
+
113
+ const targetCliCommand = 'node ./bin/cli'
114
+
115
+ /**
116
+ * Update package.json file
117
+ */
118
+ const scripts = {
119
+ 'docs:api': `${targetCliCommand} docs generate -c ./docs/docs.config.js`,
120
+ 'docs:readme': `${targetCliCommand} docs generate:readme`,
121
+ 'docs:lint': `${targetCliCommand} docs lint`,
122
+ 'docs:lint:fix': 'npm run docs:lint -- --fix',
123
+ 'docs': 'npm run docs:readme && npm run docs:api && npm run docs:lint',
124
+ 'lint': `${targetCliCommand} lint`,
125
+ 'lint:fix': 'npm run lint -- --fix',
126
+ 'lint:markdown': 'npm run docs:lint',
127
+ 'lint:markdown:fix': 'npm run docs:lint:fix',
128
+ 'release': `${targetCliCommand} release`,
129
+ 'changelog': `${targetCliCommand} changelog`,
130
+ 'git-hooks:reset': `${targetCliCommand} git-hooks reset`,
131
+ 'git-hooks:sync': `${targetCliCommand} git-hooks sync`,
132
+ 'test': `${targetCliCommand} test`
133
+ }
134
+
135
+ step('Adding scripts to package.json')
136
+ const pkg = JSON.parse(
137
+ fs.readFileSync(path.join(path.resolve(cwd, targetDir), 'package.json'), 'utf-8')
138
+ )
139
+ pkg.scripts = pkg.scripts || {}
140
+
141
+ for (const scriptName of Object.keys(scripts)) {
142
+ // Is script already defined?
143
+ const setScript = pkg.scripts[scriptName]
144
+ ? argv.force
145
+ : true
146
+ const isLastStep = scriptName === Object.keys(scripts)[Object.keys(scripts).length - 1]
147
+ if (setScript) {
148
+ await run('npm', [ 'set-script', scriptName, scripts[scriptName] ], { stdio: 'pipe', cwd: path.resolve(cwd, targetDir) })
149
+ substep($style.green(`✔ Script "${scriptName}" successfuly added`), { last: isLastStep })
150
+ } else {
151
+ substep($style.yellow(`Script "${scriptName}" already set. Use --force option to overwrite`), { last: isLastStep })
152
+ }
153
+ }
154
+
155
+ log() // Blank line
156
+ log('You should now move into the created folder and run the following command:\n - npm install')
157
+ log() // Blank line
158
+ log('You can use feature of .env file by copying .env.example:\n - cp .env.example .env')
159
+ log() // Blank line
160
+ }
161
+
162
+ main()
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "create-juisy",
3
+ "version": "2.0.0-beta.9",
4
+ "description": "Juisy boilerplate for npm init",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-juisy": "index.js"
8
+ },
9
+ "files": [
10
+ "template",
11
+ "index.js"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "scripts": {
17
+ "release": "release-it",
18
+ "test": "echo No test found..."
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://gitlab.com/hperchec/juisy.git"
23
+ },
24
+ "keywords": [
25
+ "js",
26
+ "build",
27
+ "release",
28
+ "changelog",
29
+ "bin",
30
+ "cmd",
31
+ "easy"
32
+ ],
33
+ "author": {
34
+ "name": "Hervé Perchec",
35
+ "email": "contact@herve-perchec.com",
36
+ "url": "https://gitlab.com/herveperchec"
37
+ },
38
+ "license": "GPL-3.0-only",
39
+ "bugs": {
40
+ "url": "https://gitlab.com/hperchec/juisy/issues"
41
+ },
42
+ "homepage": "https://gitlab.com/hperchec/juisy#readme",
43
+ "devDependencies": {
44
+ "juisy": "2.0.0-beta.9"
45
+ },
46
+ "dependencies": {
47
+ "execa": "^8",
48
+ "mri": "^1.2.0"
49
+ },
50
+ "release-it": {
51
+ "git": false,
52
+ "plugins": {
53
+ "@release-it/bumper": {
54
+ "out": {
55
+ "file": "package.json",
56
+ "path": [
57
+ "devDependencies.juisy"
58
+ ]
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,4 @@
1
+ NODE_ENV=development
2
+
3
+ # CLI specific environement variables
4
+ __JUISY_CLI_ENV__=private
File without changes
@@ -0,0 +1,27 @@
1
+ const { createCli, utils } = require('@hperchec/juisy')
2
+ const path = require('path')
3
+ const {
4
+ rootDir,
5
+ $style
6
+ } = utils
7
+ // Get package.json content
8
+ const packageJson = require(path.resolve(rootDir, './package.json'))
9
+
10
+ function getBanner () {
11
+ let str = ''
12
+ const title = $style.bold('CLI: ') + packageJson.name
13
+ const length = title.length - 9 // removes characters generated by $style.bold from length
14
+ str += $style.cyan('-'.repeat(length) + '\n')
15
+ str += $style.cyan(title + '\n')
16
+ str += $style.cyan('-'.repeat(length) + '\n')
17
+ str += $style.italic('Made with') + ' ' + $style.red('❤') + ' ' + $style.italic('by ') + $style.bold('Hervé Perchec <contact@herve-perchec.com') + '\n'
18
+ return str
19
+ }
20
+
21
+ // CLI
22
+ module.exports = createCli(cli => cli
23
+ .scriptName('./bin/cli')
24
+ .usage(`${getBanner()}\nUsage: $0 <command> [<options>]`)
25
+ .commandDir('cmds')
26
+ .demandCommand(1, ('Command is missing. See help to learn more.').red)
27
+ )
@@ -0,0 +1,13 @@
1
+ // Private commands
2
+ import docs from './private/docs/index.js'
3
+ import release from './private/release.js'
4
+ import test from './private/test.js'
5
+
6
+ export const commands = [
7
+ // Private commands
8
+ docs,
9
+ release,
10
+ test
11
+ // Public commands
12
+ // ...
13
+ ]
@@ -0,0 +1,44 @@
1
+ /** @type {import('juisy/cli').Command} */
2
+ export default new CLI.Command({
3
+ command: 'changelog',
4
+ describe: 'Generate CHANGELOG file',
5
+ meta: {
6
+ private: true
7
+ },
8
+ builder: function (cli) {
9
+ return cli.option('i', {
10
+ alias: 'infile',
11
+ type: 'string',
12
+ describe: 'Same as conventional-changelog option',
13
+ default: 'CHANGELOG.md',
14
+ requiresArg: true
15
+ })
16
+ },
17
+ async handler (argv) {
18
+ // Utils
19
+ const { $style, step, substep, error } = CLI.OutputUtils
20
+ const { run, wait } = CLI.InterfaceUtils
21
+ /**
22
+ * Generate changelog file
23
+ */
24
+ step('Generating changelog')
25
+ await wait('Generating', async () => {
26
+ try {
27
+ await run(
28
+ 'npx',
29
+ [
30
+ 'conventional-changelog',
31
+ '-p', 'angular',
32
+ '-i', argv.infile,
33
+ '-s' // same file to output
34
+ ],
35
+ { stdio: 'pipe' }
36
+ )
37
+ } catch (e) {
38
+ error('Unable to generate changelog', e)
39
+ }
40
+ })
41
+ substep($style.green('✔ Success'), { last: true })
42
+ this.log() // Blank line
43
+ }
44
+ })
@@ -0,0 +1,22 @@
1
+ /** @type {import('juisy/cli').Command} */
2
+ export default new CLI.Command({
3
+ command: 'generate:api',
4
+ describe: 'Generate API docs from source code',
5
+ builder (cli) {
6
+ cli
7
+ },
8
+ async handler (argv) {
9
+ // Utils
10
+ const { $style, step, substep } = CLI.OutputUtils
11
+
12
+ /**
13
+ * Generate api documentation
14
+ */
15
+ step('Generating API documentation')
16
+
17
+ substep($style.yellow('⚠ This command does nothing...'), { last: true })
18
+ this.log() // blank line
19
+ this.log($style.yellow('You can use JSDoc or TypeDoc for example. See documentation'))
20
+ this.log() // blank line
21
+ }
22
+ })
@@ -0,0 +1,11 @@
1
+ import { ReadmeTemplater } from 'juisy/templater'
2
+
3
+ /** @type {import('juisy/cli').Command} */
4
+ export default new CLI.Command({
5
+ command: 'generate:cli',
6
+ describe: 'Generate CLI docs',
7
+ async handler (argv) {
8
+ const templater = new ReadmeTemplater('./docs/cli/config.js')
9
+ await templater.generate()
10
+ }
11
+ })
@@ -0,0 +1,11 @@
1
+ import { ReadmeTemplater } from 'juisy/templater'
2
+
3
+ /** @type {import('juisy/cli').Command} */
4
+ export default new CLI.Command({
5
+ command: 'generate:readme',
6
+ describe: 'Generate README.md',
7
+ async handler (argv) {
8
+ const templater = new ReadmeTemplater('./docs/readme/config.js')
9
+ await templater.generate()
10
+ }
11
+ })
@@ -0,0 +1,20 @@
1
+ import generateAPI from './generate-api.js'
2
+
3
+ /** @type {import('juisy/cli').Command} */
4
+ export default new CLI.Command({
5
+ command: 'docs <command>',
6
+ describe: 'Manage project documentation',
7
+ meta: {
8
+ private: true
9
+ },
10
+ builder (cli) {
11
+ return cli
12
+ .command([
13
+ /**
14
+ * Default command are automatically injected here...
15
+ */
16
+ generateAPI
17
+ ])
18
+ },
19
+ handler (argv) {}
20
+ })
@@ -0,0 +1,42 @@
1
+ /** @type {import('juisy/cli').Command} */
2
+ export default new CLI.Command({
3
+ command: 'lint',
4
+ describe: 'Lint markdown with markdownlint',
5
+ builder: function (cli) {
6
+ cli.option('c', {
7
+ alias: 'config',
8
+ type: 'string',
9
+ describe: 'Path to custom markdownlint config file (relative to root folder)',
10
+ requiresArg: true
11
+ })
12
+ cli.option('f', {
13
+ alias: 'fix',
14
+ type: 'boolean',
15
+ describe: 'Auto fix by passing --fix option to markdownlint-cli2',
16
+ default: false
17
+ })
18
+ return cli
19
+ },
20
+ async handler (argv) {
21
+ const { abort, run } = CLI.InterfaceUtils
22
+ const configPath = argv.config || './docs/.markdownlint-cli2.cjs' // default relative to root folder
23
+
24
+ /**
25
+ * Call markdownlint command
26
+ */
27
+ try {
28
+ await run(
29
+ 'npx',
30
+ [
31
+ 'markdownlint-cli2',
32
+ '--config',
33
+ configPath,
34
+ ...(argv.fix ? [ '--fix' ] : [])
35
+ ],
36
+ { stdio: 'inherit' }
37
+ )
38
+ } catch (error) {
39
+ abort(error.exitCode)
40
+ }
41
+ }
42
+ })
@@ -0,0 +1,20 @@
1
+ import reset from './reset.js'
2
+ import sync from './sync.js'
3
+
4
+ /** @type {import('juisy/cli').Command} */
5
+ export default new CLI.Command({
6
+ command: 'git-hooks <command>',
7
+ describe: 'Git relative commands',
8
+ meta: {
9
+ private: true
10
+ },
11
+ builder: function (cli) {
12
+ return cli
13
+ .command([
14
+ reset,
15
+ sync
16
+ ])
17
+ .demandCommand(1, 'Command is missing. See help to learn more.')
18
+ },
19
+ handler: async function (argv) {}
20
+ })
@@ -0,0 +1,48 @@
1
+ import path from 'node:path'
2
+ import fs from 'fs-extra'
3
+
4
+ /** @type {import('juisy/cli').Command} */
5
+ export default new CLI.Command({
6
+ command: 'reset',
7
+ describe: 'Reset git hooks',
8
+ builder (cli) {
9
+ return cli
10
+ },
11
+ async handler (argv) {
12
+ // Utils
13
+ const { $style, step, substep } = CLI.OutputUtils
14
+ const { rootDir, run, abort } = CLI.InterfaceUtils
15
+ /**
16
+ * Reset git hooks
17
+ */
18
+ step('Reset git hooks')
19
+
20
+ // Create temporary empty configuration file
21
+ const tempConfigFilePath = './__TEMP_SIMPLE_GIT_HOOKS_CONFIG__.json' // relative to project root folder
22
+ fs.writeFileSync(path.resolve(rootDir, tempConfigFilePath), '{}')
23
+
24
+ // Run command with empty configuration
25
+ let commandError = false
26
+ try {
27
+ await run('npx', [
28
+ 'simple-git-hooks',
29
+ tempConfigFilePath
30
+ ], { stdio: 'pipe', cwd: rootDir })
31
+ } catch (e) {
32
+ commandError = e
33
+ }
34
+
35
+ // Don't forget to always remove temporary file
36
+ fs.unlinkSync(path.resolve(rootDir, tempConfigFilePath))
37
+
38
+ // If error
39
+ if (commandError) {
40
+ substep($style.red('❌ Unable to reset git hooks.'), { last: true })
41
+ abort(1) // Abort with error
42
+ } else {
43
+ // Everything is okay
44
+ substep($style.green('✔ Git hooks successfuly reset'), { last: true })
45
+ this.log() // blank line
46
+ }
47
+ }
48
+ })
@@ -0,0 +1,19 @@
1
+ /** @type {import('juisy/cli').Command} */
2
+ export default new CLI.Command({
3
+ command: 'sync',
4
+ describe: 'Sync git hooks',
5
+ builder (cli) {
6
+ return cli
7
+ },
8
+ async handler (argv) {
9
+ // Utils
10
+ const { step } = CLI.OutputUtils
11
+ const { run } = CLI.InterfaceUtils
12
+ /**
13
+ * Sync git hooks
14
+ */
15
+ step('Sync git hooks')
16
+
17
+ await run('npx', [ 'simple-git-hooks' ])
18
+ }
19
+ })
@@ -0,0 +1,223 @@
1
+ // const path = require('path')
2
+ // const {
3
+ // $style,
4
+ // log,
5
+ // step,
6
+ // substep,
7
+ // run,
8
+ // error,
9
+ // wait,
10
+ // confirm,
11
+ // prompts
12
+ // } = juisy.utils
13
+
14
+ // const rootDir = path.resolve(__dirname, '../../../')
15
+
16
+ // const updateVersion = require('../lib/version/update-version')
17
+
18
+ import semver from 'semver'
19
+
20
+ import { getPackageInfo } from 'juisy'
21
+
22
+ /** @type {import('juisy/cli').Command} */
23
+ export default new CLI.Command({
24
+ command: 'release',
25
+ describe: 'Make a release',
26
+ meta: {
27
+ private: true
28
+ },
29
+ builder: function (cli) {
30
+ cli.option('p', {
31
+ alias: 'preid',
32
+ type: 'string',
33
+ describe: 'Pre-release id',
34
+ requiresArg: true
35
+ })
36
+ return cli
37
+ },
38
+ async handler (argv) {
39
+ const { step } = CLI.OutputUtils
40
+ const { abort, run } = CLI.InterfaceUtils
41
+
42
+ /**
43
+ * Generate docs
44
+ */
45
+ step('Release-it')
46
+ await run('npx', [ 'release-it' ], { stdio: 'pipe' })
47
+
48
+ // const packageJson = getPackageInfo()
49
+ // let targetVersion
50
+ // const currentVersion = packageJson.version
51
+ // const packageName = packageJson.name
52
+ // const preId = argv.preid || (semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0])
53
+ // const inc = i => semver.inc(currentVersion, i, preId)
54
+ // const versionIncrements = [
55
+ // 'patch',
56
+ // 'minor',
57
+ // 'major',
58
+ // ...(preId ? [ 'prepatch', 'preminor', 'premajor', 'prerelease' ] : [])
59
+ // ]
60
+
61
+ // /**
62
+ // * First, check if local repository is clean
63
+ // */
64
+ // step('Checking changes to commit')
65
+ // const { stdout } = await run('git', [ 'diff' ], { stdio: 'pipe', cwd: rootDir })
66
+ // if (stdout) {
67
+ // error('Please commit your changes before creating a new release!', new Error('There are changes to commit'))
68
+ // }
69
+ // substep($style.green('✔ Local repository is clean'), { last: true })
70
+ // log() // Blank line
71
+
72
+ // /**
73
+ // * Release prompt
74
+ // */
75
+ // step('Setup')
76
+ // const { release } = await prompts([
77
+ // {
78
+ // type: 'select',
79
+ // name: 'release',
80
+ // message: 'Release type:',
81
+ // choices: versionIncrements.map(i => ({ title: `${i} (${inc(i)})`, value: inc(i) })).concat([ { title: 'custom', value: 'custom' } ])
82
+ // }
83
+ // ])
84
+ // // If custom release
85
+ // if (release === 'custom') {
86
+ // const { version: customVersion } = await prompts([
87
+ // {
88
+ // type: 'text',
89
+ // name: 'version',
90
+ // message: 'New custom version:',
91
+ // initial: currentVersion,
92
+ // validate: value => Boolean(semver.valid(value))
93
+ // }
94
+ // ])
95
+ // targetVersion = customVersion
96
+ // } else {
97
+ // targetVersion = release
98
+ // }
99
+
100
+ // /**
101
+ // * Demand confirmation
102
+ // */
103
+ // await confirm({ message: `Releasing v${targetVersion}. Confirm?` })
104
+ // log() // Blank line
105
+
106
+ // /**
107
+ // * Run tests
108
+ // */
109
+ // // step(`Running tests`)
110
+ // // ... here run tests
111
+
112
+ // /**
113
+ // * Update version in necessary files
114
+ // */
115
+ // await updateVersion(targetVersion)
116
+ // log() // Blank line
117
+
118
+ // /**
119
+ // * Generate docs
120
+ // */
121
+ // step('Generate docs')
122
+ // await wait('API docs', async () => {
123
+ // await run('node', [ './bin/cli', 'docs', 'generate:api' ], { stdio: 'pipe', cwd: rootDir })
124
+ // })
125
+ // substep($style.green('✔ API docs successfuly generated'))
126
+ // await wait('CLI docs', async () => {
127
+ // await run('node', [ './bin/cli', 'docs', 'generate:cli' ], { stdio: 'pipe', cwd: rootDir })
128
+ // })
129
+ // substep($style.green('✔ CLI docs successfuly generated'))
130
+ // await wait('README', async () => {
131
+ // await run('node', [ './bin/cli', 'docs', 'generate:readme' ], { stdio: 'pipe', cwd: rootDir })
132
+ // })
133
+ // substep($style.green('✔ README successfuly generated'), { last: true })
134
+ // // await wait('Linting', async () => {
135
+ // // await run('node', [ './bin/cli', 'docs', 'lint', '-c', './docs/.markdownlint-cli2.cjs' ], { stdio: 'pipe', cwd: rootDir })
136
+ // // })
137
+ // log() // Blank line
138
+
139
+ // /**
140
+ // * Generate changelog file
141
+ // */
142
+ // step('Generating changelog')
143
+ // await wait('Generating', async () => {
144
+ // await run('node', [ './bin/cli', 'changelog' ], { stdio: 'pipe', cwd: rootDir })
145
+ // })
146
+ // substep($style.green('✔ Success'), { last: true })
147
+ // log() // Blank line
148
+
149
+ // /**
150
+ // * Confirm changes to commit
151
+ // */
152
+ // step('Confirm changes to commit')
153
+ // substep($style.yellow('↓ The following changes will be committed:'))
154
+ // await run('git', [ 'status', '-s' ], { stdio: 'inherit', cwd: rootDir })
155
+ // substep($style.yellow('- The following tag will be created: ') + `v${targetVersion}`, { last: true })
156
+
157
+ // /**
158
+ // * Demand confirmation
159
+ // */
160
+ // await confirm()
161
+ // log() // Blank line
162
+
163
+ // /**
164
+ // * Publish package
165
+ // */
166
+ // step(`Publishing ${packageName}`)
167
+ // const releaseTag = targetVersion.includes('alpha')
168
+ // ? 'alpha'
169
+ // : targetVersion.includes('beta')
170
+ // ? 'beta'
171
+ // : targetVersion.includes('rc')
172
+ // ? 'rc'
173
+ // : null
174
+ // let alreadyPublished = false
175
+ // await wait('Publishing', async () => {
176
+ // try {
177
+ // await run('npm', [ 'publish', ...(releaseTag ? [ '--tag', releaseTag ] : []) ], { stdio: 'pipe', cwd: rootDir })
178
+ // } catch (e) {
179
+ // if (e.stderr.match(/previously published/)) {
180
+ // alreadyPublished = true
181
+ // } else {
182
+ // error('Unknown error during publishing', e)
183
+ // }
184
+ // }
185
+ // })
186
+ // substep(
187
+ // alreadyPublished
188
+ // ? $style.yellow(`Skipping already published: ${packageName}`)
189
+ // : $style.green('✔ Success'),
190
+ // { last: true }
191
+ // )
192
+ // log() // Blank line
193
+
194
+ // /**
195
+ // * Push to git
196
+ // */
197
+ // step('Pushing changes')
198
+ // await wait('Committing', async () => {
199
+ // try {
200
+ // await run('git', [ 'add', '.' ], { stdio: 'pipe', cwd: rootDir })
201
+ // await run('git', [ 'commit', '-m', `chore(release): v${targetVersion}` ], { stdio: 'pipe', cwd: rootDir })
202
+ // } catch (e) {
203
+ // error('Unable to commit', e)
204
+ // }
205
+ // })
206
+ // substep($style.green('✔ Committed'))
207
+ // await wait('Creating tag', async () => {
208
+ // try {
209
+ // await run('git', [ 'tag', '-a', `v${targetVersion}`, '-m', `v${targetVersion}` ], { stdio: 'pipe', cwd: rootDir })
210
+ // } catch (e) {
211
+ // error('Unable to create tag', e)
212
+ // }
213
+ // })
214
+ // substep($style.green('✔ Tagged'))
215
+ // log() // blank line
216
+
217
+ // log($style.green(`✔ Release v${targetVersion} successfuly created`))
218
+ // log()
219
+
220
+ // log($style.yellow('IMPORTANT: You should now run ') + 'git push origin --follow-tags')
221
+ // log()
222
+ }
223
+ })
@@ -0,0 +1,33 @@
1
+ /** @type {import('juisy/cli').Command} */
2
+ export default new CLI.Command({
3
+ command: 'test',
4
+ describe: 'Run project tests',
5
+ meta: {
6
+ private: true
7
+ },
8
+ builder (cli) {
9
+ cli.option('watch', {
10
+ alias: 'w',
11
+ type: 'boolean',
12
+ default: false,
13
+ describe: 'Run vitest in watch mode'
14
+ })
15
+ cli.option('ui', {
16
+ type: 'boolean',
17
+ default: false,
18
+ describe: 'Launch vitest UI'
19
+ })
20
+ return cli
21
+ },
22
+ async handler (args) {
23
+ const watchMode = args.watch || args.ui
24
+ const { run } = CLI.InterfaceUtils
25
+ /**
26
+ * Run tests
27
+ */
28
+ await run('vitest', [
29
+ ...(watchMode ? [] : [ 'run' ]),
30
+ ...(args.ui ? [ '--ui' ] : [])
31
+ ], { stdio: 'inherit' })
32
+ }
33
+ })
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict'
4
+
5
+ const cli = require('./cli')
6
+
7
+ cli().parse(process.argv.slice(2))
@@ -0,0 +1,33 @@
1
+ const fs = require('fs-extra')
2
+ const path = require('path')
3
+ const juisy = require('@hperchec/juisy')
4
+
5
+ const {
6
+ rootDir,
7
+ $style,
8
+ step,
9
+ substep
10
+ } = juisy.utils
11
+
12
+ /**
13
+ * Generate doc function
14
+ * @param {Object} [options = {}] - The options object
15
+ */
16
+ module.exports = async function generateAPIDoc (options = {}) {
17
+ // Process options
18
+ const distDir = options.distDir
19
+ const outputDir = options.outputDir
20
+
21
+ const outputFullPath = path.resolve(rootDir, distDir, outputDir)
22
+
23
+ /**
24
+ * Generate api documentation
25
+ */
26
+ step('Generating API documentation')
27
+
28
+ // Here do logic to generate your documentation
29
+ // ex: jsdoc
30
+ fs.writeFileSync(path.resolve(outputFullPath, './api.md'), '# Awesome API !', { encoding: 'utf8' })
31
+
32
+ substep($style.yellow('This command just creates an example file. Let\'s write your logic!'), { last: true })
33
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = async function genReleaseNote () {
2
+ console.log('Not configured...')
3
+ }
@@ -0,0 +1,51 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const juisy = require('@hperchec/juisy')
4
+
5
+ const {
6
+ rootDir,
7
+ $style,
8
+ error,
9
+ step,
10
+ substep,
11
+ run,
12
+ wait
13
+ } = juisy.utils
14
+
15
+ // File paths
16
+ const filePaths = {
17
+ packageJson: path.resolve(rootDir, './package.json')
18
+ }
19
+ // Get package.json content
20
+ const packageJson = require(filePaths.packageJson)
21
+
22
+ /**
23
+ * Update version in necessary files
24
+ * @param {string} version - The version
25
+ * @return {void}
26
+ */
27
+ module.exports = async function updateVersion (version) {
28
+ // Update version for each file
29
+ packageJson.version = version
30
+
31
+ /**
32
+ * Update files
33
+ */
34
+ step('Updating version in necessary files')
35
+ // Update package.json
36
+ try {
37
+ fs.writeFileSync(filePaths.packageJson, JSON.stringify(packageJson, null, 4), 'utf8')
38
+ } catch (e) {
39
+ error('Unable to update package.json file', e)
40
+ }
41
+ substep($style.green('✔ package.json successfuly updated'))
42
+ // Updating package-lock
43
+ await wait('Updating package-lock.json', async () => {
44
+ try {
45
+ await run('npm', [ 'install', '--prefer-offline' ], { stdio: 'pipe' })
46
+ } catch (e) {
47
+ error('Unable to update package-lock.json file', e)
48
+ }
49
+ })
50
+ substep($style.green('✔ package-lock.json successfuly updated'), { last: true })
51
+ }
@@ -0,0 +1,42 @@
1
+ const juisy = require('@hperchec/juisy')
2
+
3
+ const {
4
+ $style,
5
+ abort,
6
+ log,
7
+ rootDir,
8
+ step,
9
+ substep,
10
+ run
11
+ } = juisy.utils
12
+
13
+ /**
14
+ * commit-msg git hook
15
+ */
16
+ ;(async function () {
17
+ step('Git hook: commit-msg')
18
+
19
+ // Get git commit msg path from args
20
+ const args = process.argv.slice(2)
21
+ const gitMsgPath = args[0]
22
+
23
+ try {
24
+ // original is: npx --no -- commitlint --edit ${1}
25
+ // we replace "${1}" by gitMsgPath arg
26
+ await run('npx', [
27
+ '--no',
28
+ '--',
29
+ 'commitlint',
30
+ '--edit',
31
+ gitMsgPath
32
+ ], { cwd: rootDir })
33
+ } catch (e) {
34
+ substep($style.red('❌ Git hook: "commit-msg" failed. Please check ./COMMIT_CONVENTION.md for more informations.'), { last: true })
35
+ abort(1) // Abort with error
36
+ }
37
+
38
+ // Everything is okay
39
+ substep($style.green('✔ Git hook: "commit-msg" passed'), { last: true })
40
+
41
+ log() // blank line
42
+ })()
@@ -0,0 +1,32 @@
1
+ const juisy = require('@hperchec/juisy')
2
+
3
+ const {
4
+ $style,
5
+ error,
6
+ log,
7
+ rootDir,
8
+ step,
9
+ substep,
10
+ run
11
+ } = juisy.utils
12
+
13
+ /**
14
+ * Pre commit git hook
15
+ */
16
+ ;(async function () {
17
+ step('Git hook: pre-commit')
18
+
19
+ try {
20
+ // npx lint-staged
21
+ await run('npx', [
22
+ 'lint-staged'
23
+ ], { stdio: 'pipe', cwd: rootDir })
24
+ } catch (e) {
25
+ error('Git hook: "commit-msg" error: ', e)
26
+ }
27
+
28
+ // Everything is okay
29
+ substep($style.green('✔ Git hook: "pre-commit" passed'), { last: true })
30
+
31
+ log() // blank line
32
+ })()
@@ -0,0 +1,34 @@
1
+ /** @type {import('juisy/cli').GlobalSettings} */
2
+ export default {
3
+ changelog: {
4
+ infile: 'CHANGELOG.md',
5
+ preset: 'angular',
6
+ sameFile: true
7
+ },
8
+ docs: {
9
+ readme: {
10
+ config: './docs/readme/config.js'
11
+ },
12
+ cli: {
13
+ config: './docs/cli/config.js'
14
+ }
15
+ },
16
+ gitHooks: {
17
+ 'pre-commit': 'node ./bin/scripts/pre-commit.js',
18
+ 'commit-msg': 'node ./bin/scripts/commit-msg.js ${1}'
19
+ },
20
+ lint: {
21
+ staged: {
22
+ '*.{js,cjs,ts}': [
23
+ 'eslint --fix'
24
+ ]
25
+ }
26
+ },
27
+ release: {
28
+ git: {
29
+ commitMessage: 'chore(release): v${version}',
30
+ requireBranch: 'main',
31
+ tagAnnotation: 'v${version}'
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "./node_modules/juisy/dist/GlobalSettings.schema.json",
3
+ "changelog": {
4
+ "infile": "CHANGELOG.md",
5
+ "preset": "angular",
6
+ "sameFile": true
7
+ },
8
+ "docs": {
9
+ "readme": {
10
+ "config": "./docs/readme/config.js"
11
+ },
12
+ "cli": {
13
+ "config": "./docs/cli/config.js"
14
+ }
15
+ },
16
+ "gitHooks": {
17
+ "pre-commit": "node ./bin/scripts/pre-commit.js",
18
+ "commit-msg": "node ./bin/scripts/commit-msg.js ${1}"
19
+ },
20
+ "lint": {
21
+ "staged": {
22
+ "*.{js,cjs,ts}": [
23
+ "eslint --fix"
24
+ ]
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,28 @@
1
+ import type { GlobalSettings } from 'juisy/cli'
2
+
3
+ export default {
4
+ changelog: {
5
+ infile: 'CHANGELOG.md',
6
+ preset: 'angular',
7
+ sameFile: true
8
+ },
9
+ docs: {
10
+ readme: {
11
+ config: './docs/readme/config.js'
12
+ },
13
+ cli: {
14
+ config: './docs/cli/config.js'
15
+ }
16
+ },
17
+ gitHooks: {
18
+ 'pre-commit': 'node ./bin/scripts/pre-commit.js',
19
+ 'commit-msg': 'node ./bin/scripts/commit-msg.js ${1}'
20
+ },
21
+ lint: {
22
+ staged: {
23
+ '*.{js,cjs,ts}': [
24
+ 'eslint --fix'
25
+ ]
26
+ }
27
+ }
28
+ } satisfies GlobalSettings
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ /**
3
+ * Dist directory (relative to root folder)
4
+ */
5
+ distDir: './docs/api',
6
+ /**
7
+ * Output directory (relative to distDir folder)
8
+ */
9
+ outputDir: 'output'
10
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @hperchec/readme-generator Example configuration file
3
+ */
4
+
5
+ const path = require('path')
6
+
7
+ // Export configuration
8
+ module.exports = {
9
+ // Template path
10
+ templatePath: path.resolve(__dirname, './template.md'), // Default template file
11
+ // Output path
12
+ outputPath: path.resolve(__dirname, '../../'), // Your project root directory by default
13
+ // Output file name
14
+ outputName: 'README.md', // 'README.md' by default
15
+ // Path to ejs data file
16
+ ejsDataPath: path.resolve(__dirname, './readme.js'), // Default template ejs data file
17
+ // EJS options (see https://www.npmjs.com/package/ejs#options)
18
+ ejsOptions: {
19
+ async: true
20
+ /* your ejs options... */
21
+ }
22
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @hperchec/readme-generator Template EJS data example file
3
+ */
4
+
5
+ const markdownTable = require('markdown-table') // ! v3 not compatible, use v2
6
+
7
+ // Based on the package.json file, get some data and informations
8
+ const pkg = require('../../package.json')
9
+ const packageName = pkg.name
10
+ const packageUrl = `https://www.npmjs.com/package/${packageName}`
11
+ const dependencies = pkg.dependencies || {}
12
+ const devDependencies = pkg.devDependencies || {}
13
+ const peerDependencies = pkg.peerDependencies || {}
14
+ const author = pkg.author
15
+ const contributors = pkg.contributors || []
16
+ const license = pkg.license || 'Unknown'
17
+ const homepage = pkg.homepage
18
+ const projectUrl = pkg.repository.url
19
+ const issuesUrl = pkg.bugs.url
20
+
21
+ // Output a markdown formatted table from a js object
22
+ // Like:
23
+ // |name|version|
24
+ // |----|-------|
25
+ // | | |
26
+ function getMdDependencies (deps) {
27
+ return markdownTable([
28
+ [ 'name', 'version' ],
29
+ ...(Object.entries(deps))
30
+ ])
31
+ }
32
+
33
+ /**
34
+ * Return author link
35
+ * @param {Object} author
36
+ * @return {string}
37
+ */
38
+ function getMdAuthor (author) {
39
+ return '[' + author.name + '](' + author.url + ')'
40
+ }
41
+
42
+ /**
43
+ * Return markdown list of persons
44
+ * @param {Array} contributors
45
+ * @return {String}
46
+ */
47
+ function getMdContributors (contributors) {
48
+ let mdString = ''
49
+ contributors.forEach((person) => {
50
+ mdString += '- [' + person.name + '](' + person.url + ')\n'
51
+ })
52
+ return mdString
53
+ }
54
+
55
+ /**
56
+ * Export data for readme file templating
57
+ */
58
+ module.exports = {
59
+ packageName,
60
+ packageUrl,
61
+ projectUrl,
62
+ homepage,
63
+ issuesUrl,
64
+ dependencies: getMdDependencies(dependencies),
65
+ devDependencies: getMdDependencies(devDependencies),
66
+ peerDependencies: getMdDependencies(peerDependencies),
67
+ author: getMdAuthor(author),
68
+ contributors: getMdContributors(contributors),
69
+ license
70
+ }
@@ -0,0 +1,53 @@
1
+ # Awesome project
2
+
3
+ [![author](https://img.shields.io/static/v1?label=&message=Author:&color=black)]()
4
+ [![herve-perchec](http://herve-perchec.com/badge.svg)](http://herve-perchec.com/)
5
+
6
+ **Table of contents**:
7
+
8
+ [[*TOC*]]
9
+
10
+ ## 🚀 Get started
11
+
12
+ ```sh
13
+ # Let's go!
14
+ ```
15
+
16
+ ## 📙 Documentation
17
+
18
+ Please check the [documentation](https://www.npmjs.com/package/@hperchec/juisy)
19
+
20
+ ## 🧱 Dependencies
21
+
22
+ <details>
23
+ <summary>Global</summary>
24
+
25
+ <%%= dependencies %>
26
+
27
+ </details>
28
+
29
+ <details>
30
+ <summary>Dev</summary>
31
+
32
+ <%%= devDependencies %>
33
+
34
+ </details>
35
+
36
+ <details>
37
+ <summary>Peer</summary>
38
+
39
+ <%%= peerDependencies %>
40
+
41
+ </details>
42
+
43
+ ## ✍ Author
44
+
45
+ <%%= author %>
46
+
47
+ ## 📄 License
48
+
49
+ <%%= license %>
50
+
51
+ ----
52
+
53
+ Made with ❤ by <%%= author %>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "new-project",
3
+ "version": "1.0.0",
4
+ "description": "New project based on Juisy template",
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://gitlab.com/hperchec/juisy.git"
12
+ },
13
+ "author": {
14
+ "name": "Hervé Perchec",
15
+ "url": "https://gitlab.com/herveperchec"
16
+ },
17
+ "license": "ISC",
18
+ "bugs": {
19
+ "url": "https://gitlab.com/hperchec/juisy/issues"
20
+ },
21
+ "homepage": "https://gitlab.com/hperchec/juisy#readme",
22
+ "dependencies": {
23
+ "juisy": "^__JUISY_VERSION__"
24
+ }
25
+ }
@@ -0,0 +1,33 @@
1
+ import pkg from './package.json' assert { type: 'json' }
2
+
3
+ // Exports globals object
4
+ export default {
5
+ ENV: {
6
+ NODE_ENV: process.env.NODE_ENV
7
+ },
8
+ PACKAGE: {
9
+ NAME: pkg.name,
10
+ URL: `https://www.npmjs.com/package/${pkg.name}`
11
+ },
12
+ VERSION: pkg.version,
13
+ AUTHOR: {
14
+ EMAIL: pkg.author.email,
15
+ NAME: pkg.author.name,
16
+ URL: pkg.author.url
17
+ },
18
+ REPOSITORY: {
19
+ TYPE: pkg.repository.type,
20
+ URL: pkg.repository.url
21
+ },
22
+ ISSUES_URL: pkg.bugs.url,
23
+ HOMEPAGE: pkg.homepage,
24
+ BRAND: {
25
+ COLORS: {
26
+ GERALDINE: '#FF8E92',
27
+ SAND: '#FFC17A',
28
+ COGNAC: '#A04113',
29
+ DARK_PINK: '#E95185',
30
+ PINK_SHERBET: '#F583A3'
31
+ }
32
+ }
33
+ }