create-lib-workspace 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/README.md +118 -0
- package/index.js +217 -0
- package/package.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# create-lib-workspace
|
|
2
|
+
|
|
3
|
+
A lightweight CLI tool to instantly scaffold a local development monorepo workspace for custom JavaScript libraries built with Vite.
|
|
4
|
+
|
|
5
|
+
It automatically sets up a multi-entry library with proper ESM/CJS exports and links it to a static test application using your favorite package manager—completely dependency-free.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
You can install this tool globally to make it available anywhere on your system, or run it directly without installation.
|
|
10
|
+
|
|
11
|
+
### 1. Global Installation (Recommended)
|
|
12
|
+
|
|
13
|
+
Install the CLI globally using your preferred package manager:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Using pnpm
|
|
17
|
+
pnpm add -g create-lib-workspace
|
|
18
|
+
|
|
19
|
+
# Using npm
|
|
20
|
+
npm install -g create-lib-workspace
|
|
21
|
+
|
|
22
|
+
# Using yarn
|
|
23
|
+
yarn global add create-lib-workspace
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Run Without Installation
|
|
27
|
+
|
|
28
|
+
If you prefer not to install the package globally, you can run it on the fly using executors:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Using pnpm dlx
|
|
32
|
+
pnpm dlx create-lib-workspace
|
|
33
|
+
|
|
34
|
+
# Using npx
|
|
35
|
+
npx create-lib-workspace
|
|
36
|
+
|
|
37
|
+
# Using yarn dlx
|
|
38
|
+
yarn dlx create-lib-workspace
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
You can run the command completely empty to start the **interactive wizard**, or pre-define arguments inline.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Option A: Interactive Wizard (Recommended)
|
|
47
|
+
create-lib-workspace
|
|
48
|
+
|
|
49
|
+
# Option B: Inline Arguments
|
|
50
|
+
create-lib-workspace <workspace-name> <lib-name> <package-name> [pnpm|npm|yarn]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Arguments (Optional if using Wizard)
|
|
54
|
+
|
|
55
|
+
* **`<workspace-name>`**: The name of the root directory for your project.
|
|
56
|
+
* **`<lib-name>`**: The directory name of the library inside the workspace.
|
|
57
|
+
* **`<package-name>`**: The actual package name for your `package.json` fields (e.g., `@my-scope/my-lib`).
|
|
58
|
+
* **`[package-manager]`**: Force a specific tool (`pnpm`, `npm`, or `yarn`). Defaults to an interactive selection if omitted.
|
|
59
|
+
|
|
60
|
+
### Example (Inline)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
create-lib-workspace my-project core-lib @my-org/core pnpm
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## ⚠️ Important Note During Setup
|
|
67
|
+
|
|
68
|
+
During execution, the script delegates project scaffolding to Vite's official creator tool.
|
|
69
|
+
|
|
70
|
+
1. When prompted by Vite, **select `Vanilla` and `JavaScript`**.
|
|
71
|
+
2. **❌ DO NOT** let Vite install dependencies or run immediately if it prompts you to do so. The script will automatically handle the clean-up, structure generation, and global symlinking right after.
|
|
72
|
+
|
|
73
|
+
## What is generated?
|
|
74
|
+
|
|
75
|
+
The tool scaffolds a clean workspace structured as follows:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
my-project/
|
|
79
|
+
├── app/ # Pure static consumer app
|
|
80
|
+
│ ├── index.html # Test page
|
|
81
|
+
│ └── main.js # Imports and tests your library locally
|
|
82
|
+
└── core-lib/ # Your actual Library (Vite-powered)
|
|
83
|
+
├── src/
|
|
84
|
+
│ ├── index.js # Main entry point
|
|
85
|
+
│ └── modifiers/ # Secondary entry point sub-module
|
|
86
|
+
├── package.json # Configured with ESM/CJS exports
|
|
87
|
+
└── vite.config.js # Pre-configured multi-entry build setup
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Workflow / Local Development
|
|
91
|
+
|
|
92
|
+
Once the setup completes successfully, the library is automatically symlinked to the test app. You can start developing in real-time using two terminal windows:
|
|
93
|
+
|
|
94
|
+
### Terminal 1: Library Watcher
|
|
95
|
+
Recompiles your library automatically on every file change. Replace `<pkg>` with your chosen package manager (`pnpm`, `npm`, or `yarn`).
|
|
96
|
+
```bash
|
|
97
|
+
cd my-project/core-lib
|
|
98
|
+
<pkg> run watch
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Terminal 2: Test App Server
|
|
102
|
+
Serves the consumer application so you can see your library in action.
|
|
103
|
+
```bash
|
|
104
|
+
cd my-project/app
|
|
105
|
+
|
|
106
|
+
# If you used pnpm:
|
|
107
|
+
pnpm dlx vite --open
|
|
108
|
+
|
|
109
|
+
# If you used npm:
|
|
110
|
+
npx vite --open
|
|
111
|
+
|
|
112
|
+
# If you used yarn:
|
|
113
|
+
yarn dlx vite --open
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import readline from 'readline';
|
|
7
|
+
|
|
8
|
+
// Helper to create an interactive terminal prompt using built-in readline
|
|
9
|
+
function askQuestion(query) {
|
|
10
|
+
const rl = readline.createInterface({
|
|
11
|
+
input: process.stdin,
|
|
12
|
+
output: process.stdout,
|
|
13
|
+
});
|
|
14
|
+
return new Promise((resolve) => rl.question(query, (ans) => {
|
|
15
|
+
rl.close();
|
|
16
|
+
resolve(ans.trim());
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
console.log('\n📦 Welcome to create-lib-workspace\n');
|
|
22
|
+
|
|
23
|
+
// Parse inline arguments
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
let workspaceName = args[0];
|
|
26
|
+
let libName = args[1];
|
|
27
|
+
let packageName = args[2];
|
|
28
|
+
let pkgManager = args[3];
|
|
29
|
+
|
|
30
|
+
// Fallback to interactive prompts if arguments are missing
|
|
31
|
+
if (!workspaceName) {
|
|
32
|
+
workspaceName = await askQuestion('Enter the workspace directory name (e.g. my-workspace): ');
|
|
33
|
+
if (!workspaceName) { console.log('❌ Workspace name is required!'); process.exit(1); }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!libName) {
|
|
37
|
+
libName = await askQuestion('Enter the library directory name (e.g. core-lib): ');
|
|
38
|
+
if (!libName) { console.log('❌ Library name is required!'); process.exit(1); }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!packageName) {
|
|
42
|
+
packageName = await askQuestion('Enter the npm package name (e.g. @my-org/core): ');
|
|
43
|
+
if (!packageName) { console.log('❌ Package name is required!'); process.exit(1); }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!pkgManager) {
|
|
47
|
+
console.log('\nSelect package manager:\n1) pnpm (Default)\n2) npm\n3) yarn');
|
|
48
|
+
const choice = await askQuestion('Enter number [1-3]: ');
|
|
49
|
+
if (choice === '2') pkgManager = 'npm';
|
|
50
|
+
else if (choice === '3') pkgManager = 'yarn';
|
|
51
|
+
else pkgManager = 'pnpm';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const workspacePath = path.resolve(workspaceName);
|
|
55
|
+
const appPath = path.join(workspacePath, 'app');
|
|
56
|
+
const libPath = path.join(workspacePath, libName);
|
|
57
|
+
|
|
58
|
+
// 1. Create Workspace & App Structure
|
|
59
|
+
console.log('\n[1/5] Creating workspace and consumer application...');
|
|
60
|
+
fs.mkdirSync(appPath, { recursive: true });
|
|
61
|
+
|
|
62
|
+
const htmlContent = `<!doctype html>
|
|
63
|
+
<html>
|
|
64
|
+
<head>
|
|
65
|
+
<meta charset="UTF-8" />
|
|
66
|
+
<title>Test App</title>
|
|
67
|
+
</head>
|
|
68
|
+
<body>
|
|
69
|
+
<div id="app"></div>
|
|
70
|
+
<script type="module" src="./main.js"></script>
|
|
71
|
+
</body>
|
|
72
|
+
</html>`;
|
|
73
|
+
|
|
74
|
+
const mainJsContent = `import { pluck } from "${packageName}";
|
|
75
|
+
import { increaseAllOf, lowerCaseAllOf } from "${packageName}/modifiers";
|
|
76
|
+
|
|
77
|
+
const users = [
|
|
78
|
+
{ name: 'A', age: 22 },
|
|
79
|
+
{ name: 'B', age: 22 },
|
|
80
|
+
{ name: 'C', age: 22 }
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
console.log("Plucked Names:", pluck(users, 'name'));
|
|
84
|
+
console.log("Increased Age:", increaseAllOf(users, 'age'));
|
|
85
|
+
console.log("Lowercased Names:", lowerCaseAllOf(users, 'name'));`;
|
|
86
|
+
|
|
87
|
+
fs.writeFileSync(path.join(appPath, 'index.html'), htmlContent);
|
|
88
|
+
fs.writeFileSync(path.join(appPath, 'main.js'), mainJsContent);
|
|
89
|
+
|
|
90
|
+
// 2. Run Vite Scaffolding
|
|
91
|
+
console.log('\n---------------------------------------------------------');
|
|
92
|
+
console.log('⚠️ ATTENTION / IMPORTANT NOTE:');
|
|
93
|
+
console.log('Vite will now prompt you to target a framework and a variant.');
|
|
94
|
+
console.log('1. Please SELECT "Vanilla" and "JavaScript".');
|
|
95
|
+
console.log('2. ❌ DO NOT let it install dependencies or run immediately!');
|
|
96
|
+
console.log('---------------------------------------------------------\n');
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
execSync(`${pkgManager} create vite ${libName} --template vanilla`, {
|
|
100
|
+
cwd: workspacePath,
|
|
101
|
+
stdio: 'inherit',
|
|
102
|
+
});
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.log('❌ Vite scaffolding failed or was aborted.');
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 3. Clean up Vite Boilerplate & Generate Library Sources
|
|
109
|
+
console.log('\n[2/5] Polishing library structure and writing source files...');
|
|
110
|
+
fs.rmSync(path.join(libPath, 'src'), { recursive: true, force: true });
|
|
111
|
+
fs.mkdirSync(path.join(libPath, 'src/modifiers'), { recursive: true });
|
|
112
|
+
|
|
113
|
+
// Dynamically fetch the absolute latest Vite version from registry
|
|
114
|
+
let viteVersion = '5.0.0';
|
|
115
|
+
try {
|
|
116
|
+
viteVersion = execSync('npm info vite version', { encoding: 'utf8' }).trim();
|
|
117
|
+
} catch (e) {
|
|
118
|
+
// Graceful fallback if no network
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fs.writeFileSync(path.join(libPath, 'src/index.js'), `export function pluck(collection, field) {\n return collection.map(item => item[field]);\n}`);
|
|
122
|
+
fs.writeFileSync(path.join(libPath, 'src/modifiers/increaseAll.js'), `export function increaseAllOf(collection, field) {\n return collection.map(item => ({\n ...item,\n [field]: item[field] + 1\n }));\n}`);
|
|
123
|
+
fs.writeFileSync(path.join(libPath, 'src/modifiers/lowerCaseAll.js'), `export function lowerCaseAllOf(collection, field) {\n return collection.map(item => ({\n ...item,\n [field]: String(item[field]).toLowerCase()\n }));\n}`);
|
|
124
|
+
fs.writeFileSync(path.join(libPath, 'src/modifiers/index.js'), `import { increaseAllOf } from "./increaseAll.js";\nimport { lowerCaseAllOf } from "./lowerCaseAll.js";\n\nexport { increaseAllOf, lowerCaseAllOf };`);
|
|
125
|
+
|
|
126
|
+
// Build custom package.json
|
|
127
|
+
const libPackageJson = {
|
|
128
|
+
name: packageName,
|
|
129
|
+
version: '0.0.0',
|
|
130
|
+
type: 'module',
|
|
131
|
+
main: './dist/index.cjs',
|
|
132
|
+
module: './dist/index.js',
|
|
133
|
+
exports: {
|
|
134
|
+
'.': { import: './dist/index.js', require: './dist/index.cjs' },
|
|
135
|
+
'./modifiers': { import: './dist/modifiers.js', require: './dist/modifiers.cjs' },
|
|
136
|
+
},
|
|
137
|
+
files: ['dist'],
|
|
138
|
+
scripts: {
|
|
139
|
+
dev: 'vite',
|
|
140
|
+
build: 'vite build',
|
|
141
|
+
watch: 'vite build --watch',
|
|
142
|
+
},
|
|
143
|
+
devDependencies: {
|
|
144
|
+
vite: `^${viteVersion}`,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
fs.writeFileSync(path.join(libPath, 'package.json'), JSON.stringify(libPackageJson, null, 2));
|
|
149
|
+
|
|
150
|
+
// Build custom vite.config.js
|
|
151
|
+
const viteConfigContent = `import { defineConfig } from "vite";
|
|
152
|
+
import { resolve, dirname } from "path";
|
|
153
|
+
import { fileURLToPath } from "url";
|
|
154
|
+
|
|
155
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
156
|
+
|
|
157
|
+
export default defineConfig({
|
|
158
|
+
build: {
|
|
159
|
+
lib: {
|
|
160
|
+
entry: {
|
|
161
|
+
index: resolve(__dirname, "src/index.js"),
|
|
162
|
+
modifiers: resolve(__dirname, "src/modifiers/index.js")
|
|
163
|
+
},
|
|
164
|
+
name: "Lib",
|
|
165
|
+
formats: ["esm", "cjs"],
|
|
166
|
+
fileName: (format, entryName) => {
|
|
167
|
+
const ext = format === "esm" ? "js" : "cjs";
|
|
168
|
+
return \`\${entryName}.\${ext}\`;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});`;
|
|
173
|
+
fs.writeFileSync(path.join(libPath, 'vite.config.js'), viteConfigContent);
|
|
174
|
+
|
|
175
|
+
// 4. Installing and Linking Dependencies
|
|
176
|
+
console.log(`\n[3/5] Installing library dependencies via ${pkgManager}...`);
|
|
177
|
+
execSync(`${pkgManager} install`, { cwd: libPath, stdio: 'inherit' });
|
|
178
|
+
|
|
179
|
+
console.log('\n[4/5] Linking library globally and attaching to the test application...');
|
|
180
|
+
if (pkgManager === 'npm') {
|
|
181
|
+
execSync('npm link', { cwd: libPath, stdio: 'ignore' });
|
|
182
|
+
execSync('npm init -y', { cwd: appPath, stdio: 'ignore' });
|
|
183
|
+
execSync(`npm link ${packageName}`, { cwd: appPath, stdio: 'ignore' });
|
|
184
|
+
} else if (pkgManager === 'yarn') {
|
|
185
|
+
execSync('yarn link --global', { cwd: libPath, stdio: 'ignore' });
|
|
186
|
+
execSync('yarn init -y', { cwd: appPath, stdio: 'ignore' });
|
|
187
|
+
execSync(`yarn link ${packageName}`, { cwd: appPath, stdio: 'ignore' });
|
|
188
|
+
} else {
|
|
189
|
+
execSync('pnpm link --global', { cwd: libPath, stdio: 'ignore' });
|
|
190
|
+
execSync('pnpm init', { cwd: appPath, stdio: 'ignore' });
|
|
191
|
+
execSync(`pnpm link --global ${packageName}`, { cwd: appPath, stdio: 'ignore' });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 5. Initial Library Build
|
|
195
|
+
console.log('\n[5/5] Running initial library build...');
|
|
196
|
+
execSync(`${pkgManager} run build`, { cwd: libPath, stdio: 'inherit' });
|
|
197
|
+
|
|
198
|
+
// Define package executor instructions
|
|
199
|
+
let execCmd = 'pnpm dlx';
|
|
200
|
+
if (pkgManager === 'npm') execCmd = 'npx';
|
|
201
|
+
if (pkgManager === 'yarn') execCmd = 'yarn dlx';
|
|
202
|
+
|
|
203
|
+
console.log('\n=========================================================');
|
|
204
|
+
console.log(' 🎉 Setup successfully completed!');
|
|
205
|
+
console.log('=========================================================');
|
|
206
|
+
console.log(' To get started, open two terminals:\n');
|
|
207
|
+
console.log(' Terminal 1 (Library Watcher):');
|
|
208
|
+
console.log(` cd ${workspaceName}/${libName} && ${pkgManager} run watch\n`);
|
|
209
|
+
console.log(' Terminal 2 (Test App Server):');
|
|
210
|
+
console.log(` cd ${workspaceName}/app && ${execCmd} vite (--open)`);
|
|
211
|
+
console.log('=========================================================\n');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
main().catch((err) => {
|
|
215
|
+
console.error('An unexpected error occurred:', err);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-lib-workspace",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-lib-workspace": "./index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"index.js"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cli",
|
|
15
|
+
"vite",
|
|
16
|
+
"library",
|
|
17
|
+
"workspace",
|
|
18
|
+
"scaffold"
|
|
19
|
+
],
|
|
20
|
+
"author": "BarbWire-1 aka Barbara Kälin"
|
|
21
|
+
|
|
22
|
+
}
|