@reflex-stack/tsp 0.1.0
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.
- package/README.md +65 -0
- package/package.json +23 -0
- package/src/cli.js +225 -0
- package/src/commands/build.js +15 -0
- package/src/commands/init.js +245 -0
- package/src/commands/size-report.js +132 -0
- package/src/commands/test.js +20 -0
- package/src/config.js +18 -0
- package/src/scaffold/README.md.template +0 -0
- package/src/scaffold/tsconfig.json.template +0 -0
- package/src/utils.js +61 -0
- package/tests/example-package/README.md +13 -0
- package/tests/example-package/dist/common-dep.d.ts +1 -0
- package/tests/example-package/dist/common-dep.js +1 -0
- package/tests/example-package/dist/index.d.ts +2 -0
- package/tests/example-package/dist/index.js +2 -0
- package/tests/example-package/dist/root-dep.d.ts +1 -0
- package/tests/example-package/dist/root-dep.js +1 -0
- package/tests/example-package/dist/stuff.d.ts +1 -0
- package/tests/example-package/dist/stuff.js +4 -0
- package/tests/example-package/dist/submodule/index.d.ts +1 -0
- package/tests/example-package/dist/submodule/index.js +5 -0
- package/tests/example-package/dist/submodule/submodule-dep.d.ts +1 -0
- package/tests/example-package/dist/submodule/submodule-dep.js +1 -0
- package/tests/example-package/package-lock.json +880 -0
- package/tests/example-package/package.json +35 -0
- package/tests/example-package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# TSP
|
|
2
|
+
|
|
3
|
+
**TypeScript Package** (tsp), scaffolds and build **Typescript** sources to **EcmaScript modules** and publish them as modular packages to **NPM** or **JSR**.
|
|
4
|
+
|
|
5
|
+
## Init a new library
|
|
6
|
+
|
|
7
|
+
First, create the associated **git repository** and clone it.
|
|
8
|
+
|
|
9
|
+
Run this command in git trunk :
|
|
10
|
+
```bash
|
|
11
|
+
npx @reflex-stack/tsp init
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Created files
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
├─ dist/
|
|
18
|
+
├─ src/
|
|
19
|
+
│ ├─ submodule
|
|
20
|
+
│ │ └─ index.ts
|
|
21
|
+
│ └─ index.ts
|
|
22
|
+
├─ tests/
|
|
23
|
+
│ └─ test.js
|
|
24
|
+
├─ .gitignore
|
|
25
|
+
├─ .npmignore
|
|
26
|
+
├─ LICENCE ( if MIT )
|
|
27
|
+
├─ package.json
|
|
28
|
+
├─ package-lock.json
|
|
29
|
+
├─ README.md
|
|
30
|
+
└─ tsconfig.json
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Available commands
|
|
34
|
+
|
|
35
|
+
#### Build sources
|
|
36
|
+
```shell
|
|
37
|
+
npm run build
|
|
38
|
+
```
|
|
39
|
+
- Will clear `./dist`, build sources from `.ts` files to `.js` and `.d.ts` files.
|
|
40
|
+
- Will generate size report and generate `./reports` directory with JSON and SVG files.
|
|
41
|
+
|
|
42
|
+
> Run `npm run build --noSizeReport`
|
|
43
|
+
|
|
44
|
+
#### Test
|
|
45
|
+
- `npm run test`
|
|
46
|
+
> Will clear `./dist`, build sources and run tests. No size report.
|
|
47
|
+
|
|
48
|
+
#### Publish
|
|
49
|
+
- `npm run publish`
|
|
50
|
+
> Will clear `./dist`, build sources and run tests, and start publish process.
|
|
51
|
+
> This will ask you how to upgrade package.json version, push to git and npm.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Size report
|
|
55
|
+
- TODO SVG doc
|
|
56
|
+
- TODO JSON doc
|
|
57
|
+
|
|
58
|
+
## tsconfig
|
|
59
|
+
- TODO doc, explain forbidden properties
|
|
60
|
+
|
|
61
|
+
## TSP config
|
|
62
|
+
- TODO tsp config
|
|
63
|
+
|
|
64
|
+
## Next
|
|
65
|
+
- TODO config override from package
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reflex-stack/tsp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"tsp": "./src/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"publish": "node ./src/cli.js publish"
|
|
10
|
+
},
|
|
11
|
+
"author": "Alexis Bouhet",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"description": "",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@types/node": "^22.10.2",
|
|
16
|
+
"@zouloux/cli": "^0.2.8",
|
|
17
|
+
"@zouloux/files": "^3.0.4",
|
|
18
|
+
"brotli-size": "^4.0.0",
|
|
19
|
+
"stach": "^2.0.1",
|
|
20
|
+
"terser": "^5.37.0",
|
|
21
|
+
"typescript": "^5.7.2"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
':' //# comment; exec /usr/bin/env "${RUNTIME:-node}" "$0" "$@"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import { askInput, CLICommands, execAsync, nicePrint, oraTask, execSync, table, askList, newLine } from "@zouloux/cli";
|
|
6
|
+
import { build, clearOutput } from "./commands/build.js";
|
|
7
|
+
import { getUserPackageJson, naiveHumanFileSize, showIntroMessage } from "./utils.js";
|
|
8
|
+
import { cleanSizeReports, generateJSON, generateSVGs, sizeReport } from "./commands/size-report.js";
|
|
9
|
+
import { init } from "./commands/init.js";
|
|
10
|
+
import { getConfig } from "./config.js";
|
|
11
|
+
import { test } from "./commands/test.js";
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// ----------------------------------------------------------------------------- COMMANDS
|
|
15
|
+
|
|
16
|
+
const commands = new CLICommands({
|
|
17
|
+
noSizeReport: false,
|
|
18
|
+
noIntro: false,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Executed for all commands
|
|
22
|
+
commands.before((args, flags, commandName) => {
|
|
23
|
+
if ( commandName !== "publish" && !flags.noIntro)
|
|
24
|
+
showIntroMessage(!commandName || commandName === "init")
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* INIT COMMAND
|
|
29
|
+
* Create a new ecma-build library ready to be published.
|
|
30
|
+
*/
|
|
31
|
+
commands.add("init", async (args, flags, commandName) => {
|
|
32
|
+
await init()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* BUILD COMMAND
|
|
37
|
+
* options: --noSizeReport
|
|
38
|
+
*/
|
|
39
|
+
commands.add("build", async (args, flags) => {
|
|
40
|
+
|
|
41
|
+
const userPackage = getUserPackageJson()
|
|
42
|
+
const config = getConfig(userPackage)
|
|
43
|
+
|
|
44
|
+
await oraTask('Cleaning output', async ( task ) => {
|
|
45
|
+
await clearOutput( config )
|
|
46
|
+
task.success()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
await oraTask('Building with tsc', async ( task ) => {
|
|
50
|
+
await build( config )
|
|
51
|
+
task.success('Built ✨')
|
|
52
|
+
}, (error, task) => {
|
|
53
|
+
task.error()
|
|
54
|
+
console.log(error.stdout ?? '')
|
|
55
|
+
console.log(error.stderr ?? '')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// CLI Flag to disable reports and only build
|
|
59
|
+
if ( flags.noSizeReport )
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
// Generate size report
|
|
63
|
+
const report = await oraTask('Generating size report', async ( task ) => {
|
|
64
|
+
// Extract "exports" from package.json and use them as bundle directories
|
|
65
|
+
if ( typeof userPackage.exports !== "object" )
|
|
66
|
+
nicePrint(`{b/r}Invalid export property in package.json.`)
|
|
67
|
+
const bundles = Object.keys( userPackage.exports )
|
|
68
|
+
// Generate report data
|
|
69
|
+
let report = await sizeReport( bundles, config )
|
|
70
|
+
task.success()
|
|
71
|
+
return report
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// Generate
|
|
75
|
+
if ( config["generate-svg-report"] || config["generate-json-report"] ) {
|
|
76
|
+
await oraTask('Generating report files', async ( task ) => {
|
|
77
|
+
// Clean report directory
|
|
78
|
+
await cleanSizeReports( config )
|
|
79
|
+
// Generate JSON
|
|
80
|
+
if ( config["generate-json-report"] )
|
|
81
|
+
generateJSON( report, config )
|
|
82
|
+
// Generate SVG files
|
|
83
|
+
if ( config["generate-svg-report"] )
|
|
84
|
+
await generateSVGs( report, config )
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Print report table in CLI
|
|
89
|
+
const tableData = [
|
|
90
|
+
// Table header
|
|
91
|
+
["Bundle", "Original size", "Brotli size"]
|
|
92
|
+
]
|
|
93
|
+
// Table helpers
|
|
94
|
+
const tablePrint = s => nicePrint(s, { output: "return", newLine: false })
|
|
95
|
+
const tableLine = () => tableData.push(['', '', ''])
|
|
96
|
+
// Total bundle size
|
|
97
|
+
let totalOriginalSize = 0
|
|
98
|
+
let totalBrotliSize = 0
|
|
99
|
+
// Browse report bundles
|
|
100
|
+
report.forEach( bundleReport => {
|
|
101
|
+
tableLine()
|
|
102
|
+
// Count total bundle size
|
|
103
|
+
totalOriginalSize += bundleReport.sizes[0]
|
|
104
|
+
totalBrotliSize += bundleReport.sizes[2]
|
|
105
|
+
// Show bundle report
|
|
106
|
+
const isMainModule = bundleReport.name === "main"
|
|
107
|
+
tableData.push([
|
|
108
|
+
userPackage.name + (isMainModule ? "" : `/${bundleReport.name}`),
|
|
109
|
+
tablePrint(`{b}${naiveHumanFileSize(bundleReport.sizes[0])}`),
|
|
110
|
+
tablePrint(`{c/b}${naiveHumanFileSize(bundleReport.sizes[2])}`),
|
|
111
|
+
])
|
|
112
|
+
// Show sub-files reports
|
|
113
|
+
const totalFiles = bundleReport.files.length
|
|
114
|
+
bundleReport.files.map( (file, i) => {
|
|
115
|
+
tableData.push([
|
|
116
|
+
tablePrint(`{d}${i === totalFiles - 1 ? "└" : "├"}─ ${file.path}`),
|
|
117
|
+
tablePrint(`{d}${naiveHumanFileSize(file.sizes[0])}`),
|
|
118
|
+
tablePrint(`{d}${naiveHumanFileSize(file.sizes[2])}`),
|
|
119
|
+
])
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
// Show total if more than 1 bundle
|
|
123
|
+
if ( report.length > 1 ) {
|
|
124
|
+
tableLine()
|
|
125
|
+
tableData.push([
|
|
126
|
+
tablePrint(`{b}Total`),
|
|
127
|
+
tablePrint(`{b}${naiveHumanFileSize(totalOriginalSize)}`),
|
|
128
|
+
tablePrint(`{g/b}${naiveHumanFileSize(totalBrotliSize)}`),
|
|
129
|
+
])
|
|
130
|
+
}
|
|
131
|
+
// Print table
|
|
132
|
+
newLine()
|
|
133
|
+
table(tableData, true, [20], ' ')
|
|
134
|
+
newLine()
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* TEST COMMAND
|
|
139
|
+
*/
|
|
140
|
+
commands.add("test", async (args, flags, commandName) => {
|
|
141
|
+
|
|
142
|
+
const userPackage = getUserPackageJson()
|
|
143
|
+
const config = getConfig(userPackage)
|
|
144
|
+
|
|
145
|
+
await test( config )
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
commands.add("publish", async (args, flags, commandName) => {
|
|
149
|
+
// Check NPM connected user
|
|
150
|
+
await oraTask({text: `Connecting to npm`}, async task => {
|
|
151
|
+
try {
|
|
152
|
+
const whoami = await execAsync(`npm whoami`, 0)
|
|
153
|
+
task.success(nicePrint(`Hello {b/c}@${whoami}`, {output: 'return'}).trim())
|
|
154
|
+
return whoami
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
task.error(`Please connect to npm with ${chalk.bold('npm login')}`)
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
// TODO : When test will build only needed files, move build after tests
|
|
161
|
+
// (to build all files after test has succeed)
|
|
162
|
+
// Compile
|
|
163
|
+
//await CLICommands.run(`build`, cliArguments, cliOptions)
|
|
164
|
+
//await CLICommands.run(`test`, cliArguments, cliOptions)
|
|
165
|
+
|
|
166
|
+
// todo : Internal build
|
|
167
|
+
// todo : Run test command
|
|
168
|
+
// todo : Internal run report
|
|
169
|
+
// Prepare commands
|
|
170
|
+
const userPackage = getUserPackageJson()
|
|
171
|
+
let { version, name } = userPackage
|
|
172
|
+
const config = getConfig( userPackage )
|
|
173
|
+
const libraryExecOptions = { cwd: config.cwd };
|
|
174
|
+
const stdioLevel = 3;
|
|
175
|
+
// Test this library, and exit if it fails
|
|
176
|
+
// Test passed, show current version and git status
|
|
177
|
+
newLine()
|
|
178
|
+
nicePrint(`📦 Current version of {b/c}${name}{/} is {b/c}${version}`)
|
|
179
|
+
// Ask how to increment version
|
|
180
|
+
const increment = await askList(`How to increment ?`, {
|
|
181
|
+
patch: 'patch (0.0.X) - No new features, patch bugs or optimize code',
|
|
182
|
+
minor: 'minor (0.X.0) - No breaking change, have new or improved features',
|
|
183
|
+
major: 'major (X.0.0) - Breaking change',
|
|
184
|
+
// Keep but publish on NPM (if already increment in package.json)
|
|
185
|
+
keep: `keep (${ version }) - Publish current package.json version`,
|
|
186
|
+
// Push on git but no lib publish
|
|
187
|
+
push: `push - Push on git only, no npm publish`,
|
|
188
|
+
// Skip this lib (no publish at all, go to next library)
|
|
189
|
+
skip: `skip - Do not publish ${ name }`,
|
|
190
|
+
}, { returnType: 'key' });
|
|
191
|
+
// Go to next library
|
|
192
|
+
if ( increment === 'skip' )
|
|
193
|
+
return
|
|
194
|
+
// execSync(`git status -s`, stdioLevel, libraryExecOptions)
|
|
195
|
+
// Ask for commit message
|
|
196
|
+
let message = await askInput(`Commit message ?`);
|
|
197
|
+
message = message.replace(/["']/g, "'");
|
|
198
|
+
// If we increment, use npm version
|
|
199
|
+
if ( increment !== 'keep' && increment !== 'push' ) {
|
|
200
|
+
version = execSync(`npm version ${increment} --no-git-tag-version -m"${name} - %s - ${message}"`, stdioLevel, libraryExecOptions).toString().trim();
|
|
201
|
+
}
|
|
202
|
+
// Add to git and push
|
|
203
|
+
execSync(`git add .`, stdioLevel, libraryExecOptions);
|
|
204
|
+
execSync(`git commit -m"${name} - ${version} : ${message}"`, stdioLevel, libraryExecOptions);
|
|
205
|
+
execSync(`git push`, stdioLevel, libraryExecOptions);
|
|
206
|
+
// Publish on npm as public
|
|
207
|
+
// FIXME : Access public as an option for private repositories
|
|
208
|
+
// Ingore script to avoid infinite loop (if "package.json.scripts.publish" == "tsbundle publish")
|
|
209
|
+
if ( increment !== 'push' ) {
|
|
210
|
+
execSync(`npm publish --access public --ignore-scripts`, stdioLevel, libraryExecOptions);
|
|
211
|
+
nicePrint(`👌 {b/g}${name}{/}{g} Published, new version is {b/g}${version}`)
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
nicePrint(`👍 {b/g}${name}{/}{g} Pushed to git`)
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
commands.start(function (commandName) {
|
|
219
|
+
if ( commandName )
|
|
220
|
+
return
|
|
221
|
+
nicePrint(`{b/r}Command '${commandName}' not found`)
|
|
222
|
+
newLine()
|
|
223
|
+
nicePrint(`Available commands :`)
|
|
224
|
+
commands.list().forEach( command => nicePrint(`- ${command}`) )
|
|
225
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { execStream } from "@zouloux/cli";
|
|
2
|
+
import { Directory } from "@zouloux/files";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export async function clearOutput ( config ) {
|
|
7
|
+
const dir = new Directory( join(config.cwd, config.dist) )
|
|
8
|
+
await dir.ensureParents()
|
|
9
|
+
await dir.clean()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function build ( config ) {
|
|
13
|
+
const command = `tsc -p tsconfig.json --rootDir ${config.src} --outDir ${config.dist} --declaration true --noEmitOnError true --pretty`
|
|
14
|
+
await execStream(command, { cwd: config.cwd }, () => {})
|
|
15
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { nicePrint, askInput, askList, newLine, oraTask, execAsync } from "@zouloux/cli"
|
|
2
|
+
import { getConfig } from "../config.js";
|
|
3
|
+
import { getGitRemoteUrl, getTSPPackageJson } from "../utils.js";
|
|
4
|
+
import { existsSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { Stach } from "stach";
|
|
6
|
+
import { mkdirSync } from "fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
const licenceTemplate = `MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2022 {{ authorName }}
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.`
|
|
30
|
+
|
|
31
|
+
const readmeTemplate = `# {{ packageTextName }}
|
|
32
|
+
Main bundle is
|
|
33
|
+
<picture style="display: inline-block">
|
|
34
|
+
<source media="(prefers-color-scheme: dark)" srcset="./reports/main-dark.svg">
|
|
35
|
+
<img src="./reports/main-light.svg">
|
|
36
|
+
</picture>
|
|
37
|
+
Optional submodule is only
|
|
38
|
+
<picture style="display: inline-block">
|
|
39
|
+
<source media="(prefers-color-scheme: dark)" srcset="./reports/submodule-dark.svg">
|
|
40
|
+
<img src="./reports/submodule-light.svg">
|
|
41
|
+
</picture>
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
\`npm i {{ packageNPMName }}\`
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
##### Main module
|
|
50
|
+
- \`import { ... } from "{{ packageNPMName }}"\`
|
|
51
|
+
|
|
52
|
+
##### Sub module
|
|
53
|
+
- \`import { ... } from "{{ packageNPMName }}/submodule"\`
|
|
54
|
+
|
|
55
|
+
## tsp commands
|
|
56
|
+
|
|
57
|
+
##### Build
|
|
58
|
+
- \`npm run build\`
|
|
59
|
+
##### Test
|
|
60
|
+
- \`npm run test\`
|
|
61
|
+
##### Publish
|
|
62
|
+
- \`npm run publish\`
|
|
63
|
+
`
|
|
64
|
+
|
|
65
|
+
const tsconfigTemplate = `{
|
|
66
|
+
"compilerOptions": {
|
|
67
|
+
// Compilation level target
|
|
68
|
+
// If using a bundler in your project it should be the previous or current year
|
|
69
|
+
"target" : "{{esLevel}}",
|
|
70
|
+
// TS libs used, include needed lib.
|
|
71
|
+
// It should contain the target and "DOM" if it runs in the browser
|
|
72
|
+
// https://www.typescriptlang.org/tsconfig/#lib
|
|
73
|
+
"lib": [{{libs}}],
|
|
74
|
+
// Module config, you should not change it
|
|
75
|
+
"module": "NodeNext",
|
|
76
|
+
"isolatedModules": false,
|
|
77
|
+
"allowImportingTsExtensions": false,
|
|
78
|
+
// Compiler config
|
|
79
|
+
"strict": {{tsStrict}},
|
|
80
|
+
"allowJs": false,
|
|
81
|
+
"useDefineForClassFields" : true,
|
|
82
|
+
"allowSyntheticDefaultImports": true,
|
|
83
|
+
// ---
|
|
84
|
+
// Feel free to configure this file for your needs
|
|
85
|
+
// https://www.typescriptlang.org/tsconfig/
|
|
86
|
+
// ---
|
|
87
|
+
// Forbidden props are :
|
|
88
|
+
// - rootDir
|
|
89
|
+
// - outDir
|
|
90
|
+
// - declaration
|
|
91
|
+
// - noEmitOnError
|
|
92
|
+
// - pretty
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
`
|
|
96
|
+
|
|
97
|
+
const gitIgnoreTemplate = `.DS_Store
|
|
98
|
+
.idea
|
|
99
|
+
tmp
|
|
100
|
+
node_modules
|
|
101
|
+
dist
|
|
102
|
+
`
|
|
103
|
+
|
|
104
|
+
const npmIgnoreTemplate = `.DS_Store
|
|
105
|
+
.idea
|
|
106
|
+
LICENCE
|
|
107
|
+
tmp/
|
|
108
|
+
src/
|
|
109
|
+
.github/
|
|
110
|
+
docs/
|
|
111
|
+
`
|
|
112
|
+
|
|
113
|
+
const rootIndexTs = `// Root index file, export elements here
|
|
114
|
+
// Import nothing from submodule to keep things separated
|
|
115
|
+
export function randomFunction () {
|
|
116
|
+
return 5
|
|
117
|
+
}
|
|
118
|
+
`
|
|
119
|
+
const subModuleIndexTs = `// Sub-module index file, export elements here
|
|
120
|
+
// You can import elements from root
|
|
121
|
+
|
|
122
|
+
// Always import with the js extension, event in ts files
|
|
123
|
+
import { randomFunction } from "../index.js"
|
|
124
|
+
|
|
125
|
+
export function subRandomFunction () {
|
|
126
|
+
return randomFunction() * 12
|
|
127
|
+
}
|
|
128
|
+
`
|
|
129
|
+
|
|
130
|
+
const testFile = `// Write your test file here
|
|
131
|
+
console.log("No test implemented yet")
|
|
132
|
+
process.exit(0)
|
|
133
|
+
`
|
|
134
|
+
|
|
135
|
+
export async function init () {
|
|
136
|
+
// Check if some critical file already exists and warn before overriding
|
|
137
|
+
if ( existsSync("package.json") ) {
|
|
138
|
+
nicePrint(`{o}package.json is already existing. Continuing will override files in this directory.`)
|
|
139
|
+
const sure = await askList("Are you sure to continue ?", ["Yes", "No"], { defaultIndex: 1, returnType: "index" })
|
|
140
|
+
if ( sure === 1 )
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
// Get git remote
|
|
144
|
+
const config = getConfig()
|
|
145
|
+
let remoteURL = getGitRemoteUrl( config.cwd )
|
|
146
|
+
if ( !remoteURL )
|
|
147
|
+
nicePrint(`{o}Before scaffolding your TypeScript package, you should create the associated git repository.`)
|
|
148
|
+
else
|
|
149
|
+
nicePrint(`{d}Git origin is {/}${remoteURL}`)
|
|
150
|
+
newLine()
|
|
151
|
+
const options = { remoteURL }
|
|
152
|
+
options.packageTextName = await askInput(`Package name, in plain text {d}ex : My Package`, { notEmpty: true })
|
|
153
|
+
options.packageNPMName = await askInput(`Package name, for NPM, with namespace {d}ex : @mynamespace/mypackage`, { notEmpty: true })
|
|
154
|
+
options.authorName = await askInput(`Author name`, { notEmpty: true })
|
|
155
|
+
options.licenceName = await askInput(`Licence name`, { defaultValue: "MIT" })
|
|
156
|
+
options.esLevel = await askInput(`ES Level for tsconfig`, { defaultValue: "es2023" })
|
|
157
|
+
options.tsStrict = await askList(`Use strict Typescript?`, ["Yes", "No"], { defaultIndex: 1, returnType: "index" })
|
|
158
|
+
options.domAccess = await askList(`Will it have access to DOM?`, ["Yes", "No"], { defaultIndex: 0, returnType: "index" })
|
|
159
|
+
options.svgReport = await askList(`Export SVG size report on build for README.md?`, ["Yes", "No"], { defaultIndex: 0, returnType: "index" })
|
|
160
|
+
options.jsonReport = await askList(`Export JSON size report on build?`, ["Yes", "No"], { defaultIndex: 1, returnType: "index" })
|
|
161
|
+
options.domAccess = options.domAccess === 0
|
|
162
|
+
options.tsStrict = options.tsStrict === 0
|
|
163
|
+
options.svgReport = options.svgReport === 0
|
|
164
|
+
options.jsonReport = options.jsonReport === 0
|
|
165
|
+
options.libs = [options.domAccess ? "DOM" : "", options.esLevel]
|
|
166
|
+
.filter( Boolean )
|
|
167
|
+
.map( s => `"${s}"`)
|
|
168
|
+
options.tspVersion = getTSPPackageJson().version
|
|
169
|
+
// Generate package json
|
|
170
|
+
const packageJson = {
|
|
171
|
+
name: options.packageNPMName,
|
|
172
|
+
version: "0.1.0",
|
|
173
|
+
type: "module",
|
|
174
|
+
author: options.authorName,
|
|
175
|
+
licence: options.licenceName,
|
|
176
|
+
main: "./dist/index.js",
|
|
177
|
+
types: "./dist/index.d.ts",
|
|
178
|
+
exports: {
|
|
179
|
+
".": {
|
|
180
|
+
types: "./dist/index.d.ts",
|
|
181
|
+
default: "./dist/index.js"
|
|
182
|
+
},
|
|
183
|
+
"./submodule": {
|
|
184
|
+
types: "./dist/submodule/index.d.ts",
|
|
185
|
+
default: "./dist/submodule/index.js"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
tsp: {
|
|
189
|
+
runtime: "node",
|
|
190
|
+
src: './src',
|
|
191
|
+
dist: './dist',
|
|
192
|
+
tests: './tests',
|
|
193
|
+
"test-files": ['test.js'],
|
|
194
|
+
tmp: './tmp',
|
|
195
|
+
reports: './reports',
|
|
196
|
+
"generate-json-report": options.jsonReport,
|
|
197
|
+
"generate-svg-report": options.svgReport
|
|
198
|
+
},
|
|
199
|
+
scripts: {
|
|
200
|
+
build: "tsp build",
|
|
201
|
+
test: "tsp build --noSizeReport && tsp test --noIntro",
|
|
202
|
+
publish: "tsp build && tsp test --noIntro && tsp publish --noIntro"
|
|
203
|
+
},
|
|
204
|
+
dependencies: {
|
|
205
|
+
"@reflex-stack/tsp": options.tspVersion
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Inject git remote
|
|
209
|
+
if ( options.remoteURL ) {
|
|
210
|
+
packageJson.repository = {
|
|
211
|
+
type: "git",
|
|
212
|
+
url: options.remoteURL
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// Create directories
|
|
216
|
+
mkdirSync(config.tests, { recursive: true })
|
|
217
|
+
mkdirSync(config.dist, { recursive: true })
|
|
218
|
+
mkdirSync(join(config.src, "submodule"), { recursive: true })
|
|
219
|
+
// Generate root files
|
|
220
|
+
if ( options.licenceName === "MIT" )
|
|
221
|
+
writeFileSync("LICENCE", Stach(licenceTemplate, options))
|
|
222
|
+
writeFileSync("README.md", Stach(readmeTemplate, options))
|
|
223
|
+
writeFileSync(".gitignore", Stach(gitIgnoreTemplate, options))
|
|
224
|
+
writeFileSync(".npmignore", Stach(npmIgnoreTemplate, options))
|
|
225
|
+
writeFileSync("tsconfig.json", Stach(tsconfigTemplate, options))
|
|
226
|
+
writeFileSync("package.json", JSON.stringify(packageJson, null, 2))
|
|
227
|
+
// Generate src files
|
|
228
|
+
writeFileSync(join(config.src, "index.ts"), Stach(rootIndexTs, options))
|
|
229
|
+
writeFileSync(join(config.src, "submodule", "index.ts"), Stach(subModuleIndexTs, options))
|
|
230
|
+
// Generate test file
|
|
231
|
+
writeFileSync(join(config.tests, "test.js"), Stach(testFile, options))
|
|
232
|
+
// Install dependencies
|
|
233
|
+
await oraTask("Installing dependencies", async () => {
|
|
234
|
+
await execAsync(`npm i typescript terser`, false, { cwd: config.cwd })
|
|
235
|
+
})
|
|
236
|
+
// Show commands
|
|
237
|
+
newLine()
|
|
238
|
+
nicePrint(`{b/g}Package ${options.packageNPMName} created ✨`)
|
|
239
|
+
newLine()
|
|
240
|
+
nicePrint(`Available commands :`)
|
|
241
|
+
nicePrint(`- npm run build`)
|
|
242
|
+
nicePrint(`- npm run test`)
|
|
243
|
+
nicePrint(`- npm run publish`)
|
|
244
|
+
newLine()
|
|
245
|
+
}
|