create-lib-workspace 1.0.0 → 1.1.1

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 (3) hide show
  1. package/README.md +2 -0
  2. package/index.js +219 -149
  3. package/package.json +26 -20
package/README.md CHANGED
@@ -4,6 +4,8 @@ A lightweight CLI tool to instantly scaffold a local development monorepo worksp
4
4
 
5
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
6
 
7
+ ![creation process](workspaceCreation.png)
8
+
7
9
  ## Installation
8
10
 
9
11
  You can install this tool globally to make it available anywhere on your system, or run it directly without installation.
package/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Copyright (c) 2026
3
+ * All rights reserved.
4
+ */
1
5
  #!/usr/bin/env node
2
6
 
3
7
  import { execSync } from 'child_process';
@@ -7,59 +11,70 @@ import readline from 'readline';
7
11
 
8
12
  // Helper to create an interactive terminal prompt using built-in readline
9
13
  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
- }));
14
+ const rl = readline.createInterface({
15
+ input: process.stdin,
16
+ output: process.stdout,
17
+ });
18
+ return new Promise(resolve =>
19
+ rl.question(query, ans => {
20
+ rl.close();
21
+ resolve(ans.trim());
22
+ })
23
+ );
18
24
  }
19
25
 
20
26
  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
- }
27
+ console.log('\n📦 Welcome to create-lib-workspace\n');
35
28
 
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
- }
29
+ // Parse inline arguments
30
+ const args = process.argv.slice(2);
31
+ let workspaceName = args[0];
32
+ let libName = args[1];
33
+ let packageName = args[2];
34
+ let pkgManager = args[3];
40
35
 
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
- }
36
+ // Fallback to interactive prompts if arguments are missing
37
+ if (!workspaceName) {
38
+ workspaceName = await askQuestion('Enter the workspace directory name (e.g. my-workspace): ');
39
+ if (!workspaceName) {
40
+ console.log('❌ Workspace name is required!');
41
+ process.exit(1);
42
+ }
43
+ }
45
44
 
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
- }
45
+ if (!libName) {
46
+ libName = await askQuestion('Enter the library directory name (e.g. core-lib): ');
47
+ if (!libName) {
48
+ console.log('❌ Library name is required!');
49
+ process.exit(1);
50
+ }
51
+ }
52
+
53
+ if (!packageName) {
54
+ packageName = await askQuestion('Enter the npm package name (e.g. @my-org/core): ');
55
+ if (!packageName) {
56
+ console.log('❌ Package name is required!');
57
+ process.exit(1);
58
+ }
59
+ }
53
60
 
54
- const workspacePath = path.resolve(workspaceName);
55
- const appPath = path.join(workspacePath, 'app');
56
- const libPath = path.join(workspacePath, libName);
61
+ if (!pkgManager) {
62
+ console.log('\nSelect package manager:\n1) pnpm (Default)\n2) npm\n3) yarn');
63
+ const choice = await askQuestion('Enter number [1-3]: ');
64
+ if (choice === '2') pkgManager = 'npm';
65
+ else if (choice === '3') pkgManager = 'yarn';
66
+ else pkgManager = 'pnpm';
67
+ }
57
68
 
58
- // 1. Create Workspace & App Structure
59
- console.log('\n[1/5] Creating workspace and consumer application...');
60
- fs.mkdirSync(appPath, { recursive: true });
69
+ const workspacePath = path.resolve(workspaceName);
70
+ const appPath = path.join(workspacePath, 'app');
71
+ const libPath = path.join(workspacePath, libName);
61
72
 
62
- const htmlContent = `<!doctype html>
73
+ // 1. Create Workspace & App Structure
74
+ console.log('\n[1/5] Creating workspace and consumer application...');
75
+ fs.mkdirSync(appPath, { recursive: true });
76
+
77
+ const htmlContent = `<!doctype html>
63
78
  <html>
64
79
  <head>
65
80
  <meta charset="UTF-8" />
