@gozenc/packer 1.0.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/LICENSE +21 -0
- package/README.md +243 -0
- package/bin/packer.js +120 -0
- package/index.js +0 -0
- package/lib/bump.js +170 -0
- package/lib/config.js +46 -0
- package/lib/docs.js +42 -0
- package/lib/preview.js +82 -0
- package/lib/verify.js +119 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Fatih GΓΆzenΓ§
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# @gozenc/packer
|
|
2
|
+
|
|
3
|
+
A CLI tool with essential package builder scripts for React library projects. Streamline your React library development workflow with version management, documentation syncing, and package testing utilities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- π’ **Version Bumping**: Interactive or automated version updates across package files
|
|
8
|
+
- π **Documentation Sync**: Automatically sync built documentation to GitHub Pages
|
|
9
|
+
- π§ͺ **Package Testing**: Verify package structure and test locally before publishing
|
|
10
|
+
- π **Browser Testing**: Local development server for testing built packages
|
|
11
|
+
- β‘ **Zero Config**: Works out of the box with standard React library setups
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Install globally to use across all your projects:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @gozenc/packer
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install as a dev dependency in your project:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install --save-dev @gozenc/packer
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### Global Installation
|
|
30
|
+
|
|
31
|
+
If installed globally, use the `packer` command directly:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
packer <command> [options]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Local Installation
|
|
38
|
+
|
|
39
|
+
If installed locally, add scripts to your `package.json`:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"scripts": {
|
|
44
|
+
"bump": "packer bump",
|
|
45
|
+
"docs": "packer docs",
|
|
46
|
+
"verify": "packer verify",
|
|
47
|
+
"preview": "packer preview"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Then run with npm:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm run bump
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Commands
|
|
59
|
+
|
|
60
|
+
### `packer bump [version]`
|
|
61
|
+
|
|
62
|
+
Bump the package version across all relevant files.
|
|
63
|
+
|
|
64
|
+
**Interactive mode** (prompts for version):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
packer bump
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Direct version** (specify version):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
packer bump 1.2.3
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Updates:
|
|
77
|
+
|
|
78
|
+
- `package.json`
|
|
79
|
+
- `package-lock.json`
|
|
80
|
+
- `docs.html` (softwareVersion in JSON-LD, if exists)
|
|
81
|
+
- `docs/index.html` (softwareVersion in JSON-LD, if exists)
|
|
82
|
+
|
|
83
|
+
### `packer docs`
|
|
84
|
+
|
|
85
|
+
Sync documentation from `docs.html` to `docs/` directory for GitHub Pages or Cloudflare Pages.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
packer docs
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This command:
|
|
92
|
+
|
|
93
|
+
- Copies `docs.html` β `docs/index.html`
|
|
94
|
+
- Copies `dist/` β `docs/dist/`
|
|
95
|
+
|
|
96
|
+
**Configuration Options:**
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
packer docs --dist=build --docs-dir=public --docs-file=index.html
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Available parameters:
|
|
103
|
+
|
|
104
|
+
- `--dist` - Build directory (default: `dist`)
|
|
105
|
+
- `--docs-dir` - Documentation output directory (default: `docs`)
|
|
106
|
+
- `--docs-file` - Source HTML file (default: `docs.html`)
|
|
107
|
+
|
|
108
|
+
**Note**: The `docs/` folder should be added to `.gitignore` to avoid committing generated files. Only `docs.html` (the source file) should be committed to your repository.
|
|
109
|
+
|
|
110
|
+
### `packer verify`
|
|
111
|
+
|
|
112
|
+
Comprehensive package verification before publishing. This command builds, packs, and thoroughly tests your package to ensure it's ready for npm.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
packer verify
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
This command:
|
|
119
|
+
|
|
120
|
+
1. Builds the package (`npm run build`)
|
|
121
|
+
2. Packs it (`npm pack`)
|
|
122
|
+
3. Extracts and verifies the tarball structure
|
|
123
|
+
4. Checks for required files (package.json, README.md, LICENSE, dist files)
|
|
124
|
+
5. Analyzes bundle size
|
|
125
|
+
6. Verifies no `react-jsx-runtime` is bundled (critical for React libraries)
|
|
126
|
+
7. Cleans up temporary files
|
|
127
|
+
|
|
128
|
+
**Configuration Options:**
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
packer verify --dist=build
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Available parameters:
|
|
135
|
+
|
|
136
|
+
- `--dist` - Build directory to verify (default: `dist`)
|
|
137
|
+
|
|
138
|
+
This is the most thorough test and should be run before publishing to npm.
|
|
139
|
+
|
|
140
|
+
### `packer preview`
|
|
141
|
+
|
|
142
|
+
Start a local HTTP server to preview your documentation in a browser.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
packer preview
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Configuration Options:**
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
packer preview --host=localhost --port=3000 --docs-dir=public
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Available parameters:
|
|
155
|
+
|
|
156
|
+
- `--host` - Server host (default: `127.0.0.1`)
|
|
157
|
+
- `--port` - Server port (default: `8080`)
|
|
158
|
+
- `--docs-dir` - Documentation directory to serve (default: `docs`)
|
|
159
|
+
|
|
160
|
+
Default behavior:
|
|
161
|
+
|
|
162
|
+
- Serves files from project root
|
|
163
|
+
- Default URL: `http://127.0.0.1:8080/`
|
|
164
|
+
- Serves `docs/index.html` at root path
|
|
165
|
+
- Press `Ctrl+C` to stop the server
|
|
166
|
+
|
|
167
|
+
## Project Structure
|
|
168
|
+
|
|
169
|
+
This tool expects your React library project to follow this structure:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
your-project/
|
|
173
|
+
βββ dist/ # Built package output (gitignored)
|
|
174
|
+
βββ docs/ # Generated docs for Pages hosting (gitignored)
|
|
175
|
+
βββ docs.html # Source HTML file for documentation
|
|
176
|
+
βββ src/ # Source files
|
|
177
|
+
βββ package.json
|
|
178
|
+
βββ package-lock.json
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Important**: Add the following to your `.gitignore`:
|
|
182
|
+
|
|
183
|
+
```gitignore
|
|
184
|
+
# Build outputs
|
|
185
|
+
dist/
|
|
186
|
+
|
|
187
|
+
# Docs folder (generated from docs.html)
|
|
188
|
+
docs/
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
This keeps your repository clean by only tracking the source `docs.html` file, not the generated `docs/` folder.
|
|
192
|
+
|
|
193
|
+
## Typical Workflow
|
|
194
|
+
|
|
195
|
+
Here's a typical release workflow using packer:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# 1. Verify package (builds, packs, and tests)
|
|
199
|
+
packer verify
|
|
200
|
+
|
|
201
|
+
# 2. Preview docs in browser (optional)
|
|
202
|
+
packer preview
|
|
203
|
+
|
|
204
|
+
# 3. Sync documentation
|
|
205
|
+
packer docs
|
|
206
|
+
|
|
207
|
+
# 4. Bump version
|
|
208
|
+
packer bump
|
|
209
|
+
|
|
210
|
+
# 5. Publish to npm
|
|
211
|
+
npm publish
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Or combine steps in a release script:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"scripts": {
|
|
219
|
+
"release": "packer verify && packer docs && packer bump && npm publish"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Requirements
|
|
225
|
+
|
|
226
|
+
- Node.js >= 18.0.0
|
|
227
|
+
- npm or yarn
|
|
228
|
+
- Standard React library build setup (TypeScript + Vite recommended)
|
|
229
|
+
|
|
230
|
+
## Examples
|
|
231
|
+
|
|
232
|
+
See these projects using `@gozenc/packer`:
|
|
233
|
+
|
|
234
|
+
- [@gozenc/react-tooltip](https://github.com/gozenc/react-tooltip)
|
|
235
|
+
- [@gozenc/react-dark-mode-toggle](https://github.com/gozenc/react-dark-mode-toggle)
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT Β© Fatih GΓΆzenΓ§
|
|
240
|
+
|
|
241
|
+
## Contributing
|
|
242
|
+
|
|
243
|
+
Issues and pull requests are welcome! Please visit the [GitHub repository](https://github.com/gozenc/package-builder).
|
package/bin/packer.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { readFile } from "fs/promises";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
const commands = {
|
|
11
|
+
bump: {
|
|
12
|
+
description: "Bump package version",
|
|
13
|
+
file: "bump.js",
|
|
14
|
+
},
|
|
15
|
+
docs: {
|
|
16
|
+
description: "Sync docs from docs.html to docs/",
|
|
17
|
+
file: "docs.js",
|
|
18
|
+
},
|
|
19
|
+
verify: {
|
|
20
|
+
description: "Verify package before publishing (build, pack, and test)",
|
|
21
|
+
file: "verify.js",
|
|
22
|
+
},
|
|
23
|
+
preview: {
|
|
24
|
+
description: "Start a local server to preview docs in browser",
|
|
25
|
+
file: "preview.js",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
async function showHelp() {
|
|
30
|
+
const packageJson = JSON.parse(
|
|
31
|
+
await readFile(join(__dirname, "..", "package.json"), "utf8")
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
console.log(`
|
|
35
|
+
${packageJson.name} v${packageJson.version}
|
|
36
|
+
${packageJson.description}
|
|
37
|
+
|
|
38
|
+
Usage: packer <command> [options]
|
|
39
|
+
|
|
40
|
+
Commands:
|
|
41
|
+
${Object.entries(commands)
|
|
42
|
+
.map(([cmd, { description }]) => ` ${cmd.padEnd(20)} ${description}`)
|
|
43
|
+
.join("\n")}
|
|
44
|
+
|
|
45
|
+
help Show this help message
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
packer bump # Interactive version bump
|
|
49
|
+
packer bump 1.2.3 # Bump to specific version
|
|
50
|
+
packer docs # Sync documentation
|
|
51
|
+
packer docs --dist=<dir> --docs-dir=<dir>
|
|
52
|
+
packer verify # Verify package before publishing
|
|
53
|
+
packer verify --dist=<dir>
|
|
54
|
+
packer preview # Preview docs in browser
|
|
55
|
+
packer preview --host=<host> --port=<port>
|
|
56
|
+
|
|
57
|
+
Configuration Options:
|
|
58
|
+
--dist=<dir> Build directory (default: dist)
|
|
59
|
+
--host=<host> Server host (default: 127.0.0.1)
|
|
60
|
+
--port=<port> Server port (default: 8080)
|
|
61
|
+
--docs-dir=<dir> Documentation directory (default: docs)
|
|
62
|
+
--docs-file=<file> Source HTML file (default: docs.html)
|
|
63
|
+
|
|
64
|
+
For more information, visit: ${
|
|
65
|
+
packageJson.repository?.url || packageJson.homepage || ""
|
|
66
|
+
}
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function main() {
|
|
71
|
+
const args = process.argv.slice(2);
|
|
72
|
+
const command = args[0];
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
!command ||
|
|
76
|
+
command === "help" ||
|
|
77
|
+
command === "--help" ||
|
|
78
|
+
command === "-h"
|
|
79
|
+
) {
|
|
80
|
+
await showHelp();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (command === "version" || command === "--version" || command === "-v") {
|
|
85
|
+
const packageJson = JSON.parse(
|
|
86
|
+
await readFile(join(__dirname, "..", "package.json"), "utf8")
|
|
87
|
+
);
|
|
88
|
+
console.log(packageJson.version);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const commandConfig = commands[command];
|
|
93
|
+
|
|
94
|
+
if (!commandConfig) {
|
|
95
|
+
console.error(`β Unknown command: ${command}`);
|
|
96
|
+
console.log(`Run 'packer help' to see available commands.`);
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const commandPath = join(__dirname, "..", "lib", commandConfig.file);
|
|
103
|
+
const commandModule = await import(commandPath);
|
|
104
|
+
|
|
105
|
+
// If the module has a default export, call it with remaining args
|
|
106
|
+
if (commandModule.default) {
|
|
107
|
+
// Pass remaining arguments (after the command) to the module
|
|
108
|
+
const commandArgs = args.slice(1);
|
|
109
|
+
await commandModule.default(...commandArgs);
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error(`β Failed to execute command '${command}':`, error.message);
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
main().catch((error) => {
|
|
118
|
+
console.error("β Unexpected error:", error);
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
});
|
package/index.js
ADDED
|
File without changes
|
package/lib/bump.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFile, writeFile } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import readline from "readline";
|
|
6
|
+
|
|
7
|
+
// Use process.cwd() to get the project directory where the command is run
|
|
8
|
+
const projectDir = process.cwd();
|
|
9
|
+
|
|
10
|
+
function bumpPatch(version) {
|
|
11
|
+
const [major, minor, patch = "0"] = version.split(".");
|
|
12
|
+
const numericPatch = Number.parseInt(patch, 10);
|
|
13
|
+
if (Number.isNaN(numericPatch)) {
|
|
14
|
+
return version;
|
|
15
|
+
}
|
|
16
|
+
return `${major}.${minor}.${numericPatch + 1}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isValidVersion(value) {
|
|
20
|
+
return /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:[-+][0-9A-Za-z-.]+)?$/.test(
|
|
21
|
+
value
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function prompt(question, defaultValue) {
|
|
26
|
+
const rl = readline.createInterface({
|
|
27
|
+
input: process.stdin,
|
|
28
|
+
output: process.stdout,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const fullQuestion = defaultValue
|
|
33
|
+
? `${question} (${defaultValue}): `
|
|
34
|
+
: `${question}: `;
|
|
35
|
+
rl.question(fullQuestion, (answer) => {
|
|
36
|
+
rl.close();
|
|
37
|
+
const value = answer.trim();
|
|
38
|
+
resolve(value || defaultValue || "");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function updateJSONFile(relativePath, updater) {
|
|
44
|
+
const filePath = join(projectDir, relativePath);
|
|
45
|
+
const original = await readFile(filePath, "utf8");
|
|
46
|
+
const data = JSON.parse(original);
|
|
47
|
+
const updated = await updater(data);
|
|
48
|
+
await writeFile(filePath, `${JSON.stringify(updated, null, 2)}\n`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function updateTextFile(relativePath, replacements) {
|
|
52
|
+
const filePath = join(projectDir, relativePath);
|
|
53
|
+
const original = await readFile(filePath, "utf8");
|
|
54
|
+
let updated = original;
|
|
55
|
+
|
|
56
|
+
for (const { from, to, description } of replacements) {
|
|
57
|
+
const next = updated.replace(from, to);
|
|
58
|
+
if (next === updated) {
|
|
59
|
+
console.warn(`β οΈ No match found for ${description} in ${relativePath}`);
|
|
60
|
+
}
|
|
61
|
+
updated = next;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (updated !== original) {
|
|
65
|
+
await writeFile(filePath, updated);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function main(versionArg) {
|
|
70
|
+
console.log("π Reading package metadata...");
|
|
71
|
+
|
|
72
|
+
const packageJsonPath = join(projectDir, "package.json");
|
|
73
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
74
|
+
|
|
75
|
+
const currentVersion = packageJson.version;
|
|
76
|
+
const suggestedVersion = bumpPatch(currentVersion);
|
|
77
|
+
|
|
78
|
+
let targetVersion = versionArg?.trim();
|
|
79
|
+
|
|
80
|
+
if (!targetVersion) {
|
|
81
|
+
targetVersion = await prompt(
|
|
82
|
+
`Current version is ${currentVersion}. Enter new version`,
|
|
83
|
+
suggestedVersion
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!targetVersion) {
|
|
88
|
+
console.error("β No version provided. Aborting.");
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!isValidVersion(targetVersion)) {
|
|
94
|
+
console.error(`β ${targetVersion} is not a valid semver string.`);
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (targetVersion === currentVersion) {
|
|
100
|
+
console.error("β New version matches the current version. Nothing to do.");
|
|
101
|
+
process.exitCode = 1;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log(`π Updating version to ${targetVersion}...`);
|
|
106
|
+
|
|
107
|
+
await updateJSONFile("package.json", async (data) => ({
|
|
108
|
+
...data,
|
|
109
|
+
version: targetVersion,
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
await updateJSONFile("package-lock.json", async (data) => {
|
|
113
|
+
const updated = { ...data, version: targetVersion };
|
|
114
|
+
if (updated.packages?.[""]) {
|
|
115
|
+
updated.packages[""] = {
|
|
116
|
+
...updated.packages[""],
|
|
117
|
+
version: targetVersion,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return updated;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const softwareVersionFrom = `"softwareVersion": "${currentVersion}"`;
|
|
124
|
+
const softwareVersionTo = `"softwareVersion": "${targetVersion}"`;
|
|
125
|
+
|
|
126
|
+
// Try to update docs.html if it exists
|
|
127
|
+
try {
|
|
128
|
+
await updateTextFile("docs.html", [
|
|
129
|
+
{
|
|
130
|
+
from: softwareVersionFrom,
|
|
131
|
+
to: softwareVersionTo,
|
|
132
|
+
description: "softwareVersion JSON-LD entry",
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.warn("β οΈ docs.html not found.");
|
|
137
|
+
// File doesn't exist or doesn't have softwareVersion, skip silently
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Try to update docs/index.html if it exists
|
|
141
|
+
try {
|
|
142
|
+
await updateTextFile("docs/index.html", [
|
|
143
|
+
{
|
|
144
|
+
from: softwareVersionFrom,
|
|
145
|
+
to: softwareVersionTo,
|
|
146
|
+
description: "softwareVersion JSON-LD entry",
|
|
147
|
+
},
|
|
148
|
+
]);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.warn("β οΈ docs/index.html not found.");
|
|
151
|
+
// File doesn't exist or doesn't have softwareVersion, skip silently
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log("β
Version bump complete.");
|
|
155
|
+
console.log("π’ Previous version:", currentVersion);
|
|
156
|
+
console.log("β¨ New version:", targetVersion);
|
|
157
|
+
console.log(
|
|
158
|
+
"π¦ Remember to update the changelog and run npm publish when ready."
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Run if called directly
|
|
163
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
164
|
+
main().catch((error) => {
|
|
165
|
+
console.error("β Failed to bump version:", error);
|
|
166
|
+
process.exitCode = 1;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default main;
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse command-line arguments and return configuration
|
|
3
|
+
*/
|
|
4
|
+
export function parseArgs(args = []) {
|
|
5
|
+
const config = {
|
|
6
|
+
// Default values
|
|
7
|
+
dist: "dist",
|
|
8
|
+
host: "127.0.0.1",
|
|
9
|
+
port: 8080,
|
|
10
|
+
docsDir: "docs",
|
|
11
|
+
docsFile: "docs.html",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
for (const arg of args) {
|
|
15
|
+
if (arg.startsWith("--")) {
|
|
16
|
+
const [key, value] = arg.slice(2).split("=");
|
|
17
|
+
|
|
18
|
+
// Convert kebab-case to camelCase
|
|
19
|
+
const camelKey = key.replace(/-([a-z])/g, (_, letter) =>
|
|
20
|
+
letter.toUpperCase()
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Parse port as number
|
|
24
|
+
if (camelKey === "port") {
|
|
25
|
+
config[camelKey] = parseInt(value, 10);
|
|
26
|
+
} else if (value !== undefined) {
|
|
27
|
+
config[camelKey] = value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get default configuration
|
|
37
|
+
*/
|
|
38
|
+
export function getDefaults() {
|
|
39
|
+
return {
|
|
40
|
+
dist: "dist",
|
|
41
|
+
host: "127.0.0.1",
|
|
42
|
+
port: 8080,
|
|
43
|
+
docsDir: "docs",
|
|
44
|
+
docsFile: "docs.html",
|
|
45
|
+
};
|
|
46
|
+
}
|
package/lib/docs.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, readFile, writeFile, rm, cp } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { parseArgs } from "./config.js";
|
|
5
|
+
|
|
6
|
+
async function run(...args) {
|
|
7
|
+
// Parse configuration from arguments
|
|
8
|
+
const config = parseArgs(args);
|
|
9
|
+
|
|
10
|
+
// Use process.cwd() to get the project directory where the command is run
|
|
11
|
+
const projectDir = process.cwd();
|
|
12
|
+
const htmlSource = path.join(projectDir, config.docsFile);
|
|
13
|
+
const distSource = path.join(projectDir, config.dist);
|
|
14
|
+
const docsDir = path.join(projectDir, config.docsDir);
|
|
15
|
+
const docsDist = path.join(docsDir, config.dist);
|
|
16
|
+
const docsIndex = path.join(docsDir, "index.html");
|
|
17
|
+
|
|
18
|
+
// Create docs directory
|
|
19
|
+
await mkdir(docsDir, { recursive: true });
|
|
20
|
+
|
|
21
|
+
// Copy docs file to docs/index.html
|
|
22
|
+
const html = await readFile(htmlSource, "utf8");
|
|
23
|
+
await writeFile(docsIndex, html, "utf8");
|
|
24
|
+
|
|
25
|
+
// Remove old dist folder in docs and copy fresh one
|
|
26
|
+
await rm(docsDist, { recursive: true, force: true });
|
|
27
|
+
await cp(distSource, docsDist, { recursive: true });
|
|
28
|
+
|
|
29
|
+
console.log("β
Docs synced successfully!");
|
|
30
|
+
console.log(` ${config.docsFile} -> ${config.docsDir}/index.html`);
|
|
31
|
+
console.log(` ${config.dist}/ -> ${config.docsDir}/${config.dist}/`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Run if called directly
|
|
35
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
36
|
+
run().catch((error) => {
|
|
37
|
+
console.error("Failed to sync docs:", error);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default run;
|
package/lib/preview.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple Node.js static file server for testing
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createServer } from "http";
|
|
8
|
+
import { readFile, stat } from "fs/promises";
|
|
9
|
+
import { join, extname } from "path";
|
|
10
|
+
import { parseArgs } from "./config.js";
|
|
11
|
+
|
|
12
|
+
const mimeTypes = {
|
|
13
|
+
".html": "text/html",
|
|
14
|
+
".js": "application/javascript",
|
|
15
|
+
".css": "text/css",
|
|
16
|
+
".json": "application/json",
|
|
17
|
+
".png": "image/png",
|
|
18
|
+
".jpg": "image/jpeg",
|
|
19
|
+
".gif": "image/gif",
|
|
20
|
+
".svg": "image/svg+xml",
|
|
21
|
+
".ico": "image/x-icon",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function startServer(...args) {
|
|
25
|
+
// Parse configuration from arguments
|
|
26
|
+
const config = parseArgs(args);
|
|
27
|
+
|
|
28
|
+
// Use process.cwd() to get the project directory where the command is run
|
|
29
|
+
const projectDir = process.cwd();
|
|
30
|
+
|
|
31
|
+
const server = createServer(async (req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
let filePath =
|
|
34
|
+
req.url === "/" ? `/${config.docsDir}/index.html` : req.url;
|
|
35
|
+
filePath = join(projectDir, filePath);
|
|
36
|
+
|
|
37
|
+
const stats = await stat(filePath);
|
|
38
|
+
|
|
39
|
+
if (stats.isFile()) {
|
|
40
|
+
const ext = extname(filePath);
|
|
41
|
+
const contentType = mimeTypes[ext] || "application/octet-stream";
|
|
42
|
+
|
|
43
|
+
res.writeHead(200, {
|
|
44
|
+
"Content-Type": contentType,
|
|
45
|
+
"Access-Control-Allow-Origin": "*",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const content = await readFile(filePath);
|
|
49
|
+
res.end(content);
|
|
50
|
+
} else {
|
|
51
|
+
res.writeHead(404);
|
|
52
|
+
res.end("File not found");
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
res.writeHead(404);
|
|
56
|
+
res.end("File not found");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
server.listen(config.port, config.host, () => {
|
|
61
|
+
console.log(`π Preview at http://${config.host}:${config.port}/`);
|
|
62
|
+
console.log(`π Serving from: ${projectDir}`);
|
|
63
|
+
console.log(`π Root: ${config.docsDir}/index.html`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Handle graceful shutdown
|
|
67
|
+
process.on("SIGINT", () => {
|
|
68
|
+
console.log("\nπ Shutting down server...");
|
|
69
|
+
server.close(() => {
|
|
70
|
+
process.exit(0);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return server;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Run if called directly
|
|
78
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
79
|
+
startServer();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default startServer;
|
package/lib/verify.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test the built package locally using npm pack
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { readFileSync, existsSync, mkdirSync, rmSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { parseArgs } from "./config.js";
|
|
11
|
+
|
|
12
|
+
async function testLocal(...args) {
|
|
13
|
+
// Parse configuration from arguments
|
|
14
|
+
const config = parseArgs(args);
|
|
15
|
+
|
|
16
|
+
// Use process.cwd() to get the project directory where the command is run
|
|
17
|
+
const projectDir = process.cwd();
|
|
18
|
+
|
|
19
|
+
console.log("π§ͺ Testing built package locally...\n");
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// 1. Build the package
|
|
23
|
+
console.log("π¦ Building package...");
|
|
24
|
+
execSync("npm run build", { stdio: "inherit", cwd: projectDir });
|
|
25
|
+
|
|
26
|
+
// 2. Pack the package
|
|
27
|
+
console.log("\nπ¦ Packing package...");
|
|
28
|
+
const packResult = execSync("npm pack", {
|
|
29
|
+
encoding: "utf8",
|
|
30
|
+
cwd: projectDir,
|
|
31
|
+
});
|
|
32
|
+
const tarballName = packResult.trim();
|
|
33
|
+
|
|
34
|
+
// 3. Create test directory
|
|
35
|
+
const testDir = join(projectDir, "test-package-temp");
|
|
36
|
+
if (existsSync(testDir)) {
|
|
37
|
+
rmSync(testDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
mkdirSync(testDir);
|
|
40
|
+
|
|
41
|
+
// 4. Extract and test
|
|
42
|
+
console.log("\nπ Extracting package...");
|
|
43
|
+
execSync(`tar -xzf ${tarballName} -C ${testDir}`, {
|
|
44
|
+
stdio: "inherit",
|
|
45
|
+
cwd: projectDir,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const packageDir = join(testDir, "package");
|
|
49
|
+
|
|
50
|
+
// 5. Verify package structure
|
|
51
|
+
console.log("\nπ Verifying package structure...");
|
|
52
|
+
|
|
53
|
+
const requiredFiles = [
|
|
54
|
+
"package.json",
|
|
55
|
+
"README.md",
|
|
56
|
+
"LICENSE",
|
|
57
|
+
`${config.dist}/index.js`,
|
|
58
|
+
`${config.dist}/index.d.ts`,
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
let allFilesExist = true;
|
|
62
|
+
for (const file of requiredFiles) {
|
|
63
|
+
const filePath = join(packageDir, file);
|
|
64
|
+
if (existsSync(filePath)) {
|
|
65
|
+
console.log(`β
${file}`);
|
|
66
|
+
} else {
|
|
67
|
+
console.log(`β ${file} - MISSING`);
|
|
68
|
+
allFilesExist = false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 6. Check package.json
|
|
73
|
+
const packageJson = JSON.parse(
|
|
74
|
+
readFileSync(join(packageDir, "package.json"), "utf8")
|
|
75
|
+
);
|
|
76
|
+
console.log(`\nπ Package: ${packageJson.name}@${packageJson.version}`);
|
|
77
|
+
console.log(`π Main: ${packageJson.main}`);
|
|
78
|
+
console.log(`π Types: ${packageJson.types}`);
|
|
79
|
+
|
|
80
|
+
// 7. Check bundle size
|
|
81
|
+
const bundleContent = readFileSync(
|
|
82
|
+
join(packageDir, config.dist, "index.js"),
|
|
83
|
+
"utf8"
|
|
84
|
+
);
|
|
85
|
+
const bundleSize = (bundleContent.length / 1024).toFixed(2);
|
|
86
|
+
console.log(`π Bundle size: ${bundleSize} KB`);
|
|
87
|
+
|
|
88
|
+
// 8. Check for react-jsx-runtime
|
|
89
|
+
const hasJsxRuntime = bundleContent.includes("react-jsx-runtime");
|
|
90
|
+
console.log(
|
|
91
|
+
`π Contains react-jsx-runtime: ${hasJsxRuntime ? "β YES" : "β
NO"}`
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// 9. Cleanup
|
|
95
|
+
console.log("\nπ§Ή Cleaning up...");
|
|
96
|
+
rmSync(testDir, { recursive: true });
|
|
97
|
+
rmSync(tarballName);
|
|
98
|
+
|
|
99
|
+
if (allFilesExist && !hasJsxRuntime) {
|
|
100
|
+
console.log("\nπ Package test PASSED! Ready for publishing.");
|
|
101
|
+
console.log("\nNext steps:");
|
|
102
|
+
console.log("1. npm login");
|
|
103
|
+
console.log("2. npm publish");
|
|
104
|
+
} else {
|
|
105
|
+
console.log("\nβ Package test FAILED. Please fix the issues above.");
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("β Test failed:", error.message);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Run if called directly
|
|
115
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
116
|
+
testLocal();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default testLocal;
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gozenc/packer",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Collection of package builder scripts for React libraries.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"packer": "./bin/packer.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"lib",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"npm",
|
|
24
|
+
"packer",
|
|
25
|
+
"package",
|
|
26
|
+
"builder",
|
|
27
|
+
"react",
|
|
28
|
+
"library",
|
|
29
|
+
"node",
|
|
30
|
+
"cli",
|
|
31
|
+
"tools",
|
|
32
|
+
"version",
|
|
33
|
+
"bump",
|
|
34
|
+
"docs",
|
|
35
|
+
"testing"
|
|
36
|
+
],
|
|
37
|
+
"author": "Fatih GΓΆzenΓ§",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/gozenc/packer.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/gozenc/packer/issues"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|