@@ -71,7 +86,7 @@ async function main() {
71
86
  </body>
72
87
  </html>`;
73
88
 
74
- const mainJsContent = `import { pluck } from "${packageName}";
89
+ const mainJsContent = `import { pluck } from "${packageName}";
75
90
  import { increaseAllOf, lowerCaseAllOf } from "${packageName}/modifiers";
76
91
 
77
92
  const users = [
@@ -84,71 +99,124 @@ console.log("Plucked Names:", pluck(users, 'name'));
84
99
  console.log("Increased Age:", increaseAllOf(users, 'age'));
85
100
  console.log("Lowercased Names:", lowerCaseAllOf(users, 'name'));`;
86
101
 
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
- }
102
+ fs.writeFileSync(path.join(appPath, 'index.html'), htmlContent);
103
+ fs.writeFileSync(path.join(appPath, 'main.js'), mainJsContent);
107
104
 
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
- }
105
+ // 2. Run Vite Scaffolding
106
+ console.log('\n---------------------------------------------------------');
107
+ console.log('⚠️ ATTENTION / IMPORTANT NOTE:');
108
+ console.log('Vite will now prompt you to target a framework and a variant.');
109
+ console.log('1. Please SELECT "Vanilla" and "JavaScript".');
110
+ console.log('2. DO NOT let it install dependencies or run immediately!');
111
+ console.log('---------------------------------------------------------\n');
112
+
113
+ try {
114
+ execSync(`${pkgManager} create vite ${libName} --template vanilla`, {
115
+ cwd: workspacePath,
116
+ stdio: 'inherit',
117
+ });
118
+ } catch (error) {
119
+ console.log('❌ Vite scaffolding failed or was aborted.');
120
+ process.exit(1);
121
+ }
122
+
123
+ // 3. Clean up Vite Boilerplate & Generate Library Sources
124
+ console.log('\n[2/5] Polishing library structure and writing source files...');
125
+ fs.rmSync(path.join(libPath, 'src'), { recursive: true, force: true });
126
+ fs.mkdirSync(path.join(libPath, 'src/modifiers'), { recursive: true });
127
+
128
+ // Dynamically fetch the absolute latest Vite and Vitest versions from registry
129
+ let viteVersion = '5.0.0';
130
+ let vitestVersion = '1.0.0';
131
+ try {
132
+ viteVersion = execSync('npm info vite version', { encoding: 'utf8' }).trim();
133
+ vitestVersion = execSync('npm info vitest version', { encoding: 'utf8' }).trim();
134
+ } catch (e) {
135
+ // Graceful fallback if no network
136
+ }
137
+
138
+ // Write source files
139
+ fs.writeFileSync(
140
+ path.join(libPath, 'src/index.js'),
141
+ `export function pluck(collection, field) {\n return collection.map(item => item[field]);\n}`
142
+ );
143
+ fs.writeFileSync(
144
+ path.join(libPath, 'src/modifiers/increaseAll.js'),
145
+ `export function increaseAllOf(collection, field) {\n return collection.map(item => ({\n ...item,\n [field]: item[field] + 1\n }));\n}`
146
+ );
147
+ fs.writeFileSync(
148
+ path.join(libPath, 'src/modifiers/lowerCaseAll.js'),
149
+ `export function lowerCaseAllOf(collection, field) {\n return collection.map(item => ({\n ...item,\n [field]: String(item[field]).toLowerCase()\n }));\n}`
150
+ );
151
+ fs.writeFileSync(
152
+ path.join(libPath, 'src/modifiers/index.js'),
153
+ `import { increaseAllOf } from "./increaseAll.js";\nimport { lowerCaseAllOf } from "./lowerCaseAll.js";\n\nexport { increaseAllOf, lowerCaseAllOf };`
154
+ );
155
+
156
+ // NEW: Add a comprehensive Vitest test file matching the example code
157
+ const testContent = `import { describe, it, expect } from "vitest";
158
+ import { pluck } from "./index.js";
159
+ import { increaseAllOf, lowerCaseAllOf } from "./modifiers/index.js";
160
+
161
+ describe("Library Core & Modifiers Stresstest", () => {
162
+ const mockUsers = [
163
+ { name: 'Alice', age: 20 },
164
+ { name: 'Bob', age: 30 }
165
+ ];
120
166
 
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";
167
+ it("should correctly pluck fields from a collection", () => {
168
+ const names = pluck(mockUsers, 'name');
169
+ expect(names).toEqual(['Alice', 'Bob']);
170
+ });
171
+
172
+ it("should correctly increase numeric fields by 1", () => {
173
+ const updated = increaseAllOf(mockUsers, 'age');
174
+ expect(updated[0].age).toBe(21);
175
+ expect(updated[1].age).toBe(31);
176
+ // Ensure original object reference wasn't mutated directly
177
+ expect(mockUsers[0].age).toBe(20);
178
+ });
179
+
180
+ it("should correctly lowercase string fields", () => {
181
+ const updated = lowerCaseAllOf(mockUsers, 'name');
182
+ expect(updated).toEqual([
183
+ { name: 'alice', age: 20 },
184
+ { name: 'bob', age: 30 }
185
+ ]);
186
+ });
187
+ });`;
188
+
189
+ fs.writeFileSync(path.join(libPath, 'src/index.test.js'), testContent);
190
+
191
+ // Build custom package.json with added test scripts and vitest dependency
192
+ const libPackageJson = {
193
+ name: packageName,
194
+ version: '0.0.0',
195
+ type: 'module',
196
+ main: './dist/index.cjs',
197
+ module: './dist/index.js',
198
+ exports: {
199
+ '.': { import: './dist/index.js', require: './dist/index.cjs' },
200
+ './modifiers': { import: './dist/modifiers.js', require: './dist/modifiers.cjs' },
201
+ },
202
+ files: ['dist'],
203
+ scripts: {
204
+ dev: 'vite',
205
+ build: 'vite build',
206
+ watch: 'vite build --watch',
207
+ test: 'vitest',
208
+ 'test:run': 'vitest run',
209
+ },
210
+ devDependencies: {
211
+ vite: `^${viteVersion}`,
212
+ vitest: `^${vitestVersion}`,
213
+ },
214
+ };
215
+
216
+ fs.writeFileSync(path.join(libPath, 'package.json'), JSON.stringify(libPackageJson, null, 2));
217
+
218
+ // Build custom vite.config.js
219
+ const viteConfigContent = `import { defineConfig } from "vite";
152
220
  import { resolve, dirname } from "path";
153
221
  import { fileURLToPath } from "url";
154
222
 
@@ -170,48 +238,50 @@ export default defineConfig({
170
238
  }
171
239
  }
172
240
  });`;
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
- }
241
+ fs.writeFileSync(path.join(libPath, 'vite.config.js'), viteConfigContent);
242
+
243
+ // 4. Installing and Linking Dependencies
244
+ console.log(`\n[3/5] Installing library dependencies via ${pkgManager}...`);
245
+ execSync(`${pkgManager} install`, { cwd: libPath, stdio: 'inherit' });
246
+
247
+ console.log('\n[4/5] Linking library globally and attaching to the test application...');
248
+ if (pkgManager === 'npm') {
249
+ execSync('npm link', { cwd: libPath, stdio: 'ignore' });
250
+ execSync('npm init -y', { cwd: appPath, stdio: 'ignore' });
251
+ execSync(`npm link ${packageName}`, { cwd: appPath, stdio: 'ignore' });
252
+ } else if (pkgManager === 'yarn') {
253
+ execSync('yarn link --global', { cwd: libPath, stdio: 'ignore' });
254
+ execSync('yarn init -y', { cwd: appPath, stdio: 'ignore' });
255
+ execSync(`` + 'yarn link ' + packageName, { cwd: appPath, stdio: 'ignore' });
256
+ } else {
257
+ execSync('pnpm link --global', { cwd: libPath, stdio: 'ignore' });
258
+ execSync('pnpm init', { cwd: appPath, stdio: 'ignore' });
259
+ execSync(`pnpm link --global ${packageName}`, { cwd: appPath, stdio: 'ignore' });
260
+ }
261
+
262
+ // 5. Initial Library Build
263
+ console.log('\n[5/5] Running initial library build...');
264
+ execSync(`${pkgManager} run build`, { cwd: libPath, stdio: 'inherit' });
265
+
266
+ // Define package executor instructions
267
+ let execCmd = 'pnpm dlx';
268
+ if (pkgManager === 'npm') execCmd = 'npx';
269
+ if (pkgManager === 'yarn') execCmd = 'yarn dlx';
193
270
 
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');
271
+ console.log('\n=========================================================');
272
+ console.log(' 🎉 Setup successfully completed!');
273
+ console.log('=========================================================');
274
+ console.log(' To get started, open your terminals:\n');
275
+ console.log(' Terminal 1 (Library Watcher):');
276
+ console.log(` cd ${workspaceName}/${libName} && ${pkgManager} run watch\n`);
277
+ console.log(' Terminal 2 (Test App Server):');
278
+ console.log(` cd ${workspaceName}/app && ${execCmd} vite (--open)\n`);
279
+ console.log(' Terminal 3 (Interactive Test Suite):');
280
+ console.log(` cd ${workspaceName}/${libName} && ${pkgManager} run test`);
281
+ console.log('=========================================================\n');
212
282
  }
213
283
 
214
- main().catch((err) => {
215
- console.error('An unexpected error occurred:', err);
216
- process.exit(1);
284
+ main().catch(err => {
285
+ console.error('An unexpected error occurred:', err);
286
+ process.exit(1);
217
287
  });
package/package.json CHANGED
@@ -1,22 +1,28 @@
1
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
-
2
+ "name": "create-lib-workspace",
3
+ "version": "1.1.1",
4
+ "type": "module",
5
+ "bin": {
6
+ "create-lib-workspace": "./index.js"
7
+ },
8
+ "files": [
9
+ "index.js"
10
+ ],
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "cli",
14
+ "vite",
15
+ "library",
16
+ "workspace",
17
+ "scaffold"
18
+ ],
19
+ "author": "BarbWire-1 aka Barbara Kälin",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/BarbWire-1/create-lib-workspace.git"
23
+ },
24
+ "homepage": "https://github.com/BarbWire-1/create-lib-workspace",
25
+ "bugs": {
26
+ "url": "https://github.com/BarbWire-1/create-lib-workspace/issues"
27
+ }
22
28
  }